Compare commits

..

1 commit

Author SHA1 Message Date
Conrad Kramer
28af9003d0 merge boringtun into burrow 2023-10-15 22:27:05 -07:00
762 changed files with 1944 additions and 72202 deletions

View file

@ -1,10 +0,0 @@
.build
.derived-data
.git
.nscloud-cache
Apple/build
ci-artifacts
dist
publish
release
target

View file

@ -1 +0,0 @@
9.1.0

View file

@ -1,3 +1,6 @@
[target.'cfg(unix)']
runner = "sudo -E"
[alias] # command aliases [alias] # command aliases
rr = "run --release" rr = "run --release"
bb = "build --release" bb = "build --release"

View file

@ -1,327 +0,0 @@
name: Decrypt Release Secrets
description: Decrypts age-sealed Burrow release secrets and exports CI paths/env.
inputs:
root:
description: Repository root
required: false
default: .
runs:
using: composite
steps:
- shell: bash
run: |
set -euo pipefail
ROOT_INPUT='${{ inputs.root }}'
ROOT="$(cd "$ROOT_INPUT" && pwd)"
resolve_nix_bin() {
local candidate
for candidate in \
"${NIX_BIN:-}" \
"$(command -v nix 2>/dev/null || true)" \
/nix/var/nix/profiles/default/bin/nix \
"${HOME:-}/.nix-profile/bin/nix" \
"${HOME:-}/.local/state/nix/profile/bin/nix" \
/Users/runner/.nix-profile/bin/nix \
/Users/runner/.local/state/nix/profile/bin/nix \
/home/runner/.nix-profile/bin/nix \
/home/runner/.local/state/nix/profile/bin/nix \
; do
if [[ -n "$candidate" && -x "$candidate" ]]; then
printf '%s\n' "$candidate"
return 0
fi
done
return 1
}
resolve_decrypt_tool() {
local candidate
for candidate in \
"${AGE_BIN:-}" \
"$(command -v age 2>/dev/null || true)" \
/opt/homebrew/bin/age \
/usr/local/bin/age \
/usr/bin/age \
/run/current-system/sw/bin/age \
/etc/profiles/per-user/runner/bin/age \
"${HOME:-}/.nix-profile/bin/age" \
"${HOME:-}/.local/state/nix/profile/bin/age" \
/Users/runner/.nix-profile/bin/age \
/Users/runner/.local/state/nix/profile/bin/age \
/home/runner/.nix-profile/bin/age \
/home/runner/.local/state/nix/profile/bin/age \
; do
if [[ -n "$candidate" && -x "$candidate" ]]; then
printf 'age:%s\n' "$candidate"
return 0
fi
done
for candidate in \
"${AGENIX_BIN:-}" \
"$(command -v agenix 2>/dev/null || true)" \
; do
if [[ -n "$candidate" && -x "$candidate" ]]; then
printf 'agenix:%s\n' "$candidate"
return 0
fi
done
local nix_bin
nix_bin="$(resolve_nix_bin || true)"
if [[ -n "$nix_bin" ]]; then
local built
built="$("$nix_bin" --extra-experimental-features 'nix-command flakes' build --no-link "$ROOT#age" --print-out-paths 2>/dev/null | tail -n 1 || true)"
if [[ -n "$built" && -x "$built/bin/age" ]]; then
printf 'age:%s\n' "$built/bin/age"
return 0
fi
built="$("$nix_bin" --extra-experimental-features 'nix-command flakes' build --no-link "$ROOT#agenix" --print-out-paths 2>/dev/null | tail -n 1 || true)"
if [[ -n "$built" && -x "$built/bin/agenix" ]]; then
printf 'agenix:%s\n' "$built/bin/agenix"
return 0
fi
fi
echo "::error ::age or agenix is required to decrypt release secrets but neither is available." >&2
exit 1
}
decrypt_tool="$(resolve_decrypt_tool)"
decrypt_kind="${decrypt_tool%%:*}"
decrypt_bin="${decrypt_tool#*:}"
KEY="${AGE_IDENTITY_PATH:-}"
if [[ -z "$KEY" || ! -s "$KEY" ]]; then
echo "::error ::Missing age identity. Run Scripts/resolve-age-identity.sh before this action." >&2
exit 1
fi
decrypt_age_file() {
local file_path="$1"
if [[ "$decrypt_kind" == "agenix" ]]; then
local agenix_path="$file_path"
if [[ "$file_path" == "$ROOT/"* ]]; then
agenix_path="${file_path#"$ROOT"/}"
fi
(cd "$ROOT" && "$decrypt_bin" --identity "$KEY" --decrypt "$agenix_path")
else
"$decrypt_bin" --decrypt -i "$KEY" "$file_path"
fi
}
write_env() {
local name="$1"
local value="$2"
echo "${name}=${value}" >> "$GITHUB_ENV"
}
mask_multiline() {
local value="$1"
echo "::add-mask::${value}"
while IFS= read -r line; do
[[ -n "$line" ]] || continue
echo "::add-mask::${line}"
done <<<"$value"
}
decrypt_env_optional() {
local name="$1"
local file="$2"
if [[ ! -f "$file" ]]; then
echo "::notice ::Skipping optional release secret ${file}"
return 0
fi
local value
value="$(decrypt_age_file "$file")"
mask_multiline "$value"
{
echo "${name}<<EOF"
printf '%s\n' "$value"
echo "EOF"
} >> "$GITHUB_ENV"
}
decrypt_file_optional() {
local name="$1"
local file="$2"
local suffix="$3"
if [[ ! -f "$file" ]]; then
echo "::notice ::Skipping optional release secret ${file}"
return 0
fi
local temp_dir="${RUNNER_TEMP:-/tmp}"
local temp_file
temp_file="$(mktemp "${temp_dir}/${name}.XXXXXX")"
if [[ -n "$suffix" ]]; then
local with_suffix="${temp_file}.${suffix}"
mv "$temp_file" "$with_suffix"
temp_file="$with_suffix"
fi
decrypt_age_file "$file" > "$temp_file"
chmod 600 "$temp_file"
write_env "$name" "$temp_file"
}
decrypt_dotenv_optional() {
local file="$1"
if [[ ! -f "$file" ]]; then
echo "::notice ::Skipping optional release secret ${file}"
return 0
fi
local temp_file
temp_file="$(mktemp "${RUNNER_TEMP:-/tmp}/burrow-release-dotenv.XXXXXX")"
decrypt_age_file "$file" > "$temp_file"
# shellcheck disable=SC1090
set -a
source "$temp_file"
set +a
rm -f "$temp_file"
if [[ -z "${MINIO_ROOT_USER:-}" || -z "${MINIO_ROOT_PASSWORD:-}" ]]; then
echo "::error ::${file} must define MINIO_ROOT_USER and MINIO_ROOT_PASSWORD." >&2
exit 1
fi
echo "::add-mask::${MINIO_ROOT_USER}"
echo "::add-mask::${MINIO_ROOT_PASSWORD}"
{
echo "AWS_ACCESS_KEY_ID=${MINIO_ROOT_USER}"
echo "AWS_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}"
echo "AWS_DEFAULT_REGION=${BLOB_REGION:-hel1}"
echo "AWS_REGION=${BLOB_REGION:-hel1}"
echo "AWS_S3_ENDPOINT_URL=https://${BLOB_ENDPOINT:-hel1.your-objectstorage.com}"
} >> "$GITHUB_ENV"
}
decode_base64_stream() {
if command -v base64 >/dev/null 2>&1; then
base64 -d 2>/dev/null || base64 -D
elif command -v openssl >/dev/null 2>&1; then
openssl base64 -d -A
else
echo "::error ::base64 or openssl is required to decode release secrets." >&2
return 127
fi
}
decode_base64_to_file() {
local output="$1"
decode_base64_stream > "$output"
}
decrypt_appstore_connect() {
local file="$ROOT/secrets/apple/appstore-connect.env.age"
if [[ ! -f "$file" ]]; then
echo "::notice ::Skipping optional release secret ${file}"
return 0
fi
local temp_file key_path key_value
temp_file="$(mktemp "${RUNNER_TEMP:-/tmp}/burrow-asc-env.XXXXXX")"
decrypt_age_file "$file" > "$temp_file"
# shellcheck disable=SC1090
set -a
source "$temp_file"
set +a
rm -f "$temp_file"
if [[ -z "${ASC_API_KEY_ID:-}" || -z "${ASC_API_ISSUER_ID:-}" ]]; then
echo "::error ::${file} must define ASC_API_KEY_ID and ASC_API_ISSUER_ID." >&2
exit 1
fi
key_path="$(mktemp "${RUNNER_TEMP:-/tmp}/burrow-asc-key.XXXXXX.p8")"
if [[ -n "${ASC_API_KEY_BASE64:-}" ]]; then
printf '%s' "$ASC_API_KEY_BASE64" | decode_base64_to_file "$key_path"
elif [[ -n "${ASC_API_KEY_P8_BASE64:-}" ]]; then
printf '%s' "$ASC_API_KEY_P8_BASE64" | decode_base64_to_file "$key_path"
elif [[ -n "${APPSTORE_KEY:-}" ]]; then
printf '%s\n' "$APPSTORE_KEY" > "$key_path"
elif [[ -n "${ASC_API_KEY:-}" ]]; then
printf '%s\n' "$ASC_API_KEY" > "$key_path"
else
echo "::error ::${file} must define ASC_API_KEY_BASE64 or ASC_API_KEY." >&2
exit 1
fi
chmod 600 "$key_path"
key_value="$(cat "$key_path")"
mask_multiline "$key_value"
echo "::add-mask::${ASC_API_KEY_ID}"
echo "::add-mask::${ASC_API_ISSUER_ID}"
write_env ASC_API_KEY_ID "$ASC_API_KEY_ID"
write_env ASC_API_ISSUER_ID "$ASC_API_ISSUER_ID"
write_env ASC_API_KEY_PATH "$key_path"
}
decrypt_distribution_signing() {
local file="$ROOT/secrets/apple/distribution-signing.env.age"
if [[ ! -f "$file" ]]; then
echo "::notice ::Skipping optional release secret ${file}"
return 0
fi
local temp_file cert_path cert_password cert_base64
temp_file="$(mktemp "${RUNNER_TEMP:-/tmp}/burrow-signing-env.XXXXXX")"
decrypt_age_file "$file" > "$temp_file"
# shellcheck disable=SC1090
set -a
source "$temp_file"
set +a
rm -f "$temp_file"
cert_base64="${APPLE_SIGNING_CERTIFICATE_BASE64:-${APPLE_DISTRIBUTION_CERTIFICATE_BASE64:-}}"
cert_password="${APPLE_SIGNING_CERTIFICATE_PASSWORD:-${APPLE_DISTRIBUTION_CERTIFICATE_PASSWORD:-}}"
if [[ -z "$cert_password" && -n "${APPLE_SIGNING_CERTIFICATE_PASSWORD_BASE64:-}" ]]; then
cert_password="$(printf '%s' "$APPLE_SIGNING_CERTIFICATE_PASSWORD_BASE64" | decode_base64_stream)"
fi
if [[ -z "$cert_base64" || -z "$cert_password" ]]; then
echo "::error ::${file} must define APPLE_SIGNING_CERTIFICATE_BASE64 and APPLE_SIGNING_CERTIFICATE_PASSWORD." >&2
exit 1
fi
cert_path="$(mktemp "${RUNNER_TEMP:-/tmp}/burrow-apple-signing.XXXXXX.p12")"
printf '%s' "$cert_base64" | decode_base64_to_file "$cert_path"
chmod 600 "$cert_path"
echo "::add-mask::${cert_password}"
write_env APPLE_SIGNING_CERTIFICATE_PATH "$cert_path"
write_env APPLE_SIGNING_CERTIFICATE_PASSWORD "$cert_password"
write_env APPLE_KEYCHAIN_PASSWORD "$cert_password"
}
decrypt_sparkle() {
local file="$ROOT/secrets/apple/sparkle.env.age"
if [[ ! -f "$file" ]]; then
echo "::notice ::Skipping optional release secret ${file}"
return 0
fi
local temp_file key_path key_base64 key_value
temp_file="$(mktemp "${RUNNER_TEMP:-/tmp}/burrow-sparkle-env.XXXXXX")"
decrypt_age_file "$file" > "$temp_file"
# shellcheck disable=SC1090
set -a
source "$temp_file"
set +a
rm -f "$temp_file"
key_base64="${SPARKLE_EDDSA_KEY_BASE64:-${SPARKLE_PRIVATE_KEY_BASE64:-}}"
key_path="$(mktemp "${RUNNER_TEMP:-/tmp}/burrow-sparkle-eddsa.XXXXXX.key")"
if [[ -n "$key_base64" ]]; then
printf '%s' "$key_base64" | decode_base64_to_file "$key_path"
elif [[ -n "${SPARKLE_EDDSA_KEY:-}" ]]; then
printf '%s\n' "$SPARKLE_EDDSA_KEY" > "$key_path"
elif [[ -n "${SPARKLE_PRIVATE_KEY:-}" ]]; then
printf '%s\n' "$SPARKLE_PRIVATE_KEY" > "$key_path"
else
echo "::error ::${file} must define SPARKLE_EDDSA_KEY_BASE64 or SPARKLE_EDDSA_KEY." >&2
exit 1
fi
chmod 600 "$key_path"
key_value="$(cat "$key_path")"
mask_multiline "$key_value"
write_env SPARKLE_EDDSA_KEY_PATH "$key_path"
}
decrypt_appstore_connect
decrypt_distribution_signing
decrypt_sparkle
decrypt_dotenv_optional "$ROOT/secrets/hetzner/object-storage.age"

View file

