@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.
Files changed (37) hide show
  1. package/.github/codeql-config.yml +21 -0
  2. package/.github/dependabot.yml +35 -0
  3. package/.github/workflows/claude-code-review.yml +43 -0
  4. package/.github/workflows/claude.yml +39 -0
  5. package/.github/workflows/release.yml +48 -0
  6. package/.github/workflows/security.yml +103 -0
  7. package/.github/workflows/update-docs.yml +38 -0
  8. package/.github/workflows/validate.yml +210 -0
  9. package/.husky/pre-commit +1 -0
  10. package/.nvmrc +1 -0
  11. package/AGENT.md +129 -0
  12. package/CLAUDE.md +3 -0
  13. package/CONTRIBUTING.md +316 -0
  14. package/README.md +207 -0
  15. package/SECURITY.md +208 -0
  16. package/bin/generate-docs.js +432 -0
  17. package/comment/README.md +82 -0
  18. package/comment/action.yml +102 -0
  19. package/eslint.config.js +8 -0
  20. package/npm-publish-pr/README.md +145 -0
  21. package/npm-publish-pr/action.yml +171 -0
  22. package/package.json +52 -0
  23. package/setup-node-and-install/README.md +139 -0
  24. package/setup-node-and-install/action.yml +220 -0
  25. package/tests/fixtures/.node-version +1 -0
  26. package/tests/fixtures/.nvmrc +1 -0
  27. package/tests/fixtures/lockfiles/package-lock.json +12 -0
  28. package/tests/fixtures/lockfiles/pnpm-lock.yaml +9 -0
  29. package/tests/fixtures/lockfiles/yarn.lock +7 -0
  30. package/tests/fixtures/package-json/minimal.json +4 -0
  31. package/tests/fixtures/package-json/scoped.json +6 -0
  32. package/tests/fixtures/package-json/valid.json +13 -0
  33. package/tests/integration/comment/basic.bats +95 -0
  34. package/tests/integration/npm-pr-version/basic.bats +353 -0
  35. package/tests/integration/setup-node-and-install/basic.bats +200 -0
  36. package/tests/scripts/test-helpers.sh +113 -0
  37. 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,12 @@
1
+ {
2
+ "name": "test-package",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "test-package",
9
+ "version": "1.0.0"
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,9 @@
1
+ lockfileVersion: '6.0'
2
+
3
+ settings:
4
+ autoInstallPeers: true
5
+ excludeLinksFromLockfile: false
6
+
7
+ dependencies: {}
8
+
9
+ packages: {}
@@ -0,0 +1,7 @@
1
+ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
+ # yarn lockfile v1
3
+
4
+
5
+ "test-package@1.0.0":
6
+ version "1.0.0"
7
+ resolved "https://registry.yarnpkg.com/test-package/-/test-package-1.0.0.tgz"
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "minimal-package",
3
+ "version": "0.1.0"
4
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "@test-org/scoped-package",
3
+ "version": "2.0.0",
4
+ "description": "Scoped package for testing",
5
+ "private": false
6
+ }
@@ -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
+ }