Skip to content

Commit 9c915e6

Browse files
kirich1409claude
andauthored
Document non-squash release back-merge procedure in CONTRIBUTING (#284)
Closes #226 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 77aee84 commit 9c915e6

1 file changed

Lines changed: 59 additions & 24 deletions

File tree

CONTRIBUTING.md

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -72,43 +72,78 @@ Featured has **no deprecation or migration window**. Breaking changes are made d
7272
- **Android (Stable):** a breaking public-API change (removed/renamed symbol, changed signature) requires a `MAJOR` version bump.
7373
- **iOS (Preview) and JVM (Preview):** public API may change in `MINOR` releases without a major bump; no migration window is provided.
7474

75+
## Branching Model
76+
77+
| Branch | Role | Merge policy |
78+
|--------|------|--------------|
79+
| `develop` | Integration branch — target of all PRs | Linear history only (squash or rebase merge). Enforced by branch ruleset `required_linear_history`. |
80+
| `main` | Release branch — updated only on releases | Accepts real merge commits (`--no-ff`) pushed directly. No squash restriction. |
81+
82+
| Operation | How |
83+
|-----------|-----|
84+
| Feature / fix PR → `develop` | **Squash merge** (keeps `develop` linear) |
85+
| `develop``main` at release time | **Real merge commit** (`git merge --no-ff`), pushed directly to `main` — not via a PR |
86+
| Back-merge `main``develop` after release | **Squash-PR** into `develop` (bumps version to next `-SNAPSHOT`, syncs `Package.swift`) |
87+
88+
The real merge on release is intentional: it makes all `develop` commits ancestors of `main`, so history never diverges. Using a squash-PR for `develop``main` would collapse those commits and cause `main` to accumulate history that is not in `develop`'s ancestry — requiring periodic manual reconciliation (see issue #226).
89+
7590
## Releasing a New Version
7691

77-
Releases are fully automated via the [publish workflow](.github/workflows/publish.yml). A release is triggered by pushing a version tag to the repository.
92+
Releases are driven by a four-step process. The [publish workflow](.github/workflows/publish.yml) is triggered by a version tag push.
93+
94+
### Step 1 — Prepare the release on `develop`
95+
96+
Update `gradle.properties` to the release version (remove the `-SNAPSHOT` suffix) and update `CHANGELOG.md`. Merge these changes into `develop` via a normal squash-PR.
97+
98+
```properties
99+
# gradle.properties
100+
VERSION_NAME=1.0.0
101+
```
102+
103+
### Step 2 — Merge `develop` into `main` with a real merge commit
104+
105+
Do **not** use a squash-PR here. A squash would collapse `develop`'s commits and cause `main` to diverge from `develop`'s history, requiring periodic manual reconciliation.
78106

79-
### Steps to Release
107+
```bash
108+
git checkout main && git pull
109+
git merge --no-ff develop -m "Release v1.0.0"
110+
git push origin main
111+
```
112+
113+
`main` has no branch ruleset restrictions — a direct push of a merge commit is allowed.
80114

81-
1. **Update the version** in `gradle.properties`:
82-
```properties
83-
VERSION_NAME=1.0.0
84-
```
85-
The version must follow [Semantic Versioning](https://semver.org/) (e.g., `1.0.0`, `1.1.0`, `2.0.0-rc1`).
115+
### Step 3 — Tag `main` and push the tag
86116

87-
2. **Commit the version bump:**
88-
```bash
89-
git add gradle.properties
90-
git commit -m "chore: release v1.0.0"
91-
```
117+
```bash
118+
git tag v1.0.0
119+
git push origin v1.0.0
120+
```
92121

93-
3. **Create and push a version tag:**
94-
```bash
95-
git tag v1.0.0
96-
git push origin v1.0.0
97-
```
98-
The tag must start with `v` followed by a semver string (e.g., `v1.0.0`, `v1.2.3-rc1`).
122+
The tag must start with `v` followed by a semver string (e.g., `v1.0.0`, `v1.2.3-rc1`). Pushing the tag triggers the automated pipeline described below.
99123

100124
### What the Workflow Does
101125

102126
Pushing a `v*` tag triggers the following automated pipeline:
103127

104128
| Step | Description |
105129
|------|-------------|
106-
| **Publish to Maven Central** | Runs on `ubuntu-latest`; signs all artifacts with GPG and publishes them to Maven Central |
130+
| **Publish to Maven Central** | Runs on `ubuntu-latest`; signs all artifacts with GPG and uploads the deployment bundle to Sonatype Central Portal as `USER_MANAGED` — the deployment must be promoted to release manually in the Portal UI |
107131
| **Build XCFramework** | Runs on `macos-latest`; assembles `FeaturedCore.xcframework.zip` |
108132
| **Create GitHub Release** | Creates a GitHub Release for the tag with auto-generated release notes and attaches the XCFramework zip |
109-
| **Update `Package.swift`** | Computes the XCFramework checksum and commits an updated `Package.swift` back to `main` |
133+
| **Update `Package.swift`** | Computes the XCFramework checksum and commits an updated `Package.swift` directly to `main` |
134+
135+
SNAPSHOT versions can be published manually via `workflow_dispatch` on the Actions tab (branch-push SNAPSHOT publishing is disabled — the Central Portal namespace does not have SNAPSHOT deployment enabled).
136+
137+
### Step 4 — Back-merge `main` into `develop`
138+
139+
After the tag is pushed and the workflow has committed the updated `Package.swift` to `main`, open a PR from `main` into `develop`. This PR should:
140+
141+
- Bump `VERSION_NAME` in `gradle.properties` to the next development version (e.g., `1.1.0-SNAPSHOT`).
142+
- Include the `Package.swift` update committed by the workflow bot.
143+
144+
Merge this PR as a **squash merge** to keep `develop` linear.
110145

111-
Pushes to `main` (without a tag) publish a `-SNAPSHOT` version to Maven Central for integration testing.
146+
This back-merge does not introduce history divergence: because Step 2 used a real merge commit, all `develop` commits are already ancestors of `main`. The back-merge PR only carries the version bump and the bot-generated `Package.swift` change — it is not "returning" lost history.
112147

113148
### Required GitHub Secrets
114149

@@ -128,14 +163,14 @@ The following secrets must be configured in the repository settings under **Sett
128163
- The tag name must be the version prefixed with `v` (e.g., tag `v1.0.0` → published version `1.0.0`)
129164
- The tag name must match the `VERSION_NAME` in `gradle.properties` (with the `v` prefix stripped)
130165
- Pre-release versions are supported (e.g., `v1.0.0-rc1`, `v1.0.0-beta2`)
131-
- Development snapshots on `main` use the `-SNAPSHOT` suffix (e.g., `0.1.0-SNAPSHOT`)
166+
- Development snapshots use the `-SNAPSHOT` suffix (e.g., `1.1.0-SNAPSHOT`) and are published manually via `workflow_dispatch`
132167

133168
## Submitting Changes
134169

135-
1. Fork the repository and create a branch from `main`.
170+
1. Fork the repository and create a branch from `develop`.
136171
2. Make your changes in a focused, single-purpose commit or small series of commits.
137172
3. Ensure all tests pass and `spotlessCheck` is clean.
138-
4. Open a pull request against `main` with a clear description of what changed and why.
173+
4. Open a pull request against `develop` with a clear description of what changed and why.
139174

140175
## Module Overview
141176

0 commit comments

Comments
 (0)