Skip to content

Commit 5894dd5

Browse files
committed
Deploy
1 parent 74da62b commit 5894dd5

7 files changed

Lines changed: 294 additions & 14 deletions

File tree

README.md

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,72 @@
1-
# github-action-template
1+
### Collect Env
2+
3+
This GitHub Action allows you to collect environment variables with a specific prefix and write them to a file. It is useful when you want to extract environment variables that match a certain pattern and save them to a `.env` file for deployment or configuration purposes.
4+
5+
#### Usage
6+
7+
```yaml
8+
- name: Collect environment variables
9+
uses: ./action-tools/collect-env
10+
with:
11+
prefix: 'API_'
12+
output: '.env.api'
13+
remove-prefix: true
14+
```
15+
16+
#### Inputs
17+
18+
| Name | Description | Required | Default |
19+
|------|-------------|----------|---------|
20+
| prefix | The prefix to filter environment variables by | true | - |
21+
| output | The output file to write the collected variables to | true | - |
22+
| remove-prefix | Whether to remove the prefix from variable names in the output | false | false |
23+
24+
#### Example Workflow
25+
26+
```yaml
27+
name: Collect API environment variables
28+
29+
on:
30+
push:
31+
branches: [main]
32+
33+
jobs:
34+
collect-env:
35+
runs-on: ubuntu-latest
36+
steps:
37+
- name: Checkout repository
38+
uses: actions/checkout@v4
39+
40+
- name: Collect API environment variables
41+
uses: dev-five-git/collect-env-action@main
42+
with:
43+
prefix: 'API_'
44+
output: '.env.api'
45+
remove-prefix: true
46+
```
47+
48+
#### Example
49+
50+
If you have the following environment variables:
51+
- `API_KEY=secret123`
52+
- `API_URL=https://api.example.com`
53+
- `API_TIMEOUT=5000`
54+
- `DATABASE_URL=postgres://localhost`
55+
56+
And you run the action with:
57+
- `prefix: 'API_'`
58+
- `remove-prefix: true`
59+
60+
The output file will contain:
61+
```
62+
KEY=secret123
63+
URL=https://api.example.com
64+
TIMEOUT=5000
65+
```
66+
67+
If `remove-prefix: false`, the output will be:
68+
```
69+
API_KEY=secret123
70+
API_URL=https://api.example.com
71+
API_TIMEOUT=5000
72+
```

action.yml

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1-
name: Github Action Template
2-
description: Github Action Template
1+
name: Collect Env
2+
description: Collect env values and write to a file
33
author: JeongMin Oh
44

55
branding:
6-
icon: file-text
6+
icon: key
77
color: purple
88

99
inputs:
10-
input:
11-
description: The file to read from
12-
required: true
13-
default: .env.development
10+
prefix:
11+
description: The prefix to collect
12+
required: false
1413
output:
15-
description: The file to overwrite to
14+
description: The file to write to
1615
required: true
17-
default: .env.production
16+
remove-prefix:
17+
description: The prefix to remove
18+
required: false
19+
type: boolean
20+
default: true
1821

1922
runs:
2023
using: node24

