4
0
Fork 0
mirror of https://github.com/Azure/setup-helm.git synced 2025-11-07 13:06:56 +00:00
This commit is contained in:
Marcus Vinicius Tavares 2025-10-29 16:37:25 -03:00 committed by GitHub
commit 4a94aef191
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 139 additions and 18 deletions

View file

@ -1,5 +1,11 @@
# Change Log # Change Log
## [4.4.0] - 2025-10-29
### Added
- Add fallback URL support via optional `downloadBaseURLFallback` input for improved download reliability
## [4.3.1] - 2025-08-12 ## [4.3.1] - 2025-08-12
### Changed ### Changed

View file

@ -7,7 +7,7 @@ Install a specific version of helm binary on the runner.
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+ of this action only support Helm3. 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+ of this action only support Helm3.
```yaml ```yaml
- uses: azure/setup-helm@v4.3.0 - uses: azure/setup-helm@v4.4.0
with: with:
version: '<version>' # default is latest (stable) version: '<version>' # default is latest (stable)
id: install id: install

View file

@ -14,6 +14,10 @@ inputs:
description: 'Set the download base URL' description: 'Set the download base URL'
required: false required: false
default: 'https://get.helm.sh' default: 'https://get.helm.sh'
downloadBaseURLFallback:
description: 'Fallback base URL if primary download fails'
required: false
default: ''
outputs: outputs:
helm-path: helm-path:
description: 'Path to the cached helm binary' description: 'Path to the cached helm binary'

View file

@ -1,6 +1,6 @@
{ {
"name": "setuphelm", "name": "setuphelm",
"version": "4.3.1", "version": "4.4.0",
"private": true, "private": true,
"description": "Setup helm", "description": "Setup helm",
"author": "Anumita Shenoy", "author": "Anumita Shenoy",

View file

@ -228,7 +228,7 @@ describe('run.ts', () => {
return {isDirectory: () => isDirectory} as fs.Stats return {isDirectory: () => isDirectory} as fs.Stats
}) })
expect(await run.downloadHelm(downloadBaseURL, 'v4.0.0')).toBe( expect(await run.downloadHelm(downloadBaseURL, 'v4.0.0', '')).toBe(
path.join('pathToCachedDir', 'helm.exe') path.join('pathToCachedDir', 'helm.exe')
) )
expect(toolCache.find).toHaveBeenCalledWith('helm', 'v4.0.0') expect(toolCache.find).toHaveBeenCalledWith('helm', 'v4.0.0')
@ -252,9 +252,9 @@ describe('run.ts', () => {
jest.spyOn(os, 'arch').mockReturnValue('x64') jest.spyOn(os, 'arch').mockReturnValue('x64')
const downloadUrl = 'https://test.tld/helm-v3.2.1-windows-amd64.zip' const downloadUrl = 'https://test.tld/helm-v3.2.1-windows-amd64.zip'
await expect(run.downloadHelm(downloadBaseURL, 'v3.2.1')).rejects.toThrow( await expect(
`Failed to download Helm from location ${downloadUrl}` run.downloadHelm(downloadBaseURL, 'v3.2.1', '')
) ).rejects.toThrow(`Failed to download Helm from location ${downloadUrl}`)
expect(toolCache.find).toHaveBeenCalledWith('helm', 'v3.2.1') expect(toolCache.find).toHaveBeenCalledWith('helm', 'v3.2.1')
expect(toolCache.downloadTool).toHaveBeenCalledWith(`${downloadUrl}`) expect(toolCache.downloadTool).toHaveBeenCalledWith(`${downloadUrl}`)
}) })
@ -278,7 +278,7 @@ describe('run.ts', () => {
return {isDirectory: () => isDirectory} as fs.Stats return {isDirectory: () => isDirectory} as fs.Stats
}) })
expect(await run.downloadHelm(downloadBaseURL, 'v3.2.1')).toBe( expect(await run.downloadHelm(downloadBaseURL, 'v3.2.1', '')).toBe(
path.join('pathToCachedDir', 'helm.exe') path.join('pathToCachedDir', 'helm.exe')
) )
expect(toolCache.find).toHaveBeenCalledWith('helm', 'v3.2.1') expect(toolCache.find).toHaveBeenCalledWith('helm', 'v3.2.1')
@ -304,9 +304,9 @@ describe('run.ts', () => {
return {isDirectory: () => isDirectory} as fs.Stats return {isDirectory: () => isDirectory} as fs.Stats
}) })
await expect(run.downloadHelm(downloadBaseURL, 'v3.2.1')).rejects.toThrow( await expect(
'Helm executable not found in path pathToCachedDir' run.downloadHelm(downloadBaseURL, 'v3.2.1', '')
) ).rejects.toThrow('Helm executable not found in path pathToCachedDir')
expect(toolCache.find).toHaveBeenCalledWith('helm', 'v3.2.1') expect(toolCache.find).toHaveBeenCalledWith('helm', 'v3.2.1')
expect(toolCache.downloadTool).toHaveBeenCalledWith( expect(toolCache.downloadTool).toHaveBeenCalledWith(
'https://test.tld/helm-v3.2.1-windows-amd64.zip' 'https://test.tld/helm-v3.2.1-windows-amd64.zip'
@ -314,4 +314,86 @@ describe('run.ts', () => {
expect(fs.chmodSync).toHaveBeenCalledWith('pathToTool', '777') expect(fs.chmodSync).toHaveBeenCalledWith('pathToTool', '777')
expect(toolCache.extractZip).toHaveBeenCalledWith('pathToTool') expect(toolCache.extractZip).toHaveBeenCalledWith('pathToTool')
}) })
test('downloadHelm() - use fallback URL when primary download fails', async () => {
const fallbackBaseURL = 'https://fallback.tld'
jest.spyOn(toolCache, 'find').mockReturnValue('')
jest
.spyOn(toolCache, 'downloadTool')
.mockRejectedValueOnce(new Error('Primary download failed'))
.mockResolvedValueOnce('pathToTool')
jest.spyOn(toolCache, 'extractZip').mockResolvedValue('extractedPath')
jest.spyOn(toolCache, 'cacheDir').mockResolvedValue('pathToCachedDir')
jest.spyOn(os, 'platform').mockReturnValue('win32')
jest.spyOn(os, 'arch').mockReturnValue('x64')
jest.spyOn(fs, 'chmodSync').mockImplementation()
jest.spyOn(core, 'warning').mockImplementation()
jest
.spyOn(fs, 'readdirSync')
.mockImplementation((file, _) => [
'helm.exe' as unknown as fs.Dirent<Buffer<ArrayBufferLike>>
])
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(downloadBaseURL, 'v3.2.1', fallbackBaseURL)
).toBe(path.join('pathToCachedDir', 'helm.exe'))
expect(toolCache.downloadTool).toHaveBeenCalledTimes(2)
expect(toolCache.downloadTool).toHaveBeenNthCalledWith(
1,
'https://test.tld/helm-v3.2.1-windows-amd64.zip'
)
expect(toolCache.downloadTool).toHaveBeenNthCalledWith(
2,
'https://fallback.tld/helm-v3.2.1-windows-amd64.zip'
)
expect(core.warning).toHaveBeenCalled()
})
test('downloadHelm() - throw error if both primary and fallback downloads fail', async () => {
const fallbackBaseURL = 'https://fallback.tld'
jest.spyOn(toolCache, 'find').mockReturnValue('')
jest
.spyOn(toolCache, 'downloadTool')
.mockRejectedValueOnce(new Error('Primary download failed'))
.mockRejectedValueOnce(new Error('Fallback download failed'))
jest.spyOn(os, 'platform').mockReturnValue('win32')
jest.spyOn(os, 'arch').mockReturnValue('x64')
await expect(
run.downloadHelm(downloadBaseURL, 'v3.2.1', fallbackBaseURL)
).rejects.toThrow(
'Failed to download Helm from location https://test.tld/helm-v3.2.1-windows-amd64.zip or https://fallback.tld/helm-v3.2.1-windows-amd64.zip'
)
expect(toolCache.downloadTool).toHaveBeenCalledTimes(2)
})
test('downloadHelm() - work without fallback URL (backwards compatibility)', async () => {
jest.spyOn(toolCache, 'find').mockReturnValue('')
jest.spyOn(toolCache, 'downloadTool').mockResolvedValue('pathToTool')
jest.spyOn(toolCache, 'extractZip').mockResolvedValue('extractedPath')
jest.spyOn(toolCache, 'cacheDir').mockResolvedValue('pathToCachedDir')
jest.spyOn(os, 'platform').mockReturnValue('win32')
jest.spyOn(os, 'arch').mockReturnValue('x64')
jest.spyOn(fs, 'chmodSync').mockImplementation()
jest
.spyOn(fs, 'readdirSync')
.mockImplementation((file, _) => [
'helm.exe' as unknown as fs.Dirent<Buffer<ArrayBufferLike>>
])
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(downloadBaseURL, 'v3.2.1')).toBe(
path.join('pathToCachedDir', 'helm.exe')
)
expect(toolCache.downloadTool).toHaveBeenCalledTimes(1)
})
}) })

