Skip to content

Commit bd19f61

Browse files
authored
Remove vscode-dev-containers dependency (#682)
1 parent 53ee019 commit bd19f61

File tree

16 files changed

+20
-323
lines changed

16 files changed

+20
-323
lines changed

CONTRIBUTING.md

-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ The specification repo uses the following [labels](https://github.com/microsoft/
2929

3030
- Create a PR:
3131
- Updating the package version in the `package.json`.
32-
- Updating the `vscode-dev-containers` version in the `package.json`'s dependencies (if there is an update).
33-
- Run `yarn` to update `yarn.lock`.
3432
- List notable changes in the `CHANGELOG.md`.
3533
- Update ThirdPartyNotices.txt with any new dependencies.
3634
- After the PR is merged to `main` wait for the CI workflow to succeed (this builds the artifact that will be published). (TBD: Let the `publish-dev-containers` workflow wait for the CI workflow.)

esbuild.js

-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ const watch = process.argv.indexOf('--watch') !== -1;
6565
minify,
6666
platform: 'node',
6767
target: 'node14.17.0',
68-
external: ['vscode-dev-containers'],
6968
mainFields: ['module', 'main'],
7069
outdir: 'dist',
7170
plugins: [plugin],

package.json

+3-8
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
"node": "^16.13.0 || >=18.0.0"
1919
},
2020
"scripts": {
21-
"compile": "npm-run-all clean-dist definitions compile-dev",
22-
"watch": "npm-run-all clean-dist definitions compile-watch",
23-
"package": "npm-run-all clean-dist definitions compile-prod store-packagejson patch-packagejson npm-pack restore-packagejson",
21+
"compile": "npm-run-all clean-dist compile-dev",
22+
"watch": "npm-run-all clean-dist compile-watch",
23+
"package": "npm-run-all clean-dist compile-prod store-packagejson patch-packagejson npm-pack restore-packagejson",
2424
"store-packagejson": "copyfiles package.json build-tmp/",
2525
"patch-packagejson": "node build/patch-packagejson.js",
2626
"restore-packagejson": "copyfiles --up 1 build-tmp/package.json .",
@@ -32,10 +32,7 @@
3232
"tsc-b": "tsc -b",
3333
"tsc-b-w": "tsc -b -w",
3434
"precommit": "node build/hygiene.js",
35-
"definitions": "npm-run-all definitions-clean definitions-copy",
3635
"lint": "eslint -c .eslintrc.js --rulesdir ./build/eslint --max-warnings 0 --ext .ts ./src",
37-
"definitions-clean": "rimraf dist/node_modules/vscode-dev-containers",
38-
"definitions-copy": "copyfiles \"node_modules/vscode-dev-containers/container-features/{devcontainer-features.json,feature-scripts.env,fish-debian.sh,homebrew-debian.sh,install.sh}\" dist",
3936
"npm-pack": "npm pack",
4037
"clean": "npm-run-all clean-dist clean-built",
4138
"clean-dist": "rimraf dist",
@@ -52,7 +49,6 @@
5249
"ThirdPartyNotices.txt",
5350
"devcontainer.js",
5451
"dist/spec-node/devContainersSpecCLI.js",
55-
"dist/node_modules/vscode-dev-containers",
5652
"package.json",
5753
"scripts/updateUID.Dockerfile"
5854
],
@@ -106,7 +102,6 @@
106102
"stream-to-pull-stream": "^1.7.3",
107103
"tar": "^6.1.13",
108104
"text-table": "^0.2.0",
109-
"vscode-dev-containers": "https://github.com/microsoft/vscode-dev-containers/releases/download/v0.245.2/vscode-dev-containers-0.245.2.tgz",
110105
"vscode-uri": "^3.0.7",
111106
"yargs": "~17.7.1"
112107
}

src/spec-configuration/containerFeaturesConfiguration.ts

+8-96
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { Lockfile, readLockfile, writeLockfile } from './lockfile';
2222
import { computeDependsOnInstallationOrder } from './containerFeaturesOrder';
2323
import { logFeatureAdvisories } from './featureAdvisories';
2424
import { getEntPasswdShellCommand } from '../spec-common/commonUtils';
25+
import { ContainerError } from '../spec-common/errors';
2526

2627
// v1
2728
const V1_ASSET_NAME = 'devcontainer-features.tgz';
@@ -116,18 +117,14 @@ export function parseMount(str: string): Mount {
116117
.reduce((acc, [key, value]) => ({ ...acc, [(normalizedMountKeys[key] || key)]: value }), {}) as Mount;
117118
}
118119

119-
export type SourceInformation = LocalCacheSourceInformation | GithubSourceInformation | DirectTarballSourceInformation | FilePathSourceInformation | OCISourceInformation;
120+
export type SourceInformation = GithubSourceInformation | DirectTarballSourceInformation | FilePathSourceInformation | OCISourceInformation;
120121

121122
interface BaseSourceInformation {
122123
type: string;
123124
userFeatureId: string; // Dictates how a supporting tool will locate and download a given feature. See https://github.com/devcontainers/spec/blob/main/proposals/devcontainer-features.md#referencing-a-feature
124125
userFeatureIdWithoutVersion?: string;
125126
}
126127

127-
export interface LocalCacheSourceInformation extends BaseSourceInformation {
128-
type: 'local-cache';
129-
}
130-
131128
export interface OCISourceInformation extends BaseSourceInformation {
132129
type: 'oci';
133130
featureRef: OCIRef;
@@ -215,18 +212,6 @@ export interface ContainerFeatureInternalParams {
215212
experimentalFrozenLockfile?: boolean;
216213
}
217214

218-
export const multiStageBuildExploration = false;
219-
220-
const isTsnode = path.basename(process.argv[0]) === 'ts-node' || process.argv.indexOf('ts-node/register') !== -1;
221-
222-
export function getContainerFeaturesFolder(_extensionPath: string | { distFolder: string }) {
223-
if (isTsnode) {
224-
return path.join(require.resolve('vscode-dev-containers/package.json'), '..', 'container-features');
225-
}
226-
const distFolder = typeof _extensionPath === 'string' ? path.join(_extensionPath, 'dist') : _extensionPath.distFolder;
227-
return path.join(distFolder, 'node_modules', 'vscode-dev-containers', 'container-features');
228-
}
229-
230215
// TODO: Move to node layer.
231216
export function getContainerFeaturesBaseDockerFile(contentSourceRootPath: string) {
232217
return `
@@ -467,29 +452,6 @@ async function askGitHubApiForTarballUri(sourceInformation: GithubSourceInformat
467452
return undefined;
468453
}
469454

470-
export async function loadFeaturesJson(jsonBuffer: Buffer, filePath: string, output: Log): Promise<FeatureSet | undefined> {
471-
if (jsonBuffer.length === 0) {
472-
output.write('Parsed featureSet is empty.', LogLevel.Error);
473-
return undefined;
474-
}
475-
476-
const featureSet: FeatureSet = jsonc.parse(jsonBuffer.toString());
477-
if (!featureSet?.features || featureSet.features.length === 0) {
478-
output.write('Parsed featureSet contains no features.', LogLevel.Error);
479-
return undefined;
480-
}
481-
output.write(`Loaded ${filePath}, which declares ${featureSet.features.length} features and ${(!!featureSet.sourceInformation) ? 'contains' : 'does not contain'} explicit source info.`,
482-
LogLevel.Trace);
483-
484-
return updateFromOldProperties(featureSet);
485-
}
486-
487-
export async function loadV1FeaturesJsonFromDisk(pathToDirectory: string, output: Log): Promise<FeatureSet | undefined> {
488-
const filePath = path.join(pathToDirectory, V1_DEVCONTAINER_FEATURES_FILE_NAME);
489-
const jsonBuffer: Buffer = await readLocalFile(filePath);
490-
return loadFeaturesJson(jsonBuffer, filePath, output);
491-
}
492-
493455
function updateFromOldProperties<T extends { features: (Feature & { extensions?: string[]; settings?: object; customizations?: VSCodeCustomizations })[] }>(original: T): T {
494456
// https://github.com/microsoft/dev-container-spec/issues/1
495457
if (!original.features.find(f => f.extensions || f.settings)) {
@@ -522,7 +484,7 @@ function updateFromOldProperties<T extends { features: (Feature & { extensions?:
522484

523485
// Generate a base featuresConfig object with the set of locally-cached features,
524486
// as well as downloading and merging in remote feature definitions.
525-
export async function generateFeaturesConfig(params: ContainerFeatureInternalParams, dstFolder: string, config: DevContainerConfig, getLocalFeaturesFolder: (d: string) => string, additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>) {
487+
export async function generateFeaturesConfig(params: ContainerFeatureInternalParams, dstFolder: string, config: DevContainerConfig, additionalFeatures: Record<string, string | boolean | Record<string, string | boolean>>) {
526488
const { output } = params;
527489

528490
const workspaceRoot = params.cwd;
@@ -533,15 +495,6 @@ export async function generateFeaturesConfig(params: ContainerFeatureInternalPar
533495
return undefined;
534496
}
535497

536-
// load local cache of features;
537-
// TODO: Update so that cached features are always version 2
538-
const localFeaturesFolder = getLocalFeaturesFolder(params.extensionPath);
539-
const locallyCachedFeatureSet = await loadV1FeaturesJsonFromDisk(localFeaturesFolder, output); // TODO: Pass dist folder instead to also work with the devcontainer.json support package.
540-
if (!locallyCachedFeatureSet) {
541-
output.write('Failed to load locally cached features', LogLevel.Error);
542-
return undefined;
543-
}
544-
545498
let configPath = config.configFilePath && uriToFsPath(config.configFilePath, params.platform);
546499
output.write(`configPath: ${configPath}`, LogLevel.Trace);
547500

@@ -567,7 +520,7 @@ export async function generateFeaturesConfig(params: ContainerFeatureInternalPar
567520

568521
// Fetch features, stage into the appropriate build folder, and read the feature's devcontainer-feature.json
569522
output.write('--- Fetching User Features ----', LogLevel.Trace);
570-
await fetchFeatures(params, featuresConfig, locallyCachedFeatureSet, dstFolder, localFeaturesFolder, ociCacheDir, lockfile);
523+
await fetchFeatures(params, featuresConfig, dstFolder, ociCacheDir, lockfile);
571524

572525
await logFeatureAdvisories(params, featuresConfig);
573526
await writeLockfile(params, config, featuresConfig, initLockfile);
@@ -758,9 +711,10 @@ export async function getFeatureIdType(params: CommonParams, userFeatureId: stri
758711
// (1) A feature backed by a GitHub Release
759712
// Syntax: <repoOwner>/<repoName>/<featureId>[@version]
760713

761-
// DEPRECATED: This is a legacy feature-set ID
714+
// Legacy feature-set ID
762715
if (!userFeatureId.includes('/') && !userFeatureId.includes('\\')) {
763-
return { type: 'local-cache', manifest: undefined };
716+
output.write(`Legacy feature '${userFeatureId}' not supported. Please check https://containers.dev/features for replacements.`, LogLevel.Error);
717+
throw new ContainerError({ description: `Legacy feature '${userFeatureId}' not supported. Please check https://containers.dev/features for replacements.` });
764718
}
765719

766720
// Direct tarball reference
@@ -797,9 +751,6 @@ export function getBackwardCompatibleFeatureId(output: Log, id: string) {
797751
deprecatedFeaturesIntoOptions.set('maven', 'java');
798752
deprecatedFeaturesIntoOptions.set('jupyterlab', 'python');
799753

800-
// TODO: add warning logs once we have context on the new location for these Features.
801-
// const deprecatedFeatures = ['fish', 'homebrew'];
802-
803754
const newFeaturePath = 'ghcr.io/devcontainers/features';
804755
// Note: Pin the versionBackwardComp to '1' to avoid breaking changes.
805756
const versionBackwardComp = '1';
@@ -840,29 +791,6 @@ export async function processFeatureIdentifier(params: CommonParams, configPath:
840791

841792
const { type, manifest } = await getFeatureIdType(params, userFeature.userFeatureId, lockfile);
842793

843-
// cached feature
844-
// Resolves deprecated features (fish, maven, gradle, homebrew, jupyterlab)
845-
if (type === 'local-cache') {
846-
output.write(`Cached feature found.`);
847-
848-
let feat: Feature = {
849-
id: userFeature.userFeatureId,
850-
name: userFeature.userFeatureId,
851-
value: userFeature.options,
852-
included: true,
853-
};
854-
855-
let newFeaturesSet: FeatureSet = {
856-
sourceInformation: {
857-
type: 'local-cache',
858-
userFeatureId: originalUserFeatureId
859-
},
860-
features: [feat],
861-
};
862-
863-
return newFeaturesSet;
864-
}
865-
866794
// remote tar file
867795
if (type === 'direct-tarball') {
868796
output.write(`Remote tar file found.`);
@@ -1036,7 +964,7 @@ export async function processFeatureIdentifier(params: CommonParams, configPath:
1036964
// throw new Error(`Unsupported feature source type: ${type}`);
1037965
}
1038966

1039-
async function fetchFeatures(params: { extensionPath: string; cwd: string; output: Log; env: NodeJS.ProcessEnv }, featuresConfig: FeaturesConfig, localFeatures: FeatureSet, dstFolder: string, localFeaturesFolder: string, ociCacheDir: string, lockfile: Lockfile | undefined) {
967+
async function fetchFeatures(params: { extensionPath: string; cwd: string; output: Log; env: NodeJS.ProcessEnv }, featuresConfig: FeaturesConfig, dstFolder: string, ociCacheDir: string, lockfile: Lockfile | undefined) {
1040968
const featureSets = featuresConfig.featureSets;
1041969
for (let idx = 0; idx < featureSets.length; idx++) { // Index represents the previously computed installation order.
1042970
const featureSet = featureSets[idx];
@@ -1045,10 +973,6 @@ async function fetchFeatures(params: { extensionPath: string; cwd: string; outpu
1045973
continue;
1046974
}
1047975

1048-
if (!localFeatures) {
1049-
continue;
1050-
}
1051-
1052976
const { output } = params;
1053977

1054978
const feature = featureSet.features[0];
@@ -1086,18 +1010,6 @@ async function fetchFeatures(params: { extensionPath: string; cwd: string; outpu
10861010
continue;
10871011
}
10881012

1089-
if (sourceInfoType === 'local-cache') {
1090-
// create copy of the local features to set the environment variables for them.
1091-
await mkdirpLocal(featCachePath);
1092-
await cpDirectoryLocal(localFeaturesFolder, featCachePath);
1093-
1094-
if (!(await applyFeatureConfigToFeature(output, featureSet, feature, featCachePath, undefined))) {
1095-
const err = `Failed to parse feature '${featureDebugId}'. Please check your devcontainer.json 'features' attribute.`;
1096-
throw new Error(err);
1097-
}
1098-
continue;
1099-
}
1100-
11011013
if (sourceInfoType === 'file-path') {
11021014
output.write(`Detected local file path`, LogLevel.Trace);
11031015
await mkdirpLocal(featCachePath);

src/spec-node/containerFeatures.ts

+3-52
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as path from 'path';
7-
import { StringDecoder } from 'string_decoder';
8-
import * as tar from 'tar';
97

108
import { DevContainerConfig } from '../spec-configuration/configuration';
119
import { dockerCLI, dockerPtyCLI, ImageDetails, toExecParameters, toPtyExecParameters } from '../spec-shutdown/dockerUtils';
12-
import { LogLevel, makeLog, toErrorText } from '../spec-utils/log';
13-
import { FeaturesConfig, getContainerFeaturesFolder, getContainerFeaturesBaseDockerFile, getFeatureInstallWrapperScript, getFeatureLayers, getFeatureMainValue, getFeatureValueObject, generateFeaturesConfig, Feature, V1_DEVCONTAINER_FEATURES_FILE_NAME, generateContainerEnvs } from '../spec-configuration/containerFeaturesConfiguration';
10+
import { LogLevel, makeLog } from '../spec-utils/log';
11+
import { FeaturesConfig, getContainerFeaturesBaseDockerFile, getFeatureInstallWrapperScript, getFeatureLayers, getFeatureMainValue, getFeatureValueObject, generateFeaturesConfig, Feature, generateContainerEnvs } from '../spec-configuration/containerFeaturesConfiguration';
1412
import { readLocalFile } from '../spec-utils/pfs';
1513
import { includeAllConfiguredFeatures } from '../spec-utils/product';
1614
import { createFeaturesTempFolder, DockerResolverParameters, getCacheFolder, getFolderImageName, getEmptyContextFolder, SubstitutedConfig } from './utils';
@@ -124,15 +122,12 @@ export async function getExtendImageBuildInfo(params: DockerResolverParameters,
124122
// Creates the folder where the working files will be setup.
125123
const dstFolder = await createFeaturesTempFolder(params.common);
126124

127-
// Extracts the local cache of features.
128-
await createLocalFeatures(params, dstFolder);
129-
130125
// Processes the user's configuration.
131126
const platform = params.common.cliHost.platform;
132127

133128
const cacheFolder = await getCacheFolder(params.common.cliHost);
134129
const { experimentalLockfile, experimentalFrozenLockfile } = params;
135-
const featuresConfig = await generateFeaturesConfig({ ...params.common, platform, cacheFolder, experimentalLockfile, experimentalFrozenLockfile }, dstFolder, config.config, getContainerFeaturesFolder, additionalFeatures);
130+
const featuresConfig = await generateFeaturesConfig({ ...params.common, platform, cacheFolder, experimentalLockfile, experimentalFrozenLockfile }, dstFolder, config.config, additionalFeatures);
136131
if (!featuresConfig) {
137132
if (canAddLabelsToContainer && !imageBuildInfo.dockerfile) {
138133
return {
@@ -170,50 +165,6 @@ export function generateContainerEnvsV1(featuresConfig: FeaturesConfig) {
170165
return result;
171166
}
172167

173-
async function createLocalFeatures(params: DockerResolverParameters, dstFolder: string)
174-
{
175-
const { common } = params;
176-
const { cliHost, output } = common;
177-
178-
// Name of the local cache folder inside the working directory
179-
const localCacheBuildFolderName = 'local-cache';
180-
181-
const srcFolder = getContainerFeaturesFolder(common.extensionPath);
182-
output.write(`local container features stored at: ${srcFolder}`);
183-
await cliHost.mkdirp(`${dstFolder}/${localCacheBuildFolderName}`);
184-
const create = tar.c({
185-
cwd: srcFolder,
186-
filter: path => (path !== './Dockerfile' && path !== `./${V1_DEVCONTAINER_FEATURES_FILE_NAME}`),
187-
}, ['.']);
188-
const createExit = new Promise((resolve, reject) => {
189-
create.on('error', reject);
190-
create.on('finish', resolve);
191-
});
192-
const extract = await cliHost.exec({
193-
cmd: 'tar',
194-
args: [
195-
'--no-same-owner',
196-
'-x',
197-
'-f', '-',
198-
],
199-
cwd: `${dstFolder}/${localCacheBuildFolderName}`,
200-
output,
201-
});
202-
const stdoutDecoder = new StringDecoder();
203-
extract.stdout.on('data', (chunk: Buffer) => {
204-
output.write(stdoutDecoder.write(chunk));
205-
});
206-
const stderrDecoder = new StringDecoder();
207-
extract.stderr.on('data', (chunk: Buffer) => {
208-
output.write(toErrorText(stderrDecoder.write(chunk)));
209-
});
210-
create.pipe(extract.stdin);
211-
await Promise.all([
212-
extract.exit,
213-
createExit, // Allow errors to surface.
214-
]);
215-
}
216-
217168
export interface ImageBuildOptions {
218169
dstFolder: string;
219170
dockerfileContent: string;

src/spec-node/featureUtils.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DevContainerConfig } from '../spec-configuration/configuration';
2-
import { FeaturesConfig, generateFeaturesConfig, getContainerFeaturesFolder } from '../spec-configuration/containerFeaturesConfiguration';
2+
import { FeaturesConfig, generateFeaturesConfig } from '../spec-configuration/containerFeaturesConfiguration';
33
import { DockerCLIParameters } from '../spec-shutdown/dockerUtils';
44
import { PackageConfiguration } from '../spec-utils/product';
55
import { createFeaturesTempFolder, getCacheFolder } from './utils';
@@ -9,5 +9,5 @@ export async function readFeaturesConfig(params: DockerCLIParameters, pkg: Packa
99
const { cwd, env, platform } = cliHost;
1010
const featuresTmpFolder = await createFeaturesTempFolder({ cliHost, package: pkg });
1111
const cacheFolder = await getCacheFolder(cliHost);
12-
return generateFeaturesConfig({ extensionPath, cacheFolder, cwd, output, env, skipFeatureAutoMapping, platform }, featuresTmpFolder, config, getContainerFeaturesFolder, additionalFeatures);
12+
return generateFeaturesConfig({ extensionPath, cacheFolder, cwd, output, env, skipFeatureAutoMapping, platform }, featuresTmpFolder, config, additionalFeatures);
1313
}

src/test/configs/compose-Dockerfile-with-features/.devcontainer/devcontainer.json

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
2-
// https://github.com/microsoft/vscode-dev-containers/tree/v0.234.0/containers/javascript-node-mongo
3-
// Update the VARIANT arg in docker-compose.yml to pick a Node.js version
41
{
52
"name": "Node.js & Mongo DB",
63
"dockerComposeFile": "docker-compose.yml",

src/test/configs/compose-Dockerfile-with-target/.devcontainer/devcontainer.json

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
2-
// https://github.com/microsoft/vscode-dev-containers/tree/v0.234.0/containers/javascript-node-mongo
3-
// Update the VARIANT arg in docker-compose.yml to pick a Node.js version
41
{
52
"name": "Node.js & Mongo DB",
63
"dockerComposeFile": "docker-compose.yml",

src/test/configs/compose-Dockerfile-without-features/.devcontainer/devcontainer.json

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
2-
// https://github.com/microsoft/vscode-dev-containers/tree/v0.234.0/containers/javascript-node-mongo
3-
// Update the VARIANT arg in docker-compose.yml to pick a Node.js version
41
{
52
"name": "Node.js & Mongo DB",
63
"dockerComposeFile": "docker-compose.yml",

src/test/configs/compose-image-with-features/.devcontainer/devcontainer.json

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
2-
// https://github.com/microsoft/vscode-dev-containers/tree/v0.234.0/containers/javascript-node-mongo
3-
// Update the VARIANT arg in docker-compose.yml to pick a Node.js version
41
{
52
"name": "Node.js & Mongo DB",
63
"dockerComposeFile": "docker-compose.yml",

src/test/configs/compose-image-without-features/.devcontainer/devcontainer.json

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
2-
// https://github.com/microsoft/vscode-dev-containers/tree/v0.234.0/containers/javascript-node-mongo
3-
// Update the VARIANT arg in docker-compose.yml to pick a Node.js version
41
{
52
"name": "Node.js & Mongo DB",
63
"dockerComposeFile": "docker-compose.yml",

0 commit comments

Comments
 (0)