Skip to content

Commit 2150348

Browse files
committed
Remove relative paths from Husky called from 'prepare' task on npm install and privacy policy version integration to avoid breakage during build on Vercel
1 parent bfc5124 commit 2150348

5 files changed

Lines changed: 122 additions & 12 deletions

File tree

.husky/prepare.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env node
2+
/* eslint-disable no-undef */
3+
/**
4+
* Husky prepare script to install git hooks. It's designed to quiet warnings on
5+
* CI environments where .git directory may be missing when "prepare" script runs
6+
* (e.g., during "npm install" step).
7+
*/
8+
import { existsSync } from 'node:fs'
9+
import { join } from 'node:path'
10+
import { execSync } from 'node:child_process'
11+
12+
const projectRoot = process.cwd()
13+
const gitDirectory = join(projectRoot, '.git')
14+
15+
if (!existsSync(gitDirectory)) {
16+
console.warn(`✅ Skipping Husky install: missing .git directory at ${gitDirectory}`)
17+
process.exit(0)
18+
}
19+
20+
try {
21+
console.log(`Running Husky install from ${projectRoot}`)
22+
execSync('husky', { stdio: 'inherit', cwd: projectRoot })
23+
console.log('✅ Husky install complete')
24+
} catch (error) {
25+
console.error('❌ Husky install failed')
26+
const status = typeof error === 'object' && error && 'status' in error && typeof error.status === 'number'
27+
? error.status
28+
: 1
29+
process.exit(status)
30+
}

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"test:e2e:full": "dotenv -e .env.development -- cross-env FORCE_COLOR=1 E2E_MOCKS=1 npx playwright test",
6161
"test:unit": "FORCE_COLOR=1 npx vitest run",
6262
"upgrade": "npx @astrojs/upgrade",
63-
"prepare": "node -e \"const fs=require('node:fs');if(!fs.existsSync('.git')){console.log('Skipping Husky install (missing .git directory)');process.exit(0);}\" && husky"
63+
"prepare": "node .husky/prepare.js"
6464
},
6565
"dependencies": {
6666
"@astrojs/check": "0.9.6",
@@ -158,7 +158,7 @@
158158
"eslint-import-resolver-typescript": "^4.4.4",
159159
"eslint-plugin-astro": "1.5.0",
160160
"eslint-plugin-import": "2.32.0",
161-
"eslint-plugin-jsdoc": "61.4.1",
161+
"eslint-plugin-jsdoc": "61.4.2",
162162
"eslint-plugin-jsx-a11y": "6.10.2",
163163
"eslint-plugin-security": "3.0.1",
164164
"eslint-plugin-yml": "1.19.0",

src/integrations/PrivacyPolicyVersion/__tests__/index.spec.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
22
import { execSync } from 'node:child_process'
3+
import { existsSync } from 'node:fs'
34
import { TestError } from '@test/errors'
45

56
vi.mock('../../../lib/config/environmentServer', () => ({
@@ -11,6 +12,10 @@ vi.mock('node:child_process', () => ({
1112
execSync: vi.fn(),
1213
}))
1314

15+
vi.mock('node:fs', () => ({
16+
existsSync: vi.fn(() => true),
17+
}))
18+
1419
import { getOptionalEnv } from '../../../lib/config/environmentServer'
1520

1621
describe('PrivacyPolicyVersion Integration', () => {
@@ -19,6 +24,7 @@ describe('PrivacyPolicyVersion Integration', () => {
1924

2025
beforeEach(() => {
2126
vi.mocked(getOptionalEnv).mockReturnValue(undefined)
27+
vi.mocked(existsSync).mockReturnValue(true)
2228

2329
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
2430
consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
@@ -150,6 +156,35 @@ describe('PrivacyPolicyVersion Integration', () => {
150156
})
151157
expect(consoleWarnSpy).toHaveBeenCalled()
152158
})
159+
160+
it('skips git lookup entirely when repository metadata is missing', async () => {
161+
vi.mocked(existsSync).mockReturnValue(false)
162+
vi.useFakeTimers()
163+
vi.setSystemTime(new Date('2026-01-15T00:00:00Z'))
164+
165+
const { privacyPolicyVersion } = await import('../index')
166+
167+
const mockUpdateConfig = vi.fn()
168+
const integration = privacyPolicyVersion()
169+
170+
await integration.hooks['astro:config:setup']?.({
171+
updateConfig: mockUpdateConfig,
172+
// @ts-expect-error - Partial mock
173+
config: {},
174+
})
175+
176+
expect(execSync).not.toHaveBeenCalled()
177+
expect(consoleWarnSpy).toHaveBeenCalledWith(
178+
'[privacy-policy-version] Git metadata not found. Skipping git lookup.',
179+
)
180+
expect(mockUpdateConfig).toHaveBeenCalledWith({
181+
vite: {
182+
define: {
183+
'import.meta.env.PRIVACY_POLICY_VERSION': '"2026-01-15"',
184+
},
185+
},
186+
})
187+
})
153188
})
154189

155190
describe('integration metadata', () => {
@@ -211,5 +246,25 @@ describe('PrivacyPolicyVersion Integration', () => {
211246
expect.any(Object),
212247
)
213248
})
249+
250+
it('executes git commands from the project root directory', async () => {
251+
vi.mocked(execSync).mockReturnValue('2024-03-15')
252+
253+
const { privacyPolicyVersion } = await import('../index')
254+
255+
const mockUpdateConfig = vi.fn()
256+
const integration = privacyPolicyVersion()
257+
258+
await integration.hooks['astro:config:setup']?.({
259+
updateConfig: mockUpdateConfig,
260+
// @ts-expect-error - Partial mock
261+
config: {},
262+
})
263+
264+
expect(execSync).toHaveBeenCalledWith(
265+
expect.any(String),
266+
expect.objectContaining({ cwd: process.cwd() }),
267+
)
268+
})
214269
})
215270
})

src/integrations/PrivacyPolicyVersion/index.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@
1515
*/
1616

1717
import { execSync } from 'node:child_process'
18+
import { existsSync } from 'node:fs'
19+
import { join } from 'node:path'
1820
import type { AstroIntegration } from 'astro'
1921
import { getOptionalEnv } from '../../lib/config/environmentServer'
2022

21-
const PRIVACY_POLICY_PATH = 'src/pages/privacy/index.astro'
23+
const PROJECT_ROOT = process.cwd()
24+
const PRIVACY_POLICY_PATH = join(PROJECT_ROOT, 'src', 'pages', 'privacy', 'index.astro')
25+
const GIT_DIRECTORY_PATH = join(PROJECT_ROOT, '.git')
2226

2327
export const toIsoDateString = (date: Date): string => date.toISOString().slice(0, 10)
2428

@@ -32,7 +36,7 @@ function getPrivacyPolicyVersionFromGit(filePath: string): string | null {
3236
// Get last commit date for privacy policy file in YYYY-MM-DD format
3337
const lastCommitDate = execSync(
3438
`git log -1 --format=%cd --date=format:%Y-%m-%d -- ${filePath}`,
35-
{ encoding: 'utf-8' },
39+
{ encoding: 'utf-8', cwd: PROJECT_ROOT },
3640
).trim()
3741

3842
if (lastCommitDate) {
@@ -51,6 +55,23 @@ function getPrivacyPolicyVersionFromGit(filePath: string): string | null {
5155
}
5256
}
5357

58+
/**
59+
* Determine whether git metadata is available before issuing git commands.
60+
* Vercel preview builds, for example, do not clone the repo with git history,
61+
* so attempting to run git commands will fail immediately. Checking for a git
62+
* directory lets us skip the expensive call entirely.
63+
*/
64+
function hasGitRepository(): boolean {
65+
try {
66+
return existsSync(GIT_DIRECTORY_PATH)
67+
} catch (error) {
68+
console.warn(
69+
`[privacy-policy-version] Unable to verify git repository: ${error instanceof Error ? error.message : String(error)}`,
70+
)
71+
return false
72+
}
73+
}
74+
5475
/**
5576
* Resolve privacy policy version using env, git metadata, or current date fallback.
5677
*/
@@ -62,10 +83,14 @@ export function resolvePrivacyPolicyVersion(): string {
6283
return envVersion
6384
}
6485

65-
const gitVersion = getPrivacyPolicyVersionFromGit(PRIVACY_POLICY_PATH)
66-
if (gitVersion) {
67-
console.log(`✅ Privacy policy version set from git: ${gitVersion}`)
68-
return gitVersion
86+
if (hasGitRepository()) {
87+
const gitVersion = getPrivacyPolicyVersionFromGit(PRIVACY_POLICY_PATH)
88+
if (gitVersion) {
89+
console.log(`✅ Privacy policy version set from git: ${gitVersion}`)
90+
return gitVersion
91+
}
92+
} else {
93+
console.warn('[privacy-policy-version] Git metadata not found. Skipping git lookup.')
6994
}
7095

7196
const fallback = toIsoDateString(new Date())

0 commit comments

Comments
 (0)