Skip to content

Commit e85211d

Browse files
committed
fix: macOS install and tests. Moved android studios to it's own folder
1 parent 53c1fcc commit e85211d

9 files changed

Lines changed: 72 additions & 66 deletions

File tree

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Plugin, runPlugin } from '@codifycli/plugin-core';
22
import { AndroidCliResource } from './resources/android/android-cli/android-cli.js';
33
import { AndroidEmulatorResource } from './resources/android/android-cli/android-emulator.js';
4-
import { AndroidStudioResource } from './resources/android/android-studio.js';
54
import { AptResource } from './resources/apt/apt.js';
65
import { AsdfResource } from './resources/asdf/asdf.js';
76
import { AsdfInstallResource } from './resources/asdf/asdf-install.js';
@@ -76,6 +75,7 @@ import { PhpStormResource } from './resources/jetbrains/phpstorm/phpstorm.js';
7675
import { GoLandResource } from './resources/jetbrains/goland/goland.js';
7776
import { RiderResource } from './resources/jetbrains/rider/rider.js';
7877
import { RubyMineResource } from './resources/jetbrains/rubymine/rubymine.js';
78+
import {AndroidStudioResource} from "./resources/android/android-studios/android-studio.js";
7979

8080
export const MIN_SUPPORTED_CLI_VERSION: string | undefined = '1.1.0';
8181

