Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,18 @@ jobs:
- name: Audit build and test tooling
continue-on-error: true
run: pnpm audit --audit-level high

# The docs site keeps its own lockfile and is not a workspace member, so
# neither audit above can see it. Without this step a website advisory is
# invisible — which is how two of them sat open long enough to need a
# manual override.
#
# Same blocking rule as the published-dependency audit: advisory on pull
# requests, blocking on the weekly schedule and on pushes to main. Green
# here has to mean the site is clean, or the step just relocates the blind
# spot into a passing log. `!cancelled()` because the two audits above can
# fail hard, and a root advisory must not silently skip this one.
- name: Audit documentation site
if: ${{ !cancelled() }}
continue-on-error: ${{ github.event_name == 'pull_request' }}
run: pnpm audit --audit-level high --dir website
3 changes: 1 addition & 2 deletions test/commands/config-profile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,7 @@ describe('config profile interactive flow', () => {
beforeEach(() => {
vi.resetModules();

tempDir = path.join(os.tmpdir(), `openspec-config-profile-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
fs.mkdirSync(tempDir, { recursive: true });
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openspec-config-profile-test-'));

originalEnv = { ...process.env };
originalCwd = process.cwd();
Expand Down
6 changes: 2 additions & 4 deletions test/commands/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ describe('config command integration', () => {

beforeEach(() => {
// Create unique temp directory for each test
tempDir = path.join(os.tmpdir(), `openspec-config-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
fs.mkdirSync(tempDir, { recursive: true });
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openspec-config-test-'));

// Save original env and set XDG_CONFIG_HOME
originalEnv = { ...process.env };
Expand Down Expand Up @@ -245,8 +244,7 @@ describe('config profile command', () => {
let originalEnv: NodeJS.ProcessEnv;

beforeEach(() => {
tempDir = path.join(os.tmpdir(), `openspec-profile-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
fs.mkdirSync(tempDir, { recursive: true });
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openspec-profile-test-'));
originalEnv = { ...process.env };
process.env.XDG_CONFIG_HOME = tempDir;
});
Expand Down
6 changes: 1 addition & 5 deletions test/commands/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@ describe('schema command', () => {

beforeEach(() => {
// Create unique temp directory for each test
tempDir = path.join(
os.tmpdir(),
`openspec-schema-test-${Date.now()}-${Math.random().toString(36).slice(2)}`
);
fs.mkdirSync(tempDir, { recursive: true });
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openspec-schema-test-'));

// Create openspec directory structure
fs.mkdirSync(path.join(tempDir, 'openspec', 'schemas'), { recursive: true });
Expand Down
3 changes: 1 addition & 2 deletions test/core/artifact-graph/outputs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ describe('artifact-graph/outputs', () => {
const canonical = (targetPath: string): string => FileSystemUtils.canonicalizeExistingPath(targetPath);

beforeEach(() => {
tempDir = path.join(os.tmpdir(), `openspec-outputs-test-${Date.now()}`);
fs.mkdirSync(tempDir, { recursive: true });
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openspec-outputs-test-'));
});

afterEach(() => {
Expand Down
3 changes: 1 addition & 2 deletions test/core/artifact-graph/resolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ describe('artifact-graph/resolver', () => {
let originalEnv: NodeJS.ProcessEnv;

beforeEach(() => {
tempDir = path.join(os.tmpdir(), `openspec-resolver-test-${Date.now()}`);
fs.mkdirSync(tempDir, { recursive: true });
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openspec-resolver-test-'));
originalEnv = { ...process.env };
});

Expand Down
3 changes: 1 addition & 2 deletions test/core/artifact-graph/state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ describe('artifact-graph/state', () => {
});

beforeEach(() => {
tempDir = path.join(os.tmpdir(), `openspec-state-test-${Date.now()}`);
fs.mkdirSync(tempDir, { recursive: true });
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openspec-state-test-'));
});

afterEach(() => {
Expand Down
4 changes: 1 addition & 3 deletions test/core/available-tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { randomUUID } from 'crypto';
import { getAvailableTools } from '../../src/core/available-tools.js';

describe('available-tools', () => {
let testDir: string;

beforeEach(async () => {
testDir = path.join(os.tmpdir(), `openspec-test-${randomUUID()}`);
await fs.mkdir(testDir, { recursive: true });
testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-test-'));
});

afterEach(async () => {
Expand Down
2 changes: 1 addition & 1 deletion test/core/commands/change-command.list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('ChangeCommand.list', () => {
beforeAll(async () => {
cmd = new ChangeCommand();
originalCwd = process.cwd();
tempRoot = path.join(os.tmpdir(), `openspec-change-command-list-${Date.now()}`);
tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-change-command-list-'));
const changeDir = path.join(tempRoot, 'openspec', 'changes', 'demo');
await fs.mkdir(changeDir, { recursive: true });
const proposal = `# Change: Demo\n\n## Why\nTest list.\n\n## What Changes\n- **auth:** Add requirement`;
Expand Down
2 changes: 1 addition & 1 deletion test/core/commands/change-command.show-validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('ChangeCommand.show/validate', () => {
beforeAll(async () => {
cmd = new ChangeCommand();
originalCwd = process.cwd();
tempRoot = path.join(os.tmpdir(), `openspec-change-command-${Date.now()}`);
tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-change-command-'));
const changesDir = path.join(tempRoot, 'openspec', 'changes', 'sample-change');
await fs.mkdir(changesDir, { recursive: true });
const proposal = `# Change: Sample Change\n\n## Why\nConsistency in tests.\n\n## What Changes\n- **auth:** Add requirement`;
Expand Down
4 changes: 1 addition & 3 deletions test/core/completions/completion-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { randomUUID } from 'crypto';
import { CompletionProvider } from '../../../src/core/completions/completion-provider.js';

describe('CompletionProvider', () => {
let testDir: string;
let provider: CompletionProvider;

beforeEach(async () => {
testDir = path.join(os.tmpdir(), `openspec-test-${randomUUID()}`);
await fs.mkdir(testDir, { recursive: true });
testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-test-'));
provider = new CompletionProvider(2000, testDir);
});

Expand Down
7 changes: 2 additions & 5 deletions test/core/completions/installers/bash-installer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { randomUUID } from 'crypto';
import { BashInstaller } from '../../../../src/core/completions/installers/bash-installer.js';

describe('BashInstaller', () => {
Expand All @@ -11,8 +10,7 @@ describe('BashInstaller', () => {

beforeEach(async () => {
// Create a temporary home directory for testing
testHomeDir = path.join(os.tmpdir(), `openspec-bash-test-${randomUUID()}`);
await fs.mkdir(testHomeDir, { recursive: true });
testHomeDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-bash-test-'));
installer = new BashInstaller(testHomeDir);
});

Expand Down Expand Up @@ -208,8 +206,7 @@ describe('BashInstaller', () => {

it('should handle paths with spaces in .bashrc config', async () => {
// Create a test home directory with spaces
const testHomeDirWithSpaces = path.join(os.tmpdir(), `openspec bash test ${randomUUID()}`);
await fs.mkdir(testHomeDirWithSpaces, { recursive: true });
const testHomeDirWithSpaces = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec bash test '));
const installerWithSpaces = new BashInstaller(testHomeDirWithSpaces);

try {
Expand Down
7 changes: 2 additions & 5 deletions test/core/completions/installers/fish-installer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ import { FishInstaller } from '../../../../src/core/completions/installers/fish-
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { randomUUID } from 'crypto';

describe('FishInstaller', () => {
let testHomeDir: string;
let installer: FishInstaller;

beforeEach(async () => {
testHomeDir = path.join(os.tmpdir(), `openspec-fish-test-${randomUUID()}`);
await fs.mkdir(testHomeDir, { recursive: true });
testHomeDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-fish-test-'));
installer = new FishInstaller(testHomeDir);
});

Expand Down Expand Up @@ -179,8 +177,7 @@ complete -c openspec -a 'validate' -d 'Validate specs'
});

it('should handle installation with paths containing spaces', async () => {
const spacedHomeDir = path.join(os.tmpdir(), `openspec fish test ${randomUUID()}`);
await fs.mkdir(spacedHomeDir, { recursive: true });
const spacedHomeDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec fish test '));

const spacedInstaller = new FishInstaller(spacedHomeDir);
const result = await spacedInstaller.install(mockCompletionScript);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { PowerShellInstaller } from '../../../../src/core/completions/installers
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { randomUUID } from 'crypto';

describe('PowerShellInstaller', () => {
let testHomeDir: string;
Expand All @@ -20,8 +19,7 @@ describe('PowerShellInstaller', () => {
};

beforeEach(async () => {
testHomeDir = path.join(os.tmpdir(), `openspec-powershell-test-${randomUUID()}`);
await fs.mkdir(testHomeDir, { recursive: true });
testHomeDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-powershell-test-'));
installer = new PowerShellInstaller(testHomeDir);
originalPlatform = process.platform;
originalEnv = { ...process.env };
Expand Down Expand Up @@ -519,8 +517,7 @@ Register-ArgumentCompleter -CommandName openspec -ScriptBlock $openspecCompleter
});

it('should handle installation with paths containing spaces', async () => {
const spacedHomeDir = path.join(os.tmpdir(), `openspec powershell test ${randomUUID()}`);
await fs.mkdir(spacedHomeDir, { recursive: true });
const spacedHomeDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec powershell test '));

const spacedInstaller = new PowerShellInstaller(spacedHomeDir);
const result = await spacedInstaller.install(mockCompletionScript);
Expand Down
7 changes: 2 additions & 5 deletions test/core/completions/installers/zsh-installer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { randomUUID } from 'crypto';
import { ZshInstaller } from '../../../../src/core/completions/installers/zsh-installer.js';

describe('ZshInstaller', () => {
Expand All @@ -17,8 +16,7 @@ describe('ZshInstaller', () => {
delete process.env.ZSH;

// Create a temporary home directory for testing
testHomeDir = path.join(os.tmpdir(), `openspec-zsh-test-${randomUUID()}`);
await fs.mkdir(testHomeDir, { recursive: true });
testHomeDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-zsh-test-'));
installer = new ZshInstaller(testHomeDir);
});

Expand Down Expand Up @@ -271,8 +269,7 @@ describe('ZshInstaller', () => {

it('should handle paths with spaces in .zshrc config', async () => {
// Create a test home directory with spaces
const testHomeDirWithSpaces = path.join(os.tmpdir(), `openspec zsh test ${randomUUID()}`);
await fs.mkdir(testHomeDirWithSpaces, { recursive: true });
const testHomeDirWithSpaces = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec zsh test '));
const installerWithSpaces = new ZshInstaller(testHomeDirWithSpaces);

try {
Expand Down
3 changes: 1 addition & 2 deletions test/core/global-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ describe('global-config', () => {

beforeEach(() => {
// Create temp directory for tests
tempDir = path.join(os.tmpdir(), `openspec-global-config-test-${Date.now()}`);
fs.mkdirSync(tempDir, { recursive: true });
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openspec-global-config-test-'));

// Save original env
originalEnv = { ...process.env };
Expand Down
13 changes: 4 additions & 9 deletions test/core/init.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { promises as fs } from 'fs';
import { randomUUID } from 'crypto';
import path from 'path';
import os from 'os';
import { InitCommand } from '../../src/core/init.js';
Expand Down Expand Up @@ -30,12 +29,10 @@ describe('InitCommand', () => {
let originalEnv: NodeJS.ProcessEnv;

beforeEach(async () => {
testDir = path.join(os.tmpdir(), `openspec-init-test-${randomUUID()}`);
await fs.mkdir(testDir, { recursive: true });
testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-init-test-'));
originalEnv = { ...process.env };
// Use a temp dir for global config to avoid reading real config
configTempDir = path.join(os.tmpdir(), `openspec-config-init-${randomUUID()}`);
await fs.mkdir(configTempDir, { recursive: true });
configTempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-config-init-'));
process.env.XDG_CONFIG_HOME = configTempDir;
process.env.CODEX_HOME = path.join(testDir, 'codex-home');

Expand Down Expand Up @@ -646,12 +643,10 @@ describe('InitCommand - profile and detection features', () => {
let originalEnv: NodeJS.ProcessEnv;

beforeEach(async () => {
testDir = path.join(os.tmpdir(), `openspec-init-profile-test-${randomUUID()}`);
await fs.mkdir(testDir, { recursive: true });
testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-init-profile-test-'));
originalEnv = { ...process.env };
// Use a temp dir for global config to avoid polluting real config
configTempDir = path.join(os.tmpdir(), `openspec-config-test-${randomUUID()}`);
await fs.mkdir(configTempDir, { recursive: true });
configTempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-config-test-'));
process.env.XDG_CONFIG_HOME = configTempDir;
process.env.CODEX_HOME = path.join(testDir, 'codex-home');
vi.spyOn(console, 'log').mockImplementation(() => {});
Expand Down
4 changes: 1 addition & 3 deletions test/core/legacy-cleanup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { randomUUID } from 'crypto';
import {
detectLegacyArtifacts,
detectLegacyConfigFiles,
Expand Down Expand Up @@ -32,8 +31,7 @@ describe('legacy-cleanup', () => {

beforeEach(async () => {
originalEnv = { ...process.env };
testDir = path.join(os.tmpdir(), `openspec-legacy-test-${randomUUID()}`);
await fs.mkdir(testDir, { recursive: true });
testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-legacy-test-'));
process.env.CODEX_HOME = path.join(testDir, 'codex-home');
// Create openspec directory structure
await fs.mkdir(path.join(testDir, 'openspec'), { recursive: true });
Expand Down
3 changes: 1 addition & 2 deletions test/core/list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ describe('ListCommand', () => {

beforeEach(async () => {
// Create temp directory
tempDir = path.join(os.tmpdir(), `openspec-list-test-${Date.now()}`);
await fs.mkdir(tempDir, { recursive: true });
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-list-test-'));

// Mock console.log to capture output
originalLog = console.log;
Expand Down
7 changes: 2 additions & 5 deletions test/core/migration.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import path from 'path';
import os from 'os';
import { randomUUID } from 'crypto';
import fs from 'node:fs';
import { promises as fsp } from 'node:fs';
import { AI_TOOLS, type AIToolOption } from '../../src/core/config.js';
Expand Down Expand Up @@ -65,10 +64,8 @@ describe('migration', () => {
let originalEnv: NodeJS.ProcessEnv;

beforeEach(async () => {
projectDir = path.join(os.tmpdir(), `openspec-migration-project-${randomUUID()}`);
configHome = path.join(os.tmpdir(), `openspec-migration-config-${randomUUID()}`);
await fsp.mkdir(projectDir, { recursive: true });
await fsp.mkdir(configHome, { recursive: true });
projectDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'openspec-migration-project-'));
configHome = await fsp.mkdtemp(path.join(os.tmpdir(), 'openspec-migration-config-'));
originalEnv = { ...process.env };
process.env.XDG_CONFIG_HOME = configHome;
});
Expand Down
2 changes: 1 addition & 1 deletion test/core/profile-sync-drift.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('profile sync drift detection', () => {
let tempDir: string;

beforeEach(() => {
tempDir = path.join(os.tmpdir(), `openspec-profile-sync-drift-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openspec-profile-sync-drift-test-'));
fs.mkdirSync(path.join(tempDir, 'openspec'), { recursive: true });
});

Expand Down
4 changes: 1 addition & 3 deletions test/core/shared/tool-detection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import { randomUUID } from 'crypto';
import {
SKILL_NAMES,
getToolsWithSkillsDir,
Expand All @@ -18,8 +17,7 @@ describe('tool-detection', () => {
let testDir: string;

beforeEach(async () => {
testDir = path.join(os.tmpdir(), `openspec-test-${randomUUID()}`);
await fs.mkdir(testDir, { recursive: true });
testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-test-'));
});

afterEach(async () => {
Expand Down
4 changes: 1 addition & 3 deletions test/core/update.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type { GlobalConfig } from '../../src/core/global-config.js';
import path from 'path';
import fs from 'fs/promises';
import os from 'os';
import { randomUUID } from 'crypto';

// Shared mutable mock config state
const mockState = {
Expand Down Expand Up @@ -46,8 +45,7 @@ describe('UpdateCommand', () => {
beforeEach(async () => {
originalEnv = { ...process.env };
// Create a temporary test directory
testDir = path.join(os.tmpdir(), `openspec-test-${randomUUID()}`);
await fs.mkdir(testDir, { recursive: true });
testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-test-'));
process.env.CODEX_HOME = path.join(testDir, 'codex-home');

// Create openspec directory
Expand Down
Loading
Loading