envilder 0.2.0 โ†’ 0.2.2

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 CHANGED
@@ -1,18 +1,18 @@
1
- # ๐ŸŒฑ Envilder
1
+ ![image](https://github.com/user-attachments/assets/6f962001-911a-41be-8ebf-80f66bf6582f)
2
2
 
3
3
  `Envilder` is a CLI tool to manage AWS SSM Parameter Store parameters and automatically generate the required `.env` file. This tool simplifies environment variable management for projects, avoiding manual updates and ensuring consistency across environments.
4
4
 
5
- ## โœจ Features
5
+ # โœจ Features
6
6
 
7
7
  - ๐Ÿ”’ Fetch parameters securely from AWS SSM Parameter Store.
8
8
  - โšก Automatically generates a `.env` file with specified parameters.
9
- - ๐Ÿ›ก๏ธ Handles both encrypted and unencrypted SSM parameters.
9
+ - ๐Ÿ›ก๏ธ Handles encrypted (currently only supported) SSM parameters.
10
10
  - ๐Ÿชถ Lightweight and simple to use.
11
11
 
12
- ## Prerequisites
12
+ # Prerequisites
13
13
  Before using `Envilder`, ensure that you have the AWS CLI installed and properly configured on your local machine. This configuration is required for `Envilder` to access and manage parameters in AWS SSM.
14
14
 
15
- ### AWS CLI Installation & Configuration
15
+ ## AWS CLI Installation & Configuration
16
16
  1. Install the AWS CLI by following the instructions [here](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html).
17
17
  2. After installation, configure the AWS CLI using the following command:
18
18
 
@@ -28,14 +28,14 @@ Before using `Envilder`, ensure that you have the AWS CLI installed and properly
28
28
 
29
29
  Make sure that the AWS credentials you're using have the appropriate permissions to access the SSM Parameter Store in your AWS account.
30
30
 
31
- ## Installation
31
+ # Installation
32
32
  You can install `Envilder` globally using yarn. This will allow you to use the `envilder` command from any directory on your system.
33
33
 
34
34
  ```bash
35
35
  yarn global add envilder
36
36
  ```
37
37
 
38
- ## ๐Ÿ“ฆ Installation
38
+ # ๐Ÿ“ฆ Installation
39
39
 
40
40
  You can install **envilder** globally or locally using npm:
41
41
 
@@ -43,21 +43,21 @@ You can install **envilder** globally or locally using npm:
43
43
  npm install -g envilder
44
44
  ```
45
45
 
46
- ## ๐Ÿš€ Usage
46
+ # ๐Ÿš€ Usage
47
47
 
48
48
  Envilder requires two arguments:
49
49
 
50
50
  - `--map <path>`: Path to a JSON file mapping environment variable names to SSM parameters.
51
51
  - `--envfile <path>`: Path where the generated .env file will be saved.
52
52
 
53
- ## ๐Ÿ”ง Example
53
+ # ๐Ÿ”ง Example
54
54
 
55
55
  1. Create a mapping file `param_map.json`:
56
56
 
57
57
  ```json
58
58
  {
59
- "NEXT_PUBLIC_CREDENTIAL_EMAIL": "/path/to/ssm/email",
60
- "NEXT_PUBLIC_CREDENTIAL_PASSWORD": "/path/to/ssm/password"
59
+ "SECRET_TOKEN": "/path/to/ssm/token",
60
+ "SECRET_KEY": "/path/to/ssm/password"
61
61
  }
62
62
  ```
63
63
 
@@ -69,25 +69,27 @@ Envilder requires two arguments:
69
69
 
70
70
  3. The `.env` file will be generated in the specified location.
71
71
 
72
- ## ๐Ÿ“‚ Sample `.env` Output
72
+ # ๐Ÿ“‚ Sample `.env` Output
73
73
 
74
74
  ```makefile
75
- NEXT_PUBLIC_CREDENTIAL_EMAIL=mockedEmail@example.com
76
- NEXT_PUBLIC_CREDENTIAL_PASSWORD=mockedPassword
75
+ SECRET_TOKEN=mockedEmail@example.com
76
+ SECRET_KEY=mockedPassword
77
77
  ```
78
78
 
79
- ## ๐Ÿงช Running Tests
79
+ # ๐Ÿงช Running Tests
80
80
 
81
- To run the tests with coverage:
81
+ To run the tests with coverage:
82
82
 
83
83
  ```bash
84
84
  yarn test
85
85
  ```
86
86
 
87
- ## ๐Ÿ“ License
87
+ Here you can see the current coverage report: https://macalbert.github.io/envilder/
88
+
89
+ # ๐Ÿ“ License
88
90
 
89
91
  This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
90
92
 
91
- ## ๐Ÿ™Œ Contributing
93
+ # ๐Ÿ™Œ Contributing
92
94
 
93
95
  Contributions are welcome! Feel free to submit issues and pull requests.
@@ -28,6 +28,6 @@ export function cliRunner() {
28
28
  });
29
29
  }
30
30
  cliRunner().catch((error) => {
31
- console.error('Error in CLI Runner:', error);
31
+ console.error('๐Ÿšจ Uh-oh! Looks like Mario fell into the wrong pipe! ๐Ÿ„๐Ÿ’ฅ');
32
32
  });
33
33
  //# sourceMappingURL=cliRunner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cliRunner.js","sourceRoot":"","sources":["../../src/cli/cliRunner.ts"],"names":[],"mappings":";;;;;;;;;;AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC,MAAM,UAAgB,SAAS;;QAC7B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;QAE9B,OAAO;aACJ,IAAI,CAAC,UAAU,CAAC;aAChB,WAAW,CAAC,2DAA2D,CAAC;aACxE,OAAO,CAAC,OAAO,CAAC;aAChB,cAAc,CAAC,cAAc,EAAE,yDAAyD,CAAC;aACzF,cAAc,CAAC,kBAAkB,EAAE,uCAAuC,CAAC,CAAC;QAE/E,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAE/B,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;CAAA;AAED,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAC1B,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cliRunner.js","sourceRoot":"","sources":["../../src/cli/cliRunner.ts"],"names":[],"mappings":";;;;;;;;;;AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC,MAAM,UAAgB,SAAS;;QAC7B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;QAE9B,OAAO;aACJ,IAAI,CAAC,UAAU,CAAC;aAChB,WAAW,CAAC,2DAA2D,CAAC;aACxE,OAAO,CAAC,OAAO,CAAC;aAChB,cAAc,CAAC,cAAc,EAAE,yDAAyD,CAAC;aACzF,cAAc,CAAC,kBAAkB,EAAE,uCAAuC,CAAC,CAAC;QAE/E,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAE/B,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;CAAA;AAED,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAC1B,OAAO,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;AAC7E,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,wBAAsB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,iBAQ7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,wBAAsB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,iBAQ7D"}
package/lib/index.js CHANGED
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import * as fs from 'node:fs';
11
11
  import { GetParameterCommand, SSM } from '@aws-sdk/client-ssm';
12
+ import * as dotenv from 'dotenv';
12
13
  const ssm = new SSM({});
13
14
  export function run(mapPath, envFilePath) {
14
15
  return __awaiter(this, void 0, void 0, function* () {
@@ -16,46 +17,50 @@ export function run(mapPath, envFilePath) {
16
17
  const existingEnvVariables = loadExistingEnvVariables(envFilePath);
17
18
  const updatedEnvVariables = yield fetchAndUpdateEnvVariables(paramMap, existingEnvVariables);
18
19
  writeEnvFile(envFilePath, updatedEnvVariables);
19
- console.log(`.env file generated at ${envFilePath}`);
20
+ console.log(`Environment File generated at '${envFilePath}'`);
20
21
  });
21
22
  }
22
23
  function loadParamMap(mapPath) {
23
24
  const content = fs.readFileSync(mapPath, 'utf-8');
24
- return JSON.parse(content);
25
+ try {
26
+ return JSON.parse(content);
27
+ }
28
+ catch (error) {
29
+ console.error(`Error parsing JSON from ${mapPath}`);
30
+ throw new Error(`Invalid JSON in parameter map file: ${mapPath}`);
31
+ }
25
32
  }
26
33
  function loadExistingEnvVariables(envFilePath) {
27
34
  const envVariables = {};
28
35
  if (!fs.existsSync(envFilePath))
29
36
  return envVariables;
30
37
  const existingEnvContent = fs.readFileSync(envFilePath, 'utf-8');
31
- const lines = existingEnvContent.split('\n');
32
- for (const line of lines) {
33
- const [key, value] = line.split('=');
34
- if (key && value) {
35
- envVariables[key] = value;
36
- }
37
- }
38
+ const parsedEnv = dotenv.parse(existingEnvContent);
39
+ Object.assign(envVariables, parsedEnv);
38
40
  return envVariables;
39
41
  }
40
42
  function fetchAndUpdateEnvVariables(paramMap, existingEnvVariables) {
41
43
  return __awaiter(this, void 0, void 0, function* () {
42
- console.log('Fetching parameters...');
44
+ const errors = [];
43
45
  for (const [envVar, ssmName] of Object.entries(paramMap)) {
44
46
  try {
45
47
  const value = yield fetchSSMParameter(ssmName);
46
48
  if (value) {
47
49
  existingEnvVariables[envVar] = value;
48
- console.log(`${envVar}=${value}`);
50
+ console.log(`${envVar}=${value.length > 3 ? '*'.repeat(value.length - 3) + value.slice(-3) : '*'.repeat(value.length)}`);
49
51
  }
50
52
  else {
51
- console.error(`Warning: No value found for ${ssmName}`);
53
+ console.error(`Warning: No value found for: '${ssmName}'`);
52
54
  }
53
55
  }
54
56
  catch (error) {
55
- console.error(`Error fetching parameter ${ssmName}: ${error}`);
56
- throw new Error(`ParameterNotFound: ${ssmName}`);
57
+ console.error(`Error fetching parameter: '${ssmName}'`);
58
+ errors.push(`ParameterNotFound: ${ssmName}`);
57
59
  }
58
60
  }
61
+ if (errors.length > 0) {
62
+ throw new Error(`Some parameters could not be fetched:\n${errors.join('\n')}`);
63
+ }
59
64
  return existingEnvVariables;
60
65
  });
61
66
  }
@@ -71,7 +76,10 @@ function fetchSSMParameter(ssmName) {
71
76
  }
72
77
  function writeEnvFile(envFilePath, envVariables) {
73
78
  const envContent = Object.entries(envVariables)
74
- .map(([key, value]) => `${key}=${value}`)
79
+ .map(([key, value]) => {
80
+ const escapedValue = value.replace(/(\n|\r|\n\r)/g, '\\n').replace(/"/g, '\\"');
81
+ return `${key}=${escapedValue}`;
82
+ })
75
83
  .join('\n');
76
84
  fs.writeFileSync(envFilePath, envContent);
77
85
  }
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envilder",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "A CLI tool to generate .env files from AWS SSM parameters",
5
5
  "exports": {
6
6
  ".": {
@@ -42,6 +42,7 @@
42
42
  "@secretlint/secretlint-rule-preset-recommend": "^8.2.4",
43
43
  "@types/node": "^22.5.5",
44
44
  "commander": "^12.1.0",
45
+ "dotenv": "^16.4.5",
45
46
  "picocolors": "^1.1.0"
46
47
  },
47
48
  "devDependencies": {
@@ -24,5 +24,5 @@ export async function cliRunner() {
24
24
  }
25
25
 
26
26
  cliRunner().catch((error) => {
27
- console.error('Error in CLI Runner:', error);
27
+ console.error('๐Ÿšจ Uh-oh! Looks like Mario fell into the wrong pipe! ๐Ÿ„๐Ÿ’ฅ');
28
28
  });
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as fs from 'node:fs';
2
2
  import { GetParameterCommand, SSM } from '@aws-sdk/client-ssm';
3
+ import * as dotenv from 'dotenv';
3
4
 
4
5
  const ssm = new SSM({});
5
6
 
@@ -10,26 +11,27 @@ export async function run(mapPath: string, envFilePath: string) {
10
11
  const updatedEnvVariables = await fetchAndUpdateEnvVariables(paramMap, existingEnvVariables);
11
12
 
12
13
  writeEnvFile(envFilePath, updatedEnvVariables);
13
- console.log(`.env file generated at ${envFilePath}`);
14
+ console.log(`Environment File generated at '${envFilePath}'`);
14
15
  }
15
16
 
16
17
  function loadParamMap(mapPath: string): Record<string, string> {
17
18
  const content = fs.readFileSync(mapPath, 'utf-8');
18
- return JSON.parse(content);
19
+ try {
20
+ return JSON.parse(content);
21
+ } catch (error) {
22
+ console.error(`Error parsing JSON from ${mapPath}`);
23
+ throw new Error(`Invalid JSON in parameter map file: ${mapPath}`);
24
+ }
19
25
  }
20
26
 
21
27
  function loadExistingEnvVariables(envFilePath: string): Record<string, string> {
22
28
  const envVariables: Record<string, string> = {};
29
+
23
30
  if (!fs.existsSync(envFilePath)) return envVariables;
24
31
 
25
32
  const existingEnvContent = fs.readFileSync(envFilePath, 'utf-8');
26
- const lines = existingEnvContent.split('\n');
27
- for (const line of lines) {
28
- const [key, value] = line.split('=');
29
- if (key && value) {
30
- envVariables[key] = value;
31
- }
32
- }
33
+ const parsedEnv = dotenv.parse(existingEnvContent);
34
+ Object.assign(envVariables, parsedEnv);
33
35
 
34
36
  return envVariables;
35
37
  }
@@ -38,23 +40,29 @@ async function fetchAndUpdateEnvVariables(
38
40
  paramMap: Record<string, string>,
39
41
  existingEnvVariables: Record<string, string>,
40
42
  ): Promise<Record<string, string>> {
41
- console.log('Fetching parameters...');
43
+ const errors: string[] = [];
42
44
 
43
45
  for (const [envVar, ssmName] of Object.entries(paramMap)) {
44
46
  try {
45
47
  const value = await fetchSSMParameter(ssmName);
46
48
  if (value) {
47
49
  existingEnvVariables[envVar] = value;
48
- console.log(`${envVar}=${value}`);
50
+ console.log(
51
+ `${envVar}=${value.length > 3 ? '*'.repeat(value.length - 3) + value.slice(-3) : '*'.repeat(value.length)}`,
52
+ );
49
53
  } else {
50
- console.error(`Warning: No value found for ${ssmName}`);
54
+ console.error(`Warning: No value found for: '${ssmName}'`);
51
55
  }
52
56
  } catch (error) {
53
- console.error(`Error fetching parameter ${ssmName}: ${error}`);
54
- throw new Error(`ParameterNotFound: ${ssmName}`);
57
+ console.error(`Error fetching parameter: '${ssmName}'`);
58
+ errors.push(`ParameterNotFound: ${ssmName}`);
55
59
  }
56
60
  }
57
61
 
62
+ if (errors.length > 0) {
63
+ throw new Error(`Some parameters could not be fetched:\n${errors.join('\n')}`);
64
+ }
65
+
58
66
  return existingEnvVariables;
59
67
  }
60
68
 
@@ -70,7 +78,10 @@ async function fetchSSMParameter(ssmName: string): Promise<string | undefined> {
70
78
 
71
79
  function writeEnvFile(envFilePath: string, envVariables: Record<string, string>): void {
72
80
  const envContent = Object.entries(envVariables)
73
- .map(([key, value]) => `${key}=${value}`)
81
+ .map(([key, value]) => {
82
+ const escapedValue = value.replace(/(\n|\r|\n\r)/g, '\\n').replace(/"/g, '\\"');
83
+ return `${key}=${escapedValue}`;
84
+ })
74
85
  .join('\n');
75
86
 
76
87
  fs.writeFileSync(envFilePath, envContent);
@@ -79,9 +79,7 @@ describe('Envilder CLI', () => {
79
79
  const mockMapPath = './tests/param_map.json';
80
80
  const mockEnvFilePath = './tests/.env.test';
81
81
 
82
- const existingEnvContent = `
83
- EXISTING_VAR=existingValue
84
- `;
82
+ const existingEnvContent = 'EXISTING_VAR=existingValue';
85
83
  fs.writeFileSync(mockEnvFilePath, existingEnvContent);
86
84
  const paramMapContent = {
87
85
  NEXT_PUBLIC_CREDENTIAL_EMAIL: '/path/to/ssm/email',
@@ -105,9 +103,7 @@ describe('Envilder CLI', () => {
105
103
  // Arrange
106
104
  const mockMapPath = './tests/param_map.json';
107
105
  const mockEnvFilePath = './tests/.env.test';
108
- const existingEnvContent = `
109
- NEXT_PUBLIC_CREDENTIAL_EMAIL=oldEmail@example.com
110
- `;
106
+ const existingEnvContent = 'NEXT_PUBLIC_CREDENTIAL_EMAIL=oldEmail@example.com';
111
107
  fs.writeFileSync(mockEnvFilePath, existingEnvContent);
112
108
  const paramMapContent = {
113
109
  NEXT_PUBLIC_CREDENTIAL_EMAIL: '/path/to/ssm/email',