apple signing: every signable target (extensions, App Group check), build keychain unlock over SSH, api confirm docs
This commit is contained in:
parent
5b6e0992ca
commit
445ab1adbc
17 changed files with 832 additions and 42 deletions
45
cli/lib/keychain.ts
Normal file
45
cli/lib/keychain.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// Code signing keychain for `storebound apple build` outside the Mac's logged-in GUI session. codesign in an SSH
|
||||
// session (or any session without the login keychain unlocked) fails late with errSecInternalComponent. The fixes:
|
||||
// - run from the GUI session (a .command file opened with `open -a Terminal`)
|
||||
// - a CI runner / Jenkins agent started in that session, or with an unlocked keychain
|
||||
// - a build keychain: --keychain <path>, unlocked with --keychain-unlock (password from STOREBOUND_KEYCHAIN_PASSWORD,
|
||||
// never printed) and its key partition list set so codesign may use the keys without a prompt.
|
||||
// Pure planning here; cli/apple/build.ts runs it.
|
||||
export const KEYCHAIN_PASSWORD_ENV = 'STOREBOUND_KEYCHAIN_PASSWORD';
|
||||
|
||||
/** The refusal for a build in an SSH / non-GUI session, naming every fix. */
|
||||
export function sshBuildRefusal(reason: string, o: { unlockFlag?: boolean } = {}): string {
|
||||
const unlock = o.unlockFlag !== false;
|
||||
return `apple build: ${reason}; codesign there fails with errSecInternalComponent (locked keychain). Fixes, pick one:\n`
|
||||
+ ` 1. Run it in the Mac's logged-in GUI session: write the command into a .command file and \`open -a Terminal <file>\` (unset SSH_CONNECTION SSH_CLIENT SSH_TTY inside).\n`
|
||||
+ ` 2. A CI runner / Jenkins agent started in the GUI session, or one whose keychain is unlocked first (\`security unlock-keychain -p "$${KEYCHAIN_PASSWORD_ENV}" <keychain>\`, then \`security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$${KEYCHAIN_PASSWORD_ENV}" <keychain>\`).\n`
|
||||
+ (unlock ? ` 3. Let storebound do step 2: export ${KEYCHAIN_PASSWORD_ENV}=... (the keychain password; never printed) and add --keychain-unlock (the login keychain), or --keychain <path> --keychain-unlock for a build keychain that holds the Apple Distribution key + certificate (storebound apple signing ensure --keychain <path> imports them).\n`
|
||||
+ ` --allow-ssh skips this check when the keychain is already unlocked in this session.`
|
||||
: ' --allow-ssh skips this check when the keychain is already unlocked in this session.');
|
||||
}
|
||||
|
||||
export interface KeychainPlan { keychain: string; steps: string[][] }
|
||||
|
||||
/** The `security` calls --keychain-unlock runs, with PASSWORD as a placeholder (substituted by the caller, never logged). */
|
||||
export function unlockSteps(keychain: string, searchList: string[]): string[][] {
|
||||
const steps: string[][] = [['security', 'unlock-keychain', '-p', 'PASSWORD', keychain],
|
||||
['security', 'set-key-partition-list', '-S', 'apple-tool:,apple:,codesign:', '-s', '-k', 'PASSWORD', keychain]];
|
||||
if (!searchList.includes(keychain)) steps.push(['security', 'list-keychains', '-d', 'user', '-s', keychain, ...searchList]);
|
||||
return steps;
|
||||
}
|
||||
|
||||
/** `security list-keychains` output -> paths (quotes stripped). */
|
||||
export const parseKeychainList = (out: string): string[] => out.split('\n').map(l => l.trim().replace(/^"|"$/g, '')).filter(Boolean);
|
||||
|
||||
/** The password from the environment, or an error that names the variable (the value is never in a message). */
|
||||
export function keychainPassword(env: Record<string, string | undefined>): string {
|
||||
const p = env[KEYCHAIN_PASSWORD_ENV];
|
||||
if (!p) throw new Error(`--keychain-unlock needs the keychain password in the environment variable ${KEYCHAIN_PASSWORD_ENV} (not an argument: arguments end up in shell history)`);
|
||||
return p;
|
||||
}
|
||||
|
||||
/** A command line for logs with the password replaced. */
|
||||
export const showSecurity = (argv: string[]): string => argv.map(a => (a === 'PASSWORD' ? '$' + KEYCHAIN_PASSWORD_ENV : a)).join(' ');
|
||||
|
||||
/** Settings that make xcodebuild sign with a given keychain. */
|
||||
export const keychainSigningArgs = (keychain: string): string[] => [`OTHER_CODE_SIGN_FLAGS=--keychain ${keychain}`];
|
||||
Loading…
Add table
Add a link
Reference in a new issue