thinkwork-cli 0.12.10 → 0.12.12
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/cli.js +147 -22
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1693,8 +1693,12 @@ async function resolveEnterpriseDeployRequest(options, deps = {}) {
|
|
|
1693
1693
|
const repoContext = findEnterpriseDeploymentRepo(cwd);
|
|
1694
1694
|
const loadDeployment = deps.loadDeployment ?? loadEnterpriseDeployment;
|
|
1695
1695
|
const stdinIsTty = deps.stdinIsTty ?? Boolean(process.stdin.isTTY);
|
|
1696
|
-
const component = options.component ?? "all";
|
|
1697
1696
|
const inferRepository = deps.inferRepository ?? inferGitHubRepositoryFromRemote;
|
|
1697
|
+
const component = await resolveEnterpriseDeployComponent(
|
|
1698
|
+
options,
|
|
1699
|
+
stdinIsTty,
|
|
1700
|
+
deps
|
|
1701
|
+
);
|
|
1698
1702
|
const componentCheck = validateEnterpriseDeployComponent(component);
|
|
1699
1703
|
if (!componentCheck.valid) {
|
|
1700
1704
|
throw new Error(componentCheck.error);
|
|
@@ -1706,13 +1710,13 @@ async function resolveEnterpriseDeployRequest(options, deps = {}) {
|
|
|
1706
1710
|
"Customer slug is required for enterprise deploy. Pass --customer <slug>."
|
|
1707
1711
|
);
|
|
1708
1712
|
const registry = loadDeployment(customerSlug);
|
|
1709
|
-
const
|
|
1710
|
-
|
|
1713
|
+
const stageDefault = registry?.defaultStage ?? repoContext?.stages[0] ?? "dev";
|
|
1714
|
+
const stage = options.stage ?? (options.bootstrap ? await resolveEnterpriseBootstrapStage(
|
|
1715
|
+
registry,
|
|
1716
|
+
repoContext,
|
|
1711
1717
|
stdinIsTty,
|
|
1712
|
-
deps
|
|
1713
|
-
|
|
1714
|
-
"dev"
|
|
1715
|
-
) : "dev");
|
|
1718
|
+
deps
|
|
1719
|
+
) : await resolveEnterpriseDispatchStage(stageDefault, stdinIsTty, deps));
|
|
1716
1720
|
const stageCheck = validateStage(stage);
|
|
1717
1721
|
if (!stageCheck.valid) {
|
|
1718
1722
|
throw new Error(stageCheck.error);
|
|
@@ -1725,12 +1729,19 @@ async function resolveEnterpriseDeployRequest(options, deps = {}) {
|
|
|
1725
1729
|
`${customerSlug}/${customerSlug}-thinkwork-deploy`
|
|
1726
1730
|
);
|
|
1727
1731
|
const checkoutDir = options.checkoutDir ?? registry?.checkoutDir ?? registry?.targetDir ?? repoContext?.repoRoot ?? join5(resolve3(cwd), `${customerSlug}-thinkwork-deploy`);
|
|
1732
|
+
const runSmokes = await resolveEnterpriseRunSmokes(
|
|
1733
|
+
options,
|
|
1734
|
+
component,
|
|
1735
|
+
stdinIsTty,
|
|
1736
|
+
deps
|
|
1737
|
+
);
|
|
1728
1738
|
return {
|
|
1729
1739
|
customerSlug,
|
|
1730
1740
|
repository,
|
|
1731
1741
|
checkoutDir: resolve3(checkoutDir),
|
|
1732
1742
|
stage,
|
|
1733
1743
|
component,
|
|
1744
|
+
runSmokes,
|
|
1734
1745
|
bootstrap: options.bootstrap === true,
|
|
1735
1746
|
wait: options.wait !== false,
|
|
1736
1747
|
createRepo: options.createRepo === true,
|
|
@@ -1835,6 +1846,78 @@ async function runEnterpriseDeploy(options, deps = {}) {
|
|
|
1835
1846
|
workflow
|
|
1836
1847
|
};
|
|
1837
1848
|
}
|
|
1849
|
+
async function resolveEnterpriseBootstrapStage(registry, repoContext, stdinIsTty, deps) {
|
|
1850
|
+
return registry?.defaultStage ?? repoContext?.stages[0] ?? await promptWhenInteractive(
|
|
1851
|
+
"Deployment stage:",
|
|
1852
|
+
stdinIsTty,
|
|
1853
|
+
deps.promptInput,
|
|
1854
|
+
"Deployment stage is required for enterprise bootstrap. Pass --stage <name>.",
|
|
1855
|
+
"dev"
|
|
1856
|
+
);
|
|
1857
|
+
}
|
|
1858
|
+
async function resolveEnterpriseDispatchStage(defaultValue, stdinIsTty, deps) {
|
|
1859
|
+
if (!stdinIsTty) return defaultValue;
|
|
1860
|
+
return promptWhenInteractive(
|
|
1861
|
+
"Deployment stage:",
|
|
1862
|
+
stdinIsTty,
|
|
1863
|
+
deps.promptInput,
|
|
1864
|
+
"Deployment stage is required for enterprise deploy. Pass --stage <name>.",
|
|
1865
|
+
defaultValue
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1868
|
+
async function resolveEnterpriseDeployComponent(options, stdinIsTty, deps) {
|
|
1869
|
+
const defaultValue = options.component ?? "all";
|
|
1870
|
+
if (options.bootstrap || !stdinIsTty) return defaultValue;
|
|
1871
|
+
if (!ENTERPRISE_DEPLOY_COMPONENTS.includes(
|
|
1872
|
+
defaultValue
|
|
1873
|
+
)) {
|
|
1874
|
+
return defaultValue;
|
|
1875
|
+
}
|
|
1876
|
+
return promptSelectWhenInteractive(
|
|
1877
|
+
{
|
|
1878
|
+
message: "Deployment component:",
|
|
1879
|
+
choices: [
|
|
1880
|
+
{
|
|
1881
|
+
name: "all",
|
|
1882
|
+
value: "all",
|
|
1883
|
+
description: "Deploy release artifacts, Terraform, runtimes, overlays, and smokes"
|
|
1884
|
+
},
|
|
1885
|
+
{
|
|
1886
|
+
name: "foundation",
|
|
1887
|
+
value: "foundation",
|
|
1888
|
+
description: "Terraform apply only"
|
|
1889
|
+
},
|
|
1890
|
+
{
|
|
1891
|
+
name: "artifacts",
|
|
1892
|
+
value: "artifacts",
|
|
1893
|
+
description: "Release artifacts, runtime images, and static bundles"
|
|
1894
|
+
},
|
|
1895
|
+
{
|
|
1896
|
+
name: "overlays",
|
|
1897
|
+
value: "overlays",
|
|
1898
|
+
description: "Customer overlays only"
|
|
1899
|
+
},
|
|
1900
|
+
{
|
|
1901
|
+
name: "smokes",
|
|
1902
|
+
value: "smokes",
|
|
1903
|
+
description: "Smoke checks against an existing stage"
|
|
1904
|
+
}
|
|
1905
|
+
],
|
|
1906
|
+
defaultValue
|
|
1907
|
+
},
|
|
1908
|
+
deps.promptSelect
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
async function resolveEnterpriseRunSmokes(options, component, stdinIsTty, deps) {
|
|
1912
|
+
if (options.runSmokes !== void 0) return options.runSmokes;
|
|
1913
|
+
if (options.bootstrap || !stdinIsTty) return true;
|
|
1914
|
+
if (component !== "all" && component !== "smokes") return true;
|
|
1915
|
+
return promptConfirmWhenInteractive(
|
|
1916
|
+
"Run smoke checks after deploy?",
|
|
1917
|
+
true,
|
|
1918
|
+
deps.promptConfirm
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1838
1921
|
async function promptWhenInteractive(message, stdinIsTty, promptInput, nonInteractiveError, defaultValue) {
|
|
1839
1922
|
if (!stdinIsTty) throw new Error(nonInteractiveError);
|
|
1840
1923
|
if (promptInput) {
|
|
@@ -1845,6 +1928,20 @@ async function promptWhenInteractive(message, stdinIsTty, promptInput, nonIntera
|
|
|
1845
1928
|
const value = await input21({ message, default: defaultValue });
|
|
1846
1929
|
return value.trim() || defaultValue || value;
|
|
1847
1930
|
}
|
|
1931
|
+
async function promptSelectWhenInteractive(options, promptSelect) {
|
|
1932
|
+
if (promptSelect) return promptSelect(options);
|
|
1933
|
+
const { select: select10 } = await import("@inquirer/prompts");
|
|
1934
|
+
return select10({
|
|
1935
|
+
message: options.message,
|
|
1936
|
+
choices: options.choices,
|
|
1937
|
+
default: options.defaultValue
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
async function promptConfirmWhenInteractive(message, defaultValue, promptConfirm) {
|
|
1941
|
+
if (promptConfirm) return promptConfirm(message);
|
|
1942
|
+
const { confirm: confirmPrompt } = await import("@inquirer/prompts");
|
|
1943
|
+
return confirmPrompt({ message, default: defaultValue });
|
|
1944
|
+
}
|
|
1848
1945
|
function enterpriseBootstrapStages(requestedStage) {
|
|
1849
1946
|
return [.../* @__PURE__ */ new Set([requestedStage, "prod"])];
|
|
1850
1947
|
}
|
|
@@ -1879,7 +1976,7 @@ async function dispatchEnterpriseWorkflow(request, options, deps, bootstrap) {
|
|
|
1879
1976
|
repository: request.repository,
|
|
1880
1977
|
stage: request.stage,
|
|
1881
1978
|
component: request.component,
|
|
1882
|
-
runSmokes:
|
|
1979
|
+
runSmokes: request.runSmokes,
|
|
1883
1980
|
wait: request.wait,
|
|
1884
1981
|
region: bootstrap?.plan.region ?? request.registry?.region
|
|
1885
1982
|
},
|
|
@@ -1985,28 +2082,26 @@ async function runDeployCommand(opts, deps = {}) {
|
|
|
1985
2082
|
}
|
|
1986
2083
|
async function runLocalTerraformDeploy(opts) {
|
|
1987
2084
|
const startTime = Date.now();
|
|
1988
|
-
const
|
|
2085
|
+
const initialStage = await resolveStage({ flag: opts.stage });
|
|
1989
2086
|
const compCheck = validateComponent(opts.component);
|
|
1990
2087
|
if (!compCheck.valid) {
|
|
1991
2088
|
printError(compCheck.error);
|
|
1992
2089
|
process.exit(1);
|
|
1993
2090
|
}
|
|
1994
2091
|
const identity = getAwsIdentity();
|
|
1995
|
-
printHeader("deploy",
|
|
2092
|
+
printHeader("deploy", initialStage, identity);
|
|
1996
2093
|
if (!identity) {
|
|
1997
2094
|
printWarning("Could not resolve AWS identity. Is the AWS CLI configured?");
|
|
1998
2095
|
}
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
console.log(" Aborted.");
|
|
2009
|
-
process.exit(0);
|
|
2096
|
+
const stage = await confirmLocalDeployStage(initialStage, opts);
|
|
2097
|
+
if (!stage) {
|
|
2098
|
+
console.log(" Aborted.");
|
|
2099
|
+
process.exit(0);
|
|
2100
|
+
}
|
|
2101
|
+
if (stage !== initialStage) {
|
|
2102
|
+
printHeader("deploy", stage, identity);
|
|
2103
|
+
if (!identity) {
|
|
2104
|
+
printWarning("Could not resolve AWS identity. Is the AWS CLI configured?");
|
|
2010
2105
|
}
|
|
2011
2106
|
}
|
|
2012
2107
|
const terraformDir = process.env.THINKWORK_TERRAFORM_DIR || process.cwd();
|
|
@@ -2031,6 +2126,32 @@ async function runLocalTerraformDeploy(opts) {
|
|
|
2031
2126
|
await runPostDeployProbe(stage);
|
|
2032
2127
|
printSummary("deploy", stage, tiers, startTime);
|
|
2033
2128
|
}
|
|
2129
|
+
async function confirmLocalDeployStage(initialStage, opts, deps = {}) {
|
|
2130
|
+
if (opts.yes) return initialStage;
|
|
2131
|
+
let stage = initialStage;
|
|
2132
|
+
while (true) {
|
|
2133
|
+
const ok = await (deps.confirm ?? confirm)(deployConfirmMessage(stage));
|
|
2134
|
+
if (ok) return stage;
|
|
2135
|
+
const canPromptForAnotherStage = deps.stdoutIsTty ?? Boolean(process.stdout.isTTY);
|
|
2136
|
+
if (!canPromptForAnotherStage) return null;
|
|
2137
|
+
const nextStage = (await promptAlternativeDeployStage(stage, deps.promptInput)).trim();
|
|
2138
|
+
if (!nextStage) return null;
|
|
2139
|
+
const check = validateStage(nextStage);
|
|
2140
|
+
if (!check.valid) {
|
|
2141
|
+
throw new Error(check.error);
|
|
2142
|
+
}
|
|
2143
|
+
stage = nextStage;
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
function deployConfirmMessage(stage) {
|
|
2147
|
+
return isProdLike(stage) ? ` Stage "${stage}" is production-like. Deploy?` : ` Deploy to stage "${stage}"?`;
|
|
2148
|
+
}
|
|
2149
|
+
async function promptAlternativeDeployStage(currentStage, promptInput) {
|
|
2150
|
+
const message = `Deployment stage to deploy instead of "${currentStage}" (blank to abort):`;
|
|
2151
|
+
if (promptInput) return promptInput(message);
|
|
2152
|
+
const { input: input21 } = await import("@inquirer/prompts");
|
|
2153
|
+
return input21({ message });
|
|
2154
|
+
}
|
|
2034
2155
|
function printEnterpriseDeploySummary(result) {
|
|
2035
2156
|
const workflow = result.workflow;
|
|
2036
2157
|
if (result.kind === "bootstrap") {
|
|
@@ -2159,7 +2280,11 @@ async function runEnterpriseDestroy(options, deps = {}) {
|
|
|
2159
2280
|
"Refusing to destroy an enterprise stage without --yes in a non-interactive session."
|
|
2160
2281
|
);
|
|
2161
2282
|
}
|
|
2162
|
-
const
|
|
2283
|
+
const impact = ` This will dispatch operation=destroy to ${request.repository} and permanently remove the "${request.stage}" stage stack for "${request.customerSlug}". Customer-wide bootstrap resources such as the deployment repository, Terraform state bucket, artifact bucket, and OIDC trust are preserved.`;
|
|
2284
|
+
const message = isProdLike(request.stage) ? ` Stage "${request.stage}" is production-like.
|
|
2285
|
+
${impact}
|
|
2286
|
+
Continue?` : `${impact}
|
|
2287
|
+
Continue?`;
|
|
2163
2288
|
const ok = await (deps.promptConfirm ?? confirm)(message);
|
|
2164
2289
|
if (!ok) {
|
|
2165
2290
|
throw new Error("Enterprise destroy aborted.");
|