Finish composition acquisition before running candidate code (U5)
Why: Later authenticated source acquisition must never follow a frontend build or provider test. Evidence: SELF host replay passed all 23 real composition fixtures; both acquisition-order regressions failed before the repair. Impact: Prepare fixed sources and Go/npm caches before execution; retain committed tooling checks and RDM-selected Backend versions. Refs: R7, R8, R11, R14; dependency-workspace U5
This commit is contained in:
@@ -105,6 +105,47 @@ if (fs.readFileSync('dist/index.html', 'utf8') !== 'immutable-page') process.exi
|
||||
def compose(self, target="build", extra=(), env=None):
|
||||
return self.run_command([sys.executable, "-B", str(SCRIPT), "--root", str(self.root), "--target", target, "--output", str(self.out), *extra], env=env, check=False)
|
||||
|
||||
def acquisition_guard(self, env, *, mark_frontend=True):
|
||||
"""Forward to real tools, refusing acquisition after candidate execution."""
|
||||
marker = self.base / "candidate-executed"
|
||||
tools = self.base / "guarded-tools"
|
||||
tools.mkdir()
|
||||
for name in ("git", "go", "npm"):
|
||||
executable = shutil.which(name)
|
||||
wrapper = tools / name
|
||||
wrapper.write_text("#!" + sys.executable + "\n" + '''import os, pathlib, sys
|
||||
args = sys.argv[1:]
|
||||
name = pathlib.Path(sys.argv[0]).name
|
||||
acquisition = (name == 'git' and args and args[0] in {'clone', 'fetch', 'archive'}
|
||||
or name == 'go' and args and (args[0] == 'mod' or args[0] == 'list')
|
||||
or name == 'npm' and args and args[0] in {'ci', 'install'})
|
||||
if acquisition and pathlib.Path(os.environ['COMPOSITION_EXEC_MARKER']).exists():
|
||||
print('acquisition-after-execution: ' + name + ' ' + ' '.join(args[:2]), file=sys.stderr)
|
||||
sys.exit(86)
|
||||
''' + "os.execv(" + repr(executable) + ", [" + repr(executable) + ", *args])\n")
|
||||
wrapper.chmod(0o755)
|
||||
if mark_frontend:
|
||||
self.write(self.frontend, "build.cjs", (self.frontend / "build.cjs").read_text()
|
||||
+ "\nrequire('node:fs').writeFileSync(process.env.COMPOSITION_EXEC_MARKER, 'frontend-executed')\n")
|
||||
self.lock["components"]["Agent-System-Frontend"]["commit"] = self.commit(self.frontend)
|
||||
self.save_inputs()
|
||||
self.commit(self.root)
|
||||
return env | {"PATH": str(tools) + os.pathsep + env["PATH"],
|
||||
"COMPOSITION_EXEC_MARKER": str(marker), "GITEA_TOKEN": "synthetic-phase-token"}
|
||||
|
||||
def test_all_product_acquisition_precedes_candidate_execution(self):
|
||||
self.test_rdm_vendored_remote_backend_embeds_assets_without_mutating_module_cache()
|
||||
self.write(self.root, "plugins/example/plugin.json", '{"name":"fixed-plugin"}')
|
||||
self.commit(self.root)
|
||||
env = self.acquisition_guard(self.env | {"GOPROXY": (self.base / "proxy").as_uri(),
|
||||
"GOMODCACHE": str(self.base / "module-cache")})
|
||||
result = self.compose(extra=("--rdm-source", str(self.base / "rdm")), env=env)
|
||||
self.assertEqual(result.returncode, 0, result.stdout)
|
||||
self.assertEqual((self.base / "candidate-executed").read_text(), "frontend-executed")
|
||||
self.assertEqual(self.run_command([str(self.out / "agent-system-rdm-product")]).stdout, "immutable-page")
|
||||
self.assertEqual(self.run_command([str(self.out / "agent-system-admin")]).stdout, "immutable-page")
|
||||
self.assertEqual((self.out / "plugins/example/plugin.json").read_text(), '{"name":"fixed-plugin"}')
|
||||
|
||||
def test_fixed_commits_embed_assets_without_touching_developer_checkout(self):
|
||||
# Reading working trees or allowing go.work/npm overlays would produce DIRTY or fail.
|
||||
self.write(self.backend, "web/dist/index.html", "DIRTY")
|
||||
@@ -181,7 +222,10 @@ if (fs.readFileSync('dist/index.html', 'utf8') !== 'immutable-page') process.exi
|
||||
self.write(library, "probe.go", "package probe\nfunc Text() string { return \"\" }\n")
|
||||
self.write(library, "probe_test.go", '''package probe
|
||||
import ("testing"; "os")
|
||||
func TestFixedLibrary(t *testing.T) { if err:=os.WriteFile(os.Getenv("COMPOSITION_GO_MARKER"), []byte("fixed-go-test"), 0600); err!=nil {t.Fatal(err)} }
|
||||
func TestFixedLibrary(t *testing.T) {
|
||||
if err:=os.WriteFile(os.Getenv("COMPOSITION_GO_MARKER"), []byte("fixed-go-test"), 0600); err!=nil {t.Fatal(err)}
|
||||
if path:=os.Getenv("COMPOSITION_EXEC_MARKER"); path!="" { if err:=os.WriteFile(path, []byte("library-executed"), 0600); err!=nil {t.Fatal(err)} }
|
||||
}
|
||||
''')
|
||||
library_sha = self.commit(library)
|
||||
version = "v0.0.0-" + self.run_command(["git", "show", "-s", "--format=%cd", "--date=format-local:%Y%m%d%H%M%S", library_sha], library).stdout.strip() + "-" + library_sha[:12]
|
||||
@@ -221,6 +265,7 @@ func TestFixedLibrary(t *testing.T) { if err:=os.WriteFile(os.Getenv("COMPOSITIO
|
||||
])
|
||||
self.save_inputs()
|
||||
self.commit(self.root)
|
||||
env = self.acquisition_guard(env, mark_frontend=False)
|
||||
result = self.compose("test", env=env)
|
||||
self.assertEqual(result.returncode, 0, result.stdout)
|
||||
self.assertTrue((self.base / "go-tested").is_file(), "resolved Go provider tests were not executed")
|
||||
|
||||
Reference in New Issue
Block a user