diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 420a883..b624be6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @tauhid621 @kaverma +* @aksatlanta \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug-report-feature-request.md b/.github/ISSUE_TEMPLATE/bug-report-feature-request.md index 214d54b..acf70c3 100644 --- a/.github/ISSUE_TEMPLATE/bug-report-feature-request.md +++ b/.github/ISSUE_TEMPLATE/bug-report-feature-request.md @@ -3,7 +3,7 @@ name: Bug Report / Feature Request about: Create a report to help us improve title: '' labels: need-to-triage -assignees: '' +assignees: '@aksatlanta' --- diff --git a/.github/workflows/TriggerIntegrationTests.sh b/.github/workflows/TriggerIntegrationTests.sh deleted file mode 100644 index 8d9a4ad..0000000 --- a/.github/workflows/TriggerIntegrationTests.sh +++ /dev/null @@ -1,33 +0,0 @@ -token=$1 -commit=$2 -repository=$3 -prNumber=$4 -frombranch=$5 -tobranch=$6 -patUser=$6 - -getPayLoad() { - cat < x.trim()); + const branch = process.env["BRANCH"]; + const splitTag = (x) => + x + .substring(branch.length + 1) + .split(".") + .map((x) => Number(x)); + function compareTags(nums1, nums2, position = 0) { + if (nums1.length < position && nums2.length < position) return nums2; + const num1 = splitTag(nums1)[position] || 0; + const num2 = splitTag(nums2)[position] || 0; + if (num1 === num2) return compareTags(nums1, nums2, position + 1); + else if (num1 > num2) return nums1; + else return nums2; + } + const branchTags = tags.filter((tag) => tag.startsWith(branch)); + if (branchTags.length < 1) return branch + ".-1" + return branchTags.reduce((prev, curr) => compareTags(prev, curr)); + result-encoding: string + id: get-latest-tag + - name: Get new tag + uses: actions/github-script@v5 + env: + PREV: ${{ steps.get-latest-tag.outputs.result }} + with: + script: | + let version = process.env["PREV"] + if (!version.includes(".")) version += ".0"; // case of v1 or v2 + const prefix = /^([a-zA-Z]+)/.exec(version)[0]; + const numbers = version.substring(prefix.length); + let split = numbers.split("."); + split[split.length - 1] = parseInt(split[split.length - 1]) + 1; + return prefix + split.join("."); + result-encoding: string + id: get-new-tag + - uses: "marvinpinto/action-automatic-releases@v1.2.1" + with: + title: ${{ steps.get-new-tag.outputs.result }} release + automatic_release_tag: ${{ steps.get-new-tag.outputs.result }} + repo_token: "${{ secrets.GITHUB_TOKEN }}" + draft: true \ No newline at end of file diff --git a/.github/workflows/ts-build-check.yml b/.github/workflows/ts-build-check.yml new file mode 100644 index 0000000..ff431f4 --- /dev/null +++ b/.github/workflows/ts-build-check.yml @@ -0,0 +1,41 @@ +name: TypeScript Build Check + +on: pull_request + +jobs: + ts-build-check: + runs-on: ubuntu-latest + steps: + - name: Checkout Pull Request + uses: actions/checkout@v2 + with: + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + path: original-pr + - name: Setup Node + uses: actions/setup-node@v1 + with: + node-version: 12.x + - name: Clone and Build Pull Request + run: | + cp $GITHUB_WORKSPACE/original-pr/ $GITHUB_WORKSPACE/built-pr -r + cd $GITHUB_WORKSPACE/built-pr/ + npm i + npm run build + - name: Compare Built Directories + id: diff + run: | + DIFF=$(diff $GITHUB_WORKSPACE/original-pr/lib $GITHUB_WORKSPACE/built-pr/lib -rqiEZbwBd) + if [ "$DIFF" != "" ]; then exit 1; else echo -e "PR contains up-to-date compiled JavaScript."; fi + - name: Comment Unbuilt TypeScript + if: failure() && steps.diff.outcome == 'failure' + uses: actions/github-script@v2 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + github.issues.createComment({ + issue_number: ${{ github.event.number }}, + owner: context.repo.owner, + repo: context.repo.repo, + body: 'Please compile the TypeScript code with `npm run build`. The compiled JavaScript is not up-to-date.' + }) \ No newline at end of file diff --git a/lib/helpers.js b/lib/helpers.js new file mode 100644 index 0000000..2273a05 --- /dev/null +++ b/lib/helpers.js @@ -0,0 +1,32 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getExecutableExtension = exports.getkubectlDownloadURL = exports.getKubectlArch = void 0; +const os = require("os"); +const util = require("util"); +function getKubectlArch() { + const arch = os.arch(); + if (arch === 'x64') { + return 'amd64'; + } + return arch; +} +exports.getKubectlArch = getKubectlArch; +function getkubectlDownloadURL(version, arch) { + switch (os.type()) { + case 'Linux': + return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/%s/kubectl', version, arch); + case 'Darwin': + return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/darwin/%s/kubectl', version, arch); + case 'Windows_NT': + default: + return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/windows/%s/kubectl.exe', version, arch); + } +} +exports.getkubectlDownloadURL = getkubectlDownloadURL; +function getExecutableExtension() { + if (os.type().match(/^Win/)) { + return '.exe'; + } + return ''; +} +exports.getExecutableExtension = getExecutableExtension; diff --git a/lib/run.js b/lib/run.js index eb2ec23..1b8ede7 100644 --- a/lib/run.js +++ b/lib/run.js @@ -1,89 +1,76 @@ -"use strict"; -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) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.run = exports.downloadKubectl = exports.getStableKubectlVersion = exports.getkubectlDownloadURL = exports.getExecutableExtension = void 0; -const os = require("os"); -const path = require("path"); -const util = require("util"); -const fs = require("fs"); -const toolCache = require("@actions/tool-cache"); -const core = require("@actions/core"); -const kubectlToolName = 'kubectl'; -const stableKubectlVersion = 'v1.15.0'; -const stableVersionUrl = 'https://storage.googleapis.com/kubernetes-release/release/stable.txt'; -function getExecutableExtension() { - if (os.type().match(/^Win/)) { - return '.exe'; - } - return ''; -} -exports.getExecutableExtension = getExecutableExtension; -function getkubectlDownloadURL(version) { - switch (os.type()) { - case 'Linux': - return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/amd64/kubectl', version); - case 'Darwin': - return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/darwin/amd64/kubectl', version); - case 'Windows_NT': - default: - return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/windows/amd64/kubectl.exe', version); - } -} -exports.getkubectlDownloadURL = getkubectlDownloadURL; -function getStableKubectlVersion() { - return __awaiter(this, void 0, void 0, function* () { - return toolCache.downloadTool(stableVersionUrl).then((downloadPath) => { - let version = fs.readFileSync(downloadPath, 'utf8').toString().trim(); - if (!version) { - version = stableKubectlVersion; - } - return version; - }, (error) => { - core.debug(error); - core.warning('GetStableVersionFailed'); - return stableKubectlVersion; - }); - }); -} -exports.getStableKubectlVersion = getStableKubectlVersion; -function downloadKubectl(version) { - return __awaiter(this, void 0, void 0, function* () { - let cachedToolpath = toolCache.find(kubectlToolName, version); - let kubectlDownloadPath = ''; - if (!cachedToolpath) { - try { - kubectlDownloadPath = yield toolCache.downloadTool(getkubectlDownloadURL(version)); - } - catch (exception) { - throw new Error('DownloadKubectlFailed'); - } - cachedToolpath = yield toolCache.cacheFile(kubectlDownloadPath, kubectlToolName + getExecutableExtension(), kubectlToolName, version); - } - const kubectlPath = path.join(cachedToolpath, kubectlToolName + getExecutableExtension()); - fs.chmodSync(kubectlPath, '777'); - return kubectlPath; - }); -} -exports.downloadKubectl = downloadKubectl; -function run() { - return __awaiter(this, void 0, void 0, function* () { - let version = core.getInput('version', { 'required': true }); - if (version.toLocaleLowerCase() === 'latest') { - version = yield getStableKubectlVersion(); - } - let cachedPath = yield downloadKubectl(version); - core.addPath(path.dirname(cachedPath)); - console.log(`Kubectl tool version: '${version}' has been cached at ${cachedPath}`); - core.setOutput('kubectl-path', cachedPath); - }); -} -exports.run = run; -run().catch(core.setFailed); +"use strict"; +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) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.downloadKubectl = exports.getStableKubectlVersion = exports.run = void 0; +const path = require("path"); +const util = require("util"); +const fs = require("fs"); +const toolCache = require("@actions/tool-cache"); +const core = require("@actions/core"); +const helpers_1 = require("./helpers"); +const kubectlToolName = 'kubectl'; +const stableKubectlVersion = 'v1.15.0'; +const stableVersionUrl = 'https://storage.googleapis.com/kubernetes-release/release/stable.txt'; +function run() { + return __awaiter(this, void 0, void 0, function* () { + let version = core.getInput('version', { 'required': true }); + if (version.toLocaleLowerCase() === 'latest') { + version = yield getStableKubectlVersion(); + } + const cachedPath = yield downloadKubectl(version); + core.addPath(path.dirname(cachedPath)); + core.debug(`Kubectl tool version: '${version}' has been cached at ${cachedPath}`); + core.setOutput('kubectl-path', cachedPath); + }); +} +exports.run = run; +function getStableKubectlVersion() { + return __awaiter(this, void 0, void 0, function* () { + return toolCache.downloadTool(stableVersionUrl).then((downloadPath) => { + let version = fs.readFileSync(downloadPath, 'utf8').toString().trim(); + if (!version) { + version = stableKubectlVersion; + } + return version; + }, (error) => { + core.debug(error); + core.warning('GetStableVersionFailed'); + return stableKubectlVersion; + }); + }); +} +exports.getStableKubectlVersion = getStableKubectlVersion; +function downloadKubectl(version) { + return __awaiter(this, void 0, void 0, function* () { + let cachedToolpath = toolCache.find(kubectlToolName, version); + let kubectlDownloadPath = ''; + const arch = helpers_1.getKubectlArch(); + if (!cachedToolpath) { + try { + kubectlDownloadPath = yield toolCache.downloadTool(helpers_1.getkubectlDownloadURL(version, arch)); + } + catch (exception) { + if (exception instanceof toolCache.HTTPError && exception.httpStatusCode === 404) { + throw new Error(util.format("Kubectl '%s' for '%s' arch not found.", version, arch)); + } + else { + throw new Error('DownloadKubectlFailed'); + } + } + cachedToolpath = yield toolCache.cacheFile(kubectlDownloadPath, kubectlToolName + helpers_1.getExecutableExtension(), kubectlToolName, version); + } + const kubectlPath = path.join(cachedToolpath, kubectlToolName + helpers_1.getExecutableExtension()); + fs.chmodSync(kubectlPath, '777'); + return kubectlPath; + }); +} +exports.downloadKubectl = downloadKubectl; +run().catch(core.setFailed); diff --git a/lib/run.test.js b/lib/run.test.js new file mode 100644 index 0000000..3bd7914 --- /dev/null +++ b/lib/run.test.js @@ -0,0 +1,163 @@ +"use strict"; +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) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const run = require("./run"); +const helpers_1 = require("./helpers"); +const os = require("os"); +const toolCache = require("@actions/tool-cache"); +const fs = require("fs"); +const path = require("path"); +const core = require("@actions/core"); +const util = require("util"); +describe('Testing all functions in run file.', () => { + test('getExecutableExtension() - return .exe when os is Windows', () => { + jest.spyOn(os, 'type').mockReturnValue('Windows_NT'); + expect(helpers_1.getExecutableExtension()).toBe('.exe'); + expect(os.type).toBeCalled(); + }); + test('getExecutableExtension() - return empty string for non-windows OS', () => { + jest.spyOn(os, 'type').mockReturnValue('Darwin'); + expect(helpers_1.getExecutableExtension()).toBe(''); + expect(os.type).toBeCalled(); + }); + test.each([ + ['arm', 'arm'], + ['arm64', 'arm64'], + ['x64', 'amd64'] + ])("getKubectlArch() - return on %s os arch %s kubectl arch", (osArch, kubectlArch) => { + jest.spyOn(os, 'arch').mockReturnValue(osArch); + expect(helpers_1.getKubectlArch()).toBe(kubectlArch); + expect(os.arch).toBeCalled(); + }); + test.each([ + ['arm'], + ['arm64'], + ['amd64'] + ])('getkubectlDownloadURL() - return the URL to download %s kubectl for Linux', (arch) => { + jest.spyOn(os, 'type').mockReturnValue('Linux'); + const kubectlLinuxUrl = util.format('https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/linux/%s/kubectl', arch); + expect(helpers_1.getkubectlDownloadURL('v1.15.0', arch)).toBe(kubectlLinuxUrl); + expect(os.type).toBeCalled(); + }); + test.each([ + ['arm'], + ['arm64'], + ['amd64'] + ])('getkubectlDownloadURL() - return the URL to download %s kubectl for Darwin', (arch) => { + jest.spyOn(os, 'type').mockReturnValue('Darwin'); + const kubectlDarwinUrl = util.format('https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/darwin/%s/kubectl', arch); + expect(helpers_1.getkubectlDownloadURL('v1.15.0', arch)).toBe(kubectlDarwinUrl); + expect(os.type).toBeCalled(); + }); + test.each([ + ['arm'], + ['arm64'], + ['amd64'] + ])('getkubectlDownloadURL() - return the URL to download %s kubectl for Windows', (arch) => { + jest.spyOn(os, 'type').mockReturnValue('Windows_NT'); + const kubectlWindowsUrl = util.format('https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/windows/%s/kubectl.exe', arch); + expect(helpers_1.getkubectlDownloadURL('v1.15.0', arch)).toBe(kubectlWindowsUrl); + expect(os.type).toBeCalled(); + }); + test('getStableKubectlVersion() - download stable version file, read version and return it', () => __awaiter(void 0, void 0, void 0, function* () { + jest.spyOn(toolCache, 'downloadTool').mockReturnValue(Promise.resolve('pathToTool')); + jest.spyOn(fs, 'readFileSync').mockReturnValue('v1.20.4'); + expect(yield run.getStableKubectlVersion()).toBe('v1.20.4'); + expect(toolCache.downloadTool).toBeCalled(); + expect(fs.readFileSync).toBeCalledWith('pathToTool', 'utf8'); + })); + test('getStableKubectlVersion() - return default v1.15.0 if version read is empty', () => __awaiter(void 0, void 0, void 0, function* () { + jest.spyOn(toolCache, 'downloadTool').mockReturnValue(Promise.resolve('pathToTool')); + jest.spyOn(fs, 'readFileSync').mockReturnValue(''); + expect(yield run.getStableKubectlVersion()).toBe('v1.15.0'); + expect(toolCache.downloadTool).toBeCalled(); + expect(fs.readFileSync).toBeCalledWith('pathToTool', 'utf8'); + })); + test('getStableKubectlVersion() - return default v1.15.0 if unable to download file', () => __awaiter(void 0, void 0, void 0, function* () { + jest.spyOn(toolCache, 'downloadTool').mockRejectedValue('Unable to download.'); + expect(yield run.getStableKubectlVersion()).toBe('v1.15.0'); + expect(toolCache.downloadTool).toBeCalled(); + })); + test('downloadKubectl() - download kubectl, add it to toolCache and return path to it', () => __awaiter(void 0, void 0, void 0, function* () { + jest.spyOn(toolCache, 'find').mockReturnValue(''); + jest.spyOn(toolCache, 'downloadTool').mockReturnValue(Promise.resolve('pathToTool')); + jest.spyOn(toolCache, 'cacheFile').mockReturnValue(Promise.resolve('pathToCachedTool')); + jest.spyOn(os, 'type').mockReturnValue('Windows_NT'); + jest.spyOn(fs, 'chmodSync').mockImplementation(() => { }); + expect(yield run.downloadKubectl('v1.15.0')).toBe(path.join('pathToCachedTool', 'kubectl.exe')); + expect(toolCache.find).toBeCalledWith('kubectl', 'v1.15.0'); + expect(toolCache.downloadTool).toBeCalled(); + expect(toolCache.cacheFile).toBeCalled(); + expect(os.type).toBeCalled(); + expect(fs.chmodSync).toBeCalledWith(path.join('pathToCachedTool', 'kubectl.exe'), '777'); + })); + test('downloadKubectl() - throw DownloadKubectlFailed error when unable to download kubectl', () => __awaiter(void 0, void 0, void 0, function* () { + jest.spyOn(toolCache, 'find').mockReturnValue(''); + jest.spyOn(toolCache, 'downloadTool').mockRejectedValue('Unable to download kubectl.'); + yield expect(run.downloadKubectl('v1.15.0')).rejects.toThrow('DownloadKubectlFailed'); + expect(toolCache.find).toBeCalledWith('kubectl', 'v1.15.0'); + expect(toolCache.downloadTool).toBeCalled(); + })); + test('downloadKubectl() - throw kubectl not found error when receive 404 response', () => __awaiter(void 0, void 0, void 0, function* () { + const kubectlVersion = 'v1.15.0'; + const arch = 'arm128'; + jest.spyOn(os, 'arch').mockReturnValue(arch); + jest.spyOn(toolCache, 'find').mockReturnValue(''); + jest.spyOn(toolCache, 'downloadTool').mockImplementation(_ => { + throw new toolCache.HTTPError(404); + }); + yield expect(run.downloadKubectl(kubectlVersion)).rejects + .toThrow(util.format("Kubectl '%s' for '%s' arch not found.", kubectlVersion, arch)); + expect(os.arch).toBeCalled(); + expect(toolCache.find).toBeCalledWith('kubectl', kubectlVersion); + expect(toolCache.downloadTool).toBeCalled(); + })); + test('downloadKubectl() - return path to existing cache of kubectl', () => __awaiter(void 0, void 0, void 0, function* () { + jest.spyOn(toolCache, 'find').mockReturnValue('pathToCachedTool'); + jest.spyOn(os, 'type').mockReturnValue('Windows_NT'); + jest.spyOn(fs, 'chmodSync').mockImplementation(() => { }); + jest.spyOn(toolCache, 'downloadTool'); + expect(yield run.downloadKubectl('v1.15.0')).toBe(path.join('pathToCachedTool', 'kubectl.exe')); + expect(toolCache.find).toBeCalledWith('kubectl', 'v1.15.0'); + expect(os.type).toBeCalled(); + expect(fs.chmodSync).toBeCalledWith(path.join('pathToCachedTool', 'kubectl.exe'), '777'); + expect(toolCache.downloadTool).not.toBeCalled(); + })); + test('run() - download specified version and set output', () => __awaiter(void 0, void 0, void 0, function* () { + jest.spyOn(core, 'getInput').mockReturnValue('v1.15.5'); + jest.spyOn(toolCache, 'find').mockReturnValue('pathToCachedTool'); + jest.spyOn(os, 'type').mockReturnValue('Windows_NT'); + jest.spyOn(fs, 'chmodSync').mockImplementation(); + jest.spyOn(core, 'addPath').mockImplementation(); + jest.spyOn(console, 'log').mockImplementation(); + jest.spyOn(core, 'setOutput').mockImplementation(); + expect(yield run.run()).toBeUndefined(); + expect(core.getInput).toBeCalledWith('version', { 'required': true }); + expect(core.addPath).toBeCalledWith('pathToCachedTool'); + expect(core.setOutput).toBeCalledWith('kubectl-path', path.join('pathToCachedTool', 'kubectl.exe')); + })); + test('run() - get latest version, download it and set output', () => __awaiter(void 0, void 0, void 0, function* () { + jest.spyOn(core, 'getInput').mockReturnValue('latest'); + jest.spyOn(toolCache, 'downloadTool').mockReturnValue(Promise.resolve('pathToTool')); + jest.spyOn(fs, 'readFileSync').mockReturnValue('v1.20.4'); + jest.spyOn(toolCache, 'find').mockReturnValue('pathToCachedTool'); + jest.spyOn(os, 'type').mockReturnValue('Windows_NT'); + jest.spyOn(fs, 'chmodSync').mockImplementation(); + jest.spyOn(core, 'addPath').mockImplementation(); + jest.spyOn(console, 'log').mockImplementation(); + jest.spyOn(core, 'setOutput').mockImplementation(); + expect(yield run.run()).toBeUndefined(); + expect(toolCache.downloadTool).toBeCalledWith('https://storage.googleapis.com/kubernetes-release/release/stable.txt'); + expect(core.getInput).toBeCalledWith('version', { 'required': true }); + expect(core.addPath).toBeCalledWith('pathToCachedTool'); + expect(core.setOutput).toBeCalledWith('kubectl-path', path.join('pathToCachedTool', 'kubectl.exe')); + })); +}); diff --git a/package-lock.json b/package-lock.json index ba49723..0577ee1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2278,7 +2278,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true + "dev": true, + "optional": true }, "har-schema": { "version": "2.0.0", @@ -2588,6 +2589,7 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "optional": true, "requires": { "is-docker": "^2.0.0" } @@ -4191,6 +4193,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "optional": true, "requires": { "yallist": "^4.0.0" } @@ -4370,6 +4373,7 @@ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", "dev": true, + "optional": true, "requires": { "growly": "^1.3.0", "is-wsl": "^2.2.0", @@ -4384,6 +4388,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, + "optional": true, "requires": { "lru-cache": "^6.0.0" } @@ -4392,13 +4397,15 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "dev": true, + "optional": true }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "optional": true, "requires": { "isexe": "^2.0.0" } @@ -5133,7 +5140,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "signal-exit": { "version": "3.0.3", @@ -5940,7 +5948,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "optional": true }, "yargs": { "version": "15.4.1", diff --git a/src/helpers.ts b/src/helpers.ts new file mode 100644 index 0000000..34d3c61 --- /dev/null +++ b/src/helpers.ts @@ -0,0 +1,31 @@ +import * as os from 'os'; +import * as util from 'util'; + +export function getKubectlArch(): string { + const arch = os.arch(); + if (arch === 'x64') { + return 'amd64'; + } + return arch; +} + +export function getkubectlDownloadURL(version: string, arch: string): string { + switch (os.type()) { + case 'Linux': + return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/%s/kubectl', version, arch); + + case 'Darwin': + return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/darwin/%s/kubectl', version, arch); + + case 'Windows_NT': + default: + return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/windows/%s/kubectl.exe', version, arch); + } +} + +export function getExecutableExtension(): string { + if (os.type().match(/^Win/)) { + return '.exe'; + } + return ''; +} \ No newline at end of file diff --git a/__tests__/run.test.ts b/src/run.test.ts similarity index 92% rename from __tests__/run.test.ts rename to src/run.test.ts index f907011..a25dad2 100644 --- a/__tests__/run.test.ts +++ b/src/run.test.ts @@ -1,4 +1,5 @@ -import * as run from '../src/run' +import * as run from './run' +import { getkubectlDownloadURL, getKubectlArch, getExecutableExtension } from './helpers'; import * as os from 'os'; import * as toolCache from '@actions/tool-cache'; import * as fs from 'fs'; @@ -10,14 +11,14 @@ describe('Testing all functions in run file.', () => { test('getExecutableExtension() - return .exe when os is Windows', () => { jest.spyOn(os, 'type').mockReturnValue('Windows_NT'); - expect(run.getExecutableExtension()).toBe('.exe'); + expect(getExecutableExtension()).toBe('.exe'); expect(os.type).toBeCalled(); }); test('getExecutableExtension() - return empty string for non-windows OS', () => { jest.spyOn(os, 'type').mockReturnValue('Darwin'); - expect(run.getExecutableExtension()).toBe(''); + expect(getExecutableExtension()).toBe(''); expect(os.type).toBeCalled(); }); @@ -28,7 +29,7 @@ describe('Testing all functions in run file.', () => { ])("getKubectlArch() - return on %s os arch %s kubectl arch", (osArch, kubectlArch) => { jest.spyOn(os, 'arch').mockReturnValue(osArch); - expect(run.getKubectlArch()).toBe(kubectlArch); + expect(getKubectlArch()).toBe(kubectlArch); expect(os.arch).toBeCalled(); }); @@ -40,7 +41,7 @@ describe('Testing all functions in run file.', () => { jest.spyOn(os, 'type').mockReturnValue('Linux'); const kubectlLinuxUrl = util.format('https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/linux/%s/kubectl', arch); - expect(run.getkubectlDownloadURL('v1.15.0', arch)).toBe(kubectlLinuxUrl); + expect(getkubectlDownloadURL('v1.15.0', arch)).toBe(kubectlLinuxUrl); expect(os.type).toBeCalled(); }); @@ -52,7 +53,7 @@ describe('Testing all functions in run file.', () => { jest.spyOn(os, 'type').mockReturnValue('Darwin'); const kubectlDarwinUrl = util.format('https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/darwin/%s/kubectl', arch); - expect(run.getkubectlDownloadURL('v1.15.0', arch)).toBe(kubectlDarwinUrl); + expect(getkubectlDownloadURL('v1.15.0', arch)).toBe(kubectlDarwinUrl); expect(os.type).toBeCalled(); }); @@ -64,7 +65,7 @@ describe('Testing all functions in run file.', () => { jest.spyOn(os, 'type').mockReturnValue('Windows_NT'); const kubectlWindowsUrl = util.format('https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/windows/%s/kubectl.exe', arch); - expect(run.getkubectlDownloadURL('v1.15.0', arch)).toBe(kubectlWindowsUrl); + expect(getkubectlDownloadURL('v1.15.0', arch)).toBe(kubectlWindowsUrl); expect(os.type).toBeCalled(); }); @@ -98,7 +99,7 @@ describe('Testing all functions in run file.', () => { jest.spyOn(toolCache, 'downloadTool').mockReturnValue(Promise.resolve('pathToTool')); jest.spyOn(toolCache, 'cacheFile').mockReturnValue(Promise.resolve('pathToCachedTool')); jest.spyOn(os, 'type').mockReturnValue('Windows_NT'); - jest.spyOn(fs, 'chmodSync').mockImplementation(() => {}); + jest.spyOn(fs, 'chmodSync').mockImplementation(() => { }); expect(await run.downloadKubectl('v1.15.0')).toBe(path.join('pathToCachedTool', 'kubectl.exe')); expect(toolCache.find).toBeCalledWith('kubectl', 'v1.15.0'); @@ -137,7 +138,7 @@ describe('Testing all functions in run file.', () => { test('downloadKubectl() - return path to existing cache of kubectl', async () => { jest.spyOn(toolCache, 'find').mockReturnValue('pathToCachedTool'); jest.spyOn(os, 'type').mockReturnValue('Windows_NT'); - jest.spyOn(fs, 'chmodSync').mockImplementation(() => {}); + jest.spyOn(fs, 'chmodSync').mockImplementation(() => { }); jest.spyOn(toolCache, 'downloadTool'); expect(await run.downloadKubectl('v1.15.0')).toBe(path.join('pathToCachedTool', 'kubectl.exe')); diff --git a/src/run.ts b/src/run.ts index c46e612..5a2d2f6 100644 --- a/src/run.ts +++ b/src/run.ts @@ -1,4 +1,3 @@ -import * as os from 'os'; import * as path from 'path'; import * as util from 'util'; import * as fs from 'fs'; @@ -6,38 +5,23 @@ import * as fs from 'fs'; import * as toolCache from '@actions/tool-cache'; import * as core from '@actions/core'; +import { getkubectlDownloadURL, getKubectlArch, getExecutableExtension } from './helpers'; + const kubectlToolName = 'kubectl'; const stableKubectlVersion = 'v1.15.0'; const stableVersionUrl = 'https://storage.googleapis.com/kubernetes-release/release/stable.txt'; -export function getExecutableExtension(): string { - if (os.type().match(/^Win/)) { - return '.exe'; +export async function run() { + let version = core.getInput('version', { 'required': true }); + if (version.toLocaleLowerCase() === 'latest') { + version = await getStableKubectlVersion(); } - return ''; -} + const cachedPath = await downloadKubectl(version); -export function getKubectlArch(): string { - let arch = os.arch(); - if (arch === 'x64') { - return 'amd64'; - } - return arch; -} + core.addPath(path.dirname(cachedPath)); -export function getkubectlDownloadURL(version: string, arch: string): string { - switch (os.type()) { - case 'Linux': - return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/%s/kubectl', version, arch); - - case 'Darwin': - return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/darwin/%s/kubectl', version, arch); - - case 'Windows_NT': - default: - return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/windows/%s/kubectl.exe', version, arch); - - } + core.debug(`Kubectl tool version: '${version}' has been cached at ${cachedPath}`); + core.setOutput('kubectl-path', cachedPath); } export async function getStableKubectlVersion(): Promise { @@ -57,7 +41,7 @@ export async function getStableKubectlVersion(): Promise { export async function downloadKubectl(version: string): Promise { let cachedToolpath = toolCache.find(kubectlToolName, version); let kubectlDownloadPath = ''; - let arch = getKubectlArch(); + const arch = getKubectlArch(); if (!cachedToolpath) { try { kubectlDownloadPath = await toolCache.downloadTool(getkubectlDownloadURL(version, arch)); @@ -77,17 +61,4 @@ export async function downloadKubectl(version: string): Promise { return kubectlPath; } -export async function run() { - let version = core.getInput('version', { 'required': true }); - if (version.toLocaleLowerCase() === 'latest') { - version = await getStableKubectlVersion(); - } - let cachedPath = await downloadKubectl(version); - - core.addPath(path.dirname(cachedPath)); - - console.log(`Kubectl tool version: '${version}' has been cached at ${cachedPath}`); - core.setOutput('kubectl-path', cachedPath); -} - run().catch(core.setFailed); diff --git a/test/validate-kubectl.py b/test/validate-kubectl.py new file mode 100644 index 0000000..cff10a0 --- /dev/null +++ b/test/validate-kubectl.py @@ -0,0 +1,31 @@ +import os, sys, json, requests, time + +version_to_check = sys.argv[1] +version_info = None +PASSED = False + +try: + print('kubectl version --client -o json') + version_info = json.load(os.popen('kubectl version --client -o json')) +except Exception as ex: + sys.exit('kubectl not installed') + +try: + if version_to_check == 'latest': + response = None + time_to_sleep = 2 + for _ in range(10): + response = requests.get('https://storage.googleapis.com/kubernetes-release/release/stable.txt') + if response.status_code == 200: + break + print('Failed to obtain latest version info, retrying.') + time.sleep(time_to_sleep) + time_to_sleep *= 2 + version_to_check = response.content.decode('utf-8') + PASSED = True if version_info['clientVersion']['gitVersion'] == version_to_check else False +except: + pass + +if not PASSED: + sys.exit('Setting up of '+version_to_check+' kubectl failed') +print('Test passed') \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 3555b59..1882ffa 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,10 @@ { "compilerOptions": { - "target": "ES6", - "module": "commonjs" + "target": "ES6", + "module": "commonjs" }, "exclude": [ - "node_modules", - "__tests__" + "node_modules", + "test" ] } \ No newline at end of file