Skip to content

Commit 959ad6f

Browse files
Copilotchrmarti
andcommitted
Make --workspace-folder optional in build and outdated commands
Co-authored-by: chrmarti <[email protected]>
1 parent 966e7f0 commit 959ad6f

File tree

1 file changed

+52
-6
lines changed

1 file changed

+52
-6
lines changed

src/spec-node/devContainersSpecCLI.ts

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ function buildOptions(y: Argv) {
507507
'user-data-folder': { type: 'string', description: 'Host path to a directory that is intended to be persisted and share state between sessions.' },
508508
'docker-path': { type: 'string', description: 'Docker CLI path.' },
509509
'docker-compose-path': { type: 'string', description: 'Docker Compose CLI path.' },
510-
'workspace-folder': { type: 'string', required: true, description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' },
510+
'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' },
511511
'config': { type: 'string', description: 'devcontainer.json path. The default is to use .devcontainer/devcontainer.json or, if that does not exist, .devcontainer.json in the workspace folder.' },
512512
'log-level': { choices: ['info' as 'info', 'debug' as 'debug', 'trace' as 'trace'], default: 'info' as 'info', description: 'Log level.' },
513513
'log-format': { choices: ['text' as 'text', 'json' as 'json'], default: 'text' as 'text', description: 'Log format.' },
@@ -526,7 +526,13 @@ function buildOptions(y: Argv) {
526526
'experimental-lockfile': { type: 'boolean', default: false, hidden: true, description: 'Write lockfile' },
527527
'experimental-frozen-lockfile': { type: 'boolean', default: false, hidden: true, description: 'Ensure lockfile remains unchanged' },
528528
'omit-syntax-directive': { type: 'boolean', default: false, hidden: true, description: 'Omit Dockerfile syntax directives' },
529-
});
529+
})
530+
.check(argv => {
531+
if (!argv['workspace-folder'] && !argv['config']) {
532+
throw new Error('Missing required argument: Either --workspace-folder or --config is required.');
533+
}
534+
return true;
535+
});
530536
}
531537

532538
type BuildArgs = UnpackArgv<ReturnType<typeof buildOptions>>;
@@ -574,7 +580,24 @@ async function doBuild({
574580
await Promise.all(disposables.map(d => d()));
575581
};
576582
try {
577-
const workspaceFolder = path.resolve(process.cwd(), workspaceFolderArg);
583+
// If workspace-folder is not provided, derive it from config path
584+
// When config is in .devcontainer/devcontainer.json, we need to go up two levels
585+
// When config is in devcontainer.json, we need to go up one level
586+
let workspaceFolder: string;
587+
if (workspaceFolderArg) {
588+
workspaceFolder = path.resolve(process.cwd(), workspaceFolderArg);
589+
} else if (configParam) {
590+
const resolvedConfig = path.resolve(process.cwd(), configParam);
591+
const configDir = path.dirname(resolvedConfig);
592+
// If config is in a .devcontainer directory, go up one more level
593+
if (path.basename(configDir) === '.devcontainer') {
594+
workspaceFolder = path.dirname(configDir);
595+
} else {
596+
workspaceFolder = configDir;
597+
}
598+
} else {
599+
workspaceFolder = process.cwd();
600+
}
578601
const configFile: URI | undefined = configParam ? URI.file(path.resolve(process.cwd(), configParam)) : undefined;
579602
const overrideConfigFile: URI | undefined = /* overrideConfig ? URI.file(path.resolve(process.cwd(), overrideConfig)) : */ undefined;
580603
const addCacheFroms = addCacheFrom ? (Array.isArray(addCacheFrom) ? addCacheFrom as string[] : [addCacheFrom]) : [];
@@ -1107,14 +1130,20 @@ async function readConfiguration({
11071130
function outdatedOptions(y: Argv) {
11081131
return y.options({
11091132
'user-data-folder': { type: 'string', description: 'Host path to a directory that is intended to be persisted and share state between sessions.' },
1110-
'workspace-folder': { type: 'string', required: true, description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' },
1133+
'workspace-folder': { type: 'string', description: 'Workspace folder path. The devcontainer.json will be looked up relative to this path.' },
11111134
'config': { type: 'string', description: 'devcontainer.json path. The default is to use .devcontainer/devcontainer.json or, if that does not exist, .devcontainer.json in the workspace folder.' },
11121135
'output-format': { choices: ['text' as 'text', 'json' as 'json'], default: 'text', description: 'Output format.' },
11131136
'log-level': { choices: ['info' as 'info', 'debug' as 'debug', 'trace' as 'trace'], default: 'info' as 'info', description: 'Log level for the --terminal-log-file. When set to trace, the log level for --log-file will also be set to trace.' },
11141137
'log-format': { choices: ['text' as 'text', 'json' as 'json'], default: 'text' as 'text', description: 'Log format.' },
11151138
'terminal-columns': { type: 'number', implies: ['terminal-rows'], description: 'Number of columns to render the output for. This is required for some of the subprocesses to correctly render their output.' },
11161139
'terminal-rows': { type: 'number', implies: ['terminal-columns'], description: 'Number of rows to render the output for. This is required for some of the subprocesses to correctly render their output.' },
1117-
});
1140+
})
1141+
.check(argv => {
1142+
if (!argv['workspace-folder'] && !argv['config']) {
1143+
throw new Error('Missing required argument: Either --workspace-folder or --config is required.');
1144+
}
1145+
return true;
1146+
});
11181147
}
11191148

11201149
type OutdatedArgs = UnpackArgv<ReturnType<typeof outdatedOptions>>;
@@ -1139,7 +1168,24 @@ async function outdated({
11391168
};
11401169
let output: Log | undefined;
11411170
try {
1142-
const workspaceFolder = path.resolve(process.cwd(), workspaceFolderArg);
1171+
// If workspace-folder is not provided, derive it from config path
1172+
// When config is in .devcontainer/devcontainer.json, we need to go up two levels
1173+
// When config is in devcontainer.json, we need to go up one level
1174+
let workspaceFolder: string;
1175+
if (workspaceFolderArg) {
1176+
workspaceFolder = path.resolve(process.cwd(), workspaceFolderArg);
1177+
} else if (configParam) {
1178+
const resolvedConfig = path.resolve(process.cwd(), configParam);
1179+
const configDir = path.dirname(resolvedConfig);
1180+
// If config is in a .devcontainer directory, go up one more level
1181+
if (path.basename(configDir) === '.devcontainer') {
1182+
workspaceFolder = path.dirname(configDir);
1183+
} else {
1184+
workspaceFolder = configDir;
1185+
}
1186+
} else {
1187+
workspaceFolder = process.cwd();
1188+
}
11431189
const configFile = configParam ? URI.file(path.resolve(process.cwd(), configParam)) : undefined;
11441190
const cliHost = await getCLIHost(workspaceFolder, loadNativeModule, logFormat === 'text');
11451191
const extensionPath = path.join(__dirname, '..', '..');

0 commit comments

Comments
 (0)