Skip to content

Commit 9b0f84a

Browse files
authored
Add repo tags for each published tag (#117)
* repo tags for each published tag * provide hidden option to pin to CLI tag * remove v * parse new contract from (devcontainers/cli#326) * not fatal, add warning * option to disable repo tagging
1 parent 8d3e960 commit 9b0f84a

File tree

6 files changed

+123
-15
lines changed

6 files changed

+123
-15
lines changed

.devcontainer/devcontainer.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
2+
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
3+
{
4+
"name": "Node.js & TypeScript",
5+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6+
"image": "mcr.microsoft.com/devcontainers/typescript-node:0-16",
7+
8+
// Features to add to the dev container. More info: https://containers.dev/features.
9+
// "features": {},
10+
11+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
12+
// "forwardPorts": [],
13+
14+
// Use 'postCreateCommand' to run commands after the container is created.
15+
"postCreateCommand": "yarn"
16+
17+
// Configure tool-specific properties.
18+
// "customizations": {},
19+
20+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
21+
// "remoteUser": "root"
22+
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
node_modules
33

44

5+
dist
6+
57
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
68
# Logs
79
logs

action.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ inputs:
2222
description: >-
2323
Validate the schema of metadata files (devcontainer-feature.json)
2424
and exit without publishing. (Cannot be combined with any publishing step).
25+
disable-repo-tagging:
26+
required: false
27+
default: 'false'
28+
description: >-
29+
Disables adding a git repo tag for each Feature or Template release.
2530
# Feature specific inputs
2631
publish-features:
2732
required: false

src/contracts/collection.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ export interface GitHubMetadata {
99
sha?: string;
1010
}
1111

12+
export interface PublishResult {
13+
publishedVersions: string[];
14+
digest: string;
15+
version: string;
16+
}
1217
export interface DevContainerCollectionMetadata {
1318
sourceInformation: GitHubMetadata;
1419
features: Feature[];

src/main.ts

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import * as core from '@actions/core';
77
import * as exec from '@actions/exec';
88
import * as path from 'path';
99

10+
import { PublishResult } from './contracts/collection';
1011
import { generateFeaturesDocumentation, generateTemplateDocumentation } from './generateDocs';
11-
import { ensureDevcontainerCliPresent, getGitHubMetadata, readdirLocal, validateFeatureSchema } from './utils';
12+
import { addRepoTagForPublishedTag, ensureDevcontainerCliPresent, getGitHubMetadata, readdirLocal, validateFeatureSchema } from './utils';
1213

1314
async function run(): Promise<void> {
1415
core.debug('Reading input parameters...');
@@ -44,6 +45,8 @@ async function run(): Promise<void> {
4445
const disableSchemaValidationAsError = core.getInput('disable-schema-validation').toLowerCase() === 'true';
4546
const validateOnly = core.getInput('validate-only').toLowerCase() === 'true';
4647

48+
const disableRepoTagging = core.getInput('disable-repo-tagging').toLowerCase() === 'true';
49+
4750
// -- Publish
4851

4952
if (shouldPublishFeatures && shouldPublishTemplates) {
@@ -75,30 +78,60 @@ async function run(): Promise<void> {
7578
}
7679

7780
if (shouldPublishFeatures) {
78-
core.info('Publishing features...');
79-
if (!(await publish('feature', featuresBasePath, featuresOciRegistry, featuresNamespace, cliDebugMode))) {
80-
core.setFailed('(!) Failed to publish features.');
81+
core.info('Publishing Features...');
82+
const publishedFeatures = await publish('feature', featuresBasePath, featuresOciRegistry, featuresNamespace, cliDebugMode);
83+
if (!publishedFeatures) {
84+
core.setFailed('(!) Failed to publish Features.');
8185
return;
8286
}
87+
88+
// Add repo tag for this version at the current commit.
89+
if (!disableRepoTagging) {
90+
for (const featureId in publishedFeatures) {
91+
const version = publishedFeatures[featureId]?.version;
92+
if (!version) {
93+
core.debug(`No version available for '${featureId}', so no repo tag was added for Feature`);
94+
continue;
95+
}
96+
if (!(await addRepoTagForPublishedTag('feature', featureId, version))) {
97+
continue;
98+
}
99+
}
100+
}
83101
}
84102

85103
if (shouldPublishTemplates) {
86-
core.info('Publishing templates...');
87-
if (!(await publish('template', templatesBasePath, templatesOciRegistry, templatesNamespace, cliDebugMode))) {
88-
core.setFailed('(!) Failed to publish templates.');
104+
core.info('Publishing Templates...');
105+
const publishedTemplates = await publish('template', templatesBasePath, templatesOciRegistry, templatesNamespace, cliDebugMode);
106+
if (!publishedTemplates) {
107+
core.setFailed('(!) Failed to publish Templates.');
89108
return;
90109
}
110+
111+
// Add repo tag for this version at the current commit.
112+
if (!disableRepoTagging) {
113+
for (const templateId in publishedTemplates) {
114+
const version = publishedTemplates[templateId]?.version;
115+
if (!version) {
116+
core.debug(`No version available for '${templateId}', so no repo tag was added for Feature`);
117+
continue;
118+
}
119+
if (!(await addRepoTagForPublishedTag('template', templateId, version))) {
120+
continue;
121+
}
122+
}
123+
}
91124
}
92125

93126
// -- Generate Documentation
94127

95128
if (shouldGenerateDocumentation && featuresBasePath) {
96-
core.info('Generating documentation for features...');
129+
core.info('Generating documentation for Features...');
97130
await generateFeaturesDocumentation(featuresBasePath, featuresOciRegistry, featuresNamespace);
98131
}
99132

100133
if (shouldGenerateDocumentation && templatesBasePath) {
101-
core.info('Generating documentation for templates...');
134+
core.info('Generating documentation for Templates...');
102135
await generateTemplateDocumentation(templatesBasePath);
103136
}
104137
}
@@ -128,11 +161,11 @@ async function publish(
128161
ociRegistry: string,
129162
namespace: string,
130163
cliDebugMode = false
131-
): Promise<boolean> {
164+
): Promise<{ [featureId: string]: PublishResult } | undefined> {
132165
// Ensures we have the devcontainer CLI installed.
133166
if (!(await ensureDevcontainerCliPresent(cliDebugMode))) {
134167
core.setFailed('Failed to install devcontainer CLI');
135-
return false;
168+
return;
136169
}
137170

138171
try {
@@ -145,10 +178,11 @@ async function publish(
145178

146179
// Fails on non-zero exit code from the invoked process
147180
const res = await exec.getExecOutput(cmd, args, {});
148-
return res.exitCode === 0;
181+
const result: { [featureId: string]: PublishResult } = JSON.parse(res.stdout);
182+
return result;
149183
} catch (err: any) {
150184
core.setFailed(err?.message);
151-
return false;
185+
return;
152186
}
153187
}
154188

src/utils.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,39 @@ export async function isDevcontainerCliAvailable(cliDebugMode = false): Promise<
5353
}
5454
}
5555

56+
export async function addRepoTagForPublishedTag(type: string, id: string, version: string): Promise<boolean> {
57+
const octokit = github.getOctokit(process.env.GITHUB_TOKEN || '');
58+
const tag = `${type}_${id}_${version}`;
59+
core.info(`Adding repo tag '${tag}'...`);
60+
61+
try {
62+
await octokit.rest.git.createRef({
63+
owner: github.context.repo.owner,
64+
repo: github.context.repo.repo,
65+
ref: `refs/tags/${tag}`,
66+
sha: github.context.sha
67+
});
68+
69+
await octokit.rest.git.createTag({
70+
owner: github.context.repo.owner,
71+
repo: github.context.repo.repo,
72+
tag,
73+
message: `${tag}`,
74+
object: github.context.sha,
75+
type: 'commit'
76+
});
77+
} catch (err) {
78+
core.warning(`Failed to automatically add repo tag, manually tag with: 'git tag ${tag} ${github.context.sha}'`);
79+
core.debug(`${err}`);
80+
return false;
81+
}
82+
83+
core.info(`Tag '${tag}' added.`);
84+
return true;
85+
}
86+
5687
export async function ensureDevcontainerCliPresent(cliDebugMode = false): Promise<boolean> {
5788
if (await isDevcontainerCliAvailable(cliDebugMode)) {
58-
core.info('devcontainer CLI is already installed');
5989
return true;
6090
}
6191

@@ -64,14 +94,24 @@ export async function ensureDevcontainerCliPresent(cliDebugMode = false): Promis
6494
return false;
6595
}
6696

97+
// Unless this override is set,
98+
// we'll fetch the latest version of the CLI published to NPM
99+
const cliVersion = core.getInput('devcontainer-cli-version');
100+
let cli = '@devcontainers/cli';
101+
if (cliVersion) {
102+
core.info(`Manually overriding CLI version to '${cliVersion}'`);
103+
cli = `${cli}@${cliVersion}`;
104+
}
105+
67106
try {
68107
core.info('Fetching the latest @devcontainer/cli...');
69-
const res = await exec.getExecOutput('npm', ['install', '-g', '@devcontainers/cli'], {
108+
const res = await exec.getExecOutput('npm', ['install', '-g', cli], {
70109
ignoreReturnCode: true,
71110
silent: true
72111
});
73112
return res.exitCode === 0;
74113
} catch (err) {
114+
core.error(`Failed to fetch @devcontainer/cli: ${err}`);
75115
return false;
76116
}
77117
}

0 commit comments

Comments
 (0)