|
| 1 | +import * as core from '@actions/core'; |
| 2 | +import * as cli from '@actions/exec'; |
| 3 | +import semver from 'semver'; |
| 4 | + |
| 5 | +// eslint-disable-next-line @typescript-eslint/no-var-requires |
| 6 | +const registry = require('libnpm'); |
| 7 | + |
| 8 | +type AuthenticateOptions = { |
| 9 | + token?: string; |
| 10 | + username?: string; |
| 11 | + password?: string; |
| 12 | +}; |
| 13 | + |
| 14 | +/** |
| 15 | + * Resolve the provided semver to exact version of `expo-cli`. |
| 16 | + * This uses the npm registry and accepts latest, dist-tags or version ranges. |
| 17 | + * It's used to determine the cached version of `expo-cli`. |
| 18 | + */ |
| 19 | +export async function resolveVersion(version: string): Promise<string> { |
| 20 | + return (await registry.manifest(`expo-cli@${version}`)).version; |
| 21 | +} |
| 22 | + |
| 23 | +/** |
| 24 | + * Authenticate with Expo using either the token or username/password method. |
| 25 | + * If both of them are set, token has priority. |
| 26 | + */ |
| 27 | +export async function maybeAuthenticate(options: AuthenticateOptions = {}): Promise<void> { |
| 28 | + // github actions toolkit will handle commands with `.cmd` on windows, we need that |
| 29 | + const bin = process.platform === 'win32' ? 'expo.cmd' : 'expo'; |
| 30 | + |
| 31 | + if (options.token) { |
| 32 | + await cli.exec(bin, ['whoami'], { |
| 33 | + env: { ...process.env, EXPO_TOKEN: options.token }, |
| 34 | + }); |
| 35 | + return core.exportVariable('EXPO_TOKEN', options.token); |
| 36 | + } |
| 37 | + |
| 38 | + if (options.username || options.password) { |
| 39 | + if (!options.username || !options.password) { |
| 40 | + return core.info('Skipping authentication: `expo-username` and/or `expo-password` not set...'); |
| 41 | + } |
| 42 | + await cli.exec(bin, ['login', `--username=${options.username}`], { |
| 43 | + env: { ...process.env, EXPO_CLI_PASSWORD: options.password }, |
| 44 | + }); |
| 45 | + return; |
| 46 | + } |
| 47 | + |
| 48 | + core.info('Skipping authentication: `expo-token`, `expo-username`, and/or `expo-password` not set...'); |
| 49 | +} |
| 50 | + |
| 51 | +/** |
| 52 | + * Try to patch the default watcher/inotify limit. |
| 53 | + * This is a limitation from GitHub Actions and might be an issue in some Expo projects. |
| 54 | + * It sets the system's `fs.inotify` limits to a more sensible setting. |
| 55 | + * |
| 56 | + * @see https://github.com/expo/expo-github-action/issues/20 |
| 57 | + */ |
| 58 | +export async function maybePatchWatchers(): Promise<void> { |
| 59 | + if (process.platform !== 'linux') { |
| 60 | + return core.info('Skipping patch for watchers, not running on Linux...'); |
| 61 | + } |
| 62 | + |
| 63 | + core.info('Patching system watchers for the `ENOSPC` error...'); |
| 64 | + |
| 65 | + try { |
| 66 | + // see https://github.com/expo/expo-cli/issues/277#issuecomment-452685177 |
| 67 | + await cli.exec('sudo sysctl fs.inotify.max_user_instances=524288'); |
| 68 | + await cli.exec('sudo sysctl fs.inotify.max_user_watches=524288'); |
| 69 | + await cli.exec('sudo sysctl fs.inotify.max_queued_events=524288'); |
| 70 | + await cli.exec('sudo sysctl -p'); |
| 71 | + } catch { |
| 72 | + core.warning("Looks like we can't patch watchers/inotify limits, you might encouter the `ENOSPC` error."); |
| 73 | + core.warning('For more info, https://github.com/expo/expo-github-action/issues/20'); |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +/** |
| 78 | + * Check if there is a new major version available. |
| 79 | + * If there is, create a warning for people to upgrade their workflow. |
| 80 | + * Because this introduces additional requests, it should only be executed when necessary. |
| 81 | + */ |
| 82 | +export async function maybeWarnForUpdate(): Promise<void> { |
| 83 | + const latest = await resolveVersion('latest'); |
| 84 | + const current = await resolveVersion(core.getInput('expo-version') || 'latest'); |
| 85 | + |
| 86 | + if (semver.diff(latest, current) === 'major') { |
| 87 | + core.warning(`There is a new major version available of the Expo CLI (${latest})`); |
| 88 | + core.warning(`If you run into issues, try upgrading your workflow to "expo-version: ${semver.major(latest)}.x"`); |
| 89 | + } |
| 90 | +} |
| 91 | + |
| 92 | +/** |
| 93 | + * Handle errors when this action fails, providing useful next-steps for developers. |
| 94 | + * This mostly checks if the installed version is the latest version. |
| 95 | + */ |
| 96 | +export async function handleError(error: Error) { |
| 97 | + try { |
| 98 | + await maybeWarnForUpdate(); |
| 99 | + } catch { |
| 100 | + // If this fails, ignore it |
| 101 | + } |
| 102 | + |
| 103 | + core.setFailed(error); |
| 104 | +} |
0 commit comments