chore: add lefthook

This commit is contained in:
Mauricio Siu
2024-11-28 22:09:42 -06:00
parent cdd39670f5
commit e194f3c454
4 changed files with 386 additions and 252 deletions

45
lefthook.yml Normal file
View File

@@ -0,0 +1,45 @@
# EXAMPLE USAGE:
#
# Refer for explanation to following link:
# https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md
#
# pre-push:
# commands:
# packages-audit:
# tags: frontend security
# run: yarn audit
# gems-audit:
# tags: backend security
# run: bundle audit
#
# pre-commit:
# parallel: true
# commands:
# eslint:
# glob: "*.{js,ts,jsx,tsx}"
# run: yarn eslint {staged_files}
# rubocop:
# tags: backend style
# glob: "*.rb"
# exclude: '(^|/)(application|routes)\.rb$'
# run: bundle exec rubocop --force-exclusion {all_files}
# govet:
# tags: backend style
# files: git ls-files -m
# glob: "*.go"
# run: go vet {files}
# scripts:
# "hello.js":
# runner: node
# "any.go":
# runner: go run
commit-msg:
commands:
commitlint:
run: "npx commitlint --edit $1"
pre-commit:
commands:
lint-staged:
run: "npx lint-staged"

View File

@@ -17,8 +17,7 @@
"build": "pnpm -r run build", "build": "pnpm -r run build",
"format-and-lint": "biome check .", "format-and-lint": "biome check .",
"check": "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true", "check": "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true",
"format-and-lint:fix": "biome check . --write", "format-and-lint:fix": "biome check . --write"
"prepare": "node .husky/install.mjs"
}, },
"devDependencies": { "devDependencies": {
"dotenv": "16.4.5", "dotenv": "16.4.5",
@@ -26,7 +25,7 @@
"tsx": "4.16.2", "tsx": "4.16.2",
"lint-staged": "^15.2.7", "lint-staged": "^15.2.7",
"@biomejs/biome": "1.8.3", "@biomejs/biome": "1.8.3",
"husky": "^9.1.6", "lefthook": "1.8.4",
"@commitlint/cli": "^19.3.0", "@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2", "@commitlint/config-conventional": "^19.2.2",
"@types/node": "^18.17.0" "@types/node": "^18.17.0"

View File

@@ -14,195 +14,195 @@ import { type Github, findGithubById } from "@dokploy/server/services/github";
import { execAsyncRemote } from "../process/execAsync"; import { execAsyncRemote } from "../process/execAsync";
export const authGithub = (githubProvider: Github): Octokit => { export const authGithub = (githubProvider: Github): Octokit => {
if (!haveGithubRequirements(githubProvider)) { if (!haveGithubRequirements(githubProvider)) {
throw new TRPCError({ throw new TRPCError({
code: "NOT_FOUND", code: "NOT_FOUND",
message: "Github Account not configured correctly", message: "Github Account not configured correctly",
}); });
} }
const octokit: Octokit = new Octokit({ const octokit: Octokit = new Octokit({
authStrategy: createAppAuth, authStrategy: createAppAuth,
auth: { auth: {
appId: githubProvider?.githubAppId || 0, appId: githubProvider?.githubAppId || 0,
privateKey: githubProvider?.githubPrivateKey || "", privateKey: githubProvider?.githubPrivateKey || "",
installationId: githubProvider?.githubInstallationId, installationId: githubProvider?.githubInstallationId,
}, },
}); });
return octokit; return octokit;
}; };
export const getGithubToken = async ( export const getGithubToken = async (
octokit: ReturnType<typeof authGithub> octokit: ReturnType<typeof authGithub>,
) => { ) => {
const installation = (await octokit.auth({ const installation = (await octokit.auth({
type: "installation", type: "installation",
})) as { })) as {
token: string; token: string;
}; };
return installation.token; return installation.token;
}; };
export const haveGithubRequirements = (githubProvider: Github) => { export const haveGithubRequirements = (githubProvider: Github) => {
return !!( return !!(
githubProvider?.githubAppId && githubProvider?.githubAppId &&
githubProvider?.githubPrivateKey && githubProvider?.githubPrivateKey &&
githubProvider?.githubInstallationId githubProvider?.githubInstallationId
); );
}; };
const getErrorCloneRequirements = (entity: { const getErrorCloneRequirements = (entity: {
repository?: string | null; repository?: string | null;
owner?: string | null; owner?: string | null;
branch?: string | null; branch?: string | null;
}) => { }) => {
const reasons: string[] = []; const reasons: string[] = [];
const { repository, owner, branch } = entity; const { repository, owner, branch } = entity;
if (!repository) reasons.push("1. Repository not assigned."); if (!repository) reasons.push("1. Repository not assigned.");
if (!owner) reasons.push("2. Owner not specified."); if (!owner) reasons.push("2. Owner not specified.");
if (!branch) reasons.push("3. Branch not defined."); if (!branch) reasons.push("3. Branch not defined.");
return reasons; return reasons;
}; };
export type ApplicationWithGithub = InferResultType< export type ApplicationWithGithub = InferResultType<
"applications", "applications",
{ github: true } { github: true }
>; >;
export type ComposeWithGithub = InferResultType<"compose", { github: true }>; export type ComposeWithGithub = InferResultType<"compose", { github: true }>;
export const cloneGithubRepository = async ( export const cloneGithubRepository = async (
entity: ApplicationWithGithub | ComposeWithGithub, entity: ApplicationWithGithub | ComposeWithGithub,
logPath: string, logPath: string,
isCompose = false isCompose = false,
) => { ) => {
const { APPLICATIONS_PATH, COMPOSE_PATH } = paths(); const { APPLICATIONS_PATH, COMPOSE_PATH } = paths();
const writeStream = createWriteStream(logPath, { flags: "a" }); const writeStream = createWriteStream(logPath, { flags: "a" });
const { appName, repository, owner, branch, githubId } = entity; const { appName, repository, owner, branch, githubId } = entity;
if (!githubId) { if (!githubId) {
throw new TRPCError({ throw new TRPCError({
code: "NOT_FOUND", code: "NOT_FOUND",
message: "GitHub Provider not found", message: "GitHub Provider not found",
}); });
} }
const requirements = getErrorCloneRequirements(entity); const requirements = getErrorCloneRequirements(entity);
// Check if requirements are met // Check if requirements are met
if (requirements.length > 0) { if (requirements.length > 0) {
writeStream.write( writeStream.write(
`\nGitHub Repository configuration failed for application: ${appName}\n` `\nGitHub Repository configuration failed for application: ${appName}\n`,
); );
writeStream.write("Reasons:\n"); writeStream.write("Reasons:\n");
writeStream.write(requirements.join("\n")); writeStream.write(requirements.join("\n"));
writeStream.end(); writeStream.end();
throw new TRPCError({ throw new TRPCError({
code: "BAD_REQUEST", code: "BAD_REQUEST",
message: "Error: GitHub repository information is incomplete.", message: "Error: GitHub repository information is incomplete.",
}); });
} }
const githubProvider = await findGithubById(githubId); const githubProvider = await findGithubById(githubId);
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
const outputPath = join(basePath, appName, "code"); const outputPath = join(basePath, appName, "code");
const octokit = authGithub(githubProvider); const octokit = authGithub(githubProvider);
const token = await getGithubToken(octokit); const token = await getGithubToken(octokit);
const repoclone = `github.com/${owner}/${repository}.git`; const repoclone = `github.com/${owner}/${repository}.git`;
await recreateDirectory(outputPath); await recreateDirectory(outputPath);
const cloneUrl = `https://oauth2:${token}@${repoclone}`; const cloneUrl = `https://oauth2:${token}@${repoclone}`;
try { try {
writeStream.write(`\nClonning Repo ${repoclone} to ${outputPath}: ✅\n`); writeStream.write(`\nClonning Repo ${repoclone} to ${outputPath}: ✅\n`);
await spawnAsync( await spawnAsync(
"git", "git",
[ [
"clone", "clone",
"--branch", "--branch",
branch!, branch!,
"--depth", "--depth",
"1", "1",
"--recurse-submodules", "--recurse-submodules",
cloneUrl, cloneUrl,
outputPath, outputPath,
"--progress", "--progress",
], ],
(data) => { (data) => {
if (writeStream.writable) { if (writeStream.writable) {
writeStream.write(data); writeStream.write(data);
} }
} },
); );
writeStream.write(`\nCloned ${repoclone}: ✅\n`); writeStream.write(`\nCloned ${repoclone}: ✅\n`);
} catch (error) { } catch (error) {
writeStream.write(`ERROR Clonning: ${error}: ❌`); writeStream.write(`ERROR Clonning: ${error}: ❌`);
throw error; throw error;
} finally { } finally {
writeStream.end(); writeStream.end();
} }
}; };
export const getGithubCloneCommand = async ( export const getGithubCloneCommand = async (
entity: ApplicationWithGithub | ComposeWithGithub, entity: ApplicationWithGithub | ComposeWithGithub,
logPath: string, logPath: string,
isCompose = false isCompose = false,
) => { ) => {
const { appName, repository, owner, branch, githubId, serverId } = entity; const { appName, repository, owner, branch, githubId, serverId } = entity;
if (!serverId) { if (!serverId) {
throw new TRPCError({ throw new TRPCError({
code: "NOT_FOUND", code: "NOT_FOUND",
message: "Server not found", message: "Server not found",
}); });
} }
if (!githubId) { if (!githubId) {
const command = ` const command = `
echo "Error: ❌ Github Provider not found" >> ${logPath}; echo "Error: ❌ Github Provider not found" >> ${logPath};
exit 1; exit 1;
`; `;
await execAsyncRemote(serverId, command); await execAsyncRemote(serverId, command);
throw new TRPCError({ throw new TRPCError({
code: "NOT_FOUND", code: "NOT_FOUND",
message: "GitHub Provider not found", message: "GitHub Provider not found",
}); });
} }
const requirements = getErrorCloneRequirements(entity); const requirements = getErrorCloneRequirements(entity);
// Build log messages // Build log messages
let logMessages = ""; let logMessages = "";
if (requirements.length > 0) { if (requirements.length > 0) {
logMessages += `\nGitHub Repository configuration failed for application: ${appName}\n`; logMessages += `\nGitHub Repository configuration failed for application: ${appName}\n`;
logMessages += "Reasons:\n"; logMessages += "Reasons:\n";
logMessages += requirements.join("\n"); logMessages += requirements.join("\n");
const escapedLogMessages = logMessages const escapedLogMessages = logMessages
.replace(/\\/g, "\\\\") .replace(/\\/g, "\\\\")
.replace(/"/g, '\\"') .replace(/"/g, '\\"')
.replace(/\n/g, "\\n"); .replace(/\n/g, "\\n");
const bashCommand = ` const bashCommand = `
echo "${escapedLogMessages}" >> ${logPath}; echo "${escapedLogMessages}" >> ${logPath};
exit 1; # Exit with error code exit 1; # Exit with error code
`; `;
await execAsyncRemote(serverId, bashCommand); await execAsyncRemote(serverId, bashCommand);
return; return;
} }
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true); const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
const githubProvider = await findGithubById(githubId); const githubProvider = await findGithubById(githubId);
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
const outputPath = join(basePath, appName, "code"); const outputPath = join(basePath, appName, "code");
const octokit = authGithub(githubProvider); const octokit = authGithub(githubProvider);
const token = await getGithubToken(octokit); const token = await getGithubToken(octokit);
const repoclone = `github.com/${owner}/${repository}.git`; const repoclone = `github.com/${owner}/${repository}.git`;
const cloneUrl = `https://oauth2:${token}@${repoclone}`; const cloneUrl = `https://oauth2:${token}@${repoclone}`;
const cloneCommand = ` const cloneCommand = `
rm -rf ${outputPath}; rm -rf ${outputPath};
mkdir -p ${outputPath}; mkdir -p ${outputPath};
if ! git clone --branch ${branch} --depth 1 --recurse-submodules --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then if ! git clone --branch ${branch} --depth 1 --recurse-submodules --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
@@ -212,127 +212,127 @@ fi
echo "Cloned ${repoclone} to ${outputPath}: ✅" >> ${logPath}; echo "Cloned ${repoclone} to ${outputPath}: ✅" >> ${logPath};
`; `;
return cloneCommand; return cloneCommand;
}; };
export const cloneRawGithubRepository = async (entity: Compose) => { export const cloneRawGithubRepository = async (entity: Compose) => {
const { appName, repository, owner, branch, githubId } = entity; const { appName, repository, owner, branch, githubId } = entity;
if (!githubId) { if (!githubId) {
throw new TRPCError({ throw new TRPCError({
code: "NOT_FOUND", code: "NOT_FOUND",
message: "GitHub Provider not found", message: "GitHub Provider not found",
}); });
} }
const { COMPOSE_PATH } = paths(); const { COMPOSE_PATH } = paths();
const githubProvider = await findGithubById(githubId); const githubProvider = await findGithubById(githubId);
const basePath = COMPOSE_PATH; const basePath = COMPOSE_PATH;
const outputPath = join(basePath, appName, "code"); const outputPath = join(basePath, appName, "code");
const octokit = authGithub(githubProvider); const octokit = authGithub(githubProvider);
const token = await getGithubToken(octokit); const token = await getGithubToken(octokit);
const repoclone = `github.com/${owner}/${repository}.git`; const repoclone = `github.com/${owner}/${repository}.git`;
await recreateDirectory(outputPath); await recreateDirectory(outputPath);
const cloneUrl = `https://oauth2:${token}@${repoclone}`; const cloneUrl = `https://oauth2:${token}@${repoclone}`;
try { try {
await spawnAsync("git", [ await spawnAsync("git", [
"clone", "clone",
"--branch", "--branch",
branch!, branch!,
"--depth", "--depth",
"1", "1",
"--recurse-submodules", "--recurse-submodules",
cloneUrl, cloneUrl,
outputPath, outputPath,
"--progress", "--progress",
]); ]);
} catch (error) { } catch (error) {
throw error; throw error;
} }
}; };
export const cloneRawGithubRepositoryRemote = async (compose: Compose) => { export const cloneRawGithubRepositoryRemote = async (compose: Compose) => {
const { appName, repository, owner, branch, githubId, serverId } = compose; const { appName, repository, owner, branch, githubId, serverId } = compose;
if (!serverId) { if (!serverId) {
throw new TRPCError({ throw new TRPCError({
code: "NOT_FOUND", code: "NOT_FOUND",
message: "Server not found", message: "Server not found",
}); });
} }
if (!githubId) { if (!githubId) {
throw new TRPCError({ throw new TRPCError({
code: "NOT_FOUND", code: "NOT_FOUND",
message: "GitHub Provider not found", message: "GitHub Provider not found",
}); });
} }
const { COMPOSE_PATH } = paths(true); const { COMPOSE_PATH } = paths(true);
const githubProvider = await findGithubById(githubId); const githubProvider = await findGithubById(githubId);
const basePath = COMPOSE_PATH; const basePath = COMPOSE_PATH;
const outputPath = join(basePath, appName, "code"); const outputPath = join(basePath, appName, "code");
const octokit = authGithub(githubProvider); const octokit = authGithub(githubProvider);
const token = await getGithubToken(octokit); const token = await getGithubToken(octokit);
const repoclone = `github.com/${owner}/${repository}.git`; const repoclone = `github.com/${owner}/${repository}.git`;
const cloneUrl = `https://oauth2:${token}@${repoclone}`; const cloneUrl = `https://oauth2:${token}@${repoclone}`;
try { try {
const command = ` const command = `
rm -rf ${outputPath}; rm -rf ${outputPath};
git clone --branch ${branch} --depth 1 ${cloneUrl} ${outputPath} git clone --branch ${branch} --depth 1 ${cloneUrl} ${outputPath}
`; `;
await execAsyncRemote(serverId, command); await execAsyncRemote(serverId, command);
} catch (error) { } catch (error) {
throw error; throw error;
} }
}; };
export const getGithubRepositories = async (githubId?: string) => { export const getGithubRepositories = async (githubId?: string) => {
if (!githubId) { if (!githubId) {
return []; return [];
} }
const githubProvider = await findGithubById(githubId); const githubProvider = await findGithubById(githubId);
const octokit = new Octokit({ const octokit = new Octokit({
authStrategy: createAppAuth, authStrategy: createAppAuth,
auth: { auth: {
appId: githubProvider.githubAppId, appId: githubProvider.githubAppId,
privateKey: githubProvider.githubPrivateKey, privateKey: githubProvider.githubPrivateKey,
installationId: githubProvider.githubInstallationId, installationId: githubProvider.githubInstallationId,
}, },
}); });
const repositories = (await octokit.paginate( const repositories = (await octokit.paginate(
octokit.rest.apps.listReposAccessibleToInstallation octokit.rest.apps.listReposAccessibleToInstallation,
)) as unknown as Awaited< )) as unknown as Awaited<
ReturnType<typeof octokit.rest.apps.listReposAccessibleToInstallation> ReturnType<typeof octokit.rest.apps.listReposAccessibleToInstallation>
>["data"]["repositories"]; >["data"]["repositories"];
return repositories; return repositories;
}; };
export const getGithubBranches = async ( export const getGithubBranches = async (
input: typeof apiFindGithubBranches._type input: typeof apiFindGithubBranches._type,
) => { ) => {
if (!input.githubId) { if (!input.githubId) {
return []; return [];
} }
const githubProvider = await findGithubById(input.githubId); const githubProvider = await findGithubById(input.githubId);
const octokit = new Octokit({ const octokit = new Octokit({
authStrategy: createAppAuth, authStrategy: createAppAuth,
auth: { auth: {
appId: githubProvider.githubAppId, appId: githubProvider.githubAppId,
privateKey: githubProvider.githubPrivateKey, privateKey: githubProvider.githubPrivateKey,
installationId: githubProvider.githubInstallationId, installationId: githubProvider.githubInstallationId,
}, },
}); });
const branches = (await octokit.paginate(octokit.rest.repos.listBranches, { const branches = (await octokit.paginate(octokit.rest.repos.listBranches, {
owner: input.owner, owner: input.owner,
repo: input.repo, repo: input.repo,
})) as unknown as Awaited< })) as unknown as Awaited<
ReturnType<typeof octokit.rest.repos.listBranches> ReturnType<typeof octokit.rest.repos.listBranches>
>["data"]; >["data"];
return branches; return branches;
}; };

110
pnpm-lock.yaml generated
View File

@@ -30,9 +30,9 @@ importers:
esbuild: esbuild:
specifier: 0.20.2 specifier: 0.20.2
version: 0.20.2 version: 0.20.2
husky: lefthook:
specifier: ^9.1.6 specifier: 1.8.4
version: 9.1.6 version: 1.8.4
lint-staged: lint-staged:
specifier: ^15.2.7 specifier: ^15.2.7
version: 15.2.7 version: 15.2.7
@@ -4722,11 +4722,6 @@ packages:
resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
engines: {node: '>=16.17.0'} engines: {node: '>=16.17.0'}
husky@9.1.6:
resolution: {integrity: sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==}
engines: {node: '>=18'}
hasBin: true
hyperdyperid@1.2.0: hyperdyperid@1.2.0:
resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==}
engines: {node: '>=10.18'} engines: {node: '>=10.18'}
@@ -4958,6 +4953,60 @@ packages:
leac@0.6.0: leac@0.6.0:
resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==}
lefthook-darwin-arm64@1.8.4:
resolution: {integrity: sha512-OS5MsU0gvd8LYSpuQCHtmDUqwNrJ/LjCO0LGC1wNepY4OkuVl9DfX+rQ506CVUQYZiGVcwy2/qPOOBjNzA5+wQ==}
cpu: [arm64]
os: [darwin]
lefthook-darwin-x64@1.8.4:
resolution: {integrity: sha512-QLRsqK9aTMRcVW8qz4pzI2OWnGCEcaEPJlIiFjwstYsS+wfkooxOS0UkfVMjy+QoGgEcki+cxF/FoY7lE7DDtw==}
cpu: [x64]
os: [darwin]
lefthook-freebsd-arm64@1.8.4:
resolution: {integrity: sha512-chnQ1m/Cmn9c0sLdk5HL2SToE5LBJv5uQMdH1IGRRcw+nEqWqrMnDXvM75caiJAyjmUGvPH3czKTJDzTFV1E+A==}
cpu: [arm64]
os: [freebsd]
lefthook-freebsd-x64@1.8.4:
resolution: {integrity: sha512-KQi+WBUdnGLnK0rHOR58kbMH5TDVN1ZjZLu66Pv9FCG7Y7shR1qtaTXu+wmxdRhMvaLeQIXRsUEPjNRC66yMmA==}
cpu: [x64]
os: [freebsd]
lefthook-linux-arm64@1.8.4:
resolution: {integrity: sha512-CXNcqIskLwTwQARidGdFqmNxpvOU3jsWPK4KA7pq2+QmlWJ64w98ebMvNBoUmRUCXqzmUm7Udf/jpfz2fobewQ==}
cpu: [arm64]
os: [linux]
lefthook-linux-x64@1.8.4:
resolution: {integrity: sha512-pVNITkFBxUCEtamWSM/res2Gd48+m9YKbNyIBndAuZVC5pKV5aGKZy2DNq6PWUPYiUDPx+7hoAtCJg/tlAiqhw==}
cpu: [x64]
os: [linux]
lefthook-openbsd-arm64@1.8.4:
resolution: {integrity: sha512-l+i/Dg5X36kYzhpMGSPE3rMbWy1KSytbLB9lY1PmxYb6LRH6iQTYIoxvLabVUwSBPSq8HtIFa50+bvC5+scfVA==}
cpu: [arm64]
os: [openbsd]
lefthook-openbsd-x64@1.8.4:
resolution: {integrity: sha512-CqhDDPPX8oHzMLgNi/Reba823DRzj+eMNWQ8axvSiIG+zmG1w20xZH5QSs/mD3tjrND90yfDd90mWMt181qPyA==}
cpu: [x64]
os: [openbsd]
lefthook-windows-arm64@1.8.4:
resolution: {integrity: sha512-dvpvorICmVjmw29Aiczg7DcaSzkd86bEBomiGq4UsAEk3+7ExLrlWJDLFsI6xLjMKmTxy+F7eXb2uDtuFC1N4g==}
cpu: [arm64]
os: [win32]
lefthook-windows-x64@1.8.4:
resolution: {integrity: sha512-e+y8Jt4/7PnoplhOuK48twjGVJEsU4T3J5kxD4mWfl6Cbit0YSn4bme9nW41eqCqTUqOm+ky29XlfnPHFX5ZNA==}
cpu: [x64]
os: [win32]
lefthook@1.8.4:
resolution: {integrity: sha512-XNyMaTWNRuADOaocYiHidgNkNDz8SCekpdNJ7lqceFcBT2zjumnb28/o7IMaNROpLBZdQkLkJXSeaQWGqn3kog==}
hasBin: true
lilconfig@2.1.0: lilconfig@2.1.0:
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -10940,8 +10989,6 @@ snapshots:
human-signals@5.0.0: {} human-signals@5.0.0: {}
husky@9.1.6: {}
hyperdyperid@1.2.0: {} hyperdyperid@1.2.0: {}
i18next-fs-backend@2.3.2: {} i18next-fs-backend@2.3.2: {}
@@ -11153,6 +11200,49 @@ snapshots:
leac@0.6.0: {} leac@0.6.0: {}
lefthook-darwin-arm64@1.8.4:
optional: true
lefthook-darwin-x64@1.8.4:
optional: true
lefthook-freebsd-arm64@1.8.4:
optional: true
lefthook-freebsd-x64@1.8.4:
optional: true
lefthook-linux-arm64@1.8.4:
optional: true
lefthook-linux-x64@1.8.4:
optional: true
lefthook-openbsd-arm64@1.8.4:
optional: true
lefthook-openbsd-x64@1.8.4:
optional: true
lefthook-windows-arm64@1.8.4:
optional: true
lefthook-windows-x64@1.8.4:
optional: true
lefthook@1.8.4:
optionalDependencies:
lefthook-darwin-arm64: 1.8.4
lefthook-darwin-x64: 1.8.4
lefthook-freebsd-arm64: 1.8.4
lefthook-freebsd-x64: 1.8.4
lefthook-linux-arm64: 1.8.4
lefthook-linux-x64: 1.8.4
lefthook-openbsd-arm64: 1.8.4
lefthook-openbsd-x64: 1.8.4
lefthook-windows-arm64: 1.8.4
lefthook-windows-x64: 1.8.4
lilconfig@2.1.0: {} lilconfig@2.1.0: {}
lilconfig@3.1.2: {} lilconfig@3.1.2: {}