name: Release # Single pipeline: lint + test + build, then (only on push to main and # only if every gate passed) tag the release from the VERSION file. # A tag is therefore never created unless the build and tests are green. # # Why no actions/checkout + actions/setup-go: # act_runner re-clones every third-party JS action from github.com per # job. setup-go is large and that clone routinely hangs / times out on # the homelab runner. We avoid the failure mode entirely by doing the # checkout with plain git and downloading Go directly from go.dev. # /usr/local/go persists across jobs in the host-executor runner # container, so subsequent runs are no-ops. # # Tagging token: # Pushing a tag needs write access. Gitea injects a per-job # `github.token`; this workflow uses it, so the repository's # Settings → Actions → General → "Workflow permissions" must allow # read/write. If your instance restricts the auto token, set a PAT # secret named RELEASE_TAG_PAT (repo write) and it is used instead. on: push: branches: [main] pull_request: workflow_dispatch: env: GO_VERSION: "1.26.3" ERRCHECK_VERSION: "v1.20.0" STATICCHECK_VERSION: "v0.7.0" jobs: release: runs-on: self-hosted defaults: run: shell: sh env: # Gitea auto-generates a per-job read-scoped token for git fetch; # RELEASE_TAG_PAT (if set) is preferred for the tag push. GITHUB_TOKEN: ${{ github.token }} RELEASE_TAG_PAT: ${{ secrets.RELEASE_TAG_PAT }} steps: - name: Install Go and toolchain run: | set -eu missing="" command -v curl >/dev/null 2>&1 || missing="$missing curl" command -v gcc >/dev/null 2>&1 || missing="$missing build-base" command -v git >/dev/null 2>&1 || missing="$missing git" if [ -n "$missing" ]; then apk add --no-cache $missing fi want="go${GO_VERSION}" have="$(/usr/local/go/bin/go env GOVERSION 2>/dev/null || echo none)" if [ "$have" != "$want" ]; then echo "installing Go ${GO_VERSION} (had: ${have})" curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" -o /tmp/go.tgz rm -rf /usr/local/go tar -C /usr/local -xzf /tmp/go.tgz rm -f /tmp/go.tgz else echo "Go ${GO_VERSION} already installed" fi echo "/usr/local/go/bin" >> "$GITHUB_PATH" echo "$HOME/go/bin" >> "$GITHUB_PATH" echo "CGO_ENABLED=1" >> "$GITHUB_ENV" - name: Checkout run: | set -eu cd "$GITHUB_WORKSPACE" rm -rf ./* ./.[!.]* 2>/dev/null || true git init -q git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" auth=$(printf '%s' "x-access-token:${GITHUB_TOKEN}" | base64 -w0) git -c "http.extraheader=Authorization: Basic ${auth}" \ fetch --depth=1 origin "${GITHUB_SHA}" git checkout -q FETCH_HEAD - name: Install linters (errcheck, staticcheck) run: | set -eu if ! command -v errcheck >/dev/null 2>&1; then echo "installing errcheck ${ERRCHECK_VERSION}" go install "github.com/kisielk/errcheck@${ERRCHECK_VERSION}" else echo "errcheck already installed" fi if ! command -v staticcheck >/dev/null 2>&1; then echo "installing staticcheck ${STATICCHECK_VERSION}" go install "honnef.co/go/tools/cmd/staticcheck@${STATICCHECK_VERSION}" else echo "staticcheck already installed: $(staticcheck -version)" fi - name: gofmt run: | set -eu diff=$(gofmt -l $(find . -name '*.go' -not -path './vendor/*')) if [ -n "$diff" ]; then echo "gofmt would reformat:" echo "$diff" exit 1 fi - name: go vet run: go vet ./... - name: errcheck run: errcheck ./... - name: staticcheck run: staticcheck ./... - name: go test -race run: go test ./... -race - name: go build run: go build ./... # ── Release: only on push to main, and only after every gate above # passed (steps run sequentially and fail-fast). ────────────── - name: Tag release from VERSION if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} run: | set -eu cd "$GITHUB_WORKSPACE" token="${RELEASE_TAG_PAT:-$GITHUB_TOKEN}" if [ -z "$token" ]; then echo "no token available to push tags" >&2 exit 1 fi auth=$(printf '%s' "x-access-token:${token}" | base64 -w0) # Fetch existing tags so we can check the target tag. git -c "http.extraheader=Authorization: Basic ${auth}" \ fetch --quiet --tags origin || true version="$(tr -d ' \t\r\n' < VERSION)" if [ -z "$version" ]; then echo "VERSION file is empty" >&2 exit 1 fi tag="v${version}" if git rev-parse -q --verify "refs/tags/${tag}" >/dev/null; then echo "Tag ${tag} already exists — nothing to release." exit 0 fi if git -c "http.extraheader=Authorization: Basic ${auth}" \ ls-remote --tags origin "refs/tags/${tag}" | grep -q "${tag}"; then echo "Tag ${tag} already exists on the remote — nothing to release." exit 0 fi echo "Creating tag ${tag} at ${GITHUB_SHA}" git config user.name "sherlock-release" git config user.email "release@sherlock.local" git tag -a "${tag}" -m "Release ${tag}" git -c "http.extraheader=Authorization: Basic ${auth}" \ push origin "refs/tags/${tag}" echo "Pushed ${tag}."