@codfish/actions 1.1.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/.github/codeql-config.yml +21 -0
- package/.github/dependabot.yml +35 -0
- package/.github/workflows/claude-code-review.yml +43 -0
- package/.github/workflows/claude.yml +39 -0
- package/.github/workflows/release.yml +48 -0
- package/.github/workflows/security.yml +103 -0
- package/.github/workflows/update-docs.yml +38 -0
- package/.github/workflows/validate.yml +210 -0
- package/.husky/pre-commit +1 -0
- package/.nvmrc +1 -0
- package/AGENT.md +129 -0
- package/CLAUDE.md +3 -0
- package/CONTRIBUTING.md +316 -0
- package/README.md +207 -0
- package/SECURITY.md +208 -0
- package/bin/generate-docs.js +432 -0
- package/comment/README.md +82 -0
- package/comment/action.yml +102 -0
- package/eslint.config.js +8 -0
- package/npm-publish-pr/README.md +145 -0
- package/npm-publish-pr/action.yml +171 -0
- package/package.json +52 -0
- package/setup-node-and-install/README.md +139 -0
- package/setup-node-and-install/action.yml +220 -0
- package/tests/fixtures/.node-version +1 -0
- package/tests/fixtures/.nvmrc +1 -0
- package/tests/fixtures/lockfiles/package-lock.json +12 -0
- package/tests/fixtures/lockfiles/pnpm-lock.yaml +9 -0
- package/tests/fixtures/lockfiles/yarn.lock +7 -0
- package/tests/fixtures/package-json/minimal.json +4 -0
- package/tests/fixtures/package-json/scoped.json +6 -0
- package/tests/fixtures/package-json/valid.json +13 -0
- package/tests/integration/comment/basic.bats +95 -0
- package/tests/integration/npm-pr-version/basic.bats +353 -0
- package/tests/integration/setup-node-and-install/basic.bats +200 -0
- package/tests/scripts/test-helpers.sh +113 -0
- package/tests/scripts/test-runner.sh +115 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
name: setup-node-and-install
|
|
2
|
+
|
|
3
|
+
description:
|
|
4
|
+
Sets up Node.js environment and installs dependencies with automatic package manager detection (npm/pnpm/yarn),
|
|
5
|
+
intelligent caching, and .nvmrc/.node-version support
|
|
6
|
+
|
|
7
|
+
inputs:
|
|
8
|
+
node-version:
|
|
9
|
+
description: Node.js version to install (e.g. '24', 'lts/*'). Defaults to .nvmrc or .node-version file if present
|
|
10
|
+
required: false
|
|
11
|
+
cache-key-suffix:
|
|
12
|
+
description: Additional suffix for cache key to enable multiple caches per workflow
|
|
13
|
+
default: ''
|
|
14
|
+
install-options:
|
|
15
|
+
description: Extra command-line options to pass to npm/pnpm/yarn install
|
|
16
|
+
default: ''
|
|
17
|
+
working-directory:
|
|
18
|
+
description: Directory containing package.json and lockfile
|
|
19
|
+
default: .
|
|
20
|
+
|
|
21
|
+
outputs: {}
|
|
22
|
+
|
|
23
|
+
runs:
|
|
24
|
+
using: composite
|
|
25
|
+
|
|
26
|
+
steps:
|
|
27
|
+
- name: Validate inputs and detect package manager
|
|
28
|
+
id: setup
|
|
29
|
+
working-directory: ${{ inputs.working-directory }}
|
|
30
|
+
shell: bash
|
|
31
|
+
run: |
|
|
32
|
+
# Validate working directory exists
|
|
33
|
+
if [ ! -d "." ]; then
|
|
34
|
+
echo "❌ ERROR: Working directory '${{ inputs.working-directory }}' does not exist"
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Validate package.json exists
|
|
39
|
+
if [ ! -f "./package.json" ]; then
|
|
40
|
+
echo "❌ ERROR: package.json not found in '${{ inputs.working-directory }}'"
|
|
41
|
+
echo "Make sure the working-directory contains a valid Node.js project"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Validate package.json is valid JSON
|
|
46
|
+
if ! jq empty package.json 2>/dev/null; then
|
|
47
|
+
echo "❌ ERROR: package.json is not valid JSON"
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Check Node version requirements (.nvmrc, .node-version, or input)
|
|
52
|
+
if [[ ! -f "./.nvmrc" && ! -f "./.node-version" && -z "$INPUT_NODE_VERSION" ]]; then
|
|
53
|
+
echo "node-version-missing=true" >> $GITHUB_OUTPUT
|
|
54
|
+
exit
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Validate .nvmrc format if it exists
|
|
58
|
+
if [ -f "./.nvmrc" ]; then
|
|
59
|
+
nvmrc_version=$(cat .nvmrc | tr -d '\n\r' | xargs)
|
|
60
|
+
if [ -z "$nvmrc_version" ]; then
|
|
61
|
+
echo "❌ ERROR: .nvmrc file is empty"
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
echo "📋 Found .nvmrc with Node version: $nvmrc_version"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Validate .node-version format if it exists (and no .nvmrc)
|
|
68
|
+
if [ -f "./.node-version" ] && [ ! -f "./.nvmrc" ]; then
|
|
69
|
+
node_version_file=$(cat .node-version | tr -d '\n\r' | xargs)
|
|
70
|
+
if [ -z "$node_version_file" ]; then
|
|
71
|
+
echo "❌ ERROR: .node-version file is empty"
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
echo "📋 Found .node-version with Node version: $node_version_file"
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# Show priority info if both files exist
|
|
78
|
+
if [ -f "./.nvmrc" ] && [ -f "./.node-version" ]; then
|
|
79
|
+
echo "📋 Both .nvmrc and .node-version found, .nvmrc takes priority"
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# Validate node-version input format if provided
|
|
83
|
+
if [ -n "$INPUT_NODE_VERSION" ]; then
|
|
84
|
+
echo "📋 Using Node version from input: $INPUT_NODE_VERSION"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# Detect package manager based on lockfiles (including yarn)
|
|
88
|
+
if [ -f "./pnpm-lock.yaml" ]; then
|
|
89
|
+
echo "package-manager=pnpm" >> $GITHUB_OUTPUT
|
|
90
|
+
echo "lockfile-exists=true" >> $GITHUB_OUTPUT
|
|
91
|
+
echo "lockfile-path=pnpm-lock.yaml" >> $GITHUB_OUTPUT
|
|
92
|
+
echo "📦 Detected package manager: pnpm"
|
|
93
|
+
elif [ -f "./yarn.lock" ]; then
|
|
94
|
+
echo "package-manager=yarn" >> $GITHUB_OUTPUT
|
|
95
|
+
echo "lockfile-exists=true" >> $GITHUB_OUTPUT
|
|
96
|
+
echo "lockfile-path=yarn.lock" >> $GITHUB_OUTPUT
|
|
97
|
+
echo "📦 Detected package manager: yarn"
|
|
98
|
+
elif [ -f "./package-lock.json" ]; then
|
|
99
|
+
echo "package-manager=npm" >> $GITHUB_OUTPUT
|
|
100
|
+
echo "lockfile-exists=true" >> $GITHUB_OUTPUT
|
|
101
|
+
echo "lockfile-path=package-lock.json" >> $GITHUB_OUTPUT
|
|
102
|
+
echo "📦 Detected package manager: npm"
|
|
103
|
+
else
|
|
104
|
+
echo "package-manager=npm" >> $GITHUB_OUTPUT
|
|
105
|
+
echo "lockfile-exists=false" >> $GITHUB_OUTPUT
|
|
106
|
+
echo "lockfile-path=" >> $GITHUB_OUTPUT
|
|
107
|
+
echo "📦 No lockfile found, defaulting to: npm"
|
|
108
|
+
fi
|
|
109
|
+
env:
|
|
110
|
+
INPUT_NODE_VERSION: ${{ inputs.node-version }}
|
|
111
|
+
|
|
112
|
+
- if: steps.setup.outputs.node-version-missing == 'true'
|
|
113
|
+
uses: actions/github-script@v7
|
|
114
|
+
with:
|
|
115
|
+
script: |
|
|
116
|
+
core.setFailed('You need to create an .nvmrc file, .node-version file, or pass a value in the `node-version` input.')
|
|
117
|
+
|
|
118
|
+
- name: Install pnpm
|
|
119
|
+
if: steps.setup.outputs.package-manager == 'pnpm'
|
|
120
|
+
uses: pnpm/action-setup@v4
|
|
121
|
+
with:
|
|
122
|
+
run_install: false
|
|
123
|
+
|
|
124
|
+
- name: Setup node with .nvmrc
|
|
125
|
+
if: ${{ inputs.node-version != '' || hashFiles(format('{0}/.nvmrc', inputs.working-directory)) != '' }}
|
|
126
|
+
uses: actions/setup-node@v5
|
|
127
|
+
id: setup-node
|
|
128
|
+
with:
|
|
129
|
+
# use detected package manager cache if a lockfile is present
|
|
130
|
+
cache: ${{ steps.setup.outputs.lockfile-exists == 'true' && steps.setup.outputs.package-manager || '' }}
|
|
131
|
+
# supplying a node-version input will override the .nvmrc file and give a warning, but that's to be expected.
|
|
132
|
+
cache-dependency-path: ${{ inputs.working-directory }}
|
|
133
|
+
node-version: ${{ inputs.node-version }}
|
|
134
|
+
node-version-file: '${{ inputs.working-directory }}/.nvmrc'
|
|
135
|
+
registry-url: 'https://registry.npmjs.org'
|
|
136
|
+
|
|
137
|
+
- name: Read .node-version file
|
|
138
|
+
if:
|
|
139
|
+
${{ inputs.node-version == '' && hashFiles(format('{0}/.nvmrc', inputs.working-directory)) == '' &&
|
|
140
|
+
hashFiles(format('{0}/.node-version', inputs.working-directory)) != '' }}
|
|
141
|
+
id: read-node-version
|
|
142
|
+
working-directory: ${{ inputs.working-directory }}
|
|
143
|
+
shell: bash
|
|
144
|
+
run: |
|
|
145
|
+
node_version=$(cat .node-version | tr -d '\n\r' | xargs)
|
|
146
|
+
echo "node-version=$node_version" >> $GITHUB_OUTPUT
|
|
147
|
+
echo "📋 Read Node version from .node-version: $node_version"
|
|
148
|
+
|
|
149
|
+
- name: Setup node with .node-version
|
|
150
|
+
if:
|
|
151
|
+
${{ inputs.node-version == '' && hashFiles(format('{0}/.nvmrc', inputs.working-directory)) == '' &&
|
|
152
|
+
hashFiles(format('{0}/.node-version', inputs.working-directory)) != '' }}
|
|
153
|
+
uses: actions/setup-node@v5
|
|
154
|
+
id: setup-node-alt
|
|
155
|
+
with:
|
|
156
|
+
# use detected package manager cache if a lockfile is present
|
|
157
|
+
cache: ${{ steps.setup.outputs.lockfile-exists == 'true' && steps.setup.outputs.package-manager || '' }}
|
|
158
|
+
cache-dependency-path: ${{ inputs.working-directory }}
|
|
159
|
+
node-version: ${{ steps.read-node-version.outputs.node-version }}
|
|
160
|
+
registry-url: 'https://registry.npmjs.org'
|
|
161
|
+
|
|
162
|
+
# Install dependencies with pnpm
|
|
163
|
+
- name: Install dependencies with pnpm
|
|
164
|
+
if: steps.setup.outputs.package-manager == 'pnpm'
|
|
165
|
+
working-directory: ${{ inputs.working-directory }}
|
|
166
|
+
shell: bash
|
|
167
|
+
run: |
|
|
168
|
+
echo "🔧 Installing dependencies with pnpm..."
|
|
169
|
+
if ! pnpm install $INPUT_INSTALL_OPTIONS; then
|
|
170
|
+
echo "❌ ERROR: pnpm install failed"
|
|
171
|
+
exit 1
|
|
172
|
+
fi
|
|
173
|
+
echo "✅ pnpm install completed successfully"
|
|
174
|
+
env:
|
|
175
|
+
INPUT_INSTALL_OPTIONS: ${{ inputs.install-options }}
|
|
176
|
+
|
|
177
|
+
# Install dependencies with yarn
|
|
178
|
+
- name: Install dependencies with yarn
|
|
179
|
+
if: steps.setup.outputs.package-manager == 'yarn'
|
|
180
|
+
working-directory: ${{ inputs.working-directory }}
|
|
181
|
+
shell: bash
|
|
182
|
+
run: |
|
|
183
|
+
echo "🔧 Installing dependencies with yarn..."
|
|
184
|
+
if ! yarn install --frozen-lockfile $INPUT_INSTALL_OPTIONS; then
|
|
185
|
+
echo "❌ ERROR: yarn install failed"
|
|
186
|
+
exit 1
|
|
187
|
+
fi
|
|
188
|
+
echo "✅ yarn install completed successfully"
|
|
189
|
+
env:
|
|
190
|
+
INPUT_INSTALL_OPTIONS: ${{ inputs.install-options }}
|
|
191
|
+
|
|
192
|
+
# Install dependencies with npm (with lockfile)
|
|
193
|
+
- name: Install dependencies with npm (with lockfile)
|
|
194
|
+
if: steps.setup.outputs.lockfile-exists == 'true' && steps.setup.outputs.package-manager == 'npm'
|
|
195
|
+
working-directory: ${{ inputs.working-directory }}
|
|
196
|
+
shell: bash
|
|
197
|
+
run: |
|
|
198
|
+
echo "🔧 Installing dependencies with npm ci..."
|
|
199
|
+
if ! npm ci --prefer-offline --no-audit $INPUT_INSTALL_OPTIONS; then
|
|
200
|
+
echo "❌ ERROR: npm ci failed"
|
|
201
|
+
exit 1
|
|
202
|
+
fi
|
|
203
|
+
echo "✅ npm ci completed successfully"
|
|
204
|
+
env:
|
|
205
|
+
INPUT_INSTALL_OPTIONS: ${{ inputs.install-options }}
|
|
206
|
+
|
|
207
|
+
- name: Install dependencies with npm (without lockfile)
|
|
208
|
+
if: steps.setup.outputs.lockfile-exists == 'false' && steps.setup.outputs.package-manager == 'npm'
|
|
209
|
+
working-directory: ${{ inputs.working-directory }}
|
|
210
|
+
shell: bash
|
|
211
|
+
run: |
|
|
212
|
+
echo "🔧 Installing dependencies with npm install..."
|
|
213
|
+
echo "⚠️ Warning: No lockfile found, versions may vary"
|
|
214
|
+
if ! npm install --no-save --prefer-offline --no-audit $INPUT_INSTALL_OPTIONS; then
|
|
215
|
+
echo "❌ ERROR: npm install failed"
|
|
216
|
+
exit 1
|
|
217
|
+
fi
|
|
218
|
+
echo "✅ npm install completed successfully"
|
|
219
|
+
env:
|
|
220
|
+
INPUT_INSTALL_OPTIONS: ${{ inputs.install-options }}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
20.10.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
18.20.0
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "test-package",
|
|
3
|
+
"version": "1.2.3",
|
|
4
|
+
"description": "Test package for action testing",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "echo 'Building...'",
|
|
8
|
+
"test": "echo 'Testing...'"
|
|
9
|
+
},
|
|
10
|
+
"keywords": ["test"],
|
|
11
|
+
"author": "test",
|
|
12
|
+
"license": "MIT"
|
|
13
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
load "../../scripts/test-helpers.sh"
|
|
4
|
+
|
|
5
|
+
setup() {
|
|
6
|
+
setup_github_env
|
|
7
|
+
TEST_DIR=$(mktemp -d)
|
|
8
|
+
cd "$TEST_DIR"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
teardown() {
|
|
12
|
+
cd /
|
|
13
|
+
cleanup_test_env "$TEST_DIR"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@test "comment: generates correct tag format" {
|
|
17
|
+
# Test tag generation logic from action
|
|
18
|
+
TAG_INPUT="test-tag"
|
|
19
|
+
MESSAGE_INPUT="Hello, World!"
|
|
20
|
+
|
|
21
|
+
bash -c "
|
|
22
|
+
tag=\"<!-- codfish/actions/comment $TAG_INPUT -->\"
|
|
23
|
+
echo \"Generated tag: \$tag\"
|
|
24
|
+
echo \"tag=\$tag\"
|
|
25
|
+
" > output.txt
|
|
26
|
+
|
|
27
|
+
assert_output_contains "tag=<!-- codfish/actions/comment test-tag -->" "$(cat output.txt)"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@test "comment: handles multi-line messages" {
|
|
31
|
+
# Test multi-line message handling
|
|
32
|
+
MESSAGE_INPUT="Line 1
|
|
33
|
+
Line 2
|
|
34
|
+
Line 3"
|
|
35
|
+
|
|
36
|
+
bash -c '
|
|
37
|
+
body=$(printf "$1")
|
|
38
|
+
echo "Processed message:"
|
|
39
|
+
echo "$body"
|
|
40
|
+
' -- "$MESSAGE_INPUT" > output.txt
|
|
41
|
+
|
|
42
|
+
assert_output_contains "Line 1" "$(cat output.txt)"
|
|
43
|
+
assert_output_contains "Line 2" "$(cat output.txt)"
|
|
44
|
+
assert_output_contains "Line 3" "$(cat output.txt)"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@test "comment: handles markdown formatting" {
|
|
48
|
+
# Test markdown message
|
|
49
|
+
MESSAGE_INPUT="## Test Header
|
|
50
|
+
|
|
51
|
+
- Item 1
|
|
52
|
+
- Item 2
|
|
53
|
+
|
|
54
|
+
**Bold text** and *italic text*"
|
|
55
|
+
|
|
56
|
+
bash -c '
|
|
57
|
+
body=$(printf "$1")
|
|
58
|
+
echo "Markdown message:"
|
|
59
|
+
echo "$body"
|
|
60
|
+
' -- "$MESSAGE_INPUT" > output.txt
|
|
61
|
+
|
|
62
|
+
assert_output_contains "## Test Header" "$(cat output.txt)"
|
|
63
|
+
assert_output_contains "- Item 1" "$(cat output.txt)"
|
|
64
|
+
assert_output_contains "**Bold text**" "$(cat output.txt)"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@test "comment: combines message and tag correctly" {
|
|
68
|
+
# Test complete body generation
|
|
69
|
+
TAG_INPUT="build-status"
|
|
70
|
+
MESSAGE_INPUT="✅ Build successful!"
|
|
71
|
+
|
|
72
|
+
bash -c "
|
|
73
|
+
tag=\"<!-- codfish/actions/comment $TAG_INPUT -->\"
|
|
74
|
+
body=\$(printf '$MESSAGE_INPUT')
|
|
75
|
+
echo \"Complete body:\"
|
|
76
|
+
echo \"\$body\"
|
|
77
|
+
echo \"\$tag\"
|
|
78
|
+
" > output.txt
|
|
79
|
+
|
|
80
|
+
assert_output_contains "✅ Build successful!" "$(cat output.txt)"
|
|
81
|
+
assert_output_contains "<!-- codfish/actions/comment build-status -->" "$(cat output.txt)"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@test "comment: handles empty tag input" {
|
|
85
|
+
# Test with empty tag
|
|
86
|
+
TAG_INPUT=""
|
|
87
|
+
MESSAGE_INPUT="Message without tag"
|
|
88
|
+
|
|
89
|
+
bash -c "
|
|
90
|
+
tag=\"<!-- codfish/actions/comment $TAG_INPUT -->\"
|
|
91
|
+
echo \"Tag with empty input: \$tag\"
|
|
92
|
+
" > output.txt
|
|
93
|
+
|
|
94
|
+
assert_output_contains "<!-- codfish/actions/comment -->" "$(cat output.txt)"
|
|
95
|
+
}
|