@ -1,98 +0,0 @@
name: "Apple: Developer ID KMS CSR"
run-name: "Apple: Developer ID KMS CSR (${{ github.event.inputs.kms_key || 'apple-developer-id-application' }})"
on:
workflow_dispatch:
inputs:
kms_key:
description: Google KMS key name in the Burrow identity key ring
required: false
default: apple-developer-id-application
kms_version:
description: Google KMS key version
required: false
default: "1"
common_name:
description: CSR common name
required: false
default: Burrow Developer ID Application
email_address:
description: CSR email address
required: false
default: release@burrow.net
permissions:
contents: read
concurrency:
group: apple-developer-id-kms-csr-${{ github.ref }}
cancel-in-progress: false
env:
BURROW_GOOGLE_PROJECT_ID: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
GOOGLE_CLOUD_PROJECT: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
BURROW_GOOGLE_PROJECT_NUMBER: ${{ vars.GOOGLE_PROJECT_NUMBER || '416198671487' }}
BURROW_GOOGLE_WIF_POOL_ID: ${{ vars.BURROW_GOOGLE_WIF_POOL_ID || 'burrow-authentik' }}
BURROW_GOOGLE_WIF_PROVIDER_ID: ${{ vars.BURROW_GOOGLE_WIF_PROVIDER_ID || 'forgejo-runners' }}
BURROW_GOOGLE_WIF_SERVICE_ACCOUNT: ${{ vars.BURROW_GOOGLE_WIF_SERVICE_ACCOUNT || 'burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com' }}
BURROW_AUTHENTIK_WIF_ISSUER: ${{ vars.BURROW_AUTHENTIK_WIF_ISSUER || 'https://auth.burrow.net/application/o/google-cloud/' }}
BURROW_AUTHENTIK_WIF_AUDIENCE: ${{ vars.BURROW_AUTHENTIK_WIF_AUDIENCE || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_ID: ${{ vars.BURROW_AUTHENTIK_WIF_CLIENT_ID || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_SECRET: ${{ secrets.BURROW_AUTHENTIK_WIF_CLIENT_SECRET }}
BURROW_KMS_LOCATION: global
BURROW_KMS_KEYRING: burrow-identity
jobs:
csr:
name: Generate CSR
runs-on: [self-hosted, linux, x86_64, burrow-forge]
timeout-minutes: 20
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Authenticate Google WIF
shell: bash
run: nix develop .#ci -c Scripts/ci/google-wif-auth.sh
- name: Generate KMS-backed CSR
shell: bash
env:
BURROW_APPLE_DEVELOPER_ID_KMS_KEY: ${{ github.event.inputs.kms_key || 'apple-developer-id-application' }}
BURROW_KMS_VERSION: ${{ github.event.inputs.kms_version || '1' }}
BURROW_APPLE_DEVELOPER_ID_CSR_COMMON_NAME: ${{ github.event.inputs.common_name || 'Burrow Developer ID Application' }}
BURROW_APPLE_DEVELOPER_ID_CSR_EMAIL: ${{ github.event.inputs.email_address || 'release@burrow.net' }}
run: |
set -euo pipefail
mkdir -p dist/apple-developer-id
nix develop .#ci -c Scripts/apple/google-kms-csr.py \
--output dist/apple-developer-id/developer-id-application.certSigningRequest \
--public-key-output dist/apple-developer-id/google-kms-public-key.pem
if command -v openssl >/dev/null 2>&1; then
openssl req -in dist/apple-developer-id/developer-id-application.certSigningRequest -noout -verify
fi
cat > dist/apple-developer-id/README.txt <<'EOF'
This CSR is backed by a non-exportable Google Cloud KMS key in the Burrow
identity key ring. Apple currently documents Developer ID certificate creation
through the Developer website or Xcode, not App Store Connect API creation.
Upload developer-id-application.certSigningRequest in:
Certificates, Identifiers & Profiles -> Certificates -> + -> Developer ID ->
Developer ID Application.
Keep the returned .cer file with release artifacts. The private key remains in
Google Cloud KMS and is never exported as a p12.
EOF
- name: Upload CSR Artifact
uses: https://code.forgejo.org/actions/upload-artifact@v3
with:
name: burrow-developer-id-kms-csr
path: dist/apple-developer-id

View file

@ -1,530 +0,0 @@
name: "Apple: Distribute Testers"
run-name: "Apple: Distribute Testers (${{ github.ref_name }})"
on:
workflow_dispatch:
inputs:
build_number_override:
description: Optional explicit build number
required: false
default: ""
upload_app_store:
description: Upload iOS artifact to App Store Connect and TestFlight
required: false
default: "true"
upload_sparkle:
description: Publish Sparkle appcast and macOS artifact
required: false
default: "true"
sparkle_channel:
description: Sparkle channel, defaults to build-<number>
required: false
default: ""
sparkle_point_main:
description: Update Sparkle main channel pointers
required: false
default: "true"
testflight_distribute_external:
description: Distribute to external TestFlight groups
required: false
default: "false"
testflight_groups:
description: Optional comma-separated TestFlight groups
required: false
default: ""
ios_uses_non_exempt_encryption:
description: Set true only when the iOS build uses non-exempt encryption
required: false
default: "false"
permissions:
actions: write
contents: read
concurrency:
group: burrow-apple-distribute-testers-${{ github.ref }}
cancel-in-progress: true
env:
BURROW_RELEASE_GCS_BUCKET: ${{ vars.BURROW_RELEASE_GCS_BUCKET || 'burrow-net-releases' }}
BURROW_RELEASE_GCS_BACKUP: ${{ vars.BURROW_RELEASE_GCS_BACKUP || 'true' }}
BURROW_RELEASE_GARAGE_BUCKET: ${{ vars.BURROW_RELEASE_GARAGE_BUCKET || 'burrow-releases' }}
BURROW_RELEASE_REQUIRE_GARAGE: ${{ vars.BURROW_RELEASE_REQUIRE_GARAGE || 'true' }}
BURROW_GARAGE_ENDPOINT: ${{ vars.BURROW_GARAGE_ENDPOINT || 'https://objects.burrow.net' }}
BURROW_GARAGE_REGION: ${{ vars.BURROW_GARAGE_REGION || 'garage' }}
AWS_ACCESS_KEY_ID: ${{ secrets.BURROW_GARAGE_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.BURROW_GARAGE_SECRET_ACCESS_KEY }}
BURROW_GOOGLE_PROJECT_ID: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
GOOGLE_CLOUD_PROJECT: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
BURROW_GOOGLE_PROJECT_NUMBER: ${{ vars.GOOGLE_PROJECT_NUMBER || '416198671487' }}
BURROW_GOOGLE_WIF_POOL_ID: ${{ vars.BURROW_GOOGLE_WIF_POOL_ID || 'burrow-authentik' }}
BURROW_GOOGLE_WIF_PROVIDER_ID: ${{ vars.BURROW_GOOGLE_WIF_PROVIDER_ID || 'forgejo-runners' }}
BURROW_GOOGLE_WIF_SERVICE_ACCOUNT: ${{ vars.BURROW_GOOGLE_WIF_SERVICE_ACCOUNT || 'burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com' }}
BURROW_AUTHENTIK_WIF_ISSUER: ${{ vars.BURROW_AUTHENTIK_WIF_ISSUER || 'https://auth.burrow.net/application/o/google-cloud/' }}
BURROW_AUTHENTIK_WIF_AUDIENCE: ${{ vars.BURROW_AUTHENTIK_WIF_AUDIENCE || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_ID: ${{ vars.BURROW_AUTHENTIK_WIF_CLIENT_ID || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_SECRET: ${{ secrets.BURROW_AUTHENTIK_WIF_CLIENT_SECRET }}
BURROW_NIX_CACHE_URL: ${{ vars.BURROW_NIX_CACHE_URL || 'https://nix.burrow.net/burrow' }}
BURROW_NIX_CACHE_PUBLIC_KEY: ${{ vars.BURROW_NIX_CACHE_PUBLIC_KEY }}
BURROW_APPLE_BUNDLE_ID: ${{ vars.BURROW_APPLE_BUNDLE_ID || 'net.burrow.app' }}
BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID: ${{ vars.BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID || 'net.burrow.app.network' }}
BURROW_IOS_DISTRIBUTION_CERTIFICATE_ID: ${{ vars.BURROW_IOS_DISTRIBUTION_CERTIFICATE_ID || '3G42677598' }}
BURROW_DEVELOPER_ID_CERTIFICATE_ID: ${{ vars.BURROW_DEVELOPER_ID_CERTIFICATE_ID || '9JKN6HXBHC' }}
BURROW_APPLE_SIGNING_MODE: google-kms-staged
BURROW_NOTARIZE_MACOS: ${{ vars.BURROW_NOTARIZE_MACOS || 'true' }}
BURROW_SPARKLE_SIGN_WITH_KMS: ${{ vars.BURROW_SPARKLE_SIGN_WITH_KMS || 'true' }}
BURROW_SPARKLE_KMS_KEY: ${{ vars.BURROW_SPARKLE_KMS_KEY || 'sparkle-ed25519' }}
BURROW_SPARKLE_KMS_VERSION: ${{ vars.BURROW_SPARKLE_KMS_VERSION || '1' }}
BURROW_SPARKLE_FEED_URL: ${{ vars.BURROW_SPARKLE_FEED_URL || 'https://releases.burrow.net/sparkle/appcast.xml' }}
BURROW_SPARKLE_PUBLIC_ED_KEY: ${{ vars.BURROW_SPARKLE_PUBLIC_ED_KEY || 'uugZuJeqvvKd91NZ6F1Fv2cQenUbIG/ZW3L9MuaEz30=' }}
BURROW_KMS_LOCATION: ${{ vars.BURROW_KMS_LOCATION || 'global' }}
BURROW_KMS_KEYRING: ${{ vars.BURROW_KMS_KEYRING || 'burrow-identity' }}
TESTFLIGHT_WAIT_PROCESSING_TIMEOUT_SECONDS: ${{ vars.TESTFLIGHT_WAIT_PROCESSING_TIMEOUT_SECONDS || '7200' }}
BURROW_VERSION_REQUIRE_REMOTE: "1"
jobs:
prepare:
name: Prepare Apple Distribution
runs-on: namespace-profile-linux-medium-apple-v2
outputs:
build_number: ${{ steps.plan.outputs.build_number }}
sparkle_channel: ${{ steps.plan.outputs.sparkle_channel }}
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Fetch Build Tags
shell: bash
run: git fetch --force --prune origin "+refs/tags/builds/*:refs/tags/builds/*"
- name: Compute Apple Distribution Plan
id: plan
shell: bash
env:
BUILD_NUMBER_OVERRIDE: ${{ github.event.inputs.build_number_override || '' }}
SPARKLE_CHANNEL_INPUT: ${{ github.event.inputs.sparkle_channel || '' }}
run: |
set -euo pipefail
if [[ -n "$BUILD_NUMBER_OVERRIDE" ]]; then
build_number="$BUILD_NUMBER_OVERRIDE"
else
build_number="$(Scripts/version.sh pre-increment)"
fi
channel="$SPARKLE_CHANNEL_INPUT"
if [[ -z "$channel" ]]; then
channel="build-${build_number}"
fi
echo "build_number=${build_number}" >> "$GITHUB_OUTPUT"
echo "sparkle_channel=${channel}" >> "$GITHUB_OUTPUT"
printf 'build_number=%s\nsparkle_channel=%s\n' "$build_number" "$channel"
build-ios:
name: Build Apple iOS
needs: prepare
runs-on: namespace-profile-macos-large-apple-v2
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
submodules: recursive
- name: Select Apple SDK
shell: bash
run: |
set -euo pipefail
xcrun --find swiftc
xcrun --sdk iphoneos --show-sdk-path
xcrun --sdk iphonesimulator --show-sdk-path
- name: Prepare Rust Apple Targets
shell: bash
run: |
set -euo pipefail
if ! command -v rustup >/dev/null 2>&1; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable
export PATH="$HOME/.cargo/bin:$PATH"
fi
rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios
- name: Bootstrap Runner Environment
shell: bash
run: |
set -euo pipefail
runner_home="${RUNNER_HOME:-}"
if [[ -z "$runner_home" ]]; then
runner_home="$(python3 -c 'import os,pwd; print(pwd.getpwuid(os.getuid()).pw_dir)' 2>/dev/null || true)"
if [[ -z "$runner_home" ]]; then
runner_home="$PWD/.home"
fi
fi
mkdir -p "$runner_home" "$PWD/.tmp"
{
echo "HOME=$runner_home"
echo "XDG_CACHE_HOME=$runner_home/.cache"
echo "XDG_CONFIG_HOME=$runner_home/.config"
echo "XDG_DATA_HOME=$runner_home/.local/share"
echo "TMPDIR=$PWD/.tmp"
echo "TMP=$PWD/.tmp"
} >> "$GITHUB_ENV"
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Namespace Cache
shell: bash
run: |
set -euo pipefail
bash Scripts/ci/nscloud-cache.sh bazel
- name: Build Staged iOS Artifact
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
run: |
set -euo pipefail
bazel_args=()
if [[ -n "${BURROW_BAZEL_NSCCACHE_BAZELRC:-}" && -f "${BURROW_BAZEL_NSCCACHE_BAZELRC:-}" ]]; then
bazel_args+=("--bazelrc=${BURROW_BAZEL_NSCCACHE_BAZELRC}")
fi
NIX_BIN="$(bash Scripts/ci/resolve-nix-bin.sh)"
ci_path="$("$NIX_BIN" develop .#ci --command bash -lc 'printf "%s" "$PATH"')"
ci_protoc="$("$NIX_BIN" develop .#ci --command bash -lc 'command -v protoc')"
"$NIX_BIN" develop .#ci --command bazel "${bazel_args[@]}" build \
--verbose_failures \
--show_timestamps \
--experimental_ui_max_stdouterr_bytes=16777216 \
--action_env=PATH="$ci_path" \
--action_env=PROTOC="$ci_protoc" \
--action_env=BUILD_WORKSPACE_DIRECTORY="$PWD" \
--action_env=BUILD_NUMBER="$BUILD_NUMBER" \
--action_env=BURROW_RELEASE_OUT="$PWD/dist/builds/$BUILD_NUMBER" \
--action_env=BURROW_APPLE_SIGNING_MODE="$BURROW_APPLE_SIGNING_MODE" \
--action_env=BURROW_APPLE_REQUIRE_SIGNING=true \
--action_env=BURROW_APPLE_BUNDLE_ID="$BURROW_APPLE_BUNDLE_ID" \
--action_env=BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID="$BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID" \
--action_env=HOME="$HOME" \
--action_env=XDG_CACHE_HOME="$XDG_CACHE_HOME" \
//bazel/apple:release_ios_stamp
- name: Upload Staged iOS Artifact
uses: https://code.forgejo.org/actions/upload-artifact@v3
with:
name: burrow-apple-ios-${{ needs.prepare.outputs.build_number }}
path: |
dist/builds/${{ needs.prepare.outputs.build_number }}/apple/Burrow-iOS-${{ needs.prepare.outputs.build_number }}.unsigned.ipa
dist/builds/${{ needs.prepare.outputs.build_number }}/apple/Burrow-iOS-${{ needs.prepare.outputs.build_number }}.unsigned.ipa.sha256
if-no-files-found: error
build-macos:
name: Build Apple macOS
needs: prepare
runs-on: namespace-profile-macos-large-apple-v2
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
submodules: recursive
- name: Select Apple SDK
shell: bash
run: |
set -euo pipefail
xcrun --find swiftc
xcrun --sdk macosx --show-sdk-path
- name: Prepare Rust Apple Targets
shell: bash
run: |
set -euo pipefail
if ! command -v rustup >/dev/null 2>&1; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable
export PATH="$HOME/.cargo/bin:$PATH"
fi
rustup target add aarch64-apple-darwin x86_64-apple-darwin
- name: Bootstrap Runner Environment
shell: bash
run: |
set -euo pipefail
runner_home="${RUNNER_HOME:-}"
if [[ -z "$runner_home" ]]; then
runner_home="$(python3 -c 'import os,pwd; print(pwd.getpwuid(os.getuid()).pw_dir)' 2>/dev/null || true)"
if [[ -z "$runner_home" ]]; then
runner_home="$PWD/.home"
fi
fi
mkdir -p "$runner_home" "$PWD/.tmp"
{
echo "HOME=$runner_home"
echo "XDG_CACHE_HOME=$runner_home/.cache"
echo "XDG_CONFIG_HOME=$runner_home/.config"
echo "XDG_DATA_HOME=$runner_home/.local/share"
echo "TMPDIR=$PWD/.tmp"
echo "TMP=$PWD/.tmp"
} >> "$GITHUB_ENV"
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Namespace Cache
shell: bash
run: |
set -euo pipefail
bash Scripts/ci/nscloud-cache.sh bazel
- name: Build Staged macOS Artifact
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
run: |
set -euo pipefail
bazel_args=()
if [[ -n "${BURROW_BAZEL_NSCCACHE_BAZELRC:-}" && -f "${BURROW_BAZEL_NSCCACHE_BAZELRC:-}" ]]; then
bazel_args+=("--bazelrc=${BURROW_BAZEL_NSCCACHE_BAZELRC}")
fi
NIX_BIN="$(bash Scripts/ci/resolve-nix-bin.sh)"
ci_path="$("$NIX_BIN" develop .#ci --command bash -lc 'printf "%s" "$PATH"')"
ci_protoc="$("$NIX_BIN" develop .#ci --command bash -lc 'command -v protoc')"
"$NIX_BIN" develop .#ci --command bazel "${bazel_args[@]}" build \
--verbose_failures \
--show_timestamps \
--experimental_ui_max_stdouterr_bytes=16777216 \
--action_env=PATH="$ci_path" \
--action_env=PROTOC="$ci_protoc" \
--action_env=BUILD_WORKSPACE_DIRECTORY="$PWD" \
--action_env=BUILD_NUMBER="$BUILD_NUMBER" \
--action_env=BURROW_RELEASE_OUT="$PWD/dist/builds/$BUILD_NUMBER" \
--action_env=BURROW_APPLE_SIGNING_MODE="$BURROW_APPLE_SIGNING_MODE" \
--action_env=BURROW_APPLE_REQUIRE_SIGNING=true \
--action_env=BURROW_APPLE_BUNDLE_ID="$BURROW_APPLE_BUNDLE_ID" \
--action_env=BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID="$BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID" \
--action_env=BURROW_SPARKLE_FEED_URL="$BURROW_SPARKLE_FEED_URL" \
--action_env=BURROW_SPARKLE_PUBLIC_ED_KEY="$BURROW_SPARKLE_PUBLIC_ED_KEY" \
--action_env=HOME="$HOME" \
--action_env=XDG_CACHE_HOME="$XDG_CACHE_HOME" \
//bazel/apple:release_macos_stamp
- name: Upload Staged macOS Artifact
uses: https://code.forgejo.org/actions/upload-artifact@v3
with:
name: burrow-apple-macos-${{ needs.prepare.outputs.build_number }}
path: |
dist/builds/${{ needs.prepare.outputs.build_number }}/apple/Burrow-macOS-${{ needs.prepare.outputs.build_number }}.unsigned.zip
dist/builds/${{ needs.prepare.outputs.build_number }}/apple/Burrow-macOS-${{ needs.prepare.outputs.build_number }}.unsigned.zip.sha256
if-no-files-found: error
sign-apple:
name: Sign Apple Artifacts
needs:
- prepare
- build-ios
- build-macos
runs-on: namespace-profile-linux-medium-apple-v2
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Configure Age
shell: bash
env:
AGE_FORGE_SSH_KEY: ${{ secrets.AGE_FORGE_SSH_KEY }}
run: Scripts/resolve-age-identity.sh >> "$GITHUB_ENV"
- name: Decrypt Release Secrets
uses: ./.forgejo/actions/decrypt-release-secrets
- name: Validate App Store Credentials
shell: bash
run: Scripts/ci/check-release-config.sh app-store
- name: Authenticate Google WIF
shell: bash
run: nix develop .#ci -c Scripts/ci/google-wif-auth.sh
- name: Download Staged Apple Artifacts
uses: https://code.forgejo.org/actions/download-artifact@v3
with:
path: dist/downloaded
- name: Prepare Staged Apple Directory
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
run: |
set -euo pipefail
mkdir -p "dist/builds/${BUILD_NUMBER}/apple"
find dist/downloaded -type f -name 'Burrow-*.unsigned.*' -exec cp -v {} "dist/builds/${BUILD_NUMBER}/apple/" \;
find "dist/builds/${BUILD_NUMBER}/apple" -type f -print | sort
- name: Sync Apple Provisioning Profiles
shell: bash
run: nix develop .#ci -c Scripts/ci/sync-apple-provisioning-profiles.sh all
- name: Sign Apple Artifacts With KMS
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
SPARKLE_CHANNEL: ${{ needs.prepare.outputs.sparkle_channel }}
run: nix develop .#ci -c Scripts/apple/sign-release-artifacts-kms.sh all
- name: Upload Signed Apple Artifacts To Release Storage
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
run: nix develop .#ci -c Scripts/ci/upload-release-storage.sh
- name: Upload Signed Apple Artifacts
uses: https://code.forgejo.org/actions/upload-artifact@v3
with:
name: burrow-apple-signed-${{ needs.prepare.outputs.build_number }}
path: |
dist/builds/${{ needs.prepare.outputs.build_number }}/apple/Burrow-iOS-${{ needs.prepare.outputs.build_number }}.ipa
dist/builds/${{ needs.prepare.outputs.build_number }}/apple/Burrow-iOS-${{ needs.prepare.outputs.build_number }}.ipa.sha256
dist/builds/${{ needs.prepare.outputs.build_number }}/apple/Burrow-macOS-${{ needs.prepare.outputs.build_number }}.zip
dist/builds/${{ needs.prepare.outputs.build_number }}/apple/Burrow-macOS-${{ needs.prepare.outputs.build_number }}.zip.sha256
dist/builds/${{ needs.prepare.outputs.build_number }}/sparkle/**
if-no-files-found: error
upload-testflight:
name: Upload TestFlight
needs:
- prepare
- sign-apple
if: ${{ github.event.inputs.upload_app_store == 'true' }}
runs-on: namespace-profile-macos-large-testflight
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Configure Age
shell: bash
env:
AGE_FORGE_SSH_KEY: ${{ secrets.AGE_FORGE_SSH_KEY }}
run: Scripts/resolve-age-identity.sh >> "$GITHUB_ENV"
- name: Decrypt Release Secrets
uses: ./.forgejo/actions/decrypt-release-secrets
- name: Download Signed Apple Artifacts
uses: https://code.forgejo.org/actions/download-artifact@v3
with:
name: burrow-apple-signed-${{ needs.prepare.outputs.build_number }}
path: publish
- name: Upload iOS IPA To App Store Connect
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
run: |
set -euo pipefail
Scripts/ci/check-release-config.sh app-store
key_dir="${RUNNER_TEMP:-/tmp}/burrow-asc"
altool_key_dir="${HOME}/.appstoreconnect/private_keys"
mkdir -p "$key_dir" "$altool_key_dir"
key_path="$key_dir/AuthKey_${ASC_API_KEY_ID}.p8"
if [[ -n "${ASC_API_KEY_PATH:-}" && -f "${ASC_API_KEY_PATH:-}" ]]; then
cp "$ASC_API_KEY_PATH" "$key_path"
else
printf '%s' "$ASC_API_KEY_BASE64" | base64 --decode > "$key_path"
fi
cp "$key_path" "$altool_key_dir/AuthKey_${ASC_API_KEY_ID}.p8"
chmod 600 "$key_path" "$altool_key_dir/AuthKey_${ASC_API_KEY_ID}.p8"
ipa="publish/apple/Burrow-iOS-${BUILD_NUMBER}.ipa"
if [[ ! -s "$ipa" ]]; then
echo "::error ::No signed iOS IPA found at $ipa" >&2
find publish -type f -print | sort
exit 1
fi
upload_log="${RUNNER_TEMP:-/tmp}/burrow-altool-ios-${BUILD_NUMBER}.log"
set +e
xcrun altool --upload-app \
--type ios \
--file "$ipa" \
--apiKey "$ASC_API_KEY_ID" \
--apiIssuer "$ASC_API_ISSUER_ID" 2>&1 | tee "$upload_log"
altool_status=${PIPESTATUS[0]}
set -e
if (( altool_status != 0 )) || grep -Eq 'UPLOAD FAILED|Validation failed|ERROR:' "$upload_log"; then
echo "::error ::altool reported upload validation failure for $ipa" >&2
exit 1
fi
- name: Distribute iOS Build To TestFlight
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
TESTFLIGHT_DISTRIBUTE_EXTERNAL: ${{ github.event.inputs.testflight_distribute_external || 'false' }}
TESTFLIGHT_GROUPS: ${{ github.event.inputs.testflight_groups || '' }}
IOS_USES_NON_EXEMPT_ENCRYPTION: ${{ github.event.inputs.ios_uses_non_exempt_encryption || 'false' }}
run: Scripts/ci/distribute-testflight.sh
publish-sparkle:
name: Publish Sparkle
needs:
- prepare
- sign-apple
if: ${{ github.event.inputs.upload_sparkle == 'true' }}
runs-on: namespace-profile-linux-medium-apple-v2
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Authenticate Google WIF
shell: bash
run: nix develop .#ci -c Scripts/ci/google-wif-auth.sh
- name: Download Signed Apple Artifacts
uses: https://code.forgejo.org/actions/download-artifact@v3
with:
name: burrow-apple-signed-${{ needs.prepare.outputs.build_number }}
path: publish
- name: Publish Sparkle Channel
shell: bash
env:
CHANNEL: ${{ needs.prepare.outputs.sparkle_channel }}
SPARKLE_POINT_MAIN: ${{ github.event.inputs.sparkle_point_main || 'true' }}
run: |
set -euo pipefail
if [[ ! -f "publish/sparkle/${CHANNEL}/appcast.xml" ]]; then
echo "::error ::No Sparkle appcast staged for channel ${CHANNEL}" >&2
find publish -type f -print | sort
exit 1
fi
nix develop .#ci -c gcloud storage rsync --recursive "publish/sparkle/${CHANNEL}" "gs://${BURROW_RELEASE_GCS_BUCKET}/sparkle/${CHANNEL}"
if [[ "$SPARKLE_POINT_MAIN" == "true" ]]; then
printf '%s\n' "$CHANNEL" > main-channel.txt
nix develop .#ci -c gcloud storage cp main-channel.txt "gs://${BURROW_RELEASE_GCS_BUCKET}/sparkle/main-channel.txt"
nix develop .#ci -c gcloud storage cp "publish/sparkle/${CHANNEL}/appcast.xml" "gs://${BURROW_RELEASE_GCS_BUCKET}/sparkle/appcast.xml"
nix develop .#ci -c gcloud storage rsync --recursive "publish/sparkle/${CHANNEL}" "gs://${BURROW_RELEASE_GCS_BUCKET}/sparkle/default"
fi

View file

@ -1,99 +0,0 @@
name: "Apple: iOS Distribution KMS CSR"
run-name: "Apple: iOS Distribution KMS CSR (${{ github.event.inputs.kms_key || 'apple-ios-distribution' }})"
on:
workflow_dispatch:
inputs:
kms_key:
description: Google KMS key name in the Burrow identity key ring
required: false
default: apple-ios-distribution
kms_version:
description: Google KMS key version
required: false
default: "1"
common_name:
description: CSR common name
required: false
default: Burrow iOS Distribution
email_address:
description: CSR email address
required: false
default: release@burrow.net
permissions:
contents: read
concurrency:
group: apple-ios-distribution-kms-csr-${{ github.ref }}
cancel-in-progress: false
env:
BURROW_GOOGLE_PROJECT_ID: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
GOOGLE_CLOUD_PROJECT: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
BURROW_GOOGLE_PROJECT_NUMBER: ${{ vars.GOOGLE_PROJECT_NUMBER || '416198671487' }}
BURROW_GOOGLE_WIF_POOL_ID: ${{ vars.BURROW_GOOGLE_WIF_POOL_ID || 'burrow-authentik' }}
BURROW_GOOGLE_WIF_PROVIDER_ID: ${{ vars.BURROW_GOOGLE_WIF_PROVIDER_ID || 'forgejo-runners' }}
BURROW_GOOGLE_WIF_SERVICE_ACCOUNT: ${{ vars.BURROW_GOOGLE_WIF_SERVICE_ACCOUNT || 'burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com' }}
BURROW_AUTHENTIK_WIF_ISSUER: ${{ vars.BURROW_AUTHENTIK_WIF_ISSUER || 'https://auth.burrow.net/application/o/google-cloud/' }}
BURROW_AUTHENTIK_WIF_AUDIENCE: ${{ vars.BURROW_AUTHENTIK_WIF_AUDIENCE || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_ID: ${{ vars.BURROW_AUTHENTIK_WIF_CLIENT_ID || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_SECRET: ${{ secrets.BURROW_AUTHENTIK_WIF_CLIENT_SECRET }}
BURROW_KMS_LOCATION: global
BURROW_KMS_KEYRING: burrow-identity
jobs:
csr:
name: Generate CSR
runs-on: [self-hosted, linux, x86_64, burrow-forge]
timeout-minutes: 20
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Authenticate Google WIF
shell: bash
run: nix develop .#ci -c Scripts/ci/google-wif-auth.sh
- name: Generate KMS-backed CSR
shell: bash
run: |
set -euo pipefail
mkdir -p dist/apple-ios-distribution
nix develop .#ci -c Scripts/apple/google-kms-csr.py \
--kms-key "${{ github.event.inputs.kms_key || 'apple-ios-distribution' }}" \
--kms-version "${{ github.event.inputs.kms_version || '1' }}" \
--common-name "${{ github.event.inputs.common_name || 'Burrow iOS Distribution' }}" \
--email-address "${{ github.event.inputs.email_address || 'release@burrow.net' }}" \
--output dist/apple-ios-distribution/ios-distribution.certSigningRequest \
--public-key-output dist/apple-ios-distribution/google-kms-public-key.pem
if command -v openssl >/dev/null 2>&1; then
openssl req -in dist/apple-ios-distribution/ios-distribution.certSigningRequest -noout -verify
fi
cat > dist/apple-ios-distribution/README.txt <<'EOF'
This CSR is backed by a non-exportable Google Cloud KMS key in the Burrow
identity key ring.
Create an iOS Distribution certificate explicitly through App Store Connect:
Scripts/apple/create-asc-certificate.mjs \
--certificate-type IOS_DISTRIBUTION \
--csr-file ios-distribution.certSigningRequest \
--out-dir Apple/Certificates
Keep the returned .cer file with release artifacts. The private key remains in
Google Cloud KMS and is never exported as a p12.
EOF
- name: Upload CSR Artifact
uses: https://code.forgejo.org/actions/upload-artifact@v3
with:
name: burrow-ios-distribution-kms-csr
path: dist/apple-ios-distribution

View file

@ -1,67 +0,0 @@
name: "Backup: Garage Storage"
run-name: "Backup Garage Storage (${{ github.event.inputs.set || 'all' }})"
on:
schedule:
- cron: "17 9 * * *"
workflow_dispatch:
inputs:
set:
description: "Comma-separated backup set: all, releases, packages, nix-cache"
required: false
default: all
delete_unmatched:
description: Delete destination objects that are missing from Garage
required: false
default: "false"
permissions:
contents: read
concurrency:
group: backup-garage-storage-${{ github.event.inputs.set || 'all' }}
cancel-in-progress: false
jobs:
backup:
name: Mirror Garage To GCS
runs-on: namespace-profile-linux-medium
timeout-minutes: 90
env:
BURROW_GARAGE_ENDPOINT: ${{ vars.BURROW_GARAGE_ENDPOINT || 'https://objects.burrow.net' }}
BURROW_GARAGE_REGION: ${{ vars.BURROW_GARAGE_REGION || 'garage' }}
BURROW_GARAGE_BACKUP_SET: ${{ github.event.inputs.set || 'all' }}
BURROW_GARAGE_BACKUP_DELETE: ${{ github.event.inputs.delete_unmatched || 'false' }}
BURROW_RELEASE_GARAGE_BUCKET: ${{ vars.BURROW_RELEASE_GARAGE_BUCKET || 'burrow-releases' }}
BURROW_PACKAGE_GARAGE_BUCKET: ${{ vars.BURROW_PACKAGE_GARAGE_BUCKET || 'burrow-packages' }}
BURROW_NIX_CACHE_GARAGE_BUCKET: ${{ vars.BURROW_NIX_CACHE_GARAGE_BUCKET || 'attic' }}
BURROW_RELEASE_GCS_BUCKET: ${{ vars.BURROW_RELEASE_GCS_BUCKET || 'burrow-net-releases' }}
BURROW_PACKAGE_GCS_BUCKET: ${{ vars.BURROW_PACKAGE_GCS_BUCKET || 'burrow-net-packages' }}
BURROW_NIX_CACHE_GCS_BUCKET: ${{ vars.BURROW_NIX_CACHE_GCS_BUCKET || 'burrow-net-nix-cache' }}
AWS_ACCESS_KEY_ID: ${{ secrets.BURROW_GARAGE_BACKUP_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.BURROW_GARAGE_BACKUP_SECRET_ACCESS_KEY }}
BURROW_GOOGLE_PROJECT_ID: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
GOOGLE_CLOUD_PROJECT: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
BURROW_GOOGLE_PROJECT_NUMBER: ${{ vars.GOOGLE_PROJECT_NUMBER || '416198671487' }}
BURROW_GOOGLE_WIF_POOL_ID: ${{ vars.BURROW_GOOGLE_WIF_POOL_ID || 'burrow-authentik' }}
BURROW_GOOGLE_WIF_PROVIDER_ID: ${{ vars.BURROW_GOOGLE_WIF_PROVIDER_ID || 'forgejo-runners' }}
BURROW_GOOGLE_WIF_SERVICE_ACCOUNT: ${{ vars.BURROW_GOOGLE_WIF_SERVICE_ACCOUNT || 'burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com' }}
BURROW_AUTHENTIK_WIF_ISSUER: ${{ vars.BURROW_AUTHENTIK_WIF_ISSUER || 'https://auth.burrow.net/application/o/google-cloud/' }}
BURROW_AUTHENTIK_WIF_AUDIENCE: ${{ vars.BURROW_AUTHENTIK_WIF_AUDIENCE || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_ID: ${{ vars.BURROW_AUTHENTIK_WIF_CLIENT_ID || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_SECRET: ${{ secrets.BURROW_AUTHENTIK_WIF_CLIENT_SECRET }}
BURROW_NIX_CACHE_URL: ${{ vars.BURROW_NIX_CACHE_URL || 'https://nix.burrow.net/burrow' }}
BURROW_NIX_CACHE_PUBLIC_KEY: ${{ vars.BURROW_NIX_CACHE_PUBLIC_KEY }}
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
fetch-depth: 0
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Mirror Garage Buckets To GCS
shell: bash
run: nix develop .#ci -c Scripts/ci/backup-garage-to-gcs.sh

View file

@ -1,74 +0,0 @@
name: "Build: Android"
run-name: "Build: Android (${{ github.ref_name }})"
on:
push:
branches:
- main
paths:
- ".forgejo/workflows/build-android.yml"
- "Android/**"
- "BUILD.bazel"
- "MODULE.bazel"
- "MODULE.bazel.lock"
- "bazel/android/**"
- "Cargo.lock"
- "Cargo.toml"
- "crates/burrow-core/**"
- "crates/burrow-mobile-ffi/**"
- "rust-toolchain.toml"
- "Scripts/ci/nscloud-cache.sh"
pull_request:
paths:
- ".forgejo/workflows/build-android.yml"
- "Android/**"
- "BUILD.bazel"
- "MODULE.bazel"
- "MODULE.bazel.lock"
- "bazel/android/**"
- "Cargo.lock"
- "Cargo.toml"
- "crates/burrow-core/**"
- "crates/burrow-mobile-ffi/**"
- "rust-toolchain.toml"
- "Scripts/ci/nscloud-cache.sh"
workflow_dispatch:
permissions:
contents: read
concurrency:
group: burrow-build-android-${{ github.ref }}
cancel-in-progress: true
env:
BURROW_NIX_CACHE_URL: ${{ vars.BURROW_NIX_CACHE_URL || 'https://nix.burrow.net/burrow' }}
BURROW_NIX_CACHE_PUBLIC_KEY: ${{ vars.BURROW_NIX_CACHE_PUBLIC_KEY }}
jobs:
stub:
name: Android Rust Core Stub
runs-on: namespace-profile-linux-medium
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Configure Cache
shell: bash
run: |
set -euo pipefail
bash Scripts/ci/ensure-nix.sh
nix develop .#ci -c Scripts/ci/nscloud-cache.sh bazel
- name: Build Android Stub Check
shell: bash
run: |
set -euo pipefail
if [[ -n "${BURROW_BAZEL_NSCCACHE_BAZELRC:-}" ]]; then
bazel --bazelrc="$BURROW_BAZEL_NSCCACHE_BAZELRC" build //bazel/android:check_stub_stamp
else
bazel build //bazel/android:check_stub_stamp
fi

View file

@ -1,480 +0,0 @@
name: "Build: Release"
run-name: "Build: Release (${{ github.ref_name }})"
on:
workflow_dispatch:
inputs:
build_number_override:
description: Optional explicit build number
required: false
default: ""
upload_app_store:
description: Trigger downstream App Store Connect upload workflow
required: false
default: "true"
upload_sparkle:
description: Trigger downstream Sparkle publish workflow
required: false
default: "true"
upload_microsoft_store:
description: Trigger downstream Microsoft Store beta upload workflow
required: false
default: "false"
sparkle_point_main:
description: Update Sparkle main channel pointers
required: false
default: "true"
testflight_distribute_external:
description: Distribute to external TestFlight groups
required: false
default: "false"
testflight_groups:
description: Optional comma-separated TestFlight groups for external distribution
required: false
default: ""
ios_uses_non_exempt_encryption:
description: Set true only when the iOS build uses non-exempt encryption
required: false
default: "false"
push:
tags:
- "release/*"
permissions:
actions: write
contents: write
concurrency:
group: burrow-build-release-${{ github.ref }}
cancel-in-progress: true
env:
BURROW_RELEASE_GCS_BUCKET: ${{ vars.BURROW_RELEASE_GCS_BUCKET || 'burrow-net-releases' }}
BURROW_RELEASE_GCS_BACKUP: ${{ vars.BURROW_RELEASE_GCS_BACKUP || 'true' }}
BURROW_RELEASE_GARAGE_BUCKET: ${{ vars.BURROW_RELEASE_GARAGE_BUCKET || 'burrow-releases' }}
BURROW_RELEASE_REQUIRE_GARAGE: ${{ vars.BURROW_RELEASE_REQUIRE_GARAGE || 'true' }}
BURROW_GARAGE_ENDPOINT: ${{ vars.BURROW_GARAGE_ENDPOINT || 'https://objects.burrow.net' }}
BURROW_GARAGE_REGION: ${{ vars.BURROW_GARAGE_REGION || 'garage' }}
AWS_ACCESS_KEY_ID: ${{ secrets.BURROW_GARAGE_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.BURROW_GARAGE_SECRET_ACCESS_KEY }}
BURROW_NIX_CACHE_URL: ${{ vars.BURROW_NIX_CACHE_URL || 'https://nix.burrow.net/burrow' }}
BURROW_NIX_CACHE_PUBLIC_KEY: ${{ vars.BURROW_NIX_CACHE_PUBLIC_KEY }}
BURROW_GOOGLE_PROJECT_ID: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
GOOGLE_CLOUD_PROJECT: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
BURROW_GOOGLE_PROJECT_NUMBER: ${{ vars.GOOGLE_PROJECT_NUMBER || '416198671487' }}
BURROW_GOOGLE_WIF_POOL_ID: ${{ vars.BURROW_GOOGLE_WIF_POOL_ID || 'burrow-authentik' }}
BURROW_GOOGLE_WIF_PROVIDER_ID: ${{ vars.BURROW_GOOGLE_WIF_PROVIDER_ID || 'forgejo-runners' }}
BURROW_GOOGLE_WIF_SERVICE_ACCOUNT: ${{ vars.BURROW_GOOGLE_WIF_SERVICE_ACCOUNT || 'burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com' }}
BURROW_AUTHENTIK_WIF_ISSUER: ${{ vars.BURROW_AUTHENTIK_WIF_ISSUER || 'https://auth.burrow.net/application/o/google-cloud/' }}
BURROW_AUTHENTIK_WIF_AUDIENCE: ${{ vars.BURROW_AUTHENTIK_WIF_AUDIENCE || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_ID: ${{ vars.BURROW_AUTHENTIK_WIF_CLIENT_ID || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_SECRET: ${{ secrets.BURROW_AUTHENTIK_WIF_CLIENT_SECRET }}
BURROW_APPLE_BUNDLE_ID: ${{ vars.BURROW_APPLE_BUNDLE_ID || 'net.burrow.app' }}
BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID: ${{ vars.BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID || 'net.burrow.app.network' }}
BURROW_IOS_DISTRIBUTION_CERTIFICATE_ID: ${{ vars.BURROW_IOS_DISTRIBUTION_CERTIFICATE_ID || '3G42677598' }}
BURROW_DEVELOPER_ID_CERTIFICATE_ID: ${{ vars.BURROW_DEVELOPER_ID_CERTIFICATE_ID || '9JKN6HXBHC' }}
BURROW_APPLE_CREATE_MISSING_BUNDLE_IDS: ${{ vars.BURROW_APPLE_CREATE_MISSING_BUNDLE_IDS || 'false' }}
BURROW_APPLE_ENABLE_CAPABILITIES: ${{ vars.BURROW_APPLE_ENABLE_CAPABILITIES || 'false' }}
BURROW_APPLE_SIGNING_MODE: ${{ vars.BURROW_APPLE_SIGNING_MODE || 'google-kms-staged' }}
BURROW_NOTARIZE_MACOS: ${{ vars.BURROW_NOTARIZE_MACOS || 'true' }}
BURROW_SPARKLE_SIGN_WITH_KMS: ${{ vars.BURROW_SPARKLE_SIGN_WITH_KMS || 'true' }}
BURROW_SPARKLE_KMS_KEY: ${{ vars.BURROW_SPARKLE_KMS_KEY || 'sparkle-ed25519' }}
BURROW_SPARKLE_KMS_VERSION: ${{ vars.BURROW_SPARKLE_KMS_VERSION || '1' }}
BURROW_SPARKLE_FEED_URL: ${{ vars.BURROW_SPARKLE_FEED_URL || 'https://releases.burrow.net/sparkle/appcast.xml' }}
BURROW_SPARKLE_PUBLIC_ED_KEY: ${{ vars.BURROW_SPARKLE_PUBLIC_ED_KEY || 'uugZuJeqvvKd91NZ6F1Fv2cQenUbIG/ZW3L9MuaEz30=' }}
BURROW_VERSION_REQUIRE_REMOTE: "1"
jobs:
prepare:
name: Prepare
runs-on: namespace-profile-linux-medium
outputs:
do_release: ${{ steps.plan.outputs.do_release }}
build_number: ${{ steps.plan.outputs.build_number }}
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Fetch Build Tags
shell: bash
run: git fetch --force --prune origin "+refs/tags/builds/*:refs/tags/builds/*"
- name: Compute Release Plan
id: plan
shell: bash
env:
BURROW_BUILD_NUMBER_OVERRIDE: ${{ github.event.inputs.build_number_override || '' }}
run: |
set -euo pipefail
status="$(Scripts/version.sh status)"
if [[ "$status" == "clean" ]]; then
echo "do_release=false" >> "$GITHUB_OUTPUT"
echo "build_number=" >> "$GITHUB_OUTPUT"
echo "No unreleased changes."
exit 0
fi
build_number="$(Scripts/version.sh pre-increment)"
echo "do_release=true" >> "$GITHUB_OUTPUT"
echo "build_number=${build_number}" >> "$GITHUB_OUTPUT"
build-linux:
name: Build (Linux)
needs: prepare
if: ${{ needs.prepare.outputs.do_release == 'true' }}
runs-on: namespace-profile-linux-medium
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Build Linux Release
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
run: |
set -euo pipefail
bash Scripts/ci/ensure-nix.sh
nix develop .#ci -c Scripts/ci/nscloud-cache.sh nix
nix develop .#ci -c Scripts/ci/package-release-artifacts.sh linux
- name: Upload Linux Artifacts
uses: https://code.forgejo.org/actions/upload-artifact@v3
with:
name: burrow-linux-${{ needs.prepare.outputs.build_number }}
path: dist/builds/${{ needs.prepare.outputs.build_number }}/linux/*
if-no-files-found: error
- name: Upload Linux Artifacts To Release Storage
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
run: |
set -euo pipefail
bash Scripts/ci/ensure-nix.sh
nix develop .#ci -c Scripts/ci/upload-release-storage.sh
build-windows:
name: Build (Windows Stub)
needs: prepare
if: ${{ needs.prepare.outputs.do_release == 'true' }}
runs-on: namespace-profile-windows-large
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Build Rust Stub
shell: pwsh
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
run: |
if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) {
Invoke-WebRequest -Uri "https://win.rustup.rs" -OutFile "rustup-init.exe"
.\rustup-init.exe -y --profile minimal --default-toolchain stable
$env:Path = "$env:USERPROFILE\.cargo\bin;$env:Path"
}
cargo build --locked --release -p burrow-windows-stub
New-Item -ItemType Directory -Force -Path "dist/builds/$env:BUILD_NUMBER/windows/burrow-$env:BUILD_NUMBER-x86_64-pc-windows-msvc" | Out-Null
Copy-Item "target/release/burrow.exe" "dist/builds/$env:BUILD_NUMBER/windows/burrow-$env:BUILD_NUMBER-x86_64-pc-windows-msvc/burrow.exe"
Copy-Item "README.md" "dist/builds/$env:BUILD_NUMBER/windows/burrow-$env:BUILD_NUMBER-x86_64-pc-windows-msvc/README.md"
Compress-Archive -Path "dist/builds/$env:BUILD_NUMBER/windows/burrow-$env:BUILD_NUMBER-x86_64-pc-windows-msvc" -DestinationPath "dist/builds/$env:BUILD_NUMBER/windows/burrow-$env:BUILD_NUMBER-x86_64-pc-windows-msvc.zip" -Force
Get-FileHash "dist/builds/$env:BUILD_NUMBER/windows/burrow-$env:BUILD_NUMBER-x86_64-pc-windows-msvc.zip" -Algorithm SHA256 | ForEach-Object { "$($_.Hash.ToLower()) burrow-$env:BUILD_NUMBER-x86_64-pc-windows-msvc.zip" } | Set-Content "dist/builds/$env:BUILD_NUMBER/windows/burrow-$env:BUILD_NUMBER-x86_64-pc-windows-msvc.zip.sha256"
- name: Upload Windows Artifacts
uses: https://code.forgejo.org/actions/upload-artifact@v3
with:
name: burrow-windows-${{ needs.prepare.outputs.build_number }}
path: dist/builds/${{ needs.prepare.outputs.build_number }}/windows/*
if-no-files-found: error
build-apple:
name: Build (Apple ${{ matrix.platform_name }})
needs: prepare
if: ${{ needs.prepare.outputs.do_release == 'true' }}
runs-on: namespace-profile-macos-large
strategy:
fail-fast: false
max-parallel: 2
matrix:
include:
- platform: ios
platform_name: iOS
bazel_target: //bazel/apple:release_ios_stamp
- platform: macos
platform_name: macOS
bazel_target: //bazel/apple:release_macos_stamp
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
submodules: recursive
- name: Select Apple SDK
shell: bash
run: |
set -euo pipefail
if ! command -v xcrun >/dev/null 2>&1; then
echo "::error ::xcrun is required for Apple release builds" >&2
exit 1
fi
xcrun --find swiftc
xcrun --sdk macosx --show-sdk-path
xcrun --sdk iphoneos --show-sdk-path
xcrun --sdk iphonesimulator --show-sdk-path
- name: Prepare Rust Apple Targets
shell: bash
env:
PLATFORM: ${{ matrix.platform }}
run: |
set -euo pipefail
if ! command -v rustup >/dev/null 2>&1; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable
export PATH="$HOME/.cargo/bin:$PATH"
fi
case "$PLATFORM" in
ios)
rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios
;;
macos)
rustup target add aarch64-apple-darwin x86_64-apple-darwin
;;
*)
echo "::error ::unsupported Apple platform ${PLATFORM}" >&2
exit 1
;;
esac
- name: Bootstrap Runner Environment
shell: bash
run: |
set -euo pipefail
runner_home="${RUNNER_HOME:-}"
if [[ -z "$runner_home" ]]; then
runner_home="$(python3 -c 'import os,pwd; print(pwd.getpwuid(os.getuid()).pw_dir)' 2>/dev/null || true)"
if [[ -z "$runner_home" ]]; then
runner_home="$PWD/.home"
fi
fi
mkdir -p "$runner_home" "$PWD/.tmp"
{
echo "HOME=$runner_home"
echo "XDG_CACHE_HOME=$runner_home/.cache"
echo "XDG_CONFIG_HOME=$runner_home/.config"
echo "XDG_DATA_HOME=$runner_home/.local/share"
echo "TMPDIR=$PWD/.tmp"
echo "TMP=$PWD/.tmp"
} >> "$GITHUB_ENV"
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Namespace Cache
shell: bash
run: |
set -euo pipefail
NIX_BIN="$(bash Scripts/ci/resolve-nix-bin.sh)"
"$NIX_BIN" develop .#ci --command bash Scripts/ci/nscloud-cache.sh bazel
- name: Configure Age
id: age_apple
continue-on-error: true
shell: bash
env:
AGE_FORGE_SSH_KEY: ${{ secrets.AGE_FORGE_SSH_KEY }}
run: Scripts/resolve-age-identity.sh >> "$GITHUB_ENV"
- name: Decrypt Release Secrets
if: ${{ steps.age_apple.outcome == 'success' }}
uses: ./.forgejo/actions/decrypt-release-secrets
- name: Sync Apple Provisioning Profiles
shell: bash
env:
PLATFORM: ${{ matrix.platform }}
run: |
set -euo pipefail
NIX_BIN="$(bash Scripts/ci/resolve-nix-bin.sh)"
"$NIX_BIN" develop .#ci --command Scripts/ci/sync-apple-provisioning-profiles.sh "$PLATFORM"
- name: Install Apple Signing Assets
shell: bash
run: Scripts/ci/install-apple-signing-assets.sh
- name: Build Apple Release
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
PLATFORM: ${{ matrix.platform }}
UPLOAD_APP_STORE: ${{ github.event.inputs.upload_app_store || 'true' }}
UPLOAD_SPARKLE: ${{ github.event.inputs.upload_sparkle || 'true' }}
SPARKLE_CHANNEL: build-${{ needs.prepare.outputs.build_number }}
run: |
set -euo pipefail
require_signing=false
if [[ "$UPLOAD_APP_STORE" == "true" && "$PLATFORM" == "ios" ]]; then
require_signing=true
fi
if [[ "$UPLOAD_SPARKLE" == "true" && "$PLATFORM" == "macos" ]]; then
require_signing=true
fi
bazel_args=()
if [[ -n "${BURROW_BAZEL_NSCCACHE_BAZELRC:-}" && -f "${BURROW_BAZEL_NSCCACHE_BAZELRC:-}" ]]; then
bazel_args+=("--bazelrc=${BURROW_BAZEL_NSCCACHE_BAZELRC}")
fi
NIX_BIN="$(bash Scripts/ci/resolve-nix-bin.sh)"
ci_path="$("$NIX_BIN" develop .#ci --command bash -lc 'printf "%s" "$PATH"')"
ci_protoc="$("$NIX_BIN" develop .#ci --command bash -lc 'command -v protoc')"
"$NIX_BIN" develop .#ci --command bazel "${bazel_args[@]}" build \
--verbose_failures \
--show_timestamps \
--experimental_ui_max_stdouterr_bytes=16777216 \
--action_env=PATH="$ci_path" \
--action_env=PROTOC="$ci_protoc" \
--action_env=BUILD_WORKSPACE_DIRECTORY="$PWD" \
--action_env=BUILD_NUMBER="$BUILD_NUMBER" \
--action_env=BURROW_RELEASE_OUT="$PWD/dist/builds/$BUILD_NUMBER" \
--action_env=BURROW_APPLE_SIGNING_READY="${BURROW_APPLE_SIGNING_READY:-0}" \
--action_env=BURROW_APPLE_SIGNING_MODE="${BURROW_APPLE_SIGNING_MODE:-keychain}" \
--action_env=BURROW_APPLE_REQUIRE_SIGNING="$require_signing" \
--action_env=BURROW_APPLE_BUNDLE_ID="${BURROW_APPLE_BUNDLE_ID:-}" \
--action_env=BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID="${BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID:-}" \
--action_env=BURROW_APPLE_KEYCHAIN_PATH="${BURROW_APPLE_KEYCHAIN_PATH:-}" \
--action_env=BURROW_NOTARIZE_MACOS="${BURROW_NOTARIZE_MACOS:-false}" \
--action_env=BURROW_SPARKLE_SIGN_WITH_KMS="${BURROW_SPARKLE_SIGN_WITH_KMS:-false}" \
--action_env=BURROW_SPARKLE_KMS_KEY="${BURROW_SPARKLE_KMS_KEY:-sparkle-ed25519}" \
--action_env=BURROW_SPARKLE_KMS_VERSION="${BURROW_SPARKLE_KMS_VERSION:-1}" \
--action_env=BURROW_SPARKLE_FEED_URL="${BURROW_SPARKLE_FEED_URL:-https://releases.burrow.net/sparkle/appcast.xml}" \
--action_env=BURROW_SPARKLE_PUBLIC_ED_KEY="${BURROW_SPARKLE_PUBLIC_ED_KEY:-}" \
--action_env=GOOGLE_CLOUD_PROJECT="${GOOGLE_CLOUD_PROJECT:-}" \
--action_env=ASC_API_KEY_ID="${ASC_API_KEY_ID:-}" \
--action_env=ASC_API_ISSUER_ID="${ASC_API_ISSUER_ID:-}" \
--action_env=ASC_API_KEY_PATH="${ASC_API_KEY_PATH:-}" \
--action_env=SPARKLE_CHANNEL="$SPARKLE_CHANNEL" \
--action_env=HOME="$HOME" \
--action_env=XDG_CACHE_HOME="$XDG_CACHE_HOME" \
"${{ matrix.bazel_target }}"
- name: Upload Apple Artifacts
uses: https://code.forgejo.org/actions/upload-artifact@v3
with:
name: burrow-apple-${{ matrix.platform }}-${{ needs.prepare.outputs.build_number }}
path: |
dist/builds/${{ needs.prepare.outputs.build_number }}/apple/*
dist/builds/${{ needs.prepare.outputs.build_number }}/sparkle/**
if-no-files-found: error
- name: Upload Apple Artifacts To Release Storage
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
run: |
set -euo pipefail
bash Scripts/ci/ensure-nix.sh
nix develop .#ci -c Scripts/ci/upload-release-storage.sh
publish:
name: Publish (Forgejo)
needs:
- prepare
- build-linux
- build-windows
- build-apple
if: ${{ always() && needs.prepare.outputs.do_release == 'true' }}
runs-on: namespace-profile-linux-medium
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Download Artifacts
uses: https://code.forgejo.org/actions/download-artifact@v3
with:
path: dist/downloaded
- name: Prepare Release Assets
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
run: |
set -euo pipefail
mkdir -p dist
mkdir -p "dist/builds/${BUILD_NUMBER}/windows"
find dist/downloaded -path '*/burrow-windows-*/*' -type f -exec cp {} "dist/builds/${BUILD_NUMBER}/windows/" \;
find dist/downloaded -type f \( -name '*.tar.gz' -o -name '*.zip' -o -name '*.sha256' -o -name '*.ipa' -o -name '*.pkg' -o -name '*.dmg' -o -name 'README*.txt' \) -exec cp {} dist/ \;
rm -rf dist/downloaded
find dist -maxdepth 1 -type f -print | sort
- name: Upload Windows Artifacts To Release Storage
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
run: |
set -euo pipefail
if ! find "dist/builds/${BUILD_NUMBER}/windows" -type f -print -quit | grep -q .; then
echo "::warning ::No Windows artifacts staged for release storage upload."
exit 0
fi
bash Scripts/ci/ensure-nix.sh
nix develop .#ci -c Scripts/ci/upload-release-storage.sh
- name: Tag Build
shell: bash
env:
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
run: |
set -euo pipefail
tag="builds/${BUILD_NUMBER}"
head_commit="$(git rev-parse HEAD)"
remote_commit="$(git ls-remote --tags --refs origin "refs/tags/${tag}" | awk 'NR==1 { print $1 }')"
if [[ -n "$remote_commit" && "$remote_commit" != "$head_commit" ]]; then
echo "::error ::Remote tag ${tag} already points at ${remote_commit}, not ${head_commit}" >&2
exit 1
fi
git tag -f "$tag" "$head_commit"
git push --quiet --no-verify origin "refs/tags/${tag}:refs/tags/${tag}"
- name: Publish Forgejo Release
shell: bash
env:
RELEASE_TAG: builds/${{ needs.prepare.outputs.build_number }}
API_URL: ${{ github.api_url }}
REPOSITORY: ${{ github.repository }}
TOKEN: ${{ github.token }}
run: Scripts/ci/publish-forgejo-release.sh
- name: Dispatch Store Uploads
shell: bash
env:
GITHUB_TOKEN: ${{ github.token }}
BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }}
UPLOAD_APP_STORE: ${{ github.event.inputs.upload_app_store || 'true' }}
UPLOAD_SPARKLE: ${{ github.event.inputs.upload_sparkle || 'true' }}
UPLOAD_MICROSOFT_STORE: ${{ github.event.inputs.upload_microsoft_store || 'false' }}
SPARKLE_POINT_MAIN: ${{ github.event.inputs.sparkle_point_main || 'true' }}
TESTFLIGHT_DISTRIBUTE_EXTERNAL: ${{ github.event.inputs.testflight_distribute_external || 'false' }}
TESTFLIGHT_GROUPS: ${{ github.event.inputs.testflight_groups || '' }}
IOS_USES_NON_EXEMPT_ENCRYPTION: ${{ github.event.inputs.ios_uses_non_exempt_encryption || 'false' }}
run: |
set -euo pipefail
inputs="$(python3 -c 'import json, os; print(json.dumps({"build_number":os.environ["BUILD_NUMBER"],"upload_app_store":os.environ["UPLOAD_APP_STORE"],"upload_sparkle":os.environ["UPLOAD_SPARKLE"],"upload_microsoft_store":os.environ["UPLOAD_MICROSOFT_STORE"],"sparkle_point_main":os.environ["SPARKLE_POINT_MAIN"],"testflight_distribute_external":os.environ["TESTFLIGHT_DISTRIBUTE_EXTERNAL"],"testflight_groups":os.environ["TESTFLIGHT_GROUPS"],"ios_uses_non_exempt_encryption":os.environ["IOS_USES_NON_EXEMPT_ENCRYPTION"]}))')"
curl -fsS \
-X POST \
-H "Authorization: token ${GITHUB_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"ref\":\"main\",\"inputs\":${inputs}}" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/workflows/publish-store-uploads.yml/dispatches"

View file

@ -1,42 +0,0 @@
name: Build Rust
on:
push:
branches:
- main
pull_request:
branches:
- "**"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
rust:
name: Cargo Test
runs-on: [self-hosted, linux, x86_64, burrow-forge]
steps:
- name: Checkout
shell: bash
run: |
set -euo pipefail
repo_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
if [ ! -d .git ]; then
git init .
fi
if git remote get-url origin >/dev/null 2>&1; then
git remote set-url origin "${repo_url}"
else
git remote add origin "${repo_url}"
fi
git fetch --force --tags origin "${GITHUB_SHA}"
git checkout --force --detach FETCH_HEAD
git clean -ffdqx
- name: Test
shell: bash
run: |
set -euo pipefail
nix develop .#ci -c cargo test --workspace --all-features

View file

@ -1,42 +0,0 @@
name: Build Site
on:
push:
branches:
- main
pull_request:
branches:
- "**"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
site:
name: Next.js Build
runs-on: [self-hosted, linux, x86_64, burrow-forge]
steps:
- name: Checkout
shell: bash
run: |
set -euo pipefail
repo_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
if [ ! -d .git ]; then
git init .
fi
if git remote get-url origin >/dev/null 2>&1; then
git remote set-url origin "${repo_url}"
else
git remote add origin "${repo_url}"
fi
git fetch --force --tags origin "${GITHUB_SHA}"
git checkout --force --detach FETCH_HEAD
git clean -ffdqx
- name: Build
shell: bash
run: |
set -euo pipefail
nix develop .#ci -c bash -lc 'cd site && npm ci --no-audit --no-fund && npm run build'

View file

@ -1,71 +0,0 @@
name: "Apple: Distribute TestFlight"
run-name: "Apple: Distribute TestFlight (Build ${{ inputs.build_number }})"
on:
workflow_dispatch:
inputs:
build_number:
description: Already-uploaded App Store Connect build number
required: true
testflight_distribute_external:
description: Distribute to external TestFlight groups
required: false
default: "false"
testflight_groups:
description: Optional comma-separated TestFlight groups
required: false
default: ""
ios_uses_non_exempt_encryption:
description: Set true only when the iOS build uses non-exempt encryption
required: false
default: "false"
permissions:
contents: read
concurrency:
group: burrow-distribute-testflight-${{ inputs.build_number }}
cancel-in-progress: true
env:
BURROW_NIX_CACHE_URL: ${{ vars.BURROW_NIX_CACHE_URL || 'https://nix.burrow.net/burrow' }}
BURROW_NIX_CACHE_PUBLIC_KEY: ${{ vars.BURROW_NIX_CACHE_PUBLIC_KEY }}
BURROW_APPLE_BUNDLE_ID: ${{ vars.BURROW_APPLE_BUNDLE_ID || 'net.burrow.app' }}
TESTFLIGHT_WAIT_PROCESSING_TIMEOUT_SECONDS: ${{ vars.TESTFLIGHT_WAIT_PROCESSING_TIMEOUT_SECONDS || '7200' }}
jobs:
distribute-ios-testflight:
name: Distribute (TestFlight iOS Testers)
runs-on: namespace-profile-linux-testflight
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Configure Age
shell: bash
env:
AGE_FORGE_SSH_KEY: ${{ secrets.AGE_FORGE_SSH_KEY }}
run: Scripts/resolve-age-identity.sh >> "$GITHUB_ENV"
- name: Decrypt Release Secrets
uses: ./.forgejo/actions/decrypt-release-secrets
- name: Validate App Store Credentials
shell: bash
run: Scripts/ci/check-release-config.sh app-store
- name: Distribute iOS Build To TestFlight
shell: bash
env:
BUILD_NUMBER: ${{ inputs.build_number }}
TESTFLIGHT_DISTRIBUTE_EXTERNAL: ${{ inputs.testflight_distribute_external || 'false' }}
TESTFLIGHT_GROUPS: ${{ inputs.testflight_groups || '' }}
IOS_USES_NON_EXEMPT_ENCRYPTION: ${{ inputs.ios_uses_non_exempt_encryption || 'false' }}
run: Scripts/ci/distribute-testflight.sh

View file

@ -1,237 +0,0 @@
name: "Infra: OpenTofu"
run-name: "Infra: OpenTofu (${{ github.event.inputs.stack || 'grafana' }} ${{ github.event.inputs.mode || 'validate' }})"
on:
push:
branches:
- main
paths:
- ".forgejo/workflows/infra-opentofu.yml"
- "Scripts/grafana-tofu.sh"
- "Scripts/identity-tofu.sh"
- "Scripts/openbao-tofu.sh"
- "Scripts/releases-tofu.sh"
- "infra/grafana/**"
- "infra/identity/**"
- "infra/openbao/**"
- "infra/releases/**"
- "services/grafana/**"
workflow_dispatch:
inputs:
stack:
description: OpenTofu stack to run
required: true
default: grafana
type: choice
options:
- grafana
- identity
- openbao
- releases
mode:
description: OpenTofu mode
required: true
default: plan
type: choice
options:
- fmt
- validate
- plan
- apply
- import
apply_confirmation:
description: Type apply-burrow-<stack>-tofu to run apply
required: false
default: ""
import_address:
description: OpenTofu resource address for import mode
required: false
default: ""
import_id:
description: Provider import id for import mode
required: false
default: ""
permissions:
contents: read
concurrency:
group: infra-opentofu-${{ github.ref }}-${{ github.event.inputs.stack || 'grafana' }}
cancel-in-progress: false
jobs:
opentofu:
name: OpenTofu (${{ github.event.inputs.stack || 'grafana' }})
runs-on: [self-hosted, linux, x86_64, burrow-forge]
timeout-minutes: 30
env:
MODE: ${{ github.event.inputs.mode || 'validate' }}
STACK: ${{ github.event.inputs.stack || 'grafana' }}
APPLY_CONFIRMATION: ${{ github.event.inputs.apply_confirmation || '' }}
IMPORT_ADDRESS: ${{ github.event.inputs.import_address || '' }}
IMPORT_ID: ${{ github.event.inputs.import_id || '' }}
TF_INPUT: "false"
TF_IN_AUTOMATION: "true"
TF_VAR_grafana_url: ${{ vars.GRAFANA_URL || 'https://graphs.burrow.net' }}
TF_VAR_google_project_id: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
TF_VAR_google_project_number: ${{ vars.GOOGLE_PROJECT_NUMBER || '416198671487' }}
TF_VAR_google_runner_service_account_email: ${{ vars.BURROW_GOOGLE_WIF_SERVICE_ACCOUNT || 'burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com' }}
TF_VAR_google_wif_runner_client_id: ${{ vars.BURROW_AUTHENTIK_WIF_CLIENT_ID || 'google-wif.burrow.net' }}
TF_VAR_google_release_bucket_name: ${{ vars.BURROW_RELEASE_GCS_BUCKET || 'burrow-net-releases' }}
TF_VAR_google_package_bucket_name: ${{ vars.BURROW_PACKAGE_GCS_BUCKET || 'burrow-net-packages' }}
TF_VAR_google_nix_cache_bucket_name: ${{ vars.BURROW_NIX_CACHE_GCS_BUCKET || 'burrow-net-nix-cache' }}
TF_VAR_google_nix_cache_public_read: ${{ vars.BURROW_NIX_CACHE_GCS_PUBLIC_READ || 'false' }}
TF_VAR_google_storage_location: ${{ vars.BURROW_GCS_LOCATION || 'US' }}
BURROW_GOOGLE_PROJECT_ID: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
GOOGLE_CLOUD_PROJECT: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
BURROW_GOOGLE_PROJECT_NUMBER: ${{ vars.GOOGLE_PROJECT_NUMBER || '416198671487' }}
BURROW_GOOGLE_WIF_POOL_ID: ${{ vars.BURROW_GOOGLE_WIF_POOL_ID || 'burrow-authentik' }}
BURROW_GOOGLE_WIF_PROVIDER_ID: ${{ vars.BURROW_GOOGLE_WIF_PROVIDER_ID || 'forgejo-runners' }}
BURROW_GOOGLE_WIF_SERVICE_ACCOUNT: ${{ vars.BURROW_GOOGLE_WIF_SERVICE_ACCOUNT || 'burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com' }}
BURROW_AUTHENTIK_WIF_ISSUER: ${{ vars.BURROW_AUTHENTIK_WIF_ISSUER || 'https://auth.burrow.net/application/o/google-cloud/' }}
BURROW_AUTHENTIK_WIF_AUDIENCE: ${{ vars.BURROW_AUTHENTIK_WIF_AUDIENCE || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_ID: ${{ vars.BURROW_AUTHENTIK_WIF_CLIENT_ID || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_SECRET: ${{ secrets.BURROW_AUTHENTIK_WIF_CLIENT_SECRET }}
BURROW_NIX_CACHE_URL: ${{ vars.BURROW_NIX_CACHE_URL || 'https://nix.burrow.net/burrow' }}
BURROW_NIX_CACHE_PUBLIC_KEY: ${{ vars.BURROW_NIX_CACHE_PUBLIC_KEY }}
OPENTOFU_STATE_BUCKET: ${{ vars.OPENTOFU_STATE_BUCKET || '' }}
OPENTOFU_STATE_REGION: ${{ vars.OPENTOFU_STATE_REGION || vars.AWS_REGION || 'us-west-2' }}
OPENTOFU_STATE_DYNAMODB_TABLE: ${{ vars.OPENTOFU_STATE_DYNAMODB_TABLE || '' }}
steps:
- name: Checkout
shell: bash
run: |
set -euo pipefail
repo_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
if [ ! -d .git ]; then
git init .
fi
if git remote get-url origin >/dev/null 2>&1; then
git remote set-url origin "${repo_url}"
else
git remote add origin "${repo_url}"
fi
git fetch --force --tags origin "${GITHUB_SHA}"
git checkout --force --detach FETCH_HEAD
git clean -ffdqx
- name: Validate Request
shell: bash
run: |
set -euo pipefail
case "$STACK" in
grafana|identity|openbao|releases) ;;
*)
echo "::error ::Unsupported OpenTofu stack: $STACK" >&2
exit 2
;;
esac
case "$MODE" in
fmt|validate|plan|apply|import) ;;
*)
echo "::error ::Unsupported OpenTofu mode: $MODE" >&2
exit 2
;;
esac
if [[ "${GITHUB_EVENT_NAME}" != "workflow_dispatch" && "$MODE" != "validate" ]]; then
echo "::error ::Non-manual runs may only validate." >&2
exit 2
fi
if [[ "$MODE" == "apply" && "$APPLY_CONFIRMATION" != "apply-burrow-${STACK}-tofu" ]]; then
echo "::error ::apply_confirmation must be exactly apply-burrow-${STACK}-tofu." >&2
exit 1
fi
if [[ "$MODE" == "import" && ( -z "$IMPORT_ADDRESS" || -z "$IMPORT_ID" ) ]]; then
echo "::error ::import mode requires import_address and import_id." >&2
exit 2
fi
- name: Configure Age
if: ${{ github.event_name == 'workflow_dispatch' && (github.event.inputs.mode == 'plan' || github.event.inputs.mode == 'apply' || github.event.inputs.mode == 'import') }}
shell: bash
env:
AGE_FORGE_SSH_KEY: ${{ secrets.AGE_FORGE_SSH_KEY }}
run: Scripts/resolve-age-identity.sh >> "$GITHUB_ENV"
- name: Configure Grafana Provider Auth
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.stack == 'grafana' && (github.event.inputs.mode == 'plan' || github.event.inputs.mode == 'apply' || github.event.inputs.mode == 'import') }}
shell: bash
run: |
set -euo pipefail
: "${AGE_IDENTITY_PATH:?AGE_IDENTITY_PATH is required}"
agenix="$(nix build --no-link .#agenix --print-out-paths | tail -n1)/bin/agenix"
password="$("$agenix" --identity "$AGE_IDENTITY_PATH" --decrypt secrets/infra/grafana-admin-password.age | tr -d '\r')"
echo "::add-mask::${password}"
{
echo "TF_VAR_grafana_auth<<EOF"
printf 'admin:%s\n' "$password"
echo "EOF"
} >> "$GITHUB_ENV"
- name: Configure OpenTofu Backend
shell: bash
run: |
set -euo pipefail
stack_dir="infra/${STACK}"
rm -f "${stack_dir}/backend.hcl"
if [[ -n "$OPENTOFU_STATE_BUCKET" ]]; then
{
printf 'bucket = "%s"\n' "$OPENTOFU_STATE_BUCKET"
printf 'key = "%s"\n' "${STACK}/${STACK}.tfstate"
printf 'region = "%s"\n' "$OPENTOFU_STATE_REGION"
printf 'encrypt = true\n'
if [[ -n "$OPENTOFU_STATE_DYNAMODB_TABLE" ]]; then
printf 'dynamodb_table = "%s"\n' "$OPENTOFU_STATE_DYNAMODB_TABLE"
fi
} > "${stack_dir}/backend.hcl"
elif [[ "$MODE" == "apply" || "$MODE" == "import" ]]; then
echo "::error ::OPENTOFU_STATE_BUCKET is required for apply/import." >&2
exit 1
fi
- name: Authenticate Google WIF
if: ${{ github.event_name == 'workflow_dispatch' && (github.event.inputs.stack == 'identity' || github.event.inputs.stack == 'releases') && (github.event.inputs.mode == 'plan' || github.event.inputs.mode == 'apply' || github.event.inputs.mode == 'import') }}
shell: bash
run: |
set -euo pipefail
nix develop .#ci -c Scripts/ci/google-wif-auth.sh
- name: Run OpenTofu
shell: bash
run: |
set -euo pipefail
stack_script="Scripts/${STACK}-tofu.sh"
case "$MODE" in
fmt)
nix develop .#ci -c tofu -chdir="infra/${STACK}" fmt -check
;;
validate)
"$stack_script" init -backend=false
"$stack_script" validate
;;
plan)
if [[ -f "infra/${STACK}/backend.hcl" ]]; then
"$stack_script" init -backend-config=backend.hcl
else
"$stack_script" init -backend=false
fi
"$stack_script" plan
;;
apply)
"$stack_script" init -backend-config=backend.hcl
"$stack_script" apply -auto-approve
;;
import)
"$stack_script" init -backend-config=backend.hcl
"$stack_script" import "$IMPORT_ADDRESS" "$IMPORT_ID"
;;
esac

View file

@ -1,38 +0,0 @@
name: Lint Governance
on:
push:
branches:
- main
pull_request:
branches:
- "**"
workflow_dispatch:
jobs:
governance:
name: BEP Metadata
runs-on: [self-hosted, linux, x86_64, burrow-forge]
steps:
- name: Checkout
shell: bash
run: |
set -euo pipefail
repo_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
if [ ! -d .git ]; then
git init .
fi
if git remote get-url origin >/dev/null 2>&1; then
git remote set-url origin "${repo_url}"
else
git remote add origin "${repo_url}"
fi
git fetch --force --tags origin "${GITHUB_SHA}"
git checkout --force --detach FETCH_HEAD
git clean -ffdqx
- name: Validate BEP metadata
shell: bash
run: |
set -euo pipefail
python3 Scripts/check-bep-metadata.py

View file

@ -1,63 +0,0 @@
name: "Cache: Publish Nix"
run-name: "Publish Nix Cache (${{ github.ref_name }})"
on:
push:
branches:
- main
paths:
- ".forgejo/workflows/publish-nix-cache.yml"
- "Cargo.lock"
- "Cargo.toml"
- "flake.lock"
- "flake.nix"
- "crates/**"
- "services/forgejo-nsc/**"
- "nixos/**"
- "Scripts/ci/ensure-nix.sh"
- "Scripts/ci/publish-nix-cache.sh"
workflow_dispatch:
inputs:
attrs:
description: Space-separated flake attrs to build and push
required: false
default: ".#burrow .#forgejo-nsc-dispatcher .#forgejo-nsc-autoscaler"
required:
description: Fail when the cache push token is missing
required: false
default: "false"
permissions:
contents: read
concurrency:
group: publish-nix-cache-${{ github.ref }}
cancel-in-progress: true
jobs:
publish:
name: Publish Nix Cache
runs-on: namespace-profile-linux-medium
timeout-minutes: 45
env:
BURROW_NIX_CACHE_SERVER_NAME: ${{ vars.BURROW_NIX_CACHE_SERVER_NAME || 'burrow' }}
BURROW_NIX_CACHE_SERVER_URL: ${{ vars.BURROW_NIX_CACHE_SERVER_URL || 'https://nix.burrow.net' }}
BURROW_NIX_CACHE_NAME: ${{ vars.BURROW_NIX_CACHE_NAME || 'burrow' }}
BURROW_NIX_CACHE_URL: ${{ vars.BURROW_NIX_CACHE_URL || 'https://nix.burrow.net/burrow' }}
BURROW_NIX_CACHE_PUBLIC_KEY: ${{ vars.BURROW_NIX_CACHE_PUBLIC_KEY }}
BURROW_NIX_CACHE_PUSH_TOKEN: ${{ secrets.BURROW_NIX_CACHE_PUSH_TOKEN }}
BURROW_NIX_CACHE_ATTRS: ${{ github.event.inputs.attrs || '.#burrow .#forgejo-nsc-dispatcher .#forgejo-nsc-autoscaler' }}
BURROW_NIX_CACHE_REQUIRED: ${{ github.event.inputs.required || 'false' }}
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
fetch-depth: 0
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Publish Cache Paths
shell: bash
run: nix develop .#ci -c Scripts/ci/publish-nix-cache.sh

View file

@ -1,148 +0,0 @@
name: "Publish: Package Repositories"
run-name: "Publish Package Repositories (${{ github.event.inputs.channel || 'stable' }})"
on:
workflow_dispatch:
inputs:
build_number:
description: Build number or release label for package artifact paths
required: false
default: ""
channel:
description: Native package repository channel
required: true
default: stable
type: choice
options:
- stable
- beta
- nightly
publish_gcs:
description: Publish repository tree to configured package storage targets
required: false
default: "true"
permissions:
contents: read
concurrency:
group: publish-package-repositories-${{ github.ref }}-${{ github.event.inputs.channel || 'stable' }}
cancel-in-progress: false
jobs:
publish:
name: Build Native Repositories
runs-on: [self-hosted, linux, x86_64, burrow-forge]
timeout-minutes: 45
env:
BUILD_NUMBER: ${{ github.event.inputs.build_number || github.sha }}
BURROW_PACKAGE_CHANNEL: ${{ github.event.inputs.channel || 'stable' }}
BURROW_KMS_LOCATION: ${{ vars.BURROW_KMS_LOCATION || 'global' }}
BURROW_KMS_KEYRING: ${{ vars.BURROW_KMS_KEYRING || 'burrow-identity' }}
BURROW_KMS_VERSION: ${{ vars.BURROW_KMS_VERSION || '1' }}
BURROW_GOOGLE_PROJECT_ID: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
GOOGLE_CLOUD_PROJECT: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
BURROW_GOOGLE_PROJECT_NUMBER: ${{ vars.GOOGLE_PROJECT_NUMBER || '416198671487' }}
BURROW_GOOGLE_WIF_POOL_ID: ${{ vars.BURROW_GOOGLE_WIF_POOL_ID || 'burrow-authentik' }}
BURROW_GOOGLE_WIF_PROVIDER_ID: ${{ vars.BURROW_GOOGLE_WIF_PROVIDER_ID || 'forgejo-runners' }}
BURROW_GOOGLE_WIF_SERVICE_ACCOUNT: ${{ vars.BURROW_GOOGLE_WIF_SERVICE_ACCOUNT || 'burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com' }}
BURROW_AUTHENTIK_WIF_ISSUER: ${{ vars.BURROW_AUTHENTIK_WIF_ISSUER || 'https://auth.burrow.net/application/o/google-cloud/' }}
BURROW_AUTHENTIK_WIF_AUDIENCE: ${{ vars.BURROW_AUTHENTIK_WIF_AUDIENCE || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_ID: ${{ vars.BURROW_AUTHENTIK_WIF_CLIENT_ID || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_SECRET: ${{ secrets.BURROW_AUTHENTIK_WIF_CLIENT_SECRET }}
BURROW_PACKAGE_GCS_BUCKET: ${{ vars.BURROW_PACKAGE_GCS_BUCKET || 'burrow-net-packages' }}
BURROW_PACKAGE_GCS_PREFIX: ${{ vars.BURROW_PACKAGE_GCS_PREFIX || '' }}
BURROW_PACKAGE_GCS_BACKUP: ${{ vars.BURROW_PACKAGE_GCS_BACKUP || 'true' }}
BURROW_PACKAGE_GARAGE_BUCKET: ${{ vars.BURROW_PACKAGE_GARAGE_BUCKET || 'burrow-packages' }}
BURROW_PACKAGE_GARAGE_PREFIX: ${{ vars.BURROW_PACKAGE_GARAGE_PREFIX || vars.BURROW_PACKAGE_GCS_PREFIX || '' }}
BURROW_PACKAGE_REQUIRE_GARAGE: ${{ vars.BURROW_PACKAGE_REQUIRE_GARAGE || 'true' }}
BURROW_GARAGE_ENDPOINT: ${{ vars.BURROW_GARAGE_ENDPOINT || 'https://objects.burrow.net' }}
BURROW_GARAGE_REGION: ${{ vars.BURROW_GARAGE_REGION || 'garage' }}
AWS_ACCESS_KEY_ID: ${{ secrets.BURROW_GARAGE_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.BURROW_GARAGE_SECRET_ACCESS_KEY }}
BURROW_NIX_CACHE_URL: ${{ vars.BURROW_NIX_CACHE_URL || 'https://nix.burrow.net/burrow' }}
BURROW_NIX_CACHE_PUBLIC_KEY: ${{ vars.BURROW_NIX_CACHE_PUBLIC_KEY }}
steps:
- name: Checkout
shell: bash
run: |
set -euo pipefail
repo_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
if [ ! -d .git ]; then
git init .
fi
if git remote get-url origin >/dev/null 2>&1; then
git remote set-url origin "${repo_url}"
else
git remote add origin "${repo_url}"
fi
git fetch --force --tags origin "${GITHUB_SHA}"
git checkout --force --detach FETCH_HEAD
git clean -ffdqx
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Build Linux Packages
shell: bash
run: |
set -euo pipefail
export BURROW_RELEASE_OUT="$PWD/dist/builds/$BUILD_NUMBER"
nix develop .#ci -c Scripts/ci/package-release-artifacts.sh linux
- name: Authenticate Google WIF
shell: bash
run: |
set -euo pipefail
nix develop .#ci -c Scripts/ci/google-wif-auth.sh
- name: Export KMS Public Keys
shell: bash
run: |
set -euo pipefail
mkdir -p "$PWD/.kms"
export BURROW_APT_REPO_KMS_KEY="${BURROW_APT_REPO_KMS_KEY:-package-apt-repository}"
export BURROW_RPM_REPO_KMS_KEY="${BURROW_RPM_REPO_KMS_KEY:-package-rpm-repository}"
export BURROW_PACMAN_REPO_KMS_KEY="${BURROW_PACMAN_REPO_KMS_KEY:-package-pacman-repository}"
for item in \
"APT:${BURROW_APT_REPO_KMS_KEY}:apt" \
"RPM:${BURROW_RPM_REPO_KMS_KEY}:rpm" \
"PACMAN:${BURROW_PACMAN_REPO_KMS_KEY}:pacman"
do
IFS=: read -r env_prefix key_name file_prefix <<< "$item"
pem="$PWD/.kms/${file_prefix}.pem"
nix develop .#ci -c gcloud kms keys versions get-public-key "$BURROW_KMS_VERSION" \
--location "$BURROW_KMS_LOCATION" \
--keyring "$BURROW_KMS_KEYRING" \
--key "$key_name" \
--project "$GOOGLE_CLOUD_PROJECT" \
--output-file "$pem"
echo "BURROW_${env_prefix}_REPO_KMS_PUBLIC_KEY_PEM=$pem" >> "$GITHUB_ENV"
echo "BURROW_${env_prefix}_REPO_KMS_KEY=$key_name" >> "$GITHUB_ENV"
done
- name: Build Repository Metadata
shell: bash
run: |
set -euo pipefail
export BURROW_PACKAGE_INPUT_DIR="$PWD/dist/builds/$BUILD_NUMBER/linux"
export BURROW_PACKAGE_REPOSITORY_DIR="$PWD/dist/builds/$BUILD_NUMBER/repositories"
nix develop .#ci -c Scripts/package/build-repositories.sh all
- name: Upload Repository Artifact
uses: https://code.forgejo.org/actions/upload-artifact@v3
with:
name: burrow-package-repositories-${{ github.event.inputs.channel || 'stable' }}-${{ github.event.inputs.build_number || github.sha }}
path: dist/builds/${{ github.event.inputs.build_number || github.sha }}/repositories/**
if-no-files-found: error
- name: Publish Package Repository To Storage
if: ${{ github.event.inputs.publish_gcs == 'true' }}
shell: bash
run: |
set -euo pipefail
export BURROW_PACKAGE_REPOSITORY_DIR="$PWD/dist/builds/$BUILD_NUMBER/repositories"
nix develop .#ci -c Scripts/ci/upload-package-storage.sh

View file

@ -1,385 +0,0 @@
name: "Publish: Release Uploads"
run-name: "Publish: Release Uploads (Build ${{ inputs.build_number }})"
on:
workflow_dispatch:
inputs:
build_number:
description: Build number to publish
required: true
upload_app_store:
description: Upload iOS/macOS/visionOS artifacts to App Store Connect
required: false
default: "true"
distribute_testflight:
description: Distribute an already-uploaded iOS build to TestFlight
required: false
default: "false"
upload_sparkle:
description: Publish Sparkle artifacts
required: false
default: "true"
upload_microsoft_store:
description: Upload Windows package to Microsoft Partner Center beta lane
required: false
default: "false"
sparkle_channel:
description: Sparkle channel, defaults to build-<number>
required: false
default: ""
sparkle_point_main:
description: Point Sparkle main appcast/default channel to selected channel
required: false
default: "true"
testflight_distribute_external:
description: Distribute to external TestFlight groups
required: false
default: "false"
testflight_groups:
description: Optional comma-separated TestFlight groups for external distribution
required: false
default: ""
ios_uses_non_exempt_encryption:
description: Set true only when the iOS build uses non-exempt encryption
required: false
default: "false"
permissions:
contents: read
concurrency:
group: burrow-publish-store-uploads-${{ inputs.build_number }}
cancel-in-progress: true
env:
BURROW_RELEASE_GCS_BUCKET: ${{ vars.BURROW_RELEASE_GCS_BUCKET || 'burrow-net-releases' }}
BURROW_GOOGLE_PROJECT_ID: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
GOOGLE_CLOUD_PROJECT: ${{ vars.GOOGLE_PROJECT_ID || 'project-88c23ce9-918a-470a-b33' }}
BURROW_GOOGLE_PROJECT_NUMBER: ${{ vars.GOOGLE_PROJECT_NUMBER || '416198671487' }}
BURROW_GOOGLE_WIF_POOL_ID: ${{ vars.BURROW_GOOGLE_WIF_POOL_ID || 'burrow-authentik' }}
BURROW_GOOGLE_WIF_PROVIDER_ID: ${{ vars.BURROW_GOOGLE_WIF_PROVIDER_ID || 'forgejo-runners' }}
BURROW_GOOGLE_WIF_SERVICE_ACCOUNT: ${{ vars.BURROW_GOOGLE_WIF_SERVICE_ACCOUNT || 'burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com' }}
BURROW_AUTHENTIK_WIF_ISSUER: ${{ vars.BURROW_AUTHENTIK_WIF_ISSUER || 'https://auth.burrow.net/application/o/google-cloud/' }}
BURROW_AUTHENTIK_WIF_AUDIENCE: ${{ vars.BURROW_AUTHENTIK_WIF_AUDIENCE || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_ID: ${{ vars.BURROW_AUTHENTIK_WIF_CLIENT_ID || 'google-wif.burrow.net' }}
BURROW_AUTHENTIK_WIF_CLIENT_SECRET: ${{ secrets.BURROW_AUTHENTIK_WIF_CLIENT_SECRET }}
BURROW_NIX_CACHE_URL: ${{ vars.BURROW_NIX_CACHE_URL || 'https://nix.burrow.net/burrow' }}
BURROW_NIX_CACHE_PUBLIC_KEY: ${{ vars.BURROW_NIX_CACHE_PUBLIC_KEY }}
BURROW_APPLE_BUNDLE_ID: ${{ vars.BURROW_APPLE_BUNDLE_ID || 'net.burrow.app' }}
BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID: ${{ vars.BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID || 'net.burrow.app.network' }}
BURROW_IOS_DISTRIBUTION_CERTIFICATE_ID: ${{ vars.BURROW_IOS_DISTRIBUTION_CERTIFICATE_ID || '3G42677598' }}
BURROW_DEVELOPER_ID_CERTIFICATE_ID: ${{ vars.BURROW_DEVELOPER_ID_CERTIFICATE_ID || '9JKN6HXBHC' }}
BURROW_APPLE_SIGNING_MODE: ${{ vars.BURROW_APPLE_SIGNING_MODE || 'google-kms-staged' }}
BURROW_NOTARIZE_MACOS: ${{ vars.BURROW_NOTARIZE_MACOS || 'true' }}
BURROW_SPARKLE_SIGN_WITH_KMS: ${{ vars.BURROW_SPARKLE_SIGN_WITH_KMS || 'true' }}
BURROW_SPARKLE_KMS_KEY: ${{ vars.BURROW_SPARKLE_KMS_KEY || 'sparkle-ed25519' }}
BURROW_SPARKLE_KMS_VERSION: ${{ vars.BURROW_SPARKLE_KMS_VERSION || '1' }}
BURROW_KMS_LOCATION: ${{ vars.BURROW_KMS_LOCATION || 'global' }}
BURROW_KMS_KEYRING: ${{ vars.BURROW_KMS_KEYRING || 'burrow-identity' }}
TESTFLIGHT_WAIT_PROCESSING_TIMEOUT_SECONDS: ${{ vars.TESTFLIGHT_WAIT_PROCESSING_TIMEOUT_SECONDS || '7200' }}
jobs:
sign-apple-kms:
name: Sign (Apple KMS)
if: ${{ inputs.upload_app_store == 'true' || inputs.upload_sparkle == 'true' }}
runs-on: namespace-profile-linux-medium
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Configure Age
shell: bash
env:
AGE_FORGE_SSH_KEY: ${{ secrets.AGE_FORGE_SSH_KEY }}
run: Scripts/resolve-age-identity.sh >> "$GITHUB_ENV"
- name: Decrypt Release Secrets
uses: ./.forgejo/actions/decrypt-release-secrets
- name: Validate App Store Credentials
shell: bash
run: Scripts/ci/check-release-config.sh app-store
- name: Download Staged Apple Artifacts
shell: bash
env:
BUILD_NUMBER: ${{ inputs.build_number }}
run: |
set -euo pipefail
nix develop .#ci -c Scripts/ci/google-wif-auth.sh
mkdir -p "dist/builds/${BUILD_NUMBER}/apple"
nix develop .#ci -c gcloud storage rsync --recursive "gs://${BURROW_RELEASE_GCS_BUCKET}/builds/${BUILD_NUMBER}/apple" "dist/builds/${BUILD_NUMBER}/apple"
find "dist/builds/${BUILD_NUMBER}/apple" -type f -print | sort
- name: Sync Apple Provisioning Profiles
shell: bash
run: nix develop .#ci -c Scripts/ci/sync-apple-provisioning-profiles.sh all
- name: Sign Apple Artifacts With KMS
shell: bash
env:
BUILD_NUMBER: ${{ inputs.build_number }}
SPARKLE_CHANNEL_INPUT: ${{ inputs.sparkle_channel }}
run: |
set -euo pipefail
channel="${SPARKLE_CHANNEL_INPUT:-}"
if [[ -z "$channel" ]]; then
channel="build-${BUILD_NUMBER}"
fi
SPARKLE_CHANNEL="$channel" nix develop .#ci -c Scripts/apple/sign-release-artifacts-kms.sh all
- name: Upload Signed Apple Artifacts
shell: bash
env:
BUILD_NUMBER: ${{ inputs.build_number }}
run: nix develop .#ci -c Scripts/ci/upload-release-storage.sh
upload-app-store:
name: Upload (App Store Connect)
needs: sign-apple-kms
if: ${{ inputs.upload_app_store == 'true' }}
runs-on: namespace-profile-macos-large
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Configure Age
shell: bash
env:
AGE_FORGE_SSH_KEY: ${{ secrets.AGE_FORGE_SSH_KEY }}
run: Scripts/resolve-age-identity.sh >> "$GITHUB_ENV"
- name: Decrypt Release Secrets
uses: ./.forgejo/actions/decrypt-release-secrets
- name: Validate App Store Credentials
shell: bash
run: Scripts/ci/check-release-config.sh app-store
- name: Download Apple Artifacts From GCS
shell: bash
env:
BUILD_NUMBER: ${{ inputs.build_number }}
run: |
set -euo pipefail
nix develop .#ci -c Scripts/ci/google-wif-auth.sh
mkdir -p publish/apple
nix develop .#ci -c gcloud storage rsync --recursive "gs://${BURROW_RELEASE_GCS_BUCKET}/builds/${BUILD_NUMBER}/apple" publish/apple
find publish/apple -type f -print | sort
- name: Upload Apple Packages
shell: bash
run: |
set -euo pipefail
Scripts/ci/check-release-config.sh app-store
key_dir="${RUNNER_TEMP:-/tmp}/burrow-asc"
altool_key_dir="${HOME}/.appstoreconnect/private_keys"
mkdir -p "$key_dir" "$altool_key_dir"
key_path="$key_dir/AuthKey_${ASC_API_KEY_ID}.p8"
if [[ -n "${ASC_API_KEY_PATH:-}" && -f "${ASC_API_KEY_PATH:-}" ]]; then
cp "$ASC_API_KEY_PATH" "$key_path"
else
printf '%s' "$ASC_API_KEY_BASE64" | base64 --decode > "$key_path"
fi
cp "$key_path" "$altool_key_dir/AuthKey_${ASC_API_KEY_ID}.p8"
chmod 600 "$key_path" "$altool_key_dir/AuthKey_${ASC_API_KEY_ID}.p8"
shopt -s nullglob
artifacts=()
for artifact in publish/apple/*.ipa publish/apple/*.pkg; do
[[ "$artifact" == *.unsigned.ipa ]] && continue
artifacts+=("$artifact")
done
if (( ${#artifacts[@]} == 0 )); then
echo "::error ::No Apple upload artifacts found for build ${{ inputs.build_number }}."
exit 1
fi
for artifact in "${artifacts[@]}"; do
upload_type="ios"
if [[ "$artifact" == *.pkg ]]; then
upload_type="macos"
fi
upload_log="${RUNNER_TEMP:-/tmp}/burrow-altool-${upload_type}-$(basename "$artifact").log"
set +e
xcrun altool --upload-app \
--type "$upload_type" \
--file "$artifact" \
--apiKey "$ASC_API_KEY_ID" \
--apiIssuer "$ASC_API_ISSUER_ID" 2>&1 | tee "$upload_log"
altool_status=${PIPESTATUS[0]}
set -e
if (( altool_status != 0 )) || grep -Eq 'UPLOAD FAILED|Validation failed|ERROR:' "$upload_log"; then
echo "::error ::altool reported upload validation failure for $artifact" >&2
exit 1
fi
done
release-ios-testflight:
name: Release (TestFlight iOS Testers)
needs: upload-app-store
if: ${{ inputs.upload_app_store == 'true' }}
runs-on: namespace-profile-linux-testflight
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Configure Age
shell: bash
env:
AGE_FORGE_SSH_KEY: ${{ secrets.AGE_FORGE_SSH_KEY }}
run: Scripts/resolve-age-identity.sh >> "$GITHUB_ENV"
- name: Decrypt Release Secrets
uses: ./.forgejo/actions/decrypt-release-secrets
- name: Validate App Store Credentials
shell: bash
run: Scripts/ci/check-release-config.sh app-store
- name: Distribute iOS Build To TestFlight
shell: bash
env:
BUILD_NUMBER: ${{ inputs.build_number }}
TESTFLIGHT_DISTRIBUTE_EXTERNAL: ${{ inputs.testflight_distribute_external || 'false' }}
TESTFLIGHT_GROUPS: ${{ inputs.testflight_groups || '' }}
IOS_USES_NON_EXEMPT_ENCRYPTION: ${{ inputs.ios_uses_non_exempt_encryption || 'false' }}
run: Scripts/ci/distribute-testflight.sh
distribute-ios-testflight:
name: Distribute (TestFlight iOS Testers)
if: ${{ inputs.upload_app_store != 'true' && inputs.distribute_testflight == 'true' }}
runs-on: namespace-profile-linux-testflight
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Configure Age
shell: bash
env:
AGE_FORGE_SSH_KEY: ${{ secrets.AGE_FORGE_SSH_KEY }}
run: Scripts/resolve-age-identity.sh >> "$GITHUB_ENV"
- name: Decrypt Release Secrets
uses: ./.forgejo/actions/decrypt-release-secrets
- name: Validate App Store Credentials
shell: bash
run: Scripts/ci/check-release-config.sh app-store
- name: Distribute iOS Build To TestFlight
shell: bash
env:
BUILD_NUMBER: ${{ inputs.build_number }}
TESTFLIGHT_DISTRIBUTE_EXTERNAL: ${{ inputs.testflight_distribute_external || 'false' }}
TESTFLIGHT_GROUPS: ${{ inputs.testflight_groups || '' }}
IOS_USES_NON_EXEMPT_ENCRYPTION: ${{ inputs.ios_uses_non_exempt_encryption || 'false' }}
run: Scripts/ci/distribute-testflight.sh
upload-sparkle:
name: Upload (Sparkle)
needs: sign-apple-kms
if: ${{ inputs.upload_sparkle == 'true' }}
runs-on: namespace-profile-linux-medium
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Resolve Sparkle Channel
id: channel
shell: bash
env:
BUILD_NUMBER: ${{ inputs.build_number }}
run: |
set -euo pipefail
channel="${{ inputs.sparkle_channel }}"
if [[ -z "$channel" ]]; then
channel="build-${BUILD_NUMBER}"
fi
echo "channel=$channel" >> "$GITHUB_OUTPUT"
- name: Ensure Nix
shell: bash
run: bash Scripts/ci/ensure-nix.sh
- name: Publish Sparkle Channel
shell: bash
env:
BUILD_NUMBER: ${{ inputs.build_number }}
CHANNEL: ${{ steps.channel.outputs.channel }}
SPARKLE_POINT_MAIN: ${{ inputs.sparkle_point_main }}
run: |
set -euo pipefail
nix develop .#ci -c Scripts/ci/google-wif-auth.sh
mkdir -p "publish/sparkle/${CHANNEL}"
nix develop .#ci -c gcloud storage rsync --recursive "gs://${BURROW_RELEASE_GCS_BUCKET}/builds/${BUILD_NUMBER}/sparkle/${CHANNEL}" "publish/sparkle/${CHANNEL}" || true
if [[ ! -f "publish/sparkle/${CHANNEL}/appcast.xml" ]]; then
echo "::warning ::No Sparkle appcast staged for build ${BUILD_NUMBER}; skipping Sparkle publish."
find "publish/sparkle/${CHANNEL}" -maxdepth 2 -type f -print || true
exit 0
fi
nix develop .#ci -c gcloud storage rsync --recursive "publish/sparkle/${CHANNEL}" "gs://${BURROW_RELEASE_GCS_BUCKET}/sparkle/${CHANNEL}"
if [[ "$SPARKLE_POINT_MAIN" == "true" ]]; then
printf '%s\n' "$CHANNEL" > main-channel.txt
nix develop .#ci -c gcloud storage cp main-channel.txt "gs://${BURROW_RELEASE_GCS_BUCKET}/sparkle/main-channel.txt"
nix develop .#ci -c gcloud storage cp "publish/sparkle/${CHANNEL}/appcast.xml" "gs://${BURROW_RELEASE_GCS_BUCKET}/sparkle/appcast.xml"
nix develop .#ci -c gcloud storage rsync --recursive "publish/sparkle/${CHANNEL}" "gs://${BURROW_RELEASE_GCS_BUCKET}/sparkle/default"
fi
upload-microsoft-store:
name: Upload (Microsoft Store Beta)
if: ${{ inputs.upload_microsoft_store == 'true' }}
runs-on: namespace-profile-windows-large
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Validate Microsoft Store Credentials
shell: pwsh
env:
MICROSOFT_TENANT_ID: ${{ secrets.MICROSOFT_TENANT_ID }}
MICROSOFT_CLIENT_ID: ${{ secrets.MICROSOFT_CLIENT_ID }}
MICROSOFT_CLIENT_SECRET: ${{ secrets.MICROSOFT_CLIENT_SECRET }}
run: |
foreach ($name in "MICROSOFT_TENANT_ID","MICROSOFT_CLIENT_ID","MICROSOFT_CLIENT_SECRET") {
if ([string]::IsNullOrWhiteSpace([Environment]::GetEnvironmentVariable($name))) {
throw "missing required environment variable: $name"
}
}
- name: Stop Until Store Package Exists
shell: pwsh
run: |
Write-Host "::warning ::Burrow only builds a Windows Rust stub today; Microsoft Store package upload is wired for credentials but waits on MSIX packaging."

View file

@ -1,92 +0,0 @@
name: "Release: If Needed"
run-name: "Release: If Needed (${{ github.ref_name }})"
on:
push:
branches:
- main
schedule:
- cron: "*/30 * * * *"
workflow_dispatch:
inputs:
upload_app_store:
description: Trigger downstream App Store Connect upload workflow
required: false
default: "true"
upload_sparkle:
description: Trigger downstream Sparkle publish workflow
required: false
default: "true"
upload_microsoft_store:
description: Trigger downstream Microsoft Store beta upload workflow
required: false
default: "false"
sparkle_point_main:
description: Update Sparkle main channel pointers
required: false
default: "true"
testflight_distribute_external:
description: Distribute to external TestFlight groups
required: false
default: "false"
testflight_groups:
description: Optional comma-separated TestFlight groups for external distribution
required: false
default: ""
ios_uses_non_exempt_encryption:
description: Set true only when the iOS build uses non-exempt encryption
required: false
default: "false"
permissions:
contents: read
actions: write
concurrency:
group: burrow-release-if-needed-main
cancel-in-progress: true
env:
GITHUB_TOKEN: ${{ github.token }}
BURROW_VERSION_REQUIRE_REMOTE: "1"
jobs:
check:
name: Check (Release Needed)
runs-on: namespace-profile-linux-medium
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Fetch Build Tags
shell: bash
run: git fetch --force --prune origin "+refs/tags/builds/*:refs/tags/builds/*"
- name: Dispatch Release Build
shell: bash
env:
UPLOAD_APP_STORE: ${{ github.event.inputs.upload_app_store || 'true' }}
UPLOAD_SPARKLE: ${{ github.event.inputs.upload_sparkle || 'true' }}
UPLOAD_MICROSOFT_STORE: ${{ github.event.inputs.upload_microsoft_store || 'false' }}
SPARKLE_POINT_MAIN: ${{ github.event.inputs.sparkle_point_main || 'true' }}
TESTFLIGHT_DISTRIBUTE_EXTERNAL: ${{ github.event.inputs.testflight_distribute_external || 'false' }}
TESTFLIGHT_GROUPS: ${{ github.event.inputs.testflight_groups || '' }}
IOS_USES_NON_EXEMPT_ENCRYPTION: ${{ github.event.inputs.ios_uses_non_exempt_encryption || 'false' }}
run: |
set -euo pipefail
status="$(Scripts/version.sh status)"
if [[ "$status" == "clean" ]]; then
echo "No unreleased changes."
exit 0
fi
inputs="$(python3 -c 'import json, os; print(json.dumps({"upload_app_store":os.environ["UPLOAD_APP_STORE"],"upload_sparkle":os.environ["UPLOAD_SPARKLE"],"upload_microsoft_store":os.environ["UPLOAD_MICROSOFT_STORE"],"sparkle_point_main":os.environ["SPARKLE_POINT_MAIN"],"testflight_distribute_external":os.environ["TESTFLIGHT_DISTRIBUTE_EXTERNAL"],"testflight_groups":os.environ["TESTFLIGHT_GROUPS"],"ios_uses_non_exempt_encryption":os.environ["IOS_USES_NON_EXEMPT_ENCRYPTION"]}))')"
curl -fsS \
-X POST \
-H "Authorization: token ${GITHUB_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"ref\":\"main\",\"inputs\":${inputs}}" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/workflows/build-release.yml/dispatches"
echo "Dispatched build-release.yml (status=${status})."

View file

@ -1,77 +0,0 @@
name: Release
on:
push:
tags:
- "v*"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
env:
BURROW_NIX_CACHE_URL: ${{ vars.BURROW_NIX_CACHE_URL || 'https://nix.burrow.net/burrow' }}
BURROW_NIX_CACHE_PUBLIC_KEY: ${{ vars.BURROW_NIX_CACHE_PUBLIC_KEY }}
jobs:
release:
name: Release Build
runs-on: namespace-profile-linux-medium
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
token: ${{ github.token }}
fetch-depth: 0
- name: Bootstrap Nix
shell: bash
run: |
set -euo pipefail
chmod +x Scripts/ci/ensure-nix.sh
Scripts/ci/ensure-nix.sh
- name: Build release artifacts
shell: bash
env:
RELEASE_REF: ${{ github.ref_name }}
run: |
set -euo pipefail
ref="${RELEASE_REF:-manual-${GITHUB_SHA::7}}"
export RELEASE_REF="${ref}"
chmod +x Scripts/ci/build-release-artifacts.sh
nix develop .#ci -c Scripts/ci/build-release-artifacts.sh
- name: Flatten release assets
shell: bash
env:
RELEASE_REF: ${{ github.ref_name }}
run: |
set -euo pipefail
ref="${RELEASE_REF:-manual-${GITHUB_SHA::7}}"
mkdir -p dist/release-assets
find "dist/builds/${ref}" -type f \( -name '*.tar.gz' -o -name '*.zip' -o -name '*.sha256' -o -name 'README.txt' \) -exec cp {} dist/release-assets/ \;
rm -rf dist/builds
cp dist/release-assets/* dist/
find dist -maxdepth 1 -type f -print | sort
- name: Upload release artifacts
uses: https://code.forgejo.org/actions/upload-artifact@v3
with:
name: burrow-release-${{ github.ref_name }}
path: dist/*
if-no-files-found: error
- name: Publish Forgejo release
if: startsWith(github.ref, 'refs/tags/')
shell: bash
env:
RELEASE_TAG: ${{ github.ref_name }}
API_URL: ${{ github.api_url }}
REPOSITORY: ${{ github.repository }}
TOKEN: ${{ github.token }}
run: |
set -euo pipefail
chmod +x Scripts/ci/publish-forgejo-release.sh
nix develop .#ci -c Scripts/ci/publish-forgejo-release.sh

5
.github/CODEOWNERS vendored
View file

@ -1,4 +1 @@
* @conradev @malted @JettChenT @jdogcoder * @conradev @ma1ted @Muirrum
burrow/ @conradev @malted @JettChenT @jdogcoder @Muirrum
tun/ @conradev @malted @JettChenT @jdogcoder @Muirrum
burrow-gtk/ @conradev @malted @JettChenT @jdogcoder @davnotdev

View file

@ -26,18 +26,18 @@ runs:
run: | run: |
echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8 echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8
xcodebuild clean archive \ xcodebuild archive \
-allowProvisioningUpdates \ -allowProvisioningUpdates \
-allowProvisioningDeviceRegistration \ -allowProvisioningDeviceRegistration \
-skipPackagePluginValidation \
-skipMacroValidation \
-onlyUsePackageVersionsFromResolvedFile \
-authenticationKeyID ${{ inputs.app-store-key-id }} \ -authenticationKeyID ${{ inputs.app-store-key-id }} \
-authenticationKeyIssuerID ${{ inputs.app-store-key-issuer-id }} \ -authenticationKeyIssuerID ${{ inputs.app-store-key-issuer-id }} \
-authenticationKeyPath "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" \ -authenticationKeyPath "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" \
-onlyUsePackageVersionsFromResolvedFile \
-scheme '${{ inputs.scheme }}' \ -scheme '${{ inputs.scheme }}' \
-destination '${{ inputs.destination }}' \ -destination '${{ inputs.destination }}' \
-archivePath '${{ inputs.archive-path }}' \ -archivePath '${{ inputs.archive-path }}' \
-resultBundlePath BuildResults.xcresult -resultBundlePath BuildResults.xcresult
./Tools/xcresulttool-github BuildResults.xcresult
rm -rf AuthKey_${{ inputs.app-store-key-id }}.p8 rm -rf AuthKey_${{ inputs.app-store-key-id }}.p8

View file

@ -18,36 +18,31 @@ inputs:
runs: runs:
using: composite using: composite
steps: steps:
- name: Xcode Cache - name: Cache Swift Packages
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
path: | path: |
Apple/PackageCache Apple/PackageCache
Apple/SourcePackages Apple/SourcePackages
Apple/DerivedData
key: ${{ runner.os }}-${{ inputs.scheme }}-${{ hashFiles('**/Package.resolved') }} key: ${{ runner.os }}-${{ inputs.scheme }}-${{ hashFiles('**/Package.resolved') }}
restore-keys: | restore-keys: |
${{ runner.os }}-${{ inputs.scheme }}-${{ hashFiles('**/Package.resolved') }}
${{ runner.os }}-${{ inputs.scheme }}- ${{ runner.os }}-${{ inputs.scheme }}-
${{ runner.os }}-
- name: Build - name: Build
shell: bash shell: bash
working-directory: Apple working-directory: Apple
run: | run: |
echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8 echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8
xcodebuild build-for-testing \ xcodebuild clean build-for-testing \
-allowProvisioningUpdates \ -allowProvisioningUpdates \
-allowProvisioningDeviceRegistration \ -allowProvisioningDeviceRegistration \
-skipPackagePluginValidation \
-skipMacroValidation \
-onlyUsePackageVersionsFromResolvedFile \
-authenticationKeyID ${{ inputs.app-store-key-id }} \ -authenticationKeyID ${{ inputs.app-store-key-id }} \
-authenticationKeyIssuerID ${{ inputs.app-store-key-issuer-id }} \ -authenticationKeyIssuerID ${{ inputs.app-store-key-issuer-id }} \
-authenticationKeyPath "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" \ -authenticationKeyPath "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" \
-onlyUsePackageVersionsFromResolvedFile \
-clonedSourcePackagesDirPath SourcePackages \ -clonedSourcePackagesDirPath SourcePackages \
-packageCachePath $PWD/PackageCache \ -packageCachePath $PWD/PackageCache \
-derivedDataPath $PWD/DerivedData \ -skipPackagePluginValidation \
-scheme '${{ inputs.scheme }}' \ -scheme '${{ inputs.scheme }}' \
-destination '${{ inputs.destination }}' \ -destination '${{ inputs.destination }}' \
-resultBundlePath BuildResults.xcresult -resultBundlePath BuildResults.xcresult

View file

@ -1,30 +0,0 @@
name: Download Provisioning Profiles
inputs:
app-store-key:
description: App Store key in PEM PKCS#8 format
required: true
app-store-key-id:
description: App Store key ID
required: true
app-store-key-issuer-id:
description: App Store key issuer ID
required: true
runs:
using: composite
steps:
- shell: bash
env:
FASTLANE_OPT_OUT_USAGE: 'YES'
run: |
APP_STORE_KEY=$(echo "${{ inputs.app-store-key }}" | jq -sR .)
cat << EOF > api-key.json
{
"key_id": "${{ inputs.app-store-key-id }}",
"issuer_id": "${{ inputs.app-store-key-issuer-id }}",
"key": $APP_STORE_KEY
}
EOF
fastlane sigh download_all --api_key_path api-key.json
rm -rf api-key.json

View file

@ -1,4 +1,4 @@
name: Export name: Notarize
inputs: inputs:
app-store-key: app-store-key:
description: App Store key in PEM PKCS#8 format description: App Store key in PEM PKCS#8 format
@ -12,8 +12,11 @@ inputs:
archive-path: archive-path:
description: Xcode archive path description: Xcode archive path
required: true required: true
export-options: destination:
description: The export options in JSON format description: The Xcode export destination. This can either be "export" or "upload"
required: true
method:
description: The Xcode export method. This can be one of app-store, validation, ad-hoc, package, enterprise, development, developer-id, or mac-application.
required: true required: true
export-path: export-path:
description: The path to export the archive to description: The path to export the archive to
@ -21,20 +24,19 @@ inputs:
runs: runs:
using: composite using: composite
steps: steps:
- shell: bash - id: notarize
shell: bash
working-directory: Apple working-directory: Apple
run: | run: |
echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8 echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8
echo '${{ inputs.export-options }}' | plutil -convert xml1 -o ExportOptions.plist - echo '{"destination":"${{ inputs.destination }}","method":"${{ inputs.method }}"}' \
| plutil -convert xml1 -o ExportOptions.plist -
xcodebuild \ xcodebuild \
-exportArchive \ -exportArchive \
-allowProvisioningUpdates \ -allowProvisioningUpdates \
-allowProvisioningDeviceRegistration \ -allowProvisioningDeviceRegistration \
-skipPackagePluginValidation \
-skipMacroValidation \
-onlyUsePackageVersionsFromResolvedFile \
-authenticationKeyID ${{ inputs.app-store-key-id }} \ -authenticationKeyID ${{ inputs.app-store-key-id }} \
-authenticationKeyIssuerID ${{ inputs.app-store-key-issuer-id }} \ -authenticationKeyIssuerID ${{ inputs.app-store-key-issuer-id }} \
-authenticationKeyPath "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" \ -authenticationKeyPath "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" \

View file

@ -1,25 +0,0 @@
name: Notarize
inputs:
app-store-key:
description: App Store key in PEM PKCS#8 format
required: true
app-store-key-id:
description: App Store key ID
required: true
app-store-key-issuer-id:
description: App Store key issuer ID
required: true
runs:
using: composite
steps:
- id: notarize
shell: bash
working-directory: Apple
run: |
echo "${{ inputs.app-store-key }}" > AuthKey_${{ inputs.app-store-key-id }}.p8
ditto -c -k --keepParent Release/Burrow.app Upload.zip
xcrun notarytool submit --wait --issuer ${{ inputs.app-store-key-issuer-id }} --key-id ${{ inputs.app-store-key-id }} --key "${PWD}/AuthKey_${{ inputs.app-store-key-id }}.p8" Upload.zip
xcrun stapler staple Release/Burrow.app
rm -rf AuthKey_${{ inputs.app-store-key-id }}.p8 Release

View file

@ -18,6 +18,9 @@ inputs:
runs: runs:
using: composite using: composite
steps: steps:
- shell: bash
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- shell: bash - shell: bash
working-directory: Apple working-directory: Apple
run: | run: |
@ -25,10 +28,10 @@ runs:
-scheme '${{ inputs.scheme }}' \ -scheme '${{ inputs.scheme }}' \
-destination '${{ inputs.destination }}' \ -destination '${{ inputs.destination }}' \
${{ inputs.test-plan && '-testPlan ' }}${{ inputs.test-plan }} \ ${{ inputs.test-plan && '-testPlan ' }}${{ inputs.test-plan }} \
-resultBundlePath "${{ inputs.artifact-prefix }}.xcresult" -resultBundlePath "${{ inputs.artifact-prefix }}-${{ steps.vars.outputs.sha_short }}.xcresult"
- uses: kishikawakatsumi/xcresulttool@v1 - uses: kishikawakatsumi/xcresulttool@v1
if: always() if: always()
with: with:
path: Apple/${{ inputs.artifact-prefix }}.xcresult path: Apple/${{ inputs.artifact-prefix }}-${{ steps.vars.outputs.sha_short }}.xcresult
title: ${{ inputs.check-name }} title: ${{ inputs.check-name }}
show-passed-tests: false show-passed-tests: false

View file

@ -1,29 +0,0 @@
name: Build AppImage
on:
push:
branches:
- main
pull_request:
branches:
- "*"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
appimage:
name: Build AppImage
runs-on: ubuntu-latest
container: docker
steps:
- uses: actions/checkout@v4
- name: Build AppImage
run: |
docker build -t appimage-builder . -f burrow-gtk/build-aux/Dockerfile
docker create --name temp appimage-builder
docker cp temp:/app/burrow-gtk/build-appimage/Burrow-x86_64.AppImage .
docker rm temp
- uses: actions/upload-artifact@v4
name: Upload to GitHub
with:
name: AppImage
path: Burrow-x86_64.AppImage

View file

@ -1,18 +1,15 @@
name: Build Apple Apps name: Apple Build
on: on:
push: push:
branches: branches:
- main - main
pull_request: pull_request:
branches: branches:
- "*" - "*"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
build: build:
name: Build App (${{ matrix.platform }}) name: Build App (${{ matrix.platform }})
runs-on: macos-14 runs-on: macos-12
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -24,7 +21,7 @@ jobs:
rust-targets: rust-targets:
- aarch64-apple-ios - aarch64-apple-ios
- scheme: App - scheme: App
destination: platform=iOS Simulator,OS=18.0,name=iPhone 15 Pro destination: platform=iOS Simulator,OS=16.2,name=iPhone 14 Pro
platform: iOS Simulator platform: iOS Simulator
sdk-name: iphonesimulator sdk-name: iphonesimulator
rust-targets: rust-targets:
@ -38,8 +35,7 @@ jobs:
- x86_64-apple-darwin - x86_64-apple-darwin
- aarch64-apple-darwin - aarch64-apple-darwin
env: env:
DEVELOPER_DIR: /Applications/Xcode_16.0.app/Contents/Developer DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer
PROTOC_PATH: /opt/homebrew/bin/protoc
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -54,11 +50,8 @@ jobs:
- name: Install Rust - name: Install Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
toolchain: 1.85.0 toolchain: stable
targets: ${{ join(matrix.rust-targets, ', ') }} targets: ${{ join(matrix.rust-targets, ', ') }}
- name: Install Protobuf
shell: bash
run: brew install protobuf
- name: Build - name: Build
id: build id: build
uses: ./.github/actions/build-for-testing uses: ./.github/actions/build-for-testing
@ -68,7 +61,7 @@ jobs:
app-store-key: ${{ secrets.APPSTORE_KEY }} app-store-key: ${{ secrets.APPSTORE_KEY }}
app-store-key-id: ${{ secrets.APPSTORE_KEY_ID }} app-store-key-id: ${{ secrets.APPSTORE_KEY_ID }}
app-store-key-issuer-id: ${{ secrets.APPSTORE_KEY_ISSUER_ID }} app-store-key-issuer-id: ${{ secrets.APPSTORE_KEY_ISSUER_ID }}
- name: Run Unit Tests - name: Xcode Unit Test
if: ${{ matrix.xcode-unit-test != '' }} if: ${{ matrix.xcode-unit-test != '' }}
continue-on-error: true continue-on-error: true
uses: ./.github/actions/test-without-building uses: ./.github/actions/test-without-building
@ -78,7 +71,7 @@ jobs:
test-plan: ${{ matrix.xcode-unit-test }} test-plan: ${{ matrix.xcode-unit-test }}
artifact-prefix: unit-tests-${{ matrix.sdk-name }} artifact-prefix: unit-tests-${{ matrix.sdk-name }}
check-name: Xcode Unit Tests (${{ matrix.platform }}) check-name: Xcode Unit Tests (${{ matrix.platform }})
- name: Run UI Tests - name: Xcode UI Test
if: ${{ matrix.xcode-ui-test != '' }} if: ${{ matrix.xcode-ui-test != '' }}
continue-on-error: true continue-on-error: true
uses: ./.github/actions/test-without-building uses: ./.github/actions/test-without-building

View file

@ -6,9 +6,6 @@ on:
pull_request: pull_request:
branches: branches:
- "*" - "*"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
build: build:
name: Build Docker Image name: Build Docker Image
@ -36,7 +33,6 @@ jobs:
images: ghcr.io/${{ github.repository }} images: ghcr.io/${{ github.repository }}
tags: | tags: |
type=sha type=sha
type=match,pattern=builds/(.*),group=1
type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}}
- name: Build and Push - name: Build and Push
uses: docker/build-push-action@v4 uses: docker/build-push-action@v4

View file

@ -1,16 +0,0 @@
on: workflow_dispatch
name: Build Flatpak
jobs:
flatpak:
name: Build Flatpak
runs-on: ubuntu-latest
container:
image: bilelmoussaoui/flatpak-github-actions:gnome-45
options: --privileged
steps:
- uses: actions/checkout@v4
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
with:
bundle: Burrow.flatpak
manifest-path: burrow-gtk/build-aux/com.hackclub.burrow.devel.json
cache-key: flatpak-builder-${{ github.sha }}

View file

@ -1,17 +0,0 @@
on: workflow_dispatch
name: Build RPM
jobs:
build:
name: Build RPM
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
- name: Install RPM
run: cargo install cargo-generate-rpm
- name: Build RPM
run: |
cargo build --release
strip -s target/release/burrow
- name: Build RPM
run: cargo generate-rpm -p burrow

View file

@ -1,4 +1,4 @@
name: Build Rust Crate name: Rust Build
on: on:
push: push:
branches: branches:
@ -6,9 +6,6 @@ on:
pull_request: pull_request:
branches: branches:
- "*" - "*"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
build: build:
name: Build Crate (${{ matrix.platform }}) name: Build Crate (${{ matrix.platform }})
@ -24,21 +21,15 @@ jobs:
- x86_64-unknown-linux-gnu - x86_64-unknown-linux-gnu
targets: targets:
- aarch64-unknown-linux-gnu - aarch64-unknown-linux-gnu
- os: macos-13 - os: macos-12
platform: macOS (Intel) platform: macOS
xcode: /Applications/Xcode_15.2.app
test-targets: test-targets:
- x86_64-apple-darwin - x86_64-apple-darwin
targets: targets:
- x86_64-apple-ios
- os: macos-14
platform: macOS
xcode: /Applications/Xcode_16.0.app
test-targets:
- aarch64-apple-darwin - aarch64-apple-darwin
targets:
- aarch64-apple-ios - aarch64-apple-ios
- aarch64-apple-ios-sim - aarch64-apple-ios-sim
- x86_64-apple-ios
- os: windows-2022 - os: windows-2022
platform: Windows platform: Windows
test-targets: test-targets:
@ -47,11 +38,10 @@ jobs:
- aarch64-pc-windows-msvc - aarch64-pc-windows-msvc
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
DEVELOPER_DIR: ${{ matrix.xcode }}/Contents/Developer DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer
CARGO_INCREMENTAL: 0 CARGO_INCREMENTAL: 0
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
RUST_BACKTRACE: short RUST_BACKTRACE: short
PROTOC_VERSION: 3.25.1
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -64,25 +54,17 @@ jobs:
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y ${{ join(matrix.packages, ' ') }} sudo apt-get install -y ${{ join(matrix.packages, ' ') }}
- name: Configure LLVM
if: matrix.os == 'windows-2022'
shell: bash
run: echo "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\Llvm\x64\bin" >> $GITHUB_PATH
- name: Install protoc
uses: taiki-e/install-action@v2
with:
tool: protoc@${{ env.PROTOC_VERSION }}
- name: Install Rust - name: Install Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
toolchain: 1.85.0 toolchain: stable
components: rustfmt components: rustfmt
targets: ${{ join(matrix.targets, ', ') }} targets: ${{ join(matrix.targets, ', ') }}
- name: Setup Rust Cache - name: Setup Rust Cache
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
- name: Build - name: Build
shell: bash shell: bash
run: cargo build --locked --verbose --workspace --all-features --target ${{ join(matrix.targets, ' --target ') }} --target ${{ join(matrix.test-targets, ' --target ') }} run: cargo build --verbose --workspace --all-features --target ${{ join(matrix.targets, ' --target ') }} --target ${{ join(matrix.test-targets, ' --target ') }}
- name: Test - name: Test
shell: bash shell: bash
run: cargo test --locked --verbose --workspace --all-features --target ${{ join(matrix.test-targets, ' --target ') }} run: cargo test --verbose --workspace --all-features --target ${{ join(matrix.test-targets, ' --target ') }}

View file

@ -8,14 +8,13 @@ jobs:
name: Git Lint name: Git Lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - uses: actions/checkout@v3
uses: actions/checkout@v4 with:
with: ref: ${{ github.event.pull_request.head.sha }}
ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0
fetch-depth: 0 - name: Install Gitlint
- name: Install shell: bash
shell: bash run: python -m pip install gitlint
run: python -m pip install gitlint - name: Run Gitlint
- name: Lint shell: bash
shell: bash run: gitlint --commits "${{ github.event.pull_request.base.sha }}..HEAD"
run: gitlint --commits "${{ github.event.pull_request.base.sha }}..HEAD"

View file

@ -1,23 +0,0 @@
name: Governance Lint
on:
pull_request:
branches:
- "*"
jobs:
governance:
name: BEP Metadata
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Validate BEP metadata
shell: bash
run: |
set -euo pipefail
python3 Scripts/check-bep-metadata.py

View file

@ -1,5 +1,8 @@
name: Swift Lint name: Swift Lint
on: on:
push:
branches:
- main
pull_request: pull_request:
branches: branches:
- "*" - "*"
@ -11,6 +14,8 @@ jobs:
image: ghcr.io/realm/swiftlint:latest image: ghcr.io/realm/swiftlint:latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
with:
ssh-key: ${{ secrets.DEPLOY_KEY }}
- name: Lint - name: Lint
run: swiftlint lint --strict --reporter github-actions-logging run: swiftlint lint --reporter github-actions-logging

View file

@ -1,120 +1,65 @@
name: Release (Apple) name: Build Apple Release
on: on:
release: release:
types: types:
- created - created
jobs: jobs:
build: build:
name: Build ${{ matrix.platform }} Release name: Build ${{ matrix.configuration['platform'] }} Release
runs-on: macos-14 runs-on: macos-12
permissions:
contents: write
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: configuration:
- platform: iOS - scheme: App (iOS)
rust-targets: destination: generic/platform=iOS
- aarch64-apple-ios platform: iOS
- platform: macOS method: ad-hoc
rust-targets: artifact-file: Apple/Release/Burrow.ipa
- x86_64-apple-darwin - scheme: App (macOS)
- aarch64-apple-darwin destination: generic/platform=macOS
platform: macOS
method: mac-application
artifact-file: Burrow.app.txz
env: env:
DEVELOPER_DIR: /Applications/Xcode_16.0.app/Contents/Developer DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer
PROTOC_PATH: /opt/homebrew/bin/protoc
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
fetch-depth: 0 ssh-key: ${{ secrets.DEPLOY_KEY }}
submodules: recursive
- name: Import Certificate - name: Import Certificate
uses: ./.github/actions/import-cert uses: ./.github/actions/import-cert
with: with:
certificate: ${{ secrets.DEVELOPER_CERT }} certificate: ${{ secrets.DEVELOPER_CERT }}
password: ${{ secrets.DEVELOPER_CERT_PASSWORD }} password: ${{ secrets.DEVELOPER_CERT_PASSWORD }}
- name: Download Provisioning Profiles
uses: ./.github/actions/download-profiles
with:
app-store-key: ${{ secrets.APPSTORE_KEY }}
app-store-key-id: ${{ secrets.APPSTORE_KEY_ID }}
app-store-key-issuer-id: ${{ secrets.APPSTORE_KEY_ISSUER_ID }}
- name: Install Provisioning Profiles
shell: bash
run: |
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles/
cp -f Apple/Profiles/* ~/Library/MobileDevice/Provisioning\ Profiles/
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: 1.85.0
targets: ${{ join(matrix.rust-targets, ', ') }}
- name: Install Protobuf
shell: bash
run: brew install protobuf
- name: Configure Version
id: version
shell: bash
run: echo "BUILD_NUMBER=$(Tools/version.sh)" >> $GITHUB_OUTPUT
- name: Archive - name: Archive
uses: ./.github/actions/archive uses: ./.github/actions/archive
with: with:
scheme: App scheme: ${{ matrix.configuration['scheme'] }}
destination: generic/platform=${{ matrix.platform }} destination: ${{ matrix.configuration['destination'] }}
app-store-key: ${{ secrets.APPSTORE_KEY }} app-store-key: ${{ secrets.APPSTORE_KEY }}
app-store-key-id: ${{ secrets.APPSTORE_KEY_ID }} app-store-key-id: ${{ secrets.APPSTORE_KEY_ID }}
app-store-key-issuer-id: ${{ secrets.APPSTORE_KEY_ISSUER_ID }} app-store-key-issuer-id: ${{ secrets.APPSTORE_KEY_ISSUER_ID }}
archive-path: Burrow.xcarchive archive-path: Burrow.xcarchive
- name: Export - name: Export Locally
uses: ./.github/actions/export uses: ./.github/actions/export
with: with:
method: ${{ matrix.platform == 'macOS' && 'developer-id' || 'ad-hoc' }} method: ${{ matrix.configuration['method'] }}
destination: export destination: export
app-store-key: ${{ secrets.APPSTORE_KEY }} app-store-key: ${{ secrets.APPSTORE_KEY }}
app-store-key-id: ${{ secrets.APPSTORE_KEY_ID }} app-store-key-id: ${{ secrets.APPSTORE_KEY_ID }}
app-store-key-issuer-id: ${{ secrets.APPSTORE_KEY_ISSUER_ID }} app-store-key-issuer-id: ${{ secrets.APPSTORE_KEY_ISSUER_ID }}
archive-path: Burrow.xcarchive archive-path: Burrow.xcarchive
export-options: |
{"teamID":"P6PV2R9443","destination":"export","method":"developer-id","provisioningProfiles":{"com.hackclub.burrow":"Burrow Developer ID","com.hackclub.burrow.network":"Burrow Network Developer ID"},"signingCertificate":"Developer ID Application","signingStyle":"manual"}
export-path: Release export-path: Release
- name: Notarize - name: Compress
if: ${{ matrix.platform == 'macOS' }} if: ${{ matrix.configuration['platform'] == 'macOS' }}
uses: ./.github/actions/notarize
with:
app-store-key: ${{ secrets.APPSTORE_KEY }}
app-store-key-id: ${{ secrets.APPSTORE_KEY_ID }}
app-store-key-issuer-id: ${{ secrets.APPSTORE_KEY_ISSUER_ID }}
- name: Compress (iOS)
if: ${{ matrix.platform == 'iOS' }}
shell: bash shell: bash
run: | run: tar --options xz:compression-level=9 -C Apple/Release -cJf Burrow.app.txz ./
cp Apple/Release/Burrow.ipa Burrow.ipa - name: Attach Artifact
aa archive -a lzma -b 8m -d Apple -subdir Burrow.xcarchive -o Burrow-${{ matrix.platform }}.xcarchive.aar uses: SierraSoftworks/gh-releases@v1.0.6
rm -rf Apple/Release
- name: Compress (macOS)
if: ${{ matrix.platform == 'macOS' }}
shell: bash
run: |
aa archive -a lzma -b 8m -d Apple/Release -subdir Burrow.app -o Burrow.app.aar
aa archive -a lzma -b 8m -d Apple -subdir Burrow.xcarchive -o Burrow-${{ matrix.platform }}.xcarchive.aar
rm -rf Apple/Release
- name: Upload to GitHub
uses: SierraSoftworks/gh-releases@v1.0.7
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
release_tag: ${{ github.ref_name }} overwrite: 'false'
overwrite: 'true' files: ${{ matrix.configuration['artifact-file'] }}
files: |
${{ matrix.platform == 'macOS' && 'Burrow.aap.aar' || 'Burrow.ipa' }}
Burrow-${{ matrix.platform }}.xcarchive.aar
- name: Upload to App Store Connect
if: ${{ matrix.platform == 'iOS' }}
uses: ./.github/actions/export
with:
app-store-key: ${{ secrets.APPSTORE_KEY }}
app-store-key-id: ${{ secrets.APPSTORE_KEY_ID }}
app-store-key-issuer-id: ${{ secrets.APPSTORE_KEY_ISSUER_ID }}
archive-path: Burrow.xcarchive
export-options: |
{"method": "app-store", "destination": "upload"}
export-path: Release

View file

@ -1,23 +0,0 @@
name: Create Release If Needed
on:
workflow_dispatch:
schedule:
- cron: '0 10 * * *'
concurrency:
group: ${{ github.workflow }}
jobs:
create:
name: Create Release If Needed
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- shell: bash
run: |
if [[ $(Tools/version.sh status) == "dirty" ]]; then
gh workflow run release-now.yml
fi

View file

@ -1,29 +0,0 @@
name: Release (Linux)
on:
release:
types:
- created
jobs:
appimage:
name: Build AppImage
runs-on: ubuntu-latest
container: docker
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build AppImage
run: |
docker build -t appimage-builder . -f burrow-gtk/build-aux/Dockerfile
docker create --name temp appimage-builder
docker cp temp:/app/burrow-gtk/build-appimage/Burrow-x86_64.AppImage .
docker rm temp
- name: Attach Artifacts
uses: SierraSoftworks/gh-releases@v1.0.7
with:
token: ${{ secrets.GITHUB_TOKEN }}
release_tag: ${{ github.ref_name }}
overwrite: "true"
files: |
Burrow-x86_64.AppImage

View file

@ -1,17 +0,0 @@
name: Create Release
on: workflow_dispatch
concurrency:
group: ${{ github.workflow }}
jobs:
create:
env:
GH_TOKEN: ${{ secrets.GH_RELEASE_TOKEN }}
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- shell: bash
run: Tools/version.sh increment

27
.gitignore vendored
View file

@ -1,32 +1,5 @@
# Xcode # Xcode
xcuserdata xcuserdata
Apple/build/
.derived-data/
AuthKey_*.p8
burrow-certs-pass.txt
*.p12
# Swift
Apple/Package/.swiftpm/
# Rust # Rust
target/ target/
.env
# Bazel
bazel-*
.DS_STORE
.idea/
tmp/
.cache/
intake/
dist/
publish/
release/
ci-artifacts/
*.db
*.sqlite3
*.sock

View file

@ -30,6 +30,7 @@ opt_in_rules:
- function_default_parameter_at_end - function_default_parameter_at_end
- ibinspectable_in_extension - ibinspectable_in_extension
- identical_operands - identical_operands
- implicitly_unwrapped_optional
- indentation_width - indentation_width
- joined_default_parameter - joined_default_parameter
- last_where - last_where
@ -45,6 +46,7 @@ opt_in_rules:
- multiline_parameters - multiline_parameters
- multiline_parameters_brackets - multiline_parameters_brackets
- no_extension_access_modifier - no_extension_access_modifier
- no_grouping_extension
- nslocalizedstring_key - nslocalizedstring_key
- nslocalizedstring_require_bundle - nslocalizedstring_require_bundle
- number_separator - number_separator
@ -74,7 +76,9 @@ opt_in_rules:
- sorted_first_last - sorted_first_last
- sorted_imports - sorted_imports
- static_operator - static_operator
- strict_fileprivate
- strong_iboutlet - strong_iboutlet
- switch_case_on_newline
- test_case_accessibility - test_case_accessibility
- toggle_bool - toggle_bool
- trailing_closure - trailing_closure
@ -93,5 +97,3 @@ disabled_rules:
- force_try - force_try
- nesting - nesting
- todo - todo
- trailing_comma
- switch_case_on_newline

41
.vscode/settings.json vendored
View file

@ -1,26 +1,17 @@
{ {
"files.autoSave": "onFocusChange", "files.autoSave": "onFocusChange",
"files.defaultLanguage": "rust", "files.defaultLanguage": "rust",
"editor.formatOnPaste": true, "editor.formatOnPaste": true,
"editor.formatOnSave": true, "editor.formatOnSave": true,
"files.trimTrailingWhitespace": true, "files.trimTrailingWhitespace": true,
"editor.suggest.preview": true, "editor.suggest.preview": true,
"editor.acceptSuggestionOnEnter": "on", "editor.acceptSuggestionOnEnter": "on",
"rust-analyzer.restartServerOnConfigChange": true, "rust-analyzer.restartServerOnConfigChange": true,
"rust-analyzer.cargo.features": "all", "rust-analyzer.cargo.features": "all",
"rust-analyzer.rustfmt.extraArgs": ["+nightly"], "rust-analyzer.rustfmt.extraArgs": [
"[rust]": { "+nightly"
"editor.defaultFormatter": "rust-lang.rust-analyzer" ],
}, "[rust]": {
"rust-analyzer.inlayHints.typeHints.enable": false, "editor.defaultFormatter": "rust-lang.rust-analyzer",
"rust-analyzer.linkedProjects": [ }
"./burrow/Cargo.toml" }
],
"[yaml]": {
"editor.insertSpaces": true,
"editor.tabSize": 2,
"editor.autoIndent": "advanced",
"diffEditor.ignoreTrimWhitespace": false,
"editor.formatOnSave": false
}
}

View file

@ -1,14 +0,0 @@
# instructions for agents
1. Spell the project name as `Burrow` in user-facing copy and `burrow` in code, package, and protocol identifiers unless an existing integration requires a different literal.
2. Read [CONSTITUTION.md](CONSTITUTION.md) before changing Apple clients, the daemon, the control plane, forge infrastructure, identity, or security-sensitive code.
3. Anchor non-trivial changes in a Burrow Evolution Proposal (BEP) under [evolution/](evolution/README.md) so future contributors can inherit the rationale, safeguards, and rollout shape.
4. Before touching the Apple app, daemon IPC, or Tailnet flows, review:
- [evolution/proposals/BEP-0002-control-plane-bootstrap-and-local-auth.md](evolution/proposals/BEP-0002-control-plane-bootstrap-and-local-auth.md)
- [evolution/proposals/BEP-0003-connect-ip-and-negotiation-roadmap.md](evolution/proposals/BEP-0003-connect-ip-and-negotiation-roadmap.md)
- [evolution/proposals/BEP-0005-daemon-ipc-and-apple-boundary.md](evolution/proposals/BEP-0005-daemon-ipc-and-apple-boundary.md)
- [evolution/proposals/BEP-0006-tailnet-authority-first-control-plane.md](evolution/proposals/BEP-0006-tailnet-authority-first-control-plane.md)
5. Apple clients must talk only to the daemon over gRPC. Do not add direct HTTP, control-plane, or helper-process calls from Swift UI code.
6. Treat Tailnet as one protocol family. Tailscale-managed and self-hosted Headscale-style deployments differ by authority, policy, and auth details, not by a separate user-facing protocol surface.
7. Maintain canonical identity and operator metadata in [contributors.nix](contributors.nix). If Burrow forge, Authentik, Headscale, or admin/group mappings need to change, edit that registry first and derive runtime configuration from it.
8. When process or architecture is unclear, stop and draft or update a BEP instead of improvising durable behavior in code.

View file

@ -1,14 +0,0 @@
# Burrow Android
The Android app starts as a Kotlin shell linked to `crates/burrow-mobile-ffi`,
which exposes the cross-platform `burrow-core` crate through JNI.
The current stub proves the package shape and Rust core boundary. Full VPN
support must use Android `VpnService` and keep platform consent, Play Store
policy, and disclosure requirements in the release lane.
The Bazel entrypoint is:
```sh
bazel build //bazel/android:check_stub_stamp
```

View file

@ -1,21 +0,0 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "net.burrow.android"
compileSdk = 36
defaultConfig {
applicationId = "net.burrow.android"
minSdk = 26
targetSdk = 36
versionCode = 1
versionName = "0.1.0"
}
}
kotlin {
jvmToolchain(17)
}

View file

@ -1,18 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/BurrowTheme">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -1,9 +0,0 @@
package net.burrow.android
object BurrowCore {
init {
System.loadLibrary("burrow_mobile_ffi")
}
external fun version(): String
}

View file

@ -1,32 +0,0 @@
package net.burrow.android
import android.app.Activity
import android.os.Bundle
import android.widget.LinearLayout
import android.widget.TextView
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val versionText = runCatching { BurrowCore.version() }
.getOrElse { "burrow-core unavailable" }
val root = LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
setPadding(40, 40, 40, 40)
}
root.addView(TextView(this).apply {
text = getString(R.string.app_name)
textSize = 24f
})
root.addView(TextView(this).apply {
text = versionText
textSize = 14f
})
setContentView(root)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

View file

@ -1,3 +0,0 @@
<resources>
<string name="app_name">Burrow</string>
</resources>

View file

@ -1,8 +0,0 @@
<resources>
<style name="BurrowTheme" parent="android:style/Theme.Material.Light.NoActionBar">
<item name="android:fontFamily">sans</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:navigationBarColor">#ffffff</item>
<item name="android:statusBarColor">#ffffff</item>
</style>
</resources>

View file

@ -1,4 +0,0 @@
plugins {
id("com.android.application") version "8.10.1" apply false
id("org.jetbrains.kotlin.android") version "2.1.21" apply false
}

View file

@ -1,18 +0,0 @@
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "BurrowAndroid"
include(":app")

View file

@ -2,11 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:burrow.rs?mode=developer</string>
<string>webcredentials:burrow.rs?mode=developer</string>
</array>
<key>com.apple.developer.networking.networkextension</key> <key>com.apple.developer.networking.networkextension</key>
<array> <array>
<string>packet-tunnel-provider</string> <string>packet-tunnel-provider</string>

View file

@ -2,11 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:burrow.rs?mode=developer</string>
<string>webcredentials:burrow.rs?mode=developer</string>
</array>
<key>com.apple.developer.networking.networkextension</key> <key>com.apple.developer.networking.networkextension</key>
<array> <array>
<string>packet-tunnel-provider</string> <string>packet-tunnel-provider</string>

View file

@ -10,20 +10,9 @@ INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphone*] = YES
INFOPLIST_KEY_UIStatusBarStyle[sdk=iphone*] = UIStatusBarStyleDefault INFOPLIST_KEY_UIStatusBarStyle[sdk=iphone*] = UIStatusBarStyleDefault
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES[sdk=iphone*] = BurrowPanel02 BurrowPanel03 BurrowPanel04 BurrowPanel05 BurrowPanel06
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS[sdk=iphone*] = YES
TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2 TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2
EXCLUDED_SOURCE_FILE_NAMES = MainMenu.xib
EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*] =
INFOPLIST_KEY_LSUIElement[sdk=macosx*] = YES
INFOPLIST_KEY_NSMainNibFile[sdk=macosx*] = MainMenu
INFOPLIST_KEY_NSPrincipalClass[sdk=macosx*] = NSApplication
INFOPLIST_KEY_LSApplicationCategoryType[sdk=macosx*] = public.app-category.utilities INFOPLIST_KEY_LSApplicationCategoryType[sdk=macosx*] = public.app-category.utilities
BURROW_SPARKLE_FEED_URL = https:/$()/releases.burrow.net/sparkle/appcast.xml
BURROW_SPARKLE_PUBLIC_ED_KEY = uugZuJeqvvKd91NZ6F1Fv2cQenUbIG/ZW3L9MuaEz30=
INFOPLIST_KEY_SUFeedURL[sdk=macosx*] = $(BURROW_SPARKLE_FEED_URL)
INFOPLIST_KEY_SUPublicEDKey[sdk=macosx*] = $(BURROW_SPARKLE_PUBLIC_ED_KEY)
CODE_SIGN_ENTITLEMENTS = App/App-iOS.entitlements CODE_SIGN_ENTITLEMENTS = App/App-iOS.entitlements
CODE_SIGN_ENTITLEMENTS[sdk=macosx*] = App/App-macOS.entitlements CODE_SIGN_ENTITLEMENTS[sdk=macosx*] = App/App-macOS.entitlements

View file

@ -1,19 +1,9 @@
#if os(macOS) #if os(macOS)
import AppKit import AppKit
import BurrowUI
import Sparkle
import SwiftUI import SwiftUI
@main
@MainActor @MainActor
class AppDelegate: NSObject, NSApplicationDelegate { class AppDelegate: NSObject, NSApplicationDelegate {
private var windowController: NSWindowController?
private let updaterController = SPUStandardUpdaterController(
startingUpdater: true,
updaterDelegate: nil,
userDriverDelegate: nil
)
private let quitItem: NSMenuItem = { private let quitItem: NSMenuItem = {
let quitItem = NSMenuItem( let quitItem = NSMenuItem(
title: "Quit Burrow", title: "Quit Burrow",
@ -25,29 +15,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
return quitItem return quitItem
}() }()
private lazy var openItem: NSMenuItem = {
let item = NSMenuItem(
title: "Open Burrow",
action: #selector(openWindow),
keyEquivalent: "o"
)
item.target = self
item.keyEquivalentModifierMask = .command
return item
}()
private lazy var checkForUpdatesItem: NSMenuItem = {
let item = NSMenuItem(
title: "Check for Updates...",
action: #selector(checkForUpdates(_:)),
keyEquivalent: ""
)
item.target = self
return item
}()
private let toggleItem: NSMenuItem = { private let toggleItem: NSMenuItem = {
let toggleView = NSHostingView(rootView: MenuItemToggleView()) let toggleView = NSHostingView(rootView: MenuItemToggleView(tunnel: BurrowApp.tunnel))
toggleView.frame.size = CGSize(width: 300, height: 32) toggleView.frame.size = CGSize(width: 300, height: 32)
toggleView.autoresizingMask = [.width] toggleView.autoresizingMask = [.width]
@ -60,8 +29,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let menu = NSMenu() let menu = NSMenu()
menu.items = [ menu.items = [
toggleItem, toggleItem,
openItem,
checkForUpdatesItem,
.separator(), .separator(),
quitItem quitItem
] ]
@ -72,7 +39,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let statusBar = NSStatusBar.system let statusBar = NSStatusBar.system
let statusItem = statusBar.statusItem(withLength: NSStatusItem.squareLength) let statusItem = statusBar.statusItem(withLength: NSStatusItem.squareLength)
if let button = statusItem.button { if let button = statusItem.button {
button.image = NSImage(systemSymbolName: "pipe.and.drop.fill", accessibilityDescription: nil) button.image = NSImage(systemSymbolName: "network.badge.shield.half.filled", accessibilityDescription: nil)
} }
return statusItem return statusItem
}() }()
@ -80,33 +47,5 @@ class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) { func applicationDidFinishLaunching(_ notification: Notification) {
statusItem.menu = menu statusItem.menu = menu
} }
@objc
private func openWindow() {
if let window = windowController?.window {
window.makeKeyAndOrderFront(nil)
NSApplication.shared.activate(ignoringOtherApps: true)
return
}
let contentView = BurrowView()
let hostingController = NSHostingController(rootView: contentView)
let window = NSWindow(contentViewController: hostingController)
window.title = "Burrow"
window.setContentSize(NSSize(width: 820, height: 720))
window.styleMask.insert([.titled, .closable, .miniaturizable, .resizable])
window.center()
let controller = NSWindowController(window: window)
controller.shouldCascadeWindows = true
controller.showWindow(nil)
windowController = controller
NSApplication.shared.activate(ignoringOtherApps: true)
}
@objc
private func checkForUpdates(_ sender: Any?) {
updaterController.checkForUpdates(sender)
}
} }
#endif #endif

View file

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,63 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -1,46 +1,21 @@
#if !os(macOS)
import BurrowUI
import SwiftUI import SwiftUI
#if os(iOS)
import UIKit
#endif
@MainActor
@main @main
@MainActor
struct BurrowApp: App { struct BurrowApp: App {
static let tunnel = Tunnel { manager, proto in
proto.serverAddress = "hackclub.com"
manager.localizedDescription = "Burrow"
}
#if os(macOS)
@NSApplicationDelegateAdaptor(AppDelegate.self)
var delegate
#endif
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
#if os(iOS) TunnelView()
BurrowView(appIconManager: UIKitAppIconManager())
#else
BurrowView()
#endif
} }
} }
} }
#if os(iOS)
@MainActor
private struct UIKitAppIconManager: AppIconManaging {
var supportsAlternateIcons: Bool {
UIApplication.shared.supportsAlternateIcons
}
var alternateIconName: String? {
UIApplication.shared.alternateIconName
}
func setAlternateIconName(_ iconName: String?) async throws {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, any Error>) in
UIApplication.shared.setAlternateIconName(iconName) { error in
if let error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: ())
}
}
}
}
}
#endif
#endif

View file

@ -1,679 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23091" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23091"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="BurrowApp" customModuleProvider="target"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="Burrow" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Burrow" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About Burrow" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide Burrow" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit Burrow" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
<connections>
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
<connections>
<action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
<connections>
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
<connections>
<action selector="print:" target="-1" id="qaZ-4w-aoO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="-1" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleSidebar:" target="-1" id="iwa-gc-5KM"/>
</connections>
</menuItem>
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="Burrow Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
<point key="canvasLocation" x="200" y="121"/>
</menu>
</objects>
</document>

View file

@ -0,0 +1,61 @@
//
// MenuView.swift
// App
//
// Created by Thomas Stubblefield on 5/13/23.
//
import SwiftUI
struct MenuItemToggleView: View {
@ObservedObject var tunnel: Tunnel
var body: some View {
HStack {
Text("Burrow")
.font(.headline)
Spacer()
Toggle("Burrow", isOn: tunnel.isOn)
.labelsHidden()
.disabled(tunnel.isDisabled)
.toggleStyle(.switch)
}
.padding(.horizontal, 4)
.padding(10)
.frame(minWidth: 300, minHeight: 32, maxHeight: 32)
.task { await tunnel.update() }
}
}
extension Tunnel {
var isDisabled: Bool {
switch self.status {
case .disconnected, .permissionRequired, .connected:
return false
case .unknown, .disabled, .connecting, .reasserting, .disconnecting, .invalid, .configurationReadWriteFailed:
return true
}
}
var isOn: Binding<Bool> {
Binding {
switch self.status {
case .unknown, .disabled, .disconnecting, .disconnected, .invalid, .permissionRequired, .configurationReadWriteFailed:
return false
case .connecting, .reasserting, .connected:
return true
}
} set: { newValue in
switch (self.status, newValue) {
case (.permissionRequired, true):
Task { try await self.configure() }
case (.disconnected, true):
try? self.start()
case (.connected, false):
self.stop()
default:
return
}
}
}
}

View file

@ -1,24 +1,30 @@
import NetworkExtension import NetworkExtension
extension NEVPNManager: @unchecked @retroactive Sendable { extension NEVPNManager {
func remove() async throws { func remove() async throws {
_ = try await withUnsafeThrowingContinuation { continuation in let _: Void = try await withUnsafeThrowingContinuation { continuation in
removeFromPreferences(completionHandler: completion(continuation)) removeFromPreferences(completionHandler: completion(continuation))
} }
} }
func save() async throws { func save() async throws {
_ = try await withUnsafeThrowingContinuation { continuation in let _: Void = try await withUnsafeThrowingContinuation { continuation in
saveToPreferences(completionHandler: completion(continuation)) saveToPreferences(completionHandler: completion(continuation))
} }
} }
} }
extension NETunnelProviderManager: @unchecked @retroactive Sendable { extension NETunnelProviderManager {
class var managers: [NETunnelProviderManager] { class var managers: [NETunnelProviderManager] {
get async throws { get async throws {
try await withUnsafeThrowingContinuation { continuation in try await withUnsafeThrowingContinuation { continuation in
loadAllFromPreferences(completionHandler: completion(continuation)) loadAllFromPreferences { managers, error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: managers ?? [])
}
}
} }
} }
} }
@ -26,20 +32,10 @@ extension NETunnelProviderManager: @unchecked @retroactive Sendable {
private func completion(_ continuation: UnsafeContinuation<Void, Error>) -> (Error?) -> Void { private func completion(_ continuation: UnsafeContinuation<Void, Error>) -> (Error?) -> Void {
return { error in return { error in
if let error { if let error = error {
continuation.resume(throwing: error) continuation.resume(throwing: error)
} else { } else {
continuation.resume(returning: ()) continuation.resume(returning: ())
} }
} }
} }
private func completion<T: Sendable>(_ continuation: UnsafeContinuation<T, Error>) -> (T?, Error?) -> Void {
return { value, error in
if let error {
continuation.resume(throwing: error)
} else if let value {
continuation.resume(returning: value)
}
}
}

42
Apple/App/Status.swift Normal file
View file

@ -0,0 +1,42 @@
import Foundation
import NetworkExtension
extension Tunnel {
enum Status: CustomStringConvertible, Equatable, Hashable {
case unknown
case permissionRequired
case disabled
case connecting
case connected(Date)
case disconnecting
case disconnected
case reasserting
case invalid
case configurationReadWriteFailed
var description: String {
switch self {
case .unknown:
return "Unknown"
case .permissionRequired:
return "Permission Required"
case .disconnected:
return "Disconnected"
case .disabled:
return "Disabled"
case .connecting:
return "Connecting"
case .connected:
return "Connected"
case .disconnecting:
return "Disconnecting"
case .reasserting:
return "Reasserting"
case .invalid:
return "Invalid"
case .configurationReadWriteFailed:
return "System Error"
}
}
}
}

135
Apple/App/Tunnel.swift Normal file
View file

@ -0,0 +1,135 @@
import Combine
import NetworkExtension
import SwiftUI
@MainActor
class Tunnel: ObservableObject {
@Published private(set) var status: Status = .unknown
@Published private var error: NEVPNError?
private let bundleIdentifier: String
private let configure: (NETunnelProviderManager, NETunnelProviderProtocol) -> Void
private var tasks: [Task<Void, Error>] = []
private var managers: [NEVPNManager]? {
didSet { status = currentStatus }
}
private var currentStatus: Status {
guard let managers = managers else {
guard let error = error else {
return .unknown
}
switch error.code {
case .configurationReadWriteFailed:
return .configurationReadWriteFailed
default:
return .unknown
}
}
guard let manager = managers.first else {
return .permissionRequired
}
guard manager.isEnabled else {
return .disabled
}
return manager.connection.tunnelStatus
}
convenience init(configure: @escaping (NETunnelProviderManager, NETunnelProviderProtocol) -> Void) {
self.init("com.hackclub.burrow.network", configure: configure)
}
init(_ bundleIdentifier: String, configure: @escaping (NETunnelProviderManager, NETunnelProviderProtocol) -> Void) {
self.bundleIdentifier = bundleIdentifier
self.configure = configure
let statusTask = Task {
for try await _ in NotificationCenter.default.notifications(named: .NEVPNStatusDidChange) {
status = currentStatus
}
}
let configurationTask = Task {
for try await _ in NotificationCenter.default.notifications(named: .NEVPNConfigurationChange) {
await update()
}
}
tasks = [statusTask, configurationTask]
}
func update() async {
do {
managers = try await NETunnelProviderManager.managers
} catch let error as NEVPNError {
self.error = error
} catch {
print(error)
}
}
func configure() async throws {
if managers == nil {
await update()
}
guard let managers = managers else { return }
if managers.count > 1 {
try await withThrowingTaskGroup(of: Void.self, returning: Void.self) { group in
for manager in managers.suffix(from: 1) {
group.addTask { try await manager.remove() }
}
try await group.waitForAll()
}
}
if managers.isEmpty {
let manager = NETunnelProviderManager()
let proto = NETunnelProviderProtocol()
proto.providerBundleIdentifier = bundleIdentifier
configure(manager, proto)
manager.protocolConfiguration = proto
try await manager.save()
}
}
func start() throws {
guard let manager = managers?.first else { return }
try manager.connection.startVPNTunnel()
}
func stop() {
guard let manager = managers?.first else { return }
manager.connection.stopVPNTunnel()
}
deinit {
tasks.forEach { $0.cancel() }
}
}
extension NEVPNConnection {
var tunnelStatus: Tunnel.Status {
switch status {
case .connected:
return .connected(connectedDate!)
case .connecting:
return .connecting
case .disconnecting:
return .disconnecting
case .disconnected:
return .disconnected
case .reasserting:
return .reasserting
case .invalid:
return .invalid
@unknown default:
return .unknown
}
}
}

View file

@ -0,0 +1,36 @@
import SwiftUI
struct TunnelView: View {
// @ObservedObject var tunnel: Tunnel
var body: some View {
EmptyView()
// VStack {
// Text(verbatim: tunnel.status.description)
// switch tunnel.status {
// case .connected:
// Button("Disconnect", action: stop)
// case .permissionRequired:
// Button("Allow", action: configure)
// case .disconnected:
// Button("Start", action: start)
// default:
// EmptyView()
// }
// }
// .task { await tunnel.update() }
// .padding()
}
// private func start() {
// try? tunnel.start()
// }
//
// private func stop() {
// tunnel.stop()
// }
//
// private func configure() {
// Task { try await tunnel.configure() }
// }
}

View file

@ -1,439 +0,0 @@
import XCTest
import UIKit
@MainActor
final class BurrowTailnetLoginUITests: XCTestCase {
private enum TailnetLoginMode: String, Decodable {
case tailscale
case discovered
}
private struct TestConfig: Decodable {
let email: String
let username: String
let password: String
let mode: TailnetLoginMode?
}
override func setUpWithError() throws {
continueAfterFailure = false
}
func testTailnetLoginThroughAuthentikWebSession() throws {
let config = try loadTestConfig()
let email = config.email
let username = config.username
let password = config.password
let mode = config.mode ?? .tailscale
let browserIdentity = mode == .tailscale ? email : username
let app = XCUIApplication()
app.launch()
let tailnetButton = app.buttons["quick-add-tailnet"]
XCTAssertTrue(tailnetButton.waitForExistence(timeout: 15), "Tailnet add button did not appear")
tailnetButton.tap()
configureTailnetIfNeeded(in: app, mode: mode)
let discoveryField = app.textFields["tailnet-discovery-email"]
XCTAssertTrue(discoveryField.waitForExistence(timeout: 10), "Tailnet discovery email field did not appear")
replaceText(in: discoveryField, with: email)
let serverCard = app.descendants(matching: .any)
.matching(identifier: "tailnet-server-card")
.firstMatch
XCTAssertTrue(serverCard.waitForExistence(timeout: 5), "Tailnet server card did not appear")
let signInButton = app.buttons["tailnet-start-sign-in"]
XCTAssertTrue(signInButton.waitForExistence(timeout: 10), "Tailnet sign-in button did not appear")
signInButton.tap()
acceptAuthenticationPromptIfNeeded(in: app, timeout: 20)
let webSession = webAuthenticationSession()
XCTAssertTrue(webSession.waitForExistence(timeout: 20), "Safari authentication session did not appear")
signIntoAuthentik(in: webSession, username: browserIdentity, password: password)
app.activate()
XCTAssertTrue(
waitForTailnetSignedIn(in: app, timeout: 60),
"Tailnet sign-in never reached the running state"
)
}
private func configureTailnetIfNeeded(in app: XCUIApplication, mode: TailnetLoginMode) {
guard mode == .discovered else { return }
openTailnetMenu(in: app)
tapMenuButton(named: "Edit Custom Server", in: app)
openTailnetMenu(in: app)
tapMenuButton(named: "Show Advanced Settings", in: app)
let authorityField = app.textFields["tailnet-authority"]
XCTAssertTrue(authorityField.waitForExistence(timeout: 10), "Tailnet authority field did not appear")
replaceText(in: authorityField, with: "")
}
private func openTailnetMenu(in app: XCUIApplication) {
let moreButton = app.buttons["More"]
XCTAssertTrue(moreButton.waitForExistence(timeout: 5), "Tailnet menu button did not appear")
moreButton.tap()
}
private func tapMenuButton(named title: String, in app: XCUIApplication) {
let menuButton = firstExistingElement(
from: [
app.buttons[title],
app.descendants(matching: .button)[title],
],
timeout: 5
)
XCTAssertTrue(menuButton.exists, "Menu action \(title) did not appear")
menuButton.tap()
}
private func acceptAuthenticationPromptIfNeeded(
in app: XCUIApplication,
timeout: TimeInterval
) {
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let deadline = Date().addingTimeInterval(timeout)
repeat {
let promptCandidates = [
springboard.buttons["Continue"],
springboard.buttons["Allow"],
app.buttons["Continue"],
app.buttons["Allow"],
]
for button in promptCandidates where button.exists && button.isHittable {
button.tap()
return
}
RunLoop.current.run(until: Date().addingTimeInterval(0.25))
} while Date() < deadline
let promptCandidates = [
springboard.buttons["Continue"],
springboard.buttons["Allow"],
app.buttons["Continue"],
app.buttons["Allow"],
]
for button in promptCandidates where button.exists {
button.tap()
return
}
}
private func webAuthenticationSession() -> XCUIApplication {
let safariViewService = XCUIApplication(bundleIdentifier: "com.apple.SafariViewService")
if safariViewService.waitForExistence(timeout: 5) {
return safariViewService
}
let safari = XCUIApplication(bundleIdentifier: "com.apple.mobilesafari")
_ = safari.waitForExistence(timeout: 5)
return safari
}
private func signIntoAuthentik(in webSession: XCUIApplication, username: String, password: String) {
followTailnetRedirectIfNeeded(in: webSession)
if !webSession.exists {
return
}
let immediatePasswordField = firstExistingSecureField(in: webSession, timeout: 2)
if immediatePasswordField.exists {
replaceSecureText(in: immediatePasswordField, within: webSession, with: password)
submitAuthenticationForm(in: webSession, focusedField: immediatePasswordField)
return
}
let usernameField = firstExistingElement(
in: webSession,
queries: [
{ $0.textFields["Username"] },
{ $0.textFields["Email or Username"] },
{ $0.textFields["Email address"] },
{ $0.textFields["Email"] },
{ $0.webViews.textFields["Username"] },
{ $0.webViews.textFields["Email or Username"] },
{ $0.descendants(matching: .textField).firstMatch },
],
timeout: 12
)
if !usernameField.exists {
return
}
replaceText(in: usernameField, with: username)
tapFirstExistingButton(
in: webSession,
titles: ["Continue", "Next", "Sign In", "Log in", "Login"],
timeout: 5
)
let passwordField = firstExistingSecureField(in: webSession, timeout: 20)
XCTAssertTrue(passwordField.exists, "Authentik password field did not appear")
replaceSecureText(in: passwordField, within: webSession, with: password)
submitAuthenticationForm(in: webSession, focusedField: passwordField)
}
private func followTailnetRedirectIfNeeded(in webSession: XCUIApplication) {
let redirectCandidates = [
webSession.links["Found"],
webSession.webViews.links["Found"],
webSession.buttons["Found"],
webSession.webViews.buttons["Found"],
]
let redirectLink = firstExistingElement(from: redirectCandidates, timeout: 8)
if redirectLink.exists {
redirectLink.tap()
}
}
private func firstExistingSecureField(in app: XCUIApplication, timeout: TimeInterval) -> XCUIElement {
let candidates = [
app.descendants(matching: .secureTextField).firstMatch,
app.secureTextFields["Password"],
app.secureTextFields["Password or Token"],
app.webViews.secureTextFields["Password"],
app.webViews.secureTextFields["Password or Token"],
]
return firstExistingElement(from: candidates, timeout: timeout)
}
private func tapFirstExistingButton(
in app: XCUIApplication,
titles: [String],
timeout: TimeInterval
) {
let candidates = titles.flatMap { title in
[
app.buttons[title],
app.webViews.buttons[title],
]
} + [app.descendants(matching: .button).firstMatch]
let button = firstExistingElement(from: candidates, timeout: timeout)
XCTAssertTrue(button.exists, "Expected one of \(titles.joined(separator: ", ")) to appear")
button.tap()
}
private func submitAuthenticationForm(in app: XCUIApplication, focusedField: XCUIElement) {
focus(focusedField)
focusedField.typeText("\n")
if waitForAny(
[
{ !focusedField.exists },
{ !app.staticTexts["Burrow Tailnet Authentication"].exists },
],
timeout: 1.5
) {
return
}
let keyboard = app.keyboards.firstMatch
if keyboard.waitForExistence(timeout: 2) {
let keyboardCandidates = [
"Return",
"return",
"Go",
"go",
"Continue",
"continue",
"Done",
"done",
"Join",
"join",
"Sign In",
"Log In",
"Login",
]
for title in keyboardCandidates {
let key = keyboard.buttons[title]
if key.exists && key.isHittable {
key.tap()
return
}
}
if let lastKey = keyboard.buttons.allElementsBoundByIndex.last,
lastKey.exists,
lastKey.isHittable
{
lastKey.tap()
return
}
}
tapFirstExistingButton(
in: app,
titles: ["Continue", "Sign In", "Log in", "Login"],
timeout: 5
)
}
private func loadTestConfig() throws -> TestConfig {
let environment = ProcessInfo.processInfo.environment
if let email = nonEmptyEnvironment("BURROW_UI_TEST_EMAIL"),
let password = nonEmptyEnvironment("BURROW_UI_TEST_PASSWORD")
{
return TestConfig(
email: email,
username: nonEmptyEnvironment("BURROW_UI_TEST_USERNAME") ?? email,
password: password,
mode: nonEmptyEnvironment("BURROW_UI_TEST_TAILNET_MODE")
.flatMap(TailnetLoginMode.init(rawValue:))
)
}
let configPath = environment["BURROW_UI_TEST_CONFIG_PATH"] ?? "/tmp/burrow-ui-test-config.json"
let configURL = URL(fileURLWithPath: configPath)
guard FileManager.default.fileExists(atPath: configURL.path) else {
throw XCTSkip(
"Missing UI test configuration. Expected env vars or config file at \(configURL.path)"
)
}
let data = try Data(contentsOf: configURL)
return try JSONDecoder().decode(TestConfig.self, from: data)
}
private func nonEmptyEnvironment(_ key: String) -> String? {
guard let value = ProcessInfo.processInfo.environment[key]?
.trimmingCharacters(in: .whitespacesAndNewlines),
!value.isEmpty
else {
return nil
}
return value
}
private func waitForFieldValue(
_ field: XCUIElement,
containing substring: String,
timeout: TimeInterval
) -> Bool {
let predicate = NSPredicate(format: "value CONTAINS %@", substring)
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: field)
return XCTWaiter.wait(for: [expectation], timeout: timeout) == .completed
}
private func waitForButtonLabel(
_ button: XCUIElement,
equals expected: String,
timeout: TimeInterval
) -> Bool {
let predicate = NSPredicate(format: "label == %@", expected)
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: button)
return XCTWaiter.wait(for: [expectation], timeout: timeout) == .completed
}
private func waitForTailnetSignedIn(in app: XCUIApplication, timeout: TimeInterval) -> Bool {
let button = app.buttons["tailnet-start-sign-in"]
let deadline = Date().addingTimeInterval(timeout)
repeat {
acceptAuthenticationPromptIfNeeded(in: app, timeout: 1)
if button.exists, button.label == "Signed In" {
return true
}
RunLoop.current.run(until: Date().addingTimeInterval(0.3))
} while Date() < deadline
return button.exists && button.label == "Signed In"
}
private func waitForAny(_ conditions: [() -> Bool], timeout: TimeInterval) -> Bool {
let deadline = Date().addingTimeInterval(timeout)
repeat {
if conditions.contains(where: { $0() }) {
return true
}
RunLoop.current.run(until: Date().addingTimeInterval(0.2))
} while Date() < deadline
return conditions.contains(where: { $0() })
}
private func firstExistingElement(
in app: XCUIApplication,
queries: [(XCUIApplication) -> XCUIElement],
timeout: TimeInterval
) -> XCUIElement {
firstExistingElement(from: queries.map { $0(app) }, timeout: timeout)
}
private func firstExistingElement(from candidates: [XCUIElement], timeout: TimeInterval) -> XCUIElement {
let deadline = Date().addingTimeInterval(timeout)
repeat {
for candidate in candidates where candidate.exists {
return candidate
}
RunLoop.current.run(until: Date().addingTimeInterval(0.2))
} while Date() < deadline
return candidates[0]
}
private func replaceText(in element: XCUIElement, with value: String) {
focus(element)
clearText(in: element)
element.typeText(value)
}
private func replaceSecureText(in element: XCUIElement, within app: XCUIApplication, with value: String) {
UIPasteboard.general.string = value
focus(element)
for revealMenu in [
{ element.doubleTap() },
{ element.press(forDuration: 1.2) },
] {
revealMenu()
let pasteButton = firstExistingElement(from: pasteCandidates(in: app), timeout: 3)
if pasteButton.exists {
pasteButton.tap()
return
}
}
focus(element)
element.typeText(value)
}
private func clearText(in element: XCUIElement) {
guard let currentValue = element.value as? String, !currentValue.isEmpty else {
return
}
let deleteSequence = String(repeating: XCUIKeyboardKey.delete.rawValue, count: currentValue.count)
element.typeText(deleteSequence)
}
private func focus(_ element: XCUIElement) {
element.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
RunLoop.current.run(until: Date().addingTimeInterval(0.3))
}
private func pasteCandidates(in app: XCUIApplication) -> [XCUIElement] {
let pasteLabels = ["Paste", "Incolla", "Paste from Clipboard"]
return pasteLabels.flatMap { label in
[
app.menuItems[label],
app.buttons[label],
app.webViews.buttons[label],
app.descendants(matching: .button).matching(NSPredicate(format: "label == %@", label)).firstMatch,
app.descendants(matching: .menuItem).matching(NSPredicate(format: "label == %@", label)).firstMatch,
]
}
}
}

View file

@ -7,59 +7,21 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
43AA26D82A10004900F14CE6 /* MenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AA26D72A10004900F14CE6 /* MenuView.swift */; };
D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00AA8962A4669BC005C8102 /* AppDelegate.swift */; }; D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00AA8962A4669BC005C8102 /* AppDelegate.swift */; };
D11000012F70000100112233 /* BurrowUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D11000042F70000100112233 /* BurrowUITests.swift */; };
D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */; }; D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */; };
D020F65D29E4A697002790F6 /* BurrowNetworkExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; D020F65D29E4A697002790F6 /* BurrowNetworkExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
D03383AD2C8E67E300F7C44E /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = D078F7E22C8DA375008A8CEC /* SwiftProtobuf */; };
D03383AE2C8E67E300F7C44E /* NIO in Frameworks */ = {isa = PBXBuildFile; productRef = D044EE902C8DAB2000778185 /* NIO */; };
D03383AF2C8E67E300F7C44E /* NIOConcurrencyHelpers in Frameworks */ = {isa = PBXBuildFile; productRef = D044EE922C8DAB2000778185 /* NIOConcurrencyHelpers */; };
D03383B02C8E67E300F7C44E /* NIOTransportServices in Frameworks */ = {isa = PBXBuildFile; productRef = D044EE952C8DAB2800778185 /* NIOTransportServices */; };
D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */; }; D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */; };
D09150422B9D2AF700BE3CB0 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D09150412B9D2AF700BE3CB0 /* MainMenu.xib */; platformFilters = (macos, ); }; D05B9F7829E39EEC008CB1F9 /* TunnelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7729E39EEC008CB1F9 /* TunnelView.swift */; };
D0A11C102F90000100112233 /* DequeModule in Frameworks */ = {isa = PBXBuildFile; productRef = D0A11C0F2F90000100112233 /* DequeModule */; }; D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D05B9F7929E39EED008CB1F9 /* Assets.xcassets */; };
D0B1D1102C436152004B7823 /* AsyncAlgorithms in Frameworks */ = {isa = PBXBuildFile; productRef = D0B1D10F2C436152004B7823 /* AsyncAlgorithms */; }; D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */; };
D0BCC5FF2A086E1C00AD070D /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BCC5FE2A086E1C00AD070D /* Status.swift */; };
D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B98FC629FDC5B5004E7149 /* Tunnel.swift */; };
D0BCC6092A09A03E00AD070D /* libburrow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCC6032A09535900AD070D /* libburrow.a */; }; D0BCC6092A09A03E00AD070D /* libburrow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCC6032A09535900AD070D /* libburrow.a */; };
D0BF09522C8E66F6000D8DEC /* BurrowConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */; }; D0BCC60A2A09A0B800AD070D /* build-rust.sh in Resources */ = {isa = PBXBuildFile; fileRef = D0B98FBF29FD8072004E7149 /* build-rust.sh */; };
D0BF09552C8E66FD000D8DEC /* BurrowConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */; };
D0D4E53A2C8D996F007F820A /* BurrowCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
D0D4E56B2C8D9C2F007F820A /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E49A2C8D921A007F820A /* Logging.swift */; };
D0D4E5702C8D9C62007F820A /* BurrowCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; };
D0D4E5722C8D9C6F007F820A /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E49E2C8D921A007F820A /* Network.swift */; };
D0D4E5732C8D9C6F007F820A /* WireGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E49F2C8D921A007F820A /* WireGuard.swift */; };
D0D4E5742C8D9C6F007F820A /* BurrowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A22C8D921A007F820A /* BurrowView.swift */; };
D0D4E5752C8D9C6F007F820A /* FloatingButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A32C8D921A007F820A /* FloatingButtonStyle.swift */; };
D0D4E5762C8D9C6F007F820A /* MenuItemToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A42C8D921A007F820A /* MenuItemToggleView.swift */; };
D0D4E5772C8D9C6F007F820A /* NetworkCarouselView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A52C8D921A007F820A /* NetworkCarouselView.swift */; };
D0D4E5782C8D9C6F007F820A /* NetworkExtension+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A62C8D921A007F820A /* NetworkExtension+Async.swift */; };
D0D4E5792C8D9C6F007F820A /* NetworkExtensionTunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A72C8D921A007F820A /* NetworkExtensionTunnel.swift */; };
D0D4E57A2C8D9C6F007F820A /* NetworkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4A82C8D921A007F820A /* NetworkView.swift */; };
D0D4E57C2C8D9C6F007F820A /* Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4AA2C8D921A007F820A /* Tunnel.swift */; };
D0D4E57D2C8D9C6F007F820A /* TunnelButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4AB2C8D921A007F820A /* TunnelButton.swift */; };
D0D4E57E2C8D9C6F007F820A /* TunnelStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4AC2C8D921A007F820A /* TunnelStatusView.swift */; };
D0D4E5812C8D9C94007F820A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D0D4E4A12C8D921A007F820A /* Assets.xcassets */; };
D0D4E5892C8D9C94007F820A /* BurrowUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5582C8D9BF2007F820A /* BurrowUI.framework */; };
D0D4E58A2C8D9C9E007F820A /* BurrowUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5582C8D9BF2007F820A /* BurrowUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
D0D4E58B2C8D9CA4007F820A /* BurrowConfiguration.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
D0D4E5922C8D9D15007F820A /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E58F2C8D9D0A007F820A /* Constants.swift */; };
D0D4E5A62C8D9E65007F820A /* BurrowCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; };
D0F4FAD32C8DC79C0068730A /* BurrowCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; };
D0F7594E2C8DAB6B00126CF3 /* GRPC in Frameworks */ = {isa = PBXBuildFile; productRef = D078F7E02C8DA375008A8CEC /* GRPC */; };
D0FA10012D10200100112233 /* burrow.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA10032D10200100112233 /* burrow.pb.swift */; };
D0FA10022D10200100112233 /* burrow.grpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA10042D10200100112233 /* burrow.grpc.swift */; };
D0F7597E2C8DB30500126CF3 /* CGRPCZlib in Frameworks */ = {isa = PBXBuildFile; productRef = D0F7597D2C8DB30500126CF3 /* CGRPCZlib */; };
D0F7598D2C8DB3DA00126CF3 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4992C8D921A007F820A /* Client.swift */; };
D0C0DE102FA0000100112233 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; platformFilters = (macos, ); productRef = D0C0DE122FA0000100112233 /* Sparkle */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
D11000022F70000100112233 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D05B9F7129E39EEC008CB1F9;
remoteInfo = App;
};
D020F65B29E4A697002790F6 /* PBXContainerItemProxy */ = { D020F65B29E4A697002790F6 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */; containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */;
@ -67,48 +29,6 @@
remoteGlobalIDString = D020F65229E4A697002790F6; remoteGlobalIDString = D020F65229E4A697002790F6;
remoteInfo = BurrowNetworkExtension; remoteInfo = BurrowNetworkExtension;
}; };
D0BF09502C8E66F1000D8DEC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D0D4E55A2C8D9BF4007F820A;
remoteInfo = Configuration;
};
D0BF09532C8E66FA000D8DEC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D0D4E55A2C8D9BF4007F820A;
remoteInfo = Configuration;
};
D0D4E56E2C8D9C5D007F820A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D0D4E5302C8D996F007F820A;
remoteInfo = Core;
};
D0D4E57F2C8D9C78007F820A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D0D4E5302C8D996F007F820A;
remoteInfo = Core;
};
D0D4E5872C8D9C88007F820A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D0D4E5502C8D9BF2007F820A;
remoteInfo = UI;
};
D0F4FAD12C8DC7960068730A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05B9F6A29E39EEC008CB1F9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D0D4E5302C8D996F007F820A;
remoteInfo = Core;
};
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
@ -123,27 +43,11 @@
name = "Embed Foundation Extensions"; name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
D0D4E53F2C8D996F007F820A /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
D0D4E58B2C8D9CA4007F820A /* BurrowConfiguration.framework in Embed Frameworks */,
D0D4E58A2C8D9C9E007F820A /* BurrowUI.framework in Embed Frameworks */,
D0D4E53A2C8D996F007F820A /* BurrowCore.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
D00117422B30348D00D87C25 /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Configuration.xcconfig; sourceTree = "<group>"; }; 43AA26D72A10004900F14CE6 /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = "<group>"; };
D00AA8962A4669BC005C8102 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; D00AA8962A4669BC005C8102 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
D11000032F70000100112233 /* BurrowUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BurrowUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D11000042F70000100112233 /* BurrowUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowUITests.swift; sourceTree = "<group>"; };
D11000052F70000100112233 /* UITests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = UITests.xcconfig; sourceTree = "<group>"; };
D020F63D29E4A1FF002790F6 /* Identity.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Identity.xcconfig; sourceTree = "<group>"; }; D020F63D29E4A1FF002790F6 /* Identity.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Identity.xcconfig; sourceTree = "<group>"; };
D020F64029E4A1FF002790F6 /* Compiler.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Compiler.xcconfig; sourceTree = "<group>"; }; D020F64029E4A1FF002790F6 /* Compiler.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Compiler.xcconfig; sourceTree = "<group>"; };
D020F64229E4A1FF002790F6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; D020F64229E4A1FF002790F6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -158,59 +62,25 @@
D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NetworkExtension-iOS.entitlements"; sourceTree = "<group>"; }; D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NetworkExtension-iOS.entitlements"; sourceTree = "<group>"; };
D020F66829E4AA74002790F6 /* App-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-iOS.entitlements"; sourceTree = "<group>"; }; D020F66829E4AA74002790F6 /* App-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-iOS.entitlements"; sourceTree = "<group>"; };
D020F66929E4AA74002790F6 /* App-macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-macOS.entitlements"; sourceTree = "<group>"; }; D020F66929E4AA74002790F6 /* App-macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-macOS.entitlements"; sourceTree = "<group>"; };
D04A3E1D2BAF465F0043EC85 /* Version.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = "<group>"; };
D05B9F7229E39EEC008CB1F9 /* Burrow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Burrow.app; sourceTree = BUILT_PRODUCTS_DIR; }; D05B9F7229E39EEC008CB1F9 /* Burrow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Burrow.app; sourceTree = BUILT_PRODUCTS_DIR; };
D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowApp.swift; sourceTree = "<group>"; }; D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowApp.swift; sourceTree = "<group>"; };
D09150412B9D2AF700BE3CB0 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; }; D05B9F7729E39EEC008CB1F9 /* TunnelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelView.swift; sourceTree = "<group>"; };
D05B9F7929E39EED008CB1F9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
D0B98FBF29FD8072004E7149 /* build-rust.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-rust.sh"; sourceTree = "<group>"; }; D0B98FBF29FD8072004E7149 /* build-rust.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-rust.sh"; sourceTree = "<group>"; };
D0B98FC629FDC5B5004E7149 /* Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tunnel.swift; sourceTree = "<group>"; };
D0B98FD829FDDB6F004E7149 /* libburrow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libburrow.h; sourceTree = "<group>"; }; D0B98FD829FDDB6F004E7149 /* libburrow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libburrow.h; sourceTree = "<group>"; };
D0B98FDC29FDDDCF004E7149 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; }; D0B98FDC29FDDDCF004E7149 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkExtension+Async.swift"; sourceTree = "<group>"; };
D0BCC5FE2A086E1C00AD070D /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = "<group>"; };
D0BCC6032A09535900AD070D /* libburrow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libburrow.a; sourceTree = BUILT_PRODUCTS_DIR; }; D0BCC6032A09535900AD070D /* libburrow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libburrow.a; sourceTree = BUILT_PRODUCTS_DIR; };
D0BF09582C8E6789000D8DEC /* UI.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = UI.xcconfig; sourceTree = "<group>"; };
D0D4E4952C8D921A007F820A /* burrow.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = burrow.proto; sourceTree = "<group>"; };
D0D4E4992C8D921A007F820A /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = "<group>"; };
D0D4E49A2C8D921A007F820A /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; };
D0D4E49E2C8D921A007F820A /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = "<group>"; };
D0D4E49F2C8D921A007F820A /* WireGuard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireGuard.swift; sourceTree = "<group>"; };
D0D4E4A12C8D921A007F820A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
D0D4E4A22C8D921A007F820A /* BurrowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowView.swift; sourceTree = "<group>"; };
D0D4E4A32C8D921A007F820A /* FloatingButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingButtonStyle.swift; sourceTree = "<group>"; };
D0D4E4A42C8D921A007F820A /* MenuItemToggleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemToggleView.swift; sourceTree = "<group>"; };
D0D4E4A52C8D921A007F820A /* NetworkCarouselView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkCarouselView.swift; sourceTree = "<group>"; };
D0D4E4A62C8D921A007F820A /* NetworkExtension+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkExtension+Async.swift"; sourceTree = "<group>"; };
D0D4E4A72C8D921A007F820A /* NetworkExtensionTunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkExtensionTunnel.swift; sourceTree = "<group>"; };
D0D4E4A82C8D921A007F820A /* NetworkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkView.swift; sourceTree = "<group>"; };
D0D4E4AA2C8D921A007F820A /* Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tunnel.swift; sourceTree = "<group>"; };
D0D4E4AB2C8D921A007F820A /* TunnelButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelButton.swift; sourceTree = "<group>"; };
D0D4E4AC2C8D921A007F820A /* TunnelStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatusView.swift; sourceTree = "<group>"; };
D0D4E4F62C8D932D007F820A /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
D0D4E4F72C8D941D007F820A /* Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Framework.xcconfig; sourceTree = "<group>"; };
D0D4E5312C8D996F007F820A /* BurrowCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BurrowCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D0D4E5582C8D9BF2007F820A /* BurrowUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BurrowUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BurrowConfiguration.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D0D4E58E2C8D9D0A007F820A /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = "<group>"; };
D0D4E58F2C8D9D0A007F820A /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
D0D4E5902C8D9D0A007F820A /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
D0FA10032D10200100112233 /* burrow.pb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generated/burrow.pb.swift; sourceTree = "<group>"; };
D0FA10042D10200100112233 /* burrow.grpc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generated/burrow.grpc.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
D11000062F70000100112233 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
D020F65029E4A697002790F6 /* Frameworks */ = { D020F65029E4A697002790F6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D0BF09522C8E66F6000D8DEC /* BurrowConfiguration.framework in Frameworks */,
D0D4E5A62C8D9E65007F820A /* BurrowCore.framework in Frameworks */,
D0BCC6092A09A03E00AD070D /* libburrow.a in Frameworks */, D0BCC6092A09A03E00AD070D /* libburrow.a in Frameworks */,
D0B1D1102C436152004B7823 /* AsyncAlgorithms in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -218,43 +88,18 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D0BF09552C8E66FD000D8DEC /* BurrowConfiguration.framework in Frameworks */,
D0F4FAD32C8DC79C0068730A /* BurrowCore.framework in Frameworks */,
D0D4E5892C8D9C94007F820A /* BurrowUI.framework in Frameworks */,
D0C0DE102FA0000100112233 /* Sparkle in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D078F7CF2C8DA213008A8CEC /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D03383B02C8E67E300F7C44E /* NIOTransportServices in Frameworks */,
D03383AF2C8E67E300F7C44E /* NIOConcurrencyHelpers in Frameworks */,
D03383AE2C8E67E300F7C44E /* NIO in Frameworks */,
D03383AD2C8E67E300F7C44E /* SwiftProtobuf in Frameworks */,
D0F7594E2C8DAB6B00126CF3 /* GRPC in Frameworks */,
D0F7597E2C8DB30500126CF3 /* CGRPCZlib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D0D4E5532C8D9BF2007F820A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D0A11C102F90000100112233 /* DequeModule in Frameworks */,
D0D4E5702C8D9C62007F820A /* BurrowCore.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
D00117432B30372900D87C25 /* Frameworks */ = { 43AA26D62A0FFFD000F14CE6 /* Menu */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
43AA26D72A10004900F14CE6 /* MenuView.swift */,
); );
name = Frameworks; path = Menu;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D020F63C29E4A1FF002790F6 /* Configuration */ = { D020F63C29E4A1FF002790F6 /* Configuration */ = {
@ -263,14 +108,8 @@
D020F63D29E4A1FF002790F6 /* Identity.xcconfig */, D020F63D29E4A1FF002790F6 /* Identity.xcconfig */,
D020F64A29E4A452002790F6 /* App.xcconfig */, D020F64A29E4A452002790F6 /* App.xcconfig */,
D020F66329E4A703002790F6 /* Extension.xcconfig */, D020F66329E4A703002790F6 /* Extension.xcconfig */,
D0D4E4F72C8D941D007F820A /* Framework.xcconfig */,
D020F64029E4A1FF002790F6 /* Compiler.xcconfig */, D020F64029E4A1FF002790F6 /* Compiler.xcconfig */,
D0D4E4F62C8D932D007F820A /* Debug.xcconfig */,
D11000052F70000100112233 /* UITests.xcconfig */,
D04A3E1D2BAF465F0043EC85 /* Version.xcconfig */,
D020F64229E4A1FF002790F6 /* Info.plist */, D020F64229E4A1FF002790F6 /* Info.plist */,
D0D4E5912C8D9D0A007F820A /* Constants */,
D00117422B30348D00D87C25 /* Configuration.xcconfig */,
); );
path = Configuration; path = Configuration;
sourceTree = "<group>"; sourceTree = "<group>";
@ -292,13 +131,9 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D05B9F7429E39EEC008CB1F9 /* App */, D05B9F7429E39EEC008CB1F9 /* App */,
D11000072F70000100112233 /* AppUITests */,
D020F65629E4A697002790F6 /* NetworkExtension */, D020F65629E4A697002790F6 /* NetworkExtension */,
D0D4E49C2C8D921A007F820A /* Core */,
D0D4E4AD2C8D921A007F820A /* UI */,
D020F63C29E4A1FF002790F6 /* Configuration */, D020F63C29E4A1FF002790F6 /* Configuration */,
D05B9F7329E39EEC008CB1F9 /* Products */, D05B9F7329E39EEC008CB1F9 /* Products */,
D00117432B30372900D87C25 /* Frameworks */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -306,12 +141,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D05B9F7229E39EEC008CB1F9 /* Burrow.app */, D05B9F7229E39EEC008CB1F9 /* Burrow.app */,
D11000032F70000100112233 /* BurrowUITests.xctest */,
D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */, D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */,
D0BCC6032A09535900AD070D /* libburrow.a */,
D0D4E5312C8D996F007F820A /* BurrowCore.framework */,
D0D4E5582C8D9BF2007F820A /* BurrowUI.framework */,
D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@ -319,9 +149,14 @@
D05B9F7429E39EEC008CB1F9 /* App */ = { D05B9F7429E39EEC008CB1F9 /* App */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
43AA26D62A0FFFD000F14CE6 /* Menu */,
D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */, D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */,
D00AA8962A4669BC005C8102 /* AppDelegate.swift */, D00AA8962A4669BC005C8102 /* AppDelegate.swift */,
D09150412B9D2AF700BE3CB0 /* MainMenu.xib */, D05B9F7729E39EEC008CB1F9 /* TunnelView.swift */,
D0B98FC629FDC5B5004E7149 /* Tunnel.swift */,
D0BCC5FE2A086E1C00AD070D /* Status.swift */,
D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */,
D05B9F7929E39EED008CB1F9 /* Assets.xcassets */,
D020F66829E4AA74002790F6 /* App-iOS.entitlements */, D020F66829E4AA74002790F6 /* App-iOS.entitlements */,
D020F66929E4AA74002790F6 /* App-macOS.entitlements */, D020F66929E4AA74002790F6 /* App-macOS.entitlements */,
D020F64929E4A34B002790F6 /* App.xcconfig */, D020F64929E4A34B002790F6 /* App.xcconfig */,
@ -329,104 +164,20 @@
path = App; path = App;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D11000072F70000100112233 /* AppUITests */ = {
isa = PBXGroup;
children = (
D11000042F70000100112233 /* BurrowUITests.swift */,
);
path = AppUITests;
sourceTree = "<group>";
};
D0B98FD729FDDB57004E7149 /* libburrow */ = { D0B98FD729FDDB57004E7149 /* libburrow */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D0B98FBF29FD8072004E7149 /* build-rust.sh */, D0B98FBF29FD8072004E7149 /* build-rust.sh */,
D0B98FDC29FDDDCF004E7149 /* module.modulemap */, D0B98FDC29FDDDCF004E7149 /* module.modulemap */,
D0B98FD829FDDB6F004E7149 /* libburrow.h */, D0B98FD829FDDB6F004E7149 /* libburrow.h */,
D0BCC6032A09535900AD070D /* libburrow.a */,
); );
path = libburrow; path = libburrow;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D0D4E4982C8D921A007F820A /* Client */ = {
isa = PBXGroup;
children = (
D0D4E4952C8D921A007F820A /* burrow.proto */,
D0FA10032D10200100112233 /* burrow.pb.swift */,
D0FA10042D10200100112233 /* burrow.grpc.swift */,
);
path = Client;
sourceTree = "<group>";
};
D0D4E49C2C8D921A007F820A /* Core */ = {
isa = PBXGroup;
children = (
D0D4E49A2C8D921A007F820A /* Logging.swift */,
D0D4E4992C8D921A007F820A /* Client.swift */,
D0D4E4982C8D921A007F820A /* Client */,
);
path = Core;
sourceTree = "<group>";
};
D0D4E4A02C8D921A007F820A /* Networks */ = {
isa = PBXGroup;
children = (
D0D4E49E2C8D921A007F820A /* Network.swift */,
D0D4E49F2C8D921A007F820A /* WireGuard.swift */,
);
path = Networks;
sourceTree = "<group>";
};
D0D4E4AD2C8D921A007F820A /* UI */ = {
isa = PBXGroup;
children = (
D0D4E4A22C8D921A007F820A /* BurrowView.swift */,
D0D4E4A02C8D921A007F820A /* Networks */,
D0D4E4A32C8D921A007F820A /* FloatingButtonStyle.swift */,
D0D4E4A42C8D921A007F820A /* MenuItemToggleView.swift */,
D0D4E4A52C8D921A007F820A /* NetworkCarouselView.swift */,
D0D4E4A62C8D921A007F820A /* NetworkExtension+Async.swift */,
D0D4E4A72C8D921A007F820A /* NetworkExtensionTunnel.swift */,
D0D4E4A82C8D921A007F820A /* NetworkView.swift */,
D0D4E4AA2C8D921A007F820A /* Tunnel.swift */,
D0D4E4AB2C8D921A007F820A /* TunnelButton.swift */,
D0D4E4AC2C8D921A007F820A /* TunnelStatusView.swift */,
D0D4E4A12C8D921A007F820A /* Assets.xcassets */,
D0BF09582C8E6789000D8DEC /* UI.xcconfig */,
);
path = UI;
sourceTree = "<group>";
};
D0D4E5912C8D9D0A007F820A /* Constants */ = {
isa = PBXGroup;
children = (
D0D4E58E2C8D9D0A007F820A /* Constants.h */,
D0D4E58F2C8D9D0A007F820A /* Constants.swift */,
D0D4E5902C8D9D0A007F820A /* module.modulemap */,
);
path = Constants;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
D11000082F70000100112233 /* BurrowUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = D110000E2F70000100112233 /* Build configuration list for PBXNativeTarget "BurrowUITests" */;
buildPhases = (
D110000A2F70000100112233 /* Sources */,
D11000062F70000100112233 /* Frameworks */,
D11000092F70000100112233 /* Resources */,
);
buildRules = (
);
dependencies = (
D110000B2F70000100112233 /* PBXTargetDependency */,
);
name = BurrowUITests;
productName = BurrowUITests;
productReference = D11000032F70000100112233 /* BurrowUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
D020F65229E4A697002790F6 /* NetworkExtension */ = { D020F65229E4A697002790F6 /* NetworkExtension */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = D020F65E29E4A697002790F6 /* Build configuration list for PBXNativeTarget "NetworkExtension" */; buildConfigurationList = D020F65E29E4A697002790F6 /* Build configuration list for PBXNativeTarget "NetworkExtension" */;
@ -434,12 +185,12 @@
D0BCC60B2A09A0C100AD070D /* Compile Rust */, D0BCC60B2A09A0C100AD070D /* Compile Rust */,
D020F64F29E4A697002790F6 /* Sources */, D020F64F29E4A697002790F6 /* Sources */,
D020F65029E4A697002790F6 /* Frameworks */, D020F65029E4A697002790F6 /* Frameworks */,
D020F65129E4A697002790F6 /* Resources */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
D0BF09512C8E66F1000D8DEC /* PBXTargetDependency */, D0BCC6122A0B328800AD070D /* PBXTargetDependency */,
D0D4E5802C8D9C78007F820A /* PBXTargetDependency */,
); );
name = NetworkExtension; name = NetworkExtension;
productName = BurrowNetworkExtension; productName = BurrowNetworkExtension;
@ -453,89 +204,19 @@
D05B9F6E29E39EEC008CB1F9 /* Sources */, D05B9F6E29E39EEC008CB1F9 /* Sources */,
D05B9F6F29E39EEC008CB1F9 /* Frameworks */, D05B9F6F29E39EEC008CB1F9 /* Frameworks */,
D05B9F7029E39EEC008CB1F9 /* Resources */, D05B9F7029E39EEC008CB1F9 /* Resources */,
D0D4E53F2C8D996F007F820A /* Embed Frameworks */,
D020F66129E4A697002790F6 /* Embed Foundation Extensions */, D020F66129E4A697002790F6 /* Embed Foundation Extensions */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
D0BF09542C8E66FA000D8DEC /* PBXTargetDependency */, D0BCC6142A0B329200AD070D /* PBXTargetDependency */,
D0F4FAD22C8DC7960068730A /* PBXTargetDependency */,
D0D4E5882C8D9C88007F820A /* PBXTargetDependency */,
D020F65C29E4A697002790F6 /* PBXTargetDependency */, D020F65C29E4A697002790F6 /* PBXTargetDependency */,
); );
name = App; name = App;
packageProductDependencies = (
D0C0DE122FA0000100112233 /* Sparkle */,
);
productName = Burrow; productName = Burrow;
productReference = D05B9F7229E39EEC008CB1F9 /* Burrow.app */; productReference = D05B9F7229E39EEC008CB1F9 /* Burrow.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
D0D4E5302C8D996F007F820A /* Core */ = {
isa = PBXNativeTarget;
buildConfigurationList = D0D4E53C2C8D996F007F820A /* Build configuration list for PBXNativeTarget "Core" */;
buildPhases = (
D0D4E52D2C8D996F007F820A /* Sources */,
D078F7CF2C8DA213008A8CEC /* Frameworks */,
);
buildRules = (
);
dependencies = (
D0F7598A2C8DB34200126CF3 /* PBXTargetDependency */,
);
name = Core;
packageProductDependencies = (
D078F7E02C8DA375008A8CEC /* GRPC */,
D078F7E22C8DA375008A8CEC /* SwiftProtobuf */,
D044EE902C8DAB2000778185 /* NIO */,
D044EE922C8DAB2000778185 /* NIOConcurrencyHelpers */,
D044EE952C8DAB2800778185 /* NIOTransportServices */,
D0F7597D2C8DB30500126CF3 /* CGRPCZlib */,
);
productName = Core;
productReference = D0D4E5312C8D996F007F820A /* BurrowCore.framework */;
productType = "com.apple.product-type.framework";
};
D0D4E5502C8D9BF2007F820A /* UI */ = {
isa = PBXNativeTarget;
buildConfigurationList = D0D4E5552C8D9BF2007F820A /* Build configuration list for PBXNativeTarget "UI" */;
buildPhases = (
D0D4E5522C8D9BF2007F820A /* Sources */,
D0D4E5532C8D9BF2007F820A /* Frameworks */,
D0D4E5542C8D9BF2007F820A /* Resources */,
);
buildRules = (
);
dependencies = (
D0D4E56F2C8D9C5D007F820A /* PBXTargetDependency */,
);
name = UI;
packageProductDependencies = (
D0A11C0F2F90000100112233 /* DequeModule */,
);
productName = Core;
productReference = D0D4E5582C8D9BF2007F820A /* BurrowUI.framework */;
productType = "com.apple.product-type.framework";
};
D0D4E55A2C8D9BF4007F820A /* Configuration */ = {
isa = PBXNativeTarget;
buildConfigurationList = D0D4E55F2C8D9BF4007F820A /* Build configuration list for PBXNativeTarget "Configuration" */;
buildPhases = (
D0F759912C8DB49E00126CF3 /* Configure Version */,
D0D4E55C2C8D9BF4007F820A /* Sources */,
);
buildRules = (
);
dependencies = (
);
name = Configuration;
packageProductDependencies = (
);
productName = Core;
productReference = D0D4E5622C8D9BF4007F820A /* BurrowConfiguration.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
/* Begin PBXProject section */ /* Begin PBXProject section */
@ -543,22 +224,15 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
BuildIndependentTargetsInParallel = 1; BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1600; LastSwiftUpdateCheck = 1430;
LastUpgradeCheck = 1520; LastUpgradeCheck = 1430;
TargetAttributes = { TargetAttributes = {
D11000082F70000100112233 = {
CreatedOnToolsVersion = 16.0;
TestTargetID = D05B9F7129E39EEC008CB1F9;
};
D020F65229E4A697002790F6 = { D020F65229E4A697002790F6 = {
CreatedOnToolsVersion = 14.3; CreatedOnToolsVersion = 14.3;
}; };
D05B9F7129E39EEC008CB1F9 = { D05B9F7129E39EEC008CB1F9 = {
CreatedOnToolsVersion = 14.3; CreatedOnToolsVersion = 14.3;
}; };
D0D4E5302C8D996F007F820A = {
CreatedOnToolsVersion = 16.0;
};
}; };
}; };
buildConfigurationList = D05B9F6D29E39EEC008CB1F9 /* Build configuration list for PBXProject "Burrow" */; buildConfigurationList = D05B9F6D29E39EEC008CB1F9 /* Build configuration list for PBXProject "Burrow" */;
@ -571,33 +245,23 @@
); );
mainGroup = D05B9F6929E39EEC008CB1F9; mainGroup = D05B9F6929E39EEC008CB1F9;
packageReferences = ( packageReferences = (
D0B1D10E2C436152004B7823 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */,
D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */,
D0D4E4852C8D8F29007F820A /* XCRemoteSwiftPackageReference "swift-protobuf" */,
D044EE8F2C8DAB2000778185 /* XCRemoteSwiftPackageReference "swift-nio" */,
D044EE942C8DAB2800778185 /* XCRemoteSwiftPackageReference "swift-nio-transport-services" */,
D0A11C0D2F90000100112233 /* XCRemoteSwiftPackageReference "swift-collections" */,
D0C0DE112FA0000100112233 /* XCRemoteSwiftPackageReference "Sparkle" */,
); );
productRefGroup = D05B9F7329E39EEC008CB1F9 /* Products */; productRefGroup = D05B9F7329E39EEC008CB1F9 /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
targets = ( targets = (
D05B9F7129E39EEC008CB1F9 /* App */, D05B9F7129E39EEC008CB1F9 /* App */,
D11000082F70000100112233 /* BurrowUITests */,
D020F65229E4A697002790F6 /* NetworkExtension */, D020F65229E4A697002790F6 /* NetworkExtension */,
D0D4E5502C8D9BF2007F820A /* UI */,
D0D4E5302C8D996F007F820A /* Core */,
D0D4E55A2C8D9BF4007F820A /* Configuration */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */ /* Begin PBXResourcesBuildPhase section */
D11000092F70000100112233 /* Resources */ = { D020F65129E4A697002790F6 /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D0BCC60A2A09A0B800AD070D /* build-rust.sh in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -605,15 +269,7 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D09150422B9D2AF700BE3CB0 /* MainMenu.xib in Resources */, D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */,
D0D4E5812C8D9C94007F820A /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D0D4E5542C8D9BF2007F820A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -640,39 +296,9 @@
shellScript = "\"${PROJECT_DIR}/NetworkExtension/libburrow/build-rust.sh\"\n"; shellScript = "\"${PROJECT_DIR}/NetworkExtension/libburrow/build-rust.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
D0F759912C8DB49E00126CF3 /* Configure Version */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"$(PROJECT_DIR)/../Tools/version.sh",
"$(PROJECT_DIR)/../.git",
);
name = "Configure Version";
outputFileListPaths = (
);
outputPaths = (
"$(PROJECT_DIR)/Configuration/Version.xcconfig",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"$PROJECT_DIR/../Tools/version.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
D110000A2F70000100112233 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D11000012F70000100112233 /* BurrowUITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D020F64F29E4A697002790F6 /* Sources */ = { D020F64F29E4A697002790F6 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -685,113 +311,35 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */,
43AA26D82A10004900F14CE6 /* MenuView.swift in Sources */,
D05B9F7829E39EEC008CB1F9 /* TunnelView.swift in Sources */,
D0BCC5FF2A086E1C00AD070D /* Status.swift in Sources */,
D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */, D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */,
D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */, D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */,
); D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */,
runOnlyForDeploymentPostprocessing = 0;
};
D0D4E52D2C8D996F007F820A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D0FA10012D10200100112233 /* burrow.pb.swift in Sources */,
D0FA10022D10200100112233 /* burrow.grpc.swift in Sources */,
D0F7598D2C8DB3DA00126CF3 /* Client.swift in Sources */,
D0D4E56B2C8D9C2F007F820A /* Logging.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D0D4E5522C8D9BF2007F820A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D0D4E5722C8D9C6F007F820A /* Network.swift in Sources */,
D0D4E5732C8D9C6F007F820A /* WireGuard.swift in Sources */,
D0D4E5742C8D9C6F007F820A /* BurrowView.swift in Sources */,
D0D4E5752C8D9C6F007F820A /* FloatingButtonStyle.swift in Sources */,
D0D4E5762C8D9C6F007F820A /* MenuItemToggleView.swift in Sources */,
D0D4E5772C8D9C6F007F820A /* NetworkCarouselView.swift in Sources */,
D0D4E5782C8D9C6F007F820A /* NetworkExtension+Async.swift in Sources */,
D0D4E5792C8D9C6F007F820A /* NetworkExtensionTunnel.swift in Sources */,
D0D4E57A2C8D9C6F007F820A /* NetworkView.swift in Sources */,
D0D4E57C2C8D9C6F007F820A /* Tunnel.swift in Sources */,
D0D4E57D2C8D9C6F007F820A /* TunnelButton.swift in Sources */,
D0D4E57E2C8D9C6F007F820A /* TunnelStatusView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D0D4E55C2C8D9BF4007F820A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D0D4E5922C8D9D15007F820A /* Constants.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */ /* Begin PBXTargetDependency section */
D110000B2F70000100112233 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D05B9F7129E39EEC008CB1F9 /* App */;
targetProxy = D11000022F70000100112233 /* PBXContainerItemProxy */;
};
D020F65C29E4A697002790F6 /* PBXTargetDependency */ = { D020F65C29E4A697002790F6 /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
target = D020F65229E4A697002790F6 /* NetworkExtension */; target = D020F65229E4A697002790F6 /* NetworkExtension */;
targetProxy = D020F65B29E4A697002790F6 /* PBXContainerItemProxy */; targetProxy = D020F65B29E4A697002790F6 /* PBXContainerItemProxy */;
}; };
D0BF09512C8E66F1000D8DEC /* PBXTargetDependency */ = { D0BCC6122A0B328800AD070D /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
target = D0D4E55A2C8D9BF4007F820A /* Configuration */; productRef = D0BCC6112A0B328800AD070D /* SwiftLintPlugin */;
targetProxy = D0BF09502C8E66F1000D8DEC /* PBXContainerItemProxy */;
}; };
D0BF09542C8E66FA000D8DEC /* PBXTargetDependency */ = { D0BCC6142A0B329200AD070D /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
target = D0D4E55A2C8D9BF4007F820A /* Configuration */; productRef = D0BCC6132A0B329200AD070D /* SwiftLintPlugin */;
targetProxy = D0BF09532C8E66FA000D8DEC /* PBXContainerItemProxy */;
};
D0D4E56F2C8D9C5D007F820A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D0D4E5302C8D996F007F820A /* Core */;
targetProxy = D0D4E56E2C8D9C5D007F820A /* PBXContainerItemProxy */;
};
D0D4E5802C8D9C78007F820A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D0D4E5302C8D996F007F820A /* Core */;
targetProxy = D0D4E57F2C8D9C78007F820A /* PBXContainerItemProxy */;
};
D0D4E5882C8D9C88007F820A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D0D4E5502C8D9BF2007F820A /* UI */;
targetProxy = D0D4E5872C8D9C88007F820A /* PBXContainerItemProxy */;
};
D0F4FAD22C8DC7960068730A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D0D4E5302C8D996F007F820A /* Core */;
targetProxy = D0F4FAD12C8DC7960068730A /* PBXContainerItemProxy */;
};
D0F7598A2C8DB34200126CF3 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = D0F759892C8DB34200126CF3 /* GRPC */;
}; };
/* End PBXTargetDependency section */ /* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
D110000C2F70000100112233 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D11000052F70000100112233 /* UITests.xcconfig */;
buildSettings = {
};
name = Debug;
};
D110000D2F70000100112233 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D11000052F70000100112233 /* UITests.xcconfig */;
buildSettings = {
};
name = Release;
};
D020F65F29E4A697002790F6 /* Debug */ = { D020F65F29E4A697002790F6 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = D020F66229E4A6E5002790F6 /* NetworkExtension.xcconfig */; baseConfigurationReference = D020F66229E4A6E5002790F6 /* NetworkExtension.xcconfig */;
@ -834,60 +382,9 @@
}; };
name = Release; name = Release;
}; };
D0D4E53D2C8D996F007F820A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D0D4E4F72C8D941D007F820A /* Framework.xcconfig */;
buildSettings = {
};
name = Debug;
};
D0D4E53E2C8D996F007F820A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D0D4E4F72C8D941D007F820A /* Framework.xcconfig */;
buildSettings = {
};
name = Release;
};
D0D4E5562C8D9BF2007F820A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D0BF09582C8E6789000D8DEC /* UI.xcconfig */;
buildSettings = {
};
name = Debug;
};
D0D4E5572C8D9BF2007F820A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D0BF09582C8E6789000D8DEC /* UI.xcconfig */;
buildSettings = {
};
name = Release;
};
D0D4E5602C8D9BF4007F820A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D00117422B30348D00D87C25 /* Configuration.xcconfig */;
buildSettings = {
};
name = Debug;
};
D0D4E5612C8D9BF4007F820A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D00117422B30348D00D87C25 /* Configuration.xcconfig */;
buildSettings = {
};
name = Release;
};
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
D110000E2F70000100112233 /* Build configuration list for PBXNativeTarget "BurrowUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D110000C2F70000100112233 /* Debug */,
D110000D2F70000100112233 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D020F65E29E4A697002790F6 /* Build configuration list for PBXNativeTarget "NetworkExtension" */ = { D020F65E29E4A697002790F6 /* Build configuration list for PBXNativeTarget "NetworkExtension" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
@ -915,144 +412,29 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
D0D4E53C2C8D996F007F820A /* Build configuration list for PBXNativeTarget "Core" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D0D4E53D2C8D996F007F820A /* Debug */,
D0D4E53E2C8D996F007F820A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D0D4E5552C8D9BF2007F820A /* Build configuration list for PBXNativeTarget "UI" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D0D4E5562C8D9BF2007F820A /* Debug */,
D0D4E5572C8D9BF2007F820A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D0D4E55F2C8D9BF4007F820A /* Build configuration list for PBXNativeTarget "Configuration" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D0D4E5602C8D9BF4007F820A /* Debug */,
D0D4E5612C8D9BF4007F820A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */ /* Begin XCRemoteSwiftPackageReference section */
D044EE8F2C8DAB2000778185 /* XCRemoteSwiftPackageReference "swift-nio" */ = { D0BCC6102A0B327700AD070D /* XCRemoteSwiftPackageReference "SwiftLint" */ = {
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-nio.git"; repositoryURL = "https://github.com/realm/SwiftLint.git";
requirement = { requirement = {
kind = upToNextMajorVersion; kind = upToNextMajorVersion;
minimumVersion = 2.72.0; minimumVersion = 0.51.0;
};
};
D044EE942C8DAB2800778185 /* XCRemoteSwiftPackageReference "swift-nio-transport-services" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-nio-transport-services.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.21.0;
};
};
D0A11C0D2F90000100112233 /* XCRemoteSwiftPackageReference "swift-collections" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-collections.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.1.3;
};
};
D0B1D10E2C436152004B7823 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-async-algorithms.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.0.1;
};
};
D0C0DE112FA0000100112233 /* XCRemoteSwiftPackageReference "Sparkle" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/sparkle-project/Sparkle.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.9.2;
};
};
D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/grpc/grpc-swift.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.23.0;
};
};
D0D4E4852C8D8F29007F820A /* XCRemoteSwiftPackageReference "swift-protobuf" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-protobuf.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.28.1;
}; };
}; };
/* End XCRemoteSwiftPackageReference section */ /* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */ /* Begin XCSwiftPackageProductDependency section */
D044EE902C8DAB2000778185 /* NIO */ = { D0BCC6112A0B328800AD070D /* SwiftLintPlugin */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = D044EE8F2C8DAB2000778185 /* XCRemoteSwiftPackageReference "swift-nio" */; package = D0BCC6102A0B327700AD070D /* XCRemoteSwiftPackageReference "SwiftLint" */;
productName = NIO; productName = "plugin:SwiftLintPlugin";
}; };
D044EE922C8DAB2000778185 /* NIOConcurrencyHelpers */ = { D0BCC6132A0B329200AD070D /* SwiftLintPlugin */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = D044EE8F2C8DAB2000778185 /* XCRemoteSwiftPackageReference "swift-nio" */; package = D0BCC6102A0B327700AD070D /* XCRemoteSwiftPackageReference "SwiftLint" */;
productName = NIOConcurrencyHelpers; productName = "plugin:SwiftLintPlugin";
};
D044EE952C8DAB2800778185 /* NIOTransportServices */ = {
isa = XCSwiftPackageProductDependency;
package = D044EE942C8DAB2800778185 /* XCRemoteSwiftPackageReference "swift-nio-transport-services" */;
productName = NIOTransportServices;
};
D078F7E02C8DA375008A8CEC /* GRPC */ = {
isa = XCSwiftPackageProductDependency;
package = D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */;
productName = GRPC;
};
D078F7E22C8DA375008A8CEC /* SwiftProtobuf */ = {
isa = XCSwiftPackageProductDependency;
package = D0D4E4852C8D8F29007F820A /* XCRemoteSwiftPackageReference "swift-protobuf" */;
productName = SwiftProtobuf;
};
D0A11C0F2F90000100112233 /* DequeModule */ = {
isa = XCSwiftPackageProductDependency;
package = D0A11C0D2F90000100112233 /* XCRemoteSwiftPackageReference "swift-collections" */;
productName = DequeModule;
};
D0B1D10F2C436152004B7823 /* AsyncAlgorithms */ = {
isa = XCSwiftPackageProductDependency;
package = D0B1D10E2C436152004B7823 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */;
productName = AsyncAlgorithms;
};
D0C0DE122FA0000100112233 /* Sparkle */ = {
isa = XCSwiftPackageProductDependency;
package = D0C0DE112FA0000100112233 /* XCRemoteSwiftPackageReference "Sparkle" */;
productName = Sparkle;
};
D0F7597D2C8DB30500126CF3 /* CGRPCZlib */ = {
isa = XCSwiftPackageProductDependency;
package = D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */;
productName = CGRPCZlib;
};
D0F759892C8DB34200126CF3 /* GRPC */ = {
isa = XCSwiftPackageProductDependency;
package = D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */;
productName = GRPC;
}; };
/* End XCSwiftPackageProductDependency section */ /* End XCSwiftPackageProductDependency section */
}; };

