diff --git a/.github/workflows/test-action.yml b/.github/workflows/test-action.yml index 9dc0a68..a6e6bdc 100644 --- a/.github/workflows/test-action.yml +++ b/.github/workflows/test-action.yml @@ -16,7 +16,7 @@ jobs: test-custom-version: runs-on: ubuntu-latest env: - TEST_VERSION: '1.19.0' + TEST_VERSION: '1.18.0' VERSION_OFFSET: 27 # internal version = minor + offset (v1.19 → v0.46) steps: - uses: actions/checkout@v4 diff --git a/index.js b/index.js index e29ec8a..c1e33c0 100644 --- a/index.js +++ b/index.js @@ -3,12 +3,6 @@ const exec = require('@actions/exec'); const path = require('path'); const fs = require('fs'); -// Using latest as default -const DEFAULT_FUNC_VERSION = 'latest'; -const DEFAULT_BINARY_SOURCE = 'https://github.com/knative/func/releases/download'; -const DEFAULT_LATEST_BINARY_SOURCE = 'https://github.com/knative/func/releases/latest/download'; - -// Returns the binary name for the current OS/arch from GitHub releases function getOsBinName() { const osBinName = core.getInput('binary'); if (osBinName !== "") { @@ -47,116 +41,90 @@ function resolveFullPathBin() { return path.resolve(destination, bin); } -// Normalizes version to release tag format: knative-vX.Y.Z -// Ex.: '1.16' or 'v1.16' will return 'knative-v1.16.0' function smartVersionUpdate(version) { - const versionRegex = /^(?knative-)?(?v?)(?\d+)\.(?\d+)(\.(?\d+))?$/; - const match = version.match(versionRegex); - if (!match) { - throw new Error(`Invalid version format (${version}). Expected format: "1.16[.X]" or "v1.16[.X]"`); + const match = version.match(/^(?:knative-)?v?(\d+)\.(\d+)(?:\.(\d+))?$/); + if (!match) throw new Error(`Invalid version format (${version}). Expected format: "1.16[.X]" or "v1.16[.X]"`); + return `knative-v${match[1]}.${match[2]}.${match[3] ?? 0}`; +} + +function resolveVersion() { + if (core.getInput('binarySource')) return null; + const version = core.getInput('version') || 'latest'; + if (version.toLowerCase().trim() === 'latest') return null; + return smartVersionUpdate(version); +} + +function resolveDownloadUrl(version, binName) { + const binarySource = core.getInput('binarySource'); + if (binarySource) { + core.info(`Using custom binary source: ${binarySource}`); + return binarySource; + } + + if (!version) { + core.info('Using latest version...'); + return `https://github.com/knative/func/releases/latest/download/${binName}`; } - const knprefix = 'knative-'; - const prefix = 'v'; - const patch = match.groups.patch ?? 0; - return `${knprefix}${prefix}${match.groups.major}.${match.groups.minor}.${patch}`; + core.info(`Using specific version ${version}`); + return `https://github.com/knative/func/releases/download/${version}/${binName}`; } -// Downloads binary from release URL and makes it executable async function downloadFuncBinary(url, binPath) { core.info(`Downloading from: ${url}`); - await exec.exec('curl', ['-L', '--fail', '-o', binPath, url]); if (!fs.existsSync(binPath)) { throw new Error("Download failed, couldn't find the binary on disk"); } - if (process.env.RUNNER_OS !== 'Windows') { await exec.exec('chmod', ['+x', binPath]); } } -// Adds binary directory to PATH for current and subsequent steps function addBinToPath(binPath) { const dir = path.dirname(binPath); fs.appendFileSync(process.env.GITHUB_PATH, `\n${dir}`); - if (!process.env.PATH.split(path.delimiter).includes(dir)) { - process.env.PATH = process.env.PATH + path.delimiter + dir; + process.env.PATH += path.delimiter + dir; core.info(`${dir} added to PATH`); } } -// Resolve download url based on given input -// binName: name of func binary when it is to be constructed for full URL -// (when not using binarySource) -function resolveDownloadUrl(binName) { - const binarySource = core.getInput('binarySource'); - if (binarySource !== "") { - core.info(`Using custom binary source: ${binarySource}`); - return binarySource; - } - - const versionInput = core.getInput('version') || DEFAULT_FUNC_VERSION; - if (versionInput.toLowerCase().trim() === DEFAULT_FUNC_VERSION) { - core.info("Using latest version..."); - return buildUrlString(DEFAULT_FUNC_VERSION); - } - const version = smartVersionUpdate(versionInput); - core.info(`Using specific version ${version}`); - return buildUrlString(version); - - function buildUrlString(version) { - return version === DEFAULT_FUNC_VERSION - ? `${DEFAULT_LATEST_BINARY_SOURCE}/${binName}` - : `${DEFAULT_BINARY_SOURCE}/${version}/${binName}`; +async function warnStaleVersion(version) { + try { + const res = await fetch('https://github.com/knative/func/releases/latest', { + method: 'HEAD', + redirect: 'manual', + }); + const loc = res.headers.get('location'); + if (!loc) return; + + const latest = loc.split('/').pop(); + const toNum = (v) => { const m = v.match(/(\d+)\.(\d+)/); return m && m[1] * 100 + +m[2]; }; + const diff = toNum(latest) - toNum(version); + + if (diff >= 3) { + core.warning(`You are using func ${version}, which is ${diff} minor versions behind the latest (${latest}). Upgrading is recommended.`); + } + } catch { + core.debug('Skipping stale version check'); } } async function run() { - let osBinName; - try { - osBinName = getOsBinName(); - } catch (error) { - core.setFailed(error.message); - return; - } - - let url; try { - url = resolveDownloadUrl(osBinName); - } catch (error) { - core.setFailed(`Failed to resolve url: ${error.message}`); - return; - } - - let fullPathBin; - try { - fullPathBin = resolveFullPathBin(); - } catch (error) { - core.setFailed(error.message); - return; - } + const osBinName = getOsBinName(); + const version = resolveVersion(); + const url = resolveDownloadUrl(version, osBinName); + const fullPathBin = resolveFullPathBin(); - try { await downloadFuncBinary(url, fullPathBin); - } catch (error) { - core.setFailed(`Download failed: ${error.message}`); - return; - } - - try { addBinToPath(fullPathBin); - } catch (error) { - core.setFailed(error.message); - return; - } - try { + if (version) await warnStaleVersion(version); await exec.exec(fullPathBin, ['version']); } catch (error) { core.setFailed(error.message); - return; } }