View file

@ -24,9 +24,16 @@ export async function run() {
} }
const downloadBaseURL = core.getInput('downloadBaseURL', {required: false}) const downloadBaseURL = core.getInput('downloadBaseURL', {required: false})
const downloadBaseURLFallback = core.getInput('downloadBaseURLFallback', {
required: false
})
core.startGroup(`Installing ${version}`) core.startGroup(`Installing ${version}`)
const cachedPath = await downloadHelm(downloadBaseURL, version) const cachedPath = await downloadHelm(
downloadBaseURL,
version,
downloadBaseURLFallback
)
core.endGroup() core.endGroup()
try { try {
@ -84,7 +91,8 @@ export function getHelmDownloadURL(baseURL: string, version: string): string {
export async function downloadHelm( export async function downloadHelm(
baseURL: string, baseURL: string,
version: string version: string,
fallbackBaseURL?: string
): Promise<string> { ): Promise<string> {
let cachedToolpath = toolCache.find(helmToolName, version) let cachedToolpath = toolCache.find(helmToolName, version)
if (cachedToolpath) { if (cachedToolpath) {
@ -97,6 +105,26 @@ export async function downloadHelm(
getHelmDownloadURL(baseURL, version) getHelmDownloadURL(baseURL, version)
) )
} catch (exception) { } catch (exception) {
if (fallbackBaseURL) {
core.warning(
`Failed to download Helm from location ${getHelmDownloadURL(
baseURL,
version
)}. Attempting to download from fallback URL: ${fallbackBaseURL}`
)
try {
helmDownloadPath = await toolCache.downloadTool(
getHelmDownloadURL(fallbackBaseURL, version)
)
} catch (fallbackException) {
throw new Error(
`Failed to download Helm from location ${getHelmDownloadURL(
baseURL,
version
)} or ${getHelmDownloadURL(fallbackBaseURL, version)}`
)
}
} else {
throw new Error( throw new Error(
`Failed to download Helm from location ${getHelmDownloadURL( `Failed to download Helm from location ${getHelmDownloadURL(
baseURL, baseURL,
@ -104,6 +132,7 @@ export async function downloadHelm(
)}` )}`
) )
} }
}
fs.chmodSync(helmDownloadPath, '777') fs.chmodSync(helmDownloadPath, '777')
const extractedPath = const extractedPath =