@sibyllinesoft/arbiter 0.1.0
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.
- package/README.md +222 -0
- package/dist/cli.js +28718 -0
- package/dist/templates/plopfiles/__tests__/build-systems.test.ts +730 -0
- package/dist/templates/plopfiles/__tests__/e2e.test.ts +451 -0
- package/dist/templates/plopfiles/__tests__/generation.test.ts +384 -0
- package/dist/templates/plopfiles/__tests__/linting.test.ts +854 -0
- package/dist/templates/plopfiles/__tests__/modules.test.ts +278 -0
- package/dist/templates/plopfiles/__tests__/smoke.test.ts +619 -0
- package/dist/templates/plopfiles/__tests__/templates.test.ts +341 -0
- package/dist/templates/plopfiles/_modules/backends/go-chi/module.js +43 -0
- package/dist/templates/plopfiles/_modules/backends/go-chi/templates/.air.toml.hbs +30 -0
- package/dist/templates/plopfiles/_modules/backends/go-chi/templates/cmd/server/main.go.hbs +50 -0
- package/dist/templates/plopfiles/_modules/backends/go-chi/templates/go.mod.hbs +9 -0
- package/dist/templates/plopfiles/_modules/backends/go-chi/templates/internal/handlers/health.go.hbs +51 -0
- package/dist/templates/plopfiles/_modules/backends/kotlin-ktor/module.js +38 -0
- package/dist/templates/plopfiles/_modules/backends/kotlin-ktor/templates/build.gradle.kts.hbs +37 -0
- package/dist/templates/plopfiles/_modules/backends/kotlin-ktor/templates/settings.gradle.kts.hbs +1 -0
- package/dist/templates/plopfiles/_modules/backends/kotlin-ktor/templates/src/main/kotlin/Application.kt.hbs +44 -0
- package/dist/templates/plopfiles/_modules/backends/kotlin-ktor/templates/src/main/kotlin/routes/Health.kt.hbs +41 -0
- package/dist/templates/plopfiles/_modules/backends/kotlin-ktor/templates/src/main/resources/logback.xml.hbs +11 -0
- package/dist/templates/plopfiles/_modules/backends/node-express/module.js +54 -0
- package/dist/templates/plopfiles/_modules/backends/node-express/templates/src/index.ts.hbs +37 -0
- package/dist/templates/plopfiles/_modules/backends/node-express/templates/src/routes/health.ts.hbs +21 -0
- package/dist/templates/plopfiles/_modules/backends/node-express/templates/tsconfig.json.hbs +18 -0
- package/dist/templates/plopfiles/_modules/backends/node-hono/module.js +56 -0
- package/dist/templates/plopfiles/_modules/backends/node-hono/templates/src/index.ts.hbs +30 -0
- package/dist/templates/plopfiles/_modules/backends/node-hono/templates/src/routes/health.ts.hbs +64 -0
- package/dist/templates/plopfiles/_modules/backends/node-hono/templates/src/routes/index.ts.hbs +38 -0
- package/dist/templates/plopfiles/_modules/backends/node-hono/templates/tsconfig.json.hbs +20 -0
- package/dist/templates/plopfiles/_modules/backends/node-hono/templates/typedoc.json.hbs +25 -0
- package/dist/templates/plopfiles/_modules/backends/python-fastapi/module.js +65 -0
- package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/app/__init__.py.hbs +1 -0
- package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/app/config.py.hbs +28 -0
- package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/app/main.py.hbs +40 -0
- package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/app/routers/__init__.py.hbs +1 -0
- package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/app/routers/health.py.hbs +24 -0
- package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/pyproject.toml.hbs +40 -0
- package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/requirements.txt.hbs +6 -0
- package/dist/templates/plopfiles/_modules/backends/rust-axum/module.js +44 -0
- package/dist/templates/plopfiles/_modules/backends/rust-axum/templates/Cargo.toml.hbs +23 -0
- package/dist/templates/plopfiles/_modules/backends/rust-axum/templates/src/health.rs.hbs +41 -0
- package/dist/templates/plopfiles/_modules/backends/rust-axum/templates/src/main.rs.hbs +57 -0
- package/dist/templates/plopfiles/_modules/cloud/aws/module.js +33 -0
- package/dist/templates/plopfiles/_modules/cloud/aws/templates/aws-alb.tf.hbs +92 -0
- package/dist/templates/plopfiles/_modules/cloud/aws/templates/aws-ecs.tf.hbs +84 -0
- package/dist/templates/plopfiles/_modules/cloud/aws/templates/aws-iam.tf.hbs +60 -0
- package/dist/templates/plopfiles/_modules/cloud/aws/templates/aws-vpc.tf.hbs +106 -0
- package/dist/templates/plopfiles/_modules/cloud/aws/templates/aws.tf.hbs +56 -0
- package/dist/templates/plopfiles/_modules/cloud/azure/module.js +31 -0
- package/dist/templates/plopfiles/_modules/cloud/azure/templates/azure-container-apps.tf.hbs +99 -0
- package/dist/templates/plopfiles/_modules/cloud/azure/templates/azure.tf.hbs +54 -0
- package/dist/templates/plopfiles/_modules/cloud/cloudflare/module.js +43 -0
- package/dist/templates/plopfiles/_modules/cloud/cloudflare/templates/src/types.d.ts.hbs +6 -0
- package/dist/templates/plopfiles/_modules/cloud/cloudflare/templates/src/worker.ts.hbs +51 -0
- package/dist/templates/plopfiles/_modules/cloud/cloudflare/templates/wrangler.toml.hbs +36 -0
- package/dist/templates/plopfiles/_modules/cloud/gcp/module.js +31 -0
- package/dist/templates/plopfiles/_modules/cloud/gcp/templates/gcp-cloudrun.tf.hbs +89 -0
- package/dist/templates/plopfiles/_modules/cloud/gcp/templates/gcp.tf.hbs +62 -0
- package/dist/templates/plopfiles/_modules/cloud/supabase/module.js +51 -0
- package/dist/templates/plopfiles/_modules/cloud/supabase/templates/src/lib/supabase.ts.hbs +25 -0
- package/dist/templates/plopfiles/_modules/cloud/supabase/templates/src/types/supabase.ts.hbs +52 -0
- package/dist/templates/plopfiles/_modules/cloud/supabase/templates/supabase/config.toml.hbs +71 -0
- package/dist/templates/plopfiles/_modules/cloud/supabase/templates/supabase/functions/hello/index.ts.hbs +34 -0
- package/dist/templates/plopfiles/_modules/cloud/supabase/templates/supabase/migrations/00000000000000_init.sql.hbs +60 -0
- package/dist/templates/plopfiles/_modules/composer.js +180 -0
- package/dist/templates/plopfiles/_modules/databases/postgres-drizzle/drizzle.config.ts.hbs +10 -0
- package/dist/templates/plopfiles/_modules/databases/postgres-drizzle/module.js +53 -0
- package/dist/templates/plopfiles/_modules/databases/postgres-drizzle/templates/index.ts.hbs +15 -0
- package/dist/templates/plopfiles/_modules/databases/postgres-drizzle/templates/schema.ts.hbs +21 -0
- package/dist/templates/plopfiles/_modules/desktop/electron/module.js +51 -0
- package/dist/templates/plopfiles/_modules/desktop/electron/templates/forge.config.ts.hbs +38 -0
- package/dist/templates/plopfiles/_modules/desktop/electron/templates/package.json.hbs +22 -0
- package/dist/templates/plopfiles/_modules/desktop/electron/templates/src/main.ts.hbs +48 -0
- package/dist/templates/plopfiles/_modules/desktop/electron/templates/src/preload.ts.hbs +18 -0
- package/dist/templates/plopfiles/_modules/desktop/electron/templates/tsconfig.json.hbs +14 -0
- package/dist/templates/plopfiles/_modules/desktop/tauri/module.js +38 -0
- package/dist/templates/plopfiles/_modules/desktop/tauri/templates/src-tauri/Cargo.toml.hbs +20 -0
- package/dist/templates/plopfiles/_modules/desktop/tauri/templates/src-tauri/build.rs.hbs +3 -0
- package/dist/templates/plopfiles/_modules/desktop/tauri/templates/src-tauri/src/main.rs.hbs +30 -0
- package/dist/templates/plopfiles/_modules/desktop/tauri/templates/src-tauri/tauri.conf.json.hbs +44 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/module.js +61 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/architecture/components.md.hbs +37 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/architecture/overview.md.hbs +43 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/contributing/development.md.hbs +164 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/contributing/guidelines.md.hbs +50 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/getting-started/configuration.md.hbs +37 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/getting-started/installation.md.hbs +53 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/getting-started/quickstart.md.hbs +39 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/index.md.hbs +44 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/javascripts/extra.js.hbs +23 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/stylesheets/extra.css.hbs +51 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/mkdocs.yml.hbs +141 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/requirements-docs.txt.hbs +15 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/scripts/aggregate-docs.mjs.hbs +188 -0
- package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/scripts/gen_ref_pages.py.hbs +54 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/module.js +60 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/index.html.hbs +13 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/nginx.conf.hbs +31 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/postcss.config.js.hbs +6 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/src/App.tsx.hbs +12 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/src/index.css.hbs +25 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/src/lib/api.ts.hbs +52 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/src/main.tsx.hbs +18 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/src/pages/Home.tsx.hbs +31 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/tailwind.config.js.hbs +8 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/tsconfig.json.hbs +25 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/tsconfig.node.json.hbs +10 -0
- package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/vite.config.ts.hbs +21 -0
- package/dist/templates/plopfiles/_modules/frontends/solid-vite/module.js +51 -0
- package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/index.html.hbs +13 -0
- package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/postcss.config.js.hbs +6 -0
- package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/src/App.tsx.hbs +12 -0
- package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/src/index.css.hbs +3 -0
- package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/src/index.tsx.hbs +21 -0
- package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/src/lib/api.ts.hbs +52 -0
- package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/src/pages/Home.tsx.hbs +34 -0
- package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/tailwind.config.js.hbs +8 -0
- package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/tsconfig.json.hbs +24 -0
- package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/vite.config.ts.hbs +21 -0
- package/dist/templates/plopfiles/_modules/frontends/vue-vite/module.js +54 -0
- package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/index.html.hbs +13 -0
- package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/postcss.config.js.hbs +6 -0
- package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/src/App.vue.hbs +9 -0
- package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/src/index.css.hbs +3 -0
- package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/src/lib/api.ts.hbs +52 -0
- package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/src/main.ts.hbs +14 -0
- package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/src/pages/Home.vue.hbs +27 -0
- package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/src/router.ts.hbs +13 -0
- package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/tailwind.config.js.hbs +8 -0
- package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/tsconfig.json.hbs +25 -0
- package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/vite.config.ts.hbs +21 -0
- package/dist/templates/plopfiles/_modules/infra/docker-compose/module.js +52 -0
- package/dist/templates/plopfiles/_modules/infra/docker-compose/templates/Dockerfile.backend.hbs +32 -0
- package/dist/templates/plopfiles/_modules/infra/docker-compose/templates/Dockerfile.frontend.hbs +31 -0
- package/dist/templates/plopfiles/_modules/infra/docker-compose/templates/docker-compose.yml.hbs +97 -0
- package/dist/templates/plopfiles/_modules/infra/github-actions/module.js +24 -0
- package/dist/templates/plopfiles/_modules/infra/github-actions/templates/.github/workflows/ci.yml.hbs +78 -0
- package/dist/templates/plopfiles/_modules/infra/github-actions/templates/.github/workflows/deploy.yml.hbs +60 -0
- package/dist/templates/plopfiles/_modules/infra/just/module.js +26 -0
- package/dist/templates/plopfiles/_modules/infra/just/templates/justfile.hbs +205 -0
- package/dist/templates/plopfiles/_modules/infra/kubernetes/module.js +30 -0
- package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/base/deployment.yaml.hbs +48 -0
- package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/base/ingress.yaml.hbs +23 -0
- package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/base/kustomization.yaml.hbs +11 -0
- package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/base/service.yaml.hbs +15 -0
- package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/overlays/dev/deployment-patch.yaml.hbs +17 -0
- package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/overlays/dev/kustomization.yaml.hbs +15 -0
- package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/overlays/prod/deployment-patch.yaml.hbs +17 -0
- package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/overlays/prod/kustomization.yaml.hbs +15 -0
- package/dist/templates/plopfiles/_modules/infra/pulumi/module.js +30 -0
- package/dist/templates/plopfiles/_modules/infra/pulumi/templates/.gitignore.hbs +5 -0
- package/dist/templates/plopfiles/_modules/infra/pulumi/templates/Pulumi.dev.yaml.hbs +3 -0
- package/dist/templates/plopfiles/_modules/infra/pulumi/templates/Pulumi.yaml.hbs +7 -0
- package/dist/templates/plopfiles/_modules/infra/pulumi/templates/index.ts.hbs +26 -0
- package/dist/templates/plopfiles/_modules/infra/pulumi/templates/package.json.hbs +12 -0
- package/dist/templates/plopfiles/_modules/infra/pulumi/templates/tsconfig.json.hbs +16 -0
- package/dist/templates/plopfiles/_modules/infra/terraform/module.js +31 -0
- package/dist/templates/plopfiles/_modules/infra/terraform/templates/.gitignore.hbs +12 -0
- package/dist/templates/plopfiles/_modules/infra/terraform/templates/main.tf.hbs +34 -0
- package/dist/templates/plopfiles/_modules/infra/terraform/templates/outputs.tf.hbs +15 -0
- package/dist/templates/plopfiles/_modules/infra/terraform/templates/terraform.tfvars.example.hbs +6 -0
- package/dist/templates/plopfiles/_modules/infra/terraform/templates/variables.tf.hbs +30 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/module.js +38 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/build.gradle.kts.hbs +102 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/proguard-rules.pro.hbs +20 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/AndroidManifest.xml.hbs +25 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/kotlin/com/example/app/ApiClient.kt.hbs +38 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/kotlin/com/example/app/App.kt.hbs +116 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/kotlin/com/example/app/MainActivity.kt.hbs +28 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/kotlin/com/example/app/ui/theme/Color.kt.hbs +11 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/kotlin/com/example/app/ui/theme/Theme.kt.hbs +55 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/kotlin/com/example/app/ui/theme/Type.kt.hbs +31 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/res/values/strings.xml.hbs +3 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/res/values/themes.xml.hbs +4 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/build.gradle.kts.hbs +6 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/gradle.properties.hbs +4 -0
- package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/settings.gradle.kts.hbs +18 -0
- package/dist/templates/plopfiles/_modules/mobile/flutter/module.js +36 -0
- package/dist/templates/plopfiles/_modules/mobile/flutter/templates/.env.hbs +1 -0
- package/dist/templates/plopfiles/_modules/mobile/flutter/templates/lib/main.dart.hbs +29 -0
- package/dist/templates/plopfiles/_modules/mobile/flutter/templates/lib/screens/home_screen.dart.hbs +88 -0
- package/dist/templates/plopfiles/_modules/mobile/flutter/templates/lib/services/api_service.dart.hbs +61 -0
- package/dist/templates/plopfiles/_modules/mobile/flutter/templates/pubspec.yaml.hbs +25 -0
- package/dist/templates/plopfiles/_modules/mobile/ios-swift/module.js +39 -0
- package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/App.swift.hbs +10 -0
- package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/Assets.xcassets/AccentColor.colorset/Contents.json.hbs +11 -0
- package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/Assets.xcassets/AppIcon.appiconset/Contents.json.hbs +13 -0
- package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/Assets.xcassets/Contents.json.hbs +6 -0
- package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/ContentView.swift.hbs +13 -0
- package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/Services/APIClient.swift.hbs +58 -0
- package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/Views/DetailsView.swift.hbs +22 -0
- package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/Views/HomeView.swift.hbs +62 -0
- package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App.xcodeproj/project.pbxproj.hbs +363 -0
- package/dist/templates/plopfiles/_modules/mobile/react-native/module.js +36 -0
- package/dist/templates/plopfiles/_modules/mobile/react-native/templates/app/_layout.tsx.hbs +16 -0
- package/dist/templates/plopfiles/_modules/mobile/react-native/templates/app/index.tsx.hbs +66 -0
- package/dist/templates/plopfiles/_modules/mobile/react-native/templates/app.json.hbs +37 -0
- package/dist/templates/plopfiles/_modules/mobile/react-native/templates/package.json.hbs +29 -0
- package/dist/templates/plopfiles/_modules/mobile/react-native/templates/src/lib/api.ts.hbs +52 -0
- package/dist/templates/plopfiles/_modules/mobile/react-native/templates/tsconfig.json.hbs +11 -0
- package/dist/templates/plopfiles/_modules/quality/biome/module.js +44 -0
- package/dist/templates/plopfiles/_modules/quality/biome/templates/biome.json.hbs +126 -0
- package/dist/templates/plopfiles/_modules/quality/clippy/module.js +40 -0
- package/dist/templates/plopfiles/_modules/quality/clippy/templates/clippy.toml.hbs +29 -0
- package/dist/templates/plopfiles/_modules/quality/clippy/templates/rustfmt.toml.hbs +44 -0
- package/dist/templates/plopfiles/_modules/quality/golangci-lint/module.js +40 -0
- package/dist/templates/plopfiles/_modules/quality/golangci-lint/templates/.golangci.yml.hbs +199 -0
- package/dist/templates/plopfiles/_modules/quality/ruff/module.js +43 -0
- package/dist/templates/plopfiles/_modules/quality/ruff/templates/mypy.ini.hbs +48 -0
- package/dist/templates/plopfiles/_modules/quality/ruff/templates/ruff.toml.hbs +111 -0
- package/dist/templates/plopfiles/_modules/storybook/react-storybook/module.js +54 -0
- package/dist/templates/plopfiles/_modules/storybook/react-storybook/templates/.storybook/main.ts.hbs +44 -0
- package/dist/templates/plopfiles/_modules/storybook/react-storybook/templates/.storybook/preview.ts.hbs +68 -0
- package/dist/templates/plopfiles/_modules/storybook/react-storybook/templates/src/stories/Button.stories.tsx.hbs +158 -0
- package/dist/templates/plopfiles/_modules/types.ts +98 -0
- package/dist/templates/plopfiles/full-stack/plopfile.js +132 -0
- package/dist/templates/plopfiles/full-stack/templates/gitignore.hbs +44 -0
- package/dist/templates/plopfiles/full-stack/templates/package.json.hbs +21 -0
- package/dist/templates/plopfiles/presets/cli-go/module.js +43 -0
- package/dist/templates/plopfiles/presets/cli-go/templates/README.md.hbs +42 -0
- package/dist/templates/plopfiles/presets/cli-go/templates/config.json.hbs +9 -0
- package/dist/templates/plopfiles/presets/cli-node/module.js +45 -0
- package/dist/templates/plopfiles/presets/cli-node/templates/README.md.hbs +42 -0
- package/dist/templates/plopfiles/presets/cli-node/templates/config.json.hbs +9 -0
- package/dist/templates/plopfiles/presets/cli-python/module.js +43 -0
- package/dist/templates/plopfiles/presets/cli-python/templates/README.md.hbs +42 -0
- package/dist/templates/plopfiles/presets/cli-python/templates/config.json.hbs +9 -0
- package/dist/templates/plopfiles/presets/cli-rust/module.js +43 -0
- package/dist/templates/plopfiles/presets/cli-rust/templates/README.md.hbs +42 -0
- package/dist/templates/plopfiles/presets/cli-rust/templates/config.json.hbs +9 -0
- package/dist/templates/plopfiles/presets/loader.js +125 -0
- package/dist/templates/plopfiles/typescript-service/plopfile.js +186 -0
- package/dist/templates/plopfiles/typescript-service/templates/Dockerfile.hbs +29 -0
- package/dist/templates/plopfiles/typescript-service/templates/README.md.hbs +85 -0
- package/dist/templates/plopfiles/typescript-service/templates/config.ts.hbs +15 -0
- package/dist/templates/plopfiles/typescript-service/templates/db/index.ts.hbs +19 -0
- package/dist/templates/plopfiles/typescript-service/templates/db/schema.ts.hbs +16 -0
- package/dist/templates/plopfiles/typescript-service/templates/index.ts.hbs +89 -0
- package/dist/templates/plopfiles/typescript-service/templates/package.json.hbs +36 -0
- package/dist/templates/plopfiles/typescript-service/templates/routes/health.ts.hbs +61 -0
- package/dist/templates/plopfiles/typescript-service/templates/routes/index.ts.hbs +35 -0
- package/dist/templates/plopfiles/typescript-service/templates/tsconfig.json.hbs +24 -0
- package/dist/templates/plopfiles/typescript-service/templates/types.ts.hbs +28 -0
- package/dist/templates/typescript/component.module.css.tpl +6 -0
- package/dist/templates/typescript/component.test.tsx.tpl +9 -0
- package/dist/templates/typescript/component.tsx.tpl +17 -0
- package/dist/templates/typescript/component.types.ts.tpl +3 -0
- package/dist/templates/typescript/project/nextjs/app/globals.css.tpl +10 -0
- package/dist/templates/typescript/project/nextjs/app/layout.tsx.tpl +15 -0
- package/dist/templates/typescript/project/nextjs/app/page.tsx.tpl +8 -0
- package/dist/templates/typescript/project/nextjs/babel.config.js.tpl +8 -0
- package/dist/templates/typescript/project/nextjs/jest.config.js.tpl +10 -0
- package/dist/templates/typescript/project/nextjs/jest.setup.ts.tpl +6 -0
- package/dist/templates/typescript/project/nextjs/next-env.d.ts.tpl +5 -0
- package/dist/templates/typescript/project/nextjs/next.config.js.tpl +9 -0
- package/dist/templates/typescript/project/nextjs/package.json.tpl +1 -0
- package/dist/templates/typescript/project/nextjs/tsconfig.json.tpl +1 -0
- package/dist/templates/typescript/project/vite/App.css.tpl +8 -0
- package/dist/templates/typescript/project/vite/App.tsx.tpl +17 -0
- package/dist/templates/typescript/project/vite/index.css.tpl +8 -0
- package/dist/templates/typescript/project/vite/index.html.tpl +13 -0
- package/dist/templates/typescript/project/vite/main.tsx.tpl +10 -0
- package/dist/templates/typescript/project/vite/package.json.tpl +1 -0
- package/dist/templates/typescript/project/vite/test-setup.ts.tpl +1 -0
- package/dist/templates/typescript/project/vite/tsconfig.build.json.tpl +1 -0
- package/dist/templates/typescript/project/vite/tsconfig.json.tpl +1 -0
- package/dist/templates/typescript/project/vite/tsconfig.node.json.tpl +1 -0
- package/dist/templates/typescript/project/vite/vite.config.ts.tpl +18 -0
- package/dist/templates/typescript/service.api.ts.tpl +27 -0
- package/dist/templates/typescript/service.class.ts.tpl +28 -0
- package/dist/templates/typescript/service.handler.ts.tpl +42 -0
- package/dist/templates/typescript/service.schema.ts.tpl +16 -0
- package/package.json +95 -0
- package/templates/typescript/component.module.css.tpl +6 -0
- package/templates/typescript/component.test.tsx.tpl +9 -0
- package/templates/typescript/component.tsx.tpl +17 -0
- package/templates/typescript/component.types.ts.tpl +3 -0
- package/templates/typescript/project/nextjs/app/globals.css.tpl +10 -0
- package/templates/typescript/project/nextjs/app/layout.tsx.tpl +15 -0
- package/templates/typescript/project/nextjs/app/page.tsx.tpl +8 -0
- package/templates/typescript/project/nextjs/babel.config.js.tpl +8 -0
- package/templates/typescript/project/nextjs/jest.config.js.tpl +10 -0
- package/templates/typescript/project/nextjs/jest.setup.ts.tpl +6 -0
- package/templates/typescript/project/nextjs/next-env.d.ts.tpl +5 -0
- package/templates/typescript/project/nextjs/next.config.js.tpl +9 -0
- package/templates/typescript/project/nextjs/package.json.tpl +1 -0
- package/templates/typescript/project/nextjs/tsconfig.json.tpl +1 -0
- package/templates/typescript/project/vite/App.css.tpl +8 -0
- package/templates/typescript/project/vite/App.tsx.tpl +17 -0
- package/templates/typescript/project/vite/index.css.tpl +8 -0
- package/templates/typescript/project/vite/index.html.tpl +13 -0
- package/templates/typescript/project/vite/main.tsx.tpl +10 -0
- package/templates/typescript/project/vite/package.json.tpl +1 -0
- package/templates/typescript/project/vite/test-setup.ts.tpl +1 -0
- package/templates/typescript/project/vite/tsconfig.build.json.tpl +1 -0
- package/templates/typescript/project/vite/tsconfig.json.tpl +1 -0
- package/templates/typescript/project/vite/tsconfig.node.json.tpl +1 -0
- package/templates/typescript/project/vite/vite.config.ts.tpl +18 -0
- package/templates/typescript/service.api.ts.tpl +27 -0
- package/templates/typescript/service.class.ts.tpl +28 -0
- package/templates/typescript/service.handler.ts.tpl +42 -0
- package/templates/typescript/service.schema.ts.tpl +16 -0
|
@@ -0,0 +1,854 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infrastructure Linting Tests
|
|
3
|
+
*
|
|
4
|
+
* Uses specialized linters to validate generated infrastructure files.
|
|
5
|
+
* Tests are skipped if the linter isn't available.
|
|
6
|
+
*
|
|
7
|
+
* Supported linters:
|
|
8
|
+
* - terraform: terraform validate, tflint
|
|
9
|
+
* - kubernetes: kubectl --dry-run, kubeconform
|
|
10
|
+
* - docker: docker compose config, hadolint
|
|
11
|
+
* - github actions: actionlint
|
|
12
|
+
* - yaml: yamllint
|
|
13
|
+
*
|
|
14
|
+
* Run with: bun test linting.test.ts
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
18
|
+
import { tmpdir } from "os";
|
|
19
|
+
import { dirname, join } from "path";
|
|
20
|
+
import { fileURLToPath } from "url";
|
|
21
|
+
import { $ } from "bun";
|
|
22
|
+
import { mkdir, mkdtemp, readdir, rm } from "fs/promises";
|
|
23
|
+
import nodePlop from "node-plop";
|
|
24
|
+
import { loadModule } from "../_modules/composer.js";
|
|
25
|
+
|
|
26
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
|
|
28
|
+
const LINT_TIMEOUT = 60_000;
|
|
29
|
+
|
|
30
|
+
// Helper to execute plop actions
|
|
31
|
+
async function executePlopActions(
|
|
32
|
+
destPath: string,
|
|
33
|
+
actions: Array<Record<string, unknown>>,
|
|
34
|
+
context: Record<string, unknown>,
|
|
35
|
+
) {
|
|
36
|
+
const plop = await nodePlop(undefined, {
|
|
37
|
+
destBasePath: destPath,
|
|
38
|
+
force: true,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Register helpers on plop's Handlebars instance
|
|
42
|
+
plop.setHelper(
|
|
43
|
+
"kebabCase",
|
|
44
|
+
(str: string) =>
|
|
45
|
+
str
|
|
46
|
+
?.toLowerCase()
|
|
47
|
+
.replace(/\s+/g, "-")
|
|
48
|
+
.replace(/[^a-z0-9-]/g, "") || "",
|
|
49
|
+
);
|
|
50
|
+
plop.setHelper(
|
|
51
|
+
"snakeCase",
|
|
52
|
+
(str: string) =>
|
|
53
|
+
str
|
|
54
|
+
?.toLowerCase()
|
|
55
|
+
.replace(/[\s-]+/g, "_")
|
|
56
|
+
.replace(/[^a-z0-9_]/g, "") || "",
|
|
57
|
+
);
|
|
58
|
+
plop.setHelper("camelCase", (str: string) => {
|
|
59
|
+
if (!str) return "";
|
|
60
|
+
return str.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (_, chr: string) => chr.toUpperCase());
|
|
61
|
+
});
|
|
62
|
+
plop.setHelper("pascalCase", (str: string) => {
|
|
63
|
+
if (!str) return "";
|
|
64
|
+
const camel = str
|
|
65
|
+
.toLowerCase()
|
|
66
|
+
.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr: string) => chr.toUpperCase());
|
|
67
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
68
|
+
});
|
|
69
|
+
plop.setHelper(
|
|
70
|
+
"titleCase",
|
|
71
|
+
(str: string) => str?.replace(/\b\w/g, (char) => char.toUpperCase()) || "",
|
|
72
|
+
);
|
|
73
|
+
plop.setHelper("eq", (a: unknown, b: unknown) => a === b);
|
|
74
|
+
plop.setHelper("ne", (a: unknown, b: unknown) => a !== b);
|
|
75
|
+
plop.setHelper("json", (obj: unknown) => JSON.stringify(obj, null, 2));
|
|
76
|
+
|
|
77
|
+
plop.setGenerator("temp", {
|
|
78
|
+
description: "temp",
|
|
79
|
+
prompts: [],
|
|
80
|
+
actions: actions as any[],
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const gen = plop.getGenerator("temp");
|
|
84
|
+
const results = await gen.runActions(context);
|
|
85
|
+
|
|
86
|
+
if (results.failures && results.failures.length > 0) {
|
|
87
|
+
const errors = results.failures.map((f) => f.error || f.message || String(f)).join("\n");
|
|
88
|
+
throw new Error(`Generation failed:\n${errors}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return results;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Helper to generate a single module
|
|
95
|
+
async function generateModule(
|
|
96
|
+
category: string,
|
|
97
|
+
moduleName: string,
|
|
98
|
+
destPath: string,
|
|
99
|
+
context: Record<string, unknown>,
|
|
100
|
+
) {
|
|
101
|
+
const mod = await loadModule(category, moduleName);
|
|
102
|
+
const actions = mod.default(context);
|
|
103
|
+
await executePlopActions(destPath, actions, context);
|
|
104
|
+
return mod;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Helper to check if a command exists
|
|
108
|
+
async function commandExists(cmd: string): Promise<boolean> {
|
|
109
|
+
try {
|
|
110
|
+
await $`which ${cmd}`.quiet();
|
|
111
|
+
return true;
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Track available linters
|
|
118
|
+
const linters: Record<string, boolean> = {};
|
|
119
|
+
|
|
120
|
+
beforeAll(async () => {
|
|
121
|
+
linters.terraform = await commandExists("terraform");
|
|
122
|
+
linters.tflint = await commandExists("tflint");
|
|
123
|
+
linters.kubectl = await commandExists("kubectl");
|
|
124
|
+
linters.kubeconform = await commandExists("kubeconform");
|
|
125
|
+
linters.docker = await commandExists("docker");
|
|
126
|
+
linters.hadolint = await commandExists("hadolint");
|
|
127
|
+
linters.actionlint = await commandExists("actionlint");
|
|
128
|
+
linters.yamllint = await commandExists("yamllint");
|
|
129
|
+
|
|
130
|
+
console.log(
|
|
131
|
+
"Available linters:",
|
|
132
|
+
Object.entries(linters)
|
|
133
|
+
.filter(([_, v]) => v)
|
|
134
|
+
.map(([k]) => k)
|
|
135
|
+
.join(", ") || "none",
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("Terraform Linting", () => {
|
|
140
|
+
let tempDir: string;
|
|
141
|
+
|
|
142
|
+
beforeAll(async () => {
|
|
143
|
+
tempDir = await mkdtemp(join(tmpdir(), "arbiter-lint-tf-"));
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
afterAll(async () => {
|
|
147
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test(
|
|
151
|
+
"terraform validate passes",
|
|
152
|
+
async () => {
|
|
153
|
+
if (!linters.terraform) {
|
|
154
|
+
console.log("Skipping: terraform not available");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const projectDir = join(tempDir, "tf-validate");
|
|
159
|
+
await mkdir(projectDir, { recursive: true });
|
|
160
|
+
|
|
161
|
+
const context = { name: "tf-validate", projectDir };
|
|
162
|
+
await generateModule("infra", "terraform", projectDir, context);
|
|
163
|
+
|
|
164
|
+
const tfDir = join(projectDir, "infra/terraform");
|
|
165
|
+
|
|
166
|
+
// Init and validate
|
|
167
|
+
const initResult = await $`cd ${tfDir} && terraform init -backend=false`.nothrow();
|
|
168
|
+
expect(initResult.exitCode).toBe(0);
|
|
169
|
+
|
|
170
|
+
const validateResult = await $`cd ${tfDir} && terraform validate`.nothrow();
|
|
171
|
+
expect(validateResult.exitCode).toBe(0);
|
|
172
|
+
|
|
173
|
+
// Check formatting
|
|
174
|
+
const fmtResult = await $`cd ${tfDir} && terraform fmt -check -recursive`.nothrow();
|
|
175
|
+
if (fmtResult.exitCode !== 0) {
|
|
176
|
+
console.warn("Terraform formatting issues detected (non-blocking)");
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
LINT_TIMEOUT,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
test(
|
|
183
|
+
"tflint passes",
|
|
184
|
+
async () => {
|
|
185
|
+
if (!linters.tflint) {
|
|
186
|
+
console.log("Skipping: tflint not available");
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const projectDir = join(tempDir, "tf-tflint");
|
|
191
|
+
await mkdir(projectDir, { recursive: true });
|
|
192
|
+
|
|
193
|
+
const context = { name: "tf-tflint", projectDir };
|
|
194
|
+
await generateModule("infra", "terraform", projectDir, context);
|
|
195
|
+
|
|
196
|
+
const tfDir = join(projectDir, "infra/terraform");
|
|
197
|
+
|
|
198
|
+
// Run tflint
|
|
199
|
+
const result = await $`cd ${tfDir} && tflint --init && tflint`.nothrow();
|
|
200
|
+
// tflint may have warnings, exit 0 or 2 is acceptable
|
|
201
|
+
expect(result.exitCode).toBeLessThanOrEqual(2);
|
|
202
|
+
},
|
|
203
|
+
LINT_TIMEOUT,
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe("Kubernetes Linting", () => {
|
|
208
|
+
let tempDir: string;
|
|
209
|
+
|
|
210
|
+
beforeAll(async () => {
|
|
211
|
+
tempDir = await mkdtemp(join(tmpdir(), "arbiter-lint-k8s-"));
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
afterAll(async () => {
|
|
215
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test(
|
|
219
|
+
"kubectl dry-run validates manifests",
|
|
220
|
+
async () => {
|
|
221
|
+
if (!linters.kubectl) {
|
|
222
|
+
console.log("Skipping: kubectl not available");
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check if kubectl can actually connect to a cluster (not just have a config)
|
|
227
|
+
// Use a simple API call that requires a working connection
|
|
228
|
+
const clusterCheck = await $`kubectl get --raw /api/v1 2>&1`.quiet().nothrow();
|
|
229
|
+
if (clusterCheck.exitCode !== 0) {
|
|
230
|
+
console.log(
|
|
231
|
+
"Skipping: No Kubernetes cluster connection (use kubeconform for offline validation)",
|
|
232
|
+
);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const projectDir = join(tempDir, "k8s-kubectl");
|
|
237
|
+
await mkdir(projectDir, { recursive: true });
|
|
238
|
+
|
|
239
|
+
const context = { name: "k8s-kubectl", projectDir, backendDir: "backend" };
|
|
240
|
+
await generateModule("infra", "kubernetes", projectDir, context);
|
|
241
|
+
|
|
242
|
+
const k8sDir = join(projectDir, "k8s/base");
|
|
243
|
+
|
|
244
|
+
// Validate each yaml file (skip kustomization.yaml and ingress.yaml - they need cluster CRDs)
|
|
245
|
+
const files = await readdir(k8sDir);
|
|
246
|
+
const yamlFiles = files.filter(
|
|
247
|
+
(f) =>
|
|
248
|
+
(f.endsWith(".yaml") || f.endsWith(".yml")) &&
|
|
249
|
+
!f.startsWith("kustomization") &&
|
|
250
|
+
!f.includes("ingress"), // ingress needs networking.k8s.io CRD from cluster
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
for (const file of yamlFiles) {
|
|
254
|
+
const result =
|
|
255
|
+
await $`kubectl apply --dry-run=client -f ${join(k8sDir, file)} 2>&1`.nothrow();
|
|
256
|
+
if (result.exitCode !== 0) {
|
|
257
|
+
console.error(`kubectl validation failed for ${file}:`, result.stdout.toString());
|
|
258
|
+
}
|
|
259
|
+
expect(result.exitCode).toBe(0);
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
LINT_TIMEOUT,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
test(
|
|
266
|
+
"kubeconform validates against schemas",
|
|
267
|
+
async () => {
|
|
268
|
+
if (!linters.kubeconform) {
|
|
269
|
+
console.log("Skipping: kubeconform not available");
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const projectDir = join(tempDir, "k8s-kubeconform");
|
|
274
|
+
await mkdir(projectDir, { recursive: true });
|
|
275
|
+
|
|
276
|
+
const context = { name: "k8s-kubeconform", projectDir, backendDir: "backend" };
|
|
277
|
+
await generateModule("infra", "kubernetes", projectDir, context);
|
|
278
|
+
|
|
279
|
+
const k8sDir = join(projectDir, "k8s/base");
|
|
280
|
+
|
|
281
|
+
// Run kubeconform, skipping Kustomization CRDs (not in standard schemas)
|
|
282
|
+
const result = await $`kubeconform -summary -skip Kustomization ${k8sDir}`.nothrow();
|
|
283
|
+
expect(result.exitCode).toBe(0);
|
|
284
|
+
},
|
|
285
|
+
LINT_TIMEOUT,
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
describe("Docker Linting", () => {
|
|
290
|
+
let tempDir: string;
|
|
291
|
+
|
|
292
|
+
beforeAll(async () => {
|
|
293
|
+
tempDir = await mkdtemp(join(tmpdir(), "arbiter-lint-docker-"));
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
afterAll(async () => {
|
|
297
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test(
|
|
301
|
+
"docker compose config validates",
|
|
302
|
+
async () => {
|
|
303
|
+
if (!linters.docker) {
|
|
304
|
+
console.log("Skipping: docker not available");
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const projectDir = join(tempDir, "docker-compose");
|
|
309
|
+
await mkdir(projectDir, { recursive: true });
|
|
310
|
+
|
|
311
|
+
const context = {
|
|
312
|
+
name: "docker-compose",
|
|
313
|
+
projectDir,
|
|
314
|
+
backendDir: "backend",
|
|
315
|
+
frontendDir: "frontend",
|
|
316
|
+
backend: "node-hono",
|
|
317
|
+
frontend: "react-vite",
|
|
318
|
+
database: "postgres-drizzle",
|
|
319
|
+
};
|
|
320
|
+
await generateModule("infra", "docker-compose", projectDir, context);
|
|
321
|
+
|
|
322
|
+
// Validate docker-compose.yml
|
|
323
|
+
const result = await $`cd ${projectDir} && docker compose config`.nothrow();
|
|
324
|
+
expect(result.exitCode).toBe(0);
|
|
325
|
+
},
|
|
326
|
+
LINT_TIMEOUT,
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
test(
|
|
330
|
+
"hadolint validates Dockerfiles",
|
|
331
|
+
async () => {
|
|
332
|
+
if (!linters.hadolint) {
|
|
333
|
+
console.log("Skipping: hadolint not available");
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const projectDir = join(tempDir, "hadolint");
|
|
338
|
+
await mkdir(projectDir, { recursive: true });
|
|
339
|
+
|
|
340
|
+
// Generate a backend that includes a Dockerfile
|
|
341
|
+
const context = { name: "hadolint", projectDir, backendDir: "backend" };
|
|
342
|
+
await generateModule("backends", "node-hono", projectDir, context);
|
|
343
|
+
|
|
344
|
+
const dockerfilePath = join(projectDir, "backend/Dockerfile");
|
|
345
|
+
|
|
346
|
+
// Check if Dockerfile exists
|
|
347
|
+
const dockerfileExists = await Bun.file(dockerfilePath).exists();
|
|
348
|
+
if (!dockerfileExists) {
|
|
349
|
+
console.log("Skipping: No Dockerfile in node-hono module");
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Run hadolint
|
|
354
|
+
const result = await $`hadolint ${dockerfilePath}`.nothrow();
|
|
355
|
+
// hadolint may have warnings (exit 1), only fail on errors (exit 2+)
|
|
356
|
+
expect(result.exitCode).toBeLessThanOrEqual(1);
|
|
357
|
+
},
|
|
358
|
+
LINT_TIMEOUT,
|
|
359
|
+
);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
describe("GitHub Actions Linting", () => {
|
|
363
|
+
let tempDir: string;
|
|
364
|
+
|
|
365
|
+
beforeAll(async () => {
|
|
366
|
+
tempDir = await mkdtemp(join(tmpdir(), "arbiter-lint-gha-"));
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
afterAll(async () => {
|
|
370
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
test(
|
|
374
|
+
"actionlint validates workflow files",
|
|
375
|
+
async () => {
|
|
376
|
+
if (!linters.actionlint) {
|
|
377
|
+
console.log("Skipping: actionlint not available");
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const projectDir = join(tempDir, "gha");
|
|
382
|
+
await mkdir(projectDir, { recursive: true });
|
|
383
|
+
|
|
384
|
+
const context = {
|
|
385
|
+
name: "gha",
|
|
386
|
+
projectDir,
|
|
387
|
+
backendDir: "backend",
|
|
388
|
+
frontendDir: "frontend",
|
|
389
|
+
backend: "node-hono",
|
|
390
|
+
frontend: "react-vite",
|
|
391
|
+
};
|
|
392
|
+
await generateModule("infra", "github-actions", projectDir, context);
|
|
393
|
+
|
|
394
|
+
const workflowsDir = join(projectDir, ".github/workflows");
|
|
395
|
+
|
|
396
|
+
// List workflow files first (glob doesn't expand in bun shell)
|
|
397
|
+
let files: string[];
|
|
398
|
+
try {
|
|
399
|
+
files = await readdir(workflowsDir);
|
|
400
|
+
} catch (e) {
|
|
401
|
+
// Directory may not exist if generation failed
|
|
402
|
+
console.log("Skipping: Workflows directory not generated");
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const workflowFiles = files
|
|
407
|
+
.filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"))
|
|
408
|
+
.map((f) => join(workflowsDir, f));
|
|
409
|
+
|
|
410
|
+
if (workflowFiles.length === 0) {
|
|
411
|
+
console.log("Skipping: No workflow files generated");
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Run actionlint on each file
|
|
416
|
+
for (const file of workflowFiles) {
|
|
417
|
+
const result = await $`actionlint ${file}`.nothrow();
|
|
418
|
+
if (result.exitCode !== 0) {
|
|
419
|
+
console.error(`actionlint errors in ${file}:`, result.stderr.toString());
|
|
420
|
+
}
|
|
421
|
+
expect(result.exitCode).toBe(0);
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
LINT_TIMEOUT,
|
|
425
|
+
);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
describe("YAML Linting", () => {
|
|
429
|
+
let tempDir: string;
|
|
430
|
+
|
|
431
|
+
beforeAll(async () => {
|
|
432
|
+
tempDir = await mkdtemp(join(tmpdir(), "arbiter-lint-yaml-"));
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
afterAll(async () => {
|
|
436
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
test(
|
|
440
|
+
"yamllint validates kubernetes manifests",
|
|
441
|
+
async () => {
|
|
442
|
+
if (!linters.yamllint) {
|
|
443
|
+
console.log("Skipping: yamllint not available");
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const projectDir = join(tempDir, "yaml-k8s");
|
|
448
|
+
await mkdir(projectDir, { recursive: true });
|
|
449
|
+
|
|
450
|
+
const context = { name: "yaml-k8s", projectDir, backendDir: "backend" };
|
|
451
|
+
await generateModule("infra", "kubernetes", projectDir, context);
|
|
452
|
+
|
|
453
|
+
const k8sDir = join(projectDir, "k8s/base");
|
|
454
|
+
|
|
455
|
+
// Run yamllint with relaxed rules (allow long lines, etc)
|
|
456
|
+
const result = await $`yamllint -d relaxed ${k8sDir}`.nothrow();
|
|
457
|
+
// yamllint warnings are fine (exit 1)
|
|
458
|
+
expect(result.exitCode).toBeLessThanOrEqual(1);
|
|
459
|
+
},
|
|
460
|
+
LINT_TIMEOUT,
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
test(
|
|
464
|
+
"yamllint validates docker-compose",
|
|
465
|
+
async () => {
|
|
466
|
+
if (!linters.yamllint) {
|
|
467
|
+
console.log("Skipping: yamllint not available");
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const projectDir = join(tempDir, "yaml-docker");
|
|
472
|
+
await mkdir(projectDir, { recursive: true });
|
|
473
|
+
|
|
474
|
+
const context = {
|
|
475
|
+
name: "yaml-docker",
|
|
476
|
+
projectDir,
|
|
477
|
+
backendDir: "backend",
|
|
478
|
+
frontendDir: "frontend",
|
|
479
|
+
backend: "node-hono",
|
|
480
|
+
frontend: "react-vite",
|
|
481
|
+
database: "postgres-drizzle",
|
|
482
|
+
};
|
|
483
|
+
await generateModule("infra", "docker-compose", projectDir, context);
|
|
484
|
+
|
|
485
|
+
const composeFile = join(projectDir, "docker-compose.yml");
|
|
486
|
+
|
|
487
|
+
const result = await $`yamllint -d relaxed ${composeFile}`.nothrow();
|
|
488
|
+
expect(result.exitCode).toBeLessThanOrEqual(1);
|
|
489
|
+
},
|
|
490
|
+
LINT_TIMEOUT,
|
|
491
|
+
);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
describe("Pulumi Linting", () => {
|
|
495
|
+
let tempDir: string;
|
|
496
|
+
|
|
497
|
+
beforeAll(async () => {
|
|
498
|
+
tempDir = await mkdtemp(join(tmpdir(), "arbiter-lint-pulumi-"));
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
afterAll(async () => {
|
|
502
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
test(
|
|
506
|
+
"pulumi typescript compiles",
|
|
507
|
+
async () => {
|
|
508
|
+
const projectDir = join(tempDir, "pulumi");
|
|
509
|
+
await mkdir(projectDir, { recursive: true });
|
|
510
|
+
|
|
511
|
+
const context = { name: "pulumi", projectDir };
|
|
512
|
+
await generateModule("infra", "pulumi", projectDir, context);
|
|
513
|
+
|
|
514
|
+
const pulumiDir = join(projectDir, "infra/pulumi");
|
|
515
|
+
|
|
516
|
+
// Install dependencies and typecheck
|
|
517
|
+
await $`cd ${pulumiDir} && bun install`.quiet();
|
|
518
|
+
const result = await $`cd ${pulumiDir} && bunx tsc --noEmit`.nothrow();
|
|
519
|
+
|
|
520
|
+
// TypeScript should compile without errors
|
|
521
|
+
expect(result.exitCode).toBe(0);
|
|
522
|
+
},
|
|
523
|
+
LINT_TIMEOUT,
|
|
524
|
+
);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
describe("Build System Linting", () => {
|
|
528
|
+
let tempDir: string;
|
|
529
|
+
|
|
530
|
+
beforeAll(async () => {
|
|
531
|
+
tempDir = await mkdtemp(join(tmpdir(), "arbiter-lint-build-"));
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
afterAll(async () => {
|
|
535
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
test(
|
|
539
|
+
"bazel configs are valid with buildifier",
|
|
540
|
+
async () => {
|
|
541
|
+
const buildifierAvailable = await commandExists("buildifier");
|
|
542
|
+
if (!buildifierAvailable) {
|
|
543
|
+
console.log("Skipping: buildifier not available");
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const projectDir = join(tempDir, "bazel");
|
|
548
|
+
await mkdir(projectDir, { recursive: true });
|
|
549
|
+
await mkdir(join(projectDir, "backend"), { recursive: true });
|
|
550
|
+
|
|
551
|
+
const context = {
|
|
552
|
+
name: "bazel-test",
|
|
553
|
+
projectDir,
|
|
554
|
+
backendDir: "backend",
|
|
555
|
+
backend: "go-chi",
|
|
556
|
+
frontend: "none",
|
|
557
|
+
};
|
|
558
|
+
await generateModule("build", "bazel", projectDir, context);
|
|
559
|
+
|
|
560
|
+
// Validate WORKSPACE.bazel
|
|
561
|
+
const workspaceResult =
|
|
562
|
+
await $`buildifier --lint=warn --mode=check ${join(projectDir, "WORKSPACE.bazel")}`.nothrow();
|
|
563
|
+
// buildifier returns 0 for valid, 4 for lint warnings (acceptable)
|
|
564
|
+
expect(workspaceResult.exitCode === 0 || workspaceResult.exitCode === 4).toBe(true);
|
|
565
|
+
|
|
566
|
+
// Validate BUILD.bazel files
|
|
567
|
+
const rootBuildResult =
|
|
568
|
+
await $`buildifier --lint=warn --mode=check ${join(projectDir, "BUILD.bazel")}`.nothrow();
|
|
569
|
+
expect(rootBuildResult.exitCode === 0 || rootBuildResult.exitCode === 4).toBe(true);
|
|
570
|
+
|
|
571
|
+
const backendBuildResult =
|
|
572
|
+
await $`buildifier --lint=warn --mode=check ${join(projectDir, "backend/BUILD.bazel")}`.nothrow();
|
|
573
|
+
expect(backendBuildResult.exitCode === 0 || backendBuildResult.exitCode === 4).toBe(true);
|
|
574
|
+
},
|
|
575
|
+
LINT_TIMEOUT,
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
test(
|
|
579
|
+
"nx.json is valid JSON with correct schema",
|
|
580
|
+
async () => {
|
|
581
|
+
const projectDir = join(tempDir, "nx");
|
|
582
|
+
await mkdir(projectDir, { recursive: true });
|
|
583
|
+
await mkdir(join(projectDir, "backend"), { recursive: true });
|
|
584
|
+
|
|
585
|
+
const context = {
|
|
586
|
+
name: "nx-test",
|
|
587
|
+
projectDir,
|
|
588
|
+
backendDir: "backend",
|
|
589
|
+
backend: "node-hono",
|
|
590
|
+
frontend: "none",
|
|
591
|
+
};
|
|
592
|
+
await generateModule("build", "nx", projectDir, context);
|
|
593
|
+
|
|
594
|
+
// Read and parse nx.json
|
|
595
|
+
const nxJsonPath = join(projectDir, "nx.json");
|
|
596
|
+
const nxJsonContent = await Bun.file(nxJsonPath).text();
|
|
597
|
+
|
|
598
|
+
let nxJson: any;
|
|
599
|
+
expect(() => {
|
|
600
|
+
nxJson = JSON.parse(nxJsonContent);
|
|
601
|
+
}).not.toThrow();
|
|
602
|
+
|
|
603
|
+
// Validate required fields
|
|
604
|
+
expect(nxJson.$schema).toBeDefined();
|
|
605
|
+
expect(nxJson.targetDefaults).toBeDefined();
|
|
606
|
+
expect(nxJson.targetDefaults.build).toBeDefined();
|
|
607
|
+
expect(nxJson.targetDefaults.test).toBeDefined();
|
|
608
|
+
|
|
609
|
+
// Validate project.json
|
|
610
|
+
const projectJsonPath = join(projectDir, "backend/project.json");
|
|
611
|
+
const projectJsonContent = await Bun.file(projectJsonPath).text();
|
|
612
|
+
|
|
613
|
+
let projectJson: any;
|
|
614
|
+
expect(() => {
|
|
615
|
+
projectJson = JSON.parse(projectJsonContent);
|
|
616
|
+
}).not.toThrow();
|
|
617
|
+
|
|
618
|
+
expect(projectJson.name).toBeDefined();
|
|
619
|
+
expect(projectJson.targets).toBeDefined();
|
|
620
|
+
expect(projectJson.targets.build).toBeDefined();
|
|
621
|
+
},
|
|
622
|
+
LINT_TIMEOUT,
|
|
623
|
+
);
|
|
624
|
+
|
|
625
|
+
test(
|
|
626
|
+
"turbo.json is valid JSON with correct schema",
|
|
627
|
+
async () => {
|
|
628
|
+
const projectDir = join(tempDir, "turborepo");
|
|
629
|
+
await mkdir(projectDir, { recursive: true });
|
|
630
|
+
await mkdir(join(projectDir, "backend"), { recursive: true });
|
|
631
|
+
await mkdir(join(projectDir, "frontend"), { recursive: true });
|
|
632
|
+
|
|
633
|
+
const context = {
|
|
634
|
+
name: "turbo-test",
|
|
635
|
+
projectDir,
|
|
636
|
+
backendDir: "backend",
|
|
637
|
+
frontendDir: "frontend",
|
|
638
|
+
backend: "node-hono",
|
|
639
|
+
frontend: "react-vite",
|
|
640
|
+
};
|
|
641
|
+
await generateModule("build", "turborepo", projectDir, context);
|
|
642
|
+
|
|
643
|
+
// Read and parse turbo.json
|
|
644
|
+
const turboJsonPath = join(projectDir, "turbo.json");
|
|
645
|
+
const turboJsonContent = await Bun.file(turboJsonPath).text();
|
|
646
|
+
|
|
647
|
+
let turboJson: any;
|
|
648
|
+
expect(() => {
|
|
649
|
+
turboJson = JSON.parse(turboJsonContent);
|
|
650
|
+
}).not.toThrow();
|
|
651
|
+
|
|
652
|
+
// Validate required fields
|
|
653
|
+
expect(turboJson.$schema).toBeDefined();
|
|
654
|
+
expect(turboJson.tasks).toBeDefined();
|
|
655
|
+
expect(turboJson.tasks.build).toBeDefined();
|
|
656
|
+
expect(turboJson.tasks.test).toBeDefined();
|
|
657
|
+
expect(turboJson.tasks.lint).toBeDefined();
|
|
658
|
+
|
|
659
|
+
// Validate backend turbo.json
|
|
660
|
+
const backendTurboPath = join(projectDir, "backend/turbo.json");
|
|
661
|
+
const backendTurboContent = await Bun.file(backendTurboPath).text();
|
|
662
|
+
|
|
663
|
+
let backendTurbo: any;
|
|
664
|
+
expect(() => {
|
|
665
|
+
backendTurbo = JSON.parse(backendTurboContent);
|
|
666
|
+
}).not.toThrow();
|
|
667
|
+
|
|
668
|
+
expect(backendTurbo.extends).toContain("//");
|
|
669
|
+
expect(backendTurbo.tasks).toBeDefined();
|
|
670
|
+
|
|
671
|
+
// Validate frontend turbo.json
|
|
672
|
+
const frontendTurboPath = join(projectDir, "frontend/turbo.json");
|
|
673
|
+
const frontendTurboContent = await Bun.file(frontendTurboPath).text();
|
|
674
|
+
|
|
675
|
+
let frontendTurbo: any;
|
|
676
|
+
expect(() => {
|
|
677
|
+
frontendTurbo = JSON.parse(frontendTurboContent);
|
|
678
|
+
}).not.toThrow();
|
|
679
|
+
|
|
680
|
+
expect(frontendTurbo.extends).toContain("//");
|
|
681
|
+
},
|
|
682
|
+
LINT_TIMEOUT,
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
test(
|
|
686
|
+
"turbo CLI validates config",
|
|
687
|
+
async () => {
|
|
688
|
+
const turboAvailable = await commandExists("turbo");
|
|
689
|
+
if (!turboAvailable) {
|
|
690
|
+
console.log("Skipping: turbo not available");
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const projectDir = join(tempDir, "turbo-cli");
|
|
695
|
+
await mkdir(projectDir, { recursive: true });
|
|
696
|
+
|
|
697
|
+
const context = {
|
|
698
|
+
name: "turbo-cli-test",
|
|
699
|
+
projectDir,
|
|
700
|
+
backend: "none",
|
|
701
|
+
frontend: "none",
|
|
702
|
+
};
|
|
703
|
+
await generateModule("build", "turborepo", projectDir, context);
|
|
704
|
+
|
|
705
|
+
// Create minimal package.json for turbo
|
|
706
|
+
await Bun.write(
|
|
707
|
+
join(projectDir, "package.json"),
|
|
708
|
+
JSON.stringify({ name: "turbo-cli-test", private: true, workspaces: [] }, null, 2),
|
|
709
|
+
);
|
|
710
|
+
|
|
711
|
+
// turbo --dry-run should parse config without errors
|
|
712
|
+
const result = await $`cd ${projectDir} && turbo build --dry-run=json 2>&1`.nothrow();
|
|
713
|
+
// turbo may return non-zero if no tasks match, but shouldn't crash on invalid config
|
|
714
|
+
// Exit code 1 is acceptable (no matching packages), but segfault/crash would be different
|
|
715
|
+
expect(result.exitCode).toBeLessThanOrEqual(1);
|
|
716
|
+
},
|
|
717
|
+
LINT_TIMEOUT,
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
test(
|
|
721
|
+
"nx CLI validates config",
|
|
722
|
+
async () => {
|
|
723
|
+
const nxAvailable = await commandExists("nx");
|
|
724
|
+
if (!nxAvailable) {
|
|
725
|
+
console.log("Skipping: nx not available");
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
const projectDir = join(tempDir, "nx-cli");
|
|
730
|
+
await mkdir(projectDir, { recursive: true });
|
|
731
|
+
|
|
732
|
+
const context = {
|
|
733
|
+
name: "nx-cli-test",
|
|
734
|
+
projectDir,
|
|
735
|
+
backend: "none",
|
|
736
|
+
frontend: "none",
|
|
737
|
+
};
|
|
738
|
+
await generateModule("build", "nx", projectDir, context);
|
|
739
|
+
|
|
740
|
+
// Create minimal package.json
|
|
741
|
+
await Bun.write(
|
|
742
|
+
join(projectDir, "package.json"),
|
|
743
|
+
JSON.stringify(
|
|
744
|
+
{
|
|
745
|
+
name: "nx-cli-test",
|
|
746
|
+
private: true,
|
|
747
|
+
devDependencies: { nx: "^17.0.0" },
|
|
748
|
+
},
|
|
749
|
+
null,
|
|
750
|
+
2,
|
|
751
|
+
),
|
|
752
|
+
);
|
|
753
|
+
|
|
754
|
+
// nx show projects should work with valid config
|
|
755
|
+
// First install nx
|
|
756
|
+
await $`cd ${projectDir} && bun install`.quiet().nothrow();
|
|
757
|
+
|
|
758
|
+
const result = await $`cd ${projectDir} && bunx nx show projects 2>&1`.nothrow();
|
|
759
|
+
// May return empty or error if no projects, but shouldn't crash on config
|
|
760
|
+
expect(result.exitCode).toBeLessThanOrEqual(1);
|
|
761
|
+
},
|
|
762
|
+
LINT_TIMEOUT,
|
|
763
|
+
);
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
describe("Cloud Provider Configs", () => {
|
|
767
|
+
let tempDir: string;
|
|
768
|
+
|
|
769
|
+
beforeAll(async () => {
|
|
770
|
+
tempDir = await mkdtemp(join(tmpdir(), "arbiter-lint-cloud-"));
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
afterAll(async () => {
|
|
774
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
test(
|
|
778
|
+
"aws terraform files are valid",
|
|
779
|
+
async () => {
|
|
780
|
+
if (!linters.terraform) {
|
|
781
|
+
console.log("Skipping: terraform not available");
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
const projectDir = join(tempDir, "aws");
|
|
786
|
+
await mkdir(projectDir, { recursive: true });
|
|
787
|
+
|
|
788
|
+
const context = { name: "aws-test", projectDir };
|
|
789
|
+
// AWS module is standalone (includes its own terraform block)
|
|
790
|
+
await generateModule("cloud", "aws", projectDir, context);
|
|
791
|
+
|
|
792
|
+
const tfDir = join(projectDir, "infra/terraform");
|
|
793
|
+
|
|
794
|
+
const initResult = await $`cd ${tfDir} && terraform init -backend=false`.nothrow();
|
|
795
|
+
expect(initResult.exitCode).toBe(0);
|
|
796
|
+
|
|
797
|
+
const validateResult = await $`cd ${tfDir} && terraform validate`.nothrow();
|
|
798
|
+
expect(validateResult.exitCode).toBe(0);
|
|
799
|
+
},
|
|
800
|
+
LINT_TIMEOUT,
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
test(
|
|
804
|
+
"gcp terraform files are valid",
|
|
805
|
+
async () => {
|
|
806
|
+
if (!linters.terraform) {
|
|
807
|
+
console.log("Skipping: terraform not available");
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
const projectDir = join(tempDir, "gcp");
|
|
812
|
+
await mkdir(projectDir, { recursive: true });
|
|
813
|
+
|
|
814
|
+
const context = { name: "gcp-test", projectDir };
|
|
815
|
+
// GCP module is standalone (includes its own terraform block)
|
|
816
|
+
await generateModule("cloud", "gcp", projectDir, context);
|
|
817
|
+
|
|
818
|
+
const tfDir = join(projectDir, "infra/terraform");
|
|
819
|
+
|
|
820
|
+
const initResult = await $`cd ${tfDir} && terraform init -backend=false`.nothrow();
|
|
821
|
+
expect(initResult.exitCode).toBe(0);
|
|
822
|
+
|
|
823
|
+
const validateResult = await $`cd ${tfDir} && terraform validate`.nothrow();
|
|
824
|
+
expect(validateResult.exitCode).toBe(0);
|
|
825
|
+
},
|
|
826
|
+
LINT_TIMEOUT,
|
|
827
|
+
);
|
|
828
|
+
|
|
829
|
+
test(
|
|
830
|
+
"azure terraform files are valid",
|
|
831
|
+
async () => {
|
|
832
|
+
if (!linters.terraform) {
|
|
833
|
+
console.log("Skipping: terraform not available");
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
const projectDir = join(tempDir, "azure");
|
|
838
|
+
await mkdir(projectDir, { recursive: true });
|
|
839
|
+
|
|
840
|
+
const context = { name: "azure-test", projectDir };
|
|
841
|
+
// Azure module is standalone (includes its own terraform block)
|
|
842
|
+
await generateModule("cloud", "azure", projectDir, context);
|
|
843
|
+
|
|
844
|
+
const tfDir = join(projectDir, "infra/terraform");
|
|
845
|
+
|
|
846
|
+
const initResult = await $`cd ${tfDir} && terraform init -backend=false`.nothrow();
|
|
847
|
+
expect(initResult.exitCode).toBe(0);
|
|
848
|
+
|
|
849
|
+
const validateResult = await $`cd ${tfDir} && terraform validate`.nothrow();
|
|
850
|
+
expect(validateResult.exitCode).toBe(0);
|
|
851
|
+
},
|
|
852
|
+
LINT_TIMEOUT,
|
|
853
|
+
);
|
|
854
|
+
});
|