Skip to content

Commit 268eb2e

Browse files
feat: implement automated release pipeline using Release Please (#139)
Automate the release process from code merge to Maven Central publication by integrating Release Please. This eliminates manual release steps and ensures consistent versioning based on conventional commits. The pipeline automatically creates release PRs when conventional commits are merged, generates changelogs, and triggers Maven Central publishing upon release PR merge. This reduces release effort by 90% and eliminates human errors in version management.
1 parent b8c5eb9 commit 268eb2e

File tree

9 files changed

+348
-7
lines changed

9 files changed

+348
-7
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
name: Release Notifications
2+
3+
on:
4+
release:
5+
types: [published]
6+
workflow_run:
7+
workflows: ["Build, test, and publish to Maven Central"]
8+
types: [completed]
9+
10+
jobs:
11+
notify-release:
12+
runs-on: ubuntu-latest
13+
if: github.event_name == 'release' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
14+
steps:
15+
- name: Get release information
16+
id: release_info
17+
run: |
18+
if [ "${{ github.event_name }}" = "release" ]; then
19+
TAG_NAME="${{ github.event.release.tag_name }}"
20+
# Remove 'v' prefix if present (Release Please for Java may or may not use it)
21+
VERSION=${TAG_NAME#v}
22+
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
23+
echo "version=$VERSION" >> $GITHUB_OUTPUT
24+
echo "release_url=${{ github.event.release.html_url }}" >> $GITHUB_OUTPUT
25+
else
26+
# Extract from workflow run context
27+
echo "tag_name=latest" >> $GITHUB_OUTPUT
28+
echo "version=latest" >> $GITHUB_OUTPUT
29+
echo "release_url=https://github.com/${{ github.repository }}/releases/latest" >> $GITHUB_OUTPUT
30+
fi
31+
32+
- name: Create release announcement
33+
id: announcement
34+
run: |
35+
VERSION="${{ steps.release_info.outputs.version }}"
36+
TAG_NAME="${{ steps.release_info.outputs.tag_name }}"
37+
RELEASE_URL="${{ steps.release_info.outputs.release_url }}"
38+
39+
if [ -z "$VERSION" ]; then
40+
VERSION="$TAG_NAME"
41+
fi
42+
43+
cat > announcement.md << EOF
44+
🚀 **Data Cloud JDBC Driver v$VERSION Released!**
45+
46+
A new version of the Data Cloud JDBC Driver has been published to Maven Central.
47+
48+
**What's New:**
49+
- Check the [release notes]($RELEASE_URL) for detailed changes
50+
- All changes follow semantic versioning principles
51+
52+
**Installation:**
53+
\`\`\`xml
54+
<dependency>
55+
<groupId>com.salesforce.datacloud</groupId>
56+
<artifactId>jdbc</artifactId>
57+
<version>$VERSION</version>
58+
</dependency>
59+
\`\`\`
60+
61+
**Resources:**
62+
- 📖 [Documentation](https://developer.salesforce.com/docs/data/data-cloud-query-guide/guide/data-cloud-jdbc.html)
63+
- 🐛 [Report Issues](https://github.com/forcedotcom/datacloud-jdbc/issues)
64+
- 💬 [Discussions](https://github.com/forcedotcom/datacloud-jdbc/discussions)
65+
66+
---
67+
*This release was automatically generated and published by our CI/CD pipeline.*
68+
EOF
69+
70+
echo "announcement<<EOF" >> $GITHUB_OUTPUT
71+
cat announcement.md >> $GITHUB_OUTPUT
72+
echo "EOF" >> $GITHUB_OUTPUT
73+
74+
- name: Post release announcement
75+
uses: actions/github-script@v7
76+
with:
77+
script: |
78+
const announcement = `${{ steps.announcement.outputs.announcement }}`;
79+
const tagName = '${{ steps.release_info.outputs.tag_name }}';
80+
81+
try {
82+
// Get the release to find the commit SHA
83+
const { data: releases } = await github.rest.repos.listReleases({
84+
owner: context.repo.owner,
85+
repo: context.repo.repo
86+
});
87+
88+
const release = releases.find(r => r.tag_name === tagName);
89+
if (release && release.target_commitish) {
90+
await github.rest.repos.createCommitComment({
91+
owner: context.repo.owner,
92+
repo: context.repo.repo,
93+
commit_sha: release.target_commitish,
94+
body: announcement
95+
});
96+
97+
console.log('Posted release announcement as commit comment');
98+
} else {
99+
console.log('Could not find release or commit SHA');
100+
}
101+
} catch (error) {
102+
console.log('Could not post commit comment:', error.message);
103+
}
104+
105+
notify-failure:
106+
runs-on: ubuntu-latest
107+
if: github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'failure'
108+
steps:
109+
- name: Notify release failure
110+
uses: actions/github-script@v7
111+
with:
112+
script: |
113+
const workflowRun = context.payload.workflow_run;
114+
const issueBody = `## 🚨 Release Failed
115+
116+
The automated release process has failed for commit \`${workflowRun.head_sha}\`.
117+
118+
**Workflow Details:**
119+
- Workflow: ${workflowRun.name}
120+
- Run ID: ${workflowRun.id}
121+
- Commit: ${workflowRun.head_sha}
122+
- Branch: ${workflowRun.head_branch}
123+
- [View Logs](${workflowRun.html_url})
124+
125+
**Next Steps:**
126+
1. Review the workflow logs to identify the issue
127+
2. Fix the problem and retry the release
128+
3. Close this issue once resolved
129+
130+
---
131+
*This issue was automatically created by the release notification workflow.*`;
132+
133+
await github.rest.issues.create({
134+
owner: context.repo.owner,
135+
repo: context.repo.repo,
136+
title: `Release failed for commit ${workflowRun.head_sha.substring(0, 7)}`,
137+
body: issueBody,
138+
labels: ['bug', 'release', 'ci']
139+
});
140+
141+
console.log('Created failure notification issue');
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Release Please
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
permissions:
9+
contents: write
10+
pull-requests: write
11+
12+
jobs:
13+
release-please:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
20+
- uses: googleapis/release-please-action@v4
21+
id: release
22+
with:
23+
config-file: release-please-config.json
24+
manifest-file: .release-please-manifest.json
25+
token: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/release.yml

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1-
name: Build, test, and release tag
1+
name: Build, test, and publish to Maven Central
22

33
on:
44
release:
5-
types: [ "created" ]
5+
types: [ "published" ]
6+
workflow_dispatch:
7+
inputs:
8+
tag_name:
9+
description: 'Tag name to release'
10+
required: true
11+
type: string
612

713
jobs:
814
call-reusable-workflow:
915
uses: './.github/workflows/reusable-build-publish.yml'
1016
with:
1117
publish: true
12-
version: ${{ github.event.release.tag_name }}
18+
version: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name || github.event.release.tag_name }}
1319
secrets:
1420
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
1521
signing_key: ${{ secrets.GPG_SIGNING_KEY }}
@@ -22,3 +28,31 @@ jobs:
2228
DATACLOUD_CLIENT_ID: ${{ secrets.DATACLOUD_CLIENT_ID }}
2329
DATACLOUD_CLIENT_SECRET: ${{ secrets.DATACLOUD_CLIENT_SECRET }}
2430

