Files
probo/.github/workflows/make.yaml
Bryan Frimin c4e81ed092 Inline OAuth2 signing key in config
The OAuth2/OIDC server accepted its signing key via a file path
(key-file), while every other PEM key in the probod config (SAML
private key, ACME account key) is embedded inline. Switch the
field to a private-key string so the convention is uniform.

The signing key is operator-supplied material that must outlive
any process restart, so the bootstrap builder now treats
OAUTH2_SERVER_SIGNING_KEY as required and refuses to start
without one; silently minting a fresh key per boot would break
token validation across rollouts. The OAUTH2_SERVER_* env vars
otherwise flow through builder.Build like the existing SAML
block so the new OAuth2Server section is populated end-to-end.

Rework the e2e harness to render its config via bootstrap at
test setup, which removes the static
e2e/console/testdata/config.yaml and the previously generated
test-only PEM file. A per-run RSA key is minted via
bootstrap.GenerateOAuth2SigningKey (kept public for test
tooling) and injected through the builder env map. CI now
passes ACME_ROOT_CA inline instead of mutating a YAML on disk.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-04-21 17:56:00 +02:00

420 lines
16 KiB
YAML

name: "make"
on:
push:
branches:
- "main"
pull_request:
branches:
- "main"
permissions:
contents: "read"
jobs:
# ── Snapshot: build frontend apps ──────────────────────────────────
build-apps:
name: "build-apps"
if: github.event_name == 'push'
runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache"
permissions:
contents: "read"
steps:
- uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6
with:
submodules: recursive
- uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2
- uses: "./.github/actions/setup"
with:
go: "false"
- run: "npm --workspace @probo/emails run build"
- run: "make relay"
- name: "Build console"
run: |
npm --workspace @probo/console run check
NODE_ENV=production npm --workspace @probo/console run build
- name: "Build trust"
run: |
npm --workspace @probo/trust run check
NODE_ENV=production npm --workspace @probo/trust run build
- uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7
with:
name: "frontend-apps"
path: |
apps/console/dist/
apps/trust/dist/
packages/emails/dist/
retention-days: 1
# ── Snapshot: build Go binaries (matrix by GOOS/GOARCH) ────────────
build-snapshot-binary:
name: "binary (${{ matrix.goos }}/${{ matrix.goarch }})"
if: github.event_name == 'push'
needs: [build-apps]
runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache"
permissions:
contents: "read"
strategy:
fail-fast: false
matrix:
include:
- { goos: linux, goarch: amd64 }
- { goos: linux, goarch: arm64 }
- { goos: darwin, goarch: amd64 }
- { goos: darwin, goarch: arm64 }
- { goos: windows, goarch: amd64 }
- { goos: freebsd, goarch: amd64 }
- { goos: freebsd, goarch: arm64 }
- { goos: openbsd, goarch: amd64 }
- { goos: openbsd, goarch: arm64 }
steps:
- uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6
with:
submodules: recursive
- uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2
- uses: "./.github/actions/setup"
with:
node: "false"
- uses: "actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" # v8
with:
name: "frontend-apps"
- name: "Generate Go code"
run: |
go generate ./pkg/server/api/connect/v1
go generate ./pkg/server/api/console/v1
go generate ./pkg/server/api/trust/v1
go generate ./pkg/server/api/mcp/v1
- name: "Build binaries"
env:
CGO_ENABLED: "0"
GOOS: "${{ matrix.goos }}"
GOARCH: "${{ matrix.goarch }}"
run: |
EXT=""
if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi
go build -ldflags "-s -w -X 'main.version=snapshot' -X 'main.env=prod'" \
-gcflags="-e" -o "dist/probod${EXT}" ./cmd/probod/main.go
go build -ldflags "-s -w" \
-gcflags="-e" -o "dist/probod-bootstrap${EXT}" ./cmd/probod-bootstrap/main.go
go build -ldflags "-s -w -X 'main.version=snapshot'" \
-gcflags="-e" -o "dist/prb${EXT}" ./cmd/prb/main.go
- uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7
with:
name: "binary-${{ matrix.goos }}-${{ matrix.goarch }}"
path: "dist/"
retention-days: 1
# ── Snapshot: build Docker images (matrix by architecture) ─────────
build-snapshot-docker:
name: "docker (${{ matrix.arch }})"
if: github.event_name == 'push'
needs: [build-snapshot-binary]
runs-on: "runs-on=${{ github.run_id }}/runner=${{ matrix.runner }}/extras=s3-cache"
permissions:
contents: "read"
security-events: "write"
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
platform: "linux/amd64"
runner: "4cpu-linux-x64"
- arch: arm64
platform: "linux/arm64"
runner: "4cpu-linux-arm64"
steps:
- uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6
- uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2
- uses: "docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd" # v4.0.0
- uses: "docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2" # v4.0.0
with:
username: "gearnode"
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- uses: "actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" # v8
with:
name: "binary-linux-${{ matrix.arch }}"
path: "linux/${{ matrix.arch }}"
- name: "Prepare binaries"
run: "chmod +x linux/${{ matrix.arch }}/*"
- name: "Build Docker image"
run: |
docker buildx build \
--platform "${{ matrix.platform }}" \
--tag "ghcr.io/getprobo/probo:snapshot-${{ matrix.arch }}" \
--load \
.
- name: "Cache Trivy database"
uses: "runs-on/cache@a5f51d6f3fece787d03b7b4e981c82538a0654ed" # v4
with:
path: ~/.cache/trivy
key: "trivy-db-${{ matrix.arch }}-${{ github.run_id }}"
restore-keys: "trivy-db-${{ matrix.arch }}-"
- name: "Scan Docker image with Trivy"
uses: "aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1" # 0.35.0
with:
image-ref: "ghcr.io/getprobo/probo:snapshot-${{ matrix.arch }}"
format: "sarif"
output: "trivy-results.sarif"
exit-code: 0
ignore-unfixed: true
vuln-type: "os,library"
severity: "CRITICAL,HIGH"
cache-dir: ~/.cache/trivy
- name: "Upload Trivy scan results"
uses: "github/codeql-action/upload-sarif@6bc82e05fd0ea64601dd4b465378bbcf57de0314" # v4.32.1
with:
sarif_file: "trivy-results.sarif"
category: "trivy-${{ matrix.arch }}"
# ── Snapshot: SBOM & vulnerability scan ────────────────────────────
snapshot-scan:
name: "snapshot-scan"
if: github.event_name == 'push'
needs: [build-snapshot-binary]
runs-on: "runs-on=${{ github.run_id }}/runner=2cpu-linux-x64/extras=s3-cache"
permissions:
contents: "read"
steps:
- uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6
with:
submodules: recursive
- uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2
- uses: "anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610" # v0.24.0
with:
path: ./
format: cyclonedx-json
output-file: sbom.json
- uses: "anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2" # v7.4.0
with:
sbom: "sbom.json"
fail-build: true
severity-cutoff: critical
output-format: table
# ── Build (PR + push validation) ───────────────────────────────────
build:
name: "build"
runs-on: "runs-on=${{ github.run_id }}/runner=8cpu-linux-x64/extras=s3-cache"
permissions:
contents: "read"
steps:
- uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6
with:
submodules: recursive
- uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2
- uses: "./.github/actions/setup"
with:
node: "false"
- name: "Create placeholder dist files"
run: |
mkdir -p apps/console/dist apps/trust/dist packages/emails/dist
echo dev-server > apps/console/dist/index.html
echo dev-server > apps/trust/dist/index.html
echo dev-server > packages/emails/dist/placeholder
- name: "Generate Go code"
run: |
go generate ./pkg/server/api/connect/v1
go generate ./pkg/server/api/console/v1
go generate ./pkg/server/api/trust/v1
go generate ./pkg/server/api/mcp/v1
- name: "Build binaries"
env:
CGO_ENABLED: "0"
run: |
pids=()
go build -ldflags "-s -w -X 'main.version=snapshot' -X 'main.env=prod'" \
-gcflags="-e" -o bin/probod ./cmd/probod/main.go & pids+=($!)
go build -ldflags "-s -w" \
-gcflags="-e" -o bin/probod-bootstrap ./cmd/probod-bootstrap/main.go & pids+=($!)
go build -ldflags "-s -w -X 'main.version=snapshot'" \
-gcflags="-e" -o bin/prb ./cmd/prb/main.go & pids+=($!)
for pid in "${pids[@]}"; do wait "$pid"; done
lint-go:
name: "lint-go"
runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache"
permissions:
contents: "read"
pull-requests: "write"
steps:
- uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6
with:
submodules: recursive
- uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2
- uses: "./.github/actions/setup"
with:
node: "false"
- uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
install-only: true
- uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0
- name: "Create placeholder dist files"
run: |
mkdir -p apps/console/dist apps/trust/dist packages/emails/dist
echo dev-server > apps/console/dist/index.html
echo dev-server > apps/trust/dist/index.html
echo dev-server > packages/emails/dist/placeholder
- name: "Generate Go code"
run: |
go generate ./pkg/server/api/connect/v1
go generate ./pkg/server/api/console/v1
go generate ./pkg/server/api/trust/v1
go generate ./pkg/server/api/mcp/v1
- name: "Run gofmt"
run: |
output="$(gofmt -l apps cmd packages pkg e2e)"
if [ -n "$output" ]; then
echo "error: 'gofmt' found unformatted files:"
echo "$output"
exit 1
fi
- name: "Run go fix"
run: |
output="$(CGO_ENABLED=0 go fix -diff -omitzero=false ./apps/... ./cmd/... ./packages/... ./pkg/... ./e2e/...)"
if [ -n "$output" ]; then
echo "error: 'go fix' suggests changes; please apply them"
echo "$output"
exit 1
fi
- name: "Run golangci-lint"
env:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
golangci-lint run --out-format=line-number ./... 2>&1 | \
reviewdog -f=golangci-lint -reporter=github-pr-review -filter-mode=nofilter -fail-level=error -name="golangci-lint"
else
golangci-lint run ./...
fi
lint-js:
name: "lint-js"
runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache"
permissions:
contents: "read"
pull-requests: "write"
steps:
- uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6
with:
submodules: recursive
- uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2
- uses: "./.github/actions/setup"
with:
go: "false"
- uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0
- name: "Generate Relay artifacts"
run: make relay
- name: "Run eslint"
env:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
for dir in apps/console apps/trust packages/ui packages/eslint-config; do
(cd "$dir" && npx eslint . --concurrency 4 --format stylish 2>/dev/null) | \
reviewdog -f=eslint -reporter=github-pr-review -filter-mode=nofilter -fail-level=error -name="eslint ($dir)"
done
(cd packages/n8n-node && npx n8n-node lint)
else
npm run lint
fi
test:
name: "test"
runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache"
permissions:
contents: "read"
steps:
- uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6
with:
submodules: recursive
- uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2
- uses: "./.github/actions/setup"
- name: "Create placeholder dist files"
run: |
mkdir -p apps/console/dist apps/trust/dist
echo dev-server > apps/console/dist/index.html
echo dev-server > apps/trust/dist/index.html
- run: "npm --workspace @probo/emails run build"
- run: "make test"
env:
GOTESTSUM_JUNITFILE: "junit.xml"
- name: "Upload test results"
uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7
if: "always()"
continue-on-error: true
with:
name: "junit-results"
path: "junit.xml"
retention-days: 30
- run: "go tool cover -html=coverage.out -o coverage.html"
- uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7
continue-on-error: true
with:
name: "coverage-reports"
path: |
coverage.out
coverage.html
retention-days: 30
test-e2e:
name: "test-e2e"
runs-on: "runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/extras=s3-cache"
permissions:
contents: "read"
steps:
- uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6
with:
submodules: recursive
- uses: "runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0" # v2
- uses: "./.github/actions/setup"
- uses: "docker/setup-compose-action@8cccb8c14b6500aaffebff1aa49c502c34d2e5e6" # v2.1.0
- uses: "docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2" # v4.0.0
with:
username: "gearnode"
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: "Install mkcert"
run: |
go install filippo.io/mkcert@latest
sudo mkcert -install 2>&1 | grep -v 'no Firefox and/or Chrome/Chromium security databases found' || true
- name: "Cache Docker images"
id: docker-cache
uses: "runs-on/cache@a5f51d6f3fece787d03b7b4e981c82538a0654ed" # v4
with:
path: /tmp/docker-images
key: "docker-images-${{ hashFiles('compose.yaml') }}"
- name: "Load cached Docker images"
if: steps.docker-cache.outputs.cache-hit == 'true'
run: "docker load -i /tmp/docker-images/images.tar"
- name: "Pull and save Docker images"
if: steps.docker-cache.outputs.cache-hit != 'true'
run: |
docker compose pull
mkdir -p /tmp/docker-images
docker save $(docker compose config --images) -o /tmp/docker-images/images.tar
- name: "Build and start stack in parallel"
run: |
make stack-up &
STACK_PID=$!
SKIP_APPS=1 make bin/probod
wait $STACK_PID
- run: "make stack-ps"
- name: "Run e2e tests"
env:
PROBO_E2E_BINARY: "${{ github.workspace }}/bin/probod"
GOTESTSUM_FORMAT: "testname"
GOTESTSUM_JUNITFILE: "junit-e2e.xml"
run: |
ACME_ROOT_CA="$(cat compose/pebble/certs/rootCA.pem)" \
CGO_ENABLED=1 go tool gotestsum -- -race -cover -coverprofile=coverage.out -count=1 ./e2e/console/...
- name: "Upload test results"
uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7
if: "always()"
continue-on-error: true
with:
name: "junit-e2e-results"
path: "junit-e2e.xml"
retention-days: 30