From e3c4bd51952ac1e89e38ef1a8df37a206d06e71f Mon Sep 17 00:00:00 2001 From: Justin Harms Date: Mon, 1 Jun 2026 03:04:35 -0500 Subject: [PATCH] Implement comprehensive release workflow with validation, testing, and Docker support - Added a validation job for release tags to ensure correct format. - Introduced a testing job to run UI tests and race tests before release. - Enhanced the release process to build and publish Docker images alongside binaries. - Updated artifact handling to use validated tags for naming and uploading. - Streamlined the workflow to ensure dependencies are built and tested prior to release. --- .gitea/workflows/release.yaml | 188 ++++++++++++++++++++++++++++++++-- 1 file changed, 178 insertions(+), 10 deletions(-) diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml index b5455b3..71f9597 100644 --- a/.gitea/workflows/release.yaml +++ b/.gitea/workflows/release.yaml @@ -7,16 +7,21 @@ on: workflow_dispatch: jobs: - release: - name: Build and publish release + validate-tag: + name: Validate release tag runs-on: ubuntu-latest + outputs: + tag: ${{ steps.meta.outputs.tag }} + prerelease: ${{ steps.meta.outputs.prerelease }} steps: - name: Validate tag and determine prerelease flag - id: release_meta + id: meta shell: bash run: | set -eu - tag="${{ github.ref_name }}" + tag="${{ gitea.ref_name }}" + echo "tag=${tag}" >> "${GITHUB_OUTPUT}" + # Allowed tags: # - Stable: 1.2.3 # - Prerelease: 1.2.3-alpha1 | 1.2.3-beta1 | 1.2.3-rc1 @@ -34,6 +39,44 @@ jobs: exit 1 fi + test: + name: Test release candidate + needs: validate-tag + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: npm + cache-dependency-path: web/ui/package-lock.json + + - name: Install UI deps + run: npm --prefix ./web/ui ci + + - name: Build UI assets + run: npm --prefix ./web/ui run build + + - name: Run tests + run: make test + + - name: Run race tests + run: make test-race + + release-binaries: + name: Build executable release artifacts + needs: [validate-tag, test] + runs-on: ubuntu-latest + steps: - name: Checkout uses: actions/checkout@v4 with: @@ -87,7 +130,7 @@ jobs: for bin in dist/scratchbox-linux-*; do name="$(basename "${bin}")" - archive="scratchbox-${{ github.ref_name }}-${name#scratchbox-}.tar.gz" + archive="scratchbox-${{ needs.validate-tag.outputs.tag }}-${name#scratchbox-}.tar.gz" stage="release-packages/${name}" rm -rf "${stage}" @@ -101,7 +144,7 @@ jobs: shell: bash run: | set -eu - current_tag="${{ github.ref_name }}" + current_tag="${{ needs.validate-tag.outputs.tag }}" prev_tag="$(git tag --sort=-creatordate | awk -v cur="${current_tag}" '$0 != cur { print; exit }')" { @@ -125,12 +168,137 @@ jobs: echo "- \`linux-arm64-static\`" } > RELEASE_NOTES.md + - name: Upload release bundle artifact + uses: https://gitea.com/actions/gitea-upload-artifact@v4 + with: + name: release-bundle + path: |- + scratchbox-${{ needs.validate-tag.outputs.tag }}-linux-*.tar.gz + RELEASE_NOTES.md + + docker-image: + name: Build Docker image artifact + needs: [validate-tag, test] + runs-on: ubuntu-latest + permissions: + code: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Resolve image coordinates + id: image + shell: bash + run: | + set -eu + host="${{ gitea.server_url }}" + host="${host#http://}" + host="${host#https://}" + host="${host%%/*}" + repo="$(printf '%s' "${{ gitea.repository }}" | tr '[:upper:]' '[:lower:]')" + echo "registry_host=${host}" >> "${GITHUB_OUTPUT}" + echo "image=${host}/${repo}" >> "${GITHUB_OUTPUT}" + + - name: Login to registry + env: + REGISTRY_HOST: ${{ steps.image.outputs.registry_host }} + REGISTRY_USER: ${{ secrets.REGISTRY_USERNAME }} + REGISTRY_PASS: ${{ secrets.REGISTRY_PASSWORD }} + FALLBACK_USER: ${{ gitea.actor }} + FALLBACK_PASS: ${{ gitea.token }} + shell: bash + run: | + set -eu + user="${REGISTRY_USER:-${FALLBACK_USER}}" + pass="${REGISTRY_PASS:-${FALLBACK_PASS}}" + if [ -z "${pass}" ]; then + echo "No registry password available. Set REGISTRY_PASSWORD secret." + exit 1 + fi + printf '%s' "${pass}" | docker login "${REGISTRY_HOST}" --username "${user}" --password-stdin + + - name: Build image + env: + IMAGE: ${{ steps.image.outputs.image }} + VERSION_TAG: ${{ needs.validate-tag.outputs.tag }} + shell: bash + run: | + set -eu + docker build -t "${IMAGE}:${VERSION_TAG}" -t "${IMAGE}:latest" . + docker save -o docker-image.tar "${IMAGE}:${VERSION_TAG}" "${IMAGE}:latest" + { + echo "IMAGE=${IMAGE}" + echo "VERSION_TAG=${VERSION_TAG}" + } > docker-image.env + + - name: Upload docker bundle artifact + uses: https://gitea.com/actions/gitea-upload-artifact@v4 + with: + name: docker-bundle + path: |- + docker-image.tar + docker-image.env + + publish-release: + name: Publish release + needs: [validate-tag, release-binaries, docker-image] + runs-on: ubuntu-latest + steps: + - name: Download release bundle + uses: https://gitea.com/actions/gitea-download-artifact@v4 + with: + name: release-bundle + path: . + - name: Create release and upload archives uses: https://gitea.com/actions/gitea-release-action@v1 with: - tag_name: ${{ github.ref_name }} - name: ${{ github.ref_name }} - prerelease: ${{ steps.release_meta.outputs.prerelease }} + tag_name: ${{ needs.validate-tag.outputs.tag }} + name: ${{ needs.validate-tag.outputs.tag }} + prerelease: ${{ needs.validate-tag.outputs.prerelease }} body_path: RELEASE_NOTES.md files: |- - scratchbox-${{ github.ref_name }}-linux-*.tar.gz + scratchbox-${{ needs.validate-tag.outputs.tag }}-linux-*.tar.gz + + publish-docker: + name: Publish Docker image + needs: [validate-tag, release-binaries, docker-image] + runs-on: ubuntu-latest + permissions: + code: read + packages: write + steps: + - name: Download docker bundle + uses: https://gitea.com/actions/gitea-download-artifact@v4 + with: + name: docker-bundle + path: . + + - name: Login to registry + env: + REGISTRY_USER: ${{ secrets.REGISTRY_USERNAME }} + REGISTRY_PASS: ${{ secrets.REGISTRY_PASSWORD }} + FALLBACK_USER: ${{ gitea.actor }} + FALLBACK_PASS: ${{ gitea.token }} + shell: bash + run: | + set -eu + . ./docker-image.env + registry_host="${IMAGE%%/*}" + user="${REGISTRY_USER:-${FALLBACK_USER}}" + pass="${REGISTRY_PASS:-${FALLBACK_PASS}}" + if [ -z "${pass}" ]; then + echo "No registry password available. Set REGISTRY_PASSWORD secret." + exit 1 + fi + printf '%s' "${pass}" | docker login "${registry_host}" --username "${user}" --password-stdin + + - name: Load and push Docker image tags + shell: bash + run: | + set -eu + . ./docker-image.env + docker load -i docker-image.tar + docker push "${IMAGE}:${VERSION_TAG}" + docker push "${IMAGE}:latest"