From acde1311e9f51f34478f7cf1ec3962824efd8082 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Wed, 19 Aug 2026 16:22:32 -0700 Subject: [PATCH] fix(cli): add .playwright-cli/ to .gitignore on workspace install Fixes: https://github.com/microsoft/playwright/issues/42307 --- .../src/tools/cli-daemon/program.ts | 17 +++++++++++++++++ tests/mcp/cli-misc.spec.ts | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/packages/playwright-core/src/tools/cli-daemon/program.ts b/packages/playwright-core/src/tools/cli-daemon/program.ts index 07c216829a998..40b5f5337765a 100644 --- a/packages/playwright-core/src/tools/cli-daemon/program.ts +++ b/packages/playwright-core/src/tools/cli-daemon/program.ts @@ -93,6 +93,7 @@ export async function initWorkspace(initSkills: string | undefined, initSkillsGl const playwrightDir = path.join(cwd, '.playwright'); await fs.promises.mkdir(playwrightDir, { recursive: true }); console.log(`✅ Workspace initialized at \`${cwd}\`.`); + await patchGitIgnore(cwd); } const skills = initSkillsGlobal ?? initSkills; @@ -111,6 +112,22 @@ export async function initWorkspace(initSkills: string | undefined, initSkillsGl await ensureConfiguredBrowserInstalled(); } +async function patchGitIgnore(cwd: string) { + if (!fs.existsSync(path.join(cwd, '.git'))) + return; + try { + const gitIgnorePath = path.join(cwd, '.gitignore'); + const existing = await fs.promises.readFile(gitIgnorePath, 'utf8').catch(() => ''); + if (existing.split('\n').some(line => line.trim() === '.playwright-cli/')) + return; + const separator = existing && !existing.endsWith('\n') ? '\n' : ''; + await fs.promises.appendFile(gitIgnorePath, separator + '# Playwright CLI output (may contain credentials)\n.playwright-cli/\n'); + console.log('✅ Added `.playwright-cli/` to `.gitignore`.'); + } catch (error) { + console.log(`⚠️ Failed to update \`.gitignore\`: ${error instanceof Error ? error.message : error}`); + } +} + async function ensureConfiguredBrowserInstalled() { if (getAsBooleanFromENV('PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD')) return; diff --git a/tests/mcp/cli-misc.spec.ts b/tests/mcp/cli-misc.spec.ts index a3e8630d56634..c99f422704f1f 100644 --- a/tests/mcp/cli-misc.spec.ts +++ b/tests/mcp/cli-misc.spec.ts @@ -40,6 +40,23 @@ test('install workspace', async ({ cli }, testInfo) => { expect(fs.existsSync(playwrightDir)).toBe(true); }); +test('install adds .playwright-cli/ to .gitignore', async ({ cli }, testInfo) => { + const outsideGitRepo = await cli('install'); + expect(outsideGitRepo.output).not.toContain('.gitignore'); + expect(fs.existsSync(testInfo.outputPath('.gitignore'))).toBe(false); + + await fs.promises.mkdir(testInfo.outputPath('.git'), { recursive: true }); + await fs.promises.writeFile(testInfo.outputPath('.gitignore'), 'node_modules/'); + const insideGitRepo = await cli('install'); + expect(insideGitRepo.output).toContain('Added `.playwright-cli/` to `.gitignore`.'); + const expectedContent = 'node_modules/\n# Playwright CLI output (may contain credentials)\n.playwright-cli/\n'; + expect(await fs.promises.readFile(testInfo.outputPath('.gitignore'), 'utf8')).toBe(expectedContent); + + const secondRun = await cli('install'); + expect(secondRun.output).not.toContain('.gitignore'); + expect(await fs.promises.readFile(testInfo.outputPath('.gitignore'), 'utf8')).toBe(expectedContent); +}); + test('install workspace w/skills', async ({ cli }, testInfo) => { const { output } = await cli('install', '--skills'); expect(output).toContain(`Skill installed to \`.claude${path.sep}skills${path.sep}playwright-cli\`.`);