Skip to content

Commit

Permalink
CRs
Browse files Browse the repository at this point in the history
  • Loading branch information
zeyadkhaled committed Feb 14, 2024
1 parent cb13faa commit dee086c
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
);
Expand Down
75 changes: 30 additions & 45 deletions packages/cli/src/util/autoVersion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "@[email protected]";
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.",
);
});
Expand Down
16 changes: 10 additions & 6 deletions packages/cli/src/util/autoVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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<string> {
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(
Expand All @@ -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<string> {
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();
Expand Down Expand Up @@ -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.`,
);
}
}
Expand All @@ -92,5 +95,6 @@ function gitDescribe(matchPrefix: string | undefined): string {
`Unable to determine the version automatically: ${error}.\nPlease supply a --version argument.`,
);
}

return gitVersion;
}
27 changes: 11 additions & 16 deletions packages/cli/src/util/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@ const CONFIG_FILE_NAMES: string[] = [
"foundry.config.json",
];

const SUPPORTED_EXTENSIONS: string[] = [
".json",
];

const CONFIG_FILE_SCHEMA: JSONSchemaType<FoundryConfig> = {
type: "object",
properties: {
Expand Down Expand Up @@ -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}`,
);
}

Expand All @@ -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();
}
4 changes: 2 additions & 2 deletions packages/cli/src/util/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<LoadedToken> {
let token: string;
Expand Down

0 comments on commit dee086c

Please sign in to comment.