Compare commits
3 Commits
b4b48067cc
..
v0.1.5
| Author | SHA1 | Date | |
|---|---|---|---|
| d63a440f63 | |||
| f38a2a689c | |||
| ba4133bf5d |
@@ -1,37 +0,0 @@
|
||||
name: Generate protobuf code
|
||||
description: >
|
||||
Installs protoc and the pinned protoc-gen-go / protoc-gen-go-grpc plugins,
|
||||
then runs `make proto-gen`. api/grpc/pb/ is gitignored (see .gitignore) and
|
||||
regenerated at build time (mirrors what the Dockerfile does for image
|
||||
builds), so any job that compiles Go code needs this step first or
|
||||
`github.com/igodwin/notifier/api/grpc/pb` won't resolve.
|
||||
#
|
||||
# Local composite action, referenced from ci.yml via:
|
||||
# uses: ./.gitea/actions/generate-proto
|
||||
# Gitea Actions supports local composite actions the same way GitHub Actions
|
||||
# does. Must run after a Go toolchain is on PATH (i.e. after actions/setup-go).
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install protoc
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends protobuf-compiler
|
||||
protoc --version
|
||||
|
||||
# Pinned exactly to the versions this module already depends on
|
||||
# (google.golang.org/protobuf in go.mod, and the matching
|
||||
# protoc-gen-go-grpc release) - deliberately not @latest, so CI can't
|
||||
# drift out from under the checked-in go.mod without review.
|
||||
- name: Install protoc-gen-go / protoc-gen-go-grpc (pinned)
|
||||
shell: bash
|
||||
run: |
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.10
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5.1
|
||||
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Generate protobuf code (make proto-gen)
|
||||
shell: bash
|
||||
run: make proto-gen
|
||||
@@ -0,0 +1,169 @@
|
||||
name: Build and Publish Container
|
||||
|
||||
# Builds and publishes a multi-arch image on every push to main, minting the
|
||||
# next patch version from git tags (vX.Y.Z) and pushing the tag back.
|
||||
#
|
||||
# NOTE (public repo): unlike private app repos, this workflow deliberately has
|
||||
# NO step that pushes to the deployment (GitOps) repository and holds no
|
||||
# credentials for it. Deployment repos are expected to poll the registry and
|
||||
# pull new tags themselves.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'Git tag (e.g. v0.1.5) or commit SHA to rebuild. Leave empty to build latest.'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
env:
|
||||
REGISTRY: gitea.ivangodwin.com
|
||||
IMAGE_NAME: ${{ gitea.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
runs-on: docker
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
run: |
|
||||
if [ -n "${{ inputs.ref }}" ]; then
|
||||
git clone https://${{ gitea.actor }}:${{ gitea.token }}@gitea.ivangodwin.com/${{ gitea.repository }}.git .
|
||||
git fetch --tags
|
||||
git checkout "${{ inputs.ref }}"
|
||||
else
|
||||
git clone --depth 1 https://${{ gitea.actor }}:${{ gitea.token }}@gitea.ivangodwin.com/${{ gitea.repository }}.git .
|
||||
git checkout ${{ gitea.sha }}
|
||||
fi
|
||||
|
||||
- name: Determine next version
|
||||
run: |
|
||||
REF="${{ inputs.ref }}"
|
||||
if [ -n "$REF" ] && echo "$REF" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
|
||||
VERSION="$REF"
|
||||
echo "REBUILD=true" >> $GITHUB_ENV
|
||||
else
|
||||
git fetch --tags
|
||||
LATEST=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || true)
|
||||
if [ -z "$LATEST" ]; then
|
||||
VERSION="v0.1.0"
|
||||
else
|
||||
MAJOR=$(echo "$LATEST" | cut -d. -f1 | tr -d 'v')
|
||||
MINOR=$(echo "$LATEST" | cut -d. -f2)
|
||||
PATCH=$(echo "$LATEST" | cut -d. -f3)
|
||||
VERSION="v${MAJOR}.${MINOR}.$((PATCH + 1))"
|
||||
fi
|
||||
echo "REBUILD=false" >> $GITHUB_ENV
|
||||
fi
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
||||
|
||||
- name: Log in to Gitea Container Registry
|
||||
run: |
|
||||
echo "${{ secrets.CI_TOKEN }}" | docker login -u "${{ secrets.CI_USER }}" --password-stdin ${{ env.REGISTRY }}
|
||||
|
||||
- name: Register QEMU emulators
|
||||
run: |
|
||||
docker run --rm --privileged tonistiigi/binfmt:latest --install all
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
run: |
|
||||
docker buildx inspect multiarch >/dev/null 2>&1 \
|
||||
|| docker buildx create --name multiarch --driver docker-container
|
||||
docker buildx use multiarch
|
||||
docker buildx inspect --bootstrap
|
||||
|
||||
- name: Build and push multi-arch image
|
||||
run: |
|
||||
IMAGE_REF="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }}"
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--build-arg VERSION="${{ env.VERSION }}" \
|
||||
--build-arg GIT_COMMIT="$(git rev-parse --short HEAD)" \
|
||||
--build-arg BUILD_TIME="$(date -u '+%Y-%m-%d_%H:%M:%S_UTC')" \
|
||||
--no-cache \
|
||||
--provenance=false \
|
||||
-t "$IMAGE_REF" \
|
||||
--push \
|
||||
.
|
||||
|
||||
- name: Tag release
|
||||
if: env.REBUILD != 'true'
|
||||
run: |
|
||||
git tag "${{ env.VERSION }}"
|
||||
git push https://${{ gitea.actor }}:${{ gitea.token }}@gitea.ivangodwin.com/${{ gitea.repository }}.git "${{ env.VERSION }}"
|
||||
|
||||
- name: Clean up old container images
|
||||
continue-on-error: true
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
CI_TOKEN: ${{ secrets.CI_TOKEN }}
|
||||
run: |
|
||||
# Best-effort cleanup of old container image versions; must never
|
||||
# fail or stall the pipeline, so the whole body runs under a hard
|
||||
# timeout and the step always exits 0 itself.
|
||||
cat > /tmp/cleanup.sh <<'CLEAN'
|
||||
#!/bin/sh
|
||||
set -u
|
||||
|
||||
if ! apk add -q --no-cache curl; then
|
||||
echo "curl unavailable; skipping cleanup."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
API="https://gitea.ivangodwin.com/api/v1"
|
||||
OWNER="igodwin"
|
||||
NAME="notifier"
|
||||
KEEP=5
|
||||
|
||||
BODY=$(curl -s --connect-timeout 15 --max-time 30 \
|
||||
-H "Authorization: token ${CI_TOKEN}" \
|
||||
"${API}/packages/${OWNER}?type=container&q=${NAME}&limit=200" || true)
|
||||
|
||||
if [ -z "$BODY" ] || [ "$(printf '%s' "$BODY" | tr -d ' \t\r\n')" = "[]" ]; then
|
||||
echo "No packages found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
VERSIONS=$(printf '%s' "$BODY" \
|
||||
| grep -oE '"version":"v[0-9]+\.[0-9]+\.[0-9]+"' \
|
||||
| grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' \
|
||||
| sort -u)
|
||||
MINORS=$(printf '%s' "$VERSIONS" | grep -oE '^v[0-9]+\.[0-9]+' | sort -u)
|
||||
|
||||
for MINOR in $MINORS; do
|
||||
PATCHES=$(printf '%s' "$VERSIONS" | grep "^${MINOR}\." | sort -t. -k3,3n)
|
||||
TOTAL=$(printf '%s\n' "$PATCHES" | grep -c .)
|
||||
|
||||
if [ "$TOTAL" -le "$KEEP" ]; then
|
||||
echo "${MINOR}: ${TOTAL} versions, nothing to delete"
|
||||
continue
|
||||
fi
|
||||
|
||||
TO_DELETE=$((TOTAL - KEEP))
|
||||
echo "${MINOR}: ${TOTAL} versions, keeping ${KEEP}, deleting ${TO_DELETE}"
|
||||
|
||||
printf '%s\n' "$PATCHES" | head -n "$TO_DELETE" | while read -r VER; do
|
||||
STATUS=$(curl -s --connect-timeout 15 --max-time 60 -o /dev/null -w "%{http_code}" \
|
||||
-X DELETE -H "Authorization: token ${CI_TOKEN}" \
|
||||
"${API}/packages/${OWNER}/container/${NAME}/${VER}" || echo 000)
|
||||
case "$STATUS" in
|
||||
200|202|204) echo " deleted ${NAME}:${VER} (HTTP ${STATUS})" ;;
|
||||
*) echo " WARN: could not delete ${NAME}:${VER} (HTTP ${STATUS})" ;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
CLEAN
|
||||
|
||||
timeout 240 sh /tmp/cleanup.sh
|
||||
rc=$?
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
echo "cleanup did not finish cleanly (rc=${rc}); ignoring and exiting green."
|
||||
fi
|
||||
exit 0
|
||||
@@ -0,0 +1,47 @@
|
||||
name: Bump Version
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
component:
|
||||
description: 'Version component to bump'
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- minor
|
||||
- major
|
||||
|
||||
jobs:
|
||||
bump:
|
||||
runs-on: docker
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
run: |
|
||||
git clone --depth 1 https://${{ gitea.actor }}:${{ gitea.token }}@gitea.ivangodwin.com/${{ gitea.repository }}.git .
|
||||
|
||||
- name: Compute and push new version tag
|
||||
run: |
|
||||
git fetch --tags
|
||||
LATEST=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || true)
|
||||
if [ -z "$LATEST" ]; then
|
||||
if [ "${{ inputs.component }}" = "major" ]; then
|
||||
VERSION="v1.0.0"
|
||||
else
|
||||
VERSION="v0.1.0"
|
||||
fi
|
||||
else
|
||||
MAJOR=$(echo "$LATEST" | cut -d. -f1 | tr -d 'v')
|
||||
MINOR=$(echo "$LATEST" | cut -d. -f2)
|
||||
if [ "${{ inputs.component }}" = "major" ]; then
|
||||
VERSION="v$((MAJOR + 1)).0.0"
|
||||
else
|
||||
VERSION="v${MAJOR}.$((MINOR + 1)).0"
|
||||
fi
|
||||
fi
|
||||
echo "Bumping to ${VERSION}"
|
||||
git tag "$VERSION"
|
||||
git push https://${{ gitea.actor }}:${{ gitea.token }}@gitea.ivangodwin.com/${{ gitea.repository }}.git "$VERSION"
|
||||
+64
-68
@@ -1,10 +1,9 @@
|
||||
name: CI
|
||||
|
||||
# Gitea Actions reads workflows from .gitea/workflows/ and executes them with
|
||||
# a GitHub-Actions-compatible engine (act_runner). Standard actions/* steps
|
||||
# work as long as the runner can resolve github.com (either directly or via a
|
||||
# configured actions mirror on the Gitea instance) - see notes at the bottom
|
||||
# of this file for offline/mirrored setups.
|
||||
# Runner-native workflow: the self-hosted act_runner's job containers have no
|
||||
# Node.js, so JavaScript actions (actions/checkout, actions/setup-go, ...)
|
||||
# fail with "Cannot find: node in PATH". Every step here is a plain shell
|
||||
# run-step inside a golang container instead.
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -13,8 +12,6 @@ on:
|
||||
pull_request:
|
||||
|
||||
# Cancel superseded runs for the same ref to save runner capacity.
|
||||
# Gitea Actions accepts both the `gitea.*` and `github.*` context aliases;
|
||||
# `github.*` is used here since it's the more portable spelling.
|
||||
concurrency:
|
||||
group: ci-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -22,98 +19,97 @@ concurrency:
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: docker
|
||||
container:
|
||||
image: golang:1.25-alpine
|
||||
# Advisory while the pre-existing lint backlog (~95 findings) is worked
|
||||
# off; flip to blocking by removing continue-on-error once clean.
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
# api/grpc/pb/ is gitignored and generated at build time (see
|
||||
# .gitignore and the Dockerfile), so anything that compiles this
|
||||
# module - including the linter, which type-checks packages - needs
|
||||
# the generated code in place first.
|
||||
- name: Generate protobuf code
|
||||
uses: ./.gitea/actions/generate-proto
|
||||
|
||||
# Installing the pinned binary via the official install script is more
|
||||
# portable across Gitea Actions runner images than golangci-lint-action,
|
||||
# which assumes a GitHub-hosted runner environment (it works, but the
|
||||
# install script approach has fewer surprises on self-hosted runners
|
||||
# and lets us pin an exact version without depending on the action's
|
||||
# own release cadence).
|
||||
- name: Install golangci-lint
|
||||
run: |
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | \
|
||||
sh -s -- -b "$(go env GOPATH)/bin" v2.12.2
|
||||
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
|
||||
apk add -q --no-cache git make protobuf protobuf-dev curl
|
||||
git init -q .
|
||||
git remote add origin https://gitea.ivangodwin.com/${{ gitea.repository }}.git
|
||||
git fetch -q --depth 1 origin ${{ gitea.sha }}
|
||||
git checkout -q FETCH_HEAD
|
||||
|
||||
# api/grpc/pb/ is gitignored and generated at build time, so anything
|
||||
# that compiles this module - including the linter, which type-checks
|
||||
# packages - needs the generated code in place first.
|
||||
- name: Generate protobuf code
|
||||
run: |
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.10
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5.1
|
||||
export PATH="$PATH:$(go env GOPATH)/bin"
|
||||
make proto-gen
|
||||
|
||||
# Installed via `go install` (module proxy + sumdb verification): the
|
||||
# official install.sh tarball download hit checksum mismatches on this
|
||||
# runner.
|
||||
- name: Run golangci-lint
|
||||
run: golangci-lint run ./...
|
||||
run: |
|
||||
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2
|
||||
"$(go env GOPATH)/bin/golangci-lint" run ./...
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: docker
|
||||
container:
|
||||
image: golang:1.25-alpine
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
run: |
|
||||
apk add -q --no-cache git make protobuf protobuf-dev gcc musl-dev
|
||||
git init -q .
|
||||
git remote add origin https://gitea.ivangodwin.com/${{ gitea.repository }}.git
|
||||
git fetch -q --depth 1 origin ${{ gitea.sha }}
|
||||
git checkout -q FETCH_HEAD
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
# api/grpc/pb/ is gitignored and generated at build time; without this
|
||||
# the module won't compile (pkg/client, internal/service, etc. import
|
||||
# the generated package).
|
||||
- name: Generate protobuf code
|
||||
uses: ./.gitea/actions/generate-proto
|
||||
run: |
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.10
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5.1
|
||||
export PATH="$PATH:$(go env GOPATH)/bin"
|
||||
make proto-gen
|
||||
|
||||
# tests/e2e uses testcontainers-go and requires a Docker daemon that
|
||||
# isn't guaranteed to be available/usable on Gitea Actions runners, so
|
||||
# it is excluded from CI here via `go list ... | grep -v`. Run it
|
||||
# locally (or on a runner with Docker-in-Docker configured) with:
|
||||
# go test -race ./tests/e2e/...
|
||||
# -race needs cgo, hence gcc/musl-dev above. tests/e2e uses
|
||||
# testcontainers-go (needs a Docker daemon) and is excluded; run it
|
||||
# locally with: go test -race ./tests/e2e/...
|
||||
# coverage.out is left in the workspace; artifact upload is omitted
|
||||
# until the instance's artifact storage is confirmed working.
|
||||
- name: Run tests (excluding e2e)
|
||||
run: |
|
||||
go test -race -covermode=atomic -coverprofile=coverage.out \
|
||||
$(go list ./... | grep -v '/tests/e2e')
|
||||
# coverage.out is left in the workspace for inspection; artifact
|
||||
# upload is intentionally omitted since actions/upload-artifact
|
||||
# support varies by Gitea version/configuration - add it back once
|
||||
# your instance's artifact storage is confirmed working.
|
||||
|
||||
vuln:
|
||||
name: Vulnerability scan
|
||||
runs-on: docker
|
||||
container:
|
||||
image: golang:1.25-alpine
|
||||
# Advisory: govulncheck also reports Go-stdlib findings that are only
|
||||
# fixable by toolchain updates; flip to blocking once triaged.
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
run: |
|
||||
apk add -q --no-cache git make protobuf protobuf-dev
|
||||
git init -q .
|
||||
git remote add origin https://gitea.ivangodwin.com/${{ gitea.repository }}.git
|
||||
git fetch -q --depth 1 origin ${{ gitea.sha }}
|
||||
git checkout -q FETCH_HEAD
|
||||
|
||||
# govulncheck also loads and type-checks the module's packages, so the
|
||||
# generated protobuf code has to exist first.
|
||||
- name: Generate protobuf code
|
||||
uses: ./.gitea/actions/generate-proto
|
||||
|
||||
- name: Install govulncheck
|
||||
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
run: |
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.10
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5.1
|
||||
export PATH="$PATH:$(go env GOPATH)/bin"
|
||||
make proto-gen
|
||||
|
||||
- name: Run govulncheck
|
||||
run: govulncheck ./...
|
||||
run: |
|
||||
go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
"$(go env GOPATH)/bin/govulncheck" ./...
|
||||
|
||||
Reference in New Issue
Block a user