Unverified Commit 4ecc275a authored by Josh Spicer's avatar Josh Spicer Committed by GitHub

update action to a82f06a29168a3c6594fb7206dd0afd3af8efdf2

parent ef87daf5
name: 'Dev Container'
description: 'Publish custom development container feature(s) and/or templates(s)'
description: 'Publish custom development container assets'
author: 'GitHub'
branding:
icon: box
......@@ -10,10 +10,6 @@ inputs:
required: false
default: "false"
description: "['true'/'false'] Publish dev container 'feature' artifacts"
publish-templates:
required: false
default: "false"
description: "['true'/'false'] Publish dev container 'template' artifacts"
generate-docs:
required: false
default: "false"
......@@ -26,37 +22,11 @@ inputs:
description: "Relative path to the 'src' folder containing dev container 'feature(s)'"
oci-registry:
required: false
default: "ghcr.io"
description: "Name of the OCI registry that implements the OCI Artifact Distribution Specification"
features-namespace:
required: false
default: "<owner>/<repo>"
description: "A unique indentifier for the collection of features"
# 'template' options
base-path-to-templates:
required: false
default: ''
description: "Relative path to the folder containing dev container 'template(s)'"
# EXPERIMENTAL
tag-individual-features:
required: false
default: "false"
description: "Tag individual features"
publish-to-npm:
required: false
default: "false"
description: "Should publish features to NPM?"
publish-release-artifacts:
required: false
default: "false"
description: "Publish release artifacts (classic)"
publish-to-oci:
required: false
default: "false"
description: "Publish to OCI?"
runs:
using: 'node16'
main: 'dist/index.js'
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -10,6 +10,18 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@actions/exec
MIT
The MIT License (MIT)
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@actions/github
MIT
The MIT License (MIT)
......@@ -47,6 +59,18 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@actions/io
MIT
The MIT License (MIT)
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@octokit/auth-token
MIT
The MIT License
......@@ -434,25 +458,6 @@ Apache-2.0
limitations under the License.
chownr
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
deprecation
ISC
The ISC License
......@@ -472,25 +477,6 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
fs-minipass
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
is-plain-object
MIT
The MIT License (MIT)
......@@ -516,80 +502,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
minipass
ISC
The ISC License
Copyright (c) 2017-2022 npm, Inc., Isaac Z. Schlueter, and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
minizlib
MIT
Minizlib was created by Isaac Z. Schlueter.
It is a derivative work of the Node.js project.
"""
Copyright Isaac Z. Schlueter and Contributors
Copyright Node.js contributors. All rights reserved.
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
mkdirp
MIT
Copyright James Halliday (mail@substack.net) and Isaac Z. Schlueter (i@izs.me)
This project is free software released under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
node-fetch
MIT
The MIT License (MIT)
......@@ -635,25 +547,6 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
tar
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
tr46
MIT
......@@ -693,6 +586,19 @@ Permission to use, copy, modify, and/or distribute this software for any purpose
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
uuid
MIT
The MIT License (MIT)
Copyright (c) 2010-2020 Robert Kieffer and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
webidl-conversions
BSD-2-Clause
# The BSD 2-Clause License
......@@ -751,22 +657,3 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
yallist
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
......@@ -56,9 +56,11 @@ const FEATURES_README_TEMPLATE = `
#{OptionsTable}
#{Notes}
---
_Note: This file was auto-generated from the [devcontainer-feature.json](#{RepoUrl})._
_Note: This file was auto-generated from the [devcontainer-feature.json](#{RepoUrl}). Add additional notes to a \`NOTES.md\`._
`;
const TEMPLATE_README_TEMPLATE = `
# #{Name}
......@@ -107,8 +109,6 @@ function _generateDocumentation(basePath, readmeTemplate, metadataFile, ociRegis
return;
}
const srcInfo = (0, utils_1.getGitHubMetadata)();
const owner = srcInfo.owner;
const repo = srcInfo.repo;
// Add version
let version = 'latest';
const parsedVersion = parsedJson === null || parsedJson === void 0 ? void 0 : parsedJson.version;
......@@ -131,6 +131,10 @@ function _generateDocumentation(basePath, readmeTemplate, metadataFile, ociRegis
.join('\n');
return '| Options Id | Description | Type | Default Value |\n' + '|-----|-----|-----|-----|\n' + contents;
};
const generateNotesMarkdown = () => {
const notesPath = path.join(basePath, f, 'NOTES.md');
return fs.existsSync(notesPath) ? fs.readFileSync(path.join(notesPath), 'utf8') : '';
};
let urlToConfig = './devcontainer-feature.json';
const basePathTrimmed = basePath.startsWith('./') ? basePath.substring(2) : basePath;
if (srcInfo.owner && srcInfo.repo) {
......@@ -142,9 +146,10 @@ function _generateDocumentation(basePath, readmeTemplate, metadataFile, ociRegis
.replace('#{Name}', parsedJson.name ? `${parsedJson.name} (${parsedJson.id})` : `${parsedJson.id}`)
.replace('#{Description}', (_a = parsedJson.description) !== null && _a !== void 0 ? _a : '')
.replace('#{OptionsTable}', generateOptionsMarkdown())
.replace('#{Notes}', generateNotesMarkdown())
// Features Only
.replace('#{Registry}', ociRegistry)
.replace('#{Namespace}', namespace == '<owner>/<repo>' ? `${owner}/${repo}` : namespace)
.replace('#{Namespace}', namespace)
.replace('#{Version}', version)
// Templates Only
.replace('#{ManifestName}', (_c = (_b = parsedJson === null || parsedJson === void 0 ? void 0 : parsedJson.image) === null || _b === void 0 ? void 0 : _b.manifest) !== null && _c !== void 0 ? _c : '')
......
......@@ -39,83 +39,55 @@ Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const generateDocs_1 = require("./generateDocs");
const utils_1 = require("./utils");
const exec = __importStar(require("@actions/exec"));
function run() {
return __awaiter(this, void 0, void 0, function* () {
core.debug('Reading input parameters...');
// Read inputs
const shouldPublishFeatures = core.getInput('publish-features').toLowerCase() === 'true';
const shouldPublishTemplates = core.getInput('publish-templates').toLowerCase() === 'true';
const shouldGenerateDocumentation = core.getInput('generate-docs').toLowerCase() === 'true';
// Experimental
const shouldTagIndividualFeatures = core.getInput('tag-individual-features').toLowerCase() === 'true';
const shouldPublishToNPM = core.getInput('publish-to-npm').toLowerCase() === 'true';
const shouldPublishReleaseArtifacts = core.getInput('publish-release-artifacts').toLowerCase() === 'true';
const shouldPublishToOCI = core.getInput('publish-to-oci').toLowerCase() === 'true';
const opts = {
shouldTagIndividualFeatures,
shouldPublishToNPM,
shouldPublishReleaseArtifacts,
shouldPublishToOCI
};
const featuresBasePath = core.getInput('base-path-to-features');
const templatesBasePath = core.getInput('base-path-to-templates');
const ociRegistry = core.getInput('oci-registry');
const namespace = core.getInput('features-namespace');
let featuresMetadata = undefined;
let templatesMetadata = undefined;
// -- Package Release Artifacts
const sourceMetadata = (0, utils_1.getGitHubMetadata)();
const inputOciRegistry = core.getInput('oci-registry');
const ociRegistry = inputOciRegistry && inputOciRegistry !== '' ? inputOciRegistry : 'ghcr.io';
const inputNamespace = core.getInput('namespace');
const namespace = inputNamespace && inputNamespace !== '' ? inputNamespace : `${sourceMetadata.owner}/${sourceMetadata.repo}`;
const cliDebugMode = core.getInput('devcontainer-cli-debug-mode').toLowerCase() === 'true';
// -- Publish
if (shouldPublishFeatures) {
core.info('Publishing features...');
featuresMetadata = yield packageFeatures(featuresBasePath, opts);
}
if (shouldPublishTemplates) {
core.info('Publishing template...');
templatesMetadata = yield packageTemplates(templatesBasePath);
yield publishFeatures(featuresBasePath, ociRegistry, namespace, cliDebugMode);
}
// -- Generate Documentation
if (shouldGenerateDocumentation && featuresBasePath) {
core.info('Generating documentation for features...');
yield (0, generateDocs_1.generateFeaturesDocumentation)(featuresBasePath, ociRegistry, namespace);
}
if (shouldGenerateDocumentation && templatesBasePath) {
core.info('Generating documentation for templates...');
yield (0, generateDocs_1.generateTemplateDocumentation)(templatesBasePath);
}
// -- Programatically add feature/template metadata to collections file.
core.info('Generating metadata file: devcontainer-collection.json');
yield (0, utils_1.addCollectionsMetadataFile)(featuresMetadata, templatesMetadata, opts);
});
}
function packageFeatures(basePath, opts) {
function publishFeatures(basePath, ociRegistry, namespace, cliDebugMode = false) {
return __awaiter(this, void 0, void 0, function* () {
try {
core.info(`Archiving all features in ${basePath}`);
const metadata = yield (0, utils_1.getFeaturesAndPackage)(basePath, opts);
core.info('Packaging features has finished.');
return metadata;
}
catch (error) {
if (error instanceof Error) {
core.setFailed(error.message);
}
// Ensures we have the devcontainer CLI installed.
if (!(yield (0, utils_1.ensureDevcontainerCliPresent)(cliDebugMode))) {
core.setFailed('Failed to install devcontainer CLI');
return false;
}
return;
});
}
function packageTemplates(basePath) {
return __awaiter(this, void 0, void 0, function* () {
try {
core.info(`Archiving all templates in ${basePath}`);
const metadata = yield (0, utils_1.getTemplatesAndPackage)(basePath);
core.info('Packaging templates has finished.');
return metadata;
}
catch (error) {
if (error instanceof Error) {
core.setFailed(error.message);
let cmd = 'devcontainer';
let args = ['features', 'publish', '-r', ociRegistry, '-n', namespace, basePath];
if (cliDebugMode) {
cmd = 'npx';
args = ['-y', './devcontainer.tgz', ...args];
}
const res = yield exec.getExecOutput(cmd, args, {
ignoreReturnCode: true
});
return res.exitCode === 0;
}
catch (err) {
core.setFailed(err === null || err === void 0 ? void 0 : err.message);
return false;
}
return;
});
}
run();
......@@ -31,42 +31,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTemplatesAndPackage = exports.getFeaturesAndPackage = exports.pushCollectionsMetadataToOCI = exports.addCollectionsMetadataFile = exports.getGitHubMetadata = exports.tarDirectory = exports.renameLocal = exports.mkdirLocal = exports.writeLocalFile = exports.readLocalFile = void 0;
exports.ensureDevcontainerCliPresent = exports.isDevcontainerCliAvailable = exports.getGitHubMetadata = exports.renameLocal = exports.mkdirLocal = exports.writeLocalFile = exports.readLocalFile = void 0;
const github = __importStar(require("@actions/github"));
const tar = __importStar(require("tar"));
const fs = __importStar(require("fs"));
const core = __importStar(require("@actions/core"));
const child_process = __importStar(require("child_process"));
const util_1 = require("util");
const path_1 = __importDefault(require("path"));
const core = __importStar(require("@actions/core"));
const exec = __importStar(require("@actions/exec"));
exports.readLocalFile = (0, util_1.promisify)(fs.readFile);
exports.writeLocalFile = (0, util_1.promisify)(fs.writeFile);
exports.mkdirLocal = (0, util_1.promisify)(fs.mkdir);
exports.renameLocal = (0, util_1.promisify)(fs.rename);
// Filter what gets included in the tar.c
const filter = (file, _) => {
// Don't include the archive itself.
if (file === './devcontainer-features.tgz') {
return false;
}
return true;
};
function tarDirectory(path, tgzName) {
return __awaiter(this, void 0, void 0, function* () {
return tar.create({ file: tgzName, C: path, filter }, ['.']).then(_ => {
core.info(`Compressed ${path} directory to file ${tgzName}`);
});
});
}
exports.tarDirectory = tarDirectory;
function getGitHubMetadata() {
// Insert github repo metadata
const ref = github.context.ref;
let sourceInformation = {
let metadata = {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
ref,
......@@ -75,255 +54,54 @@ function getGitHubMetadata() {
// Add tag if parseable
if (ref.includes('refs/tags/')) {
const tag = ref.replace('refs/tags/', '');
sourceInformation = Object.assign(Object.assign({}, sourceInformation), { tag });
metadata = Object.assign(Object.assign({}, metadata), { tag });
}
return sourceInformation;
return metadata;
}
exports.getGitHubMetadata = getGitHubMetadata;
function tagFeatureAtVersion(featureMetaData) {
return __awaiter(this, void 0, void 0, function* () {
const featureId = featureMetaData.id;
const featureVersion = featureMetaData.version;
const tagName = `${featureId}_v${featureVersion}`;
// Get GITHUB_TOKEN from environment
const githubToken = process.env.GITHUB_TOKEN;
if (!githubToken) {
core.setFailed('GITHUB_TOKEN environment variable is not set.');
return;
}
// Setup Octokit client
const octokit = github.getOctokit(githubToken);
// Use octokit to get all tags for this repo
const tags = yield octokit.rest.repos.listTags({
owner: github.context.repo.owner,
repo: github.context.repo.repo
});
// See if tags for this release was already created.
const tagExists = tags.data.some(tag => tag.name === tagName);
if (tagExists) {
core.info(`Tag ${tagName} already exists. Skipping...`);
return;
}
// Create tag
const createdTag = yield octokit.rest.git.createTag({
tag: tagName,
message: `Feature ${featureId} version ${featureVersion}`,
object: github.context.sha,
type: 'commit',
owner: github.context.repo.owner,
repo: github.context.repo.repo
});
if (createdTag.status === 201) {
core.info(`Tagged '${tagName}'`);
}
else {
core.setFailed(`Failed to tag '${tagName}'`);
return;
}
// Create reference to tag
const createdRef = yield octokit.rest.git.createRef({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
ref: `refs/tags/${tagName}`,
sha: createdTag.data.sha
});
if (createdRef.status === 201) {
core.info(`Created reference for '${tagName}'`);
}
else {
core.setFailed(`Failed to reference of tag '${tagName}'`);
return;
}
});
}
function addCollectionsMetadataFile(featuresMetadata, templatesMetadata, opts) {
function isDevcontainerCliAvailable(cliDebugMode = false) {
return __awaiter(this, void 0, void 0, function* () {
const p = path_1.default.join('.', 'devcontainer-collection.json');
const sourceInformation = getGitHubMetadata();
const metadata = {
sourceInformation,
features: featuresMetadata || [],
templates: templatesMetadata || []
};
// Write to the file
yield (0, exports.writeLocalFile)(p, JSON.stringify(metadata, undefined, 4));
if (opts.shouldPublishToOCI) {
pushCollectionsMetadataToOCI(p);
}
});
}
exports.addCollectionsMetadataFile = addCollectionsMetadataFile;
function pushArtifactToOCI(version, featureName, artifactPath) {
return __awaiter(this, void 0, void 0, function* () {
const exec = (0, util_1.promisify)(child_process.exec);
const versions = [version, '1.0', '1', 'latest']; // TODO: Generate semantic versions from 'version'
const sourceInfo = getGitHubMetadata();
yield Promise.all(versions.map((v) => __awaiter(this, void 0, void 0, function* () {
const ociRepo = `${sourceInfo.owner}/${sourceInfo.repo}/${featureName}:${v}`;
try {
const cmd = `oras push ghcr.io/${ociRepo} \
--manifest-config /dev/null:application/vnd.devcontainers \
./${artifactPath}:application/vnd.devcontainers.layer.v1+tar`;
yield exec(cmd);
core.info(`Pushed artifact to '${ociRepo}'`);
}
catch (error) {
if (error instanceof Error)
core.setFailed(`Failed to push '${ociRepo}': ${error.message}`);
}
})));
});
}
function pushCollectionsMetadataToOCI(collectionJsonPath) {
return __awaiter(this, void 0, void 0, function* () {
const exec = (0, util_1.promisify)(child_process.exec);
const sourceInfo = getGitHubMetadata();
const ociRepo = `${sourceInfo.owner}/${sourceInfo.repo}:latest`;
try {
const cmd = `oras push ghcr.io/${ociRepo} \
--manifest-config /dev/null:application/vnd.devcontainers \
./${collectionJsonPath}:application/vnd.devcontainers.collection.layer.v1+json`;
yield exec(cmd);
core.info(`Pushed collection metadata to '${ociRepo}'`);
let cmd = 'devcontainer';
let args = ['--version'];
if (cliDebugMode) {
cmd = 'npx';
args = ['-y', './devcontainer.tgz', ...args];
}
const res = yield exec.getExecOutput(cmd, args, {
ignoreReturnCode: true,
silent: true
});
core.info(`Devcontainer CLI version '${res.stdout}' is installed.`);
return res.exitCode === 0;
}
catch (error) {
if (error instanceof Error)
core.setFailed(`Failed to push collection metadata '${ociRepo}': ${error.message}`);
catch (err) {
return false;
}
});
}
exports.pushCollectionsMetadataToOCI = pushCollectionsMetadataToOCI;
function loginToGHCR() {
exports.isDevcontainerCliAvailable = isDevcontainerCliAvailable;
function ensureDevcontainerCliPresent(cliDebugMode = false) {
return __awaiter(this, void 0, void 0, function* () {
const exec = (0, util_1.promisify)(child_process.exec);
// Get GITHUB_TOKEN from environment
const githubToken = process.env.GITHUB_TOKEN;
if (!githubToken) {
core.setFailed('GITHUB_TOKEN environment variable is not set.');
return;
if (yield isDevcontainerCliAvailable(cliDebugMode)) {
core.info('devcontainer CLI is already installed');
return true;
}
try {
yield exec(`oras login ghcr.io -u USERNAME -p ${githubToken}`);
core.info('Oras logged in successfully!');
if (cliDebugMode) {
core.error('Cannot remotely fetch CLI in debug mode');
return false;
}
catch (error) {
if (error instanceof Error)
core.setFailed(` Oras login failed!`);
}
});
}
function getFeaturesAndPackage(basePath, opts) {
return __awaiter(this, void 0, void 0, function* () {
const { shouldPublishToNPM, shouldTagIndividualFeatures, shouldPublishReleaseArtifacts, shouldPublishToOCI } = opts;
const featureDirs = fs.readdirSync(basePath);
let metadatas = [];
const exec = (0, util_1.promisify)(child_process.exec);
if (shouldPublishToOCI) {
yield loginToGHCR();
}
yield Promise.all(featureDirs.map((f) => __awaiter(this, void 0, void 0, function* () {
var _a;
core.info(`feature ==> ${f}`);
if (!f.startsWith('.')) {
const featureFolder = path_1.default.join(basePath, f);
const featureJsonPath = path_1.default.join(featureFolder, 'devcontainer-feature.json');
if (!fs.existsSync(featureJsonPath)) {
core.error(`Feature '${f}' is missing a devcontainer-feature.json`);
core.setFailed('All features must have a devcontainer-feature.json');
return;
}
const featureMetadata = JSON.parse(fs.readFileSync(featureJsonPath, 'utf8'));
if (!featureMetadata.id || !featureMetadata.version) {
core.error(`Feature '${f}' is must defined an id and version`);
core.setFailed('Incomplete devcontainer-feature.json');
}
metadatas.push(featureMetadata);
const sourceInfo = getGitHubMetadata();
if (!sourceInfo.owner) {
core.setFailed('Could not determine repository owner.');
return;
}
const archiveName = `${f}.tgz`;
// ---- PUBLISH RELEASE ARTIFACTS (classic method) ----
if (shouldPublishReleaseArtifacts || shouldPublishToOCI) {
core.info(`** Tar'ing feature`);
yield tarDirectory(featureFolder, archiveName);
}
// ---- PUBLISH TO NPM ----
if (shouldPublishToOCI) {
core.info(`** Publishing to OCI`);
// TODO: CHECK IF THE FEATURE IS ALREADY PUBLISHED UNDER GIVEN TAG
yield pushArtifactToOCI(featureMetadata.version, f, archiveName);
}
// ---- TAG INDIVIDUAL FEATURES ----
if (shouldTagIndividualFeatures) {
core.info(`** Tagging individual feature`);
yield tagFeatureAtVersion(featureMetadata);
}
// ---- PUBLISH TO NPM ----
if (shouldPublishToNPM) {
core.info(`** Publishing to NPM`);
// Adds a package.json file to the feature folder
const packageJsonPath = path_1.default.join(featureFolder, 'package.json');
// if (!sourceInfo.tag) {
// core.error(`Feature ${f} is missing a tag! Cannot publish to NPM.`);
// core.setFailed('All features published to NPM must be tagged with a version');
// }
const packageJsonObject = {
name: `@${sourceInfo.owner}/${f}`,
version: featureMetadata.version,
description: `${(_a = featureMetadata.description) !== null && _a !== void 0 ? _a : 'My cool feature'}`,
author: `${sourceInfo.owner}`,
keywords: ['devcontainer-features']
};
yield (0, exports.writeLocalFile)(packageJsonPath, JSON.stringify(packageJsonObject, undefined, 4));
core.info(`Feature Folder is: ${featureFolder}`);
// Run npm pack, which 'tars' the folder
const packageName = yield exec(`npm pack ./${featureFolder}`);
if (packageName.stderr) {
core.error(`${packageName.stderr.toString()}`);
}
const publishOutput = yield exec(`npm publish --access public "${packageName.stdout.trim()}"`);
core.info(publishOutput.stdout);
if (publishOutput.stderr) {
core.error(`${publishOutput.stderr}`);
}
}
}
})));
if (metadatas.length === 0) {
core.setFailed('No features found');
return;
try {
core.info('Fetching the latest @devcontainer/cli...');
const res = yield exec.getExecOutput('npm', ['install', '-g', '@devcontainers/cli'], {
ignoreReturnCode: true,
silent: true
});
return res.exitCode === 0;
}
return metadatas;
});
}
exports.getFeaturesAndPackage = getFeaturesAndPackage;
function getTemplatesAndPackage(basePath) {
return __awaiter(this, void 0, void 0, function* () {
const templateDirs = fs.readdirSync(basePath);
let metadatas = [];
yield Promise.all(templateDirs.map((t) => __awaiter(this, void 0, void 0, function* () {
core.info(`template ==> ${t}`);
if (!t.startsWith('.')) {
const templateFolder = path_1.default.join(basePath, t);
const archiveName = `devcontainer-template-${t}.tgz`;
// await tarDirectory(templateFolder, archiveName);
const templateJsonPath = path_1.default.join(templateFolder, 'devcontainer-template.json');
if (!fs.existsSync(templateJsonPath)) {
core.error(`Template '${t}' is missing a devcontainer-template.json`);
core.setFailed('All templates must have a devcontainer-template.json');
return;
}
const templateMetadata = JSON.parse(fs.readFileSync(templateJsonPath, 'utf8'));
metadatas.push(templateMetadata);
}
})));
if (metadatas.length === 0) {
core.setFailed('No templates found');
return;
catch (err) {
return false;
}
return metadatas;
});
}
exports.getTemplatesAndPackage = getTemplatesAndPackage;
exports.ensureDevcontainerCliPresent = ensureDevcontainerCliPresent;
......@@ -8,20 +8,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Oras
run: |
curl -LO https://github.com/oras-project/oras/releases/download/v0.13.0/oras_0.13.0_linux_amd64.tar.gz
mkdir -p oras-install/
tar -zxf oras_0.13.0_*.tar.gz -C oras-install/
mv oras-install/oras /usr/local/bin/
rm -rf oras_0.13.0_*.tar.gz oras-install/
- name: "Publish features to OCI"
uses: ./.github/devcontainers-action # TODO: Once 'devcontainers/action' is published, use that.
with:
publish-features: "true"
publish-to-oci: "true"
base-path-to-features: "./src"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment