Skip to content

Commit 6f58343

Browse files
committed
feat: added ability to supply appleId + password. Added completions
1 parent e63238c commit 6f58343

6 files changed

Lines changed: 131 additions & 75 deletions

File tree

completions-cron/src/__generated__/completions-index.ts

Lines changed: 62 additions & 60 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/resources/(resources)/xcodes.mdx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ description: Reference page for the xcodes resource
55

66
The xcodes resource installs the [xcodes CLI](https://github.com/XcodesOrg/xcodes) tool and manages multiple Xcode versions on macOS. xcodes is the recommended way for iOS/macOS teams to ensure all developers are running the same Xcode version.
77

8-
Installing Xcode versions requires an Apple Developer account. xcodes will prompt for Apple ID credentials interactively on first use and caches them in the macOS Keychain. For non-interactive environments, set `XCODES_USERNAME` and `XCODES_PASSWORD` environment variables before applying.
8+
Installing Xcode versions requires an Apple Developer account. xcodes will prompt for Apple ID credentials interactively on first use and caches them in the macOS Keychain. For non-interactive environments, supply `appleId` and `appSpecificPassword` directly in the resource config.
99

1010
## Parameters
1111

1212
- **xcodeVersions**: *(string[])* List of Xcode versions to install (e.g. `["15.4", "14.3.1"]`). Version strings match what `xcodes list` shows — stable versions use a dotted number (`15.4`), beta/RC versions include the label (`15 Beta 3`).
1313
- **selected**: *(string)* The active Xcode version to use, equivalent to running `xcodes select`. Must be one of the installed `xcodeVersions`.
14+
- **appleId**: *(string, optional)* Apple ID email used to authenticate with Apple's servers when downloading Xcode. If omitted, xcodes will prompt interactively.
15+
- **appleIdPassword**: *(string, optional)* Apple ID password. Required alongside `appleId` for non-interactive installs.
1416

1517
## Example usage
1618

@@ -42,11 +44,19 @@ Multiple versions side by side:
4244

4345
xcodes requires Apple ID credentials to download Xcode from Apple's servers. On first install Codify will prompt for your credentials interactively (including two-factor authentication). The credentials are stored in the macOS Keychain for future use.
4446

45-
For CI or fully non-interactive environments, export the following environment variables before running Codify:
47+
For CI or fully non-interactive environments, add `appleId` and `appSpecificPassword` to the resource config:
4648

47-
```bash
48-
export XCODES_USERNAME="your@apple.id"
49-
export XCODES_PASSWORD="your-app-specific-password"
49+
```json title="codify.jsonc"
50+
[
51+
{
52+
"type": "xcodes",
53+
"xcodeVersions": ["15.4"],
54+
"selected": "15.4",
55+
"appleId": "your@apple.id",
56+
"appleIdPassword": "<Replace me here!>",
57+
"os": ["macOS"]
58+
}
59+
]
5060
```
5161

52-
Use an [app-specific password](https://appleid.apple.com/account/manage) rather than your main Apple ID password when running non-interactively.
62+
Note that 2FA will still trigger interactively even with credentials set — xcodes does not support fully headless 2FA bypass.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "default",
3-
"version": "1.13.0",
3+
"version": "1.14.0-beta.4",
44
"description": "Default plugin for Codify - provides 50+ declarative resources for managing development tools and system configuration across macOS and Linux",
55
"main": "dist/index.js",
66
"scripts": {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const XCODE_RELEASES_URL = 'https://xcodereleases.com/data.json';
2+
3+
interface XcodeRelease {
4+
version: {
5+
number: string;
6+
release: { release?: boolean; beta?: number; rc?: number; dp?: number };
7+
};
8+
}
9+
10+
function toXcodesVersionString(release: XcodeRelease): string {
11+
const { number, release: rel } = release.version;
12+
if (rel.release) return number;
13+
if (rel.beta != null) return `${number} Beta ${rel.beta}`;
14+
if (rel.rc != null) return `${number} RC ${rel.rc}`;
15+
if (rel.dp != null) return `${number} DP ${rel.dp}`;
16+
return number;
17+
}
18+
19+
export default async function loadXcodeVersions(): Promise<string[]> {
20+
const response = await fetch(XCODE_RELEASES_URL);
21+
const releases = await response.json() as XcodeRelease[];
22+
return releases.map(toXcodesVersionString);
23+
}

src/resources/xcodes/xcode-versions-parameter.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ArrayStatefulParameter, getPty } from '@codifycli/plugin-core';
1+
import { ArrayStatefulParameter, Plan, getPty } from '@codifycli/plugin-core';
22

33
import { XcodesConfig } from './xcodes-resource.js';
44

@@ -9,9 +9,19 @@ export class XcodeVersionsParameter extends ArrayStatefulParameter<XcodesConfig,
99
return parseInstalledVersions(data);
1010
}
1111

12-
override async addItem(version: string): Promise<void> {
12+
override async addItem(version: string, plan: Plan<XcodesConfig>): Promise<void> {
1313
const $ = getPty();
14-
await $.spawn(`xcodes install "${version}"`, { interactive: true, stdin: true });
14+
const { appleId, appleIdPassword } = plan.desiredConfig ?? {};
15+
16+
const env: Record<string, string> = {};
17+
if (appleId) env['XCODES_USERNAME'] = appleId;
18+
if (appleIdPassword) env['XCODES_PASSWORD'] = appleIdPassword;
19+
20+
await $.spawn(`xcodes install "${version}"`, {
21+
interactive: true,
22+
stdin: true,
23+
...(Object.keys(env).length > 0 ? { env } : {}),
24+
});
1525
}
1626

1727
override async removeItem(version: string): Promise<void> {

src/resources/xcodes/xcodes-resource.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,7 @@ const schema = z
1717
.object({
1818
xcodeVersions: z
1919
.array(z.string())
20-
.describe(
21-
'List of Xcode versions to install via xcodes (e.g. ["15.2", "14.3.1"]). ' +
22-
'Installing Xcode requires Apple ID credentials — xcodes will prompt interactively or use ' +
23-
'the XCODES_USERNAME and XCODES_PASSWORD environment variables for non-interactive installs.'
24-
)
20+
.describe('List of Xcode versions to install via xcodes (e.g. ["15.2", "14.3.1"]).')
2521
.optional(),
2622
selected: z
2723
.string()
@@ -30,6 +26,19 @@ const schema = z
3026
'Must be one of the installed xcodeVersions. Equivalent to running xcodes select.'
3127
)
3228
.optional(),
29+
appleId: z
30+
.string()
31+
.optional()
32+
.describe(
33+
'Apple ID email used to authenticate with Apple servers when downloading Xcode. ' +
34+
'If omitted, xcodes will prompt interactively on first install.'
35+
),
36+
appleIdPassword: z
37+
.string()
38+
.optional()
39+
.describe(
40+
'Apple ID password. Required alongside appleId for non-interactive installs.'
41+
),
3342
})
3443
.describe('xcodes resource — install and manage multiple Xcode versions via the xcodes CLI');
3544

@@ -75,6 +84,8 @@ export class XcodesResource extends Resource<XcodesConfig> {
7584
parameterSettings: {
7685
xcodeVersions: { type: 'stateful', definition: new XcodeVersionsParameter(), order: 1 },
7786
selected: { type: 'stateful', definition: new XcodesSelectedParameter(), order: 2 },
87+
appleId: { type: 'string', setting: true },
88+
appleIdPassword: { type: 'string', setting: true },
7889
},
7990
};
8091
}

0 commit comments

Comments
 (0)