Compare commits

..

2 Commits

Author SHA1 Message Date
jkunz 6f0928e7c7 v2.19.0
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-05-13 10:21:25 +00:00
jkunz 26effadcc9 feat(release): delegate docker target to tsdocker 2026-05-13 10:19:56 +00:00
9 changed files with 336 additions and 55 deletions
+2 -1
View File
@@ -63,7 +63,8 @@
}, },
"docker": { "docker": {
"enabled": false, "enabled": false,
"images": [] "engine": "tsdocker",
"patterns": []
} }
} }
} }
+8
View File
@@ -3,6 +3,14 @@
## Pending ## Pending
## 2026-05-13 - 2.19.0
### Features
- Delegate Docker release targets to `tsdocker`.
- Replace Docker image template publishing with `tsdocker push` execution.
- Move Docker registry behavior to `@git.zone/tsdocker` config and validate removed image config as invalid.
## 2026-05-10 - 2.18.1 ## 2026-05-10 - 2.18.1
### Fixes ### Fixes
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "@git.zone/cli", "name": "@git.zone/cli",
"private": false, "private": false,
"version": "2.18.1", "version": "2.19.0",
"description": "A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.", "description": "A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts", "typings": "dist_ts/index.d.ts",
+20 -4
View File
@@ -147,7 +147,7 @@ Targets decide what happens after that:
| --- | --- | | --- | --- |
| `git` | Pushes the release commit and tags, often triggering remote CI release builds | | `git` | Pushes the release commit and tags, often triggering remote CI release builds |
| `npm` | Publishes the package to configured npm registries | | `npm` | Publishes the package to configured npm registries |
| `docker` | Builds and pushes configured Docker images | | `docker` | Delegates container builds and pushes to `tsdocker` |
```bash ```bash
# Preview the resolved release plan # Preview the resolved release plan
@@ -205,7 +205,7 @@ The standard buckets are `Breaking Changes`, `Features`, `Fixes`, `Documentation
## Configuration ## Configuration
All CLI config lives under `@git.zone/cli` in `.smartconfig.json`. CLI workflow config lives under `@git.zone/cli` in `.smartconfig.json`. Docker build and registry behavior lives under `@git.zone/tsdocker` and is used by the Docker release target.
```json ```json
{ {
@@ -237,11 +237,21 @@ All CLI config lives under `@git.zone/cli` in `.smartconfig.json`.
"alreadyPublished": "success" "alreadyPublished": "success"
}, },
"docker": { "docker": {
"enabled": false, "enabled": true,
"images": [] "engine": "tsdocker",
"patterns": [],
"cached": true,
"parallel": true
} }
} }
} }
},
"@git.zone/tsdocker": {
"registries": ["registry.gitlab.com"],
"registryRepoMap": {
"registry.gitlab.com": "myorg/myproject"
},
"platforms": ["linux/amd64", "linux/arm64"]
} }
} }
``` ```
@@ -252,6 +262,12 @@ NPM registries belong only here:
@git.zone/cli.release.targets.npm.registries @git.zone/cli.release.targets.npm.registries
``` ```
Docker registries belong only here and should be registry hosts without `http://` or `https://`:
```text
@git.zone/tsdocker.registries
```
Useful config commands: Useful config commands:
```bash ```bash
+1 -1
View File
@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@git.zone/cli', name: '@git.zone/cli',
version: '2.18.1', version: '2.19.0',
description: 'A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.' description: 'A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.'
} }
+20 -2
View File
@@ -68,8 +68,10 @@ const migrateToV2 = (smartconfigJson: Record<string, any>): boolean => {
migrated = true; migrated = true;
} }
if (isPlainObject(releaseConfig.docker) && !isPlainObject(targets.docker)) { if (isPlainObject(releaseConfig.docker)) {
targets.docker = releaseConfig.docker; targets.docker = isPlainObject(targets.docker)
? { ...releaseConfig.docker, ...targets.docker }
: releaseConfig.docker;
delete releaseConfig.docker; delete releaseConfig.docker;
migrated = true; migrated = true;
} }
@@ -141,11 +143,27 @@ const migrateToV2 = (smartconfigJson: Record<string, any>): boolean => {
if (dockerTarget.enabled === undefined) { if (dockerTarget.enabled === undefined) {
dockerTarget.enabled = true; dockerTarget.enabled = true;
} }
dockerTarget.engine = "tsdocker";
} }
delete releaseConfig.steps; delete releaseConfig.steps;
migrated = true; migrated = true;
} }
if (isPlainObject(targets.docker)) {
if (targets.docker.images) {
delete targets.docker.images;
migrated = true;
}
if (targets.docker.engine !== "tsdocker") {
targets.docker.engine = "tsdocker";
migrated = true;
}
if (!Array.isArray(targets.docker.patterns)) {
targets.docker.patterns = [];
migrated = true;
}
}
if (releaseConfig.changelog) { if (releaseConfig.changelog) {
delete releaseConfig.changelog; delete releaseConfig.changelog;
migrated = true; migrated = true;
+20 -3
View File
@@ -52,7 +52,12 @@ export interface IReleaseNpmTargetConfig {
export interface IReleaseDockerTargetConfig { export interface IReleaseDockerTargetConfig {
enabled?: boolean; enabled?: boolean;
images?: string[]; engine?: "tsdocker";
patterns?: string[];
cached?: boolean;
parallel?: boolean | number;
context?: string;
noBuild?: boolean;
} }
export interface IReleaseWorkflowConfig { export interface IReleaseWorkflowConfig {
@@ -109,7 +114,12 @@ export interface IResolvedReleaseWorkflow {
npmAccessLevel: "public" | "private"; npmAccessLevel: "public" | "private";
npmAlreadyPublished: "success" | "error"; npmAlreadyPublished: "success" | "error";
dockerEnabled: boolean; dockerEnabled: boolean;
dockerImages: string[]; dockerEngine: "tsdocker";
dockerPatterns: string[];
dockerCached: boolean;
dockerParallel: boolean | number;
dockerContext?: string;
dockerNoBuild: boolean;
} }
interface ICliWorkflowConfig { interface ICliWorkflowConfig {
@@ -382,6 +392,13 @@ export const resolveReleaseWorkflow = async (argvArg: any): Promise<IResolvedRel
npmAccessLevel: npmConfig.accessLevel || "public", npmAccessLevel: npmConfig.accessLevel || "public",
npmAlreadyPublished: npmConfig.alreadyPublished || "success", npmAlreadyPublished: npmConfig.alreadyPublished || "success",
dockerEnabled, dockerEnabled,
dockerImages: dockerConfig.images || [], dockerEngine: "tsdocker",
dockerPatterns: Array.isArray(dockerConfig.patterns) ? dockerConfig.patterns : [],
dockerCached: dockerConfig.cached ?? false,
dockerParallel: dockerConfig.parallel ?? false,
dockerContext: typeof dockerConfig.context === "string" && dockerConfig.context.trim()
? dockerConfig.context.trim()
: undefined,
dockerNoBuild: dockerConfig.noBuild ?? false,
}; };
}; };
+218 -20
View File
@@ -793,7 +793,7 @@ async function handleRelease(mode: ICliMode): Promise<void> {
choices: [ choices: [
{ name: "git - push branch and tags", value: "git" }, { name: "git - push branch and tags", value: "git" },
{ name: "npm - publish package registries", value: "npm" }, { name: "npm - publish package registries", value: "npm" },
{ name: "docker - build and push images", value: "docker" }, { name: "docker - build and push through tsdocker", value: "docker" },
], ],
default: getDefaultEnabledTargets(currentTargets), default: getDefaultEnabledTargets(currentTargets),
}); });
@@ -860,21 +860,49 @@ async function handleRelease(mode: ICliMode): Promise<void> {
} }
if (enabledTargets.includes("docker")) { if (enabledTargets.includes("docker")) {
const images = await askValue<string>(interactInstance, { const patterns = await askValue<string>(interactInstance, {
type: "input", type: "input",
name: "dockerImages", name: "dockerPatterns",
message: "Docker image templates (comma-separated, supports {{version}}):", message: "tsdocker Dockerfile patterns (comma-separated, empty means all):",
default: Array.isArray(currentTargets.docker?.images) default: Array.isArray(currentTargets.docker?.patterns)
? currentTargets.docker.images.join(", ") ? currentTargets.docker.patterns.join(", ")
: "", : "",
}); });
const cached = await askValue<boolean>(interactInstance, {
type: "confirm",
name: "dockerCached",
message: "Use tsdocker cached builds?",
default: currentTargets.docker?.cached ?? false,
});
const parallel = await askValue<string>(interactInstance, {
type: "input",
name: "dockerParallel",
message: "tsdocker parallel mode (false, true, or concurrency number):",
default: formatDockerParallel(currentTargets.docker?.parallel ?? false),
});
const context = await askValue<string>(interactInstance, {
type: "input",
name: "dockerContext",
message: "Docker context for tsdocker (empty for default):",
default: currentTargets.docker?.context || "",
});
const noBuild = await askValue<boolean>(interactInstance, {
type: "confirm",
name: "dockerNoBuild",
message: "Skip tsdocker build and only push existing local registry images?",
default: currentTargets.docker?.noBuild ?? false,
});
releaseTargets.docker = { releaseTargets.docker = {
...(currentTargets.docker || {}),
enabled: true, enabled: true,
images: parseCsv(images), engine: "tsdocker",
patterns: parseCsv(patterns),
cached,
parallel: parseDockerParallel(parallel),
context: context.trim() || undefined,
noBuild,
}; };
} else { } else {
releaseTargets.docker = { ...(currentTargets.docker || {}), enabled: false }; releaseTargets.docker = { enabled: false, engine: "tsdocker" };
} }
setCliConfigValueInData(smartconfigData, "schemaVersion", CURRENT_GITZONE_CLI_SCHEMA_VERSION); setCliConfigValueInData(smartconfigData, "schemaVersion", CURRENT_GITZONE_CLI_SCHEMA_VERSION);
@@ -1043,7 +1071,7 @@ async function collectDoctorFindings(): Promise<IDoctorFinding[]> {
await validateDetectedProjectType(cliConfig, findings); await validateDetectedProjectType(cliConfig, findings);
validateCommitConfig(cliConfig.commit || {}, findings); validateCommitConfig(cliConfig.commit || {}, findings);
await validateReleaseConfig(cliConfig.release || {}, findings); await validateReleaseConfig(cliConfig.release || {}, smartconfigData, findings);
return findings; return findings;
} }
@@ -1291,9 +1319,14 @@ function formatTarget(enabled: unknown, targetConfig: any): string {
details.push(`registries=${targetConfig.registries.length}`); details.push(`registries=${targetConfig.registries.length}`);
} }
if (targetConfig.accessLevel) details.push(`access=${targetConfig.accessLevel}`); if (targetConfig.accessLevel) details.push(`access=${targetConfig.accessLevel}`);
if (Array.isArray(targetConfig.images)) { if (targetConfig.engine) details.push(`engine=${targetConfig.engine}`);
details.push(`images=${targetConfig.images.length}`); if (Array.isArray(targetConfig.patterns)) {
details.push(`patterns=${targetConfig.patterns.length}`);
} }
if (targetConfig.cached) details.push("cached=true");
if (targetConfig.parallel) details.push(`parallel=${targetConfig.parallel}`);
if (targetConfig.context) details.push(`context=${targetConfig.context}`);
if (targetConfig.noBuild) details.push("noBuild=true");
return details.length > 0 ? `${state} (${details.join(", ")})` : state; return details.length > 0 ? `${state} (${details.join(", ")})` : state;
} }
@@ -1338,6 +1371,29 @@ function parseCsv(value: string): string[] {
return result; return result;
} }
function formatDockerParallel(value: unknown): string {
if (value === true) return "true";
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
return String(Math.floor(value));
}
return "false";
}
function parseDockerParallel(value: string): boolean | number {
const normalizedValue = value.trim().toLowerCase();
if (!normalizedValue || ["false", "no", "off", "0"].includes(normalizedValue)) {
return false;
}
if (["true", "yes", "on"].includes(normalizedValue)) {
return true;
}
const numericValue = Number(normalizedValue);
if (Number.isFinite(numericValue) && numericValue > 0) {
return Math.floor(numericValue);
}
return false;
}
function normalizeRegistryUrl(url: string): string { function normalizeRegistryUrl(url: string): string {
let normalizedUrl = url.trim(); let normalizedUrl = url.trim();
if (!normalizedUrl.startsWith("http://") && !normalizedUrl.startsWith("https://")) { if (!normalizedUrl.startsWith("http://") && !normalizedUrl.startsWith("https://")) {
@@ -1391,6 +1447,7 @@ function buildConfigFixPrompt(
`- Use schemaVersion ${CURRENT_GITZONE_CLI_SCHEMA_VERSION} for ` + `- Use schemaVersion ${CURRENT_GITZONE_CLI_SCHEMA_VERSION} for ` +
"`@git.zone/cli`.", "`@git.zone/cli`.",
"- Use target-based release config: `release.targets.git`, `release.targets.npm`, and `release.targets.docker`.", "- Use target-based release config: `release.targets.git`, `release.targets.npm`, and `release.targets.docker`.",
"- Docker release targets must use `release.targets.docker.engine = \"tsdocker\"`; Docker registries belong under `@git.zone/tsdocker`.",
"- Keep npm registries only at `@git.zone/cli.release.targets.npm.registries`.", "- Keep npm registries only at `@git.zone/cli.release.targets.npm.registries`.",
"- Do not add runtime legacy compatibility code. If legacy config exists, migrate it explicitly.", "- Do not add runtime legacy compatibility code. If legacy config exists, migrate it explicitly.",
"- Do not commit, release, install dependencies, or modify unrelated files.", "- Do not commit, release, install dependencies, or modify unrelated files.",
@@ -1514,6 +1571,7 @@ function validateCommitConfig(
async function validateReleaseConfig( async function validateReleaseConfig(
releaseConfig: Record<string, any>, releaseConfig: Record<string, any>,
smartconfigData: Record<string, any>,
findings: IDoctorFinding[], findings: IDoctorFinding[],
): Promise<void> { ): Promise<void> {
const confirmation = releaseConfig.confirmation; const confirmation = releaseConfig.confirmation;
@@ -1554,7 +1612,7 @@ async function validateReleaseConfig(
const targets = releaseConfig.targets || {}; const targets = releaseConfig.targets || {};
await validateGitTarget(targets.git || {}, findings); await validateGitTarget(targets.git || {}, findings);
await validateNpmTarget(targets.npm || {}, findings); await validateNpmTarget(targets.npm || {}, findings);
validateDockerTarget(targets.docker || {}, findings); await validateDockerTarget(targets.docker || {}, smartconfigData, findings);
} }
async function validateGitTarget( async function validateGitTarget(
@@ -1718,31 +1776,171 @@ async function validateNpmAuth(
} }
} }
function validateDockerTarget( async function validateDockerTarget(
dockerTarget: Record<string, any>, dockerTarget: Record<string, any>,
smartconfigData: Record<string, any>,
findings: IDoctorFinding[], findings: IDoctorFinding[],
): void { ): Promise<void> {
if ("images" in dockerTarget) {
findings.push({
level: "error",
message: "Docker release target still uses removed images config",
fix: "Remove release.targets.docker.images and configure @git.zone/tsdocker instead.",
});
}
const enabled = dockerTarget.enabled ?? false; const enabled = dockerTarget.enabled ?? false;
if (!enabled) { if (!enabled) {
findings.push({ level: "ok", message: "Docker release target is disabled" }); findings.push({ level: "ok", message: "Docker release target is disabled" });
return; return;
} }
if (!Array.isArray(dockerTarget.images) || dockerTarget.images.length === 0) { if (dockerTarget.engine !== "tsdocker") {
findings.push({ findings.push({
level: "error", level: "error",
message: "Docker release target is enabled without images", message: "Docker release target must use tsdocker",
fix: "Set release.targets.docker.images or disable release.targets.docker.enabled.", fix: "Set release.targets.docker.engine to tsdocker.",
}); });
return; }
if (dockerTarget.patterns !== undefined && !Array.isArray(dockerTarget.patterns)) {
findings.push({
level: "error",
message: "Docker release target patterns must be an array",
fix: "Set release.targets.docker.patterns to an array of Dockerfile patterns or remove it.",
});
}
if (!isValidDockerParallel(dockerTarget.parallel)) {
findings.push({
level: "error",
message: `Invalid tsdocker parallel setting: ${formatValue(dockerTarget.parallel)}`,
fix: "Use false, true, or a positive concurrency number.",
});
}
const tsdockerConfig = smartconfigData["@git.zone/tsdocker"];
if (!isPlainObject(tsdockerConfig)) {
findings.push({
level: "error",
message: "Docker release target is enabled but @git.zone/tsdocker config is missing",
fix: "Add @git.zone/tsdocker.registries and optional registryRepoMap/platforms config.",
});
} else {
validateTsdockerProjectConfig(tsdockerConfig, findings);
}
await validateTsdockerCommand(findings);
findings.push({
level: "ok",
message: `Docker release target uses tsdocker (${formatDockerPatterns(dockerTarget.patterns)})`,
});
}
function formatDockerPatterns(patterns: unknown): string {
return Array.isArray(patterns) && patterns.length > 0
? patterns.map((pattern) => String(pattern)).join(", ")
: "all Dockerfiles";
}
function isPlainObject(value: unknown): value is Record<string, any> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function isValidDockerParallel(value: unknown): boolean {
return value === undefined ||
value === false ||
value === true ||
(typeof value === "number" && Number.isFinite(value) && value > 0);
}
function validateTsdockerProjectConfig(
tsdockerConfig: Record<string, any>,
findings: IDoctorFinding[],
): void {
const registries = Array.isArray(tsdockerConfig.registries)
? tsdockerConfig.registries
: [];
if (registries.length === 0) {
findings.push({
level: "error",
message: "@git.zone/tsdocker.registries is empty",
fix: "Set @git.zone/tsdocker.registries to registry hosts such as registry.gitlab.com.",
});
}
for (const registry of registries) {
if (typeof registry !== "string" || !registry.trim()) {
findings.push({
level: "error",
message: `Invalid tsdocker registry: ${formatValue(registry)}`,
fix: "Use registry hosts such as registry.gitlab.com.",
});
continue;
}
if (registry.startsWith("http://") || registry.startsWith("https://")) {
findings.push({
level: "error",
message: `tsdocker registry must not include a protocol: ${registry}`,
fix: `Use ${registry.replace(/^https?:\/\//, "")}`,
});
}
}
const registryRepoMap = tsdockerConfig.registryRepoMap;
if (registryRepoMap !== undefined && !isPlainObject(registryRepoMap)) {
findings.push({
level: "error",
message: "@git.zone/tsdocker.registryRepoMap must be an object",
});
} else if (isPlainObject(registryRepoMap)) {
for (const registry of Object.keys(registryRepoMap)) {
if (registry.startsWith("http://") || registry.startsWith("https://")) {
findings.push({
level: "error",
message: `tsdocker registryRepoMap key must not include a protocol: ${registry}`,
fix: `Use ${registry.replace(/^https?:\/\//, "")}`,
});
}
}
} }
findings.push({ findings.push({
level: "ok", level: "ok",
message: `Docker release target has ${dockerTarget.images.length} image template(s)`, message: `@git.zone/tsdocker has ${registries.length} registries`,
}); });
} }
async function validateTsdockerCommand(findings: IDoctorFinding[]): Promise<void> {
const smartshellInstance = new plugins.smartshell.Smartshell({
executor: "bash",
sourceFilePaths: [],
});
try {
const result = await smartshellInstance.execSpawn(
"tsdocker",
["--version"],
{ silent: true, timeout: 8000 },
);
if (result.exitCode === 0) {
findings.push({ level: "ok", message: "tsdocker command is available" });
} else {
findings.push({
level: "error",
message: "tsdocker command is not available",
fix: "Install @git.zone/tsdocker globally or make it available on PATH.",
});
}
} catch (error) {
findings.push({
level: "error",
message: "Could not execute tsdocker",
fix: error instanceof Error ? error.message : String(error),
});
}
}
async function validateDetectedProjectType( async function validateDetectedProjectType(
cliConfig: Record<string, any>, cliConfig: Record<string, any>,
findings: IDoctorFinding[], findings: IDoctorFinding[],
+46 -23
View File
@@ -107,7 +107,7 @@ export const run = async (argvArg: any) => {
npmResults.push(...(await runNpmTarget(smartshellInstance, workflow))); npmResults.push(...(await runNpmTarget(smartshellInstance, workflow)));
} }
if (workflow.targets.includes("docker")) { if (workflow.targets.includes("docker")) {
dockerResults.push(...(await runDockerTarget(smartshellInstance, workflow, newVersion))); dockerResults.push(...(await runDockerTarget(smartshellInstance, workflow)));
} }
printReleaseSummary(newVersion, gitResults, npmResults, dockerResults); printReleaseSummary(newVersion, gitResults, npmResults, dockerResults);
@@ -262,31 +262,43 @@ async function runNpmTarget(
async function runDockerTarget( async function runDockerTarget(
smartshellInstance: plugins.smartshell.Smartshell, smartshellInstance: plugins.smartshell.Smartshell,
workflow: IResolvedReleaseWorkflow, workflow: IResolvedReleaseWorkflow,
newVersion: string,
): Promise<ITargetResult[]> { ): Promise<ITargetResult[]> {
if (!workflow.dockerEnabled) { if (!workflow.dockerEnabled) {
return [{ target: "docker", status: "skipped", message: "disabled" }]; return [{ target: "docker", status: "skipped", message: "disabled" }];
} }
if (workflow.dockerImages.length === 0) {
return [{ target: "docker", status: "failed", message: "no images configured" }];
}
const results: ITargetResult[] = []; const command = buildTsdockerPushCommand(workflow);
for (const imageTemplate of workflow.dockerImages) { const result = await smartshellInstance.exec(command);
const image = imageTemplate.replaceAll("{{version}}", newVersion); const output = `${result.stdout || ""}\n${(result as any).stderr || ""}\n${(result as any).combinedOutput || ""}`;
const buildResult = await smartshellInstance.exec(`docker build -t ${shellQuote(image)} .`); return [{
if (buildResult.exitCode !== 0) { target: workflow.dockerPatterns.length > 0
results.push({ target: image, status: "failed", message: "docker build failed" }); ? `tsdocker:${workflow.dockerPatterns.join(",")}`
continue; : "tsdocker",
} status: result.exitCode === 0 ? "success" : "failed",
const pushResult = await smartshellInstance.exec(`docker push ${shellQuote(image)}`); message: result.exitCode === 0 ? undefined : firstMeaningfulLine(output),
results.push({ }];
target: image, }
status: pushResult.exitCode === 0 ? "success" : "failed",
message: pushResult.exitCode === 0 ? undefined : "docker push failed", function buildTsdockerPushCommand(workflow: IResolvedReleaseWorkflow): string {
}); const commandParts = ["tsdocker", "push"];
if (workflow.dockerNoBuild) {
commandParts.push("--no-build");
} }
return results; if (workflow.dockerCached) {
commandParts.push("--cached");
}
if (workflow.dockerParallel === true) {
commandParts.push("--parallel");
} else if (typeof workflow.dockerParallel === "number" && Number.isFinite(workflow.dockerParallel) && workflow.dockerParallel > 0) {
commandParts.push(`--parallel=${Math.floor(workflow.dockerParallel)}`);
}
if (workflow.dockerContext) {
commandParts.push(`--context=${shellQuote(workflow.dockerContext)}`);
}
for (const pattern of workflow.dockerPatterns) {
commandParts.push(shellQuote(pattern));
}
return commandParts.join(" ");
} }
function isAlreadyPublishedOutput(output: string): boolean { function isAlreadyPublishedOutput(output: string): boolean {
@@ -315,11 +327,22 @@ function printReleasePlan(workflow: IResolvedReleaseWorkflow): void {
console.log(`npm registries: ${workflow.npmRegistries.length > 0 ? workflow.npmRegistries.join(", ") : "none"}`); console.log(`npm registries: ${workflow.npmRegistries.length > 0 ? workflow.npmRegistries.join(", ") : "none"}`);
} }
if (workflow.targets.includes("docker")) { if (workflow.targets.includes("docker")) {
console.log(`docker images: ${workflow.dockerImages.length > 0 ? workflow.dockerImages.join(", ") : "none"}`); console.log(`docker engine: ${workflow.dockerEngine}`);
console.log(`docker patterns: ${workflow.dockerPatterns.length > 0 ? workflow.dockerPatterns.join(", ") : "all Dockerfiles"}`);
console.log(`docker options: ${formatDockerOptions(workflow)}`);
} }
console.log(""); console.log("");
} }
function formatDockerOptions(workflow: IResolvedReleaseWorkflow): string {
const options: string[] = [];
if (workflow.dockerCached) options.push("cached");
if (workflow.dockerParallel) options.push(`parallel=${workflow.dockerParallel === true ? "true" : workflow.dockerParallel}`);
if (workflow.dockerNoBuild) options.push("no-build");
if (workflow.dockerContext) options.push(`context=${workflow.dockerContext}`);
return options.length > 0 ? options.join(", ") : "default";
}
function printReleaseSummary( function printReleaseSummary(
newVersion: string, newVersion: string,
gitResults: ITargetResult[], gitResults: ITargetResult[],
@@ -365,7 +388,7 @@ export function showHelp(mode?: ICliMode): void {
{ flag: "-p, --push", description: "Enable the git release target" }, { flag: "-p, --push", description: "Enable the git release target" },
{ flag: "--target <names>", description: "Release only selected targets: git,npm,docker" }, { flag: "--target <names>", description: "Release only selected targets: git,npm,docker" },
{ flag: "--npm", description: "Enable the npm release target" }, { flag: "--npm", description: "Enable the npm release target" },
{ flag: "--docker", description: "Enable the Docker release target" }, { flag: "--docker", description: "Enable the tsdocker release target" },
{ flag: "--no-publish", description: "Run release core and git target only" }, { flag: "--no-publish", description: "Run release core and git target only" },
{ flag: "--plan", description: "Show resolved workflow without mutating files" }, { flag: "--plan", description: "Show resolved workflow without mutating files" },
], ],
@@ -385,7 +408,7 @@ export function showHelp(mode?: ICliMode): void {
console.log(" -p, --push Enable the git release target"); console.log(" -p, --push Enable the git release target");
console.log(" --target <names> Release only selected targets: git,npm,docker"); console.log(" --target <names> Release only selected targets: git,npm,docker");
console.log(" --npm Enable the npm release target"); console.log(" --npm Enable the npm release target");
console.log(" --docker Enable the Docker release target"); console.log(" --docker Enable the tsdocker release target");
console.log(" --no-publish Run release core and git target only"); console.log(" --no-publish Run release core and git target only");
console.log(" --major|--minor|--patch Override inferred semver level"); console.log(" --major|--minor|--patch Override inferred semver level");
console.log(" --plan Show resolved workflow without mutating files"); console.log(" --plan Show resolved workflow without mutating files");