Skip to content

Commit 572a64f

Browse files
committed
Init
0 parents  commit 572a64f

File tree

24 files changed

+803
-0
lines changed

24 files changed

+803
-0
lines changed

.gitignore

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# dependencies (bun install)
2+
node_modules
3+
4+
# output
5+
out
6+
*.tgz
7+
8+
# code coverage
9+
coverage
10+
*.lcov
11+
12+
# logs
13+
logs
14+
_.log
15+
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
16+
17+
# dotenv environment variable files
18+
.env
19+
.env.development.local
20+
.env.test.local
21+
.env.production.local
22+
.env.local
23+
24+
# caches
25+
.eslintcache
26+
.cache
27+
*.tsbuildinfo
28+
29+
# IntelliJ based IDEs
30+
.idea
31+
32+
# Finder (MacOS) folder config
33+
.DS_Store

.husky/pre-commit

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env sh
2+
bun run lint
3+
bun test --coverage

README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Action Tools
2+
3+
## Actions
4+
### Overwrite Env File
5+
6+
This GitHub Action allows you to overwrite the contents of an environment file with the contents of another file. It is useful when you want to promote/stage environment variables from one environment to another (for example, from a `.env.development` file to a `.env.production` file) automatically in your workflows.
7+
8+
#### Usage
9+
10+
```yaml
11+
- name: Overwrite production env file with development env file
12+
uses: ./action-tools/overwrite-env-file
13+
with:
14+
input-file: .env.development
15+
output-file: .env.production
16+
```
17+
18+
#### Inputs
19+
20+
| Name | Description | Required | Default |
21+
|--------------|------------------------------|----------|---------------------|
22+
| input-file | The file to read from | true | .env.development |
23+
| output-file | The file to overwrite to | true | .env.production |
24+
25+
#### Example Workflow
26+
27+
```yaml
28+
name: Promote env to production
29+
30+
on:
31+
push:
32+
branches: [main]
33+
34+
jobs:
35+
promote-env:
36+
runs-on: ubuntu-latest
37+
steps:
38+
- name: Checkout repository
39+
uses: actions/checkout@v4
40+
41+
- name: Overwrite production env file with development env file
42+
uses: ./actions/overwrite-env-file
43+
with:
44+
input-file: .env.development
45+
output-file: .env.production
46+
```
47+

actions/collect-env/README.md

Whitespace-only changes.

actions/collect-env/action.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Collect Env
2+
description: Collect env values and write to a file
3+
author: JeongMin Oh
4+
5+
branding:
6+
icon: key
7+
color: purple
8+
9+
# Define your inputs here.
10+
inputs:
11+
prefix:
12+
description: The prefix to collect
13+
required: false
14+
output:
15+
description: The file to write to
16+
required: true
17+
remove-prefix:
18+
description: The prefix to remove
19+
required: false
20+
type: boolean
21+
default: true
22+
23+
runs:
24+
using: node24
25+
main: ./dist/index.js

actions/collect-env/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.

actions/collect-env/package.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "collect-env",
3+
"version": "1.0.0",
4+
"description": "Collect env values and write to a file",
5+
"author": "JeongMin Oh",
6+
"license": "Apache-2.0",
7+
"scripts": {
8+
"build": "bunx @vercel/ncc build ./src/index.ts -m -o dist"
9+
},
10+
"dependencies": {
11+
"@actions/core": "^1.11",
12+
"@actions/io": "^1.1"
13+
},
14+
"devDependencies": {
15+
"@types/bun": "^1.3",
16+
"@types/node": "^24.7",
17+
"typescript": "^5.9"
18+
}
19+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { expect, mock, test } from 'bun:test'
2+
3+
test('should call run() when imported', async () => {
4+
const originalModule = { ...(await import('../run')) }
5+
const mockRun = mock(() => {})
6+
mock.module('../run', () => ({
7+
run: mockRun,
8+
}))
9+
await import('../index')
10+
11+
expect(mockRun).toHaveBeenCalled()
12+
13+
mock.module('../run', () => originalModule)
14+
})
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+
})

actions/collect-env/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { run } from './run'
2+
3+
run()

0 commit comments

Comments
 (0)