@gengjiawen/os-init 1.5.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ pnpm lint-staged
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.7.0](https://github.com/gengjiawen/os-init/compare/v1.6.0...v1.7.0) (2025-11-09)
4
+
5
+
6
+ ### Features
7
+
8
+ * add setup android sdk ([cb18d40](https://github.com/gengjiawen/os-init/commit/cb18d40e53e312b0aedc66a9cde6738bc2f4f1ba))
9
+
10
+ ## [1.6.0](https://github.com/gengjiawen/os-init/compare/v1.5.1...v1.6.0) (2025-10-25)
11
+
12
+
13
+ ### Features
14
+
15
+ * update Docker configuration to expose additional ports for development ([c1f0be1](https://github.com/gengjiawen/os-init/commit/c1f0be1f5f7559a32cbb2e5f1a829fd79f85d910))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * set-dev build issues ([728c0e9](https://github.com/gengjiawen/os-init/commit/728c0e9964d19cdd7624a7e9c5f2e68efaf4858f))
21
+
3
22
  ## [1.5.1](https://github.com/gengjiawen/os-init/compare/v1.5.0...v1.5.1) (2025-10-25)
4
23
 
5
24
 
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  ## os-init CLI
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/%40gengjiawen%2Fos-init)](https://www.npmjs.com/package/@gengjiawen/os-init)
4
+
3
5
  A CLI tool to quickly configure AI development tools and environments.
4
6
 
5
7
  ## Usage
@@ -52,6 +54,32 @@ Example:
52
54
  pnpx @gengjiawen/os-init set-dev "ssh-rsa AAAAB3NzaC1yc2..."
53
55
  ```
54
56
 
57
+ ### Setup Android Development Environment
58
+
59
+ ```bash
60
+ pnpx @gengjiawen/os-init set-android [options]
61
+ ```
62
+
63
+ Sets up a complete Android development environment on macOS and Linux. This command will:
64
+ - Install Android SDK to `~/Android` (or custom path with `--android-home <path>`)
65
+ - Download and install Android SDK Command-line Tools, Platform Tools, Build Tools, CMake, and NDK
66
+ - Automatically accept Android SDK licenses
67
+ - Configure environment variables in your shell config (~/.bashrc, ~/.zshrc, or ~/.config/fish/config.fish)
68
+ - Works on both macOS (Intel and Apple Silicon) and Linux
69
+ - No sudo access required
70
+
71
+ Example:
72
+ ```bash
73
+ # Basic installation
74
+ pnpx @gengjiawen/os-init set-android
75
+
76
+ # Custom installation path
77
+ pnpx @gengjiawen/os-init set-android --android-home ~/my-android-sdk
78
+
79
+ # Skip environment variable configuration
80
+ pnpx @gengjiawen/os-init set-android --skip-env-vars
81
+ ```
82
+
55
83
  ---
56
84
 
57
85
  Project generated by [gengjiawen/ts-scaffold](https://github.com/gengjiawen/ts-scaffold)
@@ -0,0 +1,128 @@
1
+ # Android Development Environment Setup
2
+
3
+ This command sets up a complete Android development environment on macOS and Linux systems.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ os-init set-android [options]
9
+ ```
10
+
11
+ ## What It Does
12
+
13
+ The `set-android` command:
14
+
15
+ 1. **Creates Android SDK directory** at `~/Android` (or custom path)
16
+ 2. **Downloads and installs Android SDK** command-line tools (automatically detects macOS or Linux)
17
+ 3. **Accepts Android SDK licenses** automatically
18
+ 4. **Installs essential components**:
19
+ - Platform tools (adb, fastboot, etc.)
20
+ - Android 36 platform
21
+ - Build tools 36.0.0
22
+ - CMake 3.30.5
23
+ - NDK 29.0.14206865
24
+ 5. **Configures environment variables** in your shell configuration file (~/.bashrc, ~/.zshrc, or ~/.config/fish/config.fish)
25
+
26
+ ## Options
27
+
28
+ - `--android-home <path>` - Custom Android SDK installation path (default: `~/Android`)
29
+ - `--skip-env-vars` - Skip adding environment variables to shell config
30
+
31
+ ## Examples
32
+
33
+ ### Basic Installation
34
+
35
+ ```bash
36
+ os-init set-android
37
+ ```
38
+
39
+ This installs the Android SDK to `~/Android` and adds environment variables to your shell config.
40
+
41
+ ### Custom Installation Path
42
+
43
+ ```bash
44
+ os-init set-android --android-home /home/user/android-sdk
45
+ ```
46
+
47
+ ### Skip Environment Variables
48
+
49
+ If you prefer to manage environment variables manually:
50
+
51
+ ```bash
52
+ os-init set-android --skip-env-vars
53
+ ```
54
+
55
+ ## Requirements
56
+
57
+ - **macOS or Linux operating system**
58
+ - Internet connection to download Android SDK
59
+ - Node.js 22.12.0 or higher
60
+
61
+ The script uses `@compilets/unzip-url` package to download and extract the Android SDK, so no external `curl` or `unzip` tools are required. No sudo access is needed as the SDK is installed in your home directory.
62
+
63
+ ## Environment Variables
64
+
65
+ After installation, the following environment variables will be set:
66
+
67
+ ```bash
68
+ export ANDROID_HOME=~/Android
69
+ export ANDROID_SDK_ROOT=${ANDROID_HOME}
70
+ export ANDROID_NDK_HOME=${ANDROID_HOME}/ndk/29.0.14206865
71
+ export PATH=${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/emulator:${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/tools:${ANDROID_HOME}/tools/bin:${PATH}
72
+ ```
73
+
74
+ ## Activating the Environment
75
+
76
+ After installation, activate the environment in your current shell:
77
+
78
+ ```bash
79
+ source ~/.bashrc # or ~/.zshrc, or ~/.config/fish/config.fish
80
+ ```
81
+
82
+ Or simply open a new terminal session.
83
+
84
+ ## Verifying Installation
85
+
86
+ Check that the Android SDK is properly installed:
87
+
88
+ ```bash
89
+ adb --version
90
+ sdkmanager --list
91
+ ```
92
+
93
+ ## Platform-Specific Notes
94
+
95
+ ### macOS
96
+ - The SDK is downloaded from Google's macOS-specific distribution
97
+ - Works on both Intel and Apple Silicon Macs
98
+ - No sudo permissions required
99
+
100
+ ### Linux
101
+ - The SDK is downloaded from Google's Linux-specific distribution
102
+ - Tested on Ubuntu and Debian-based systems
103
+ - No sudo permissions required when installing to home directory
104
+
105
+ ## Troubleshooting
106
+
107
+ ### Command Not Found After Installation
108
+
109
+ Make sure you've sourced your shell configuration file or opened a new terminal:
110
+
111
+ ```bash
112
+ source ~/.bashrc # or your shell's config file
113
+ ```
114
+
115
+ ### Download Failures
116
+
117
+ If the Android SDK download fails, check your internet connection and try again. The script will show detailed error messages.
118
+
119
+ ## What Gets Installed
120
+
121
+ - **Android SDK Command-line Tools** (version 11076708)
122
+ - **Platform Tools** - adb, fastboot, etc.
123
+ - **Android Platform 36** - Latest Android API level
124
+ - **Build Tools 36.0.0** - Tools for building Android apps
125
+ - **CMake 3.30.5** - For native code compilation
126
+ - **NDK 29.0.14206865** - Native Development Kit for C/C++ code
127
+
128
+
package/bin/bin.js CHANGED
@@ -8,6 +8,7 @@ const {
8
8
  installCodexDeps,
9
9
  writeRaycastConfig,
10
10
  setupDevEnvironment,
11
+ setupAndroidEnvironment,
11
12
  } = require('../build')
12
13
 
13
14
  const program = new Command()
@@ -102,4 +103,29 @@ program
102
103
  }
103
104
  })
104
105
 
106
+ program
107
+ .command('set-android')
108
+ .description('setup Android development environment (macOS and Linux)')
109
+ .option('--android-home <path>', 'Custom Android SDK installation path')
110
+ .option(
111
+ '--skip-env-vars',
112
+ 'Skip adding environment variables to shell config'
113
+ )
114
+ .action(async (options) => {
115
+ try {
116
+ const { androidHome, envVarsAdded, shellRcFile } =
117
+ await setupAndroidEnvironment({
118
+ androidHome: options.androidHome,
119
+ skipEnvVars: options.skipEnvVars,
120
+ })
121
+ console.log(`\n✅ Android SDK installed at: ${androidHome}`)
122
+ if (envVarsAdded && shellRcFile) {
123
+ console.log(`✅ Environment variables added to: ${shellRcFile}`)
124
+ }
125
+ } catch (err) {
126
+ console.error('Failed to setup Android environment:', err.message)
127
+ process.exit(1)
128
+ }
129
+ })
130
+
105
131
  program.parse(process.argv)
@@ -0,0 +1,8 @@
1
+ export declare function setupAndroidEnvironment(options?: {
2
+ androidHome?: string;
3
+ skipEnvVars?: boolean;
4
+ }): Promise<{
5
+ androidHome: string;
6
+ envVarsAdded: boolean;
7
+ shellRcFile?: string;
8
+ }>;
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupAndroidEnvironment = setupAndroidEnvironment;
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const os = require("os");
7
+ const execa_1 = require("execa");
8
+ const unzip_url_1 = require("@compilets/unzip-url");
9
+ const ANDROID_CONFIG = {
10
+ sdkVersion: '11076708',
11
+ platformVersion: 'android-36',
12
+ buildToolsVersion: '36.0.0',
13
+ cmakeVersion: '3.30.5',
14
+ ndkVersion: '29.0.14206865',
15
+ };
16
+ function getDefaultAndroidHome() {
17
+ return path.join(os.homedir(), 'Android');
18
+ }
19
+ function getSdkDownloadUrl(sdkVersion) {
20
+ const platform = os.platform();
21
+ if (platform === 'darwin') {
22
+ return `https://dl.google.com/android/repository/commandlinetools-mac-${sdkVersion}_latest.zip`;
23
+ }
24
+ else if (platform === 'linux') {
25
+ return `https://dl.google.com/android/repository/commandlinetools-linux-${sdkVersion}_latest.zip`;
26
+ }
27
+ else {
28
+ throw new Error(`Unsupported platform: ${platform}. Only macOS and Linux are supported.`);
29
+ }
30
+ }
31
+ function getAndroidEnvVars(androidHome, ndkVersion) {
32
+ return `
33
+ # Android development environment
34
+ export ANDROID_HOME=${androidHome}
35
+ export ANDROID_SDK_ROOT=\${ANDROID_HOME}
36
+ export ANDROID_NDK_HOME=\${ANDROID_HOME}/ndk/${ndkVersion}
37
+ export PATH=\${ANDROID_HOME}/cmdline-tools/latest/bin:\${ANDROID_HOME}/emulator:\${ANDROID_HOME}/platform-tools:\${ANDROID_HOME}/tools:\${ANDROID_HOME}/tools/bin:\${PATH}
38
+ `;
39
+ }
40
+ function getShellRcFile() {
41
+ const shell = process.env.SHELL || '';
42
+ const homeDir = os.homedir();
43
+ if (shell.includes('zsh')) {
44
+ return path.join(homeDir, '.zshrc');
45
+ }
46
+ else if (shell.includes('fish')) {
47
+ return path.join(homeDir, '.config', 'fish', 'config.fish');
48
+ }
49
+ else {
50
+ return path.join(homeDir, '.bashrc');
51
+ }
52
+ }
53
+ function hasAndroidEnvVars(rcFile) {
54
+ if (!fs.existsSync(rcFile)) {
55
+ return false;
56
+ }
57
+ const content = fs.readFileSync(rcFile, 'utf-8');
58
+ return content.includes('ANDROID_HOME');
59
+ }
60
+ function appendEnvVarsToShellConfig(rcFile, envVars) {
61
+ const dir = path.dirname(rcFile);
62
+ if (!fs.existsSync(dir)) {
63
+ fs.mkdirSync(dir, { recursive: true });
64
+ }
65
+ fs.appendFileSync(rcFile, envVars);
66
+ console.log(`Environment variables added to: ${rcFile}`);
67
+ }
68
+ async function setupAndroidEnvironment(options) {
69
+ const androidHome = options?.androidHome || getDefaultAndroidHome();
70
+ const { sdkVersion, platformVersion, buildToolsVersion, cmakeVersion, ndkVersion, } = ANDROID_CONFIG;
71
+ console.log('Starting Android development environment setup...\n');
72
+ const platform = os.platform();
73
+ if (platform !== 'linux' && platform !== 'darwin') {
74
+ throw new Error(`Unsupported platform: ${platform}. Only macOS and Linux are supported.`);
75
+ }
76
+ console.log(`Platform: ${platform === 'darwin' ? 'macOS' : 'Linux'}`);
77
+ console.log(`Creating Android SDK directory at: ${androidHome}`);
78
+ try {
79
+ if (!fs.existsSync(androidHome)) {
80
+ fs.mkdirSync(androidHome, { recursive: true });
81
+ }
82
+ }
83
+ catch (error) {
84
+ throw new Error(`Failed to create Android directory: ${error instanceof Error ? error.message : String(error)}`);
85
+ }
86
+ console.log('\nDownloading and setting up Android SDK...');
87
+ const sdkUrl = getSdkDownloadUrl(sdkVersion);
88
+ try {
89
+ const cmdlineToolsPath = path.join(androidHome, 'cmdline-tools');
90
+ if (!fs.existsSync(cmdlineToolsPath)) {
91
+ fs.mkdirSync(cmdlineToolsPath, { recursive: true });
92
+ }
93
+ console.log(`Downloading and extracting from: ${sdkUrl}`);
94
+ await (0, unzip_url_1.unzip)(sdkUrl, cmdlineToolsPath);
95
+ const tempToolsPath = path.join(cmdlineToolsPath, 'cmdline-tools');
96
+ const latestToolsPath = path.join(cmdlineToolsPath, 'latest');
97
+ if (fs.existsSync(tempToolsPath)) {
98
+ fs.renameSync(tempToolsPath, latestToolsPath);
99
+ }
100
+ console.log('Android SDK extracted successfully');
101
+ }
102
+ catch (error) {
103
+ throw new Error(`Failed to download/extract Android SDK: ${error instanceof Error ? error.message : String(error)}`);
104
+ }
105
+ const sdkmanagerEnv = {
106
+ ...process.env,
107
+ ANDROID_HOME: androidHome,
108
+ ANDROID_SDK_ROOT: androidHome,
109
+ PATH: `${androidHome}/cmdline-tools/latest/bin:${process.env.PATH}`,
110
+ };
111
+ console.log('\nAccepting Android SDK licenses...');
112
+ try {
113
+ await (0, execa_1.execa)('bash', ['-c', 'yes | sdkmanager --licenses'], {
114
+ env: sdkmanagerEnv,
115
+ stdio: 'inherit',
116
+ });
117
+ }
118
+ catch (error) {
119
+ console.warn('Warning: License acceptance may have failed, but continuing...');
120
+ }
121
+ console.log('\nInstalling Android SDK components...');
122
+ const components = [
123
+ 'platform-tools',
124
+ `platforms;${platformVersion}`,
125
+ `build-tools;${buildToolsVersion}`,
126
+ `cmake;${cmakeVersion}`,
127
+ `ndk;${ndkVersion}`,
128
+ ];
129
+ try {
130
+ const componentsList = components.join(' ');
131
+ await (0, execa_1.execa)('bash', ['-c', `yes | sdkmanager ${componentsList}`], {
132
+ env: sdkmanagerEnv,
133
+ stdio: 'inherit',
134
+ });
135
+ console.log('Android SDK components installed successfully');
136
+ }
137
+ catch (error) {
138
+ throw new Error(`Failed to install SDK components: ${error instanceof Error ? error.message : String(error)}`);
139
+ }
140
+ const androidConfigDir = path.join(androidHome, '.android');
141
+ if (fs.existsSync(androidConfigDir)) {
142
+ fs.rmSync(androidConfigDir, { recursive: true, force: true });
143
+ }
144
+ if (platform === 'linux' && androidHome.startsWith('/opt')) {
145
+ try {
146
+ await (0, execa_1.execa)('chmod', ['-R', '777', androidHome], { stdio: 'inherit' });
147
+ }
148
+ catch (error) {
149
+ console.warn('Warning: Failed to set permissions, but continuing...');
150
+ }
151
+ }
152
+ console.log('\n✅ Android SDK installation completed!');
153
+ let envVarsAdded = false;
154
+ let shellRcFile;
155
+ if (!options?.skipEnvVars) {
156
+ shellRcFile = getShellRcFile();
157
+ console.log(`\nConfiguring shell environment in: ${shellRcFile}`);
158
+ if (hasAndroidEnvVars(shellRcFile)) {
159
+ console.log('Android environment variables already exist in shell config, skipping...');
160
+ }
161
+ else {
162
+ const envVars = getAndroidEnvVars(androidHome, ndkVersion);
163
+ appendEnvVarsToShellConfig(shellRcFile, envVars);
164
+ envVarsAdded = true;
165
+ }
166
+ console.log('\n📋 To activate the environment in your current shell, run:');
167
+ console.log(` source ${shellRcFile}`);
168
+ }
169
+ console.log('\n✅ Android development environment setup completed successfully!');
170
+ return {
171
+ androidHome,
172
+ envVarsAdded,
173
+ shellRcFile,
174
+ };
175
+ }
@@ -43,15 +43,15 @@ async function setupDevEnvironment(sshPublicKey, targetDir) {
43
43
  }
44
44
  console.log(`Copying dev-setup to: ${targetPath}`);
45
45
  copyDirSync(sourceDir, targetPath);
46
- const dockerfilePath = path.join(targetPath, 'Dockerfile.txt');
46
+ const dockerfilePath = path.join(targetPath, 'Dockerfile');
47
47
  if (fs.existsSync(dockerfilePath)) {
48
48
  let content = fs.readFileSync(dockerfilePath, 'utf-8');
49
49
  content = content.replace('ssh-public-key-placeholder', sshPublicKey);
50
50
  fs.writeFileSync(dockerfilePath, content);
51
- console.log('SSH public key has been configured in Dockerfile.txt');
51
+ console.log('SSH public key has been configured in Dockerfile');
52
52
  }
53
53
  else {
54
- throw new Error(`Dockerfile.txt not found in ${targetPath}`);
54
+ throw new Error(`Dockerfile not found in ${targetPath}`);
55
55
  }
56
56
  const hasDockerCompose = await commandExists('docker-compose');
57
57
  if (hasDockerCompose) {
@@ -84,7 +84,7 @@ async function setupDevEnvironment(sshPublicKey, targetDir) {
84
84
  console.log(' docker-compose build && docker-compose up -d');
85
85
  }
86
86
  const localIp = ip.address();
87
- console.log('\n📡 SSH Connection:');
87
+ console.log('\n SSH Connection:');
88
88
  console.log(` ssh gitpod@${localIp} -p 2222`);
89
89
  console.log('\nOr use localhost if connecting from the same machine:');
90
90
  console.log(' ssh gitpod@localhost -p 2222');
package/build/index.d.ts CHANGED
@@ -12,3 +12,4 @@ export declare function writeRaycastConfig(apiKey: string): {
12
12
  configPath: string;
13
13
  };
14
14
  export { setupDevEnvironment } from './dev-setup';
15
+ export { setupAndroidEnvironment } from './android-setup';
package/build/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setupDevEnvironment = void 0;
3
+ exports.setupAndroidEnvironment = exports.setupDevEnvironment = void 0;
4
4
  exports.writeClaudeConfig = writeClaudeConfig;
5
5
  exports.installDeps = installDeps;
6
6
  exports.writeCodexConfig = writeCodexConfig;
@@ -165,3 +165,5 @@ function writeRaycastConfig(apiKey) {
165
165
  }
166
166
  var dev_setup_1 = require("./dev-setup");
167
167
  Object.defineProperty(exports, "setupDevEnvironment", { enumerable: true, get: function () { return dev_setup_1.setupDevEnvironment; } });
168
+ var android_setup_1 = require("./android-setup");
169
+ Object.defineProperty(exports, "setupAndroidEnvironment", { enumerable: true, get: function () { return android_setup_1.setupAndroidEnvironment; } });
@@ -15,5 +15,6 @@ RUN set -eux; \
15
15
 
16
16
  # Run sshd in foreground
17
17
  USER root
18
- EXPOSE 22
18
+ EXPOSE 22 3000 5173
19
19
  CMD ["/usr/sbin/sshd", "-D", "-e"]
20
+
@@ -8,8 +8,9 @@ services:
8
8
  tty: true
9
9
  ports:
10
10
  - '2222:22'
11
+ - '3000:3000'
12
+ - '5173:5173'
11
13
  cap_add:
12
14
  - SYS_PTRACE
13
15
  volumes:
14
16
  - .:/pwd
15
- - $HOME/.local:/root/.local
@@ -29,3 +29,4 @@ AcceptEnv LANG LC_*
29
29
 
30
30
  # SFTP Subsystem
31
31
  Subsystem sftp /usr/lib/openssh/sftp-server
32
+
@@ -0,0 +1,243 @@
1
+ import * as fs from 'fs'
2
+ import * as path from 'path'
3
+ import * as os from 'os'
4
+ import { execa } from 'execa'
5
+ import { unzip } from '@compilets/unzip-url'
6
+
7
+ /** Android SDK configuration */
8
+ const ANDROID_CONFIG = {
9
+ sdkVersion: '11076708',
10
+ platformVersion: 'android-36',
11
+ buildToolsVersion: '36.0.0',
12
+ cmakeVersion: '3.30.5',
13
+ ndkVersion: '29.0.14206865',
14
+ } as const
15
+
16
+ /** Get default Android home directory */
17
+ function getDefaultAndroidHome(): string {
18
+ return path.join(os.homedir(), 'Android')
19
+ }
20
+
21
+ /** Get SDK download URL based on platform */
22
+ function getSdkDownloadUrl(sdkVersion: string): string {
23
+ const platform = os.platform()
24
+ if (platform === 'darwin') {
25
+ return `https://dl.google.com/android/repository/commandlinetools-mac-${sdkVersion}_latest.zip`
26
+ } else if (platform === 'linux') {
27
+ return `https://dl.google.com/android/repository/commandlinetools-linux-${sdkVersion}_latest.zip`
28
+ } else {
29
+ throw new Error(
30
+ `Unsupported platform: ${platform}. Only macOS and Linux are supported.`
31
+ )
32
+ }
33
+ }
34
+
35
+ /** Get Android environment variables */
36
+ function getAndroidEnvVars(androidHome: string, ndkVersion: string): string {
37
+ return `
38
+ # Android development environment
39
+ export ANDROID_HOME=${androidHome}
40
+ export ANDROID_SDK_ROOT=\${ANDROID_HOME}
41
+ export ANDROID_NDK_HOME=\${ANDROID_HOME}/ndk/${ndkVersion}
42
+ export PATH=\${ANDROID_HOME}/cmdline-tools/latest/bin:\${ANDROID_HOME}/emulator:\${ANDROID_HOME}/platform-tools:\${ANDROID_HOME}/tools:\${ANDROID_HOME}/tools/bin:\${PATH}
43
+ `
44
+ }
45
+
46
+ /** Detect shell configuration file */
47
+ function getShellRcFile(): string {
48
+ const shell = process.env.SHELL || ''
49
+ const homeDir = os.homedir()
50
+
51
+ if (shell.includes('zsh')) {
52
+ return path.join(homeDir, '.zshrc')
53
+ } else if (shell.includes('fish')) {
54
+ return path.join(homeDir, '.config', 'fish', 'config.fish')
55
+ } else {
56
+ return path.join(homeDir, '.bashrc')
57
+ }
58
+ }
59
+
60
+ /** Check if Android environment variables already exist in shell config */
61
+ function hasAndroidEnvVars(rcFile: string): boolean {
62
+ if (!fs.existsSync(rcFile)) {
63
+ return false
64
+ }
65
+ const content = fs.readFileSync(rcFile, 'utf-8')
66
+ return content.includes('ANDROID_HOME')
67
+ }
68
+
69
+ /** Append Android environment variables to shell config */
70
+ function appendEnvVarsToShellConfig(rcFile: string, envVars: string): void {
71
+ const dir = path.dirname(rcFile)
72
+ if (!fs.existsSync(dir)) {
73
+ fs.mkdirSync(dir, { recursive: true })
74
+ }
75
+
76
+ fs.appendFileSync(rcFile, envVars)
77
+ console.log(`Environment variables added to: ${rcFile}`)
78
+ }
79
+
80
+ /** Setup Android development environment */
81
+ export async function setupAndroidEnvironment(options?: {
82
+ androidHome?: string
83
+ skipEnvVars?: boolean
84
+ }): Promise<{
85
+ androidHome: string
86
+ envVarsAdded: boolean
87
+ shellRcFile?: string
88
+ }> {
89
+ const androidHome = options?.androidHome || getDefaultAndroidHome()
90
+ const {
91
+ sdkVersion,
92
+ platformVersion,
93
+ buildToolsVersion,
94
+ cmakeVersion,
95
+ ndkVersion,
96
+ } = ANDROID_CONFIG
97
+
98
+ console.log('Starting Android development environment setup...\n')
99
+
100
+ // Check if running on supported platform
101
+ const platform = os.platform()
102
+ if (platform !== 'linux' && platform !== 'darwin') {
103
+ throw new Error(
104
+ `Unsupported platform: ${platform}. Only macOS and Linux are supported.`
105
+ )
106
+ }
107
+
108
+ console.log(`Platform: ${platform === 'darwin' ? 'macOS' : 'Linux'}`)
109
+
110
+ // Create Android directory
111
+ console.log(`Creating Android SDK directory at: ${androidHome}`)
112
+ try {
113
+ if (!fs.existsSync(androidHome)) {
114
+ fs.mkdirSync(androidHome, { recursive: true })
115
+ }
116
+ } catch (error) {
117
+ throw new Error(
118
+ `Failed to create Android directory: ${error instanceof Error ? error.message : String(error)}`
119
+ )
120
+ }
121
+
122
+ // Download and setup Android SDK
123
+ console.log('\nDownloading and setting up Android SDK...')
124
+ const sdkUrl = getSdkDownloadUrl(sdkVersion)
125
+
126
+ try {
127
+ // Create cmdline-tools directory
128
+ const cmdlineToolsPath = path.join(androidHome, 'cmdline-tools')
129
+ if (!fs.existsSync(cmdlineToolsPath)) {
130
+ fs.mkdirSync(cmdlineToolsPath, { recursive: true })
131
+ }
132
+
133
+ // Download and extract SDK using unzip-url
134
+ console.log(`Downloading and extracting from: ${sdkUrl}`)
135
+ await unzip(sdkUrl, cmdlineToolsPath)
136
+
137
+ // Move cmdline-tools to latest
138
+ const tempToolsPath = path.join(cmdlineToolsPath, 'cmdline-tools')
139
+ const latestToolsPath = path.join(cmdlineToolsPath, 'latest')
140
+ if (fs.existsSync(tempToolsPath)) {
141
+ fs.renameSync(tempToolsPath, latestToolsPath)
142
+ }
143
+
144
+ console.log('Android SDK extracted successfully')
145
+ } catch (error) {
146
+ throw new Error(
147
+ `Failed to download/extract Android SDK: ${error instanceof Error ? error.message : String(error)}`
148
+ )
149
+ }
150
+
151
+ // Set environment variables for sdkmanager
152
+ const sdkmanagerEnv = {
153
+ ...process.env,
154
+ ANDROID_HOME: androidHome,
155
+ ANDROID_SDK_ROOT: androidHome,
156
+ PATH: `${androidHome}/cmdline-tools/latest/bin:${process.env.PATH}`,
157
+ }
158
+
159
+ // Accept licenses
160
+ console.log('\nAccepting Android SDK licenses...')
161
+ try {
162
+ await execa('bash', ['-c', 'yes | sdkmanager --licenses'], {
163
+ env: sdkmanagerEnv,
164
+ stdio: 'inherit',
165
+ })
166
+ } catch (error) {
167
+ console.warn(
168
+ 'Warning: License acceptance may have failed, but continuing...'
169
+ )
170
+ }
171
+
172
+ // Install SDK components
173
+ console.log('\nInstalling Android SDK components...')
174
+ const components = [
175
+ 'platform-tools',
176
+ `platforms;${platformVersion}`,
177
+ `build-tools;${buildToolsVersion}`,
178
+ `cmake;${cmakeVersion}`,
179
+ `ndk;${ndkVersion}`,
180
+ ]
181
+
182
+ try {
183
+ const componentsList = components.join(' ')
184
+ await execa('bash', ['-c', `yes | sdkmanager ${componentsList}`], {
185
+ env: sdkmanagerEnv,
186
+ stdio: 'inherit',
187
+ })
188
+ console.log('Android SDK components installed successfully')
189
+ } catch (error) {
190
+ throw new Error(
191
+ `Failed to install SDK components: ${error instanceof Error ? error.message : String(error)}`
192
+ )
193
+ }
194
+
195
+ // Clean up .android directory
196
+ const androidConfigDir = path.join(androidHome, '.android')
197
+ if (fs.existsSync(androidConfigDir)) {
198
+ fs.rmSync(androidConfigDir, { recursive: true, force: true })
199
+ }
200
+
201
+ // Set permissions (only needed on Linux for system-wide installs)
202
+ if (platform === 'linux' && androidHome.startsWith('/opt')) {
203
+ try {
204
+ await execa('chmod', ['-R', '777', androidHome], { stdio: 'inherit' })
205
+ } catch (error) {
206
+ console.warn('Warning: Failed to set permissions, but continuing...')
207
+ }
208
+ }
209
+
210
+ console.log('\n✅ Android SDK installation completed!')
211
+
212
+ // Handle environment variables
213
+ let envVarsAdded = false
214
+ let shellRcFile: string | undefined
215
+
216
+ if (!options?.skipEnvVars) {
217
+ shellRcFile = getShellRcFile()
218
+ console.log(`\nConfiguring shell environment in: ${shellRcFile}`)
219
+
220
+ if (hasAndroidEnvVars(shellRcFile)) {
221
+ console.log(
222
+ 'Android environment variables already exist in shell config, skipping...'
223
+ )
224
+ } else {
225
+ const envVars = getAndroidEnvVars(androidHome, ndkVersion)
226
+ appendEnvVarsToShellConfig(shellRcFile, envVars)
227
+ envVarsAdded = true
228
+ }
229
+
230
+ console.log('\n📋 To activate the environment in your current shell, run:')
231
+ console.log(` source ${shellRcFile}`)
232
+ }
233
+
234
+ console.log(
235
+ '\n✅ Android development environment setup completed successfully!'
236
+ )
237
+
238
+ return {
239
+ androidHome,
240
+ envVarsAdded,
241
+ shellRcFile,
242
+ }
243
+ }
package/libs/dev-setup.ts CHANGED
@@ -57,15 +57,15 @@ export async function setupDevEnvironment(
57
57
  console.log(`Copying dev-setup to: ${targetPath}`)
58
58
  copyDirSync(sourceDir, targetPath)
59
59
 
60
- // Replace SSH_PUBLIC_KEY placeholder in Dockerfile.txt
61
- const dockerfilePath = path.join(targetPath, 'Dockerfile.txt')
60
+ // Replace SSH_PUBLIC_KEY placeholder in Dockerfile
61
+ const dockerfilePath = path.join(targetPath, 'Dockerfile')
62
62
  if (fs.existsSync(dockerfilePath)) {
63
63
  let content = fs.readFileSync(dockerfilePath, 'utf-8')
64
64
  content = content.replace('ssh-public-key-placeholder', sshPublicKey)
65
65
  fs.writeFileSync(dockerfilePath, content)
66
- console.log('SSH public key has been configured in Dockerfile.txt')
66
+ console.log('SSH public key has been configured in Dockerfile')
67
67
  } else {
68
- throw new Error(`Dockerfile.txt not found in ${targetPath}`)
68
+ throw new Error(`Dockerfile not found in ${targetPath}`)
69
69
  }
70
70
 
71
71
  // Check if docker-compose is available
@@ -109,7 +109,7 @@ export async function setupDevEnvironment(
109
109
 
110
110
  // Display SSH connection instructions
111
111
  const localIp = ip.address()
112
- console.log('\n📡 SSH Connection:')
112
+ console.log('\n SSH Connection:')
113
113
  console.log(` ssh gitpod@${localIp} -p 2222`)
114
114
  console.log('\nOr use localhost if connecting from the same machine:')
115
115
  console.log(' ssh gitpod@localhost -p 2222')
package/libs/index.ts CHANGED
@@ -210,3 +210,6 @@ export function writeRaycastConfig(apiKey: string): { configPath: string } {
210
210
 
211
211
  // Re-export dev-setup functionality
212
212
  export { setupDevEnvironment } from './dev-setup'
213
+
214
+ // Re-export android-setup functionality
215
+ export { setupAndroidEnvironment } from './android-setup'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gengjiawen/os-init",
3
3
  "private": false,
4
- "version": "1.5.1",
4
+ "version": "1.7.0",
5
5
  "description": "",
6
6
  "main": "index.js",
7
7
  "bin": {
@@ -10,7 +10,7 @@
10
10
  "scripts": {
11
11
  "dev": "tsc -w",
12
12
  "server": "nodemon --exec ts-node libs/index.ts",
13
- "prepare": "husky install",
13
+ "prepare": "husky",
14
14
  "clean": "rimraf build",
15
15
  "format": "prettier --write \"{examples,libs,script,bin}/**/*.{js,ts}\" \"**/*.yml\"",
16
16
  "format:check": "prettier --list-different \"{examples,libs,script,bin}/**/*.{js,ts}\" \"**/*.yml\"",
@@ -19,6 +19,7 @@
19
19
  "postbuild": "cpy '**/*' '!**/*.ts' ../build/ --cwd=libs --parents"
20
20
  },
21
21
  "dependencies": {
22
+ "@compilets/unzip-url": "^1.0.0",
22
23
  "commander": "^12.1.0",
23
24
  "ip": "^2.0.1",
24
25
  "execa": "^8.0.1"