Skip to content

Commit 2416d3b

Browse files
committed
support latest version
1 parent 675fe07 commit 2416d3b

4 files changed

Lines changed: 124 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+
| `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: 106 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,112 +3,135 @@ 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';
13+
const runnerOS = process.env.RUNNER_OS;
14+
const runnerArch = process.env.RUNNER_ARCH;
15+
16+
if (runnerOS === 'Linux') {
17+
switch (runnerArch) {
18+
case 'X64': return 'func_linux_amd64';
19+
case 'ARM64': return 'func_linux_arm64';
20+
case 'PPC64LE': return 'func_linux_ppc64le';
21+
case 'S390X': return 'func_linux_s390x';
22+
default: return 'unknown';
23+
}
24+
} else if (runnerOS === 'macOS') {
25+
return runnerArch === 'X64' ? 'func_darwin_amd64' : 'func_darwin_arm64';
26+
} else if (runnerOS === 'Windows') {
27+
return 'func_windows_amd64.exe';
28+
} else {
29+
return 'unknown';
2130
}
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-
}
2931
}
3032

3133
// Normalizes version to release tag format: knative-vX.Y.Z
3234
// Ex.: '1.16' or 'v1.16' will return 'knative-v1.16.0'
3335
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}`;
36+
const versionRegex = /^(?<knprefix>knative-)?(?<prefix>v?)(?<major>\d+)\.(?<minor>\d+)(\.(?<patch>\d+))?$/;
37+
const match = version.match(versionRegex);
38+
if (!match) {
39+
throw new Error(`Invalid version format (${version}). Expected format: "1.16[.X]" or "v1.16[.X]"`);
40+
}
41+
const knprefix = 'knative-';
42+
const prefix = 'v';
43+
const patch = match.groups.patch ?? 0;
44+
return `${knprefix}${prefix}${match.groups.major}.${match.groups.minor}.${patch}`;
4345
}
4446

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

4748
// 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}`);
49+
async function downloadFuncBinary(url, binPath) {
50+
core.info(`Downloading from: ${url}`);
5151

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

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

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

6363
// Adds binary directory to PATH for current and subsequent steps
6464
async function addBinToPath(binPath) {
65-
const dir = path.dirname(binPath);
66-
fs.appendFileSync(process.env.GITHUB_PATH, `\n${dir}`);
65+
const dir = path.dirname(binPath);
66+
fs.appendFileSync(process.env.GITHUB_PATH, `\n${dir}`);
6767

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-
}
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+
}
73+
74+
// Resolve download url based on given input
75+
// binName: name of func binary when it is to be constructed for full URL
76+
// (when not using binarySource)
77+
function resolveDownloadUrl(binName) {
78+
const binarySource = core.getInput('binarySource');
79+
if (binarySource !== "") {
80+
core.info(`Using custom binary source: ${binarySource}`);
81+
return binarySource;
82+
}
83+
84+
const versionInput = core.getInput('version') || DEFAULT_FUNC_VERSION;
85+
if (versionInput.toLowerCase().trim() === DEFAULT_FUNC_VERSION) {
86+
core.info("Using latest version...");
87+
return buildUrlString(DEFAULT_FUNC_VERSION);
88+
}
89+
const version = smartVersionUpdate(versionInput);
90+
core.info(`Using specific version ${version}`);
91+
return buildUrlString(version);
92+
93+
function buildUrlString(version) {
94+
return version === DEFAULT_FUNC_VERSION
95+
? `${DEFAULT_LATEST_BINARY_SOURCE}/${binName}`
96+
: `${DEFAULT_BINARY_SOURCE}/${version}/${binName}`;
97+
}
7298
}
7399

74100
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']);
101+
const osBinName = core.getInput('binary') || getOsBinName();
102+
if (osBinName === "unknown") {
103+
core.setFailed("Invalid os binary determination, try setting it specifically using 'binary'");
104+
return;
105+
}
106+
107+
const destination = core.getInput('destination') || process.cwd();
108+
let bin = core.getInput('name') || 'func';
109+
if (process.env.RUNNER_OS === 'Windows' && !bin.endsWith('.exe')) {
110+
bin += '.exe';
111+
}
112+
113+
let url;
114+
try {
115+
url = resolveDownloadUrl(osBinName);
116+
} catch (error) {
117+
core.setFailed(`Failed to resolve url: ${error.message}`);
118+
return;
119+
}
120+
121+
if (!fs.existsSync(destination)) {
122+
fs.mkdirSync(destination, { recursive: true });
123+
}
124+
125+
const fullPathBin = path.resolve(destination, bin);
126+
try {
127+
await downloadFuncBinary(url, fullPathBin);
128+
} catch (error) {
129+
core.setFailed(`Download failed: ${error.message}`);
130+
return;
131+
}
132+
133+
await addBinToPath(fullPathBin);
134+
await exec.exec(fullPathBin, ['version']);
112135
}
113136

114-
run();
137+
run();

0 commit comments

Comments
 (0)