Wire Forge-native release infrastructure
Some checks failed
Cache: Publish Nix / Publish Nix Cache (push) Waiting to run
Build Rust / Cargo Test (push) Successful in 4m14s
Build Site / Next.js Build (push) Failing after 6s
Infra: OpenTofu / OpenTofu (grafana) (push) Successful in 5s
Lint Governance / BEP Metadata (push) Successful in 0s
Build: Android / Android Rust Core Stub (push) Failing after 23s
Some checks failed
Cache: Publish Nix / Publish Nix Cache (push) Waiting to run
Build Rust / Cargo Test (push) Successful in 4m14s
Build Site / Next.js Build (push) Failing after 6s
Infra: OpenTofu / OpenTofu (grafana) (push) Successful in 5s
Lint Governance / BEP Metadata (push) Successful in 0s
Build: Android / Android Rust Core Stub (push) Failing after 23s
This commit is contained in:
parent
97c569fb35
commit
002bd382e9
199 changed files with 14268 additions and 185 deletions
102
Scripts/ci/backup-garage-to-gcs.sh
Executable file
102
Scripts/ci/backup-garage-to-gcs.sh
Executable file
|
|
@ -0,0 +1,102 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
: "${BURROW_GARAGE_ENDPOINT:?BURROW_GARAGE_ENDPOINT is required}"
|
||||
: "${AWS_ACCESS_KEY_ID:?AWS_ACCESS_KEY_ID is required for Garage backup}"
|
||||
: "${AWS_SECRET_ACCESS_KEY:?AWS_SECRET_ACCESS_KEY is required for Garage backup}"
|
||||
|
||||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
if ! command -v aws >/dev/null 2>&1; then
|
||||
echo "aws CLI is required for Garage backup" >&2
|
||||
exit 127
|
||||
fi
|
||||
|
||||
if ! command -v gcloud >/dev/null 2>&1; then
|
||||
echo "gcloud is required for GCS backup" >&2
|
||||
exit 127
|
||||
fi
|
||||
|
||||
if ! gcloud auth list --filter=status:ACTIVE --format='value(account)' | grep -q .; then
|
||||
Scripts/ci/google-wif-auth.sh
|
||||
fi
|
||||
|
||||
export AWS_DEFAULT_REGION="${BURROW_GARAGE_REGION:-garage}"
|
||||
export AWS_EC2_METADATA_DISABLED=true
|
||||
|
||||
requested_releases=0
|
||||
requested_packages=0
|
||||
requested_nix_cache=0
|
||||
IFS=',' read -r -a requested_items <<< "${BURROW_GARAGE_BACKUP_SET:-all}"
|
||||
|
||||
for raw_item in "${requested_items[@]}"; do
|
||||
item="${raw_item//[[:space:]]/}"
|
||||
case "$item" in
|
||||
"" )
|
||||
;;
|
||||
all )
|
||||
requested_releases=1
|
||||
requested_packages=1
|
||||
requested_nix_cache=1
|
||||
;;
|
||||
release|releases )
|
||||
requested_releases=1
|
||||
;;
|
||||
package|packages )
|
||||
requested_packages=1
|
||||
;;
|
||||
attic|nix|nix-cache|nix_cache )
|
||||
requested_nix_cache=1
|
||||
;;
|
||||
* )
|
||||
echo "unknown Garage backup set item: $item" >&2
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$requested_releases" == "0" && "$requested_packages" == "0" && "$requested_nix_cache" == "0" ]]; then
|
||||
echo "no Garage backup buckets selected" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
work_root="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/burrow-garage-backup"
|
||||
rsync_flags=(--recursive)
|
||||
if [[ "${BURROW_GARAGE_BACKUP_DELETE:-false}" == "true" ]]; then
|
||||
rsync_flags+=(--delete-unmatched-destination-objects)
|
||||
fi
|
||||
|
||||
sync_bucket() {
|
||||
local name="$1"
|
||||
local garage_bucket="$2"
|
||||
local gcs_bucket="$3"
|
||||
local source_dir="${work_root}/${name}"
|
||||
|
||||
rm -rf "$source_dir"
|
||||
mkdir -p "$source_dir"
|
||||
|
||||
aws --endpoint-url "$BURROW_GARAGE_ENDPOINT" s3 sync "s3://${garage_bucket}" "$source_dir" --no-progress
|
||||
gcloud storage rsync "${rsync_flags[@]}" "$source_dir" "gs://${gcs_bucket}"
|
||||
}
|
||||
|
||||
if [[ "$requested_releases" == "1" ]]; then
|
||||
sync_bucket \
|
||||
releases \
|
||||
"${BURROW_RELEASE_GARAGE_BUCKET:-burrow-releases}" \
|
||||
"${BURROW_RELEASE_GCS_BUCKET:-burrow-net-releases}"
|
||||
fi
|
||||
|
||||
if [[ "$requested_packages" == "1" ]]; then
|
||||
sync_bucket \
|
||||
packages \
|
||||
"${BURROW_PACKAGE_GARAGE_BUCKET:-burrow-packages}" \
|
||||
"${BURROW_PACKAGE_GCS_BUCKET:-burrow-net-packages}"
|
||||
fi
|
||||
|
||||
if [[ "$requested_nix_cache" == "1" ]]; then
|
||||
sync_bucket \
|
||||
nix-cache \
|
||||
"${BURROW_NIX_CACHE_GARAGE_BUCKET:-attic}" \
|
||||
"${BURROW_NIX_CACHE_GCS_BUCKET:-burrow-net-nix-cache}"
|
||||
fi
|
||||
|
|
@ -4,17 +4,8 @@ set -euo pipefail
|
|||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
release_ref="${RELEASE_REF:-manual-${GITHUB_SHA:-unknown}}"
|
||||
target="x86_64-unknown-linux-gnu"
|
||||
out_dir="${repo_root}/dist"
|
||||
staging="${out_dir}/burrow-${release_ref}-${target}"
|
||||
build_number="${BUILD_NUMBER:-${RELEASE_REF:-manual-${GITHUB_SHA:-unknown}}}"
|
||||
export BUILD_NUMBER="$build_number"
|
||||
export BURROW_RELEASE_OUT="${BURROW_RELEASE_OUT:-${repo_root}/dist/builds/${build_number}}"
|
||||
|
||||
mkdir -p "${staging}"
|
||||
|
||||
cargo build --locked --release -p burrow --bin burrow
|
||||
install -m 0755 target/release/burrow "${staging}/burrow"
|
||||
cp README.md "${staging}/README.md"
|
||||
|
||||
tarball="${out_dir}/burrow-${release_ref}-${target}.tar.gz"
|
||||
tar -C "${out_dir}" -czf "${tarball}" "$(basename "${staging}")"
|
||||
shasum -a 256 "${tarball}" > "${tarball}.sha256"
|
||||
Scripts/ci/package-release-artifacts.sh "${1:-all}"
|
||||
|
|
|
|||
68
Scripts/ci/check-release-config.sh
Executable file
68
Scripts/ci/check-release-config.sh
Executable file
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
require_env() {
|
||||
local name="$1"
|
||||
if [[ -z "${!name:-}" ]]; then
|
||||
echo "missing required environment variable: ${name}" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_file_or_env() {
|
||||
local file_name="$1"
|
||||
local env_name="$2"
|
||||
if [[ -n "${!file_name:-}" && -f "${!file_name:-}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ -n "${!env_name:-}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
echo "missing required ${file_name} file or ${env_name} environment value" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
case "${1:-all}" in
|
||||
gcs)
|
||||
require_env BURROW_GOOGLE_PROJECT_ID
|
||||
require_env BURROW_GOOGLE_PROJECT_NUMBER
|
||||
require_env BURROW_GOOGLE_WIF_POOL_ID
|
||||
require_env BURROW_GOOGLE_WIF_PROVIDER_ID
|
||||
require_env BURROW_GOOGLE_WIF_SERVICE_ACCOUNT
|
||||
require_env BURROW_AUTHENTIK_WIF_ISSUER
|
||||
require_env BURROW_AUTHENTIK_WIF_AUDIENCE
|
||||
require_env BURROW_AUTHENTIK_WIF_CLIENT_ID
|
||||
if [[ -z "${BURROW_AUTHENTIK_WIF_TOKEN:-}" && -z "${BURROW_AUTHENTIK_WIF_CLIENT_SECRET:-}" ]]; then
|
||||
if [[ -z "${BURROW_AUTHENTIK_WIF_TOKEN_FILE:-}" || ! -f "${BURROW_AUTHENTIK_WIF_TOKEN_FILE:-}" ]]; then
|
||||
echo "missing Authentik WIF token, token file, or client secret" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
app-store)
|
||||
require_env ASC_API_KEY_ID
|
||||
require_env ASC_API_ISSUER_ID
|
||||
require_file_or_env ASC_API_KEY_PATH ASC_API_KEY_BASE64
|
||||
;;
|
||||
apple-signing)
|
||||
require_file_or_env APPLE_SIGNING_CERTIFICATE_PATH APPLE_SIGNING_CERTIFICATE_BASE64
|
||||
require_env APPLE_SIGNING_CERTIFICATE_PASSWORD
|
||||
;;
|
||||
sparkle)
|
||||
"$0" gcs
|
||||
;;
|
||||
microsoft-store)
|
||||
require_env MICROSOFT_TENANT_ID
|
||||
require_env MICROSOFT_CLIENT_ID
|
||||
require_env MICROSOFT_CLIENT_SECRET
|
||||
;;
|
||||
all)
|
||||
"$0" gcs
|
||||
"$0" app-store
|
||||
"$0" apple-signing
|
||||
;;
|
||||
*)
|
||||
echo "unknown release config group: $1" >&2
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
|
|
@ -143,13 +143,18 @@ if [[ -e "${config_file}" && ! -w "${config_file}" ]]; then
|
|||
fi
|
||||
|
||||
mkdir -p "$(dirname -- "${config_file}")"
|
||||
cat > "${config_file}" <<'EOF'
|
||||
experimental-features = nix-command flakes
|
||||
sandbox = true
|
||||
fallback = true
|
||||
substituters = https://cache.nixos.org
|
||||
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
|
||||
EOF
|
||||
{
|
||||
echo "experimental-features = nix-command flakes"
|
||||
echo "sandbox = true"
|
||||
echo "fallback = true"
|
||||
if [[ -n "${BURROW_NIX_CACHE_PUBLIC_KEY:-}" ]]; then
|
||||
echo "substituters = ${BURROW_NIX_CACHE_URL:-https://nix.burrow.net/${BURROW_NIX_CACHE_NAME:-burrow}} https://cache.nixos.org"
|
||||
echo "trusted-public-keys = ${BURROW_NIX_CACHE_PUBLIC_KEY} cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
|
||||
else
|
||||
echo "substituters = https://cache.nixos.org"
|
||||
echo "trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
|
||||
fi
|
||||
} > "${config_file}"
|
||||
|
||||
command -v nix >/dev/null 2>&1 || {
|
||||
echo "nix is still unavailable after bootstrap" >&2
|
||||
|
|
|
|||
73
Scripts/ci/ensure-nsc.sh
Executable file
73
Scripts/ci/ensure-nsc.sh
Executable file
|
|
@ -0,0 +1,73 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if command -v nsc >/dev/null 2>&1; then
|
||||
nsc version || true
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
echo "::error ::curl is required to install nsc" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v tar >/dev/null 2>&1; then
|
||||
echo "::error ::tar is required to install nsc" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
arch="$(uname -m)"
|
||||
case "$arch" in
|
||||
x86_64|amd64) arch="amd64" ;;
|
||||
aarch64|arm64) arch="arm64" ;;
|
||||
*)
|
||||
echo "::error ::Unsupported arch for nsc: ${arch}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$os" != "linux" ]]; then
|
||||
echo "::warning ::nsc bootstrap is only supported on Linux; relying on the runner or Nix shell for ${os}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version="${NSC_VERSION:-0.0.506}"
|
||||
expected_sha256=""
|
||||
case "${version}/${arch}" in
|
||||
0.0.506/amd64) expected_sha256="4a093e0269878a9514e7c00d99b88a924f307ad15fb9d1595ee5f6582f99a0f0" ;;
|
||||
0.0.506/arm64) expected_sha256="6acf074b67fcbaaf59a4437f6bd40da7e38438c427849c1b5fcf4621ce75aee2" ;;
|
||||
esac
|
||||
|
||||
asset="nsc_${version}_linux_${arch}.tar.gz"
|
||||
url="https://get.namespace.so/packages/nsc/v${version}/${asset}"
|
||||
tmp_dir="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/burrow-nsc.$$"
|
||||
install_dir="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/nsc-bin"
|
||||
mkdir -p "$tmp_dir" "$install_dir"
|
||||
trap 'rm -rf "$tmp_dir"' EXIT
|
||||
|
||||
curl -fsSL -o "${tmp_dir}/${asset}" "$url"
|
||||
if [[ -n "$expected_sha256" ]] && command -v sha256sum >/dev/null 2>&1; then
|
||||
actual_sha256="$(sha256sum "${tmp_dir}/${asset}" | awk '{print $1}')"
|
||||
if [[ "$actual_sha256" != "$expected_sha256" ]]; then
|
||||
echo "::error ::nsc ${asset} sha256 mismatch: expected ${expected_sha256}, got ${actual_sha256}" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
tar -xzf "${tmp_dir}/${asset}" -C "$tmp_dir"
|
||||
for bin in nsc bazel-credential-nsc docker-credential-nsc; do
|
||||
if [[ -x "${tmp_dir}/${bin}" ]]; then
|
||||
install -m 0755 "${tmp_dir}/${bin}" "${install_dir}/${bin}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ! -x "${install_dir}/nsc" || ! -x "${install_dir}/bazel-credential-nsc" ]]; then
|
||||
echo "::error ::Failed to install nsc and bazel-credential-nsc from ${asset}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "${GITHUB_PATH:-}" ]]; then
|
||||
echo "${install_dir}" >> "${GITHUB_PATH}"
|
||||
fi
|
||||
export PATH="${install_dir}:$PATH"
|
||||
nsc version || true
|
||||
60
Scripts/ci/ensure-spacectl.sh
Executable file
60
Scripts/ci/ensure-spacectl.sh
Executable file
|
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if command -v spacectl >/dev/null 2>&1; then
|
||||
spacectl version || true
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
echo "::error ::curl is required to install spacectl" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v tar >/dev/null 2>&1; then
|
||||
echo "::error ::tar is required to install spacectl" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
arch="$(uname -m)"
|
||||
case "$arch" in
|
||||
x86_64|amd64) arch="amd64" ;;
|
||||
aarch64|arm64) arch="arm64" ;;
|
||||
*)
|
||||
echo "::error ::Unsupported arch for spacectl: ${arch}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$os" != "linux" && "$os" != "darwin" ]]; then
|
||||
echo "::error ::Unsupported OS for spacectl: ${os}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version="${SPACECTL_VERSION:-v0.5.0}"
|
||||
version_num="${version#v}"
|
||||
asset="spacectl_${version_num}_${os}_${arch}.tar.gz"
|
||||
url="https://github.com/namespacelabs/spacectl/releases/download/${version}/${asset}"
|
||||
tmp_dir="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/burrow-spacectl.$$"
|
||||
install_dir="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/spacectl-bin"
|
||||
mkdir -p "$tmp_dir" "$install_dir"
|
||||
trap 'rm -rf "$tmp_dir"' EXIT
|
||||
|
||||
curl -fsSL -o "${tmp_dir}/${asset}" "$url"
|
||||
tar -xzf "${tmp_dir}/${asset}" -C "$tmp_dir"
|
||||
|
||||
bin="${tmp_dir}/spacectl"
|
||||
if [[ ! -x "$bin" ]]; then
|
||||
bin="$(find "$tmp_dir" -maxdepth 3 -type f -name spacectl -perm -111 | head -n1 || true)"
|
||||
fi
|
||||
if [[ -z "${bin}" || ! -x "${bin}" ]]; then
|
||||
echo "::error ::Failed to locate spacectl binary in ${asset}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
install -m 0755 "$bin" "${install_dir}/spacectl"
|
||||
if [[ -n "${GITHUB_PATH:-}" ]]; then
|
||||
echo "${install_dir}" >> "${GITHUB_PATH}"
|
||||
fi
|
||||
export PATH="${install_dir}:$PATH"
|
||||
spacectl version || true
|
||||
126
Scripts/ci/google-wif-auth.sh
Executable file
126
Scripts/ci/google-wif-auth.sh
Executable file
|
|
@ -0,0 +1,126 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
project_id="${GOOGLE_CLOUD_PROJECT:-${BURROW_GOOGLE_PROJECT_ID:-project-88c23ce9-918a-470a-b33}}"
|
||||
project_number="${BURROW_GOOGLE_PROJECT_NUMBER:-416198671487}"
|
||||
pool_id="${BURROW_GOOGLE_WIF_POOL_ID:-burrow-authentik}"
|
||||
provider_id="${BURROW_GOOGLE_WIF_PROVIDER_ID:-forgejo-runners}"
|
||||
service_account="${BURROW_GOOGLE_WIF_SERVICE_ACCOUNT:-burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com}"
|
||||
authentik_issuer="${BURROW_AUTHENTIK_WIF_ISSUER:-https://auth.burrow.net/application/o/google-cloud/}"
|
||||
authentik_audience="${BURROW_AUTHENTIK_WIF_AUDIENCE:-google-wif.burrow.net}"
|
||||
authentik_client_id="${BURROW_AUTHENTIK_WIF_CLIENT_ID:-${AUTHENTIK_GOOGLE_WIF_CLIENT_ID:-google-wif.burrow.net}}"
|
||||
authentik_client_secret="${BURROW_AUTHENTIK_WIF_CLIENT_SECRET:-${AUTHENTIK_GOOGLE_WIF_CLIENT_SECRET:-}}"
|
||||
authentik_token_file="${BURROW_AUTHENTIK_WIF_TOKEN_FILE:-}"
|
||||
authentik_token="${BURROW_AUTHENTIK_WIF_TOKEN:-}"
|
||||
work_dir="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/burrow-google-wif"
|
||||
emit_env=1
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: Scripts/ci/google-wif-auth.sh [--no-emit-env]
|
||||
|
||||
Authenticates gcloud to the Burrow Google project through Authentik-issued OIDC
|
||||
and Google Workload Identity Federation. No Google service-account JSON key is
|
||||
used or written.
|
||||
|
||||
Inputs:
|
||||
GOOGLE_CLOUD_PROJECT / BURROW_GOOGLE_PROJECT_ID
|
||||
BURROW_GOOGLE_PROJECT_NUMBER
|
||||
BURROW_GOOGLE_WIF_POOL_ID
|
||||
BURROW_GOOGLE_WIF_PROVIDER_ID
|
||||
BURROW_GOOGLE_WIF_SERVICE_ACCOUNT
|
||||
BURROW_AUTHENTIK_WIF_ISSUER
|
||||
BURROW_AUTHENTIK_WIF_AUDIENCE
|
||||
BURROW_AUTHENTIK_WIF_TOKEN or BURROW_AUTHENTIK_WIF_TOKEN_FILE
|
||||
|
||||
If no token is supplied, the script requests one from Authentik using
|
||||
BURROW_AUTHENTIK_WIF_CLIENT_ID and BURROW_AUTHENTIK_WIF_CLIENT_SECRET.
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--no-emit-env)
|
||||
emit_env=0
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "unknown argument: $1" >&2
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
require_command() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
echo "$1 is required for Google WIF authentication" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
|
||||
require_command gcloud
|
||||
require_command curl
|
||||
require_command jq
|
||||
|
||||
mkdir -p "$work_dir"
|
||||
chmod 0700 "$work_dir"
|
||||
|
||||
token_path="$work_dir/authentik-oidc-token.jwt"
|
||||
credential_config="$work_dir/google-wif-credentials.json"
|
||||
|
||||
if [[ -n "$authentik_token_file" ]]; then
|
||||
if [[ ! -s "$authentik_token_file" ]]; then
|
||||
echo "Authentik WIF token file is empty or missing: $authentik_token_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
cp "$authentik_token_file" "$token_path"
|
||||
elif [[ -n "$authentik_token" ]]; then
|
||||
printf '%s' "$authentik_token" > "$token_path"
|
||||
else
|
||||
if [[ -z "$authentik_client_secret" ]]; then
|
||||
echo "missing Authentik WIF token. Set BURROW_AUTHENTIK_WIF_TOKEN, BURROW_AUTHENTIK_WIF_TOKEN_FILE, or BURROW_AUTHENTIK_WIF_CLIENT_SECRET." >&2
|
||||
exit 1
|
||||
fi
|
||||
token_endpoint="${authentik_issuer%/}/token/"
|
||||
curl -fsS \
|
||||
-u "${authentik_client_id}:${authentik_client_secret}" \
|
||||
-d grant_type=client_credentials \
|
||||
-d scope="openid profile email groups" \
|
||||
-d audience="$authentik_audience" \
|
||||
"$token_endpoint" \
|
||||
| jq -r '.id_token // empty' > "$token_path"
|
||||
fi
|
||||
|
||||
chmod 0600 "$token_path"
|
||||
if [[ ! -s "$token_path" ]]; then
|
||||
echo "Authentik did not return an OIDC token for Google WIF" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
provider_resource="projects/${project_number}/locations/global/workloadIdentityPools/${pool_id}/providers/${provider_id}"
|
||||
gcloud iam workload-identity-pools create-cred-config "$provider_resource" \
|
||||
--service-account "$service_account" \
|
||||
--subject-token-type="urn:ietf:params:oauth:token-type:jwt" \
|
||||
--credential-source-file "$token_path" \
|
||||
--output-file "$credential_config"
|
||||
|
||||
chmod 0600 "$credential_config"
|
||||
gcloud auth login --cred-file="$credential_config" --brief
|
||||
gcloud config set project "$project_id" >/dev/null
|
||||
|
||||
{
|
||||
echo "GOOGLE_APPLICATION_CREDENTIALS=$credential_config"
|
||||
echo "CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE=$credential_config"
|
||||
echo "CLOUDSDK_CORE_PROJECT=$project_id"
|
||||
echo "GOOGLE_CLOUD_PROJECT=$project_id"
|
||||
} > "$work_dir/google-wif.env"
|
||||
|
||||
if [[ "$emit_env" == "1" && -n "${GITHUB_ENV:-}" ]]; then
|
||||
cat "$work_dir/google-wif.env" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
echo "Authenticated gcloud through Authentik WIF as ${service_account} in ${project_id}."
|
||||
124
Scripts/ci/install-apple-signing-assets.sh
Executable file
124
Scripts/ci/install-apple-signing-assets.sh
Executable file
|
|
@ -0,0 +1,124 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "$repo_root"
|
||||
|
||||
signing_ready=0
|
||||
keychain_path=""
|
||||
|
||||
write_env() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
if [[ -n "${GITHUB_ENV:-}" ]]; then
|
||||
echo "${key}=${value}" >> "$GITHUB_ENV"
|
||||
fi
|
||||
}
|
||||
|
||||
install_profile_file() {
|
||||
local source="$1"
|
||||
[[ -f "$source" ]] || return 0
|
||||
|
||||
local uuid
|
||||
uuid="$(security cms -D -i "$source" 2>/dev/null | plutil -extract UUID raw -o - - 2>/dev/null || true)"
|
||||
if [[ -z "$uuid" ]]; then
|
||||
uuid="$(basename "$source")"
|
||||
fi
|
||||
|
||||
local profiles_dir="$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||
mkdir -p "$profiles_dir"
|
||||
cp "$source" "${profiles_dir}/${uuid}.provisionprofile"
|
||||
echo "Installed provisioning profile ${uuid}."
|
||||
}
|
||||
|
||||
install_profile_base64() {
|
||||
local name="$1"
|
||||
local value="${!name:-}"
|
||||
[[ -n "$value" ]] || return 0
|
||||
|
||||
local out="${RUNNER_TEMP:-/tmp}/${name}.provisionprofile"
|
||||
printf '%s' "$value" | base64 --decode > "$out"
|
||||
install_profile_file "$out"
|
||||
}
|
||||
|
||||
install_profile_env_path() {
|
||||
local name="$1"
|
||||
local path="${!name:-}"
|
||||
[[ -n "$path" ]] || return 0
|
||||
if [[ ! -f "$path" ]]; then
|
||||
echo "::error ::${name} points to a missing provisioning profile: ${path}" >&2
|
||||
exit 1
|
||||
fi
|
||||
install_profile_file "$path"
|
||||
}
|
||||
|
||||
for profile in \
|
||||
Apple/Profiles/*.provisionprofile \
|
||||
Apple/Profiles/*.mobileprovision \
|
||||
.forgejo/actions/export/*.provisionprofile \
|
||||
.forgejo/actions/export/*.mobileprovision; do
|
||||
[[ -e "$profile" ]] || continue
|
||||
install_profile_file "$profile"
|
||||
done
|
||||
install_profile_env_path APPLE_PROVISIONING_PROFILE_PATH
|
||||
install_profile_env_path APPLE_NETWORK_EXTENSION_PROVISIONING_PROFILE_PATH
|
||||
install_profile_env_path APPLE_MACOS_PROVISIONING_PROFILE_PATH
|
||||
install_profile_base64 APPLE_PROVISIONING_PROFILE_BASE64
|
||||
install_profile_base64 APPLE_NETWORK_EXTENSION_PROVISIONING_PROFILE_BASE64
|
||||
install_profile_base64 APPLE_MACOS_PROVISIONING_PROFILE_BASE64
|
||||
|
||||
cert_base64="${APPLE_SIGNING_CERTIFICATE_BASE64:-${APPLE_DISTRIBUTION_CERTIFICATE_BASE64:-}}"
|
||||
cert_path_env="${APPLE_SIGNING_CERTIFICATE_PATH:-${APPLE_DISTRIBUTION_CERTIFICATE_PATH:-}}"
|
||||
cert_password="${APPLE_SIGNING_CERTIFICATE_PASSWORD:-${APPLE_DISTRIBUTION_CERTIFICATE_PASSWORD:-}}"
|
||||
keychain_password="${APPLE_KEYCHAIN_PASSWORD:-${cert_password}}"
|
||||
|
||||
if [[ -n "$cert_base64" || -n "$cert_path_env" ]]; then
|
||||
if [[ -z "$cert_password" ]]; then
|
||||
echo "::error ::APPLE_SIGNING_CERTIFICATE_PASSWORD is required when Apple signing certificate material is set." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cert_path="${RUNNER_TEMP:-/tmp}/burrow-apple-signing.p12"
|
||||
keychain_path="$HOME/Library/Keychains/BurrowRelease.keychain-db"
|
||||
if [[ -n "$cert_path_env" ]]; then
|
||||
if [[ ! -f "$cert_path_env" ]]; then
|
||||
echo "::error ::APPLE_SIGNING_CERTIFICATE_PATH points to a missing file: ${cert_path_env}" >&2
|
||||
exit 1
|
||||
fi
|
||||
cp "$cert_path_env" "$cert_path"
|
||||
else
|
||||
printf '%s' "$cert_base64" | base64 --decode > "$cert_path"
|
||||
fi
|
||||
|
||||
security create-keychain -p "$keychain_password" "$keychain_path" || true
|
||||
security set-keychain-settings -lut 21600 "$keychain_path"
|
||||
security unlock-keychain -p "$keychain_password" "$keychain_path"
|
||||
security import "$cert_path" \
|
||||
-k "$keychain_path" \
|
||||
-P "$cert_password" \
|
||||
-T /usr/bin/codesign \
|
||||
-T /usr/bin/security \
|
||||
-T /usr/bin/productbuild \
|
||||
-T /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$keychain_password" "$keychain_path" >/dev/null
|
||||
|
||||
existing="$(security list-keychains -d user | sed 's/[ "]//g' || true)"
|
||||
if ! printf '%s\n' "$existing" | grep -Fxq "$keychain_path"; then
|
||||
security list-keychains -d user -s "$keychain_path" $existing
|
||||
fi
|
||||
|
||||
signing_ready=1
|
||||
write_env BURROW_APPLE_KEYCHAIN_PATH "$keychain_path"
|
||||
else
|
||||
identities="$(security find-identity -v -p codesigning 2>/dev/null || true)"
|
||||
if printf '%s\n' "$identities" | grep -Eq '"(Apple Distribution|Developer ID Application):'; then
|
||||
signing_ready=1
|
||||
fi
|
||||
fi
|
||||
|
||||
write_env BURROW_APPLE_SIGNING_READY "$signing_ready"
|
||||
if [[ "$signing_ready" == "1" ]]; then
|
||||
echo "Apple signing assets are available."
|
||||
else
|
||||
echo "::notice ::Apple signing certificate is not configured; release lane will build unsigned validation artifacts only."
|
||||
fi
|
||||
225
Scripts/ci/nscloud-cache.sh
Executable file
225
Scripts/ci/nscloud-cache.sh
Executable file
|
|
@ -0,0 +1,225 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ "$#" -eq 0 ]]; then
|
||||
echo "No cache modes specified; skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
want_bazel_cache=0
|
||||
for mode in "$@"; do
|
||||
if [[ "$mode" == "bazel" ]]; then
|
||||
want_bazel_cache=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
portable_mkdir() {
|
||||
hash -r >/dev/null 2>&1 || true
|
||||
command -p mkdir "$@"
|
||||
}
|
||||
|
||||
append_bazel_storage_cache() {
|
||||
local bazelrc_path="$1"
|
||||
local cache_root="${BURROW_BAZEL_STORAGE_CACHE:-}"
|
||||
if [[ -z "$cache_root" && -n "${NSC_CACHE_PATH:-}" ]]; then
|
||||
cache_root="${NSC_CACHE_PATH}/bazel"
|
||||
fi
|
||||
if [[ -z "$cache_root" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
portable_mkdir -p "${cache_root}/disk" "${cache_root}/repository"
|
||||
{
|
||||
echo "build --disk_cache=${cache_root}/disk"
|
||||
echo "build --repository_cache=${cache_root}/repository"
|
||||
} >> "$bazelrc_path"
|
||||
echo "Namespace Bazel storage cache configured: ${cache_root}"
|
||||
}
|
||||
|
||||
configure_bazel_remote_cache() {
|
||||
if [[ "$want_bazel_cache" != "1" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
export BURROW_BAZEL_NSCCACHE_ACTIVE=0
|
||||
if ! command -v nsc >/dev/null 2>&1; then
|
||||
if bash Scripts/ci/ensure-nsc.sh; then
|
||||
nsc_bin_dir="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/nsc-bin"
|
||||
if [[ -x "${nsc_bin_dir}/nsc" ]]; then
|
||||
export PATH="${nsc_bin_dir}:$PATH"
|
||||
fi
|
||||
else
|
||||
echo "::warning ::nsc not found; using local Bazel disk cache only."
|
||||
if [[ -n "${GITHUB_ENV:-}" ]]; then
|
||||
echo "BURROW_BAZEL_NSCCACHE_ACTIVE=0" >> "$GITHUB_ENV"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
local bazelrc_path
|
||||
bazelrc_path="${NSC_BAZELRC_PATH:-${TMPDIR:-/tmp}/burrow-nsc-cache.bazelrc}"
|
||||
portable_mkdir -p "$(dirname "$bazelrc_path")"
|
||||
if nsc auth check-login >/dev/null 2>&1 && nsc cache bazel setup --bazelrc "$bazelrc_path" >/dev/null 2>&1; then
|
||||
append_bazel_storage_cache "$bazelrc_path"
|
||||
export BURROW_BAZEL_NSCCACHE_BAZELRC="$bazelrc_path"
|
||||
export BURROW_BAZEL_NSCCACHE_ACTIVE=1
|
||||
echo "Namespace Bazel cache configured: $bazelrc_path"
|
||||
if [[ -n "${GITHUB_ENV:-}" ]]; then
|
||||
{
|
||||
echo "BURROW_BAZEL_NSCCACHE_ACTIVE=1"
|
||||
echo "BURROW_BAZEL_NSCCACHE_BAZELRC=$bazelrc_path"
|
||||
echo "NSC_BAZELRC_PATH=$bazelrc_path"
|
||||
} >> "$GITHUB_ENV"
|
||||
fi
|
||||
else
|
||||
append_bazel_storage_cache "$bazelrc_path"
|
||||
echo "::warning ::Namespace Bazel remote cache unavailable; using local Bazel repository/disk cache."
|
||||
if [[ -n "${GITHUB_ENV:-}" ]]; then
|
||||
echo "BURROW_BAZEL_NSCCACHE_ACTIVE=0" >> "$GITHUB_ENV"
|
||||
echo "BURROW_BAZEL_NSCCACHE_BAZELRC=$bazelrc_path" >> "$GITHUB_ENV"
|
||||
echo "NSC_BAZELRC_PATH=$bazelrc_path" >> "$GITHUB_ENV"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
cache_root="${NSC_CACHE_PATH:-}"
|
||||
if [[ -z "$cache_root" ]]; then
|
||||
ensure_dir() {
|
||||
local dir="$1"
|
||||
if portable_mkdir -p "$dir" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
if [[ "$(id -u)" != "0" ]] && command -v sudo >/dev/null 2>&1 && sudo -n true >/dev/null 2>&1; then
|
||||
sudo mkdir -p "$dir" >/dev/null 2>&1 || return 1
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
if [[ "$(uname -s 2>/dev/null || true)" == "Linux" ]]; then
|
||||
tmp_root="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/burrow-nscloud-cache"
|
||||
for candidate in "/cache/nscloud" "$tmp_root" "$PWD/.nscloud-cache"; do
|
||||
if ensure_dir "$candidate"; then
|
||||
cache_root="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
elif ensure_dir "$PWD/.nscloud-cache"; then
|
||||
cache_root="$PWD/.nscloud-cache"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$cache_root" ]]; then
|
||||
echo "NSCloud cache volume not configured (NSC_CACHE_PATH missing); skipping."
|
||||
configure_bazel_remote_cache
|
||||
exit 0
|
||||
fi
|
||||
|
||||
export NSC_CACHE_PATH="$cache_root"
|
||||
if [[ -n "${GITHUB_ENV:-}" ]]; then
|
||||
echo "NSC_CACHE_PATH=$cache_root" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
spacectl_bin_dir="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/spacectl-bin"
|
||||
if ! command -v spacectl >/dev/null 2>&1; then
|
||||
if ! bash Scripts/ci/ensure-spacectl.sh; then
|
||||
echo "::warning ::Unable to install spacectl; skipping filesystem cache mount."
|
||||
configure_bazel_remote_cache
|
||||
exit 0
|
||||
fi
|
||||
if [[ -x "${spacectl_bin_dir}/spacectl" ]]; then
|
||||
export PATH="${spacectl_bin_dir}:$PATH"
|
||||
fi
|
||||
fi
|
||||
|
||||
mode_args=()
|
||||
for mode in "$@"; do
|
||||
[[ -z "$mode" ]] && continue
|
||||
if [[ "$mode" == "bazel" ]]; then
|
||||
continue
|
||||
fi
|
||||
if [[ "$mode" == "nix" && "${BURROW_NSC_DIRECT_NIX_VOLUME:-0}" == "1" ]]; then
|
||||
echo "Namespace Nix cache volume is mounted directly at /nix; skipping spacectl nix mount."
|
||||
continue
|
||||
fi
|
||||
if [[ "$mode" == "nix" && "$(uname -s)" == "Darwin" ]]; then
|
||||
echo "Skipping late Nix cache mount on macOS; using existing Nix profile and Bazel cache."
|
||||
continue
|
||||
fi
|
||||
mode_args+=("--mode=${mode}")
|
||||
done
|
||||
|
||||
if [[ "${#mode_args[@]}" -eq 0 ]]; then
|
||||
echo "No filesystem cache modes require mounting; skipping spacectl cache mount."
|
||||
configure_bazel_remote_cache
|
||||
exit 0
|
||||
fi
|
||||
|
||||
portable_mkdir -p "$cache_root" 2>/dev/null || true
|
||||
|
||||
args=(-o json cache mount "--dry_run=false" "--cache_root=${cache_root}" "${mode_args[@]}")
|
||||
echo "Mounting NSCloud cache: spacectl ${args[*]//${cache_root}/<cache_root>}"
|
||||
|
||||
spacectl_bin="$(command -v spacectl)"
|
||||
run_mount=("$spacectl_bin" "${args[@]}")
|
||||
if [[ "$(id -u)" != "0" ]]; then
|
||||
if ! command -v sudo >/dev/null 2>&1 || ! sudo -n true >/dev/null 2>&1; then
|
||||
echo "::warning ::spacectl cache mount requires passwordless sudo; skipping filesystem cache."
|
||||
configure_bazel_remote_cache
|
||||
exit 0
|
||||
fi
|
||||
run_mount=(sudo -n env "PATH=$PATH" "$spacectl_bin" "${args[@]}")
|
||||
fi
|
||||
|
||||
tmp_err="$(mktemp "${TMPDIR:-/tmp}/spacectl.XXXXXX")"
|
||||
cleanup() {
|
||||
rm -f "$tmp_err"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
json=""
|
||||
if ! json="$("${run_mount[@]}" 2>"$tmp_err")"; then
|
||||
echo "::warning ::spacectl cache mount failed; skipping filesystem cache."
|
||||
if [[ -n "$json" ]]; then
|
||||
printf '%s\n' "$json" || true
|
||||
fi
|
||||
if [[ -s "$tmp_err" ]]; then
|
||||
sed -n '1,200p' "$tmp_err" || true
|
||||
fi
|
||||
configure_bazel_remote_cache
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
NSC_MOUNT_JSON="$json" python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
raw = os.environ.get("NSC_MOUNT_JSON", "").strip()
|
||||
if not raw:
|
||||
sys.exit(0)
|
||||
|
||||
try:
|
||||
payload = json.loads(raw)
|
||||
except Exception as exc:
|
||||
print(f"::warning ::Unable to parse spacectl JSON output; skipping cache env export: {exc}")
|
||||
sys.exit(0)
|
||||
|
||||
if payload.get("error"):
|
||||
print(f"::warning ::spacectl cache mount skipped: {payload.get('message') or 'error=true'}")
|
||||
sys.exit(0)
|
||||
|
||||
add_envs = (payload.get("output") or {}).get("add_envs") or {}
|
||||
gh_env = os.environ.get("GITHUB_ENV")
|
||||
if gh_env and isinstance(add_envs, dict):
|
||||
with open(gh_env, "a", encoding="utf-8") as f:
|
||||
for key, value in add_envs.items():
|
||||
if key and value is not None:
|
||||
f.write(f"{key}={value}\n")
|
||||
PY
|
||||
fi
|
||||
|
||||
configure_bazel_remote_cache
|
||||
287
Scripts/ci/package-apple-artifacts.sh
Executable file
287
Scripts/ci/package-apple-artifacts.sh
Executable file
|
|
@ -0,0 +1,287 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "$repo_root"
|
||||
|
||||
platform="${1:-all}"
|
||||
build_number="${BUILD_NUMBER:-${RELEASE_BUILD_NUMBER:-${RELEASE_REF:-manual}}}"
|
||||
out_root="${BURROW_RELEASE_OUT:-${repo_root}/dist/builds/${build_number}}"
|
||||
apple_root="${out_root}/apple"
|
||||
archives_root="${apple_root}/archives"
|
||||
derived_data="${BURROW_DERIVED_DATA_PATH:-${repo_root}/.derived-data/apple}"
|
||||
source_packages="${BURROW_SWIFTPM_CACHE_PATH:-${XDG_CACHE_HOME:-${HOME}/.cache}/burrow/swiftpm}"
|
||||
project="Apple/Burrow.xcodeproj"
|
||||
scheme="${BURROW_APPLE_SCHEME:-App}"
|
||||
bundle_id="${BURROW_APPLE_BUNDLE_ID:-com.hackclub.burrow}"
|
||||
network_extension_bundle_id="${BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID:-${bundle_id}.network}"
|
||||
signing_ready="${BURROW_APPLE_SIGNING_READY:-0}"
|
||||
require_signing="${BURROW_APPLE_REQUIRE_SIGNING:-false}"
|
||||
sparkle_feed_url="${BURROW_SPARKLE_FEED_URL:-https://releases.burrow.net/sparkle/appcast.xml}"
|
||||
sparkle_public_ed_key="${BURROW_SPARKLE_PUBLIC_ED_KEY:-Myv9ZNZT6YGKMtMezh52ra4WqaeEKc4VlvVU0evhJeI=}"
|
||||
sparkle_sign_with_kms="${BURROW_SPARKLE_SIGN_WITH_KMS:-false}"
|
||||
|
||||
mkdir -p "$apple_root" "$archives_root" "$derived_data" "$source_packages"
|
||||
|
||||
truthy() {
|
||||
case "${1:-}" in
|
||||
1|true|TRUE|True|yes|YES|Yes|y|Y|on|ON|On) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
sha256_file() {
|
||||
local file="$1"
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
shasum -a 256 "$file" > "${file}.sha256"
|
||||
else
|
||||
sha256sum "$file" > "${file}.sha256"
|
||||
fi
|
||||
}
|
||||
|
||||
write_version() {
|
||||
mkdir -p Apple/Configuration
|
||||
printf 'CURRENT_PROJECT_VERSION = %s\n' "$build_number" > Apple/Configuration/Version.xcconfig
|
||||
}
|
||||
|
||||
xcode_common_args=(
|
||||
-project "$project"
|
||||
-scheme "$scheme"
|
||||
-configuration Release
|
||||
-derivedDataPath "$derived_data"
|
||||
-clonedSourcePackagesDirPath "$source_packages"
|
||||
CURRENT_PROJECT_VERSION="$build_number"
|
||||
APP_BUNDLE_IDENTIFIER="$bundle_id"
|
||||
NETWORK_EXTENSION_BUNDLE_IDENTIFIER="$network_extension_bundle_id"
|
||||
BURROW_SPARKLE_FEED_URL="$sparkle_feed_url"
|
||||
BURROW_SPARKLE_PUBLIC_ED_KEY="$sparkle_public_ed_key"
|
||||
)
|
||||
|
||||
xcode_auth_args=()
|
||||
if [[ -n "${ASC_API_KEY_ID:-}" && -n "${ASC_API_ISSUER_ID:-}" && -n "${ASC_API_KEY_PATH:-}" && -f "${ASC_API_KEY_PATH:-}" ]]; then
|
||||
xcode_auth_args+=(
|
||||
-authenticationKeyID "$ASC_API_KEY_ID"
|
||||
-authenticationKeyIssuerID "$ASC_API_ISSUER_ID"
|
||||
-authenticationKeyPath "$ASC_API_KEY_PATH"
|
||||
)
|
||||
fi
|
||||
|
||||
if [[ -n "${BURROW_APPLE_KEYCHAIN_PATH:-}" ]]; then
|
||||
xcode_common_args+=(OTHER_CODE_SIGN_FLAGS="--keychain ${BURROW_APPLE_KEYCHAIN_PATH}")
|
||||
fi
|
||||
|
||||
require_xcodebuild() {
|
||||
if ! command -v xcodebuild >/dev/null 2>&1; then
|
||||
echo "xcodebuild is required for Apple release artifacts" >&2
|
||||
exit 1
|
||||
fi
|
||||
xcodebuild -version
|
||||
xcrun --find swiftc >/dev/null
|
||||
}
|
||||
|
||||
unsigned_note() {
|
||||
local platform_name="$1"
|
||||
local artifact_name="$2"
|
||||
{
|
||||
echo "Burrow ${platform_name} signing assets were not available."
|
||||
echo
|
||||
echo "The lane built an unsigned validation artifact instead of an uploadable release artifact."
|
||||
echo "Seal the Apple release credentials with agenix and let CI sync provisioning profiles from App Store Connect to produce ${artifact_name}."
|
||||
} > "${apple_root}/README-${platform_name}-unsigned.txt"
|
||||
}
|
||||
|
||||
ensure_signing_or_note() {
|
||||
local platform_name="$1"
|
||||
local artifact_name="$2"
|
||||
if [[ "$signing_ready" == "1" ]]; then
|
||||
return 0
|
||||
fi
|
||||
if truthy "$require_signing"; then
|
||||
echo "Apple signing is required for ${platform_name}, but signing assets are not configured." >&2
|
||||
exit 1
|
||||
fi
|
||||
unsigned_note "$platform_name" "$artifact_name"
|
||||
return 1
|
||||
}
|
||||
|
||||
write_export_options() {
|
||||
local method="$1"
|
||||
local path="$2"
|
||||
cat > "$path" <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>method</key>
|
||||
<string>${method}</string>
|
||||
<key>destination</key>
|
||||
<string>export</string>
|
||||
<key>signingStyle</key>
|
||||
<string>automatic</string>
|
||||
<key>stripSwiftSymbols</key>
|
||||
<true/>
|
||||
<key>teamID</key>
|
||||
<string>${DEVELOPMENT_TEAM:-P6PV2R9443}</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
}
|
||||
|
||||
build_ios_unsigned() {
|
||||
local product_dir zip_path
|
||||
xcodebuild build \
|
||||
"${xcode_common_args[@]}" \
|
||||
-destination "generic/platform=iOS Simulator" \
|
||||
ARCHS=arm64 \
|
||||
ONLY_ACTIVE_ARCH=YES \
|
||||
CODE_SIGNING_ALLOWED=NO \
|
||||
CODE_SIGNING_REQUIRED=NO
|
||||
|
||||
product_dir="${derived_data}/Build/Products/Release-iphonesimulator"
|
||||
zip_path="${apple_root}/Burrow-iOS-simulator-${build_number}.unsigned.zip"
|
||||
if [[ -d "${product_dir}/Burrow.app" ]]; then
|
||||
(cd "$product_dir" && zip -qr "$zip_path" Burrow.app)
|
||||
sha256_file "$zip_path"
|
||||
fi
|
||||
}
|
||||
|
||||
build_ios_release() {
|
||||
local archive_path export_dir export_options ipa
|
||||
if ! ensure_signing_or_note "iOS" "Burrow-iOS-${build_number}.ipa"; then
|
||||
build_ios_unsigned
|
||||
return 0
|
||||
fi
|
||||
|
||||
archive_path="${archives_root}/Burrow-iOS-${build_number}.xcarchive"
|
||||
export_dir="${apple_root}/ios-export"
|
||||
export_options="${apple_root}/ExportOptions-iOS.plist"
|
||||
write_export_options "app-store-connect" "$export_options"
|
||||
|
||||
xcodebuild archive \
|
||||
"${xcode_common_args[@]}" \
|
||||
-destination "generic/platform=iOS" \
|
||||
-archivePath "$archive_path" \
|
||||
ARCHS=arm64 \
|
||||
-allowProvisioningUpdates \
|
||||
"${xcode_auth_args[@]}"
|
||||
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath "$archive_path" \
|
||||
-exportPath "$export_dir" \
|
||||
-exportOptionsPlist "$export_options" \
|
||||
-allowProvisioningUpdates \
|
||||
"${xcode_auth_args[@]}"
|
||||
|
||||
ipa="$(find "$export_dir" -maxdepth 1 -type f -name '*.ipa' | sort | head -n 1 || true)"
|
||||
if [[ -z "$ipa" ]]; then
|
||||
echo "iOS export completed but did not produce an IPA" >&2
|
||||
exit 1
|
||||
fi
|
||||
cp "$ipa" "${apple_root}/Burrow-iOS-${build_number}.ipa"
|
||||
sha256_file "${apple_root}/Burrow-iOS-${build_number}.ipa"
|
||||
}
|
||||
|
||||
build_macos_unsigned() {
|
||||
local product_dir zip_path
|
||||
xcodebuild build \
|
||||
"${xcode_common_args[@]}" \
|
||||
-destination "platform=macOS" \
|
||||
CODE_SIGNING_ALLOWED=NO \
|
||||
CODE_SIGNING_REQUIRED=NO
|
||||
|
||||
product_dir="${derived_data}/Build/Products/Release"
|
||||
zip_path="${apple_root}/Burrow-macOS-${build_number}.unsigned.zip"
|
||||
if [[ -d "${product_dir}/Burrow.app" ]]; then
|
||||
ditto -c -k --keepParent "${product_dir}/Burrow.app" "$zip_path"
|
||||
sha256_file "$zip_path"
|
||||
fi
|
||||
}
|
||||
|
||||
build_macos_release() {
|
||||
local archive_path export_dir export_options app dmg channel sparkle_dir length pubdate url
|
||||
if ! ensure_signing_or_note "macOS" "Burrow-macOS-${build_number}.dmg"; then
|
||||
build_macos_unsigned
|
||||
return 0
|
||||
fi
|
||||
|
||||
archive_path="${archives_root}/Burrow-macOS-${build_number}.xcarchive"
|
||||
export_dir="${apple_root}/macos-export"
|
||||
export_options="${apple_root}/ExportOptions-macOS.plist"
|
||||
write_export_options "${BURROW_MACOS_EXPORT_METHOD:-developer-id}" "$export_options"
|
||||
|
||||
xcodebuild archive \
|
||||
"${xcode_common_args[@]}" \
|
||||
-destination "generic/platform=macOS" \
|
||||
-archivePath "$archive_path" \
|
||||
-allowProvisioningUpdates \
|
||||
"${xcode_auth_args[@]}"
|
||||
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath "$archive_path" \
|
||||
-exportPath "$export_dir" \
|
||||
-exportOptionsPlist "$export_options" \
|
||||
-allowProvisioningUpdates \
|
||||
"${xcode_auth_args[@]}"
|
||||
|
||||
app="$(find "$export_dir" -maxdepth 2 -type d -name 'Burrow.app' | sort | head -n 1 || true)"
|
||||
if [[ -z "$app" ]]; then
|
||||
echo "macOS export completed but did not produce Burrow.app" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dmg="${apple_root}/Burrow-macOS-${build_number}.dmg"
|
||||
rm -f "$dmg"
|
||||
hdiutil create -volname "Burrow ${build_number}" -srcfolder "$app" -ov -format UDZO "$dmg"
|
||||
sha256_file "$dmg"
|
||||
|
||||
channel="${SPARKLE_CHANNEL:-build-${build_number}}"
|
||||
sparkle_dir="${out_root}/sparkle/${channel}"
|
||||
mkdir -p "$sparkle_dir"
|
||||
cp "$dmg" "$sparkle_dir/"
|
||||
length="$(wc -c < "$dmg" | tr -d ' ')"
|
||||
pubdate="$(LC_ALL=C date -u '+%a, %d %b %Y %H:%M:%S +0000')"
|
||||
url="${SPARKLE_DOWNLOAD_PREFIX:-https://releases.burrow.net/sparkle}/${channel}/$(basename "$dmg")"
|
||||
cat > "${sparkle_dir}/appcast.xml" <<EOF
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
|
||||
<channel>
|
||||
<title>Burrow ${channel}</title>
|
||||
<item>
|
||||
<title>Burrow ${build_number}</title>
|
||||
<pubDate>${pubdate}</pubDate>
|
||||
<sparkle:version>${build_number}</sparkle:version>
|
||||
<sparkle:shortVersionString>0.1</sparkle:shortVersionString>
|
||||
<enclosure url="${url}" sparkle:version="${build_number}" length="${length}" type="application/octet-stream" />
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
EOF
|
||||
|
||||
if truthy "$sparkle_sign_with_kms"; then
|
||||
Scripts/sparkle/sign-appcast-kms.py \
|
||||
--appcast "${sparkle_dir}/appcast.xml" \
|
||||
--artifact-dir "$sparkle_dir"
|
||||
fi
|
||||
}
|
||||
|
||||
require_xcodebuild
|
||||
write_version
|
||||
|
||||
case "$platform" in
|
||||
all)
|
||||
build_ios_release
|
||||
build_macos_release
|
||||
;;
|
||||
ios)
|
||||
build_ios_release
|
||||
;;
|
||||
macos)
|
||||
build_macos_release
|
||||
;;
|
||||
*)
|
||||
echo "unknown Apple release platform: $platform" >&2
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
|
||||
find "$out_root" -type f -print | sort
|
||||
332
Scripts/ci/package-release-artifacts.sh
Executable file
332
Scripts/ci/package-release-artifacts.sh
Executable file
|
|
@ -0,0 +1,332 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
platform="${1:-all}"
|
||||
build_number="${BUILD_NUMBER:-${RELEASE_BUILD_NUMBER:-${RELEASE_REF:-manual}}}"
|
||||
out_root="${BURROW_RELEASE_OUT:-${repo_root}/dist/builds/${build_number}}"
|
||||
|
||||
mkdir -p "${out_root}"
|
||||
|
||||
sha256_file() {
|
||||
local file="$1"
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
shasum -a 256 "$file" > "${file}.sha256"
|
||||
else
|
||||
sha256sum "$file" > "${file}.sha256"
|
||||
fi
|
||||
}
|
||||
|
||||
burrow_crate_version() {
|
||||
awk -F '"' '/^version = / { print $2; exit }' burrow/Cargo.toml
|
||||
}
|
||||
|
||||
package_release_suffix() {
|
||||
printf '%s' "$build_number" | tr -c 'A-Za-z0-9.+~' '.'
|
||||
}
|
||||
|
||||
arch_package_suffix() {
|
||||
printf '%s' "$build_number" | tr -c 'A-Za-z0-9.+_' '.'
|
||||
}
|
||||
|
||||
deb_arch_for_target() {
|
||||
case "$1" in
|
||||
x86_64-unknown-linux-gnu) printf 'amd64' ;;
|
||||
aarch64-unknown-linux-gnu) printf 'arm64' ;;
|
||||
riscv64gc-unknown-linux-gnu) printf 'riscv64' ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
rpm_arch_for_target() {
|
||||
case "$1" in
|
||||
x86_64-unknown-linux-gnu) printf 'x86_64' ;;
|
||||
aarch64-unknown-linux-gnu) printf 'aarch64' ;;
|
||||
riscv64gc-unknown-linux-gnu) printf 'riscv64' ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
pacman_arch_for_target() {
|
||||
case "$1" in
|
||||
x86_64-unknown-linux-gnu) printf 'x86_64' ;;
|
||||
aarch64-unknown-linux-gnu) printf 'aarch64' ;;
|
||||
riscv64gc-unknown-linux-gnu) printf 'riscv64' ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
copy_license_readme() {
|
||||
local dst="$1"
|
||||
cp README.md "$dst/README.md"
|
||||
if [[ -f LICENSE ]]; then
|
||||
cp LICENSE "$dst/LICENSE"
|
||||
elif [[ -f LICENSE.md ]]; then
|
||||
cp LICENSE.md "$dst/LICENSE.md"
|
||||
fi
|
||||
}
|
||||
|
||||
package_deb() {
|
||||
local target binary deb_arch pkg_version suffix staging deb_file installed_size
|
||||
target="$1"
|
||||
binary="$2"
|
||||
if ! deb_arch="$(deb_arch_for_target "$target")"; then
|
||||
echo "warning: no Debian architecture mapping for ${target}; skipping .deb package" >&2
|
||||
return 0
|
||||
fi
|
||||
if ! command -v dpkg-deb >/dev/null 2>&1; then
|
||||
echo "warning: dpkg-deb is not available; skipping .deb package" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
suffix="$(package_release_suffix)"
|
||||
pkg_version="${BURROW_PACKAGE_VERSION:-$(burrow_crate_version)+${suffix}}"
|
||||
staging="${out_root}/linux/pkg-deb/burrow_${pkg_version}_${deb_arch}"
|
||||
rm -rf "$staging"
|
||||
mkdir -p \
|
||||
"$staging/DEBIAN" \
|
||||
"$staging/usr/bin" \
|
||||
"$staging/usr/lib/systemd/system" \
|
||||
"$staging/usr/share/doc/burrow" \
|
||||
"$staging/usr/share/licenses/burrow"
|
||||
|
||||
install -m 0755 "$binary" "$staging/usr/bin/burrow"
|
||||
install -m 0644 systemd/burrow.service "$staging/usr/lib/systemd/system/burrow.service"
|
||||
install -m 0644 systemd/burrow.socket "$staging/usr/lib/systemd/system/burrow.socket"
|
||||
install -m 0644 README.md "$staging/usr/share/doc/burrow/README.md"
|
||||
install -m 0644 LICENSE.md "$staging/usr/share/licenses/burrow/LICENSE.md"
|
||||
installed_size="$(du -sk "$staging/usr" | awk '{ print $1 }')"
|
||||
|
||||
cat > "$staging/DEBIAN/control" <<EOF
|
||||
Package: burrow
|
||||
Version: ${pkg_version}
|
||||
Section: net
|
||||
Priority: optional
|
||||
Architecture: ${deb_arch}
|
||||
Maintainer: Burrow Release Automation <packages@burrow.net>
|
||||
Installed-Size: ${installed_size}
|
||||
Depends: systemd
|
||||
Description: Burrow VPN daemon and CLI
|
||||
Burrow installs the daemon entrypoint and systemd socket used by the desktop UI.
|
||||
EOF
|
||||
|
||||
deb_file="${out_root}/linux/burrow_${pkg_version}_${deb_arch}.deb"
|
||||
dpkg-deb --build --root-owner-group "$staging" "$deb_file"
|
||||
sha256_file "$deb_file"
|
||||
}
|
||||
|
||||
package_rpm() {
|
||||
local target binary rpm_arch
|
||||
target="$1"
|
||||
binary="$2"
|
||||
if ! rpm_arch="$(rpm_arch_for_target "$target")"; then
|
||||
echo "warning: no RPM architecture mapping for ${target}; skipping .rpm package" >&2
|
||||
return 0
|
||||
fi
|
||||
if ! command -v cargo-generate-rpm >/dev/null 2>&1; then
|
||||
echo "warning: cargo-generate-rpm is not available; skipping .rpm package" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p target/release
|
||||
cp "$binary" target/release/burrow
|
||||
if cargo generate-rpm -p burrow; then
|
||||
find target/generate-rpm -type f -name '*.rpm' -exec cp {} "${out_root}/linux/" \; 2>/dev/null || true
|
||||
find "${out_root}/linux" -maxdepth 1 -type f -name '*.rpm' -print | while read -r rpm; do
|
||||
sha256_file "$rpm"
|
||||
done
|
||||
else
|
||||
echo "warning: cargo-generate-rpm failed for ${rpm_arch}; skipping .rpm package" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
package_pacman() {
|
||||
local target binary pacman_arch pkg_version pkgrel work source_sum service_sum socket_sum readme_sum license_sum
|
||||
target="$1"
|
||||
binary="$2"
|
||||
if ! pacman_arch="$(pacman_arch_for_target "$target")"; then
|
||||
echo "warning: no pacman architecture mapping for ${target}; skipping Arch package" >&2
|
||||
return 0
|
||||
fi
|
||||
if ! command -v makepkg >/dev/null 2>&1; then
|
||||
echo "warning: makepkg is not available; skipping Arch package" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
pkg_version="${BURROW_ARCH_PKGVER:-$(burrow_crate_version)_$(arch_package_suffix)}"
|
||||
pkgrel="${BURROW_ARCH_PKGREL:-1}"
|
||||
work="${out_root}/linux/pkg-arch/${pacman_arch}"
|
||||
rm -rf "$work"
|
||||
mkdir -p "$work"
|
||||
install -m 0755 "$binary" "$work/burrow"
|
||||
install -m 0644 systemd/burrow.service "$work/burrow.service"
|
||||
install -m 0644 systemd/burrow.socket "$work/burrow.socket"
|
||||
install -m 0644 README.md "$work/README.md"
|
||||
install -m 0644 LICENSE.md "$work/LICENSE.md"
|
||||
|
||||
source_sum="$(sha256sum "$work/burrow" | awk '{ print $1 }')"
|
||||
service_sum="$(sha256sum "$work/burrow.service" | awk '{ print $1 }')"
|
||||
socket_sum="$(sha256sum "$work/burrow.socket" | awk '{ print $1 }')"
|
||||
readme_sum="$(sha256sum "$work/README.md" | awk '{ print $1 }')"
|
||||
license_sum="$(sha256sum "$work/LICENSE.md" | awk '{ print $1 }')"
|
||||
|
||||
cat > "$work/PKGBUILD" <<EOF
|
||||
# Maintainer: Burrow Release Automation <packages@burrow.net>
|
||||
pkgname=burrow
|
||||
pkgver=${pkg_version}
|
||||
pkgrel=${pkgrel}
|
||||
pkgdesc="Burrow VPN daemon and CLI"
|
||||
arch=("${pacman_arch}")
|
||||
url="https://git.burrow.net/hackclub/burrow"
|
||||
license=("GPL-3.0-or-later")
|
||||
depends=("systemd")
|
||||
source=("burrow" "burrow.service" "burrow.socket" "README.md" "LICENSE.md")
|
||||
sha256sums=("${source_sum}" "${service_sum}" "${socket_sum}" "${readme_sum}" "${license_sum}")
|
||||
|
||||
package() {
|
||||
install -Dm755 "\${srcdir}/burrow" "\${pkgdir}/usr/bin/burrow"
|
||||
install -Dm644 "\${srcdir}/burrow.service" "\${pkgdir}/usr/lib/systemd/system/burrow.service"
|
||||
install -Dm644 "\${srcdir}/burrow.socket" "\${pkgdir}/usr/lib/systemd/system/burrow.socket"
|
||||
install -Dm644 "\${srcdir}/README.md" "\${pkgdir}/usr/share/doc/burrow/README.md"
|
||||
install -Dm644 "\${srcdir}/LICENSE.md" "\${pkgdir}/usr/share/licenses/burrow/LICENSE.md"
|
||||
}
|
||||
EOF
|
||||
|
||||
(
|
||||
cd "$work"
|
||||
makepkg --force --nodeps --noconfirm
|
||||
)
|
||||
find "$work" -maxdepth 1 -type f -name '*.pkg.tar.*' -exec cp {} "${out_root}/linux/" \;
|
||||
find "${out_root}/linux" -maxdepth 1 -type f -name '*.pkg.tar.*' -print | while read -r package; do
|
||||
sha256_file "$package"
|
||||
done
|
||||
}
|
||||
|
||||
package_linux() {
|
||||
local target host archive staging
|
||||
host="$(rustc -vV | awk '/^host:/ { print $2 }')"
|
||||
target="${BURROW_LINUX_TARGET:-$host}"
|
||||
if [[ "$target" != *-linux-* && -z "${BURROW_LINUX_TARGET:-}" ]]; then
|
||||
mkdir -p "${out_root}/linux"
|
||||
echo "warning: host target ${target} is not Linux; skipping Linux artifact on this host" >&2
|
||||
printf 'Linux build was skipped on host target %s. Run this lane on a Linux Namespace runner or set BURROW_LINUX_TARGET explicitly.\n' "$target" > "${out_root}/linux/README.txt"
|
||||
return 0
|
||||
fi
|
||||
staging="${out_root}/linux/burrow-${build_number}-${target}"
|
||||
mkdir -p "$staging"
|
||||
|
||||
cargo build --locked --release -p burrow --bin burrow --target "$target"
|
||||
install -m 0755 "target/${target}/release/burrow" "$staging/burrow"
|
||||
copy_license_readme "$staging"
|
||||
|
||||
archive="${out_root}/linux/burrow-${build_number}-${target}.tar.gz"
|
||||
tar -C "${out_root}/linux" -czf "$archive" "$(basename "$staging")"
|
||||
sha256_file "$archive"
|
||||
|
||||
package_deb "$target" "target/${target}/release/burrow"
|
||||
package_rpm "$target" "target/${target}/release/burrow"
|
||||
package_pacman "$target" "target/${target}/release/burrow"
|
||||
}
|
||||
|
||||
package_windows_stub_unix() {
|
||||
local target exe archive staging
|
||||
target="${BURROW_WINDOWS_TARGET:-x86_64-pc-windows-gnu}"
|
||||
staging="${out_root}/windows/burrow-${build_number}-${target}"
|
||||
mkdir -p "$staging"
|
||||
|
||||
target_libdir="$(rustc --print target-libdir --target "$target" 2>/dev/null || true)"
|
||||
if [[ -z "$target_libdir" || ! -d "$target_libdir" ]]; then
|
||||
echo "warning: Rust target ${target} is not installed on this host" >&2
|
||||
printf 'Windows stub build for %s was skipped on this host. Use the Namespace Windows lane for the native artifact.\n' "$target" > "${out_root}/windows/README.txt"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if cargo build --locked --release -p burrow-windows-stub --target "$target"; then
|
||||
exe="target/${target}/release/burrow.exe"
|
||||
else
|
||||
echo "warning: unable to build the Windows release stub on this host" >&2
|
||||
printf 'Windows stub build for %s was skipped on this host. Use the Namespace Windows lane for the native artifact.\n' "$target" > "${out_root}/windows/README.txt"
|
||||
return 0
|
||||
fi
|
||||
|
||||
install -m 0755 "$exe" "$staging/burrow.exe"
|
||||
copy_license_readme "$staging"
|
||||
printf 'Burrow Windows stub build %s\n' "$build_number" > "$staging/README-Windows.txt"
|
||||
|
||||
archive="${out_root}/windows/burrow-${build_number}-${target}.zip"
|
||||
(cd "${out_root}/windows" && zip -qr "$archive" "$(basename "$staging")")
|
||||
sha256_file "$archive"
|
||||
}
|
||||
|
||||
package_sparkle_unsigned() {
|
||||
local macos_dir appcast channel dmg
|
||||
channel="${SPARKLE_CHANNEL:-build-${build_number}}"
|
||||
macos_dir="${out_root}/macos"
|
||||
appcast="${out_root}/sparkle/${channel}/appcast.xml"
|
||||
mkdir -p "$(dirname "$appcast")"
|
||||
|
||||
dmg="$(find "$macos_dir" -maxdepth 1 -type f -name '*.dmg' -print 2>/dev/null | sort | tail -n 1 || true)"
|
||||
if [[ -z "$dmg" ]]; then
|
||||
echo "No macOS DMG exists yet; Sparkle appcast generation is waiting on the macOS signed artifact." > "${out_root}/sparkle/${channel}/README.txt"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local name length pubdate url
|
||||
name="$(basename "$dmg")"
|
||||
length="$(wc -c < "$dmg" | tr -d ' ')"
|
||||
pubdate="$(LC_ALL=C date -u '+%a, %d %b %Y %H:%M:%S +0000')"
|
||||
url="${SPARKLE_DOWNLOAD_PREFIX:-https://releases.burrow.net/sparkle}/${channel}/${name}"
|
||||
cat > "$appcast" <<EOF
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
|
||||
<channel>
|
||||
<title>Burrow ${channel}</title>
|
||||
<item>
|
||||
<title>Burrow ${build_number}</title>
|
||||
<pubDate>${pubdate}</pubDate>
|
||||
<sparkle:version>${build_number}</sparkle:version>
|
||||
<sparkle:shortVersionString>0.1</sparkle:shortVersionString>
|
||||
<enclosure url="${url}" sparkle:version="${build_number}" length="${length}" type="application/octet-stream" />
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
EOF
|
||||
cp "$dmg" "${out_root}/sparkle/${channel}/"
|
||||
}
|
||||
|
||||
case "$platform" in
|
||||
all)
|
||||
package_linux
|
||||
package_windows_stub_unix
|
||||
if [[ "$(uname -s)" == "Darwin" ]] && command -v xcodebuild >/dev/null 2>&1; then
|
||||
Scripts/ci/package-apple-artifacts.sh all
|
||||
else
|
||||
mkdir -p "${out_root}/apple"
|
||||
printf 'Apple artifacts require a macOS runner with Xcode. The release workflow builds them in the dedicated Apple lane.\n' > "${out_root}/apple/README.txt"
|
||||
fi
|
||||
package_sparkle_unsigned
|
||||
;;
|
||||
linux)
|
||||
package_linux
|
||||
;;
|
||||
windows-stub|windows)
|
||||
package_windows_stub_unix
|
||||
;;
|
||||
sparkle)
|
||||
package_sparkle_unsigned
|
||||
;;
|
||||
apple)
|
||||
Scripts/ci/package-apple-artifacts.sh all
|
||||
;;
|
||||
ios|macos)
|
||||
Scripts/ci/package-apple-artifacts.sh "$platform"
|
||||
;;
|
||||
*)
|
||||
echo "unknown release platform: $platform" >&2
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
|
||||
find "$out_root" -type f -print | sort
|
||||
|
|
@ -7,7 +7,8 @@ set -euo pipefail
|
|||
: "${TOKEN:?TOKEN is required}"
|
||||
|
||||
release_api="${API_URL}/repos/${REPOSITORY}/releases"
|
||||
tag_api="${release_api}/tags/${RELEASE_TAG}"
|
||||
encoded_release_tag="$(python3 -c 'import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1], safe=""))' "${RELEASE_TAG}")"
|
||||
tag_api="${release_api}/tags/${encoded_release_tag}"
|
||||
release_json="$(mktemp)"
|
||||
create_json="$(mktemp)"
|
||||
trap 'rm -f "${release_json}" "${create_json}"' EXIT
|
||||
|
|
@ -50,6 +51,9 @@ if [[ -z "${release_id}" || "${release_id}" == "null" ]]; then
|
|||
fi
|
||||
|
||||
for file in dist/*; do
|
||||
if [[ ! -f "${file}" ]]; then
|
||||
continue
|
||||
fi
|
||||
name="$(basename "${file}")"
|
||||
asset_id="$(jq -r --arg name "${name}" '.assets[]? | select(.name == $name) | .id' "${release_json}" | head -n1)"
|
||||
if [[ -n "${asset_id}" ]]; then
|
||||
|
|
|
|||
49
Scripts/ci/publish-nix-cache.sh
Executable file
49
Scripts/ci/publish-nix-cache.sh
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
cache_server_name="${BURROW_NIX_CACHE_SERVER_NAME:-burrow}"
|
||||
cache_server_url="${BURROW_NIX_CACHE_SERVER_URL:-https://nix.burrow.net}"
|
||||
cache_name="${BURROW_NIX_CACHE_NAME:-burrow}"
|
||||
cache_ref="${cache_server_name}:${cache_name}"
|
||||
token="${BURROW_NIX_CACHE_PUSH_TOKEN:-}"
|
||||
|
||||
if [[ -z "$token" ]]; then
|
||||
if [[ "${BURROW_NIX_CACHE_REQUIRED:-false}" == "true" ]]; then
|
||||
echo "BURROW_NIX_CACHE_PUSH_TOKEN is required to publish Nix cache paths" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "::notice ::BURROW_NIX_CACHE_PUSH_TOKEN is not set; skipping Nix cache publish."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v attic >/dev/null 2>&1; then
|
||||
echo "attic is required to publish Nix cache paths" >&2
|
||||
exit 127
|
||||
fi
|
||||
|
||||
if ! command -v nix >/dev/null 2>&1; then
|
||||
echo "nix is required to build paths for cache publishing" >&2
|
||||
exit 127
|
||||
fi
|
||||
|
||||
attic login "$cache_server_name" "$cache_server_url" "$token" --set-default
|
||||
|
||||
push_inputs=("$@")
|
||||
if [[ "${#push_inputs[@]}" -eq 0 ]]; then
|
||||
read -r -a attrs <<< "${BURROW_NIX_CACHE_ATTRS:-.#burrow .#forgejo-nsc-dispatcher .#forgejo-nsc-autoscaler}"
|
||||
if [[ "${#attrs[@]}" -eq 0 ]]; then
|
||||
echo "no Nix attrs were supplied for cache publishing" >&2
|
||||
exit 1
|
||||
fi
|
||||
mapfile -t push_inputs < <(nix build --no-link --print-out-paths "${attrs[@]}")
|
||||
fi
|
||||
|
||||
if [[ "${#push_inputs[@]}" -eq 0 ]]; then
|
||||
echo "no Nix output paths were produced for cache publishing" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
attic push "$cache_ref" "${push_inputs[@]}"
|
||||
56
Scripts/ci/resolve-nix-bin.sh
Executable file
56
Scripts/ci/resolve-nix-bin.sh
Executable file
|
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
find_nix_bin() {
|
||||
local candidate
|
||||
for candidate in \
|
||||
"${NIX_BIN:-}" \
|
||||
"$(command -v nix 2>/dev/null || true)" \
|
||||
"${HOME:-}/.nix-profile/bin/nix" \
|
||||
"${HOME:-}/.local/state/nix/profile/bin/nix" \
|
||||
"/nix/var/nix/profiles/per-user/${USER:-runner}/profile/bin/nix" \
|
||||
/nix/var/nix/profiles/default/bin/nix \
|
||||
/run/current-system/sw/bin/nix \
|
||||
/etc/profiles/per-user/runner/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 \
|
||||
/nix/profile/bin/nix; do
|
||||
if [[ -n "$candidate" && -x "$candidate" ]]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
candidate="$(
|
||||
find \
|
||||
"${HOME:-/nonexistent}" \
|
||||
/Users/runner \
|
||||
/home/runner \
|
||||
/nix/var/nix/profiles \
|
||||
/etc/profiles/per-user \
|
||||
-path '*/bin/nix' -type f 2>/dev/null | head -n 1 || true
|
||||
)"
|
||||
if [[ -n "$candidate" && -x "$candidate" ]]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
if nix_bin="$(find_nix_bin)"; then
|
||||
printf '%s\n' "$nix_bin"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if bash Scripts/ci/ensure-nix.sh >/dev/null 2>&1; then
|
||||
if nix_bin="$(find_nix_bin)"; then
|
||||
printf '%s\n' "$nix_bin"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "::error ::nix is required but is not on PATH and no standard install path exists" >&2
|
||||
exit 1
|
||||
72
Scripts/ci/sync-apple-provisioning-profiles.sh
Executable file
72
Scripts/ci/sync-apple-provisioning-profiles.sh
Executable file
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "$repo_root"
|
||||
|
||||
platform="${1:-all}"
|
||||
out_dir="${BURROW_APPLE_PROFILES_OUT_DIR:-.forgejo/actions/export}"
|
||||
app_id="${BURROW_APPLE_BUNDLE_ID:-com.hackclub.burrow}"
|
||||
network_id="${BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID:-${app_id}.network}"
|
||||
replace_certificate_mismatch="${BURROW_APPLE_REPLACE_CERTIFICATE_MISMATCH:-false}"
|
||||
if [[ -n "${BURROW_IOS_DISTRIBUTION_CERTIFICATE_ID:-}" || -n "${BURROW_DEVELOPER_ID_CERTIFICATE_ID:-}" ]]; then
|
||||
replace_certificate_mismatch="${BURROW_APPLE_REPLACE_CERTIFICATE_MISMATCH:-true}"
|
||||
fi
|
||||
|
||||
key_id="${ASC_API_KEY_ID:-${APPSTORE_KEY_ID:-}}"
|
||||
issuer_id="${ASC_API_ISSUER_ID:-${APPSTORE_KEY_ISSUER_ID:-}}"
|
||||
key_path="${ASC_API_KEY_PATH:-${APPSTORE_KEY_PATH:-}}"
|
||||
key_base64="${ASC_API_KEY_BASE64:-}"
|
||||
|
||||
if [[ -z "$key_id" || -z "$issuer_id" ]]; then
|
||||
echo "::notice ::App Store Connect credentials are not available; provisioning profile sync skipped."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
temp_key=""
|
||||
cleanup() {
|
||||
[[ -z "$temp_key" ]] || rm -f "$temp_key"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ -z "$key_path" ]]; then
|
||||
if [[ -z "$key_base64" ]]; then
|
||||
echo "::notice ::ASC_API_KEY_PATH/ASC_API_KEY_BASE64 is not available; provisioning profile sync skipped."
|
||||
exit 0
|
||||
fi
|
||||
temp_key="$(mktemp "${RUNNER_TEMP:-/tmp}/burrow-asc-key.XXXXXX.p8")"
|
||||
printf '%s' "$key_base64" | base64 --decode > "$temp_key"
|
||||
chmod 600 "$temp_key"
|
||||
key_path="$temp_key"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$key_path" ]]; then
|
||||
echo "::error ::App Store Connect key path does not exist: ${key_path}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$out_dir"
|
||||
profile_args=(
|
||||
--platform "$platform"
|
||||
--key-id "$key_id"
|
||||
--issuer-id "$issuer_id"
|
||||
--key-file "$key_path"
|
||||
--out-dir "$out_dir"
|
||||
--app-id "$app_id"
|
||||
--network-id "$network_id"
|
||||
--create-missing-bundle-ids "${BURROW_APPLE_CREATE_MISSING_BUNDLE_IDS:-false}"
|
||||
--enable-capabilities "${BURROW_APPLE_ENABLE_CAPABILITIES:-false}"
|
||||
--replace-certificate-mismatch "$replace_certificate_mismatch"
|
||||
--bundle-platform "${BURROW_APPLE_BUNDLE_PLATFORM:-UNIVERSAL}"
|
||||
--associated-domains "${BURROW_APPLE_ASSOCIATED_DOMAINS:-applinks:burrow.rs?mode=developer,webcredentials:burrow.rs?mode=developer}"
|
||||
)
|
||||
|
||||
if [[ -n "${BURROW_IOS_DISTRIBUTION_CERTIFICATE_ID:-}" ]]; then
|
||||
profile_args+=(--ios-certificate-id "$BURROW_IOS_DISTRIBUTION_CERTIFICATE_ID")
|
||||
fi
|
||||
|
||||
if [[ -n "${BURROW_DEVELOPER_ID_CERTIFICATE_ID:-}" ]]; then
|
||||
profile_args+=(--macos-certificate-id "$BURROW_DEVELOPER_ID_CERTIFICATE_ID")
|
||||
fi
|
||||
|
||||
node Scripts/apple/sync-provisioning-profiles.mjs "${profile_args[@]}"
|
||||
35
Scripts/ci/upload-package-garage.sh
Executable file
35
Scripts/ci/upload-package-garage.sh
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
: "${BURROW_GARAGE_ENDPOINT:?BURROW_GARAGE_ENDPOINT is required}"
|
||||
: "${AWS_ACCESS_KEY_ID:?AWS_ACCESS_KEY_ID is required for Garage upload}"
|
||||
: "${AWS_SECRET_ACCESS_KEY:?AWS_SECRET_ACCESS_KEY is required for Garage upload}"
|
||||
|
||||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
source_dir="${BURROW_PACKAGE_REPOSITORY_DIR:-${repo_root}/publish/repositories}"
|
||||
|
||||
if [[ ! -d "$source_dir" ]]; then
|
||||
echo "package repository directory not found: $source_dir" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v aws >/dev/null 2>&1; then
|
||||
echo "aws CLI is required for Garage package repository upload" >&2
|
||||
exit 127
|
||||
fi
|
||||
|
||||
bucket="${BURROW_PACKAGE_GARAGE_BUCKET:-burrow-packages}"
|
||||
prefix="${BURROW_PACKAGE_GARAGE_PREFIX:-${BURROW_PACKAGE_GCS_PREFIX:-}}"
|
||||
region="${BURROW_GARAGE_REGION:-garage}"
|
||||
destination="s3://${bucket}"
|
||||
|
||||
if [[ -n "$prefix" ]]; then
|
||||
destination="${destination}/${prefix}"
|
||||
fi
|
||||
|
||||
export AWS_DEFAULT_REGION="$region"
|
||||
export AWS_EC2_METADATA_DISABLED=true
|
||||
|
||||
aws --endpoint-url "$BURROW_GARAGE_ENDPOINT" s3 sync "$source_dir" "$destination" --no-progress
|
||||
28
Scripts/ci/upload-package-repositories.sh
Executable file
28
Scripts/ci/upload-package-repositories.sh
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
prefix="${BURROW_PACKAGE_GCS_PREFIX:-}"
|
||||
source_dir="${BURROW_PACKAGE_REPOSITORY_DIR:-${repo_root}/publish/repositories}"
|
||||
|
||||
if [[ ! -d "$source_dir" ]]; then
|
||||
echo "package repository directory not found: $source_dir" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bucket="${BURROW_PACKAGE_GCS_BUCKET:-burrow-net-packages}"
|
||||
if ! command -v gcloud >/dev/null 2>&1; then
|
||||
echo "gcloud is required for GCS package repository upload" >&2
|
||||
exit 127
|
||||
fi
|
||||
gcloud auth list --filter=status:ACTIVE --format='value(account)' | grep -q . || {
|
||||
echo "gcloud has no active account; run Scripts/ci/google-wif-auth.sh first" >&2
|
||||
exit 1
|
||||
}
|
||||
destination="gs://${bucket}"
|
||||
if [[ -n "$prefix" ]]; then
|
||||
destination="${destination}/${prefix}"
|
||||
fi
|
||||
gcloud storage rsync --recursive "$source_dir" "$destination"
|
||||
39
Scripts/ci/upload-package-storage.sh
Executable file
39
Scripts/ci/upload-package-storage.sh
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
uploaded=0
|
||||
garage_configured=0
|
||||
|
||||
if [[ -n "${BURROW_GARAGE_ENDPOINT:-}" && -n "${AWS_ACCESS_KEY_ID:-}" && -n "${AWS_SECRET_ACCESS_KEY:-}" ]]; then
|
||||
garage_configured=1
|
||||
fi
|
||||
|
||||
if [[ "$garage_configured" == "1" ]]; then
|
||||
Scripts/ci/upload-package-garage.sh
|
||||
uploaded=1
|
||||
elif [[ "${BURROW_PACKAGE_REQUIRE_GARAGE:-false}" == "true" ]]; then
|
||||
echo "Garage package repository upload is required, but BURROW_GARAGE_ENDPOINT or AWS credentials are missing" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "::notice ::Garage package repository upload not configured; skipping primary S3-compatible target."
|
||||
fi
|
||||
|
||||
if [[ "${BURROW_PACKAGE_GCS_BACKUP:-true}" != "false" ]]; then
|
||||
if ! command -v gcloud >/dev/null 2>&1; then
|
||||
echo "gcloud is required for GCS package repository backup" >&2
|
||||
exit 127
|
||||
fi
|
||||
if ! gcloud auth list --filter=status:ACTIVE --format='value(account)' | grep -q .; then
|
||||
Scripts/ci/google-wif-auth.sh
|
||||
fi
|
||||
Scripts/ci/upload-package-repositories.sh
|
||||
uploaded=1
|
||||
fi
|
||||
|
||||
if [[ "$uploaded" != "1" ]]; then
|
||||
echo "no package repository storage targets were enabled" >&2
|
||||
exit 1
|
||||
fi
|
||||
31
Scripts/ci/upload-release-garage.sh
Executable file
31
Scripts/ci/upload-release-garage.sh
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
: "${BUILD_NUMBER:?BUILD_NUMBER is required}"
|
||||
: "${BURROW_GARAGE_ENDPOINT:?BURROW_GARAGE_ENDPOINT is required}"
|
||||
: "${AWS_ACCESS_KEY_ID:?AWS_ACCESS_KEY_ID is required for Garage upload}"
|
||||
: "${AWS_SECRET_ACCESS_KEY:?AWS_SECRET_ACCESS_KEY is required for Garage upload}"
|
||||
|
||||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
source_dir="${BURROW_RELEASE_OUT:-${repo_root}/dist/builds/${BUILD_NUMBER}}"
|
||||
|
||||
if [[ ! -d "$source_dir" ]]; then
|
||||
echo "release artifact directory not found: $source_dir" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v aws >/dev/null 2>&1; then
|
||||
echo "aws CLI is required for Garage release upload" >&2
|
||||
exit 127
|
||||
fi
|
||||
|
||||
bucket="${BURROW_RELEASE_GARAGE_BUCKET:-burrow-releases}"
|
||||
region="${BURROW_GARAGE_REGION:-garage}"
|
||||
destination="s3://${bucket}/builds/${BUILD_NUMBER}"
|
||||
|
||||
export AWS_DEFAULT_REGION="$region"
|
||||
export AWS_EC2_METADATA_DISABLED=true
|
||||
|
||||
aws --endpoint-url "$BURROW_GARAGE_ENDPOINT" s3 sync "$source_dir" "$destination" --no-progress
|
||||
25
Scripts/ci/upload-release-gcs.sh
Executable file
25
Scripts/ci/upload-release-gcs.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
: "${BUILD_NUMBER:?BUILD_NUMBER is required}"
|
||||
|
||||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
source_dir="${BURROW_RELEASE_OUT:-${repo_root}/dist/builds/${BUILD_NUMBER}}"
|
||||
|
||||
if [[ ! -d "$source_dir" ]]; then
|
||||
echo "release artifact directory not found: $source_dir" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bucket="${BURROW_RELEASE_GCS_BUCKET:-burrow-net-releases}"
|
||||
if ! command -v gcloud >/dev/null 2>&1; then
|
||||
echo "gcloud is required for GCS release upload" >&2
|
||||
exit 127
|
||||
fi
|
||||
gcloud auth list --filter=status:ACTIVE --format='value(account)' | grep -q . || {
|
||||
echo "gcloud has no active account; run Scripts/ci/google-wif-auth.sh first" >&2
|
||||
exit 1
|
||||
}
|
||||
gcloud storage rsync --recursive "$source_dir" "gs://${bucket}/builds/${BUILD_NUMBER}"
|
||||
41
Scripts/ci/upload-release-storage.sh
Executable file
41
Scripts/ci/upload-release-storage.sh
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
: "${BUILD_NUMBER:?BUILD_NUMBER is required}"
|
||||
|
||||
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
|
||||
uploaded=0
|
||||
garage_configured=0
|
||||
|
||||
if [[ -n "${BURROW_GARAGE_ENDPOINT:-}" && -n "${AWS_ACCESS_KEY_ID:-}" && -n "${AWS_SECRET_ACCESS_KEY:-}" ]]; then
|
||||
garage_configured=1
|
||||
fi
|
||||
|
||||
if [[ "$garage_configured" == "1" ]]; then
|
||||
Scripts/ci/upload-release-garage.sh
|
||||
uploaded=1
|
||||
elif [[ "${BURROW_RELEASE_REQUIRE_GARAGE:-false}" == "true" ]]; then
|
||||
echo "Garage release upload is required, but BURROW_GARAGE_ENDPOINT or AWS credentials are missing" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "::notice ::Garage release upload not configured; skipping primary S3-compatible target."
|
||||
fi
|
||||
|
||||
if [[ "${BURROW_RELEASE_GCS_BACKUP:-true}" != "false" ]]; then
|
||||
if ! command -v gcloud >/dev/null 2>&1; then
|
||||
echo "gcloud is required for GCS release backup" >&2
|
||||
exit 127
|
||||
fi
|
||||
if ! gcloud auth list --filter=status:ACTIVE --format='value(account)' | grep -q .; then
|
||||
Scripts/ci/google-wif-auth.sh
|
||||
fi
|
||||
Scripts/ci/upload-release-gcs.sh
|
||||
uploaded=1
|
||||
fi
|
||||
|
||||
if [[ "$uploaded" != "1" ]]; then
|
||||
echo "no release storage targets were enabled" >&2
|
||||
exit 1
|
||||
fi
|
||||
Loading…
Add table
Add a link
Reference in a new issue