diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4fffee4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,41 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Purpose + +Integration test project for [premex-ab/setup-android](https://github.com/premex-ab/setup-android). Each `sdk-*` folder is a standalone minimal Android Gradle project that builds with a specific cmdline-tools version. CI runs all of them across Ubuntu, Windows, and macOS. + +## Build Commands + +```bash +# Build a specific SDK version +cd sdk-{N} +./gradlew --no-daemon assembleDebug + +# Sync SDK versions (fetches new cmdline-tools from Google, creates folders, updates CI matrix and docs) +python3 scripts/sync-sdk-versions.py +``` + +## Architecture + +- **`sdk-*` folders** — Each is an independent Gradle project with its own wrapper, AGP version, and Java compatibility. The app itself is a no-op `MainActivity` with zero dependencies. +- **`scripts/sync-sdk-versions.py`** — Parses Google's [repository2-3.xml](https://dl.google.com/android/repository/repository2-3.xml) to discover stable cmdline-tools versions, creates missing `sdk-*` folders from templates, and regenerates `.github/workflows/build.yml`, `README.md`, and `CLAUDE.md`. +- **`.github/workflows/build.yml`** — CI matrix: OS × SDK version. Auto-generated by the sync script — do not edit the matrix entries by hand. +- **`.github/workflows/sync-sdk-versions.yml`** — Weekly (Monday 06:00 UTC) cron job that runs the sync script and opens a PR if new versions are found. Uses a GitHub App token for authentication. + +## Version Mapping + +The sync script maps cmdline-tools major versions to build tooling via `get_build_config()`: + + +| cmdline-tools | AGP | Gradle | Java | compileSdk | +|---------------|-----|--------|------|------------| +| 1–6 | 7.2.2 | 7.5.1 | 11 | 32 | +| 7–9 | 7.4.2 | 7.6.4 | 11 | 33 | +| 10–11 | 8.1.4 | 8.4 | 17 | 34 | +| 12–13 | 8.5.2 | 8.7 | 17 | 34 | +| 14+ | 8.7.3 | 8.12 | 17 | 35 | + + +When adding support for newer AGP/Gradle versions, update the mapping in `scripts/sync-sdk-versions.py`. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f1f8b04 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# Sample Application + +Minimal Android application used to integration-test [premex-ab/setup-android](https://github.com/premex-ab/setup-android). + +## Structure + +Each `sdk-*` folder is a standalone Android Gradle project targeting a specific [cmdline-tools](https://developer.android.com/tools) version: + + +| Folder | cmdline-tools | AGP | Gradle | Java | +|--------|--------------|-----|--------|------| +| sdk-1 | 1.0 (6200805) | 7.2.2 | 7.5.1 | 11 | +| sdk-2 | 2.1 (6609375) | 7.2.2 | 7.5.1 | 11 | +| sdk-3 | 3.0 (6858069) | 7.2.2 | 7.5.1 | 11 | +| sdk-4 | 4.0 (7302050) | 7.2.2 | 7.5.1 | 11 | +| sdk-5 | 5.0 (7583922) | 7.2.2 | 7.5.1 | 11 | +| sdk-6 | 6.0 (8092744) | 7.2.2 | 7.5.1 | 11 | +| sdk-7 | 7.0 (8512546) | 7.4.2 | 7.6.4 | 11 | +| sdk-8 | 8.0 (9123335) | 7.4.2 | 7.6.4 | 11 | +| sdk-9 | 9.0 (9477386) | 7.4.2 | 7.6.4 | 11 | +| sdk-10 | 10.0 (9862592) | 8.1.4 | 8.4 | 17 | +| sdk-11 | 11.0 (10406996) | 8.1.4 | 8.4 | 17 | +| sdk-12 | 12.0 (11076708) | 8.5.2 | 8.7 | 17 | +| sdk-13 | 13.0 (11479570) | 8.5.2 | 8.7 | 17 | +| sdk-16 | 16.0 (12266719) | 8.7.3 | 8.12 | 17 | +| sdk-17 | 17.0 (12700392) | 8.7.3 | 8.12 | 17 | +| sdk-19 | 19.0 (13114758) | 8.7.3 | 8.12 | 17 | +| sdk-20 | 20.0 (14742923) | 8.7.3 | 8.12 | 17 | + + +## CI + +Every push to `main` builds all SDK versions on Ubuntu, Windows, and macOS using `premex-ab/setup-android`. + +## Auto-sync + +A weekly workflow (`sync-sdk-versions.yml`) fetches the available cmdline-tools versions from Google's [SDK repository](https://dl.google.com/android/repository/repository2-3.xml). If new versions are found, it creates the corresponding `sdk-*` folder and opens a PR. diff --git a/scripts/sync-sdk-versions.py b/scripts/sync-sdk-versions.py index ac581a4..7403cd8 100755 --- a/scripts/sync-sdk-versions.py +++ b/scripts/sync-sdk-versions.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ Fetches available Android SDK cmdline-tools versions from Google's repository -and creates/updates sdk-* folders and the CI workflow matrix accordingly. +and creates/updates sdk-* folders, the CI workflow matrix, and documentation. """ import json @@ -15,6 +15,8 @@ REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) REPO_XML_URL = "https://dl.google.com/android/repository/repository2-3.xml" WORKFLOW_PATH = os.path.join(REPO_ROOT, ".github", "workflows", "build.yml") +README_PATH = os.path.join(REPO_ROOT, "README.md") +CLAUDE_MD_PATH = os.path.join(REPO_ROOT, "CLAUDE.md") # Template files come from the newest existing sdk-* folder TEMPLATE_FILES = [ @@ -26,6 +28,7 @@ ] # AGP / Gradle / Java / compileSdk mapping based on cmdline-tools major version +# Source: https://developer.android.com/build/releases/gradle-plugin def get_build_config(major_version): if major_version <= 6: return {"agp": "7.2.2", "gradle": "7.5.1", "java": "11", "compile_sdk": "32"} @@ -251,6 +254,91 @@ def update_workflow(versions): print(f"Updated {WORKFLOW_PATH} with {len(matrix_entries)} SDK versions") +def generate_sdk_table(versions): + """Generate the per-folder SDK table for README.md.""" + existing = sorted(get_existing_sdk_dirs()) + lines = [ + "| Folder | cmdline-tools | AGP | Gradle | Java |", + "|--------|--------------|-----|--------|------|", + ] + for major in existing: + if major in versions: + ver = versions[major]["version"] + build = versions[major]["build"] + config = get_build_config(major) + lines.append( + f"| sdk-{major} | {ver} ({build}) | {config['agp']} | {config['gradle']} | {config['java']} |" + ) + return "\n".join(lines) + + +def generate_version_map(): + """Generate the version mapping table for CLAUDE.md.""" + # Collect unique configs in order + configs = [] + seen = set() + for major in range(1, 100): + config = get_build_config(major) + key = (config["agp"], config["gradle"], config["java"], config["compile_sdk"]) + if key not in seen: + seen.add(key) + configs.append((major, config)) + # Stop after we've seen the "default" catch-all config twice + if len(configs) > 1 and major > 50: + break + + lines = [ + "| cmdline-tools | AGP | Gradle | Java | compileSdk |", + "|---------------|-----|--------|------|------------|", + ] + + for i, (start_major, config) in enumerate(configs): + if i + 1 < len(configs): + end_major = configs[i + 1][0] - 1 + label = f"{start_major}\u2013{end_major}" if start_major != end_major else str(start_major) + else: + label = f"{start_major}+" + lines.append( + f"| {label} | {config['agp']} | {config['gradle']} | {config['java']} | {config['compile_sdk']} |" + ) + + return "\n".join(lines) + + +def update_doc(file_path, begin_marker, end_marker, content): + """Replace content between markers in a file.""" + if not os.path.exists(file_path): + return + + with open(file_path, "r") as f: + text = f.read() + + pattern = re.compile( + rf"({re.escape(begin_marker)}\n).*?({re.escape(end_marker)})", + re.DOTALL, + ) + + if not pattern.search(text): + print(f"Warning: markers not found in {file_path}") + return + + new_text = pattern.sub(rf"\g<1>{content}\n\g<2>", text) + + with open(file_path, "w") as f: + f.write(new_text) + + print(f"Updated {file_path}") + + +def update_docs(versions): + """Update README.md and CLAUDE.md with current version tables.""" + sdk_table = generate_sdk_table(versions) + version_map = generate_version_map() + + update_doc(README_PATH, "", "", sdk_table) + update_doc(CLAUDE_MD_PATH, "", "", version_map) + + def main(): versions = fetch_versions() print(f"Found {len(versions)} stable cmdline-tools versions: {sorted(versions.keys())}") @@ -267,8 +355,9 @@ def main(): for major in sorted(new_versions.keys()): create_sdk_folder(major, new_versions[major]["build"]) - # Always regenerate workflow to keep it in sync + # Always regenerate workflow and docs to keep them in sync update_workflow(versions) + update_docs(versions) if new_versions: # Output for GitHub Actions