Skip to content

Commit 5ff3578

Browse files
SakshiKoli-CSclaude
andcommitted
refactor(config): keep only uid and organizationUid in .cs-launch.json
No resolution spec ever addressed `name`, so `ProjectConfigStore.load()` no longer keeps it and `projects:create` no longer writes it. The linked-folder refusal names the project by uid alone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent b2ebaad commit 5ff3578

8 files changed

Lines changed: 41 additions & 43 deletions

File tree

‎.talismanrc‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,20 @@ fileignoreconfig:
109109
- "what `limit` resolves to when nobody passed it"
110110
- "a test passes `var`, `env-file` and `from-stack`"
111111
- "an in-flight secret registry to redact against"
112+
- filename: src/core/project-config.ts
113+
allowed_patterns:
114+
- "ProjectConfigKey"
115+
- "DEFAULT_BLOCK_KEY"
116+
- filename: src/core/project-config.test.ts
117+
allowed_patterns:
118+
- "DEFAULT_BLOCK_KEY"
119+
- filename: src/core/resolve.test.ts
120+
allowed_patterns:
121+
- "Pass --org, set it in"
122+
- "config key and no prompt"
123+
- "an explicitly passed --skip 0"
124+
- "when the flag was not passed"
125+
- "Object\\.keys\\(resolved\\)"
126+
- "when every rule passes"
127+
- "Pass exactly one of"
112128
version: "1.0"

‎AGENTS.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,8 @@ that way, keeping the field name; without it every validation 400 read "request
417417
**The `.cs-launch.json` file.** `ProjectConfigStore` owns it. `load()` returns a typed
418418
`ProjectConfig`, applying the v1 rule that several branch blocks are usable only when they
419419
agree on one project. A resolution spec addresses it by a key of `ProjectConfig`, never a
420-
dotted string. `load()` keeps only the three keys a resolution spec can read - `uid`,
421-
`organizationUid` and `name` - and only when each is a string or `null`, so a number in the file is
420+
dotted string. `load()` keeps only the two keys a resolution spec can read - `uid`
421+
and `organizationUid` - and only when each is a string or `null`, so a number in the file is
422422
absent rather than a value that reaches a request header. `save()` has one caller, `projects:create`,
423423
which records the project it created. It merges into every branch block, keeping every other key it
424424
finds there; it writes a single `project` block into a missing file or one holding an empty object; it refuses, as a

