Skip to content

Arch option for setup-node #194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0635b31
feat: add readme for architecture
aminya Sep 3, 2020
2d88871
feat: node-arch in action.yml
aminya Sep 3, 2020
42d59fb
feat: add arch to run
aminya Sep 3, 2020
f27ebaf
feat: add arch to getNode
aminya Sep 3, 2020
6668516
feat: add arch to resolveVersionFromManifest
aminya Sep 3, 2020
3e84c35
feat: add arch to INodeVersionInfo
aminya Sep 3, 2020
6afbfca
feat: add arch to getInfoFromManifest
aminya Sep 3, 2020
bf47dec
feat: add arch to tc.find
aminya Sep 3, 2020
3eb4bc9
feat: add arch to acquireNodeFromFallbackLocation
aminya Sep 3, 2020
c7e4d38
feat: add arch to downloading message
aminya Sep 3, 2020
2e40994
feat: add arch to getInfoFromDist
aminya Sep 3, 2020
8227138
feat: add arch to cacheDir
aminya Sep 3, 2020
66f3e9f
feat: add arch to queryDistForMatch
aminya Sep 3, 2020
7a3669a
test: add arch tests
aminya Sep 3, 2020
922f2f9
test: revert using nock and fixtures
aminya Sep 3, 2020
a83bf51
test: test using main.run and spying
aminya Sep 3, 2020
63fffe4
chore: add line-ending to gitattribute
aminya Sep 3, 2020
0a161fe
fix: use arch instead of osArch in INodeVersionInfo
aminya Sep 5, 2020
2801f51
chore: build
aminya Sep 5, 2020
2a5c060
fix: add warning for when arch is supplied but version is missing
aminya Sep 6, 2020
99d584a
docs: enhance the readme example to include multiple os, version, and…
aminya Sep 6, 2020
2f532ce
Merge branch 'main' into arch
aminya Sep 16, 2020
aabe026
Merge remote-tracking branch 'upstream/main' into arch
aminya Dec 8, 2020
5984462
Rename node-arch to architecture
aminya Dec 8, 2020
2bbfc76
Add Architecture only example
aminya Dec 8, 2020
d6fea3c
update message to be more general
aminya Dec 9, 2020
05e7d6c
Add e2e arch test
aminya Dec 9, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
# Set default behavior to automatically normalize line endings, and force everything to be LF, except for Windows batch files that require CRLF, so that if a repo is accessed in Unix via a file share from Windows, the scripts will work.
* text=auto eol=lf
*.{cmd,[cC][mM][dD]} text eol=crlf
*.{bat,[bB][aA][tT]} text eol=crlf
.licenses/** -diff linguist-generated=true
13 changes: 13 additions & 0 deletions .github/workflows/versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,17 @@ jobs:
node-version: 0.12.18
- name: Verify node
run: __tests__/verify-node.sh 0.12.18 SKIP_NPM
shell: bash

arch:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Setup node 12 x86 from dist
uses: ./
with:
node-version: '12'
architecture: 'x86'
- name: Verify node
run: __tests__/verify-arch.sh "ia32"
shell: bash
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,61 @@ jobs:
- run: npm test
```

Architecture:

You can use any of the [supported operating systems](https://docs.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners), and the compatible `architecture` can be selected using `architecture`. Values are `x86`, `x64`, `arm64`, `armv6l`, `armv7l`, `ppc64le`, `s390x` (not all of the architectures are available on all platforms).

When using `architecture`, `node-version` must be provided as well.
```yaml
jobs:
build:
runs-on: windows-latest
name: Node sample
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '12'
architecture: 'x64' # optional, x64 or x86. If not specified, x64 will be used by default
- run: npm install
- run: npm test
```

Multiple Operating Systems and Architectures:

```yaml
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
node_version:
- 10
- 12
- 14
architecture:
- x64
# an extra windows-x86 run:
include:
- os: windows-2016
node_version: 12
architecture: x86
name: Node ${{ matrix.node_version }} - ${{ matrix.architecture }} on ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Setup node
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node_version }}
architecture: ${{ matrix.architecture }}
- run: npm install
- run: npm test
```

Publish to npmjs and GPR with npm:
```yaml
steps:
Expand Down
44 changes: 42 additions & 2 deletions __tests__/installer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ describe('setup-node', () => {
expect(dlSpy).toHaveBeenCalled();
expect(exSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${resolvedVersion} from ${expectedUrl}`
`Acquiring ${resolvedVersion} - ${os.arch} from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download ${versionSpec}...`
Expand Down Expand Up @@ -341,6 +341,46 @@ describe('setup-node', () => {
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
});

it('Acquires specified architecture of node', async () => {
for (const {arch, version, osSpec} of [
{arch: 'x86', version: '12.16.2', osSpec: 'win32'},
{arch: 'x86', version: '14.0.0', osSpec: 'win32'}
]) {
os.platform = osSpec;
os.arch = arch;
const fileExtension = os.platform === 'win32' ? '7z' : 'tar.gz';
const platform = {
linux: 'linux',
darwin: 'darwin',
win32: 'win'
}[os.platform];

inputs['node-version'] = version;
inputs['architecture'] = arch;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';

let expectedUrl =
arch === 'x64'
? `https://github.com/actions/node-versions/releases/download/${version}/node-${version}-${platform}-${arch}.zip`
: `https://nodejs.org/dist/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;

// ... but not in the local cache
findSpy.mockImplementation(() => '');

dlSpy.mockImplementation(async () => '/some/temp/path');
let toolPath = path.normalize(`/cache/node/${version}/${arch}`);
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);

await main.run();
expect(dlSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${version} - ${arch} from ${expectedUrl}`
);
}
}, 100000);

describe('check-latest flag', () => {
it('use local version and dont check manifest if check-latest is not specified', async () => {
os.platform = 'linux';
Expand Down Expand Up @@ -403,7 +443,7 @@ describe('setup-node', () => {
);
expect(logSpy).toHaveBeenCalledWith("Resolved as '12.16.2'");
expect(logSpy).toHaveBeenCalledWith(
`Acquiring 12.16.2 from ${expectedUrl}`
`Acquiring 12.16.2 - ${os.arch} from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
});
Expand Down
11 changes: 11 additions & 0 deletions __tests__/verify-arch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/sh

if [ -n "$1" ]; then
architecture="$(node -e 'console.log(process.arch)')"
if [ -z "$(echo $architecture | grep --fixed-strings $1)" ]; then
echo "Unexpected architecture"
exit 1
fi
else
echo "Skip testing architecture"
fi
2 changes: 2 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ inputs:
default: 'false'
node-version:
description: 'Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0'
architecture:
description: 'Target architecture for Node to use. Examples: x86, x64. Will use system architecture by default.'
check-latest:
description: 'Set this option if you want the action to check for the latest available version that satisfies the version spec'
default: false
Expand Down
58 changes: 35 additions & 23 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4694,6 +4694,7 @@ const installer = __importStar(__webpack_require__(749));
const auth = __importStar(__webpack_require__(202));
const path = __importStar(__webpack_require__(622));
const url_1 = __webpack_require__(835);
const os = __webpack_require__(87);
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
Expand All @@ -4705,12 +4706,21 @@ function run() {
if (!version) {
version = core.getInput('version');
}
let arch = core.getInput('architecture');
// if architecture supplied but node-version is not
// if we don't throw a warning, the already installed x64 node will be used which is not probably what user meant.
if (arch && !version) {
core.warning('`architecture` is provided but `node-version` is missing. In this configuration, the version/architecture of Node will not be changed. To fix this, provide `architecture` in combination with `node-version`');
}
if (!arch) {
arch = os.arch();
}
if (version) {
let token = core.getInput('token');
let auth = !token || isGhes() ? undefined : `token ${token}`;
let stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
const checkLatest = (core.getInput('check-latest') || 'false').toUpperCase() === 'TRUE';
yield installer.getNode(version, stable, checkLatest, auth);
yield installer.getNode(version, stable, checkLatest, auth, arch);
}
const registryUrl = core.getInput('registry-url');
const alwaysAuth = core.getInput('always-auth');
Expand Down Expand Up @@ -13093,13 +13103,13 @@ const tc = __importStar(__webpack_require__(533));
const path = __importStar(__webpack_require__(622));
const semver = __importStar(__webpack_require__(280));
const fs = __webpack_require__(747);
function getNode(versionSpec, stable, checkLatest, auth) {
function getNode(versionSpec, stable, checkLatest, auth, arch = os.arch()) {
return __awaiter(this, void 0, void 0, function* () {
let osPlat = os.platform();
let osArch = translateArchToDistUrl(os.arch());
let osArch = translateArchToDistUrl(arch);
if (checkLatest) {
core.info('Attempt to resolve the latest version from manifest...');
const resolvedVersion = yield resolveVersionFromManifest(versionSpec, stable, auth);
const resolvedVersion = yield resolveVersionFromManifest(versionSpec, stable, auth, osArch);
if (resolvedVersion) {
versionSpec = resolvedVersion;
core.info(`Resolved as '${versionSpec}'`);
Expand All @@ -13110,7 +13120,7 @@ function getNode(versionSpec, stable, checkLatest, auth) {
}
// check cache
let toolPath;
toolPath = tc.find('node', versionSpec);
toolPath = tc.find('node', versionSpec, osArch);
// If not found in cache, download
if (toolPath) {
core.info(`Found in cache @ ${toolPath}`);
Expand All @@ -13123,9 +13133,9 @@ function getNode(versionSpec, stable, checkLatest, auth) {
// Try download from internal distribution (popular versions only)
//
try {
info = yield getInfoFromManifest(versionSpec, stable, auth);
info = yield getInfoFromManifest(versionSpec, stable, auth, osArch);
if (info) {
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
core.info(`Acquiring ${info.resolvedVersion} - ${info.arch} from ${info.downloadUrl}`);
downloadPath = yield tc.downloadTool(info.downloadUrl, undefined, auth);
}
else {
Expand All @@ -13148,17 +13158,17 @@ function getNode(versionSpec, stable, checkLatest, auth) {
// Download from nodejs.org
//
if (!downloadPath) {
info = yield getInfoFromDist(versionSpec);
info = yield getInfoFromDist(versionSpec, arch);
if (!info) {
throw new Error(`Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`);
}
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
core.info(`Acquiring ${info.resolvedVersion} - ${info.arch} from ${info.downloadUrl}`);
try {
downloadPath = yield tc.downloadTool(info.downloadUrl);
}
catch (err) {
if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
return yield acquireNodeFromFallbackLocation(info.resolvedVersion);
return yield acquireNodeFromFallbackLocation(info.resolvedVersion, info.arch);
}
throw err;
}
Expand Down Expand Up @@ -13189,7 +13199,7 @@ function getNode(versionSpec, stable, checkLatest, auth) {
// Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded
//
core.info('Adding to the cache ...');
toolPath = yield tc.cacheDir(extPath, 'node', info.resolvedVersion);
toolPath = yield tc.cacheDir(extPath, 'node', info.resolvedVersion, info.arch);
core.info('Done');
}
//
Expand All @@ -13206,26 +13216,27 @@ function getNode(versionSpec, stable, checkLatest, auth) {
});
}
exports.getNode = getNode;
function getInfoFromManifest(versionSpec, stable, auth) {
function getInfoFromManifest(versionSpec, stable, auth, osArch = translateArchToDistUrl(os.arch())) {
return __awaiter(this, void 0, void 0, function* () {
let info = null;
const releases = yield tc.getManifestFromRepo('actions', 'node-versions', auth, 'main');
const rel = yield tc.findFromManifest(versionSpec, stable, releases);
const rel = yield tc.findFromManifest(versionSpec, stable, releases, osArch);
if (rel && rel.files.length > 0) {
info = {};
info.resolvedVersion = rel.version;
info.arch = rel.files[0].arch;
info.downloadUrl = rel.files[0].download_url;
info.fileName = rel.files[0].filename;
}
return info;
});
}
function getInfoFromDist(versionSpec) {
function getInfoFromDist(versionSpec, arch = os.arch()) {
return __awaiter(this, void 0, void 0, function* () {
let osPlat = os.platform();
let osArch = translateArchToDistUrl(os.arch());
let osArch = translateArchToDistUrl(arch);
let version;
version = yield queryDistForMatch(versionSpec);
version = yield queryDistForMatch(versionSpec, arch);
if (!version) {
return null;
}
Expand All @@ -13241,14 +13252,15 @@ function getInfoFromDist(versionSpec) {
return {
downloadUrl: url,
resolvedVersion: version,
arch: arch,
fileName: fileName
};
});
}
function resolveVersionFromManifest(versionSpec, stable, auth) {
function resolveVersionFromManifest(versionSpec, stable, auth, osArch = translateArchToDistUrl(os.arch())) {
return __awaiter(this, void 0, void 0, function* () {
try {
const info = yield getInfoFromManifest(versionSpec, stable, auth);
const info = yield getInfoFromManifest(versionSpec, stable, auth, osArch);
return info === null || info === void 0 ? void 0 : info.resolvedVersion;
}
catch (err) {
Expand Down Expand Up @@ -13283,10 +13295,10 @@ function evaluateVersions(versions, versionSpec) {
}
return version;
}
function queryDistForMatch(versionSpec) {
function queryDistForMatch(versionSpec, arch = os.arch()) {
return __awaiter(this, void 0, void 0, function* () {
let osPlat = os.platform();
let osArch = translateArchToDistUrl(os.arch());
let osArch = translateArchToDistUrl(arch);
// node offers a json list of versions
let dataFileName;
switch (osPlat) {
Expand Down Expand Up @@ -13339,10 +13351,10 @@ exports.getVersionsFromDist = getVersionsFromDist;
// This method attempts to download and cache the resources from these alternative locations.
// Note also that the files are normally zipped but in this case they are just an exe
// and lib file in a folder, not zipped.
function acquireNodeFromFallbackLocation(version) {
function acquireNodeFromFallbackLocation(version, arch = os.arch()) {
return __awaiter(this, void 0, void 0, function* () {
let osPlat = os.platform();
let osArch = translateArchToDistUrl(os.arch());
let osArch = translateArchToDistUrl(arch);
// Create temporary folder to download in to
const tempDownloadFolder = 'temp_' + Math.floor(Math.random() * 2000000000);
const tempDirectory = process.env['RUNNER_TEMP'] || '';
Expand Down Expand Up @@ -13373,7 +13385,7 @@ function acquireNodeFromFallbackLocation(version) {
throw err;
}
}
let toolPath = yield tc.cacheDir(tempDir, 'node', version);
let toolPath = yield tc.cacheDir(tempDir, 'node', version, arch);
core.addPath(toolPath);
return toolPath;
});
Expand Down
Loading