@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,82 @@
|
|
|
1
|
+
# comment
|
|
2
|
+
|
|
3
|
+
Creates or updates pull request comments with intelligent upsert functionality using unique tags.
|
|
4
|
+
|
|
5
|
+
<!-- DOCTOC SKIP -->
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
See [action.yml](action.yml).
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
- name: Comment on PR
|
|
13
|
+
uses: codfish/actions/comment@v1
|
|
14
|
+
with:
|
|
15
|
+
message: '✅ Build successful!'
|
|
16
|
+
tag: 'build-status'
|
|
17
|
+
upsert: true
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Inputs
|
|
21
|
+
|
|
22
|
+
<!-- start inputs -->
|
|
23
|
+
|
|
24
|
+
| Input | Description | Required | Default |
|
|
25
|
+
| --------- | ------------------------------------------------------------------------------------- | -------- | ------- |
|
|
26
|
+
| `message` | The comment message content (supports markdown formatting) | Yes | - |
|
|
27
|
+
| `tag` | Unique identifier to find and update existing comments (required when upsert is true) | No | - |
|
|
28
|
+
| `upsert` | Update existing comment with matching tag instead of creating new comment | No | `false` |
|
|
29
|
+
|
|
30
|
+
<!-- end inputs -->
|
|
31
|
+
|
|
32
|
+
## Examples
|
|
33
|
+
|
|
34
|
+
### Basic comment
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
- uses: codfish/actions/comment@v1
|
|
38
|
+
with:
|
|
39
|
+
message: 'Hello from GitHub Actions! 👋'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Updating comments with upsert
|
|
43
|
+
|
|
44
|
+
Use the `upsert` feature to update the same comment instead of creating multiple comments:
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
- name: Update build status
|
|
48
|
+
uses: codfish/actions/comment@v1
|
|
49
|
+
with:
|
|
50
|
+
message: |
|
|
51
|
+
## Build Status
|
|
52
|
+
⏳ Build in progress...
|
|
53
|
+
tag: 'build-status'
|
|
54
|
+
upsert: true
|
|
55
|
+
|
|
56
|
+
# Later in the workflow...
|
|
57
|
+
- name: Update build status
|
|
58
|
+
uses: codfish/actions/comment@v1
|
|
59
|
+
with:
|
|
60
|
+
message: |
|
|
61
|
+
## Build Status
|
|
62
|
+
✅ Build completed successfully!
|
|
63
|
+
tag: 'build-status'
|
|
64
|
+
upsert: true
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Multi-line markdown comment
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
- uses: codfish/actions/comment@v1
|
|
71
|
+
with:
|
|
72
|
+
message: |
|
|
73
|
+
## 📊 Test Results
|
|
74
|
+
|
|
75
|
+
- ✅ Unit tests: 42 passed
|
|
76
|
+
- ✅ Integration tests: 12 passed
|
|
77
|
+
- 📦 Coverage: 98%
|
|
78
|
+
|
|
79
|
+
Great work! 🎉
|
|
80
|
+
tag: 'test-results'
|
|
81
|
+
upsert: true
|
|
82
|
+
```
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
name: comment
|
|
2
|
+
|
|
3
|
+
description: Creates or updates a comment in a pull request with optional tagging for upsert functionality
|
|
4
|
+
|
|
5
|
+
inputs:
|
|
6
|
+
message:
|
|
7
|
+
description: The comment message content (supports markdown formatting)
|
|
8
|
+
required: true
|
|
9
|
+
tag:
|
|
10
|
+
description: Unique identifier to find and update existing comments (required when upsert is true)
|
|
11
|
+
required: false
|
|
12
|
+
upsert:
|
|
13
|
+
description: Update existing comment with matching tag instead of creating new comment
|
|
14
|
+
required: false
|
|
15
|
+
default: 'false'
|
|
16
|
+
|
|
17
|
+
runs:
|
|
18
|
+
using: composite
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: Validate inputs and set globals
|
|
22
|
+
id: globals
|
|
23
|
+
shell: bash
|
|
24
|
+
run: |
|
|
25
|
+
# Validate required inputs
|
|
26
|
+
if [ -z "${{ inputs.message }}" ]; then
|
|
27
|
+
echo "❌ ERROR: 'message' input is required"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Validate upsert logic
|
|
32
|
+
if [ "${{ inputs.upsert }}" = "true" ] && [ -z "${{ inputs.tag }}" ]; then
|
|
33
|
+
echo "❌ ERROR: 'tag' input is required when upsert is true"
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Create dynamic tag based on repository name
|
|
38
|
+
repo_namespace="${{ github.repository }}"
|
|
39
|
+
tag="<!-- ${repo_namespace}/comment ${{ inputs.tag }} -->"
|
|
40
|
+
body=$(printf '${{ inputs.message }}')
|
|
41
|
+
|
|
42
|
+
echo "💬 Comment tag: $tag"
|
|
43
|
+
echo "tag=$tag" >> $GITHUB_OUTPUT
|
|
44
|
+
echo "body<<EOF"$'\n'"$body"'\n'"$tag"$'\n'EOF >> "$GITHUB_OUTPUT"
|
|
45
|
+
|
|
46
|
+
- name: Check existing comments
|
|
47
|
+
id: check-comments
|
|
48
|
+
if: inputs.upsert == 'true'
|
|
49
|
+
uses: actions/github-script@v7
|
|
50
|
+
with:
|
|
51
|
+
script: |
|
|
52
|
+
try {
|
|
53
|
+
const comments = await github.rest.issues.listComments({
|
|
54
|
+
owner: context.repo.owner,
|
|
55
|
+
repo: context.repo.repo,
|
|
56
|
+
issue_number: context.issue.number,
|
|
57
|
+
});
|
|
58
|
+
const existingComment = comments.data.find(comment => comment.body.includes('${{ steps.globals.outputs.tag }}'));
|
|
59
|
+
core.setOutput('comment-id', existingComment ? existingComment.id : null);
|
|
60
|
+
|
|
61
|
+
if (existingComment) {
|
|
62
|
+
console.log(`Found existing comment with ID: ${existingComment.id}`);
|
|
63
|
+
} else {
|
|
64
|
+
console.log('No existing comment found, will create new one');
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
core.setFailed(`Failed to check existing comments: ${error.message}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
- name: Update existing comment
|
|
71
|
+
if: steps.check-comments.outputs.comment-id != null
|
|
72
|
+
uses: actions/github-script@v7
|
|
73
|
+
with:
|
|
74
|
+
script: |
|
|
75
|
+
try {
|
|
76
|
+
await github.rest.issues.updateComment({
|
|
77
|
+
owner: context.repo.owner,
|
|
78
|
+
repo: context.repo.repo,
|
|
79
|
+
comment_id: ${{ steps.check-comments.outputs.comment-id }},
|
|
80
|
+
body: `${{ steps.globals.outputs.body }}`,
|
|
81
|
+
});
|
|
82
|
+
console.log('✅ Successfully updated existing comment');
|
|
83
|
+
} catch (error) {
|
|
84
|
+
core.setFailed(`Failed to update comment: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
- name: Create new comment
|
|
88
|
+
if: steps.check-comments.outputs.comment-id == null
|
|
89
|
+
uses: actions/github-script@v7
|
|
90
|
+
with:
|
|
91
|
+
script: |
|
|
92
|
+
try {
|
|
93
|
+
const response = await github.rest.issues.createComment({
|
|
94
|
+
owner: context.repo.owner,
|
|
95
|
+
repo: context.repo.repo,
|
|
96
|
+
issue_number: context.issue.number,
|
|
97
|
+
body: `${{ steps.globals.outputs.body }}`,
|
|
98
|
+
});
|
|
99
|
+
console.log(`✅ Successfully created new comment with ID: ${response.data.id}`);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
core.setFailed(`Failed to create comment: ${error.message}`);
|
|
102
|
+
}
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# npm-pr-version
|
|
2
|
+
|
|
3
|
+
Publishes packages with PR-specific version numbers for testing in downstream applications before merging. Automatically
|
|
4
|
+
detects your package manager (npm, yarn, or pnpm) and uses the appropriate publish command. The action generates
|
|
5
|
+
versions in the format `0.0.0-PR-{number}--{short-sha}` and automatically comments on the pull request with the
|
|
6
|
+
published version.
|
|
7
|
+
|
|
8
|
+
**Key Features:**
|
|
9
|
+
|
|
10
|
+
- Automatic package manager detection (npm/yarn/pnpm)
|
|
11
|
+
- Automatic PR version generation
|
|
12
|
+
- Publishes to registry with `pr` tag
|
|
13
|
+
- Automatic PR commenting with version info
|
|
14
|
+
- No git history modification
|
|
15
|
+
|
|
16
|
+
<!-- DOCTOC SKIP -->
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
See [action.yml](action.yml).
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v5
|
|
25
|
+
|
|
26
|
+
- uses: codfish/actions/setup-node-and-install@v1
|
|
27
|
+
with:
|
|
28
|
+
node-version: lts/*
|
|
29
|
+
|
|
30
|
+
- run: npm run build
|
|
31
|
+
|
|
32
|
+
- uses: codfish/actions/npm-pr-version@v1
|
|
33
|
+
with:
|
|
34
|
+
npm-token: ${{ secrets.NPM_TOKEN }}
|
|
35
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Disable PR Comments
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
- uses: codfish/actions/npm-pr-version@v1
|
|
42
|
+
with:
|
|
43
|
+
npm-token: ${{ secrets.NPM_TOKEN }}
|
|
44
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
45
|
+
comment: false
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Custom Comment Tag
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
- uses: codfish/actions/npm-pr-version@v1
|
|
52
|
+
with:
|
|
53
|
+
npm-token: ${{ secrets.NPM_TOKEN }}
|
|
54
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
55
|
+
comment-tag: my-custom-tag
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Complete Workflow Example
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
name: PR Package Testing
|
|
62
|
+
|
|
63
|
+
on: pull_request_target
|
|
64
|
+
|
|
65
|
+
permissions:
|
|
66
|
+
contents: write
|
|
67
|
+
pull-requests: write
|
|
68
|
+
|
|
69
|
+
jobs:
|
|
70
|
+
publish-pr-package:
|
|
71
|
+
runs-on: ubuntu-latest
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/checkout@v5
|
|
74
|
+
|
|
75
|
+
- uses: codfish/actions/setup-node-and-install@v1
|
|
76
|
+
with:
|
|
77
|
+
node-version: 'lts/*'
|
|
78
|
+
|
|
79
|
+
- name: Build package
|
|
80
|
+
run: npm run build
|
|
81
|
+
|
|
82
|
+
- name: Publish PR package
|
|
83
|
+
uses: codfish/actions/npm-pr-version@v1
|
|
84
|
+
with:
|
|
85
|
+
npm-token: ${{ secrets.NPM_TOKEN }}
|
|
86
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Testing Downstream
|
|
90
|
+
|
|
91
|
+
After the action runs, you can install the PR version in downstream projects:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npm install my-package@0.0.0-PR-123--abc1234
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The package is published under the `pr` tag, so it won't interfere with your regular releases.
|
|
98
|
+
|
|
99
|
+
## Inputs
|
|
100
|
+
|
|
101
|
+
<!-- start inputs -->
|
|
102
|
+
|
|
103
|
+
| Input | Description | Required | Default |
|
|
104
|
+
| -------------- | ----------------------------------------------------------------------------------- | -------- | ---------------- |
|
|
105
|
+
| `npm-token` | Registry authentication token with publish permissions (works with npm/yarn/pnpm) | No | - |
|
|
106
|
+
| `github-token` | GitHub token with pull request comment permissions (typically secrets.GITHUB_TOKEN) | Yes | - |
|
|
107
|
+
| `comment` | Whether to comment on the PR with the published version (true/false) | No | `true` |
|
|
108
|
+
| `comment-tag` | Tag to use for PR comments (for comment identification and updates) | No | `npm-publish-pr` |
|
|
109
|
+
|
|
110
|
+
<!-- end inputs -->
|
|
111
|
+
|
|
112
|
+
## Package Manager Support
|
|
113
|
+
|
|
114
|
+
The action automatically detects your package manager and uses the appropriate publish command:
|
|
115
|
+
|
|
116
|
+
- **npm**: Uses `npm publish --access public --tag pr`
|
|
117
|
+
- **yarn**: Uses `yarn publish --access public --tag pr --new-version {version} --no-git-tag-version`
|
|
118
|
+
- **pnpm**: Uses `pnpm publish --access public --tag pr`
|
|
119
|
+
|
|
120
|
+
Detection is based on lockfile presence:
|
|
121
|
+
|
|
122
|
+
- `yarn.lock` → yarn
|
|
123
|
+
- `pnpm-lock.yaml` → pnpm
|
|
124
|
+
- `package-lock.json` or no lockfile → npm
|
|
125
|
+
|
|
126
|
+
## Outputs
|
|
127
|
+
|
|
128
|
+
<!-- start outputs -->
|
|
129
|
+
|
|
130
|
+
| Output | Description |
|
|
131
|
+
| --------------- | --------------------------------------------------------------------- |
|
|
132
|
+
| `version` | Generated PR-specific version number (0.0.0-PR-{number}--{short-sha}) |
|
|
133
|
+
| `package-name` | Package name from package.json |
|
|
134
|
+
| `error-message` | Error message if publish fails |
|
|
135
|
+
|
|
136
|
+
<!-- end outputs -->
|
|
137
|
+
|
|
138
|
+
## Version Format
|
|
139
|
+
|
|
140
|
+
Published versions follow the pattern: `0.0.0-PR-{pr-number}--{short-sha}`
|
|
141
|
+
|
|
142
|
+
Examples:
|
|
143
|
+
|
|
144
|
+
- `0.0.0-PR-123--abc1234` (PR #123, commit abc1234)
|
|
145
|
+
- `0.0.0-PR-456--def5678` (PR #456, commit def5678)
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
name: npm-pr-version
|
|
2
|
+
|
|
3
|
+
description:
|
|
4
|
+
Publishes package with PR-specific version (0.0.0-PR-123--abc1234) using detected package manager (npm/yarn/pnpm) and
|
|
5
|
+
automatically comments on PR
|
|
6
|
+
|
|
7
|
+
inputs:
|
|
8
|
+
npm-token:
|
|
9
|
+
required: false
|
|
10
|
+
description: Registry authentication token with publish permissions (works with npm/yarn/pnpm)
|
|
11
|
+
github-token:
|
|
12
|
+
required: true
|
|
13
|
+
description: GitHub token with pull request comment permissions (typically secrets.GITHUB_TOKEN)
|
|
14
|
+
comment:
|
|
15
|
+
required: false
|
|
16
|
+
default: 'true'
|
|
17
|
+
description: Whether to comment on the PR with the published version (true/false)
|
|
18
|
+
comment-tag:
|
|
19
|
+
required: false
|
|
20
|
+
default: npm-publish-pr
|
|
21
|
+
description: Tag to use for PR comments (for comment identification and updates)
|
|
22
|
+
|
|
23
|
+
outputs:
|
|
24
|
+
version:
|
|
25
|
+
description: Generated PR-specific version number (0.0.0-PR-{number}--{short-sha})
|
|
26
|
+
value: '${{ steps.publish.outputs.version }}'
|
|
27
|
+
package-name:
|
|
28
|
+
description: Package name from package.json
|
|
29
|
+
value: '${{ steps.publish.outputs.package-name }}'
|
|
30
|
+
error-message:
|
|
31
|
+
description: Error message if publish fails
|
|
32
|
+
value: '${{ steps.publish.outputs.error-message }}'
|
|
33
|
+
|
|
34
|
+
runs:
|
|
35
|
+
using: composite
|
|
36
|
+
|
|
37
|
+
steps:
|
|
38
|
+
- uses: codfish/actions/comment@v1
|
|
39
|
+
if: inputs.comment == 'true'
|
|
40
|
+
with:
|
|
41
|
+
message: ⏳ Publishing PR version...
|
|
42
|
+
upsert: true
|
|
43
|
+
tag: ${{ inputs.comment-tag }}
|
|
44
|
+
|
|
45
|
+
- name: Validate and publish to registry
|
|
46
|
+
id: publish
|
|
47
|
+
shell: bash
|
|
48
|
+
run: |
|
|
49
|
+
set +e # Don't exit on error so we can handle failures
|
|
50
|
+
|
|
51
|
+
# Initialize outputs for error handling
|
|
52
|
+
error_message=""
|
|
53
|
+
package_name=""
|
|
54
|
+
version=""
|
|
55
|
+
|
|
56
|
+
# Validate package.json exists
|
|
57
|
+
if [ ! -f "package.json" ]; then
|
|
58
|
+
error_message="❌ ERROR: package.json not found in current directory. Make sure you're running this action in a directory with a package.json file"
|
|
59
|
+
echo "$error_message"
|
|
60
|
+
echo "error-message=$error_message" >> $GITHUB_OUTPUT
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Validate package.json is valid JSON
|
|
65
|
+
if ! jq empty package.json 2>/dev/null; then
|
|
66
|
+
error_message="❌ ERROR: package.json is not valid JSON"
|
|
67
|
+
echo "$error_message"
|
|
68
|
+
echo "error-message=$error_message" >> $GITHUB_OUTPUT
|
|
69
|
+
exit 1
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Check if package has a name
|
|
73
|
+
package_name=$(jq -r '.name // empty' package.json)
|
|
74
|
+
if [ -z "$package_name" ] || [ "$package_name" = "null" ]; then
|
|
75
|
+
error_message="❌ ERROR: package.json must have a 'name' field"
|
|
76
|
+
echo "$error_message"
|
|
77
|
+
echo "error-message=$error_message" >> $GITHUB_OUTPUT
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# Output package name for use in error handling
|
|
82
|
+
echo "package-name=$package_name" >> $GITHUB_OUTPUT
|
|
83
|
+
|
|
84
|
+
# Detect package manager
|
|
85
|
+
if [ -f "./yarn.lock" ]; then
|
|
86
|
+
package_manager="yarn"
|
|
87
|
+
echo "📦 Detected package manager: yarn"
|
|
88
|
+
elif [ -f "./pnpm-lock.yaml" ]; then
|
|
89
|
+
package_manager="pnpm"
|
|
90
|
+
echo "📦 Detected package manager: pnpm"
|
|
91
|
+
else
|
|
92
|
+
package_manager="npm"
|
|
93
|
+
echo "📦 Detected package manager: npm"
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# Generate version
|
|
97
|
+
version="0.0.0-PR-${PR}--$(echo ${SHA} | cut -c -7)"
|
|
98
|
+
echo "📦 Publishing $package_name@$version with $package_manager"
|
|
99
|
+
echo "version=$version" >> $GITHUB_OUTPUT
|
|
100
|
+
|
|
101
|
+
# Update package.json version (all package managers support npm version)
|
|
102
|
+
version_output=$(npm version $version --no-git-tag-version 2>&1)
|
|
103
|
+
version_exit_code=$?
|
|
104
|
+
if [ $version_exit_code -ne 0 ]; then
|
|
105
|
+
error_message="❌ ERROR: Failed to update package version. Check if the version format is valid. Error: $version_output"
|
|
106
|
+
echo "$error_message"
|
|
107
|
+
echo "error-message=$error_message" >> $GITHUB_OUTPUT
|
|
108
|
+
exit 1
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# Publish package based on detected package manager
|
|
112
|
+
case "$package_manager" in
|
|
113
|
+
"yarn")
|
|
114
|
+
publish_output=$(yarn publish --access public --tag pr --new-version $version --no-git-tag-version 2>&1)
|
|
115
|
+
publish_exit_code=$?
|
|
116
|
+
if [ $publish_exit_code -ne 0 ]; then
|
|
117
|
+
error_message="❌ ERROR: Failed to publish package with yarn. Error: $publish_output"
|
|
118
|
+
echo "$error_message"
|
|
119
|
+
echo "error-message=$error_message" >> $GITHUB_OUTPUT
|
|
120
|
+
exit 1
|
|
121
|
+
fi
|
|
122
|
+
;;
|
|
123
|
+
"pnpm")
|
|
124
|
+
publish_output=$(pnpm publish --access public --tag pr 2>&1)
|
|
125
|
+
publish_exit_code=$?
|
|
126
|
+
if [ $publish_exit_code -ne 0 ]; then
|
|
127
|
+
error_message="❌ ERROR: Failed to publish package with pnpm. Error: $publish_output"
|
|
128
|
+
echo "$error_message"
|
|
129
|
+
echo "error-message=$error_message" >> $GITHUB_OUTPUT
|
|
130
|
+
exit 1
|
|
131
|
+
fi
|
|
132
|
+
;;
|
|
133
|
+
*)
|
|
134
|
+
publish_output=$(npm publish --access public --tag pr 2>&1)
|
|
135
|
+
publish_exit_code=$?
|
|
136
|
+
if [ $publish_exit_code -ne 0 ]; then
|
|
137
|
+
error_message="❌ ERROR: Failed to publish package with npm. Error: $publish_output"
|
|
138
|
+
echo "$error_message"
|
|
139
|
+
echo "error-message=$error_message" >> $GITHUB_OUTPUT
|
|
140
|
+
exit 1
|
|
141
|
+
fi
|
|
142
|
+
;;
|
|
143
|
+
esac
|
|
144
|
+
|
|
145
|
+
echo "✅ Successfully published $package_name@$version using $package_manager"
|
|
146
|
+
env:
|
|
147
|
+
NODE_AUTH_TOKEN: ${{ inputs.npm-token }}
|
|
148
|
+
PR: ${{ github.event.number }}
|
|
149
|
+
SHA: ${{ github.event.pull_request.head.sha }}
|
|
150
|
+
|
|
151
|
+
- uses: codfish/actions/comment@v1
|
|
152
|
+
if: failure() && inputs.comment == 'true'
|
|
153
|
+
with:
|
|
154
|
+
message: |
|
|
155
|
+
❌ **PR package publish failed!**
|
|
156
|
+
|
|
157
|
+
Error: ${{ steps.publish.outputs.error-message }}
|
|
158
|
+
|
|
159
|
+
Please check the workflow logs for more details.
|
|
160
|
+
upsert: true
|
|
161
|
+
tag: ${{ inputs.comment-tag }}
|
|
162
|
+
|
|
163
|
+
- uses: codfish/actions/comment@v1
|
|
164
|
+
if: success() && inputs.comment == 'true'
|
|
165
|
+
with:
|
|
166
|
+
message: |
|
|
167
|
+
✅ **PR package published successfully!**
|
|
168
|
+
|
|
169
|
+
Install with: `npm install ${{ steps.publish.outputs.package-name }}@${{ steps.publish.outputs.version }}`
|
|
170
|
+
upsert: true
|
|
171
|
+
tag: ${{ inputs.comment-tag }}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codfish/actions",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"description": "Composite GitHub Actions for my projects.",
|
|
5
|
+
"author": "Chris O'Donnell <chris@codfish.dev>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"version": "1.1.0",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=20"
|
|
13
|
+
},
|
|
14
|
+
"volta": {
|
|
15
|
+
"node": "24.8.0"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"lint": "eslint .",
|
|
19
|
+
"fix": "eslint . --fix",
|
|
20
|
+
"format": "prettier --write \"**/*.{json,css,md}\" --config ./node_modules/@codfish/eslint-config/prettier.js",
|
|
21
|
+
"test": "bash tests/scripts/test-runner.sh",
|
|
22
|
+
"test:integration": "bash tests/scripts/test-runner.sh integration",
|
|
23
|
+
"test:unit": "bash tests/scripts/test-runner.sh unit",
|
|
24
|
+
"docs:generate": "node bin/generate-docs.js",
|
|
25
|
+
"prepare": "husky"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@codfish/eslint-config": "^12.1.1",
|
|
29
|
+
"bats": "^1.10.0",
|
|
30
|
+
"doctoc": "^2.2.1",
|
|
31
|
+
"eslint": "^9.36.0",
|
|
32
|
+
"husky": "^9.1.7",
|
|
33
|
+
"js-yaml": "^4.1.0",
|
|
34
|
+
"lint-staged": "^16.2.0",
|
|
35
|
+
"prettier": "^3.6.2"
|
|
36
|
+
},
|
|
37
|
+
"packageManager": "pnpm@10.17.1",
|
|
38
|
+
"commitlint": {
|
|
39
|
+
"extends": [
|
|
40
|
+
"./node_modules/@codfish/eslint-config/commitlint.js"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"lint-staged": {
|
|
44
|
+
"*.{json,css}": [
|
|
45
|
+
"prettier --write --config ./node_modules/@codfish/eslint-config/prettier.js"
|
|
46
|
+
],
|
|
47
|
+
"*.md": [
|
|
48
|
+
"prettier --write --config ./node_modules/@codfish/eslint-config/prettier.js",
|
|
49
|
+
"doctoc --title '## Table of Contents'"
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# setup-node-and-install
|
|
2
|
+
|
|
3
|
+
Sets up Node.js environment and installs dependencies with automatic package manager detection, intelligent caching, and
|
|
4
|
+
.nvmrc/.node-version support.
|
|
5
|
+
|
|
6
|
+
This action provides the following functionality:
|
|
7
|
+
|
|
8
|
+
- Automatically detects package manager (npm, yarn, or pnpm) from lockfiles
|
|
9
|
+
- Uses GitHub's official `setup-node` action with optimized caching
|
|
10
|
+
- Installs dependencies with appropriate commands based on detected package manager
|
|
11
|
+
- Supports .nvmrc and .node-version files for version specification
|
|
12
|
+
- Intelligent caching of node_modules when lockfiles are present
|
|
13
|
+
|
|
14
|
+
<!-- DOCTOC SKIP -->
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
See [action.yml](action.yml).
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v5
|
|
23
|
+
|
|
24
|
+
# will install latest Node v18.x
|
|
25
|
+
- uses: codfish/actions/setup-node-and-install@v1
|
|
26
|
+
with:
|
|
27
|
+
node-version: 18
|
|
28
|
+
cache-key-suffix: '-${{ github.head_ref || github.event.release.tag_name }}'
|
|
29
|
+
|
|
30
|
+
- run: npm test
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The `node-version` input is optional. If not supplied, this action will attempt to use an `.nvmrc` file in your project.
|
|
34
|
+
If neither is supplied, it will fail your workflow.
|
|
35
|
+
|
|
36
|
+
The `cache-key-suffix` input is optional. If not supplied, no suffix will be applied to the cache key used to restore
|
|
37
|
+
cache in subsequent workflow runs.
|
|
38
|
+
|
|
39
|
+
The `install-options` input is optional. If not supplied, the npm install commands will execute as defined without any
|
|
40
|
+
additional options.
|
|
41
|
+
|
|
42
|
+
**With `.nvmrc` file**
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
# .nvmrc
|
|
46
|
+
v18.14.1
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v5
|
|
52
|
+
# will install Node v18.14.1
|
|
53
|
+
- uses: codfish/actions/setup-node-and-install@v1
|
|
54
|
+
- run: npm test
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**With `.node-version` file**
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
# .node-version
|
|
61
|
+
20.10.0
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/checkout@v5
|
|
67
|
+
# will install Node v20.10.0
|
|
68
|
+
- uses: codfish/actions/setup-node-and-install@v1
|
|
69
|
+
- run: npm test
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Node Version File Priority
|
|
73
|
+
|
|
74
|
+
When multiple version specification methods are present, the action uses this priority order:
|
|
75
|
+
|
|
76
|
+
1. **Input parameter** (`node-version`) - highest priority
|
|
77
|
+
2. **`.nvmrc` file** - takes precedence over .node-version
|
|
78
|
+
3. **`.node-version` file** - used if no .nvmrc exists
|
|
79
|
+
4. **Error** - if none of the above are present
|
|
80
|
+
|
|
81
|
+
## Inputs
|
|
82
|
+
|
|
83
|
+
<!-- start inputs -->
|
|
84
|
+
|
|
85
|
+
| Input | Description | Required | Default |
|
|
86
|
+
| ------------------- | ----------------------------------------------------------------------------------------------------- | -------- | ------- |
|
|
87
|
+
| `node-version` | Node.js version to install (e.g. '24', 'lts/\*'). Defaults to .nvmrc or .node-version file if present | No | - |
|
|
88
|
+
| `cache-key-suffix` | Additional suffix for cache key to enable multiple caches per workflow | No | - |
|
|
89
|
+
| `install-options` | Extra command-line options to pass to npm/pnpm/yarn install | No | - |
|
|
90
|
+
| `working-directory` | Directory containing package.json and lockfile | No | `.` |
|
|
91
|
+
|
|
92
|
+
<!-- end inputs -->
|
|
93
|
+
|
|
94
|
+
## Package Manager Detection
|
|
95
|
+
|
|
96
|
+
The action automatically detects your package manager:
|
|
97
|
+
|
|
98
|
+
- **pnpm**: Detected when `pnpm-lock.yaml` exists
|
|
99
|
+
- **npm**: Detected when `package-lock.json` exists or as fallback
|
|
100
|
+
|
|
101
|
+
## Examples
|
|
102
|
+
|
|
103
|
+
### With specific Node version
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
- uses: codfish/actions/setup-node-and-install@v1
|
|
107
|
+
with:
|
|
108
|
+
node-version: '18'
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### With pnpm in subdirectory
|
|
112
|
+
|
|
113
|
+
```yaml
|
|
114
|
+
- uses: codfish/actions/setup-node-and-install@v1
|
|
115
|
+
with:
|
|
116
|
+
working-directory: './frontend'
|
|
117
|
+
install-options: '--frozen-lockfile'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### With custom cache key
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
- uses: codfish/actions/setup-node-and-install@v1
|
|
124
|
+
with:
|
|
125
|
+
cache-key-suffix: '-${{ github.head_ref }}'
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Migrating
|
|
129
|
+
|
|
130
|
+
Replace multiple setup steps with this single action:
|
|
131
|
+
|
|
132
|
+
```diff
|
|
133
|
+
- - uses: actions/setup-node@v4
|
|
134
|
+
- with:
|
|
135
|
+
- node-version-file: '.nvmrc'
|
|
136
|
+
- cache: 'npm'
|
|
137
|
+
- - run: npm ci --prefer-offline --no-audit
|
|
138
|
+
+ - uses: codfish/actions/setup-node-and-install@v1
|
|
139
|
+
```
|