Watch
1
0
Fork
You've already forked RedFlag
0
RedFlag/.gitea/workflows/ci.yml
Fimeg ffa7afbb58 desktop: put this computer health first
The Agent now owns one local view of machine health and top processes. RedFlag opens at dashboard scale, names itself plainly, and renders that evidence before updates.\n\nCI now compiles the Tauri bridge before release day.
2026-08-31 21:20:00 -04:00

328 lines
13 KiB
YAML

name: ci
on:
push:
branches: [main, public]
pull_request:
branches: [main, public]
workflow_dispatch:
jobs:
go-vet:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version-file: agent/go.mod
cache: true
- name: go vet (server)
run: cd server && go vet ./...
- name: go vet (agent)
run: cd agent && go vet ./...
go-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version-file: agent/go.mod
cache: true
- name: go test -race (server)
run: cd server && go test -race -count=1 ./...
- name: go test -race (agent)
run: cd agent && go test -race -count=1 ./...
rust-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
components: clippy
- name: cargo test
run: cd helper && cargo test
- name: cargo clippy
run: cd helper && cargo clippy -- -D warnings
# Cross-compile check: does it build for every target platform?
# Tests run only on native linux-amd64 above; this catches portability
# regressions (cfg(target_os), FFI, path assumptions) without needing
# a runner per OS.
cross-compile:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- goos: linux
goarch: arm64
rust_target: aarch64-unknown-linux-gnu
linker: gcc-aarch64-linux-gnu
use_zigbuild: false
skip_helper: false
- goos: windows
goarch: amd64
rust_target: ""
linker: gcc-mingw-w64-x86-64
use_zigbuild: false
skip_helper: true
- goos: darwin
goarch: arm64
rust_target: aarch64-apple-darwin
linker: ""
use_zigbuild: true
skip_helper: false
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version-file: agent/go.mod
cache: true
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
targets: ${{ matrix.rust_target }}
- name: Install cross-linker
if: matrix.linker != ''
run: sudo apt-get update && sudo apt-get install -y ${{ matrix.linker }}
- name: Install cargo-zigbuild
if: matrix.use_zigbuild
run: pip3 install --break-system-packages cargo-zigbuild
- name: Cross-compile Go (server)
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: "0"
run: cd server && go build -o /dev/null ./cmd/server/
- name: Cross-compile Go (agent)
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: "0"
run: cd agent && go build -o /dev/null ./cmd/agent/
- name: Cross-compile Rust (helper)
if: "!matrix.skip_helper"
run: |
cd helper
if [ "${{ matrix.use_zigbuild }}" = "true" ]; then
cargo zigbuild --release --target ${{ matrix.rust_target }}
else
cargo build --release --target ${{ matrix.rust_target }}
fi
web-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 20
cache: npm
cache-dependency-path: web/package-lock.json
# npm run build = tsc && vite build — type errors and bundling failures
# both surface here, not at release time.
- name: Build web UI
run: cd web && npm ci && npm run build
desktop-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 20
cache: npm
cache-dependency-path: web/package-lock.json
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
- name: Install Tauri system dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
- name: Check RedFlag desktop
run: |
cd web
npm ci
npm run build:desktop
cd ../desktop
cargo check
installer-integrity:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version-file: agent/go.mod
cache: true
- name: Install template integrity
run: cd server && go test -run 'TestInstallTemplateRenders|TestFreshInstallConfigKeys|TestInstallTemplateScriptletSyntax' -v -count=1 ./internal/services/
# Dependency vulnerability scanning — RedFlag held to the supply-chain standard
# it enforces on the fleet. Tools installed directly (no third-party actions) so
# the socket-mounted runner's surface stays small. Go is reachability-gated via a
# documented allowlist (.govulncheck-allow, mirrored in SECURITY.md); npm gates the
# production tree and treats dev-only advisories as warnings; cargo gates outright.
dep-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version-file: agent/go.mod
cache: true
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 20
cache: npm
cache-dependency-path: web/package-lock.json
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
# Provenance first: record the substrate even if a later scan fails the job.
# "Are we using hacked programs to build it?" — this is how we SEE the answer.
# Floor enforcement (fail on an out-of-date engine/toolchain) is the next layer.
- name: Record build substrate
run: |
{
echo "## Build substrate"
echo '```'
echo "go: $(go version)"
echo "rustc: $(rustc --version)"
echo "cargo: $(cargo --version)"
echo "node: $(node --version)"
echo "npm: $(npm --version)"
echo "docker: $(docker version --format '{{.Server.Version}}' 2>&1 || echo 'no engine reachable')"
echo "runner: ${RUNNER_NAME:-unknown} / $(uname -srm)"
echo '```'
} | tee -a "${GITHUB_STEP_SUMMARY:-/dev/stdout}"
# Scanners run latest on purpose — an old scanner misses new advisories.
- name: Install scanners
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
cargo install cargo-audit --locked
# One audited script gates Go (reachability + allowlist), web (prod tree),
# and the Rust helper. release.yml runs the same script with --posture-out
# to emit the attested posture — CI and release can't drift on the verdict.
- name: Dependency gate
run: scripts/dep-scan.sh
no-ai-attribution:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- name: Check commit messages for AI attribution
run: |
# Check all commits in the push/PR range.
# On push: compare against the base branch.
# On PR: compare against the PR base.
if [ "${{ github.event_name }}" = "pull_request" ]; then
RANGE="${{ github.event.pull_request.base.sha }}..${{ github.sha }}"
else
RANGE="${{ github.event.before }}..${{ github.sha }}"
# First push — no before sha. Check last 10 commits.
if [ "${{ github.event.before }}" = "0000000000000000000000000000000000000000" ]; then
RANGE="HEAD~10..HEAD"
fi
fi
PATTERNS="Co-Authored-By:.*[Cc]laude|Co-Authored-By:.*OpenAI|Co-Authored-By:.*ChatGPT|Co-Authored-By:.*Copilot|Co-Authored-By:.*Letta|Co-Authored-By:.*Cursor|Generated by|Generated with|AI-assisted|Auto-generated by"
FAIL=0
while IFS= read -r msg; do
if echo "$msg" | grep -qiE "$PATTERNS"; then
echo "::error::AI attribution found in commit: $msg"
FAIL=1
fi
done < <(git log --format='%s%n%b' $RANGE 2>/dev/null)
if [ "$FAIL" -eq 1 ]; then
echo "::error::Commits contain AI attribution lines. Remove them before merging."
exit 1
fi
echo "No AI attribution found in commits."
action-pins:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Check for floating action refs
run: |
if grep -rE 'uses:.*@(v[0-9]+|stable|main|master)(\s|$)' .gitea/workflows/; then
echo "::error::Floating action refs found — run scripts/update-action-pins.sh"
exit 1
fi
echo "All action refs are SHA-pinned."
public-history:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/public' || github.base_ref == 'public'
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- name: Fetch pinned gitleaks
run: |
command -v curl || (apt-get update -qq && apt-get install -y -qq curl)
curl -fsSL -o /tmp/gitleaks.tar.gz \
https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz
echo '551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb /tmp/gitleaks.tar.gz' |
sha256sum -c -
tar -xzf /tmp/gitleaks.tar.gz -C /tmp gitleaks
- name: Scan complete public history
run: |
/tmp/gitleaks git . --redact --no-banner
scripts/check-public-history.sh HEAD
# The internal forge is the only writer. This job projects the exact tested
# public SHA to Forgejo, without force, then reads it back anonymously.
sync-public:
runs-on: ubuntu-latest
needs: [go-vet, go-test, rust-test, cross-compile, web-build, desktop-check, installer-integrity, dep-scan, no-ai-attribution, action-pins, public-history]
if: github.ref == 'refs/heads/public'
env:
PUBLIC_FORGE_TOKEN: ${{ secrets.PUBLIC_FORGE_TOKEN }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- name: Publish and verify exact SHA
run: |
set -euo pipefail
: "${PUBLIC_FORGE_TOKEN:?PUBLIC_FORGE_TOKEN is required}"
expected="${GITHUB_SHA}"
forge_url='https://forge.caseytunturi.com/Fimeg/RedFlag.git'
check_fast_forward() {
url="$1"
remote_sha="$(git ls-remote "$url" refs/heads/public | awk '{print $1}')"
if [ -z "$remote_sha" ]; then
return 0
fi
if ! git cat-file -e "${remote_sha}^{commit}" 2>/dev/null; then
echo "[publish] remote public SHA is not in tested history: $url" >&2
exit 1
fi
if ! git merge-base --is-ancestor "$remote_sha" "$expected"; then
echo "[publish] refusing non-fast-forward public history: $url" >&2
exit 1
fi
}
check_fast_forward "$forge_url"
forge_auth="$(printf 'publisher-redflag:%s' "$PUBLIC_FORGE_TOKEN" | base64 -w0)"
git -c "http.https://forge.caseytunturi.com/.extraheader=Authorization: Basic $forge_auth" \
push "$forge_url" public:public
forge_sha="$(git ls-remote "$forge_url" refs/heads/public | awk '{print $1}')"
test "$forge_sha" = "$expected"
echo "[publish] exact SHA is anonymous on Forgejo: $expected"