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/integration-tests.yml b/.github/workflows/integration-tests.yml index 52f2db3..ff431f4 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -1,19 +1,41 @@ -name: "Trigger Integration tests" -on: - pull_request: - branches: - - main - - 'releases/*' -jobs: - trigger-integration-tests: - name: Trigger Integration tests - runs-on: ubuntu-latest - steps: - - name: Check out repository - uses: actions/checkout@v2 - with: - path: IntegrationTests - - - name: Trigger Test run - run: | - bash ./IntegrationTests/.github/workflows/TriggerIntegrationTests.sh ${{ secrets.L2_REPO_TOKEN }} ${{ github.event.pull_request.head.sha }} ${{ github.repository }} ${{ github.event.pull_request.number }} ${{ github.event.pull_request.head.ref }} ${{ github.event.pull_request.base.ref }} ${{ secrets.L2_REPO_USER }} +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/__tests__/run.test.ts b/__tests__/run.test.ts deleted file mode 100644 index f907011..0000000 --- a/__tests__/run.test.ts +++ /dev/null @@ -1,182 +0,0 @@ -import * as run from '../src/run' -import * as os from 'os'; -import * as toolCache from '@actions/tool-cache'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as core from '@actions/core'; -import * as util from 'util'; - -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(os.type).toBeCalled(); - }); - - test('getExecutableExtension() - return empty string for non-windows OS', () => { - jest.spyOn(os, 'type').mockReturnValue('Darwin'); - - expect(run.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(run.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(run.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(run.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(run.getkubectlDownloadURL('v1.15.0', arch)).toBe(kubectlWindowsUrl); - expect(os.type).toBeCalled(); - }); - - test('getStableKubectlVersion() - download stable version file, read version and return it', async () => { - jest.spyOn(toolCache, 'downloadTool').mockReturnValue(Promise.resolve('pathToTool')); - jest.spyOn(fs, 'readFileSync').mockReturnValue('v1.20.4'); - - expect(await 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', async () => { - jest.spyOn(toolCache, 'downloadTool').mockReturnValue(Promise.resolve('pathToTool')); - jest.spyOn(fs, 'readFileSync').mockReturnValue(''); - - expect(await 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', async () => { - jest.spyOn(toolCache, 'downloadTool').mockRejectedValue('Unable to download.'); - - expect(await run.getStableKubectlVersion()).toBe('v1.15.0'); - expect(toolCache.downloadTool).toBeCalled(); - }); - - test('downloadKubectl() - download kubectl, add it to toolCache and return path to it', async () => { - 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(await 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', async () => { - jest.spyOn(toolCache, 'find').mockReturnValue(''); - jest.spyOn(toolCache, 'downloadTool').mockRejectedValue('Unable to download kubectl.'); - - await 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', async () => { - 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); - }); - - await 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', async () => { - jest.spyOn(toolCache, 'find').mockReturnValue('pathToCachedTool'); - jest.spyOn(os, 'type').mockReturnValue('Windows_NT'); - jest.spyOn(fs, 'chmodSync').mockImplementation(() => {}); - jest.spyOn(toolCache, 'downloadTool'); - - expect(await 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', async () => { - 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(await 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', async () => { - 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(await 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')); - }); -}); \ No newline at end of file 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/run.ts b/src/run.ts index c46e612..fd44a90 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 * as helpers 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); - - } + console.log(`Kubectl tool version: '${version}' has been cached at ${cachedPath}`); + core.setOutput('kubectl-path', cachedPath); } export async function getStableKubectlVersion(): Promise { @@ -57,10 +41,10 @@ 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 = helpers.getKubectlArch(); if (!cachedToolpath) { try { - kubectlDownloadPath = await toolCache.downloadTool(getkubectlDownloadURL(version, arch)); + kubectlDownloadPath = await toolCache.downloadTool(helpers.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)); @@ -69,25 +53,12 @@ export async function downloadKubectl(version: string): Promise { } } - cachedToolpath = await toolCache.cacheFile(kubectlDownloadPath, kubectlToolName + getExecutableExtension(), kubectlToolName, version); + cachedToolpath = await toolCache.cacheFile(kubectlDownloadPath, kubectlToolName + helpers.getExecutableExtension(), kubectlToolName, version); } - const kubectlPath = path.join(cachedToolpath, kubectlToolName + getExecutableExtension()); + const kubectlPath = path.join(cachedToolpath, kubectlToolName + helpers.getExecutableExtension()); fs.chmodSync(kubectlPath, '777'); 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);