Skip to content

Commit 2e2e559

Browse files
committed
support latest version
1 parent 675fe07 commit 2e2e559

4 files changed

Lines changed: 154 additions & 95 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+
| `binarySource` | Full URL for the func binary | empty (uses GitHub releases) |

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: 136 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -3,112 +3,161 @@ 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';
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';
810

911
// Returns the binary name for the current OS/arch from GitHub releases
1012
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';
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';
28-
}
13+
const osBinName = core.getInput('binary');
14+
if (osBinName !== "") {
15+
return osBinName;
16+
}
17+
18+
const runnerOS = process.env.RUNNER_OS;
19+
const runnerArch = process.env.RUNNER_ARCH;
20+
21+
if (runnerOS === 'Linux') {
22+
switch (runnerArch) {
23+
case 'X64': return 'func_linux_amd64';
24+
case 'ARM64': return 'func_linux_arm64';
25+
case 'PPC64LE': return 'func_linux_ppc64le';
26+
case 'S390X': return 'func_linux_s390x';
27+
default: throw new Error(`unknown runner: ${runnerArch}`);
28+
}
29+
} else if (runnerOS === 'macOS') {
30+
return runnerArch === 'X64' ? 'func_darwin_amd64' : 'func_darwin_arm64';
31+
} else if (runnerOS === 'Windows') {
32+
return 'func_windows_amd64.exe';
33+
} else {
34+
throw new Error(`unknown runner: ${runnerArch}`);
35+
}
36+
}
37+
38+
function resolveFullPathBin() {
39+
const destination = core.getInput('destination') || process.cwd();
40+
let bin = core.getInput('name') || 'func';
41+
if (process.env.RUNNER_OS === 'Windows' && !bin.endsWith('.exe')) {
42+
bin += '.exe';
43+
}
44+
if (!fs.existsSync(destination)) {
45+
fs.mkdirSync(destination, { recursive: true });
46+
}
47+
return path.resolve(destination, bin);
2948
}
3049

3150
// Normalizes version to release tag format: knative-vX.Y.Z
3251
// Ex.: '1.16' or 'v1.16' will return 'knative-v1.16.0'
3352
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}`;
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]"`);
57+
}
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}`;
4362
}
4463

45-
const DEFAULT_BINARY_SOURCE = 'https://github.com/knative/func/releases/download';
46-
4764
// 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}`);
65+
async function downloadFuncBinary(url, binPath) {
66+
core.info(`Downloading from: ${url}`);
5167

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

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

58-
if (process.env.RUNNER_OS !== 'Windows') {
59-
await exec.exec('chmod', ['+x', binPath]);
60-
}
74+
if (process.env.RUNNER_OS !== 'Windows') {
75+
await exec.exec('chmod', ['+x', binPath]);
76+
}
6177
}
6278

6379
// Adds binary directory to PATH for current and subsequent steps
64-
async function addBinToPath(binPath) {
65-
const dir = path.dirname(binPath);
66-
fs.appendFileSync(process.env.GITHUB_PATH, `\n${dir}`);
67-
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-
}
80+
function addBinToPath(binPath) {
81+
const dir = path.dirname(binPath);
82+
fs.appendFileSync(process.env.GITHUB_PATH, `\n${dir}`);
83+
84+
if (!process.env.PATH.split(path.delimiter).includes(dir)) {
85+
process.env.PATH = process.env.PATH + path.delimiter + dir;
86+
core.info(`${dir} added to PATH`);
87+
}
88+
}
89+
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+
}
72114
}
73115

74116
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']);
117+
let osBinName;
118+
try {
119+
osBinName = getOsBinName();
120+
} catch (error) {
121+
core.setFailed(error.message);
122+
return;
123+
}
124+
125+
let url;
126+
try {
127+
url = resolveDownloadUrl(osBinName);
128+
} catch (error) {
129+
core.setFailed(`Failed to resolve url: ${error.message}`);
130+
return;
131+
}
132+
133+
let fullPathBin;
134+
try {
135+
fullPathBin = resolveFullPathBin();
136+
} catch (error) {
137+
core.setFailed(error.message);
138+
return;
139+
}
140+
141+
try {
142+
await downloadFuncBinary(url, fullPathBin);
143+
} catch (error) {
144+
core.setFailed(`Download failed: ${error.message}`);
145+
return;
146+
}
147+
148+
try {
149+
addBinToPath(fullPathBin);
150+
} catch (error) {
151+
core.setFailed(error.message);
152+
return;
153+
}
154+
155+
try {
156+
await exec.exec(fullPathBin, ['version']);
157+
} catch (error) {
158+
core.setFailed(error.message);
159+
return;
160+
}
112161
}
113162

114-
run();
163+
run();

0 commit comments

Comments
 (0)