mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-26 05:29:45 +00:00
Reject shipped documentation drift before CI fan-out
PR2576's deterministic mirror failure admitted expensive frontend, backend and browser work that could not land. Run the existing whole-tree checker before dependency work in the build admission job and all three E2E entrypoints, retaining every existing check, secret scan and test gate. Exercise the actual workflow commands with synced, stale and orphaned guides. Change-source: pulse-maintainer
This commit is contained in:
@@ -79,6 +79,11 @@ jobs:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
# Reject deterministic docs drift before queuing builds/tests. Keep the
|
||||
# independent per-push secret scan and Public docs check unchanged.
|
||||
- name: Check shipped docs before dependency work
|
||||
run: python3 scripts/check_docs_mirror.py
|
||||
|
||||
- name: Classify changed files
|
||||
id: filter
|
||||
run: |
|
||||
|
||||
@@ -47,6 +47,10 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Fail before installs, Docker builds or the eight-shard fan-out.
|
||||
- name: Check shipped docs before dependency work
|
||||
run: python3 scripts/check_docs_mirror.py
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
@@ -82,6 +86,10 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
# Fail before installs, Docker builds or the eight-shard fan-out.
|
||||
- name: Check shipped docs before dependency work
|
||||
run: python3 scripts/check_docs_mirror.py
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
@@ -297,6 +305,10 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Fail before installs, Docker builds or the eight-shard fan-out.
|
||||
- name: Check shipped docs before dependency work
|
||||
run: python3 scripts/check_docs_mirror.py
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
|
||||
@@ -15,6 +15,23 @@
|
||||
|
||||
## Purpose
|
||||
|
||||
### Shipped documentation is checked before CI dependency work
|
||||
|
||||
Build and Test checks every shipped Markdown mirror immediately after the
|
||||
`changes` job's checkout, before classification can admit frontend, backend,
|
||||
script/build or benchmark work. Core E2E does the same before dependency setup
|
||||
in tier selection and both independent provisioning jobs; a failing tier check
|
||||
does not admit the eight browser shards. Existing check names, secret scanning,
|
||||
Public docs validation, test coverage and failure verdicts remain unchanged.
|
||||
|
||||
The check is blocking and unconditional, including dispatches. It uses the
|
||||
whole-tree mode of `scripts/check_docs_mirror.py`, not staged-only tolerance.
|
||||
`TestCIDocsMirrorPreflight` parses all four actual workflow entrypoints and
|
||||
executes their command with the real checker: stale or orphaned shipped guides
|
||||
stop before a dependency-work marker, while identical copies allow it. This
|
||||
prevents deterministic documentation faults from consuming expensive CI work;
|
||||
local controls do not establish hosted queue-time improvement or publication.
|
||||
|
||||
### Native helper verification follows helper-only changes
|
||||
|
||||
Unified Agent Native Verification admits helper command, protocol, shared
|
||||
|
||||
@@ -3786,6 +3786,106 @@ func TestReleaseTrainCITriggersIncludeBuildAndE2E(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Documentation drift is deterministic and cheap to reject. It must not wait
|
||||
// behind dependency installation or fan out into builds that cannot land.
|
||||
func TestCIDocsMirrorPreflight(t *testing.T) {
|
||||
type step struct {
|
||||
Name string `yaml:"name"`
|
||||
Uses string `yaml:"uses"`
|
||||
Run string `yaml:"run"`
|
||||
If string `yaml:"if"`
|
||||
ContinueOnError bool `yaml:"continue-on-error"`
|
||||
Directory string `yaml:"working-directory"`
|
||||
}
|
||||
type job struct {
|
||||
Steps []step `yaml:"steps"`
|
||||
}
|
||||
for _, site := range []struct{ workflow, job string }{
|
||||
{"build-and-test.yml", "changes"},
|
||||
{"test-e2e.yml", "tier-selection"},
|
||||
{"test-e2e.yml", "offline-org-provisioning"},
|
||||
{"test-e2e.yml", "agent-registration"},
|
||||
} {
|
||||
t.Run(site.workflow+"/"+site.job, func(t *testing.T) {
|
||||
content, err := os.ReadFile(repoFile(".github", "workflows", site.workflow))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var workflow struct {
|
||||
Jobs map[string]job `yaml:"jobs"`
|
||||
}
|
||||
if err := yaml.Unmarshal(content, &workflow); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
steps := workflow.Jobs[site.job].Steps
|
||||
if len(steps) < 2 || !strings.HasPrefix(steps[0].Uses, "actions/checkout@") {
|
||||
t.Fatal("preflight must follow source checkout")
|
||||
}
|
||||
guard := steps[1]
|
||||
if guard.Run != "python3 scripts/check_docs_mirror.py" || guard.If != "" ||
|
||||
guard.ContinueOnError || guard.Directory != "" || guard.Uses != "" {
|
||||
t.Fatal("whole-tree mirror check must be an unconditional blocking step immediately after checkout")
|
||||
}
|
||||
checker, err := os.ReadFile(repoFile("scripts", "check_docs_mirror.py"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, fixture := range []struct {
|
||||
name, source, mirror, diagnostic string
|
||||
wantExit int
|
||||
}{
|
||||
{"synced", "# Safe guidance\n", "# Safe guidance\n", "passed (1 shipped docs)", 0},
|
||||
{"stale-shipped-guide", "# Safe guidance\nDo not repeat a guest probe.\n", "# Safe guidance\n", "docs/GUIDE.md and frontend-modern/public/docs/GUIDE.md differ", 1},
|
||||
{"missing-source", "", "# Orphan guide\n", "shipped copy has no repo source docs/GUIDE.md", 1},
|
||||
} {
|
||||
t.Run(fixture.name, func(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
write := func(path string, data []byte) {
|
||||
t.Helper()
|
||||
full := filepath.Join(root, path)
|
||||
if err := os.MkdirAll(filepath.Dir(full), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(full, data, 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
write("scripts/check_docs_mirror.py", checker)
|
||||
write("frontend-modern/public/docs/GUIDE.md", []byte(fixture.mirror))
|
||||
if fixture.source != "" {
|
||||
write("docs/GUIDE.md", []byte(fixture.source))
|
||||
}
|
||||
// Run the workflow's actual command and a harmless stand-in for
|
||||
// the next dependency step under GitHub's fail-fast shell mode.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, "bash", "--noprofile", "--norc", "-e", "-o", "pipefail", "-c", guard.Run+"\nprintf started > dependency-work")
|
||||
cmd.Dir = root
|
||||
output, err := cmd.CombinedOutput()
|
||||
exit := 0
|
||||
if err != nil {
|
||||
failure, ok := err.(*exec.ExitError)
|
||||
if !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exit = failure.ExitCode()
|
||||
}
|
||||
if exit != fixture.wantExit || !strings.Contains(string(output), fixture.diagnostic) {
|
||||
t.Fatalf("exit=%d, want=%d, output=%s", exit, fixture.wantExit, output)
|
||||
}
|
||||
_, markerErr := os.Stat(filepath.Join(root, "dependency-work"))
|
||||
if fixture.wantExit == 0 && markerErr != nil {
|
||||
t.Fatal("synced documentation must allow later dependency work")
|
||||
}
|
||||
if fixture.wantExit != 0 && !os.IsNotExist(markerErr) {
|
||||
t.Fatal("failed preflight must stop before dependency work")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderPairDockerProofRunsBeforeFreezeAndOnExactCandidate(t *testing.T) {
|
||||
const testName = "TestIntegrationProviderPairNetworkIsolation"
|
||||
const helperImage = "alpine:3.24@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6"
|
||||
|
||||
Reference in New Issue
Block a user