|
| 1 | +# GitProxy Releases |
| 2 | + |
| 3 | +GitProxy has a standardized release process to ensure they are done in a timely manner, and to prevent extensive merge conflicts. We encourage contributors to read this before opening a PR. |
| 4 | + |
| 5 | +## Branching Strategy |
| 6 | + |
| 7 | +GitProxy follows a [GitLabFlow branching strategy](https://about.gitlab.com/topics/version-control/what-is-gitlab-flow/) with **cascade-based merging** for bugfixes as opposed to cherry-picking. [This blogpost](https://blog.joshuins.com/implementing-cascading-merges-in-github-actions-part-1-99a907e566f3) explains in more detail how cascade-based merging works. See this [StackExchange discussion](https://softwareengineering.stackexchange.com/questions/460758/is-cascade-merging-forward-porting-riskier-than-backporting) for pros and cons of cascade-based merging. |
| 8 | + |
| 9 | +There are two types of long-lived branches in GitProxy: `main`, and release branches named `release/X.Y` (example: `release/2.1`). The `main` branch is where we integrate any new PRs. Every feature, refactor, dependency bump, documentation improvement, etc., is opened as a PR against `main`. |
| 10 | + |
| 11 | +When a milestone reaches completion, we cut a `release/X.Y` branch from `main`, and from that point **the branch's scope is frozen**. Only bugfixes are allowed into the release branch. |
| 12 | + |
| 13 | +When a bug affecting a released version is reported, we **open a PR against the oldest supported release branch containing the bug**. After the fix is merged to that branch, we **merge it forward**. Suppose we support the following versions: `1.1`, `2.2` and `3.0`. If a bug affecting `2.2` is discovered, we first merge the fix into `2.2`. Then, we merge `2.2` into `3.0`, which ensures that all affected, supported versions inherit the bugfix. Since commit identity is preserved, a single SHA exists on all branches the fix was applied to. We can then easily audit that a particular version contains a fix by using `git merge-base --is-ancestor`. |
| 14 | + |
| 15 | +Features and changes other than bugfixes never go into a release branch. If you want a feature included in the next minor release, you should target `main` so it gets picked up in the next milestone. |
| 16 | + |
| 17 | +## Release Planning |
| 18 | + |
| 19 | +GitProxy uses [GitHub milestones](https://github.com/finos/git-proxy/milestones) to plan minor and major releases. Milestones have a fixed scope and a target deadline which we agree upon during our fortnightly [Community Meeting](https://github.com/finos/git-proxy#contact). The deadline serves as the intended release date, and allows GitProxy to keep a predictable cadence. |
| 20 | + |
| 21 | +Historically, GitProxy has had [trouble with scope creep](https://github.com/finos/git-proxy/discussions/1442) delaying releases. To remediate this, new features are added to future milestones rather than existing ones, save for rare exceptions. When every issue in a milestone is closed, we create the corresponding `release/X.Y` branch from `main` and the scope is officially frozen. |
| 22 | + |
| 23 | +Patch releases, on the other hand, are excluded from milestone planning as they follow the cascade-merge process described earlier. This means we can ship fixes as soon as they're completed regardless of the planning cycle. |
| 24 | + |
| 25 | +## Versioning |
| 26 | + |
| 27 | +GitProxy follows [Semantic Versioning](https://semver.org/): |
| 28 | + |
| 29 | +- Breaking changes: bump the major version (`1.4.5` bumps to `2.0.0`) |
| 30 | +- Backward-compatible features: bump the minor version (`1.4.5` bumps to `1.5.0`) |
| 31 | +- Bugfixes, maintenance, documentation, etc.: bump the patch version (`1.4.5` bumps to `1.4.6`) |
| 32 | + |
| 33 | +Version bumps are computed automatically by our release drafter workflow, based on this [configuration file](https://github.com/finos/git-proxy/blob/main/.github/release-drafter-config.yml). Thus, we encourage contributors to label their PRs and commits appropriately as described below. |
| 34 | + |
| 35 | +## Pull Request Conventions |
| 36 | + |
| 37 | +Both release notes and the release drafter workflow rely on [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/)-style PR titles for automatic tagging. |
| 38 | + |
| 39 | +- `feat:` labels the PR as a feature (minor bump) |
| 40 | +- `fix:` labels the PR as a bugfix (patch bump) |
| 41 | +- `break:` labels the PR as a breaking change (major bump) |
| 42 | +- Adding an exclamation mark (ex: `feat!:`) also labels the PR as a breaking change (major bump) |
| 43 | +- Other prefixes map to the maintenance categories defined in `release-drafter.yml` |
| 44 | + |
| 45 | +Release notes use PR titles directly. They should be written as user-facing changelog entries, in other words: in present tense, descriptive, and free of administrative references (meetings, etc.) or jargon. |
| 46 | + |
| 47 | +- **Good:** fix: handle crashing on empty proxy config |
| 48 | +- **Not good:** fix: bug from last community call |
| 49 | + |
| 50 | +Keep in mind that maintainers may adjust the labels manually if appropriate. |
| 51 | + |
| 52 | +## Release Process |
| 53 | + |
| 54 | +The actual release process is largely automated via two GitHub Actions workflows: [`release-drafter.yml`](https://github.com/finos/git-proxy/blob/main/.github/workflows/release-drafter.yml) and [`npm.yml`](https://github.com/finos/git-proxy/blob/main/.github/workflows/npm.yml). |
| 55 | + |
| 56 | +### Release Drafter |
| 57 | + |
| 58 | +This workflow runs whenever: |
| 59 | + |
| 60 | +1. You create a `release/*` branch from `main` |
| 61 | +2. You push to an existing `release/*` branch |
| 62 | + |
| 63 | +It generates a draft GitHub Release, which is kept up-to-date with a categorised changelog and the right version number. The release drafter action can be customized in [release-drafter-config.yml](https://github.com/finos/git-proxy/blob/main/.github/release-drafter-config.yml). |
| 64 | + |
| 65 | +When a release is ready to ship, a maintainer: |
| 66 | + |
| 67 | +1. Reviews the draft for the relevant `release/*` branch |
| 68 | +2. Edits the release notes if necessary (for instance, adding a migration guide for breaking changes) |
| 69 | +3. Clicks Publish. |
| 70 | + |
| 71 | +Publishing creates a git tag, and triggers the `npm.yml` workflow described below. |
| 72 | + |
| 73 | +### Publish to NPM |
| 74 | + |
| 75 | +This workflow runs whenever you publish a GitHub release. As of writing, we don't have any [automation for bumping the versions](https://github.com/finos/git-proxy/issues/1533) in `/package.json` and `/packages/git-proxy-cli/package.json`. You'll have to manually set these according to the appropriate semantic version generated by the release drafter. |
| 76 | + |
| 77 | +As detailed in [`npm.yml`](https://github.com/finos/git-proxy/blob/main/.github/workflows/npm.yml), it: |
| 78 | + |
| 79 | +1. Builds the package |
| 80 | +2. Detects whether it's the latest version, according to the current `package.json` (e.g.: if the version you're publishing is `2.3`, and the latest on NPM `2.2`) |
| 81 | + |
| 82 | +- If it's the latest version, it gets tagged as `'latest'` which is the default version that gets installed when calling `npx @finos/git-proxy` |
| 83 | +- If it's a maintenance release on an older line, it gets tagged as `release-X.Y` so users can pin it explicitly if needed (either via the tag, or the version itself) |
| 84 | + |
| 85 | +3. Publishes the package to NPM with the tag defined above |
| 86 | + |
| 87 | +### Publishing a Patch Release |
| 88 | + |
| 89 | +For patch releases, the same process applies. The only difference is that first we must perform the cascade-based merge flow from above: |
| 90 | + |
| 91 | +First, we make a PR against the earliest supported version where the bug can be reproduced. Deprecated versions need not inherit the fix. Then, we open a PR against that version's release branch, ex. `release/2.2`. Once the bugfix has been merged, this automatically generates a draft release with a patch bump (ex. `2.2.0` -> `2.2.1`). We **must publish the release** (to prevent the draft from getting overwritten). Finally, we merge (_cascade_) the `release/2.2` branch into the next supported version, ex. `release/3.0`, and repeat the publishing process. |
| 92 | + |
| 93 | +### Cheatsheet and Troubleshooting |
| 94 | + |
| 95 | +The flows are roughly as follows: |
| 96 | + |
| 97 | +#### Releases |
| 98 | + |
| 99 | +1. You merge new PRs to `main` |
| 100 | +2. When a milestone is completed, make a PR to bump the GitProxy version on all of the relevant `package.json` files. This is necessary for publishing to NPM. |
| 101 | +3. Make a `release/X.Y` branch to freeze feature development. New features and improvements go into `main`, not into the `release/X.Y` branch |
| 102 | +4. Upon creating the branch, a draft release is automatically generated. |
| 103 | +5. Review the draft release notes, and click Publish if appropriate. This will automatically build and publish to NPM |
| 104 | + |
| 105 | +#### Patches |
| 106 | + |
| 107 | +1. Figure out which is the earliest _supported_ version where the bug can be reproduced. Suppose the latest two major versions are supported (`1.20.x` and `2.2.x`), then we only care about reproducing the bug on `release/1.20` and `release/2.2`. |
| 108 | +2. Fix the bug and make a PR against the relevant version |
| 109 | +3. In this PR, you **must** also bump the GitProxy version on all relevant `package.json` files for publishing to NPM (ex: `1.20.0` -> `1.20.1`) |
| 110 | +4. Once the PR is merged, a draft release will be generated to bump the patch version |
| 111 | +5. Review the draft release notes and publish if appropriate. This will automatically **update** the NPM `release-1.20` line with the patched package. **Tip: Don't forget to uncheck the "Set as latest release" default option** |
| 112 | +6. Then, make a PR to merge the `release/1.20` branch into `release/2.2`. **Don't forget to bump the GitProxy version for the new line** (ex: `2.2.0` -> `2.2.1`) |
| 113 | +7. Repeat steps 4-6 until all the supported versions have been published to both GitHub and NPM |
| 114 | + |
| 115 | +#### Troubleshooting |
| 116 | + |
| 117 | +##### My draft release got overwritten! |
| 118 | + |
| 119 | +This usually happens when forgetting to publish the draft for an older release branch (during cascade merges). |
| 120 | + |
| 121 | +Just re-run the `release-drafter.yml` workflow from the related release branch. Example: If your `1.2.x` draft got overwritten, go to the [Release Drafter action page](https://github.com/finos/git-proxy/actions/workflows/release-drafter.yml), click on the run matching the latest `release/1.2` branch, and then click "Re-run all jobs". |
| 122 | + |
| 123 | +##### I published the draft release and forgot to bump the `package.json`s! |
| 124 | + |
| 125 | +Usually, this will fail to publish to NPM at all. Just delete the bad release from the [GitHub releases page](https://github.com/finos/git-proxy/releases), delete the old related tag, then make a PR to the relevant `release/X.Y` branch bumping the `package.json` versions. When the PR is merged, publish the draft release making sure that a new tag is being created. This should trigger the NPM publish workflow and publish the updated package or override the existing one. |
| 126 | + |
| 127 | +## Questions |
| 128 | + |
| 129 | +If you have any questions or suggestions regarding releases, feel free to [open a discussion](https://github.com/finos/git-proxy/discussions). |
0 commit comments