@simplysm/sd-cli 13.0.99 → 14.0.1
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/dist/commands/build.js +29 -19
- package/dist/commands/build.js.map +1 -6
- package/dist/commands/check.d.ts +1 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +130 -115
- package/dist/commands/check.js.map +1 -6
- package/dist/commands/dev.d.ts +6 -7
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +24 -14
- package/dist/commands/dev.js.map +1 -6
- package/dist/commands/lint.d.ts +1 -1
- package/dist/commands/lint.js +158 -116
- package/dist/commands/lint.js.map +1 -6
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +637 -510
- package/dist/commands/publish.js.map +1 -6
- package/dist/commands/replace-deps.js +12 -12
- package/dist/commands/replace-deps.js.map +1 -6
- package/dist/commands/typecheck.d.ts +5 -30
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +144 -207
- package/dist/commands/typecheck.js.map +1 -6
- package/dist/commands/watch.d.ts +6 -4
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +25 -16
- package/dist/commands/watch.js.map +1 -6
- package/dist/engines/NgtscEngine.d.ts +47 -0
- package/dist/engines/NgtscEngine.d.ts.map +1 -0
- package/dist/engines/NgtscEngine.js +151 -0
- package/dist/engines/NgtscEngine.js.map +1 -0
- package/dist/engines/ServerEsbuildEngine.d.ts +47 -0
- package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -0
- package/dist/engines/ServerEsbuildEngine.js +159 -0
- package/dist/engines/ServerEsbuildEngine.js.map +1 -0
- package/dist/engines/TscEngine.d.ts +47 -0
- package/dist/engines/TscEngine.d.ts.map +1 -0
- package/dist/engines/TscEngine.js +153 -0
- package/dist/engines/TscEngine.js.map +1 -0
- package/dist/engines/ViteEngine.d.ts +49 -0
- package/dist/engines/ViteEngine.d.ts.map +1 -0
- package/dist/engines/ViteEngine.js +161 -0
- package/dist/engines/ViteEngine.js.map +1 -0
- package/dist/engines/index.d.ts +26 -0
- package/dist/engines/index.d.ts.map +1 -0
- package/dist/engines/index.js +30 -0
- package/dist/engines/index.js.map +1 -0
- package/dist/engines/types.d.ts +77 -0
- package/dist/engines/types.d.ts.map +1 -0
- package/dist/engines/types.js +2 -0
- package/dist/engines/types.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -6
- package/dist/infra/ResultCollector.d.ts +1 -1
- package/dist/infra/ResultCollector.d.ts.map +1 -1
- package/dist/infra/ResultCollector.js +30 -27
- package/dist/infra/ResultCollector.js.map +1 -6
- package/dist/infra/SignalHandler.js +45 -42
- package/dist/infra/SignalHandler.js.map +1 -6
- package/dist/infra/WorkerManager.js +56 -53
- package/dist/infra/WorkerManager.js.map +1 -6
- package/dist/orchestrators/BuildOrchestrator.d.ts +33 -1
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.js +314 -309
- package/dist/orchestrators/BuildOrchestrator.js.map +1 -6
- package/dist/orchestrators/DevWatchOrchestrator.d.ts +60 -0
- package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -0
- package/dist/orchestrators/DevWatchOrchestrator.js +465 -0
- package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -0
- package/dist/sd-cli-entry.d.ts.map +1 -1
- package/dist/sd-cli-entry.js +190 -266
- package/dist/sd-cli-entry.js.map +1 -6
- package/dist/sd-cli.js +77 -49
- package/dist/sd-cli.js.map +1 -6
- package/dist/sd-config.types.d.ts +2 -0
- package/dist/sd-config.types.d.ts.map +1 -1
- package/dist/sd-config.types.js +2 -1
- package/dist/sd-config.types.js.map +1 -6
- package/dist/utils/angular-build.d.ts +77 -0
- package/dist/utils/angular-build.d.ts.map +1 -0
- package/dist/utils/angular-build.js +84 -0
- package/dist/utils/angular-build.js.map +1 -0
- package/dist/utils/build-env.js +9 -9
- package/dist/utils/build-env.js.map +1 -6
- package/dist/utils/concurrency.d.ts +15 -0
- package/dist/utils/concurrency.d.ts.map +1 -0
- package/dist/utils/concurrency.js +38 -0
- package/dist/utils/concurrency.js.map +1 -0
- package/dist/utils/copy-public.js +104 -87
- package/dist/utils/copy-public.js.map +1 -6
- package/dist/utils/copy-src.js +49 -35
- package/dist/utils/copy-src.js.map +1 -6
- package/dist/utils/esbuild-config.d.ts +0 -29
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/esbuild-config.js +151 -218
- package/dist/utils/esbuild-config.js.map +1 -6
- package/dist/utils/ngtsc-build-core.d.ts +49 -0
- package/dist/utils/ngtsc-build-core.d.ts.map +1 -0
- package/dist/utils/ngtsc-build-core.js +250 -0
- package/dist/utils/ngtsc-build-core.js.map +1 -0
- package/dist/utils/output-path-rewriter.d.ts +23 -0
- package/dist/utils/output-path-rewriter.d.ts.map +1 -0
- package/dist/utils/output-path-rewriter.js +74 -0
- package/dist/utils/output-path-rewriter.js.map +1 -0
- package/dist/utils/output-utils.js +55 -40
- package/dist/utils/output-utils.js.map +1 -6
- package/dist/utils/package-utils.d.ts +8 -0
- package/dist/utils/package-utils.d.ts.map +1 -1
- package/dist/utils/package-utils.js +103 -73
- package/dist/utils/package-utils.js.map +1 -6
- package/dist/utils/rebuild-manager.js +41 -44
- package/dist/utils/rebuild-manager.js.map +1 -6
- package/dist/utils/replace-deps.js +283 -184
- package/dist/utils/replace-deps.js.map +1 -6
- package/dist/utils/scss-compiler.d.ts +10 -0
- package/dist/utils/scss-compiler.d.ts.map +1 -0
- package/dist/utils/scss-compiler.js +36 -0
- package/dist/utils/scss-compiler.js.map +1 -0
- package/dist/utils/sd-config.js +29 -19
- package/dist/utils/sd-config.js.map +1 -6
- package/dist/utils/tsc-build.d.ts +36 -0
- package/dist/utils/tsc-build.d.ts.map +1 -0
- package/dist/utils/tsc-build.js +130 -0
- package/dist/utils/tsc-build.js.map +1 -0
- package/dist/utils/tsconfig.d.ts +7 -26
- package/dist/utils/tsconfig.d.ts.map +1 -1
- package/dist/utils/tsconfig.js +39 -64
- package/dist/utils/tsconfig.js.map +1 -6
- package/dist/utils/typecheck-non-package.d.ts +18 -0
- package/dist/utils/typecheck-non-package.d.ts.map +1 -0
- package/dist/utils/typecheck-non-package.js +64 -0
- package/dist/utils/typecheck-non-package.js.map +1 -0
- package/dist/utils/typecheck-serialization.js +58 -40
- package/dist/utils/typecheck-serialization.js.map +1 -6
- package/dist/utils/worker-events.js +48 -40
- package/dist/utils/worker-events.js.map +1 -6
- package/dist/utils/worker-utils.js +48 -28
- package/dist/utils/worker-utils.js.map +1 -6
- package/dist/vitest-plugin.d.ts +9 -0
- package/dist/vitest-plugin.d.ts.map +1 -0
- package/dist/vitest-plugin.js +85 -0
- package/dist/vitest-plugin.js.map +1 -0
- package/dist/workers/library-build.worker.d.ts +54 -0
- package/dist/workers/library-build.worker.d.ts.map +1 -0
- package/dist/workers/library-build.worker.js +97 -0
- package/dist/workers/library-build.worker.js.map +1 -0
- package/dist/workers/lint.worker.js +9 -6
- package/dist/workers/lint.worker.js.map +1 -6
- package/dist/workers/ngtsc-build.worker.d.ts +23 -0
- package/dist/workers/ngtsc-build.worker.d.ts.map +1 -0
- package/dist/workers/ngtsc-build.worker.js +98 -0
- package/dist/workers/ngtsc-build.worker.js.map +1 -0
- package/dist/workers/{server.worker.d.ts → server-build.worker.d.ts} +39 -29
- package/dist/workers/server-build.worker.d.ts.map +1 -0
- package/dist/workers/server-build.worker.js +399 -0
- package/dist/workers/server-build.worker.js.map +1 -0
- package/dist/workers/server-runtime.worker.d.ts +3 -2
- package/dist/workers/server-runtime.worker.d.ts.map +1 -1
- package/dist/workers/server-runtime.worker.js +100 -95
- package/dist/workers/server-runtime.worker.js.map +1 -6
- package/dist/workers/vite-build.worker.d.ts +56 -0
- package/dist/workers/vite-build.worker.d.ts.map +1 -0
- package/dist/workers/vite-build.worker.js +167 -0
- package/dist/workers/vite-build.worker.js.map +1 -0
- package/package.json +10 -16
- package/src/commands/check.ts +21 -3
- package/src/commands/dev.ts +10 -8
- package/src/commands/lint.ts +1 -1
- package/src/commands/publish.ts +4 -0
- package/src/commands/typecheck.ts +89 -256
- package/src/commands/watch.ts +9 -8
- package/src/engines/NgtscEngine.ts +190 -0
- package/src/engines/ServerEsbuildEngine.ts +195 -0
- package/src/engines/TscEngine.ts +189 -0
- package/src/engines/ViteEngine.ts +203 -0
- package/src/engines/index.ts +49 -0
- package/src/engines/types.ts +79 -0
- package/src/index.ts +0 -3
- package/src/infra/ResultCollector.ts +1 -1
- package/src/orchestrators/BuildOrchestrator.ts +87 -157
- package/src/orchestrators/DevWatchOrchestrator.ts +573 -0
- package/src/sd-cli-entry.ts +13 -116
- package/src/sd-config.types.ts +2 -0
- package/src/utils/angular-build.ts +157 -0
- package/src/utils/concurrency.ts +43 -0
- package/src/utils/esbuild-config.ts +1 -122
- package/src/utils/ngtsc-build-core.ts +379 -0
- package/src/utils/output-path-rewriter.ts +82 -0
- package/src/utils/package-utils.ts +20 -0
- package/src/utils/scss-compiler.ts +58 -0
- package/src/utils/tsc-build.ts +175 -0
- package/src/utils/tsconfig.ts +27 -95
- package/src/utils/typecheck-non-package.ts +87 -0
- package/src/vitest-plugin.ts +118 -0
- package/src/workers/library-build.worker.ts +153 -0
- package/src/workers/ngtsc-build.worker.ts +146 -0
- package/src/workers/server-build.worker.ts +565 -0
- package/src/workers/server-runtime.worker.ts +17 -26
- package/src/workers/vite-build.worker.ts +252 -0
- package/tests/commands/check.spec.ts +276 -0
- package/tests/commands/dev.spec.ts +53 -0
- package/tests/commands/lint.spec.ts +243 -0
- package/tests/commands/publish.spec.ts +1159 -0
- package/tests/commands/typecheck.spec.ts +294 -0
- package/tests/commands/watch.spec.ts +53 -0
- package/tests/engines/engine-selection.spec.ts +247 -0
- package/tests/engines/ngtsc-engine.spec.ts +274 -0
- package/tests/engines/server-esbuild-engine.spec.ts +256 -0
- package/tests/engines/tsc-engine.spec.ts +213 -0
- package/tests/engines/vite-engine.spec.ts +358 -0
- package/tests/infra/result-collector.spec.ts +46 -0
- package/tests/infra/signal-handler.spec.ts +32 -0
- package/tests/infra/worker-manager.spec.ts +63 -0
- package/tests/orchestrators/build-orchestrator.spec.ts +772 -0
- package/tests/orchestrators/dev-watch-orchestrator.spec.ts +1173 -0
- package/tests/sd-cli-entry.spec.ts +49 -0
- package/tests/utils/angular-build.spec.ts +251 -0
- package/tests/utils/build-env.spec.ts +33 -0
- package/tests/utils/concurrency.spec.ts +65 -0
- package/tests/utils/copy-src.spec.ts +144 -0
- package/tests/utils/esbuild-config.spec.ts +186 -0
- package/tests/utils/external-modules.spec.ts +161 -0
- package/tests/utils/ngtsc-scss-refactor.spec.ts +66 -0
- package/tests/utils/output-path-rewriter.spec.ts +165 -0
- package/tests/utils/output-utils.spec.ts +104 -0
- package/tests/utils/package-utils.spec.ts +52 -0
- package/tests/utils/rebuild-manager.spec.ts +30 -27
- package/tests/utils/replace-deps.spec.ts +69 -0
- package/tests/utils/scss-compiler.spec.ts +131 -0
- package/tests/utils/sd-config.spec.ts +77 -0
- package/tests/utils/tsc-build.spec.ts +358 -0
- package/tests/utils/tsconfig-angular.spec.ts +71 -0
- package/tests/utils/typecheck-non-package.spec.ts +120 -0
- package/tests/utils/worker-events.spec.ts +155 -0
- package/tests/utils/worker-utils.spec.ts +43 -0
- package/tests/vitest-plugin-cwd.spec.ts +68 -0
- package/tests/vitest-plugin.spec.ts +103 -0
- package/tests/workers/library-build-worker.spec.ts +258 -0
- package/tests/workers/ngtsc-build-worker.spec.ts +187 -0
- package/tests/workers/server-build-worker.spec.ts +566 -0
- package/tests/workers/server-runtime-worker.spec.ts +251 -0
- package/README.md +0 -295
- package/dist/builders/BaseBuilder.d.ts +0 -88
- package/dist/builders/BaseBuilder.d.ts.map +0 -1
- package/dist/builders/BaseBuilder.js +0 -142
- package/dist/builders/BaseBuilder.js.map +0 -6
- package/dist/builders/DtsBuilder.d.ts +0 -22
- package/dist/builders/DtsBuilder.d.ts.map +0 -1
- package/dist/builders/DtsBuilder.js +0 -72
- package/dist/builders/DtsBuilder.js.map +0 -6
- package/dist/builders/LibraryBuilder.d.ts +0 -22
- package/dist/builders/LibraryBuilder.d.ts.map +0 -1
- package/dist/builders/LibraryBuilder.js +0 -85
- package/dist/builders/LibraryBuilder.js.map +0 -6
- package/dist/builders/types.d.ts +0 -55
- package/dist/builders/types.d.ts.map +0 -1
- package/dist/builders/types.js +0 -1
- package/dist/builders/types.js.map +0 -6
- package/dist/capacitor/capacitor.d.ts +0 -151
- package/dist/capacitor/capacitor.d.ts.map +0 -1
- package/dist/capacitor/capacitor.js +0 -694
- package/dist/capacitor/capacitor.js.map +0 -6
- package/dist/commands/device.d.ts +0 -22
- package/dist/commands/device.d.ts.map +0 -1
- package/dist/commands/device.js +0 -98
- package/dist/commands/device.js.map +0 -6
- package/dist/commands/init.d.ts +0 -14
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -72
- package/dist/commands/init.js.map +0 -6
- package/dist/electron/electron.d.ts +0 -84
- package/dist/electron/electron.d.ts.map +0 -1
- package/dist/electron/electron.js +0 -263
- package/dist/electron/electron.js.map +0 -6
- package/dist/orchestrators/DevOrchestrator.d.ts +0 -83
- package/dist/orchestrators/DevOrchestrator.d.ts.map +0 -1
- package/dist/orchestrators/DevOrchestrator.js +0 -540
- package/dist/orchestrators/DevOrchestrator.js.map +0 -6
- package/dist/orchestrators/WatchOrchestrator.d.ts +0 -57
- package/dist/orchestrators/WatchOrchestrator.d.ts.map +0 -1
- package/dist/orchestrators/WatchOrchestrator.js +0 -199
- package/dist/orchestrators/WatchOrchestrator.js.map +0 -6
- package/dist/utils/tailwind-config-deps.d.ts +0 -8
- package/dist/utils/tailwind-config-deps.d.ts.map +0 -1
- package/dist/utils/tailwind-config-deps.js +0 -82
- package/dist/utils/tailwind-config-deps.js.map +0 -6
- package/dist/utils/template.d.ts +0 -14
- package/dist/utils/template.d.ts.map +0 -1
- package/dist/utils/template.js +0 -33
- package/dist/utils/template.js.map +0 -6
- package/dist/utils/vite-config.d.ts +0 -35
- package/dist/utils/vite-config.d.ts.map +0 -1
- package/dist/utils/vite-config.js +0 -259
- package/dist/utils/vite-config.js.map +0 -6
- package/dist/workers/client.worker.d.ts +0 -83
- package/dist/workers/client.worker.d.ts.map +0 -1
- package/dist/workers/client.worker.js +0 -111
- package/dist/workers/client.worker.js.map +0 -6
- package/dist/workers/dts.worker.d.ts +0 -75
- package/dist/workers/dts.worker.d.ts.map +0 -1
- package/dist/workers/dts.worker.js +0 -270
- package/dist/workers/dts.worker.js.map +0 -6
- package/dist/workers/library.worker.d.ts +0 -75
- package/dist/workers/library.worker.d.ts.map +0 -1
- package/dist/workers/library.worker.js +0 -166
- package/dist/workers/library.worker.js.map +0 -6
- package/dist/workers/server.worker.d.ts.map +0 -1
- package/dist/workers/server.worker.js +0 -482
- package/dist/workers/server.worker.js.map +0 -6
- package/src/builders/BaseBuilder.ts +0 -218
- package/src/builders/DtsBuilder.ts +0 -92
- package/src/builders/LibraryBuilder.ts +0 -110
- package/src/builders/types.ts +0 -60
- package/src/capacitor/capacitor.ts +0 -931
- package/src/commands/device.ts +0 -140
- package/src/commands/init.ts +0 -113
- package/src/electron/electron.ts +0 -362
- package/src/orchestrators/DevOrchestrator.ts +0 -744
- package/src/orchestrators/WatchOrchestrator.ts +0 -277
- package/src/utils/tailwind-config-deps.ts +0 -98
- package/src/utils/template.ts +0 -56
- package/src/utils/vite-config.ts +0 -390
- package/src/workers/client.worker.ts +0 -250
- package/src/workers/dts.worker.ts +0 -453
- package/src/workers/library.worker.ts +0 -316
- package/src/workers/server.worker.ts +0 -734
- package/templates/init/.gitignore.hbs +0 -34
- package/templates/init/.npmrc.hbs +0 -1
- package/templates/init/.prettierignore +0 -1
- package/templates/init/.prettierrc.yaml +0 -12
- package/templates/init/eslint.config.ts +0 -15
- package/templates/init/mise.toml +0 -3
- package/templates/init/package.json.hbs +0 -32
- package/templates/init/packages/client-admin/index.html.hbs +0 -144
- package/templates/init/packages/client-admin/package.json.hbs +0 -27
- package/templates/init/packages/client-admin/public/assets/logo-landscape.png +0 -0
- package/templates/init/packages/client-admin/public/assets/logo.png +0 -0
- package/templates/init/packages/client-admin/public/favicon.ico +0 -0
- package/templates/init/packages/client-admin/src/App.tsx +0 -42
- package/templates/init/packages/client-admin/src/dev/DevDialog.tsx +0 -34
- package/templates/init/packages/client-admin/src/events/AuthChangeEvent.ts +0 -3
- package/templates/init/packages/client-admin/src/main.css +0 -4
- package/templates/init/packages/client-admin/src/main.tsx.hbs +0 -146
- package/templates/init/packages/client-admin/src/providers/AppServiceProvider.tsx.hbs +0 -103
- package/templates/init/packages/client-admin/src/providers/AppStructureProvider.tsx +0 -84
- package/templates/init/packages/client-admin/src/providers/AuthProvider.tsx.hbs +0 -96
- package/templates/init/packages/client-admin/src/providers/configureSharedData.ts.hbs +0 -67
- package/templates/init/packages/client-admin/src/views/auth/LoginView.tsx +0 -132
- package/templates/init/packages/client-admin/src/views/home/HomeView.tsx +0 -108
- package/templates/init/packages/client-admin/src/views/home/base/employee/EmployeeDetail.tsx.hbs +0 -243
- package/templates/init/packages/client-admin/src/views/home/base/employee/EmployeeSheet.tsx.hbs +0 -271
- package/templates/init/packages/client-admin/src/views/home/base/role-permission/RoleDetail.tsx.hbs +0 -146
- package/templates/init/packages/client-admin/src/views/home/base/role-permission/RolePermissionDetail.tsx.hbs +0 -121
- package/templates/init/packages/client-admin/src/views/home/base/role-permission/RolePermissionView.tsx +0 -52
- package/templates/init/packages/client-admin/src/views/home/base/role-permission/RoleSheet.tsx.hbs +0 -125
- package/templates/init/packages/client-admin/src/views/home/main/MainView.tsx.hbs +0 -13
- package/templates/init/packages/client-admin/src/views/home/my-info/MyInfoDetail.tsx.hbs +0 -241
- package/templates/init/packages/client-admin/src/views/home/system/system-log/SystemLogSheet.tsx.hbs +0 -169
- package/templates/init/packages/client-admin/src/views/not-found/NotFoundView.tsx +0 -15
- package/templates/init/packages/client-admin/tailwind.config.ts +0 -10
- package/templates/init/packages/db-main/package.json.hbs +0 -13
- package/templates/init/packages/db-main/src/MainDbContext.ts +0 -22
- package/templates/init/packages/db-main/src/dataLogExt.ts +0 -127
- package/templates/init/packages/db-main/src/index.ts +0 -14
- package/templates/init/packages/db-main/src/tables/base/Employee.ts +0 -24
- package/templates/init/packages/db-main/src/tables/base/EmployeeConfig.ts +0 -13
- package/templates/init/packages/db-main/src/tables/base/Role.ts +0 -9
- package/templates/init/packages/db-main/src/tables/base/RolePermission.ts +0 -13
- package/templates/init/packages/db-main/src/tables/system/_DataLog.ts +0 -19
- package/templates/init/packages/db-main/src/tables/system/_Log.ts +0 -16
- package/templates/init/packages/server/package.json.hbs +0 -20
- package/templates/init/packages/server/public-dev/dev//354/264/210/352/270/260/355/231/224.xlsx +0 -0
- package/templates/init/packages/server/src/index.ts +0 -4
- package/templates/init/packages/server/src/main.ts.hbs +0 -34
- package/templates/init/packages/server/src/services/AuthService.ts.hbs +0 -171
- package/templates/init/packages/server/src/services/DevService.ts.hbs +0 -94
- package/templates/init/packages/server/src/services/EmployeeService.ts.hbs +0 -122
- package/templates/init/packages/server/src/services/RoleService.ts.hbs +0 -59
- package/templates/init/pnpm-workspace.yaml +0 -15
- package/templates/init/sd.config.ts.hbs +0 -48
- package/templates/init/tsconfig.json.hbs +0 -39
- package/templates/init/vitest.config.ts +0 -36
- package/tests/capacitor-exclude.spec.ts +0 -78
- package/tests/capacitor.spec.ts +0 -49
- package/tests/copy-src.spec.ts +0 -50
- package/tests/electron-exclude.spec.ts +0 -61
- package/tests/get-compiler-options-for-package.spec.ts +0 -80
- package/tests/get-package-source-files.spec.ts +0 -139
- package/tests/get-types-from-package-json.spec.ts +0 -92
- package/tests/infra/ResultCollector.spec.ts +0 -30
- package/tests/infra/SignalHandler.spec.ts +0 -38
- package/tests/infra/WorkerManager.spec.ts +0 -63
- package/tests/load-ignore-patterns.spec.ts +0 -163
- package/tests/load-sd-config.spec.ts +0 -100
- package/tests/package-utils.spec.ts +0 -188
- package/tests/parse-root-tsconfig.spec.ts +0 -89
- package/tests/publish-config-narrowing.spec.ts +0 -20
- package/tests/replace-deps.spec.ts +0 -308
- package/tests/run-lint.spec.ts +0 -366
- package/tests/run-typecheck.spec.ts +0 -544
- package/tests/run-watch.spec.ts +0 -76
- package/tests/sd-cli.spec.ts +0 -265
- package/tests/sd-public-dev-plugin-mime.spec.ts +0 -19
- package/tests/tailwind-config-deps.spec.ts +0 -30
- package/tests/template.spec.ts +0 -70
- package/tests/vite-config-exclude.spec.ts +0 -35
- package/tests/vite-config-outdir.spec.ts +0 -38
- package/tests/write-changed-output-files.spec.ts +0 -97
package/dist/commands/publish.js
CHANGED
|
@@ -6,6 +6,7 @@ import { fsx } from "@simplysm/core-node";
|
|
|
6
6
|
import { env, json } from "@simplysm/core-common";
|
|
7
7
|
import "@simplysm/core-common";
|
|
8
8
|
import { loadSdConfig } from "../utils/sd-config.js";
|
|
9
|
+
import { validateTargets } from "../utils/package-utils.js";
|
|
9
10
|
import { execa } from "execa";
|
|
10
11
|
import { runBuild } from "./build.js";
|
|
11
12
|
import { parseWorkspaceGlobs } from "../utils/replace-deps.js";
|
|
@@ -14,544 +15,670 @@ import fs from "fs";
|
|
|
14
15
|
import ssh2 from "ssh2";
|
|
15
16
|
import { password as passwordPrompt } from "@inquirer/prompts";
|
|
16
17
|
const { Client: SshClient, utils } = ssh2;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region Utilities
|
|
20
|
+
/**
|
|
21
|
+
* Replace environment variables (%VAR% format)
|
|
22
|
+
* @throws throws an error if any variable substitution is empty
|
|
23
|
+
*/
|
|
17
24
|
function replaceEnvVariables(str, version, projectPath) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
const result = str.replace(/%([^%]+)%/g, (match, envName) => {
|
|
26
|
+
if (envName === "VER") {
|
|
27
|
+
return version;
|
|
28
|
+
}
|
|
29
|
+
if (envName === "PROJECT") {
|
|
30
|
+
return projectPath;
|
|
31
|
+
}
|
|
32
|
+
return env[envName] ?? match;
|
|
33
|
+
});
|
|
34
|
+
// Throw error if any unsubstituted environment variables remain
|
|
35
|
+
if (/%[^%]+%/.test(result)) {
|
|
36
|
+
throw new Error(`Environment variable substitution failed: ${str} → ${result}`);
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
31
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Wait with countdown
|
|
42
|
+
*/
|
|
32
43
|
async function waitWithCountdown(message, seconds) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
for (let i = seconds; i > 0; i--) {
|
|
45
|
+
if (i !== seconds && process.stdout.isTTY) {
|
|
46
|
+
process.stdout.cursorTo(0);
|
|
47
|
+
}
|
|
48
|
+
process.stdout.write(`${message} ${i}`);
|
|
49
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
50
|
+
}
|
|
51
|
+
if (process.stdout.isTTY) {
|
|
52
|
+
process.stdout.cursorTo(0);
|
|
53
|
+
process.stdout.clearLine(0);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
process.stdout.write("\n");
|
|
57
|
+
}
|
|
46
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Pre-verify and configure SSH key authentication
|
|
61
|
+
*
|
|
62
|
+
* For SFTP servers without pass:
|
|
63
|
+
* 1. Generate SSH key file if it doesn't exist
|
|
64
|
+
* 2. Test key authentication, and if it fails, register public key using password
|
|
65
|
+
*/
|
|
47
66
|
async function ensureSshAuth(publishPackages, logger) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
fs.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
67
|
+
// Collect SFTP servers without pass (deduplicate user@host)
|
|
68
|
+
const sshTargets = new Map();
|
|
69
|
+
for (const pkg of publishPackages) {
|
|
70
|
+
if (pkg.config.type === "npm")
|
|
71
|
+
continue;
|
|
72
|
+
if (pkg.config.type !== "sftp")
|
|
73
|
+
continue;
|
|
74
|
+
if (pkg.config.password != null)
|
|
75
|
+
continue;
|
|
76
|
+
if (pkg.config.user == null) {
|
|
77
|
+
throw new Error(`[${pkg.name}] SFTP config missing user.`);
|
|
78
|
+
}
|
|
79
|
+
const key = `${pkg.config.user}@${pkg.config.host}`;
|
|
80
|
+
sshTargets.set(key, {
|
|
81
|
+
host: pkg.config.host,
|
|
82
|
+
port: pkg.config.port,
|
|
83
|
+
user: pkg.config.user,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (sshTargets.size === 0)
|
|
87
|
+
return;
|
|
88
|
+
// Check/create SSH key file
|
|
89
|
+
const sshDir = path.join(os.homedir(), ".ssh");
|
|
90
|
+
const keyPath = path.join(sshDir, "id_ed25519");
|
|
91
|
+
const pubKeyPath = path.join(sshDir, "id_ed25519.pub");
|
|
92
|
+
if (!fs.existsSync(keyPath)) {
|
|
93
|
+
logger.info("SSH key not found. Creating one...");
|
|
94
|
+
if (!fs.existsSync(sshDir)) {
|
|
95
|
+
fs.mkdirSync(sshDir, { mode: 0o700 });
|
|
96
|
+
}
|
|
97
|
+
const keyPair = utils.generateKeyPairSync("ed25519");
|
|
98
|
+
fs.writeFileSync(keyPath, keyPair.private, { mode: 0o600 });
|
|
99
|
+
fs.writeFileSync(pubKeyPath, keyPair.public + "\n", { mode: 0o644 });
|
|
100
|
+
logger.info(`SSH key created: ${keyPath}`);
|
|
101
|
+
}
|
|
102
|
+
const privateKeyData = fs.readFileSync(keyPath);
|
|
103
|
+
const publicKey = fs.readFileSync(pubKeyPath, "utf-8").trim();
|
|
104
|
+
// Check if privateKey is encrypted
|
|
105
|
+
const parsed = utils.parseKey(privateKeyData);
|
|
106
|
+
const isKeyEncrypted = parsed instanceof Error;
|
|
107
|
+
const sshAgent = process.env["SSH_AUTH_SOCK"];
|
|
108
|
+
// Verify key authentication for each server
|
|
109
|
+
for (const [label, target] of sshTargets) {
|
|
110
|
+
const canAuth = await testSshKeyAuth(target, {
|
|
111
|
+
privateKey: isKeyEncrypted ? undefined : privateKeyData,
|
|
112
|
+
agent: sshAgent,
|
|
113
|
+
});
|
|
114
|
+
if (canAuth) {
|
|
115
|
+
logger.debug(`SSH key authentication verified: ${label}`);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
// Key authentication failed → register public key using password
|
|
119
|
+
logger.info(`${label}: SSH key not registered on server.`);
|
|
120
|
+
const pass = await passwordPrompt({
|
|
121
|
+
message: `${label} password (to register public key):`,
|
|
122
|
+
});
|
|
123
|
+
await registerSshPublicKey(target, pass, publicKey);
|
|
124
|
+
logger.info(`SSH public key registered: ${label}`);
|
|
90
125
|
}
|
|
91
|
-
logger.info(`${label}: SSH key not registered on server.`);
|
|
92
|
-
const pass = await passwordPrompt({
|
|
93
|
-
message: `${label} password (to register public key):`
|
|
94
|
-
});
|
|
95
|
-
await registerSshPublicKey(target, pass, publicKey);
|
|
96
|
-
logger.info(`SSH public key registered: ${label}`);
|
|
97
|
-
}
|
|
98
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Test SSH key authentication (connect and immediately disconnect)
|
|
129
|
+
*/
|
|
99
130
|
function testSshKeyAuth(target, auth) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
131
|
+
if (auth.privateKey == null && auth.agent == null) {
|
|
132
|
+
return Promise.resolve(false);
|
|
133
|
+
}
|
|
134
|
+
return new Promise((resolve) => {
|
|
135
|
+
const conn = new SshClient();
|
|
136
|
+
conn.on("ready", () => {
|
|
137
|
+
conn.end();
|
|
138
|
+
resolve(true);
|
|
139
|
+
});
|
|
140
|
+
conn.on("error", () => {
|
|
141
|
+
resolve(false);
|
|
142
|
+
});
|
|
143
|
+
conn.connect({
|
|
144
|
+
host: target.host,
|
|
145
|
+
port: target.port ?? 22,
|
|
146
|
+
username: target.user,
|
|
147
|
+
...(auth.privateKey != null ? { privateKey: auth.privateKey } : {}),
|
|
148
|
+
...(auth.agent != null ? { agent: auth.agent } : {}),
|
|
149
|
+
readyTimeout: 10_000,
|
|
150
|
+
});
|
|
119
151
|
});
|
|
120
|
-
});
|
|
121
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Connect to server with password and register SSH public key
|
|
155
|
+
*/
|
|
122
156
|
function registerSshPublicKey(target, pass, publicKey) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
const conn = new SshClient();
|
|
159
|
+
conn.on("ready", () => {
|
|
160
|
+
// Add public key to authorized_keys
|
|
161
|
+
const escapedKey = publicKey.replace(/'/g, "'\\''");
|
|
162
|
+
const cmd = [
|
|
163
|
+
"mkdir -p ~/.ssh",
|
|
164
|
+
"chmod 700 ~/.ssh",
|
|
165
|
+
`echo '${escapedKey}' >> ~/.ssh/authorized_keys`,
|
|
166
|
+
"chmod 600 ~/.ssh/authorized_keys",
|
|
167
|
+
].join(" && ");
|
|
168
|
+
conn.exec(cmd, (err, stream) => {
|
|
169
|
+
if (err) {
|
|
170
|
+
conn.end();
|
|
171
|
+
reject(new Error(`Failed to execute SSH command: ${err.message}`));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
let stderr = "";
|
|
175
|
+
stream.on("data", () => { }); // Consume stdout (unconsumed stream won't close)
|
|
176
|
+
stream.stderr.on("data", (data) => {
|
|
177
|
+
stderr += data.toString();
|
|
178
|
+
});
|
|
179
|
+
stream.on("exit", (code) => {
|
|
180
|
+
conn.end();
|
|
181
|
+
if (code !== 0) {
|
|
182
|
+
reject(new Error(`Failed to register SSH public key (exit code: ${code}): ${stderr}`));
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
resolve();
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
});
|
|
141
189
|
});
|
|
142
|
-
|
|
143
|
-
|
|
190
|
+
conn.on("error", (err) => {
|
|
191
|
+
reject(new Error(`SSH connection failed (${target.host}): ${err.message}`));
|
|
144
192
|
});
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
193
|
+
conn.connect({
|
|
194
|
+
host: target.host,
|
|
195
|
+
port: target.port ?? 22,
|
|
196
|
+
username: target.user,
|
|
197
|
+
password: pass,
|
|
198
|
+
readyTimeout: 10_000,
|
|
152
199
|
});
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
conn.on("error", (err) => {
|
|
156
|
-
reject(new Error(`SSH connection failed (${target.host}): ${err.message}`));
|
|
157
200
|
});
|
|
158
|
-
conn.connect({
|
|
159
|
-
host: target.host,
|
|
160
|
-
port: target.port ?? 22,
|
|
161
|
-
username: target.user,
|
|
162
|
-
password: pass,
|
|
163
|
-
readyTimeout: 1e4
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
201
|
}
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region Version Upgrade
|
|
204
|
+
/**
|
|
205
|
+
* Upgrade project and package versions
|
|
206
|
+
* @param dryRun if true, only calculate new version without modifying files
|
|
207
|
+
*/
|
|
167
208
|
async function upgradeVersion(cwd, allPkgPaths, dryRun) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
209
|
+
const changedFiles = [];
|
|
210
|
+
const projPkgPath = path.resolve(cwd, "package.json");
|
|
211
|
+
const projPkg = await fsx.readJson(projPkgPath);
|
|
212
|
+
const currentVersion = projPkg.version;
|
|
213
|
+
const prereleaseInfo = semver.prerelease(currentVersion);
|
|
214
|
+
// Determine increment strategy based on prerelease status
|
|
215
|
+
const newVersion = prereleaseInfo !== null
|
|
216
|
+
? semver.inc(currentVersion, "prerelease")
|
|
217
|
+
: semver.inc(currentVersion, "patch");
|
|
218
|
+
if (dryRun) {
|
|
219
|
+
// dry-run: return new version without modifying files
|
|
220
|
+
return { version: newVersion, changedFiles: [] };
|
|
221
|
+
}
|
|
222
|
+
projPkg.version = newVersion;
|
|
223
|
+
await fsx.write(projPkgPath, json.stringify(projPkg, { space: 2 }) + "\n");
|
|
224
|
+
changedFiles.push(projPkgPath);
|
|
225
|
+
// Set version in each package's package.json
|
|
226
|
+
for (const pkgPath of allPkgPaths) {
|
|
227
|
+
const pkgJsonPath = path.resolve(pkgPath, "package.json");
|
|
228
|
+
const pkgJson = await fsx.readJson(pkgJsonPath);
|
|
229
|
+
pkgJson.version = newVersion;
|
|
230
|
+
await fsx.write(pkgJsonPath, json.stringify(pkgJson, { space: 2 }) + "\n");
|
|
231
|
+
changedFiles.push(pkgJsonPath);
|
|
232
|
+
}
|
|
233
|
+
// Synchronize @simplysm package version in template files
|
|
234
|
+
const templateFiles = await fsx.glob(path.resolve(cwd, "packages/sd-cli/templates/**/*.hbs"));
|
|
235
|
+
const versionRegex = /("@simplysm\/[^"]+"\s*:\s*)"~[^"]+"/g;
|
|
236
|
+
for (const templatePath of templateFiles) {
|
|
237
|
+
const content = await fsx.read(templatePath);
|
|
238
|
+
const newContent = content.replace(versionRegex, `$1"~${newVersion}"`);
|
|
239
|
+
if (content !== newContent) {
|
|
240
|
+
await fsx.write(templatePath, newContent);
|
|
241
|
+
changedFiles.push(templatePath);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return { version: newVersion, changedFiles };
|
|
198
245
|
}
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region Package Publishing
|
|
248
|
+
/**
|
|
249
|
+
* Publish individual package
|
|
250
|
+
* @param dryRun if true, simulate deployment without actually publishing
|
|
251
|
+
*/
|
|
199
252
|
async function publishPackage(pkgPath, publishConfig, version, projectPath, logger, dryRun) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
253
|
+
const pkgName = path.basename(pkgPath);
|
|
254
|
+
if (publishConfig.type === "npm") {
|
|
255
|
+
// npm publish
|
|
256
|
+
const prereleaseInfo = semver.prerelease(version);
|
|
257
|
+
const args = ["publish", "--access", "public", "--no-git-checks"];
|
|
258
|
+
if (prereleaseInfo !== null && typeof prereleaseInfo[0] === "string") {
|
|
259
|
+
args.push("--tag", prereleaseInfo[0]);
|
|
260
|
+
}
|
|
261
|
+
if (dryRun) {
|
|
262
|
+
args.push("--dry-run");
|
|
263
|
+
logger.info(`[DRY-RUN] [${pkgName}] pnpm ${args.join(" ")}`);
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
logger.debug(`[${pkgName}] pnpm ${args.join(" ")}`);
|
|
267
|
+
}
|
|
268
|
+
await execa("pnpm", args, { cwd: pkgPath });
|
|
269
|
+
}
|
|
270
|
+
else if (publishConfig.type === "local-directory") {
|
|
271
|
+
// Copy to local directory
|
|
272
|
+
const targetPath = replaceEnvVariables(publishConfig.path, version, projectPath);
|
|
273
|
+
const distPath = path.resolve(pkgPath, "dist");
|
|
274
|
+
if (dryRun) {
|
|
275
|
+
logger.info(`[DRY-RUN] [${pkgName}] copy to local: ${distPath} → ${targetPath}`);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
logger.debug(`[${pkgName}] copy to local: ${distPath} → ${targetPath}`);
|
|
279
|
+
await fsx.copy(distPath, targetPath);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
// Upload to storage
|
|
284
|
+
const distPath = path.resolve(pkgPath, "dist");
|
|
285
|
+
const remotePath = publishConfig.path ?? "/";
|
|
286
|
+
if (dryRun) {
|
|
287
|
+
logger.info(`[DRY-RUN] [${pkgName}] ${publishConfig.type} upload: ${distPath} → ${remotePath}`);
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
logger.debug(`[${pkgName}] ${publishConfig.type} upload: ${distPath} → ${remotePath}`);
|
|
291
|
+
await StorageFactory.connect(publishConfig.type, {
|
|
292
|
+
host: publishConfig.host,
|
|
293
|
+
port: publishConfig.port,
|
|
294
|
+
user: publishConfig.user,
|
|
295
|
+
password: publishConfig.password,
|
|
296
|
+
}, async (storage) => {
|
|
297
|
+
await storage.uploadDir(distPath, remotePath);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
206
300
|
}
|
|
207
|
-
if (dryRun) {
|
|
208
|
-
args.push("--dry-run");
|
|
209
|
-
logger.info(`[DRY-RUN] [${pkgName}] pnpm ${args.join(" ")}`);
|
|
210
|
-
} else {
|
|
211
|
-
logger.debug(`[${pkgName}] pnpm ${args.join(" ")}`);
|
|
212
|
-
}
|
|
213
|
-
await execa("pnpm", args, { cwd: pkgPath });
|
|
214
|
-
} else if (publishConfig.type === "local-directory") {
|
|
215
|
-
const targetPath = replaceEnvVariables(publishConfig.path, version, projectPath);
|
|
216
|
-
const distPath = path.resolve(pkgPath, "dist");
|
|
217
|
-
if (dryRun) {
|
|
218
|
-
logger.info(`[DRY-RUN] [${pkgName}] copy to local: ${distPath} \u2192 ${targetPath}`);
|
|
219
|
-
} else {
|
|
220
|
-
logger.debug(`[${pkgName}] copy to local: ${distPath} \u2192 ${targetPath}`);
|
|
221
|
-
await fsx.copy(distPath, targetPath);
|
|
222
|
-
}
|
|
223
|
-
} else {
|
|
224
|
-
const distPath = path.resolve(pkgPath, "dist");
|
|
225
|
-
const remotePath = publishConfig.path ?? "/";
|
|
226
|
-
if (dryRun) {
|
|
227
|
-
logger.info(
|
|
228
|
-
`[DRY-RUN] [${pkgName}] ${publishConfig.type} upload: ${distPath} \u2192 ${remotePath}`
|
|
229
|
-
);
|
|
230
|
-
} else {
|
|
231
|
-
logger.debug(`[${pkgName}] ${publishConfig.type} upload: ${distPath} \u2192 ${remotePath}`);
|
|
232
|
-
await StorageFactory.connect(
|
|
233
|
-
publishConfig.type,
|
|
234
|
-
{
|
|
235
|
-
host: publishConfig.host,
|
|
236
|
-
port: publishConfig.port,
|
|
237
|
-
user: publishConfig.user,
|
|
238
|
-
password: publishConfig.password
|
|
239
|
-
},
|
|
240
|
-
async (storage) => {
|
|
241
|
-
await storage.uploadDir(distPath, remotePath);
|
|
242
|
-
}
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
301
|
}
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region Dependency Levels
|
|
304
|
+
/**
|
|
305
|
+
* Calculate dependency levels for packages to publish.
|
|
306
|
+
* Packages with no dependencies → Level 0, depends only on Level 0 → Level 1, ...
|
|
307
|
+
*/
|
|
247
308
|
async function computePublishLevels(publishPkgs) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
309
|
+
const pkgNames = new Set(publishPkgs.map((p) => p.name));
|
|
310
|
+
// Collect workspace dependencies for each package
|
|
311
|
+
const depsMap = new Map();
|
|
312
|
+
for (const pkg of publishPkgs) {
|
|
313
|
+
const pkgJson = await fsx.readJson(path.resolve(pkg.path, "package.json"));
|
|
314
|
+
const allDeps = {
|
|
315
|
+
...pkgJson.dependencies,
|
|
316
|
+
...pkgJson.peerDependencies,
|
|
317
|
+
...pkgJson.optionalDependencies,
|
|
318
|
+
};
|
|
319
|
+
const workspaceDeps = new Set();
|
|
320
|
+
for (const depName of Object.keys(allDeps)) {
|
|
321
|
+
const shortName = depName.replace(/^@simplysm\//, "");
|
|
322
|
+
if (shortName !== depName && pkgNames.has(shortName)) {
|
|
323
|
+
workspaceDeps.add(shortName);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
depsMap.set(pkg.name, workspaceDeps);
|
|
327
|
+
}
|
|
328
|
+
// Topological sort to classify into levels
|
|
329
|
+
const levels = [];
|
|
330
|
+
const assigned = new Set();
|
|
331
|
+
const remaining = new Map(publishPkgs.map((p) => [p.name, p]));
|
|
332
|
+
while (remaining.size > 0) {
|
|
333
|
+
const level = [];
|
|
334
|
+
for (const [name, pkg] of remaining) {
|
|
335
|
+
const deps = depsMap.get(name);
|
|
336
|
+
if ([...deps].every((d) => assigned.has(d))) {
|
|
337
|
+
level.push(pkg);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (level.length === 0) {
|
|
341
|
+
// Circular dependency — place all remaining packages in final level
|
|
342
|
+
levels.push([...remaining.values()]);
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
for (const pkg of level) {
|
|
346
|
+
assigned.add(pkg.name);
|
|
347
|
+
remaining.delete(pkg.name);
|
|
348
|
+
}
|
|
349
|
+
levels.push(level);
|
|
350
|
+
}
|
|
351
|
+
return levels;
|
|
288
352
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const projPkgPath = path.resolve(cwd, "package.json");
|
|
307
|
-
const projPkg = await fsx.readJson(projPkgPath);
|
|
308
|
-
const workspaceYamlPath = path.resolve(cwd, "pnpm-workspace.yaml");
|
|
309
|
-
const workspaceGlobs = [];
|
|
310
|
-
if (await fsx.exists(workspaceYamlPath)) {
|
|
311
|
-
const yamlContent = await fsx.read(workspaceYamlPath);
|
|
312
|
-
workspaceGlobs.push(...parseWorkspaceGlobs(yamlContent));
|
|
313
|
-
}
|
|
314
|
-
const allPkgPaths = (await Promise.all(workspaceGlobs.map((item) => fsx.glob(path.resolve(cwd, item))))).flat().filter((item) => !path.basename(item).includes("."));
|
|
315
|
-
const publishPackages = [];
|
|
316
|
-
for (const [name, config] of Object.entries(sdConfig.packages)) {
|
|
317
|
-
if (config == null) continue;
|
|
318
|
-
const pkgConfig = config;
|
|
319
|
-
if (pkgConfig.publish == null) continue;
|
|
320
|
-
if (targets.length > 0 && !targets.includes(name)) continue;
|
|
321
|
-
const pkgPath = allPkgPaths.find((p) => path.basename(p) === name);
|
|
322
|
-
if (pkgPath == null) {
|
|
323
|
-
logger.warn(`Package not found: ${name}`);
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
publishPackages.push({
|
|
327
|
-
name,
|
|
328
|
-
path: pkgPath,
|
|
329
|
-
config: pkgConfig.publish
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
if (publishPackages.length === 0) {
|
|
333
|
-
process.stdout.write("\u2714 No packages to deploy.\n");
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
logger.debug(
|
|
337
|
-
"Target packages to deploy",
|
|
338
|
-
publishPackages.map((p) => p.name)
|
|
339
|
-
);
|
|
340
|
-
const hasGit = await fsx.exists(path.resolve(cwd, ".git"));
|
|
341
|
-
if (publishPackages.some((p) => p.config.type === "npm")) {
|
|
342
|
-
logger.debug("Verifying npm authentication...");
|
|
343
|
-
try {
|
|
344
|
-
const { stdout: whoami } = await execa("npm", ["whoami"]);
|
|
345
|
-
if (whoami.trim() === "") {
|
|
346
|
-
throw new Error("npm login information not found.");
|
|
347
|
-
}
|
|
348
|
-
logger.debug(`npm login verified: ${whoami.trim()}`);
|
|
349
|
-
} catch (err) {
|
|
350
|
-
logger.error(`npm whoami failed:`, err);
|
|
351
|
-
process.exitCode = 1;
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
try {
|
|
356
|
-
await ensureSshAuth(publishPackages, logger);
|
|
357
|
-
} catch (err) {
|
|
358
|
-
logger.error(`Failed to setup SSH authentication: ${err instanceof Error ? err.message : err}`);
|
|
359
|
-
process.exitCode = 1;
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
if (!noBuild && hasGit) {
|
|
363
|
-
logger.debug("Checking git commit status...");
|
|
364
|
-
try {
|
|
365
|
-
const { stdout: diff } = await execa("git", ["diff", "--name-only"]);
|
|
366
|
-
const { stdout: stagedDiff } = await execa("git", ["diff", "--cached", "--name-only"]);
|
|
367
|
-
if (diff.trim() !== "" || stagedDiff.trim() !== "") {
|
|
368
|
-
logger.info("Uncommitted changes detected. Attempting auto-commit with claude...");
|
|
369
|
-
try {
|
|
370
|
-
await execa("claude", [
|
|
371
|
-
"-p",
|
|
372
|
-
"/sd-commit all",
|
|
373
|
-
"--dangerously-skip-permissions",
|
|
374
|
-
"--model",
|
|
375
|
-
"haiku"
|
|
376
|
-
], { stdio: "inherit" });
|
|
377
|
-
} catch (e) {
|
|
378
|
-
throw new Error(
|
|
379
|
-
"Auto-commit failed. Please commit manually and try again.\n" + (e instanceof Error ? e.message : String(e))
|
|
380
|
-
);
|
|
381
|
-
}
|
|
382
|
-
const { stdout: recheckDiff } = await execa("git", ["diff", "--name-only"]);
|
|
383
|
-
const { stdout: recheckStaged } = await execa("git", ["diff", "--cached", "--name-only"]);
|
|
384
|
-
if (recheckDiff.trim() !== "" || recheckStaged.trim() !== "") {
|
|
385
|
-
throw new Error(
|
|
386
|
-
"Uncommitted changes still remain after auto-commit.\n" + recheckDiff + recheckStaged
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
logger.info("Auto-commit completed.");
|
|
390
|
-
}
|
|
391
|
-
} catch (err) {
|
|
392
|
-
logger.error(err instanceof Error ? err.message : err);
|
|
393
|
-
process.exitCode = 1;
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
let version = projPkg.version;
|
|
398
|
-
if (noBuild) {
|
|
399
|
-
logger.warn("Deploying without building is quite dangerous.");
|
|
400
|
-
await waitWithCountdown("Press 'CTRL+C' to stop the process.", 5);
|
|
401
|
-
} else {
|
|
402
|
-
logger.debug("Upgrading version...");
|
|
403
|
-
const upgradeResult = await upgradeVersion(cwd, allPkgPaths, dryRun);
|
|
404
|
-
version = upgradeResult.version;
|
|
405
|
-
const _changedFiles = upgradeResult.changedFiles;
|
|
353
|
+
//#endregion
|
|
354
|
+
//#region Main
|
|
355
|
+
/**
|
|
356
|
+
* Execute publish command.
|
|
357
|
+
*
|
|
358
|
+
* **Deployment order (safety first):**
|
|
359
|
+
* 1. Pre-validation (npm auth, Git status)
|
|
360
|
+
* 2. Version upgrade (package.json + templates)
|
|
361
|
+
* 3. Build
|
|
362
|
+
* 4. Git commit/tag/push (explicitly stage only changed files)
|
|
363
|
+
* 5. pnpm deployment
|
|
364
|
+
* 6. postPublish (continue even if it fails)
|
|
365
|
+
*/
|
|
366
|
+
export async function runPublish(options) {
|
|
367
|
+
const { targets, noBuild, dryRun } = options;
|
|
368
|
+
const cwd = process.cwd();
|
|
369
|
+
const logger = consola.withTag("sd:cli:publish");
|
|
406
370
|
if (dryRun) {
|
|
407
|
-
|
|
408
|
-
} else {
|
|
409
|
-
logger.info(`Version upgrade: ${projPkg.version} \u2192 ${version}`);
|
|
371
|
+
logger.info("[DRY-RUN] Simulation mode - no actual deployment");
|
|
410
372
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
373
|
+
logger.debug("publish start", { targets, noBuild, dryRun });
|
|
374
|
+
// Load sd.config.ts
|
|
375
|
+
let sdConfig;
|
|
376
|
+
try {
|
|
377
|
+
sdConfig = await loadSdConfig({ cwd, dev: false, options: options.options });
|
|
378
|
+
logger.debug("sd.config.ts loaded");
|
|
379
|
+
}
|
|
380
|
+
catch (err) {
|
|
381
|
+
logger.error(`Failed to load sd.config.ts: ${err instanceof Error ? err.message : err}`);
|
|
382
|
+
process.exitCode = 1;
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
// Validate targets
|
|
386
|
+
validateTargets(targets, sdConfig.packages);
|
|
387
|
+
// Load package.json
|
|
388
|
+
const projPkgPath = path.resolve(cwd, "package.json");
|
|
389
|
+
const projPkg = await fsx.readJson(projPkgPath);
|
|
390
|
+
// Collect workspace package paths from pnpm-workspace.yaml
|
|
391
|
+
const workspaceYamlPath = path.resolve(cwd, "pnpm-workspace.yaml");
|
|
392
|
+
const workspaceGlobs = [];
|
|
393
|
+
if (await fsx.exists(workspaceYamlPath)) {
|
|
394
|
+
const yamlContent = await fsx.read(workspaceYamlPath);
|
|
395
|
+
workspaceGlobs.push(...parseWorkspaceGlobs(yamlContent));
|
|
396
|
+
}
|
|
397
|
+
const allPkgPaths = (await Promise.all(workspaceGlobs.map((item) => fsx.glob(path.resolve(cwd, item)))))
|
|
398
|
+
.flat()
|
|
399
|
+
.filter((item) => !path.basename(item).includes("."));
|
|
400
|
+
// Filter packages with publish configuration
|
|
401
|
+
const publishPackages = [];
|
|
402
|
+
for (const [name, config] of Object.entries(sdConfig.packages)) {
|
|
403
|
+
if (config == null)
|
|
404
|
+
continue;
|
|
405
|
+
const pkgConfig = config;
|
|
406
|
+
if (pkgConfig.publish == null)
|
|
407
|
+
continue;
|
|
408
|
+
// If targets is specified, include only those packages
|
|
409
|
+
if (targets.length > 0 && !targets.includes(name))
|
|
410
|
+
continue;
|
|
411
|
+
const pkgPath = allPkgPaths.find((p) => path.basename(p) === name);
|
|
412
|
+
if (pkgPath == null) {
|
|
413
|
+
logger.warn(`Package not found: ${name}`);
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
publishPackages.push({
|
|
417
|
+
name,
|
|
418
|
+
path: pkgPath,
|
|
419
|
+
config: pkgConfig.publish,
|
|
420
|
+
});
|
|
415
421
|
}
|
|
422
|
+
if (publishPackages.length === 0) {
|
|
423
|
+
process.stdout.write("✔ No packages to deploy.\n");
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
logger.debug("Target packages to deploy", publishPackages.map((p) => p.name));
|
|
427
|
+
// Check if Git is available
|
|
428
|
+
const hasGit = await fsx.exists(path.resolve(cwd, ".git"));
|
|
429
|
+
//#region Phase 1: Pre-validation
|
|
430
|
+
// Verify npm authentication (if npm publish config exists)
|
|
431
|
+
if (publishPackages.some((p) => p.config.type === "npm")) {
|
|
432
|
+
logger.debug("Verifying npm authentication...");
|
|
433
|
+
try {
|
|
434
|
+
const { stdout: whoami } = await execa("npm", ["whoami"]);
|
|
435
|
+
if (whoami.trim() === "") {
|
|
436
|
+
throw new Error("npm login information not found.");
|
|
437
|
+
}
|
|
438
|
+
logger.debug(`npm login verified: ${whoami.trim()}`);
|
|
439
|
+
}
|
|
440
|
+
catch (err) {
|
|
441
|
+
logger.error(`npm whoami failed:`, err);
|
|
442
|
+
/*logger.error(
|
|
443
|
+
"npm token is invalid or expired.\n" +
|
|
444
|
+
"Create a Granular Access Token at https://www.npmjs.com/settings/~/tokens, then:\n" +
|
|
445
|
+
" npm config set //registry.npmjs.org/:_authToken <token>",
|
|
446
|
+
);*/
|
|
447
|
+
process.exitCode = 1;
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
// Verify SSH key authentication (if SFTP publish config without pass exists)
|
|
416
452
|
try {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
logger.
|
|
427
|
-
} else {
|
|
428
|
-
logger.error(
|
|
429
|
-
"Build failed. Manual recovery may be necessary:\n To revert version changes:\n git checkout -- package.json packages/*/package.json packages/sd-cli/templates/"
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
process.exitCode = 1;
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
if (hasGit) {
|
|
436
|
-
if (dryRun) {
|
|
437
|
-
logger.info("[DRY-RUN] Simulating Git commit/tag/push...");
|
|
438
|
-
logger.info(`[DRY-RUN] git add (${_changedFiles.length} files)`);
|
|
439
|
-
logger.info(`[DRY-RUN] git commit -m "v${version}"`);
|
|
440
|
-
logger.info(`[DRY-RUN] git tag -a v${version} -m "v${version}"`);
|
|
441
|
-
logger.info("[DRY-RUN] git push --dry-run");
|
|
442
|
-
await execa("git", ["push", "--dry-run"]);
|
|
443
|
-
logger.info("[DRY-RUN] git push --tags --dry-run");
|
|
444
|
-
await execa("git", ["push", "--tags", "--dry-run"]);
|
|
445
|
-
logger.info("[DRY-RUN] Git operations simulation completed");
|
|
446
|
-
} else {
|
|
447
|
-
logger.debug("Git commit/tag/push...");
|
|
453
|
+
await ensureSshAuth(publishPackages, logger);
|
|
454
|
+
}
|
|
455
|
+
catch (err) {
|
|
456
|
+
logger.error(`Failed to setup SSH authentication: ${err instanceof Error ? err.message : err}`);
|
|
457
|
+
process.exitCode = 1;
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
// Check for uncommitted changes and attempt auto-commit (unless noBuild is set)
|
|
461
|
+
if (!noBuild && hasGit) {
|
|
462
|
+
logger.debug("Checking git commit status...");
|
|
448
463
|
try {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
464
|
+
const { stdout: diff } = await execa("git", ["diff", "--name-only"]);
|
|
465
|
+
const { stdout: stagedDiff } = await execa("git", ["diff", "--cached", "--name-only"]);
|
|
466
|
+
if (diff.trim() !== "" || stagedDiff.trim() !== "") {
|
|
467
|
+
logger.info("Uncommitted changes detected. Attempting auto-commit with claude...");
|
|
468
|
+
try {
|
|
469
|
+
await execa("claude", [
|
|
470
|
+
"-p",
|
|
471
|
+
"/sd-commit all",
|
|
472
|
+
"--dangerously-skip-permissions",
|
|
473
|
+
"--model",
|
|
474
|
+
"haiku",
|
|
475
|
+
], { stdio: "inherit" });
|
|
476
|
+
}
|
|
477
|
+
catch (e) {
|
|
478
|
+
throw new Error("Auto-commit failed. Please commit manually and try again.\n" +
|
|
479
|
+
(e instanceof Error ? e.message : String(e)));
|
|
480
|
+
}
|
|
481
|
+
// Re-verify after commit
|
|
482
|
+
const { stdout: recheckDiff } = await execa("git", ["diff", "--name-only"]);
|
|
483
|
+
const { stdout: recheckStaged } = await execa("git", ["diff", "--cached", "--name-only"]);
|
|
484
|
+
if (recheckDiff.trim() !== "" || recheckStaged.trim() !== "") {
|
|
485
|
+
throw new Error("Uncommitted changes still remain after auto-commit.\n" + recheckDiff + recheckStaged);
|
|
486
|
+
}
|
|
487
|
+
logger.info("Auto-commit completed.");
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
catch (err) {
|
|
491
|
+
logger.error(err instanceof Error ? err.message : err);
|
|
492
|
+
process.exitCode = 1;
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region Phase 2 & 3: Build or noBuild warning
|
|
498
|
+
let version = projPkg.version;
|
|
499
|
+
if (noBuild) {
|
|
500
|
+
// noBuild warning
|
|
501
|
+
logger.warn("Deploying without building is quite dangerous.");
|
|
502
|
+
await waitWithCountdown("Press 'CTRL+C' to stop the process.", 5);
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
// Version upgrade
|
|
506
|
+
logger.debug("Upgrading version...");
|
|
507
|
+
const upgradeResult = await upgradeVersion(cwd, allPkgPaths, dryRun);
|
|
508
|
+
version = upgradeResult.version;
|
|
509
|
+
const _changedFiles = upgradeResult.changedFiles;
|
|
510
|
+
if (dryRun) {
|
|
511
|
+
logger.info(`[DRY-RUN] Version upgrade: ${projPkg.version} → ${version} (files not modified)`);
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
logger.info(`Version upgrade: ${projPkg.version} → ${version}`);
|
|
515
|
+
}
|
|
516
|
+
// Run build
|
|
517
|
+
if (dryRun) {
|
|
518
|
+
logger.info("[DRY-RUN] Starting build (validation only)...");
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
logger.debug("Starting build...");
|
|
522
|
+
}
|
|
478
523
|
try {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
524
|
+
await runBuild({
|
|
525
|
+
targets: publishPackages.map((p) => p.name),
|
|
526
|
+
options: options.options,
|
|
527
|
+
});
|
|
528
|
+
// Check build failure
|
|
529
|
+
if (process.exitCode === 1) {
|
|
530
|
+
throw new Error("Build failed");
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
catch {
|
|
534
|
+
if (dryRun) {
|
|
535
|
+
logger.error("[DRY-RUN] Build failed");
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
logger.error("Build failed. Manual recovery may be necessary:\n" +
|
|
539
|
+
" To revert version changes:\n" +
|
|
540
|
+
" git checkout -- package.json packages/*/package.json packages/sd-cli/templates/");
|
|
541
|
+
}
|
|
542
|
+
process.exitCode = 1;
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
//#region Phase 3: Git commit/tag/push
|
|
546
|
+
if (hasGit) {
|
|
547
|
+
if (dryRun) {
|
|
548
|
+
logger.info("[DRY-RUN] Simulating Git commit/tag/push...");
|
|
549
|
+
logger.info(`[DRY-RUN] git add (${_changedFiles.length} files)`);
|
|
550
|
+
logger.info(`[DRY-RUN] git commit -m "v${version}"`);
|
|
551
|
+
logger.info(`[DRY-RUN] git tag -a v${version} -m "v${version}"`);
|
|
552
|
+
logger.info("[DRY-RUN] git push --dry-run");
|
|
553
|
+
await execa("git", ["push", "--dry-run"]);
|
|
554
|
+
logger.info("[DRY-RUN] git push --tags --dry-run");
|
|
555
|
+
await execa("git", ["push", "--tags", "--dry-run"]);
|
|
556
|
+
logger.info("[DRY-RUN] Git operations simulation completed");
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
logger.debug("Git commit/tag/push...");
|
|
560
|
+
try {
|
|
561
|
+
await execa("git", ["add", ..._changedFiles]);
|
|
562
|
+
await execa("git", ["commit", "-m", `v${version}`]);
|
|
563
|
+
await execa("git", ["tag", "-a", `v${version}`, "-m", `v${version}`]);
|
|
564
|
+
await execa("git", ["push"]);
|
|
565
|
+
await execa("git", ["push", "--tags"]);
|
|
566
|
+
logger.debug("Git operations completed");
|
|
567
|
+
}
|
|
568
|
+
catch (err) {
|
|
569
|
+
logger.error(`Git operations failed: ${err instanceof Error ? err.message : err}\n` +
|
|
570
|
+
"Manual recovery may be necessary:\n" +
|
|
571
|
+
` git revert HEAD # Revert version commit\n` +
|
|
572
|
+
` git tag -d v${version} # Delete tag`);
|
|
573
|
+
process.exitCode = 1;
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
//#endregion
|
|
579
|
+
}
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region Phase 4: Deployment (sequential by dependency level, parallel within level)
|
|
582
|
+
const levels = await computePublishLevels(publishPackages);
|
|
583
|
+
const publishedPackages = [];
|
|
584
|
+
let publishFailed = false;
|
|
585
|
+
// Sequential execution per level
|
|
586
|
+
for (let levelIdx = 0; levelIdx < levels.length; levelIdx++) {
|
|
587
|
+
if (publishFailed)
|
|
588
|
+
break;
|
|
589
|
+
const levelPkgs = levels[levelIdx];
|
|
590
|
+
logger.start(`Level ${levelIdx + 1}/${levels.length}`);
|
|
591
|
+
// Parallel execution within level (Promise.allSettled)
|
|
592
|
+
const publishPromises = levelPkgs.map(async (pkg) => {
|
|
593
|
+
const maxRetries = 3;
|
|
594
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
595
|
+
try {
|
|
596
|
+
await publishPackage(pkg.path, pkg.config, version, cwd, logger, dryRun);
|
|
597
|
+
logger.debug(dryRun ? `[DRY-RUN] ${pkg.name}` : pkg.name);
|
|
598
|
+
publishedPackages.push(pkg.name);
|
|
599
|
+
return { status: "success", name: pkg.name };
|
|
600
|
+
}
|
|
601
|
+
catch (err) {
|
|
602
|
+
if (attempt < maxRetries) {
|
|
603
|
+
const delay = attempt * 5_000;
|
|
604
|
+
logger.debug(dryRun
|
|
605
|
+
? `[DRY-RUN] ${pkg.name} (retry ${attempt + 1}/${maxRetries})`
|
|
606
|
+
: `${pkg.name} (retry ${attempt + 1}/${maxRetries})`);
|
|
607
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
throw err;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
// Fallback for TypeScript type checker (actually unreachable)
|
|
615
|
+
return { status: "error", name: pkg.name, error: new Error("Unknown error") };
|
|
616
|
+
});
|
|
617
|
+
const results = await Promise.allSettled(publishPromises);
|
|
618
|
+
// Check for failures within level
|
|
619
|
+
const rejectedResults = results.filter((r) => r.status === "rejected");
|
|
620
|
+
if (rejectedResults.length > 0) {
|
|
621
|
+
publishFailed = true;
|
|
622
|
+
for (const r of rejectedResults) {
|
|
623
|
+
logger.error(r.reason instanceof Error ? r.reason.message : r.reason);
|
|
624
|
+
}
|
|
625
|
+
logger.fail(`Level ${levelIdx + 1}/${levels.length}`);
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
logger.success(`Level ${levelIdx + 1}/${levels.length}`);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
// Check failed packages
|
|
632
|
+
const allPkgNames = publishPackages.map((p) => p.name);
|
|
633
|
+
const failedPkgNames = allPkgNames.filter((n) => !publishedPackages.includes(n));
|
|
634
|
+
if (failedPkgNames.length > 0) {
|
|
635
|
+
if (publishedPackages.length > 0) {
|
|
636
|
+
logger.error("Error during deployment.\n" +
|
|
637
|
+
"Already deployed packages:\n" +
|
|
638
|
+
publishedPackages.map((n) => ` - ${n}`).join("\n") +
|
|
639
|
+
"\n\nManual recovery may be necessary.\n" +
|
|
640
|
+
"npm packages can be deleted within 72 hours with `npm unpublish <pkg>@<version>`.");
|
|
641
|
+
}
|
|
642
|
+
for (const name of failedPkgNames) {
|
|
643
|
+
logger.error(`[${name}] Deployment failed`);
|
|
644
|
+
}
|
|
645
|
+
process.exitCode = 1;
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
//#endregion
|
|
649
|
+
//#region Phase 5: postPublish
|
|
650
|
+
if (sdConfig.postPublish != null && sdConfig.postPublish.length > 0) {
|
|
535
651
|
if (dryRun) {
|
|
536
|
-
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
652
|
+
logger.info("[DRY-RUN] Simulating postPublish scripts...");
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
logger.debug("Running postPublish scripts...");
|
|
656
|
+
}
|
|
657
|
+
for (const script of sdConfig.postPublish) {
|
|
658
|
+
try {
|
|
659
|
+
const cmd = replaceEnvVariables(script.cmd, version, cwd);
|
|
660
|
+
const args = script.args.map((arg) => replaceEnvVariables(arg, version, cwd));
|
|
661
|
+
if (dryRun) {
|
|
662
|
+
logger.info(`[DRY-RUN] Will execute: ${cmd} ${args.join(" ")}`);
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
logger.debug(`Executing: ${cmd} ${args.join(" ")}`);
|
|
666
|
+
await execa(cmd, args, { cwd });
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
catch (err) {
|
|
670
|
+
// On postPublish failure, only warn (no deployment rollback possible)
|
|
671
|
+
logger.warn(`postPublish script failed (continuing): ${err instanceof Error ? err.message : err}`);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
//#endregion
|
|
676
|
+
if (dryRun) {
|
|
677
|
+
logger.info(`[DRY-RUN] Simulation completed. Actual deployment version: v${version}`);
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
logger.info(`All deployments completed. (v${version})`);
|
|
681
|
+
}
|
|
553
682
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
};
|
|
557
|
-
//# sourceMappingURL=publish.js.map
|
|
683
|
+
//#endregion
|
|
684
|
+
//# sourceMappingURL=publish.js.map
|