name: Deploy on: push: branches: [main] concurrency: group: deploy-main cancel-in-progress: false jobs: test: name: Run Tests runs-on: x86_64-linux steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: "1.25.7" - run: go mod download - run: go test -v -race -coverprofile=coverage.out ./... - run: go tool cover -func=coverage.out lint: name: Lint runs-on: x86_64-linux steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: "1.25.7" - run: go vet ./... - name: Check formatting run: | if [ -n "$(gofmt -l .)" ]; then echo "Go files are not formatted:" gofmt -l . exit 1 fi - name: Check go.mod tidy run: | go mod tidy if [ -n "$(git diff --name-only go.mod go.sum)" ]; then echo "go.mod or go.sum is not tidy. Run 'go mod tidy' and commit the changes." git diff go.mod go.sum exit 1 fi - name: golangci-lint uses: golangci/golangci-lint-action@v7 with: version: latest - name: Install govulncheck run: go install golang.org/x/vuln/cmd/govulncheck@latest - name: Run govulncheck run: govulncheck ./... deploy: needs: [test, lint] runs-on: debian-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install Go uses: actions/setup-go@v5 with: go-version: "1.25.7" - name: Compute next version run: | LATEST_TAG=$(git tag --list 'v*' --sort=-v:refname | head -n1) if [ -z "${LATEST_TAG}" ]; then NEXT="v0.1.0" else # Bump patch: v0.1.2 -> v0.1.3 PREFIX="${LATEST_TAG%.*}" PATCH="${LATEST_TAG##*.}" NEXT="${PREFIX}.$((PATCH + 1))" fi echo "VERSION=${NEXT}" >> "$GITHUB_ENV" echo "Next version: ${NEXT}" - name: Build server binary run: CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o ./bin/cairn-server ./cmd/cairn-server - name: Cross-compile CLI run: | LDFLAGS="-w -s -X main.version=${VERSION}" for pair in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64; do GOOS="${pair%/*}" GOARCH="${pair#*/}" CGO_ENABLED=0 GOOS="${GOOS}" GOARCH="${GOARCH}" \ go build -ldflags="${LDFLAGS}" -o "./bin/cairn-${GOOS}-${GOARCH}" ./cmd/cairn done cp ./bin/cairn-linux-amd64 ./bin/cairn - name: Install Docker CLI run: | apt-get update && apt-get install -y curl curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-27.5.1.tgz \ | tar xz --strip-components=1 -C /usr/local/bin docker/docker - name: Wait for Docker run: | timeout=30 elapsed=0 while ! docker info >/dev/null 2>&1; do [ $elapsed -ge $timeout ] && echo "Docker not ready" && exit 1 sleep 2 elapsed=$((elapsed + 2)) done - name: Build image run: | REGISTRY="registry.ts.mattnite.net" docker build -t "${REGISTRY}/cairn:${GITHUB_SHA}" -t "${REGISTRY}/cairn:latest" . - name: Push image run: | REGISTRY="registry.ts.mattnite.net" docker push "${REGISTRY}/cairn:${GITHUB_SHA}" docker push "${REGISTRY}/cairn:latest" - name: Create git tag run: | git tag "${VERSION}" git push origin "${VERSION}" - name: Publish CLI to package registry run: | PKG="${GITHUB_SERVER_URL}/api/packages/${GITHUB_REPOSITORY_OWNER}/generic/cairn" AUTH="Authorization: token ${{ secrets.PACKAGES_TOKEN }}" for file in ./bin/cairn-*; do filename="$(basename "${file}")" curl -fsSL -X PUT -H "${AUTH}" --upload-file "${file}" "${PKG}/${VERSION}/${filename}" done # Overwrite "latest": delete old files then upload for file in ./bin/cairn-*; do filename="$(basename "${file}")" curl -sS -X DELETE -H "${AUTH}" "${PKG}/latest/${filename}" || true curl -fsSL -X PUT -H "${AUTH}" --upload-file "${file}" "${PKG}/latest/${filename}" done - name: Update infra uses: https://git.ts.mattnite.net/mattnite/infra/actions/update-image@main with: updates: | cairn ${{ github.sha }} cairn/cairn.hcl forgejo_token: ${{ secrets.INFRA_API_TOKEN }}