Skip to content

Commit bb562b6

Browse files
SakshiKoli-CSclaude
andcommitted
fix(environments): one error line per failure, an org check on the project, and FileUpload-aware env output
Errors - Every failure prints a single `error: <message>` line on stderr, as V1 did, instead of oclif's `Error:` block (src/core/failure.ts). LaunchCommand, RetiredCommand and launch:functions:serve report through it, and bin/run.js and bin/dev.js use it as the last handler so an unknown command gets the same line. - reportFailure passes through an error it has already printed (skipOclifErrorHandling), so the bin's handler no longer prints the line a second time. The manual harness caught the doubled line on every failing env command. - eslint.config.mjs gives bin/*.js its Node globals, and the bins drop disable comments for rules this config does not load, so lint-staged accepts them. Project resolution - A project uid given by flag or config must belong to the selected organization; otherwise the command stops with `No project with UID "…" found in this organization.` A project picked at the prompt is already scoped, so it is not looked up again. Environment and project output - launch:env:list drops the BRANCH column, and launch:env:get the BRANCH row, for a FileUpload project (the project is fetched alongside the environment). - env:get rows read ORGANIZATION UID and PROJECT UID; the project list column reads PROJECT UID. - The env:update checklist asks "Select the fields to update:", and "No records found." is yellow on a terminal. Tests - test/support/project-ownership.ts mocks the ownership lookup; the integration suites, including the variables suites merged from v2-dev, register it. - .talismanrc checksums refreshed for the test files Talisman flags by name (`env`) or by the word "pass" in a test title; none holds a credential. #claude_code# 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent d5f8730 commit bb562b6

45 files changed

Lines changed: 625 additions & 147 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.talismanrc‎

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
fileignoreconfig:
2+
- filename: src/core/failure.test.ts
3+
checksum: 242d43885d1e3ef46edf1e347899a90549ebf95ec6827426746852f7a6dfc418
24
- filename: test/integration/retired-commands.test.ts
3-
checksum: 15a04dda74e97d9d1d89340b7a90744fb239d9de1fbada08e7d42cfe12cc649e
5+
checksum: 3d2af18ef58effd89e398e6cb523f6680d7385eb34cc660f559be59114ab07f7
46
- filename: test/integration/manage-base-path.test.ts
57
checksum: 79eb9ad420ea47d5bee8045d812c45b010bbeeafcaf5f85d11653140b0cd0db1
68
- filename: src/transport/rest-client.ts
@@ -142,19 +144,19 @@ fileignoreconfig:
142144
ignore_detectors:
143145
- filename
144146
- filename: src/environments/environment.update.prompt.ts
145-
checksum: 12b8bfafabc5f5174559179b2e926131fb04793685c9bba63cf0620d1cb089a2
147+
checksum: 5f2da5ae044eda92f9e555c997d23f065b5454cda8a340c12595597795ecfbb9
146148
ignore_detectors:
147149
- filename
148150
- filename: src/environments/environment.update.prompt.test.ts
149-
checksum: 5b77e234c5c331d52268157241802345006d59b3af4c0476faa9b28825a61d71
151+
checksum: b83aa8da125e6a45b455cf03c50fe82cc41890343be9be0c2ccf0a01d09bd160
150152
ignore_detectors:
151153
- filename
152154
- filename: src/environments/environment.presenter.ts
153-
checksum: e980e4fc6dcbe721a2a6d4c826273a3a4ddaa9a4e52a6661ee1b599a9ea9c783
155+
checksum: 3ca3e566a136fb0100cd794e9e973fce70a80c88224b6f93027d8190ac0b6882
154156
ignore_detectors:
155157
- filename
156158
- filename: src/environments/environment.presenter.test.ts
157-
checksum: 3f497b982f33c721c988c8245f91534fd50b42317c84cc8f1de784da2d13c942
159+
checksum: f5f6e5ec469a16ca8e1c9175ec88aa406a24c6318ef95520d534c444fdd3ab88
158160
ignore_detectors:
159161
- filename
160162
- filename: src/environments/environment.resolver.ts
@@ -203,7 +205,7 @@ fileignoreconfig:
203205
ignore_detectors:
204206
- filename
205207
- filename: test/integration/environments-list-command.test.ts
206-
checksum: 87bf2ec14656f2bcf1bf97dd1ea248c8be087e0f15bf1b7e212c8308d8f13cfe
208+
checksum: 34abcf0f1ab6de513fde298243dfe1e6cf2859e29c72ade7ae9f2418de7db074
207209
- filename: test/fixtures/projects-list.json
208210
allowed_patterns:
209211
- "a1b2c3d4e5f60718293a4b5c"
@@ -251,7 +253,7 @@ fileignoreconfig:
251253
- filename: src/core/resolve.ts
252254
checksum: 5d150db11cb1c976911ecba61336118ddad051942da95f809695c0cf0346de42
253255
- filename: src/core/resolve.test.ts
254-
checksum: 42dfd6c77b752f64f6a927845a2d98626934ba0952c911cf701b3df502cb0443
256+
checksum: b62b53aedd32ab01212508d79bf4c306dfbd9b0c9788e5fc449886eb770e0377
255257
- filename: src/git/local-repository.ts
256258
checksum: b9091ed135dfebdf590a5412bbfba2b2f96d4a177cf944a20909b93405268e9f
257259
- filename: test/guards/layering.test.ts
@@ -261,25 +263,25 @@ fileignoreconfig:
261263
- filename: src/commands/launch/projects/delete.test.ts
262264
checksum: d67d8a804d14f9b809153d10b0cf9d5e52b70f9e0b65678ebb1663a7d2da22c1
263265
- filename: test/integration/projects-delete.test.ts
264-
checksum: 78baba5c3e3aa5fd054dec04cee0e72a99e85cb8451b2346f53761f099509191
266+
checksum: bb59cbe1f4f7b3ac6aa7944067c1ab1214a7c5d20229e9d6577cdaabec56622e
265267
- filename: src/core/launch-command.ts
266-
checksum: 85f86ea5f075bfdd78652dc05d20a2f6e89348466c6bd41ae903bcb1b7e8515d
268+
checksum: b14abd1d4cee3e93c4458ab3567b1f252564bda7e12773cfa1e0d678967664a4
267269
- filename: src/core/launch-command.test.ts
268-
checksum: 930069aeddbf4348097f33713e4e57bd26cb3be1b7970eb60142bb53010e1674
270+
checksum: c4d3785f25377db560bb9daa5d0d080a0131056e6c31e3b45cb4f1f2347305ed
269271
- filename: src/projects/project.inputs.test.ts
270272
checksum: eaecaba9eddec128186871550fc5802166fa56a2e89741d72ac955fcfe2f952f
271273
- filename: test/integration/projects-command-flows.test.ts
272-
checksum: 786a470c71f3a7ce58463d0a08e9bf47e36f936c55a2ca7cd8ef2a058ac01e4a
274+
checksum: 30858bb3dff8a22406efa4ac82d882d1501797cdda24e09555aecc0bcd8dca0b
273275
- filename: src/core/redact.test.ts
274276
checksum: ae497ea8c3a4c3f12217d2c4c028a1da3d1324e18362d68c355ba8120902bcb3
275277
- filename: src/commands/launch/projects/list.test.ts
276-
checksum: d01cbf70469a771425c8258fb1800b70a6ff0b807208fc76f3060b69a2cc05bc
278+
checksum: ba1876fb42b3031d3ae2eb23fd6591f827d3dc552a5edba765a7983c11fb3ba8
277279
- filename: src/commands/launch/env/list.test.ts
278-
checksum: 36040ec3ae8230a0b9ae28e1609fd9edf5965b9d472bd2f9a1808c060ea897bf
280+
checksum: f5de16e0fd37a85ceb31a7fbc8e9058347ccc072f53c95d4f49ef15e923f4e95
279281
- filename: src/commands/launch/projects/get.test.ts
280-
checksum: fec6ca189bc3d93aefa109ee6405f8ce74a42318aa18a99045d40c3cb5561b9e
282+
checksum: ecc48c6a381de44858eafa57fdfd8c24c087837d2869909c8326db8e77148f29
281283
- filename: test/integration/projects-list-command.test.ts
282-
checksum: d9617f9ae02a32a03d707be711f495350e4a442e61ac1a3514b8bc9190e9e6a2
284+
checksum: 5f49dc7f384fb9e33c8fb91f1c67b17ffc2fd3bbde3663f90f820056407e98a0
283285
- filename: src/functions/cloud-functions.test.ts
284286
checksum: 5faf7b0497efc9f620983220019736a0ec2e8c43f22316f888201912bbcb5d62
285287
- filename: src/transport/proxy.test.ts
@@ -309,9 +311,9 @@ fileignoreconfig:
309311
- filename: src/environments/types.ts
310312
checksum: 9c2387b8bd819af79b256f8638d54a33c41bccc1ab04f02dfd1cda48c035e6e7
311313
- filename: src/commands/launch/env/get.test.ts
312-
checksum: 26863ca165d20df9a9ea8afac1eda71c9f96be15fe23bddb4fd7ddfcefab2256
314+
checksum: 6e739ef12bc0d887e652e23ee54d9fac30dd6a3505cf464cdaefc0e696cf3e51
313315
- filename: test/integration/environments-delete.test.ts
314-
checksum: 7df1b364699953cad40b7aea3beeaaae6e2b781c72b7ae8f36ea78ebf50887e1
316+
checksum: a6f002256162971e5f4ace879153454c95199101d2acc853f2a54b39d1d48914
315317
- filename: src/commands/launch/env/delete.test.ts
316318
checksum: 14116789eb5f9000f2a7d6f569cd15aabf2a1ec3b127ed4427afe13fd515f0c2
317319
- filename: src/environments/environment.errors.ts
@@ -321,7 +323,7 @@ fileignoreconfig:
321323
- filename: src/core/prompt.test.ts
322324
checksum: 2deacd402e8d7f79458ee6d3917205c6dbe5cd7e6a98a2e5c49e37ac1a9d57c4
323325
- filename: test/integration/environments-update.test.ts
324-
checksum: 650627ec2cc6fed82f000b0e0a368a3725238dbb64ec18a5866c2fa3abb3a27f
326+
checksum: 4cd0106bf1881d373d23f063314627057d85cab0c43b793f013ffc2ca8859036
325327
- filename: src/commands/launch/env/update.ts
326328
checksum: 06add97108d878eaf415e32160439b8963683e63a0e37ce77a56f66b88383c7a
327329
- filename: src/commands/launch/env/update.test.ts
@@ -333,7 +335,7 @@ fileignoreconfig:
333335
- "-p, --password"
334336
- "-u, --username"
335337
- filename: test/integration/environments-create.test.ts
336-
checksum: 9582c39ab4732e6ab530c6d80b5a08bac28920ba5421608527479fe5bace8c95
338+
checksum: 09269b51e499870a04bccfa5e0f2febd287f5246f176c258347c884240678289
337339
- filename: src/commands/launch/variables/list.test.ts
338340
checksum: 87ccb762f4931326fc50bca1c01aeb4a16d552e4dfa141d00370fdfafa4363be
339341
- filename: src/commands/launch/variables/set.test.ts
@@ -411,7 +413,7 @@ fileignoreconfig:
411413
- filename: test/fixtures/cms-environment-variables.json
412414
checksum: cb2cbb2d1d858b1cf2115c218a0ecca04ce04baf054fe11fe6123589c1afce27
413415
- filename: test/integration/variables-list.test.ts
414-
checksum: c0ccb0bc394c9013f75b69c4354cc5d52b2f6916a35af902de36bef1cda1648c
416+
checksum: 5ea6a0fcd331496ca19773e90dab81e09eb5290b8f22a1880bffc7916f3778b2
415417
- filename: test/integration/variables-set-unset.test.ts
416-
checksum: 25ae86d9a50f02c5209823a91ae0ff1433e5a0bff3cfa27f84e48274d0e9c571
418+
checksum: cafc4eed5f80fe9763cb9362acce9ba51663da7a615abf683b23545f5342b913
417419
version: "1.0"

‎bin/dev.js‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
#!/usr/bin/env node_modules/.bin/ts-node
2-
// eslint-disable-next-line node/shebang, unicorn/prefer-top-level-await
32
(async () => {
43
const oclif = await import('@oclif/core');
5-
await oclif.execute({ development: true, dir: __dirname });
6-
})();
4+
const { handleStandaloneFailure } = require('../dist/core/failure');
5+
6+
process.env.NODE_ENV = 'development';
7+
oclif.settings.debug = true;
8+
9+
await oclif
10+
.run(process.argv.slice(2), __dirname)
11+
.then(() => oclif.flush())
12+
.catch(handleStandaloneFailure);
13+
})();

‎bin/run.js‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
#!/usr/bin/env node
22

3-
// eslint-disable-next-line unicorn/prefer-top-level-await
43
(async () => {
54
const oclif = await import('@oclif/core');
6-
await oclif.execute({ development: false, dir: __dirname });
7-
})();
5+
const { handleStandaloneFailure } = require('../dist/core/failure');
6+
7+
await oclif
8+
.run(process.argv.slice(2), __dirname)
9+
.then(() => oclif.flush())
10+
.catch(handleStandaloneFailure);
11+
})();

‎eslint.config.mjs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ export default tseslint.config(
1515
},
1616
},
1717
},
18+
{
19+
files: ['bin/**/*.js'],
20+
languageOptions: {
21+
globals: {
22+
require: 'readonly',
23+
process: 'readonly',
24+
__dirname: 'readonly',
25+
},
26+
},
27+
rules: {
28+
'@typescript-eslint/no-require-imports': 'off',
29+
},
30+
},
1831
{
1932
files: ['src/**/*.{js,ts}', 'test/**/*.{js,ts}'],
2033
rules: {

‎src/commands/launch/env/get.test.ts‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { UxLike } from '../../../core/prompt';
22
import EnvironmentsGet from './get';
33

4-
function commandUnderTest(answer: () => Promise<unknown>) {
4+
function commandUnderTest(answer: () => Promise<unknown>, projectType = 'GITPROVIDER') {
55
const lines: string[] = [];
66
const ux: UxLike = {
77
print: (message: string) => {
@@ -20,6 +20,7 @@ function commandUnderTest(answer: () => Promise<unknown>) {
2020
isTTY: false,
2121
outputIsTTY: false,
2222
api: {
23+
projects: { get: async () => ({ uid: 'proj1', projectType }) },
2324
environments: {
2425
get: async (params: unknown) => {
2526
fetched.push(params);
@@ -41,11 +42,20 @@ describe('launch:env:get', () => {
4142

4243
expect(fetched).toEqual([{ org: 'org1', project: 'proj1', environment: 'env1' }]);
4344
expect(lines[1]).toBe('│ FIELD │ VALUE │');
44-
expect(lines[3]).toBe('│ UID │ env1 │');
45+
expect(lines[3]).toBe('│ ENVIRONMENT UID │ env1 │');
4546
expect(lines[5]).toBe('│ NAME │ production │');
4647
expect(lines).toHaveLength(39);
4748
});
4849

50+
it('leaves the branch row out for an environment of a file-upload project', async () => {
51+
const { command, lines } = commandUnderTest(async () => ({ uid: 'env1', gitBranch: 'main' }), 'FILEUPLOAD');
52+
53+
await command.run();
54+
55+
expect(lines.some((line) => line.includes('BRANCH'))).toBe(false);
56+
expect(lines).toHaveLength(37);
57+
});
58+
4959
it('propagates a failed lookup', async () => {
5060
const failure = new Error('not found');
5161
const { command, lines } = commandUnderTest(async () => Promise.reject(failure));

‎src/commands/launch/env/get.ts‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ export default class EnvironmentsGet extends LaunchCommand<typeof getInputs> {
1919
static flags = flagsFor(getInputs);
2020

2121
async run(): Promise<void> {
22-
const environment = await this.services.api.environments.get({
23-
org: this.resolved.org,
24-
project: this.resolved.project,
25-
environment: this.resolved.env,
26-
});
22+
const scope = { org: this.resolved.org, project: this.resolved.project };
23+
const [environment, project] = await Promise.all([
24+
this.services.api.environments.get({ ...scope, environment: this.resolved.env }),
25+
this.services.api.projects.get(scope),
26+
]);
2727

2828
renderTable(
2929
this.ux,
3030
ENVIRONMENT_DETAIL_COLUMNS,
31-
environmentDetailRows({ ...environment, project: this.resolved.project }),
31+
environmentDetailRows({ ...environment, project: this.resolved.project }, project.projectType !== 'FILEUPLOAD'),
3232
process.stdout.columns,
3333
ENVIRONMENT_VALUE_COLUMN,
3434
this.services.outputIsTTY,

‎src/commands/launch/env/list.test.ts‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ function commandUnderTest(
55
page: unknown,
66
resolved: Record<string, unknown> = { org: 'org1', project: 'proj1', limit: 50, skip: 0 },
77
answer: () => Promise<unknown> = async () => page,
8+
projectType = 'GITPROVIDER',
89
) {
910
const lines: string[] = [];
1011
const ux: UxLike = {
@@ -23,6 +24,7 @@ function commandUnderTest(
2324
ux,
2425
isTTY: false,
2526
api: {
27+
projects: { get: async () => ({ uid: 'proj1', projectType }) },
2628
environments: {
2729
list: async (params: unknown) => {
2830
listed.push(params);
@@ -54,11 +56,23 @@ describe('launch:env:list', () => {
5456
await command.run();
5557

5658
expect(listed).toEqual([{ org: 'org1', project: 'proj1', limit: 50, skip: 0 }]);
57-
expect(lines[1]).toBe('│ NAME │ BRANCH │ URL │ UPDATED │ UID │');
58-
expect(lines[3]).toBe('│ production │ main │ https://a.io │ 2026-09-01 │ e1 │');
59+
expect(lines[1]).toBe('│ NAME │ BRANCH │ URL │ UPDATED │ ENVIRONMENT UID │');
60+
expect(lines[3]).toBe('│ production │ main │ https://a.io │ 2026-09-01 │ e1 │');
5961
expect(lines[5]).toBe('Showing 1-1 of 1');
6062
});
6163

64+
it('leaves the branch column out for a file-upload project', async () => {
65+
const page = {
66+
pagination: { count: 1, limit: 50, skip: 0 },
67+
environments: [{ uid: 'e1', name: 'production', domains: [{ url: 'https://a.io' }], updatedAt: '2026-09-01' }],
68+
};
69+
const { command, lines } = commandUnderTest(page, undefined, undefined, 'FILEUPLOAD');
70+
71+
await command.run();
72+
73+
expect(lines[1]).toBe('│ NAME │ URL │ UPDATED │ ENVIRONMENT UID │');
74+
});
75+
6276
it('shows the first domain that carries a url, skipping domains without one', async () => {
6377
const { command, lines } = commandUnderTest({
6478
pagination: { count: 1, limit: 50, skip: 0 },
@@ -75,7 +89,7 @@ describe('launch:env:list', () => {
7589

7690
await command.run();
7791

78-
expect(lines[3]).toBe('│ production │ main │ https://a.io │ 2026-09-01 │ e1 │');
92+
expect(lines[3]).toBe('│ production │ main │ https://a.io │ 2026-09-01 │ e1 │');
7993
});
8094

8195
it.each([[{}], [{ domains: [] }], [{ domains: [{ url: ' ' }] }]])(
@@ -85,8 +99,8 @@ describe('launch:env:list', () => {
8599

86100
await command.run();
87101

88-
expect(lines[1]).toBe('│ NAME │ BRANCH │ URL │ UPDATED │ UID │');
89-
expect(lines[3]).toBe('│ - │ - │ - │ - │ - │');
102+
expect(lines[1]).toBe('│ NAME │ BRANCH │ URL │ UPDATED │ ENVIRONMENT UID │');
103+
expect(lines[3]).toBe('│ - │ - │ - │ - │ - │');
90104
},
91105
);
92106

‎src/commands/launch/env/list.ts‎

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { LaunchCommand } from '../../../core/launch-command';
22
import { flagsFor, inputs } from '../../../core/inputs';
33
import { renderPagination, renderTable } from '../../../core/render';
4-
import { ENVIRONMENT_COLUMNS } from '../../../environments/environment.presenter';
4+
import { environmentColumns } from '../../../environments/environment.presenter';
55

66
const listInputs = inputs({ org: { required: true }, project: { required: true }, limit: {}, skip: {} });
77

@@ -15,16 +15,15 @@ export default class EnvironmentsList extends LaunchCommand<typeof listInputs> {
1515
static flags = flagsFor(listInputs);
1616

1717
async run(): Promise<void> {
18-
const page = await this.services.api.environments.list({
19-
org: this.resolved.org,
20-
project: this.resolved.project,
21-
limit: this.resolved.limit,
22-
skip: this.resolved.skip,
23-
});
18+
const scope = { org: this.resolved.org, project: this.resolved.project };
19+
const [page, project] = await Promise.all([
20+
this.services.api.environments.list({ ...scope, limit: this.resolved.limit, skip: this.resolved.skip }),
21+
this.services.api.projects.get(scope),
22+
]);
2423

2524
renderTable(
2625
this.ux,
27-
ENVIRONMENT_COLUMNS,
26+
environmentColumns(project.projectType !== 'FILEUPLOAD'),
2827
page.environments,
2928
process.stdout.columns,
3029
undefined,

‎src/commands/launch/functions/serve.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Command } from '@oclif/core';
22

33
import { EXIT_RUNTIME, EXIT_USAGE } from '../../../core/constants';
4+
import { failureOutputOf, reportFailure } from '../../../core/failure';
45
import Contentfly from '../../../functions';
56
import { PortInUseError } from '../../../functions/function.errors';
67
import { isValidPort, serveFlags } from '../../../functions/function.inputs';
@@ -50,4 +51,8 @@ export default class Functions extends Command {
5051
throw error;
5152
}
5253
}
54+
55+
protected async catch(err: Error): Promise<unknown> {
56+
return reportFailure(err, failureOutputOf((original) => super.catch(original)));
57+
}
5358
}

0 commit comments

Comments
 (0)