envmatic 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +567 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +203 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/add.d.ts +11 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +77 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/delete.d.ts +6 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +78 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/edit.d.ts +13 -0
- package/dist/commands/edit.d.ts.map +1 -0
- package/dist/commands/edit.js +364 -0
- package/dist/commands/edit.js.map +1 -0
- package/dist/commands/import.d.ts +11 -0
- package/dist/commands/import.d.ts.map +1 -0
- package/dist/commands/import.js +103 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +237 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/link.d.ts +16 -0
- package/dist/commands/link.d.ts.map +1 -0
- package/dist/commands/link.js +157 -0
- package/dist/commands/link.js.map +1 -0
- package/dist/commands/list.d.ts +9 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +73 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/lock.d.ts +16 -0
- package/dist/commands/lock.d.ts.map +1 -0
- package/dist/commands/lock.js +245 -0
- package/dist/commands/lock.js.map +1 -0
- package/dist/commands/rotate.d.ts +15 -0
- package/dist/commands/rotate.d.ts.map +1 -0
- package/dist/commands/rotate.js +406 -0
- package/dist/commands/rotate.js.map +1 -0
- package/dist/commands/show.d.ts +9 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +72 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/sync.d.ts +13 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +174 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/use.d.ts +19 -0
- package/dist/commands/use.d.ts.map +1 -0
- package/dist/commands/use.js +238 -0
- package/dist/commands/use.js.map +1 -0
- package/dist/constants.d.ts +20 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +47 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/services/config.d.ts +64 -0
- package/dist/services/config.d.ts.map +1 -0
- package/dist/services/config.js +133 -0
- package/dist/services/config.js.map +1 -0
- package/dist/services/encryption.d.ts +30 -0
- package/dist/services/encryption.d.ts.map +1 -0
- package/dist/services/encryption.js +146 -0
- package/dist/services/encryption.js.map +1 -0
- package/dist/services/envfile.d.ts +76 -0
- package/dist/services/envfile.d.ts.map +1 -0
- package/dist/services/envfile.js +247 -0
- package/dist/services/envfile.js.map +1 -0
- package/dist/services/git.d.ts +60 -0
- package/dist/services/git.d.ts.map +1 -0
- package/dist/services/git.js +239 -0
- package/dist/services/git.js.map +1 -0
- package/dist/services/linker.d.ts +46 -0
- package/dist/services/linker.d.ts.map +1 -0
- package/dist/services/linker.js +222 -0
- package/dist/services/linker.js.map +1 -0
- package/dist/services/protection.d.ts +32 -0
- package/dist/services/protection.d.ts.map +1 -0
- package/dist/services/protection.js +190 -0
- package/dist/services/protection.js.map +1 -0
- package/dist/types/index.d.ts +73 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/display.d.ts +74 -0
- package/dist/utils/display.d.ts.map +1 -0
- package/dist/utils/display.js +138 -0
- package/dist/utils/display.js.map +1 -0
- package/dist/utils/editor.d.ts +22 -0
- package/dist/utils/editor.d.ts.map +1 -0
- package/dist/utils/editor.js +159 -0
- package/dist/utils/editor.js.map +1 -0
- package/dist/utils/prompts.d.ts +41 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +222 -0
- package/dist/utils/prompts.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Env File Service
|
|
3
|
+
* Manages .env files in the vault
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'fs-extra';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { parse as parseDotenv } from 'dotenv';
|
|
8
|
+
import { VAULT_PATH, ENCRYPTED_EXT } from '../constants.js';
|
|
9
|
+
import { encrypt, decrypt } from './encryption.js';
|
|
10
|
+
import { getManifest, saveManifest, commitChanges } from './git.js';
|
|
11
|
+
import { getConfig } from './config.js';
|
|
12
|
+
/**
|
|
13
|
+
* Generate file ID from project and environment
|
|
14
|
+
*/
|
|
15
|
+
export function generateFileId(project, environment, name = '.env') {
|
|
16
|
+
return `${project}/${environment}/${name}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get the full path for an env file in the vault
|
|
20
|
+
*/
|
|
21
|
+
export function getEnvFilePath(fileId, encrypted = false) {
|
|
22
|
+
const ext = encrypted ? ENCRYPTED_EXT : '';
|
|
23
|
+
return path.join(VAULT_PATH, fileId + ext);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse .env content to key-value pairs
|
|
27
|
+
*/
|
|
28
|
+
export function parseEnvContent(content) {
|
|
29
|
+
// Use dotenv parser
|
|
30
|
+
const parsed = parseDotenv(Buffer.from(content));
|
|
31
|
+
return parsed;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Serialize key-value pairs to .env format
|
|
35
|
+
*/
|
|
36
|
+
export function serializeEnvContent(variables) {
|
|
37
|
+
const lines = [];
|
|
38
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
39
|
+
// Quote values that contain special characters
|
|
40
|
+
const needsQuotes = value.includes(' ') ||
|
|
41
|
+
value.includes('#') ||
|
|
42
|
+
value.includes('\n') ||
|
|
43
|
+
value.includes('"') ||
|
|
44
|
+
value.includes("'");
|
|
45
|
+
if (needsQuotes) {
|
|
46
|
+
// Escape double quotes and use double quotes
|
|
47
|
+
const escaped = value.replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
|
48
|
+
lines.push(`${key}="${escaped}"`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
lines.push(`${key}=${value}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return lines.join('\n') + '\n';
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create a new env file
|
|
58
|
+
*/
|
|
59
|
+
export async function createEnvFile(project, environment, variables, options = {}) {
|
|
60
|
+
const config = await getConfig();
|
|
61
|
+
if (!config) {
|
|
62
|
+
throw new Error('Envmatic not configured. Run `envmatic init` first.');
|
|
63
|
+
}
|
|
64
|
+
const name = options.name || '.env';
|
|
65
|
+
const fileId = generateFileId(project, environment, name);
|
|
66
|
+
const encrypted = config.encryptionEnabled && !!options.encryptionOptions;
|
|
67
|
+
const filePath = getEnvFilePath(fileId, encrypted);
|
|
68
|
+
// Ensure directory exists
|
|
69
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
70
|
+
// Serialize content
|
|
71
|
+
let content = serializeEnvContent(variables);
|
|
72
|
+
// Encrypt if needed
|
|
73
|
+
if (encrypted && options.encryptionOptions) {
|
|
74
|
+
content = await encrypt(content, options.encryptionOptions);
|
|
75
|
+
}
|
|
76
|
+
// Write file
|
|
77
|
+
await fs.writeFile(filePath, content);
|
|
78
|
+
// Create metadata
|
|
79
|
+
const now = new Date().toISOString();
|
|
80
|
+
const envFile = {
|
|
81
|
+
id: fileId,
|
|
82
|
+
name,
|
|
83
|
+
project,
|
|
84
|
+
environment,
|
|
85
|
+
description: options.description,
|
|
86
|
+
createdAt: now,
|
|
87
|
+
updatedAt: now,
|
|
88
|
+
encrypted,
|
|
89
|
+
immutable: options.immutable ?? config.immutableByDefault,
|
|
90
|
+
};
|
|
91
|
+
// Update manifest
|
|
92
|
+
const manifest = await getManifest();
|
|
93
|
+
// Add project if new
|
|
94
|
+
if (!manifest.projects.includes(project)) {
|
|
95
|
+
manifest.projects.push(project);
|
|
96
|
+
}
|
|
97
|
+
// Add or update file entry
|
|
98
|
+
const existingIndex = manifest.files.findIndex(f => f.id === fileId);
|
|
99
|
+
if (existingIndex >= 0) {
|
|
100
|
+
manifest.files[existingIndex] = envFile;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
manifest.files.push(envFile);
|
|
104
|
+
}
|
|
105
|
+
await saveManifest(manifest);
|
|
106
|
+
// Commit changes
|
|
107
|
+
await commitChanges(`Add ${project}/${environment}/${name}`);
|
|
108
|
+
return envFile;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Read an env file
|
|
112
|
+
*/
|
|
113
|
+
export async function readEnvFile(fileId, encryptionOptions) {
|
|
114
|
+
const manifest = await getManifest();
|
|
115
|
+
const metadata = manifest.files.find(f => f.id === fileId);
|
|
116
|
+
if (!metadata) {
|
|
117
|
+
throw new Error(`Env file not found: ${fileId}`);
|
|
118
|
+
}
|
|
119
|
+
const filePath = getEnvFilePath(fileId, metadata.encrypted);
|
|
120
|
+
if (!(await fs.pathExists(filePath))) {
|
|
121
|
+
throw new Error(`Env file not found on disk: ${filePath}`);
|
|
122
|
+
}
|
|
123
|
+
let content = await fs.readFile(filePath, 'utf-8');
|
|
124
|
+
// Decrypt if needed
|
|
125
|
+
if (metadata.encrypted) {
|
|
126
|
+
if (!encryptionOptions) {
|
|
127
|
+
throw new Error('Encryption options required to read encrypted file');
|
|
128
|
+
}
|
|
129
|
+
content = await decrypt(content, encryptionOptions);
|
|
130
|
+
}
|
|
131
|
+
const variables = parseEnvContent(content);
|
|
132
|
+
return {
|
|
133
|
+
metadata,
|
|
134
|
+
variables,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Update an env file
|
|
139
|
+
*/
|
|
140
|
+
export async function updateEnvFile(fileId, variables, encryptionOptions) {
|
|
141
|
+
const manifest = await getManifest();
|
|
142
|
+
const metadataIndex = manifest.files.findIndex(f => f.id === fileId);
|
|
143
|
+
if (metadataIndex < 0) {
|
|
144
|
+
throw new Error(`Env file not found: ${fileId}`);
|
|
145
|
+
}
|
|
146
|
+
const metadata = manifest.files[metadataIndex];
|
|
147
|
+
const filePath = getEnvFilePath(fileId, metadata.encrypted);
|
|
148
|
+
// Serialize content
|
|
149
|
+
let content = serializeEnvContent(variables);
|
|
150
|
+
// Encrypt if needed
|
|
151
|
+
if (metadata.encrypted) {
|
|
152
|
+
if (!encryptionOptions) {
|
|
153
|
+
throw new Error('Encryption options required to update encrypted file');
|
|
154
|
+
}
|
|
155
|
+
content = await encrypt(content, encryptionOptions);
|
|
156
|
+
}
|
|
157
|
+
// Write file
|
|
158
|
+
await fs.writeFile(filePath, content);
|
|
159
|
+
// Update metadata
|
|
160
|
+
metadata.updatedAt = new Date().toISOString();
|
|
161
|
+
manifest.files[metadataIndex] = metadata;
|
|
162
|
+
await saveManifest(manifest);
|
|
163
|
+
await commitChanges(`Update ${metadata.project}/${metadata.environment}/${metadata.name}`);
|
|
164
|
+
return metadata;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Delete an env file
|
|
168
|
+
*/
|
|
169
|
+
export async function deleteEnvFile(fileId) {
|
|
170
|
+
const manifest = await getManifest();
|
|
171
|
+
const metadata = manifest.files.find(f => f.id === fileId);
|
|
172
|
+
if (!metadata) {
|
|
173
|
+
throw new Error(`Env file not found: ${fileId}`);
|
|
174
|
+
}
|
|
175
|
+
const filePath = getEnvFilePath(fileId, metadata.encrypted);
|
|
176
|
+
// Remove file
|
|
177
|
+
if (await fs.pathExists(filePath)) {
|
|
178
|
+
await fs.remove(filePath);
|
|
179
|
+
}
|
|
180
|
+
// Update manifest
|
|
181
|
+
manifest.files = manifest.files.filter(f => f.id !== fileId);
|
|
182
|
+
// Remove project if no more files
|
|
183
|
+
const projectFiles = manifest.files.filter(f => f.project === metadata.project);
|
|
184
|
+
if (projectFiles.length === 0) {
|
|
185
|
+
manifest.projects = manifest.projects.filter(p => p !== metadata.project);
|
|
186
|
+
}
|
|
187
|
+
await saveManifest(manifest);
|
|
188
|
+
await commitChanges(`Delete ${metadata.project}/${metadata.environment}/${metadata.name}`);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* List all env files
|
|
192
|
+
*/
|
|
193
|
+
export async function listEnvFiles(project) {
|
|
194
|
+
const manifest = await getManifest();
|
|
195
|
+
if (project) {
|
|
196
|
+
return manifest.files.filter(f => f.project === project);
|
|
197
|
+
}
|
|
198
|
+
return manifest.files;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* List all projects
|
|
202
|
+
*/
|
|
203
|
+
export async function listProjects() {
|
|
204
|
+
const manifest = await getManifest();
|
|
205
|
+
return manifest.projects;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Import an existing .env file into the vault
|
|
209
|
+
*/
|
|
210
|
+
export async function importEnvFile(sourcePath, project, environment, options = {}) {
|
|
211
|
+
const content = await fs.readFile(sourcePath, 'utf-8');
|
|
212
|
+
const variables = parseEnvContent(content);
|
|
213
|
+
return createEnvFile(project, environment, variables, options);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Export an env file to a target path
|
|
217
|
+
*/
|
|
218
|
+
export async function exportEnvFile(fileId, targetPath, encryptionOptions) {
|
|
219
|
+
const { variables } = await readEnvFile(fileId, encryptionOptions);
|
|
220
|
+
const content = serializeEnvContent(variables);
|
|
221
|
+
await fs.ensureDir(path.dirname(targetPath));
|
|
222
|
+
await fs.writeFile(targetPath, content);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Add or update a single variable in an env file
|
|
226
|
+
*/
|
|
227
|
+
export async function setVariable(fileId, key, value, encryptionOptions) {
|
|
228
|
+
const { variables } = await readEnvFile(fileId, encryptionOptions);
|
|
229
|
+
variables[key] = value;
|
|
230
|
+
await updateEnvFile(fileId, variables, encryptionOptions);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Remove a variable from an env file
|
|
234
|
+
*/
|
|
235
|
+
export async function removeVariable(fileId, key, encryptionOptions) {
|
|
236
|
+
const { variables } = await readEnvFile(fileId, encryptionOptions);
|
|
237
|
+
delete variables[key];
|
|
238
|
+
await updateEnvFile(fileId, variables, encryptionOptions);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Get a single variable value
|
|
242
|
+
*/
|
|
243
|
+
export async function getVariable(fileId, key, encryptionOptions) {
|
|
244
|
+
const { variables } = await readEnvFile(fileId, encryptionOptions);
|
|
245
|
+
return variables[key];
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=envfile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envfile.js","sourceRoot":"","sources":["../../src/services/envfile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Service
|
|
3
|
+
* Handles all Git operations for the vault repository
|
|
4
|
+
*/
|
|
5
|
+
import type { EnvmaticManifest } from '../types/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* Check if vault is initialized (git repo exists)
|
|
8
|
+
*/
|
|
9
|
+
export declare function isVaultInitialized(): Promise<boolean>;
|
|
10
|
+
/**
|
|
11
|
+
* Clone the remote repository to vault path
|
|
12
|
+
*/
|
|
13
|
+
export declare function cloneRepository(repoUrl: string, branch?: string): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Initialize a new git repository in vault (for first-time setup with empty repo)
|
|
16
|
+
*/
|
|
17
|
+
export declare function initRepository(repoUrl: string, branch?: string): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Pull latest changes from remote
|
|
20
|
+
*/
|
|
21
|
+
export declare function pull(): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Push changes to remote (always sets upstream to handle fresh repos)
|
|
24
|
+
*/
|
|
25
|
+
export declare function push(): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Stage and commit changes
|
|
28
|
+
*/
|
|
29
|
+
export declare function commitChanges(message: string): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Sync: commit changes, pull, then push
|
|
32
|
+
*/
|
|
33
|
+
export declare function sync(): Promise<{
|
|
34
|
+
pulled: boolean;
|
|
35
|
+
pushed: boolean;
|
|
36
|
+
committed: boolean;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Get current status
|
|
40
|
+
*/
|
|
41
|
+
export declare function getStatus(): Promise<{
|
|
42
|
+
branch: string;
|
|
43
|
+
ahead: number;
|
|
44
|
+
behind: number;
|
|
45
|
+
modified: number;
|
|
46
|
+
staged: number;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* Check if remote is accessible
|
|
50
|
+
*/
|
|
51
|
+
export declare function checkRemoteAccess(repoUrl: string): Promise<boolean>;
|
|
52
|
+
/**
|
|
53
|
+
* Get the manifest from the vault
|
|
54
|
+
*/
|
|
55
|
+
export declare function getManifest(): Promise<EnvmaticManifest>;
|
|
56
|
+
/**
|
|
57
|
+
* Save the manifest to the vault
|
|
58
|
+
*/
|
|
59
|
+
export declare function saveManifest(manifest: EnvmaticManifest): Promise<void>;
|
|
60
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/services/git.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAoB1D;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO3D;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBrG;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmDpG;AAED;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAG1C;AAED;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAiB1C;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWlE;AAED;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CA4C9F;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAWD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQzE;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAa7D;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5E"}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Service
|
|
3
|
+
* Handles all Git operations for the vault repository
|
|
4
|
+
*/
|
|
5
|
+
import { simpleGit } from 'simple-git';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { VAULT_PATH, DEFAULT_BRANCH, MANIFEST_FILE } from '../constants.js';
|
|
9
|
+
let gitInstance = null;
|
|
10
|
+
/**
|
|
11
|
+
* Get or create git instance for vault
|
|
12
|
+
*/
|
|
13
|
+
function getGit() {
|
|
14
|
+
if (!gitInstance) {
|
|
15
|
+
const options = {
|
|
16
|
+
baseDir: VAULT_PATH,
|
|
17
|
+
binary: 'git',
|
|
18
|
+
maxConcurrentProcesses: 1,
|
|
19
|
+
trimmed: false,
|
|
20
|
+
};
|
|
21
|
+
gitInstance = simpleGit(options);
|
|
22
|
+
}
|
|
23
|
+
return gitInstance;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check if vault is initialized (git repo exists)
|
|
27
|
+
*/
|
|
28
|
+
export async function isVaultInitialized() {
|
|
29
|
+
try {
|
|
30
|
+
const gitDir = path.join(VAULT_PATH, '.git');
|
|
31
|
+
return await fs.pathExists(gitDir);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Clone the remote repository to vault path
|
|
39
|
+
*/
|
|
40
|
+
export async function cloneRepository(repoUrl, branch = DEFAULT_BRANCH) {
|
|
41
|
+
await fs.ensureDir(VAULT_PATH);
|
|
42
|
+
// Check if directory is empty
|
|
43
|
+
const files = await fs.readdir(VAULT_PATH);
|
|
44
|
+
const nonHiddenFiles = files.filter(f => !f.startsWith('.'));
|
|
45
|
+
if (nonHiddenFiles.length > 0 || files.includes('.git')) {
|
|
46
|
+
throw new Error('Vault directory is not empty. Please remove existing files first.');
|
|
47
|
+
}
|
|
48
|
+
const git = simpleGit();
|
|
49
|
+
await git.clone(repoUrl, VAULT_PATH, ['--branch', branch]);
|
|
50
|
+
// Reset instance to use new repo
|
|
51
|
+
gitInstance = null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Initialize a new git repository in vault (for first-time setup with empty repo)
|
|
55
|
+
*/
|
|
56
|
+
export async function initRepository(repoUrl, branch = DEFAULT_BRANCH) {
|
|
57
|
+
await fs.ensureDir(VAULT_PATH);
|
|
58
|
+
const git = getGit();
|
|
59
|
+
await git.init();
|
|
60
|
+
await git.addRemote('origin', repoUrl);
|
|
61
|
+
await git.checkout(['-b', branch]);
|
|
62
|
+
// Create initial manifest
|
|
63
|
+
const manifest = {
|
|
64
|
+
version: '1.0.0',
|
|
65
|
+
files: [],
|
|
66
|
+
projects: [],
|
|
67
|
+
};
|
|
68
|
+
const manifestPath = path.join(VAULT_PATH, MANIFEST_FILE);
|
|
69
|
+
await fs.writeJson(manifestPath, manifest, { spaces: 2 });
|
|
70
|
+
// Create .gitignore to exclude local-only files
|
|
71
|
+
const gitignorePath = path.join(VAULT_PATH, '.gitignore');
|
|
72
|
+
await fs.writeFile(gitignorePath, '# Local files\n.envmatic-local\n');
|
|
73
|
+
// Create README
|
|
74
|
+
const readmePath = path.join(VAULT_PATH, 'README.md');
|
|
75
|
+
await fs.writeFile(readmePath, `# Envmatic Vault
|
|
76
|
+
|
|
77
|
+
This repository is managed by [Envmatic](https://github.com/envmatic).
|
|
78
|
+
|
|
79
|
+
**⚠️ This is a private repository containing encrypted secrets.**
|
|
80
|
+
|
|
81
|
+
## Structure
|
|
82
|
+
|
|
83
|
+
\`\`\`
|
|
84
|
+
vault/
|
|
85
|
+
├── <project-name>/
|
|
86
|
+
│ ├── development/
|
|
87
|
+
│ │ └── .env.enc
|
|
88
|
+
│ ├── staging/
|
|
89
|
+
│ │ └── .env.enc
|
|
90
|
+
│ └── production/
|
|
91
|
+
│ └── .env.enc
|
|
92
|
+
└── shared/
|
|
93
|
+
└── common/
|
|
94
|
+
└── .env.enc
|
|
95
|
+
\`\`\`
|
|
96
|
+
|
|
97
|
+
Do not manually edit encrypted files. Use the Envmatic CLI to manage secrets.
|
|
98
|
+
`);
|
|
99
|
+
await git.add('.');
|
|
100
|
+
await git.commit('Initial envmatic vault setup');
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Pull latest changes from remote
|
|
104
|
+
*/
|
|
105
|
+
export async function pull() {
|
|
106
|
+
const git = getGit();
|
|
107
|
+
await git.pull();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Push changes to remote (always sets upstream to handle fresh repos)
|
|
111
|
+
*/
|
|
112
|
+
export async function push() {
|
|
113
|
+
const git = getGit();
|
|
114
|
+
const status = await git.status();
|
|
115
|
+
const branch = status.current || 'main';
|
|
116
|
+
try {
|
|
117
|
+
// Try normal push first
|
|
118
|
+
await git.push();
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
122
|
+
// If no upstream, set it and push
|
|
123
|
+
if (errorMessage.includes('no upstream') || errorMessage.includes('set-upstream')) {
|
|
124
|
+
await git.push(['-u', 'origin', branch]);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Stage and commit changes
|
|
133
|
+
*/
|
|
134
|
+
export async function commitChanges(message) {
|
|
135
|
+
const git = getGit();
|
|
136
|
+
await git.add('.');
|
|
137
|
+
// Check if there are changes to commit
|
|
138
|
+
const status = await git.status();
|
|
139
|
+
if (status.files.length === 0) {
|
|
140
|
+
return; // Nothing to commit
|
|
141
|
+
}
|
|
142
|
+
await git.commit(message);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Sync: commit changes, pull, then push
|
|
146
|
+
*/
|
|
147
|
+
export async function sync() {
|
|
148
|
+
const git = getGit();
|
|
149
|
+
let pulled = false;
|
|
150
|
+
let pushed = false;
|
|
151
|
+
let committed = false;
|
|
152
|
+
// First, commit any uncommitted changes
|
|
153
|
+
const preStatus = await git.status();
|
|
154
|
+
if (preStatus.files.length > 0) {
|
|
155
|
+
await git.add('.');
|
|
156
|
+
await git.commit('Sync from envmatic');
|
|
157
|
+
committed = true;
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
await git.pull();
|
|
161
|
+
pulled = true;
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
// If pull fails due to no upstream, that's okay for new repos
|
|
165
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
166
|
+
if (!errorMessage.includes('no tracking information')) {
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const status = await git.status();
|
|
172
|
+
if (status.ahead > 0) {
|
|
173
|
+
await git.push(['-u', 'origin', 'HEAD']);
|
|
174
|
+
pushed = true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
179
|
+
// Handle first push to empty remote
|
|
180
|
+
if (errorMessage.includes('has no upstream')) {
|
|
181
|
+
await git.push(['-u', 'origin', 'HEAD']);
|
|
182
|
+
pushed = true;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return { pulled, pushed, committed };
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Get current status
|
|
192
|
+
*/
|
|
193
|
+
export async function getStatus() {
|
|
194
|
+
const git = getGit();
|
|
195
|
+
const status = await git.status();
|
|
196
|
+
return {
|
|
197
|
+
branch: status.current || DEFAULT_BRANCH,
|
|
198
|
+
ahead: status.ahead,
|
|
199
|
+
behind: status.behind,
|
|
200
|
+
modified: status.modified.length + status.not_added.length,
|
|
201
|
+
staged: status.staged.length,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Check if remote is accessible
|
|
206
|
+
*/
|
|
207
|
+
export async function checkRemoteAccess(repoUrl) {
|
|
208
|
+
try {
|
|
209
|
+
const git = simpleGit();
|
|
210
|
+
await git.listRemote([repoUrl]);
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get the manifest from the vault
|
|
219
|
+
*/
|
|
220
|
+
export async function getManifest() {
|
|
221
|
+
const manifestPath = path.join(VAULT_PATH, MANIFEST_FILE);
|
|
222
|
+
if (await fs.pathExists(manifestPath)) {
|
|
223
|
+
return fs.readJson(manifestPath);
|
|
224
|
+
}
|
|
225
|
+
// Return empty manifest if not found
|
|
226
|
+
return {
|
|
227
|
+
version: '1.0.0',
|
|
228
|
+
files: [],
|
|
229
|
+
projects: [],
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Save the manifest to the vault
|
|
234
|
+
*/
|
|
235
|
+
export async function saveManifest(manifest) {
|
|
236
|
+
const manifestPath = path.join(VAULT_PATH, MANIFEST_FILE);
|
|
237
|
+
await fs.writeJson(manifestPath, manifest, { spaces: 2 });
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/services/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linker Service
|
|
3
|
+
* Handles symlinks and copies for project integration
|
|
4
|
+
*/
|
|
5
|
+
export { getLinksForEnvFile } from './config.js';
|
|
6
|
+
import type { LinkInfo, EncryptionOptions } from '../types/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* Create a symlink from source to target
|
|
9
|
+
* On Windows, this may require admin privileges for file symlinks
|
|
10
|
+
*/
|
|
11
|
+
export declare function createSymlink(sourceFileId: string, targetPath: string, encryptionOptions?: EncryptionOptions): Promise<LinkInfo>;
|
|
12
|
+
/**
|
|
13
|
+
* Create a copy of an env file at target path
|
|
14
|
+
* This is useful for encrypted files or when symlinks aren't suitable
|
|
15
|
+
*/
|
|
16
|
+
export declare function createCopy(sourceFileId: string, targetPath: string, encryptionOptions?: EncryptionOptions, autoSync?: boolean): Promise<LinkInfo>;
|
|
17
|
+
/**
|
|
18
|
+
* Update all copies for a given source file
|
|
19
|
+
*/
|
|
20
|
+
export declare function syncCopies(sourceFileId: string, encryptionOptions?: EncryptionOptions): Promise<number>;
|
|
21
|
+
/**
|
|
22
|
+
* Unlink a target path (remove symlink or copy)
|
|
23
|
+
*/
|
|
24
|
+
export declare function unlink(targetPath: string): Promise<boolean>;
|
|
25
|
+
/**
|
|
26
|
+
* List all links
|
|
27
|
+
*/
|
|
28
|
+
export declare function listLinks(): Promise<LinkInfo[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Check if a link is valid (target exists and matches source)
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateLink(link: LinkInfo): Promise<{
|
|
33
|
+
valid: boolean;
|
|
34
|
+
exists: boolean;
|
|
35
|
+
isSymlink: boolean;
|
|
36
|
+
error?: string;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Repair broken links
|
|
40
|
+
*/
|
|
41
|
+
export declare function repairLinks(encryptionOptions?: EncryptionOptions): Promise<{
|
|
42
|
+
repaired: number;
|
|
43
|
+
removed: number;
|
|
44
|
+
errors: string[];
|
|
45
|
+
}>;
|
|
46
|
+
//# sourceMappingURL=linker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linker.d.ts","sourceRoot":"","sources":["../../src/services/linker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAWrE;;;GAGG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,OAAO,CAAC,QAAQ,CAAC,CAwEnB;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,iBAAiB,CAAC,EAAE,iBAAiB,EACrC,QAAQ,GAAE,OAAe,GACxB,OAAO,CAAC,QAAQ,CAAC,CAyBnB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,YAAY,EAAE,MAAM,EACpB,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAYjE;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAErD;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC1D,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAkCD;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC,CAoCD"}
|