fix(P1): wrap CI jobs in scripts/ci-run.sh — CoreCI forbids shell metacharacters in invoke: (REQ-184)
CoreCI's ValidateShellCommand (internal/runner/validate.go) rejects invoke: strings containing &|;`><$() — security measure to prevent shell injection. The previous .coreci.yml jobs had inline invoke: commands with || redirects and $(date) substitution, causing: job "gitleaks" failed: shell command contains forbidden metacharacters Fix: all complex logic moved to scripts/ci-run.sh. Each .coreci.yml job uses invoke: "sh scripts/ci-run.sh <job-name>" — no metacharacters in the invoke: string. The script itself can use any shell features internally (CoreCI only validates the invoke: field, not what the script does). ---ci--- project: orca phase: 1 milestone: v0.16 status: execute ---/ci---
This commit is contained in:
+19
-133
@@ -16,12 +16,14 @@ description: Orca — offline/CLI-first orchestration engine. Full release flow
|
||||
# `on: push: tags: ['v*']`, so every `coreci run` invocation is already
|
||||
# a release run — no tag-conditional rules needed here.
|
||||
#
|
||||
# Each job uses `invoke:` only (no `plugin:`). CoreCI's validate()
|
||||
# rejects jobs with both `plugin` and `invoke` set (mutually exclusive).
|
||||
# Jobs run via the shell-isolated executor, which runs `sh -c <invoke>`
|
||||
# directly. The Gitea Actions runner has Go 1.25 installed via
|
||||
# actions/setup-go, so all Go commands work. Tool installation via
|
||||
# `go install` (gosec, govulncheck) and `curl` (gitleaks, tea) works.
|
||||
# Each job uses `invoke:` only (no `plugin:`) — CoreCI's validate()
|
||||
# rejects jobs with both plugin and invoke set (mutually exclusive).
|
||||
# Jobs run via the shell-isolated executor (sh -c <invoke>).
|
||||
#
|
||||
# CoreCI's ValidateShellCommand forbids shell metacharacters (&|;`><$())
|
||||
# in the invoke: string. All complex logic lives in scripts/ci-run.sh,
|
||||
# which the invoke: field calls as "sh scripts/ci-run.sh <job-name>".
|
||||
# The script itself can use any shell features internally.
|
||||
#
|
||||
# Security scans (REQ-014, REQ-027, REQ-039):
|
||||
# - gosec Static analysis for Go security smells
|
||||
@@ -33,162 +35,46 @@ description: Orca — offline/CLI-first orchestration engine. Full release flow
|
||||
jobs:
|
||||
# ── validate ──────────────────────────────────────────────────────────
|
||||
go-vet:
|
||||
invoke: |
|
||||
go version
|
||||
gofmt -l .
|
||||
go vet ./...
|
||||
invoke: "sh scripts/ci-run.sh go-vet"
|
||||
|
||||
verify-reqs:
|
||||
needs: [go-vet]
|
||||
invoke: "make verify-reqs"
|
||||
invoke: "sh scripts/ci-run.sh verify-reqs"
|
||||
|
||||
gosec:
|
||||
needs: [go-vet]
|
||||
invoke: |
|
||||
go install github.com/securego/gosec/v2/cmd/gosec@v2.18.2
|
||||
gosec -fmt text -quiet ./...
|
||||
invoke: "sh scripts/ci-run.sh gosec"
|
||||
|
||||
govulncheck:
|
||||
needs: [go-vet]
|
||||
vars:
|
||||
GOFLAGS: "-mod=mod"
|
||||
invoke: |
|
||||
go install golang.org/x/vuln/cmd/govulncheck@v1.1.3
|
||||
govulncheck -mode binary ./...
|
||||
invoke: "sh scripts/ci-run.sh govulncheck"
|
||||
|
||||
gitleaks:
|
||||
needs: [go-vet]
|
||||
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
|
||||
invoke: "sh scripts/ci-run.sh gitleaks"
|
||||
|
||||
# ── 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]
|
||||
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
|
||||
invoke: "sh scripts/ci-run.sh build"
|
||||
|
||||
# ── test (REQ-031: -race) ────────────────────────────────────────────
|
||||
test:
|
||||
needs: [build]
|
||||
invoke: |
|
||||
go test -race -coverprofile=coverage.out ./...
|
||||
go tool cover -func=coverage.out | tail -1
|
||||
invoke: "sh scripts/ci-run.sh test"
|
||||
|
||||
# ── 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.
|
||||
# binary assets. Handles duplicate release (ship workflow creates
|
||||
# release first with title+body; this job attaches binary assets).
|
||||
# GITEA_TOKEN is resolved from env via CoreCI's secret resolver
|
||||
# (os.Getenv fallback in run.go:146-153) and forwarded by PassThroughEnv.
|
||||
# (os.Getenv fallback) and forwarded by PassThroughEnv.
|
||||
release:
|
||||
needs: [test]
|
||||
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"
|
||||
invoke: "sh scripts/ci-run.sh release"
|
||||
Reference in New Issue
Block a user