@valbuild/cli 0.138.1 → 0.138.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @valbuild/cli
2
2
 
3
+ ## 0.138.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#763](https://github.com/valbuild/val/pull/763) [`443cdcd`](https://github.com/valbuild/val/commit/443cdcdfa8948db22a54adef2ad5e18d9f9868e9) Thanks [@freekh](https://github.com/freekh)! - `val publish` now wires the site at the project's branch, which the content service names, instead of the branch the checkout is on. The site's Val saves its edits to the branch it is wired at, so publishing a managed project from a feature branch made every Studio edit land where no publish looks, and Publish answered "nothing to publish". When the two differ, the CLI says so. With a content service that does not name the branch, the checkout's is used, as before.
8
+ - Updated dependencies [[`2beb226`](https://github.com/valbuild/val/commit/2beb226446c76eb71026f43e04c13943da4a1e31), [`cf3bbc8`](https://github.com/valbuild/val/commit/cf3bbc8fc89ff349aac5ab101d0b4f28164583e6), [`443cdcd`](https://github.com/valbuild/val/commit/443cdcdfa8948db22a54adef2ad5e18d9f9868e9)]:
9
+ - @valbuild/shared@0.138.2
10
+ - @valbuild/core@0.138.2
11
+ - @valbuild/server@0.138.2
12
+ - @valbuild/language-server@0.138.2
13
+
3
14
  ## 0.138.1
4
15
 
5
16
  ### Patch Changes
@@ -3142,6 +3142,16 @@ async function runPublish(options) {
3142
3142
  if (collected === null) {
3143
3143
  log("Asking content what to build against");
3144
3144
  const target = await client.buildTarget();
3145
+ /*
3146
+ * Wired at the PROJECT's branch, which content names, not the
3147
+ * checkout's: the build's Val saves its edits to the branch it is wired
3148
+ * at, and a publish only ever commits the project's. Wired at a feature
3149
+ * branch, every Studio edit was saved where no publish looks.
3150
+ */
3151
+ const wiredBranch = target.branch ?? git.branch;
3152
+ if (wiredBranch !== git.branch) {
3153
+ log(`warn: this checkout is on ${git.branch}; the project publishes ${wiredBranch}, ` + `so the site saves its edits to ${wiredBranch}.`);
3154
+ }
3145
3155
  const build = await buildArtifacts({
3146
3156
  root,
3147
3157
  target,
@@ -3149,7 +3159,7 @@ async function runPublish(options) {
3149
3159
  contentHost,
3150
3160
  git: {
3151
3161
  commit: git.commit,
3152
- branch: git.branch
3162
+ branch: wiredBranch
3153
3163
  },
3154
3164
  env,
3155
3165
  log
@@ -3142,6 +3142,16 @@ async function runPublish(options) {
3142
3142
  if (collected === null) {
3143
3143
  log("Asking content what to build against");
3144
3144
  const target = await client.buildTarget();
3145
+ /*
3146
+ * Wired at the PROJECT's branch, which content names, not the
3147
+ * checkout's: the build's Val saves its edits to the branch it is wired
3148
+ * at, and a publish only ever commits the project's. Wired at a feature
3149
+ * branch, every Studio edit was saved where no publish looks.
3150
+ */
3151
+ const wiredBranch = target.branch ?? git.branch;
3152
+ if (wiredBranch !== git.branch) {
3153
+ log(`warn: this checkout is on ${git.branch}; the project publishes ${wiredBranch}, ` + `so the site saves its edits to ${wiredBranch}.`);
3154
+ }
3145
3155
  const build = await buildArtifacts({
3146
3156
  root,
3147
3157
  target,
@@ -3149,7 +3159,7 @@ async function runPublish(options) {
3149
3159
  contentHost,
3150
3160
  git: {
3151
3161
  commit: git.commit,
3152
- branch: git.branch
3162
+ branch: wiredBranch
3153
3163
  },
3154
3164
  env,
3155
3165
  log
@@ -3107,6 +3107,16 @@ async function runPublish(options) {
3107
3107
  if (collected === null) {
3108
3108
  log("Asking content what to build against");
3109
3109
  const target = await client.buildTarget();
3110
+ /*
3111
+ * Wired at the PROJECT's branch, which content names, not the
3112
+ * checkout's: the build's Val saves its edits to the branch it is wired
3113
+ * at, and a publish only ever commits the project's. Wired at a feature
3114
+ * branch, every Studio edit was saved where no publish looks.
3115
+ */
3116
+ const wiredBranch = target.branch ?? git.branch;
3117
+ if (wiredBranch !== git.branch) {
3118
+ log(`warn: this checkout is on ${git.branch}; the project publishes ${wiredBranch}, ` + `so the site saves its edits to ${wiredBranch}.`);
3119
+ }
3110
3120
  const build = await buildArtifacts({
3111
3121
  root,
3112
3122
  target,
@@ -3114,7 +3124,7 @@ async function runPublish(options) {
3114
3124
  contentHost,
3115
3125
  git: {
3116
3126
  commit: git.commit,
3117
- branch: git.branch
3127
+ branch: wiredBranch
3118
3128
  },
3119
3129
  env,
3120
3130
  log
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@valbuild/cli",
3
3
  "private": false,
4
- "version": "0.138.1",
4
+ "version": "0.138.2",
5
5
  "description": "Val CLI tools",
6
6
  "repository": {
7
7
  "type": "git",
@@ -26,11 +26,11 @@
26
26
  "picocolors": "^1.1.1",
27
27
  "zod": "^4.4.3",
28
28
  "zod-validation-error": "^5.0.0",
29
- "@valbuild/core": "0.138.0",
29
+ "@valbuild/core": "0.138.2",
30
30
  "@valbuild/eslint-plugin": "0.125.0",
31
- "@valbuild/language-server": "0.138.1",
32
- "@valbuild/server": "0.138.1",
33
- "@valbuild/shared": "0.138.0"
31
+ "@valbuild/language-server": "0.138.2",
32
+ "@valbuild/server": "0.138.2",
33
+ "@valbuild/shared": "0.138.2"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "prettier": "*",
@@ -68,6 +68,9 @@ export const Route = createFileRoute("/build/")({
68
68
  });
69
69
  `,
70
70
  "src/styles.css": `h1 { color: rebeccapurple; }\n`,
71
+ // A Val project, so the publish wires it -- and wires it at a branch.
72
+ "val.config.ts": `export const config = { project: "acme/site" };\n`,
73
+ "val.modules.ts": `export default { modules: [] };\n`,
71
74
  "public/robots.txt": "User-agent: *\n",
72
75
  };
73
76
 
@@ -98,6 +101,10 @@ type Report = {
98
101
  buildRouteBuilt: boolean;
99
102
  /** Whether the checkout was left exactly as it was found. */
100
103
  checkoutUntouched: boolean;
104
+ /** The branch the stored `val.server.ts` is wired at. */
105
+ wiredBranch: string | null;
106
+ /** Whether the run warned that the checkout's branch is not the project's. */
107
+ warnedAboutBranch: boolean;
101
108
  };
102
109
 
103
110
  /** A 1x1 PNG: small enough to inline, so the page carries it as a data URI. */
@@ -139,15 +146,19 @@ function listed(root: string): string[] {
139
146
  async function publishOnce(
140
147
  root: string,
141
148
  fake: FakeContentService,
149
+ branch = "main",
142
150
  ): Promise<Report> {
143
151
  const before = listed(root);
152
+ const logged: string[] = [];
144
153
  const result = await runPublish({
145
154
  root,
146
155
  commit: COMMIT,
147
- branch: "main",
156
+ branch,
148
157
  env: { VAL_PROJECT_TOKEN: TOKEN, VAL_CONTENT_URL: fake.url },
149
158
  sleep: () => Promise.resolve(),
150
- log: () => undefined,
159
+ log: (line: string) => {
160
+ logged.push(line);
161
+ },
151
162
  });
152
163
  const declared = [...fake.declarations.values()].map((body) => {
153
164
  const d = body as {
@@ -206,9 +217,25 @@ async function publishOnce(
206
217
  (JSON.parse(source.toString("utf8")) as Record<string, string>)
207
218
  : false),
208
219
  checkoutUntouched: JSON.stringify(listed(root)) === JSON.stringify(before),
220
+ wiredBranch: source ? wiredBranchOf(source) : null,
221
+ warnedAboutBranch: logged.some(
222
+ (line) => line.startsWith("warn:") && line.includes("publishes"),
223
+ ),
209
224
  };
210
225
  }
211
226
 
227
+ /** The branch the stored `val.server.ts` was wired at: its `BUILT_FROM`. */
228
+ function wiredBranchOf(source: Buffer): string | null {
229
+ const files = JSON.parse(source.toString("utf8")) as Record<string, string>;
230
+ const server = Object.entries(files).find(([key]) =>
231
+ key.endsWith("val.server.ts"),
232
+ )?.[1];
233
+ const match = server ? /^const BUILT_FROM = (.+);$/m.exec(server) : null;
234
+ if (!match?.[1] || match[1] === "null") return null;
235
+ const parsed = JSON.parse(match[1]) as { branch?: unknown };
236
+ return typeof parsed.branch === "string" ? parsed.branch : null;
237
+ }
238
+
212
239
  (async () => {
213
240
  const root = writeProject();
214
241
  try {
@@ -228,7 +255,16 @@ async function publishOnce(
228
255
  });
229
256
  const held = await publishOnce(root, second).finally(() => second.close());
230
257
 
231
- process.stdout.write(`${JSON.stringify({ fresh, held })}\n`);
258
+ // From a checkout on a feature branch, of a project content says is on main.
259
+ const third = await startFakeContentService({
260
+ token: TOKEN,
261
+ buildTarget: { ...FAKE_BUILD_TARGET, branch: "main" },
262
+ });
263
+ const elsewhere = await publishOnce(root, third, "feature/x").finally(() =>
264
+ third.close(),
265
+ );
266
+
267
+ process.stdout.write(`${JSON.stringify({ fresh, held, elsewhere })}\n`);
232
268
  } finally {
233
269
  fs.rmSync(root, { recursive: true, force: true });
234
270
  }
@@ -35,9 +35,11 @@ type Run = {
35
35
  clientHasLogo: boolean;
36
36
  buildRouteBuilt: boolean;
37
37
  checkoutUntouched: boolean;
38
+ wiredBranch: string | null;
39
+ warnedAboutBranch: boolean;
38
40
  };
39
41
 
40
- let probe: { fresh: Run; held: Run };
42
+ let probe: { fresh: Run; held: Run; elsewhere: Run };
41
43
 
42
44
  beforeAll(() => {
43
45
  const out = execFileSync(
@@ -110,6 +112,18 @@ test("a layer content already holds is named, not sent again", () => {
110
112
  expect(held.declared[0]!.buildHash).toBe(fresh.declared[0]!.buildHash);
111
113
  });
112
114
 
115
+ test("a checkout on another branch is wired at the project's, and says so", () => {
116
+ // Content names the project's branch; the site's Val saves its edits to the
117
+ // branch it is wired at, so a feature branch here was a site whose every
118
+ // Studio edit went where no publish looks.
119
+ expect(probe.elsewhere.result.status).toBe("live");
120
+ expect(probe.elsewhere.wiredBranch).toBe("main");
121
+ expect(probe.elsewhere.warnedAboutBranch).toBe(true);
122
+ // Where they agree there is nothing to say.
123
+ expect(probe.fresh.wiredBranch).toBe("main");
124
+ expect(probe.fresh.warnedAboutBranch).toBe(false);
125
+ });
126
+
113
127
  test("the checkout is left as it was found", () => {
114
128
  // The route tree is generated on disk and put back; the build is written
115
129
  // to a temporary directory, not into `.val/publish`.
@@ -172,12 +172,25 @@ export async function runPublish(
172
172
  if (collected === null) {
173
173
  log("Asking content what to build against");
174
174
  const target = await client.buildTarget();
175
+ /*
176
+ * Wired at the PROJECT's branch, which content names, not the
177
+ * checkout's: the build's Val saves its edits to the branch it is wired
178
+ * at, and a publish only ever commits the project's. Wired at a feature
179
+ * branch, every Studio edit was saved where no publish looks.
180
+ */
181
+ const wiredBranch = target.branch ?? git.branch;
182
+ if (wiredBranch !== git.branch) {
183
+ log(
184
+ `warn: this checkout is on ${git.branch}; the project publishes ${wiredBranch}, ` +
185
+ `so the site saves its edits to ${wiredBranch}.`,
186
+ );
187
+ }
175
188
  const build = await buildArtifacts({
176
189
  root,
177
190
  target,
178
191
  project: project ?? "",
179
192
  contentHost,
180
- git: { commit: git.commit, branch: git.branch },
193
+ git: { commit: git.commit, branch: wiredBranch },
181
194
  env,
182
195
  log,
183
196
  });