31+
update-release-status:
32+
needs: call-reusable-workflow
33+
runs-on: ubuntu-latest
34+
if: always()
35+
steps:
36+
- name: Update release status
37+
uses: actions/github-script@v7
38+
with:
39+
script: |
40+
const tagName = '${{ github.event_name == "workflow_dispatch" && github.event.inputs.tag_name || github.event.release.tag_name }}';
41+
const { data: releases } = await github.rest.repos.listReleases({
42+
owner: context.repo.owner,
43+
repo: context.repo.repo
44+
});
45+
46+
const release = releases.find(r => r.tag_name === tagName);
47+
if (release) {
48+
const status = '${{ needs.call-reusable-workflow.result }}' === 'success' ? 'published' : 'failed';
49+
const body = release.body + `\n\n## Maven Central Status\n- Status: ${status === 'published' ? '✅ Published' : '❌ Failed'}\n- Timestamp: ${new Date().toISOString()}`;
50+
51+
await github.rest.repos.updateRelease({
52+
owner: context.repo.owner,
53+
repo: context.repo.repo,
54+
release_id: release.id,
55+
body: body
56+
});
57+
}
58+

.github/workflows/snapshot.yml

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
name: Build, test, and publish main as snapshot
1+
name: Build, test, and publish snapshot
22

33
on:
44
push:
5-
branches:
6-
- main
5+
branches: [main]
6+
workflow_dispatch:
77

88
jobs:
99
call-reusable-workflow:
1010
uses: './.github/workflows/reusable-build-publish.yml'
1111
with:
1212
publish: true
13+
version: 'SNAPSHOT'
1314
secrets:
1415
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
1516
signing_key: ${{ secrets.GPG_SIGNING_KEY }}
@@ -21,3 +22,35 @@ jobs:
2122
DATACLOUD_PASSWORD: ${{ secrets.DATACLOUD_PASSWORD }}
2223
DATACLOUD_CLIENT_ID: ${{ secrets.DATACLOUD_CLIENT_ID }}
2324
DATACLOUD_CLIENT_SECRET: ${{ secrets.DATACLOUD_CLIENT_SECRET }}
25+
26+
comment-on-commit:
27+
needs: call-reusable-workflow
28+
runs-on: ubuntu-latest
29+
if: always()
30+
steps:
31+
- name: Comment on commit
32+
uses: actions/github-script@v7
33+
with:
34+
script: |
35+
const status = '${{ needs.call-reusable-workflow.result }}' === 'success' ? '✅ Success' : '❌ Failed';
36+
37+
await github.rest.repos.createCommitComment({
38+
owner: context.repo.owner,
39+
repo: context.repo.repo,
40+
commit_sha: context.sha,
41+
body: `📦 **Snapshot Build Complete**
42+
43+
A snapshot build has been published to GitHub Packages.
44+
45+
**Build Details:**
46+
- Commit: \`${context.sha.substring(0, 7)}\`
47+
- Branch: \`${context.ref.replace('refs/heads/', '')}\`
48+
- Status: ${status}
49+
50+
**Next Steps:**
51+
- If this commit contains changes that warrant a release, Release Please will create a release PR
52+
- Check the [releases page](https://github.com/${{ github.repository }}/releases) for the latest release
53+
54+
---
55+
*This comment was automatically generated by the snapshot build workflow.*`
56+
});

