Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88f0e24b46 | ||
|
|
603e1e344b | ||
|
|
96e47ab87a | ||
|
|
c3418b3c3b | ||
|
|
5048968cdf | ||
|
|
a845157c67 | ||
|
|
68f3a610bb |
@@ -1,8 +1,10 @@
|
|||||||
|
# Release — Go binaries. Runs on version tags (v1.2.3) pushed to main.
|
||||||
|
# Builds the platform matrix, creates the Gitea release, uploads binaries.
|
||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: ["v*.*.*"]
|
tags: ["v*"]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -26,33 +28,31 @@ jobs:
|
|||||||
- goos: freebsd
|
- goos: freebsd
|
||||||
goarch: riscv64
|
goarch: riscv64
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: "1.26"
|
go-version: "1.26"
|
||||||
|
|
||||||
- name: Download dependencies
|
- name: Download dependencies
|
||||||
run: go mod download
|
run: go mod download
|
||||||
|
|
||||||
- name: Build
|
- name: Validate tag and build
|
||||||
id: build
|
id: build
|
||||||
env:
|
env:
|
||||||
REF: ${{ gitea.ref }}
|
VERSION: ${{ gitea.ref_name }}
|
||||||
|
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
VERSION="${REF##*/}"
|
if ! echo "$VERSION" | grep -qE '^v[0-9]+(\.[0-9]+){0,2}([-+].*)?$'; then
|
||||||
|
echo "ERROR: expected a semver tag like v1.2.3, got: '$VERSION'"
|
||||||
if [[ ! "$VERSION" =~ ^v[0-9]+(\.[0-9]+){0,2}([-+].*)?$ ]]; then
|
|
||||||
echo "ERROR: expected a semver tag like v1.2.3, got: '$VERSION' (ref: '$REF')"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
VERSION_NO_V="${VERSION#v}"
|
VERSION_NO_V="${VERSION#v}"
|
||||||
echo "version_no_v=${VERSION_NO_V}" >> "$GITEA_OUTPUT"
|
echo "version_no_v=${VERSION_NO_V}" >> "$GITEA_OUTPUT"
|
||||||
|
|
||||||
|
# The tag is the source of truth — inject it into the binary.
|
||||||
LDFLAGS="-s -w -X sourcedock.dev/petrbalvin/nuntius/internal/version.Version=${VERSION_NO_V}"
|
LDFLAGS="-s -w -X sourcedock.dev/petrbalvin/nuntius/internal/version.Version=${VERSION_NO_V}"
|
||||||
mkdir -p bin
|
mkdir -p bin
|
||||||
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} CGO_ENABLED=0 \
|
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} CGO_ENABLED=0 \
|
||||||
@@ -60,6 +60,7 @@ jobs:
|
|||||||
-o "bin/nuntius-${VERSION_NO_V}-${{ matrix.goos }}-${{ matrix.goarch }}" \
|
-o "bin/nuntius-${VERSION_NO_V}-${{ matrix.goos }}-${{ matrix.goarch }}" \
|
||||||
./cmd/server
|
./cmd/server
|
||||||
|
|
||||||
|
# Artifacts stay on v3 — v4 detects Gitea as GHES and aborts.
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
@@ -67,7 +68,9 @@ jobs:
|
|||||||
path: bin/nuntius-${{ steps.build.outputs.version_no_v }}-${{ matrix.goos }}-${{ matrix.goarch }}
|
path: bin/nuntius-${{ steps.build.outputs.version_no_v }}-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
# Only binaries matching the runner can be smoke-tested.
|
||||||
- name: Smoke test
|
- name: Smoke test
|
||||||
|
if: matrix.goos == 'linux' && matrix.goarch == 'amd64'
|
||||||
run: |
|
run: |
|
||||||
chmod +x bin/nuntius-${{ steps.build.outputs.version_no_v }}-${{ matrix.goos }}-${{ matrix.goarch }}
|
chmod +x bin/nuntius-${{ steps.build.outputs.version_no_v }}-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||||
./bin/nuntius-${{ steps.build.outputs.version_no_v }}-${{ matrix.goos }}-${{ matrix.goarch }} --version
|
./bin/nuntius-${{ steps.build.outputs.version_no_v }}-${{ matrix.goos }}-${{ matrix.goarch }} --version
|
||||||
@@ -75,8 +78,10 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
runs-on: fedora
|
runs-on: fedora
|
||||||
needs: build
|
needs: build
|
||||||
|
permissions:
|
||||||
|
releases: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v3
|
||||||
@@ -108,17 +113,12 @@ jobs:
|
|||||||
- name: Create release
|
- name: Create release
|
||||||
env:
|
env:
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
GITEA_SERVER_URL: ${{ gitea.server_url }}
|
||||||
|
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||||
|
GITEA_REF_NAME: ${{ gitea.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
# JSON-escape the release body with sed: backslashes first, then
|
BODY=$(sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\t/\\t/g' -e 's/\r//g' release-body.md | sed ':a;N;$!ba;s/\n/\\n/g')
|
||||||
# quotes, tabs, and carriage returns, and finally fold newlines into
|
|
||||||
# \n. Wrap the result in quotes to form a JSON string.
|
|
||||||
BODY=$(sed -e 's/\\/\\\\/g' \
|
|
||||||
-e 's/"/\\"/g' \
|
|
||||||
-e 's/\t/\\t/g' \
|
|
||||||
-e 's/\r//g' \
|
|
||||||
release-body.md \
|
|
||||||
| sed ':a;N;$!ba;s/\n/\\n/g')
|
|
||||||
BODY="\"${BODY}\""
|
BODY="\"${BODY}\""
|
||||||
|
|
||||||
response=$(curl -sS -w '\n%{http_code}' \
|
response=$(curl -sS -w '\n%{http_code}' \
|
||||||
@@ -126,31 +126,27 @@ jobs:
|
|||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-X POST \
|
-X POST \
|
||||||
"${GITEA_SERVER_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases" \
|
"${GITEA_SERVER_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases" \
|
||||||
-d "{
|
-d "{\"tag_name\":\"${GITEA_REF_NAME}\",\"name\":\"${GITEA_REF_NAME}\",\"body\":${BODY},\"draft\":false,\"prerelease\":false}")
|
||||||
\"tag_name\": \"${GITEA_REF_NAME}\",
|
|
||||||
\"name\": \"${GITEA_REF_NAME}\",
|
|
||||||
\"body\": ${BODY},
|
|
||||||
\"draft\": false,
|
|
||||||
\"prerelease\": false
|
|
||||||
}")
|
|
||||||
|
|
||||||
http_code=$(echo "$response" | tail -1)
|
http_code=$(echo "$response" | tail -1)
|
||||||
body=$(echo "$response" | sed '$d')
|
payload=$(echo "$response" | sed '$d')
|
||||||
|
|
||||||
echo "HTTP ${http_code}"
|
echo "HTTP ${http_code}"
|
||||||
|
|
||||||
if [ "$http_code" != "201" ]; then
|
if [ "$http_code" != "201" ]; then
|
||||||
echo "Failed to create release: ${body}"
|
echo "Failed to create release: ${payload}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
RELEASE_ID=$(echo "$body" | grep -oE '"id"[[:space:]]*:[[:space:]]*[0-9]+' | head -1 | grep -oE '[0-9]+')
|
RELEASE_ID=$(echo "$payload" | grep -oE '"id"[[:space:]]*:[[:space:]]*[0-9]+' | head -1 | grep -oE '[0-9]+')
|
||||||
echo "Created release ID=${RELEASE_ID}"
|
echo "Created release ID=${RELEASE_ID}"
|
||||||
printf '%s' "${RELEASE_ID}" > release-id.txt
|
printf '%s' "${RELEASE_ID}" > release-id.txt
|
||||||
|
|
||||||
- name: Upload assets
|
- name: Upload assets
|
||||||
env:
|
env:
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
GITEA_SERVER_URL: ${{ gitea.server_url }}
|
||||||
|
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||||
|
GITEA_REF_NAME: ${{ gitea.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
RELEASE_ID=$(cat release-id.txt)
|
RELEASE_ID=$(cat release-id.txt)
|
||||||
|
|||||||
+22
-17
@@ -1,3 +1,4 @@
|
|||||||
|
# Test — Go. Runs on push and pull request to development. Never on main.
|
||||||
name: Test
|
name: Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -10,9 +11,9 @@ jobs:
|
|||||||
vet:
|
vet:
|
||||||
runs-on: fedora
|
runs-on: fedora
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: "1.26"
|
go-version: "1.26"
|
||||||
|
|
||||||
@@ -20,12 +21,11 @@ jobs:
|
|||||||
run: go mod download
|
run: go mod download
|
||||||
|
|
||||||
- name: gofmt
|
- name: gofmt
|
||||||
# `gofmt -l` prints the names of unformatted files. An empty
|
# gofmt -l prints unformatted files; an empty output is the only pass.
|
||||||
# output is the only acceptable result.
|
|
||||||
run: |
|
run: |
|
||||||
unformatted=$(gofmt -l cmd internal pkg)
|
unformatted=$(gofmt -l .)
|
||||||
if [ -n "$unformatted" ]; then
|
if [ -n "$unformatted" ]; then
|
||||||
echo "The following files need gofmt:"
|
echo "These files need gofmt:"
|
||||||
echo "$unformatted"
|
echo "$unformatted"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -37,36 +37,44 @@ jobs:
|
|||||||
runs-on: fedora
|
runs-on: fedora
|
||||||
needs: vet
|
needs: vet
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: "1.26"
|
go-version: "1.26"
|
||||||
|
|
||||||
- name: Download dependencies
|
- name: Download dependencies
|
||||||
run: go mod download
|
run: go mod download
|
||||||
|
|
||||||
- name: Install build tools
|
# The runner images have no gcc; the race detector needs CGO.
|
||||||
|
- name: Install gcc
|
||||||
run: dnf install -y gcc
|
run: dnf install -y gcc
|
||||||
|
|
||||||
- name: go test -race
|
- name: go test -race
|
||||||
run: go test -race -count=1 ./...
|
run: go test -race -count=1 ./...
|
||||||
|
|
||||||
- name: Check coverage threshold
|
# Coverage is measured over the library packages; cmd/ is thin glue.
|
||||||
|
- name: Coverage gate — 80 % minimum
|
||||||
run: |
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
go test -coverprofile=coverage.out ./internal/... ./pkg/...
|
go test -coverprofile=coverage.out ./internal/... ./pkg/...
|
||||||
go tool cover -func=coverage.out | grep '^total:' | python3 -c "import sys; pct=float(sys.stdin.read().split()[2].rstrip('%')); sys.exit(0 if pct >= 80 else 1)" || (echo "ERROR: coverage < 80%"; exit 1)
|
coverage=$(go tool cover -func=coverage.out | awk '/^total:/ { gsub("%", "", $3); print $3 }')
|
||||||
|
echo "Total coverage: ${coverage}%"
|
||||||
|
if awk -v c="$coverage" 'BEGIN { exit !(c+0 < 80) }'; then
|
||||||
|
echo "ERROR: coverage ${coverage}% is below the 80% threshold"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: go test ./examples
|
- name: Build examples
|
||||||
run: go build ./examples/...
|
run: go build ./examples/...
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: fedora
|
runs-on: fedora
|
||||||
needs: test
|
needs: test
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: "1.26"
|
go-version: "1.26"
|
||||||
|
|
||||||
@@ -76,8 +84,5 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: go build -ldflags="-s -w" -o bin/nuntius ./cmd/server
|
run: go build -ldflags="-s -w" -o bin/nuntius ./cmd/server
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: go test ./...
|
|
||||||
|
|
||||||
- name: Smoke test
|
- name: Smoke test
|
||||||
run: ./bin/nuntius --version
|
run: ./bin/nuntius --version
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
when it finds unformatted files.
|
when it finds unformatted files.
|
||||||
- **Missing `just fmt` recipe** — added `gofmt -w` target.
|
- **Missing `just fmt` recipe** — added `gofmt -w` target.
|
||||||
- **Copyright headers** — added to all Go and Python source files.
|
- **Copyright headers** — added to all Go and Python source files.
|
||||||
|
- **Smoke test restricted to native builds** — cross-compiled binaries now
|
||||||
|
skip the smoke test step since they cannot execute on the Linux amd64 CI
|
||||||
|
runner.
|
||||||
|
|
||||||
[development]: https://sourcedock.dev/petrbalvin/nuntius/compare/v1.1.0...development
|
[development]: https://sourcedock.dev/petrbalvin/nuntius/compare/v1.1.0...development
|
||||||
[1.1.0]: https://sourcedock.dev/petrbalvin/nuntius/releases/tag/v1.1.0
|
[1.1.0]: https://sourcedock.dev/petrbalvin/nuntius/releases/tag/v1.1.0
|
||||||
|
|||||||
Reference in New Issue
Block a user