cilocal

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 18, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package cilocal is what the pull request pipeline decides, readable outside the pipeline: the path filters of `ci.yml`'s `changes` job, and which of its jobs a change runs (ADR-0079).

Two readers need it. `make verify-pr` selects the gates a branch needs locally from the same filters CI selects them from, so that the two cannot drift; and `test/architecture` holds both the filters and the job table below to the workflow, so that a job added to `ci.yml` without a local counterpart, or a filter changed in one place only, is a red gate rather than a surprise.

Index

Constants

View Source
const WorkflowPath = ".github/workflows/ci.yml"

WorkflowPath is where the pipeline lives, relative to the repository root.

Variables

View Source
var Jobs = []Job{
	{ID: "quick", Filters: goPipeline, InVerify: true},
	{ID: "unit", Filters: goPipeline, InVerify: true},
	{ID: "architecture", Filters: goPipeline, InVerify: true},
	{ID: "security", Filters: goPipeline, InVerify: true},
	{ID: "observability", Filters: goPipeline, InVerify: true},
	{ID: "chart", Filters: goPipeline, InVerify: true},
	{ID: "licences", InVerify: true},
	{ID: "docs", InVerify: true},

	{ID: "api-compat", Filters: goPipeline, Steps: []Step{
		{Name: "gate-api-compat", Command: `make gate-api-compat API_BASE="$(git merge-base origin/main HEAD)"`},
	}},

	{ID: "numbers", Steps: []Step{
		{Name: "gate-numbers", Command: "make gate-numbers BASE=origin/main"},
	}},
	{ID: "build", Filters: goPipeline, Steps: []Step{
		{Name: "build (linux/amd64)", Command: "GOOS=linux GOARCH=amd64 make build"},
		{Name: "build (linux/arm64)", Command: "GOOS=linux GOARCH=arm64 make build"},
	}},
	{ID: "integration", Filters: goPipeline, Steps: []Step{
		{Name: "gate-integration", Command: "make gate-integration"},
		{Name: "gate-contract", Command: "make gate-contract"},
	}},
	{ID: "data", Filters: goPipeline, Steps: []Step{
		{Name: "gate-data", Command: "make gate-data"},
		{Name: "gate-privacy-full", Command: "make gate-privacy-full"},
	}},
	{ID: "resilience", Filters: goPipeline, Steps: []Step{
		{Name: "gate-resilience", Command: "make gate-resilience"},
	}},

	{ID: "selftest", Filters: goPipeline, Steps: []Step{
		{Name: "gate-selftest", Command: "make gate-selftest"},
	}},
	{ID: "tokens-drift", Filters: []string{"design_system", "go", "ci"}, Steps: []Step{
		{Name: "tokens without a diff", Command: "make tools-node && pnpm install --frozen-lockfile && make tokens && " +
			"git diff --exit-code -- core/domain/model/shared/LabelTokens.go"},
	}},
	{ID: "node", Filters: []string{"webapp", "website", "design_system", "api_client", "sync_engine", "n8n_node", "zapier_app", "sdk_typescript", "openapi", "ci"}, Steps: []Step{
		{Name: "workspace install and map", Command: "make tools-node && pnpm install --frozen-lockfile && " +
			"node build/lint-workspace-map.mjs --selftest && node build/lint-workspace-map.mjs"},
		pnpmPackage("design-system", "design_system", "ci"),
		pnpmPackage("api-client", "api_client", "openapi", "ci"),
		pnpmPackage("sync-engine", "sync_engine", "api_client", "openapi", "ci"),
		pnpmPackage("n8n-nodes-hubtask", "n8n_node", "api_client", "openapi", "ci"),
		pnpmPackage("zapier-app", "zapier_app", "api_client", "openapi", "ci"),
		pnpmPackage("sdk-typescript", "sdk_typescript", "openapi", "ci"),
		pnpmPackage("webapp", "webapp", "design_system", "sync_engine", "api_client", "openapi", "ci"),
		{Name: "make website", Filters: websiteFilters, Command: "make website"},
		pnpmPackage("website", websiteFilters...),
	}},
	{ID: "engines", Filters: []string{"webapp", "design_system", "sync_engine", "api_client", "openapi", "ci"}, Steps: []Step{
		{Name: "the three engines", Command: "make tools-node && pnpm install --frozen-lockfile && " +
			`pnpm --filter "@hubtask/webapp^..." build && pnpm --filter @hubtask/webapp build && ` +
			"pnpm --filter @hubtask/webapp test:engines"},
	}},
	{ID: "api-client-drift", Filters: []string{"openapi", "api_client", "ci"}, Steps: []Step{
		{Name: "the API client from the contract", Command: "make tools-node && pnpm install --frozen-lockfile && " +
			"make api-client && pnpm -r build && pnpm -r typecheck && git diff --exit-code"},
	}},
	{ID: "compose", Container: true, Filters: []string{"go", "openapi", "db", "deploy", "ci", "webapp", "design_system", "api_client"}, Steps: []Step{
		{Name: "gate-compose", Command: "make gate-compose"},
	}},
	{ID: "e2e", Container: true, Filters: goPipeline, Steps: []Step{
		{Name: "gate-e2e", Command: "make gate-e2e"},
	}},
	{ID: "engine-session", Container: true, Filters: goPipeline, Steps: []Step{
		{Name: "gate-engine-conformance", Command: "make tools-node && pnpm install --frozen-lockfile && make gate-engine-conformance"},
	}},

	{ID: "actions-allowed", Filters: []string{"ci"}, CIOnly: "GitHub's job set-up checks the actions against the repository setting; nothing local can ask it"},
	{ID: "secrets", CIOnly: "gitleaks scans the history and is not among the pinned tools; push protection stops a key at the push"},
	{ID: "dependencies", CIOnly: "the dependency review compares the pull request with its base through GitHub's API"},

	{ID: "pr-description", CIOnly: "the description is read from the pull request - verify-pr checks it with make gate-pr itself"},
}

