Skip to content

Commit 388dcfc

Browse files
committed
Add release automation
1 parent a034891 commit 388dcfc

4 files changed

Lines changed: 133 additions & 2 deletions

File tree

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,43 @@ Module comparison
158158
| -------------- | -------------------- | ------------------------------------------------- |
159159
| amqp-client.js | 0 | 1743 |
160160
| amqplib | 14 | 6720 (w/o dependencies) |
161+
162+
## Release
163+
164+
This project uses automated release scripts for version management.
165+
166+
### Release Commands
167+
168+
The package.json includes several npm scripts for releasing:
169+
170+
- **`npm run release`** - Performs a patch version bump (e.g., 3.2.1 → 3.2.2) and creates a release
171+
- **`npm run release:minor`** - Performs a minor version bump (e.g., 3.2.1 → 3.3.0) and creates a release
172+
- **`npm run release:major`** - Performs a major version bump (e.g., 3.2.1 → 4.0.0) and creates a release
173+
174+
### What happens during a release
175+
176+
1. **Tests**: All tests are run to ensure everything passes (`preversion`)
177+
2. **Version bump**: The version is updated in `package.json` and `src/amqp-base-client.ts` (`version`)
178+
3. **Changelog update**: The `## [Unreleased]` section is converted to the actual version and date (`postversion`)
179+
4. **Git commit**: A commit is created with the version change and changelog update
180+
5. **Git tag**: An annotated tag is created with the full changelog content as the tag message
181+
6. **Push**: Both the commit and tags are pushed to the remote repository
182+
7. **CI deployment**: The GitHub Actions workflow automatically publishes the new version to npm
183+
184+
### Prerequisites
185+
186+
Before releasing:
187+
188+
1. Add your changes to the `## [Unreleased]` section in `CHANGELOG.md`
189+
2. All tests should pass (`npm test`)
190+
3. The working directory should be clean (no uncommitted changes)
191+
192+
### Automated npm Publishing
193+
194+
When a new tag is pushed (e.g., `v3.3.0`), the GitHub Actions workflow (`.github/workflows/release.yml`) automatically:
195+
196+
- Builds the project
197+
- Publishes the package to npm with public access and provenance
198+
- Creates a GitHub release with browser bundle artifacts
199+
200+
The git tag contains the complete changelog section for that version, including version header, all changes, and PR links. This makes it easy to see what changed in each release directly from the git tag.

eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export default tseslint.config(
1111
tseslint.configs.recommended,
1212
prettierConfig,
1313
{
14-
files: ["examples/*.js"],
14+
files: ["examples/*.js", "scripts/*.js"],
1515
languageOptions: {
1616
globals: {
1717
...globals.node,

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@
4040
"postbuild": "echo '{\"type\": \"commonjs\"}' > lib/cjs/package.json",
4141
"prepare": "npm run build",
4242
"preversion": "npm test",
43-
"version": "sed -i'' \"s/VERSION = .*/VERSION = '$npm_package_version'/\" src/amqp-base-client.ts && git add src/amqp-base-client.ts"
43+
"version": "sed -i'' \"s/VERSION = .*/VERSION = '$npm_package_version'/\" src/amqp-base-client.ts && git add src/amqp-base-client.ts",
44+
"postversion": "node scripts/release-tag.js",
45+
"release": "npm version patch && git push && git push --tags",
46+
"release:minor": "npm version minor && git push && git push --tags",
47+
"release:major": "npm version major && git push && git push --tags"
4448
},
4549
"files": [
4650
"src/",

scripts/release-tag.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env node
2+
3+
import fs from "fs"
4+
import { execSync } from "child_process"
5+
6+
function main() {
7+
// Read package.json to get current version
8+
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"))
9+
const version = pkg.version
10+
11+
console.log(`Creating tag for version ${version}...`)
12+
13+
// Read changelog
14+
let changelog = fs.readFileSync("CHANGELOG.md", "utf8")
15+
16+
// Update [Unreleased] section to current version if it exists
17+
const unreleasedHeader = "## [Unreleased]"
18+
const versionHeader = `## [${version}]`
19+
const today = new Date().toISOString().split("T")[0] // YYYY-MM-DD format
20+
const newVersionHeader = `## [${version}] - ${today}`
21+
22+
if (changelog.includes(unreleasedHeader)) {
23+
console.log("Updating [Unreleased] section to current version...")
24+
changelog = changelog.replace(unreleasedHeader, newVersionHeader)
25+
26+
// Add a new [Unreleased] section at the top for future changes
27+
const changelogLines = changelog.split("\n")
28+
const headerIndex = changelogLines.findIndex((line) => line.startsWith("## ["))
29+
if (headerIndex !== -1) {
30+
changelogLines.splice(headerIndex, 0, "## [Unreleased]", "")
31+
changelog = changelogLines.join("\n")
32+
}
33+
34+
// Write updated changelog back to file
35+
fs.writeFileSync("CHANGELOG.md", changelog, "utf8")
36+
37+
// Stage the changelog file for commit
38+
execSync("git add CHANGELOG.md", { stdio: "inherit" })
39+
console.log("✅ Updated CHANGELOG.md and staged for commit")
40+
}
41+
42+
// Find the section for this version
43+
const startIdx =
44+
changelog.indexOf(newVersionHeader) !== -1 ? changelog.indexOf(newVersionHeader) : changelog.indexOf(versionHeader)
45+
46+
if (startIdx === -1) {
47+
console.error(`Error: Version ${version} not found in CHANGELOG.md`)
48+
process.exit(1)
49+
}
50+
51+
// Find the next version section to know where this version's content ends
52+
const nextVersionIdx = changelog.indexOf("\n## [", startIdx + 1)
53+
54+
// Extract the content for this version
55+
const content = changelog.substring(startIdx, nextVersionIdx === -1 ? undefined : nextVersionIdx).trim()
56+
57+
console.log("Changelog content:")
58+
console.log(content)
59+
console.log()
60+
61+
// Create the git tag with the changelog content as the message
62+
const tagName = `v${version}`
63+
64+
// Check if tag already exists
65+
try {
66+
execSync(`git rev-parse ${tagName}`, { stdio: "pipe" })
67+
console.log(`⚠️ Tag ${tagName} already exists. Skipping tag creation.`)
68+
return
69+
} catch {
70+
// Tag doesn't exist, continue with creation
71+
}
72+
73+
// Escape backticks and other shell special characters in the content
74+
const escapedContent = content.replace(/`/g, "\\`").replace(/\$/g, "\\$")
75+
76+
try {
77+
execSync(`git tag -a ${tagName} -m ${JSON.stringify(escapedContent)}`, {
78+
stdio: "inherit",
79+
})
80+
console.log(`✅ Created tag ${tagName} successfully`)
81+
} catch (error) {
82+
console.error(`❌ Failed to create tag: ${error.message}`)
83+
process.exit(1)
84+
}
85+
}
86+
87+
main()

0 commit comments

Comments
 (0)