Skip to content

Commit 4d0250f

Browse files
committed
refactor(xtask): make regen a thin wrapper over gloam regen
Discovery, path handling, and the locked-vs-fresh semantics now live in `gloam regen` itself, so the xtask just builds the working-copy gloam and runs it. The tree root defaults to the current directory, so a bare `cargo xtask regen` regenerates every tree in the repo — including examples/rust, which the old replay-with-tree-root-cwd scheme could not handle (its recorded --out-path was relative to the repo root, the C examples' to examples/c; no single cwd satisfied both). Signed-off-by: Steven Noonan <steven@uplinklabs.net>
1 parent 101c10d commit 4d0250f

3 files changed

Lines changed: 58 additions & 115 deletions

File tree

CONTRIBUTING.md

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,36 @@ are enforced structurally at generation time instead.
204204

205205
### Regenerating downstream trees
206206

207-
`cargo xtask regen <tree-root>` re-runs the command line recorded in every
208-
gloam manifest under `<tree-root>` (e.g. a gloam-pregen checkout) using the
209-
current working copy, pinned to each tree's recorded provenance via
210-
`--lock`, so `git diff` in the tree shows only the effect of code changes.
211-
Pass `--fresh` to run the recorded commands verbatim instead (advancing
212-
pins to upstream HEAD — the normal tree-update workflow).
207+
`gloam regen [paths...]` regenerates existing output trees in place by
208+
replaying the command line recorded in each tree's
209+
`.gloam/manifest.json`. A path may be a tree root, a directory to search
210+
recursively (the default is `.`), or a manifest file itself. The effective
211+
output path is derived from each manifest's own location — the recorded
212+
`--out-path` was relative to the original invocation's cwd, which is
213+
unknowable later — and the recorded command line is re-recorded verbatim,
214+
so regeneration works from any directory and never rewrites what the
215+
manifest says produced the tree.
216+
217+
By default each replay is pinned to the tree's recorded provenance
218+
(`--lock` semantics), so `git diff` shows only the effect of gloam code
219+
changes; pass `--fresh` to re-resolve sources instead (advancing the tree
220+
to the current bundle, or to upstream HEAD if the recorded command used
221+
`--fetch`).
222+
223+
`cargo xtask regen [tree-root] [--fresh]` builds the working-copy gloam
224+
and runs it over `tree-root` (default: the current directory) — use this
225+
for `examples/` here or a gloam-pregen checkout. To review a regen,
226+
filter out gloam's own version/commit stamp churn:
227+
228+
```sh
229+
git diff -I'^ \* @generated by gloam ' -I'^// @generated by gloam ' \
230+
-I'^ "(version|describe|commit)": ' -I'^ "blob": '
231+
```
232+
233+
(Ignoring 6-space `"blob"` lines is safe: a provenance pin's blob never
234+
moves without its unfiltered sibling `"commit"` line moving too, so real
235+
source changes always stay visible; the output BOM's blob lines just track
236+
stamp churn.)
213237

214238
## Debug tracing
215239

examples/README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,10 @@ gloam --api vk=1.3 \
5858
gloam --api gl:core=3.3,gles2=3.0 --merge --out-path gl-triangle/gloam rust --alias
5959
```
6060

61-
`cargo xtask regen examples/c` and `cargo xtask regen examples/rust` re-run the
62-
recorded commands with the working-copy gloam.
61+
`cargo xtask regen examples` re-runs the recorded commands with the
62+
working-copy gloam, regenerating every tree in place (the output path is
63+
derived from each `.gloam/manifest.json` location, so the recorded
64+
`--out-path` values above don't need to match your cwd). Add `--fresh` to
65+
advance the trees to the current bundle instead of pinning to their
66+
recorded provenance — CI does this automatically when the weekly bundle
67+
update changes example output.

xtask/src/main.rs

Lines changed: 21 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@
66
//! `bundled/provenance.json`. Sharing gloam's acquisition code guarantees the
77
//! bundled and `--fetch` provenance are produced identically.
88
//!
9-
//! `cargo xtask regen <tree-root> [--fresh]` regenerates every gloam output
10-
//! tree found under `<tree-root>` (e.g. a gloam-pregen checkout) by re-running
11-
//! the command line recorded in each tree's manifest with the current working
12-
//! copy of gloam. By default each run is pinned to the tree's recorded
13-
//! provenance (`--lock`), so `git diff` in the tree shows only the effect of
14-
//! gloam code changes; `--fresh` runs the recorded commands verbatim instead,
15-
//! advancing to upstream HEAD (the normal tree-update workflow).
9+
//! `cargo xtask regen [tree-root] [--fresh]` builds the working-copy gloam
10+
//! and runs `gloam regen` over `tree-root` (default: the current directory),
11+
//! regenerating every gloam output tree found beneath it (e.g. `examples/`
12+
//! here, or a gloam-pregen checkout). Discovery, path handling, and the
13+
//! locked-vs-fresh semantics all live in `gloam regen` itself; this task
14+
//! just supplies a freshly built binary.
1615
1716
use std::path::{Path, PathBuf};
1817

@@ -21,7 +20,7 @@ use indexmap::IndexMap;
2120

2221
use gloam::provenance::acquire::Github;
2322
use gloam::provenance::manifest::{
24-
BundledProvenance, Manifest, ProvenancePin, SCHEMA_VERSION, preserve_unchanged_repos,
23+
BundledProvenance, ProvenancePin, SCHEMA_VERSION, preserve_unchanged_repos,
2524
};
2625
use gloam::provenance::{CLUSTERS, bundled_rel_path};
2726

@@ -48,23 +47,7 @@ fn regen(args: &[String]) -> Result<()> {
4847
other => bail!("unexpected regen argument '{other}'"),
4948
}
5049
}
51-
let root = root.context("usage: cargo xtask regen <tree-root> [--fresh]")?;
52-
if !root.is_dir() {
53-
bail!("tree root {} is not a directory", root.display());
54-
}
55-
// Keep the path as given (no canonicalize: Windows turns those into
56-
// \\?\ UNC paths, which is noise in every printed command). Child
57-
// processes run with this as their cwd, so relative roots work too.
58-
59-
// Every gloam manifest records the exact command line that produced its
60-
// tree, so the tree set is self-describing: generation trees carry
61-
// `.gloam/manifest.json`, lock snapshots are bare `manifest.json` files.
62-
let mut manifests: Vec<PathBuf> = Vec::new();
63-
find_manifests(&root, &mut manifests);
64-
manifests.sort();
65-
if manifests.is_empty() {
66-
bail!("no gloam manifests found under {}", root.display());
67-
}
50+
let root = root.unwrap_or_else(|| PathBuf::from("."));
6851

6952
// Build and locate the working-copy gloam binary.
7053
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
@@ -81,93 +64,24 @@ fn regen(args: &[String]) -> Result<()> {
8164
.join("debug")
8265
.join(format!("gloam{}", std::env::consts::EXE_SUFFIX));
8366

84-
let mut ran = 0usize;
85-
for manifest_path in &manifests {
86-
let Some(recorded) = recorded_command(manifest_path) else {
87-
continue; // not a gloam manifest (or unreadable) — skip
88-
};
89-
90-
let rel = manifest_path.strip_prefix(&root).unwrap_or(manifest_path);
91-
92-
// Drop the recorded argv[0] ("gloam"); in locked mode, pin the run to
93-
// the tree's own provenance. --lock must precede the subcommand, so
94-
// it goes first. The path is root-relative because the child runs
95-
// with the tree root as its cwd (recorded --out-path values are too).
96-
let mut argv: Vec<String> = recorded
97-
.split_whitespace()
98-
.skip(1)
99-
.map(String::from)
100-
.collect();
101-
if !fresh {
102-
argv.splice(0..0, ["--lock".to_string(), rel.display().to_string()]);
103-
}
104-
eprintln!("· {} $ gloam {}", rel.display(), argv.join(" "));
105-
106-
// Recorded --out-path values are relative to the tree root.
107-
let status = std::process::Command::new(&bin)
108-
.args(&argv)
109-
.current_dir(&root)
110-
.status()
111-
.with_context(|| format!("running gloam for {}", rel.display()))?;
112-
if !status.success() {
113-
bail!("gloam failed for {}", rel.display());
114-
}
115-
ran += 1;
67+
// `gloam regen` does the rest: recursive discovery, path handling, and
68+
// locked-vs-fresh replay. It runs with our cwd, so a relative root
69+
// behaves the same as it would for find/grep.
70+
let mut cmd = std::process::Command::new(&bin);
71+
cmd.arg("regen");
72+
if fresh {
73+
cmd.arg("--fresh");
11674
}
117-
118-
if ran == 0 {
119-
bail!(
120-
"found {} manifest.json file(s) under {}, but none recorded a gloam command line",
121-
manifests.len(),
122-
root.display()
123-
);
75+
cmd.arg(&root);
76+
let status = cmd
77+
.status()
78+
.with_context(|| format!("running {} regen", bin.display()))?;
79+
if !status.success() {
80+
bail!("gloam regen failed");
12481
}
125-
126-
eprintln!("regenerated {ran} tree(s) under {}", root.display());
127-
eprintln!("review with:");
128-
eprintln!(
129-
" git -C {} diff -I'^ \\* Generated by gloam ' -I'^ \"(version|describe|commit)\": '",
130-
root.display()
131-
);
13282
Ok(())
13383
}
13484

135-
/// Recursively collect `manifest.json` files under `dir`, skipping `.git`.
136-
fn find_manifests(dir: &Path, out: &mut Vec<PathBuf>) {
137-
let Ok(entries) = std::fs::read_dir(dir) else {
138-
return;
139-
};
140-
for entry in entries.flatten() {
141-
let path = entry.path();
142-
if path.is_dir() {
143-
if path.file_name().is_some_and(|n| n == ".git") {
144-
continue;
145-
}
146-
find_manifests(&path, out);
147-
} else if path.file_name().is_some_and(|n| n == "manifest.json") {
148-
out.push(path);
149-
}
150-
}
151-
}
152-
153-
/// The gloam command line recorded in a manifest, if the file parses as a
154-
/// gloam manifest at the current schema and recorded one.
155-
fn recorded_command(path: &Path) -> Option<String> {
156-
let text = std::fs::read_to_string(path).ok()?;
157-
let m = Manifest::from_json(&text).ok()?;
158-
if m.schema_version != SCHEMA_VERSION {
159-
eprintln!(
160-
"· {}: skipping (schema_version {} != {})",
161-
path.display(),
162-
m.schema_version,
163-
SCHEMA_VERSION
164-
);
165-
return None;
166-
}
167-
let cmd = m.gloam.command_line;
168-
(cmd.split_whitespace().next() == Some("gloam")).then_some(cmd)
169-
}
170-
17185
/// Best-effort read of the checked-in provenance manifest. Missing,
17286
/// unreadable, or schema-mismatched files are ignored — the bundle is simply
17387
/// recorded fresh at the newly resolved commits.

0 commit comments

Comments
 (0)