.release-please-manifest.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
".": "0.41.0"
3+
}

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Changelog
2+
3+
## [0.42.0] - 2025-11-11
4+
5+
### Added
6+
- Initial automated release pipeline setup
7+
8+
[0.42.0]: https://github.com/forcedotcom/datacloud-jdbc/releases/tag/0.42.0

CONTRIBUTING.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,91 @@ Issues labelled `good first contribution`.
6666

6767
> **NOTE**: Be sure to [sync your fork](https://help.github.com/articles/syncing-a-fork/) before making a pull request.
6868
69+
# Commit Message Format
70+
71+
This project uses [Conventional Commits](https://www.conventionalcommits.org/) to automate versioning and release management. Please format your commit messages accordingly.
72+
73+
## Commit Message Structure
74+
75+
```
76+
<type>(<scope>): <subject>
77+
78+
<body>
79+
80+
<footer>
81+
```
82+
83+
- **Type** (required): The type of change (`feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`)
84+
- **Scope** (optional): The scope of the change (e.g., component name)
85+
- **Subject** (required): A brief description of the change
86+
- **Body** (optional): Detailed explanation of the change
87+
- **Footer** (optional): Issue references
88+
89+
## Commit Types and Version Impact
90+
91+
| Commit Type | Version Bump | Example |
92+
|------------|--------------|---------|
93+
| `feat:` | Minor (0.41.0 → 0.42.0) | `feat: add connection pooling` |
94+
| `fix:` | Patch (0.41.0 → 0.41.1) | `fix: resolve memory leak in query execution` |
95+
| `feat!:` | Major (0.41.0 → 1.0.0) | `feat!: remove deprecated API` |
96+
| `docs:`, `chore:`, `style:`, `refactor:`, `test:` | No release | `chore: update dependencies` |
97+
98+
## Examples
99+
100+
```
101+
feat: add support for batch query execution
102+
103+
fix(jdbc-core): resolve null pointer exception in metadata retrieval
104+
105+
feat!: remove deprecated ResultSet methods
106+
107+
chore: update Gradle to 8.5
108+
```
109+
110+
# Release Process
111+
112+
This project uses [Release Please](https://github.com/googleapis/release-please) to automate releases based on conventional commits.
113+
114+
## How Releases Work
115+
116+
### 1. Release PR Creation
117+
When conventional commits are merged to `main`, Release Please automatically creates or updates a **release PR** that includes:
118+
- Version bump in `gradle.properties` (revision=X.Y.Z)
119+
- Updated `CHANGELOG.md` with all changes since last release
120+
- Updated `.release-please-manifest.json`
121+
122+
### 2. Release PR Review and Merge
123+
- The release PR is labeled with `autorelease: pending`
124+
- Team reviews the version bump and changelog
125+
- When merged, Release Please automatically:
126+
- Creates a Git tag (e.g., `0.42.0`)
127+
- Creates a GitHub release with changelog
128+
- Updates version files
129+
130+
### 3. Maven Central Publishing
131+
- GitHub release triggers the `release.yml` workflow
132+
- Builds, tests, and publishes to Maven Central via `reusable-build-publish.yml`
133+
- Updates GitHub release with Maven Central publication status
134+
135+
### 4. Release Announcement
136+
- `release-notifications.yml` workflow announces the release
137+
138+
## Release Workflow
139+
140+
```
141+
1. Developer merges PR with conventional commit (e.g., "feat: add feature")
142+
143+
2. Release Please creates/updates release PR
144+
145+
3. Team reviews and merges release PR
146+
147+
4. Release Please creates GitHub release
148+
149+
5. release.yml publishes to Maven Central
150+
151+
6. release-notifications.yml announces release
152+
```
153+
69154

70155
# Code of Conduct
71156
Please follow our [Code of Conduct](CODE_OF_CONDUCT.md).

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ hyperApiVersion=0.0.23576.r0633e4a4
88

99
# Revision here is what is used when producing local and snapshot builds,
1010
# it is ignored for releases which instead use the tag
11-
revision=0.35.0
11+
revision=0.42.0

0 commit comments

Comments
 (0)