diff --git a/action.yml b/action.yml index 4c1e06f0..e0728254 100644 --- a/action.yml +++ b/action.yml @@ -1,10 +1,14 @@ name: 'Helm tool installer' description: 'Install a specific version of helm binary. Acceptable values are latest or any semantic version string like 1.15.0' -inputs: +inputs: version: description: 'Version of helm' required: true default: 'latest' + token: + description: 'Github token' + default: ${{ github.token }} + required: true outputs: helm-path: description: 'Path to the cached helm binary' diff --git a/lib/run.js b/lib/run.js index 550c95fa..99539536 100644 --- a/lib/run.js +++ b/lib/run.js @@ -1,6 +1,26 @@ "use strict"; // Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -10,14 +30,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); +exports.getStableHelmVersion = void 0; const os = __importStar(require("os")); const path = __importStar(require("path")); const util = __importStar(require("util")); @@ -25,8 +39,13 @@ const fs = __importStar(require("fs")); const semver = __importStar(require("semver")); const toolCache = __importStar(require("@actions/tool-cache")); const core = __importStar(require("@actions/core")); +const graphql_1 = require("@octokit/graphql"); const helmToolName = 'helm'; const stableHelmVersion = 'v3.2.1'; +const stableHelm3Version = 'v3.5.3'; +const stableHelm2Version = 'v2.17.0'; +const LATEST_HELM2_VERSION = '2.*'; +const LATEST_HELM3_VERSION = '3.*'; const helmAllReleasesUrl = 'https://api.github.com/repos/helm/helm/releases'; function getExecutableExtension() { if (os.type().match(/^Win/)) { @@ -71,6 +90,7 @@ function getStableHelmVersion() { return stableHelmVersion; }); } +exports.getStableHelmVersion = getStableHelmVersion; var walkSync = function (dir, filelist, fileToFind) { var files = fs.readdirSync(dir); filelist = filelist || []; @@ -90,7 +110,7 @@ var walkSync = function (dir, filelist, fileToFind) { function downloadHelm(version) { return __awaiter(this, void 0, void 0, function* () { if (!version) { - version = yield getStableHelmVersion(); + version = yield getLatestHelmVersionFor("v3"); } let cachedToolpath = toolCache.find(helmToolName, version); if (!cachedToolpath) { @@ -113,6 +133,45 @@ function downloadHelm(version) { return helmpath; }); } +function getLatestHelmVersionFor(type) { + return __awaiter(this, void 0, void 0, function* () { + const token = core.getInput('token', { 'required': true }); + try { + const { repository } = yield graphql_1.graphql(`{ + repository(name: "helm", owner: "helm") { + releases(last: 100) { + nodes { + tagName + } + } + } + }`, { + headers: { + authorization: `token ${token}` + } + }); + const releases = repository.releases.nodes.reverse(); + let latestValidRelease = releases.find(release => isValidVersion(release.tagName, type)); + if (latestValidRelease) + return latestValidRelease.tagName; + } + catch (err) { + core.warning(util.format("Error while fetching the latest Helm %s release. Error: %s. Using default Helm version %s.", type, err.toString(), getStableHelmVersionFor(type))); + return getStableHelmVersionFor(type); + } + core.warning(util.format("Could not find stable release for Helm %s. Using default Helm version %s.", type, getStableHelmVersionFor(type))); + return getStableHelmVersionFor(type); + }); +} +function getStableHelmVersionFor(type) { + return type === "v2" ? stableHelm2Version : stableHelm3Version; +} +// isValidVersion checks if verison matches the specified type and is a stable release +function isValidVersion(version, type) { + if (!version.toLocaleLowerCase().startsWith(type)) + return false; + return version.indexOf('rc') == -1; +} function findHelm(rootFolder) { fs.chmodSync(rootFolder, '777'); var filelist = []; @@ -127,12 +186,26 @@ function findHelm(rootFolder) { function run() { return __awaiter(this, void 0, void 0, function* () { let version = core.getInput('version', { 'required': true }); - if (version.toLocaleLowerCase() === 'latest') { - version = yield getStableHelmVersion(); + if (process.env['HELM_INSTALLER_LEGACY_VERSIONING'] == 'true') { + if (version.toLocaleLowerCase() === 'latest') { + version = yield getStableHelmVersion(); + } + else if (!version.toLocaleLowerCase().startsWith('v')) { + version = 'v' + version; + } } - else if (!version.toLocaleLowerCase().startsWith('v')) { - version = 'v' + version; + else { + if (version.toLocaleLowerCase() === 'latest' || version === LATEST_HELM3_VERSION) { + version = yield getLatestHelmVersionFor("v3"); + } + else if (version === LATEST_HELM2_VERSION) { + version = yield getLatestHelmVersionFor("v2"); + } + else if (!version.toLocaleLowerCase().startsWith('v')) { + version = 'v' + version; + } } + core.debug(util.format("Downloading %s", version)); let cachedPath = yield downloadHelm(version); try { if (!process.env['PATH'].startsWith(path.dirname(cachedPath))) { diff --git a/package-lock.json b/package-lock.json index eed33cf1..d37aae92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6166,4 +6166,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index edc3171b..e91075ed 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,11 @@ "author": "Anumita Shenoy", "license": "MIT", "dependencies": { - "@actions/tool-cache": "1.1.2", - "@actions/io": "^1.0.0", "@actions/core": "^1.2.6", "@actions/exec": "^1.0.0", + "@actions/io": "^1.0.0", + "@actions/tool-cache": "1.1.2", + "@octokit/graphql": "^4.6.1", "semver": "^6.1.0" }, "main": "lib/run.js", diff --git a/src/run.ts b/src/run.ts index 767ca611..616d1b5b 100644 --- a/src/run.ts +++ b/src/run.ts @@ -1,4 +1,5 @@ // Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. import * as os from 'os'; @@ -9,9 +10,14 @@ import * as semver from 'semver'; import * as toolCache from '@actions/tool-cache'; import * as core from '@actions/core'; +import { graphql } from '@octokit/graphql'; const helmToolName = 'helm'; const stableHelmVersion = 'v3.2.1'; +const stableHelm3Version = 'v3.5.3'; +const stableHelm2Version = 'v2.17.0'; +const LATEST_HELM2_VERSION = '2.*'; +const LATEST_HELM3_VERSION = '3.*'; const helmAllReleasesUrl = 'https://api.github.com/repos/helm/helm/releases'; export function getExecutableExtension(): string { @@ -32,7 +38,6 @@ export function getHelmDownloadURL(version: string): string { case 'Windows_NT': default: return util.format('https://get.helm.sh/helm-%s-windows-amd64.zip', version); - } } @@ -61,7 +66,6 @@ export async function getStableHelmVersion(): Promise { return stableHelmVersion; } - export var walkSync = function (dir, filelist, fileToFind) { var files = fs.readdirSync(dir); filelist = filelist || []; @@ -104,6 +108,49 @@ export async function downloadHelm(version: string): Promise { return helmpath; } +async function getLatestHelmVersionFor(type: string): Promise { + const token = core.getInput('token', { 'required': true }); + try { + const { repository } = await graphql( + `{ + repository(name: "helm", owner: "helm") { + releases(last: 100) { + nodes { + tagName + } + } + } + }`, + { + headers: { + authorization: `token ${token}` + } + } + ); + + const releases = repository.releases.nodes.reverse(); + let latestValidRelease = releases.find(release => isValidVersion(release.tagName, type)); + if (latestValidRelease) + return latestValidRelease.tagName; + } catch (err) { + core.warning(util.format("Error while fetching the latest Helm %s release. Error: %s. Using default Helm version %s.", type, err.toString(), getStableHelmVersionFor(type))); + return getStableHelmVersionFor(type); + } + core.warning(util.format("Could not find stable release for Helm %s. Using default Helm version %s.", type, getStableHelmVersionFor(type))); + return getStableHelmVersionFor(type); +} + +function getStableHelmVersionFor(type: string) { + return type === "v2" ? stableHelm2Version : stableHelm3Version; +} + +// isValidVersion checks if verison matches the specified type and is a stable release +function isValidVersion(version: string, type: string): boolean { + if (!version.toLocaleLowerCase().startsWith(type)) + return false; + return version.indexOf('rc') == -1; +} + export function findHelm(rootFolder: string): string { fs.chmodSync(rootFolder, '777'); var filelist: string[] = []; @@ -118,12 +165,24 @@ export function findHelm(rootFolder: string): string { export async function run() { let version = core.getInput('version', { 'required': true }); - if (version.toLocaleLowerCase() === 'latest') { - version = await getStableHelmVersion(); - } else if (!version.toLocaleLowerCase().startsWith('v')) { - version = 'v' + version; + + if (process.env['HELM_INSTALLER_LEGACY_VERSIONING'] == 'true') { + if (version.toLocaleLowerCase() === 'latest') { + version = await getStableHelmVersion(); + } else if (!version.toLocaleLowerCase().startsWith('v')) { + version = 'v' + version; + } + } else { + if (version.toLocaleLowerCase() === 'latest' || version === LATEST_HELM3_VERSION) { + version = await getLatestHelmVersionFor("v3"); + } else if (version === LATEST_HELM2_VERSION) { + version = await getLatestHelmVersionFor("v2"); + } else if (!version.toLocaleLowerCase().startsWith('v')) { + version = 'v' + version; + } } + core.debug(util.format("Downloading %s", version)); let cachedPath = await downloadHelm(version); try {