Skip to content

Commit bf0aeeb

Browse files
committed
support latest version
1 parent 675fe07 commit bf0aeeb

4 files changed

Lines changed: 128 additions & 91 deletions

File tree

.github/workflows/test-action.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: test-action
22
on: [pull_request, workflow_dispatch]
33

44
jobs:
5-
test-default:
5+
test-default-latest:
66
strategy:
77
matrix:
88
os: [ubuntu-latest,windows-latest,macos-latest]
@@ -52,6 +52,16 @@ jobs:
5252
- name: Verify func using path
5353
run: /tmp/func-bin/func version
5454

55+
test-custom-binary-source:
56+
runs-on: ubuntu-latest
57+
steps:
58+
- uses: actions/checkout@v4
59+
- name: Setup func CLI with custom binarySource URL
60+
uses: ./
61+
with:
62+
binarySource: 'https://github.com/knative/func/releases/download/knative-v1.19.0/func_linux_amd64'
63+
- run: func version
64+
5565
test-invalid-version:
5666
runs-on: ubuntu-latest
5767
steps:

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ GitHub Action to download and setup the func CLI. Automatically detects OS and a
77
```yaml
88
- uses: functions-dev/action@main
99
with:
10-
version: 'v1.20.0' # optional
11-
name: 'func' # optional
10+
version: 'v1.20.0' # optional - uses latest as default
1211
```
1312
1413
## Inputs
1514
1615
| Input | Description | Default |
1716
|-------|-------------|---------|
18-
| `version` | Version to download (e.g. `v1.20.0`) | recent stable |
17+
| `version` | Version to download (e.g. `v1.20.0`) | latest |
1918
| `name` | Binary name | `func` |
20-
| `binary` | Specific binary to download | auto-detected |
19+
| `binary` | Specific binary to download from GitHub release | auto-detected |
2120
| `destination` | Download directory | cwd |
21+
| `urlSource` | Full URL for downloading func binary | Github release |

action.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ inputs:
66
binary:
77
description: '(optional) Binary you want to download (exact string expected), otherwise will be determined via the OS of GH Runner'
88
version:
9-
description: '(optional) Provide version to download. Any version in release pages works https://github.com/knative/func/tags'
9+
description: '(optional) Version to download. Use "latest" or specify a version from release pages https://github.com/knative/func/tags'
1010
destination:
11-
description: '(optional) Provide a path where to move the desired downloaded binary, otherwise cwd is used'
11+
description: '(optional) Path where to move the desired downloaded binary, otherwise cwd is used'
1212
binarySource:
13-
description: '(optional) Base URL for downloading binaries. Defaults to GitHub releases. Pattern: <url-base>/<version>/<os-bin-name>'
13+
description: '(optional) Full URL for downloading the binary. Overrides version if provided. Must be curl-able returning the func binary'
1414
runs:
1515
using: 'node20'
1616
main: 'index.js'

index.js

Lines changed: 110 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,112 +3,139 @@ const exec = require('@actions/exec');
33
const path = require('path');
44
const fs = require('fs');
55

6-
// Static version - update manually when new func releases are available
7-
const DEFAULT_FUNC_VERSION = 'knative-v1.20.1';
6+
// Using latest as default
7+
const DEFAULT_FUNC_VERSION = 'latest';
88

