Compare commits

..

28 Commits

Author SHA1 Message Date
83902c34cd Clear and verify npm cache 2022-07-01 14:48:58 -07:00
7b23fe67ac upgrade ncc version 2022-07-01 14:45:18 -07:00
7f0153c54c Vidya reddy prettier (#75)
* upgraded to Node16

* Enforce Prettier

* code fix

* Update package-lock.json

resolved the conflicts.

* executed prettier

Co-authored-by: Vidya Reddy <vidyareddy@microsoft.com>
2022-06-27 18:27:44 -04:00
a14110f6e6 increment version in README to v3 (#79) 2022-06-27 14:20:10 -04:00
a767c8d3a1 fix jest version (#76) 2022-06-27 14:00:33 -04:00
97c3a3f138 upgraded to Node16 (#74)
Co-authored-by: Vidya Reddy <vidyareddy@microsoft.com>
2022-06-16 15:17:55 -04:00
0a86c98d61 Fix codeowners (#71) 2022-04-11 11:05:10 -04:00
39f78708c2 add version support message (#69) 2022-04-11 11:05:03 -04:00
fa870ea9a2 run to index (#67)
* run to index

* action.yaml route

* move to dev dep
2022-02-09 11:26:07 -05:00
e00756a00e Version fix (#66)
* Added version validation check

* Added check for latest

* Changed Helm 3.5.0 test to also test lack of v in version

* Pushing integration tests

* Removed push integration test

* Added more context to integration test

* Addressing comment
2022-02-08 17:07:21 -05:00
2998c83e16 Updated workflows, codeowner, .gitignore (#65) 2022-02-04 13:04:30 -05:00
5876560d6c Add arm64 support (#64)
* add arm64 support
2022-02-04 09:45:03 -05:00
7e6f48e5b4 master to main rename (#61) 2022-02-03 11:29:11 -05:00
da63a48ad7 Cleanup action for release (#56)
* Removed empty try catch

* Initial commit

* Compiled to js

* Escape slash for nodejs

* Updating getStableHelmVersion test

* Removing old versions of run

* Added comment on getLatestHelmVersion and variable for helm release url to command

* Pushing recent changes before computer reset

* Latest getLatestHelmVersion iteration

* Removed test

* Testing async test

* getLatestHelmVersion refactor

* fixed method to access helm releses JSON

* Edited comment on isValidVersion

* Removed old integration tests

* Small temp test

* Removed IntegrationTests path

* Updated package-lock.json and removed unused dep

* Integration testing

* Fixed if statement in run command

* Added exit

* Double brackets for new test

* Added version confirmation output and different check for version

* Helm 3.7.2 test

* added env to workflow paths

* adjusted env in workflow paths

* 3.5.0 tests

* Requested changes

* Fixed downloadHelm test

* Clean up for release

* Updated CODEOWNERS

* Updated CODEOWNERS
2022-01-26 16:35:07 -05:00
cc2c85bd7e support only v3 and switch away from github token 2022-01-26 15:27:11 -05:00
7e3c8fd604 Update README.md (#48) 2021-08-10 19:41:32 +05:30
ac8103f453 Create CODEOWNERS (#47) 2021-08-10 15:53:58 +05:30
feeafa6152 Update issue templates (#46) 2021-06-30 11:05:14 +05:30
b8c59ed1e4 Create defaultLabels.yml (#44) 2021-06-25 17:05:14 +05:30
14d85694fc Update run.ts 2021-06-24 19:26:50 +05:30
cfc72cf63e Update run.ts 2021-06-24 19:25:35 +05:30
470bf91618 Syncing package-lock.json in master with release (#42)
* Syncing package.json and package-lock.json in master with release

* reverting changes in package.json
2021-06-03 14:10:06 +05:30
0a4da9a67f Bump ws from 7.4.4 to 7.4.6 (#41)
Bumps [ws](https://github.com/websockets/ws) from 7.4.4 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.4...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-01 16:01:11 +05:30
07143cd16a Bump browserslist from 4.16.3 to 4.16.6 (#40)
Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.16.3 to 4.16.6.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/browserslist/browserslist/compare/4.16.3...4.16.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-01 16:00:44 +05:30
dcb450d677 Bump hosted-git-info from 2.8.8 to 2.8.9 (#39)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-26 13:05:55 +05:30
1571d72e78 skipLibCheck (#31)
* Update package.json

* Update tsconfig.json
2021-04-01 14:19:07 +05:30
62aeada586 Added trigger for L2 tests. (#26) 2021-03-31 17:23:12 +05:30
edc063ca5f adding graph ql for Helm versions - master (#22)
* Adding graphql to find out latest helm version of specified type

* Updating package-loc.json

* Updating js file

* Fixing PR comments

* Adding feature flag as environment variable

* Changing feature flag name

* Updating package-loc.json
2021-03-30 22:43:44 +05:30
25 changed files with 12063 additions and 6910 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
* @Azure/aks-atlanta

View File

@ -0,0 +1,7 @@
---
name: 'Issue: Bug Report / Feature Request'
about: Create a report to help us improve
title: ''
labels: need-to-triage
assignees: ''
---

35
.github/workflows/defaultLabels.yml vendored Normal file
View File

@ -0,0 +1,35 @@
name: setting-default-labels
# Controls when the action will run.
on:
schedule:
- cron: '0 0/3 * * *'
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/stale@v3
name: Setting issue as idle
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is idle because it has been open for 14 days with no activity.'
stale-issue-label: 'idle'
days-before-stale: 14
days-before-close: -1
operations-per-run: 100
exempt-issue-labels: 'backlog'
- uses: actions/stale@v3
name: Setting PR as idle
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-pr-message: 'This PR is idle because it has been open for 14 days with no activity.'
stale-pr-label: 'idle'
days-before-stale: 14
days-before-close: -1
operations-per-run: 100

65
.github/workflows/integration-tests.yml vendored Normal file
View File

@ -0,0 +1,65 @@
name: 'Trigger Integration tests'
on:
pull_request:
branches:
- main
- 'releases/*'
jobs:
trigger-integration-tests:
name: Trigger Integration tests
runs-on: ubuntu-latest
env:
HELM_3_8_0: 'v3.8.0'
HELM_3_7_2: 'v3.7.2'
HELM_NO_V: '3.5.0'
PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
steps:
- name: Check out repository
uses: actions/checkout@v2
- name: npm install and build
id: action-npm-build
run: |
echo $PR_BASE_REF
if [[ $PR_BASE_REF != releases/* ]]; then
npm install
npm run build
fi
- name: Setup helm
uses: ./
with:
version: ${{ env.HELM_3_8_0 }}
- name: Validate helm 3.8.0
run: |
if [[ $(helm version) != *$HELM_3_8_0* ]]; then
echo "HELM VERSION INCORRECT: HELM VERSION DOES NOT CONTAIN v3.8.0"
echo "HELM VERSION OUTPUT: $(helm version)"
exit 1
else
echo "HELM VERSION $HELM_3_8_0 INSTALLED SUCCESSFULLY"
fi
- name: Setup helm 3.7.2
uses: ./
with:
version: ${{ env.HELM_3_7_2 }}
- name: Validate 3.7.2
run: |
if [[ $(helm version) != *$HELM_3_7_2* ]]; then
echo "HELM VERSION INCORRECT: HELM VERSION DOES NOT CONTAIN v3.7.2"
echo "HELM VERSION OUTPUT: $(helm version)"
exit 1
else
echo "HELM VERSION $HELM_3_7_2 INSTALLED SUCCESSFULLY"
fi
- name: Setup helm 3.5.0 with no v in version
uses: ./
with:
version: ${{ env.HELM_NO_V }}
- name: Validate 3.5.0 without v in version
run: |
if [[ $(helm version) != *$HELM_NO_V* ]]; then
echo "HELM VERSION INCORRECT: HELM VERSION DOES NOT CONTAIN v3.5.0"
echo "HELM VERSION OUTPUT: $(helm version)"
exit 1
else
echo "HELM VERSION $HELM_3_5_0 INSTALLED SUCCESSFULLY"
fi

18
.github/workflows/prettify-code.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: 'Run prettify'
on:
pull_request:
push:
branches: [main]
jobs:
prettier:
name: Prettier Check
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Enforce Prettier
uses: actionsx/prettier@v2
with:
args: --check .

14
.github/workflows/release-pr.yml vendored Normal file
View File

@ -0,0 +1,14 @@
name: Create release PR
on:
workflow_dispatch:
inputs:
release:
description: 'Define release version (ex: v1, v2, v3)'
required: true
jobs:
release-pr:
uses: OliverMKing/javascript-release-workflow/.github/workflows/release-pr.yml@main
with:
release: ${{ github.event.inputs.release }}

10
.github/workflows/tag-and-draft.yml vendored Normal file
View File

@ -0,0 +1,10 @@
name: Tag and create release draft
on:
push:
branches:
- releases/*
jobs:
tag-and-release:
uses: OliverMKing/javascript-release-workflow/.github/workflows/tag-and-release.yml@main

View File

@ -1,20 +0,0 @@
name: "build-test"
on: # rebuild any PRs and main branch changes
pull_request:
branches:
- master
- 'releases/*'
push:
branches:
- master
- 'releases/*'
jobs:
build: # make sure build/ci works properly
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- run: |
npm install
npm build
npm test

21
.github/workflows/unit-tests.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: 'Run unit tests.'
on: # rebuild any PRs and main branch changes
pull_request:
branches:
- main
- 'releases/*'
push:
branches:
- main
- 'releases/*'
jobs:
build: # make sure build/ci works properly
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Run L0 tests.
run: |
npm install
npm test

3
.gitignore vendored
View File

@ -62,3 +62,6 @@ typings/
node_modules
coverage
# Transpiled JS
lib/

4
.prettierignore Normal file
View File

@ -0,0 +1,4 @@
# dependencies
/node_modules
coverage
/lib

8
.prettierrc.json Normal file
View File

@ -0,0 +1,8 @@
{
"trailingComma": "none",
"bracketSpacing": false,
"semi": false,
"tabWidth": 3,
"singleQuote": true,
"printWidth": 80
}

View File

@ -1,10 +1,13 @@
# Setup Helm
#### Install a specific version of helm binary on the runner.
Acceptable values are latest or any semantic version string like v2.16.7 Use this action in workflow to define which version of helm will be used.
Install a specific version of helm binary on the runner.
## Example
Acceptable values are latest or any semantic version string like v3.5.0 Use this action in workflow to define which version of helm will be used. v2 and v3 of this action only supports Helm3.
```yaml
- uses: azure/setup-helm@v1
- uses: azure/setup-helm@v3
with:
version: '<version>' # default is latest stable
id: install

View File

@ -4,7 +4,7 @@
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below.
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](<https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)>) of a security vulnerability, please report it to us as described below.
## Reporting Security Issues
@ -18,13 +18,13 @@ You should receive a response within 24 hours. If for some reason you do not, pl
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
- Full paths of source file(s) related to the manifestation of the issue
- The location of the affected source code (tag/branch/commit or direct URL)
- Any special configuration required to reproduce the issue
- Step-by-step instructions to reproduce the issue
- Proof-of-concept or exploit code (if possible)
- Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.

View File

@ -1,195 +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';
describe('run.ts', () => {
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('getHelmDownloadURL() - return the URL to download helm for Linux', () => {
jest.spyOn(os, 'type').mockReturnValue('Linux');
const kubectlLinuxUrl = 'https://get.helm.sh/helm-v3.2.1-linux-amd64.zip'
expect(run.getHelmDownloadURL('v3.2.1')).toBe(kubectlLinuxUrl);
expect(os.type).toBeCalled();
});
test('getHelmDownloadURL() - return the URL to download helm for Darwin', () => {
jest.spyOn(os, 'type').mockReturnValue('Darwin');
const kubectlDarwinUrl = 'https://get.helm.sh/helm-v3.2.1-darwin-amd64.zip'
expect(run.getHelmDownloadURL('v3.2.1')).toBe(kubectlDarwinUrl);
expect(os.type).toBeCalled();
});
test('getHelmDownloadURL() - return the URL to download helm for Windows', () => {
jest.spyOn(os, 'type').mockReturnValue('Windows_NT');
const kubectlWindowsUrl = 'https://get.helm.sh/helm-v3.2.1-windows-amd64.zip'
expect(run.getHelmDownloadURL('v3.2.1')).toBe(kubectlWindowsUrl);
expect(os.type).toBeCalled();
});
test('getStableHelmVersion() - download stable version file, read version and return it', async () => {
jest.spyOn(toolCache, 'downloadTool').mockResolvedValue('pathToTool');
const response = JSON.stringify(
[
{
'tag_name': 'v4.0.0'
}, {
'tag_name': 'v3.0.0'
}, {
'tag_name': 'v2.0.0'
}
]
);
jest.spyOn(fs, 'readFileSync').mockReturnValue(response);
expect(await run.getStableHelmVersion()).toBe('v4.0.0');
expect(toolCache.downloadTool).toBeCalled();
expect(fs.readFileSync).toBeCalledWith('pathToTool', 'utf8');
});
test('getStableHelmVersion() - return default version if error occurs while getting latest version', async () => {
jest.spyOn(toolCache, 'downloadTool').mockRejectedValue('Unable to download');
jest.spyOn(core, 'warning').mockImplementation();
expect(await run.getStableHelmVersion()).toBe('v3.2.1');
expect(toolCache.downloadTool).toBeCalled();
expect(core.warning).toBeCalledWith("Cannot get the latest Helm info from https://api.github.com/repos/helm/helm/releases. Error Unable to download. Using default Helm version v3.2.1.");
});
test('walkSync() - return path to the all files matching fileToFind in dir', () => {
jest.spyOn(fs, 'readdirSync').mockImplementation((file, _) => {
if (file == 'mainFolder') return ['file1' as unknown as fs.Dirent, 'file2' as unknown as fs.Dirent, 'folder1' as unknown as fs.Dirent, 'folder2' as unknown as fs.Dirent];
if (file == path.join('mainFolder', 'folder1')) return ['file11' as unknown as fs.Dirent, 'file12' as unknown as fs.Dirent];
if (file == path.join('mainFolder', 'folder2')) return ['file21' as unknown as fs.Dirent, 'file22' as unknown as fs.Dirent];
});
jest.spyOn(core, 'debug').mockImplementation();
jest.spyOn(fs, 'statSync').mockImplementation((file) => {
const isDirectory = (file as string).toLowerCase().indexOf('file') == -1 ? true: false
return { isDirectory: () => isDirectory } as fs.Stats;
});
expect(run.walkSync('mainFolder', null, 'file21')).toEqual([path.join('mainFolder', 'folder2', 'file21')]);
expect(fs.readdirSync).toBeCalledTimes(3);
expect(fs.statSync).toBeCalledTimes(8);
});
test('walkSync() - return empty array if no file with name fileToFind exists', () => {
jest.spyOn(fs, 'readdirSync').mockImplementation((file, _) => {
if (file == 'mainFolder') return ['file1' as unknown as fs.Dirent, 'file2' as unknown as fs.Dirent, 'folder1' as unknown as fs.Dirent, 'folder2' as unknown as fs.Dirent];
if (file == path.join('mainFolder', 'folder1')) return ['file11' as unknown as fs.Dirent, 'file12' as unknown as fs.Dirent];
if (file == path.join('mainFolder', 'folder2')) return ['file21' as unknown as fs.Dirent, 'file22' as unknown as fs.Dirent];
});
jest.spyOn(core, 'debug').mockImplementation();
jest.spyOn(fs, 'statSync').mockImplementation((file) => {
const isDirectory = (file as string).toLowerCase().indexOf('file') == -1 ? true: false
return { isDirectory: () => isDirectory } as fs.Stats;
});
expect(run.walkSync('mainFolder', null, 'helm.exe')).toEqual([]);
expect(fs.readdirSync).toBeCalledTimes(3);
expect(fs.statSync).toBeCalledTimes(8);
});
test('findHelm() - change access permissions and find the helm in given directory', () => {
jest.spyOn(fs, 'chmodSync').mockImplementation(() => {});
jest.spyOn(fs, 'readdirSync').mockImplementation((file, _) => {
if (file == 'mainFolder') return ['helm.exe' as unknown as fs.Dirent];
});
jest.spyOn(fs, 'statSync').mockImplementation((file) => {
const isDirectory = (file as string).indexOf('folder') == -1 ? false: true
return { isDirectory: () => isDirectory } as fs.Stats;
});
jest.spyOn(os, 'type').mockReturnValue('Windows_NT');
expect(run.findHelm('mainFolder')).toBe(path.join('mainFolder', 'helm.exe'));
});
test('findHelm() - throw error if executable not found', () => {
jest.spyOn(fs, 'chmodSync').mockImplementation(() => {});
jest.spyOn(fs, 'readdirSync').mockImplementation((file, _) => {
if (file == 'mainFolder') return [];
});
jest.spyOn(fs, 'statSync').mockImplementation((file) => { return { isDirectory: () => true } as fs.Stats});
jest.spyOn(os, 'type').mockReturnValue('Windows_NT');
expect(() => run.findHelm('mainFolder')).toThrow('Helm executable not found in path mainFolder');
});
test('downloadHelm() - download helm and return path to it', async () => {
jest.spyOn(toolCache, 'find').mockReturnValue('');
jest.spyOn(toolCache, 'downloadTool').mockResolvedValue('pathToTool');
const response = JSON.stringify([{'tag_name': 'v4.0.0'}]);
jest.spyOn(fs, 'readFileSync').mockReturnValue(response);
jest.spyOn(os, 'type').mockReturnValue('Windows_NT');
jest.spyOn(fs, 'chmodSync').mockImplementation(() => {});
jest.spyOn(toolCache, 'extractZip').mockResolvedValue('pathToUnzippedHelm');
jest.spyOn(toolCache, 'cacheDir').mockResolvedValue('pathToCachedDir');
jest.spyOn(fs, 'readdirSync').mockImplementation((file, _) => ['helm.exe' as unknown as fs.Dirent]);
jest.spyOn(fs, 'statSync').mockImplementation((file) => {
const isDirectory = (file as string).indexOf('folder') == -1 ? false: true
return { isDirectory: () => isDirectory } as fs.Stats;
});
expect(await run.downloadHelm(null)).toBe(path.join('pathToCachedDir', 'helm.exe'));
expect(toolCache.find).toBeCalledWith('helm', 'v4.0.0');
expect(toolCache.downloadTool).toBeCalledWith('https://get.helm.sh/helm-v4.0.0-windows-amd64.zip');
expect(fs.chmodSync).toBeCalledWith('pathToTool', '777');
expect(toolCache.extractZip).toBeCalledWith('pathToTool');
expect(fs.chmodSync).toBeCalledWith(path.join('pathToCachedDir', 'helm.exe'), '777');
});
test('downloadHelm() - throw error if unable to download', async () => {
jest.spyOn(toolCache, 'find').mockReturnValue('');
jest.spyOn(toolCache, 'downloadTool').mockImplementation(async () => { throw 'Unable to download'});
jest.spyOn(os, 'type').mockReturnValue('Windows_NT');
await expect(run.downloadHelm('v3.2.1')).rejects.toThrow('Failed to download Helm from location https://get.helm.sh/helm-v3.2.1-windows-amd64.zip');
expect(toolCache.find).toBeCalledWith('helm', 'v3.2.1');
expect(toolCache.downloadTool).toBeCalledWith('https://get.helm.sh/helm-v3.2.1-windows-amd64.zip');
});
test('downloadHelm() - return path to helm tool with same version from toolCache', async () => {
jest.spyOn(toolCache, 'find').mockReturnValue('pathToCachedDir');
jest.spyOn(fs, 'chmodSync').mockImplementation(() => {});
expect(await run.downloadHelm('v3.2.1')).toBe(path.join('pathToCachedDir', 'helm.exe'));
expect(toolCache.find).toBeCalledWith('helm', 'v3.2.1');
expect(fs.chmodSync).toBeCalledWith(path.join('pathToCachedDir', 'helm.exe'), '777');
});
test('downloadHelm() - throw error is helm is not found in path', async () => {
jest.spyOn(toolCache, 'find').mockReturnValue('');
jest.spyOn(toolCache, 'downloadTool').mockResolvedValue('pathToTool');
jest.spyOn(os, 'type').mockReturnValue('Windows_NT');
jest.spyOn(fs, 'chmodSync').mockImplementation();
jest.spyOn(toolCache, 'extractZip').mockResolvedValue('pathToUnzippedHelm');
jest.spyOn(toolCache, 'cacheDir').mockResolvedValue('pathToCachedDir');
jest.spyOn(fs, 'readdirSync').mockImplementation((file, _) => []);
jest.spyOn(fs, 'statSync').mockImplementation((file) => {
const isDirectory = (file as string).indexOf('folder') == -1 ? false: true
return { isDirectory: () => isDirectory } as fs.Stats;
});
await expect(run.downloadHelm('v3.2.1')).rejects.toThrow('Helm executable not found in path pathToCachedDir');
expect(toolCache.find).toBeCalledWith('helm', 'v3.2.1');
expect(toolCache.downloadTool).toBeCalledWith('https://get.helm.sh/helm-v3.2.1-windows-amd64.zip');
expect(fs.chmodSync).toBeCalledWith('pathToTool', '777');
expect(toolCache.extractZip).toBeCalledWith('pathToTool');
});
});

View File

@ -5,15 +5,11 @@ inputs:
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'
branding:
color: 'blue'
runs:
using: 'node12'
main: 'lib/run.js'
using: 'node16'
main: 'lib/index.js'

View File

@ -8,11 +8,11 @@ module.exports = {
},
verbose: true,
coverageThreshold: {
"global": {
"branches": 0,
"functions": 14,
"lines": 27,
"statements": 27
global: {
branches: 0,
functions: 14,
lines: 27,
statements: 27
}
}
}

View File

@ -1,222 +0,0 @@
"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) {
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.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/)) {
return '.exe';
}
return '';
}
function getHelmDownloadURL(version) {
switch (os.type()) {
case 'Linux':
return util.format('https://get.helm.sh/helm-%s-linux-amd64.zip', version);
case 'Darwin':
return util.format('https://get.helm.sh/helm-%s-darwin-amd64.zip', version);
case 'Windows_NT':
default:
return util.format('https://get.helm.sh/helm-%s-windows-amd64.zip', version);
}
}
function getStableHelmVersion() {
return __awaiter(this, void 0, void 0, function* () {
try {
const downloadPath = yield toolCache.downloadTool(helmAllReleasesUrl);
const responseArray = JSON.parse(fs.readFileSync(downloadPath, 'utf8').toString().trim());
let latestHelmVersion = semver.clean(stableHelmVersion);
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;
}
}
}
});
latestHelmVersion = "v" + latestHelmVersion;
return latestHelmVersion;
}
catch (error) {
core.warning(util.format("Cannot get the latest Helm info from %s. Error %s. Using default Helm version %s.", helmAllReleasesUrl, error, stableHelmVersion));
}
return stableHelmVersion;
});
}
exports.getStableHelmVersion = getStableHelmVersion;
var walkSync = function (dir, filelist, fileToFind) {
var files = fs.readdirSync(dir);
filelist = filelist || [];
files.forEach(function (file) {
if (fs.statSync(path.join(dir, file)).isDirectory()) {
filelist = walkSync(path.join(dir, file), filelist, fileToFind);
}
else {
core.debug(file);
if (file == fileToFind) {
filelist.push(path.join(dir, file));
}
}
});
return filelist;
};
function downloadHelm(version) {
return __awaiter(this, void 0, void 0, function* () {
if (!version) {
version = yield getLatestHelmVersionFor("v3");
}
let cachedToolpath = toolCache.find(helmToolName, version);
if (!cachedToolpath) {
let helmDownloadPath;
try {
helmDownloadPath = yield toolCache.downloadTool(getHelmDownloadURL(version));
}
catch (exception) {
throw new Error(util.format("Failed to download Helm from location ", getHelmDownloadURL(version)));
}
fs.chmodSync(helmDownloadPath, '777');
const unzipedHelmPath = yield toolCache.extractZip(helmDownloadPath);
cachedToolpath = yield toolCache.cacheDir(unzipedHelmPath, helmToolName, version);
}
const helmpath = findHelm(cachedToolpath);
if (!helmpath) {
throw new Error(util.format("Helm executable not found in path ", cachedToolpath));
}
fs.chmodSync(helmpath, '777');
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 = [];
walkSync(rootFolder, filelist, helmToolName + getExecutableExtension());
if (!filelist) {
throw new Error(util.format("Helm executable not found in path ", rootFolder));
}
else {
return filelist[0];
}
}
function run() {
return __awaiter(this, void 0, void 0, function* () {
let version = core.getInput('version', { 'required': true });
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() === '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))) {
core.addPath(path.dirname(cachedPath));
}
}
catch (_a) {
//do nothing, set as output variable
}
console.log(`Helm tool version: '${version}' has been cached at ${cachedPath}`);
core.setOutput('helm-path', cachedPath);
});
}
run().catch(core.setFailed);

10214
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,17 +13,20 @@
"@octokit/graphql": "^4.6.1",
"semver": "^6.1.0"
},
"main": "lib/run.js",
"main": "lib/index.js",
"scripts": {
"build": "tsc",
"build": "ncc build src/run.ts -o lib",
"test": "jest",
"test-coverage": "jest --coverage"
"test-coverage": "jest --coverage",
"format": "prettier --write .",
"format-check": "prettier --check ."
},
"devDependencies": {
"@types/jest": "^26.0.0",
"@types/node": "^12.0.10",
"typescript": "^3.5.2",
"@vercel/ncc": "^0.34.0",
"jest": "^26.0.1",
"@types/jest": "^25.2.2",
"ts-jest": "^25.5.1"
"ts-jest": "^26.0.0",
"typescript": "^3.5.2"
}
}

273
src/run.test.ts Normal file
View File

@ -0,0 +1,273 @@
import * as run from './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'
describe('run.ts', () => {
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('getHelmDownloadURL() - return the URL to download helm for Linux', () => {
jest.spyOn(os, 'type').mockReturnValue('Linux')
jest.spyOn(os, 'arch').mockReturnValueOnce('unknown')
const kubectlLinuxUrl = 'https://get.helm.sh/helm-v3.8.0-linux-amd64.zip'
expect(run.getHelmDownloadURL('v3.8.0')).toBe(kubectlLinuxUrl)
expect(os.type).toBeCalled()
expect(os.arch).toBeCalled()
// arm64
jest.spyOn(os, 'type').mockReturnValue('Linux')
jest.spyOn(os, 'arch').mockReturnValueOnce('arm64')
const kubectlLinuxArm64Url =
'https://get.helm.sh/helm-v3.8.0-linux-arm64.zip'
expect(run.getHelmDownloadURL('v3.8.0')).toBe(kubectlLinuxArm64Url)
expect(os.type).toBeCalled()
expect(os.arch).toBeCalled()
})
test('getHelmDownloadURL() - return the URL to download helm for Darwin', () => {
jest.spyOn(os, 'type').mockReturnValue('Darwin')
jest.spyOn(os, 'arch').mockReturnValueOnce('unknown')
const kubectlDarwinUrl =
'https://get.helm.sh/helm-v3.8.0-darwin-amd64.zip'
expect(run.getHelmDownloadURL('v3.8.0')).toBe(kubectlDarwinUrl)
expect(os.type).toBeCalled()
expect(os.arch).toBeCalled()
// arm64
jest.spyOn(os, 'type').mockReturnValue('Darwin')
jest.spyOn(os, 'arch').mockReturnValueOnce('arm64')
const kubectlDarwinArm64Url =
'https://get.helm.sh/helm-v3.8.0-darwin-arm64.zip'
expect(run.getHelmDownloadURL('v3.8.0')).toBe(kubectlDarwinArm64Url)
expect(os.type).toBeCalled()
expect(os.arch).toBeCalled()
})
test('getValidVersion() - return version with v prepended', () => {
expect(run.getValidVersion('3.8.0')).toBe('v3.8.0')
})
test('getHelmDownloadURL() - return the URL to download helm for Windows', () => {
jest.spyOn(os, 'type').mockReturnValue('Windows_NT')
const kubectlWindowsUrl =
'https://get.helm.sh/helm-v3.8.0-windows-amd64.zip'
expect(run.getHelmDownloadURL('v3.8.0')).toBe(kubectlWindowsUrl)
expect(os.type).toBeCalled()
})
test('getLatestHelmVersion() - return the latest version of HELM', async () => {
try {
expect(await run.getLatestHelmVersion()).toBe('v3.8.0')
} catch (e) {
return e
}
})
test('walkSync() - return path to the all files matching fileToFind in dir', () => {
jest.spyOn(fs, 'readdirSync').mockImplementation((file, _) => {
if (file == 'mainFolder')
return [
'file1' as unknown as fs.Dirent,
'file2' as unknown as fs.Dirent,
'folder1' as unknown as fs.Dirent,
'folder2' as unknown as fs.Dirent
]
if (file == path.join('mainFolder', 'folder1'))
return [
'file11' as unknown as fs.Dirent,
'file12' as unknown as fs.Dirent
]
if (file == path.join('mainFolder', 'folder2'))
return [
'file21' as unknown as fs.Dirent,
'file22' as unknown as fs.Dirent
]
})
jest.spyOn(core, 'debug').mockImplementation()
jest.spyOn(fs, 'statSync').mockImplementation((file) => {
const isDirectory =
(file as string).toLowerCase().indexOf('file') == -1 ? true : false
return {isDirectory: () => isDirectory} as fs.Stats
})
expect(run.walkSync('mainFolder', null, 'file21')).toEqual([
path.join('mainFolder', 'folder2', 'file21')
])
expect(fs.readdirSync).toBeCalledTimes(3)
expect(fs.statSync).toBeCalledTimes(8)
})
test('walkSync() - return empty array if no file with name fileToFind exists', () => {
jest.spyOn(fs, 'readdirSync').mockImplementation((file, _) => {
if (file == 'mainFolder')
return [
'file1' as unknown as fs.Dirent,
'file2' as unknown as fs.Dirent,
'folder1' as unknown as fs.Dirent,
'folder2' as unknown as fs.Dirent
]
if (file == path.join('mainFolder', 'folder1'))
return [
'file11' as unknown as fs.Dirent,
'file12' as unknown as fs.Dirent
]
if (file == path.join('mainFolder', 'folder2'))
return [
'file21' as unknown as fs.Dirent,
'file22' as unknown as fs.Dirent
]
})
jest.spyOn(core, 'debug').mockImplementation()
jest.spyOn(fs, 'statSync').mockImplementation((file) => {
const isDirectory =
(file as string).toLowerCase().indexOf('file') == -1 ? true : false
return {isDirectory: () => isDirectory} as fs.Stats
})
expect(run.walkSync('mainFolder', null, 'helm.exe')).toEqual([])
expect(fs.readdirSync).toBeCalledTimes(3)
expect(fs.statSync).toBeCalledTimes(8)
})
test('findHelm() - change access permissions and find the helm in given directory', () => {
jest.spyOn(fs, 'chmodSync').mockImplementation(() => {})
jest.spyOn(fs, 'readdirSync').mockImplementation((file, _) => {
if (file == 'mainFolder') return ['helm.exe' as unknown as fs.Dirent]
})
jest.spyOn(fs, 'statSync').mockImplementation((file) => {
const isDirectory =
(file as string).indexOf('folder') == -1 ? false : true
return {isDirectory: () => isDirectory} as fs.Stats
})
jest.spyOn(os, 'type').mockReturnValue('Windows_NT')
expect(run.findHelm('mainFolder')).toBe(
path.join('mainFolder', 'helm.exe')
)
})
test('findHelm() - throw error if executable not found', () => {
jest.spyOn(fs, 'chmodSync').mockImplementation(() => {})
jest.spyOn(fs, 'readdirSync').mockImplementation((file, _) => {
if (file == 'mainFolder') return []
})
jest.spyOn(fs, 'statSync').mockImplementation((file) => {
return {isDirectory: () => true} as fs.Stats
})
jest.spyOn(os, 'type').mockReturnValue('Windows_NT')
expect(() => run.findHelm('mainFolder')).toThrow(
'Helm executable not found in path mainFolder'
)
})
test('downloadHelm() - download helm and return path to it', async () => {
jest.spyOn(toolCache, 'find').mockReturnValue('')
jest.spyOn(toolCache, 'downloadTool').mockResolvedValue('pathToTool')
const response = JSON.stringify([{tag_name: 'v4.0.0'}])
jest.spyOn(fs, 'readFileSync').mockReturnValue(response)
jest.spyOn(os, 'type').mockReturnValue('Windows_NT')
jest.spyOn(fs, 'chmodSync').mockImplementation(() => {})
jest
.spyOn(toolCache, 'extractZip')
.mockResolvedValue('pathToUnzippedHelm')
jest.spyOn(toolCache, 'cacheDir').mockResolvedValue('pathToCachedDir')
jest
.spyOn(fs, 'readdirSync')
.mockImplementation((file, _) => ['helm.exe' as unknown as fs.Dirent])
jest.spyOn(fs, 'statSync').mockImplementation((file) => {
const isDirectory =
(file as string).indexOf('folder') == -1 ? false : true
return {isDirectory: () => isDirectory} as fs.Stats
})
expect(await run.downloadHelm('v4.0.0')).toBe(
path.join('pathToCachedDir', 'helm.exe')
)
expect(toolCache.find).toBeCalledWith('helm', 'v4.0.0')
expect(toolCache.downloadTool).toBeCalledWith(
'https://get.helm.sh/helm-v4.0.0-windows-amd64.zip'
)
expect(fs.chmodSync).toBeCalledWith('pathToTool', '777')
expect(toolCache.extractZip).toBeCalledWith('pathToTool')
expect(fs.chmodSync).toBeCalledWith(
path.join('pathToCachedDir', 'helm.exe'),
'777'
)
})
test('downloadHelm() - throw error if unable to download', async () => {
jest.spyOn(toolCache, 'find').mockReturnValue('')
jest.spyOn(toolCache, 'downloadTool').mockImplementation(async () => {
throw 'Unable to download'
})
jest.spyOn(os, 'type').mockReturnValue('Windows_NT')
await expect(run.downloadHelm('v3.2.1')).rejects.toThrow(
'Failed to download Helm from location https://get.helm.sh/helm-v3.2.1-windows-amd64.zip'
)
expect(toolCache.find).toBeCalledWith('helm', 'v3.2.1')
expect(toolCache.downloadTool).toBeCalledWith(
'https://get.helm.sh/helm-v3.2.1-windows-amd64.zip'
)
})
test('downloadHelm() - return path to helm tool with same version from toolCache', async () => {
jest.spyOn(toolCache, 'find').mockReturnValue('pathToCachedDir')
jest.spyOn(fs, 'chmodSync').mockImplementation(() => {})
expect(await run.downloadHelm('v3.2.1')).toBe(
path.join('pathToCachedDir', 'helm.exe')
)
expect(toolCache.find).toBeCalledWith('helm', 'v3.2.1')
expect(fs.chmodSync).toBeCalledWith(
path.join('pathToCachedDir', 'helm.exe'),
'777'
)
})
test('downloadHelm() - throw error is helm is not found in path', async () => {
jest.spyOn(toolCache, 'find').mockReturnValue('')
jest.spyOn(toolCache, 'downloadTool').mockResolvedValue('pathToTool')
jest.spyOn(os, 'type').mockReturnValue('Windows_NT')
jest.spyOn(fs, 'chmodSync').mockImplementation()
jest
.spyOn(toolCache, 'extractZip')
.mockResolvedValue('pathToUnzippedHelm')
jest.spyOn(toolCache, 'cacheDir').mockResolvedValue('pathToCachedDir')
jest.spyOn(fs, 'readdirSync').mockImplementation((file, _) => [])
jest.spyOn(fs, 'statSync').mockImplementation((file) => {
const isDirectory =
(file as string).indexOf('folder') == -1 ? false : true
return {isDirectory: () => isDirectory} as fs.Stats
})
await expect(run.downloadHelm('v3.2.1')).rejects.toThrow(
'Helm executable not found in path pathToCachedDir'
)
expect(toolCache.find).toBeCalledWith('helm', 'v3.2.1')
expect(toolCache.downloadTool).toBeCalledWith(
'https://get.helm.sh/helm-v3.2.1-windows-amd64.zip'
)
expect(fs.chmodSync).toBeCalledWith('pathToTool', '777')
expect(toolCache.extractZip).toBeCalledWith('pathToTool')
})
})

View File

@ -2,201 +2,193 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import * as os from 'os';
import * as path from 'path';
import * as util from 'util';
import * as fs from 'fs';
import * as semver from 'semver';
import * as os from 'os'
import * as path from 'path'
import * as util from 'util'
import * as fs from 'fs'
import * as toolCache from '@actions/tool-cache';
import * as core from '@actions/core';
import { graphql } from '@octokit/graphql';
import * as toolCache from '@actions/tool-cache'
import * as core from '@actions/core'
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 {
if (os.type().match(/^Win/)) {
return '.exe';
}
return '';
}
export function getHelmDownloadURL(version: string): string {
switch (os.type()) {
case 'Linux':
return util.format('https://get.helm.sh/helm-%s-linux-amd64.zip', version);
case 'Darwin':
return util.format('https://get.helm.sh/helm-%s-darwin-amd64.zip', version);
case 'Windows_NT':
default:
return util.format('https://get.helm.sh/helm-%s-windows-amd64.zip', version);
}
}
export async function getStableHelmVersion(): Promise<string> {
try {
const downloadPath = await toolCache.downloadTool(helmAllReleasesUrl);
const responseArray = JSON.parse(fs.readFileSync(downloadPath, 'utf8').toString().trim());
let latestHelmVersion = semver.clean(stableHelmVersion);
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;
}
}
}
});
latestHelmVersion = "v" + latestHelmVersion;
return latestHelmVersion;
} catch (error) {
core.warning(util.format("Cannot get the latest Helm info from %s. Error %s. Using default Helm version %s.", helmAllReleasesUrl, error, stableHelmVersion));
}
return stableHelmVersion;
}
export var walkSync = function (dir, filelist, fileToFind) {
var files = fs.readdirSync(dir);
filelist = filelist || [];
files.forEach(function (file) {
if (fs.statSync(path.join(dir, file)).isDirectory()) {
filelist = walkSync(path.join(dir, file), filelist, fileToFind);
}
else {
core.debug(file);
if (file == fileToFind) {
filelist.push(path.join(dir, file));
}
}
});
return filelist;
};
export async function downloadHelm(version: string): Promise<string> {
if (!version) { version = await getStableHelmVersion(); }
let cachedToolpath = toolCache.find(helmToolName, version);
if (!cachedToolpath) {
let helmDownloadPath;
try {
helmDownloadPath = await toolCache.downloadTool(getHelmDownloadURL(version));
} catch (exception) {
throw new Error(util.format("Failed to download Helm from location", getHelmDownloadURL(version)));
}
fs.chmodSync(helmDownloadPath, '777');
const unzipedHelmPath = await toolCache.extractZip(helmDownloadPath);
cachedToolpath = await toolCache.cacheDir(unzipedHelmPath, helmToolName, version);
}
const helmpath = findHelm(cachedToolpath);
if (!helmpath) {
throw new Error(util.format("Helm executable not found in path", cachedToolpath));
}
fs.chmodSync(helmpath, '777');
return helmpath;
}
async function getLatestHelmVersionFor(type: string): Promise<string> {
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[] = [];
walkSync(rootFolder, filelist, helmToolName + getExecutableExtension());
if (!filelist || filelist.length == 0) {
throw new Error(util.format("Helm executable not found in path", rootFolder));
}
else {
return filelist[0];
}
}
const helmToolName = 'helm'
const stableHelmVersion = 'v3.8.0'
const helmAllReleasesUrl = 'https://api.github.com/repos/helm/helm/releases'
export async function run() {
let version = core.getInput('version', { 'required': true });
let version = core.getInput('version', {required: true})
if (process.env['HELM_INSTALLER_LEGACY_VERSIONING'] == 'true') {
if (version !== 'latest' && version[0] !== 'v') {
version = getValidVersion(version)
}
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;
}
version = await getLatestHelmVersion()
}
core.debug(util.format("Downloading %s", version));
let cachedPath = await downloadHelm(version);
core.debug(util.format('Downloading %s', version))
let cachedPath = await downloadHelm(version)
try {
if (!process.env['PATH'].startsWith(path.dirname(cachedPath))) {
core.addPath(path.dirname(cachedPath));
core.addPath(path.dirname(cachedPath))
}
}
catch {
} catch {
//do nothing, set as output variable
}
console.log(`Helm tool version: '${version}' has been cached at ${cachedPath}`);
core.setOutput('helm-path', cachedPath);
console.log(
`Helm tool version: '${version}' has been cached at ${cachedPath}`
)
core.setOutput('helm-path', cachedPath)
}
run().catch(core.setFailed);
//Returns version with proper v before it
export function getValidVersion(version: string): string {
return 'v' + version
}
// Downloads the helm releases JSON and parses all the recent versions of helm from it.
// Defaults to sending stable helm version if none are valid or if it fails
export async function getLatestHelmVersion(): Promise<string> {
const helmJSONPath: string = await toolCache.downloadTool(helmAllReleasesUrl)
try {
const helmJSON = JSON.parse(fs.readFileSync(helmJSONPath, 'utf-8'))
for (let i in helmJSON) {
if (isValidVersion(helmJSON[i].tag_name)) {
return helmJSON[i].tag_name
}
}
} catch (err) {
core.warning(
util.format(
'Error while fetching the latest Helm release. Error: %s. Using default Helm version %s',
err.toString(),
stableHelmVersion
)
)
return stableHelmVersion
}
return stableHelmVersion
}
// isValidVersion checks if verison is a stable release
function isValidVersion(version: string): boolean {
return version.indexOf('rc') == -1
}
export function getExecutableExtension(): string {
if (os.type().match(/^Win/)) {
return '.exe'
}
return ''
}
const LINUX = 'Linux'
const MAC_OS = 'Darwin'
const WINDOWS = 'Windows_NT'
const ARM64 = 'arm64'
export function getHelmDownloadURL(version: string): string {
const arch = os.arch()
const operatingSystem = os.type()
switch (true) {
case operatingSystem == LINUX && arch == ARM64:
return util.format(
'https://get.helm.sh/helm-%s-linux-arm64.zip',
version
)
case operatingSystem == LINUX:
return util.format(
'https://get.helm.sh/helm-%s-linux-amd64.zip',
version
)
case operatingSystem == MAC_OS && arch == ARM64:
return util.format(
'https://get.helm.sh/helm-%s-darwin-arm64.zip',
version
)
case operatingSystem == MAC_OS:
return util.format(
'https://get.helm.sh/helm-%s-darwin-amd64.zip',
version
)
case operatingSystem == WINDOWS:
default:
return util.format(
'https://get.helm.sh/helm-%s-windows-amd64.zip',
version
)
}
}
export async function downloadHelm(version: string): Promise<string> {
let cachedToolpath = toolCache.find(helmToolName, version)
if (!cachedToolpath) {
let helmDownloadPath
try {
helmDownloadPath = await toolCache.downloadTool(
getHelmDownloadURL(version)
)
} catch (exception) {
throw new Error(
util.format(
'Failed to download Helm from location',
getHelmDownloadURL(version)
)
)
}
fs.chmodSync(helmDownloadPath, '777')
const unzipedHelmPath = await toolCache.extractZip(helmDownloadPath)
cachedToolpath = await toolCache.cacheDir(
unzipedHelmPath,
helmToolName,
version
)
}
const helmpath = findHelm(cachedToolpath)
if (!helmpath) {
throw new Error(
util.format('Helm executable not found in path', cachedToolpath)
)
}
fs.chmodSync(helmpath, '777')
return helmpath
}
export function findHelm(rootFolder: string): string {
fs.chmodSync(rootFolder, '777')
var filelist: string[] = []
walkSync(rootFolder, filelist, helmToolName + getExecutableExtension())
if (!filelist || filelist.length == 0) {
throw new Error(
util.format('Helm executable not found in path', rootFolder)
)
} else {
return filelist[0]
}
}
export var walkSync = function (dir, filelist, fileToFind) {
var files = fs.readdirSync(dir)
filelist = filelist || []
files.forEach(function (file) {
if (fs.statSync(path.join(dir, file)).isDirectory()) {
filelist = walkSync(path.join(dir, file), filelist, fileToFind)
} else {
core.debug(file)
if (file == fileToFind) {
filelist.push(path.join(dir, file))
}
}
})
return filelist
}
run().catch(core.setFailed)

View File

@ -2,8 +2,8 @@
"compilerOptions": {
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
@ -11,8 +11,8 @@
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./lib", /* Redirect output structure to the directory. */
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"outDir": "./lib" /* Redirect output structure to the directory. */,
"rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
@ -23,7 +23,7 @@
/* Strict Type-Checking Options */
//"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
"noImplicitAny": false /* Raise error on expressions and declarations with an implied 'any' type. */,
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
@ -58,6 +58,7 @@
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
"skipLibCheck": true
},
"exclude": ["node_modules", "**/*.test.ts"]
}