From 972916e11ca61ec7301f4240124b1dfdfafbf3ce Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Mon, 6 Oct 2025 11:02:05 -0700 Subject: [PATCH 1/2] Updates build/release workflows --- .github/workflows/build.yml | 246 ++++++++++++++++++---------------- .github/workflows/release.yml | 92 ++++++------- 2 files changed, 175 insertions(+), 163 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2e9ac2f..727ea74 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,147 +1,167 @@ -name: Build -on: [ push, pull_request, workflow_dispatch ] +name: Build / Test / Push + +on: + push: + branches: + - '**' + workflow_dispatch: + env: - REGISTRY: ghcr.io + DOCKER_METADATA_SET_OUTPUT_ENV: 'true' jobs: - # TODO: DRY w/release.yml - setup: - runs-on: ubuntu-latest - + build: + runs-on: ${{ matrix.runner }} + outputs: + build-image-arm: ${{ steps.gen-output.outputs.image-arm64 }} + build-image-x64: ${{ steps.gen-output.outputs.image-x64 }} + strategy: + fail-fast: false + matrix: + runner: + - ubuntu-24.04 + - ubuntu-24.04-arm steps: - # See https://github.com/docker/build-push-action/blob/v2.10.0/TROUBLESHOOTING.md#repository-name-must-be-lowercase - - name: Sanitize image name - uses: actions/github-script@v6 - id: image-name + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 with: - result-encoding: string - script: return '${{ env.REGISTRY }}/${{ github.repository }}'.toLowerCase() + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Get short SHA + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + # note Specifies a single tag to ensure the default doesn't add more than one. + # The actual tag is not used, this is just used to sanitize the registry name + # and produce labels. + tags: type=sha + + - name: Sanitize registry repository name + id: get-reg run: | - echo SHORT_SHA="${GITHUB_SHA:0:7}" >> $GITHUB_ENV + echo "registry=$(echo '${{ steps.meta.outputs.tags }}' | cut -f1 -d:)" | tee -a "$GITHUB_OUTPUT" - outputs: - base_image_name: ${{ steps.image-name.outputs.result }} - build_image: ${{ steps.image-name.outputs.result }}:${{ env.SHORT_SHA }} + - name: Build/push the arch-specific image + id: build + uses: docker/build-push-action@v6 + with: + # @todo GHA caching needs tuning, these tend not to hit. Perhaps switch to type=registry? + cache-from: type=gha + cache-to: type=gha,mode=max + labels: ${{ steps.meta.outputs.labels }} + provenance: mode=max + sbom: true + tags: ${{ steps.get-reg.outputs.registry }} + outputs: type=image,push-by-digest=true,push=true + + - name: Write arch-specific image digest to outputs + id: gen-output + run: | + echo "image-${RUNNER_ARCH,,}=${{ steps.get-reg.outputs.registry }}@${{ steps.build.outputs.digest }}" | tee -a "$GITHUB_OUTPUT" - build: - if: github.event_name != 'release' - needs: setup + merge: + runs-on: ubuntu-24.04 + needs: + - build env: - BUILD_IMAGE: ${{ needs.setup.outputs.build_image }} - - runs-on: ubuntu-latest - - permissions: - packages: write - + DOCKER_APP_IMAGE_ARM64: ${{ needs.build.outputs.build-image-arm }} + DOCKER_APP_IMAGE_X64: ${{ needs.build.outputs.build-image-x64 }} + outputs: + build-image: ${{ steps.meta.outputs.tags }} steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout code + uses: actions/checkout@v4 - - name: Log in to the Container registry - uses: docker/login-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 with: - registry: ${{ env.REGISTRY }} + registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Get build start time - run: | - echo BUILD_TIMESTAMP="$(date --utc --iso-8601=seconds)" >> $GITHUB_ENV - - - name: Build and push Docker image - uses: docker/build-push-action@v3 + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 with: - context: . - push: true - tags: ${{ env.BUILD_IMAGE }} - build-args: | - BUILD_TIMESTAMP=${{ env.BUILD_TIMESTAMP }} - BUILD_URL=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - DOCKER_TAG=${{ env.BUILD_IMAGE }} - GIT_BRANCH=${{ github.ref_name }} - GIT_COMMIT=${{ github.sha }} - GIT_URL=${{ github.repositoryUrl }} + images: ghcr.io/${{ github.repository }} + tags: | + type=sha,suffix=-build-${{ github.run_id }}_${{ github.run_attempt }} - outputs: - build_image: ${{ env.BUILD_IMAGE }} + - name: Push the multi-platform image + run: | + docker buildx imagetools create \ + --tag "$DOCKER_METADATA_OUTPUT_TAGS" \ + "$DOCKER_APP_IMAGE_ARM64" "$DOCKER_APP_IMAGE_X64" test: - if: github.event_name != 'release' - needs: build - - runs-on: ubuntu-latest - - container: - image: ${{ needs.build.outputs.build_image }} - - defaults: - run: - working-directory: /opt/app + runs-on: ubuntu-24.04 + needs: + - merge + env: + COMPOSE_FILE: docker-compose.yml:docker-compose.ci.yml + DOCKER_APP_IMAGE: ${{ needs.merge.outputs.build-image }} + steps: + - name: Checkout code + uses: actions/checkout@v4 - services: - db: - image: postgres - env: - POSTGRES_USER: root - POSTGRES_PASSWORD: root + - name: Set up Docker Compose + uses: docker/setup-compose-action@v1 - steps: - - name: Run tests - env: - RAILS_ENV: test - run: bundle exec rake check -t - - - name: Run style checks - run: bundle exec rubocop - - - name: Validate database migrations - env: - RAILS_ENV: production - DISABLE_DATABASE_ENVIRONMENT_CHECK: 1 - run: rails --trace db:drop db:create db:migrate - - - name: Upload artifacts - if: ${{ always() }} - uses: actions/upload-artifact@v4 + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 with: - name: artifacts - path: artifacts/** + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - # TODO: DRY w/release.yml - push: - if: github.event_name != 'release' + - name: Run the test script + run: | + docker compose run --rm --user root app chown -R avplayer:avplayer artifacts + docker compose up --detach --wait + docker compose exec app bin/test - needs: [ setup, build, test ] + push: + runs-on: ubuntu-24.04 + needs: + - merge + - test env: - BASE_IMAGE_NAME: ${{ needs.setup.outputs.base_image_name }} - BUILD_IMAGE: ${{ needs.build.outputs.build_image }} - - runs-on: ubuntu-latest - - permissions: - packages: write - + DOCKER_APP_IMAGE: ${{ needs.merge.outputs.build-image }} steps: - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.BASE_IMAGE_NAME }} + - name: Checkout code + uses: actions/checkout@v4 - - name: Log in to the Container registry - uses: docker/login-action@v2 + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 with: - registry: ${{ env.REGISTRY }} + registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Tag and push image - uses: akhilerm/tag-push-action@v2.1.0 + - name: Produce permanent image tags + id: branch-meta + uses: docker/metadata-action@v5 with: - src: ${{ env.BUILD_IMAGE }} - dst: | - ${{ steps.meta.outputs.tags }} + images: ghcr.io/${{ github.repository }} + tags: | + type=sha + type=ref,event=branch + type=raw,value=latest,enable={{is_default_branch}} + - name: Retag and push the image + run: | + docker pull "$DOCKER_APP_IMAGE" + echo "$DOCKER_METADATA_OUTPUT_TAGS" | tr ' ' '\n' | xargs -n1 docker tag "$DOCKER_APP_IMAGE" + docker push --all-tags "$(echo "$DOCKER_APP_IMAGE" | cut -f1 -d:)" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1238b5f..d6564d0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,70 +1,62 @@ -name: Release +name: Push Release Tags on: - release: - types: - - published + push: + tags: + - '**' workflow_dispatch: env: - REGISTRY: ghcr.io + DOCKER_METADATA_SET_OUTPUT_ENV: 'true' jobs: - # TODO: DRY w/build.yml - setup: + retag: runs-on: ubuntu-latest - steps: - # See https://github.com/docker/build-push-action/blob/v2.10.0/TROUBLESHOOTING.md#repository-name-must-be-lowercase - - name: Sanitize image name - uses: actions/github-script@v6 - id: image-name - with: - result-encoding: string - script: return '${{ env.REGISTRY }}/${{ github.repository }}'.toLowerCase() + - name: Checkout code + uses: actions/checkout@v4 - - name: Get short SHA - run: | - echo SHORT_SHA="${GITHUB_SHA:0:7}" >> $GITHUB_ENV + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 - outputs: - base_image_name: ${{ steps.image-name.outputs.result }} - build_image: ${{ steps.image-name.outputs.result }}:${{ env.SHORT_SHA }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - # TODO: DRY w/build.yml - push-release: - needs: setup - env: - BASE_IMAGE_NAME: ${{ needs.setup.outputs.base_image_name }} - BUILD_IMAGE: ${{ needs.setup.outputs.build_image }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - runs-on: ubuntu-latest + - name: Determine the sha-based image tag to retag + id: get-base-image + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + tags: type=sha - permissions: - packages: write + - name: Verify that the image was previously built + env: + BASE_IMAGE: ${{ steps.get-base-image.outputs.tags }} + run: | + docker pull "$BASE_IMAGE" - steps: - # TODO: Make this fail if tag is a bad semver (see https://github.com/docker/metadata-action/issues/200) - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v4 + - name: Produce release tags + id: tag-meta + uses: docker/metadata-action@v5 with: - images: ${{ env.BASE_IMAGE_NAME }} - tags: + images: ghcr.io/${{ github.repository }} + flavor: latest=false + tags: | + type=ref,event=tag type=semver,pattern={{major}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{version}} - - name: Log in to the Container registry - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Tag and push image - uses: akhilerm/tag-push-action@v2.1.0 - with: - src: ${{ env.BUILD_IMAGE }} - dst: | - ${{ steps.meta.outputs.tags }} + - name: Retag the pulled image + env: + BASE_IMAGE: ${{ steps.get-base-image.outputs.tags }} + run: | + echo "$DOCKER_METADATA_OUTPUT_TAGS" | tr ' ' '\n' | xargs -n1 docker tag "$BASE_IMAGE" + docker push --all-tags "$(echo "$BASE_IMAGE" | cut -f1 -d:)" From dfa5cc1bfbcd241124220e54b5361663d3ca9aef Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Mon, 6 Oct 2025 11:06:04 -0700 Subject: [PATCH 2/2] Get old test job working --- .github/workflows/build.yml | 52 ++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 727ea74..431b12e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -106,31 +106,41 @@ jobs: "$DOCKER_APP_IMAGE_ARM64" "$DOCKER_APP_IMAGE_X64" test: - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest needs: - merge - env: - COMPOSE_FILE: docker-compose.yml:docker-compose.ci.yml - DOCKER_APP_IMAGE: ${{ needs.merge.outputs.build-image }} + container: + image: ${{ needs.merge.outputs.build-image }} + defaults: + run: + working-directory: /opt/app + services: + db: + image: postgres + env: + POSTGRES_USER: root + POSTGRES_PASSWORD: root steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Docker Compose - uses: docker/setup-compose-action@v1 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + - name: Run tests + env: + RAILS_ENV: test + run: bundle exec rake check -t + + - name: Run style checks + run: bundle exec rubocop + + - name: Validate database migrations + env: + RAILS_ENV: production + DISABLE_DATABASE_ENVIRONMENT_CHECK: 1 + run: rails --trace db:drop db:create db:migrate + + - name: Upload artifacts + if: ${{ always() }} + uses: actions/upload-artifact@v4 with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Run the test script - run: | - docker compose run --rm --user root app chown -R avplayer:avplayer artifacts - docker compose up --detach --wait - docker compose exec app bin/test + name: artifacts + path: artifacts/** push: runs-on: ubuntu-24.04