99
// Returns the binary name for the current OS/arch from GitHub releases
1010
function getOsBinName() {
11-
const runnerOS = process.env.RUNNER_OS;
12-
const runnerArch = process.env.RUNNER_ARCH;
13-
14-
if (runnerOS === 'Linux') {
15-
switch (runnerArch) {
16-
case 'X64': return 'func_linux_amd64';
17-
case 'ARM64': return 'func_linux_arm64';
18-
case 'PPC64LE': return 'func_linux_ppc64le';
19-
case 'S390X': return 'func_linux_s390x';
20-
default: return 'unknown';
11+
const runnerOS = process.env.RUNNER_OS;
12+
const runnerArch = process.env.RUNNER_ARCH;
13+
14+
if (runnerOS === 'Linux') {
15+
switch (runnerArch) {
16+
case 'X64': return 'func_linux_amd64';
17+
case 'ARM64': return 'func_linux_arm64';
18+
case 'PPC64LE': return 'func_linux_ppc64le';
19+
case 'S390X': return 'func_linux_s390x';
20+
default: return 'unknown';
21+
}
22+
} else if (runnerOS === 'macOS') {
23+
return runnerArch === 'X64' ? 'func_darwin_amd64' : 'func_darwin_arm64';
24+
} else if (runnerOS === 'Windows') {
25+
return 'func_windows_amd64.exe';
26+
} else {
27+
return 'unknown';
2128
}
22-
} else if (runnerOS === 'macOS') {
23-
return runnerArch === 'X64' ? 'func_darwin_amd64' : 'func_darwin_arm64';
24-
} else if (runnerOS === 'Windows') {
25-
return 'func_windows_amd64.exe';
26-
} else {
27-
return 'unknown';
28-
}
2929
}
3030

3131
// Normalizes version to release tag format: knative-vX.Y.Z
3232
// Ex.: '1.16' or 'v1.16' will return 'knative-v1.16.0'
3333
function smartVersionUpdate(version) {
34-
const versionRegex = /^(?<knprefix>knative-)?(?<prefix>v?)(?<major>\d+)\.(?<minor>\d+)(.(?<patch>\d+))?$/;
35-
const match = version.match(versionRegex);
36-
if (!match) {
37-
throw new Error(`Invalid version format (${version}). Expected format: "1.16[.X]" or "v1.16[.X]"`);
38-
}
39-
const knprefix = 'knative-';
40-
const prefix = 'v';
41-
const patch = match.groups.patch ?? 0;
42-
return `${knprefix}${prefix}${match.groups.major}.${match.groups.minor}.${patch}`;
34+
const versionRegex = /^(?<knprefix>knative-)?(?<prefix>v?)(?<major>\d+)\.(?<minor>\d+)(.(?<patch>\d+))?$/;
35+
const match = version.match(versionRegex);
36+
if (!match) {
37+
throw new Error(`Invalid version format (${version}). Expected format: "1.16[.X]" or "v1.16[.X]"`);
38+
}
39+
const knprefix = 'knative-';
40+
const prefix = 'v';
41+
const patch = match.groups.patch ?? 0;
42+
return `${knprefix}${prefix}${match.groups.major}.${match.groups.minor}.${patch}`;
4343
}
4444

45-
const DEFAULT_BINARY_SOURCE = 'https://github.com/knative/func/releases/download';
4645

4746
// Downloads binary from release URL and makes it executable
48-
async function downloadFuncBinary(version, osBinName, binPath, binarySource) {
49-
const url = `${binarySource}/${version}/${osBinName}`;
50-
core.info(`Downloading from: ${url}`);
47+
async function downloadFuncBinary(url, binPath) {
48+
core.info(`Downloading from: ${url}`);
5149

52-
await exec.exec('curl', ['-L', '--fail', '-o', binPath, url]);
50+
await exec.exec('curl', ['-L', '--fail', '-o', binPath, url]);
5351

54-
if (!fs.existsSync(binPath)) {
55-
throw new Error("Download failed, couldn't find the binary on disk");
56-
}
52+
if (!fs.existsSync(binPath)) {
53+
throw new Error("Download failed, couldn't find the binary on disk");
54+
}
5755

58-
if (process.env.RUNNER_OS !== 'Windows') {
59-
await exec.exec('chmod', ['+x', binPath]);
60-
}
56+
if (process.env.RUNNER_OS !== 'Windows') {
57+
await exec.exec('chmod', ['+x', binPath]);
58+
}
6159
}
6260

6361
// Adds binary directory to PATH for current and subsequent steps
6462
async function addBinToPath(binPath) {
65-
const dir = path.dirname(binPath);
66-
fs.appendFileSync(process.env.GITHUB_PATH, `\n${dir}`);
63+
const dir = path.dirname(binPath);
64+
fs.appendFileSync(process.env.GITHUB_PATH, `\n${dir}`);
65+
66+
if (!process.env.PATH.includes(dir)) {
67+
process.env.PATH = process.env.PATH + path.delimiter + dir;
68+
core.info(`${dir} added to PATH`);
69+
}
70+
}
6771

68-
if (!process.env.PATH.includes(dir)) {
69-
process.env.PATH = process.env.PATH + path.delimiter + dir;
70-
core.info(`${dir} added to PATH`);
71-
}
72+
const DEFAULT_BINARY_SOURCE = 'https://github.com/knative/func/releases/download';
73+
const DEFAULT_LATEST_BINARY_SOURCE = 'https://github.com/knative/func/releases/latest/download';
74+
75+
// resolve url based on given input
76+
// version: can be 'latest' or specific version based on GitHub releases
77+
// bin: binary name - is passed in to form the full url
78+
function resolveUrl(version, bin) {
79+
let base;
80+
let last;
81+
if (version === "latest") {
82+
base = DEFAULT_LATEST_BINARY_SOURCE;
83+
last = bin
84+
} else {
85+
base = DEFAULT_BINARY_SOURCE;
86+
last = `${version}/${bin}`
87+
}
88+
return `${base}/${last}`
7289
}
7390

7491
async function run() {
75-
const osBinName = core.getInput('binary') || getOsBinName();
76-
if (osBinName === "unknown") {
77-
core.setFailed("Invalid os binary determination, try setting it specifically using 'binary'");
78-
return;
79-
}
80-
81-
const versionInput = core.getInput('version') || DEFAULT_FUNC_VERSION;
82-
const destination = core.getInput('destination') || process.cwd();
83-
const binarySource = core.getInput('binarySource') || DEFAULT_BINARY_SOURCE;
84-
let bin = core.getInput('name') || 'func';
85-
if (process.env.RUNNER_OS === 'Windows' && !bin.endsWith('.exe')) {
86-
bin += '.exe';
87-
}
88-
89-
let version;
90-
try {
91-
version = smartVersionUpdate(versionInput);
92-
} catch (error) {
93-
core.setFailed(error.message);
94-
return;
95-
}
96-
97-
if (!fs.existsSync(destination)) {
98-
fs.mkdirSync(destination, { recursive: true });
99-
}
100-
101-
const fullPathBin = path.resolve(destination, bin);
102-
103-
try {
104-
await downloadFuncBinary(version, osBinName, fullPathBin, binarySource);
105-
} catch (error) {
106-
core.setFailed(`Download failed: ${error.message}`);
107-
return;
108-
}
109-
110-
await addBinToPath(fullPathBin);
111-
await exec.exec(fullPathBin, ['version']);
92+
const osBinName = core.getInput('binary') || getOsBinName();
93+
if (osBinName === "unknown") {
94+
core.setFailed("Invalid os binary determination, try setting it specifically using 'binary'");
95+
return;
96+
}
97+
98+
const versionInput = core.getInput('version') || DEFAULT_FUNC_VERSION;
99+
const destination = core.getInput('destination') || process.cwd();
100+
const binarySourceInput = core.getInput('binarySource');
101+
let bin = core.getInput('name') || 'func';
102+
if (process.env.RUNNER_OS === 'Windows' && !bin.endsWith('.exe')) {
103+
bin += '.exe';
104+
}
105+
106+
let url;
107+
if (binarySourceInput) {
108+
core.info(`Using custom binary source: ${binarySourceInput}`);
109+
url = binarySourceInput;
110+
} else if (versionInput.toLowerCase().trim() == 'latest') {
111+
// use latest version
112+
core.info("Using latest version...");
113+
url = resolveUrl('latest', osBinName);
114+
} else {
115+
// try smart version update
116+
try {
117+
const version = smartVersionUpdate(versionInput);
118+
url = resolveUrl(version, osBinName)
119+
} catch (error) {
120+
core.setFailed(error.message);
121+
return;
122+
}
123+
}
124+
125+
if (!fs.existsSync(destination)) {
126+
fs.mkdirSync(destination, { recursive: true });
127+
}
128+
129+
const fullPathBin = path.resolve(destination, bin);
130+
try {
131+
await downloadFuncBinary(url, fullPathBin);
132+
} catch (error) {
133+
core.setFailed(`Download failed: ${error.message}`);
134+
return;
135+
}
136+
137+
await addBinToPath(fullPathBin);
138+
await exec.exec(fullPathBin, ['version']);
112139
}
113140

114-
run();
141+
run();

0 commit comments

Comments
 (0)