Skip to content

Commit 439ff4c

Browse files
authored
refactor: clean up setup action (#146)
* refactor: replace cache with opinionated tool cacher * refactor: replace install with packager * refactor: replace various tools with worker BEARKING CHANGE: Dropping username and password authentication in favor of token authentication only. * refactor: update setup action with latest refactors * chore: rebuild project * chore: remove semver and rebuild project * refactor: update tests for new components * chore: fix linting issues and rebuild project
1 parent 3ae7999 commit 439ff4c

19 files changed

Lines changed: 918 additions & 4677 deletions

build/setup/index.js

Lines changed: 372 additions & 3574 deletions
Large diffs are not rendered by default.

build/setup/license.txt

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -736,25 +736,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
736736

737737

738738

739-
lru-cache
740-
ISC
741-
The ISC License
742-
743-
Copyright (c) Isaac Z. Schlueter and Contributors
744-
745-
Permission to use, copy, modify, and/or distribute this software for any
746-
purpose with or without fee is hereby granted, provided that the above
747-
copyright notice and this permission notice appear in all copies.
748-
749-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
750-
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
751-
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
752-
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
753-
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
754-
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
755-
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
756-
757-
758739
mime-db
759740
MIT
760741

@@ -1106,22 +1087,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11061087
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
11071088
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
11081089
THE SOFTWARE.
1109-
1110-
1111-
yallist
1112-
ISC
1113-
The ISC License
1114-
1115-
Copyright (c) Isaac Z. Schlueter and Contributors
1116-
1117-
Permission to use, copy, modify, and/or distribute this software for any
1118-
purpose with or without fee is hereby granted, provided that the above
1119-
copyright notice and this permission notice appear in all copies.
1120-
1121-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1122-
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1123-
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1124-
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1125-
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1126-
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
1127-
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,14 @@
2525
"@actions/core": "^1.6.0",
2626
"@actions/exec": "^1.1.0",
2727
"@actions/io": "^1.1.1",
28-
"@actions/tool-cache": "^1.7.1",
29-
"semver": "^7.3.5"
28+
"@actions/tool-cache": "^1.7.1"
3029
},
3130
"devDependencies": {
3231
"@semantic-release/changelog": "^6.0.1",
3332
"@semantic-release/git": "^10.0.1",
3433
"@tsconfig/node16": "^1.0.2",
3534
"@types/jest": "^27.4.0",
3635
"@types/node": "^16.11.19",
37-
"@types/semver": "^7.3.9",
3836
"@typescript-eslint/eslint-plugin": "^5.9.1",
3937
"@typescript-eslint/parser": "^5.9.1",
4038
"@vercel/ncc": "^0.33.1",

src/actions/setup.ts

Lines changed: 66 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,79 @@
1-
import { addPath, getBooleanInput, getInput, group, info } from '@actions/core';
1+
import { getBooleanInput, getInput, group, info } from '@actions/core';
22

3-
import { install } from '../install';
4-
import { resolveVersion } from '../packager';
5-
import * as tools from '../tools';
3+
import { restoreFromCache, saveToCache } from '../cacher';
4+
import { installPackage, resolvePackage } from '../packager';
5+
import { executeAction, expoAuthenticate, findTool, installToolFromPackage, patchWatchers } from '../worker';
66

77
// Auto-execute in GitHub actions
8-
tools.performAction(setupAction);
9-
10-
export async function setupAction(): Promise<void> {
11-
const expoVersion = await installCli('expo-cli');
12-
const easVersion = await installCli('eas-cli');
13-
14-
await group('Checking current authenticated account', () =>
15-
tools.maybeAuthenticate({
16-
cli: expoVersion ? 'expo-cli' : easVersion ? 'eas-cli' : undefined,
17-
token: getInput('token') || undefined,
18-
username: getInput('username') || undefined,
19-
password: getInput('password') || undefined,
20-
})
21-
);
22-
23-
if (!getInput('patch-watchers') || getBooleanInput('patch-watchers') !== false) {
24-
await group('Patching system watchers for the `ENOSPC` error', () => tools.maybePatchWatchers());
25-
}
8+
executeAction(setupAction);
9+
10+
export type SetupInput = ReturnType<typeof setupInput>;
11+
12+
export function setupInput() {
13+
return {
14+
easCache: getBooleanInput('eas-cache'),
15+
easVersion: getInput('eas-version'),
16+
expoCache: getBooleanInput('expo-cache'),
17+
expoVersion: getInput('expo-version'),
18+
packager: getInput('packager') || 'yarn',
19+
patchWatchers: !getInput('patch-watchers') || getBooleanInput('patch-watchers'),
20+
token: getInput('token'),
21+
};
2622
}
2723

28-
async function installCli(name: tools.PackageName): Promise<string | void> {
29-
const shortName = tools.getBinaryName(name);
30-
const inputVersion = getInput(`${shortName}-version`);
31-
const packager = getInput('packager') || 'yarn';
24+
export async function setupAction(input: SetupInput = setupInput()): Promise<void> {
25+
if (!input.expoVersion) {
26+
info(`Skipped installing expo-cli: 'expo-version' not provided.`);
27+
} else {
28+
const version = await resolvePackage('expo-cli', input.expoVersion);
29+
const message = input.expoCache
30+
? `Installing expo-cli (${version}) from cache or with ${input.packager}`
31+
: `Installing expo-cli (${version}) with ${input.packager}`;
3232

33-
if (!inputVersion) {
34-
return info(`Skipping installation of ${name}, \`${shortName}-version\` not provided.`);
33+
await group(message, () => installCli('expo-cli', version, input.packager, input.expoCache));
3534
}
3635

37-
const version = await resolveVersion(name, inputVersion);
38-
const cache = getBooleanInput(`${shortName}-cache`);
39-
40-
try {
41-
const path = await group(
42-
cache
43-
? `Installing ${name} (${version}) from cache or with ${packager}`
44-
: `Installing ${name} (${version}) with ${packager}`,
45-
() =>
46-
install({
47-
packager,
48-
version,
49-
cache,
50-
package: name,
51-
cacheKey: getInput(`${shortName}-cache-key`) || undefined,
52-
})
36+
if (!input.easVersion) {
37+
info(`Skipped installing eas-cli: 'eas-version' not provided.`);
38+
} else {
39+
const version = await resolvePackage('eas-cli', input.easVersion);
40+
const message = input.easCache
41+
? `Installing eas-cli (${version}) from cache or with ${input.packager}`
42+
: `Installing eas-cli (${version}) with ${input.packager}`;
43+
44+
await group(message, () => installCli('eas-cli', input.easVersion, input.packager, input.easCache));
45+
}
46+
47+
if (!input.token) {
48+
info(`Skipped authentication: 'token' not provided.`);
49+
} else {
50+
await group('Validating authenticated account', () =>
51+
expoAuthenticate(input.token, input.easVersion ? 'eas' : input.expoVersion ? 'expo' : undefined)
5352
);
53+
}
54+
55+
if (!input.patchWatchers) {
56+
info(`Skipped patching watchers: 'patch-watchers' disabled.`);
57+
} else {
58+
await group(`Patching system watchers for the 'ENOSPC' error`, patchWatchers);
59+
}
60+
}
61+
62+
async function installCli(name: string, version: string, packager: string, cache: boolean = true) {
63+
const cliVersion = await resolvePackage(name, version);
64+
let cliPath = findTool(name, cliVersion) || undefined;
65+
66+
if (!cliPath && cache) {
67+
cliPath = await restoreFromCache(name, cliVersion, packager);
68+
}
69+
70+
if (!cliPath) {
71+
cliPath = await installPackage(name, cliVersion, packager);
5472

55-
addPath(path);
56-
} catch (error) {
57-
tools.handleError(name, error);
73+
if (cache) {
74+
await saveToCache(name, cliVersion, packager);
75+
}
5876
}
5977

60-
return version;
78+
installToolFromPackage(cliPath);
6179
}

src/cache.ts

Lines changed: 0 additions & 94 deletions
This file was deleted.

src/cacher.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { saveCache, restoreCache, ReserveCacheError } from '@actions/cache';
2+
import { warning } from '@actions/core';
3+
import os from 'os';
4+
5+
import { toolPath } from './worker';
6+
7+
/**
8+
* Get the exact cache key for the package.
9+
* We can prefix this when there are breaking changes in this action.
10+
*/
11+
export function cacheKey(name: string, version: string, manager: string): string {
12+
return `${name}-${process.platform}-${os.arch()}-${manager}-${version}`;
13+
}
14+
15+
/**
16+
* Restore a tool from the remote cache.
17+
* This will install the tool back into the local tool cache.
18+
*/
19+
export async function restoreFromCache(name: string, version: string, manager: string) {
20+
const dir = toolPath(name, version)!;
21+
try {
22+
if (await restoreCache([dir], cacheKey(name, version, manager))) {
23+
return dir;
24+
}
25+
} catch (error) {
26+
handleCacheError(error);
27+
}
28+
}
29+
30+
/**
31+
* Save a tool to the remote cache.
32+
* This will fetch the tool from the local tool cache.
33+
*/
34+
export async function saveToCache(name: string, version: string, manager: string) {
35+
try {
36+
await saveCache([toolPath(name, version)], cacheKey(name, version, manager));
37+
} catch (error) {
38+
handleCacheError(error);
39+
}
40+
}
41+
42+
/**
43+
* Try to handle incoming cache errors.
44+
* Because workers can operate in environments without cache configured,
45+
* we need to make sure to only skip the cache instead of fail.
46+
*
47+
* Currently we handle these types of errors:
48+
* - ReserveCacheError
49+
* - "cache service url not found"
50+
*/
51+
export function handleCacheError(error: Error): void {
52+
const isReserveCacheError = error instanceof ReserveCacheError;
53+
const isCacheUnavailable = error.message.toLowerCase().includes('cache service url not found');
54+
55+
if (isReserveCacheError || isCacheUnavailable) {
56+
warning('Skipped remote cache, encountered error:');
57+
warning(error.message);
58+
} else {
59+
throw error;
60+
}
61+
}

0 commit comments

Comments
 (0)