View file

@ -1,132 +0,0 @@
{
"originHash" : "5d81b59cbecacd7aae6aa598b32e3513c636250dadab9280a55b905bb0e0ee60",
"pins" : [
{
"identity" : "grpc-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/grpc/grpc-swift.git",
"state" : {
"revision" : "6a90b7e77e29f9bda6c2b3a4165a40d6c02cfda1",
"version" : "1.23.0"
}
},
{
"identity" : "sparkle",
"kind" : "remoteSourceControl",
"location" : "https://github.com/sparkle-project/Sparkle.git",
"state" : {
"revision" : "6276ba2b404829d139c45ff98427cf90e2efc59b",
"version" : "2.9.2"
}
},
{
"identity" : "swift-async-algorithms",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-async-algorithms.git",
"state" : {
"revision" : "6ae9a051f76b81cc668305ceed5b0e0a7fd93d20",
"version" : "1.0.1"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
"version" : "1.2.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "9bf03ff58ce34478e66aaee630e491823326fd06",
"version" : "1.1.3"
}
},
{
"identity" : "swift-http-types",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-http-types",
"state" : {
"revision" : "ae67c8178eb46944fd85e4dc6dd970e1f3ed6ccd",
"version" : "1.3.0"
}
},
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "9cb486020ebf03bfa5b5df985387a14a98744537",
"version" : "1.6.1"
}
},
{
"identity" : "swift-nio",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "9746cf80e29edfef2a39924a66731249223f42a3",
"version" : "2.72.0"
}
},
{
"identity" : "swift-nio-extras",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-extras.git",
"state" : {
"revision" : "d1ead62745cc3269e482f1c51f27608057174379",
"version" : "1.24.0"
}
},
{
"identity" : "swift-nio-http2",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-http2.git",
"state" : {
"revision" : "b5f7062b60e4add1e8c343ba4eb8da2e324b3a94",
"version" : "1.34.0"
}
},
{
"identity" : "swift-nio-ssl",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-ssl.git",
"state" : {
"revision" : "7b84abbdcef69cc3be6573ac12440220789dcd69",
"version" : "2.27.2"
}
},
{
"identity" : "swift-nio-transport-services",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-transport-services.git",
"state" : {
"revision" : "38ac8221dd20674682148d6451367f89c2652980",
"version" : "1.21.0"
}
},
{
"identity" : "swift-protobuf",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "edb6ed4919f7756157fe02f2552b7e3850a538e5",
"version" : "1.28.1"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "d2ba781702a1d8285419c15ee62fd734a9437ff5",
"version" : "1.3.2"
}
}
],
"version" : 3
}

