Skip to content

Commit 41d3368

Browse files
feat: Add android-cli resource (#69)
* feat: Add android-cli resource (auto-generated from issue #68) * fix: macOS install and tests. Moved android studios to it's own folder * feat: add android studio version completions * fix: skip tests for linux arm (unsupported) * feat: made android cli emulator a stateful parameter instead. Improved docs for android * feat: improved documentation (capitalized folder names) * fix: added warning message for tart clone if disk is not accessible. Added apply note for android cli. Fixed android cli emulator list * feat: add beta completions * feat: add retry to android studio download * feat: add backup wget as well for linux * chore: bump version --------- Co-authored-by: kevinwang5658 <20214115+kevinwang5658@users.noreply.github.com> Co-authored-by: kevinwang <kevinwang5658@gmail.com>
1 parent 8b58370 commit 41d3368

39 files changed

Lines changed: 714 additions & 68 deletions

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

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

completions-cron/src/index.ts

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,25 @@ const BATCH_SIZE = 1000
66
async function getResourceId(
77
supabase: SupabaseClient,
88
resourceType: string,
9+
prerelease: boolean,
910
cache: Map<string, string>
1011
): Promise<string> {
11-
if (cache.has(resourceType)) {
12-
return cache.get(resourceType)!
12+
const cacheKey = `${resourceType}:${prerelease}`
13+
if (cache.has(cacheKey)) {
14+
return cache.get(cacheKey)!
1315
}
1416

1517
const { data, error } = await supabase
1618
.from('registry_resources')
1719
.select('id')
1820
.eq('type', resourceType)
21+
.eq('prerelease', prerelease)
1922

2023
if (error || !data?.[0]?.id) {
21-
throw new Error(`Resource type '${resourceType}' not found in registry_resources`)
24+
throw new Error(`Resource type '${resourceType}' (prerelease=${prerelease}) not found in registry_resources`)
2225
}
2326

24-
cache.set(resourceType, data[0].id)
27+
cache.set(cacheKey, data[0].id)
2528
return data[0].id
2629
}
2730

@@ -30,14 +33,15 @@ async function processModule(
3033
resourceType: string,
3134
parameterPath: string,
3235
fetchFn: () => Promise<string[]>,
36+
prerelease: boolean,
3337
resourceIdCache: Map<string, string>
3438
): Promise<void> {
3539
console.log(`Processing ${resourceType}${parameterPath}...`)
3640

3741
const values = await fetchFn()
3842
console.log(` [${resourceType}${parameterPath}] Fetched ${values.length} values`)
3943

40-
const resourceId = await getResourceId(supabase, resourceType, resourceIdCache)
44+
const resourceId = await getResourceId(supabase, resourceType, prerelease, resourceIdCache)
4145

4246
await supabase
4347
.from('resource_parameter_completions')
@@ -66,31 +70,44 @@ async function processModule(
6670
console.log(` [${resourceType}${parameterPath}] Done: inserted ${values.length} completions`)
6771
}
6872

73+
async function runCompletions(env: Env): Promise<void> {
74+
const supabase = createClient(env.SUPABASE_URL, env.SUPABASE_SERVICE_ROLE_KEY)
75+
const prerelease = env.PRERELEASE === 'true'
76+
const resourceIdCache = new Map<string, string>()
77+
78+
const results = await Promise.allSettled(
79+
completionModules.map(({ resourceType, parameterPath, fetch }: CompletionModule) =>
80+
processModule(supabase, resourceType, parameterPath, fetch, prerelease, resourceIdCache)
81+
)
82+
)
83+
84+
for (const result of results) {
85+
if (result.status === 'rejected') {
86+
console.error('Completion module failed:', result.reason)
87+
}
88+
}
89+
90+
console.log('Successfully processed all resource completion tasks')
91+
}
92+
6993
export default {
70-
async fetch(req: Request) {
94+
async fetch(req: Request, env: Env, ctx: ExecutionContext) {
7195
const url = new URL(req.url)
96+
97+
if (req.method === 'POST' && url.pathname === '/trigger') {
98+
if (req.headers.get('Authorization') !== env.TRIGGER_SECRET) {
99+
return new Response('Unauthorized', { status: 401 })
100+
}
101+
ctx.waitUntil(runCompletions(env))
102+
return new Response('Triggered', { status: 202 })
103+
}
104+
72105
url.pathname = '/__scheduled'
73106
url.searchParams.append('cron', '* * * * *')
74107
return new Response(`To test the scheduled handler, ensure you have used the "--test-scheduled" then try running "curl ${url.href}".`)
75108
},
76109

77110
async scheduled(_event: ScheduledEvent, env: Env, _ctx: ExecutionContext): Promise<void> {
78-
console.log('hihi')
79-
const supabase = createClient(env.SUPABASE_URL, env.SUPABASE_SERVICE_ROLE_KEY)
80-
const resourceIdCache = new Map<string, string>()
81-
82-
const results = await Promise.allSettled(
83-
completionModules.map(({ resourceType, parameterPath, fetch }: CompletionModule) =>
84-
processModule(supabase, resourceType, parameterPath, fetch, resourceIdCache)
85-
)
86-
)
87-
88-
for (const result of results) {
89-
if (result.status === 'rejected') {
90-
console.error('Completion module failed:', result.reason)
91-
}
92-
}
93-
94-
console.log('Successfully processed all resource completion tasks')
111+
await runCompletions(env)
95112
},
96113
} satisfies ExportedHandler<Env>

completions-cron/types/worker-configuration.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ declare namespace Cloudflare {
66
mainModule: typeof import("../src");
77
}
88
interface Env {
9+
SUPABASE_URL: string;
10+
SUPABASE_SERVICE_ROLE_KEY: string;
11+
PRERELEASE: string;
12+
TRIGGER_SECRET: string;
913
}
1014
}
1115
interface Env extends Cloudflare.Env {}

completions-cron/wrangler.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ head_sampling_rate = 1
1010

1111
[vars]
1212
SUPABASE_URL = "https://kdctbvqvqjfquplxhqrm.supabase.co"
13+
PRERELEASE = "false"
1314

1415
[triggers]
1516
crons = ["0 5 * * *"]
17+
18+
# Beta environment — deploys as a separate worker: resource-completions-cron-beta
19+
[env.beta]
20+
name = "resource-completions-cron-beta"
21+
22+
[env.beta.vars]
23+
SUPABASE_URL = "https://kdctbvqvqjfquplxhqrm.supabase.co"
24+
PRERELEASE = "true"
25+
26+
[env.beta.triggers]
27+
crons = ["0 5 * * *"]
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
{
2-
"title": "ai & agents",
3-
"pages": ["claude-code", "claude-code-project", "ollama", "openclaw"]
2+
"title": "AI & Agents",
3+
"pages": [
4+
"claude-code",
5+
"claude-code-project",
6+
"ollama",
7+
"openclaw"
8+
]
49
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
---
2+
title: android-cli
3+
description: Reference pages for the Android CLI resources
4+
---
5+
6+
The `android-cli` resource installs and configures [Android CLI](https://developer.android.com/tools/agents/android-cli), Google's command-line tool for managing the Android development environment. It manages the CLI itself, SDK packages, and Android Virtual Devices (AVDs) in a single resource.
7+
8+
On macOS, Android CLI is installed via the official curl script (ARM64 and x86_64 supported). On Linux, only AMD64/x86_64 is supported.
9+
10+
## Parameters
11+
12+
- **sdkPath**: *(string)* Path to the Android SDK directory. Written to `~/.androidrc` as `--sdk=<path>`. Defaults to the android CLI's built-in default location if omitted.
13+
- **sdkPackages**: *(string[])* Android SDK packages to install declaratively. Package paths use forward-slash notation (e.g. `platforms/android-35`, `build-tools/35.0.0`, `cmdline-tools/latest`, `platform-tools`, `system-images/android-35/google_apis_playstore/x86_64`). Run `android sdk list --all` to see all available identifiers.
14+
- **emulators**: *(string[])* Android emulator profiles to create as AVDs. Each string is a hardware profile name (e.g. `medium_phone`, `pixel_9`). Emulators are always created after `sdkPackages` are installed. Run `android emulator create --list-profiles` to see available profiles.
15+
16+
## Example usage
17+
18+
Install the CLI with essential SDK packages:
19+
20+
```json title="codify.jsonc"
21+
[
22+
{
23+
"type": "android-cli",
24+
"sdkPackages": [
25+
"cmdline-tools/latest",
26+
"platform-tools",
27+
"platforms/android-35",
28+
"build-tools/35.0.0"
29+
]
30+
}
31+
]
32+
```
33+
34+
Full Android development environment with an emulator:
35+
36+
```json title="codify.jsonc"
37+
[
38+
{
39+
"type": "android-cli",
40+
"sdkPackages": [
41+
"cmdline-tools/latest",
42+
"platform-tools",
43+
"platforms/android-35",
44+
"build-tools/35.0.0",
45+
"system-images/android-35/google_apis_playstore/x86_64"
46+
],
47+
"emulators": ["pixel_9"]
48+
}
49+
]
50+
```
51+
52+
## Common emulator profiles
53+
54+
| Profile | Description |
55+
|---------|-------------|
56+
| `medium_phone` | Generic medium phone (default) |
57+
| `small_phone` | Generic small phone |
58+
| `foldable` | Foldable form factor |
59+
| `medium_tablet` | Generic medium tablet |
60+
| `pixel_9` | Google Pixel 9 |
61+
| `pixel_9_pro` | Google Pixel 9 Pro |
62+
| `pixel_9_pro_fold` | Google Pixel 9 Pro Fold |
63+
| `pixel_8` | Google Pixel 8 |
64+
| `wear_os_large_round` | Wear OS round watch |
65+
| `tv_1080p` | Android TV 1080p |
66+
| `automotive_1024p_landscape` | Android Automotive |
67+
68+
## Notes
69+
70+
- Linux ARM64 is **not** supported. Only AMD64/x86_64 is supported on Linux.
71+
- AVDs are removed using `android emulator remove`.
72+
- Run `android info` to display the default SDK path in use.

docs/resources/(resources)/editors-ides/android-studio.mdx renamed to docs/resources/(resources)/android/android-studio.mdx

File renamed without changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"title": "Android",
3+
"pages": [
4+
"android-cli",
5+
"android-studio"
6+
]
7+
}
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{
2-
"title": "asdf",
3-
"pages": ["asdf", "asdf-install", "asdf-plugin"]
2+
"title": "Asdf",
3+
"pages": [
4+
"asdf",
5+
"asdf-install",
6+
"asdf-plugin"
7+
]
48
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{
2-
"title": "editors & ides",
3-
"pages": ["vscode", "cursor", "intellij-idea", "clion", "goland", "phpstorm", "pycharm", "rider", "rubymine", "rustrover", "webstorm", "android-studio"]
2+
"title": "Editors & IDEs",
3+
"pages": [
4+
"[android-studio](/docs/resources/android/android-studio)",
5+
"..."
6+
]
47
}

0 commit comments

Comments
 (0)