name: Release Artifacts on: push: tags: - "*" workflow_dispatch: jobs: 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: meta shell: bash run: | set -eu 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 if [[ "${tag}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "prerelease=false" >> "${GITHUB_OUTPUT}" elif [[ "${tag}" =~ ^[0-9]+\.[0-9]+\.[0-9]+-(alpha|beta|rc)[0-9]+$ ]]; then echo "prerelease=true" >> "${GITHUB_OUTPUT}" else echo "Invalid tag format: ${tag}" echo "Expected one of:" echo " - 1.2.3" echo " - 1.2.3-alpha1" echo " - 1.2.3-beta1" echo " - 1.2.3-rc1" exit 1 fi build-and-release-executables: name: Build and release executables needs: validate-tag runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - 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 - name: Build release binaries shell: bash run: | set -eu mkdir -p dist for arch in amd64 arm64; do base="scratchbox-linux-${arch}" CGO_ENABLED=0 GOOS=linux GOARCH="${arch}" \ go build -trimpath -ldflags "-s -w" \ -o "dist/${base}-default" ./cmd/scratchbox CGO_ENABLED=0 GOOS=linux GOARCH="${arch}" \ go build -trimpath -tags "netgo osusergo" -ldflags "-s -w" \ -o "dist/${base}-static" ./cmd/scratchbox file "dist/${base}-static" | grep -q "statically linked" done - name: Package release archives shell: bash run: | set -eu rm -rf release-packages mkdir -p release-packages for bin in dist/scratchbox-linux-*; do name="$(basename "${bin}")" archive="scratchbox-${{ needs.validate-tag.outputs.tag }}-${name#scratchbox-}.tar.gz" stage="release-packages/${name}" rm -rf "${stage}" mkdir -p "${stage}" cp README.md "${stage}/" cp "${bin}" "${stage}/" tar -C "${stage}" -czf "${archive}" . done - name: Generate release notes shell: bash run: | set -eu current_tag="${{ needs.validate-tag.outputs.tag }}" prev_tag="$(git tag --sort=-creatordate | awk -v cur="${current_tag}" '$0 != cur { print; exit }')" { echo "## Release ${current_tag}" echo if [ -n "${prev_tag}" ]; then echo "Changes since \`${prev_tag}\`:" echo git log --pretty='- %h %s' "${prev_tag}..${current_tag}" else echo "Changes in this release:" echo git log --pretty='- %h %s' "${current_tag}" fi echo echo "## Artifacts" echo echo "- \`linux-amd64-default\`" echo "- \`linux-amd64-static\`" echo "- \`linux-arm64-default\`" echo "- \`linux-arm64-static\`" } > RELEASE_NOTES.md - name: Create release and upload archives uses: https://gitea.com/actions/gitea-release-action@v1 with: 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-${{ needs.validate-tag.outputs.tag }}-linux-*.tar.gz build-and-release-docker-image: name: Build and release Docker image needs: [validate-tag, build-and-release-executables] 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: ${{ secrets.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 and push Docker image tags 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 push "${IMAGE}:${VERSION_TAG}" docker push "${IMAGE}:latest"