name: Distribute Release Assets # Triggered after a GitHub Release is officially published. # Mirrors installer assets to the configured distribution endpoint for external download. # # Why `published` (not `released`)? # - `released` is supposed to fire when a non-prerelease release is published, # but GitHub sometimes only emits `published` (not `released`) when a pre-existing # draft is publicly released, causing the workflow to be silently skipped. # - `published` reliably fires on every draft → public transition, including # prereleases. The `if` guard on the job filters out prereleases explicitly. # # All credentials / identifiers are read from repository secrets: # - AWS_REGION # - AWS_ROLE_ARN # - AWS_S3_BUCKET # Do NOT hardcode these values in this file. on: release: types: [published] # Manual trigger for smoke testing or for retrying a missed release: # pick any existing release tag to re-run the distribution against. workflow_dispatch: inputs: tag: description: 'Existing release tag to redistribute (e.g. v1.9.21)' required: true type: string permissions: id-token: write # required for OIDC token contents: read env: S3_RELEASE_PREFIX: releases jobs: distribute: name: Distribute release assets runs-on: ubuntu-latest # Skip prereleases: manual dispatch always runs, automatic event runs only when not a prerelease. if: github.event_name == 'workflow_dispatch' || github.event.release.prerelease == false steps: - name: Extract version from tag id: version env: EVENT_TAG: ${{ github.event.release.tag_name }} INPUT_TAG: ${{ github.event.inputs.tag }} run: | TAG="${EVENT_TAG:-$INPUT_TAG}" if [ -z "$TAG" ]; then echo "::error::No tag provided (event did not supply one)." exit 1 fi VERSION="${TAG#v}" echo "tag=$TAG" >> $GITHUB_OUTPUT echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Tag: $TAG / Version: $VERSION" - name: Configure AWS credentials (OIDC) uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: ${{ secrets.AWS_REGION }} - name: Guard against same-version overwrite env: S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} VERSION: ${{ steps.version.outputs.version }} run: | set -euo pipefail PREFIX="${S3_RELEASE_PREFIX}/${VERSION}/" DEST="s3://${S3_BUCKET}/${PREFIX}" COUNT=$(aws s3api list-objects-v2 \ --bucket "$S3_BUCKET" \ --prefix "$PREFIX" \ --no-paginate \ --query 'KeyCount' \ --output text) if [ "$COUNT" -gt 0 ]; then echo "::error::Version directory already contains $COUNT file(s). Refusing to overwrite." echo "::error::Same-version re-publish is not allowed (downstream caches would serve stale files). Release a new version instead." aws s3 ls "$DEST" exit 1 fi - name: Download release assets from GitHub env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail mkdir -p dist gh release download "${{ steps.version.outputs.tag }}" \ --repo "${{ github.repository }}" \ --dir dist \ --pattern "*.dmg" \ --pattern "*.exe" \ --pattern "*.msi" \ --pattern "*.deb" \ --pattern "*.zip" \ --pattern "latest*.yml" echo "Downloaded files:" ls -la dist - name: Validate updater metadata id: metadata env: VERSION: ${{ steps.version.outputs.version }} run: | set -euo pipefail metadata_files=( dist/latest.yml dist/latest-win-arm64.yml dist/latest-mac.yml dist/latest-arm64-mac.yml dist/latest-linux.yml dist/latest-linux-arm64.yml ) for file in "${metadata_files[@]}"; do if [ ! -f "$file" ]; then echo "::error::Missing updater metadata file: $file." exit 1 fi metadata_version=$(grep -E "^version:" "$file" | head -n 1 | sed -E "s/^version:[[:space:]]*['\"]?([^'\"]+).*/\1/") if [ "$metadata_version" != "$VERSION" ]; then echo "::error::$file version is $metadata_version, expected $VERSION." exit 1 fi done { echo "files<> "$GITHUB_OUTPUT" - name: Upload assets env: S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} VERSION: ${{ steps.version.outputs.version }} run: | set -euo pipefail aws s3 cp dist/ "s3://${S3_BUCKET}/${S3_RELEASE_PREFIX}/${VERSION}/" \ --recursive echo "Uploaded release ${VERSION}" - name: Upload updater metadata env: S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} run: | set -euo pipefail aws s3 cp dist/ "s3://${S3_BUCKET}/${S3_RELEASE_PREFIX}/" \ --recursive \ --exclude "*" \ --include "latest*.yml" \ --content-type "text/yaml" \ --cache-control "public, max-age=60" echo "Uploaded updater metadata" - name: Summary env: VERSION: ${{ steps.version.outputs.version }} TAG: ${{ steps.version.outputs.tag }} FILES: ${{ steps.metadata.outputs.files }} run: | { echo "### Release assets distributed" echo "" echo "- Tag: \`${TAG}\`" echo "- Version: \`${VERSION}\`" echo "- Asset prefix: \`${S3_RELEASE_PREFIX}/${VERSION}/\`" echo "- Metadata prefix: \`${S3_RELEASE_PREFIX}/\`" echo "" echo "Uploaded metadata:" while IFS= read -r file; do [ -n "$file" ] && echo "- \`${S3_RELEASE_PREFIX}/$file\`" done <<< "$FILES" } >> $GITHUB_STEP_SUMMARY