Jobs lists every job `ci-required` waits for except `changes`, which only classifies, in the order verify-pr runs them: the fast and the Go gates first, the workspace next, the containers last, because they hold the machine's lock.

Functions

func LoadFilters

func LoadFilters(workflow []byte) (map[string][]string, error)

LoadFilters reads the `filters:` block the `changes` job hands to dorny/paths-filter.

The block is a YAML document inside a YAML string, so it is extracted by indentation and parsed on its own. It carries an anchor and aliases (`*workspace-manifests`), which is exactly why it is parsed rather than pattern-matched: a reader has to see what a package's filter really contains, aliases resolved.

func Matches

func Matches(pattern, file string) bool

Matches covers the three shapes the filter list uses: a tree (`core/**`), an extension anywhere (`**/*.md`), and an exact path (`go.mod`). Deliberately not a general glob engine - a fourth shape should be read here by whoever writes it rather than resolved by a dependency.

func Runs

func Runs(filters []string, touched map[string]bool) bool

Runs answers whether a job, or a step of it, starts for the filters a change sets. No filters means always.

func Touched

func Touched(filters map[string][]string, files []string) map[string]bool

Touched answers which filters a set of changed files sets, the way the `changes` job's outputs would read for a pull request carrying exactly those files.

Types

type Job

type Job struct {
	ID      string
	Filters []string
	// InVerify says `make verify` already runs what this job runs; verify-pr always starts there.
	InVerify bool
	// Steps run in order, each only when one of its own filters is set (none: the job's).
	Steps []Step
	// Container jobs need Docker and run one session at a time, under a lock: their compose
	// projects and host ports are fixed, and every worktree on a machine shares one daemon.
	Container bool
	// CIOnly is why the job cannot run locally. A job is either run here or says why it is not.
	CIOnly string
}

Job is one job of `ci.yml` as `make verify-pr` runs it on a laptop.

Filters are the `changes` outputs that start the job on a pull request, any one of them being enough; none means the job runs on every pull request. They are a copy of the job's `if:`, and test/architecture compares the two, so the copy cannot quietly go stale.

type Step

type Step struct {
	Name    string
	Filters []string
	Command string
}

Step is one command of a job, run through bash with `.tools` first on the PATH.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL