@devcontainers/cli
Advanced tools
Comparing version 0.9.0 to 0.9.1
@@ -70,4 +70,3 @@ /// <reference types="node" /> | ||
type: 'file-path'; | ||
filePath: string; | ||
isRelative: boolean; | ||
resolvedFilePath: string; | ||
} | ||
@@ -134,3 +133,3 @@ export interface GithubSourceInformation extends BaseSourceInformation { | ||
}, dstFolder: string, config: DevContainerConfig, getLocalFeaturesFolder: (d: string) => string): Promise<FeaturesConfig | undefined>; | ||
export declare function getFeatureIdType(output: Log, env: NodeJS.ProcessEnv, id: string): Promise<{ | ||
export declare function getFeatureIdType(output: Log, env: NodeJS.ProcessEnv, userFeatureId: string): Promise<{ | ||
type: string; | ||
@@ -142,3 +141,3 @@ manifest: undefined; | ||
}>; | ||
export declare function parseFeatureIdentifier(output: Log, env: NodeJS.ProcessEnv, userFeature: DevContainerFeature): Promise<FeatureSet | undefined>; | ||
export declare function processFeatureIdentifier(output: Log, env: NodeJS.ProcessEnv, workspaceCwd: string, userFeature: DevContainerFeature): Promise<FeatureSet | undefined>; | ||
export declare function getFeatureMainProperty(feature: Feature): "version" | undefined; | ||
@@ -145,0 +144,0 @@ export declare function getFeatureMainValue(feature: Feature): string | boolean | undefined; |
@@ -26,3 +26,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getFeatureValueObject = exports.getFeatureMainValue = exports.getFeatureMainProperty = exports.parseFeatureIdentifier = exports.getFeatureIdType = exports.generateFeaturesConfig = exports.loadV1FeaturesJsonFromDisk = exports.loadFeaturesJson = exports.generateContainerEnvs = exports.getFeatureLayers = exports.getContainerFeaturesBaseDockerFile = exports.getSourceInfoString = exports.getContainerFeaturesFolder = exports.multiStageBuildExploration = exports.collapseFeaturesConfig = exports.DEVCONTAINER_FEATURE_FILE_NAME = exports.V1_DEVCONTAINER_FEATURES_FILE_NAME = void 0; | ||
exports.getFeatureValueObject = exports.getFeatureMainValue = exports.getFeatureMainProperty = exports.processFeatureIdentifier = exports.getFeatureIdType = exports.generateFeaturesConfig = exports.loadV1FeaturesJsonFromDisk = exports.loadFeaturesJson = exports.generateContainerEnvs = exports.getFeatureLayers = exports.getContainerFeaturesBaseDockerFile = exports.getSourceInfoString = exports.getContainerFeaturesFolder = exports.multiStageBuildExploration = exports.collapseFeaturesConfig = exports.DEVCONTAINER_FEATURE_FILE_NAME = exports.V1_DEVCONTAINER_FEATURES_FILE_NAME = void 0; | ||
const jsonc = __importStar(require("jsonc-parser")); | ||
@@ -78,3 +78,3 @@ const path = __importStar(require("path")); | ||
case 'file-path': | ||
return srcInfo.filePath + '-' + getCounter(); | ||
return srcInfo.resolvedFilePath + '-' + getCounter(); | ||
case 'oci': | ||
@@ -261,2 +261,4 @@ return `oci-${srcInfo.featureRef.resource}-${getCounter()}`; | ||
const { output } = params; | ||
const workspaceCwd = params.cwd; | ||
output.write(`workspaceCwd: ${workspaceCwd}`, log_1.LogLevel.Trace); | ||
const userFeatures = featuresToArray(config); | ||
@@ -282,5 +284,6 @@ if (!userFeatures) { | ||
output.write('--- Processing User Features ----', log_1.LogLevel.Trace); | ||
featuresConfig = await processUserFeatures(params.output, params.env, userFeatures, featuresConfig); | ||
featuresConfig = await processUserFeatures(params.output, params.env, workspaceCwd, userFeatures, featuresConfig); | ||
output.write(JSON.stringify(featuresConfig, null, 4), log_1.LogLevel.Trace); | ||
const ociCacheDir = await prepareOCICache(dstFolder); | ||
// Fetch features and get version information | ||
// Fetch features, stage into the appropriate build folder, and read the feature's devcontainer-feature.json | ||
output.write('--- Fetching User Features ----', log_1.LogLevel.Trace); | ||
@@ -319,5 +322,5 @@ await fetchFeatures(params, featuresConfig, locallyCachedFeatureSet, dstFolder, localFeaturesFolder, ociCacheDir); | ||
// Creates one feature set per feature to aid in support of the previous structure. | ||
async function processUserFeatures(output, env, userFeatures, featuresConfig) { | ||
async function processUserFeatures(output, env, workspaceCwd, userFeatures, featuresConfig) { | ||
for (const userFeature of userFeatures) { | ||
const newFeatureSet = await parseFeatureIdentifier(output, env, userFeature); | ||
const newFeatureSet = await processFeatureIdentifier(output, env, workspaceCwd, userFeature); | ||
if (!newFeatureSet) { | ||
@@ -330,3 +333,3 @@ throw new Error(`Failed to process feature ${userFeature.id}`); | ||
} | ||
async function getFeatureIdType(output, env, id) { | ||
async function getFeatureIdType(output, env, userFeatureId) { | ||
// See the specification for valid feature identifiers: | ||
@@ -342,12 +345,12 @@ // > https://github.com/devcontainers/spec/blob/main/proposals/devcontainer-features.md#referencing-a-feature | ||
// DEPRECATED: This is a legacy feature-set ID | ||
if (!id.includes('/') && !id.includes('\\')) { | ||
if (!userFeatureId.includes('/') && !userFeatureId.includes('\\')) { | ||
return { type: 'local-cache', manifest: undefined }; | ||
} | ||
// Direct tarball reference | ||
if (id.startsWith('https://')) { | ||
if (userFeatureId.startsWith('https://')) { | ||
return { type: 'direct-tarball', manifest: undefined }; | ||
} | ||
// Local feature on disk | ||
const validPath = path.parse(id); | ||
if (id.startsWith('./') || id.startsWith('../') || (validPath && path.isAbsolute(id))) { | ||
// !! NOTE: The ability for paths outside the project file tree will soon be removed. | ||
if (userFeatureId.startsWith('./') || userFeatureId.startsWith('../') || userFeatureId.startsWith('/')) { | ||
return { type: 'file-path', manifest: undefined }; | ||
@@ -357,6 +360,6 @@ } | ||
// DEPRECATED: This is a legacy feature-set ID | ||
if (id.includes('@')) { | ||
if (userFeatureId.includes('@')) { | ||
return { type: 'github-repo', manifest: undefined }; | ||
} | ||
const manifest = await (0, containerFeaturesOCI_1.fetchOCIFeatureManifestIfExists)(output, env, id); | ||
const manifest = await (0, containerFeaturesOCI_1.fetchOCIFeatureManifestIfExists)(output, env, userFeatureId); | ||
if (manifest) { | ||
@@ -372,3 +375,5 @@ return { type: 'oci', manifest: manifest }; | ||
exports.getFeatureIdType = getFeatureIdType; | ||
async function parseFeatureIdentifier(output, env, userFeature) { | ||
// Strictly processes the user provided feature identifier to determine sourceInformation type. | ||
// Returns a featureSet per feature. | ||
async function processFeatureIdentifier(output, env, workspaceCwd, userFeature) { | ||
output.write(`* Processing feature: ${userFeature.id}`); | ||
@@ -419,12 +424,24 @@ const { type, manifest } = await getFeatureIdType(output, env, userFeature.id); | ||
} | ||
// If its a valid path | ||
// If it matches the path syntax, it is a 'local' feature pointing to source code. | ||
// Resolves the absolute path and ensures that the directory exists. | ||
if (type === 'file-path') { | ||
output.write(`Local disk feature.`); | ||
const userFeaturePath = path.parse(userFeature.id); | ||
//if (userFeaturePath && ((path.isAbsolute(userFeature.id) && existsSync(userFeature.id)) || !path.isAbsolute(userFeature.id))) { | ||
const filePath = userFeature.id; | ||
const id = userFeaturePath.name; | ||
const isRelative = !path.isAbsolute(userFeature.id); | ||
let resolvedFilePathToFeatureFolder = ''; | ||
if (path.isAbsolute(userFeature.id)) { | ||
resolvedFilePathToFeatureFolder = userFeature.id; | ||
} | ||
else { | ||
// A relative path to a feature directory is | ||
// computed relative to the `--workspace-folder` | ||
resolvedFilePathToFeatureFolder = path.join(workspaceCwd, userFeature.id); | ||
} | ||
output.write(`Resolved: ${userFeature.id} -> ${resolvedFilePathToFeatureFolder}`, log_1.LogLevel.Trace); | ||
if (!resolvedFilePathToFeatureFolder || !(0, pfs_1.isLocalFolder)(resolvedFilePathToFeatureFolder)) { | ||
output.write(`Local file path parse error. Resolved path to feature folder: ${resolvedFilePathToFeatureFolder}`, log_1.LogLevel.Error); | ||
return undefined; | ||
} | ||
const id = path.basename(resolvedFilePathToFeatureFolder); | ||
output.write(`Parsed feature id: ${id}`, log_1.LogLevel.Trace); | ||
let feat = { | ||
id: id, | ||
id, | ||
name: userFeature.id, | ||
@@ -437,4 +454,3 @@ value: userFeature.options, | ||
type: 'file-path', | ||
filePath, | ||
isRelative: isRelative | ||
resolvedFilePath: resolvedFilePathToFeatureFolder, | ||
}, | ||
@@ -513,3 +529,3 @@ features: [feat], | ||
} | ||
exports.parseFeatureIdentifier = parseFeatureIdentifier; | ||
exports.processFeatureIdentifier = processFeatureIdentifier; | ||
async function fetchFeatures(params, featuresConfig, localFeatures, dstFolder, localFeaturesFolder, ociCacheDir) { | ||
@@ -543,3 +559,3 @@ var _a, _b; | ||
} | ||
if (!(await parseDevContainerFeature(output, featureSet, feature, featCachePath))) { | ||
if (!(await applyFeatureConfigToFeature(output, featureSet, feature, featCachePath))) { | ||
const err = `Failed to parse feature '${featureDebugId}'. Please check your devcontainer.json 'features' attribute.`; | ||
@@ -554,3 +570,3 @@ throw new Error(err); | ||
await (0, pfs_1.cpDirectoryLocal)(localFeaturesFolder, featCachePath); | ||
if (!(await parseDevContainerFeature(output, featureSet, feature, featCachePath))) { | ||
if (!(await applyFeatureConfigToFeature(output, featureSet, feature, featCachePath))) { | ||
const err = `Failed to parse feature '${featureDebugId}'. Please check your devcontainer.json 'features' attribute.`; | ||
@@ -563,9 +579,9 @@ throw new Error(err); | ||
output.write(`Detected local file path`, log_1.LogLevel.Trace); | ||
const executionPath = featureSet.sourceInformation.isRelative ? path.join(params.cwd, featureSet.sourceInformation.filePath) : featureSet.sourceInformation.filePath; | ||
if (!(await parseDevContainerFeature(output, featureSet, feature, featCachePath))) { | ||
await (0, pfs_1.mkdirpLocal)(featCachePath); | ||
const executionPath = featureSet.sourceInformation.resolvedFilePath; | ||
await (0, pfs_1.cpDirectoryLocal)(executionPath, featCachePath); | ||
if (!(await applyFeatureConfigToFeature(output, featureSet, feature, featCachePath))) { | ||
const err = `Failed to parse feature '${featureDebugId}'. Please check your devcontainer.json 'features' attribute.`; | ||
throw new Error(err); | ||
} | ||
await (0, pfs_1.mkdirpLocal)(featCachePath); | ||
await (0, pfs_1.cpDirectoryLocal)(executionPath, featCachePath); | ||
continue; | ||
@@ -605,3 +621,3 @@ } | ||
output.write(`Succeeded fetching ${tarballUri}`, log_1.LogLevel.Trace); | ||
if (!(await parseDevContainerFeature(output, featureSet, feature, featCachePath))) { | ||
if (!(await applyFeatureConfigToFeature(output, featureSet, feature, featCachePath))) { | ||
const err = `Failed to parse feature '${featureDebugId}'. Please check your devcontainer.json 'features' attribute.`; | ||
@@ -665,10 +681,12 @@ throw new Error(err); | ||
} | ||
// Implements the latest ('internalVersion' = '2') parsing logic, | ||
// Falls back to earlier implementation(s) if requirements not present. | ||
// | ||
// Returns a boolean indicating whether the feature was successfully parsed. | ||
async function parseDevContainerFeature(output, featureSet, feature, featCachePath) { | ||
// Reads the feature's 'devcontainer-feature.json` and applies any attributes to the in-memory Feature object. | ||
// NOTE: | ||
// Implements the latest ('internalVersion' = '2') parsing logic, | ||
// Falls back to earlier implementation(s) if requirements not present. | ||
// Returns a boolean indicating whether the feature was successfully parsed. | ||
async function applyFeatureConfigToFeature(output, featureSet, feature, featCachePath) { | ||
const innerJsonPath = path.join(featCachePath, exports.DEVCONTAINER_FEATURE_FILE_NAME); | ||
if (!(await (0, pfs_1.isLocalFile)(innerJsonPath))) { | ||
output.write(`Feature ${feature.id} is not a 'v2' feature. Attempting fallback to 'v1' implementation.`, log_1.LogLevel.Warning); | ||
output.write(`For v2, expected devcontainer-feature.json at ${innerJsonPath}`, log_1.LogLevel.Trace); | ||
return await parseDevContainerFeature_v1Impl(output, featureSet, feature, featCachePath); | ||
@@ -675,0 +693,0 @@ } |
{ | ||
"name": "@devcontainers/cli", | ||
"description": "Dev Containers CLI", | ||
"version": "0.9.0", | ||
"version": "0.9.1", | ||
"bin": { | ||
@@ -34,5 +34,3 @@ "devcontainer": "devcontainer.js" | ||
"test": "env TS_NODE_PROJECT=src/test/tsconfig.json mocha -r ts-node/register --exit src/test/*.test.ts", | ||
"test-container-features": "env TS_NODE_PROJECT=src/test/tsconfig.json mocha -r ts-node/register --exit src/test/container-features/**/*.test.ts", | ||
"test-container-features-offline": "env TS_NODE_PROJECT=src/test/tsconfig.json mocha -r ts-node/register --exit src/test/container-features/**/*.offline.test.ts", | ||
"test-container-features-online": "env TS_NODE_PROJECT=src/test/tsconfig.json mocha -r ts-node/register --exit src/test/container-features/**/*.online.test.ts" | ||
"test-container-features": "env TS_NODE_PROJECT=src/test/tsconfig.json mocha -r ts-node/register --exit src/test/container-features/*.test.ts" | ||
}, | ||
@@ -39,0 +37,0 @@ "devDependencies": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1772332
13692