11name : Publish
22
3+ # Manual trigger with version input
34on :
4- push :
5- tags :
6- - ' v*'
5+ workflow_dispatch :
6+ inputs :
7+ version :
8+ description : ' Version number (e.g., 1.2.3 - no v prefix)'
9+ required : true
10+ type : string
11+
12+ # Required permissions for version commits, tags, releases, and npm provenance
13+ permissions :
14+ contents : write
15+ id-token : write
716
817jobs :
18+ # Job 1: Validate input and create version commit
19+ create-version-commit :
20+ name : Create Version Commit
21+ runs-on : ubuntu-latest
22+ outputs :
23+ commit_sha : ${{ steps.commit.outputs.sha }}
24+ steps :
25+ - name : Checkout
26+ uses : actions/checkout@v4
27+ with :
28+ fetch-depth : 0
29+ token : ${{ secrets.GITHUB_TOKEN }}
30+
31+ - name : Setup Node.js
32+ uses : actions/setup-node@v4
33+ with :
34+ node-version : ' 20'
35+ cache : ' yarn'
36+
37+ - name : Validate version format
38+ run : |
39+ VERSION="${{ github.event.inputs.version }}"
40+ if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
41+ echo "::error title=Invalid Version Format::Version must be in X.Y.Z format (e.g., 1.2.3). Got: $VERSION"
42+ exit 1
43+ fi
44+ echo "✓ Version format valid: $VERSION"
45+
46+ - name : Check for existing tag
47+ run : |
48+ VERSION="${{ github.event.inputs.version }}"
49+ if git rev-parse "v$VERSION" >/dev/null 2>&1; then
50+ echo "::error title=Tag Already Exists::Tag v$VERSION already exists. Please use a different version number."
51+ exit 1
52+ fi
53+ echo "✓ Tag v$VERSION does not exist"
54+
55+ - name : Check for dirty working directory
56+ run : |
57+ if ! git diff-index --quiet HEAD --; then
58+ echo "::error title=Uncommitted Changes::Working directory has uncommitted changes. Please commit or stash them first."
59+ git status
60+ exit 1
61+ fi
62+ echo "✓ Working directory is clean"
63+
64+ - name : Update package.json version
65+ run : |
66+ VERSION="${{ github.event.inputs.version }}"
67+ echo "Updating package.json to version $VERSION"
68+ npm version $VERSION --no-git-tag-version
69+ echo "✓ package.json updated to version $VERSION"
70+
71+ - name : Configure git
72+ run : |
73+ git config user.name "github-actions[bot]"
74+ git config user.email "github-actions[bot]@users.noreply.github.com"
75+
76+ - name : Create version commit
77+ id : commit
78+ run : |
79+ VERSION="${{ github.event.inputs.version }}"
80+ git add package.json
81+ git commit -m "chore(release): bump version to $VERSION"
82+
83+ COMMIT_SHA=$(git rev-parse HEAD)
84+ echo "sha=$COMMIT_SHA" >> $GITHUB_OUTPUT
85+ echo "✓ Version commit created: $COMMIT_SHA"
86+
87+ - name : Push version commit
88+ run : |
89+ git push origin HEAD
90+ echo "✓ Version commit pushed to remote"
91+
92+ # Job 2: Create and push version tag
93+ create-version-tag :
94+ name : Create Version Tag
95+ runs-on : ubuntu-latest
96+ needs : create-version-commit
97+ steps :
98+ - name : Checkout
99+ uses : actions/checkout@v4
100+ with :
101+ fetch-depth : 0
102+ ref : ${{ github.ref }}
103+ token : ${{ secrets.GITHUB_TOKEN }}
104+
105+ - name : Fetch latest commits
106+ run : |
107+ git fetch origin
108+ git pull origin ${{ github.ref_name }}
109+
110+ - name : Create and push tag
111+ run : |
112+ VERSION="${{ github.event.inputs.version }}"
113+ git tag "v$VERSION"
114+ git push origin "v$VERSION"
115+ echo "✓ Tag v$VERSION created and pushed"
116+
117+ # Job 3: Run full CI validation
118+ run-ci-validation :
119+ name : Run CI Validation
120+ needs : create-version-tag
121+ uses : ./.github/workflows/ci.yml
122+
123+ # Job 4: Publish to NPM (only if CI passes)
9124 publish-npm :
10125 name : Publish to NPM
11126 runs-on : ubuntu-latest
127+ needs : run-ci-validation
12128 permissions :
13129 contents : write
14130 id-token : write
@@ -34,17 +150,23 @@ jobs:
34150 - name : Build library
35151 run : yarn prepare
36152
37- - name : Verify build
153+ - name : Verify build outputs
38154 run : |
39- test -d lib/module || exit 1
40- test -d lib/typescript || exit 1
41- test -d nitrogen/generated || exit 1
155+ echo "Verifying build outputs..."
156+ test -d lib/module || (echo "::error::lib/module directory not found" && exit 1)
157+ test -d lib/typescript || (echo "::error::lib/typescript directory not found" && exit 1)
158+ test -d nitrogen/generated || (echo "::error::nitrogen/generated directory not found" && exit 1)
159+ echo "✓ All build outputs verified"
42160
43161 - name : Publish to NPM
44- run : npm publish --provenance --access public
162+ run : |
163+ echo "Publishing to NPM with provenance..."
164+ npm publish --provenance --access public
165+ echo "✓ Package published to NPM registry"
45166 env :
46167 NODE_AUTH_TOKEN : ${{ secrets.NPM_TOKEN }}
47168
169+ # Job 5: Create GitHub Release with changelog
48170 create-github-release :
49171 name : Create GitHub Release
50172 runs-on : ubuntu-latest
@@ -57,23 +179,66 @@ jobs:
57179 with :
58180 fetch-depth : 0
59181
60- - name : Create Release
61- uses : softprops/action-gh-release@v1
62- with :
63- generate_release_notes : true
64- body : |
65- ## Installation
182+ - name : Generate Release Notes
183+ id : release_notes
184+ run : |
185+ VERSION="${{ github.event.inputs.version }}"
186+ PREVIOUS_TAG=$(git describe --tags --abbrev=0 --exclude="v$VERSION" 2>/dev/null || echo "")
66187
67- ```bash
68- npm install react-native-nitro-device-info
69- # or
70- yarn add react-native-nitro-device-info
71- ```
188+ echo "Generating release notes for v$VERSION..."
189+ echo "Previous tag: $PREVIOUS_TAG"
72190
73- ## Documentation
191+ # Get merged PRs since last tag
192+ if [ -n "$PREVIOUS_TAG" ]; then
193+ PREVIOUS_DATE=$(git log -1 --format=%aI "$PREVIOUS_TAG" 2>/dev/null)
194+ PRS=$(gh pr list --state merged --search "merged:>$PREVIOUS_DATE" --json number,title,author --jq '.[] | "- #\(.number) \(.title) (@\(.author.login))"')
195+ else
196+ PRS=$(gh pr list --state merged --json number,title,author --jq '.[] | "- #\(.number) \(.title) (@\(.author.login))"')
197+ fi
74198
75- - [README](https://github.com/l2hyunwoo/react-native-nitro-device-info#readme)
76- - [API Reference](docs/API.md)
77- - [Migration Guide](docs/MIGRATION.md)
199+ # Create release notes
200+ cat > release_notes.md << EOF
201+ ## What's Changed
202+
203+ EOF
204+
205+ if [ -n "$PRS" ]; then
206+ echo "$PRS" >> release_notes.md
207+ else
208+ echo "- No merged PRs found" >> release_notes.md
209+ fi
210+
211+ cat >> release_notes.md << EOF
212+
213+ ## Installation
214+
215+ ```bash
216+ npm install react-native-nitro-device-info@$VERSION
217+ # or
218+ yarn add react-native-nitro-device-info@$VERSION
219+ ```
220+
221+ ## Documentation
222+
223+ - [README](https://github.com/l2hyunwoo/react-native-nitro-device-info#readme)
224+
225+ **Full Changelog**: https://github.com/l2hyunwoo/react-native-nitro-device-info/compare/$PREVIOUS_TAG...v$VERSION
226+ EOF
227+
228+ # Output the release notes
229+ RELEASE_NOTES=$(cat release_notes.md)
230+ echo "release_notes<<EOF" >> $GITHUB_OUTPUT
231+ echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT
232+ echo "EOF" >> $GITHUB_OUTPUT
233+
234+ echo "✓ Release notes generated"
235+ env :
236+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
237+
238+ - name : Create Release
239+ uses : softprops/action-gh-release@v1
240+ with :
241+ tag_name : v${{ github.event.inputs.version }}
242+ body : ${{ steps.release_notes.outputs.release_notes }}
78243 env :
79244 GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
0 commit comments