From 795822060751a659664ccc55797fc437f7454db1 Mon Sep 17 00:00:00 2001 From: Calvin Wilkinson Date: Thu, 20 Jun 2024 10:45:36 +0100 Subject: [PATCH 1/5] Start work for issue #21 From 22b2beb43e487cc520455635627b4ee784333ae3 Mon Sep 17 00:00:00 2001 From: Calvin Wilkinson Date: Thu, 20 Jun 2024 11:45:25 +0100 Subject: [PATCH 2/5] enhance: add ability to use a json key path to the json version value --- prepare-release-settings.json | 3 +- src/csharp-version-updater.ts | 8 +++-- src/json-version-updater.ts | 62 +++++++++++++++++++++++++-------- src/prepare-release-settings.ts | 5 +++ src/release-prepper.ts | 12 ++++--- 5 files changed, 66 insertions(+), 24 deletions(-) diff --git a/prepare-release-settings.json b/prepare-release-settings.json index a777ee1..6e9e028 100644 --- a/prepare-release-settings.json +++ b/prepare-release-settings.json @@ -27,5 +27,6 @@ "prTitle": "🚀Production Release - ${VERSION}" }], "githubTokenEnvVarName": "CICD_TOKEN", - "versionFilePath": "./deno.json" + "versionFilePath": "./deno.json", + "versionJSONKeyPath": "version" } diff --git a/src/csharp-version-updater.ts b/src/csharp-version-updater.ts index bfd8f84..96de6de 100644 --- a/src/csharp-version-updater.ts +++ b/src/csharp-version-updater.ts @@ -1,5 +1,6 @@ import { ParamGuards } from "./core/param-guards.ts"; import { existsSync } from "../deps.ts"; +import { PrepareReleaseSettings } from "./prepare-release-settings.ts"; /** * Updates versions in csharp project files. @@ -7,17 +8,18 @@ import { existsSync } from "../deps.ts"; export class CSharpVersionUpdater { /** * Updates the version in the csharp project file at the given {@link versionFilePath}. - * @param versionFilePath The full or relative file path to the version file. + * @param settings The prepare release settings. * @param newVersion The new version. * @throws {Error} Thrown for the following reasons: * 1. If the csproj file does not exist. * 2. If the csproj file does not contain a version XML element. * 3. If the csproj file does not contain a file version XML element. */ - public updateVersion(versionFilePath: string, newVersion: string) { - ParamGuards.isNothing(versionFilePath, "versionFilePath null, empty, or undefined."); + public updateVersion(settings: PrepareReleaseSettings, newVersion: string) { ParamGuards.isNothing(newVersion, "newVersion null, empty, or undefined."); + const versionFilePath = settings.versionFilePath ?? ""; + if (!existsSync(versionFilePath, { isFile: true })) { throw new Error(`The version file path '${versionFilePath}' does not exist.`); } diff --git a/src/json-version-updater.ts b/src/json-version-updater.ts index 05a269a..0bfca6d 100644 --- a/src/json-version-updater.ts +++ b/src/json-version-updater.ts @@ -1,5 +1,6 @@ import { existsSync } from "../deps.ts"; import { ParamGuards } from "./core/param-guards.ts"; +import { PrepareReleaseSettings } from "./prepare-release-settings.ts"; /** * Updates the version in a JSON file. @@ -7,39 +8,70 @@ import { ParamGuards } from "./core/param-guards.ts"; export class JsonVersionUpdater { /** * Updates the version in the JSON version file at the given {@link versionFilePath}. - * @param versionFilePath The full or relative file path to the version file. + * @param settings The prepare release settings. * @param newVersion The new version. * @throws {Error} Thrown if the version file does not exist or the version file does not contain a 'version' property. */ - public updateVersion(versionFilePath: string, newVersion: string) { - ParamGuards.isNothing(versionFilePath, "versionFilePath null, empty, or undefined."); + public updateVersion(settings: PrepareReleaseSettings, newVersion: string) { ParamGuards.isNothing(newVersion, "newVersion null, empty, or undefined."); + const versionFilePath = settings.versionFilePath ?? ""; + if (!existsSync(versionFilePath, { isFile: true })) { throw new Error(`The version file path does not exist: ${versionFilePath}`); } const versionFileContent = Deno.readTextFileSync(versionFilePath); + const versionConfig = JSON.parse(versionFileContent); - const versionConfig: { version: string } = JSON.parse(versionFileContent); + const propChain = settings.versionJSONKeyPath?.split(".").map((i) => i.trim()) ?? ["version"]; - if (!this.containsVersionProp(versionConfig)) { - throw new Error(`The version file does not contain a version property: ${versionFilePath}`); - } + const result = this.setPropertyValue(versionConfig, propChain, newVersion); - versionConfig.version = newVersion; + if (result[0] === false) { + console.log(`%c${result[1]}`, "color: red;"); + Deno.exit(1); + } Deno.writeTextFileSync(versionFilePath, `${JSON.stringify(versionConfig, null, 4)}\n`); } /** - * Validates that the given {@link obj} contains a version property. - * @param obj The object to validate. - * @returns True if the object contains a version property; otherwise, false. + * Sets a property in the given {@link propChain} to the given {@link newValue}. + * @param obj The object that might contains the property to update. + * @param propChain The property chain path to the property to update. + * @param newValue The new value to set the last prop in the chain to. + * @returns A tuple where the first item is a boolean indicating if the property was set and the second item is an error message if the property was not set. */ - private containsVersionProp(obj: unknown): obj is { version: string } { - return obj !== null && - obj !== undefined && - typeof obj === "object" && "version" in obj; + private setPropertyValue(obj: any, propChain: string[], newValue: string | number): [boolean, string | undefined] { + if (propChain.length === 0) { + return [false, ""]; + } + + let currentObj = obj; + + for (let i = 0; i < propChain.length - 1; i++) { + const propertyName = propChain[i]; + + if (!(propertyName in currentObj)) { + return [false, `The property '${propertyName}' does not exist.`]; + } + + if (currentObj !== null && typeof currentObj === 'object') { + currentObj = currentObj[propertyName]; + } else { + return [false, "Cannot set a value on a non-object"]; + } + } + + const lastPropName = propChain[propChain.length - 1]; + + if (!(lastPropName in currentObj)) { + return [false, `The property '${lastPropName}' does not exist.`]; + } + + currentObj[lastPropName] = newValue; + + return [true, undefined]; } } diff --git a/src/prepare-release-settings.ts b/src/prepare-release-settings.ts index 06bcc48..646ef4a 100644 --- a/src/prepare-release-settings.ts +++ b/src/prepare-release-settings.ts @@ -34,4 +34,9 @@ export interface PrepareReleaseSettings { * @remarks If undefined, null, or empty, then the version will not be updated. */ versionFilePath?: string; + + /** + * Gets the dot separated path to the JSON key that contains the version. + */ + versionJSONKeyPath?: string; } diff --git a/src/release-prepper.ts b/src/release-prepper.ts index ad185c1..968c8d8 100644 --- a/src/release-prepper.ts +++ b/src/release-prepper.ts @@ -139,7 +139,7 @@ export class ReleasePrepper { if (!Guards.isNothing(settings.versionFilePath)) { try { - this.updateVersion(settings.versionFilePath, chosenVersion); + this.updateVersion(settings, chosenVersion); } catch (error) { console.log(crayon.lightRed(error.message)); } @@ -208,22 +208,24 @@ export class ReleasePrepper { /** * Updates the version in the version file at the given {@link versionFilePath} to the given {@link newVersion}. - * @param versionFilePath The full or relative file path to the version file. + * @param settings The prepare release settings. * @param newVersion The new version. */ - private updateVersion(versionFilePath: string, newVersion: string) { + private updateVersion(settings: PrepareReleaseSettings, newVersion: string) { + const versionFilePath = settings.versionFilePath ?? ""; + const extension = extname(versionFilePath).toLowerCase().trim(); newVersion = newVersion.trim(); switch (extension) { case ".json": { const jsonUpdater = new JsonVersionUpdater(); - jsonUpdater.updateVersion(versionFilePath, newVersion); + jsonUpdater.updateVersion(settings, newVersion); break; } case ".csproj": { const csharpUpdater = new CSharpVersionUpdater(); - csharpUpdater.updateVersion(versionFilePath, newVersion); + csharpUpdater.updateVersion(settings, newVersion); break; } default: { From 172906d46896e330a6b7fabb3240d5fa26edf145 Mon Sep 17 00:00:00 2001 From: Calvin Wilkinson Date: Thu, 20 Jun 2024 21:01:28 +0100 Subject: [PATCH 3/5] cleanup: remove unused files --- .github/cicd/core/EnvVarService.ts | 63 ------------------------------ .github/cicd/core/PATService.ts | 24 ------------ 2 files changed, 87 deletions(-) delete mode 100644 .github/cicd/core/EnvVarService.ts delete mode 100644 .github/cicd/core/PATService.ts diff --git a/.github/cicd/core/EnvVarService.ts b/.github/cicd/core/EnvVarService.ts deleted file mode 100644 index 3a8b196..0000000 --- a/.github/cicd/core/EnvVarService.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { CLI } from "../../../src/core/CLI.ts"; - -export class EnvVarService { - private readonly cli: CLI; - - /** - * Creates a new instance of the {@link EnvVarService} class. - */ - constructor() { - this.cli = new CLI(); - } - - public getVar(varName: string): string { - if (Deno.build.os === "windows") { - return this.getVarWin(varName) ?? ""; - } else { - return this.getVarLinux(varName) ?? ""; - } - } - - public setVar(varName: string, value: string): void { - if (Deno.build.os === "windows") { - this.setVarWin(varName, value); - } else { - this.setVarLinux(varName, value); - } - } - - public varExists(varName: string): boolean { - if (Deno.build.os === "windows") { - return !(this.getVarWin(varName) === undefined); - } else { - return this.exitWithUnsupportedOsError() as boolean; - } - } - - private getVarWin(varName: string): string | undefined { - return Deno.env.get(varName); - } - - private getVarLinux(varName: string): string | undefined { - return this.exitWithUnsupportedOsError() as string; - } - - private async setVarWin(varName: string, value: string): Promise { - const commandResult = await this.cli.runAsync(`setx ${varName} ${value}`); - - if (commandResult instanceof Error) { - console.log(commandResult.message); - Deno.exit(1); - } - } - - private async setVarLinux(varName: string, value: string): Promise { - this.exitWithUnsupportedOsError(); - } - - private exitWithUnsupportedOsError(): void | boolean | string { - const errorMsg = `Environment variable support for the operating system '${Deno.build.os}' not implemented.`; - console.log(errorMsg); - Deno.exit(); - } -} diff --git a/.github/cicd/core/PATService.ts b/.github/cicd/core/PATService.ts deleted file mode 100644 index 47d33ac..0000000 --- a/.github/cicd/core/PATService.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { EnvVarService } from "./EnvVarService.ts"; -import { TokenEnvVarName } from "./types.ts"; - -export class PATService { - private readonly envVarService: EnvVarService; - private readonly KDCLI_TOKEN: TokenEnvVarName = "KDCLI_TOKEN"; - - /** - * Creates a new instance of the {@link PATService} class. - */ - constructor() { - this.envVarService = new EnvVarService(); - } - - public patExists(): boolean { - const result = this.envVarService.varExists(this.KDCLI_TOKEN); - - return result; - } - - public setPAT(pat: string): void { - this.envVarService.setVar(this.KDCLI_TOKEN, pat); - } -} From dc7164aa10b3b1a5cb52934b2909c63b6108359b Mon Sep 17 00:00:00 2001 From: Calvin Wilkinson Date: Thu, 20 Jun 2024 21:01:47 +0100 Subject: [PATCH 4/5] refactor: change code to meeting coding standards --- src/json-version-updater.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/json-version-updater.ts b/src/json-version-updater.ts index 0bfca6d..ecbbf12 100644 --- a/src/json-version-updater.ts +++ b/src/json-version-updater.ts @@ -47,31 +47,31 @@ export class JsonVersionUpdater { if (propChain.length === 0) { return [false, ""]; } - + let currentObj = obj; - + for (let i = 0; i < propChain.length - 1; i++) { const propertyName = propChain[i]; - + if (!(propertyName in currentObj)) { return [false, `The property '${propertyName}' does not exist.`]; } - - if (currentObj !== null && typeof currentObj === 'object') { + + if (currentObj !== null && typeof currentObj === "object") { currentObj = currentObj[propertyName]; } else { return [false, "Cannot set a value on a non-object"]; } } - + const lastPropName = propChain[propChain.length - 1]; - + if (!(lastPropName in currentObj)) { return [false, `The property '${lastPropName}' does not exist.`]; } - + currentObj[lastPropName] = newValue; - + return [true, undefined]; } } From de1b52ea0da886eaf173478f31621d0d57697cd7 Mon Sep 17 00:00:00 2001 From: Calvin Wilkinson Date: Thu, 20 Jun 2024 21:22:49 +0100 Subject: [PATCH 5/5] chore: improve the setPropertyValue function --- src/json-version-updater.ts | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/json-version-updater.ts b/src/json-version-updater.ts index ecbbf12..3a5a157 100644 --- a/src/json-version-updater.ts +++ b/src/json-version-updater.ts @@ -43,35 +43,29 @@ export class JsonVersionUpdater { * @param newValue The new value to set the last prop in the chain to. * @returns A tuple where the first item is a boolean indicating if the property was set and the second item is an error message if the property was not set. */ - private setPropertyValue(obj: any, propChain: string[], newValue: string | number): [boolean, string | undefined] { - if (propChain.length === 0) { - return [false, ""]; - } - - let currentObj = obj; + private setPropertyValue( + obj: T, + propChain: string[], + newValue: string | number, + ): [boolean, string | undefined] { + let currentObj: Record = obj; // Start with the entire object for (let i = 0; i < propChain.length - 1; i++) { const propertyName = propChain[i]; - if (!(propertyName in currentObj)) { return [false, `The property '${propertyName}' does not exist.`]; } - if (currentObj !== null && typeof currentObj === "object") { - currentObj = currentObj[propertyName]; - } else { - return [false, "Cannot set a value on a non-object"]; - } + currentObj = currentObj[propertyName] as Record; } - const lastPropName = propChain[propChain.length - 1]; + const finalPropName = propChain[propChain.length - 1]; - if (!(lastPropName in currentObj)) { - return [false, `The property '${lastPropName}' does not exist.`]; + if (finalPropName in currentObj) { + (currentObj as Record)[finalPropName] = newValue; // Set the new value + return [true, undefined]; } - currentObj[lastPropName] = newValue; - - return [true, undefined]; + return [false, `The property '${finalPropName}' does not exist.`]; } }