‎src/core/project-config.test.ts‎

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,9 @@ describe('ProjectConfigStore.save', () => {
281281
it('merges into the sole existing block rather than replacing it', () => {
282282
const store = storeFor({ main: { uid: 'p1', organizationUid: 'org1', name: 'old-name' } });
283283

284-
store.save({ name: 'new-name' });
284+
store.save({ organizationUid: 'org2' });
285285

286-
expect(fileAt(store)).toEqual({ main: { uid: 'p1', organizationUid: 'org1', name: 'new-name' } });
286+
expect(fileAt(store)).toEqual({ main: { uid: 'p1', organizationUid: 'org2', name: 'old-name' } });
287287
});
288288

289289
it('updates every branch block and discards none of them', () => {
@@ -302,14 +302,14 @@ describe('ProjectConfigStore.save', () => {
302302
},
303303
});
304304

305-
store.save({ uid: 'p1', name: 'renamed' });
305+
store.save({ uid: 'p1', organizationUid: 'org2' });
306306

307307
expect(fileAt(store)).toEqual({
308-
main: { uid: 'p1', organizationUid: 'org1', name: 'renamed', environments: [{ uid: 'e1', name: 'Default' }] },
308+
main: { uid: 'p1', organizationUid: 'org2', name: 'my-site', environments: [{ uid: 'e1', name: 'Default' }] },
309309
'feature/checkout': {
310310
uid: 'p1',
311-
organizationUid: 'org1',
312-
name: 'renamed',
311+
organizationUid: 'org2',
312+
name: 'my-site',
313313
environments: [{ uid: 'e2', name: 'Preview' }],
314314
},
315315
});
@@ -321,8 +321,8 @@ describe('ProjectConfigStore.save', () => {
321321
staging: { uid: 'p2', organizationUid: 'org1' },
322322
});
323323

324-
expect(() => store.save({ name: 'renamed' })).toThrow(UsageError);
325-
expect(() => store.save({ name: 'renamed' })).toThrow('main, staging');
324+
expect(() => store.save({ organizationUid: 'org2' })).toThrow(UsageError);
325+
expect(() => store.save({ organizationUid: 'org2' })).toThrow('main, staging');
326326
expect(fileAt(store)).toEqual({
327327
main: { uid: 'p1', organizationUid: 'org1' },
328328
staging: { uid: 'p2', organizationUid: 'org1' },
@@ -332,8 +332,8 @@ describe('ProjectConfigStore.save', () => {
332332
it('refuses to write when the sole existing block names a different project', () => {
333333
const store = storeFor({ main: { uid: 'p1', organizationUid: 'org1', name: 'other-site' } });
334334

335-
expect(() => store.save({ uid: 'p2', organizationUid: 'org1', name: 'my-site' })).toThrow(UsageError);
336-
expect(() => store.save({ uid: 'p2', organizationUid: 'org1', name: 'my-site' })).toThrow(
335+
expect(() => store.save({ uid: 'p2', organizationUid: 'org1' })).toThrow(UsageError);
336+
expect(() => store.save({ uid: 'p2', organizationUid: 'org1' })).toThrow(
337337
`The config file at '${store.path}' already names project p1. Delete it or pass --config with another path.`,
338338
);
339339
expect(fileAt(store)).toEqual({ main: { uid: 'p1', organizationUid: 'org1', name: 'other-site' } });
@@ -370,7 +370,7 @@ describe('ProjectConfigStore.save', () => {
370370

371371
it('writes the file so that a later load reads back exactly what was saved', () => {
372372
const store = new ProjectConfigStore(tempPath());
373-
const config = { uid: 'p1', organizationUid: 'org1', name: 'my-site' };
373+
const config = { uid: 'p1', organizationUid: 'org1' };
374374

375375
store.save(config);
376376

@@ -389,11 +389,11 @@ describe('ProjectConfigStore.save', () => {
389389
const store = new ProjectConfigStore(path);
390390

391391
const loaded = store.load();
392-
store.save({ name: 'renamed' });
392+
store.save({ organizationUid: 'org1' });
393393

394-
expect(loaded).toEqual({ organizationUid: null, name: 'my-site' });
394+
expect(loaded).toEqual({ organizationUid: null });
395395
expect(JSON.parse(readFileSync(path, 'utf8'))).toEqual({
396-
project: { uid: 42, organizationUid: null, name: 'renamed', environments: [{ uid: 'e1' }], extra: true },
396+
project: { uid: 42, organizationUid: 'org1', name: 'my-site', environments: [{ uid: 'e1' }], extra: true },
397397
});
398398
});
399399

‎src/core/project-config.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { UsageError } from './errors';
55
export interface ProjectConfig {
66
uid?: string | null;
77
organizationUid?: string | null;
8-
name?: string | null;
98
}
109

1110
export type ProjectConfigKey = keyof ProjectConfig;
@@ -41,7 +40,7 @@ function textOrNull(value: unknown): string | null | undefined {
4140
function configFrom(block: Record<string, unknown>): ProjectConfig {
4241
const config: ProjectConfig = {};
4342

44-
for (const field of ['uid', 'organizationUid', 'name'] as const) {
43+
for (const field of ['uid', 'organizationUid'] as const) {
4544
const value = textOrNull(block[field]);
4645

4746
if (value !== undefined) {

‎src/core/resolve.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ describe('resolveInputs', () => {
114114
it('advises only the remedies that exist for a required input with no config key and no prompt', async () => {
115115
const promise = resolveInputs(inputs({ name: { required: true } }), {
116116
parsed: {},
117-
projectConfig: { name: 'from-config' },
117+
projectConfig: { organizationUid: 'from-config' },
118118
services: services({ isTTY: true }),
119119
});
120120

‎src/projects/project.create.test.ts‎

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,7 @@ describe('ProjectCreator writing the project config', () => {
10771077
await creator.create(gitRequest({ configPath: configPathIn(dataDir) }));
10781078

10791079
expect(configFileIn(dataDir)).toEqual({
1080-
project: { uid: PROJECT_UID, organizationUid: ORG, name: 'My Site' },
1080+
project: { uid: PROJECT_UID, organizationUid: ORG },
10811081
});
10821082
});
10831083

@@ -1087,7 +1087,7 @@ describe('ProjectCreator writing the project config', () => {
10871087
await creator.create(uploadRequest({ configPath: configPathIn(dataDir) }));
10881088

10891089
expect(configFileIn(dataDir)).toEqual({
1090-
project: { uid: PROJECT_UID, organizationUid: ORG, name: 'My Site' },
1090+
project: { uid: PROJECT_UID, organizationUid: ORG },
10911091
});
10921092
});
10931093

@@ -1107,43 +1107,32 @@ describe('ProjectCreator writing the project config', () => {
11071107
main: {
11081108
uid: PROJECT_UID,
11091109
organizationUid: ORG,
1110-
name: 'My Site',
11111110
environments: [{ uid: 'e1', name: 'Default' }],
11121111
},
11131112
'feature/checkout': {
11141113
uid: PROJECT_UID,
11151114
organizationUid: ORG,
1116-
name: 'My Site',
11171115
environments: [{ uid: 'e2', name: 'Preview' }],
11181116
},
11191117
});
11201118
});
11211119

11221120
it('refuses before creating anything, and leaves the file alone, when the folder already names a project', async () => {
1123-
const existing = { project: { uid: 'other-project', organizationUid: ORG, name: 'Other Site' } };
1121+
const existing = { project: { uid: 'other-project', organizationUid: ORG } };
11241122
writeFileSync(configPathIn(dataDir), JSON.stringify(existing));
11251123
const { creator, created } = harness();
11261124

11271125
const failure = await creator.create(gitRequest({ configPath: configPathIn(dataDir) })).catch((error: Error) => error);
11281126

11291127
expect(failure).toBeInstanceOf(UsageError);
11301128
expect((failure as UsageError).message).toBe(
1131-
`This folder is already linked to the project "Other Site" (other-project) in ${configPathIn(dataDir)}. ` +
1129+
`This folder is already linked to the project other-project in ${configPathIn(dataDir)}. ` +
11321130
'To create a new project, remove that file or pass --config with a different path.',
11331131
);
11341132
expect(created).toEqual([]);
11351133
expect(configFileIn(dataDir)).toEqual(existing);
11361134
});
11371135

1138-
it('names a linked project by uid alone when the file carries no name', async () => {
1139-
writeFileSync(configPathIn(dataDir), JSON.stringify({ project: { uid: 'other-project' } }));
1140-
const { creator } = harness();
1141-
1142-
const failure = await creator.create(gitRequest({ configPath: configPathIn(dataDir) })).catch((error: Error) => error);
1143-
1144-
expect((failure as UsageError).message).toContain('already linked to the project other-project in');
1145-
});
1146-
11471136
it('reports the miss and leaves an unparseable config file untouched, still completing the create', async () => {
11481137
writeFileSync(configPathIn(dataDir), '{ not json ');
11491138
const { creator, printed, created } = harness();

‎src/projects/project.create.ts‎

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,6 @@ export class ProjectCreator {
183183

184184
const config: ProjectConfig = { uid: project.uid, organizationUid: request.org };
185185

186-
if (project.name !== undefined) {
187-
config.name = project.name;
188-
}
189-
190186
try {
191187
new ProjectConfigStore(path).save(config);
192188
} catch (error) {
@@ -494,10 +490,8 @@ export class ProjectCreator {
494490
return;
495491
}
496492

497-
const named = linked.name ? `"${linked.name}" (${linked.uid})` : `${linked.uid}`;
498-
499493
throw new UsageError(
500-
`This folder is already linked to the project ${named} in ${request.configPath}. ` +
494+
`This folder is already linked to the project ${linked.uid} in ${request.configPath}. ` +
501495
'To create a new project, remove that file or pass --config with a different path.',
502496
);
503497
}

‎test/integration/projects-create.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,7 @@ describe('integration: launch:projects:create on the wire', () => {
892892
const other = randomBytes(12).toString('hex');
893893
writeFileSync(
894894
join(dataDir, '.cs-launch.json'),
895-
JSON.stringify({ project: { uid: other, organizationUid: ORG_UID, name: 'Existing Site' } }),
895+
JSON.stringify({ project: { uid: other, organizationUid: ORG_UID } }),
896896
);
897897
const signed = hub().get('/manage/projects/upload/signed_url').query({}).reply(200, awsSignedUpload());
898898
const create = captureCreate();
@@ -917,7 +917,7 @@ describe('integration: launch:projects:create on the wire', () => {
917917
);
918918

919919
expect(error?.oclif?.exit).toBe(2);
920-
expect(error?.message).toContain(`already linked to the project "Existing Site" (${other})`);
920+
expect(error?.message).toContain(`already linked to the project ${other}`);
921921
expect(error?.message).toContain('--config');
922922
expect(signed.isDone()).toBe(false);
923923
expect(create.scope.isDone()).toBe(false);

0 commit comments

Comments
 (0)