fix(agent): retry cloud metadata location auth #58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Publish Agent Release | |
| on: | |
| push: | |
| branches: | |
| - main | |
| tags: | |
| - agent-v* | |
| paths: | |
| - .github/workflows/agent-release.yml | |
| - CHANGELOG.md | |
| - cmd/** | |
| - internal/** | |
| - scripts/** | |
| - go.mod | |
| - go.sum | |
| - README.md | |
| - config.example.json | |
| workflow_dispatch: | |
| inputs: | |
| release_version: | |
| description: Optional existing tagged release version such as 0.1.0 | |
| required: false | |
| type: string | |
| publish_latest: | |
| description: Also refresh the latest channel | |
| required: true | |
| default: true | |
| type: boolean | |
| concurrency: | |
| group: agent-release-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: write | |
| jobs: | |
| publish: | |
| name: Build and publish agent artifacts | |
| runs-on: ubuntu-latest | |
| env: | |
| AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} | |
| AWS_REGION: auto | |
| R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }} | |
| R2_BUCKET: ${{ secrets.R2_BUCKET }} | |
| PUBLIC_RELEASE_BASE_URL: https://cdn.noderax.net/noderax-agent/releases | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: "1.22" | |
| - name: Verify dependencies | |
| run: go mod verify | |
| - name: Test | |
| run: go test ./... | |
| - name: Resolve release metadata | |
| id: release | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| trim() { | |
| local value="${1:-}" | |
| value="${value#"${value%%[![:space:]]*}"}" | |
| value="${value%"${value##*[![:space:]]}"}" | |
| printf '%s' "${value}" | |
| } | |
| release_version="" | |
| publish_latest="true" | |
| if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then | |
| release_version="$(trim "${{ inputs.release_version }}")" | |
| publish_latest="${{ inputs.publish_latest }}" | |
| elif [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then | |
| release_version="$(trim "${GITHUB_REF_NAME#agent-v}")" | |
| fi | |
| if [[ -n "${release_version}" && ! "${release_version}" =~ ^[A-Za-z0-9._-]+$ ]]; then | |
| echo "Release version must be URL-safe. Got: ${release_version}" >&2 | |
| exit 1 | |
| fi | |
| if [[ -n "${release_version}" ]]; then | |
| if ! git rev-parse --verify "refs/tags/agent-v${release_version}" >/dev/null 2>&1; then | |
| echo "Versioned releases must map to an existing git tag agent-v${release_version}." >&2 | |
| exit 1 | |
| fi | |
| fi | |
| binary_version="${release_version}" | |
| if [[ -z "${binary_version}" ]]; then | |
| binary_version="main-${GITHUB_SHA::7}" | |
| fi | |
| build_date="$(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| echo "release_version=${release_version}" >> "${GITHUB_OUTPUT}" | |
| echo "binary_version=${binary_version}" >> "${GITHUB_OUTPUT}" | |
| echo "publish_latest=${publish_latest}" >> "${GITHUB_OUTPUT}" | |
| echo "build_date=${build_date}" >> "${GITHUB_OUTPUT}" | |
| - name: Build release assets | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| chmod +x ./scripts/build-release.sh | |
| ./scripts/build-release.sh \ | |
| --version "${{ steps.release.outputs.binary_version }}" \ | |
| --commit "${GITHUB_SHA}" \ | |
| --build-date "${{ steps.release.outputs.build_date }}" \ | |
| --output-dir "./dist/release" | |
| - name: Generate tagged release metadata | |
| if: steps.release.outputs.release_version != '' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| version="${{ steps.release.outputs.release_version }}" | |
| download_base_url="${PUBLIC_RELEASE_BASE_URL}/${version}" | |
| go run ./cmd/release-metadata bundle \ | |
| --version "${version}" \ | |
| --commit "${GITHUB_SHA}" \ | |
| --published-at "${{ steps.release.outputs.build_date }}" \ | |
| --artifacts-dir "./dist/release" \ | |
| --download-base-url "${download_base_url}" \ | |
| --manifest-output "./dist/release/release-manifest.json" \ | |
| --release-notes-output "./dist/release/release-notes.md" \ | |
| --changelog "./CHANGELOG.md" | |
| if curl -fsSL "${PUBLIC_RELEASE_BASE_URL}/catalog.json" -o "./dist/release/catalog-existing.json"; then | |
| echo "Loaded existing CDN catalog." | |
| else | |
| printf '{\"releases\":[]}\n' > "./dist/release/catalog-existing.json" | |
| fi | |
| go run ./cmd/release-metadata catalog \ | |
| --manifest "./dist/release/release-manifest.json" \ | |
| --existing "./dist/release/catalog-existing.json" \ | |
| --output "./dist/release/catalog.json" | |
| - name: Show release assets | |
| run: ls -lah ./dist/release | |
| - name: Validate R2 configuration | |
| id: r2 | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| trim() { | |
| local value="${1:-}" | |
| value="${value#"${value%%[![:space:]]*}"}" | |
| value="${value%"${value##*[![:space:]]}"}" | |
| printf '%s' "${value}" | |
| } | |
| : "${AWS_ACCESS_KEY_ID:?Missing R2_ACCESS_KEY_ID secret}" | |
| : "${AWS_SECRET_ACCESS_KEY:?Missing R2_SECRET_ACCESS_KEY secret}" | |
| : "${R2_ACCOUNT_ID:?Missing R2_ACCOUNT_ID secret}" | |
| : "${R2_BUCKET:?Missing R2_BUCKET secret}" | |
| raw_bucket="$(trim "${R2_BUCKET}")" | |
| if [[ "${raw_bucket}" == s3://* ]]; then | |
| raw_bucket="${raw_bucket#s3://}" | |
| fi | |
| if [[ "${raw_bucket}" == http://* || "${raw_bucket}" == https://* ]]; then | |
| path_part="${raw_bucket#http://}" | |
| path_part="${path_part#https://}" | |
| if [[ "${path_part}" != */* ]]; then | |
| echo "R2_BUCKET must be the bucket name only, not a URL without a bucket path." >&2 | |
| echo "Example: media-assets" >&2 | |
| exit 1 | |
| fi | |
| raw_bucket="${path_part#*/}" | |
| fi | |
| raw_bucket="${raw_bucket#/}" | |
| raw_bucket="${raw_bucket%%/*}" | |
| if [[ -z "${raw_bucket}" ]]; then | |
| echo "Resolved R2 bucket name is empty. Set R2_BUCKET to your bucket name, for example media-assets." >&2 | |
| exit 1 | |
| fi | |
| if [[ ! "${raw_bucket}" =~ ^[a-zA-Z0-9._-]{1,255}$ ]]; then | |
| echo "Resolved R2 bucket name is invalid: ${raw_bucket}" >&2 | |
| echo "Set R2_BUCKET to the plain bucket name only, without https:// or extra path segments." >&2 | |
| exit 1 | |
| fi | |
| echo "bucket_name=${raw_bucket}" >> "${GITHUB_OUTPUT}" | |
| echo "endpoint_url=https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" >> "${GITHUB_OUTPUT}" | |
| aws --version | |
| - name: Verify R2 bucket exists | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| endpoint="${{ steps.r2.outputs.endpoint_url }}" | |
| bucket="${{ steps.r2.outputs.bucket_name }}" | |
| if ! aws s3api head-bucket --bucket "${bucket}" --endpoint-url "${endpoint}" >/dev/null 2>&1; then | |
| echo "R2 bucket '${bucket}' was not found for the configured account/credentials." >&2 | |
| echo "Check that R2_BUCKET matches the exact bucket name in Cloudflare R2." >&2 | |
| echo "Also confirm R2_ACCOUNT_ID and the access keys belong to the same Cloudflare account as that bucket." >&2 | |
| exit 1 | |
| fi | |
| - name: Publish installer and latest channel | |
| if: steps.release.outputs.publish_latest == 'true' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| endpoint="${{ steps.r2.outputs.endpoint_url }}" | |
| bucket="s3://${{ steps.r2.outputs.bucket_name }}" | |
| aws s3 cp ./dist/release/install.sh "${bucket}/noderax-agent/install.sh" \ | |
| --endpoint-url "${endpoint}" \ | |
| --cache-control "public, max-age=300" \ | |
| --content-type "text/x-shellscript; charset=utf-8" | |
| aws s3 cp ./dist/release/noderax-agent-linux-amd64 "${bucket}/noderax-agent/releases/latest/noderax-agent-linux-amd64" \ | |
| --endpoint-url "${endpoint}" \ | |
| --cache-control "public, max-age=300" \ | |
| --content-type "application/octet-stream" | |
| aws s3 cp ./dist/release/noderax-agent-linux-arm64 "${bucket}/noderax-agent/releases/latest/noderax-agent-linux-arm64" \ | |
| --endpoint-url "${endpoint}" \ | |
| --cache-control "public, max-age=300" \ | |
| --content-type "application/octet-stream" | |
| aws s3 cp ./dist/release/SHA256SUMS "${bucket}/noderax-agent/releases/latest/SHA256SUMS" \ | |
| --endpoint-url "${endpoint}" \ | |
| --cache-control "public, max-age=300" \ | |
| --content-type "text/plain; charset=utf-8" | |
| - name: Publish versioned release and catalog | |
| if: steps.release.outputs.release_version != '' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| endpoint="${{ steps.r2.outputs.endpoint_url }}" | |
| bucket="s3://${{ steps.r2.outputs.bucket_name }}" | |
| version="${{ steps.release.outputs.release_version }}" | |
| aws s3 cp ./dist/release/noderax-agent-linux-amd64 "${bucket}/noderax-agent/releases/${version}/noderax-agent-linux-amd64" \ | |
| --endpoint-url "${endpoint}" \ | |
| --cache-control "public, max-age=31536000, immutable" \ | |
| --content-type "application/octet-stream" | |
| aws s3 cp ./dist/release/noderax-agent-linux-arm64 "${bucket}/noderax-agent/releases/${version}/noderax-agent-linux-arm64" \ | |
| --endpoint-url "${endpoint}" \ | |
| --cache-control "public, max-age=31536000, immutable" \ | |
| --content-type "application/octet-stream" | |
| aws s3 cp ./dist/release/SHA256SUMS "${bucket}/noderax-agent/releases/${version}/SHA256SUMS" \ | |
| --endpoint-url "${endpoint}" \ | |
| --cache-control "public, max-age=31536000, immutable" \ | |
| --content-type "text/plain; charset=utf-8" | |
| aws s3 cp ./dist/release/release-manifest.json "${bucket}/noderax-agent/releases/${version}/release-manifest.json" \ | |
| --endpoint-url "${endpoint}" \ | |
| --cache-control "public, max-age=31536000, immutable" \ | |
| --content-type "application/json; charset=utf-8" | |
| aws s3 cp ./dist/release/catalog.json "${bucket}/noderax-agent/releases/catalog.json" \ | |
| --endpoint-url "${endpoint}" \ | |
| --cache-control "public, max-age=300" \ | |
| --content-type "application/json; charset=utf-8" | |
| - name: Publish GitHub Release metadata | |
| if: steps.release.outputs.release_version != '' | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: agent-v${{ steps.release.outputs.release_version }} | |
| name: Noderax Agent ${{ steps.release.outputs.release_version }} | |
| body_path: ./dist/release/release-notes.md | |
| fail_on_unmatched_files: true | |
| files: | | |
| ./dist/release/noderax-agent-linux-amd64 | |
| ./dist/release/noderax-agent-linux-arm64 | |
| ./dist/release/SHA256SUMS | |
| ./dist/release/release-manifest.json |