biucingcli 0.9.1__py3-none-any.whl
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.
- biucingcli/__init__.py +5 -0
- biucingcli/cli.py +849 -0
- biucingcli/template_data/android/template/.editorconfig +9 -0
- biucingcli/template_data/android/template/.gitignore +26 -0
- biucingcli/template_data/android/template/.mise.toml +3 -0
- biucingcli/template_data/android/template/Brewfile +6 -0
- biucingcli/template_data/android/template/Makefile +103 -0
- biucingcli/template_data/android/template/README.md +163 -0
- biucingcli/template_data/android/template/app/build.gradle.kts +178 -0
- biucingcli/template_data/android/template/app/proguard-rules.pro +1 -0
- biucingcli/template_data/android/template/app/src/androidTest/java/app/HomeRouteUiTest.kt +21 -0
- biucingcli/template_data/android/template/app/src/main/AndroidManifest.xml +18 -0
- biucingcli/template_data/android/template/app/src/main/java/app/MainActivity.kt +36 -0
- biucingcli/template_data/android/template/app/src/main/res/values/strings.xml +4 -0
- biucingcli/template_data/android/template/app/src/main/res/values/themes.xml +8 -0
- biucingcli/template_data/android/template/app/src/test/java/app/AppSmokeTest.kt +19 -0
- biucingcli/template_data/android/template/build.gradle.kts +27 -0
- biucingcli/template_data/android/template/core/designsystem/build.gradle.kts +33 -0
- biucingcli/template_data/android/template/core/designsystem/src/main/java/designsystem/Components.kt +75 -0
- biucingcli/template_data/android/template/core/designsystem/src/main/java/designsystem/DesignTokens.kt +23 -0
- biucingcli/template_data/android/template/core/designsystem/src/main/java/designsystem/Theme.kt +110 -0
- biucingcli/template_data/android/template/core/model/build.gradle.kts +26 -0
- biucingcli/template_data/android/template/core/model/src/main/java/model/Greeting.kt +6 -0
- biucingcli/template_data/android/template/core/model/src/test/java/model/GreetingTest.kt +16 -0
- biucingcli/template_data/android/template/core/network/build.gradle.kts +26 -0
- biucingcli/template_data/android/template/core/network/src/main/java/network/AppEnvironment.kt +17 -0
- biucingcli/template_data/android/template/core/testing/build.gradle.kts +27 -0
- biucingcli/template_data/android/template/core/testing/src/main/java/testing/FakeAppEnvironmentProvider.kt +13 -0
- biucingcli/template_data/android/template/docs/release-delivery.md +68 -0
- biucingcli/template_data/android/template/docs/release-signing.properties.example +20 -0
- biucingcli/template_data/android/template/fastlane/.env.example +22 -0
- biucingcli/template_data/android/template/fastlane/Appfile +1 -0
- biucingcli/template_data/android/template/fastlane/Fastfile +121 -0
- biucingcli/template_data/android/template/feature/home/build.gradle.kts +35 -0
- biucingcli/template_data/android/template/feature/home/src/main/java/home/HomeRoute.kt +38 -0
- biucingcli/template_data/android/template/feature/settings/build.gradle.kts +34 -0
- biucingcli/template_data/android/template/feature/settings/src/main/java/settings/SettingsRoute.kt +35 -0
- biucingcli/template_data/android/template/gradle/libs.versions.toml +35 -0
- biucingcli/template_data/android/template/gradle/verification-metadata.xml +3462 -0
- biucingcli/template_data/android/template/gradle/wrapper/gradle-wrapper.jar +0 -0
- biucingcli/template_data/android/template/gradle/wrapper/gradle-wrapper.jar.sha256 +1 -0
- biucingcli/template_data/android/template/gradle/wrapper/gradle-wrapper.properties +10 -0
- biucingcli/template_data/android/template/gradle.properties +4 -0
- biucingcli/template_data/android/template/gradlew +252 -0
- biucingcli/template_data/android/template/gradlew.bat +94 -0
- biucingcli/template_data/android/template/scripts/artifact-info +11 -0
- biucingcli/template_data/android/template/scripts/bootstrap +17 -0
- biucingcli/template_data/android/template/scripts/doctor +133 -0
- biucingcli/template_data/android/template/scripts/setup-android-sdk +23 -0
- biucingcli/template_data/android/template/scripts/sync-gradle-wrapper +19 -0
- biucingcli/template_data/android/template/scripts/verify-gradle-supply-chain +61 -0
- biucingcli/template_data/android/template/scripts/verify-release-artifact +104 -0
- biucingcli/template_data/android/template/settings.gradle.kts +25 -0
- biucingcli/template_data/android/template.json +146 -0
- biucingcli/template_data/apple/template/.gitignore +28 -0
- biucingcli/template_data/apple/template/.mise.toml +4 -0
- biucingcli/template_data/apple/template/.swiftformat +10 -0
- biucingcli/template_data/apple/template/.swiftlint.yml +33 -0
- biucingcli/template_data/apple/template/App/Config/ProjectDescriptionHelpers/Module.swift +6 -0
- biucingcli/template_data/apple/template/App/Config/XCConfig/Base.xcconfig +4 -0
- biucingcli/template_data/apple/template/App/Project.swift +66 -0
- biucingcli/template_data/apple/template/App/Targets/App/Resources/.gitkeep +1 -0
- biucingcli/template_data/apple/template/App/Targets/App/Sources/AppEntry.swift +9 -0
- biucingcli/template_data/apple/template/App/Targets/App/Sources/HomeView.swift +18 -0
- biucingcli/template_data/apple/template/App/Targets/App/Sources/HomeViewModel.swift +38 -0
- biucingcli/template_data/apple/template/App/Targets/AppTests/Sources/AppTests.swift +59 -0
- biucingcli/template_data/apple/template/Brewfile +8 -0
- biucingcli/template_data/apple/template/Makefile +128 -0
- biucingcli/template_data/apple/template/Packages/AppServices/Package.swift +20 -0
- biucingcli/template_data/apple/template/Packages/AppServices/Sources/AppServices/StarterMetadata.swift +52 -0
- biucingcli/template_data/apple/template/Packages/DesignSystem/Package.swift +20 -0
- biucingcli/template_data/apple/template/Packages/DesignSystem/Sources/DesignSystem/Theme.swift +8 -0
- biucingcli/template_data/apple/template/README.md +133 -0
- biucingcli/template_data/apple/template/Tuist.swift +8 -0
- biucingcli/template_data/apple/template/Workspace.swift +11 -0
- biucingcli/template_data/apple/template/docs/release-delivery.md +55 -0
- biucingcli/template_data/apple/template/fastlane/.env.example +20 -0
- biucingcli/template_data/apple/template/fastlane/Appfile +3 -0
- biucingcli/template_data/apple/template/fastlane/Fastfile +126 -0
- biucingcli/template_data/apple/template/fastlane/Matchfile +7 -0
- biucingcli/template_data/apple/template/scripts/bootstrap +9 -0
- biucingcli/template_data/apple/template/scripts/doctor +163 -0
- biucingcli/template_data/apple/template/scripts/setup-xcode +6 -0
- biucingcli/template_data/apple/template/scripts/test-destination +42 -0
- biucingcli/template_data/apple/template/scripts/verify-release-identity +88 -0
- biucingcli/template_data/apple/template.json +110 -0
- biucingcli/template_data/frontend/template/.dockerignore +8 -0
- biucingcli/template_data/frontend/template/.env.example +1 -0
- biucingcli/template_data/frontend/template/.gitignore +7 -0
- biucingcli/template_data/frontend/template/.npmrc +1 -0
- biucingcli/template_data/frontend/template/.prettierignore +2 -0
- biucingcli/template_data/frontend/template/.prettierrc.json +4 -0
- biucingcli/template_data/frontend/template/Dockerfile +25 -0
- biucingcli/template_data/frontend/template/Dockerfile.dev +35 -0
- biucingcli/template_data/frontend/template/Dockerfile.dev.full +40 -0
- biucingcli/template_data/frontend/template/Makefile +137 -0
- biucingcli/template_data/frontend/template/README.md +146 -0
- biucingcli/template_data/frontend/template/compose.dev.yaml +29 -0
- biucingcli/template_data/frontend/template/eslint.config.js +27 -0
- biucingcli/template_data/frontend/template/index.html +12 -0
- biucingcli/template_data/frontend/template/nginx.conf +11 -0
- biucingcli/template_data/frontend/template/package.json +50 -0
- biucingcli/template_data/frontend/template/playwright.production.config.ts +21 -0
- biucingcli/template_data/frontend/template/playwright.smoke.config.ts +19 -0
- biucingcli/template_data/frontend/template/pnpm-lock.yaml +4054 -0
- biucingcli/template_data/frontend/template/public/.gitkeep +1 -0
- biucingcli/template_data/frontend/template/scripts/browser-smoke-production +55 -0
- biucingcli/template_data/frontend/template/scripts/doctor +46 -0
- biucingcli/template_data/frontend/template/src/App.test.tsx +18 -0
- biucingcli/template_data/frontend/template/src/App.tsx +5 -0
- biucingcli/template_data/frontend/template/src/components/MetricCard.tsx +14 -0
- biucingcli/template_data/frontend/template/src/components/TaskPanel.tsx +32 -0
- biucingcli/template_data/frontend/template/src/config/env.ts +3 -0
- biucingcli/template_data/frontend/template/src/hooks/useProjectOverview.ts +24 -0
- biucingcli/template_data/frontend/template/src/index.css +208 -0
- biucingcli/template_data/frontend/template/src/main.tsx +11 -0
- biucingcli/template_data/frontend/template/src/pages/HomePage.tsx +60 -0
- biucingcli/template_data/frontend/template/src/router/AppRouter.tsx +14 -0
- biucingcli/template_data/frontend/template/src/services/api/client.ts +33 -0
- biucingcli/template_data/frontend/template/src/services/projectOverview.ts +39 -0
- biucingcli/template_data/frontend/template/src/test/setup.ts +1 -0
- biucingcli/template_data/frontend/template/src/types/projectOverview.ts +22 -0
- biucingcli/template_data/frontend/template/src/vite-env.d.ts +9 -0
- biucingcli/template_data/frontend/template/tests/browser-smoke.spec.ts +16 -0
- biucingcli/template_data/frontend/template/tests/production-browser-smoke.spec.ts +22 -0
- biucingcli/template_data/frontend/template/tsconfig.json +21 -0
- biucingcli/template_data/frontend/template/vite.config.ts +12 -0
- biucingcli/template_data/frontend/template.json +81 -0
- biucingcli/template_data/harmonyos/template/.gitignore +21 -0
- biucingcli/template_data/harmonyos/template/.mise.toml +2 -0
- biucingcli/template_data/harmonyos/template/AppScope/app.json5 +10 -0
- biucingcli/template_data/harmonyos/template/AppScope/resources/base/element/string.json +8 -0
- biucingcli/template_data/harmonyos/template/AppScope/resources/base/media/background.svg +3 -0
- biucingcli/template_data/harmonyos/template/AppScope/resources/base/media/foreground.svg +4 -0
- biucingcli/template_data/harmonyos/template/AppScope/resources/base/media/layered_image.json +6 -0
- biucingcli/template_data/harmonyos/template/Makefile +106 -0
- biucingcli/template_data/harmonyos/template/README.md +152 -0
- biucingcli/template_data/harmonyos/template/build-profile.json5 +36 -0
- biucingcli/template_data/harmonyos/template/code-linter.json5 +12 -0
- biucingcli/template_data/harmonyos/template/docs/release-signing.local.properties.example +14 -0
- biucingcli/template_data/harmonyos/template/entry/build-profile.json5 +9 -0
- biucingcli/template_data/harmonyos/template/entry/hvigorfile.ts +6 -0
- biucingcli/template_data/harmonyos/template/entry/oh-package.json5 +9 -0
- biucingcli/template_data/harmonyos/template/entry/src/main/ets/core/config/AppConfig.ets +10 -0
- biucingcli/template_data/harmonyos/template/entry/src/main/ets/core/designsystem/Tokens.ets +28 -0
- biucingcli/template_data/harmonyos/template/entry/src/main/ets/entryability/EntryAbility.ets +29 -0
- biucingcli/template_data/harmonyos/template/entry/src/main/ets/pages/Index.ets +47 -0
- biucingcli/template_data/harmonyos/template/entry/src/main/ets/pages/Settings.ets +96 -0
- biucingcli/template_data/harmonyos/template/entry/src/main/module.json5 +34 -0
- biucingcli/template_data/harmonyos/template/entry/src/main/resources/base/element/color.json +24 -0
- biucingcli/template_data/harmonyos/template/entry/src/main/resources/base/element/string.json +12 -0
- biucingcli/template_data/harmonyos/template/entry/src/main/resources/base/media/app_icon.svg +5 -0
- biucingcli/template_data/harmonyos/template/entry/src/main/resources/base/profile/main_pages.json +6 -0
- biucingcli/template_data/harmonyos/template/entry/src/test/List.test.ets +11 -0
- biucingcli/template_data/harmonyos/template/hvigor/hvigor-config.json5 +8 -0
- biucingcli/template_data/harmonyos/template/hvigorfile.ts +6 -0
- biucingcli/template_data/harmonyos/template/oh-package-lock.json5 +19 -0
- biucingcli/template_data/harmonyos/template/oh-package.json5 +13 -0
- biucingcli/template_data/harmonyos/template/scripts/artifact-info +31 -0
- biucingcli/template_data/harmonyos/template/scripts/bootstrap +38 -0
- biucingcli/template_data/harmonyos/template/scripts/doctor +147 -0
- biucingcli/template_data/harmonyos/template/scripts/lint +105 -0
- biucingcli/template_data/harmonyos/template/scripts/release-build +199 -0
- biucingcli/template_data/harmonyos/template/scripts/release-preflight +109 -0
- biucingcli/template_data/harmonyos/template/scripts/test +28 -0
- biucingcli/template_data/harmonyos/template/scripts/verify-release-artifact +173 -0
- biucingcli/template_data/harmonyos/template/scripts/verify-supply-chain +70 -0
- biucingcli/template_data/harmonyos/template.json +134 -0
- biucingcli/template_data/microservice/template/.air.toml +11 -0
- biucingcli/template_data/microservice/template/.dockerignore +5 -0
- biucingcli/template_data/microservice/template/.gitignore +4 -0
- biucingcli/template_data/microservice/template/.mise.toml +3 -0
- biucingcli/template_data/microservice/template/Brewfile +7 -0
- biucingcli/template_data/microservice/template/Dockerfile +44 -0
- biucingcli/template_data/microservice/template/Dockerfile.dev +50 -0
- biucingcli/template_data/microservice/template/Makefile +145 -0
- biucingcli/template_data/microservice/template/README.md +191 -0
- biucingcli/template_data/microservice/template/api/buf.gen.yaml +11 -0
- biucingcli/template_data/microservice/template/api/buf.yaml +11 -0
- biucingcli/template_data/microservice/template/api/proto/service/v1/service.proto +18 -0
- biucingcli/template_data/microservice/template/cmd/server/main.go +81 -0
- biucingcli/template_data/microservice/template/compose.dev.yaml +49 -0
- biucingcli/template_data/microservice/template/configs/config.yaml +18 -0
- biucingcli/template_data/microservice/template/deploy/compose.yaml +35 -0
- biucingcli/template_data/microservice/template/deploy/otel-collector.yaml +15 -0
- biucingcli/template_data/microservice/template/go.mod +53 -0
- biucingcli/template_data/microservice/template/go.sum +138 -0
- biucingcli/template_data/microservice/template/internal/config/config.go +112 -0
- biucingcli/template_data/microservice/template/internal/config/config_test.go +92 -0
- biucingcli/template_data/microservice/template/internal/handler/health.go +25 -0
- biucingcli/template_data/microservice/template/internal/handler/ping.go +25 -0
- biucingcli/template_data/microservice/template/internal/model/health.go +8 -0
- biucingcli/template_data/microservice/template/internal/model/ping.go +7 -0
- biucingcli/template_data/microservice/template/internal/repository/.gitkeep +1 -0
- biucingcli/template_data/microservice/template/internal/router/router.go +26 -0
- biucingcli/template_data/microservice/template/internal/runtime/server.go +116 -0
- biucingcli/template_data/microservice/template/internal/runtime/server_test.go +161 -0
- biucingcli/template_data/microservice/template/internal/service/health.go +26 -0
- biucingcli/template_data/microservice/template/internal/service/ping.go +23 -0
- biucingcli/template_data/microservice/template/internal/telemetry/telemetry.go +56 -0
- biucingcli/template_data/microservice/template/internal/transport/grpc.go +51 -0
- biucingcli/template_data/microservice/template/internal/transport/ping.go +28 -0
- biucingcli/template_data/microservice/template/scripts/bootstrap +29 -0
- biucingcli/template_data/microservice/template/scripts/doctor +115 -0
- biucingcli/template_data/microservice/template/tests/server_test.go +153 -0
- biucingcli/template_data/microservice/template.json +114 -0
- biucingcli/template_data/web-service/template/.air.toml +11 -0
- biucingcli/template_data/web-service/template/.dockerignore +5 -0
- biucingcli/template_data/web-service/template/.gitignore +5 -0
- biucingcli/template_data/web-service/template/.mise.toml +2 -0
- biucingcli/template_data/web-service/template/Brewfile +5 -0
- biucingcli/template_data/web-service/template/Dockerfile +41 -0
- biucingcli/template_data/web-service/template/Dockerfile.dev +45 -0
- biucingcli/template_data/web-service/template/Makefile +113 -0
- biucingcli/template_data/web-service/template/README.md +164 -0
- biucingcli/template_data/web-service/template/cmd/server/main.go +50 -0
- biucingcli/template_data/web-service/template/compose.dev.yaml +28 -0
- biucingcli/template_data/web-service/template/compose.yaml +14 -0
- biucingcli/template_data/web-service/template/configs/config.yaml +10 -0
- biucingcli/template_data/web-service/template/go.mod +36 -0
- biucingcli/template_data/web-service/template/go.sum +89 -0
- biucingcli/template_data/web-service/template/internal/config/config.go +84 -0
- biucingcli/template_data/web-service/template/internal/config/config_test.go +88 -0
- biucingcli/template_data/web-service/template/internal/handler/health.go +25 -0
- biucingcli/template_data/web-service/template/internal/handler/ping.go +25 -0
- biucingcli/template_data/web-service/template/internal/handler/user.go +40 -0
- biucingcli/template_data/web-service/template/internal/model/health.go +6 -0
- biucingcli/template_data/web-service/template/internal/model/ping.go +7 -0
- biucingcli/template_data/web-service/template/internal/model/user.go +7 -0
- biucingcli/template_data/web-service/template/internal/repository/.gitkeep +1 -0
- biucingcli/template_data/web-service/template/internal/repository/user.go +37 -0
- biucingcli/template_data/web-service/template/internal/router/router.go +28 -0
- biucingcli/template_data/web-service/template/internal/runtime/server.go +76 -0
- biucingcli/template_data/web-service/template/internal/runtime/server_test.go +139 -0
- biucingcli/template_data/web-service/template/internal/service/health.go +22 -0
- biucingcli/template_data/web-service/template/internal/service/ping.go +23 -0
- biucingcli/template_data/web-service/template/internal/service/user.go +27 -0
- biucingcli/template_data/web-service/template/scripts/bootstrap +23 -0
- biucingcli/template_data/web-service/template/scripts/doctor +83 -0
- biucingcli/template_data/web-service/template/tests/server_test.go +160 -0
- biucingcli/template_data/web-service/template.json +88 -0
- biucingcli/template_data/worker/template/.dockerignore +3 -0
- biucingcli/template_data/worker/template/.gitignore +2 -0
- biucingcli/template_data/worker/template/.mise.toml +2 -0
- biucingcli/template_data/worker/template/Brewfile +1 -0
- biucingcli/template_data/worker/template/Dockerfile +19 -0
- biucingcli/template_data/worker/template/Dockerfile.dev +9 -0
- biucingcli/template_data/worker/template/Makefile +105 -0
- biucingcli/template_data/worker/template/README.md +164 -0
- biucingcli/template_data/worker/template/cmd/worker/main.go +41 -0
- biucingcli/template_data/worker/template/compose.dev.yaml +29 -0
- biucingcli/template_data/worker/template/configs/config.json +11 -0
- biucingcli/template_data/worker/template/go.mod +3 -0
- biucingcli/template_data/worker/template/go.sum +1 -0
- biucingcli/template_data/worker/template/internal/config/config.go +108 -0
- biucingcli/template_data/worker/template/internal/config/config_test.go +86 -0
- biucingcli/template_data/worker/template/internal/runtime/runner.go +199 -0
- biucingcli/template_data/worker/template/internal/runtime/runner_test.go +215 -0
- biucingcli/template_data/worker/template/internal/task/heartbeat.go +19 -0
- biucingcli/template_data/worker/template/scripts/bootstrap +6 -0
- biucingcli/template_data/worker/template/scripts/doctor +41 -0
- biucingcli/template_data/worker/template/tests/worker_test.go +44 -0
- biucingcli/template_data/worker/template.json +100 -0
- biucingcli/templates.py +902 -0
- biucingcli-0.9.1.dist-info/METADATA +204 -0
- biucingcli-0.9.1.dist-info/RECORD +269 -0
- biucingcli-0.9.1.dist-info/WHEEL +5 -0
- biucingcli-0.9.1.dist-info/entry_points.txt +2 -0
- biucingcli-0.9.1.dist-info/top_level.txt +1 -0
biucingcli/cli.py
ADDED
|
@@ -0,0 +1,849 @@
|
|
|
1
|
+
"""Command entry point for BiucingCLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from biucingcli import __version__
|
|
10
|
+
from biucingcli.templates import (
|
|
11
|
+
BiucingError,
|
|
12
|
+
InvalidTemplateError,
|
|
13
|
+
load_template,
|
|
14
|
+
load_templates,
|
|
15
|
+
render_template,
|
|
16
|
+
render_text,
|
|
17
|
+
resolve_variables_detailed,
|
|
18
|
+
validate_resolved_variables,
|
|
19
|
+
validate_templates,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def default_display_name(project_name: str) -> str:
|
|
24
|
+
"""Return a human-friendly display name from a directory name."""
|
|
25
|
+
return project_name.replace("-", " ").replace("_", " ").title()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def default_swift_module_name(project_name: str) -> str:
|
|
29
|
+
"""Return a Swift-safe module name derived from a directory name."""
|
|
30
|
+
parts = [part for part in project_name.replace("_", "-").split("-") if part]
|
|
31
|
+
if not parts:
|
|
32
|
+
return "App"
|
|
33
|
+
return "".join(part[:1].upper() + part[1:] for part in parts)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def default_kotlin_module_name(project_name: str) -> str:
|
|
37
|
+
"""Return a Kotlin-safe module name derived from a directory name."""
|
|
38
|
+
parts = [part for part in project_name.replace("_", "-").split("-") if part]
|
|
39
|
+
if not parts:
|
|
40
|
+
return "App"
|
|
41
|
+
return "".join(part[:1].upper() + part[1:] for part in parts)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def apple_platform_config(platform: str | None, minimum_os_version: str | None) -> dict[str, str]:
|
|
45
|
+
"""Return derived Apple platform values for template rendering."""
|
|
46
|
+
requested = (platform or "ios").lower()
|
|
47
|
+
supported = {
|
|
48
|
+
"ios": {
|
|
49
|
+
"apple_platform": "ios",
|
|
50
|
+
"apple_platform_name": "iOS",
|
|
51
|
+
"fastlane_platform": "ios",
|
|
52
|
+
"app_store_platform": "ios",
|
|
53
|
+
"minimum_os_version": "26.0",
|
|
54
|
+
"tuist_destinations": ".iOS",
|
|
55
|
+
"tuist_deployment_targets": '.iOS("{{MINIMUM_OS_VERSION}}")',
|
|
56
|
+
"xcodebuild_destination": "generic/platform=iOS Simulator",
|
|
57
|
+
"swiftpm_supported_platform": '.iOS("{{MINIMUM_OS_VERSION}}")',
|
|
58
|
+
},
|
|
59
|
+
"macos": {
|
|
60
|
+
"apple_platform": "macos",
|
|
61
|
+
"apple_platform_name": "macOS",
|
|
62
|
+
"fastlane_platform": "mac",
|
|
63
|
+
"app_store_platform": "osx",
|
|
64
|
+
"minimum_os_version": "26.0",
|
|
65
|
+
"tuist_destinations": ".macOS",
|
|
66
|
+
"tuist_deployment_targets": '.macOS("{{MINIMUM_OS_VERSION}}")',
|
|
67
|
+
"xcodebuild_destination": "platform=macOS",
|
|
68
|
+
"swiftpm_supported_platform": '.macOS("{{MINIMUM_OS_VERSION}}")',
|
|
69
|
+
},
|
|
70
|
+
"watchos": {
|
|
71
|
+
"apple_platform": "watchos",
|
|
72
|
+
"apple_platform_name": "watchOS",
|
|
73
|
+
"fastlane_platform": "ios",
|
|
74
|
+
"app_store_platform": "ios",
|
|
75
|
+
"minimum_os_version": "26.0",
|
|
76
|
+
"tuist_destinations": ".watchOS",
|
|
77
|
+
"tuist_deployment_targets": '.watchOS("{{MINIMUM_OS_VERSION}}")',
|
|
78
|
+
"xcodebuild_destination": "generic/platform=watchOS Simulator",
|
|
79
|
+
"swiftpm_supported_platform": '.watchOS("{{MINIMUM_OS_VERSION}}")',
|
|
80
|
+
},
|
|
81
|
+
"tvos": {
|
|
82
|
+
"apple_platform": "tvos",
|
|
83
|
+
"apple_platform_name": "tvOS",
|
|
84
|
+
"fastlane_platform": "ios",
|
|
85
|
+
"app_store_platform": "appletvos",
|
|
86
|
+
"minimum_os_version": "26.0",
|
|
87
|
+
"tuist_destinations": ".tvOS",
|
|
88
|
+
"tuist_deployment_targets": '.tvOS("{{MINIMUM_OS_VERSION}}")',
|
|
89
|
+
"xcodebuild_destination": "generic/platform=tvOS Simulator",
|
|
90
|
+
"swiftpm_supported_platform": '.tvOS("{{MINIMUM_OS_VERSION}}")',
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
if requested not in supported:
|
|
94
|
+
raise ValueError(
|
|
95
|
+
"Unsupported Apple platform. Expected one of: ios, macos, watchos, tvos"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
resolved = dict(supported[requested])
|
|
99
|
+
resolved["minimum_os_version"] = minimum_os_version or resolved["minimum_os_version"]
|
|
100
|
+
resolved["tuist_deployment_targets"] = resolved["tuist_deployment_targets"].replace(
|
|
101
|
+
"{{MINIMUM_OS_VERSION}}", resolved["minimum_os_version"]
|
|
102
|
+
)
|
|
103
|
+
resolved["swiftpm_supported_platform"] = resolved["swiftpm_supported_platform"].replace(
|
|
104
|
+
"{{MINIMUM_OS_VERSION}}", resolved["minimum_os_version"]
|
|
105
|
+
)
|
|
106
|
+
return resolved
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def apple_platform_snippets(values: dict[str, str]) -> dict[str, str]:
|
|
110
|
+
"""Return platform-specific Apple template snippets using resolved values."""
|
|
111
|
+
platform = values.get("apple_platform", "ios")
|
|
112
|
+
display_name = values.get("display_name", "App")
|
|
113
|
+
|
|
114
|
+
if platform == "macos":
|
|
115
|
+
return {
|
|
116
|
+
"apple_scene_body": "\n".join(
|
|
117
|
+
[
|
|
118
|
+
f' WindowGroup("{display_name}") {{',
|
|
119
|
+
" HomeView()",
|
|
120
|
+
" }",
|
|
121
|
+
" .defaultSize(width: 1100, height: 720)",
|
|
122
|
+
]
|
|
123
|
+
),
|
|
124
|
+
"apple_home_body": "\n".join(
|
|
125
|
+
[
|
|
126
|
+
" var body: some View {",
|
|
127
|
+
" NavigationSplitView {",
|
|
128
|
+
' List {',
|
|
129
|
+
' Section("Workspace") {',
|
|
130
|
+
' Label("Overview", systemImage: "sidebar.left")',
|
|
131
|
+
' Label("Release Checklist", systemImage: "checkmark.circle")',
|
|
132
|
+
" }",
|
|
133
|
+
" }",
|
|
134
|
+
" .navigationSplitViewColumnWidth(min: 220, ideal: 240)",
|
|
135
|
+
" } detail: {",
|
|
136
|
+
" ScrollView {",
|
|
137
|
+
" VStack(alignment: .leading, spacing: 20) {",
|
|
138
|
+
" Text(viewModel.title)",
|
|
139
|
+
" .font(BiucingTheme.titleFont)",
|
|
140
|
+
"",
|
|
141
|
+
" Text(viewModel.subtitle)",
|
|
142
|
+
" .font(BiucingTheme.bodyFont)",
|
|
143
|
+
" .foregroundStyle(.secondary)",
|
|
144
|
+
"",
|
|
145
|
+
' GroupBox("Project Summary") {',
|
|
146
|
+
" VStack(alignment: .leading, spacing: 8) {",
|
|
147
|
+
" ForEach(viewModel.facts, id: \\.label) { fact in",
|
|
148
|
+
' Label("\\(fact.label): \\(fact.value)", systemImage: fact.systemImage)',
|
|
149
|
+
" }",
|
|
150
|
+
" }",
|
|
151
|
+
" .font(BiucingTheme.captionFont)",
|
|
152
|
+
" }",
|
|
153
|
+
"",
|
|
154
|
+
' GroupBox("Release Checklist") {',
|
|
155
|
+
" VStack(alignment: .leading, spacing: 8) {",
|
|
156
|
+
" ForEach(viewModel.releaseChecklist(), id: \\.self) { item in",
|
|
157
|
+
' Label(item, systemImage: "checkmark.circle")',
|
|
158
|
+
" }",
|
|
159
|
+
" }",
|
|
160
|
+
" .font(BiucingTheme.captionFont)",
|
|
161
|
+
" }",
|
|
162
|
+
" }",
|
|
163
|
+
" .frame(maxWidth: 680, alignment: .leading)",
|
|
164
|
+
" .padding(24)",
|
|
165
|
+
" }",
|
|
166
|
+
' .navigationTitle("Overview")',
|
|
167
|
+
" }",
|
|
168
|
+
" }",
|
|
169
|
+
]
|
|
170
|
+
),
|
|
171
|
+
"apple_platform_output_note": (
|
|
172
|
+
"macOS starters use a split-view workspace with a fixed desktop window size."
|
|
173
|
+
),
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if platform == "ios":
|
|
177
|
+
return {
|
|
178
|
+
"apple_scene_body": "\n".join(
|
|
179
|
+
[
|
|
180
|
+
" WindowGroup {",
|
|
181
|
+
" HomeView()",
|
|
182
|
+
" }",
|
|
183
|
+
]
|
|
184
|
+
),
|
|
185
|
+
"apple_home_body": "\n".join(
|
|
186
|
+
[
|
|
187
|
+
" var body: some View {",
|
|
188
|
+
" NavigationStack {",
|
|
189
|
+
" List {",
|
|
190
|
+
' Section("Project Summary") {',
|
|
191
|
+
" ForEach(viewModel.facts, id: \\.label) { fact in",
|
|
192
|
+
' Label("\\(fact.label): \\(fact.value)", systemImage: fact.systemImage)',
|
|
193
|
+
" }",
|
|
194
|
+
" }",
|
|
195
|
+
"",
|
|
196
|
+
' Section("Release Checklist") {',
|
|
197
|
+
" ForEach(viewModel.releaseChecklist(), id: \\.self) { item in",
|
|
198
|
+
' Label(item, systemImage: "checkmark.circle")',
|
|
199
|
+
" }",
|
|
200
|
+
" }",
|
|
201
|
+
" }",
|
|
202
|
+
" .listStyle(.insetGrouped)",
|
|
203
|
+
' .navigationTitle("Starter Overview")',
|
|
204
|
+
" }",
|
|
205
|
+
" }",
|
|
206
|
+
]
|
|
207
|
+
),
|
|
208
|
+
"apple_platform_output_note": (
|
|
209
|
+
"iOS starters use a stacked overview screen tuned for simulator-first mobile flows."
|
|
210
|
+
),
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
"apple_scene_body": "\n".join(
|
|
215
|
+
[
|
|
216
|
+
" WindowGroup {",
|
|
217
|
+
" HomeView()",
|
|
218
|
+
" }",
|
|
219
|
+
]
|
|
220
|
+
),
|
|
221
|
+
"apple_home_body": "\n".join(
|
|
222
|
+
[
|
|
223
|
+
" var body: some View {",
|
|
224
|
+
" NavigationStack {",
|
|
225
|
+
" VStack(alignment: .leading, spacing: 16) {",
|
|
226
|
+
" Text(viewModel.title)",
|
|
227
|
+
" .font(BiucingTheme.titleFont)",
|
|
228
|
+
"",
|
|
229
|
+
" Text(viewModel.subtitle)",
|
|
230
|
+
" .font(BiucingTheme.bodyFont)",
|
|
231
|
+
" .foregroundStyle(.secondary)",
|
|
232
|
+
"",
|
|
233
|
+
" VStack(alignment: .leading, spacing: 8) {",
|
|
234
|
+
" ForEach(viewModel.facts, id: \\.label) { fact in",
|
|
235
|
+
' Label("\\(fact.label): \\(fact.value)", systemImage: fact.systemImage)',
|
|
236
|
+
" }",
|
|
237
|
+
" }",
|
|
238
|
+
" .font(BiucingTheme.captionFont)",
|
|
239
|
+
"",
|
|
240
|
+
" VStack(alignment: .leading, spacing: 8) {",
|
|
241
|
+
' Text("Release Checklist")',
|
|
242
|
+
" .font(BiucingTheme.sectionTitleFont)",
|
|
243
|
+
"",
|
|
244
|
+
" ForEach(viewModel.releaseChecklist(), id: \\.self) { item in",
|
|
245
|
+
' Label(item, systemImage: "checkmark.circle")',
|
|
246
|
+
" }",
|
|
247
|
+
" }",
|
|
248
|
+
" .font(BiucingTheme.captionFont)",
|
|
249
|
+
" }",
|
|
250
|
+
" .padding(24)",
|
|
251
|
+
' .navigationTitle("Overview")',
|
|
252
|
+
" }",
|
|
253
|
+
" }",
|
|
254
|
+
]
|
|
255
|
+
),
|
|
256
|
+
"apple_platform_output_note": (
|
|
257
|
+
f"{values.get('apple_platform_name', 'Apple')} starters currently keep the shared overview layout."
|
|
258
|
+
),
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def microservice_dependency_config(store: str | None, service_name: str) -> dict[str, str]:
|
|
263
|
+
"""Return derived local dependency values for the microservice template."""
|
|
264
|
+
selected = (store or "postgres").lower()
|
|
265
|
+
supported = {
|
|
266
|
+
"postgres": {
|
|
267
|
+
"dependency_store": "postgres",
|
|
268
|
+
"dependency_store_image": "postgres:16-alpine",
|
|
269
|
+
"dependency_store_port": "5432",
|
|
270
|
+
"dependency_store_dsn": f"postgres://postgres:postgres@localhost:5432/{service_name}?sslmode=disable",
|
|
271
|
+
"dependency_store_container_dsn": (
|
|
272
|
+
f"postgres://postgres:postgres@postgres:5432/{service_name}?sslmode=disable"
|
|
273
|
+
),
|
|
274
|
+
"dependency_store_env_block": "\n".join(
|
|
275
|
+
[
|
|
276
|
+
" environment:",
|
|
277
|
+
f" POSTGRES_DB: {service_name}",
|
|
278
|
+
" POSTGRES_USER: postgres",
|
|
279
|
+
" POSTGRES_PASSWORD: postgres",
|
|
280
|
+
]
|
|
281
|
+
),
|
|
282
|
+
},
|
|
283
|
+
"redis": {
|
|
284
|
+
"dependency_store": "redis",
|
|
285
|
+
"dependency_store_image": "redis:7-alpine",
|
|
286
|
+
"dependency_store_port": "6379",
|
|
287
|
+
"dependency_store_dsn": "redis://localhost:6379/0",
|
|
288
|
+
"dependency_store_container_dsn": "redis://redis:6379/0",
|
|
289
|
+
"dependency_store_env_block": "",
|
|
290
|
+
},
|
|
291
|
+
}
|
|
292
|
+
if selected not in supported:
|
|
293
|
+
raise ValueError("Unsupported dependency store. Expected one of: postgres, redis")
|
|
294
|
+
return dict(supported[selected])
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def parse_set_values(items: list[str]) -> dict[str, str]:
|
|
298
|
+
"""Parse repeated KEY=VALUE pairs from the CLI."""
|
|
299
|
+
values: dict[str, str] = {}
|
|
300
|
+
for item in items:
|
|
301
|
+
if "=" not in item:
|
|
302
|
+
raise ValueError(f"Invalid --set value '{item}'. Expected KEY=VALUE.")
|
|
303
|
+
key, value = item.split("=", 1)
|
|
304
|
+
normalized_key = key.strip().replace("-", "_")
|
|
305
|
+
if not normalized_key:
|
|
306
|
+
raise ValueError(f"Invalid --set value '{item}'. Expected KEY=VALUE.")
|
|
307
|
+
values[normalized_key] = value
|
|
308
|
+
return values
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
312
|
+
"""Build the top-level argument parser."""
|
|
313
|
+
parser = argparse.ArgumentParser(prog="biucing", description="Project scaffold generator.")
|
|
314
|
+
parser.add_argument(
|
|
315
|
+
"--version",
|
|
316
|
+
action="version",
|
|
317
|
+
version=f"%(prog)s {__version__}",
|
|
318
|
+
help="Show the installed BiucingCLI version.",
|
|
319
|
+
)
|
|
320
|
+
subparsers = parser.add_subparsers(dest="command")
|
|
321
|
+
|
|
322
|
+
list_parser = subparsers.add_parser("list", help="List available templates.")
|
|
323
|
+
list_parser.add_argument(
|
|
324
|
+
"--json",
|
|
325
|
+
action="store_true",
|
|
326
|
+
help="Print template metadata as JSON.",
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
info_parser = subparsers.add_parser("info", help="Show details about a template.")
|
|
330
|
+
info_parser.add_argument("template", help="Template name.")
|
|
331
|
+
info_parser.add_argument(
|
|
332
|
+
"--json",
|
|
333
|
+
action="store_true",
|
|
334
|
+
help="Print template metadata as JSON.",
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
validate_parser = subparsers.add_parser(
|
|
338
|
+
"validate", help="Validate template metadata and placeholder consistency."
|
|
339
|
+
)
|
|
340
|
+
validate_parser.add_argument(
|
|
341
|
+
"--json",
|
|
342
|
+
action="store_true",
|
|
343
|
+
help="Print validation results as JSON.",
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
create_parser = subparsers.add_parser("create", help="Create a new project from a template.")
|
|
347
|
+
create_parser.add_argument("template", help="Template name.")
|
|
348
|
+
create_parser.add_argument("project_name", help="Project directory name.")
|
|
349
|
+
create_parser.add_argument("--output-dir", default=".", help="Base directory for generation.")
|
|
350
|
+
create_parser.add_argument(
|
|
351
|
+
"--set",
|
|
352
|
+
dest="set_values",
|
|
353
|
+
action="append",
|
|
354
|
+
default=[],
|
|
355
|
+
metavar="KEY=VALUE",
|
|
356
|
+
help="Set any template variable via KEY=VALUE. Can be repeated.",
|
|
357
|
+
)
|
|
358
|
+
create_parser.add_argument(
|
|
359
|
+
"--non-interactive",
|
|
360
|
+
action="store_true",
|
|
361
|
+
help="Fail instead of prompting for missing required values.",
|
|
362
|
+
)
|
|
363
|
+
create_parser.add_argument(
|
|
364
|
+
"--json",
|
|
365
|
+
action="store_true",
|
|
366
|
+
help="Print the create plan or result as JSON.",
|
|
367
|
+
)
|
|
368
|
+
create_parser.add_argument(
|
|
369
|
+
"--dry-run",
|
|
370
|
+
action="store_true",
|
|
371
|
+
help="Resolve values and preview the generation result without writing files.",
|
|
372
|
+
)
|
|
373
|
+
create_parser.add_argument(
|
|
374
|
+
"--plan",
|
|
375
|
+
action="store_true",
|
|
376
|
+
help="Print a human-readable generation plan without writing files.",
|
|
377
|
+
)
|
|
378
|
+
create_parser.add_argument("--display-name", help="Display name for frontend projects.")
|
|
379
|
+
create_parser.add_argument("--package-name", help="Package name for frontend projects.")
|
|
380
|
+
create_parser.add_argument("--module-name", help="Go module name for web service projects.")
|
|
381
|
+
create_parser.add_argument("--service-name", help="Service name for web service projects.")
|
|
382
|
+
create_parser.add_argument("--http-port", help="HTTP port for web service projects.")
|
|
383
|
+
create_parser.add_argument("--worker-name", help="Worker process name for worker projects.")
|
|
384
|
+
create_parser.add_argument(
|
|
385
|
+
"--run-mode",
|
|
386
|
+
choices=["scheduled", "oneshot"],
|
|
387
|
+
help="Worker execution mode for worker projects.",
|
|
388
|
+
)
|
|
389
|
+
create_parser.add_argument(
|
|
390
|
+
"--tick-interval-seconds",
|
|
391
|
+
help="Tick interval in seconds for scheduled worker projects.",
|
|
392
|
+
)
|
|
393
|
+
create_parser.add_argument(
|
|
394
|
+
"--shutdown-timeout-seconds",
|
|
395
|
+
help="Shutdown timeout in seconds for worker projects.",
|
|
396
|
+
)
|
|
397
|
+
create_parser.add_argument("--grpc-port", help="gRPC port for microservice projects.")
|
|
398
|
+
create_parser.add_argument("--proto-package", help="Proto package for microservice projects.")
|
|
399
|
+
create_parser.add_argument(
|
|
400
|
+
"--dependency-store",
|
|
401
|
+
choices=["postgres", "redis"],
|
|
402
|
+
help="Local dependency store for microservice projects.",
|
|
403
|
+
)
|
|
404
|
+
create_parser.add_argument(
|
|
405
|
+
"--otel-exporter-endpoint",
|
|
406
|
+
help="OpenTelemetry exporter endpoint for microservice projects.",
|
|
407
|
+
)
|
|
408
|
+
create_parser.add_argument(
|
|
409
|
+
"--platform",
|
|
410
|
+
choices=["ios", "macos", "watchos", "tvos"],
|
|
411
|
+
help="Apple platform for the apple template.",
|
|
412
|
+
)
|
|
413
|
+
create_parser.add_argument("--bundle-identifier", help="Apple app bundle identifier.")
|
|
414
|
+
create_parser.add_argument("--minimum-os-version", help="Minimum Apple OS version.")
|
|
415
|
+
create_parser.add_argument("--development-team", help="Apple development team ID.")
|
|
416
|
+
create_parser.add_argument("--organization-name", help="Organization or team name.")
|
|
417
|
+
create_parser.add_argument("--swift-module-name", help="Swift module name for app targets.")
|
|
418
|
+
create_parser.add_argument("--application-id", help="Android application ID.")
|
|
419
|
+
create_parser.add_argument("--compile-sdk", help="Android compile SDK version.")
|
|
420
|
+
create_parser.add_argument("--min-sdk", help="Android minimum SDK version.")
|
|
421
|
+
create_parser.add_argument("--target-sdk", help="Android target SDK version.")
|
|
422
|
+
create_parser.add_argument("--version-code", help="Android version code.")
|
|
423
|
+
create_parser.add_argument("--version-name", help="Android version name.")
|
|
424
|
+
create_parser.add_argument("--java-version", help="Java version for Android builds.")
|
|
425
|
+
create_parser.add_argument("--android-namespace", help="Android namespace.")
|
|
426
|
+
create_parser.add_argument("--kotlin-module-name", help="Kotlin module name for Android code.")
|
|
427
|
+
create_parser.add_argument("--bundle-name", help="HarmonyOS bundle name.")
|
|
428
|
+
create_parser.add_argument("--harmony-module-name", help="HarmonyOS module name.")
|
|
429
|
+
create_parser.add_argument("--ability-name", help="HarmonyOS entry ability name.")
|
|
430
|
+
create_parser.add_argument("--compatible-sdk-version", help="HarmonyOS compatible SDK version.")
|
|
431
|
+
create_parser.add_argument("--target-sdk-version", help="HarmonyOS target SDK version.")
|
|
432
|
+
create_parser.add_argument("--min-api-version", help="HarmonyOS minimum API version.")
|
|
433
|
+
create_parser.add_argument("--harmony-version-code", help="HarmonyOS version code.")
|
|
434
|
+
create_parser.add_argument("--harmony-version-name", help="HarmonyOS version name.")
|
|
435
|
+
return parser
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def format_template_summary() -> str:
|
|
439
|
+
"""Return a concise summary of available templates."""
|
|
440
|
+
lines = ["Available templates:"]
|
|
441
|
+
for definition in load_templates():
|
|
442
|
+
lines.append(f"- {definition.name}: {definition.description}")
|
|
443
|
+
return "\n".join(lines)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def format_template_summary_json() -> str:
|
|
447
|
+
"""Return a machine-readable template list."""
|
|
448
|
+
payload = {"templates": [definition.to_dict() for definition in load_templates()]}
|
|
449
|
+
return json.dumps(payload, indent=2)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def format_template_info(template_name: str) -> str:
|
|
453
|
+
"""Return a detailed view of one template."""
|
|
454
|
+
definition = load_template(template_name)
|
|
455
|
+
lines = [
|
|
456
|
+
f"Template: {definition.name}",
|
|
457
|
+
f"Description: {definition.description}",
|
|
458
|
+
f"Category: {definition.category}",
|
|
459
|
+
f"Platforms: {', '.join(definition.platforms)}",
|
|
460
|
+
f"Tags: {', '.join(definition.tags)}",
|
|
461
|
+
f"Workflow labels: {', '.join(definition.workflow_labels)}",
|
|
462
|
+
f"Maturity: {definition.maturity.level} - {definition.maturity.summary}",
|
|
463
|
+
f"Validation: {definition.validation.status}",
|
|
464
|
+
f"Verification tier: {definition.validation.verification_tier}",
|
|
465
|
+
f"Worktree support: {definition.worktree.support_level}",
|
|
466
|
+
f"Worktree isolation: {', '.join(definition.worktree.isolation_dimensions)}",
|
|
467
|
+
f"Stack: {', '.join(definition.stack)}",
|
|
468
|
+
"Operating assumptions:",
|
|
469
|
+
]
|
|
470
|
+
for assumption in definition.operating_assumptions:
|
|
471
|
+
lines.append(f"- {assumption}")
|
|
472
|
+
lines.append("Worktree diagnostics:")
|
|
473
|
+
for diagnostic in definition.worktree.diagnostics:
|
|
474
|
+
lines.append(f"- {diagnostic}")
|
|
475
|
+
lines.append("Worktree cleanup:")
|
|
476
|
+
for cleanup in definition.worktree.cleanup:
|
|
477
|
+
lines.append(f"- {cleanup}")
|
|
478
|
+
lines.append("Variables:")
|
|
479
|
+
for variable in definition.variables:
|
|
480
|
+
required = "required" if variable.required else "optional"
|
|
481
|
+
details = [required]
|
|
482
|
+
if variable.default is not None:
|
|
483
|
+
details.append(f"default={variable.default}")
|
|
484
|
+
if variable.default_from is not None:
|
|
485
|
+
details.append(f"default_from={variable.default_from}")
|
|
486
|
+
lines.append(f"- {variable.name} ({', '.join(details)})")
|
|
487
|
+
lines.append("Next steps:")
|
|
488
|
+
for step in definition.next_steps:
|
|
489
|
+
lines.append(f"- {step}")
|
|
490
|
+
return "\n".join(lines)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def format_template_info_json(template_name: str) -> str:
|
|
494
|
+
"""Return a machine-readable template detail payload."""
|
|
495
|
+
definition = load_template(template_name)
|
|
496
|
+
return json.dumps(definition.to_dict(), indent=2)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def format_validation_report(errors: list[str]) -> str:
|
|
500
|
+
"""Return a human-readable validation report."""
|
|
501
|
+
if not errors:
|
|
502
|
+
return "Template validation passed."
|
|
503
|
+
|
|
504
|
+
lines = ["Template validation failed:"]
|
|
505
|
+
lines.extend(f"- {error}" for error in errors)
|
|
506
|
+
return "\n".join(lines)
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def format_validation_report_json(errors: list[str]) -> str:
|
|
510
|
+
"""Return a machine-readable validation report."""
|
|
511
|
+
payload = {
|
|
512
|
+
"ok": not errors,
|
|
513
|
+
"error_count": len(errors),
|
|
514
|
+
"errors": errors,
|
|
515
|
+
}
|
|
516
|
+
return json.dumps(payload, indent=2)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def count_template_files(template_dir: Path) -> int:
|
|
520
|
+
"""Return the number of files in a template directory."""
|
|
521
|
+
return sum(1 for path in template_dir.rglob("*") if path.is_file())
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def top_level_template_entries(template_dir: Path) -> list[str]:
|
|
525
|
+
"""Return top-level template entries for preview and manifest output."""
|
|
526
|
+
return sorted(path.name for path in template_dir.iterdir())
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
def build_create_context(args: argparse.Namespace) -> dict[str, object]:
|
|
530
|
+
"""Resolve a create request into a reusable context."""
|
|
531
|
+
definition = load_template(args.template)
|
|
532
|
+
requested_project_name = args.project_name.strip()
|
|
533
|
+
set_values = parse_set_values(args.set_values)
|
|
534
|
+
allowed_keys = {variable.name for variable in definition.variables}
|
|
535
|
+
unknown_keys = sorted(key for key in set_values if key not in allowed_keys)
|
|
536
|
+
if unknown_keys:
|
|
537
|
+
unknown_list = ", ".join(unknown_keys)
|
|
538
|
+
raise ValueError(f"Unknown template variable(s) for {args.template}: {unknown_list}")
|
|
539
|
+
|
|
540
|
+
cli_inputs = {
|
|
541
|
+
"display_name": args.display_name,
|
|
542
|
+
"package_name": args.package_name,
|
|
543
|
+
"module_name": args.module_name,
|
|
544
|
+
"service_name": args.service_name,
|
|
545
|
+
"http_port": args.http_port,
|
|
546
|
+
"worker_name": args.worker_name,
|
|
547
|
+
"run_mode": args.run_mode,
|
|
548
|
+
"tick_interval_seconds": args.tick_interval_seconds,
|
|
549
|
+
"shutdown_timeout_seconds": args.shutdown_timeout_seconds,
|
|
550
|
+
"grpc_port": args.grpc_port,
|
|
551
|
+
"proto_package": args.proto_package,
|
|
552
|
+
"dependency_store": args.dependency_store,
|
|
553
|
+
"otel_exporter_endpoint": args.otel_exporter_endpoint,
|
|
554
|
+
"apple_platform": args.platform,
|
|
555
|
+
"bundle_identifier": args.bundle_identifier,
|
|
556
|
+
"minimum_os_version": args.minimum_os_version,
|
|
557
|
+
"development_team": args.development_team,
|
|
558
|
+
"organization_name": args.organization_name,
|
|
559
|
+
"swift_module_name": args.swift_module_name,
|
|
560
|
+
"application_id": args.application_id,
|
|
561
|
+
"compile_sdk": args.compile_sdk,
|
|
562
|
+
"min_sdk": args.min_sdk,
|
|
563
|
+
"target_sdk": args.target_sdk,
|
|
564
|
+
"version_code": args.version_code,
|
|
565
|
+
"version_name": args.version_name,
|
|
566
|
+
"java_version": args.java_version,
|
|
567
|
+
"android_namespace": args.android_namespace,
|
|
568
|
+
"kotlin_module_name": args.kotlin_module_name,
|
|
569
|
+
"bundle_name": args.bundle_name,
|
|
570
|
+
"harmony_module_name": args.harmony_module_name,
|
|
571
|
+
"ability_name": args.ability_name,
|
|
572
|
+
"compatible_sdk_version": args.compatible_sdk_version,
|
|
573
|
+
"target_sdk_version": args.target_sdk_version,
|
|
574
|
+
"min_api_version": args.min_api_version,
|
|
575
|
+
"harmony_version_code": args.harmony_version_code,
|
|
576
|
+
"harmony_version_name": args.harmony_version_name,
|
|
577
|
+
}
|
|
578
|
+
unsupported_cli_inputs = sorted(
|
|
579
|
+
key for key, value in cli_inputs.items() if value is not None and key not in allowed_keys
|
|
580
|
+
)
|
|
581
|
+
if unsupported_cli_inputs:
|
|
582
|
+
unsupported_list = ", ".join(unsupported_cli_inputs)
|
|
583
|
+
raise ValueError(
|
|
584
|
+
f"Unsupported option(s) for {args.template}: {unsupported_list}"
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
provided_values: dict[str, str | None] = dict(set_values)
|
|
588
|
+
provided_values["project_name"] = requested_project_name
|
|
589
|
+
|
|
590
|
+
if args.display_name is not None:
|
|
591
|
+
provided_values["display_name"] = args.display_name
|
|
592
|
+
elif "display_name" not in provided_values:
|
|
593
|
+
provided_values["display_name"] = default_display_name(requested_project_name)
|
|
594
|
+
|
|
595
|
+
if args.package_name is not None:
|
|
596
|
+
provided_values["package_name"] = args.package_name
|
|
597
|
+
|
|
598
|
+
explicit_values = {
|
|
599
|
+
"module_name": args.module_name,
|
|
600
|
+
"service_name": args.service_name,
|
|
601
|
+
"http_port": args.http_port,
|
|
602
|
+
"worker_name": args.worker_name,
|
|
603
|
+
"run_mode": args.run_mode,
|
|
604
|
+
"tick_interval_seconds": args.tick_interval_seconds,
|
|
605
|
+
"shutdown_timeout_seconds": args.shutdown_timeout_seconds,
|
|
606
|
+
"grpc_port": args.grpc_port,
|
|
607
|
+
"proto_package": args.proto_package,
|
|
608
|
+
"dependency_store": args.dependency_store,
|
|
609
|
+
"otel_exporter_endpoint": args.otel_exporter_endpoint,
|
|
610
|
+
"apple_platform": args.platform,
|
|
611
|
+
"bundle_identifier": args.bundle_identifier,
|
|
612
|
+
"minimum_os_version": args.minimum_os_version,
|
|
613
|
+
"development_team": args.development_team,
|
|
614
|
+
"organization_name": args.organization_name,
|
|
615
|
+
"application_id": args.application_id,
|
|
616
|
+
"compile_sdk": args.compile_sdk,
|
|
617
|
+
"min_sdk": args.min_sdk,
|
|
618
|
+
"target_sdk": args.target_sdk,
|
|
619
|
+
"version_code": args.version_code,
|
|
620
|
+
"version_name": args.version_name,
|
|
621
|
+
"java_version": args.java_version,
|
|
622
|
+
"android_namespace": args.android_namespace,
|
|
623
|
+
"kotlin_module_name": args.kotlin_module_name,
|
|
624
|
+
"bundle_name": args.bundle_name,
|
|
625
|
+
"harmony_module_name": args.harmony_module_name,
|
|
626
|
+
"ability_name": args.ability_name,
|
|
627
|
+
"compatible_sdk_version": args.compatible_sdk_version,
|
|
628
|
+
"target_sdk_version": args.target_sdk_version,
|
|
629
|
+
"min_api_version": args.min_api_version,
|
|
630
|
+
"harmony_version_code": args.harmony_version_code,
|
|
631
|
+
"harmony_version_name": args.harmony_version_name,
|
|
632
|
+
"swift_module_name": args.swift_module_name,
|
|
633
|
+
}
|
|
634
|
+
for key, value in explicit_values.items():
|
|
635
|
+
if value is not None:
|
|
636
|
+
provided_values[key] = value
|
|
637
|
+
|
|
638
|
+
resolution_result = resolve_variables_detailed(
|
|
639
|
+
definition,
|
|
640
|
+
provided_values,
|
|
641
|
+
interactive=not args.non_interactive,
|
|
642
|
+
)
|
|
643
|
+
values = dict(resolution_result.values)
|
|
644
|
+
derived_values: dict[str, str] = {}
|
|
645
|
+
if args.template == "microservice":
|
|
646
|
+
derived_values.update(
|
|
647
|
+
microservice_dependency_config(
|
|
648
|
+
values.get("dependency_store"),
|
|
649
|
+
values.get("service_name", values["project_name"]),
|
|
650
|
+
)
|
|
651
|
+
)
|
|
652
|
+
derived_values["service_type_name"] = default_swift_module_name(
|
|
653
|
+
values["project_name"]
|
|
654
|
+
)
|
|
655
|
+
if args.template == "apple":
|
|
656
|
+
derived_values.update(
|
|
657
|
+
apple_platform_config(
|
|
658
|
+
values.get("apple_platform"),
|
|
659
|
+
values.get("minimum_os_version"),
|
|
660
|
+
)
|
|
661
|
+
)
|
|
662
|
+
derived_values["swift_module_name"] = values.get(
|
|
663
|
+
"swift_module_name"
|
|
664
|
+
) or default_swift_module_name(values["project_name"])
|
|
665
|
+
if args.template == "android":
|
|
666
|
+
derived_values["kotlin_module_name"] = values.get(
|
|
667
|
+
"kotlin_module_name"
|
|
668
|
+
) or default_kotlin_module_name(values["project_name"])
|
|
669
|
+
values.update(derived_values)
|
|
670
|
+
if args.template == "apple":
|
|
671
|
+
values.update(apple_platform_snippets(values))
|
|
672
|
+
|
|
673
|
+
input_errors = validate_resolved_variables(definition, values)
|
|
674
|
+
if input_errors:
|
|
675
|
+
raise ValueError(
|
|
676
|
+
f"Invalid input value(s) for {definition.name}: " + "; ".join(input_errors)
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
target_dir = Path(args.output_dir).resolve() / values["project_name"]
|
|
680
|
+
rendered_next_steps = [render_text(step, values) for step in definition.next_steps]
|
|
681
|
+
system_derived_values = {
|
|
682
|
+
key: values[key]
|
|
683
|
+
for key in sorted(derived_values)
|
|
684
|
+
if key in values
|
|
685
|
+
}
|
|
686
|
+
return {
|
|
687
|
+
"definition": definition,
|
|
688
|
+
"project_name": values["project_name"],
|
|
689
|
+
"target_dir": target_dir,
|
|
690
|
+
"values": values,
|
|
691
|
+
"resolved_variables": [item.to_dict() for item in resolution_result.resolved_variables],
|
|
692
|
+
"derived_values": system_derived_values,
|
|
693
|
+
"rendered_next_steps": rendered_next_steps,
|
|
694
|
+
"template_file_count": count_template_files(definition.template_dir),
|
|
695
|
+
"template_top_level_entries": top_level_template_entries(definition.template_dir),
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
def create_manifest(context: dict[str, object], mode: str) -> dict[str, object]:
|
|
700
|
+
"""Build a machine-readable preview or generation result."""
|
|
701
|
+
definition = context["definition"]
|
|
702
|
+
assert hasattr(definition, "name")
|
|
703
|
+
return {
|
|
704
|
+
"operation": mode,
|
|
705
|
+
"template": {
|
|
706
|
+
"name": definition.name,
|
|
707
|
+
"description": definition.description,
|
|
708
|
+
"category": definition.category,
|
|
709
|
+
"stack": definition.stack,
|
|
710
|
+
"platforms": definition.platforms,
|
|
711
|
+
},
|
|
712
|
+
"project_name": context["project_name"],
|
|
713
|
+
"output_path": str(context["target_dir"]),
|
|
714
|
+
"target_exists": Path(context["target_dir"]).exists(),
|
|
715
|
+
"resolved_variables": context["resolved_variables"],
|
|
716
|
+
"derived_values": context["derived_values"],
|
|
717
|
+
"next_steps": context["rendered_next_steps"],
|
|
718
|
+
"template_file_count": context["template_file_count"],
|
|
719
|
+
"template_top_level_entries": context["template_top_level_entries"],
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
def format_create_preview(context: dict[str, object], preview_mode: str) -> str:
|
|
724
|
+
"""Return a human-readable create preview."""
|
|
725
|
+
definition = context["definition"]
|
|
726
|
+
target_dir = Path(context["target_dir"])
|
|
727
|
+
lines = [
|
|
728
|
+
f"Create preview ({preview_mode}) for {definition.name}: {context['project_name']}",
|
|
729
|
+
f"Location: {target_dir}",
|
|
730
|
+
f"Target exists: {'yes' if target_dir.exists() else 'no'}",
|
|
731
|
+
f"Stack: {', '.join(definition.stack)}",
|
|
732
|
+
"Resolved variables:",
|
|
733
|
+
]
|
|
734
|
+
for item in context["resolved_variables"]:
|
|
735
|
+
lines.append(f" - {item['name']} [{item['source']}]: {item['value']}")
|
|
736
|
+
if context["derived_values"]:
|
|
737
|
+
lines.append("Derived values:")
|
|
738
|
+
for key, value in context["derived_values"].items():
|
|
739
|
+
lines.append(f" - {key}: {value}")
|
|
740
|
+
lines.extend(
|
|
741
|
+
[
|
|
742
|
+
f"Template file count: {context['template_file_count']}",
|
|
743
|
+
"Top-level template entries:",
|
|
744
|
+
]
|
|
745
|
+
)
|
|
746
|
+
lines.extend(f" - {entry}" for entry in context["template_top_level_entries"])
|
|
747
|
+
lines.extend(
|
|
748
|
+
[
|
|
749
|
+
"Next steps:",
|
|
750
|
+
f" cd {context['project_name']}",
|
|
751
|
+
]
|
|
752
|
+
)
|
|
753
|
+
lines.extend(f" {step}" for step in context["rendered_next_steps"])
|
|
754
|
+
lines.append("No files were written.")
|
|
755
|
+
return "\n".join(lines)
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
def format_create_success(context: dict[str, object]) -> str:
|
|
759
|
+
"""Return a human-readable create success summary."""
|
|
760
|
+
definition = context["definition"]
|
|
761
|
+
target_dir = Path(context["target_dir"])
|
|
762
|
+
lines = [
|
|
763
|
+
f"Created {definition.name} project: {context['project_name']}",
|
|
764
|
+
f"Location: {target_dir}",
|
|
765
|
+
f"Stack: {', '.join(definition.stack)}",
|
|
766
|
+
"Resolved variables:",
|
|
767
|
+
]
|
|
768
|
+
for item in context["resolved_variables"]:
|
|
769
|
+
lines.append(f" - {item['name']} [{item['source']}]: {item['value']}")
|
|
770
|
+
lines.extend(
|
|
771
|
+
[
|
|
772
|
+
f"Template file count: {context['template_file_count']}",
|
|
773
|
+
"Next steps:",
|
|
774
|
+
f" cd {context['project_name']}",
|
|
775
|
+
]
|
|
776
|
+
)
|
|
777
|
+
lines.extend(f" {step}" for step in context["rendered_next_steps"])
|
|
778
|
+
return "\n".join(lines)
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
def preview_project(args: argparse.Namespace, preview_mode: str) -> str:
|
|
782
|
+
"""Preview a project generation request."""
|
|
783
|
+
context = build_create_context(args)
|
|
784
|
+
if args.json:
|
|
785
|
+
return json.dumps(create_manifest(context, mode=preview_mode), indent=2)
|
|
786
|
+
return format_create_preview(context, preview_mode)
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
def create_project_output(args: argparse.Namespace) -> str:
|
|
790
|
+
"""Create a project and return either text or JSON output."""
|
|
791
|
+
context = build_create_context(args)
|
|
792
|
+
render_template(
|
|
793
|
+
context["definition"],
|
|
794
|
+
context["values"],
|
|
795
|
+
context["target_dir"],
|
|
796
|
+
)
|
|
797
|
+
if args.json:
|
|
798
|
+
return json.dumps(create_manifest(context, mode="create"), indent=2)
|
|
799
|
+
return format_create_success(context)
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
def main(argv: list[str] | None = None) -> None:
|
|
803
|
+
"""Run the CLI."""
|
|
804
|
+
parser = build_parser()
|
|
805
|
+
args = parser.parse_args(argv)
|
|
806
|
+
|
|
807
|
+
try:
|
|
808
|
+
if args.command is None:
|
|
809
|
+
print(format_template_summary())
|
|
810
|
+
return
|
|
811
|
+
|
|
812
|
+
if args.command == "list":
|
|
813
|
+
print(format_template_summary_json() if args.json else format_template_summary())
|
|
814
|
+
return
|
|
815
|
+
|
|
816
|
+
if args.command == "info":
|
|
817
|
+
output = (
|
|
818
|
+
format_template_info_json(args.template)
|
|
819
|
+
if args.json
|
|
820
|
+
else format_template_info(args.template)
|
|
821
|
+
)
|
|
822
|
+
print(output)
|
|
823
|
+
return
|
|
824
|
+
|
|
825
|
+
if args.command == "validate":
|
|
826
|
+
errors = validate_templates()
|
|
827
|
+
if args.json:
|
|
828
|
+
print(format_validation_report_json(errors))
|
|
829
|
+
else:
|
|
830
|
+
print(format_validation_report(errors))
|
|
831
|
+
if errors:
|
|
832
|
+
parser.exit(1)
|
|
833
|
+
return
|
|
834
|
+
|
|
835
|
+
if args.command == "create":
|
|
836
|
+
if args.dry_run or args.plan:
|
|
837
|
+
preview_mode = "dry-run" if args.dry_run else "plan"
|
|
838
|
+
print(preview_project(args, preview_mode))
|
|
839
|
+
else:
|
|
840
|
+
print(create_project_output(args))
|
|
841
|
+
return
|
|
842
|
+
except InvalidTemplateError as exc:
|
|
843
|
+
parser.exit(1, f"error: {exc}\n")
|
|
844
|
+
except (BiucingError, ValueError) as exc:
|
|
845
|
+
parser.exit(2, f"error: {exc}\n")
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
if __name__ == "__main__":
|
|
849
|
+
main()
|