Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .gds/bundle.lock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ bundle:
version: "0.8.0-dev"
release_sequence: 0
channel: "development"
source_tree_digest: "sha256:670e14659a7a4ee1d99c49841c7ed04210d67fef3828f8cfc8ca135be19ac483"
digest: "sha256:085a6f985c4abb09f96c225e1123996e9c54fe28f2862af1a2f85f8a908c6cbf"
source_tree_digest: "sha256:a2b5ad7bdb6a7198d0de86e3de879062c71df5e86ff2f257257693aee319aa4a"
digest: "sha256:010d85cbeeeb4b010e438b0ffcbe5c9211f86d8f170b66e187aa35b23480fc17"

projection:
input_digest: "sha256:eb612421c50ec33e68468e080d48f24c57ba8f8cd4dcf738d0debcfb584936f4"
output_digest: "sha256:0f3891bccad430b0c41acc687d4f9b26e0bb6cba00434f3f13a8598575d6ef66"
input_digest: "sha256:f914cbb8b69051475867d7fe14977debf309cb4ee1bd9bcddd6e64c7098bd886"
output_digest: "sha256:0a7d6b71492567ed5728b5b49c40225fab549fc3f7c252920f781aa9a671aa3a"
files:
- path: ".gds/compiled-policy.json"
digest: "sha256:b5517ed46f67866220c2b18dbfbda4a40d99f00327611e56742a118d0ac59d0b"
- path: ".github/workflows/gds-ci.yml"
digest: "sha256:bba562704e7c072f0be565c4fe3a22b52d691b87aa0fe44c4d998f8e9336b0da"
digest: "sha256:1da9d1ccf7c40ec6328f2c957aeb910c9de68ab91d8e07917d1486165cf209cc"
3 changes: 2 additions & 1 deletion .gds/repository.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ verification:
build:
- "go build -trimpath ./core/cmd/gds"
fast:
- "scripts/validate_go_core.sh --quick"
- "scripts/validate_shell.sh"
- "scripts/validate_go_core.sh --fast"
pr-required:
- "go test ./..."
- "python3 -m pip install --quiet --require-hashes -r requirements/test.txt"
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/gds-ci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# GENERATED FILE - DO NOT EDIT DIRECTLY
# generator: gds
# bundle: 0.8.0-dev
# source-tree-digest: sha256:670e14659a7a4ee1d99c49841c7ed04210d67fef3828f8cfc8ca135be19ac483
# input-digest: sha256:eb612421c50ec33e68468e080d48f24c57ba8f8cd4dcf738d0debcfb584936f4
# output-digest: sha256:01fb4854784be9e4564bcc84e70786484b370879be5e5ab1dd49f8b73ea2dea4
# source-tree-digest: sha256:a2b5ad7bdb6a7198d0de86e3de879062c71df5e86ff2f257257693aee319aa4a
# input-digest: sha256:f914cbb8b69051475867d7fe14977debf309cb4ee1bd9bcddd6e64c7098bd886
# output-digest: sha256:4a875d4ed62fe6b2d279515f9a19274e159ae8ed58f3a1a0843c60a49f663886
# edit-source:
# - .gds/repository.yaml
# - policies/base/repository-default.yaml
Expand Down Expand Up @@ -42,14 +42,14 @@ jobs:

runner: "ubuntu-latest"
go_version: "1.26.7"
test_command: "go test ./..."
# Declared fast commands own their build scope; the complete build is below.
build_command: ""
test_command: "scripts/validate_shell.sh && scripts/validate_go_core.sh --fast"
timeout_minutes: 30
pr-required:
name: GDS PR required
# Serialized behind fast: both tiers run the integrated assurance scenario,
# which asserts wall-clock budgets, and running two of them side by side on
# one host measures contention rather than the code. It is also the right
# gate order — there is no value in the expensive tier once fast is red.
# Run the declared fast checks first. Repositories can keep this phase
# inexpensive and reserve their full test/build commands for this job.
needs: fast
permissions:
contents: read
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ Versioning.

### Changed

- Generated Go callers honor declared fast verification commands, falling back
to the existing test command when none are declared. GDS uses a dedicated
formatting/module/vet/schema fast mode and runs its full Go tests once in the
PR-required job; the complete build remains in that job.

