@expressots/cli 3.0.0 → 4.0.0-preview.2
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 +41 -95
- package/bin/cicd/cli.d.ts +6 -0
- package/bin/cicd/cli.js +126 -0
- package/bin/cicd/form.d.ts +29 -0
- package/bin/cicd/form.js +345 -0
- package/bin/cicd/generators/azure-devops.d.ts +2 -0
- package/bin/cicd/generators/azure-devops.js +370 -0
- package/bin/cicd/generators/bitbucket.d.ts +2 -0
- package/bin/cicd/generators/bitbucket.js +217 -0
- package/bin/cicd/generators/circleci.d.ts +2 -0
- package/bin/cicd/generators/circleci.js +274 -0
- package/bin/cicd/generators/github-actions.d.ts +14 -0
- package/bin/cicd/generators/github-actions.js +426 -0
- package/bin/cicd/generators/gitlab-ci.d.ts +2 -0
- package/bin/cicd/generators/gitlab-ci.js +237 -0
- package/bin/cicd/generators/index.d.ts +6 -0
- package/bin/cicd/generators/index.js +15 -0
- package/bin/cicd/generators/jenkins.d.ts +2 -0
- package/bin/cicd/generators/jenkins.js +248 -0
- package/bin/cicd/generators/template-loader.d.ts +17 -0
- package/bin/cicd/generators/template-loader.js +128 -0
- package/bin/cicd/index.d.ts +1 -0
- package/bin/cicd/index.js +5 -0
- package/bin/cli.d.ts +1 -1
- package/bin/cli.js +18 -3
- package/bin/commands/project.commands.d.ts +19 -6
- package/bin/commands/project.commands.js +390 -61
- package/bin/config/index.d.ts +5 -0
- package/bin/config/index.js +10 -0
- package/bin/config/manager.d.ts +98 -0
- package/bin/config/manager.js +222 -0
- package/bin/containerize/analyzers/bootstrap-analyzer.d.ts +46 -0
- package/bin/containerize/analyzers/bootstrap-analyzer.js +187 -0
- package/bin/containerize/analyzers/project-analyzer.d.ts +20 -0
- package/bin/containerize/analyzers/project-analyzer.js +150 -0
- package/bin/containerize/cli.d.ts +4 -0
- package/bin/containerize/cli.js +113 -0
- package/bin/containerize/form.d.ts +15 -0
- package/bin/containerize/form.js +154 -0
- package/bin/containerize/generators/ci-generator.d.ts +31 -0
- package/bin/containerize/generators/ci-generator.js +936 -0
- package/bin/containerize/generators/docker-compose-generator.d.ts +8 -0
- package/bin/containerize/generators/docker-compose-generator.js +186 -0
- package/bin/containerize/generators/dockerfile-generator.d.ts +8 -0
- package/bin/containerize/generators/dockerfile-generator.js +635 -0
- package/bin/containerize/generators/kubernetes-generator.d.ts +8 -0
- package/bin/containerize/generators/kubernetes-generator.js +133 -0
- package/bin/containerize/generators/template-loader.d.ts +36 -0
- package/bin/containerize/generators/template-loader.js +129 -0
- package/bin/containerize/index.d.ts +4 -0
- package/bin/containerize/index.js +13 -0
- package/bin/containerize/presets/preset-registry.d.ts +20 -0
- package/bin/containerize/presets/preset-registry.js +102 -0
- package/bin/costs/cli.d.ts +5 -0
- package/bin/costs/cli.js +183 -0
- package/bin/costs/form.d.ts +44 -0
- package/bin/costs/form.js +412 -0
- package/bin/costs/index.d.ts +4 -0
- package/bin/costs/index.js +25 -0
- package/bin/costs/pricing-manager.d.ts +84 -0
- package/bin/costs/pricing-manager.js +342 -0
- package/bin/costs/providers/index.d.ts +32 -0
- package/bin/costs/providers/index.js +153 -0
- package/bin/costs/sources/api-source.d.ts +10 -0
- package/bin/costs/sources/api-source.js +32 -0
- package/bin/costs/sources/index.d.ts +6 -0
- package/bin/costs/sources/index.js +15 -0
- package/bin/costs/sources/local-json-source.d.ts +23 -0
- package/bin/costs/sources/local-json-source.js +59 -0
- package/bin/costs/sources/remote-json-source.d.ts +11 -0
- package/bin/costs/sources/remote-json-source.js +53 -0
- package/bin/costs/types.d.ts +53 -0
- package/bin/costs/types.js +5 -0
- package/bin/dev/cli.d.ts +4 -0
- package/bin/dev/cli.js +134 -0
- package/bin/dev/form.d.ts +36 -0
- package/bin/dev/form.js +254 -0
- package/bin/dev/index.d.ts +1 -0
- package/bin/dev/index.js +5 -0
- package/bin/generate/cli.js +29 -2
- package/bin/generate/form.d.ts +5 -1
- package/bin/generate/form.js +3 -3
- package/bin/generate/templates/nonopinionated/config.tpl +12 -0
- package/bin/generate/templates/nonopinionated/event.tpl +10 -0
- package/bin/generate/templates/nonopinionated/guard.tpl +18 -0
- package/bin/generate/templates/nonopinionated/handler.tpl +12 -0
- package/bin/generate/templates/nonopinionated/interceptor.tpl +27 -0
- package/bin/generate/templates/opinionated/config.tpl +47 -0
- package/bin/generate/templates/opinionated/entity.tpl +1 -8
- package/bin/generate/templates/opinionated/event.tpl +15 -0
- package/bin/generate/templates/opinionated/guard.tpl +41 -0
- package/bin/generate/templates/opinionated/handler.tpl +23 -0
- package/bin/generate/templates/opinionated/interceptor.tpl +50 -0
- package/bin/generate/utils/command-utils.d.ts +7 -3
- package/bin/generate/utils/command-utils.js +95 -31
- package/bin/generate/utils/nonopininated-cmd.d.ts +10 -1
- package/bin/generate/utils/nonopininated-cmd.js +100 -1
- package/bin/generate/utils/opinionated-cmd.d.ts +10 -1
- package/bin/generate/utils/opinionated-cmd.js +112 -7
- package/bin/generate/utils/string-utils.d.ts +6 -0
- package/bin/generate/utils/string-utils.js +13 -1
- package/bin/help/form.js +11 -3
- package/bin/migrate/analyzers/platform-detector.d.ts +14 -0
- package/bin/migrate/analyzers/platform-detector.js +116 -0
- package/bin/migrate/cli.d.ts +6 -0
- package/bin/migrate/cli.js +96 -0
- package/bin/migrate/form.d.ts +25 -0
- package/bin/migrate/form.js +347 -0
- package/bin/migrate/generators/compose-to-k8s.d.ts +2 -0
- package/bin/migrate/generators/compose-to-k8s.js +324 -0
- package/bin/migrate/generators/compose-to-railway.d.ts +2 -0
- package/bin/migrate/generators/compose-to-railway.js +138 -0
- package/bin/migrate/generators/compose-to-render.d.ts +2 -0
- package/bin/migrate/generators/compose-to-render.js +148 -0
- package/bin/migrate/generators/generic-migration.d.ts +9 -0
- package/bin/migrate/generators/generic-migration.js +221 -0
- package/bin/migrate/generators/heroku-to-fly.d.ts +2 -0
- package/bin/migrate/generators/heroku-to-fly.js +291 -0
- package/bin/migrate/generators/heroku-to-railway.d.ts +2 -0
- package/bin/migrate/generators/heroku-to-railway.js +283 -0
- package/bin/migrate/generators/heroku-to-render.d.ts +2 -0
- package/bin/migrate/generators/heroku-to-render.js +148 -0
- package/bin/migrate/generators/index.d.ts +7 -0
- package/bin/migrate/generators/index.js +17 -0
- package/bin/migrate/generators/template-loader.d.ts +21 -0
- package/bin/migrate/generators/template-loader.js +59 -0
- package/bin/migrate/index.d.ts +1 -0
- package/bin/migrate/index.js +5 -0
- package/bin/new/cli.js +21 -6
- package/bin/new/form.d.ts +25 -4
- package/bin/new/form.js +285 -70
- package/bin/profile/analyzers/dockerfile-analyzer.d.ts +27 -0
- package/bin/profile/analyzers/dockerfile-analyzer.js +122 -0
- package/bin/profile/analyzers/image-analyzer.d.ts +19 -0
- package/bin/profile/analyzers/image-analyzer.js +85 -0
- package/bin/profile/cli.d.ts +4 -0
- package/bin/profile/cli.js +92 -0
- package/bin/profile/form.d.ts +56 -0
- package/bin/profile/form.js +400 -0
- package/bin/profile/index.d.ts +1 -0
- package/bin/profile/index.js +5 -0
- package/bin/profile/optimizers/index.d.ts +19 -0
- package/bin/profile/optimizers/index.js +137 -0
- package/bin/providers/add/form.d.ts +1 -1
- package/bin/providers/add/form.js +27 -6
- package/bin/providers/create/form.js +2 -1
- package/bin/scripts/form.js +27 -5
- package/bin/studio/cli.d.ts +15 -0
- package/bin/studio/cli.js +166 -0
- package/bin/studio/index.d.ts +5 -0
- package/bin/studio/index.js +9 -0
- package/bin/templates/cache.d.ts +54 -0
- package/bin/templates/cache.js +180 -0
- package/bin/templates/cli.d.ts +8 -0
- package/bin/templates/cli.js +292 -0
- package/bin/templates/fetcher.d.ts +49 -0
- package/bin/templates/fetcher.js +208 -0
- package/bin/templates/index.d.ts +11 -0
- package/bin/templates/index.js +37 -0
- package/bin/templates/manager.d.ts +116 -0
- package/bin/templates/manager.js +323 -0
- package/bin/templates/renderer.d.ts +49 -0
- package/bin/templates/renderer.js +204 -0
- package/bin/templates/types.d.ts +51 -0
- package/bin/templates/types.js +5 -0
- package/bin/utils/add-module-to-container.d.ts +2 -2
- package/bin/utils/add-module-to-container.js +15 -5
- package/bin/utils/cli-ui.d.ts +30 -3
- package/bin/utils/cli-ui.js +95 -13
- package/bin/utils/index.d.ts +4 -0
- package/bin/utils/index.js +4 -0
- package/bin/utils/input-validation.d.ts +50 -0
- package/bin/utils/input-validation.js +143 -0
- package/bin/utils/package-manager-commands.d.ts +24 -0
- package/bin/utils/package-manager-commands.js +50 -0
- package/bin/utils/safe-spawn.d.ts +35 -0
- package/bin/utils/safe-spawn.js +51 -0
- package/bin/utils/update-tsconfig-paths.d.ts +35 -0
- package/bin/utils/update-tsconfig-paths.js +286 -0
- package/package.json +154 -154
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateCircleCI = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const template_loader_1 = require("./template-loader");
|
|
11
|
+
async function generateCircleCI(outputDir, options) {
|
|
12
|
+
const circleDir = path_1.default.join(outputDir, ".circleci");
|
|
13
|
+
fs_1.default.mkdirSync(circleDir, { recursive: true });
|
|
14
|
+
// Try remote template, fall back to embedded
|
|
15
|
+
const result = await (0, template_loader_1.loadCICDTemplate)("circleci", options.strategy, options, generateCircleCIContentEmbedded);
|
|
16
|
+
(0, template_loader_1.logTemplateSource)("CircleCI", result.source);
|
|
17
|
+
const filePath = path_1.default.join(circleDir, "config.yml");
|
|
18
|
+
fs_1.default.writeFileSync(filePath, result.content, "utf-8");
|
|
19
|
+
console.log(chalk_1.default.green(` ✓ Created .circleci/config.yml`));
|
|
20
|
+
}
|
|
21
|
+
exports.generateCircleCI = generateCircleCI;
|
|
22
|
+
/**
|
|
23
|
+
* Embedded template generator (fallback when remote templates unavailable)
|
|
24
|
+
*/
|
|
25
|
+
function generateCircleCIContentEmbedded(options) {
|
|
26
|
+
const { nodeVersion, packageManager, strategy, includeSecurity, includeE2E, includeCoverage, deployTarget, branch, } = options;
|
|
27
|
+
const installCmd = packageManager === "pnpm"
|
|
28
|
+
? "pnpm install"
|
|
29
|
+
: packageManager === "yarn"
|
|
30
|
+
? "yarn install"
|
|
31
|
+
: "npm ci";
|
|
32
|
+
const testCmd = packageManager === "pnpm"
|
|
33
|
+
? "pnpm test"
|
|
34
|
+
: packageManager === "yarn"
|
|
35
|
+
? "yarn test"
|
|
36
|
+
: "npm test";
|
|
37
|
+
const buildCmd = packageManager === "pnpm"
|
|
38
|
+
? "pnpm build"
|
|
39
|
+
: packageManager === "yarn"
|
|
40
|
+
? "yarn build"
|
|
41
|
+
: "npm run build";
|
|
42
|
+
const lintCmd = packageManager === "pnpm"
|
|
43
|
+
? "pnpm lint"
|
|
44
|
+
: packageManager === "yarn"
|
|
45
|
+
? "yarn lint"
|
|
46
|
+
: "npm run lint";
|
|
47
|
+
let content = `# CircleCI Configuration
|
|
48
|
+
# Generated by ExpressoTS CLI
|
|
49
|
+
# Strategy: ${strategy}
|
|
50
|
+
|
|
51
|
+
version: 2.1
|
|
52
|
+
|
|
53
|
+
orbs:
|
|
54
|
+
node: circleci/node@5.2
|
|
55
|
+
docker: circleci/docker@2.5
|
|
56
|
+
|
|
57
|
+
executors:
|
|
58
|
+
node-executor:
|
|
59
|
+
docker:
|
|
60
|
+
- image: cimg/node:${nodeVersion}
|
|
61
|
+
working_directory: ~/project
|
|
62
|
+
|
|
63
|
+
commands:
|
|
64
|
+
install-deps:
|
|
65
|
+
steps:
|
|
66
|
+
- restore_cache:
|
|
67
|
+
keys:
|
|
68
|
+
- v1-deps-{{ checksum "package-lock.json" }}
|
|
69
|
+
- v1-deps-
|
|
70
|
+
- run:
|
|
71
|
+
name: Install dependencies
|
|
72
|
+
command: ${installCmd}
|
|
73
|
+
- save_cache:
|
|
74
|
+
key: v1-deps-{{ checksum "package-lock.json" }}
|
|
75
|
+
paths:
|
|
76
|
+
- node_modules
|
|
77
|
+
|
|
78
|
+
jobs:
|
|
79
|
+
lint:
|
|
80
|
+
executor: node-executor
|
|
81
|
+
steps:
|
|
82
|
+
- checkout
|
|
83
|
+
- install-deps
|
|
84
|
+
- run:
|
|
85
|
+
name: Run linter
|
|
86
|
+
command: ${lintCmd}
|
|
87
|
+
|
|
88
|
+
test:
|
|
89
|
+
executor: node-executor
|
|
90
|
+
steps:
|
|
91
|
+
- checkout
|
|
92
|
+
- install-deps
|
|
93
|
+
- run:
|
|
94
|
+
name: Run tests
|
|
95
|
+
command: ${testCmd}${includeCoverage ? ` --coverage` : ""}
|
|
96
|
+
`;
|
|
97
|
+
if (includeCoverage) {
|
|
98
|
+
content += ` - store_artifacts:
|
|
99
|
+
path: coverage
|
|
100
|
+
- run:
|
|
101
|
+
name: Upload coverage
|
|
102
|
+
command: |
|
|
103
|
+
if [ -n "$CODECOV_TOKEN" ]; then
|
|
104
|
+
npx codecov
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
`;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
content += `
|
|
111
|
+
`;
|
|
112
|
+
}
|
|
113
|
+
// Security job
|
|
114
|
+
if (includeSecurity &&
|
|
115
|
+
(strategy === "comprehensive" || strategy === "security-focused")) {
|
|
116
|
+
content += ` security:
|
|
117
|
+
docker:
|
|
118
|
+
- image: aquasec/trivy:latest
|
|
119
|
+
steps:
|
|
120
|
+
- checkout
|
|
121
|
+
- run:
|
|
122
|
+
name: Run security scan
|
|
123
|
+
command: trivy fs --exit-code 0 --severity HIGH,CRITICAL .
|
|
124
|
+
|
|
125
|
+
`;
|
|
126
|
+
}
|
|
127
|
+
// E2E tests
|
|
128
|
+
if (includeE2E) {
|
|
129
|
+
content += ` e2e:
|
|
130
|
+
executor: node-executor
|
|
131
|
+
steps:
|
|
132
|
+
- checkout
|
|
133
|
+
- install-deps
|
|
134
|
+
- run:
|
|
135
|
+
name: Build application
|
|
136
|
+
command: ${buildCmd}
|
|
137
|
+
- run:
|
|
138
|
+
name: Start application
|
|
139
|
+
command: npm start
|
|
140
|
+
background: true
|
|
141
|
+
- run:
|
|
142
|
+
name: Wait for app
|
|
143
|
+
command: sleep 10
|
|
144
|
+
- run:
|
|
145
|
+
name: Run E2E tests
|
|
146
|
+
command: npm run test:e2e
|
|
147
|
+
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
// Build job
|
|
151
|
+
content += ` build:
|
|
152
|
+
executor: node-executor
|
|
153
|
+
steps:
|
|
154
|
+
- checkout
|
|
155
|
+
- install-deps
|
|
156
|
+
- run:
|
|
157
|
+
name: Build application
|
|
158
|
+
command: ${buildCmd}
|
|
159
|
+
- persist_to_workspace:
|
|
160
|
+
root: .
|
|
161
|
+
paths:
|
|
162
|
+
- dist
|
|
163
|
+
|
|
164
|
+
docker-build:
|
|
165
|
+
executor: docker/docker
|
|
166
|
+
steps:
|
|
167
|
+
- checkout
|
|
168
|
+
- setup_remote_docker:
|
|
169
|
+
version: 20.10.24
|
|
170
|
+
- docker/check
|
|
171
|
+
- docker/build:
|
|
172
|
+
image: $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
|
|
173
|
+
tag: $CIRCLE_SHA1,latest
|
|
174
|
+
- docker/push:
|
|
175
|
+
image: $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
|
|
176
|
+
tag: $CIRCLE_SHA1,latest
|
|
177
|
+
|
|
178
|
+
`;
|
|
179
|
+
// Deploy job
|
|
180
|
+
if (deployTarget !== "none") {
|
|
181
|
+
content += generateCircleCIDeployJob(deployTarget);
|
|
182
|
+
}
|
|
183
|
+
// Workflows
|
|
184
|
+
content += `workflows:
|
|
185
|
+
build-and-deploy:
|
|
186
|
+
jobs:
|
|
187
|
+
- lint:
|
|
188
|
+
filters:
|
|
189
|
+
branches:
|
|
190
|
+
only:
|
|
191
|
+
- ${branch}
|
|
192
|
+
- /^feature/.*/
|
|
193
|
+
- test:
|
|
194
|
+
requires:
|
|
195
|
+
- lint
|
|
196
|
+
`;
|
|
197
|
+
if (includeSecurity &&
|
|
198
|
+
(strategy === "comprehensive" || strategy === "security-focused")) {
|
|
199
|
+
content += ` - security:
|
|
200
|
+
requires:
|
|
201
|
+
- lint
|
|
202
|
+
`;
|
|
203
|
+
}
|
|
204
|
+
if (includeE2E) {
|
|
205
|
+
content += ` - e2e:
|
|
206
|
+
requires:
|
|
207
|
+
- test
|
|
208
|
+
`;
|
|
209
|
+
}
|
|
210
|
+
content += ` - build:
|
|
211
|
+
requires:
|
|
212
|
+
- test
|
|
213
|
+
`;
|
|
214
|
+
if (includeSecurity &&
|
|
215
|
+
(strategy === "comprehensive" || strategy === "security-focused")) {
|
|
216
|
+
content += ` - security
|
|
217
|
+
`;
|
|
218
|
+
}
|
|
219
|
+
content += ` - docker-build:
|
|
220
|
+
requires:
|
|
221
|
+
- build
|
|
222
|
+
filters:
|
|
223
|
+
branches:
|
|
224
|
+
only: ${branch}
|
|
225
|
+
`;
|
|
226
|
+
if (deployTarget !== "none") {
|
|
227
|
+
content += ` - deploy:
|
|
228
|
+
requires:
|
|
229
|
+
- docker-build
|
|
230
|
+
filters:
|
|
231
|
+
branches:
|
|
232
|
+
only: ${branch}
|
|
233
|
+
`;
|
|
234
|
+
}
|
|
235
|
+
return content;
|
|
236
|
+
}
|
|
237
|
+
function generateCircleCIDeployJob(target) {
|
|
238
|
+
switch (target) {
|
|
239
|
+
case "kubernetes":
|
|
240
|
+
return ` deploy:
|
|
241
|
+
docker:
|
|
242
|
+
- image: bitnami/kubectl:latest
|
|
243
|
+
steps:
|
|
244
|
+
- run:
|
|
245
|
+
name: Deploy to Kubernetes
|
|
246
|
+
command: |
|
|
247
|
+
kubectl set image deployment/expressots-app app=$DOCKER_IMAGE:$CIRCLE_SHA1
|
|
248
|
+
kubectl rollout status deployment/expressots-app
|
|
249
|
+
|
|
250
|
+
`;
|
|
251
|
+
case "railway":
|
|
252
|
+
return ` deploy:
|
|
253
|
+
executor: node-executor
|
|
254
|
+
steps:
|
|
255
|
+
- checkout
|
|
256
|
+
- run:
|
|
257
|
+
name: Install Railway CLI
|
|
258
|
+
command: npm install -g @railway/cli
|
|
259
|
+
- run:
|
|
260
|
+
name: Deploy to Railway
|
|
261
|
+
command: railway up
|
|
262
|
+
|
|
263
|
+
`;
|
|
264
|
+
default:
|
|
265
|
+
return ` deploy:
|
|
266
|
+
executor: node-executor
|
|
267
|
+
steps:
|
|
268
|
+
- run:
|
|
269
|
+
name: Deploy
|
|
270
|
+
command: echo "Deploy to ${target}"
|
|
271
|
+
|
|
272
|
+
`;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface GeneratorOptions {
|
|
2
|
+
projectName: string;
|
|
3
|
+
nodeVersion: string;
|
|
4
|
+
packageManager: string;
|
|
5
|
+
strategy: string;
|
|
6
|
+
includeSecurity: boolean;
|
|
7
|
+
includeE2E: boolean;
|
|
8
|
+
includeCoverage: boolean;
|
|
9
|
+
dockerRegistry?: string;
|
|
10
|
+
deployTarget: string;
|
|
11
|
+
branch: string;
|
|
12
|
+
port: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function generateGitHubActions(outputDir: string, options: GeneratorOptions): Promise<void>;
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateGitHubActions = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const template_loader_1 = require("./template-loader");
|
|
11
|
+
async function generateGitHubActions(outputDir, options) {
|
|
12
|
+
const workflowsDir = path_1.default.join(outputDir, ".github", "workflows");
|
|
13
|
+
// Create directory if it doesn't exist
|
|
14
|
+
fs_1.default.mkdirSync(workflowsDir, { recursive: true });
|
|
15
|
+
// Try remote template, fall back to embedded
|
|
16
|
+
const result = await (0, template_loader_1.loadCICDTemplate)("github", options.strategy, options, generateWorkflowContentEmbedded);
|
|
17
|
+
(0, template_loader_1.logTemplateSource)("GitHub Actions", result.source);
|
|
18
|
+
const filePath = path_1.default.join(workflowsDir, "ci.yml");
|
|
19
|
+
fs_1.default.writeFileSync(filePath, result.content, "utf-8");
|
|
20
|
+
console.log(chalk_1.default.green(` ✓ Created .github/workflows/ci.yml`));
|
|
21
|
+
}
|
|
22
|
+
exports.generateGitHubActions = generateGitHubActions;
|
|
23
|
+
/**
|
|
24
|
+
* Embedded template generator (fallback when remote templates unavailable)
|
|
25
|
+
*/
|
|
26
|
+
function generateWorkflowContentEmbedded(options) {
|
|
27
|
+
const { projectName, nodeVersion, packageManager, strategy, includeSecurity, includeE2E, includeCoverage, dockerRegistry, deployTarget, branch, } = options;
|
|
28
|
+
const registry = dockerRegistry || "ghcr.io";
|
|
29
|
+
const installCmd = packageManager === "pnpm"
|
|
30
|
+
? "pnpm install"
|
|
31
|
+
: packageManager === "yarn"
|
|
32
|
+
? "yarn install"
|
|
33
|
+
: "npm ci";
|
|
34
|
+
const testCmd = packageManager === "pnpm"
|
|
35
|
+
? "pnpm test"
|
|
36
|
+
: packageManager === "yarn"
|
|
37
|
+
? "yarn test"
|
|
38
|
+
: "npm test";
|
|
39
|
+
const buildCmd = packageManager === "pnpm"
|
|
40
|
+
? "pnpm build"
|
|
41
|
+
: packageManager === "yarn"
|
|
42
|
+
? "yarn build"
|
|
43
|
+
: "npm run build";
|
|
44
|
+
const lintCmd = packageManager === "pnpm"
|
|
45
|
+
? "pnpm lint"
|
|
46
|
+
: packageManager === "yarn"
|
|
47
|
+
? "yarn lint"
|
|
48
|
+
: "npm run lint";
|
|
49
|
+
let workflow = `# GitHub Actions CI/CD Pipeline
|
|
50
|
+
# Generated by ExpressoTS CLI
|
|
51
|
+
# Strategy: ${strategy}
|
|
52
|
+
|
|
53
|
+
name: CI/CD Pipeline
|
|
54
|
+
|
|
55
|
+
on:
|
|
56
|
+
push:
|
|
57
|
+
branches: [${branch}]
|
|
58
|
+
pull_request:
|
|
59
|
+
branches: [${branch}]
|
|
60
|
+
|
|
61
|
+
env:
|
|
62
|
+
NODE_VERSION: '${nodeVersion}'
|
|
63
|
+
REGISTRY: ${registry}
|
|
64
|
+
IMAGE_NAME: \${{ github.repository }}
|
|
65
|
+
|
|
66
|
+
jobs:
|
|
67
|
+
`;
|
|
68
|
+
// Lint job
|
|
69
|
+
workflow += ` lint:
|
|
70
|
+
name: Lint
|
|
71
|
+
runs-on: ubuntu-latest
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/checkout@v4
|
|
74
|
+
|
|
75
|
+
- name: Setup Node.js
|
|
76
|
+
uses: actions/setup-node@v4
|
|
77
|
+
with:
|
|
78
|
+
node-version: \${{ env.NODE_VERSION }}
|
|
79
|
+
cache: '${packageManager}'
|
|
80
|
+
|
|
81
|
+
- name: Install dependencies
|
|
82
|
+
run: ${installCmd}
|
|
83
|
+
|
|
84
|
+
- name: Run linter
|
|
85
|
+
run: ${lintCmd}
|
|
86
|
+
|
|
87
|
+
`;
|
|
88
|
+
// Test job
|
|
89
|
+
workflow += ` test:
|
|
90
|
+
name: Test
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
needs: lint
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/checkout@v4
|
|
95
|
+
|
|
96
|
+
- name: Setup Node.js
|
|
97
|
+
uses: actions/setup-node@v4
|
|
98
|
+
with:
|
|
99
|
+
node-version: \${{ env.NODE_VERSION }}
|
|
100
|
+
cache: '${packageManager}'
|
|
101
|
+
|
|
102
|
+
- name: Install dependencies
|
|
103
|
+
run: ${installCmd}
|
|
104
|
+
|
|
105
|
+
- name: Run tests
|
|
106
|
+
run: ${testCmd}
|
|
107
|
+
`;
|
|
108
|
+
// Add coverage if enabled
|
|
109
|
+
if (includeCoverage) {
|
|
110
|
+
workflow += `
|
|
111
|
+
- name: Run tests with coverage
|
|
112
|
+
run: ${testCmd} -- --coverage
|
|
113
|
+
|
|
114
|
+
- name: Upload coverage to Codecov
|
|
115
|
+
uses: codecov/codecov-action@v4
|
|
116
|
+
with:
|
|
117
|
+
token: \${{ secrets.CODECOV_TOKEN }}
|
|
118
|
+
fail_ci_if_error: false
|
|
119
|
+
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
workflow += `
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
// Security job (comprehensive and security-focused strategies)
|
|
127
|
+
if (includeSecurity &&
|
|
128
|
+
(strategy === "comprehensive" || strategy === "security-focused")) {
|
|
129
|
+
workflow += ` security:
|
|
130
|
+
name: Security Scan
|
|
131
|
+
runs-on: ubuntu-latest
|
|
132
|
+
needs: lint
|
|
133
|
+
steps:
|
|
134
|
+
- uses: actions/checkout@v4
|
|
135
|
+
|
|
136
|
+
- name: Run Trivy vulnerability scanner
|
|
137
|
+
uses: aquasecurity/trivy-action@master
|
|
138
|
+
with:
|
|
139
|
+
scan-type: 'fs'
|
|
140
|
+
scan-ref: '.'
|
|
141
|
+
format: 'sarif'
|
|
142
|
+
output: 'trivy-results.sarif'
|
|
143
|
+
|
|
144
|
+
- name: Upload Trivy scan results
|
|
145
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
146
|
+
if: always()
|
|
147
|
+
with:
|
|
148
|
+
sarif_file: 'trivy-results.sarif'
|
|
149
|
+
`;
|
|
150
|
+
if (strategy === "security-focused") {
|
|
151
|
+
workflow += `
|
|
152
|
+
- name: Run Snyk security scan
|
|
153
|
+
uses: snyk/actions/node@master
|
|
154
|
+
continue-on-error: true
|
|
155
|
+
env:
|
|
156
|
+
SNYK_TOKEN: \${{ secrets.SNYK_TOKEN }}
|
|
157
|
+
with:
|
|
158
|
+
args: --severity-threshold=high
|
|
159
|
+
|
|
160
|
+
- name: OWASP Dependency Check
|
|
161
|
+
uses: dependency-check/Dependency-Check_Action@main
|
|
162
|
+
with:
|
|
163
|
+
project: '${projectName}'
|
|
164
|
+
path: '.'
|
|
165
|
+
format: 'HTML'
|
|
166
|
+
`;
|
|
167
|
+
}
|
|
168
|
+
workflow += `
|
|
169
|
+
`;
|
|
170
|
+
}
|
|
171
|
+
// E2E tests (if enabled)
|
|
172
|
+
if (includeE2E) {
|
|
173
|
+
workflow += ` e2e:
|
|
174
|
+
name: E2E Tests
|
|
175
|
+
runs-on: ubuntu-latest
|
|
176
|
+
needs: test
|
|
177
|
+
steps:
|
|
178
|
+
- uses: actions/checkout@v4
|
|
179
|
+
|
|
180
|
+
- name: Setup Node.js
|
|
181
|
+
uses: actions/setup-node@v4
|
|
182
|
+
with:
|
|
183
|
+
node-version: \${{ env.NODE_VERSION }}
|
|
184
|
+
cache: '${packageManager}'
|
|
185
|
+
|
|
186
|
+
- name: Install dependencies
|
|
187
|
+
run: ${installCmd}
|
|
188
|
+
|
|
189
|
+
- name: Build application
|
|
190
|
+
run: ${buildCmd}
|
|
191
|
+
|
|
192
|
+
- name: Start application
|
|
193
|
+
run: |
|
|
194
|
+
npm start &
|
|
195
|
+
sleep 10
|
|
196
|
+
|
|
197
|
+
- name: Run E2E tests
|
|
198
|
+
run: npm run test:e2e
|
|
199
|
+
|
|
200
|
+
`;
|
|
201
|
+
}
|
|
202
|
+
// Build job
|
|
203
|
+
workflow += ` build:
|
|
204
|
+
name: Build
|
|
205
|
+
runs-on: ubuntu-latest
|
|
206
|
+
needs: [test${includeSecurity && (strategy === "comprehensive" || strategy === "security-focused") ? ", security" : ""}${includeE2E ? ", e2e" : ""}]
|
|
207
|
+
steps:
|
|
208
|
+
- uses: actions/checkout@v4
|
|
209
|
+
|
|
210
|
+
- name: Setup Node.js
|
|
211
|
+
uses: actions/setup-node@v4
|
|
212
|
+
with:
|
|
213
|
+
node-version: \${{ env.NODE_VERSION }}
|
|
214
|
+
cache: '${packageManager}'
|
|
215
|
+
|
|
216
|
+
- name: Install dependencies
|
|
217
|
+
run: ${installCmd}
|
|
218
|
+
|
|
219
|
+
- name: Build application
|
|
220
|
+
run: ${buildCmd}
|
|
221
|
+
|
|
222
|
+
- name: Upload build artifacts
|
|
223
|
+
uses: actions/upload-artifact@v4
|
|
224
|
+
with:
|
|
225
|
+
name: dist
|
|
226
|
+
path: dist/
|
|
227
|
+
retention-days: 7
|
|
228
|
+
|
|
229
|
+
`;
|
|
230
|
+
// Docker build job
|
|
231
|
+
workflow += ` docker:
|
|
232
|
+
name: Docker Build
|
|
233
|
+
runs-on: ubuntu-latest
|
|
234
|
+
needs: build
|
|
235
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/${branch}'
|
|
236
|
+
permissions:
|
|
237
|
+
contents: read
|
|
238
|
+
packages: write
|
|
239
|
+
steps:
|
|
240
|
+
- uses: actions/checkout@v4
|
|
241
|
+
|
|
242
|
+
- name: Set up Docker Buildx
|
|
243
|
+
uses: docker/setup-buildx-action@v3
|
|
244
|
+
|
|
245
|
+
- name: Log in to Container Registry
|
|
246
|
+
uses: docker/login-action@v3
|
|
247
|
+
with:
|
|
248
|
+
registry: \${{ env.REGISTRY }}
|
|
249
|
+
username: \${{ github.actor }}
|
|
250
|
+
password: \${{ secrets.GITHUB_TOKEN }}
|
|
251
|
+
|
|
252
|
+
- name: Extract metadata
|
|
253
|
+
id: meta
|
|
254
|
+
uses: docker/metadata-action@v5
|
|
255
|
+
with:
|
|
256
|
+
images: \${{ env.REGISTRY }}/\${{ env.IMAGE_NAME }}
|
|
257
|
+
tags: |
|
|
258
|
+
type=ref,event=branch
|
|
259
|
+
type=sha,prefix=
|
|
260
|
+
type=raw,value=latest,enable={{is_default_branch}}
|
|
261
|
+
|
|
262
|
+
- name: Build and push
|
|
263
|
+
uses: docker/build-push-action@v5
|
|
264
|
+
with:
|
|
265
|
+
context: .
|
|
266
|
+
push: true
|
|
267
|
+
tags: \${{ steps.meta.outputs.tags }}
|
|
268
|
+
labels: \${{ steps.meta.outputs.labels }}
|
|
269
|
+
cache-from: type=gha
|
|
270
|
+
cache-to: type=gha,mode=max
|
|
271
|
+
`;
|
|
272
|
+
// Add container vulnerability scan
|
|
273
|
+
if (includeSecurity) {
|
|
274
|
+
workflow += `
|
|
275
|
+
- name: Scan container for vulnerabilities
|
|
276
|
+
uses: aquasecurity/trivy-action@master
|
|
277
|
+
with:
|
|
278
|
+
image-ref: \${{ env.REGISTRY }}/\${{ env.IMAGE_NAME }}:latest
|
|
279
|
+
format: 'sarif'
|
|
280
|
+
output: 'container-scan.sarif'
|
|
281
|
+
|
|
282
|
+
- name: Upload container scan results
|
|
283
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
284
|
+
if: always()
|
|
285
|
+
with:
|
|
286
|
+
sarif_file: 'container-scan.sarif'
|
|
287
|
+
`;
|
|
288
|
+
}
|
|
289
|
+
workflow += `
|
|
290
|
+
`;
|
|
291
|
+
// Deploy job (if configured)
|
|
292
|
+
if (deployTarget !== "none") {
|
|
293
|
+
workflow += generateDeployJob(deployTarget, options);
|
|
294
|
+
}
|
|
295
|
+
return workflow;
|
|
296
|
+
}
|
|
297
|
+
function generateDeployJob(target, options) {
|
|
298
|
+
const { branch } = options;
|
|
299
|
+
switch (target) {
|
|
300
|
+
case "kubernetes":
|
|
301
|
+
return ` deploy:
|
|
302
|
+
name: Deploy to Kubernetes
|
|
303
|
+
runs-on: ubuntu-latest
|
|
304
|
+
needs: docker
|
|
305
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/${branch}'
|
|
306
|
+
environment: production
|
|
307
|
+
steps:
|
|
308
|
+
- uses: actions/checkout@v4
|
|
309
|
+
|
|
310
|
+
- name: Set up kubectl
|
|
311
|
+
uses: azure/setup-kubectl@v3
|
|
312
|
+
|
|
313
|
+
- name: Configure kubectl
|
|
314
|
+
run: |
|
|
315
|
+
echo "\${{ secrets.KUBE_CONFIG }}" | base64 -d > kubeconfig.yaml
|
|
316
|
+
export KUBECONFIG=kubeconfig.yaml
|
|
317
|
+
|
|
318
|
+
- name: Deploy to Kubernetes
|
|
319
|
+
run: |
|
|
320
|
+
kubectl set image deployment/expressots-app \\
|
|
321
|
+
app=\${{ env.REGISTRY }}/\${{ env.IMAGE_NAME }}:\${{ github.sha }}
|
|
322
|
+
kubectl rollout status deployment/expressots-app
|
|
323
|
+
|
|
324
|
+
`;
|
|
325
|
+
case "railway":
|
|
326
|
+
return ` deploy:
|
|
327
|
+
name: Deploy to Railway
|
|
328
|
+
runs-on: ubuntu-latest
|
|
329
|
+
needs: docker
|
|
330
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/${branch}'
|
|
331
|
+
environment: production
|
|
332
|
+
steps:
|
|
333
|
+
- uses: actions/checkout@v4
|
|
334
|
+
|
|
335
|
+
- name: Install Railway CLI
|
|
336
|
+
run: npm install -g @railway/cli
|
|
337
|
+
|
|
338
|
+
- name: Deploy to Railway
|
|
339
|
+
run: railway up
|
|
340
|
+
env:
|
|
341
|
+
RAILWAY_TOKEN: \${{ secrets.RAILWAY_TOKEN }}
|
|
342
|
+
|
|
343
|
+
`;
|
|
344
|
+
case "render":
|
|
345
|
+
return ` deploy:
|
|
346
|
+
name: Deploy to Render
|
|
347
|
+
runs-on: ubuntu-latest
|
|
348
|
+
needs: docker
|
|
349
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/${branch}'
|
|
350
|
+
environment: production
|
|
351
|
+
steps:
|
|
352
|
+
- name: Deploy to Render
|
|
353
|
+
run: |
|
|
354
|
+
curl -X POST \\
|
|
355
|
+
-H "Authorization: Bearer \${{ secrets.RENDER_API_KEY }}" \\
|
|
356
|
+
-H "Content-Type: application/json" \\
|
|
357
|
+
"\${{ secrets.RENDER_DEPLOY_HOOK_URL }}"
|
|
358
|
+
|
|
359
|
+
`;
|
|
360
|
+
case "fly":
|
|
361
|
+
return ` deploy:
|
|
362
|
+
name: Deploy to Fly.io
|
|
363
|
+
runs-on: ubuntu-latest
|
|
364
|
+
needs: docker
|
|
365
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/${branch}'
|
|
366
|
+
environment: production
|
|
367
|
+
steps:
|
|
368
|
+
- uses: actions/checkout@v4
|
|
369
|
+
|
|
370
|
+
- name: Setup Fly CLI
|
|
371
|
+
uses: superfly/flyctl-actions/setup-flyctl@master
|
|
372
|
+
|
|
373
|
+
- name: Deploy to Fly.io
|
|
374
|
+
run: flyctl deploy --remote-only
|
|
375
|
+
env:
|
|
376
|
+
FLY_API_TOKEN: \${{ secrets.FLY_API_TOKEN }}
|
|
377
|
+
|
|
378
|
+
`;
|
|
379
|
+
case "ecs":
|
|
380
|
+
return ` deploy:
|
|
381
|
+
name: Deploy to AWS ECS
|
|
382
|
+
runs-on: ubuntu-latest
|
|
383
|
+
needs: docker
|
|
384
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/${branch}'
|
|
385
|
+
environment: production
|
|
386
|
+
steps:
|
|
387
|
+
- name: Configure AWS credentials
|
|
388
|
+
uses: aws-actions/configure-aws-credentials@v4
|
|
389
|
+
with:
|
|
390
|
+
aws-access-key-id: \${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
391
|
+
aws-secret-access-key: \${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
392
|
+
aws-region: \${{ secrets.AWS_REGION }}
|
|
393
|
+
|
|
394
|
+
- name: Update ECS service
|
|
395
|
+
run: |
|
|
396
|
+
aws ecs update-service \\
|
|
397
|
+
--cluster \${{ secrets.ECS_CLUSTER }} \\
|
|
398
|
+
--service \${{ secrets.ECS_SERVICE }} \\
|
|
399
|
+
--force-new-deployment
|
|
400
|
+
|
|
401
|
+
`;
|
|
402
|
+
case "cloudrun":
|
|
403
|
+
return ` deploy:
|
|
404
|
+
name: Deploy to Cloud Run
|
|
405
|
+
runs-on: ubuntu-latest
|
|
406
|
+
needs: docker
|
|
407
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/${branch}'
|
|
408
|
+
environment: production
|
|
409
|
+
steps:
|
|
410
|
+
- name: Authenticate to Google Cloud
|
|
411
|
+
uses: google-github-actions/auth@v2
|
|
412
|
+
with:
|
|
413
|
+
credentials_json: \${{ secrets.GCP_CREDENTIALS }}
|
|
414
|
+
|
|
415
|
+
- name: Deploy to Cloud Run
|
|
416
|
+
uses: google-github-actions/deploy-cloudrun@v2
|
|
417
|
+
with:
|
|
418
|
+
service: expressots-app
|
|
419
|
+
image: \${{ env.REGISTRY }}/\${{ env.IMAGE_NAME }}:\${{ github.sha }}
|
|
420
|
+
region: \${{ secrets.GCP_REGION }}
|
|
421
|
+
|
|
422
|
+
`;
|
|
423
|
+
default:
|
|
424
|
+
return "";
|
|
425
|
+
}
|
|
426
|
+
}
|