Add chart-releaser action

Signed-off-by: Reinhard Naegele <unguiculus@gmail.com>
This commit is contained in:
Reinhard Naegele
2019-11-22 16:00:54 +01:00
commit b000906c7a
7 changed files with 375 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
name: "chart-releaser action"
description: "Run the chart-releaser tool"
author: "unguiculus"
inputs:
charts-dir:
description: The charts directory
default: charts
charts-repo-url:
description: "The GitHub Pages URL to the charts repo (default: https://<owner>.github.io/<repo>)"
token:
description: The GitHub token
runs:
using: "node12"
main: "lib/main.js"
+77
View File
@@ -0,0 +1,77 @@
{
"name": "chart-testing",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@actions/core": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.0.tgz",
"integrity": "sha512-ZKdyhlSlyz38S6YFfPnyNgCDZuAF2T0Qv5eHflNWytPS8Qjvz39bZFMry9Bb/dpSnqWcNeav5yM2CTYpJeY+Dw=="
},
"@actions/exec": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.1.tgz",
"integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ=="
},
"@actions/io": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.1.tgz",
"integrity": "sha512-rhq+tfZukbtaus7xyUtwKfuiCRXd1hWSfmJNEpFgBQJ4woqPEpsBw04awicjwz9tyG2/MVhAEMfVn664Cri5zA=="
},
"@actions/tool-cache": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.1.2.tgz",
"integrity": "sha512-IJczPaZr02ECa3Lgws/TJEVco9tjOujiQSZbO3dHuXXjhd5vrUtfOgGwhmz3/f97L910OraPZ8SknofUk6RvOQ==",
"requires": {
"@actions/core": "^1.1.0",
"@actions/exec": "^1.0.1",
"@actions/io": "^1.0.1",
"semver": "^6.1.0",
"typed-rest-client": "^1.4.0",
"uuid": "^3.3.2"
}
},
"@types/node": {
"version": "12.12.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.8.tgz",
"integrity": "sha512-XLla8N+iyfjvsa0KKV+BP/iGSoTmwxsu5Ci5sM33z9TjohF72DEz95iNvD6pPmemvbQgxAv/909G73gUn8QR7w==",
"dev": true
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"tunnel": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz",
"integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM="
},
"typed-rest-client": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.5.0.tgz",
"integrity": "sha512-DVZRlmsfnTjp6ZJaatcdyvvwYwbWvR4YDNFDqb+qdTxpvaVP99YCpBkA8rxsLtAPjBVoDe4fNsnMIdZTiPuKWg==",
"requires": {
"tunnel": "0.0.4",
"underscore": "1.8.3"
}
},
"typescript": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz",
"integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==",
"dev": true
},
"underscore": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
"integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
},
"uuid": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
}
}
}
+22
View File
@@ -0,0 +1,22 @@
{
"name": "chart-testing",
"version": "0.0.0",
"description": "kind GitHub action",
"main": "lib/main.js",
"scripts": {
"build": "tsc",
"test": "jest"
},
"author": "The Helm Authors",
"license": "Apache",
"dependencies": {
"@actions/core": "^1.2.0",
"@actions/tool-cache": "^1.1.2",
"@actions/exec": "^1.0.1",
"@actions/io": "^1.0.1"
},
"devDependencies": {
"@types/node": "^12.12.8",
"typescript": "^3.7.2"
}
}
+87
View File
@@ -0,0 +1,87 @@
// Copyright The Helm Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import * as exec from '@actions/exec';
import * as io from '@actions/io';
import {cr, git, helm} from "./tools";
export class ChartReleaser {
constructor(private readonly owner: string, private readonly repository: string,
private readonly chartRepoUrl: string, private readonly token: string) {
if (owner === "") {
throw new Error("owner is required")
}
if (repository === "") {
throw new Error("repository is required")
}
if (chartRepoUrl == "") {
this.chartRepoUrl = `https://${this.owner}.github.io/${this.repository}`
}
if (token === "") {
throw new Error("token is required")
}
}
async execute(charts: Set<string>) {
if (charts.size === 0) {
console.log("Charts set is emtpy. Nothing to do.")
return
}
await io.rmRF(".cr-release-packages");
await io.mkdirP(".cr-release-packages");
await io.rmRF(".cr-index");
await io.mkdirP(".cr-index");
for (const chart of charts) {
await this.packageChart(chart)
}
await this.releaseCharts();
await this.updateIndex();
}
private async packageChart(chart: string) {
console.log(`Packacking chart '${chart}'...`);
await helm("package", chart, "--destination", ".cr-release-packages", "--save=false", "--dependency-update");
}
private async releaseCharts() {
console.log("Releasing charts...");
await cr(this.token, "upload", "-o", this.owner, "-r", this.repository);
}
private async updateIndex() {
console.log("Updating repo index...");
await cr(this.token, "index", "-o", this.owner, "-r", this.repository, "-c", this.chartRepoUrl);
await git("checkout", "gh-pages");
await exec.exec("cp", ["--force", ".cr-index/index.yaml", "index.yaml"]);
const actor = process.env["GITHUB_ACTOR"] || "";
await git("config", "--local", "user.name", actor);
await git("config", "--local", "user.email", "noreply@github.com");
await git("add", "index.yaml");
await git("commit", "--message='Update index.yaml'", "--signoff");
const repoUrl = `https://x-access-token:${this.token}@github.com/${this.owner}/${this.repository}`;
await git("push", repoUrl, "gh-pages");
}
}
+90
View File
@@ -0,0 +1,90 @@
// Copyright The Helm Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import * as core from '@actions/core';
import {ChartReleaser} from "./cr";
import {git} from "./tools";
const ChartsDirInput = "charts-dir";
const ChartsRepoUrlInput = "charts-repo-url";
const TokenInput = "token";
export function createChartReleaser(): ChartReleaser {
const ownerRepo = process.env["GITHUB_REPOSITORY"] || "";
const split = ownerRepo.split("/");
const owner = split[0];
const repo = split[1];
const chartsRepoUrl = core.getInput(ChartsRepoUrlInput);
const token = core.getInput(TokenInput);
return new ChartReleaser(owner, repo, chartsRepoUrl, token)
}
async function run() {
try {
const workspace = process.env["GITHUB_WORKSPACE"];
if (workspace !== process.cwd()) {
core.setFailed("action must be run in the workspace root");
return
}
let chartsDir = core.getInput(ChartsDirInput);
if (chartsDir === "") {
chartsDir = "charts"
}
console.log("Looking up latest tag...");
const tag = await findLatestTag();
console.log(`Identifying changed charts since ${tag}...`)
const charts = await findChangedCharts(chartsDir, tag);
if (charts.size == 0) {
console.log("Nothing to do. No chart changes detected.")
return
}
const cr = createChartReleaser();
await cr.execute(charts)
} catch (error) {
core.setFailed(error.message);
}
}
async function findLatestTag(): Promise<string> {
let tag: string;
try {
tag = await git("describe", "--tags", "--abbrev=0");
} catch (ex) {
console.log("No tag found in repo. Getting first commit instead...");
tag = await git("rev-list", "--max-parents=0", "--first-parent", "HEAD");
}
return tag.trim()
}
export async function findChangedCharts(chartsDir: string, ref: string): Promise<Set<string>> {
let changes = await git("diff", "--find-renames", "--name-only", ref, "--", chartsDir);
const set = new Set<string>();
changes = changes.trim();
if (changes) {
for (let change of changes.split("\n")) {
const split = change.split("/");
set.add(split[0] + "/" + split[1])
}
}
return set
}
run();
+61
View File
@@ -0,0 +1,61 @@
import * as exec from '@actions/exec';
import * as io from '@actions/io';
import fs from 'fs';
export async function git(...args: string[]): Promise<string> {
let output = '';
const options = {};
// @ts-ignore
options.listeners = {
stdout: (data: Buffer) => {
output += data.toString();
}
};
await exec.exec("git", args, options);
return output
}
export async function helm(...args: string[]) {
const home = process.env["HOME"];
const workspace = process.env["GITHUB_WORKSPACE"];
const dockerArgs = [
"run",
"--interactive",
"--rm",
"--volume", `${home}/.helm:/root/.helm`,
"--volume", `${workspace}:/workdir`,
"--workdir", "/workdir",
"lachlanevenson/k8s-helm:v2.16.1"
];
if (!fs.existsSync(`${home}/.helm`)) {
await io.mkdirP(`${home}/.helm`);
await exec.exec("docker", dockerArgs.concat("init", "--client-only"))
}
await exec.exec("docker", dockerArgs.concat(args));
}
export async function cr(token: string, ...args: string[]) {
const workspace = process.env["GITHUB_WORKSPACE"];
const options = {};
// @ts-ignore
options.env = {
CR_TOKEN: token
};
const dockerArgs = [
"run",
"--interactive",
"--rm",
"--env", "CR_TOKEN",
"--volume", `${workspace}:/workdir`,
"--workdir", "/workdir",
"quay.io/helmpack/chart-releaser:v0.2.3"
];
await exec.exec("docker", dockerArgs.concat("cr", args), options);
}
+24
View File
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"outDir": "./lib",
"rootDir": "./src",
"strict": true,
"noImplicitAny": false,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true
},
"exclude": [
"node_modules"
]
}