From dee086ca62fe2e10dcd77076733d9a9db7974541 Mon Sep 17 00:00:00 2001 From: Zeyad Abuamer Date: Wed, 14 Feb 2024 12:02:55 +0000 Subject: [PATCH] CRs --- .../site/deploy/siteDeployCommand.mts | 2 +- packages/cli/src/util/autoVersion.test.ts | 75 ++++++++----------- packages/cli/src/util/autoVersion.ts | 16 ++-- packages/cli/src/util/config.ts | 27 +++---- packages/cli/src/util/token.ts | 4 +- 5 files changed, 54 insertions(+), 70 deletions(-) diff --git a/packages/cli/src/commands/site/deploy/siteDeployCommand.mts b/packages/cli/src/commands/site/deploy/siteDeployCommand.mts index 906ea43e0..5ab9705c0 100644 --- a/packages/cli/src/commands/site/deploy/siteDeployCommand.mts +++ b/packages/cli/src/commands/site/deploy/siteDeployCommand.mts @@ -59,7 +59,7 @@ export default async function siteDeployCommand( if (version != null) { siteVersion = version; } else { - siteVersion = findAutoVersion(gitTagPrefix); + siteVersion = await findAutoVersion(gitTagPrefix); consola.info( `Auto version inferred next version to be: ${siteVersion}`, ); diff --git a/packages/cli/src/util/autoVersion.test.ts b/packages/cli/src/util/autoVersion.test.ts index 10c3d76ee..1c730043f 100644 --- a/packages/cli/src/util/autoVersion.test.ts +++ b/packages/cli/src/util/autoVersion.test.ts @@ -14,98 +14,83 @@ * limitations under the License. */ -import { execSync } from "node:child_process"; +import { exec } from "node:child_process"; +import { promisify } from "node:util"; import { describe, expect, it, vi } from "vitest"; import { autoVersion } from "./autoVersion.js"; vi.mock("node:child_process"); +const execAsync = promisify(exec); describe("autoVersion", () => { - const execSyncMock = vi.mocked(execSync); + const execMock = vi.mocked(execAsync); + const execReturnValue = (out: string) => ({ stdout: out, stderr: "" }); - it("should return a valid SemVer version from git describe", () => { + it("should return a valid SemVer version from git describe", async () => { const validGitVersion = "1.2.3"; - execSyncMock.mockReturnValue(validGitVersion); + execMock.mockResolvedValue(execReturnValue(validGitVersion)); - const version = autoVersion(); + const version = await autoVersion(); expect(version).toBe("1.2.3"); - - expect(execSyncMock).toHaveBeenCalledWith( - "git describe --tags --first-parent --dirty", - { encoding: "utf8" }, - ); }); - it("should replace default prefix v from git describe output", () => { + it("should replace default prefix v from git describe output", async () => { const validGitVersion = "v1.2.3"; - execSyncMock.mockReturnValue(validGitVersion); - const version = autoVersion(); + execMock.mockResolvedValue(execReturnValue(validGitVersion)); + const version = await autoVersion(); expect(version).toBe("1.2.3"); - expect(execSyncMock).toHaveBeenCalledWith( - "git describe --tags --first-parent --dirty", - { encoding: "utf8" }, - ); }); - it("should replace the prefix from the found git tag", () => { + it("should replace the prefix from the found git tag", async () => { const validGitVersion = "@package@1.2.3"; - execSyncMock.mockReturnValue(validGitVersion); - - const version = autoVersion("@package@"); + execMock.mockResolvedValue(execReturnValue(validGitVersion)); + const version = await autoVersion("@package@"); expect(version).toBe("1.2.3"); - expect(execSyncMock).toHaveBeenCalledWith( - "git describe --tags --first-parent --dirty --match=\"@package@*\"", - { encoding: "utf8" }, - ); }); - it("should only replace the prefix if found at the start of the tag only", () => { + it("should only replace the prefix if found at the start of the tag only", async () => { const validGitVersion = "1.2.3-package"; - execSyncMock.mockReturnValue(validGitVersion); - - const version = autoVersion("-package"); + execMock.mockResolvedValue(execReturnValue(validGitVersion)); + const version = await autoVersion("-package"); expect(version).toBe("1.2.3-package"); - expect(execSyncMock).toHaveBeenCalledWith( - "git describe --tags --first-parent --dirty --match=\"-package*\"", - { encoding: "utf8" }, - ); }); - it("should throw an error if git describe returns a non-SemVer string", () => { + it("should throw an error if git describe returns a non-SemVer string", async () => { const nonSemVerGitVersion = "not-semver"; - execSyncMock.mockReturnValue(nonSemVerGitVersion); - expect(() => autoVersion()).toThrowError(); + execMock.mockResolvedValue(execReturnValue(nonSemVerGitVersion)); + + await expect(autoVersion()).rejects.toThrowError(); }); - it("should throw an error if git isn't found", () => { - execSyncMock.mockImplementation(() => { + it("should throw an error if git isn't found", async () => { + execMock.mockImplementation(() => { throw new Error("Command not found"); }); - expect(() => autoVersion()).toThrowError( + await expect(autoVersion()).rejects.toThrowError( "Unable to determine the version using git-describe as git is not installed", ); }); - it("should throw an error if git isn't found", () => { - execSyncMock.mockImplementation(() => { + it("should throw an error if the current directory is not a git repository", async () => { + execMock.mockImplementation(() => { throw new Error("fatal: not a git repository"); }); - expect(() => autoVersion()).toThrowError( + await expect(autoVersion()).rejects.toThrowError( "Unable to determine the version using git-describe as the current directory is not a git repository", ); }); - it("should throw an error if git isn't found", () => { - execSyncMock.mockImplementation(() => { + it("should throw an error if no git tags are found", async () => { + execMock.mockImplementation(() => { throw new Error("fatal: no names found, cannot describe anything."); }); - expect(() => autoVersion()).toThrowError( + await expect(autoVersion()).rejects.toThrowError( "Unable to determine the version using git-describe as no tags were found.", ); }); diff --git a/packages/cli/src/util/autoVersion.ts b/packages/cli/src/util/autoVersion.ts index a3fab909f..c1805e6c6 100644 --- a/packages/cli/src/util/autoVersion.ts +++ b/packages/cli/src/util/autoVersion.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import { execSync } from "node:child_process"; +import { exec } from "node:child_process"; +import { promisify } from "node:util"; import { ExitProcessError } from "../ExitProcessError.js"; import { isValidSemver } from "./isValidSemver.js"; @@ -25,12 +26,12 @@ import { isValidSemver } from "./isValidSemver.js"; * @returns A promise that resolves to the version string. * @throws An error if the version string is not SemVer compliant or if the version cannot be determined. */ -export function autoVersion(tagPrefix: string = ""): string { +export async function autoVersion(tagPrefix: string = ""): Promise { const [matchPrefix, prefixRegex] = tagPrefix !== "" ? [tagPrefix, new RegExp(`^${tagPrefix}`)] : [undefined, new RegExp(`^v?`)]; - const gitVersion = gitDescribe(matchPrefix); + const gitVersion = await gitDescribe(matchPrefix); const version = gitVersion.trim().replace(prefixRegex, ""); if (!isValidSemver(version)) { throw new ExitProcessError( @@ -42,15 +43,17 @@ export function autoVersion(tagPrefix: string = ""): string { return version; } -function gitDescribe(matchPrefix: string | undefined): string { +async function gitDescribe(matchPrefix: string | undefined): Promise { let gitVersion; try { - gitVersion = execSync( + const execAsync = promisify(exec); + const { stdout } = await execAsync( `git describe --tags --first-parent --dirty${ matchPrefix != null ? ` --match="${matchPrefix}*"` : "" }`, { encoding: "utf8" }, ); + gitVersion = stdout; } catch (error: any) { if (error instanceof Error) { const errorMessage: string = error.message.toLowerCase(); @@ -82,7 +85,7 @@ function gitDescribe(matchPrefix: string | undefined): string { ) { throw new ExitProcessError( 2, - `Unable to determine the version using git-describe as no tags were found.\nPlease tag a version or supply a --version option.`, + `Unable to determine the version using git-describe as no matching tags were found.\nPlease tag a matching version or supply a --version option.`, ); } } @@ -92,5 +95,6 @@ function gitDescribe(matchPrefix: string | undefined): string { `Unable to determine the version automatically: ${error}.\nPlease supply a --version argument.`, ); } + return gitVersion; } diff --git a/packages/cli/src/util/config.ts b/packages/cli/src/util/config.ts index f9a5c597d..3ff94a5de 100644 --- a/packages/cli/src/util/config.ts +++ b/packages/cli/src/util/config.ts @@ -45,10 +45,6 @@ const CONFIG_FILE_NAMES: string[] = [ "foundry.config.json", ]; -const SUPPORTED_EXTENSIONS: string[] = [ - ".json", -]; - const CONFIG_FILE_SCHEMA: JSONSchemaType = { type: "object", properties: { @@ -96,21 +92,15 @@ export async function loadFoundryConfig(): Promise< const configFilePath = await findUp(CONFIG_FILE_NAMES); if (configFilePath) { - const extension = extname(configFilePath); - if (!SUPPORTED_EXTENSIONS.includes(extension)) { - throw new ExitProcessError( - 2, - `Unsupported file extension: ${extension} for config file. Only the following extensions are allowed ${SUPPORTED_EXTENSIONS}`, - ); - } let foundryConfig: FoundryConfig; try { const fileContent = await fsPromises.readFile(configFilePath, "utf-8"); + const extension = extname(configFilePath); foundryConfig = parseConfigFile(fileContent, extension); - } catch { + } catch (error) { throw new ExitProcessError( 2, - `Couldn't read or parse config file ${configFilePath}`, + `Couldn't read or parse config file ${configFilePath}. Error: ${error}`, ); } @@ -133,8 +123,13 @@ function parseConfigFile( fileContent: string, extension: string, ): FoundryConfig { - if (extension === ".json") { - return JSON.parse(fileContent); + switch (extension) { + case ".json": + return JSON.parse(fileContent); + default: + throw new ExitProcessError( + 2, + `Unsupported file extension: ${extension} for config file.`, + ); } - throw Error(); } diff --git a/packages/cli/src/util/token.ts b/packages/cli/src/util/token.ts index 50817f1e7..10a20596f 100644 --- a/packages/cli/src/util/token.ts +++ b/packages/cli/src/util/token.ts @@ -74,10 +74,10 @@ interface LoadedToken { token: string; } /** - * Synchronously reads a JWT Auth Token from a file. + * Reads a JWT Auth Token from a file. * @param filePath The path to the token file. * @returns The token as a string. - * @throws An error if the file cannot be read or if the file does not contain a valid JWT. + * @throws An error if the file cannot be read. */ export async function loadTokenFile(filePath: string): Promise { let token: string;