View file

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1600" LastUpgradeVersion = "1430"
version = "1.7"> version = "1.7">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES" buildImplicitDependencies = "YES">
buildArchitectures = "Automatic">
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"
@ -28,20 +27,7 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES" shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "NO"> shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D11000082F70000100112233"
BuildableName = "BurrowUITests.xctest"
BlueprintName = "BurrowUITests"
ReferencedContainer = "container:Burrow.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"

View file

@ -1,12 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1600" LastUpgradeVersion = "1430"
wasCreatedForAppExtension = "YES"
version = "2.0"> version = "2.0">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES" buildImplicitDependencies = "YES">
buildArchitectures = "Automatic">
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"

View file

@ -1,41 +0,0 @@
# Apple Certificates
Public Apple certificate material that can be inspected by release tooling lives
here. Private keys do not belong in this directory.
## Developer ID Application
- Apple certificate id: `9JKN6HXBHC`
- Certificate file: `Apple/Certificates/developer-id-application-9JKN6HXBHC.cer`
- Subject: `Developer ID Application: Conrad Kramer (87PW93R2ZR)`
- Issuer: `Developer ID Certification Authority`, `G2`
- Team id: `87PW93R2ZR`
- Validity: `2026-06-07T12:13:47Z` through `2031-06-08T12:13:46Z`
- SHA-256 fingerprint: `7B:2D:58:A5:BB:1E:34:5E:FB:FF:7F:E0:A1:CA:4F:B1:5B:6E:08:AF:CE:FD:BE:D0:2B:0A:E9:9C:82:E3:4F:74`
- Google KMS key:
`projects/project-88c23ce9-918a-470a-b33/locations/global/keyRings/burrow-identity/cryptoKeys/apple-developer-id-application/cryptoKeyVersions/1`
- KMS public-key SHA-256:
`9cfaf4755158f445f20a215ef52e7a7ba00da1a310e2e934ba7db5dbb29d2869`
The certificate was created from a CSR signed by the non-exportable Google KMS
key above. There is no `.p12` for this identity. Release signing must use a
signer that delegates the relevant Apple code-signing operations to Google KMS.
## iOS Distribution
- Apple certificate id: `3G42677598`
- Certificate file: `Apple/Certificates/ios-distribution-3G42677598.cer`
- Subject: `iPhone Distribution: Conrad Kramer (87PW93R2ZR)`
- Issuer: `Apple Worldwide Developer Relations Certification Authority`, `G3`
- Team id: `87PW93R2ZR`
- Validity: `2026-06-07T12:21:32Z` through `2027-06-07T12:21:31Z`
- SHA-256 fingerprint: `5A:2F:BA:6E:FB:CA:2D:58:01:1F:A0:C8:0F:CF:6B:58:BE:AA:73:7F:4E:F8:06:1C:8A:14:1B:22:8A:2A:29:2B`
- Google KMS key:
`projects/project-88c23ce9-918a-470a-b33/locations/global/keyRings/burrow-identity/cryptoKeys/apple-ios-distribution/cryptoKeyVersions/1`
- KMS public-key SHA-256:
`f8613ce059de90b29acf01cbd4a7deb37567653bd154bdf6c447098b5c4fae91`
The certificate was created from a CSR signed by the non-exportable Google KMS
key above through App Store Connect certificate creation. There is no `.p12`
for this identity. iOS signing requires a signing path that can delegate Apple
code-signing operations to Google KMS.

