Skip to content

Commit

Permalink
[eas] Add custom EAS build function to configure macOS credentials
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrieldonadel committed Dec 15, 2023
1 parent 2be71d9 commit 40383cc
Show file tree
Hide file tree
Showing 11 changed files with 576 additions and 4 deletions.
25 changes: 25 additions & 0 deletions apps/menu-bar/.eas/build/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ build:
name: Run tests
steps:
- eas/checkout
- configure_macos_credentials:
inputs:
buildCredentials: ${ eas.job.secrets.buildCredentials }
- eas/install_node_modules
- run:
name: Build packages
Expand All @@ -13,6 +16,11 @@ build:
working_directory: ../../apps/cli
command: |
yarn archive
- run:
name: Codesign CLI
working_directory: ../../apps/cli
command: |
yarn codesign
- run:
name: Install menu-bar pods
working_directory: ./macos
Expand All @@ -22,4 +30,21 @@ build:
working_directory: ../../apps/menu-bar
command: |
yarn archive
- run:
name: Export archive menu-bar
working_directory: ../../apps/menu-bar
command: |
yarn export-archive
- run:
name: Notarize menu-bar
working_directory: ../../apps/menu-bar
command: |
yarn notarize
- eas/find_and_upload_build_artifacts
functions:
configure_macos_credentials:
name: Configure macOS Credentials
inputs:
- name: buildCredentials
type: json
path: ./configureMacOSCredentials
33 changes: 33 additions & 0 deletions apps/menu-bar/.eas/build/configureMacOSCredentials/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# My EAS Build function

This is an EAS Build function written in TypeScript. It can be used as a step in a [custom build](https://docs.expo.dev/preview/custom-build-config/).

## How to use it

1. Install dependencies: `npm install`.
2. Implement your function in `src/index.ts`.
3. Install [`ncc`](https://github.com/vercel/ncc) if not yet installed: `npm install -g @vercel/ncc`.
4. Compile the function with `ncc` by running `npm run build`. Ensure that the `build` directory is not ignored by `.gitignore` / `.easignore`, it must be included in the [archive uploaded when running `eas build`](https://expo.fyi/eas-build-archive).
5. Use the function in a custom build YAML config. For example:

```yml
build:
name: Custom build
steps:
- run:
name: Hi!
command: echo "Hello! Let's run a custom function!"
- my_function:
id: my-function-call
- run:
name: Bye!
command: echo "Bye! The custom function has finished its job."

functions:
my_function:
name: my-function
path: ./my-function # The path is resolved relative to this config file.
```
## Learn more
Refer to the [custom builds documentation](https://docs.expo.dev/preview/custom-build-config/).
3 changes: 3 additions & 0 deletions apps/menu-bar/.eas/build/configureMacOSCredentials/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"expo": {}
}

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions apps/menu-bar/.eas/build/configureMacOSCredentials/eas.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"cli": {
"version": ">= 5.4.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal"
},
"production": {}
},
"submit": {
"production": {}
}
}
18 changes: 18 additions & 0 deletions apps/menu-bar/.eas/build/configureMacOSCredentials/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "configure-macos-credentials",
"version": "1.0.0",
"description": "",
"main": "./build/index.js",
"type": "commonjs",
"scripts": {
"build": "ncc build ./src/index.ts -o build/ --minify --no-cache --no-source-map-register",
"watch": "ncc build ./src/index.ts -o build/ --minify --no-cache --no-source-map-register --watch"
},
"keywords": [],
"author": "",
"devDependencies": {
"@types/node": "^18.11.18",
"typescript": "^5.1.6",
"@expo/steps": "^1.0.34"
}
}
111 changes: 111 additions & 0 deletions apps/menu-bar/.eas/build/configureMacOSCredentials/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import path from 'path';
import fs from 'fs-extra';
import { v4 as uuid } from 'uuid';
import { BuildStepInput, BuildStepInputValueTypeName } from '@expo/steps';
import spawnAsync from '@expo/spawn-async';

import { BuildStepContext } from '@expo/steps';

interface FunctionInputs {
buildCredentials: BuildStepInput<BuildStepInputValueTypeName.JSON, true>;
}

const KEYCHAIN_NAME = 'orbit-keychain';
const KEYCHAIN_PASSWORD = 'orbit-keychain-password';

async function configureMacOSCredentials(
ctx: BuildStepContext,
{
inputs,
}: {
inputs: FunctionInputs;
}
): Promise<void> {
try {
// Download Apple Developer ID intermediate certificate
await spawnAsync('curl', [
'https://www.apple.com/certificateauthority/DeveloperIDG2CA.cer',
'-o',
'DeveloperIDG2CA.cer',
]);

// Allow `security add-trusted-cert` command to run without password prompt
await spawnAsync('sudo', [
'security',
'authorizationdb',
'write',
'com.apple.trust-settings.admin',
'allow',
]);

// Add DeveloperIDG2CA certificate to System keychain
await spawnAsync('sudo', [
'security',
'add-trusted-cert',
'-d',
'-k',
'/Library/Keychains/System.keychain',
'-r',
'trustRoot',
'DeveloperIDG2CA.cer',
]);

// Read distribution certificate from build credentials and write it to a file
const rawCredentialsInput = inputs.buildCredentials.value as Record<string, any>;
const target = Object.keys(rawCredentialsInput)[0];
const certificate = rawCredentialsInput[target].distributionCertificate;
const distCertPath = path.join(ctx.workingDirectory, `${uuid()}.p12`);
ctx.logger.info(`Writing distribution certificate to ${distCertPath}`);
await fs.writeFile(distCertPath, Buffer.from(certificate.dataBase64, 'base64'));

// Create a new keychain
await spawnAsync('security', ['create-keychain', '-p', KEYCHAIN_PASSWORD, KEYCHAIN_NAME]);

// Add new keychain to search list
const keychainsListResult = await spawnAsync('security', ['list-keychains']);
await spawnAsync('security', [
'list-keychains',
'-s',
KEYCHAIN_NAME,
...keychainsListResult.output,
]);

// Import the distribution certificate into the new keychain
await spawnAsync('security', [
'import',
distCertPath,
'-P',
certificate.password,
'-k',
KEYCHAIN_NAME,
'-T',
'/usr/bin/codesign',
]);

// Set a partition list to avoid interactive prompts
await spawnAsync('security', [
'set-key-partition-list',
'-S',
'apple-tool:,apple:',
'-s',
'-k',
KEYCHAIN_PASSWORD,
KEYCHAIN_NAME,
]);

// List all available identities
let result = await spawnAsync('security', ['find-identity']);
ctx.logger.info(`Identities: ${result.output}`);

// Unlock the keychain so we don't get prompted for the password
await spawnAsync('security', ['unlock-keychain', '-p', KEYCHAIN_PASSWORD, KEYCHAIN_NAME]);

// Set the keychain timeout to infinity
await spawnAsync('security', ['set-keychain-settings', KEYCHAIN_NAME]);
} catch (error) {
ctx.logger.error(`ERROR: ${error}`);
throw error;
}
}

export default configureMacOSCredentials;
26 changes: 26 additions & 0 deletions apps/menu-bar/.eas/build/configureMacOSCredentials/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"sourceMap": true,
"inlineSources": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true,
"skipLibCheck": true,
"noEmit": false,
"forceConsistentCasingInFileNames": true,
"outDir": "build",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"rootDir": "src",
"declaration": false,
"composite": false
},
"include": ["src/**/*.ts"],
"exclude": ["**/__mocks__/*", "**/__tests__/*"]
}
Loading

0 comments on commit 40383cc

Please sign in to comment.