Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding graph ql for Helm versions - master #22

Merged
merged 8 commits into from Mar 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion 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'
Expand Down
97 changes: 85 additions & 12 deletions 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) {
Expand All @@ -10,23 +30,22 @@ 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"));
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/)) {
Expand Down Expand Up @@ -71,6 +90,7 @@ function getStableHelmVersion() {
return stableHelmVersion;
});
}
exports.getStableHelmVersion = getStableHelmVersion;
var walkSync = function (dir, filelist, fileToFind) {
var files = fs.readdirSync(dir);
filelist = filelist || [];
Expand All @@ -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) {
Expand All @@ -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 = [];
Expand All @@ -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))) {
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Expand Up @@ -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",
Expand Down
71 changes: 65 additions & 6 deletions 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';
Expand All @@ -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 {
Expand All @@ -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);

}
}

Expand Down Expand Up @@ -61,7 +66,6 @@ export async function getStableHelmVersion(): Promise<string> {
return stableHelmVersion;
}


export var walkSync = function (dir, filelist, fileToFind) {
var files = fs.readdirSync(dir);
filelist = filelist || [];
Expand Down Expand Up @@ -104,6 +108,49 @@ export async function downloadHelm(version: string): Promise<string> {
return helmpath;
}

async function getLatestHelmVersionFor(type: string): Promise<string> {
const token = core.getInput('token', { 'required': true });
try {
const { repository } = await graphql(
`{
shigupt202 marked this conversation as resolved.
Show resolved Hide resolved
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));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We expect latest release to be the latest version for the type?
This seems to be a behavior change compared to the older logic which allowed all releases to compete to be the latest:

responseArray.forEach(response => {
   if (response && response.tag_name) {
       let currentHelmVerison = semver.clean(response.tag_name.toString());
       if (currentHelmVerison) {
           if (currentHelmVerison.toString().indexOf('rc') == -1 && semver.gt(currentHelmVerison, latestHelmVersion)) {
               //If current helm version is not a pre release and is greater than latest helm version
               latestHelmVersion = currentHelmVerison;
           }
       }
   }
});

Seems like a fair assumption to me. But I think this also presses the need for feature flagging to be on the safe side. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. But earlier we were comparing the versions so that we could get the last 3.* release of Helm. It's possible that they released a 2.* version after their latest Helm 3 release, and we didn't want to pick that up.
We can still assume that among all Helm 3.* releases, the last one would be the latest, and same goes for Helm 2.*.
Anyway, to be on the safe side I'll add a feature flag probably.

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[] = [];
Expand All @@ -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 {
Expand Down