name: Release on: push: tags: - 'v*' workflow_dispatch: jobs: # Gates every publishing job below. A tag that fails here publishes nothing: # the binaries, the image and the chart are all downstream of it. test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: go.mod - name: Vet run: go vet ./... - name: Test run: go test ./... build: needs: test runs-on: ubuntu-latest strategy: matrix: include: - goos: linux goarch: amd64 - goos: linux goarch: arm64 - goos: darwin goarch: amd64 - goos: darwin goarch: arm64 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: go.mod - name: Build env: GOOS: ${{ matrix.goos }} GOARCH: ${{ matrix.goarch }} run: | go build \ -ldflags "-w -s -X main.version=${{ github.ref_name }}" \ -o terdut-${{ github.ref_name }}-${{ matrix.goos }}-${{ matrix.goarch }} \ ./cmd/terdut - uses: actions/upload-artifact@v4 with: name: terdut-${{ github.ref_name }}-${{ matrix.goos }}-${{ matrix.goarch }} path: terdut-${{ github.ref_name }}-${{ matrix.goos }}-${{ matrix.goarch }} docker: needs: test runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64,linux/arm64 push: true build-args: VERSION=${{ github.ref_name }} tags: | ghcr.io/yeniklas/terdut-server:latest ghcr.io/yeniklas/terdut-server:${{ github.ref_name }} chart: needs: test runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Configure Git run: | git config user.name "$GITHUB_ACTOR" git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - name: Install Helm uses: azure/setup-helm@v4 # This job is the only thing that publishes the chart, which is what keeps # the published metadata honest. There used to be a second publisher -- # chart-release.yml, on every charts/** push to main -- and the two raced # for the same chart version with different answers: this one stamps # version and appVersion from the tag, that one took Chart.yaml verbatim, # where appVersion is the hardcoded "latest". Whichever landed first won # and skip_existing turned the loser into a no-op, so the metadata of a # release depended on which runner was quicker. Chart 0.9.0 went out on # 2026-08-08 reading appVersion "latest" that way. # # It could not be fixed by making both agree: the tag is pushed after the # branch, so a workflow triggered by the main push cannot know the version # it is about to be tagged with. One publisher, triggered by the tag. # # The cost is that the chart only ships with an app release. That is no # real loss -- the sed below ties the chart version to the app version, so # a chart-only change has no version of its own to be released under # anyway. Chart fixes ride the next tag. - name: Update chart versions run: | VERSION="${{ github.ref_name }}" if [[ ! "$VERSION" =~ ^v[0-9] ]]; then echo "::error::refusing to publish a chart for non-version tag ${VERSION}" exit 1 fi CHART_VERSION="${VERSION#v}" sed -i "s/^version:.*/version: ${CHART_VERSION}/" charts/terdut-server/Chart.yaml sed -i "s/^appVersion:.*/appVersion: \"${VERSION}\"/" charts/terdut-server/Chart.yaml - name: Run chart-releaser uses: helm/chart-releaser-action@v1.6.0 with: # Keeps a re-run of a failed release idempotent rather than failing on # the chart that already went out. It is no longer papering over a # race -- see above. skip_existing: true env: CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" release: needs: build runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/download-artifact@v4 with: merge-multiple: true - uses: softprops/action-gh-release@v2 with: files: 'terdut-*'