Repository navigation
release: v0.7.1 #1734
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: codefly core | |
| on: | |
| push: | |
| branches: [ main ] | |
| pull_request: | |
| branches: [ main ] | |
| # Cancel an in-progress run when a new push to the same ref lands. | |
| # A commit that's been superseded doesn't need to finish CI — saves | |
| # minutes and shortens the feedback loop. Group by workflow+ref so | |
| # main-branch pushes only cancel each other (not, say, a PR run). | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: write | |
| jobs: | |
| # Separate from build deliberately. The gate needs full history and a network | |
| # round trip to the BSR, and neither belongs in the job that runs the test | |
| # suite: inside build, a buf.build hiccup would abort before `go test` ever | |
| # ran and report as a schema break, and a red Build would say nothing about | |
| # which half failed. Split, the two answer independently and in parallel. | |
| proto: | |
| name: Proto | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| # buf reads the baseline out of this checkout's own .git, and the | |
| # default depth-1 fetch populates no refs/remotes/origin/*, so there | |
| # would be no main to diff against. | |
| fetch-depth: 0 | |
| - name: set up buf | |
| uses: bufbuild/buf-setup-action@a47c93e0b1648d5651a065437926377d060baa99 # v1.50.0 | |
| with: | |
| # Must match the buf the proto companion bakes | |
| # (companions/proto/Dockerfile); internal/ciguard's | |
| # TestWorkflowBufMatchesTheCompanionImage fails when the two drift. | |
| # Left unset, the action installs 1.50.0 — its own default, not the | |
| # latest. | |
| version: 1.73.0 | |
| github_token: ${{ github.token }} | |
| - name: proto breaking-change guard | |
| # proto/ is the schema source of truth for the whole ecosystem, and | |
| # nothing in CI read it — a wire-breaking edit and its regenerated | |
| # bindings were verified only on the contributor's machine. | |
| # | |
| # The script is the gate, and `make buf-breaking` runs the same one, so | |
| # what CI checks and what a developer can run before pushing cannot | |
| # drift into two programs. It owns the baseline (the merge base), the | |
| # BSR retry, and the one way a reviewed schema deletion may land: a | |
| # commit touching proto/ that declares the break in Conventional | |
| # Commits. An undeclared break is still exit 100, and the findings are | |
| # printed either way. internal/ciguard holds all three of those. | |
| run: ./scripts/check_proto_breaking.sh | |
| - name: proto lint guard | |
| # proto/buf.yaml has selected BASIC + COMMENTS since the module landed | |
| # and proto/README.md has always said to run `buf lint` before | |
| # committing, but no workflow ran it. The obligation rotted into 363 | |
| # standing COMMENTS findings, which is what an undocumented field costs | |
| # once the next real one is indistinguishable from them (#567). | |
| # | |
| # Exit 100 and the retry loop mean what they mean in the step above. | |
| working-directory: proto | |
| run: | | |
| for attempt in 1 2 3; do | |
| if buf lint; then | |
| exit 0 | |
| else | |
| code=$? | |
| fi | |
| if [ "$code" -eq 100 ]; then | |
| exit 100 | |
| fi | |
| echo "buf exited $code (attempt $attempt/3), not a rule violation; retrying in 15s" >&2 | |
| sleep 15 | |
| done | |
| echo "buf lint could not complete after 3 attempts" >&2 | |
| exit 1 | |
| build: | |
| name: Build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Pull Alpine Image | |
| run: docker pull alpine:latest | |
| # We use it for tests | |
| - name: set up | |
| uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 | |
| with: | |
| go-version: "1.27.0" | |
| - name: checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: agent contract release notes | |
| run: python3 -m unittest discover -s scripts -p test_agent_contract_release.py | |
| - name: version/tag drift guard | |
| # version/info.codefly.yaml is embedded into the binary and gates every | |
| # package install through composition's minimum-codefly-version check, | |
| # but no test reads it — so it has silently rotted (0.3.8 while v0.3.18 | |
| # shipped; 0.3.20 while v0.3.24 shipped). Fail here instead of at a | |
| # user's install. Reads tag names via ls-remote, so the shallow | |
| # checkout above is fine. | |
| run: ./scripts/check_version_tag.sh | |
| - name: Cache Go Modules | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| continue-on-error: true | |
| with: | |
| path: ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go | |
| - name: Install Dependencies | |
| run: go mod tidy && go mod download | |
| - name: CGO-free surface guard | |
| # cgo-ness is a link-time property of the import graph: a new import can | |
| # silently drag a cgo-only package (the tree-sitter grammars under | |
| # code/semantic) into a surface that must stay CGO-free, and nothing | |
| # fails until a downstream CGO_ENABLED=0 build (companion publish, an | |
| # alpine image) tries to link — weeks later, one repo away. This builds | |
| # every package except the documented cgo allowlist with CGO_ENABLED=0 | |
| # so creep fails HERE, at the PR that introduces it. See docs/cgo.md. | |
| run: ./scripts/check_cgo_free.sh | |
| - name: Darwin surface guard | |
| # Every runner in this repository is ubuntu, so nothing in CI ever | |
| # compiles the GOOS=darwin files: runners/base's process-identity and | |
| # postgres-IPC implementations, plus the darwin-only tests under | |
| # runners/ and toolbox/launch. A type error in one of them merges green | |
| # and breaks only on a developer's machine. Type-check that surface from | |
| # this runner so it fails HERE instead. | |
| # | |
| # Mirrors scripts/check_cgo_free.sh: the tree-sitter grammars under | |
| # code/semantic cannot cross-compile without cgo, so they are excluded | |
| # and the supported CGO-free constructor is selected with | |
| # codefly_nosemantic. vet covers test files, which `go build` does not. | |
| # | |
| # This type-checks only. RUNNING the darwin tests still needs a macOS | |
| # runner, which would add a required status check — see | |
| # docs/runbooks/branch-protection.md. | |
| run: | | |
| set -euo pipefail | |
| pkgs=() | |
| while IFS= read -r line; do | |
| pkgs+=("$line") | |
| done < <(GOOS=darwin go list -f '{{if .GoFiles}}{{.ImportPath}}{{end}}' ./... \ | |
| | grep -vxF -e "github.com/codefly-dev/core/code/semantic") | |
| echo "Type-checking the darwin surface: ${#pkgs[@]} packages" | |
| CGO_ENABLED=0 GOOS=darwin go vet -tags codefly_nosemantic "${pkgs[@]}" | |
| - name: Install apt deps (bubblewrap, ripgrep) | |
| # bubblewrap → core/runners/sandbox tests (default + sandbox_e2e) | |
| # ripgrep → core/code search tests (real-repo grep coverage) | |
| # Both are part of the strict signal — codefly's security | |
| # stack and code-search depend on them, so a broken install | |
| # MUST fail CI. The retry loop handles transient mirror flakes | |
| # (azure.archive.ubuntu.com periodically hiccups for ~5-10 | |
| # minutes); sustained outages still fail loud as intended. | |
| run: | | |
| for i in 1 2 3; do | |
| sudo apt-get update && sudo apt-get install -y bubblewrap ripgrep && exit 0 | |
| echo "apt failed (attempt $i / 3); waiting 30s before retry" | |
| sleep 30 | |
| done | |
| echo "apt install failed after 3 attempts" >&2 | |
| exit 1 | |
| - name: Install uv | |
| # Strict — uv drives core/runners/python's formula runner tests | |
| # (formula_run_test.go), which exercise the REAL uv+python toolchain | |
| # and fail-loud (requireUv never skips) when uv is absent. astral-sh's | |
| # setup-uv is the de-facto standard installer for GH Actions: pinned, | |
| # deterministic, adds uv to PATH. uv provisions its own Python on | |
| # demand, so no separate python setup is needed. | |
| uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0 | |
| - name: Python agent contract SDK | |
| env: | |
| PYTHONPATH: generated/python/codefly-cli/codefly_cli | |
| run: >- | |
| uv run --no-project --with protobuf==7.36.1 --with grpcio==1.83.1 | |
| --with googleapis-common-protos==1.75.3 | |
| python -m unittest discover | |
| -s generated/python/codefly-cli/codefly_cli/tests | |
| -p test_effective_inputs_contract.py | |
| - name: Install Nix | |
| # Strict — nix is core to the platform; if it doesn't install, | |
| # CI must fail loud rather than silently skipping nix tests. | |
| # DeterminateSystems' installer is the de-facto standard for | |
| # GH Actions: deterministic, no daemon issues on ephemeral | |
| # runners. | |
| # | |
| # This ref tracked the `main` BRANCH before it was pinned. The pinned | |
| # commit is also tagged v23, so Dependabot's github-actions updater can | |
| # still move it (it bumps SHA pins to newer releases, and would have | |
| # nothing to move an untagged branch commit to). | |
| uses: DeterminateSystems/nix-installer-action@3138316df39ed29be04236d7ffc686fa525866aa # v23 | |
| - name: Allow unprivileged user namespaces (for bwrap) | |
| # Ubuntu 24.04 ships with kernel.apparmor_restrict_unprivileged_userns=1 | |
| # which blocks bubblewrap's user-namespace setup ("bwrap: setting | |
| # up uid map: Permission denied"). Disabling the restriction is | |
| # the standard CI workaround — bwrap then works in unprivileged | |
| # mode, which is what our sandbox layer expects. | |
| run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 | |
| # | |
| # - name: golangci-lint | |
| # uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 | |
| # with: | |
| # version: v1.55 | |
| - name: govulncheck audit | |
| # scripts/govulncheck.sh mirrors `codefly audit go` / `codefly | |
| # agent build`'s audit logic: runs govulncheck, partitions | |
| # findings against .govulncheck.yaml suppressions + staleness, | |
| # fails only when an unsuppressed vuln has an upstream fix | |
| # available (unpatched-upstream issues can't be cleared by a | |
| # rebuild). | |
| # | |
| # This deliberately does NOT shell out to the codefly CLI: | |
| # github.com/codefly-dev/cli is a PRIVATE module, and core's CI | |
| # checks out core alone with no credentials for sibling repos, so | |
| # `go install github.com/codefly-dev/cli@latest` fails with | |
| # "could not read Username" / "Authentication failed". The script | |
| # only needs govulncheck (public, go-installable) and is fully | |
| # self-contained — no private-repo dependency. | |
| run: | | |
| go install golang.org/x/vuln/cmd/govulncheck@latest | |
| ./scripts/govulncheck.sh | |
| - name: generate test generate coverage | |
| # Single strict signal. -tags=sandbox_e2e,nix_required opts | |
| # into the integration tests that depend on bubblewrap (the | |
| # OS sandbox layer) and the nix binary. Both are installed | |
| # strict above; if either install failed, CI already failed | |
| # before reaching this step. | |
| # | |
| # NOT included: proto_companion_required. Those tests | |
| # (grpc_test.go, openapi_test.go) need the ghcr.io/codefly-dev/proto | |
| # Docker image which can't be built from core's CI alone | |
| # (it cross-builds the codefly CLI from a sibling checkout). | |
| # Run locally with `go test -tags=proto_companion_required`, | |
| # OR — once codefly CLI is installable in CI — bring the | |
| # image build into the workflow as `codefly companion build | |
| # proto` and add the tag here. | |
| run: go test ./... -tags=sandbox_e2e,nix_required -coverprofile=cover.out -covermode=atomic -coverpkg=./... | |
| - name: check test coverage | |
| id: coverage | |
| uses: vladopajic/go-test-coverage@f94bcf0d6b9fa5fb8b783830b22648f6c17475e2 # v2.19.0 | |
| with: | |
| config: ./.testcoverage.yaml | |
| ## when token is not specified (value '') this feature is turned off | |
| ## in this example badge is created and committed only for main branch | |
| git-token: ${{ github.ref_name == 'main' && github.token || '' }} | |
| ## name of branch where badges are stored | |
| ## ideally this should be orphan branch (see below how to create this branch) | |
| git-branch: badges | |
| - name: test with race detection | |
| # Strict, separate from coverage. Races are real bugs (we | |
| # found a bytes.Buffer concurrent-write race in NativeProc | |
| # that flaked Linux CI for months), so the race detector | |
| # has to run alongside the regular suite — not as a | |
| # commented-out optional. CLI already does this; bring | |
| # core to parity. | |
| # | |
| # We skip sandbox_e2e + nix_required tags here: the race | |
| # detector roughly doubles test runtime, and the integration | |
| # suites also run in the regular step above with full | |
| # coverage. Race-detector signal lands fastest by sticking | |
| # to the unit + light-integration scope. | |
| run: go test -race ./... -timeout 10m | |
| - name: Notify Slack on Failure | |
| # Push-only, and not merely to cut PR noise: a workflow run triggered by | |
| # Dependabot (or a fork) is handled as though it came from a fork, so | |
| # `secrets.*` resolves to the empty string. slack-github-action rejects | |
| # an empty webhook outright -- `throw new Error('Need to provide at | |
| # least one botToken or webhookUrl')` -- and core.setFailed turns the | |
| # whole job red after build, test, race and coverage have all passed. | |
| # These alerts are about main's health anyway, so gate them on push. | |
| if: failure() && github.event_name == 'push' | |
| uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0 | |
| with: | |
| # The action resolves the webhook URL from SLACK_WEBHOOK_URL below, | |
| # but reads the type from this input alone -- there is no environment | |
| # fallback for it -- and throws before sending when it is absent. | |
| webhook-type: incoming-webhook | |
| payload: | | |
| { | |
| "text": "Build Failed in ${{ github.repository }} on ${{ github.ref }}. See GitHub Actions for more details." | |
| } | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} | |
| - name: Notify Slack on Success | |
| # Push-only for the same reason as the failure notification above: on a | |
| # Dependabot pull request this step would fire on every GREEN run and | |
| # fail it. See the comment there. | |
| if: success() && github.event_name == 'push' | |
| uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0 | |
| with: | |
| # See the failure notification above. | |
| webhook-type: incoming-webhook | |
| payload: | | |
| { | |
| "text": "Build Succeeded in ${{ github.repository }} on ${{ github.ref }}. Great job!" | |
| } | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} |