View file

@ -1,8 +1,10 @@
LD_EXPORT_SYMBOLS = NO
SKIP_INSTALL = NO SKIP_INSTALL = NO
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks
LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor
ENABLE_PREVIEWS = YES

View file

@ -1,43 +1,61 @@
#include "Identity.xcconfig" #include "Identity.xcconfig"
#include "Debug.xcconfig"
#include "Version.xcconfig"
SDKROOT = auto SDKROOT = auto
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES
SUPPORTED_PLATFORMS = iphoneos iphonesimulator macosx SUPPORTED_PLATFORMS = iphoneos iphonesimulator macosx
SWIFT_VERSION = 6.0 IPHONEOS_DEPLOYMENT_TARGET = 15.0
IPHONEOS_DEPLOYMENT_TARGET = 17.0 MACOSX_DEPLOYMENT_TARGET = 12.0
MACOSX_DEPLOYMENT_TARGET = 14.0
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO
SUPPORTS_MACCATALYST = NO SUPPORTS_MACCATALYST = NO
ALWAYS_SEARCH_USER_PATHS = NO
PRODUCT_NAME = $(TARGET_NAME:c99extidentifier) PRODUCT_NAME = $(TARGET_NAME:c99extidentifier)
PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).$(PRODUCT_NAME) PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).$(PRODUCT_NAME)
CURRENT_PROJECT_VERSION = 1
MARKETING_VERSION = 0.1 MARKETING_VERSION = 0.1
SKIP_INSTALL = YES SKIP_INSTALL = YES
CODE_SIGN_IDENTITY = Apple Development CODE_SIGN_IDENTITY = Apple Development
GENERATE_INFOPLIST_FILE = YES
INFOPLIST_FILE = Configuration/Info.plist INFOPLIST_FILE = Configuration/Info.plist
INFOPLIST_KEY_NSHumanReadableCopyright = Copyright © 2023-2024 Hack Club GENERATE_INFOPLIST_FILE = YES
INFOPLIST_KEY_NSHumanReadableCopyright = Copyright © 2023 Hack Club
INFOPLIST_KEY_CFBundleDisplayName = Burrow INFOPLIST_KEY_CFBundleDisplayName = Burrow
ENABLE_APP_SANDBOX[sdk=macosx*] = YES
ENABLE_BITCODE = NO ENABLE_BITCODE = NO
ALWAYS_SEARCH_USER_PATHS = NO
ENABLE_APP_SANDBOX[sdk=macosx*] = YES
ENABLE_HARDENED_RUNTIME[sdk=macosx*] = YES
COMBINE_HIDPI_IMAGES = YES COMBINE_HIDPI_IMAGES = YES
EAGER_LINKING = YES COPY_PHASE_STRIP = NO
FUSE_BUILD_SCRIPT_PHASES = YES FUSE_BUILD_SCRIPT_PHASES = YES
SWIFT_EMIT_LOC_STRINGS = YES
LOCALIZATION_PREFERS_STRING_CATALOGS = YES
ENABLE_DEBUG_DYLIB = NO
APP_GROUP_IDENTIFIER = group.$(APP_BUNDLE_IDENTIFIER) APP_GROUP_IDENTIFIER = group.$(APP_BUNDLE_IDENTIFIER)
APP_GROUP_IDENTIFIER[sdk=macosx*] = $(DEVELOPMENT_TEAM).$(APP_BUNDLE_IDENTIFIER) APP_GROUP_IDENTIFIER[sdk=macosx*] = $(DEVELOPMENT_TEAM).$(APP_BUNDLE_IDENTIFIER)
NETWORK_EXTENSION_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).network NETWORK_EXTENSION_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).network
OTHER_SWIFT_FLAGS = $(inherited) // Swift
SWIFT_VERSION = 5.0
SWIFT_EMIT_LOC_STRINGS = YES
// Release
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
SWIFT_COMPILATION_MODE = wholemodule
SWIFT_OPTIMIZATION_LEVEL = -Osize
LLVM_LTO = YES
DEAD_CODE_STRIPPING = YES
VALIDATE_PRODUCT = YES
// Debug
ONLY_ACTIVE_ARCH[config=Debug] = YES
DEBUG_INFORMATION_FORMAT[config=Debug] = dwarf
ENABLE_TESTABILITY[config=Debug] = YES
SWIFT_OPTIMIZATION_LEVEL[config=Debug] = -Onone
SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Debug] = DEBUG
SWIFT_COMPILATION_MODE[config=Debug] = singlefile
LLVM_LTO[config=Debug] = NO
DEAD_CODE_STRIPPING[config=Debug] = NO
VALIDATE_PRODUCT[config=Debug] = NO

