Skip to content

Commit 90f77ec

Browse files
authored
Warn on older versions of func
Warn on older versions of func
2 parents 7660a62 + 7573b59 commit 90f77ec

2 files changed

Lines changed: 71 additions & 82 deletions

File tree

.github/workflows/test-action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
test-custom-version:
1717
runs-on: ubuntu-latest
1818
env:
19-
TEST_VERSION: '1.19.0'
19+
TEST_VERSION: '1.18.0'
2020
VERSION_OFFSET: 27 # internal version = minor + offset (v1.19 → v0.46)
2121
steps:
2222
- uses: actions/checkout@v4

index.js

Lines changed: 70 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,11 @@ const exec = require('@actions/exec');
33
const path = require('path');
44
const fs = require('fs');
55

6-
// Using latest as default
7-
const DEFAULT_FUNC_VERSION = 'latest';
8-
const DEFAULT_BINARY_SOURCE = 'https://github.com/knative/func/releases/download';
9-
const DEFAULT_LATEST_BINARY_SOURCE = 'https://github.com/knative/func/releases/latest/download';
6+
const LATEST = 'latest';
107

11-
// Returns the binary name for the current OS/arch from GitHub releases
128
function getOsBinName() {
139
const osBinName = core.getInput('binary');
14-
if (osBinName !== "") {
10+
if (osBinName) {
1511
return osBinName;
1612
}
1713

@@ -24,14 +20,14 @@ function getOsBinName() {
2420
case 'ARM64': return 'func_linux_arm64';
2521
case 'PPC64LE': return 'func_linux_ppc64le';
2622
case 'S390X': return 'func_linux_s390x';
27-
default: throw new Error(`unknown runner: ${runnerArch}`);
23+
default: throw new Error(`unknown runner arch: ${runnerArch}`);
2824
}
2925
} else if (runnerOS === 'macOS') {
3026
return runnerArch === 'X64' ? 'func_darwin_amd64' : 'func_darwin_arm64';
3127
} else if (runnerOS === 'Windows') {
3228
return 'func_windows_amd64.exe';
3329
} else {
34-
throw new Error(`unknown runner: ${runnerArch}`);
30+
throw new Error(`unknown runner os: ${runnerOS}`);
3531
}
3632
}
3733

@@ -47,117 +43,110 @@ function resolveFullPathBin() {
4743
return path.resolve(destination, bin);
4844
}
4945

