fix: db migrations #6
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: Release and Publish Docker Images | |
| on: | |
| pull_request_target: | |
| types: [closed] | |
| branches: | |
| - main | |
| - master | |
| env: | |
| REGISTRY: ghcr.io | |
| API_IMAGE_NAME: ${{ github.repository }}-api | |
| WORKER_IMAGE_NAME: ${{ github.repository }}-worker | |
| permissions: | |
| contents: write | |
| packages: write | |
| pull-requests: read | |
| concurrency: | |
| group: release-${{ github.event.pull_request.base.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| # ── Step 1: Compute version and create git tag ────────────── | |
| release: | |
| if: github.event.pull_request.merged == true | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| major_minor: ${{ steps.version.outputs.major_minor }} | |
| next_tag: ${{ steps.version.outputs.next_tag }} | |
| steps: | |
| - name: Determine bump type from PR labels | |
| id: bump | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const labels = (context.payload.pull_request.labels || []).map((label) => | |
| label.name.toLowerCase() | |
| ); | |
| core.info(`PR labels: ${labels.join(", ") || "none"}`); | |
| let bump = "patch"; | |
| if (labels.includes("major")) { | |
| bump = "major"; | |
| } else if (labels.includes("minor")) { | |
| bump = "minor"; | |
| } else if (labels.includes("fix") || labels.includes("patch")) { | |
| bump = "patch"; | |
| } else { | |
| core.info("No semver label found. Defaulting to patch bump."); | |
| } | |
| core.setOutput("bump", bump); | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Compute next semantic version | |
| id: version | |
| env: | |
| MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git fetch --tags --force | |
| existing_tag="" | |
| while IFS= read -r tag; do | |
| if [[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| existing_tag="$tag" | |
| break | |
| fi | |
| done < <(git tag --points-at "$MERGE_SHA" --sort=-v:refname) | |
| if [[ -n "$existing_tag" ]]; then | |
| next_tag="$existing_tag" | |
| create_tag="false" | |
| else | |
| latest_tag="$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | sed -n '1p')" | |
| if [[ -z "$latest_tag" ]]; then | |
| latest_tag="v0.0.0" | |
| fi | |
| current="${latest_tag#v}" | |
| IFS='.' read -r major minor patch <<< "$current" | |
| if [[ -z "${major:-}" || -z "${minor:-}" || -z "${patch:-}" ]]; then | |
| echo "Latest tag '$latest_tag' is not semver format (v<major>.<minor>.<patch>)." | |
| exit 1 | |
| fi | |
| case "${{ steps.bump.outputs.bump }}" in | |
| major) | |
| major=$((major + 1)) | |
| minor=0 | |
| patch=0 | |
| ;; | |
| minor) | |
| minor=$((minor + 1)) | |
| patch=0 | |
| ;; | |
| patch) | |
| patch=$((patch + 1)) | |
| ;; | |
| *) | |
| echo "Unknown bump type: ${{ steps.bump.outputs.bump }}" | |
| exit 1 | |
| ;; | |
| esac | |
| next_tag="v${major}.${minor}.${patch}" | |
| create_tag="true" | |
| fi | |
| version="${next_tag#v}" | |
| major_minor="$(echo "$version" | awk -F. '{print $1 "." $2}')" | |
| echo "next_tag=$next_tag" >> "$GITHUB_OUTPUT" | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| echo "major_minor=$major_minor" >> "$GITHUB_OUTPUT" | |
| echo "create_tag=$create_tag" >> "$GITHUB_OUTPUT" | |
| echo "Next tag: $next_tag" | |
| - name: Create and push git tag | |
| if: steps.version.outputs.create_tag == 'true' | |
| env: | |
| NEXT_TAG: ${{ steps.version.outputs.next_tag }} | |
| MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git tag "$NEXT_TAG" "$MERGE_SHA" | |
| git push origin "$NEXT_TAG" | |
| # ── Step 2: Build each platform natively in parallel ──────── | |
| build: | |
| needs: release | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: linux/amd64 | |
| runner: ubuntu-latest | |
| slug: amd64 | |
| - platform: linux/arm64 | |
| runner: ubuntu-24.04-arm | |
| slug: arm64 | |
| runs-on: ${{ matrix.runner }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Lowercase image names | |
| run: | | |
| echo "API_IMAGE_NAME=${API_IMAGE_NAME,,}" >> $GITHUB_ENV | |
| echo "WORKER_IMAGE_NAME=${WORKER_IMAGE_NAME,,}" >> $GITHUB_ENV | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and push API image (by digest) | |
| id: build-api | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: docker/Dockerfile.api | |
| platforms: ${{ matrix.platform }} | |
| outputs: type=image,name=${{ env.REGISTRY }}/${{ env.API_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true | |
| cache-from: type=gha,scope=api-${{ matrix.slug }} | |
| cache-to: type=gha,scope=api-${{ matrix.slug }},mode=max | |
| - name: Build and push Worker image (by digest) | |
| id: build-worker | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: docker/Dockerfile.worker | |
| platforms: ${{ matrix.platform }} | |
| outputs: type=image,name=${{ env.REGISTRY }}/${{ env.WORKER_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true | |
| cache-from: type=gha,scope=worker-${{ matrix.slug }} | |
| cache-to: type=gha,scope=worker-${{ matrix.slug }},mode=max | |
| build-args: | | |
| INSTALL_EXTRAS=qualitative-voice,reports | |
| - name: Export digests | |
| run: | | |
| mkdir -p /tmp/digests/api /tmp/digests/worker | |
| api_digest="${{ steps.build-api.outputs.digest }}" | |
| worker_digest="${{ steps.build-worker.outputs.digest }}" | |
| touch "/tmp/digests/api/${api_digest#sha256:}" | |
| touch "/tmp/digests/worker/${worker_digest#sha256:}" | |
| - name: Upload API digest | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: api-digest-${{ matrix.slug }} | |
| path: /tmp/digests/api/* | |
| if-no-files-found: error | |
| retention-days: 1 | |
| - name: Upload Worker digest | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: worker-digest-${{ matrix.slug }} | |
| path: /tmp/digests/worker/* | |
| if-no-files-found: error | |
| retention-days: 1 | |
| # ── Step 3: Merge platform digests into multi-arch manifests ─ | |
| merge: | |
| needs: [release, build] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Lowercase image names | |
| run: | | |
| echo "API_IMAGE_NAME=${API_IMAGE_NAME,,}" >> $GITHUB_ENV | |
| echo "WORKER_IMAGE_NAME=${WORKER_IMAGE_NAME,,}" >> $GITHUB_ENV | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Download API digests | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: /tmp/digests/api | |
| pattern: api-digest-* | |
| merge-multiple: true | |
| - name: Download Worker digests | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: /tmp/digests/worker | |
| pattern: worker-digest-* | |
| merge-multiple: true | |
| - name: Create API multi-arch manifest | |
| env: | |
| VERSION: ${{ needs.release.outputs.version }} | |
| MAJOR_MINOR: ${{ needs.release.outputs.major_minor }} | |
| IMAGE: ${{ env.REGISTRY }}/${{ env.API_IMAGE_NAME }} | |
| run: | | |
| cd /tmp/digests/api | |
| docker buildx imagetools create \ | |
| -t "$IMAGE:${VERSION}" \ | |
| -t "$IMAGE:${MAJOR_MINOR}" \ | |
| -t "$IMAGE:latest" \ | |
| $(printf "$IMAGE@sha256:%s " *) | |
| - name: Create Worker multi-arch manifest | |
| env: | |
| VERSION: ${{ needs.release.outputs.version }} | |
| MAJOR_MINOR: ${{ needs.release.outputs.major_minor }} | |
| IMAGE: ${{ env.REGISTRY }}/${{ env.WORKER_IMAGE_NAME }} | |
| run: | | |
| cd /tmp/digests/worker | |
| docker buildx imagetools create \ | |
| -t "$IMAGE:${VERSION}" \ | |
| -t "$IMAGE:${MAJOR_MINOR}" \ | |
| -t "$IMAGE:latest" \ | |
| $(printf "$IMAGE@sha256:%s " *) | |
| - name: Create GitHub release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| NEXT_TAG: ${{ needs.release.outputs.next_tag }} | |
| MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if gh release view "$NEXT_TAG" >/dev/null 2>&1; then | |
| echo "Release $NEXT_TAG already exists. Skipping." | |
| exit 0 | |
| fi | |
| gh release create "$NEXT_TAG" \ | |
| --target "$MERGE_SHA" \ | |
| --title "$NEXT_TAG" \ | |
| --generate-notes |