Skip to content

feat(rename): let the user choose the app's domain (--domain) - #158

Merged
dadachi merged 1 commit into
mainfrom
feat/custom-domain
Oct 18, 2026
Merged

dadachi merged 1 commit into
mainfrom
feat/custom-domain

Conversation

@dadachi

@dadachi dadachi commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Closes #157

What

--domain=<domain> (CLI), domain (MCP generate_app), and "domain is …" in the generate-app skill. Without it, nothing changes: rename.rb gets no appDomain and produces byte-for-byte today's output (<flat product>.com, com.<flat>).

With --domain=orangebaton.app (project Orange Baton):

Template Generated
nativeapptemplate.com, api.nativeapptemplate.com, support@nativeapptemplate.com, URLs orangebaton.app, api.orangebaton.app, support@orangebaton.app
iOS com.nativeapptemplate.NativeAppTemplate.ios (PRODUCT_BUNDLE_IDENTIFIER), …NativeAppTemplateTests, keychain services, log subsystem com.nativeapptemplate app.orangebaton.OrangeBaton.ios, app.orangebaton.OrangeBatonTests, app.orangebaton.OrangeBaton.…, app.orangebaton
Android com.nativeapptemplate.nativeapptemplate: applicationId, namespace (app, model), package/import, proto java_package, google-services.json package_name app.orangebaton.orangebaton
Android dirs com/nativeapptemplate/nativeapptemplate (app main/test/androidTest, model main/test, …) and the slash form in tests (File("src/main/kotlin/com/…")) app/orangebaton/orangebaton
API devices_controller_test.rb bundle ids (com.nativeapptemplate.example, com.nativeapptemplate.nativeapptemplate) app.orangebaton.example, app.orangebaton.orangebaton

Links to the template's repos and nativeapptemplate-agent (#155/#156) are still masked and untouched. The API's APNs topic lives in its credentials, not in files; the output checklist says to set it.

How

  • src/app-domain.ts: validation, the Java/Kotlin segment mapping, derived prefixes, and reading the bundle ID / applicationId back from the generated projects.
  • scripts/ruby/rename.rb: an optional appDomain adds four patterns ahead of every product pattern, in the same single-pass combined regex: nativeapptemplate.com, com.nativeapptemplate before .nativeapptemplate… (Android, Java-safe prefix), its slash form, and any other com.nativeapptemplate (iOS-style prefix, hyphens kept). What follows the prefix is renamed by the usual rules, and replaced text is never scanned again. A new Pass 1.5 moves <root>/com/nativeapptemplate under the prefix before Pass 2. It removes the emptied com/ (a lone .DS_Store doesn't count) and pins the new directories, so Pass 2 leaves a label like shop in shop.example alone.
  • Report: meta.appDomain (domain, custom, prefixes, remapped labels, the iOS bundle ID and Android applicationId read back from the output). The HTML header shows the domain, and the Domain plan section has an "App identifiers" card.

Validation

Lowercased. Needs ≥2 labels, each a DNS label (1–63 [a-z0-9-], no leading or trailing hyphen) and ≤253 chars total. Rejects a scheme, path, port or email, an all-numeric top label (IP), and a label starting with a digit. I checked the digit case: the JLS's _ prefix gives app._1orange.orangebaton, which aapt2 rejects (attribute 'package' in <manifest> tag is not a valid Android package name). In the Android package only, - becomes _ and Java keywords/literals plus Kotlin hard keywords get a _ suffix. iOS keeps hyphens. The CLI exits 2 with the message, and MCP returns isError with the same message.

Tests

Unit tests cover validation, the segment mapping, appIdentifiers, the read-back helpers, parseArgs, MCP (invalid and valid), and the report/HTML. Rename fixture tests cover: default unchanged (and --domain=<default>.com gives an identical tree); the custom domain for URLs, subdomains, emails, iOS, Android, directories, and the protected link (Layer 1 clean); hyphen mapping; and a domain label that is a rename token. I broke the code six ways (no pin, domain patterns last, no dir move, no keyword/digit rule, no hyphen mapping, no .DS_Store handling) and each time the matching tests failed.

Sanity check (real paid substrates, rsync'd to a temp dir, Orange Baton plan + --domain=orangebaton.app)

  • 0 nativeapptemplate.com, 0 com.orangebaton / com/orangebaton, 0 com.nativeapptemplate, no com/ dirs left; Layer 1 PASS ×3.
  • ./gradlew --no-daemon assembleDebug: BUILD SUCCESSFUL (processDebugGoogleServices included, since package_name follows the applicationId). The APK package is app.orangebaton.orangebaton.
  • xcodebuild build (generic iOS Simulator): BUILD SUCCEEDED. CFBundleIdentifier is app.orangebaton.OrangeBaton.ios plus the template's SAMPLE_CODE_DISAMBIGUATOR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UZJxUoubntzu7Ec7DPVfAr

--domain=<domain> on the CLI, `domain` on the MCP generate_app tool and
"domain is ..." in the generate-app skill. It replaces the template's
nativeapptemplate.com (bare, subdomains, URLs, emails) and, reversed,
the com.nativeapptemplate prefix: the iOS bundle ID and every other
com.nativeapptemplate id (app.orangebaton.OrangeBaton.ios, test target,
keychain services, log subsystem) and the Android package
(app.orangebaton.orangebaton: applicationId, namespace, package/import
declarations, proto java_package, google-services.json package_name, the
API's FCM test), with the source directories moved from
com/nativeapptemplate/<product> to the new prefix in every module and
source set.

rename.rb takes an optional appDomain and matches these whole, ahead of
the product-name patterns, in the same single pass, so the product rename
still handles what follows the prefix and never re-touches the replaced
text. The package directories are moved before Pass 2 and pinned, so a
domain label that is a rename token (shop.example) stays. Without
--domain nothing is added: the rename is byte-for-byte today's.

The domain is lowercased and must have at least two DNS labels, each
starting with a letter (aapt2 rejects a package segment like _1password)
and not all-numeric at the top. In the Android package a hyphen becomes
_ and a Java or Kotlin keyword gets a trailing _; the run reports such
labels. report.json meta.appDomain and the HTML report record the domain
and the bundle ID / applicationId read back from the generated projects.

Closes #157

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZJxUoubntzu7Ec7DPVfAr
@dadachi
dadachi merged commit c03d5b0 into main Oct 18, 2026
1 check passed
@dadachi
dadachi deleted the feat/custom-domain branch October 18, 2026 01:17
@dadachi dadachi mentioned this pull request Oct 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Let the user choose the domain (and derive bundle / application IDs from it)

1 participant