50-
// Normalizes version to release tag format: knative-vX.Y.Z
51-
// Ex.: '1.16' or 'v1.16' will return 'knative-v1.16.0'
5246
function smartVersionUpdate(version) {
53-
const versionRegex = /^(?<knprefix>knative-)?(?<prefix>v?)(?<major>\d+)\.(?<minor>\d+)(\.(?<patch>\d+))?$/;
54-
const match = version.match(versionRegex);
55-
if (!match) {
56-
throw new Error(`Invalid version format (${version}). Expected format: "1.16[.X]" or "v1.16[.X]"`);
47+
const match = version.match(/^(?:knative-)?v?(\d+)\.(\d+)(?:\.(\d+))?$/);
48+
if (!match) throw new Error(`Invalid version format (${version}). Expected format: "1.16[.X]" or "v1.16[.X]"`);
49+
return `knative-v${match[1]}.${match[2]}.${match[3] ?? 0}`;
50+
}
51+
52+
function resolveVersion() {
53+
const version = core.getInput('version') || LATEST;
54+
if (version.toLowerCase().trim() === LATEST) return LATEST;
55+
return smartVersionUpdate(version);
56+
}
57+
58+
function resolveDownloadUrl(version, binName) {
59+
const binarySource = core.getInput('binarySource');
60+
if (binarySource) {
61+
core.info(`Using custom binary source: ${binarySource}`);
62+
return binarySource;
63+
}
64+
65+
if (version === LATEST) {
66+
core.info('Using latest version...');
67+
return `https://github.com/knative/func/releases/latest/download/${binName}`;
5768
}
58-
const knprefix = 'knative-';
59-
const prefix = 'v';
60-
const patch = match.groups.patch ?? 0;
61-
return `${knprefix}${prefix}${match.groups.major}.${match.groups.minor}.${patch}`;
69+
core.info(`Using specific version ${version}`);
70+
return `https://github.com/knative/func/releases/download/${version}/${binName}`;
6271
}
6372

64-
// Downloads binary from release URL and makes it executable
6573
async function downloadFuncBinary(url, binPath) {
6674
core.info(`Downloading from: ${url}`);
67-
6875
await exec.exec('curl', ['-L', '--fail', '-o', binPath, url]);
6976

7077
if (!fs.existsSync(binPath)) {
7178
throw new Error("Download failed, couldn't find the binary on disk");
7279
}
73-
7480
if (process.env.RUNNER_OS !== 'Windows') {
7581
await exec.exec('chmod', ['+x', binPath]);
7682
}
7783
}
7884

79-
// Adds binary directory to PATH for current and subsequent steps
8085
function addBinToPath(binPath) {
8186
const dir = path.dirname(binPath);
8287
fs.appendFileSync(process.env.GITHUB_PATH, `\n${dir}`);
83-
8488
if (!process.env.PATH.split(path.delimiter).includes(dir)) {
85-
process.env.PATH = process.env.PATH + path.delimiter + dir;
89+
process.env.PATH += path.delimiter + dir;
8690
core.info(`${dir} added to PATH`);
8791
}
8892
}
8993

90-
// Resolve download url based on given input
91-
// binName: name of func binary when it is to be constructed for full URL
92-
// (when not using binarySource)
93-
function resolveDownloadUrl(binName) {
94-
const binarySource = core.getInput('binarySource');
95-
if (binarySource !== "") {
96-
core.info(`Using custom binary source: ${binarySource}`);
97-
return binarySource;
98-
}
99-
100-
const versionInput = core.getInput('version') || DEFAULT_FUNC_VERSION;
101-
if (versionInput.toLowerCase().trim() === DEFAULT_FUNC_VERSION) {
102-
core.info("Using latest version...");
103-
return buildUrlString(DEFAULT_FUNC_VERSION);
104-
}
105-
const version = smartVersionUpdate(versionInput);
106-
core.info(`Using specific version ${version}`);
107-
return buildUrlString(version);
108-
109-
function buildUrlString(version) {
110-
return version === DEFAULT_FUNC_VERSION
111-
? `${DEFAULT_LATEST_BINARY_SOURCE}/${binName}`
112-
: `${DEFAULT_BINARY_SOURCE}/${version}/${binName}`;
113-
}
114-
}
115-
116-
async function run() {
117-
let osBinName;
118-
try {
119-
osBinName = getOsBinName();
120-
} catch (error) {
121-
core.setFailed(error.message);
94+
async function warnStaleVersion(version) {
95+
// Skip version check for 'latest' or custom binary source
96+
if (version === LATEST || core.getInput('binarySource')) {
12297
return;
12398
}
12499

125-
let url;
100+
let res;
126101
try {
127-
url = resolveDownloadUrl(osBinName);
102+
res = await fetch('https://github.com/knative/func/releases/latest', {
103+
method: 'HEAD',
104+
redirect: 'manual',
105+
});
128106
} catch (error) {
129-
core.setFailed(`Failed to resolve url: ${error.message}`);
107+
core.warning(`Failed to fetch latest version: ${error.message}`);
108+
core.debug('Skipping stale version check');
130109
return;
131110
}
132111

133-
let fullPathBin;
134-
try {
135-
fullPathBin = resolveFullPathBin();
136-
} catch (error) {
137-
core.setFailed(error.message);
112+
const loc = res.headers.get('location');
113+
if (!loc) {
114+
core.warning('Could not determine latest version: no redirect location');
115+
core.debug('Skipping stale version check');
138116
return;
139117
}
140118

141-
try {
142-
await downloadFuncBinary(url, fullPathBin);
143-
} catch (error) {
144-
core.setFailed(`Download failed: ${error.message}`);
119+
const latest = loc.split('/').pop();
120+
// Convert version string "vX.Y" to number (X*100 + Y) for comparison,
121+
// e.g. "v1.21" → 121.
122+
// Returns null if no match, otherwise multiplies major by 100 and adds minor
123+
// (+ coerces string to number).
124+
const toNum = (v) => { const m = v.match(/(\d+)\.(\d+)/); return m && m[1] * 100 + +m[2]; };
125+
const latestNum = toNum(latest);
126+
const versionNum = toNum(version);
127+
if (versionNum == null || latestNum == null) {
128+
core.debug('could not parse version numbers to compare');
145129
return;
146130
}
147-
148-
try {
149-
addBinToPath(fullPathBin);
150-
} catch (error) {
151-
core.setFailed(error.message);
152-
return;
131+
const diff = latestNum - versionNum;
132+
if (diff >= 3) {
133+
core.warning(`You are using func ${version}, which is ${diff} minor versions behind the latest (${latest}). Upgrading is recommended.`);
153134
}
135+
}
154136

155-
try {
156-
await exec.exec(fullPathBin, ['version']);
157-
} catch (error) {
158-
core.setFailed(error.message);
159-
return;
160-
}
137+
async function run() {
138+
const osBinName = getOsBinName();
139+
const version = resolveVersion();
140+
await warnStaleVersion(version);
141+
142+
const url = resolveDownloadUrl(version, osBinName);
143+
const fullPathBin = resolveFullPathBin();
144+
145+
await downloadFuncBinary(url, fullPathBin);
146+
addBinToPath(fullPathBin);
147+
148+
await exec.exec(fullPathBin, ['version']);
161149
}
162150

163-
run();
151+
run().catch(error => core.setFailed(error.message));
152+

0 commit comments

Comments
 (0)