From cd265339fcc4fa28a3b1748f0192e03a2d55cf2f Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Fri, 29 May 2026 11:37:52 +0530 Subject: [PATCH 1/5] add android quickstart template --- README.md | 1 + docs/llms.txt | 2 +- internal/cli/integration_quickstart_test.go | 5 +++- internal/cli/quickstart.go | 26 ++++++++++++++++++--- internal/cli/quickstart_test.go | 24 +++++++++++++++++++ 5 files changed, 53 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1f6f407..0ab2216 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ agora init my-nextjs-demo --template nextjs | Next.js video app | `agora init my-nextjs-demo --template nextjs` | A cloned Next.js quickstart, project binding, and `.env.local` | | Python voice agent | `agora init my-python-demo --template python` | A Python quickstart with Agora credentials written for the backend | | Go token service | `agora init my-go-demo --template go` | A Go server quickstart with project metadata and env wiring | +| Android conversational AI app | `agora quickstart create my-android-demo --template android` | A cloned Android quickstart on the `rest-api` branch | ## Install diff --git a/docs/llms.txt b/docs/llms.txt index d875887..27441b1 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -39,7 +39,7 @@ Check project health: agora project doctor --json - **Project Management**: Initialize, configure, and validate Agora projects - **JSON Output**: All commands support --json for automation and scripting (see Automation Notes below for one documented exception) - **Stable Exit Codes**: Consistent error codes for CI/CD integration -- **Template System**: Quick-start templates for Next.js, Python, and Go (see `agora init --help` for the current catalog) +- **Template System**: Quick-start templates for Next.js, Python, and Go (see `agora init --help` for the init-capable catalog); Android is clone-only for now and appears in `agora quickstart list` - **Cross-Platform**: macOS, Linux, Windows support - **Agentic discovery**: `agora introspect --json` and `agora --help --all --json` emit the same machine-readable command tree - **MCP server**: `agora mcp serve` exposes the CLI as Model Context Protocol tools for agents diff --git a/internal/cli/integration_quickstart_test.go b/internal/cli/integration_quickstart_test.go index 96dc52b..79f828f 100644 --- a/internal/cli/integration_quickstart_test.go +++ b/internal/cli/integration_quickstart_test.go @@ -53,11 +53,14 @@ func TestCLIQuickstartListAndCreate(t *testing.T) { if list.exitCode != 0 || !strings.Contains(list.stdout, `"id":"nextjs"`) || !strings.Contains(list.stdout, `"id":"python"`) || !strings.Contains(list.stdout, `"id":"go"`) { t.Fatalf("unexpected quickstart list result: %+v", list) } + if !strings.Contains(list.stdout, `"id":"android"`) { + t.Fatalf("expected android quickstart in list result: %+v", list) + } listAll := runCLI(t, []string{"quickstart", "list", "--show-all", "--json"}, cliRunOptions{env: map[string]string{ "XDG_CONFIG_HOME": configHome, "AGORA_LOG_LEVEL": "error", }}) - if listAll.exitCode != 0 || !strings.Contains(listAll.stdout, `"id":"go"`) { + if listAll.exitCode != 0 || !strings.Contains(listAll.stdout, `"id":"go"`) || !strings.Contains(listAll.stdout, `"id":"android"`) { t.Fatalf("unexpected quickstart list --show-all result: %+v", listAll) } diff --git a/internal/cli/quickstart.go b/internal/cli/quickstart.go index 1becb5e..bba6340 100644 --- a/internal/cli/quickstart.go +++ b/internal/cli/quickstart.go @@ -18,6 +18,7 @@ type quickstartTemplate struct { Description string Runtime string RepoURL string + Ref string DocsURL string DetectPaths []string EnvExamplePath string @@ -79,6 +80,21 @@ func quickstartTemplates() []quickstartTemplate { SupportsInit: true, Available: true, }, + { + ID: "android", + Title: "Conversational AI Android Quickstart", + Description: "Clone the official Android conversational AI quickstart.", + Runtime: "android", + RepoURL: "https://github.com/AgoraIO-Conversational-AI/agent-quickstart-android", + Ref: "rest-api", + DocsURL: "https://github.com/AgoraIO-Conversational-AI/agent-quickstart-android/tree/rest-api", + DetectPaths: []string{"settings.gradle", "gradlew", "app/src/main/AndroidManifest.xml"}, + InstallCommand: "Open in Android Studio", + RunCommand: "Run from Android Studio or Gradle", + EnvDocsSummary: "Android quickstart template. Clone-only for now; env seeding is not yet wired into the CLI.", + SupportsInit: false, + Available: true, + }, } } @@ -292,11 +308,15 @@ func (a *App) quickstartCreate(template quickstartTemplate, targetDir, explicitP if err != nil { return nil, err } + effectiveRef := strings.TrimSpace(ref) + if effectiveRef == "" { + effectiveRef = strings.TrimSpace(template.Ref) + } if overrideKey != "" { progress.emit("clone:override", fmt.Sprintf("Using repo override from %s", overrideKey), map[string]any{"repoUrl": repoURL, "envVar": overrideKey}) } - progress.emit("clone:start", "Cloning quickstart repository", map[string]any{"repoUrl": repoURL, "targetPath": absTarget, "ref": ref}) - if err := cloneQuickstartRepo(repoURL, absTarget, ref); err != nil { + progress.emit("clone:start", "Cloning quickstart repository", map[string]any{"repoUrl": repoURL, "targetPath": absTarget, "ref": effectiveRef}) + if err := cloneQuickstartRepo(repoURL, absTarget, effectiveRef); err != nil { return nil, err } progress.emit("clone:complete", "Quickstart repository cloned", map[string]any{"targetPath": absTarget}) @@ -346,7 +366,7 @@ func (a *App) quickstartCreate(template quickstartTemplate, targetDir, explicitP "title": template.Title, "written": written, "nextSteps": initNextSteps(template, absTarget), - "ref": ref, + "ref": effectiveRef, } if boundProject != nil { result["projectId"] = boundProject.project.ProjectID diff --git a/internal/cli/quickstart_test.go b/internal/cli/quickstart_test.go index a4a82fa..53636d4 100644 --- a/internal/cli/quickstart_test.go +++ b/internal/cli/quickstart_test.go @@ -153,3 +153,27 @@ func TestQuickstartRepoURLOverride(t *testing.T) { } } } + +func TestQuickstartTemplatesIncludeAndroid(t *testing.T) { + var android quickstartTemplate + found := false + for _, tmpl := range quickstartTemplates() { + if tmpl.ID == "android" { + android = tmpl + found = true + break + } + } + if !found { + t.Fatal("expected android quickstart template to exist") + } + if android.RepoURL != "https://github.com/AgoraIO-Conversational-AI/agent-quickstart-android" { + t.Fatalf("unexpected android repo url: %q", android.RepoURL) + } + if android.Ref != "rest-api" { + t.Fatalf("unexpected android default ref: %q", android.Ref) + } + if !android.Available || android.SupportsInit { + t.Fatalf("unexpected android flags: available=%v supportsInit=%v", android.Available, android.SupportsInit) + } +} From 2998ae3feedfca50459737d65115f534755ddf85 Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Fri, 19 Jun 2026 12:34:16 +0530 Subject: [PATCH 2/5] make android quickstart fully supported --- CHANGELOG.md | 5 ++ README.md | 4 +- docs/automation.md | 10 ++- docs/llms.txt | 2 +- internal/cli/integration_quickstart_test.go | 84 +++++++++++++++++++++ internal/cli/quickstart.go | 24 +++--- internal/cli/quickstart_test.go | 7 +- 7 files changed, 118 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40b436a..dd90c86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,14 @@ Earlier entries pre-date this convention and only carry their version's compare ## [Unreleased] +### Added + +- Add the Android conversational AI quickstart as a fully supported template with project-bound env seeding for the Python backend and `init` support. + ### Fixed - Update GoReleaser Cosign signing to emit `checksums.txt.sigstore.json` with `--bundle`, matching Cosign's current bundle-based signing flow. +- Remove the Android quickstart clone-only behavior so `quickstart create`, `quickstart env write`, and `init` all follow the same project-binding flow as Next.js, Python, and Go. ## [0.2.2] - 2026-05-26 diff --git a/README.md b/README.md index 0ab2216..b525604 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ agora init my-nextjs-demo --template nextjs | Next.js video app | `agora init my-nextjs-demo --template nextjs` | A cloned Next.js quickstart, project binding, and `.env.local` | | Python voice agent | `agora init my-python-demo --template python` | A Python quickstart with Agora credentials written for the backend | | Go token service | `agora init my-go-demo --template go` | A Go server quickstart with project metadata and env wiring | -| Android conversational AI app | `agora quickstart create my-android-demo --template android` | A cloned Android quickstart on the `rest-api` branch | +| Android conversational AI app | `agora init my-android-demo --template android` | An Android quickstart with the Python backend env file written to `server/.env` | ## Install @@ -180,6 +180,7 @@ Prints build metadata. Release binaries include version, commit, and build date. ```bash agora login agora init my-nextjs-demo --template nextjs +agora init my-android-demo --template android ``` ### Use an existing project with a quickstart @@ -231,6 +232,7 @@ Template-specific behavior: - Next.js quickstarts write `.env.local` and use `NEXT_PUBLIC_AGORA_APP_ID` plus `NEXT_AGORA_APP_CERTIFICATE` - Python quickstarts copy `server/env.example` to `server/.env`, then use `APP_ID` plus `APP_CERTIFICATE` - Go quickstarts copy `server-go/env.example` to `server-go/.env`, then use `APP_ID` plus `APP_CERTIFICATE` +- Android quickstarts copy `server/env.example` to `server/.env`, then use `APP_ID` plus `APP_CERTIFICATE` The CLI also writes repo-local project metadata to: diff --git a/docs/automation.md b/docs/automation.md index 4488908..b868988 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -340,6 +340,7 @@ Example: ```bash ./agora init my-nextjs-demo --template nextjs --json ./agora init my-nextjs-demo --template nextjs --new-project --json +./agora init my-android-demo --template android --json ``` By default `init` reuses an existing project — preferring one named exactly `"Default Project"`. If no default exists, interactive sessions show existing projects with a create-new option and default to the most recently created project; JSON, CI, and non-TTY runs select the most recent project automatically. Pass `--new-project` to force creation. Use `--project ` to bind to a specific project. @@ -349,7 +350,7 @@ Required `data` fields: - `action` Always `init`. - `template` - Template ID such as `nextjs`, `python`, or `go`. + Template ID such as `nextjs`, `python`, `go`, or `android`. - `projectAction` `created` or `existing`. - `reusedExistingProject` @@ -616,7 +617,7 @@ Automation notes: Example: ```bash -./agora quickstart create my-python-demo --template python --project my-project --json +./agora quickstart create my-android-demo --template android --project my-project --json ``` Required `data` fields: @@ -657,7 +658,7 @@ Safe branch fields: Example: ```bash -./agora quickstart env write /abs/path/to/my-python-demo --json +./agora quickstart env write /abs/path/to/my-android-demo --json ``` Required `data` fields: @@ -679,7 +680,8 @@ Required `data` fields: Env write behavior: - quickstart env files contain only the App ID and App Certificate variable names required by the template - Next.js uses `NEXT_PUBLIC_AGORA_APP_ID` and `NEXT_AGORA_APP_CERTIFICATE` -- Python and Go use `APP_ID` and `APP_CERTIFICATE` +- Python, Go, and Android use `APP_ID` and `APP_CERTIFICATE` +- Android quickstarts read and write `server/env.example` and `server/.env` - project metadata such as project ID, project name, region, template, projectType, and env path is stored in `.agora/project.json` - existing quickstart env files are preserved; missing credential keys are appended and existing credential keys are updated - stale Agora credential aliases for another runtime are commented out to avoid ambiguous dotenv resolution; for example, a Next.js quickstart prefers `NEXT_PUBLIC_AGORA_APP_ID` and comments out old `AGORA_APP_ID` / `APP_ID` entries when replacing them diff --git a/docs/llms.txt b/docs/llms.txt index 27441b1..d0e2fc2 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -39,7 +39,7 @@ Check project health: agora project doctor --json - **Project Management**: Initialize, configure, and validate Agora projects - **JSON Output**: All commands support --json for automation and scripting (see Automation Notes below for one documented exception) - **Stable Exit Codes**: Consistent error codes for CI/CD integration -- **Template System**: Quick-start templates for Next.js, Python, and Go (see `agora init --help` for the init-capable catalog); Android is clone-only for now and appears in `agora quickstart list` +- **Template System**: Quick-start templates for Next.js, Python, Go, and Android (see `agora init --help` for the init-capable catalog and `agora quickstart list` for the full catalog) - **Cross-Platform**: macOS, Linux, Windows support - **Agentic discovery**: `agora introspect --json` and `agora --help --all --json` emit the same machine-readable command tree - **MCP server**: `agora mcp serve` exposes the CLI as Model Context Protocol tools for agents diff --git a/internal/cli/integration_quickstart_test.go b/internal/cli/integration_quickstart_test.go index 79f828f..a6bcbd1 100644 --- a/internal/cli/integration_quickstart_test.go +++ b/internal/cli/integration_quickstart_test.go @@ -33,6 +33,14 @@ func TestCLIQuickstartListAndCreate(t *testing.T) { "server-go/main.go": "package main\nfunc main() {}\n", "web-client/package.json": `{"name":"go-quickstart-web"}`, }) + androidRepo := createLocalGitRepo(t, map[string]string{ + "README.md": "# Android Quickstart\n", + "settings.gradle": "rootProject.name = \"android-quickstart\"\n", + "gradlew": "#!/bin/sh\n", + "app/src/main/AndroidManifest.xml": "\n", + "server/env.example": "APP_ID=\nAPP_CERTIFICATE=\nPORT=8000\n", + "server/main.py": "print('hello from backend')\n", + }) project := buildFakeProject("Project Alpha", "prj_123456", "app_123456", "global") api.projects[project.ProjectID] = &project @@ -170,6 +178,82 @@ func TestCLIQuickstartListAndCreate(t *testing.T) { t.Fatalf("unexpected python quickstart env write result: %+v", writePythonEnv) } + androidUnboundTarget := filepath.Join(rootDir, "android-unbound") + createAndroidUnbound := runCLI(t, []string{"quickstart", "create", "android-unbound", "--template", "android", "--dir", androidUnboundTarget, "--json"}, cliRunOptions{ + env: map[string]string{ + "XDG_CONFIG_HOME": t.TempDir(), + "AGORA_LOG_LEVEL": "error", + "AGORA_QUICKSTART_ANDROID_REPO_URL": androidRepo, + }, + workdir: rootDir, + }) + if createAndroidUnbound.exitCode != 0 || !strings.Contains(createAndroidUnbound.stdout, `"envStatus":"template-only"`) { + t.Fatalf("unexpected unbound android quickstart create result: %+v", createAndroidUnbound) + } + if _, err := os.Stat(filepath.Join(androidUnboundTarget, "server", ".env")); !errors.Is(err, os.ErrNotExist) { + t.Fatalf("did not expect backend env in unbound android scaffold, got %v", err) + } + + androidBoundTarget := filepath.Join(rootDir, "android-demo") + createAndroidBound := runCLI(t, []string{"quickstart", "create", "android-demo", "--template", "android", "--dir", androidBoundTarget, "--json"}, cliRunOptions{ + env: map[string]string{ + "XDG_CONFIG_HOME": configHome, + "AGORA_API_BASE_URL": api.baseURL, + "AGORA_LOG_LEVEL": "error", + "AGORA_QUICKSTART_ANDROID_REPO_URL": androidRepo, + }, + workdir: rootDir, + }) + if createAndroidBound.exitCode != 0 || !strings.Contains(createAndroidBound.stdout, `"envStatus":"configured"`) || !strings.Contains(createAndroidBound.stdout, `"projectId":"prj_123456"`) { + t.Fatalf("unexpected bound android quickstart create result: %+v", createAndroidBound) + } + androidEnv, err := os.ReadFile(filepath.Join(androidBoundTarget, "server", ".env")) + if err != nil { + t.Fatalf("expected android backend .env in bound scaffold: %v", err) + } + if !strings.Contains(string(androidEnv), "APP_ID=app_123456") || !strings.Contains(string(androidEnv), "APP_CERTIFICATE=") || !strings.Contains(string(androidEnv), "PORT=8000") { + t.Fatalf("unexpected android backend env contents: %s", string(androidEnv)) + } + androidMetadata, err := os.ReadFile(filepath.Join(androidBoundTarget, ".agora", "project.json")) + if err != nil { + t.Fatalf("expected android .agora/project.json in bound scaffold: %v", err) + } + if !strings.Contains(string(androidMetadata), `"template": "android"`) { + t.Fatalf("unexpected android .agora/project.json contents: %s", string(androidMetadata)) + } + + writeAndroidEnv := runCLI(t, []string{"quickstart", "env", "write", androidBoundTarget, "--template", "android", "--json"}, cliRunOptions{ + env: map[string]string{ + "XDG_CONFIG_HOME": configHome, + "AGORA_API_BASE_URL": api.baseURL, + "AGORA_LOG_LEVEL": "error", + }, + workdir: rootDir, + }) + if writeAndroidEnv.exitCode != 0 || !strings.Contains(writeAndroidEnv.stdout, `"template":"android"`) { + t.Fatalf("unexpected android quickstart env write result: %+v", writeAndroidEnv) + } + androidEnvAfterWrite, err := os.ReadFile(filepath.Join(androidBoundTarget, "server", ".env")) + if err != nil { + t.Fatalf("expected android backend .env after env write: %v", err) + } + if !strings.Contains(string(androidEnvAfterWrite), "APP_ID=app_123456") || !strings.Contains(string(androidEnvAfterWrite), "APP_CERTIFICATE=") { + t.Fatalf("unexpected android backend env after env write: %s", string(androidEnvAfterWrite)) + } + + initAndroid := runCLI(t, []string{"init", "android-init-demo", "--template", "android", "--project", "prj_123456", "--json"}, cliRunOptions{ + env: map[string]string{ + "XDG_CONFIG_HOME": configHome, + "AGORA_API_BASE_URL": api.baseURL, + "AGORA_LOG_LEVEL": "error", + "AGORA_QUICKSTART_ANDROID_REPO_URL": androidRepo, + }, + workdir: rootDir, + }) + if initAndroid.exitCode != 0 || !strings.Contains(initAndroid.stdout, `"template":"android"`) || !strings.Contains(initAndroid.stdout, `"envPath":"server/.env"`) { + t.Fatalf("unexpected android init result: %+v", initAndroid) + } + repoScopedConfig := t.TempDir() persistSessionForIntegration(t, repoScopedConfig) repoShow := runCLI(t, []string{"project", "show", "--json"}, cliRunOptions{ diff --git a/internal/cli/quickstart.go b/internal/cli/quickstart.go index bba6340..049614a 100644 --- a/internal/cli/quickstart.go +++ b/internal/cli/quickstart.go @@ -86,13 +86,14 @@ func quickstartTemplates() []quickstartTemplate { Description: "Clone the official Android conversational AI quickstart.", Runtime: "android", RepoURL: "https://github.com/AgoraIO-Conversational-AI/agent-quickstart-android", - Ref: "rest-api", - DocsURL: "https://github.com/AgoraIO-Conversational-AI/agent-quickstart-android/tree/rest-api", - DetectPaths: []string{"settings.gradle", "gradlew", "app/src/main/AndroidManifest.xml"}, - InstallCommand: "Open in Android Studio", - RunCommand: "Run from Android Studio or Gradle", - EnvDocsSummary: "Android quickstart template. Clone-only for now; env seeding is not yet wired into the CLI.", - SupportsInit: false, + DocsURL: "https://github.com/AgoraIO-Conversational-AI/agent-quickstart-android/tree/main", + DetectPaths: []string{"settings.gradle", "gradlew", "server/env.example"}, + EnvExamplePath: "server/env.example", + EnvTargetPath: "server/.env", + InstallCommand: "Install Python backend dependencies and Android Studio", + RunCommand: "Run the Python backend, then launch the Android app", + EnvDocsSummary: "Copies server/env.example to server/.env, then writes APP_ID and APP_CERTIFICATE for the Python backend.", + SupportsInit: true, Available: true, }, } @@ -120,6 +121,7 @@ Use this group when you want a standalone demo or onboarding project.`, agora quickstart create my-nextjs-demo --template nextjs agora quickstart create my-python-demo --template python --project my-agent-demo agora quickstart create my-go-demo --template go --project my-agent-demo + agora quickstart create my-android-demo --template android --project my-agent-demo `), RunE: func(cmd *cobra.Command, args []string) error { if len(args) > 0 { @@ -238,6 +240,7 @@ The CLI can infer the quickstart type from the repository layout, or you can for agora quickstart env write apps/my-nextjs-demo agora quickstart env write apps/my-python-demo --project my-agent-demo agora quickstart env write apps/my-go-demo --project my-agent-demo + agora quickstart env write apps/my-android-demo --project my-agent-demo agora quickstart env write . --template nextjs `), RunE: func(cmd *cobra.Command, args []string) error { @@ -253,12 +256,13 @@ The CLI can infer the quickstart type from the repository layout, or you can for Long: `Write the runtime-specific env file expected by a cloned quickstart repository. Next.js quickstarts receive NEXT_PUBLIC_* client env vars plus server-only Agora credentials. -Python and Go quickstarts receive backend APP_ID and APP_CERTIFICATE values.`, +Python, Go, and Android quickstarts receive backend APP_ID and APP_CERTIFICATE values.`, Example: example(` agora quickstart env write agora quickstart env write apps/my-nextjs-demo agora quickstart env write apps/my-python-demo --project my-agent-demo agora quickstart env write apps/my-go-demo --project my-agent-demo + agora quickstart env write apps/my-android-demo --project my-agent-demo agora quickstart env write . --template python `), RunE: func(cmd *cobra.Command, args []string) error { @@ -637,7 +641,7 @@ func conflictingQuickstartEnvKeys(templateID string) []string { switch templateID { case "nextjs": return []string{"AGORA_APP_ID", "AGORA_APP_CERTIFICATE", "APP_ID", "APP_CERTIFICATE"} - case "python", "go": + case "android", "python", "go": return []string{"AGORA_APP_ID", "AGORA_APP_CERTIFICATE", "NEXT_PUBLIC_AGORA_APP_ID", "NEXT_AGORA_APP_CERTIFICATE"} default: return nil @@ -651,7 +655,7 @@ func renderQuickstartEnvValues(template quickstartTemplate, project projectDetai "NEXT_PUBLIC_AGORA_APP_ID": project.AppID, "NEXT_AGORA_APP_CERTIFICATE": *project.SignKey, } - case "python", "go": + case "android", "python", "go": return map[string]any{ "APP_ID": project.AppID, "APP_CERTIFICATE": *project.SignKey, diff --git a/internal/cli/quickstart_test.go b/internal/cli/quickstart_test.go index 53636d4..7159857 100644 --- a/internal/cli/quickstart_test.go +++ b/internal/cli/quickstart_test.go @@ -170,10 +170,13 @@ func TestQuickstartTemplatesIncludeAndroid(t *testing.T) { if android.RepoURL != "https://github.com/AgoraIO-Conversational-AI/agent-quickstart-android" { t.Fatalf("unexpected android repo url: %q", android.RepoURL) } - if android.Ref != "rest-api" { + if android.Ref != "" { t.Fatalf("unexpected android default ref: %q", android.Ref) } - if !android.Available || android.SupportsInit { + if android.EnvExamplePath != "server/env.example" || android.EnvTargetPath != "server/.env" { + t.Fatalf("unexpected android env paths: example=%q target=%q", android.EnvExamplePath, android.EnvTargetPath) + } + if !android.Available || !android.SupportsInit { t.Fatalf("unexpected android flags: available=%v supportsInit=%v", android.Available, android.SupportsInit) } } From f662edf90259509b4134d6249eaf62806636aec5 Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Fri, 19 Jun 2026 12:50:49 +0530 Subject: [PATCH 3/5] nit revert --- docs/automation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/automation.md b/docs/automation.md index f861621..5689d0f 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -619,7 +619,7 @@ Automation notes: Example: ```bash -./agora quickstart create my-android-demo --template android --project my-project --json +./agora quickstart create my-python-demo --template python --project my-project --json ``` Required `data` fields: @@ -660,7 +660,7 @@ Safe branch fields: Example: ```bash -./agora quickstart env write /abs/path/to/my-android-demo --json +./agora quickstart env write /abs/path/to/my-python-demo --json ``` Required `data` fields: From d794c68090c456c87fa9e7597e0bd76cd22eddb6 Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Tue, 21 Jul 2026 14:03:05 +0530 Subject: [PATCH 4/5] Support Android quickstart init flow --- CHANGELOG.md | 2 +- README.md | 9 ++++--- docs/automation.md | 8 +++--- docs/llms.txt | 3 +++ internal/cli/doctor.go | 2 ++ internal/cli/integration_quickstart_test.go | 27 +++++++++---------- internal/cli/quickstart.go | 29 ++++++++++++--------- 7 files changed, 47 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c270358..223b351 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Earlier entries pre-date this convention and only carry their version's compare - **BREAKING**: Update public JSON shapes for region-aware profiles: `auth login --json` and `auth status --json` include `data.region`, while project list/show API models no longer expose a project `region` field because the project APIs do not return it. - **BREAKING**: Stop persisting CLI API/OAuth integration values in `config.json`. `apiBaseUrl`, `oauthBaseUrl`, `oauthClientId`, and `oauthScope` are now derived from the selected login region or from explicit environment variable overrides (`AGORA_API_BASE_URL`, `AGORA_OAUTH_BASE_URL`, `AGORA_OAUTH_CLIENT_ID`, `AGORA_OAUTH_SCOPE`). Existing configs auto-migrate to schema version `4` and drop those legacy keys on first load; users who previously pinned custom endpoints in `config.json` should move those values to environment variables. - Add `PROJECT_REGION_MISMATCH` when a repo-local `.agora/project.json` binding points to a different region than the active login region. +- Support Android as a full quickstart template: `quickstart create`, `quickstart env write`, and `init` now bind Agora project credentials into root `local.properties` on the default Android repo branch. ## [0.2.5] - 2026-06-05 @@ -60,7 +61,6 @@ Release signing fix. ### Fixed - Update GoReleaser Cosign signing to emit `checksums.txt.sigstore.json` with `--bundle`, matching Cosign's current bundle-based signing flow. -- Remove the Android quickstart clone-only behavior so `quickstart create`, `quickstart env write`, and `init` all follow the same project-binding flow as Next.js, Python, and Go. ## [0.2.2] - 2026-05-26 diff --git a/README.md b/README.md index 5ed08ce..05bb095 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ agora init my-nextjs-demo --template nextjs | Next.js video app | `agora init my-nextjs-demo --template nextjs` | A cloned Next.js quickstart, project binding, and `.env.local` | | Python voice agent | `agora init my-python-demo --template python` | A Python quickstart with Agora credentials written for the backend | | Go token service | `agora init my-go-demo --template go` | A Go server quickstart with project metadata and env wiring | -| Android conversational AI app | `agora init my-android-demo --template android` | An Android quickstart with the Python backend env file written to `server/.env` | +| Android conversational AI app | `agora init my-android-demo --template android` | An Android quickstart with app credentials written to root `local.properties` | ## Install @@ -234,9 +234,9 @@ Quickstart template behavior: - Next.js quickstarts write `.env.local` with `NEXT_PUBLIC_AGORA_APP_ID` plus `NEXT_AGORA_APP_CERTIFICATE` - Python quickstarts copy `server/env.example` to `server/.env`, then use `APP_ID` plus `APP_CERTIFICATE` - Go quickstarts copy `server-go/env.example` to `server-go/.env`, then use `APP_ID` plus `APP_CERTIFICATE` -- Android quickstarts copy `server/env.example` to `server/.env`, then use `APP_ID` plus `APP_CERTIFICATE` +- Android quickstarts write root `local.properties` with `AGORA_APP_ID` plus `AGORA_APP_CERTIFICATE` -`project env write` auto-detects Next.js workspaces (or accepts `--template nextjs|standard`) and writes `AGORA_APP_ID` / `AGORA_APP_CERTIFICATE` or the Next.js equivalents. It does not use `APP_ID` / `APP_CERTIFICATE`; use `quickstart env write` for Python and Go quickstart layouts. +`project env write` auto-detects Next.js workspaces (or accepts `--template nextjs|standard`) and writes `AGORA_APP_ID` / `AGORA_APP_CERTIFICATE` or the Next.js equivalents. It does not use `APP_ID` / `APP_CERTIFICATE`; use `quickstart env write` for Python and Go quickstart layouts, and for Android `local.properties`. Existing `.env` and `.env.local` files are preserved: the CLI appends missing credentials, updates existing credential keys, and comments out duplicate or stale Agora credential aliases for the selected runtime. @@ -312,6 +312,7 @@ For scripts, CI, and agentic workflows: - prefer `--json` for machine consumption - set `AGORA_HOME` to an isolated temporary directory in CI or multi-agent runs - prefer `init` for end-to-end setup; decompose with lower-level commands when a workflow must be resumed in stages +- use `agora skills list` and `agora skills show ` before changing Agora integrations so agent workflows follow the current Agora skill guidance - use `agora introspect --json` and [AGENTS.md](AGENTS.md) for agent discovery; [docs/automation.md](docs/automation.md) for the JSON envelope contract Example: @@ -319,6 +320,8 @@ Example: ```bash export AGORA_HOME="$(mktemp -d)" agora init my-nextjs-demo --template nextjs --json +agora init my-android-demo --template android --project my-project --json +agora skills list --json agora quickstart create my-python-demo --template python --project my-project --json agora quickstart env write my-python-demo --json agora project doctor --json diff --git a/docs/automation.md b/docs/automation.md index ce6e8cd..0cbc4f9 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -342,12 +342,14 @@ Example: ```bash ./agora init my-nextjs-demo --template nextjs --json ./agora init my-nextjs-demo --template nextjs --new-project --json -./agora init my-android-demo --template android --json +./agora init my-android-demo --template android --project my-project --json ``` By default `init` reuses an existing project — preferring one named exactly `"Default Project"`. If no default exists, interactive sessions show existing projects with a create-new option and default to the most recently created project; JSON, CI, and non-TTY runs select the most recent project automatically. Pass `--new-project` to force creation. Use `--project ` to bind to a specific project. For deterministic automation, always pass `--project ` or `--new-project`. +Android uses the same project-binding flow as the other supported quickstarts. It runs directly from the Android app; `init`, `quickstart create --project`, and `quickstart env write --template android` write Agora credentials to root `local.properties`. + Required `data` fields: - `action` Always `init`. @@ -682,8 +684,8 @@ Required `data` fields: Env write behavior: - quickstart env files contain only the App ID and App Certificate variable names required by the template - Next.js uses `NEXT_PUBLIC_AGORA_APP_ID` and `NEXT_AGORA_APP_CERTIFICATE` -- Python, Go, and Android use `APP_ID` and `APP_CERTIFICATE` -- Android quickstarts read and write `server/env.example` and `server/.env` +- Python and Go use `APP_ID` and `APP_CERTIFICATE` +- Android quickstarts write `AGORA_APP_ID` and `AGORA_APP_CERTIFICATE` to root `local.properties` - project metadata such as project ID, project name, region, template, projectType, and env path is stored in `.agora/project.json` - existing quickstart env files are preserved; missing credential keys are appended and existing credential keys are updated - stale Agora credential aliases for another runtime are commented out to avoid ambiguous dotenv resolution; for example, a Next.js quickstart prefers `NEXT_PUBLIC_AGORA_APP_ID` and comments out old `AGORA_APP_ID` / `APP_ID` entries when replacing them diff --git a/docs/llms.txt b/docs/llms.txt index c1128f0..83c25f7 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -53,6 +53,8 @@ Check project health: agora project doctor --json - **Project webhooks**: MCP exposes `agora.project.webhook.{events,list,show,create,update,delete}` for feature-scoped webhook automation; delete requires `confirm: true`. - **When shelling out**: run commands like `agora init ... --json` and parse stdout line-by-line (progress events + final envelope). - **Capability discovery**: prefer `agora --help --all --json` or `agora introspect --json`. +- **Agora skills**: before editing Agora integrations, use `agora skills list` and `agora skills show ` to load current Agora workflow guidance. +- **Android quickstart**: `agora init --template android` uses the Android repo's default branch and writes credentials to root `local.properties`. - **Environment catalog**: use `agora env-help --json` to enumerate all supported `AGORA_*` controls. ## Headless / CI guidance @@ -78,6 +80,7 @@ agora auth status --json ### Project Initialization ``` agora init my-app --template nextjs +agora init my-android-demo --template android --project --json agora init my-app --template python --new-project --json agora init my-app --template go --project --json ``` diff --git a/internal/cli/doctor.go b/internal/cli/doctor.go index 61a3783..596a04a 100644 --- a/internal/cli/doctor.go +++ b/internal/cli/doctor.go @@ -50,6 +50,8 @@ func quickstartAppIDKey(templateID string) string { switch templateID { case "nextjs": return "NEXT_PUBLIC_AGORA_APP_ID" + case "android": + return "AGORA_APP_ID" case "python", "go": return "APP_ID" default: diff --git a/internal/cli/integration_quickstart_test.go b/internal/cli/integration_quickstart_test.go index 8142277..d8f37c2 100644 --- a/internal/cli/integration_quickstart_test.go +++ b/internal/cli/integration_quickstart_test.go @@ -35,11 +35,10 @@ func TestCLIQuickstartListAndCreate(t *testing.T) { }) androidRepo := createLocalGitRepo(t, map[string]string{ "README.md": "# Android Quickstart\n", - "settings.gradle": "rootProject.name = \"android-quickstart\"\n", + "settings.gradle.kts": "rootProject.name = \"android-quickstart\"\n", "gradlew": "#!/bin/sh\n", + "app/build.gradle.kts": "plugins { id(\"com.android.application\") }\n", "app/src/main/AndroidManifest.xml": "\n", - "server/env.example": "APP_ID=\nAPP_CERTIFICATE=\nPORT=8000\n", - "server/main.py": "print('hello from backend')\n", }) project := buildFakeProject("Project Alpha", "prj_123456", "app_123456", "global") @@ -192,8 +191,8 @@ func TestCLIQuickstartListAndCreate(t *testing.T) { if createAndroidUnbound.exitCode != 0 || !strings.Contains(createAndroidUnbound.stdout, `"envStatus":"template-only"`) { t.Fatalf("unexpected unbound android quickstart create result: %+v", createAndroidUnbound) } - if _, err := os.Stat(filepath.Join(androidUnboundTarget, "server", ".env")); !errors.Is(err, os.ErrNotExist) { - t.Fatalf("did not expect backend env in unbound android scaffold, got %v", err) + if _, err := os.Stat(filepath.Join(androidUnboundTarget, "local.properties")); !errors.Is(err, os.ErrNotExist) { + t.Fatalf("did not expect local.properties in unbound android scaffold, got %v", err) } androidBoundTarget := filepath.Join(rootDir, "android-demo") @@ -209,12 +208,12 @@ func TestCLIQuickstartListAndCreate(t *testing.T) { if createAndroidBound.exitCode != 0 || !strings.Contains(createAndroidBound.stdout, `"envStatus":"configured"`) || !strings.Contains(createAndroidBound.stdout, `"projectId":"prj_123456"`) { t.Fatalf("unexpected bound android quickstart create result: %+v", createAndroidBound) } - androidEnv, err := os.ReadFile(filepath.Join(androidBoundTarget, "server", ".env")) + androidEnv, err := os.ReadFile(filepath.Join(androidBoundTarget, "local.properties")) if err != nil { - t.Fatalf("expected android backend .env in bound scaffold: %v", err) + t.Fatalf("expected android local.properties in bound scaffold: %v", err) } - if !strings.Contains(string(androidEnv), "APP_ID=app_123456") || !strings.Contains(string(androidEnv), "APP_CERTIFICATE=") || !strings.Contains(string(androidEnv), "PORT=8000") { - t.Fatalf("unexpected android backend env contents: %s", string(androidEnv)) + if !strings.Contains(string(androidEnv), "AGORA_APP_ID=app_123456") || !strings.Contains(string(androidEnv), "AGORA_APP_CERTIFICATE=") { + t.Fatalf("unexpected android local.properties contents: %s", string(androidEnv)) } androidMetadata, err := os.ReadFile(filepath.Join(androidBoundTarget, ".agora", "project.json")) if err != nil { @@ -235,12 +234,12 @@ func TestCLIQuickstartListAndCreate(t *testing.T) { if writeAndroidEnv.exitCode != 0 || !strings.Contains(writeAndroidEnv.stdout, `"template":"android"`) { t.Fatalf("unexpected android quickstart env write result: %+v", writeAndroidEnv) } - androidEnvAfterWrite, err := os.ReadFile(filepath.Join(androidBoundTarget, "server", ".env")) + androidEnvAfterWrite, err := os.ReadFile(filepath.Join(androidBoundTarget, "local.properties")) if err != nil { - t.Fatalf("expected android backend .env after env write: %v", err) + t.Fatalf("expected android local.properties after env write: %v", err) } - if !strings.Contains(string(androidEnvAfterWrite), "APP_ID=app_123456") || !strings.Contains(string(androidEnvAfterWrite), "APP_CERTIFICATE=") { - t.Fatalf("unexpected android backend env after env write: %s", string(androidEnvAfterWrite)) + if !strings.Contains(string(androidEnvAfterWrite), "AGORA_APP_ID=app_123456") || !strings.Contains(string(androidEnvAfterWrite), "AGORA_APP_CERTIFICATE=") { + t.Fatalf("unexpected android local.properties after env write: %s", string(androidEnvAfterWrite)) } initAndroid := runCLI(t, []string{"init", "android-init-demo", "--template", "android", "--project", "prj_123456", "--json"}, cliRunOptions{ @@ -252,7 +251,7 @@ func TestCLIQuickstartListAndCreate(t *testing.T) { }, workdir: rootDir, }) - if initAndroid.exitCode != 0 || !strings.Contains(initAndroid.stdout, `"template":"android"`) || !strings.Contains(initAndroid.stdout, `"envPath":"server/.env"`) { + if initAndroid.exitCode != 0 || !strings.Contains(initAndroid.stdout, `"template":"android"`) || !strings.Contains(initAndroid.stdout, `"envPath":"local.properties"`) { t.Fatalf("unexpected android init result: %+v", initAndroid) } diff --git a/internal/cli/quickstart.go b/internal/cli/quickstart.go index 1e77bec..32a354d 100644 --- a/internal/cli/quickstart.go +++ b/internal/cli/quickstart.go @@ -99,12 +99,11 @@ func quickstartTemplates() []quickstartTemplate { Runtime: "android", RepoURL: "https://github.com/AgoraIO-Conversational-AI/agent-quickstart-android", DocsURL: "https://github.com/AgoraIO-Conversational-AI/agent-quickstart-android/tree/main", - DetectPaths: []string{"settings.gradle", "gradlew", "server/env.example"}, - EnvExamplePath: "server/env.example", - EnvTargetPath: "server/.env", - InstallCommand: "Install Python backend dependencies and Android Studio", - RunCommand: "Run the Python backend, then launch the Android app", - EnvDocsSummary: "Copies server/env.example to server/.env, then writes APP_ID and APP_CERTIFICATE for the Python backend.", + DetectPaths: []string{"settings.gradle.kts", "settings.gradle", "app/build.gradle.kts", "gradlew"}, + EnvTargetPath: "local.properties", + InstallCommand: "Install Android Studio dependencies or run ./gradlew assembleDebug", + RunCommand: "Open the project in Android Studio and run the app, or use ./gradlew installDebug", + EnvDocsSummary: "Writes AGORA_APP_ID and AGORA_APP_CERTIFICATE to local.properties for the Android app.", SupportsInit: true, Available: true, }, @@ -206,6 +205,7 @@ If a current project context exists, or if --project is passed, the CLI also wri agora quickstart create my-nextjs-demo --template nextjs agora quickstart create my-python-demo --template python --project my-agent-demo agora quickstart create my-go-demo --template go --project my-agent-demo + agora quickstart create my-android-demo --template android --project my-agent-demo agora quickstart create demo --template nextjs --dir apps/demo `), RunE: func(cmd *cobra.Command, args []string) error { @@ -268,7 +268,8 @@ The CLI can infer the quickstart type from the repository layout, or you can for Long: `Write the runtime-specific env file expected by a cloned quickstart repository. Next.js quickstarts receive NEXT_PUBLIC_* client env vars plus server-only Agora credentials. -Python, Go, and Android quickstarts receive backend APP_ID and APP_CERTIFICATE values.`, +Python and Go quickstarts receive backend APP_ID and APP_CERTIFICATE values. +Android quickstarts receive AGORA_APP_ID and AGORA_APP_CERTIFICATE in local.properties.`, Example: example(` agora quickstart env write agora quickstart env write apps/my-nextjs-demo @@ -325,9 +326,6 @@ func (a *App) quickstartCreate(template quickstartTemplate, targetDir, explicitP return nil, err } effectiveRef := strings.TrimSpace(ref) - if effectiveRef == "" { - effectiveRef = strings.TrimSpace(template.Ref) - } if overrideKey != "" { progress.emit("clone:override", fmt.Sprintf("Using repo override from %s", overrideKey), map[string]any{"repoUrl": repoURL, "envVar": overrideKey}) } @@ -684,7 +682,9 @@ func conflictingQuickstartEnvKeys(templateID string) []string { switch templateID { case "nextjs": return []string{"AGORA_APP_ID", "AGORA_APP_CERTIFICATE", "APP_ID", "APP_CERTIFICATE"} - case "android", "python", "go": + case "android": + return []string{"APP_ID", "APP_CERTIFICATE", "NEXT_PUBLIC_AGORA_APP_ID", "NEXT_AGORA_APP_CERTIFICATE"} + case "python", "go": return []string{"AGORA_APP_ID", "AGORA_APP_CERTIFICATE", "NEXT_PUBLIC_AGORA_APP_ID", "NEXT_AGORA_APP_CERTIFICATE"} default: return nil @@ -698,7 +698,12 @@ func renderQuickstartEnvValues(template quickstartTemplate, project projectDetai "NEXT_PUBLIC_AGORA_APP_ID": project.AppID, "NEXT_AGORA_APP_CERTIFICATE": *project.SignKey, } - case "android", "python", "go": + case "android": + return map[string]any{ + "AGORA_APP_ID": project.AppID, + "AGORA_APP_CERTIFICATE": *project.SignKey, + } + case "python", "go": return map[string]any{ "APP_ID": project.AppID, "APP_CERTIFICATE": *project.SignKey, From 62d54b11308faed0f30e693805407c6e6134c5f0 Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Tue, 21 Jul 2026 14:20:54 +0530 Subject: [PATCH 5/5] Fix CI formatting and Go vuln check --- CHANGELOG.md | 1 + go.mod | 2 +- internal/cli/quickstart_test.go | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 223b351..376a6fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Earlier entries pre-date this convention and only carry their version's compare - **BREAKING**: Stop persisting CLI API/OAuth integration values in `config.json`. `apiBaseUrl`, `oauthBaseUrl`, `oauthClientId`, and `oauthScope` are now derived from the selected login region or from explicit environment variable overrides (`AGORA_API_BASE_URL`, `AGORA_OAUTH_BASE_URL`, `AGORA_OAUTH_CLIENT_ID`, `AGORA_OAUTH_SCOPE`). Existing configs auto-migrate to schema version `4` and drop those legacy keys on first load; users who previously pinned custom endpoints in `config.json` should move those values to environment variables. - Add `PROJECT_REGION_MISMATCH` when a repo-local `.agora/project.json` binding points to a different region than the active login region. - Support Android as a full quickstart template: `quickstart create`, `quickstart env write`, and `init` now bind Agora project credentials into root `local.properties` on the default Android repo branch. +- Bump the pinned Go toolchain to 1.26.5 so govulncheck picks up the latest `crypto/tls` and `os` standard-library security fixes. ## [0.2.5] - 2026-06-05 diff --git a/go.mod b/go.mod index 285a358..e024fdc 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/AgoraIO/cli -go 1.26.4 +go 1.26.5 require ( github.com/spf13/cobra v1.10.2 diff --git a/internal/cli/quickstart_test.go b/internal/cli/quickstart_test.go index ce179a0..80f41de 100644 --- a/internal/cli/quickstart_test.go +++ b/internal/cli/quickstart_test.go @@ -210,4 +210,4 @@ func TestQuickstartDocsURLForRegion(t *testing.T) { if got := quickstartDocsURL(tmpl, regionCN); got != tmpl.DocsURLCN { t.Fatalf("cn docs url = %q, want %q", got, tmpl.DocsURLCN) } -} \ No newline at end of file +}