1*8d3f3824SCedric van Puttenimport spawnAsync, { SpawnOptions, SpawnPromise } from '@expo/spawn-async';
2*8d3f3824SCedric van Puttenimport sudo from 'sudo-prompt';
3*8d3f3824SCedric van Putten
4*8d3f3824SCedric van Putten/**
5*8d3f3824SCedric van Putten * The pending spawn promise is similar to the spawn promise from `@expo/spawn-async`.
6*8d3f3824SCedric van Putten * Instead of the `child` process being available immediately, the `child` is behind another promise.
7*8d3f3824SCedric van Putten * We need this to perform async tasks before running the actual spawn promise.
8*8d3f3824SCedric van Putten * Use it like: `await manager.installAsync().child`
9*8d3f3824SCedric van Putten */
10*8d3f3824SCedric van Puttenexport interface PendingSpawnPromise<T> extends Promise<T> {
11*8d3f3824SCedric van Putten  /**
12*8d3f3824SCedric van Putten   * The child process from the delayed spawn.
13*8d3f3824SCedric van Putten   * This is `null` whenever the promise before the spawn promise is rejected.
14*8d3f3824SCedric van Putten   */
15*8d3f3824SCedric van Putten  child: Promise<SpawnPromise<T>['child'] | null>;
16*8d3f3824SCedric van Putten}
17*8d3f3824SCedric van Putten
18*8d3f3824SCedric van Puttenexport function createPendingSpawnAsync<V, T>(
19*8d3f3824SCedric van Putten  actionAsync: () => Promise<V>,
20*8d3f3824SCedric van Putten  spawnAsync: (result: V) => SpawnPromise<T>
21*8d3f3824SCedric van Putten): PendingSpawnPromise<T> {
22*8d3f3824SCedric van Putten  // Manually rsolve the child promise whenever the prepending async action is resolved.
23*8d3f3824SCedric van Putten  // Avoid `childReject` to prevent "unhandled promise rejection" for one of the two promises.
24*8d3f3824SCedric van Putten  let childResolve: (child: SpawnPromise<T>['child'] | null) => void;
25*8d3f3824SCedric van Putten  const child: Promise<SpawnPromise<T>['child'] | null> = new Promise((resolve, reject) => {
26*8d3f3824SCedric van Putten    childResolve = resolve;
27*8d3f3824SCedric van Putten  });
28*8d3f3824SCedric van Putten
29*8d3f3824SCedric van Putten  const pendingPromise = new Promise<T>((spawnResolve, spawnReject) => {
30*8d3f3824SCedric van Putten    actionAsync()
31*8d3f3824SCedric van Putten      .then((result) => {
32*8d3f3824SCedric van Putten        const spawnPromise = spawnAsync(result);
33*8d3f3824SCedric van Putten        childResolve(spawnPromise.child);
34*8d3f3824SCedric van Putten        spawnPromise.then(spawnResolve).catch(spawnReject);
35*8d3f3824SCedric van Putten      })
36*8d3f3824SCedric van Putten      .catch((error) => {
37*8d3f3824SCedric van Putten        childResolve(null);
38*8d3f3824SCedric van Putten        spawnReject(error);
39*8d3f3824SCedric van Putten      });
40*8d3f3824SCedric van Putten  });
41*8d3f3824SCedric van Putten
42*8d3f3824SCedric van Putten  (pendingPromise as PendingSpawnPromise<T>).child = child;
43*8d3f3824SCedric van Putten  return pendingPromise as PendingSpawnPromise<T>;
44*8d3f3824SCedric van Putten}
45*8d3f3824SCedric van Putten
46*8d3f3824SCedric van Putten/**
47*8d3f3824SCedric van Putten * Spawn a command with sudo privileges.
48*8d3f3824SCedric van Putten * On windows, this uses the `sudo-prompt` package.
49*8d3f3824SCedric van Putten * on other systems, this uses the `sudo` binary.
50*8d3f3824SCedric van Putten */
51*8d3f3824SCedric van Puttenexport async function spawnSudoAsync(command: string[], spawnOptions: SpawnOptions): Promise<void> {
52*8d3f3824SCedric van Putten  // sudo prompt only seems to work on win32 machines.
53*8d3f3824SCedric van Putten  if (process.platform === 'win32') {
54*8d3f3824SCedric van Putten    return new Promise((resolve, reject) => {
55*8d3f3824SCedric van Putten      sudo.exec(command.join(' '), { name: 'pod install' }, (error) => {
56*8d3f3824SCedric van Putten        if (error) {
57*8d3f3824SCedric van Putten          reject(error);
58*8d3f3824SCedric van Putten        }
59*8d3f3824SCedric van Putten        resolve();
60*8d3f3824SCedric van Putten      });
61*8d3f3824SCedric van Putten    });
62*8d3f3824SCedric van Putten  } else {
63*8d3f3824SCedric van Putten    // Attempt to use sudo to run the command on Mac and Linux.
64*8d3f3824SCedric van Putten    // TODO(Bacon): Make a v of sudo-prompt that's win32 only for better bundle size.
65*8d3f3824SCedric van Putten    console.log(
66*8d3f3824SCedric van Putten      'Your password might be needed to install CocoaPods CLI: https://guides.cocoapods.org/using/getting-started.html#installation'
67*8d3f3824SCedric van Putten    );
68*8d3f3824SCedric van Putten    await spawnAsync('sudo', command, spawnOptions);
69*8d3f3824SCedric van Putten  }
70*8d3f3824SCedric van Putten}
71