bun.lock

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"name": "action-tools",
2+
"name": "collect-env",
33
"version": "1.0.0",
4-
"description": "github action template",
4+
"description": "Collect env values and write to a file",
55
"author": "JeongMin Oh",
66
"license": "Apache-2.0",
77
"scripts": {
@@ -10,6 +10,10 @@
1010
"lint:fix": "biome check --write",
1111
"prepare": "husky"
1212
},
13+
"dependencies": {
14+
"@actions/core": "^1.11",
15+
"@actions/io": "^1.1"
16+
},
1317
"devDependencies": {
1418
"@biomejs/biome": "^2.2",
1519
"@types/bun": "^1.3",

src/__tests__/run.test.ts

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import { expect, mock, test } from 'bun:test'
2+
import { writeFile } from 'node:fs/promises'
3+
import { getBooleanInput, getInput, setFailed } from '@actions/core'
4+
5+
const mockWriteFile = mock(writeFile)
6+
const mockGetInput = mock(getInput)
7+
const mockSetFailed = mock(setFailed)
8+
const mockGetBooleanInput = mock(getBooleanInput)
9+
10+
mock.module('node:fs/promises', () => ({
11+
writeFile: mockWriteFile,
12+
}))
13+
14+
mock.module('@actions/core', () => ({
15+
getInput: mockGetInput,
16+
setFailed: mockSetFailed,
17+
getBooleanInput: mockGetBooleanInput,
18+
}))
19+
20+
import { run } from '../run'
21+
22+
test('should write filtered environment variables to file', async () => {
23+
const testPrefix = 'TEST_'
24+
const testOutput = 'test.env'
25+
26+
process.env.TEST_VAR1 = 'value1'
27+
process.env.TEST_VAR2 = 'value2'
28+
process.env.OTHER_VAR = 'other'
29+
30+
mockGetInput.mockImplementation((name: string) => {
31+
switch (name as 'prefix' | 'output') {
32+
case 'prefix':
33+
return testPrefix
34+
case 'output':
35+
return testOutput
36+
}
37+
})
38+
39+
mockWriteFile.mockResolvedValue(undefined)
40+
mockGetBooleanInput.mockReturnValue(false)
41+
42+
await run()
43+
44+
expect(mockGetInput).toHaveBeenCalledWith('prefix')
45+
expect(mockGetInput).toHaveBeenCalledWith('output')
46+
expect(mockGetBooleanInput).toHaveBeenCalledWith('remove-prefix')
47+
expect(mockWriteFile).toHaveBeenCalledWith(
48+
testOutput,
49+
'TEST_VAR1=value1\nTEST_VAR2=value2',
50+
)
51+
})
52+
53+
test('should handle writeFile error', async () => {
54+
const testPrefix = 'TEST_'
55+
const testOutput = 'test.env'
56+
const testError = 'Write failed'
57+
58+
process.env.TEST_VAR = 'value'
59+
60+
mockGetInput.mockImplementation((name: string) => {
61+
switch (name as 'prefix' | 'output') {
62+
case 'prefix':
63+
return testPrefix
64+
case 'output':
65+
return testOutput
66+
}
67+
})
68+
69+
mockWriteFile.mockRejectedValue(testError)
70+
mockGetBooleanInput.mockReturnValue(false)
71+
72+
await run()
73+
74+
expect(mockSetFailed).toHaveBeenCalledWith(testError)
75+
process.exitCode = 0
76+
})
77+
78+
test('should handle empty environment variables', async () => {
79+
const testPrefix = 'NONEXISTENT_'
80+
const testOutput = 'empty.env'
81+
82+
mockGetInput.mockImplementation((name: string) => {
83+
switch (name as 'prefix' | 'output') {
84+
case 'prefix':
85+
return testPrefix
86+
case 'output':
87+
return testOutput
88+
}
89+
})
90+
91+
mockWriteFile.mockResolvedValue(undefined)
92+
mockGetBooleanInput.mockReturnValue(false)
93+
94+
await run()
95+
96+
expect(mockWriteFile).toHaveBeenCalledWith(testOutput, '')
97+
})
98+
99+
test('should filter environment variables by prefix', async () => {
100+
const testPrefix = 'API_'
101+
const testOutput = 'api.env'
102+
103+
process.env.API_KEY = 'secret123'
104+
process.env.API_URL = 'https://api.example.com'
105+
process.env.DATABASE_URL = 'postgres://localhost'
106+
process.env.API_TIMEOUT = '5000'
107+
108+
mockGetInput.mockImplementation((name: string) => {
109+
switch (name as 'prefix' | 'output') {
110+
case 'prefix':
111+
return testPrefix
112+
case 'output':
113+
return testOutput
114+
}
115+
})
116+
117+
mockWriteFile.mockResolvedValue(undefined)
118+
mockGetBooleanInput.mockReturnValue(false)
119+
await run()
120+
121+
const expectedContent = [
122+
'API_KEY=secret123',
123+
'API_URL=https://api.example.com',
124+
'API_TIMEOUT=5000',
125+
].join('\n')
126+
127+
expect(mockWriteFile).toHaveBeenCalledWith(testOutput, expectedContent)
128+
})
129+
130+
test('should remove prefix from environment variables', async () => {
131+
const testPrefix = 'API_'
132+
const testOutput = 'api.env'
133+
const testRemovePrefix = true
134+
process.env.API_KEY = 'secret123'
135+
process.env.API_URL = 'https://api.example.com'
136+
process.env.API_TIMEOUT = '5000'
137+
138+
mockGetInput.mockImplementation((name: string) => {
139+
switch (name as 'prefix' | 'output') {
140+
case 'prefix':
141+
return testPrefix
142+
case 'output':
143+
return testOutput
144+
}
145+
})
146+
147+
mockWriteFile.mockResolvedValue(undefined)
148+
mockGetBooleanInput.mockReturnValue(testRemovePrefix)
149+
150+
await run()
151+
152+
const expectedContent = [
153+
'KEY=secret123',
154+
'URL=https://api.example.com',
155+
'TIMEOUT=5000',
156+
].join('\n')
157+
158+
expect(mockWriteFile).toHaveBeenCalledWith(testOutput, expectedContent)
159+
})

src/run.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,23 @@
1-
export async function run() {}
1+
import { writeFile } from 'node:fs/promises'
2+
import { getBooleanInput, getInput, setFailed } from '@actions/core'
3+
4+
export async function run() {
5+
const prefix = getInput('prefix')
6+
const output = getInput('output')
7+
const removePrefix = getBooleanInput('remove-prefix')
8+
9+
try {
10+
await writeFile(
11+
output,
12+
Object.entries(process.env)
13+
.filter(([key]) => key.startsWith(prefix))
14+
.map(
15+
([key, value]) =>
16+
`${removePrefix ? key.substring(prefix.length) : key}=${value}`,
17+
)
18+
.join('\n'),
19+
)
20+
} catch (err: unknown) {
21+
setFailed(err as Error)
22+
}
23+
}

0 commit comments

Comments
 (0)