src/resources/android/android-cli/android-cli.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {
22
CreatePlan,
33
DestroyPlan,
44
ModifyPlan,
5-
PackageManager,
65
ParameterChange,
76
Resource,
87
ResourceSettings,
@@ -27,7 +26,7 @@ export const schema = z
2726
'Path to the Android SDK directory. Written to ~/.androidrc as --sdk=<path>. Defaults to the android CLI default location.'
2827
)
2928
.optional(),
30-
packages: z
29+
sdkPackages: z
3130
.array(z.string())
3231
.describe(
3332
'Android SDK packages to install. Examples: "platforms/android-35", "build-tools/35.0.0", "platform-tools", "cmdline-tools/latest", "system-images/android-35/google_apis_playstore/x86_64".'
@@ -41,7 +40,7 @@ export type AndroidCliConfig = z.infer<typeof schema>;
4140
const ANDROIDRC_PATH = path.join(os.homedir(), '.androidrc');
4241

4342
const defaultConfig: Partial<AndroidCliConfig> = {
44-
packages: [],
43+
sdkPackages: [],
4544
};
4645

4746
export class AndroidCliResource extends Resource<AndroidCliConfig> {
@@ -57,7 +56,7 @@ export class AndroidCliResource extends Resource<AndroidCliConfig> {
5756
schema,
5857
parameterSettings: {
5958
sdkPath: { type: 'directory', canModify: true },
60-
packages: { type: 'stateful', definition: new AndroidSdkPackagesParameter() },
59+
sdkPackages: { type: 'stateful', definition: new AndroidSdkPackagesParameter() },
6160
},
6261
};
6362
}
@@ -87,13 +86,16 @@ export class AndroidCliResource extends Resource<AndroidCliConfig> {
8786
async create(plan: CreatePlan<AndroidCliConfig>): Promise<void> {
8887
const $ = getPty();
8988

89+
const isArm = await Utils.isArmArch();
90+
9091
if (Utils.isMacOS()) {
91-
await $.spawnSafe('brew tap android/tap', {
92-
env: { HOMEBREW_NO_AUTO_UPDATE: '1', HOMEBREW_NO_ASK: '1', NONINTERACTIVE: '1' },
93-
});
94-
await Utils.installViaPkgMgr('android-cli', undefined, PackageManager.BREW);
92+
const arch = isArm ? 'darwin_arm64' : 'darwin_x86_64';
93+
await $.spawn(
94+
`curl -fsSL https://dl.google.com/android/cli/latest/${arch}/install.sh | bash`,
95+
{ interactive: true }
96+
);
9597
} else {
96-
if (await Utils.isArmArch()) {
98+
if (isArm) {
9799
throw new Error(
98100
'Android CLI does not support Linux ARM64. Only AMD64/x86_64 is supported on Linux.'
99101
);
@@ -120,12 +122,8 @@ export class AndroidCliResource extends Resource<AndroidCliConfig> {
120122
}
121123

122124
async destroy(plan: DestroyPlan<AndroidCliConfig>): Promise<void> {
123-
if (Utils.isMacOS()) {
124-
await Utils.uninstallViaPkgMgr('android-cli', undefined, PackageManager.BREW);
125-
} else {
126-
const androidBinPath = path.join(os.homedir(), '.local', 'bin', 'android');
127-
await fs.rm(androidBinPath, { force: true });
128-
}
125+
const androidBinPath = path.join(os.homedir(), '.local', 'bin', 'android');
126+
await fs.rm(androidBinPath, { force: true });
129127

130128
if (plan.currentConfig.sdkPath) {
131129
await this.removeSdkPath();

src/resources/android/android-cli/android-emulator.ts

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,8 @@ export const schema = z
1919
profile: z
2020
.string()
2121
.describe(
22-
'Android hardware profile for the emulator (e.g. "medium_phone", "pixel_9"). Run `android emulator create --list-profiles` to see all available profiles.'
22+
'Android hardware profile for the emulator (e.g. "medium_phone", "pixel_9"). Run `android emulator create --list-profiles` to see all available profiles. The profile name is also used as the AVD name.'
2323
),
24-
name: z
25-
.string()
26-
.describe(
27-
'Custom name for the Android Virtual Device. Defaults to the profile name if not specified.'
28-
)
29-
.optional(),
3024
})
3125
.describe('Create and manage an Android Virtual Device (AVD) using the android CLI');
3226

@@ -52,10 +46,9 @@ export class AndroidEmulatorResource extends Resource<AndroidEmulatorConfig> {
5246
dependencies: ['android-cli'],
5347
parameterSettings: {
5448
profile: {},
55-
name: { canModify: false },
5649
},
5750
allowMultiple: {
58-
identifyingParameters: ['profile', 'name'],
51+
identifyingParameters: ['profile'],
5952
},
6053
};
6154
}
@@ -66,7 +59,7 @@ export class AndroidEmulatorResource extends Resource<AndroidEmulatorConfig> {
6659
const { status, data } = await $.spawnSafe('android emulator list', { interactive: true });
6760
if (status === SpawnStatus.ERROR) return null;
6861

69-
const avdName = this.resolveAvdName(params);
62+
const avdName = params.profile;
7063
if (!avdName) return null;
7164

7265
const lines = data.split('\n').map((l) => l.trim()).filter(Boolean);
@@ -76,41 +69,24 @@ export class AndroidEmulatorResource extends Resource<AndroidEmulatorConfig> {
7669

7770
if (!found) return null;
7871

79-
return {
80-
profile: params.profile,
81-
...(params.name ? { name: params.name } : {}),
82-
};
72+
return { profile: params.profile };
8373
}
8474

8575
async create(plan: CreatePlan<AndroidEmulatorConfig>): Promise<void> {
8676
const $ = getPty();
87-
const { profile, name } = plan.desiredConfig;
88-
89-
let cmd = `android emulator create --profile="${profile}"`;
90-
if (name) {
91-
// The android CLI may support --name in future releases; include it if provided.
92-
cmd += ` --name="${name}"`;
93-
}
94-
95-
await $.spawn(cmd, { interactive: true });
77+
await $.spawn(`android emulator create "${plan.desiredConfig.profile}"`, { interactive: true });
9678
}
9779

9880
async destroy(plan: DestroyPlan<AndroidEmulatorConfig>): Promise<void> {
9981
const $ = getPty();
100-
const avdName = this.resolveAvdName(plan.currentConfig);
82+
const avdName = plan.currentConfig.profile;
10183
if (!avdName) return;
10284

103-
// Try avdmanager first (available when cmdline-tools is installed)
10485
const { status } = await $.spawnSafe(`avdmanager delete avd -n "${avdName}"`, { interactive: true });
10586

10687
if (status === SpawnStatus.ERROR) {
107-
// Fallback: remove AVD files directly
10888
await fs.rm(path.join(AVD_DIR, `${avdName}.avd`), { recursive: true, force: true });
10989
await fs.rm(path.join(AVD_DIR, `${avdName}.ini`), { force: true });
11090
}
11191
}
112-
113-
private resolveAvdName(params: Partial<AndroidEmulatorConfig>): string | undefined {
114-
return params.name ?? params.profile;
115-
}
11692
}

src/resources/android/android-cli/android-sdk-packages-parameter.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ export class AndroidSdkPackagesParameter extends ArrayStatefulParameter<AndroidC
1111

1212
return data
1313
.split('\n')
14-
.map((l) => l.trim())
15-
.filter((l) => l && !l.startsWith('-') && !l.startsWith('Path') && !l.startsWith('Installed') && !l.includes('|'));
14+
.filter((l) => l.match(/^\s{2}\S/))
15+
.map((l) => l.trim().split(/\s+/)[0])
16+
.filter(Boolean);
1617
}
1718

1819
async addItem(item: string): Promise<void> {

src/resources/android/android-cli/examples.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const exampleAndroidCliBasic: ExampleConfig = {
66
configs: [
77
{
88
type: 'android-cli',
9-
packages: ['cmdline-tools/latest', 'platform-tools', 'platforms/android-35', 'build-tools/35.0.0'],
9+
sdkPackages:['cmdline-tools/latest', 'platform-tools', 'platforms/android-35', 'build-tools/35.0.0'],
1010
},
1111
],
1212
};
@@ -17,7 +17,7 @@ export const exampleAndroidCliFullSetup: ExampleConfig = {
1717
configs: [
1818
{
1919
type: 'android-cli',
20-
packages: [
20+
sdkPackages:[
2121
'cmdline-tools/latest',
2222
'platform-tools',
2323
'platforms/android-35',
@@ -38,7 +38,7 @@ export const exampleAndroidEmulatorBasic: ExampleConfig = {
3838
configs: [
3939
{
4040
type: 'android-cli',
41-
packages: [
41+
sdkPackages:[
4242
'cmdline-tools/latest',
4343
'platform-tools',
4444
'platforms/android-35',
File renamed without changes.

src/resources/android/android-studio.ts renamed to src/resources/android/android-studios/android-studio.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import os from 'node:os';
55
import path from 'node:path';
66
import plist from 'plist';
77

8-
import { Utils as LocalUtils } from '../../utils/index.js';
8+
import { Utils as LocalUtils } from '../../../utils/index.js';
99
import { AndroidStudioPlist, AndroidStudioVersionData } from './types.js';
1010

1111
export const schema = z.object({
File renamed without changes.

test/android/android-cli.test.ts

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ describe('Android CLI integration tests', async () => {
3636
[
3737
{
3838
type: 'android-cli',
39-
packages: ['cmdline-tools/latest', 'platform-tools'],
39+
sdkPackages:['cmdline-tools/latest', 'platform-tools'],
4040
},
4141
],
4242
{
@@ -48,19 +48,6 @@ describe('Android CLI integration tests', async () => {
4848
expect(list.status).toBe(SpawnStatus.SUCCESS);
4949
expect(list.data).toContain('platform-tools');
5050
},
51-
testModify: {
52-
modifiedConfigs: [
53-
{
54-
type: 'android-cli',
55-
packages: ['platform-tools'],
56-
},
57-
],
58-
validateModify: async () => {
59-
const list = await testSpawn('android sdk list');
60-
expect(list.status).toBe(SpawnStatus.SUCCESS);
61-
expect(list.data).toContain('platform-tools');
62-
},
63-
},
6451
validateDestroy: async () => {
6552
const result = await testSpawn('which android');
6653
expect(result.status).toBe(SpawnStatus.ERROR);
@@ -69,3 +56,47 @@ describe('Android CLI integration tests', async () => {
6956
);
7057
});
7158
});
59+
60+
describe('Android Emulator integration tests', async () => {
61+
const pluginPath = path.resolve('./src/index.ts');
62+
63+
beforeAll(async () => {
64+
const result = await testSpawn('which android');
65+
if (result.status === SpawnStatus.SUCCESS) {
66+
await PluginTester.uninstall(pluginPath, [{ type: 'android-cli' }]);
67+
}
68+
}, 120_000);
69+
70+
it('Can create and destroy an Android emulator', { timeout: 900_000 }, async () => {
71+
await PluginTester.fullTest(
72+
pluginPath,
73+
[
74+
{
75+
type: 'android-cli',
76+
sdkPackages:[
77+
'cmdline-tools/latest',
78+
'platform-tools',
79+
'platforms/android-35',
80+
'system-images/android-35/google_apis_playstore/x86_64',
81+
],
82+
},
83+
{
84+
type: 'android-emulator',
85+
profile: 'medium_phone',
86+
},
87+
],
88+
{
89+
validateApply: async () => {
90+
const list = await testSpawn('android emulator list');
91+
expect(list.status).toBe(SpawnStatus.SUCCESS);
92+
expect(list.data).toContain('medium_phone');
93+
},
94+
validateDestroy: async () => {
95+
const list = await testSpawn('android emulator list');
96+
expect(list.data).not.toContain('medium_phone');
97+
},
98+
}
99+
);
100+
});
101+
102+
});

0 commit comments

Comments
 (0)