Skip to content

Commit

Permalink
refactor: improve error handling with update check (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
byCedric authored Jan 12, 2021
1 parent ed7f67d commit 917e072
Show file tree
Hide file tree
Showing 15 changed files with 100,892 additions and 90,514 deletions.
190,599 changes: 100,448 additions & 90,151 deletions build/index.js

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@
"@actions/exec": "^1.0.4",
"@actions/io": "^1.0.2",
"@actions/tool-cache": "^1.6.1",
"libnpm": "^3.0.1"
"libnpm": "^3.0.1",
"semver": "^7.3.4"
},
"devDependencies": {
"@types/jest": "^26.0.16",
"@types/node": "^14.14.10",
"@types/semver": "^7.3.4",
"@typescript-eslint/eslint-plugin": "^4.9.0",
"@typescript-eslint/parser": "^4.9.0",
"@zeit/ncc": "^0.22.3",
Expand Down
71 changes: 0 additions & 71 deletions src/expo.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as core from '@actions/core';
import { run } from './run';
import { handleError } from './tools';

run().catch(core.setFailed);
run().catch(handleError);
24 changes: 6 additions & 18 deletions src/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ import * as core from '@actions/core';
import * as cli from '@actions/exec';
import * as io from '@actions/io';
import * as path from 'path';
import { fromLocalCache, fromRemoteCache, toLocalCache, toRemoteCache } from './cache';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const registry = require('libnpm');
import { fromLocalCache, fromRemoteCache, toLocalCache, toRemoteCache } from './cache';

export type InstallConfig = {
version: string;
Expand All @@ -14,36 +12,26 @@ export type InstallConfig = {
cacheKey?: string;
};

/**
* Resolve the provided semver to exact version of `expo-cli`.
* This uses the npm registry and accepts latest, dist-tags or version ranges.
* It's used to determine the cached version of `expo-cli`.
*/
export async function resolve(version: string): Promise<string> {
return (await registry.manifest(`expo-cli@${version}`)).version;
}

/**
* Install `expo-cli`, by version, using the packager.
* Here you can provide any semver range or dist tag used in the registry.
* It returns the path where Expo is installed.
*/
export async function install(config: InstallConfig): Promise<string> {
const exact = await resolve(config.version);
let root: string | undefined = await fromLocalCache(exact);
let root: string | undefined = await fromLocalCache(config.version);

if (!root && config.cache) {
root = await fromRemoteCache(exact, config.packager, config.cacheKey);
root = await fromRemoteCache(config.version, config.packager, config.cacheKey);
} else {
core.info('Skipping remote cache, not enabled...');
}

if (!root) {
root = await fromPackager(exact, config.packager);
root = await toLocalCache(root, exact);
root = await fromPackager(config.version, config.packager);
root = await toLocalCache(root, config.version);

if (config.cache) {
await toRemoteCache(root, exact, config.packager, config.cacheKey);
await toRemoteCache(root, config.version, config.packager, config.cacheKey);
}
}

Expand Down
15 changes: 9 additions & 6 deletions src/run.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { addPath, getInput, group } from '@actions/core';
import { authenticate } from './expo';

import { install, InstallConfig } from './install';
import { patchWatchers } from './system';
import { maybeAuthenticate, maybePatchWatchers, resolveVersion } from './tools';

export async function run(): Promise<void> {
const config: InstallConfig = {
Expand All @@ -11,18 +11,21 @@ export async function run(): Promise<void> {
cacheKey: getInput('expo-cache-key') || undefined,
};

// Resolve the exact requested Expo CLI version
config.version = await resolveVersion(config.version);

const path = await group(
config.cache
? `Installing Expo CLI from cache or with ${config.packager}`
: `Installing Expo CLI with ${config.packager}`,
? `Installing Expo CLI (${config.version}) from cache or with ${config.packager}`
: `Installing Expo CLI (${config.version}) with ${config.packager}`,
() => install(config),
);

addPath(path);

await group(
'Checking current authenticated account',
() => authenticate({
() => maybeAuthenticate({
token: getInput('expo-token') || undefined,
username: getInput('expo-username') || undefined,
password: getInput('expo-password') || undefined,
Expand All @@ -34,7 +37,7 @@ export async function run(): Promise<void> {
if (shouldPatchWatchers !== 'false') {
await group(
'Patching system watchers for the `ENOSPC` error',
() => patchWatchers(),
() => maybePatchWatchers(),
);
}
}
28 changes: 0 additions & 28 deletions src/system.ts

This file was deleted.

104 changes: 104 additions & 0 deletions src/tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import * as core from '@actions/core';
import * as cli from '@actions/exec';
import semver from 'semver';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const registry = require('libnpm');

type AuthenticateOptions = {
token?: string;
username?: string;
password?: string;
};

/**
* Resolve the provided semver to exact version of `expo-cli`.
* This uses the npm registry and accepts latest, dist-tags or version ranges.
* It's used to determine the cached version of `expo-cli`.
*/
export async function resolveVersion(version: string): Promise<string> {
return (await registry.manifest(`expo-cli@${version}`)).version;
}

/**
* Authenticate with Expo using either the token or username/password method.
* If both of them are set, token has priority.
*/
export async function maybeAuthenticate(options: AuthenticateOptions = {}): Promise<void> {
// github actions toolkit will handle commands with `.cmd` on windows, we need that
const bin = process.platform === 'win32' ? 'expo.cmd' : 'expo';

if (options.token) {
await cli.exec(bin, ['whoami'], {
env: { ...process.env, EXPO_TOKEN: options.token },
});
return core.exportVariable('EXPO_TOKEN', options.token);
}

if (options.username || options.password) {
if (!options.username || !options.password) {
return core.info('Skipping authentication: `expo-username` and/or `expo-password` not set...');
}
await cli.exec(bin, ['login', `--username=${options.username}`], {
env: { ...process.env, EXPO_CLI_PASSWORD: options.password },
});
return;
}

core.info('Skipping authentication: `expo-token`, `expo-username`, and/or `expo-password` not set...');
}

/**
* Try to patch the default watcher/inotify limit.
* This is a limitation from GitHub Actions and might be an issue in some Expo projects.
* It sets the system's `fs.inotify` limits to a more sensible setting.
*
* @see https://github.com/expo/expo-github-action/issues/20
*/
export async function maybePatchWatchers(): Promise<void> {
if (process.platform !== 'linux') {
return core.info('Skipping patch for watchers, not running on Linux...');
}

core.info('Patching system watchers for the `ENOSPC` error...');

try {
// see https://github.com/expo/expo-cli/issues/277#issuecomment-452685177
await cli.exec('sudo sysctl fs.inotify.max_user_instances=524288');
await cli.exec('sudo sysctl fs.inotify.max_user_watches=524288');
await cli.exec('sudo sysctl fs.inotify.max_queued_events=524288');
await cli.exec('sudo sysctl -p');
} catch {
core.warning("Looks like we can't patch watchers/inotify limits, you might encouter the `ENOSPC` error.");
core.warning('For more info, https://github.com/expo/expo-github-action/issues/20');
}
}

/**
* Check if there is a new major version available.
* If there is, create a warning for people to upgrade their workflow.
* Because this introduces additional requests, it should only be executed when necessary.
*/
export async function maybeWarnForUpdate(): Promise<void> {
const latest = await resolveVersion('latest');
const current = await resolveVersion(core.getInput('expo-version') || 'latest');

if (semver.diff(latest, current) === 'major') {
core.warning(`There is a new major version available of the Expo CLI (${latest})`);
core.warning(`If you run into issues, try upgrading your workflow to "expo-version: ${semver.major(latest)}.x"`);
}
}

/**
* Handle errors when this action fails, providing useful next-steps for developers.
* This mostly checks if the installed version is the latest version.
*/
export async function handleError(error: Error) {
try {
await maybeWarnForUpdate();
} catch {
// If this fails, ignore it
}

core.setFailed(error);
}
1 change: 1 addition & 0 deletions tests/cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import os from 'os';
import { join } from 'path';
import * as remoteCache from '@actions/cache';
import * as toolCache from '@actions/tool-cache';

import * as cache from '../src/cache';
import * as utils from './utils';

Expand Down
Loading

0 comments on commit 917e072

Please sign in to comment.