Skip to content

Commit

Permalink
Add repo tags for each published tag (#117)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
joshspicer authored Jan 25, 2023
1 parent 8d3e960 commit 9b0f84a
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 15 deletions.
22 changes: 22 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
{
"name": "Node.js & TypeScript",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/typescript-node:0-16",

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "yarn"

// Configure tool-specific properties.
// "customizations": {},

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
node_modules


dist

# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
# Logs
logs
Expand Down
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ inputs:
description: >-
Validate the schema of metadata files (devcontainer-feature.json)
and exit without publishing. (Cannot be combined with any publishing step).
disable-repo-tagging:
required: false
default: 'false'
description: >-
Disables adding a git repo tag for each Feature or Template release.
# Feature specific inputs
publish-features:
required: false
Expand Down
5 changes: 5 additions & 0 deletions src/contracts/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ export interface GitHubMetadata {
sha?: string;
}

export interface PublishResult {
publishedVersions: string[];
digest: string;
version: string;
}
export interface DevContainerCollectionMetadata {
sourceInformation: GitHubMetadata;
features: Feature[];
Expand Down
60 changes: 47 additions & 13 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import * as core from '@actions/core';
import * as exec from '@actions/exec';
import * as path from 'path';

import { PublishResult } from './contracts/collection';
import { generateFeaturesDocumentation, generateTemplateDocumentation } from './generateDocs';
import { ensureDevcontainerCliPresent, getGitHubMetadata, readdirLocal, validateFeatureSchema } from './utils';
import { addRepoTagForPublishedTag, ensureDevcontainerCliPresent, getGitHubMetadata, readdirLocal, validateFeatureSchema } from './utils';

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

const disableRepoTagging = core.getInput('disable-repo-tagging').toLowerCase() === 'true';

// -- Publish

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

if (shouldPublishFeatures) {
core.info('Publishing features...');
if (!(await publish('feature', featuresBasePath, featuresOciRegistry, featuresNamespace, cliDebugMode))) {
core.setFailed('(!) Failed to publish features.');
core.info('Publishing Features...');
const publishedFeatures = await publish('feature', featuresBasePath, featuresOciRegistry, featuresNamespace, cliDebugMode);
if (!publishedFeatures) {
core.setFailed('(!) Failed to publish Features.');
return;
}

// Add repo tag for this version at the current commit.
if (!disableRepoTagging) {
for (const featureId in publishedFeatures) {
const version = publishedFeatures[featureId]?.version;
if (!version) {
core.debug(`No version available for '${featureId}', so no repo tag was added for Feature`);
continue;
}
if (!(await addRepoTagForPublishedTag('feature', featureId, version))) {
continue;
}
}
}
}

if (shouldPublishTemplates) {
core.info('Publishing templates...');
if (!(await publish('template', templatesBasePath, templatesOciRegistry, templatesNamespace, cliDebugMode))) {
core.setFailed('(!) Failed to publish templates.');
core.info('Publishing Templates...');
const publishedTemplates = await publish('template', templatesBasePath, templatesOciRegistry, templatesNamespace, cliDebugMode);
if (!publishedTemplates) {
core.setFailed('(!) Failed to publish Templates.');
return;
}

// Add repo tag for this version at the current commit.
if (!disableRepoTagging) {
for (const templateId in publishedTemplates) {
const version = publishedTemplates[templateId]?.version;
if (!version) {
core.debug(`No version available for '${templateId}', so no repo tag was added for Feature`);
continue;
}
if (!(await addRepoTagForPublishedTag('template', templateId, version))) {
continue;
}
}
}
}

// -- Generate Documentation

if (shouldGenerateDocumentation && featuresBasePath) {
core.info('Generating documentation for features...');
core.info('Generating documentation for Features...');
await generateFeaturesDocumentation(featuresBasePath, featuresOciRegistry, featuresNamespace);
}

if (shouldGenerateDocumentation && templatesBasePath) {
core.info('Generating documentation for templates...');
core.info('Generating documentation for Templates...');
await generateTemplateDocumentation(templatesBasePath);
}
}
Expand Down Expand Up @@ -128,11 +161,11 @@ async function publish(
ociRegistry: string,
namespace: string,
cliDebugMode = false
): Promise<boolean> {
): Promise<{ [featureId: string]: PublishResult } | undefined> {
// Ensures we have the devcontainer CLI installed.
if (!(await ensureDevcontainerCliPresent(cliDebugMode))) {
core.setFailed('Failed to install devcontainer CLI');
return false;
return;
}

try {
Expand All @@ -145,10 +178,11 @@ async function publish(

// Fails on non-zero exit code from the invoked process
const res = await exec.getExecOutput(cmd, args, {});
return res.exitCode === 0;
const result: { [featureId: string]: PublishResult } = JSON.parse(res.stdout);
return result;
} catch (err: any) {
core.setFailed(err?.message);
return false;
return;
}
}

Expand Down
44 changes: 42 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,39 @@ export async function isDevcontainerCliAvailable(cliDebugMode = false): Promise<
}
}

export async function addRepoTagForPublishedTag(type: string, id: string, version: string): Promise<boolean> {
const octokit = github.getOctokit(process.env.GITHUB_TOKEN || '');
const tag = `${type}_${id}_${version}`;
core.info(`Adding repo tag '${tag}'...`);

try {
await octokit.rest.git.createRef({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
ref: `refs/tags/${tag}`,
sha: github.context.sha
});

await octokit.rest.git.createTag({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
tag,
message: `${tag}`,
object: github.context.sha,
type: 'commit'
});
} catch (err) {
core.warning(`Failed to automatically add repo tag, manually tag with: 'git tag ${tag} ${github.context.sha}'`);
core.debug(`${err}`);
return false;
}

core.info(`Tag '${tag}' added.`);
return true;
}

export async function ensureDevcontainerCliPresent(cliDebugMode = false): Promise<boolean> {
if (await isDevcontainerCliAvailable(cliDebugMode)) {
core.info('devcontainer CLI is already installed');
return true;
}

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

// Unless this override is set,
// we'll fetch the latest version of the CLI published to NPM
const cliVersion = core.getInput('devcontainer-cli-version');
let cli = '@devcontainers/cli';
if (cliVersion) {
core.info(`Manually overriding CLI version to '${cliVersion}'`);
cli = `${cli}@${cliVersion}`;
}

try {
core.info('Fetching the latest @devcontainer/cli...');
const res = await exec.getExecOutput('npm', ['install', '-g', '@devcontainers/cli'], {
const res = await exec.getExecOutput('npm', ['install', '-g', cli], {
ignoreReturnCode: true,
silent: true
});
return res.exitCode === 0;
} catch (err) {
core.error(`Failed to fetch @devcontainer/cli: ${err}`);
return false;
}
}
Expand Down

0 comments on commit 9b0f84a

Please sign in to comment.