Commit fc37682

Eric Bower  ·  2026-08-11 11:33:42 -0400 EDT
parent 1569539
fix: pici runner should not create a published.json artifact
2 files changed,  +88, -27
+37, -27
......@@ -1343,21 +1343,9 @@ func monitorTick(cfg *Cfg, log *slog.Logger, output io.Writer, jobStates map[str
13431343 log.Error("publish final status", "err", err)
13441344 }
13451345 }
1346- // Write published.json, then sync to include it in the rsync.
1347- // This is the only sync for completed jobs. In-progress jobs are
1348- // synced every tick in the else branch below.
1349- published := map[string]interface{}{
1350- "status": status,
1351- "exit_code": exitCode,
1352- "job_id": jobID,
1353- "finished_at": time.Now().UTC().Format(time.RFC3339),
1354- }
1355- publishedJSON, _ := json.Marshal(published)
1356- log.Info("writing sentinel", "path", sentinel)
1357- if err := os.MkdirAll(filepath.Dir(sentinel), 0755); err == nil {
1358- if err := os.WriteFile(sentinel, publishedJSON, 0644); err != nil {
1359- log.Error("write published sentinel", "err", err)
1360- }
1346+ log.Info("writing sentinel", "repo", name, "job_id", jobID)
1347+ if err := writePublishedSentinel(cfg.ArtifactDir, name, jobID, status, exitCode); err != nil {
1348+ log.Error("write published sentinel", "err", err)
13611349 }
13621350 // Regenerate index.html now that published.json exists, so the artifact list includes it.
13631351 indexHTML, indexTXT := generateJobIndex(cfg.ArtifactDir, name, jobID, group)
......@@ -2403,9 +2391,43 @@ func runLocal(cfg *Cfg, dest string) error {
24032391 fmt.Fprintf(os.Stdout, "✅ artifacts rsynced to %s\n", eventData.ArtifactDest) //nolint:errcheck
24042392 }
24052393
2394+ // Write published sentinel for local run (`pici`)
2395+ domain := getDomain(eventData.Type)
2396+ prefix := fmt.Sprintf("%s.%s.%s.", domain, eventData.Name, jobID)
2397+ if listOutput, err := exec.Command("zmx", "list").CombinedOutput(); err == nil {
2398+ sessions := parseZMXList(string(listOutput))
2399+ var jobSessions []SessionInfo
2400+ for _, s := range sessions {
2401+ if strings.HasPrefix(s.Name, prefix) {
2402+ s.Short = cleanSessionShort(s.Name, prefix, eventData.Name, jobID)
2403+ jobSessions = append(jobSessions, s)
2404+ }
2405+ }
2406+ exitCode, status := resolveJobExitCode(jobSessions)
2407+ _ = writePublishedSentinel(cfg.ArtifactDir, eventData.Name, jobID, status, exitCode)
2408+ }
2409+
24062410 return nil
24072411 }
24082412
2413+func writePublishedSentinel(artifactDir, repoName, jobID, status string, exitCode int) error {
2414+ sentinel := filepath.Join(artifactDir, repoName, jobID, "artifacts", "published.json")
2415+ published := map[string]interface{}{
2416+ "status": status,
2417+ "exit_code": exitCode,
2418+ "job_id": jobID,
2419+ "finished_at": time.Now().UTC().Format(time.RFC3339),
2420+ }
2421+ publishedJSON, err := json.Marshal(published)
2422+ if err != nil {
2423+ return err
2424+ }
2425+ if err := os.MkdirAll(filepath.Dir(sentinel), 0755); err != nil {
2426+ return err
2427+ }
2428+ return os.WriteFile(sentinel, publishedJSON, 0644)
2429+}
2430+
24092431 func stageLocalArtifacts(cfg *Cfg, log *slog.Logger, repoName, jobID, eventType string) error {
24102432 domain := getDomain(eventType)
24112433 prefix := fmt.Sprintf("%s.%s.%s.", domain, repoName, jobID)
......@@ -2449,18 +2471,6 @@ func stageLocalArtifacts(cfg *Cfg, log *slog.Logger, repoName, jobID, eventType
24492471 }
24502472 }
24512473
2452- // Write published sentinel
2453- exitCode, status := resolveJobExitCode(jobSessions)
2454- sentinel := filepath.Join(cfg.ArtifactDir, repoName, jobID, "artifacts", "published.json")
2455- published := map[string]interface{}{
2456- "status": status,
2457- "exit_code": exitCode,
2458- "job_id": jobID,
2459- "finished_at": time.Now().UTC().Format(time.RFC3339),
2460- }
2461- publishedJSON, _ := json.Marshal(published)
2462- _ = os.WriteFile(sentinel, publishedJSON, 0644)
2463-
24642474 indexHTML, indexTXT := generateJobIndex(cfg.ArtifactDir, repoName, jobID, jobSessions)
24652475 _ = stageArtifact(cfg.ArtifactDir, repoName, jobID, "index", indexHTML, ".html")
24662476 _ = stageArtifact(cfg.ArtifactDir, repoName, jobID, "index", indexTXT, ".txt")
+51, -0
......@@ -86,6 +86,12 @@ zmx run step2 echo "hello from step2"
8686 t.Fatal("timeout waiting for runner to complete")
8787 }
8888
89+ // Verify that RunRunner does NOT create published.json (monitor's job)
90+ sentinel := filepath.Join(artifactDir, "test-repo", testJobID, "artifacts", "published.json")
91+ if _, err := os.Stat(sentinel); err == nil {
92+ t.Fatalf("expected published.json NOT to exist after RunRunner, but it exists at %s", sentinel)
93+ }
94+
8995 // 4. Run the monitor and poll for incremental artifacts
9096 monitorDone := make(chan error, 1)
9197 go func() {
......@@ -972,3 +978,48 @@ func TestWorkspaceRsync_ExcludesGitAndJJ(t *testing.T) {
972978 t.Errorf("expected checksum to start with 'sha256:', got %q", checksum)
973979 }
974980 }
981+
982+func TestRunnerDoesNotCreatePublishedSentinel(t *testing.T) {
983+ tmpDir := t.TempDir()
984+ artifactDir := filepath.Join(tmpDir, "artifacts")
985+ wkDir := filepath.Join(tmpDir, "workspace")
986+
987+ if err := os.MkdirAll(wkDir, 0755); err != nil {
988+ t.Fatalf("failed to create workspace dir: %v", err)
989+ }
990+
991+ picoScript := `#!/usr/bin/env bash
992+set -euo pipefail
993+zmx run step1 -d echo "step1"
994+zmx wait "*"
995+`
996+ if err := os.WriteFile(filepath.Join(wkDir, "pico.sh"), []byte(picoScript), 0755); err != nil {
997+ t.Fatalf("failed to write pico.sh: %v", err)
998+ }
999+
1000+ jobID := "j-no-published-test"
1001+ event := Event{
1002+ Type: "git.push",
1003+ Name: "no-published-repo",
1004+ JobID: jobID,
1005+ Workspace: wkDir,
1006+ }
1007+ eventJSON, _ := json.Marshal(event)
1008+
1009+ cfg := &Cfg{
1010+ Logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
1011+ ArtifactDir: artifactDir,
1012+ EventSource: io.NopCloser(bytes.NewReader(append(eventJSON, '\n'))),
1013+ NewWorkspace: defaultWorkspaceFactory,
1014+ SessionPrefix: "test.",
1015+ }
1016+
1017+ if err := RunRunner(cfg); err != nil {
1018+ t.Fatalf("RunRunner failed: %v", err)
1019+ }
1020+
1021+ sentinel := filepath.Join(artifactDir, "no-published-repo", jobID, "artifacts", "published.json")
1022+ if _, err := os.Stat(sentinel); err == nil {
1023+ t.Errorf("expected published.json NOT to exist after RunRunner, but it exists at %s", sentinel)
1024+ }
1025+}