Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,35 @@ GTS_DOCKER_TAGS=1.0.1 1.0

As you can see, the resolved version is not the highest in the repository, but as it is the highest within its minor version the Docker tags include `1.0` as well.

## Configuration

TODO

### Configuration overrides

You can override any configuration value from the command line using the `-c/--config-value` option. This is useful for one-off changes without modifying the config file:

```shell
# Override JSON output indentation
git-that-semver -o json -c output.json.indent=2

# Configure environment variable prefix
git-that-semver -c output.env.prefix=CUSTOM_

# Enable/disable strategies
git-that-semver -c strategies.npm.enabled=true

# Configure default branches (using JSON array syntax)
git-that-semver -c 'defaults.snapshot.defaultBranches=["main","develop"]'

# Disable change request identifier for snapshots
git-that-semver -c defaults.snapshot.useChangeRequestIdentifier=false

# Configure snapshot version template
git-that-semver -c 'defaults.snapshot.versionTpl={{ prefix }}-{{ commitIdentifier }}'
```

## TODOs

- dynamic loading of SCM adapters?
- logging to STDERR colorizes to the output to red by default - this is overridden and works as long as the output is not redirected, but results in red output otherwise - any way to fix this?
- logging to STDERR colorizes to the output to red by default - this is overridden and works as long as the output is not redir
12 changes: 10 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 12 additions & 5 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ const program = new Command("git-that-semver")
.version("0.0.1")
.addOption(
new Option(
"-c, --config-file <configFile>",
"-f, --config-file <config-file>",
"Config file (git-that-semver.yaml)",
)
.env("GTS_CONFIG_FILE")
.default("git-that-semver.yaml"),
)
.addOption(
new Option(
"-c, --config-value <config-values...>",
"Override config values (e.g. output.json.indent=2)",
).default([]),
)
.addOption(
new Option("--log-level <level>", "Log level")
.env("GTS_LOG_LEVEL")
Expand All @@ -29,13 +35,13 @@ const program = new Command("git-that-semver")
)
.addOption(
new Option(
"-e, --enable-strategies <strategies...>",
"-e, --enable-strategy <strategies...>",
"Enable strategies by name",
).default([]),
)
.addOption(
new Option(
"-d, --disable-strategies <strategies...>",
"-d, --disable-strategy <strategies...>",
"Disable strategies by name",
).default([]),
)
Expand All @@ -58,9 +64,10 @@ logger.setLevel(LogLevel[program.opts().logLevel]);
try {
const config = await resolveConfig(
path.resolve(program.opts().configFile),
[...program.opts().enableStrategies],
[...program.opts().disableStrategies],
[...program.opts().enableStrategy],
[...program.opts().disableStrategy],
program.opts().outputFormat,
[...program.opts().configValue],
);

if (program.opts().dumpConfig) {
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@commander-js/extra-typings": "^13.0.0",
"@trivago/prettier-plugin-sort-imports": "^5.2.1",
"@types/bun": "^1.1.18",
"@types/lodash-es": "^4.17.12",
"@types/semver": "^7.5.8",
"@types/slug": "^5.0.9",
"husky": "^9.1.7",
Expand All @@ -19,6 +20,7 @@
"chalk": "^5.4.1",
"commander": "^13.1.0",
"liquidjs": "^10.20.2",
"lodash-es": "^4.17.21",
"merge-anything": "^6.0.2",
"semver": "^7.6.3",
"slug": "^10.0.0",
Expand Down
7 changes: 7 additions & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import YAML from "yaml";

import { logger } from "../logging";
import defaultConfigContents from "./git-that-semver.default.yaml" with { type: "text" };
import { applyConfigOverrides } from "./overrides";
import { Config } from "./types";

const configLogger = logger.childLogger("config");
Expand All @@ -12,6 +13,7 @@ export const resolveConfig = async (
enabledStrategies: string[],
disabledStrategies: string[],
outputFormat: string | undefined,
configOverrides: string[] = [],
): Promise<Config> => {
const defaultConfig = YAML.parse(defaultConfigContents);
configLogger.trace("Default config", defaultConfig);
Expand Down Expand Up @@ -49,6 +51,11 @@ export const resolveConfig = async (
mergedConfig = merge(mergedConfig, { output: { type: outputFormat } });
}

if (configOverrides.length > 0) {
configLogger.trace("Config overrides", configOverrides);
mergedConfig = applyConfigOverrides(mergedConfig, configOverrides);
}

configLogger.trace("Merged config before strategy merge", mergedConfig);

const defaultsWithoutBranchPrefixes = merge({}, mergedConfig.defaults ?? {});
Expand Down
61 changes: 61 additions & 0 deletions src/config/overrides.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { set } from "lodash-es";

import { logger } from "../logging";

const configLogger = logger.childLogger("config");

function parseConfigOverride(override: string): { path: string; value: any } {
if (!/^[^=]+=[^=]*$/.test(override)) {
throw new Error(
`Invalid config value format: ${override}. Expected format: path.to.config=value`,
);
}

const [path, value] = override.split("=", 2);
return { path, value: parseValue(value) };
}

function parseValue(value: string | undefined): any {
if (value === undefined) {
return undefined;
}

// Handle boolean values
if (value.toLowerCase() === "true") return true;
if (value.toLowerCase() === "false") return false;

// Handle array values (JSON array syntax)
if (value.startsWith("[") && value.endsWith("]")) {
try {
return JSON.parse(value);
} catch (e) {
throw new Error(
`Invalid array format: ${value}. Expected JSON array format like [value1,value2]`,
);
}
}

// Handle numeric values
const numValue = Number(value);
if (!isNaN(numValue) && value.trim() !== "") {
return numValue;
}

// Return as string if no other type matches
return value;
}

export function applyConfigOverrides(
config: Record<string, any>,
overrides: string[],
): Record<string, any> {
const result = { ...config };

for (const override of overrides) {
const { path, value } = parseConfigOverride(override);
configLogger.trace("Applying config override", { path, value });
set(result, path, value);
}

return result;
}
5 changes: 5 additions & 0 deletions src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ export const OutputConfig = z.object({
env: z.object({
prefix: z.string().default(""),
}),
json: z
.object({
indent: z.number().optional(),
})
.optional(),
});

export const Config = z.object({
Expand Down
49 changes: 43 additions & 6 deletions src/output/json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,57 @@ describe("JsonOutputPrinter", () => {
consoleSpy.mockRestore();
});

const testPrintResult = (description: string, versionResult: any) => {
it(`should print ${description} as formatted JSON`, () => {
printer.printResult({} as Config, versionResult);
const testPrintResult = (
description: string,
versionResult: any,
indent: number = 2,
) => {
it(`should print ${description} as formatted JSON with indent ${indent}`, () => {
const config = {
output: {
json: {
indent,
},
},
} as Config;

printer.printResult(config, versionResult);

expect(consoleSpy).toHaveBeenCalledTimes(1);
expect(consoleSpy).toHaveBeenCalledWith(
JSON.stringify(versionResult, null, 2),
JSON.stringify(versionResult, null, indent),
);

const printedJson = JSON.parse(consoleSpy.mock.calls[0][0]);
expect(printedJson).toEqual(versionResult);
});
};

testPrintResult("release version result", releaseVersionResult);
testPrintResult("snapshot version result", snapshotVersionResult);
describe("with default indent (2)", () => {
testPrintResult("release version result", releaseVersionResult);
testPrintResult("snapshot version result", snapshotVersionResult);
});

describe("with custom indent", () => {
testPrintResult(
"release version result with no indent",
releaseVersionResult,
0,
);
testPrintResult(
"snapshot version result with no indent",
snapshotVersionResult,
0,
);
testPrintResult(
"release version result with indent 4",
releaseVersionResult,
4,
);
testPrintResult(
"snapshot version result with indent 4",
snapshotVersionResult,
4,
);
});
});
6 changes: 4 additions & 2 deletions src/output/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import type { Config } from "../config/types";
import type { VersionResult } from "../version/versionResolver";

export class JsonOutputPrinter implements OutputPrinter {
printResult(_config: Config, versionResult: VersionResult) {
console.log(JSON.stringify(versionResult, null, 2));
printResult(config: Config, versionResult: VersionResult) {
console.log(
JSON.stringify(versionResult, null, config.output.json?.indent),
);
}
}