@bongos/core 1.20.38 → 1.20.39

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.
@@ -23,6 +23,8 @@ import fs from 'node:fs';
23
23
  import path from 'node:path';
24
24
  import vm from 'node:vm';
25
25
  import { fileURLToPath } from 'node:url';
26
+ import { createRequire } from 'node:module';
27
+ const require = createRequire(import.meta.url);
26
28
 
27
29
  const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
28
30
  const HUB = fs.readFileSync(path.join(ROOT, 'modules', 'public-landing', 'public', 'projects.html'), 'utf8').replace(/\r\n/g, '\n');
@@ -301,6 +303,53 @@ test('typing a start command clears the "how your app starts" ask at once', asyn
301
303
  assert.doesNotMatch(w.$('h-render').textContent, /how your app starts/);
302
304
  });
303
305
 
306
+ // task 1004464 — Render refused a native app with no build command, live, on project 43.
307
+ test('the build command starts as the runtime\'s usual one, follows the runtime, and yields to the owner\'s own', async () => {
308
+ const w = load({ answers: ANSWERS, draft: { appPlatform: 'render' } });
309
+ w.typeKey(KEY);
310
+ await w.check();
311
+ await w.pickWorkspace(0);
312
+ w.pickService({ value: 'new' });
313
+ assert.equal(w.$('f-renderBuild').value, 'npm install', 'Node.js starts with npm install');
314
+ w.$('f-renderRuntime').value = 'python';
315
+ w.$('renderNew').fire('change', { target: w.$('f-renderRuntime') });
316
+ assert.equal(w.$('f-renderBuild').value, 'pip install -r requirements.txt', 'it follows the runtime');
317
+ w.$('f-renderBuild').value = 'make build';
318
+ w.$('renderNew').fire('input', { target: w.$('f-renderBuild') });
319
+ w.$('f-renderRuntime').value = 'ruby';
320
+ w.$('renderNew').fire('change', { target: w.$('f-renderRuntime') });
321
+ assert.equal(w.$('f-renderBuild').value, 'make build', 'the owner\'s own command is never overwritten');
322
+ w.state.renderStart = 'bundle exec puma';
323
+ assert.equal(w.renderRequestBody().build_command, 'make build');
324
+ });
325
+
326
+ test('an emptied build command is refused before Render ever sees it, and the ask clears on typing', async () => {
327
+ const w = load({ answers: ANSWERS, draft: { appPlatform: 'render' } });
328
+ w.typeKey(KEY);
329
+ await w.check();
330
+ await w.pickWorkspace(0);
331
+ w.pickService({ value: 'new' });
332
+ w.state.renderStart = 'npm start';
333
+ w.$('f-renderBuild').value = '';
334
+ w.$('renderNew').fire('input', { target: w.$('f-renderBuild') });
335
+ assert.equal(w.validateRender(), false);
336
+ assert.match(w.$('h-render').textContent, /how your app is built/);
337
+ w.$('f-renderBuild').value = 'npm ci';
338
+ w.$('renderNew').fire('input', { target: w.$('f-renderBuild') });
339
+ assert.doesNotMatch(w.$('h-render').className, /err/);
340
+ assert.equal(w.validateRender(), true);
341
+ });
342
+
343
+ test('the page\'s build defaults are the runner\'s, key for key', () => {
344
+ const { BUILD_DEFAULTS, RUNTIMES } = require('../modules/provisioning/render-standup.js');
345
+ const src = HUB.replace(/\r\n/g, '\n').match(/var RENDER_BUILD_DEFAULTS = (\{[\s\S]*?\});/);
346
+ assert.ok(src, 'the page carries RENDER_BUILD_DEFAULTS');
347
+ const page = vm.runInNewContext('(' + src[1] + ')');
348
+ assert.deepEqual({ ...page }, { ...BUILD_DEFAULTS });
349
+ assert.deepEqual(Object.keys(page).sort(), RUNTIMES.filter((r) => r !== 'docker').sort());
350
+ assert.doesNotMatch(HUB, /leave empty if there is none/);
351
+ });
352
+
304
353
  test('the empty command boxes read as examples, not as filled-in values', () => {
305
354
  assert.match(HUB, /id="f-renderBuild"[^>]*placeholder="for example: npm ci"/);
306
355
  assert.match(HUB, /id="f-renderStart"[^>]*placeholder="for example: npm start"/);
@@ -250,7 +250,8 @@ test('Connect sends the key once, to this project, forgets it, and the card show
250
250
  const sent = w.calls.filter((c) => c.url === RENDER);
251
251
  assert.equal(sent.length, 1);
252
252
  assert.equal(sent[0].method, 'POST');
253
- assert.deepEqual(sent[0].body, { key: KEY, owner_id: TEA_A, mode: 'greenfield', runtime: 'node', start_command: 'npm start', web_plan: 'starter', database: false });
253
+ // build_command: the runtime's usual one, prefilled — Render refuses a native app without one (task 1004464)
254
+ assert.deepEqual(sent[0].body, { key: KEY, owner_id: TEA_A, mode: 'greenfield', runtime: 'node', start_command: 'npm start', build_command: 'npm install', web_plan: 'starter', database: false });
254
255
  for (const c of w.calls) assert.ok(!c.url.includes(KEY), 'no URL carries the key');
255
256
  assert.equal(w.q('f-renderKey').value, '', 'the field is emptied');
256
257
  assert.equal(w.byId('mAppConnectForm').hidden, true, 'the form closes');
@@ -335,11 +335,32 @@ await ta('a link needs a repository to check the service against (1004350 review
335
335
  assert.equal(out.ok, false);
336
336
  assert.equal(h.cleared(), 1);
337
337
  });
338
- await ta('an app with no build step sends no build command at all (1004350 review)', async () => {
338
+ // The 1004350 review asked for NO build command here; Render refused exactly that live
339
+ // ("buildCommand is required for non-static, non-docker services", task 1004464), so a
340
+ // row saved without one — every row from before the fix included — gets the runtime's
341
+ // default, and a Retry of such a row now works.
342
+ await ta('an app saved with no build command gets its runtime\'s default, as Render requires (task 1004464)', async () => {
339
343
  const h = harness({ host: { buildCommand: undefined } });
340
344
  await h.run();
341
345
  const svc = h.r.calls.find((c) => c.method === 'POST' && c.path === '/services').body;
342
- assert.deepEqual(svc.serviceDetails.envSpecificDetails, { startCommand: 'npm start' });
346
+ assert.deepEqual(svc.serviceDetails.envSpecificDetails, { buildCommand: 'npm install', startCommand: 'npm start' });
347
+ });
348
+ await ta('every native runtime has a default build command; docker still sends none (task 1004464)', () => {
349
+ const { RUNTIMES, BUILD_DEFAULTS } = require('../modules/provisioning/render-standup.js');
350
+ const { buildFor } = require('../scripts/gds/render-payload.js');
351
+ for (const rt of RUNTIMES.filter((r) => r !== 'docker')) {
352
+ assert.ok(BUILD_DEFAULTS[rt], `${rt} has a default`);
353
+ assert.equal(buildFor({ runtime: rt, startCommand: 'x' }).buildCommand, BUILD_DEFAULTS[rt]);
354
+ assert.equal(buildFor({ runtime: rt, startCommand: 'x', buildCommand: 'make' }).buildCommand, 'make', 'the owner\'s own wins');
355
+ }
356
+ assert.deepEqual(buildFor({ runtime: 'docker' }), { runtime: 'docker' });
357
+ });
358
+ await ta('a refusal from Render reads as a sentence, with Render\'s own reason kept (task 1004464)', async () => {
359
+ const h = harness({ host: { database: false }, render: { 'POST /services': new Error('buildCommand is required for non-static, non-docker services.') } });
360
+ const out = await h.run();
361
+ assert.equal(out.ok, false);
362
+ assert.equal(h.lastHost().error, 'Render would not set up the app: buildCommand is required for non-static, non-docker services.');
363
+ assert.doesNotMatch(h.lastHost().error, /POST \/services/, 'no raw HTTP line reaches the owner');
343
364
  });
344
365
 
345
366
  console.log('\nteardown forgets, never deletes:');