@swovohq/fuel 0.2.0-alpha.3 → 0.2.0-alpha.30
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/LICENSE +21 -0
- package/README.md +231 -0
- package/dist/bin/fuel.js +150 -55
- package/dist/bin/fuel.js.map +1 -1
- package/dist/commands/config-verify.d.ts +4 -0
- package/dist/commands/config-verify.d.ts.map +1 -0
- package/dist/commands/config-verify.js +66 -0
- package/dist/commands/config-verify.js.map +1 -0
- package/dist/commands/create-app.d.ts +7 -2
- package/dist/commands/create-app.d.ts.map +1 -1
- package/dist/commands/create-app.js +84 -49
- package/dist/commands/create-app.js.map +1 -1
- package/dist/commands/infra-deploy.d.ts +5 -0
- package/dist/commands/infra-deploy.d.ts.map +1 -1
- package/dist/commands/infra-deploy.js +59 -15
- package/dist/commands/infra-deploy.js.map +1 -1
- package/dist/commands/infra-destroy.d.ts +2 -0
- package/dist/commands/infra-destroy.d.ts.map +1 -1
- package/dist/commands/infra-destroy.js +5 -6
- package/dist/commands/infra-destroy.js.map +1 -1
- package/dist/commands/infra-init.d.ts +1 -0
- package/dist/commands/infra-init.d.ts.map +1 -1
- package/dist/commands/infra-init.js +99 -14
- package/dist/commands/infra-init.js.map +1 -1
- package/dist/commands/migrate-init.d.ts.map +1 -1
- package/dist/commands/migrate-init.js +28 -12
- package/dist/commands/migrate-init.js.map +1 -1
- package/dist/engines/template-source.d.ts.map +1 -1
- package/dist/engines/template-source.js +14 -1
- package/dist/engines/template-source.js.map +1 -1
- package/dist/infra/config-loader.d.ts.map +1 -1
- package/dist/infra/config-loader.js +10 -2
- package/dist/infra/config-loader.js.map +1 -1
- package/dist/infra/config-writer.d.ts +2 -7
- package/dist/infra/config-writer.d.ts.map +1 -1
- package/dist/infra/config-writer.js +5 -11
- package/dist/infra/config-writer.js.map +1 -1
- package/dist/infra/credentials.d.ts +10 -2
- package/dist/infra/credentials.d.ts.map +1 -1
- package/dist/infra/credentials.js +44 -25
- package/dist/infra/credentials.js.map +1 -1
- package/dist/infra/git-client.d.ts +2 -0
- package/dist/infra/git-client.d.ts.map +1 -1
- package/dist/infra/git-client.js +56 -2
- package/dist/infra/git-client.js.map +1 -1
- package/dist/infra/github-client.d.ts +6 -0
- package/dist/infra/github-client.d.ts.map +1 -0
- package/dist/infra/github-client.js +36 -0
- package/dist/infra/github-client.js.map +1 -0
- package/dist/infra/orchestrator.d.ts +5 -2
- package/dist/infra/orchestrator.d.ts.map +1 -1
- package/dist/infra/orchestrator.js +96 -68
- package/dist/infra/orchestrator.js.map +1 -1
- package/dist/infra/port-utils.d.ts +4 -0
- package/dist/infra/port-utils.d.ts.map +1 -0
- package/dist/infra/port-utils.js +77 -0
- package/dist/infra/port-utils.js.map +1 -0
- package/dist/infra/preflight.d.ts +1 -1
- package/dist/infra/preflight.d.ts.map +1 -1
- package/dist/infra/preflight.js +4 -4
- package/dist/infra/preflight.js.map +1 -1
- package/dist/infra/s3-state-bucket.d.ts +1 -1
- package/dist/infra/s3-state-bucket.d.ts.map +1 -1
- package/dist/infra/s3-state-bucket.js +21 -26
- package/dist/infra/s3-state-bucket.js.map +1 -1
- package/dist/infra/secrets-manager-client.d.ts.map +1 -1
- package/dist/infra/secrets-manager-client.js +35 -1
- package/dist/infra/secrets-manager-client.js.map +1 -1
- package/dist/infra/template-engine.d.ts +1 -1
- package/dist/infra/template-engine.d.ts.map +1 -1
- package/dist/infra/template-engine.js +16 -29
- package/dist/infra/template-engine.js.map +1 -1
- package/dist/infra/tofu-runner.d.ts +5 -8
- package/dist/infra/tofu-runner.d.ts.map +1 -1
- package/dist/infra/tofu-runner.js +42 -80
- package/dist/infra/tofu-runner.js.map +1 -1
- package/package.json +6 -2
- package/schema/fuel.schema.json +158 -0
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import { InfraConfig
|
|
2
|
-
export interface FuelConfig extends InfraConfig {
|
|
3
|
-
seed: string;
|
|
4
|
-
aws_region?: string;
|
|
5
|
-
github_org?: string;
|
|
6
|
-
}
|
|
1
|
+
import { InfraConfig } from './types';
|
|
7
2
|
export declare function generateSeed(): string;
|
|
8
|
-
export declare function
|
|
3
|
+
export declare function writeFuelYml(projectRoot: string, config: InfraConfig, projectName?: string): Promise<void>;
|
|
9
4
|
//# sourceMappingURL=config-writer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-writer.d.ts","sourceRoot":"","sources":["../../src/infra/config-writer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config-writer.d.ts","sourceRoot":"","sources":["../../src/infra/config-writer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAErC,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,WAAW,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAUf"}
|
|
@@ -34,25 +34,19 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.generateSeed = generateSeed;
|
|
37
|
-
exports.
|
|
37
|
+
exports.writeFuelYml = writeFuelYml;
|
|
38
38
|
const fs = __importStar(require("fs-extra"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const crypto = __importStar(require("crypto"));
|
|
41
|
+
const YAML = __importStar(require("yaml"));
|
|
41
42
|
function generateSeed() {
|
|
42
43
|
return crypto.randomBytes(12).toString('hex'); // 24 hex chars
|
|
43
44
|
}
|
|
44
|
-
async function
|
|
45
|
-
const
|
|
46
|
-
const fuelConfig = {
|
|
45
|
+
async function writeFuelYml(projectRoot, config, projectName) {
|
|
46
|
+
const output = {
|
|
47
47
|
...config,
|
|
48
48
|
...(projectName ? { name: projectName } : {}),
|
|
49
|
-
seed,
|
|
50
|
-
...(credentials ? {
|
|
51
|
-
aws_region: credentials.AWS_REGION,
|
|
52
|
-
github_org: credentials.GITHUB_ORGANIZATION,
|
|
53
|
-
} : {}),
|
|
54
49
|
};
|
|
55
|
-
await fs.
|
|
56
|
-
return fuelConfig;
|
|
50
|
+
await fs.writeFile(path.join(projectRoot, 'fuel.yml'), YAML.stringify(output), 'utf-8');
|
|
57
51
|
}
|
|
58
52
|
//# sourceMappingURL=config-writer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-writer.js","sourceRoot":"","sources":["../../src/infra/config-writer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"config-writer.js","sourceRoot":"","sources":["../../src/infra/config-writer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,oCAEC;AAED,oCAcC;AAxBD,6CAA8B;AAC9B,2CAA4B;AAC5B,+CAAgC;AAChC,2CAA4B;AAG5B,SAAgB,YAAY;IAC1B,OAAO,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA,CAAC,eAAe;AAC/D,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,WAAmB,EACnB,MAAmB,EACnB,WAAoB;IAEpB,MAAM,MAAM,GAAgB;QAC1B,GAAG,MAAM;QACT,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9C,CAAA;IACD,MAAM,EAAE,CAAC,SAAS,CAChB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,EAClC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EACtB,OAAO,CACR,CAAA;AACH,CAAC"}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
import { Credentials } from './types';
|
|
2
|
-
|
|
1
|
+
import { Credentials, CredentialKey } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve credentials by merging sources in priority order:
|
|
4
|
+
* 1. CLI flag overrides (highest)
|
|
5
|
+
* 2. .fuel-credentials file (searchDir, then CWD fallback)
|
|
6
|
+
* 3. Environment variables (lowest)
|
|
7
|
+
*
|
|
8
|
+
* Sources are merged per-key — e.g., AWS keys from env + GITHUB_TOKEN from file = valid.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveCredentials(searchDir?: string, overrides?: Partial<Record<CredentialKey, string>>): Promise<Credentials>;
|
|
3
11
|
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/infra/credentials.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/infra/credentials.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAA4B,aAAa,EAAE,MAAM,SAAS,CAAA;AAgB9E;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,GACjD,OAAO,CAAC,WAAW,CAAC,CAkCtB"}
|
|
@@ -37,38 +37,57 @@ exports.resolveCredentials = resolveCredentials;
|
|
|
37
37
|
const path = __importStar(require("path"));
|
|
38
38
|
const fs = __importStar(require("fs-extra"));
|
|
39
39
|
const types_1 = require("./types");
|
|
40
|
-
async function
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
async function tryReadCredentialsFile(credPath) {
|
|
41
|
+
if (!(await fs.pathExists(credPath)))
|
|
42
|
+
return {};
|
|
43
|
+
try {
|
|
44
|
+
const file = await fs.readJson(credPath);
|
|
45
|
+
const result = {};
|
|
46
|
+
for (const key of types_1.REQUIRED_CREDENTIAL_KEYS) {
|
|
47
|
+
if (file[key])
|
|
48
|
+
result[key] = file[key];
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
45
51
|
}
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
async function resolveCredentials(searchDir) {
|
|
49
|
-
// Priority 1: ALL keys present in env
|
|
50
|
-
const envValues = types_1.REQUIRED_CREDENTIAL_KEYS.map((k) => process.env[k]);
|
|
51
|
-
if (envValues.every(Boolean)) {
|
|
52
|
-
return Object.fromEntries(types_1.REQUIRED_CREDENTIAL_KEYS.map((k, i) => [k, envValues[i]]));
|
|
52
|
+
catch {
|
|
53
|
+
return {};
|
|
53
54
|
}
|
|
54
|
-
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Resolve credentials by merging sources in priority order:
|
|
58
|
+
* 1. CLI flag overrides (highest)
|
|
59
|
+
* 2. .fuel-credentials file (searchDir, then CWD fallback)
|
|
60
|
+
* 3. Environment variables (lowest)
|
|
61
|
+
*
|
|
62
|
+
* Sources are merged per-key — e.g., AWS keys from env + GITHUB_TOKEN from file = valid.
|
|
63
|
+
*/
|
|
64
|
+
async function resolveCredentials(searchDir, overrides) {
|
|
65
|
+
// Collect file values from searchDir and CWD
|
|
55
66
|
const credPath = searchDir
|
|
56
67
|
? path.join(searchDir, '.fuel-credentials')
|
|
57
68
|
: path.resolve('.fuel-credentials');
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
// Priority 3: fallback to CWD when searchDir was provided and differs from CWD
|
|
69
|
+
const fileValues = await tryReadCredentialsFile(credPath);
|
|
70
|
+
// Fallback to CWD if searchDir was provided and differs
|
|
62
71
|
const cwd = process.cwd();
|
|
72
|
+
let cwdValues = {};
|
|
63
73
|
if (searchDir && path.resolve(searchDir) !== cwd) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
cwdValues = await tryReadCredentialsFile(path.resolve('.fuel-credentials'));
|
|
75
|
+
}
|
|
76
|
+
// Merge per-key: overrides → file (searchDir) → file (CWD) → env
|
|
77
|
+
const resolved = {};
|
|
78
|
+
for (const key of types_1.REQUIRED_CREDENTIAL_KEYS) {
|
|
79
|
+
resolved[key] =
|
|
80
|
+
overrides?.[key] ||
|
|
81
|
+
fileValues[key] ||
|
|
82
|
+
cwdValues[key] ||
|
|
83
|
+
process.env[key] ||
|
|
84
|
+
undefined;
|
|
85
|
+
}
|
|
86
|
+
const missing = types_1.REQUIRED_CREDENTIAL_KEYS.filter((k) => !resolved[k]);
|
|
87
|
+
if (missing.length > 0) {
|
|
88
|
+
throw new Error(`Missing credentials: ${missing.join(', ')}\n` +
|
|
89
|
+
`Provide them via CLI flags, .fuel-credentials file, or environment variables.`);
|
|
68
90
|
}
|
|
69
|
-
|
|
70
|
-
const missingFromEnv = types_1.REQUIRED_CREDENTIAL_KEYS.filter((k) => !process.env[k]);
|
|
71
|
-
throw new Error(`Credentials not found. Missing env vars: ${missingFromEnv.join(', ')}\n` +
|
|
72
|
-
`Or create a .fuel-credentials JSON file with those keys.`);
|
|
91
|
+
return resolved;
|
|
73
92
|
}
|
|
74
93
|
//# sourceMappingURL=credentials.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/infra/credentials.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/infra/credentials.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,gDAqCC;AA/DD,2CAA4B;AAC5B,6CAA8B;AAC9B,mCAA8E;AAE9E,KAAK,UAAU,sBAAsB,CAAC,QAAgB;IACpD,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAAE,OAAO,EAAE,CAAA;IAC/C,IAAI,CAAC;QACH,MAAM,IAAI,GAA2B,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;QAChE,MAAM,MAAM,GAA2C,EAAE,CAAA;QACzD,KAAK,MAAM,GAAG,IAAI,gCAAwB,EAAE,CAAC;YAC3C,IAAI,IAAI,CAAC,GAAG,CAAC;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;QACxC,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,kBAAkB,CACtC,SAAkB,EAClB,SAAkD;IAElD,6CAA6C;IAC7C,MAAM,QAAQ,GAAG,SAAS;QACxB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,CAAC;QAC3C,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACrC,MAAM,UAAU,GAAG,MAAM,sBAAsB,CAAC,QAAQ,CAAC,CAAA;IAEzD,wDAAwD;IACxD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IACzB,IAAI,SAAS,GAA2C,EAAE,CAAA;IAC1D,IAAI,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,GAAG,EAAE,CAAC;QACjD,SAAS,GAAG,MAAM,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAA;IAC7E,CAAC;IAED,iEAAiE;IACjE,MAAM,QAAQ,GAA2C,EAAE,CAAA;IAC3D,KAAK,MAAM,GAAG,IAAI,gCAAwB,EAAE,CAAC;QAC3C,QAAQ,CAAC,GAAG,CAAC;YACX,SAAS,EAAE,CAAC,GAAG,CAAC;gBAChB,UAAU,CAAC,GAAG,CAAC;gBACf,SAAS,CAAC,GAAG,CAAC;gBACd,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;gBAChB,SAAS,CAAA;IACb,CAAC;IAED,MAAM,OAAO,GAAG,gCAAwB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;IACpE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,wBAAwB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAC5C,+EAA+E,CAClF,CAAA;IACH,CAAC;IAED,OAAO,QAAuB,CAAA;AAChC,CAAC"}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export declare function wireGitRemote(projectDir: string, repoUrl: string): Promise<void>;
|
|
2
|
+
export declare function commitAll(projectDir: string, message: string): Promise<boolean>;
|
|
3
|
+
export declare function pushToRemote(projectDir: string, githubToken: string, branches: string[], forcePush?: boolean): Promise<void>;
|
|
2
4
|
//# sourceMappingURL=git-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-client.d.ts","sourceRoot":"","sources":["../../src/infra/git-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"git-client.d.ts","sourceRoot":"","sources":["../../src/infra/git-client.ts"],"names":[],"mappings":"AAgCA,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWtF;AAED,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAarF;AAED,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,SAAS,CAAC,EAAE,OAAO,GAClB,OAAO,CAAC,IAAI,CAAC,CAoCf"}
|
package/dist/infra/git-client.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.wireGitRemote = wireGitRemote;
|
|
4
|
+
exports.commitAll = commitAll;
|
|
5
|
+
exports.pushToRemote = pushToRemote;
|
|
4
6
|
const child_process_1 = require("child_process");
|
|
5
7
|
function spawnSimple(cmd, args, cwd) {
|
|
6
8
|
return new Promise((resolve) => {
|
|
@@ -10,13 +12,65 @@ function spawnSimple(cmd, args, cwd) {
|
|
|
10
12
|
});
|
|
11
13
|
});
|
|
12
14
|
}
|
|
15
|
+
function spawnQuiet(cmd, args, cwd) {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
const proc = (0, child_process_1.spawn)(cmd, args, { cwd, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
18
|
+
let stdout = '';
|
|
19
|
+
proc.stdout.on('data', (chunk) => {
|
|
20
|
+
stdout += chunk.toString();
|
|
21
|
+
});
|
|
22
|
+
proc.on('close', (code) => {
|
|
23
|
+
resolve({ code: code ?? 1, stdout: stdout.trim() });
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
13
27
|
async function wireGitRemote(projectDir, repoUrl) {
|
|
14
|
-
|
|
28
|
+
// Try set-url quietly — it fails if origin doesn't exist yet
|
|
29
|
+
const setUrlResult = await spawnQuiet('git', ['remote', 'set-url', 'origin', repoUrl], projectDir);
|
|
15
30
|
if (setUrlResult.code !== 0) {
|
|
16
|
-
const addResult = await
|
|
31
|
+
const addResult = await spawnQuiet('git', ['remote', 'add', 'origin', repoUrl], projectDir);
|
|
17
32
|
if (addResult.code !== 0) {
|
|
18
33
|
throw new Error(`Failed to set git remote origin to ${repoUrl}`);
|
|
19
34
|
}
|
|
20
35
|
}
|
|
21
36
|
}
|
|
37
|
+
async function commitAll(projectDir, message) {
|
|
38
|
+
await spawnSimple('git', ['add', '-A'], projectDir);
|
|
39
|
+
const diffResult = await spawnQuiet('git', ['diff', '--cached', '--quiet'], projectDir);
|
|
40
|
+
if (diffResult.code === 0) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const commitResult = await spawnSimple('git', ['commit', '-m', message], projectDir);
|
|
44
|
+
if (commitResult.code !== 0) {
|
|
45
|
+
throw new Error(`Failed to commit: ${message}`);
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
async function pushToRemote(projectDir, githubToken, branches, forcePush) {
|
|
50
|
+
const { stdout: originalUrl } = await spawnQuiet('git', ['remote', 'get-url', 'origin'], projectDir);
|
|
51
|
+
const authUrl = originalUrl.replace('https://github.com/', `https://x-access-token:${githubToken}@github.com/`);
|
|
52
|
+
await spawnSimple('git', ['remote', 'set-url', 'origin', authUrl], projectDir);
|
|
53
|
+
try {
|
|
54
|
+
for (const branch of branches) {
|
|
55
|
+
// Create local branch if it doesn't exist (non-main branches)
|
|
56
|
+
const branchCheck = await spawnQuiet('git', ['rev-parse', '--verify', branch], projectDir);
|
|
57
|
+
if (branchCheck.code !== 0) {
|
|
58
|
+
const createResult = await spawnSimple('git', ['branch', branch], projectDir);
|
|
59
|
+
if (createResult.code !== 0) {
|
|
60
|
+
throw new Error(`Failed to create local branch: ${branch}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const pushArgs = forcePush
|
|
64
|
+
? ['push', '--force', '-u', 'origin', branch]
|
|
65
|
+
: ['push', '-u', 'origin', branch];
|
|
66
|
+
const pushResult = await spawnSimple('git', pushArgs, projectDir);
|
|
67
|
+
if (pushResult.code !== 0) {
|
|
68
|
+
throw new Error(`Failed to push branch: ${branch}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
await spawnSimple('git', ['remote', 'set-url', 'origin', originalUrl], projectDir);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
22
76
|
//# sourceMappingURL=git-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-client.js","sourceRoot":"","sources":["../../src/infra/git-client.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"git-client.js","sourceRoot":"","sources":["../../src/infra/git-client.ts"],"names":[],"mappings":";;AAgCA,sCAWC;AAED,8BAaC;AAED,oCAyCC;AArGD,iDAAqC;AAErC,SAAS,WAAW,CAClB,GAAW,EACX,IAAc,EACd,GAAW;IAEX,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,IAAA,qBAAK,EAAC,GAAG,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;QACxD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAmB,EAAE,EAAE;YACvC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,CAAA;QAC9B,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,UAAU,CACjB,GAAW,EACX,IAAc,EACd,GAAW;IAEX,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,IAAA,qBAAK,EAAC,GAAG,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAA;QACvE,IAAI,MAAM,GAAG,EAAE,CAAA;QACf,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACvC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAA;QAC5B,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAmB,EAAE,EAAE;YACvC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QACrD,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC;AAEM,KAAK,UAAU,aAAa,CAAC,UAAkB,EAAE,OAAe;IACrE,6DAA6D;IAC7D,MAAM,YAAY,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,CAAA;IAElG,IAAI,YAAY,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,CAAA;QAE3F,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,sCAAsC,OAAO,EAAE,CAAC,CAAA;QAClE,CAAC;IACH,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,SAAS,CAAC,UAAkB,EAAE,OAAe;IACjE,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,UAAU,CAAC,CAAA;IAEnD,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,UAAU,CAAC,CAAA;IACvF,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,CAAA;IACpF,IAAI,YAAY,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,qBAAqB,OAAO,EAAE,CAAC,CAAA;IACjD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,UAAkB,EAClB,WAAmB,EACnB,QAAkB,EAClB,SAAmB;IAEnB,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,UAAU,CAC9C,KAAK,EACL,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,EAC/B,UAAU,CACX,CAAA;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CACjC,qBAAqB,EACrB,0BAA0B,WAAW,cAAc,CACpD,CAAA;IAED,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,CAAA;IAE9E,IAAI,CAAC;QACH,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;YAC9B,8DAA8D;YAC9D,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAA;YAC1F,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBAC3B,MAAM,YAAY,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAA;gBAC7E,IAAI,YAAY,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;oBAC5B,MAAM,IAAI,KAAK,CAAC,kCAAkC,MAAM,EAAE,CAAC,CAAA;gBAC7D,CAAC;YACH,CAAC;YAED,MAAM,QAAQ,GAAG,SAAS;gBACxB,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC;gBAC7C,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;YACpC,MAAM,UAAU,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;YACjE,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAA;YACrD,CAAC;QACH,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,UAAU,CAAC,CAAA;IACpF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function createGitHubRepo(token: string, org: string, name: string): Promise<{
|
|
2
|
+
name: string;
|
|
3
|
+
htmlUrl: string;
|
|
4
|
+
}>;
|
|
5
|
+
export declare function setBranchProtection(token: string, owner: string, repo: string, branch: string): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=github-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-client.d.ts","sourceRoot":"","sources":["../../src/infra/github-client.ts"],"names":[],"mappings":"AAEA,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAY5C;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAmBf"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createGitHubRepo = createGitHubRepo;
|
|
4
|
+
exports.setBranchProtection = setBranchProtection;
|
|
5
|
+
const rest_1 = require("@octokit/rest");
|
|
6
|
+
async function createGitHubRepo(token, org, name) {
|
|
7
|
+
const octokit = new rest_1.Octokit({ auth: token });
|
|
8
|
+
const { data } = await octokit.rest.repos.createInOrg({
|
|
9
|
+
org,
|
|
10
|
+
name,
|
|
11
|
+
auto_init: false,
|
|
12
|
+
visibility: 'private',
|
|
13
|
+
description: 'FUEL Monorepo — generated by Fuel CLI',
|
|
14
|
+
});
|
|
15
|
+
return { name: data.name, htmlUrl: data.html_url };
|
|
16
|
+
}
|
|
17
|
+
async function setBranchProtection(token, owner, repo, branch) {
|
|
18
|
+
const octokit = new rest_1.Octokit({ auth: token });
|
|
19
|
+
await octokit.rest.repos.updateBranchProtection({
|
|
20
|
+
owner,
|
|
21
|
+
repo,
|
|
22
|
+
branch,
|
|
23
|
+
enforce_admins: false,
|
|
24
|
+
required_pull_request_reviews: {
|
|
25
|
+
required_approving_review_count: 1,
|
|
26
|
+
dismiss_stale_reviews: true,
|
|
27
|
+
},
|
|
28
|
+
required_status_checks: {
|
|
29
|
+
strict: true,
|
|
30
|
+
contexts: ['Title matches Jira Issue'],
|
|
31
|
+
},
|
|
32
|
+
restrictions: null,
|
|
33
|
+
allow_deletions: false,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=github-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-client.js","sourceRoot":"","sources":["../../src/infra/github-client.ts"],"names":[],"mappings":";;AAEA,4CAgBC;AAED,kDAwBC;AA5CD,wCAAuC;AAEhC,KAAK,UAAU,gBAAgB,CACpC,KAAa,EACb,GAAW,EACX,IAAY;IAEZ,MAAM,OAAO,GAAG,IAAI,cAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAE5C,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;QACpD,GAAG;QACH,IAAI;QACJ,SAAS,EAAE,KAAK;QAChB,UAAU,EAAE,SAAS;QACrB,WAAW,EAAE,uCAAuC;KACrD,CAAC,CAAA;IAEF,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAA;AACpD,CAAC;AAEM,KAAK,UAAU,mBAAmB,CACvC,KAAa,EACb,KAAa,EACb,IAAY,EACZ,MAAc;IAEd,MAAM,OAAO,GAAG,IAAI,cAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAE5C,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC;QAC9C,KAAK;QACL,IAAI;QACJ,MAAM;QACN,cAAc,EAAE,KAAK;QACrB,6BAA6B,EAAE;YAC7B,+BAA+B,EAAE,CAAC;YAClC,qBAAqB,EAAE,IAAI;SAC5B;QACD,sBAAsB,EAAE;YACtB,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE,CAAC,0BAA0B,CAAC;SACvC;QACD,YAAY,EAAE,IAAI;QAClB,eAAe,EAAE,KAAK;KACvB,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { InfraConfig, Credentials } from './types';
|
|
2
2
|
export interface OrchestratorOptions {
|
|
3
|
-
|
|
3
|
+
destroyOnFailure?: boolean;
|
|
4
4
|
planOnly?: boolean;
|
|
5
|
+
targetBranch?: string;
|
|
6
|
+
forcePush?: boolean;
|
|
7
|
+
skipBranchProtection?: boolean;
|
|
5
8
|
}
|
|
6
|
-
export declare function runInfraOrchestrator(targetRoot: string,
|
|
9
|
+
export declare function runInfraOrchestrator(targetRoot: string, _appName: string, config: InfraConfig, credentials: Credentials, options?: OrchestratorOptions): Promise<void>;
|
|
7
10
|
//# sourceMappingURL=orchestrator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/infra/orchestrator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/infra/orchestrator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAqDlD,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,WAAW,EACxB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA0Jf"}
|
|
@@ -37,48 +37,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.runInfraOrchestrator = runInfraOrchestrator;
|
|
40
|
-
const crypto = __importStar(require("crypto"));
|
|
41
|
-
const fs = __importStar(require("fs"));
|
|
42
40
|
const path = __importStar(require("path"));
|
|
43
41
|
const chalk_1 = __importDefault(require("chalk"));
|
|
44
42
|
const tofu_runner_1 = require("./tofu-runner");
|
|
45
43
|
const secrets_manager_client_1 = require("./secrets-manager-client");
|
|
44
|
+
const config_writer_1 = require("./config-writer");
|
|
46
45
|
const git_client_1 = require("./git-client");
|
|
46
|
+
const github_client_1 = require("./github-client");
|
|
47
|
+
const github_secrets_client_1 = require("./github-secrets-client");
|
|
47
48
|
const subprocess_1 = require("./subprocess");
|
|
48
49
|
const s3_state_bucket_1 = require("./s3-state-bucket");
|
|
49
50
|
function slugify(name) {
|
|
50
51
|
return name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
|
51
52
|
}
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
targets.push('-target=module.ecr', '-target=module.github_repo');
|
|
56
|
-
}
|
|
57
|
-
if (completedPasses.includes(2)) {
|
|
58
|
-
targets.push('-target=module.github_actions');
|
|
59
|
-
}
|
|
60
|
-
return [
|
|
61
|
-
'destroy',
|
|
62
|
-
'-var-file=default.tfvars',
|
|
63
|
-
'-auto-approve',
|
|
64
|
-
`-var=github_token=${credentials.GITHUB_TOKEN}`,
|
|
65
|
-
`-var=github_username=${credentials.GITHUB_USERNAME}`,
|
|
66
|
-
`-var=aws_access_key_id=${credentials.AWS_ACCESS_KEY_ID}`,
|
|
67
|
-
`-var=aws_secret_access_key=${credentials.AWS_SECRET_ACCESS_KEY}`,
|
|
68
|
-
`-var=aws_region=${credentials.AWS_REGION}`,
|
|
69
|
-
`-var=github_organization=${credentials.GITHUB_ORGANIZATION}`,
|
|
70
|
-
`-var=db_password=${tofuVars.db_password}`,
|
|
71
|
-
`-var=db_username=${tofuVars.db_username}`,
|
|
72
|
-
`-var=db_name=${tofuVars.db_name}`,
|
|
73
|
-
`-var=skip_ecs=true`,
|
|
74
|
-
...targets,
|
|
75
|
-
];
|
|
76
|
-
}
|
|
77
|
-
async function runCompensation(infraDir, credentials, tofuVars, completedPasses) {
|
|
78
|
-
if (completedPasses.length === 0)
|
|
79
|
-
return;
|
|
80
|
-
console.log(chalk_1.default.red('\n--- Cleaning up partial resources ---'));
|
|
81
|
-
const args = buildDestroyArgs(completedPasses, credentials, tofuVars);
|
|
53
|
+
async function runCompensation(infraDir, credentials, tofuVars) {
|
|
54
|
+
console.log(chalk_1.default.red('\n--- Cleaning up AWS resources ---'));
|
|
55
|
+
const args = (0, tofu_runner_1.buildDestroyArgs)(credentials, tofuVars);
|
|
82
56
|
const awsEnv = {
|
|
83
57
|
AWS_ACCESS_KEY_ID: credentials.AWS_ACCESS_KEY_ID,
|
|
84
58
|
AWS_SECRET_ACCESS_KEY: credentials.AWS_SECRET_ACCESS_KEY,
|
|
@@ -86,19 +60,12 @@ async function runCompensation(infraDir, credentials, tofuVars, completedPasses)
|
|
|
86
60
|
};
|
|
87
61
|
const { code } = await (0, subprocess_1.spawnStreaming)(args, infraDir, awsEnv);
|
|
88
62
|
if (code !== 0) {
|
|
89
|
-
console.error(chalk_1.default.red('Compensation destroy failed. Manually delete
|
|
90
|
-
if (completedPasses.includes(1)) {
|
|
91
|
-
console.error(chalk_1.default.red(' - ECR repository (module.ecr)'));
|
|
92
|
-
console.error(chalk_1.default.red(' - GitHub repository (module.github_repo)'));
|
|
93
|
-
}
|
|
94
|
-
if (completedPasses.includes(2)) {
|
|
95
|
-
console.error(chalk_1.default.red(' - GitHub Actions config (module.github_actions)'));
|
|
96
|
-
}
|
|
63
|
+
console.error(chalk_1.default.red('Compensation destroy failed. Manually delete AWS resources.'));
|
|
97
64
|
process.exit(1);
|
|
98
65
|
}
|
|
99
|
-
console.log(chalk_1.default.green('
|
|
66
|
+
console.log(chalk_1.default.green('AWS resources cleaned up'));
|
|
100
67
|
}
|
|
101
|
-
function printOutputTable(albDns, ecrUrl, githubRepoUrl
|
|
68
|
+
function printOutputTable(albDns, ecrUrl, githubRepoUrl) {
|
|
102
69
|
const divider = chalk_1.default.gray('─'.repeat(60));
|
|
103
70
|
console.log('\n' + divider);
|
|
104
71
|
console.log(chalk_1.default.bold(' Deployment Complete'));
|
|
@@ -106,76 +73,137 @@ function printOutputTable(albDns, ecrUrl, githubRepoUrl, dbUsername, dbPassword)
|
|
|
106
73
|
console.log(` ${chalk_1.default.dim('ALB DNS')} ${albDns}`);
|
|
107
74
|
console.log(` ${chalk_1.default.dim('ECR URL')} ${ecrUrl}`);
|
|
108
75
|
console.log(` ${chalk_1.default.dim('GitHub Repo')} ${githubRepoUrl}`);
|
|
109
|
-
console.log(` ${chalk_1.default.dim('DB Username')} ${dbUsername}`);
|
|
110
|
-
console.log(` ${chalk_1.default.dim('DB Password')} ${dbPassword} ${chalk_1.default.yellow("Save these — they won't be shown again")}`);
|
|
111
76
|
console.log(divider);
|
|
112
77
|
console.log(chalk_1.default.cyan('\n Next steps:'));
|
|
113
|
-
console.log('
|
|
78
|
+
console.log(' Your code has been pushed. Your first deploy will start shortly.\n');
|
|
114
79
|
}
|
|
115
|
-
async function runInfraOrchestrator(targetRoot,
|
|
80
|
+
async function runInfraOrchestrator(targetRoot, _appName, config, credentials, options) {
|
|
116
81
|
const infraDir = path.join(targetRoot, 'infra');
|
|
82
|
+
const repoName = `${config.name}-monorepo`;
|
|
117
83
|
// Generate TofuVars
|
|
118
84
|
const tofuVars = {
|
|
119
|
-
db_password: crypto.randomBytes(32).toString('hex'),
|
|
120
85
|
db_username: slugify(config.name),
|
|
121
86
|
db_name: slugify(config.name),
|
|
87
|
+
github_repo_name: repoName,
|
|
122
88
|
};
|
|
123
89
|
// Ensure S3 state bucket exists (before Tofu init)
|
|
124
90
|
console.log(chalk_1.default.cyan('--- Ensuring state bucket exists ---'));
|
|
125
|
-
const suffix =
|
|
91
|
+
const suffix = (0, s3_state_bucket_1.deriveSuffix)(config.name);
|
|
126
92
|
const bucketName = (0, s3_state_bucket_1.buildStateBucketName)(config.name, suffix);
|
|
127
93
|
await (0, s3_state_bucket_1.ensureStateBucket)(credentials, bucketName);
|
|
128
94
|
// Write secret to AWS Secrets Manager (before Tofu — Tofu reads it via data.tf)
|
|
129
95
|
const secretName = `${config.name}-infrastructure-variables`;
|
|
130
|
-
const
|
|
131
|
-
|
|
96
|
+
const secretPayload = {
|
|
97
|
+
...config,
|
|
98
|
+
seed: (0, config_writer_1.generateSeed)(),
|
|
99
|
+
aws_region: credentials.AWS_REGION,
|
|
100
|
+
github_org: credentials.GITHUB_ORGANIZATION,
|
|
101
|
+
};
|
|
132
102
|
console.log(chalk_1.default.cyan('--- Writing infrastructure secret ---'));
|
|
133
|
-
await (0, secrets_manager_client_1.putSecret)(credentials, secretName,
|
|
103
|
+
await (0, secrets_manager_client_1.putSecret)(credentials, secretName, secretPayload);
|
|
134
104
|
console.log(chalk_1.default.green('Secret written to AWS Secrets Manager'));
|
|
135
105
|
// SIGINT compensation setup
|
|
136
|
-
const state = { tofuStarted: false
|
|
106
|
+
const state = { tofuStarted: false };
|
|
137
107
|
const sigintHandler = async () => {
|
|
138
|
-
if (options?.
|
|
139
|
-
console.log(chalk_1.default.yellow('\nInterrupted — partial resources preserved.'));
|
|
140
|
-
console.log(chalk_1.default.yellow('Re-run "fuel infra:deploy" to resume.\n'));
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
108
|
+
if (options?.destroyOnFailure) {
|
|
143
109
|
console.log(chalk_1.default.yellow('\nInterrupted — running compensation...'));
|
|
144
|
-
if (state.tofuStarted
|
|
145
|
-
await runCompensation(infraDir, credentials, tofuVars
|
|
110
|
+
if (state.tofuStarted) {
|
|
111
|
+
await runCompensation(infraDir, credentials, tofuVars);
|
|
146
112
|
}
|
|
147
113
|
}
|
|
114
|
+
else {
|
|
115
|
+
console.log(chalk_1.default.yellow('\nInterrupted — partial resources preserved.'));
|
|
116
|
+
console.log(chalk_1.default.yellow('Re-run "fuel infra:deploy" to resume.\n'));
|
|
117
|
+
}
|
|
148
118
|
process.exit(1);
|
|
149
119
|
};
|
|
150
120
|
process.once('SIGINT', sigintHandler);
|
|
151
121
|
try {
|
|
152
|
-
//
|
|
122
|
+
// Step 1: Create GitHub repo (skip in plan-only mode)
|
|
123
|
+
if (!options?.planOnly) {
|
|
124
|
+
console.log(chalk_1.default.cyan('--- Creating GitHub repository ---'));
|
|
125
|
+
try {
|
|
126
|
+
await (0, github_client_1.createGitHubRepo)(credentials.GITHUB_TOKEN, credentials.GITHUB_ORGANIZATION, repoName);
|
|
127
|
+
console.log(chalk_1.default.green(`✓ Repository created: ${credentials.GITHUB_ORGANIZATION}/${repoName}`));
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
// If repo already exists (422), continue — it may be a re-run
|
|
131
|
+
if (err?.status === 422) {
|
|
132
|
+
console.log(chalk_1.default.yellow(`Repository ${credentials.GITHUB_ORGANIZATION}/${repoName} already exists, continuing...`));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Step 2: Single Tofu apply (all AWS resources)
|
|
153
140
|
state.tofuStarted = true;
|
|
154
|
-
const result = await (0, tofu_runner_1.
|
|
141
|
+
const result = await (0, tofu_runner_1.runTofuApply)(infraDir, credentials, tofuVars, {
|
|
155
142
|
planOnly: options?.planOnly,
|
|
156
143
|
});
|
|
157
|
-
state.completedPasses = result.completedPasses;
|
|
158
144
|
if (options?.planOnly) {
|
|
159
145
|
console.log(chalk_1.default.green('\n Plan complete — no resources were created.\n'));
|
|
160
146
|
return;
|
|
161
147
|
}
|
|
162
|
-
//
|
|
148
|
+
// Step 3: Set GitHub Actions secrets
|
|
149
|
+
console.log(chalk_1.default.cyan('--- Setting GitHub Actions secrets ---'));
|
|
150
|
+
const roleArn = result.outputs['oidc_role_arn'].value;
|
|
151
|
+
const secrets = {
|
|
152
|
+
AWS_REGION: credentials.AWS_REGION,
|
|
153
|
+
AWS_ROLE_ARN: roleArn,
|
|
154
|
+
PROJECT_NAME: config.name,
|
|
155
|
+
};
|
|
156
|
+
await (0, github_secrets_client_1.setRepoSecrets)(credentials.GITHUB_TOKEN, credentials.GITHUB_ORGANIZATION, repoName, secrets);
|
|
157
|
+
console.log(chalk_1.default.green('✓ GitHub Actions secrets configured'));
|
|
158
|
+
// Step 4: Push code
|
|
163
159
|
const githubRepoUrl = result.outputs['github_http_repo_url'].value;
|
|
164
160
|
await (0, git_client_1.wireGitRemote)(targetRoot, githubRepoUrl);
|
|
165
161
|
console.log(chalk_1.default.green('Git remote origin set'));
|
|
162
|
+
const committed = await (0, git_client_1.commitAll)(targetRoot, 'Add infrastructure configuration');
|
|
163
|
+
if (committed) {
|
|
164
|
+
console.log(chalk_1.default.green('Infrastructure files committed'));
|
|
165
|
+
}
|
|
166
|
+
const allBranches = [...new Set(config.apps.map(app => app.branch))];
|
|
167
|
+
const branches = options?.targetBranch
|
|
168
|
+
? allBranches.filter(b => b === options.targetBranch)
|
|
169
|
+
: allBranches;
|
|
170
|
+
try {
|
|
171
|
+
await (0, git_client_1.pushToRemote)(targetRoot, credentials.GITHUB_TOKEN, branches, options?.forcePush);
|
|
172
|
+
console.log(chalk_1.default.green(`Code pushed to: ${branches.join(', ')}`));
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
console.log(chalk_1.default.yellow(`Warning: automatic push failed. Push manually: git push -u origin ${branches.join(' ')}`));
|
|
176
|
+
}
|
|
177
|
+
// Step 5: Branch protections
|
|
178
|
+
if (!options?.skipBranchProtection) {
|
|
179
|
+
console.log(chalk_1.default.cyan('--- Applying branch protections ---'));
|
|
180
|
+
for (const branch of branches) {
|
|
181
|
+
try {
|
|
182
|
+
await (0, github_client_1.setBranchProtection)(credentials.GITHUB_TOKEN, credentials.GITHUB_ORGANIZATION, repoName, branch);
|
|
183
|
+
console.log(chalk_1.default.green(`✓ Branch protection set for: ${branch}`));
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
console.log(chalk_1.default.yellow(`Warning: could not set branch protection for ${branch}`));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
console.log(chalk_1.default.yellow('--- Skipping branch protections (--skip-branch-protection) ---'));
|
|
192
|
+
}
|
|
166
193
|
// Print output table
|
|
167
194
|
const albDns = result.outputs['alb_dns'].value;
|
|
168
195
|
const ecrUrl = result.outputs['ecr_url'].value;
|
|
169
|
-
printOutputTable(albDns, ecrUrl, githubRepoUrl
|
|
196
|
+
printOutputTable(albDns, ecrUrl, githubRepoUrl);
|
|
170
197
|
}
|
|
171
198
|
catch (err) {
|
|
172
|
-
if (err instanceof tofu_runner_1.
|
|
173
|
-
if (options?.
|
|
199
|
+
if (err instanceof tofu_runner_1.TofuApplyError) {
|
|
200
|
+
if (options?.destroyOnFailure) {
|
|
201
|
+
await runCompensation(infraDir, credentials, tofuVars);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
174
204
|
console.log(chalk_1.default.yellow('\n Partial resources preserved. Fix the issue and re-run:'));
|
|
175
205
|
console.log(chalk_1.default.yellow(' fuel infra:deploy\n'));
|
|
176
|
-
process.exit(1);
|
|
177
206
|
}
|
|
178
|
-
await runCompensation(infraDir, credentials, tofuVars, err.completedPasses);
|
|
179
207
|
process.exit(1);
|
|
180
208
|
}
|
|
181
209
|
throw err;
|