Skip to content

Commit

Permalink
fix: use cmd suffix when authenticating on windows
Browse files Browse the repository at this point in the history
  • Loading branch information
byCedric committed Oct 30, 2019
1 parent 8ff07e0 commit 831bcb1
Show file tree
Hide file tree
Showing 11 changed files with 269 additions and 14 deletions.
6 changes: 5 additions & 1 deletion build/expo.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ function authenticate(username, password) {
if (!username || !password) {
return core.debug('Skipping authentication, `expo-username` and/or `expo-password` not set...');
}
yield cli.exec('expo', ['login', `--username=${username}`], {
// github actions toolkit will handle commands with `.cmd` on windows, we need that
const bin = process.platform === 'win32'
? 'expo.cmd'
: 'expo';
yield cli.exec(bin, ['login', `--username=${username}`], {
env: Object.assign(Object.assign({}, process.env), { EXPO_CLI_PASSWORD: password }),
});
});
Expand Down
40 changes: 40 additions & 0 deletions build/src/expo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const cli = __importStar(require("@actions/exec"));
/**
* Authenticate at Expo using `expo login`.
* This step is required for publishing and building new apps.
* It uses the `EXPO_CLI_PASSWORD` environment variable for improved security.
*/
function authenticate(username, password) {
return __awaiter(this, void 0, void 0, function* () {
if (!username || !password) {
return core.debug('Skipping authentication, `expo-username` and/or `expo-password` not set...');
}
// github actions toolkit will handle commands with `.cmd` on windows, we need that
const bin = process.platform === 'win32'
? 'expo.cmd'
: 'expo';
yield cli.exec(bin, ['login', `--username=${username}`], {
env: Object.assign(Object.assign({}, process.env), { EXPO_CLI_PASSWORD: password }),
});
});
}
exports.authenticate = authenticate;
28 changes: 28 additions & 0 deletions build/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@actions/core");
const expo_1 = require("./expo");
const install_1 = require("./install");
const system_1 = require("./system");
function run() {
return __awaiter(this, void 0, void 0, function* () {
const path = yield install_1.install(core_1.getInput('expo-version') || 'latest', core_1.getInput('expo-packager') || 'npm');
core_1.addPath(path);
yield expo_1.authenticate(core_1.getInput('expo-username'), core_1.getInput('expo-password'));
const shouldPatchWatchers = core_1.getInput('expo-patch-watchers') || 'true';
if (shouldPatchWatchers !== 'false') {
yield system_1.patchWatchers();
}
});
}
exports.run = run;
run();
90 changes: 90 additions & 0 deletions build/src/install.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const cache = __importStar(require("@actions/tool-cache"));
const cli = __importStar(require("@actions/exec"));
const io = __importStar(require("@actions/io"));
const path = __importStar(require("path"));
// eslint-disable-next-line @typescript-eslint/no-var-requires
const registry = require('libnpm');
/**
* 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`.
*/
function resolve(version) {
return __awaiter(this, void 0, void 0, function* () {
return (yield registry.manifest(`expo-cli@${version}`)).version;
});
}
exports.resolve = resolve;
/**
* 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.
*/
function install(version, packager) {
return __awaiter(this, void 0, void 0, function* () {
const exact = yield resolve(version);
let root = yield fromCache(exact);
if (!root) {
root = yield fromPackager(exact, packager);
root = yield toCache(exact, root);
}
return path.join(root, 'node_modules', '.bin');
});
}
exports.install = install;
/**
* Install `expo-cli`, by version, using npm or yarn.
* It creates a temporary directory to store all required files.
*/
function fromPackager(version, packager) {
return __awaiter(this, void 0, void 0, function* () {
const root = process.env['RUNNER_TEMP'] || '';
const tool = yield io.which(packager);
yield io.mkdirP(root);
yield cli.exec(tool, ['add', `expo-cli@${version}`], { cwd: root });
return root;
});
}
exports.fromPackager = fromPackager;
/**
* Get the path to the `expo-cli` from cache, if any.
* Note, this cache is **NOT** shared between jobs.
*
* @see https://github.com/actions/toolkit/issues/47
*/
function fromCache(version) {
return __awaiter(this, void 0, void 0, function* () {
return cache.find('expo-cli', version);
});
}
exports.fromCache = fromCache;
/**
* Store the root of `expo-cli` in the cache, for future reuse.
* Note, this cache is **NOT** shared between jobs.
*
* @see https://github.com/actions/toolkit/issues/47
*/
function toCache(version, root) {
return __awaiter(this, void 0, void 0, function* () {
return cache.cacheDir(root, 'expo-cli', version);
});
}
exports.toCache = toCache;
47 changes: 47 additions & 0 deletions build/src/system.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const cli = __importStar(require("@actions/exec"));
/**
* 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
*/
function patchWatchers() {
return __awaiter(this, void 0, void 0, function* () {
if (process.platform !== 'linux') {
return core.debug('Skipping patch for watchers, not running on Linux...');
}
core.debug('Patching system watchers for the `ENOSPC` error...');
try {
// see https://github.com/expo/expo-cli/issues/277#issuecomment-452685177
yield cli.exec('sudo sysctl fs.inotify.max_user_instances=524288');
yield cli.exec('sudo sysctl fs.inotify.max_user_watches=524288');
yield cli.exec('sudo sysctl fs.inotify.max_queued_events=524288');
yield cli.exec('sudo sysctl -p');
}
catch (_a) {
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');
}
});
}
exports.patchWatchers = patchWatchers;
19 changes: 19 additions & 0 deletions build/tests/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// keep track of the original one to revert the platform
const originalPlatform = process.platform;
/**
* Change the node platform for testing purposes.
* With this you can fake the `process.platform`.
*/
function setPlatform(platform) {
Object.defineProperty(process, 'platform', { value: platform });
}
exports.setPlatform = setPlatform;
/**
* Revert the platform to the original one.
*/
function resetPlatform() {
setPlatform(originalPlatform);
}
exports.resetPlatform = resetPlatform;
7 changes: 6 additions & 1 deletion src/expo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ export async function authenticate(username: string, password: string) {
return core.debug('Skipping authentication, `expo-username` and/or `expo-password` not set...');
}

await cli.exec('expo', ['login', `--username=${username}`], {
// github actions toolkit will handle commands with `.cmd` on windows, we need that
const bin = process.platform === 'win32'
? 'expo.cmd'
: 'expo';

await cli.exec(bin, ['login', `--username=${username}`], {
env: {
...process.env,
EXPO_CLI_PASSWORD: password,
Expand Down
9 changes: 9 additions & 0 deletions tests/expo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ jest.mock('@actions/core', () => core);
jest.mock('@actions/exec', () => cli);

import * as expo from '../src/expo';
import { setPlatform, resetPlatform } from './utils';

describe('authenticate', () => {
test('skips authentication without credentials', async () => {
Expand Down Expand Up @@ -33,4 +34,12 @@ describe('authenticate', () => {
},
});
});

test('executes login command with `.cmd` suffix on windows', async () => {
setPlatform('win32');
await expo.authenticate('bycedric', 'mypassword');
expect(cli.exec).toBeCalled();
expect(cli.exec.mock.calls[0][0]).toBe('expo.cmd');
resetPlatform();
});
});
18 changes: 7 additions & 11 deletions tests/system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,15 @@ jest.mock('@actions/core', () => core);
jest.mock('@actions/exec', () => cli);

import * as system from '../src/system';
import { setPlatform, resetPlatform } from './utils';

describe('patchWatchers', () => {
const originalPlatform = process.platform;
const changePlatform = (platform: NodeJS.Platform) => {
Object.defineProperty(process, 'platform', { value: platform });
};

afterEach(() => {
changePlatform(originalPlatform);
resetPlatform();
});

it('increses fs inotify settings with sysctl', async () => {
changePlatform('linux');
setPlatform('linux');
await system.patchWatchers();
expect(cli.exec).toHaveBeenCalledWith('sudo sysctl fs.inotify.max_user_instances=524288');
expect(cli.exec).toHaveBeenCalledWith('sudo sysctl fs.inotify.max_user_watches=524288');
Expand All @@ -28,7 +24,7 @@ describe('patchWatchers', () => {
it('warns for unsuccessful patches', async () => {
const error = new Error('Something went wrong');
cli.exec.mockRejectedValue(error);
changePlatform('linux');
setPlatform('linux');
await system.patchWatchers();
expect(core.warning).toBeCalledWith(expect.stringContaining('can\'t patch watchers'));
expect(core.warning).toBeCalledWith(
Expand All @@ -37,19 +33,19 @@ describe('patchWatchers', () => {
});

it('skips on windows platform', async () => {
changePlatform('win32');
setPlatform('win32');
await system.patchWatchers();
expect(cli.exec).not.toHaveBeenCalled();
});

it('skips on macos platform', async () => {
changePlatform('darwin');
setPlatform('darwin');
await system.patchWatchers();
expect(cli.exec).not.toHaveBeenCalled();
});

it('runs on linux platform', async () => {
changePlatform('linux');
setPlatform('linux');
await system.patchWatchers();
expect(cli.exec).toHaveBeenCalled();
});
Expand Down
17 changes: 17 additions & 0 deletions tests/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// keep track of the original one to revert the platform
const originalPlatform = process.platform;

/**
* Change the node platform for testing purposes.
* With this you can fake the `process.platform`.
*/
export function setPlatform(platform: NodeJS.Platform) {
Object.defineProperty(process, 'platform', { value: platform });
}

/**
* Revert the platform to the original one.
*/
export function resetPlatform() {
setPlatform(originalPlatform);
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
},
"exclude": [
"node_modules",
"**/*.test.ts"
"tests/**"
]
}

0 comments on commit 831bcb1

Please sign in to comment.