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
27 changes: 1 addition & 26 deletions src/core/projectManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
import { join, basename } from 'path';
import { ProjectState, KeepFile, DecryptKey, SyncState, CapyError, ERROR_CODES } from '../types/index';
import { ProjectState, KeepFile, SyncState, CapyError, ERROR_CODES } from '../types/index';

export class ProjectManager {
private projectRoot: string;
Expand All @@ -11,11 +11,9 @@ export class ProjectManager {

async detectProjectState(): Promise<ProjectState> {
const keepPath = this.getKeepPath();
const decryptPath = this.getDecryptPath();
const envPath = this.getEnvPath();

const hasKeepFile = existsSync(keepPath);
const hasDecryptKey = existsSync(decryptPath);
const hasEnvFile = existsSync(envPath);

let projectName: string | undefined;
Expand Down Expand Up @@ -45,7 +43,6 @@ export class ProjectManager {
return {
initialized: hasKeepFile,
hasKeepFile,
hasDecryptKey,
hasEnvFile,
projectName,
organizationId,
Expand All @@ -63,10 +60,6 @@ export class ProjectManager {
return join(this.projectRoot, '.capy');
}

getDecryptPath(): string {
return join(this.getCapyDir(), 'decrypt');
}

getActiveBranchPath(): string {
return join(this.getCapyDir(), 'branch');
}
Expand Down Expand Up @@ -153,24 +146,6 @@ export class ProjectManager {
}
}

readDecryptKey(): DecryptKey | null {
const decryptPath = this.getDecryptPath();
if (!existsSync(decryptPath)) {
return null;
}

try {
const content = readFileSync(decryptPath, 'utf-8');
return JSON.parse(content) as DecryptKey;
} catch (error) {
throw new CapyError(
'Failed to read .decrypt file',
ERROR_CODES.INVALID_FORMAT,
{ error, path: decryptPath }
);
}
}

readSyncState(): SyncState | null {
const syncStatePath = this.getSyncStatePath();
if (!existsSync(syncStatePath)) {
Expand Down
28 changes: 1 addition & 27 deletions src/files/fileManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { readFileSync, writeFileSync, existsSync, appendFileSync, chmodSync, mkdirSync, unlinkSync } from 'fs';
import { join, dirname } from 'path';
import { EnvVariable, KeepFile, DecryptKey, SyncState, CapyError, ERROR_CODES } from '../types/index';
import { EnvVariable, KeepFile, SyncState, CapyError, ERROR_CODES } from '../types/index';
import { parse as parseDotenv } from 'dotenv';
import { Encryptor } from '../crypto/encryptor';
import { deriveResourceId } from '../crypto/resourceId';
Expand Down Expand Up @@ -208,32 +208,6 @@ export class FileManager {
}
}

writeDecryptKey(decryptKey: DecryptKey): void {
const capyDir = join(this.projectRoot, '.capy');
this.ensureDirectoryExists(capyDir);

const decryptPath = join(capyDir, 'decrypt');
const backup = this.createBackup(decryptPath);

try {
const content = JSON.stringify(decryptKey, null, 2);
writeFileSync(decryptPath, content + '\n', { encoding: 'utf-8', mode: 0o600 });

if (backup) {
this.removeBackup(backup);
}
} catch (error) {
if (backup) {
this.restoreBackup(backup, decryptPath);
}
throw new CapyError(
'Failed to write decrypt file',
ERROR_CODES.PERMISSION_DENIED,
{ error, path: decryptPath }
);
}
}

writeSyncState(syncState: SyncState): void {
const capyDir = join(this.projectRoot, '.capy');
this.ensureDirectoryExists(capyDir);
Expand Down
22 changes: 0 additions & 22 deletions src/sync/syncEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
SyncResult,
KeepFile,
KeepVariableEntry,
DecryptKey,
SyncState,
} from '../types/index';

Expand Down Expand Up @@ -255,27 +254,6 @@ export class SyncEngine {
return updatedKeep;
}

createDecryptKey(
organizationId: string,
projectId: string,
userId: string,
decryptionKey: string,
variableNames: string[]
): DecryptKey {
const expiresAt = new Date();
expiresAt.setDate(expiresAt.getDate() + 30); // 30 days expiration

return {
version: '1.0',
org_id: organizationId,
project_id: projectId,
user_id: userId,
decryption_key: decryptionKey,
expires_at: expiresAt.toISOString(),
permissions: variableNames
};
}

formatSyncSummary(changeSet: ChangeSet): string {
const lines: string[] = [];

Expand Down
11 changes: 0 additions & 11 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,9 @@ export interface EnvVariable {
encrypted: boolean;
}

export interface DecryptKey {
version: string;
org_id: string;
project_id: string;
user_id: string;
decryption_key: string;
expires_at: string;
permissions: string[];
}

export interface ProjectState {
initialized: boolean;
hasKeepFile: boolean;
hasDecryptKey: boolean;
hasEnvFile: boolean;
projectName?: string;
organizationId?: string;
Expand Down
4 changes: 0 additions & 4 deletions tests/commands/branchKeepFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,15 +454,13 @@ describe('Direction detection — branch-aware keep_hash', () => {
getDefaultProjectName: mock(() => 'test-project'),
getEnvPath: mock(() => '.env'),
readKeepFile: mock(() => undefined),
readDecryptKey: mock(() => undefined),
readSyncState: mock(() => null),
readActiveBranch: mock(() => null),
writeActiveBranch: mock(() => undefined),
writeSyncStateUserId: mock(() => undefined),
};
mockFileManager = {
writeKeepFile: mock(() => undefined),
writeDecryptKey: mock(() => undefined),
writeSyncState: mock(() => undefined),
writeEncryptedEnvFile: mock(() => undefined),
readEnvFile: mock(() => ({})),
Expand Down Expand Up @@ -500,7 +498,6 @@ describe('Direction detection — branch-aware keep_hash', () => {
listProjects: mock(() => Promise.resolve([])),
};
mockSyncEngine = {
createDecryptKey: mock(() => 'mock-decrypt-key'),
compareEnvironments: mock(() => undefined),
formatSyncSummary: mock(() => undefined),
validateDecisions: mock(() => []),
Expand Down Expand Up @@ -541,7 +538,6 @@ describe('Direction detection — branch-aware keep_hash', () => {
const projectState = {
initialized: true,
hasKeepFile: true,
hasDecryptKey: true,
hasEnvFile: true,
projectName: 'test-project',
projectId: 'proj-123',
Expand Down
9 changes: 0 additions & 9 deletions tests/commands/capyCommand.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ describe('CapyCommand', () => {
getDefaultProjectName: mock(() => 'test-project'),
getEnvPath: mock(() => '.env'),
readKeepFile: mock(() => undefined),
readDecryptKey: mock(() => undefined),
readSyncState: mock(() => null),
readActiveBranch: mock(() => null),
writeActiveBranch: mock(() => undefined),
Expand All @@ -111,7 +110,6 @@ describe('CapyCommand', () => {

mockFileManager = {
writeKeepFile: mock(() => undefined),
writeDecryptKey: mock(() => undefined),
writeSyncState: mock(() => undefined),
writeEncryptedEnvFile: mock(() => undefined),
readEnvFile: mock(() => ({})),
Expand Down Expand Up @@ -152,7 +150,6 @@ describe('CapyCommand', () => {
} as any;

mockSyncEngine = {
createDecryptKey: mock(() => 'mock-decrypt-key'),
compareEnvironments: mock(() => undefined),
formatSyncSummary: mock(() => undefined),
validateDecisions: mock(() => []),
Expand Down Expand Up @@ -214,7 +211,6 @@ describe('CapyCommand', () => {
mockProjectManager.detectProjectState.mockResolvedValue({
initialized: false,
hasKeepFile: false,
hasDecryptKey: false,
hasEnvFile: false,
projectName: undefined,
projectId: undefined,
Expand All @@ -233,7 +229,6 @@ describe('CapyCommand', () => {
const projectState = {
initialized: true,
hasKeepFile: true,
hasDecryptKey: true,
hasEnvFile: true,
projectName: 'test-project',
projectId: 'proj-123',
Expand All @@ -253,7 +248,6 @@ describe('CapyCommand', () => {
mockProjectManager.detectProjectState.mockResolvedValue({
initialized: true,
hasKeepFile: true,
hasDecryptKey: false,
hasEnvFile: false,
projectName: 'existing-project',
projectId: 'proj-123',
Expand Down Expand Up @@ -736,7 +730,6 @@ describe('CapyCommand', () => {
const mockProjectState = {
initialized: true,
hasKeepFile: true,
hasDecryptKey: true,
hasEnvFile: true,
projectName: 'test-project',
projectId: 'proj-123',
Expand Down Expand Up @@ -834,7 +827,6 @@ describe('CapyCommand', () => {
const mockProjectState = {
initialized: true,
hasKeepFile: true,
hasDecryptKey: true,
hasEnvFile: true,
projectName: 'test-project',
projectId: 'proj-123',
Expand Down Expand Up @@ -1055,7 +1047,6 @@ describe('CapyCommand', () => {
const mockProjectState = {
initialized: true,
hasKeepFile: true,
hasDecryptKey: true,
hasEnvFile: true,
projectName: 'test-project',
projectId: 'proj-123',
Expand Down
9 changes: 0 additions & 9 deletions tests/core/projectManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ describe('ProjectManager', () => {
test('should detect uninitialized project when no keep.lock file exists', async () => {
mockExistsSync.mockImplementation((path) => {
if (path === join(testRoot, 'keep.lock')) return false;
if (path === join(testRoot, '.capy/decrypt')) return false;
if (path === join(testRoot, '.env')) return true;
return false;
});
Expand All @@ -39,7 +38,6 @@ describe('ProjectManager', () => {
expect(state).toMatchObject({
initialized: false,
hasKeepFile: false,
hasDecryptKey: false,
hasEnvFile: true,
activeBranch: 'development',
});
Expand All @@ -56,7 +54,6 @@ describe('ProjectManager', () => {

mockExistsSync.mockImplementation((path) => {
if (path === join(testRoot, 'keep.lock')) return true;
if (path === join(testRoot, '.capy/decrypt')) return true;
if (path === join(testRoot, '.env')) return true;
return false;
});
Expand All @@ -68,7 +65,6 @@ describe('ProjectManager', () => {
expect(state).toMatchObject({
initialized: true,
hasKeepFile: true,
hasDecryptKey: true,
hasEnvFile: true,
projectName: 'test-project',
organizationId: 'org_123',
Expand Down Expand Up @@ -270,11 +266,6 @@ describe('ProjectManager', () => {
expect(path).toBe(join(testRoot, 'keep.lock'));
});

test('should return correct decrypt path', () => {
const path = (projectManager as any).getDecryptPath();
expect(path).toBe(join(testRoot, '.capy/decrypt'));
});

test('should return correct env path', () => {
const path = (projectManager as any).getEnvPath();
expect(path).toBe(join(testRoot, '.env'));
Expand Down
Loading
Loading