3be86e6daf
REQ-183: Fix .gitea/workflows/release.yml — the git clone of the private coreci repo failed because the clone command had no credentials. The actions/checkout@v4 step only injects auth for the orca repo. Fix: pass GITEA_TOKEN env to the Install CoreCI step and embed it in the clone URL (https://cloudinit-bot:${GITEA_TOKEN}@git.cloudinit.dev/...). REQ-184: Rewrite .coreci.yml from the invalid pipelines:/steps:/image:/ commands: format to CoreCI's native jobs:/plugin:/invoke:/vars: format with a proper DAG (needs:). CoreCI's Pipeline struct only recognizes jobs:/services:/env: top-level keys — unknown fields are silently dropped by yaml.Unmarshal, producing an empty Jobs map → zero jobs execute. The rewrite: - 8 jobs: go-vet → fan-out (verify-reqs, gosec, govulncheck, gitleaks) → build → test → release - plugin: docker://golang:1.25.12 + invoke: on each job (container path with shell-isolated fallback — Go is installed on the runner) - GITEA_TOKEN via vars: with ${{ secrets.GITEA_TOKEN }} (resolved from env via CoreCI's secret resolver os.Getenv fallback) - CI_COMMIT_BRANCH (tag name on tag push) and CI_COMMIT_SHA for version injection — no ${VAR} interpolation in YAML fields (shell expansion only works inside invoke: via sh -c) - No apk add (runner is ubuntu, not alpine — uses curl for tool downloads) - Release job handles duplicate release (ship workflow creates release first with title+body; coreci run attaches binary assets later via API fallback if tea releases create fails) - Release job verifies asset count ≥ 2 (REQ-097 gate C-21) with retry Root cause: all 87 releases in repo history had zero binary assets because coreci run never executed any jobs (empty Jobs map from the invalid format) and the Gitea Actions workflow failed before reaching coreci run (private repo clone had no credentials). ---ci--- project: orca phase: 1 milestone: v0.16 status: execute requirements: covered: [183, 184] partial: [] ---/ci---
201 lines
9.6 KiB
YAML
201 lines
9.6 KiB
YAML
version: "1"
|
|
name: orca-ci
|
|
description: Orca — offline/CLI-first orchestration engine. Full release flow via CoreCI.
|
|
|
|
# CoreCI configuration for orca (v0.16 rewrite — native jobs: format).
|
|
#
|
|
# CoreCI's Pipeline struct only recognizes `jobs:`, `services:`, and `env:`
|
|
# top-level keys. Unknown keys (like the old `pipelines:`) are silently
|
|
# dropped by yaml.Unmarshal, producing an empty Jobs map → zero jobs
|
|
# execute. This file uses the native `jobs:`/`plugin:`/`invoke:`/`vars:`
|
|
# format with a DAG via `needs:`.
|
|
#
|
|
# DAG: go-vet → fan-out (verify-reqs, gosec, govulncheck, gitleaks) → build → test → release
|
|
#
|
|
# The Gitea Actions workflow (.gitea/workflows/release.yml) gates on
|
|
# `on: push: tags: ['v*']`, so every `coreci run` invocation is already
|
|
# a release run — no tag-conditional rules needed here.
|
|
#
|
|
# Each job declares `plugin: docker://golang:1.25.12` for the container
|
|
# path and `invoke:` for the shell command. If no container runtime is
|
|
# available (podman/docker), CoreCI's shell-isolated executor runs the
|
|
# `invoke:` command directly — the Gitea Actions runner has Go 1.25
|
|
# installed via actions/setup-go, so Go commands work in both paths.
|
|
#
|
|
# Security scans (REQ-014, REQ-027, REQ-039):
|
|
# - gosec Static analysis for Go security smells
|
|
# - govulncheck Offline vuln scan of dependencies
|
|
# - gitleaks Pre-commit-style secret scan
|
|
# verify-reqs (REQ-060): ROADMAP COMPLETE ↔ REQUIREMENTS Complete
|
|
# test runs with -race (REQ-031).
|
|
|
|
jobs:
|
|
# ── validate ──────────────────────────────────────────────────────────
|
|
go-vet:
|
|
plugin: docker://golang:1.25.12
|
|
invoke: |
|
|
go version
|
|
gofmt -l .
|
|
go vet ./...
|
|
|
|
verify-reqs:
|
|
needs: [go-vet]
|
|
plugin: docker://golang:1.25.12
|
|
invoke: "make verify-reqs"
|
|
|
|
gosec:
|
|
needs: [go-vet]
|
|
plugin: docker://golang:1.25.12
|
|
invoke: |
|
|
go install github.com/securego/gosec/v2/cmd/gosec@v2.18.2
|
|
gosec -fmt text -quiet ./...
|
|
|
|
govulncheck:
|
|
needs: [go-vet]
|
|
plugin: docker://golang:1.25.12
|
|
vars:
|
|
GOFLAGS: "-mod=mod"
|
|
invoke: |
|
|
go install golang.org/x/vuln/cmd/govulncheck@v1.1.3
|
|
govulncheck -mode binary ./...
|
|
|
|
gitleaks:
|
|
needs: [go-vet]
|
|
plugin: docker://golang:1.25.12
|
|
invoke: |
|
|
curl -fsSL https://github.com/gitleaks/gitleaks/releases/latest/download/gitleaks-linux-amd64.tar.gz -o /tmp/gitleaks.tar.gz
|
|
tar -xzf /tmp/gitleaks.tar.gz -C /tmp gitleaks
|
|
mv /tmp/gitleaks /usr/local/bin/gitleaks 2>/dev/null || sudo mv /tmp/gitleaks /usr/local/bin/gitleaks 2>/dev/null || cp /tmp/gitleaks ./gitleaks
|
|
chmod +x ./gitleaks 2>/dev/null || true
|
|
./gitleaks detect --source . --config .gitleaks.toml --baseline-path .gitleaks-baseline.json --no-banner || gitleaks detect --source . --config .gitleaks.toml --baseline-path .gitleaks-baseline.json --no-banner
|
|
|
|
# ── build ────────────────────────────────────────────────────────────
|
|
# CI_COMMIT_BRANCH contains the tag name on tag pushes (CoreCI's github.go
|
|
# maps GITHUB_REF_NAME → CI_COMMIT_BRANCH). CI_COMMIT_SHA is the commit.
|
|
# BUILD_TIME is computed inline via `date`. Shell expansion works inside
|
|
# invoke: via sh -c at runtime.
|
|
build:
|
|
needs: [verify-reqs, gosec, govulncheck, gitleaks]
|
|
plugin: docker://golang:1.25.12
|
|
invoke: |
|
|
VERSION="${CI_COMMIT_BRANCH:-dev}"
|
|
GIT_COMMIT="${CI_COMMIT_SHA:-unknown}"
|
|
BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
LDFLAGS="-s -w \
|
|
-X git.cloudinit.dev/coreci/orca/internal/cli.version=${VERSION} \
|
|
-X git.cloudinit.dev/coreci/orca/internal/cli.gitCommit=${GIT_COMMIT} \
|
|
-X git.cloudinit.dev/coreci/orca/internal/cli.buildTime=${BUILD_TIME}"
|
|
go build -trimpath -ldflags="${LDFLAGS}" -o bin/orca ./cmd/orca
|
|
file bin/orca
|
|
./bin/orca version
|
|
|
|
# ── test (REQ-031: -race) ────────────────────────────────────────────
|
|
test:
|
|
needs: [build]
|
|
plugin: docker://golang:1.25.12
|
|
invoke: |
|
|
go test -race -coverprofile=coverage.out ./...
|
|
go tool cover -func=coverage.out | tail -1
|
|
|
|
# ── release ──────────────────────────────────────────────────────────
|
|
# Builds the release tarball, creates/updates the Gitea release with
|
|
# binary assets. Handles the case where the release already exists
|
|
# (created by the CIAgent ship workflow with title+body but no binary)
|
|
# by falling back to Gitea API asset attachment.
|
|
# GITEA_TOKEN is resolved from env via CoreCI's secret resolver
|
|
# (os.Getenv fallback in run.go:146-153) and forwarded by PassThroughEnv.
|
|
release:
|
|
needs: [test]
|
|
plugin: docker://golang:1.25.12
|
|
vars:
|
|
GITEA_TOKEN: "${{ secrets.GITEA_TOKEN }}"
|
|
invoke: |
|
|
VERSION="${CI_COMMIT_BRANCH:-dev}"
|
|
GIT_COMMIT="${CI_COMMIT_SHA:-unknown}"
|
|
BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
TARBALL="orca-${VERSION}-linux-amd64.tar.gz"
|
|
|
|
# Build the release binary with version injection.
|
|
LDFLAGS="-s -w \
|
|
-X git.cloudinit.dev/coreci/orca/internal/cli.version=${VERSION} \
|
|
-X git.cloudinit.dev/coreci/orca/internal/cli.gitCommit=${GIT_COMMIT} \
|
|
-X git.cloudinit.dev/coreci/orca/internal/cli.buildTime=${BUILD_TIME}"
|
|
go build -trimpath -ldflags="${LDFLAGS}" -o bin/orca ./cmd/orca
|
|
make changelog
|
|
tar -czf "${TARBALL}" -C bin orca
|
|
sha256sum "${TARBALL}" > SHA256SUMS
|
|
ls -lh "${TARBALL}" SHA256SUMS
|
|
cat SHA256SUMS
|
|
|
|
# Install tea CLI for Gitea release creation.
|
|
sh -c "$(curl -fsSL https://gitea.com/gitea/tea/releases/latest/download/install.sh)" 2>/dev/null || true
|
|
|
|
# Create release with assets. If the release already exists (created
|
|
# by the CIAgent ship workflow with title+body but no binary), fall
|
|
# back to attaching assets via the Gitea API.
|
|
tea releases create "${VERSION}" \
|
|
--repo coreci/orca \
|
|
--title "Orca ${VERSION}" \
|
|
--note-file CHANGELOG.md \
|
|
--asset "${TARBALL}" \
|
|
--asset SHA256SUMS 2>/dev/null && echo "✓ release created via tea" || ATTACH_TO_EXISTING=1
|
|
|
|
if [ "${ATTACH_TO_EXISTING:-0}" = "1" ]; then
|
|
echo "Release ${VERSION} already exists — attaching assets via Gitea API..."
|
|
RELEASE_ID=$(curl -fsSL \
|
|
"https://git.cloudinit.dev/api/v1/repos/coreci/orca/releases/tags/${VERSION}" \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
| python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || echo "")
|
|
if [ -n "${RELEASE_ID}" ]; then
|
|
echo "Attaching assets to release ID ${RELEASE_ID}..."
|
|
curl -fsSL -X POST \
|
|
"https://git.cloudinit.dev/api/v1/repos/coreci/orca/releases/${RELEASE_ID}/assets?name=${TARBALL}" \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
-F "attachment=@${TARBALL}"
|
|
curl -fsSL -X POST \
|
|
"https://git.cloudinit.dev/api/v1/repos/coreci/orca/releases/${RELEASE_ID}/assets?name=SHA256SUMS" \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
-F "attachment=@SHA256SUMS"
|
|
echo "✓ assets attached via API"
|
|
else
|
|
echo "ERROR: Could not resolve release ID for ${VERSION}"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Verify assets are actually attached (REQ-097, gate C-21).
|
|
# tea releases create has been observed to exit 0 without attaching
|
|
# the asset in some versions. Verify via the API.
|
|
ASSET_COUNT=$(curl -fsSL \
|
|
"https://git.cloudinit.dev/api/v1/repos/coreci/orca/releases/tags/${VERSION}" \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
| python3 -c "import json,sys; print(len(json.load(sys.stdin).get('attachments',[])))" 2>/dev/null || echo "0")
|
|
echo "Release ${VERSION} has ${ASSET_COUNT} assets"
|
|
if [ "${ASSET_COUNT}" -lt 2 ]; then
|
|
echo "ERROR: Expected at least 2 assets (tarball + SHA256SUMS), got ${ASSET_COUNT}"
|
|
echo "Attempting manual asset attachment..."
|
|
RELEASE_ID=$(curl -fsSL \
|
|
"https://git.cloudinit.dev/api/v1/repos/coreci/orca/releases/tags/${VERSION}" \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
| python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || echo "")
|
|
if [ -n "${RELEASE_ID}" ]; then
|
|
curl -fsSL -X POST \
|
|
"https://git.cloudinit.dev/api/v1/repos/coreci/orca/releases/${RELEASE_ID}/assets?name=${TARBALL}" \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
-F "attachment=@${TARBALL}"
|
|
curl -fsSL -X POST \
|
|
"https://git.cloudinit.dev/api/v1/repos/coreci/orca/releases/${RELEASE_ID}/assets?name=SHA256SUMS" \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
-F "attachment=@SHA256SUMS"
|
|
ASSET_COUNT=$(curl -fsSL \
|
|
"https://git.cloudinit.dev/api/v1/repos/coreci/orca/releases/tags/${VERSION}" \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
| python3 -c "import json,sys; print(len(json.load(sys.stdin).get('attachments',[])))" 2>/dev/null || echo "0")
|
|
echo "After retry: ${ASSET_COUNT} assets"
|
|
fi
|
|
if [ "${ASSET_COUNT}" -lt 2 ]; then
|
|
echo "FATAL: assets not attached after retry (REQ-097, C-21)"
|
|
exit 1
|
|
fi
|
|
fi
|
|
echo "✓ release ${VERSION} published with ${ASSET_COUNT} binary assets" |