- Ruleset reconciliation compares the complete owned required-check identities,
including integration IDs and strict policy. Retired extra contexts or a
different check producer no longer appear synchronized; external parameters
Expand Down
20 changes: 14 additions & 6 deletions core/projections/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ type templateData struct {
GoVersion string
BuildCommand string
TestCommand string
FastCommand string
DeclaredFastCommands bool
PRRequiredCommand string
TimeoutMinutes int
WorkflowRef string
Expand Down Expand Up @@ -606,13 +608,19 @@ func projectionTemplateData(
if product == nil {
product = &domain.ProductFacts{}
}
fastCommand := ciString(anchor.CI, func(value *domain.CIPolicy) string { return value.TestCommand })
if len(anchor.Verification.Commands.Fast) != 0 {
fastCommand = strings.Join(anchor.Verification.Commands.Fast, " && ")
}
return templateData{
AdvisoryCI: compiler.AdvisoryCI(policy),
Purpose: product.Purpose,
Capabilities: product.Capabilities,
Entrypoints: product.Entrypoints,
RepositoryID: anchor.Repository.ID,
Roles: strings.Join(anchor.Repository.Roles, ", "), BundleVersion: bundle.Version,
FastCommand: fastCommand,
DeclaredFastCommands: len(anchor.Verification.Commands.Fast) != 0,
AdvisoryCI: compiler.AdvisoryCI(policy),
Purpose: product.Purpose,
Capabilities: product.Capabilities,
Entrypoints: product.Entrypoints,
RepositoryID: anchor.Repository.ID,
Roles: strings.Join(anchor.Repository.Roles, ", "), BundleVersion: bundle.Version,
ExternalWriteApproval: effectiveString(
policy.Effective, "security", "external_write_requires_approval",
),
Expand Down
30 changes: 30 additions & 0 deletions core/projections/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,3 +533,33 @@ func TestAdvisoryDeliveryGuidanceDoesNotAlterWorkflowResults(t *testing.T) {
t.Fatal("advisory CI must preserve real outcomes")
}
}

func TestGoCallerHonorsFastCommandsWithoutRepeatingBroadTests(t *testing.T) {
generator, anchor, policy, bundle := controlPlaneInputs(t)
anchor.CI.TestCommand = "go test ./..."
anchor.Verification.Commands.Fast = []string{"go vet ./...", "scripts/check-format.sh"}
anchor.Verification.Commands.PRRequired = []string{"go test ./..."}
candidate, findings := generator.Generate(anchor, policy, bundle)
if len(findings) != 0 {
t.Fatal(findings)
}
workflow := string(candidateFile(t, candidate, goCIOutputPath).Content)
if !strings.Contains(workflow, `test_command: "go vet ./... && scripts/check-format.sh"`) {
t.Fatalf("fast commands ignored:\n%s", workflow)
}
if strings.Count(workflow, `test_command: "go test ./..."`) != 1 {
t.Fatalf("broad tests repeated:\n%s", workflow)
}
if !strings.Contains(workflow, `build_command: ""`) {
t.Fatal("fast caller repeats the default broad build")
}
anchor.Verification.Commands.Fast = nil
fallback, findings := generator.Generate(anchor, policy, bundle)
if len(findings) != 0 {
t.Fatal(findings)
}
workflow = string(candidateFile(t, fallback, goCIOutputPath).Content)
if strings.Count(workflow, `test_command: "go test ./..."`) != 2 || strings.Contains(workflow, `build_command: ""`) {
t.Fatalf("undeclared fast behavior changed:\n%s", workflow)
}
}
2 changes: 1 addition & 1 deletion core/projections/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var fullWorkflowSHA = regexp.MustCompile(`(?m)^\s*uses:\s*[A-Za-z0-9_.-]+/[A-Za-
func validateGoWorkflowCaller(content []byte, anchor domain.RepositoryAnchor) error {
if anchor.CI == nil || anchor.CI.Profile != "go" || anchor.CI.GoVersion == "" ||
anchor.CI.BuildCommand == "" || anchor.CI.TestCommand == "" ||
anchor.CI.WorkflowRef == "" || len(anchor.Verification.Commands.Fast) == 0 ||
anchor.CI.WorkflowRef == "" ||
len(anchor.Verification.Commands.PRRequired) == 0 ||
anchor.CI.TimeoutMinutes < 1 || anchor.CI.TimeoutMinutes > 120 {
return errors.New("Go CI repository policy is incomplete")
Expand Down
15 changes: 15 additions & 0 deletions docs/continuous-development-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,18 @@ GitHub describes the status-check requirement in
[available rules](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets)
and the update payload in the
[repository rules REST API](https://docs.github.com/en/rest/repos/rules#update-a-repository-ruleset).

## Fast Go verification

Generated Go callers use the ordered `verification.commands.fast` list for the
fast job. Its commands own that phase's build scope, so the reusable workflow's
default build is disabled there; `ci.build_command` still runs in PR required.
With no fast list, the caller falls back to `ci.test_command` and the reusable
workflow's normal build. Older generation required a fast list but ignored its
contents.

This repository selects shell lint plus `scripts/validate_go_core.sh --fast` for
fast CI. The latter checks Go formatting, module consistency, vet, Python lock
metadata and the Go schema validator. Full Go and Python tests remain in PR
required. `--quick` still runs the complete development validation, and release
validation retains its security floor, vulnerability/race checks and rebuilds.
21 changes: 15 additions & 6 deletions scripts/validate_go_core.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ MINIMUM_SECURE_GO_VERSION=go1.26.7
RELEASE_GO_VERSION=${GDS_RELEASE_GO_VERSION:-go1.26.7}
GOVULNCHECK_VERSION=v1.6.0

if [ "${1:-}" = "--quick" ]; then
MODE=quick
shift
fi
case "${1:-}" in
--quick) MODE=quick; shift ;;
--fast) MODE=fast; shift ;;
esac
if [ "$#" -ne 0 ]; then
printf 'usage: %s [--quick]\n' "$0" >&2
printf 'usage: %s [--quick|--fast]\n' "$0" >&2
exit 4
fi

Expand Down Expand Up @@ -54,7 +54,7 @@ if ! go_version_at_least "$GO_VERSION" "$MINIMUM_SECURE_GO_VERSION"; then
fi
printf '%s\n' \
"WARNING: $GO_VERSION is older than the security floor $MINIMUM_SECURE_GO_VERSION." \
"Quick results are development-only; release evidence remains NOT_PROVEN." >&2
"This validation is development-only; release evidence remains NOT_PROVEN." >&2
fi

if [ "$MODE" = "full" ] && [ "$GO_VERSION" != "$RELEASE_GO_VERSION" ]; then
Expand All @@ -76,6 +76,15 @@ fi
go mod tidy -diff
go mod verify
go vet ./...

if [ "$MODE" = "fast" ]; then
python3 scripts/validate_python_locks.py
# The Go validator is self-contained; fast CI needs no unpinned Python packages.
go run ./core/cmd/gds --json validate schemas > /dev/null
printf 'GDS Go core validation: PASS (fast; full tests run separately)\n'
exit 0
fi

go test ./...
python3 scripts/validate_python_locks.py
python3 scripts/validate_gds_schemas.py --json >/dev/null
Expand Down
10 changes: 5 additions & 5 deletions templates/github-actions/go.yml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ jobs:
{{ end }}
runner: {{ yamlQuote .Runner }}
go_version: {{ yamlQuote .GoVersion }}
test_command: {{ yamlQuote .TestCommand }}
{{ if .DeclaredFastCommands }} # Declared fast commands own their build scope; the complete build is below.
build_command: ""
{{ end }} test_command: {{ yamlQuote .FastCommand }}
timeout_minutes: {{ .TimeoutMinutes }}
pr-required:
name: GDS PR required
# Serialized behind fast: both tiers run the integrated assurance scenario,
# which asserts wall-clock budgets, and running two of them side by side on
# one host measures contention rather than the code. It is also the right
# gate order — there is no value in the expensive tier once fast is red.
# Run the declared fast checks first. Repositories can keep this phase
# inexpensive and reserve their full test/build commands for this job.
needs: fast
permissions:
contents: read
Expand Down
2 changes: 1 addition & 1 deletion tests/golden/projections/control-plane/.claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ GENERATED FILE - DO NOT EDIT DIRECTLY
generator: gds
bundle: 0.8.0-dev
source-tree-digest: sha256:0000000000000000000000000000000000000000000000000000000000000001
input-digest: sha256:3d7c04888af26c66e2c8b7fedc84b08f90a73d1182cd18ee889c3a849151ce25
input-digest: sha256:a0d8d237e113629cdf67cf248e3010dfb024ec97d3c692824258f99a66d77421
output-digest: sha256:88cb57297d8d713287872a8afaca8d42f7146ecf7a091e4996e65eee8f962665
edit-source:
- .gds/repository.yaml
Expand Down
12 changes: 6 additions & 6 deletions tests/golden/projections/control-plane/.gds/bundle.lock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ bundle:
release_sequence: 0
channel: "development"
source_tree_digest: "sha256:0000000000000000000000000000000000000000000000000000000000000001"
digest: "sha256:2bf1503b8188a42c0433be96950a6c263a1c715154924ffa682de2b65d955ee3"
digest: "sha256:620a284abd424fa8e2d34d77447d2435f300b6c3c69016f447fbe479b5db655f"

projection:
input_digest: "sha256:3d7c04888af26c66e2c8b7fedc84b08f90a73d1182cd18ee889c3a849151ce25"
output_digest: "sha256:7127d81d7b0656bbaac983d1383d1cde816813b7e975bbc2c486954515f325de"
input_digest: "sha256:a0d8d237e113629cdf67cf248e3010dfb024ec97d3c692824258f99a66d77421"
output_digest: "sha256:e64d18089f87b0d26f43e652e10a932573b79294f4c759ac4f921e3fe5437210"
files:
- path: ".claude/CLAUDE.md"
digest: "sha256:1eea64f1a7ff5fb20ef5c11db42231217a1a4bb1f9f9904d5daed91da33037a4"
digest: "sha256:c20c42cda0f0ab9fecc75bb7fd979c93549d661fb4c23e6cd8caa1ffda6a2151"
- path: ".gds/compiled-policy.json"
digest: "sha256:24850d3c840160a5bef16753424da983b1e220984e2457fd22ee03ed278f1f1b"
- path: ".github/workflows/gds-ci.yml"
digest: "sha256:19a37bc7b84efca6d34e15d7ce68ef4fa9f886ffc208c606b262882d83640489"
digest: "sha256:42d30be16850c301efbf04d94d9982105a0b1669a85e9abb479c6d8ff5b0a0c2"
- path: "AGENTS.md"
digest: "sha256:840da70bfecd98bf2b674d6aff3be6cbae808609e0d6ce61292d4a84e1d00e2d"
digest: "sha256:8629ac16dcf62182a0c23c03ad9f57a367ee43e4677700c1c56b97996fc0c108"
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# generator: gds
# bundle: 0.8.0-dev
# source-tree-digest: sha256:0000000000000000000000000000000000000000000000000000000000000001
# input-digest: sha256:3d7c04888af26c66e2c8b7fedc84b08f90a73d1182cd18ee889c3a849151ce25
# output-digest: sha256:01fb4854784be9e4564bcc84e70786484b370879be5e5ab1dd49f8b73ea2dea4
# input-digest: sha256:a0d8d237e113629cdf67cf248e3010dfb024ec97d3c692824258f99a66d77421
# output-digest: sha256:4a875d4ed62fe6b2d279515f9a19274e159ae8ed58f3a1a0843c60a49f663886
# edit-source:
# - .gds/repository.yaml
# - policies/base/repository-default.yaml
Expand Down Expand Up @@ -42,14 +42,14 @@ jobs:

runner: "ubuntu-latest"
go_version: "1.26.7"
test_command: "go test ./..."
# Declared fast commands own their build scope; the complete build is below.
build_command: ""
test_command: "scripts/validate_shell.sh && scripts/validate_go_core.sh --fast"
timeout_minutes: 30
pr-required:
name: GDS PR required
# Serialized behind fast: both tiers run the integrated assurance scenario,
# which asserts wall-clock budgets, and running two of them side by side on
# one host measures contention rather than the code. It is also the right
# gate order — there is no value in the expensive tier once fast is red.
# Run the declared fast checks first. Repositories can keep this phase
# inexpensive and reserve their full test/build commands for this job.
needs: fast
permissions:
contents: read
Expand Down
7 changes: 4 additions & 3 deletions tests/golden/projections/control-plane/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ GENERATED FILE - DO NOT EDIT DIRECTLY
generator: gds
bundle: 0.8.0-dev
source-tree-digest: sha256:0000000000000000000000000000000000000000000000000000000000000001
input-digest: sha256:3d7c04888af26c66e2c8b7fedc84b08f90a73d1182cd18ee889c3a849151ce25
output-digest: sha256:ece934738d1ed0bb3532f6db213366f3d60c064e6c0aa79101b3c0b4a9e4e06c
input-digest: sha256:a0d8d237e113629cdf67cf248e3010dfb024ec97d3c692824258f99a66d77421
output-digest: sha256:e068119ce6b21b80367eaaeefdb3821e19e8cc2cb9d6613fe020c547ef0892d8
edit-source:
- .gds/repository.yaml
- policies/base/repository-default.yaml
Expand Down Expand Up @@ -47,7 +47,8 @@ GDS is the public engine for a multi-owner GitHub estate. It loads an external e
- Test: `go test ./...`
- Test: `scripts/validate_python.sh`
- Build: `go build -trimpath ./core/cmd/gds`
- Fast: `scripts/validate_go_core.sh --quick`
- Fast: `scripts/validate_shell.sh`
- Fast: `scripts/validate_go_core.sh --fast`
- PR required: `go test ./...`
- PR required: `python3 -m pip install --quiet --require-hashes -r requirements/test.txt`
- PR required: `python3 -m pytest`
Expand Down