View file

@ -1,4 +0,0 @@
#include "Framework.xcconfig"
SWIFT_INCLUDE_PATHS = $(PROJECT_DIR)/Configuration/Constants
GCC_PREPROCESSOR_DEFINITIONS = APP_BUNDLE_IDENTIFIER=$(APP_BUNDLE_IDENTIFIER) APP_GROUP_IDENTIFIER=$(APP_GROUP_IDENTIFIER) NETWORK_EXTENSION_BUNDLE_IDENTIFIER=$(NETWORK_EXTENSION_BUNDLE_IDENTIFIER)

View file

@ -1,12 +0,0 @@
#import <Foundation/Foundation.h>
#define MACRO_STRING_(m) #m
#define MACRO_STRING(m) @MACRO_STRING_(m)
NS_ASSUME_NONNULL_BEGIN
static NSString * const AppBundleIdentifier = MACRO_STRING(APP_BUNDLE_IDENTIFIER);
static NSString * const AppGroupIdentifier = MACRO_STRING(APP_GROUP_IDENTIFIER);
static NSString * const NetworkExtensionBundleIdentifier = MACRO_STRING(NETWORK_EXTENSION_BUNDLE_IDENTIFIER);
NS_ASSUME_NONNULL_END

View file

@ -1,56 +0,0 @@
@_implementationOnly import CConstants
import Foundation
import OSLog
public enum Constants {
enum Error: Swift.Error {
case invalidAppGroupIdentifier
}
public static let bundleIdentifier = AppBundleIdentifier
public static let appGroupIdentifier = AppGroupIdentifier
public static let networkExtensionBundleIdentifier = NetworkExtensionBundleIdentifier
public static var socketURL: URL {
get throws {
try groupContainerURL.appending(component: "burrow.sock", directoryHint: .notDirectory)
}
}
public static var databaseURL: URL {
get throws {
try groupContainerURL.appending(component: "burrow.db", directoryHint: .notDirectory)
}
}
private static var groupContainerURL: URL {
get throws { try _groupContainerURL.get() }
}
private static let _groupContainerURL: Result<URL, Error> = {
switch FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier) {
case .some(let url): .success(url)
case .none:
fallbackContainerURL().mapError { _ in .invalidAppGroupIdentifier }
}
}()
private static func fallbackContainerURL() -> Result<URL, any Swift.Error> {
#if targetEnvironment(simulator)
Result {
// The simulator app's Application Support path lives inside its sandbox container,
// so the host daemon cannot reach it. Use a shared host temp location instead.
let url = URL(filePath: "/tmp", directoryHint: .isDirectory)
.appending(component: bundleIdentifier, directoryHint: .isDirectory)
.appending(component: "SimulatorFallback", directoryHint: .isDirectory)
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
return url
}
#else
.failure(Error.invalidAppGroupIdentifier)
#endif
}
}
extension Logger {
@_dynamicReplacement(for: subsystem)
public static var subsystem: String { Constants.bundleIdentifier }
}

View file

@ -1,4 +0,0 @@
module CConstants {
header "Constants.h"
export *
}

View file

@ -1,26 +0,0 @@
// Release
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
SWIFT_COMPILATION_MODE = wholemodule
SWIFT_OPTIMIZATION_LEVEL = -Osize
LLVM_LTO = YES
DEAD_CODE_STRIPPING = YES
STRIP_INSTALLED_PRODUCT = YES
STRIP_SWIFT_SYMBOLS = YES
COPY_PHASE_STRIP = NO
VALIDATE_PRODUCT = YES
ENABLE_MODULE_VERIFIER = YES
// Debug
ONLY_ACTIVE_ARCH[config=Debug] = YES
DEBUG_INFORMATION_FORMAT[config=Debug] = dwarf
ENABLE_TESTABILITY[config=Debug] = YES
GCC_PREPROCESSOR_DEFINITIONS[config=Debug] = DEBUG=1 $(inherited)
SWIFT_OPTIMIZATION_LEVEL[config=Debug] = -Onone
SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Debug] = DEBUG
SWIFT_COMPILATION_MODE[config=Debug] = singlefile
LLVM_LTO[config=Debug] = NO
DEAD_CODE_STRIPPING[config=Debug] = NO
VALIDATE_PRODUCT[config=Debug] = NO
STRIP_INSTALLED_PRODUCT[config=Debug] = NO
STRIP_SWIFT_SYMBOLS[config=Debug] = NO
ENABLE_MODULE_VERIFIER[config=Debug] = NO

View file

@ -1,6 +1,2 @@
LD_EXPORT_SYMBOLS = NO
OTHER_SWIFT_FLAGS = $(inherited) -Xfrontend -disable-autolink-framework -Xfrontend UIKit -Xfrontend -disable-autolink-framework -Xfrontend AppKit -Xfrontend -disable-autolink-framework -Xfrontend SwiftUI
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @executable_path/../../Frameworks LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @executable_path/../../Frameworks
LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks LD_RUNPATH_SEARCH_PATHS[sdk=macos*] = $(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks

View file

@ -1,14 +0,0 @@
PRODUCT_NAME = Burrow$(TARGET_NAME:c99extidentifier)
PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).$(TARGET_NAME:c99extidentifier)
APPLICATION_EXTENSION_API_ONLY = YES
SWIFT_INSTALL_OBJC_HEADER = NO
SWIFT_SKIP_AUTOLINKING_FRAMEWORKS = YES
SWIFT_SKIP_AUTOLINKING_LIBRARIES = YES
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks
LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @loader_path/Frameworks
DYLIB_INSTALL_NAME_BASE = @rpath
DYLIB_COMPATIBILITY_VERSION = 1
DYLIB_CURRENT_VERSION = 1
VERSIONING_SYSTEM =

View file

@ -4,9 +4,5 @@
<dict> <dict>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>$(INFOPLIST_KEY_CFBundleDisplayName)</string> <string>$(INFOPLIST_KEY_CFBundleDisplayName)</string>
<key>SUFeedURL</key>
<string>$(BURROW_SPARKLE_FEED_URL)</string>
<key>SUPublicEDKey</key>
<string>$(BURROW_SPARKLE_PUBLIC_ED_KEY)</string>
</dict> </dict>
</plist> </plist>

View file

@ -1,14 +0,0 @@
#include "Compiler.xcconfig"
SUPPORTED_PLATFORMS = iphonesimulator iphoneos
TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2
PRODUCT_NAME = $(TARGET_NAME)
PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).uitests
STRING_CATALOG_GENERATE_SYMBOLS = NO
SWIFT_EMIT_LOC_STRINGS = NO
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks
TEST_TARGET_NAME = App

View file

@ -1,508 +0,0 @@
import Foundation
import GRPC
import NIOTransportServices
import SwiftProtobuf
public typealias TunnelClient = Burrow_TunnelAsyncClient
public typealias NetworksClient = Burrow_NetworksAsyncClient
public protocol Client {
init(channel: GRPCChannel)
}
extension Client {
public static func unix(socketURL: URL) -> Self {
let group = NIOTSEventLoopGroup()
let configuration = ClientConnection.Configuration.default(
target: .unixDomainSocket(socketURL.path),
eventLoopGroup: group
)
return Self(channel: ClientConnection(configuration: configuration))
}
}
extension TunnelClient: Client {
public init(channel: any GRPCChannel) {
self.init(channel: channel, defaultCallOptions: .init(), interceptors: .none)
}
}
extension NetworksClient: Client {
public init(channel: any GRPCChannel) {
self.init(channel: channel, defaultCallOptions: .init(), interceptors: .none)
}
}
public struct Burrow_TailnetDiscoverRequest: Sendable {
public var email: String = ""
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_TailnetDiscoverResponse: Sendable {
public var domain: String = ""
public var authority: String = ""
public var oidcIssuer: String = ""
public var managed: Bool = false
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_TailnetProbeRequest: Sendable {
public var authority: String = ""
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_TailnetProbeResponse: Sendable {
public var authority: String = ""
public var statusCode: Int32 = 0
public var summary: String = ""
public var detail: String = ""
public var reachable: Bool = false
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_TailnetLoginStartRequest: Sendable {
public var accountName: String = ""
public var identityName: String = ""
public var hostname: String = ""
public var authority: String = ""
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_TailnetLoginStatusRequest: Sendable {
public var sessionID: String = ""
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_TailnetLoginCancelRequest: Sendable {
public var sessionID: String = ""
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_TailnetLoginStatusResponse: Sendable {
public var sessionID: String = ""
public var backendState: String = ""
public var authURL: String = ""
public var running: Bool = false
public var needsLogin: Bool = false
public var tailnetName: String = ""
public var magicDNSSuffix: String = ""
public var selfDNSName: String = ""
public var tailnetIPs: [String] = []
public var health: [String] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_TunnelPacket: Sendable {
public var payload = Data()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
extension Burrow_TailnetDiscoverRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.TailnetDiscoverRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "email")
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.email)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.email.isEmpty {
try visitor.visitSingularStringField(value: self.email, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_TailnetDiscoverResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.TailnetDiscoverResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "domain"),
2: .same(proto: "authority"),
3: .same(proto: "oidc_issuer"),
4: .same(proto: "managed"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.domain)
case 2: try decoder.decodeSingularStringField(value: &self.authority)
case 3: try decoder.decodeSingularStringField(value: &self.oidcIssuer)
case 4: try decoder.decodeSingularBoolField(value: &self.managed)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.domain.isEmpty {
try visitor.visitSingularStringField(value: self.domain, fieldNumber: 1)
}
if !self.authority.isEmpty {
try visitor.visitSingularStringField(value: self.authority, fieldNumber: 2)
}
if !self.oidcIssuer.isEmpty {
try visitor.visitSingularStringField(value: self.oidcIssuer, fieldNumber: 3)
}
if self.managed {
try visitor.visitSingularBoolField(value: self.managed, fieldNumber: 4)
}
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_TailnetProbeRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.TailnetProbeRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "authority")
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.authority)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.authority.isEmpty {
try visitor.visitSingularStringField(value: self.authority, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_TailnetProbeResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.TailnetProbeResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "authority"),
2: .same(proto: "status_code"),
3: .same(proto: "summary"),
4: .same(proto: "detail"),
5: .same(proto: "reachable"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.authority)
case 2: try decoder.decodeSingularInt32Field(value: &self.statusCode)
case 3: try decoder.decodeSingularStringField(value: &self.summary)
case 4: try decoder.decodeSingularStringField(value: &self.detail)
case 5: try decoder.decodeSingularBoolField(value: &self.reachable)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.authority.isEmpty {
try visitor.visitSingularStringField(value: self.authority, fieldNumber: 1)
}
if self.statusCode != 0 {
try visitor.visitSingularInt32Field(value: self.statusCode, fieldNumber: 2)
}
if !self.summary.isEmpty {
try visitor.visitSingularStringField(value: self.summary, fieldNumber: 3)
}
if !self.detail.isEmpty {
try visitor.visitSingularStringField(value: self.detail, fieldNumber: 4)
}
if self.reachable {
try visitor.visitSingularBoolField(value: self.reachable, fieldNumber: 5)
}
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_TailnetLoginStartRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.TailnetLoginStartRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "account_name"),
2: .standard(proto: "identity_name"),
3: .same(proto: "hostname"),
4: .same(proto: "authority"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.accountName)
case 2: try decoder.decodeSingularStringField(value: &self.identityName)
case 3: try decoder.decodeSingularStringField(value: &self.hostname)
case 4: try decoder.decodeSingularStringField(value: &self.authority)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.accountName.isEmpty {
try visitor.visitSingularStringField(value: self.accountName, fieldNumber: 1)
}
if !self.identityName.isEmpty {
try visitor.visitSingularStringField(value: self.identityName, fieldNumber: 2)
}
if !self.hostname.isEmpty {
try visitor.visitSingularStringField(value: self.hostname, fieldNumber: 3)
}
if !self.authority.isEmpty {
try visitor.visitSingularStringField(value: self.authority, fieldNumber: 4)
}
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_TailnetLoginStatusRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.TailnetLoginStatusRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "session_id")
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.sessionID)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.sessionID.isEmpty {
try visitor.visitSingularStringField(value: self.sessionID, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_TailnetLoginCancelRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.TailnetLoginCancelRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "session_id")
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.sessionID)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.sessionID.isEmpty {
try visitor.visitSingularStringField(value: self.sessionID, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_TailnetLoginStatusResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.TailnetLoginStatusResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "session_id"),
2: .standard(proto: "backend_state"),
3: .standard(proto: "auth_url"),
4: .same(proto: "running"),
5: .standard(proto: "needs_login"),
6: .standard(proto: "tailnet_name"),
7: .standard(proto: "magic_dns_suffix"),
8: .standard(proto: "self_dns_name"),
9: .standard(proto: "tailnet_ips"),
10: .same(proto: "health"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.sessionID)
case 2: try decoder.decodeSingularStringField(value: &self.backendState)
case 3: try decoder.decodeSingularStringField(value: &self.authURL)
case 4: try decoder.decodeSingularBoolField(value: &self.running)
case 5: try decoder.decodeSingularBoolField(value: &self.needsLogin)
case 6: try decoder.decodeSingularStringField(value: &self.tailnetName)
case 7: try decoder.decodeSingularStringField(value: &self.magicDNSSuffix)
case 8: try decoder.decodeSingularStringField(value: &self.selfDNSName)
case 9: try decoder.decodeRepeatedStringField(value: &self.tailnetIPs)
case 10: try decoder.decodeRepeatedStringField(value: &self.health)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.sessionID.isEmpty {
try visitor.visitSingularStringField(value: self.sessionID, fieldNumber: 1)
}
if !self.backendState.isEmpty {
try visitor.visitSingularStringField(value: self.backendState, fieldNumber: 2)
}
if !self.authURL.isEmpty {
try visitor.visitSingularStringField(value: self.authURL, fieldNumber: 3)
}
if self.running {
try visitor.visitSingularBoolField(value: self.running, fieldNumber: 4)
}
if self.needsLogin {
try visitor.visitSingularBoolField(value: self.needsLogin, fieldNumber: 5)
}
if !self.tailnetName.isEmpty {
try visitor.visitSingularStringField(value: self.tailnetName, fieldNumber: 6)
}
if !self.magicDNSSuffix.isEmpty {
try visitor.visitSingularStringField(value: self.magicDNSSuffix, fieldNumber: 7)
}
if !self.selfDNSName.isEmpty {
try visitor.visitSingularStringField(value: self.selfDNSName, fieldNumber: 8)
}
if !self.tailnetIPs.isEmpty {
try visitor.visitRepeatedStringField(value: self.tailnetIPs, fieldNumber: 9)
}
if !self.health.isEmpty {
try visitor.visitRepeatedStringField(value: self.health, fieldNumber: 10)
}
try unknownFields.traverse(visitor: &visitor)
}
}
extension Burrow_TunnelPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = "burrow.TunnelPacket"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "payload")
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularBytesField(value: &self.payload)
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.payload.isEmpty {
try visitor.visitSingularBytesField(value: self.payload, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
}
public struct TailnetClient: Client, GRPCClient {
public let channel: GRPCChannel
public var defaultCallOptions: CallOptions
public init(channel: any GRPCChannel) {
self.channel = channel
self.defaultCallOptions = .init()
}
public func discover(
_ request: Burrow_TailnetDiscoverRequest,
callOptions: CallOptions? = nil
) async throws -> Burrow_TailnetDiscoverResponse {
try await self.performAsyncUnaryCall(
path: "/burrow.TailnetControl/Discover",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: []
)
}
public func probe(
_ request: Burrow_TailnetProbeRequest,
callOptions: CallOptions? = nil
) async throws -> Burrow_TailnetProbeResponse {
try await self.performAsyncUnaryCall(
path: "/burrow.TailnetControl/Probe",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: []
)
}
public func loginStart(
_ request: Burrow_TailnetLoginStartRequest,
callOptions: CallOptions? = nil
) async throws -> Burrow_TailnetLoginStatusResponse {
try await self.performAsyncUnaryCall(
path: "/burrow.TailnetControl/LoginStart",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: []
)
}
public func loginStatus(
_ request: Burrow_TailnetLoginStatusRequest,
callOptions: CallOptions? = nil
) async throws -> Burrow_TailnetLoginStatusResponse {
try await self.performAsyncUnaryCall(
path: "/burrow.TailnetControl/LoginStatus",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: []
)
}
public func loginCancel(
_ request: Burrow_TailnetLoginCancelRequest,
callOptions: CallOptions? = nil
) async throws -> Burrow_Empty {
try await self.performAsyncUnaryCall(
path: "/burrow.TailnetControl/LoginCancel",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: []
)
}
}
public struct TunnelPacketClient: Client, GRPCClient {
public let channel: GRPCChannel
public var defaultCallOptions: CallOptions
public init(channel: any GRPCChannel) {
self.channel = channel
self.defaultCallOptions = .init()
}
public func makeTunnelPacketsCall(
callOptions: CallOptions? = nil
) -> GRPCAsyncBidirectionalStreamingCall<Burrow_TunnelPacket, Burrow_TunnelPacket> {
self.makeAsyncBidirectionalStreamingCall(
path: "/burrow.Tunnel/TunnelPackets",
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: []
)
}
}

View file

@ -1,761 +0,0 @@
//
// DO NOT EDIT.
// swift-format-ignore-file
//
// Generated by the protocol buffer compiler.
// Source: burrow.proto
//
import GRPC
import NIO
import NIOConcurrencyHelpers
import SwiftProtobuf
/// Usage: instantiate `Burrow_TunnelClient`, then call methods of this protocol to make API calls.
public protocol Burrow_TunnelClientProtocol: GRPCClient {
var serviceName: String { get }
var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? { get }
func tunnelConfiguration(
_ request: Burrow_Empty,
callOptions: CallOptions?,
handler: @escaping (Burrow_TunnelConfigurationResponse) -> Void
) -> ServerStreamingCall<Burrow_Empty, Burrow_TunnelConfigurationResponse>
func tunnelStart(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> UnaryCall<Burrow_Empty, Burrow_Empty>
func tunnelStop(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> UnaryCall<Burrow_Empty, Burrow_Empty>
func tunnelStatus(
_ request: Burrow_Empty,
callOptions: CallOptions?,
handler: @escaping (Burrow_TunnelStatusResponse) -> Void
) -> ServerStreamingCall<Burrow_Empty, Burrow_TunnelStatusResponse>
}
extension Burrow_TunnelClientProtocol {
public var serviceName: String {
return "burrow.Tunnel"
}
/// Server streaming call to TunnelConfiguration
///
/// - Parameters:
/// - request: Request to send to TunnelConfiguration.
/// - callOptions: Call options.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.
public func tunnelConfiguration(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil,
handler: @escaping (Burrow_TunnelConfigurationResponse) -> Void
) -> ServerStreamingCall<Burrow_Empty, Burrow_TunnelConfigurationResponse> {
return self.makeServerStreamingCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelConfiguration.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelConfigurationInterceptors() ?? [],
handler: handler
)
}
/// Unary call to TunnelStart
///
/// - Parameters:
/// - request: Request to send to TunnelStart.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func tunnelStart(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> UnaryCall<Burrow_Empty, Burrow_Empty> {
return self.makeUnaryCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStart.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStartInterceptors() ?? []
)
}
/// Unary call to TunnelStop
///
/// - Parameters:
/// - request: Request to send to TunnelStop.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func tunnelStop(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> UnaryCall<Burrow_Empty, Burrow_Empty> {
return self.makeUnaryCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStop.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStopInterceptors() ?? []
)
}
/// Server streaming call to TunnelStatus
///
/// - Parameters:
/// - request: Request to send to TunnelStatus.
/// - callOptions: Call options.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.
public func tunnelStatus(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil,
handler: @escaping (Burrow_TunnelStatusResponse) -> Void
) -> ServerStreamingCall<Burrow_Empty, Burrow_TunnelStatusResponse> {
return self.makeServerStreamingCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStatus.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStatusInterceptors() ?? [],
handler: handler
)
}
}
@available(*, deprecated)
extension Burrow_TunnelClient: @unchecked Sendable {}
@available(*, deprecated, renamed: "Burrow_TunnelNIOClient")
public final class Burrow_TunnelClient: Burrow_TunnelClientProtocol {
private let lock = Lock()
private var _defaultCallOptions: CallOptions
private var _interceptors: Burrow_TunnelClientInterceptorFactoryProtocol?
public let channel: GRPCChannel
public var defaultCallOptions: CallOptions {
get { self.lock.withLock { return self._defaultCallOptions } }
set { self.lock.withLockVoid { self._defaultCallOptions = newValue } }
}
public var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? {
get { self.lock.withLock { return self._interceptors } }
set { self.lock.withLockVoid { self._interceptors = newValue } }
}
/// Creates a client for the burrow.Tunnel service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
/// - interceptors: A factory providing interceptors for each RPC.
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self._defaultCallOptions = defaultCallOptions
self._interceptors = interceptors
}
}
public struct Burrow_TunnelNIOClient: Burrow_TunnelClientProtocol {
public var channel: GRPCChannel
public var defaultCallOptions: CallOptions
public var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol?
/// Creates a client for the burrow.Tunnel service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
/// - interceptors: A factory providing interceptors for each RPC.
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self.interceptors = interceptors
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public protocol Burrow_TunnelAsyncClientProtocol: GRPCClient {
static var serviceDescriptor: GRPCServiceDescriptor { get }
var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? { get }
func makeTunnelConfigurationCall(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> GRPCAsyncServerStreamingCall<Burrow_Empty, Burrow_TunnelConfigurationResponse>
func makeTunnelStartCall(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Burrow_Empty, Burrow_Empty>
func makeTunnelStopCall(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Burrow_Empty, Burrow_Empty>
func makeTunnelStatusCall(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> GRPCAsyncServerStreamingCall<Burrow_Empty, Burrow_TunnelStatusResponse>
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Burrow_TunnelAsyncClientProtocol {
public static var serviceDescriptor: GRPCServiceDescriptor {
return Burrow_TunnelClientMetadata.serviceDescriptor
}
public var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? {
return nil
}
public func makeTunnelConfigurationCall(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncServerStreamingCall<Burrow_Empty, Burrow_TunnelConfigurationResponse> {
return self.makeAsyncServerStreamingCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelConfiguration.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelConfigurationInterceptors() ?? []
)
}
public func makeTunnelStartCall(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Burrow_Empty, Burrow_Empty> {
return self.makeAsyncUnaryCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStart.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStartInterceptors() ?? []
)
}
public func makeTunnelStopCall(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Burrow_Empty, Burrow_Empty> {
return self.makeAsyncUnaryCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStop.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStopInterceptors() ?? []
)
}
public func makeTunnelStatusCall(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncServerStreamingCall<Burrow_Empty, Burrow_TunnelStatusResponse> {
return self.makeAsyncServerStreamingCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStatus.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStatusInterceptors() ?? []
)
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Burrow_TunnelAsyncClientProtocol {
public func tunnelConfiguration(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncResponseStream<Burrow_TunnelConfigurationResponse> {
return self.performAsyncServerStreamingCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelConfiguration.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelConfigurationInterceptors() ?? []
)
}
public func tunnelStart(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) async throws -> Burrow_Empty {
return try await self.performAsyncUnaryCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStart.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStartInterceptors() ?? []
)
}
public func tunnelStop(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) async throws -> Burrow_Empty {
return try await self.performAsyncUnaryCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStop.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStopInterceptors() ?? []
)
}
public func tunnelStatus(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncResponseStream<Burrow_TunnelStatusResponse> {
return self.performAsyncServerStreamingCall(
path: Burrow_TunnelClientMetadata.Methods.tunnelStatus.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeTunnelStatusInterceptors() ?? []
)
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public struct Burrow_TunnelAsyncClient: Burrow_TunnelAsyncClientProtocol {
public var channel: GRPCChannel
public var defaultCallOptions: CallOptions
public var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol?
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self.interceptors = interceptors
}
}
public protocol Burrow_TunnelClientInterceptorFactoryProtocol: Sendable {
/// - Returns: Interceptors to use when invoking 'tunnelConfiguration'.
func makeTunnelConfigurationInterceptors() -> [ClientInterceptor<Burrow_Empty, Burrow_TunnelConfigurationResponse>]
/// - Returns: Interceptors to use when invoking 'tunnelStart'.
func makeTunnelStartInterceptors() -> [ClientInterceptor<Burrow_Empty, Burrow_Empty>]
/// - Returns: Interceptors to use when invoking 'tunnelStop'.
func makeTunnelStopInterceptors() -> [ClientInterceptor<Burrow_Empty, Burrow_Empty>]
/// - Returns: Interceptors to use when invoking 'tunnelStatus'.
func makeTunnelStatusInterceptors() -> [ClientInterceptor<Burrow_Empty, Burrow_TunnelStatusResponse>]
}
public enum Burrow_TunnelClientMetadata {
public static let serviceDescriptor = GRPCServiceDescriptor(
name: "Tunnel",
fullName: "burrow.Tunnel",
methods: [
Burrow_TunnelClientMetadata.Methods.tunnelConfiguration,
Burrow_TunnelClientMetadata.Methods.tunnelStart,
Burrow_TunnelClientMetadata.Methods.tunnelStop,
Burrow_TunnelClientMetadata.Methods.tunnelStatus,
]
)
public enum Methods {
public static let tunnelConfiguration = GRPCMethodDescriptor(
name: "TunnelConfiguration",
path: "/burrow.Tunnel/TunnelConfiguration",
type: GRPCCallType.serverStreaming
)
public static let tunnelStart = GRPCMethodDescriptor(
name: "TunnelStart",
path: "/burrow.Tunnel/TunnelStart",
type: GRPCCallType.unary
)
public static let tunnelStop = GRPCMethodDescriptor(
name: "TunnelStop",
path: "/burrow.Tunnel/TunnelStop",
type: GRPCCallType.unary
)
public static let tunnelStatus = GRPCMethodDescriptor(
name: "TunnelStatus",
path: "/burrow.Tunnel/TunnelStatus",
type: GRPCCallType.serverStreaming
)
}
}
/// Usage: instantiate `Burrow_NetworksClient`, then call methods of this protocol to make API calls.
public protocol Burrow_NetworksClientProtocol: GRPCClient {
var serviceName: String { get }
var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? { get }
func networkAdd(
_ request: Burrow_Network,
callOptions: CallOptions?
) -> UnaryCall<Burrow_Network, Burrow_Empty>
func networkList(
_ request: Burrow_Empty,
callOptions: CallOptions?,
handler: @escaping (Burrow_NetworkListResponse) -> Void
) -> ServerStreamingCall<Burrow_Empty, Burrow_NetworkListResponse>
func networkReorder(
_ request: Burrow_NetworkReorderRequest,
callOptions: CallOptions?
) -> UnaryCall<Burrow_NetworkReorderRequest, Burrow_Empty>
func networkDelete(
_ request: Burrow_NetworkDeleteRequest,
callOptions: CallOptions?
) -> UnaryCall<Burrow_NetworkDeleteRequest, Burrow_Empty>
}
extension Burrow_NetworksClientProtocol {
public var serviceName: String {
return "burrow.Networks"
}
/// Unary call to NetworkAdd
///
/// - Parameters:
/// - request: Request to send to NetworkAdd.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func networkAdd(
_ request: Burrow_Network,
callOptions: CallOptions? = nil
) -> UnaryCall<Burrow_Network, Burrow_Empty> {
return self.makeUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkAdd.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkAddInterceptors() ?? []
)
}
/// Server streaming call to NetworkList
///
/// - Parameters:
/// - request: Request to send to NetworkList.
/// - callOptions: Call options.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.
public func networkList(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil,
handler: @escaping (Burrow_NetworkListResponse) -> Void
) -> ServerStreamingCall<Burrow_Empty, Burrow_NetworkListResponse> {
return self.makeServerStreamingCall(
path: Burrow_NetworksClientMetadata.Methods.networkList.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkListInterceptors() ?? [],
handler: handler
)
}
/// Unary call to NetworkReorder
///
/// - Parameters:
/// - request: Request to send to NetworkReorder.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func networkReorder(
_ request: Burrow_NetworkReorderRequest,
callOptions: CallOptions? = nil
) -> UnaryCall<Burrow_NetworkReorderRequest, Burrow_Empty> {
return self.makeUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkReorder.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkReorderInterceptors() ?? []
)
}
/// Unary call to NetworkDelete
///
/// - Parameters:
/// - request: Request to send to NetworkDelete.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func networkDelete(
_ request: Burrow_NetworkDeleteRequest,
callOptions: CallOptions? = nil
) -> UnaryCall<Burrow_NetworkDeleteRequest, Burrow_Empty> {
return self.makeUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkDelete.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkDeleteInterceptors() ?? []
)
}
}
@available(*, deprecated)
extension Burrow_NetworksClient: @unchecked Sendable {}
@available(*, deprecated, renamed: "Burrow_NetworksNIOClient")
public final class Burrow_NetworksClient: Burrow_NetworksClientProtocol {
private let lock = Lock()
private var _defaultCallOptions: CallOptions
private var _interceptors: Burrow_NetworksClientInterceptorFactoryProtocol?
public let channel: GRPCChannel
public var defaultCallOptions: CallOptions {
get { self.lock.withLock { return self._defaultCallOptions } }
set { self.lock.withLockVoid { self._defaultCallOptions = newValue } }
}
public var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? {
get { self.lock.withLock { return self._interceptors } }
set { self.lock.withLockVoid { self._interceptors = newValue } }
}
/// Creates a client for the burrow.Networks service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
/// - interceptors: A factory providing interceptors for each RPC.
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self._defaultCallOptions = defaultCallOptions
self._interceptors = interceptors
}
}
public struct Burrow_NetworksNIOClient: Burrow_NetworksClientProtocol {
public var channel: GRPCChannel
public var defaultCallOptions: CallOptions
public var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol?
/// Creates a client for the burrow.Networks service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
/// - interceptors: A factory providing interceptors for each RPC.
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self.interceptors = interceptors
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public protocol Burrow_NetworksAsyncClientProtocol: GRPCClient {
static var serviceDescriptor: GRPCServiceDescriptor { get }
var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? { get }
func makeNetworkAddCall(
_ request: Burrow_Network,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Burrow_Network, Burrow_Empty>
func makeNetworkListCall(
_ request: Burrow_Empty,
callOptions: CallOptions?
) -> GRPCAsyncServerStreamingCall<Burrow_Empty, Burrow_NetworkListResponse>
func makeNetworkReorderCall(
_ request: Burrow_NetworkReorderRequest,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Burrow_NetworkReorderRequest, Burrow_Empty>
func makeNetworkDeleteCall(
_ request: Burrow_NetworkDeleteRequest,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Burrow_NetworkDeleteRequest, Burrow_Empty>
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Burrow_NetworksAsyncClientProtocol {
public static var serviceDescriptor: GRPCServiceDescriptor {
return Burrow_NetworksClientMetadata.serviceDescriptor
}
public var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? {
return nil
}
public func makeNetworkAddCall(
_ request: Burrow_Network,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Burrow_Network, Burrow_Empty> {
return self.makeAsyncUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkAdd.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkAddInterceptors() ?? []
)
}
public func makeNetworkListCall(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncServerStreamingCall<Burrow_Empty, Burrow_NetworkListResponse> {
return self.makeAsyncServerStreamingCall(
path: Burrow_NetworksClientMetadata.Methods.networkList.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkListInterceptors() ?? []
)
}
public func makeNetworkReorderCall(
_ request: Burrow_NetworkReorderRequest,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Burrow_NetworkReorderRequest, Burrow_Empty> {
return self.makeAsyncUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkReorder.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkReorderInterceptors() ?? []
)
}
public func makeNetworkDeleteCall(
_ request: Burrow_NetworkDeleteRequest,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Burrow_NetworkDeleteRequest, Burrow_Empty> {
return self.makeAsyncUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkDelete.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkDeleteInterceptors() ?? []
)
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Burrow_NetworksAsyncClientProtocol {
public func networkAdd(
_ request: Burrow_Network,
callOptions: CallOptions? = nil
) async throws -> Burrow_Empty {
return try await self.performAsyncUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkAdd.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkAddInterceptors() ?? []
)
}
public func networkList(
_ request: Burrow_Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncResponseStream<Burrow_NetworkListResponse> {
return self.performAsyncServerStreamingCall(
path: Burrow_NetworksClientMetadata.Methods.networkList.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkListInterceptors() ?? []
)
}
public func networkReorder(
_ request: Burrow_NetworkReorderRequest,
callOptions: CallOptions? = nil
) async throws -> Burrow_Empty {
return try await self.performAsyncUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkReorder.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkReorderInterceptors() ?? []
)
}
public func networkDelete(
_ request: Burrow_NetworkDeleteRequest,
callOptions: CallOptions? = nil
) async throws -> Burrow_Empty {
return try await self.performAsyncUnaryCall(
path: Burrow_NetworksClientMetadata.Methods.networkDelete.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeNetworkDeleteInterceptors() ?? []
)
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public struct Burrow_NetworksAsyncClient: Burrow_NetworksAsyncClientProtocol {
public var channel: GRPCChannel
public var defaultCallOptions: CallOptions
public var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol?
public init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self.interceptors = interceptors
}
}
public protocol Burrow_NetworksClientInterceptorFactoryProtocol: Sendable {
/// - Returns: Interceptors to use when invoking 'networkAdd'.
func makeNetworkAddInterceptors() -> [ClientInterceptor<Burrow_Network, Burrow_Empty>]
/// - Returns: Interceptors to use when invoking 'networkList'.
func makeNetworkListInterceptors() -> [ClientInterceptor<Burrow_Empty, Burrow_NetworkListResponse>]
/// - Returns: Interceptors to use when invoking 'networkReorder'.
func makeNetworkReorderInterceptors() -> [ClientInterceptor<Burrow_NetworkReorderRequest, Burrow_Empty>]
/// - Returns: Interceptors to use when invoking 'networkDelete'.
func makeNetworkDeleteInterceptors() -> [ClientInterceptor<Burrow_NetworkDeleteRequest, Burrow_Empty>]
}
public enum Burrow_NetworksClientMetadata {
public static let serviceDescriptor = GRPCServiceDescriptor(
name: "Networks",
fullName: "burrow.Networks",
methods: [
Burrow_NetworksClientMetadata.Methods.networkAdd,
Burrow_NetworksClientMetadata.Methods.networkList,
Burrow_NetworksClientMetadata.Methods.networkReorder,
Burrow_NetworksClientMetadata.Methods.networkDelete,
]
)
public enum Methods {
public static let networkAdd = GRPCMethodDescriptor(
name: "NetworkAdd",
path: "/burrow.Networks/NetworkAdd",
type: GRPCCallType.unary
)
public static let networkList = GRPCMethodDescriptor(
name: "NetworkList",
path: "/burrow.Networks/NetworkList",
type: GRPCCallType.serverStreaming
)
public static let networkReorder = GRPCMethodDescriptor(
name: "NetworkReorder",
path: "/burrow.Networks/NetworkReorder",
type: GRPCCallType.unary
)
public static let networkDelete = GRPCMethodDescriptor(
name: "NetworkDelete",
path: "/burrow.Networks/NetworkDelete",
type: GRPCCallType.unary
)
}
}

View file

@ -1,598 +0,0 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: burrow.proto
//
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
public enum Burrow_NetworkType: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case wireGuard // = 0
case tailnet // = 1
case UNRECOGNIZED(Int)
public init() {
self = .wireGuard
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .wireGuard
case 1: self = .tailnet
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .wireGuard: return 0
case .tailnet: return 1
case .UNRECOGNIZED(let i): return i
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Burrow_NetworkType] = [
.wireGuard,
.tailnet,
]
}
public enum Burrow_State: SwiftProtobuf.Enum, Swift.CaseIterable {
public typealias RawValue = Int
case stopped // = 0
case running // = 1
case UNRECOGNIZED(Int)
public init() {
self = .stopped
}
public init?(rawValue: Int) {
switch rawValue {
case 0: self = .stopped
case 1: self = .running
default: self = .UNRECOGNIZED(rawValue)
}
}
public var rawValue: Int {
switch self {
case .stopped: return 0
case .running: return 1
case .UNRECOGNIZED(let i): return i
}
}
// The compiler won't synthesize support with the UNRECOGNIZED case.
public static let allCases: [Burrow_State] = [
.stopped,
.running,
]
}
public struct Burrow_NetworkReorderRequest: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var id: Int32 = 0
public var index: Int32 = 0
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_WireGuardPeer: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var endpoint: String = String()
public var subnet: [String] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_WireGuardNetwork: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var address: String = String()
public var dns: String = String()
public var peer: [Burrow_WireGuardPeer] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_NetworkDeleteRequest: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var id: Int32 = 0
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_Network: @unchecked Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var id: Int32 = 0
public var type: Burrow_NetworkType = .wireGuard
public var payload: Data = Data()
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_NetworkListResponse: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var network: [Burrow_Network] = []
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_Empty: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Burrow_TunnelStatusResponse: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var state: Burrow_State = .stopped
public var start: SwiftProtobuf.Google_Protobuf_Timestamp {
get {return _start ?? SwiftProtobuf.Google_Protobuf_Timestamp()}
set {_start = newValue}
}
/// Returns true if `start` has been explicitly set.
public var hasStart: Bool {return self._start != nil}
/// Clears the value of `start`. Subsequent reads from it will return its default value.
public mutating func clearStart() {self._start = nil}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
fileprivate var _start: SwiftProtobuf.Google_Protobuf_Timestamp? = nil
}
public struct Burrow_TunnelConfigurationResponse: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var addresses: [String] = []
public var mtu: Int32 = 0
public var routes: [String] = []
public var dnsServers: [String] = []
public var searchDomains: [String] = []
public var includeDefaultRoute: Bool = false
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "burrow"
extension Burrow_NetworkType: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "WireGuard"),
1: .same(proto: "Tailnet"),
]
}
extension Burrow_State: SwiftProtobuf._ProtoNameProviding {
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "Stopped"),
1: .same(proto: "Running"),
]
}
extension Burrow_NetworkReorderRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".NetworkReorderRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .same(proto: "index"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularInt32Field(value: &self.id) }()
case 2: try { try decoder.decodeSingularInt32Field(value: &self.index) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.id != 0 {
try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1)
}
if self.index != 0 {
try visitor.visitSingularInt32Field(value: self.index, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_NetworkReorderRequest, rhs: Burrow_NetworkReorderRequest) -> Bool {
if lhs.id != rhs.id {return false}
if lhs.index != rhs.index {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_WireGuardPeer: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".WireGuardPeer"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "endpoint"),
2: .same(proto: "subnet"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.endpoint) }()
case 2: try { try decoder.decodeRepeatedStringField(value: &self.subnet) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.endpoint.isEmpty {
try visitor.visitSingularStringField(value: self.endpoint, fieldNumber: 1)
}
if !self.subnet.isEmpty {
try visitor.visitRepeatedStringField(value: self.subnet, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_WireGuardPeer, rhs: Burrow_WireGuardPeer) -> Bool {
if lhs.endpoint != rhs.endpoint {return false}
if lhs.subnet != rhs.subnet {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_WireGuardNetwork: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".WireGuardNetwork"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "address"),
2: .same(proto: "dns"),
3: .same(proto: "peer"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.address) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.dns) }()
case 3: try { try decoder.decodeRepeatedMessageField(value: &self.peer) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.address.isEmpty {
try visitor.visitSingularStringField(value: self.address, fieldNumber: 1)
}
if !self.dns.isEmpty {
try visitor.visitSingularStringField(value: self.dns, fieldNumber: 2)
}
if !self.peer.isEmpty {
try visitor.visitRepeatedMessageField(value: self.peer, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_WireGuardNetwork, rhs: Burrow_WireGuardNetwork) -> Bool {
if lhs.address != rhs.address {return false}
if lhs.dns != rhs.dns {return false}
if lhs.peer != rhs.peer {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_NetworkDeleteRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".NetworkDeleteRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularInt32Field(value: &self.id) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.id != 0 {
try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_NetworkDeleteRequest, rhs: Burrow_NetworkDeleteRequest) -> Bool {
if lhs.id != rhs.id {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_Network: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".Network"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .same(proto: "type"),
3: .same(proto: "payload"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularInt32Field(value: &self.id) }()
case 2: try { try decoder.decodeSingularEnumField(value: &self.type) }()
case 3: try { try decoder.decodeSingularBytesField(value: &self.payload) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.id != 0 {
try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1)
}
if self.type != .wireGuard {
try visitor.visitSingularEnumField(value: self.type, fieldNumber: 2)
}
if !self.payload.isEmpty {
try visitor.visitSingularBytesField(value: self.payload, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_Network, rhs: Burrow_Network) -> Bool {
if lhs.id != rhs.id {return false}
if lhs.type != rhs.type {return false}
if lhs.payload != rhs.payload {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_NetworkListResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".NetworkListResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "network"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeRepeatedMessageField(value: &self.network) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.network.isEmpty {
try visitor.visitRepeatedMessageField(value: self.network, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_NetworkListResponse, rhs: Burrow_NetworkListResponse) -> Bool {
if lhs.network != rhs.network {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_Empty: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".Empty"
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
// Load everything into unknown fields
while try decoder.nextFieldNumber() != nil {}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_Empty, rhs: Burrow_Empty) -> Bool {
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_TunnelStatusResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".TunnelStatusResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "state"),
2: .same(proto: "start"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularEnumField(value: &self.state) }()
case 2: try { try decoder.decodeSingularMessageField(value: &self._start) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if self.state != .stopped {
try visitor.visitSingularEnumField(value: self.state, fieldNumber: 1)
}
try { if let v = self._start {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
} }()
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_TunnelStatusResponse, rhs: Burrow_TunnelStatusResponse) -> Bool {
if lhs.state != rhs.state {return false}
if lhs._start != rhs._start {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Burrow_TunnelConfigurationResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".TunnelConfigurationResponse"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "addresses"),
2: .same(proto: "mtu"),
3: .same(proto: "routes"),
4: .standard(proto: "dns_servers"),
5: .standard(proto: "search_domains"),
6: .standard(proto: "include_default_route"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeRepeatedStringField(value: &self.addresses) }()
case 2: try { try decoder.decodeSingularInt32Field(value: &self.mtu) }()
case 3: try { try decoder.decodeRepeatedStringField(value: &self.routes) }()
case 4: try { try decoder.decodeRepeatedStringField(value: &self.dnsServers) }()
case 5: try { try decoder.decodeRepeatedStringField(value: &self.searchDomains) }()
case 6: try { try decoder.decodeSingularBoolField(value: &self.includeDefaultRoute) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.addresses.isEmpty {
try visitor.visitRepeatedStringField(value: self.addresses, fieldNumber: 1)
}
if self.mtu != 0 {
try visitor.visitSingularInt32Field(value: self.mtu, fieldNumber: 2)
}
if !self.routes.isEmpty {
try visitor.visitRepeatedStringField(value: self.routes, fieldNumber: 3)
}
if !self.dnsServers.isEmpty {
try visitor.visitRepeatedStringField(value: self.dnsServers, fieldNumber: 4)
}
if !self.searchDomains.isEmpty {
try visitor.visitRepeatedStringField(value: self.searchDomains, fieldNumber: 5)
}
if self.includeDefaultRoute {
try visitor.visitSingularBoolField(value: self.includeDefaultRoute, fieldNumber: 6)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Burrow_TunnelConfigurationResponse, rhs: Burrow_TunnelConfigurationResponse) -> Bool {
if lhs.addresses != rhs.addresses {return false}
if lhs.mtu != rhs.mtu {return false}
if lhs.routes != rhs.routes {return false}
if lhs.dnsServers != rhs.dnsServers {return false}
if lhs.searchDomains != rhs.searchDomains {return false}
if lhs.includeDefaultRoute != rhs.includeDefaultRoute {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

Some files were not shown because too many files have changed in this diff Show more