Skip to content

Commit b01622e

Browse files
authored
Enhance Template metadata (#875)
* update Template interface * update Template metadata to include files. Fix featureIds by calling getRef(...) * improve test * replace metadata entry for optionalPaths to full file path when only one file in target dir
1 parent fbd8dfe commit b01622e

File tree

17 files changed

+201
-5
lines changed

17 files changed

+201
-5
lines changed

src/spec-configuration/containerTemplatesConfiguration.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ export interface Template {
55
description?: string;
66
documentationURL?: string;
77
licenseURL?: string;
8-
type?: string;
9-
fileCount?: number;
8+
type?: string; // Added programatically during packaging
9+
fileCount?: number; // Added programatically during packaging
1010
featureIds?: string[];
1111
options?: Record<string, TemplateOption>;
1212
platforms?: string[];
1313
publisher?: string;
1414
keywords?: string[];
15+
optionalPaths?: string[];
16+
files: string[]; // Added programatically during packaging
1517
}
1618

1719
export type TemplateOption = {

src/spec-node/collectionCommonUtils/packageCommandImpl.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import path from 'path';
99
import { DevContainerConfig, isDockerFileConfig } from '../../spec-configuration/configuration';
1010
import { Template } from '../../spec-configuration/containerTemplatesConfiguration';
1111
import { Feature } from '../../spec-configuration/containerFeaturesConfiguration';
12+
import { getRef } from '../../spec-configuration/containerCollectionsOCI';
1213

1314
export interface SourceInformation {
1415
source: string;
@@ -133,9 +134,45 @@ async function addsAdditionalTemplateProps(srcFolder: string, devcontainerTempla
133134
return false;
134135
}
135136

137+
const fileNames = (await recursiveDirReader.default(srcFolder))?.map((f) => path.relative(srcFolder, f)) ?? [];
138+
136139
templateData.type = type;
137-
templateData.fileCount = (await recursiveDirReader.default(srcFolder)).length;
138-
templateData.featureIds = config.features ? Object.keys(config.features).map((k) => k.split(':')[0]) : [];
140+
templateData.files = fileNames;
141+
templateData.fileCount = fileNames.length;
142+
templateData.featureIds =
143+
config.features
144+
? Object.keys(config.features)
145+
.map((f) => getRef(output, f)?.resource)
146+
.filter((f) => f !== undefined) as string[]
147+
: [];
148+
149+
// If the Template is omitting a folder and that folder contains just a single file,
150+
// replace the entry in the metadata with the full file name,
151+
// as that provides a better user experience when tools consume the metadata.
152+
// Eg: If the template is omitting ".github/*" and the Template source contains just a single file
153+
// "workflow.yml", replace ".github/*" with ".github/workflow.yml"
154+
if (templateData.optionalPaths && templateData.optionalPaths?.length) {
155+
const optionalPaths = templateData.optionalPaths;
156+
for (const optPath of optionalPaths) {
157+
// Skip if not a directory
158+
if (!optPath.endsWith('/*') || optPath.length < 3) {
159+
continue;
160+
}
161+
const dirPath = optPath.slice(0, -2);
162+
const dirFiles = fileNames.filter((f) => f.startsWith(dirPath));
163+
output.write(`Given optionalPath starting with '${dirPath}' has ${dirFiles.length} files`, LogLevel.Trace);
164+
if (dirFiles.length === 1) {
165+
// If that one item is a file and not a directory
166+
const f = dirFiles[0];
167+
output.write(`Checking if optionalPath '${optPath}' with lone contents '${f}' is a file `, LogLevel.Trace);
168+
const localPath = path.join(srcFolder, f);
169+
if (await isLocalFile(localPath)) {
170+
output.write(`Checked path '${localPath}' on disk is a file. Replacing optionalPaths entry '${optPath}' with '${f}'`, LogLevel.Trace);
171+
templateData.optionalPaths[optionalPaths.indexOf(optPath)] = f;
172+
}
173+
}
174+
}
175+
}
139176

140177
await writeLocalFile(devcontainerTemplateJsonPath, JSON.stringify(templateData, null, 4));
141178

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"image": "mcr.microsoft.com/devcontainers/base",
3+
"containerEnv": {
4+
"MY_ENV_VAR": "${templateOption:anOption}"
5+
},
6+
"features": {
7+
"ghcr.io/devcontainers/features/azure-cli": {
8+
"installBicep": false
9+
},
10+
"ghcr.io/devcontainers/features/aws-cli:1": {},
11+
"ghcr.io/devcontainers/features/common-utils:2.5.1": {
12+
"userUid": "${templateOption:userUid}"
13+
},
14+
"ghcr.io/devcontainers/features/docker-in-docker@sha256:503f23cd692325b3cbb8c20a0ecfabb3444b0c786b363e0c82572bd7d71dc099": {}
15+
}
16+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "devcontainers" # See documentation for possible values
4+
directory: "/"
5+
schedule:
6+
interval: weekly
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hi
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export class C1 {
2+
constructor() {
3+
// Add your code here
4+
}
5+
6+
// Add your methods here
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export class C2 {
2+
constructor() {
3+
// Add your code here
4+
}
5+
6+
// Add your methods here
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export class C3 {
2+
constructor() {
3+
// Add your code here
4+
}
5+
6+
// Add your methods here
7+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"id": "mytemplate",
3+
"version": "1.0.0",
4+
"name": "My Template",
5+
"description": "Simple test",
6+
"documentationURL": "https://github.com/codspace/templates/tree/main/src/test",
7+
"publisher": "codspace",
8+
"licenseURL": "https://github.com/devcontainers/templates/blob/main/LICENSE",
9+
"platforms": [
10+
"Any"
11+
],
12+
"options": {
13+
"anOption": {
14+
"type": "string",
15+
"description": "A great option",
16+
"proposals": [
17+
"8.0",
18+
"7.0",
19+
"6.0"
20+
],
21+
"default": "8.0"
22+
},
23+
"userUid": {
24+
"type": "string",
25+
"description": "The user's UID",
26+
"proposals": [
27+
"1000",
28+
"1001",
29+
"1002"
30+
],
31+
"default": "1000"
32+
}
33+
},
34+
"optionalPaths": [
35+
".github/*",
36+
"example-projects/exampleA/*",
37+
"c1.ts"
38+
]
39+
}

0 commit comments

Comments
 (0)