Skip to content
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

refactor: improve error handling with update check #76

Merged
merged 2 commits into from
Jan 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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