Commit 4eaacc1
Eric Bower
·
2026-05-08 10:34:18 -0400 EDT
parent 6048817
fix: timing issue
1 files changed,
+45,
-38
M
main.go
M
main.go
+45,
-38
| ... | ... | @@ -1075,6 +1075,10 @@ func monitorTick(cfg *Cfg, log *slog.Logger, output io.Writer, jobStates map[str | |
| 1075 | 1075 | log.Error("publish final status", "err", err) | |
| 1076 | 1076 | } | |
| 1077 | 1077 | } | |
| 1078 | + | // Sync artifacts before writing sentinel — syncArtifacts skips jobs with published.json | |
| 1079 | + | if err := syncJobArtifacts(cfg, name, jobID, log); err != nil { | |
| 1080 | + | log.Error("sync artifacts", "err", err) | |
| 1081 | + | } | |
| 1078 | 1082 | // Write sentinel so we don't re-publish on subsequent ticks | |
| 1079 | 1083 | published := map[string]interface{}{ | |
| 1080 | 1084 | "status": status, |
| ... | ... | @@ -1464,6 +1468,46 @@ func stageArtifact(dir, name, jobID, short, content, ext string) error { | |
| 1464 | 1468 | return os.WriteFile(path, []byte(content), 0644) | |
| 1465 | 1469 | } | |
| 1466 | 1470 | ||
| 1471 | + | // syncJobArtifacts syncs artifacts for a single job. | |
| 1472 | + | func syncJobArtifacts(cfg *Cfg, repoName, jobID string, log *slog.Logger) error { | |
| 1473 | + | jobDir := filepath.Join(cfg.ArtifactDir, repoName, jobID) | |
| 1474 | + | eventPath := filepath.Join(jobDir, "event.json") | |
| 1475 | + | data, err := os.ReadFile(eventPath) | |
| 1476 | + | if err != nil { | |
| 1477 | + | return fmt.Errorf("read event: %w", err) | |
| 1478 | + | } | |
| 1479 | + | var event Event | |
| 1480 | + | if err := json.Unmarshal(data, &event); err != nil || event.ArtifactDest == "" { | |
| 1481 | + | return fmt.Errorf("invalid event") | |
| 1482 | + | } | |
| 1483 | + | log.Debug("syncing artifacts", "repo", repoName, "job_id", jobID, "dest", event.ArtifactDest) | |
| 1484 | + | var sshArgs string | |
| 1485 | + | if cfg.KeyLocation != "" { | |
| 1486 | + | certFile := "" | |
| 1487 | + | if cfg.CertificateLocation != "" { | |
| 1488 | + | certFile = fmt.Sprintf(" -o CertificateFile %s", cfg.CertificateLocation) | |
| 1489 | + | } | |
| 1490 | + | sshArgs = fmt.Sprintf("-F ~/.ssh/config -i %s%s", cfg.KeyLocation, certFile) | |
| 1491 | + | } | |
| 1492 | + | // Append "/" so rsync copies into the destination directory, | |
| 1493 | + | // not as a subdirectory named after the source. | |
| 1494 | + | dest := event.ArtifactDest | |
| 1495 | + | if !strings.HasSuffix(dest, "/") { | |
| 1496 | + | dest += "/" | |
| 1497 | + | } | |
| 1498 | + | var cmd *exec.Cmd | |
| 1499 | + | if sshArgs != "" { | |
| 1500 | + | cmd = exec.Command("rsync", "-e", sshArgs, "-rv", jobDir+"/", dest) | |
| 1501 | + | } else { | |
| 1502 | + | cmd = exec.Command("rsync", "-rv", jobDir+"/", dest) | |
| 1503 | + | } | |
| 1504 | + | rsyncCmd := fmt.Sprintf("rsync %s %s %s", | |
| 1505 | + | strings.TrimLeft(cmd.Args[1], "-"), | |
| 1506 | + | jobDir+"/", dest) | |
| 1507 | + | log.Info("rsync", "cmd", rsyncCmd) | |
| 1508 | + | return runCmd(cmd, log) | |
| 1509 | + | } | |
| 1510 | + | ||
| 1467 | 1511 | func syncArtifacts(cfg *Cfg, log *slog.Logger) error { | |
| 1468 | 1512 | entries, err := os.ReadDir(cfg.ArtifactDir) | |
| 1469 | 1513 | if err != nil { |
| ... | ... | @@ -1484,7 +1528,6 @@ func syncArtifacts(cfg *Cfg, log *slog.Logger) error { | |
| 1484 | 1528 | continue | |
| 1485 | 1529 | } | |
| 1486 | 1530 | jobDir := filepath.Join(cfg.ArtifactDir, entry.Name(), repoEntry.Name()) | |
| 1487 | - | eventPath := filepath.Join(jobDir, "event.json") | |
| 1488 | 1531 | publishedPath := filepath.Join(jobDir, "published.json") | |
| 1489 | 1532 | ||
| 1490 | 1533 | // Skip if already published (job is complete) |
| ... | ... | @@ -1492,43 +1535,7 @@ func syncArtifacts(cfg *Cfg, log *slog.Logger) error { | |
| 1492 | 1535 | continue | |
| 1493 | 1536 | } | |
| 1494 | 1537 | ||
| 1495 | - | data, err := os.ReadFile(eventPath) | |
| 1496 | - | if err != nil { | |
| 1497 | - | continue // no event yet, skip | |
| 1498 | - | } | |
| 1499 | - | var event Event | |
| 1500 | - | if err := json.Unmarshal(data, &event); err != nil || event.ArtifactDest == "" { | |
| 1501 | - | continue | |
| 1502 | - | } | |
| 1503 | - | log.Debug("syncing artifacts", "repo", entry.Name(), "job_id", repoEntry.Name(), "dest", event.ArtifactDest) | |
| 1504 | - | var sshArgs string | |
| 1505 | - | if cfg.KeyLocation != "" { | |
| 1506 | - | certFile := "" | |
| 1507 | - | if cfg.CertificateLocation != "" { | |
| 1508 | - | certFile = fmt.Sprintf(" -o CertificateFile %s", cfg.CertificateLocation) | |
| 1509 | - | } | |
| 1510 | - | sshArgs = fmt.Sprintf("-F ~/.ssh/config -i %s%s", cfg.KeyLocation, certFile) | |
| 1511 | - | } | |
| 1512 | - | srcDir := jobDir | |
| 1513 | - | // Append "/" so rsync copies into the destination directory, | |
| 1514 | - | // not as a subdirectory named after the source. | |
| 1515 | - | dest := event.ArtifactDest | |
| 1516 | - | if !strings.HasSuffix(dest, "/") { | |
| 1517 | - | dest += "/" | |
| 1518 | - | } | |
| 1519 | - | ||
| 1520 | - | // Build rsync command, omitting -e if no ssh args | |
| 1521 | - | var cmd *exec.Cmd | |
| 1522 | - | if sshArgs != "" { | |
| 1523 | - | cmd = exec.Command("rsync", "-e", sshArgs, "-rv", srcDir+"/", dest) | |
| 1524 | - | } else { | |
| 1525 | - | cmd = exec.Command("rsync", "-rv", srcDir+"/", dest) | |
| 1526 | - | } | |
| 1527 | - | rsyncCmd := fmt.Sprintf("rsync %s %s %s", | |
| 1528 | - | strings.TrimLeft(cmd.Args[1], "-"), | |
| 1529 | - | srcDir+"/", dest) | |
| 1530 | - | log.Info("rsync", "cmd", rsyncCmd) | |
| 1531 | - | if err := runCmd(cmd, log); err != nil { | |
| 1538 | + | if err := syncJobArtifacts(cfg, entry.Name(), repoEntry.Name(), log); err != nil { | |
| 1532 | 1539 | log.Error("sync artifacts", "repo", entry.Name(), "job_id", repoEntry.Name(), "err", err) | |
| 1533 | 1540 | } | |
| 1534 | 1541 | } |