Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions .github/workflows/release-branch-bump.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
name: Bump Patch Version on Release Branch

# Manually bumps package.json + package-lock.json (root and
# packages/git-proxy-cli) to the next patch version on existing release
# branch, and opens a PR against that branch

# This is needed since patch releases don't have an easy way to trigger the bump
# and people may forget to bump the versions manually in the patch PR
on:
workflow_dispatch:
inputs:
line:
description: 'Release branch to patch in X.Y format, e.g. 2.0 (targets release/2.0)'
required: true

permissions:
contents: write
pull-requests: write

concurrency:
group: patch-bump-${{ inputs.line }}
cancel-in-progress: false

jobs:
bump-patch:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

- name: Validate release line
id: line
env:
LINE: ${{ inputs.line }}
run: |
if [[ ! "$LINE" =~ ^[0-9]+\.[0-9]+$ ]]; then
echo "::error::'$LINE' isn't a release line in X.Y form (expected e.g. '2.0')."
exit 1
fi
echo "line=$LINE" >> "$GITHUB_OUTPUT"
echo "branch=release/$LINE" >> "$GITHUB_OUTPUT"

- name: Checkout release branch
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ steps.line.outputs.branch }}

- name: Setup Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: '24'

- name: Confirm both packages exist on this branch
run: |
if [ ! -f packages/git-proxy-cli/package.json ]; then
echo "::error::packages/git-proxy-cli/package.json doesn't exist on this branch. It probably predates the CLI package split, bump this one by hand."
exit 1
fi

- name: Compute next patch version
id: version
env:
LINE: ${{ steps.line.outputs.line }}
run: |
CURRENT=$(node -p "require('./package.json').version")

if [[ ! "$CURRENT" =~ ^([0-9]+\.[0-9]+)\.([0-9]+)$ ]]; then
echo "::error::root package.json version '$CURRENT' isn't a plain X.Y.Z, can't compute a patch bump automatically."
exit 1
fi

CURRENT_LINE="${BASH_REMATCH[1]}"
PATCH="${BASH_REMATCH[2]}"

if [ "$CURRENT_LINE" != "$LINE" ]; then
echo "::error::release/$LINE's package.json is on $CURRENT, which isn't on the $LINE line. Refusing to guess, sort this out by hand first."
exit 1
fi

VERSION="$CURRENT_LINE.$((PATCH + 1))"
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Current: $CURRENT -> Next patch: $VERSION"

- name: Check packages are in sync
env:
ROOT_VERSION: ${{ steps.version.outputs.current }}
run: |
CLI_VERSION=$(node -p "require('./packages/git-proxy-cli/package.json').version")
if [ "$CLI_VERSION" != "$ROOT_VERSION" ]; then
echo "::error::packages/git-proxy-cli is on $CLI_VERSION but root is on $ROOT_VERSION. Versions have already drifted, resolve by hand before bumping."
exit 1
fi

- name: Bump root package
env:
VERSION: ${{ steps.version.outputs.version }}
run: npm version "$VERSION" --no-git-tag-version --allow-same-version

- name: Bump git-proxy-cli package
working-directory: packages/git-proxy-cli
env:
VERSION: ${{ steps.version.outputs.version }}
run: npm version "$VERSION" --no-git-tag-version --allow-same-version

- name: Open patch bump PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.version }}
CURRENT: ${{ steps.version.outputs.current }}
BASE_BRANCH: ${{ steps.line.outputs.branch }}
run: |
BRANCH="chore/patch-bump-$VERSION"

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

git checkout -b "$BRANCH"
git add package.json package-lock.json packages/git-proxy-cli/package.json packages/git-proxy-cli/package-lock.json
git commit -m "chore: bump version to $VERSION"
git push -u origin "$BRANCH" --force

BODY=$(printf 'Automated patch bump on `%s`: `%s` -> `%s`.\n\nBumps:\n- `package.json` / `package-lock.json`\n- `packages/git-proxy-cli/package.json`\n\nOnce merged, this drafts a release on `%s`. Please publish it immedietaly, otherwise the draft release will get overwritten. Then, cascade-merge it forward into the next supported release branch (bumping that line too) if one exists.\nFor more details see our guide on [How to Publish a Patch Release](https://git-proxy.finos.org/docs/development/releases/#publishing-a-patch-release).\n' "$BASE_BRANCH" "$CURRENT" "$VERSION" "$BASE_BRANCH")

if gh pr view "$BRANCH" >/dev/null 2>&1; then
echo "PR for $BRANCH already exists. Branch updated, no new PR opened."
else
gh pr create --base "$BASE_BRANCH" --head "$BRANCH" --title "chore: bump version to $VERSION" --body "$BODY"
fi
110 changes: 110 additions & 0 deletions .github/workflows/version-bump.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: Bump Package Version

# Can be triggered by closing a milestone or manually
on:
milestone:
types: [closed]
workflow_dispatch:
inputs:
version:
description: 'Version to bump to, e.g. 2.2.0'
required: true

permissions:
contents: write
pull-requests: write

concurrency:
group: version-bump-${{ github.event.milestone.number || github.run_id }}
cancel-in-progress: false

jobs:
bump-version-${{ inputs.branch }}:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

- name: Checkout main
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: main

- name: Setup Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: '24'

- name: Resolve version
id: version
env:
MILESTONE_TITLE: ${{ github.event.milestone.title }}
MANUAL_VERSION: ${{ inputs.version }}
run: |
RAW="${MANUAL_VERSION:-$MILESTONE_TITLE}"
RAW="$(printf '%s' "$RAW" | tr -d '[:space:]')"

if [[ ! "$RAW" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::'$RAW' isn't a plain semver version (for example '2.2.0'). Not bumping."
exit 1
fi

VERSION="${RAW#v}"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Resolved version: $VERSION"

- name: Sanity check against current version
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
CURRENT=$(node -p "require('./package.json').version")
NEWEST=$(printf '%s\n%s\n' "$CURRENT" "$VERSION" | sort -V | tail -n1)
if [ "$VERSION" = "$CURRENT" ] || [ "$NEWEST" != "$VERSION" ]; then
echo "::error::$VERSION is not newer than the current package.json version ($CURRENT)."
exit 1
fi

- name: Bump root package
env:
VERSION: ${{ steps.version.outputs.version }}
run: npm version "$VERSION" --no-git-tag-version --allow-same-version

- name: Bump git-proxy-cli package
working-directory: packages/git-proxy-cli
env:
VERSION: ${{ steps.version.outputs.version }}
run: npm version "$VERSION" --no-git-tag-version --allow-same-version

- name: Open version bump PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.version }}
MILESTONE_TITLE: ${{ github.event.milestone.title }}
MILESTONE_URL: ${{ github.event.milestone.html_url }}
run: |
BRANCH="chore/bump-version-$VERSION"
RELEASE_BRANCH="release/${VERSION%.*}"

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

git checkout -b "$BRANCH"
git add package.json package-lock.json packages/git-proxy-cli/package.json
git commit -m "chore: bump git-proxy and git-proxy-cli to $VERSION"
git push -u origin "$BRANCH" --force

if [ -n "$MILESTONE_URL" ]; then
SOURCE_LINE="Triggered by closing milestone **$MILESTONE_TITLE** ($MILESTONE_URL)."
else
SOURCE_LINE="Triggered manually for version $VERSION."
fi

BODY=$(printf '%s\n\nBumps:\n- `package.json`, `package-lock.json`\n- `packages/git-proxy-cli/package.json`\n\nOnce merged, cut `%s` from `main` per our [release process](https://git-proxy.finos.org/docs/development/releases) in order to generate a GitHub draft release.\n' "$SOURCE_LINE" "$RELEASE_BRANCH")

if gh pr view "$BRANCH" >/dev/null 2>&1; then
echo "PR for $BRANCH already exists. Branch updated, no new PR opened."
else
gh pr create --base main --head "$BRANCH" --title "chore: bump version to $VERSION" --body "$BODY"
fi
31 changes: 25 additions & 6 deletions website/docs/development/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

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.

## TL;DR on how to release

1. When all issues in a milestone are completed, close it. This will generate a PR that bumps the project version
2. Review and merge the `package.json` bump
3. Make a branch from `main` called `release/X.Y`. This will generate a draft release, visible on [our Releases page](https://github.com/finos/git-proxy/releases).
4. Check the release notes, then click Publish. This will automatically [publish to NPM](https://www.npmjs.com/package/@finos/git-proxy).
5. Verify that NPM release was published correctly.

## Branching Strategy

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.
Expand Down Expand Up @@ -51,6 +59,14 @@ Keep in mind that maintainers may adjust the labels manually if appropriate.

## Release Process

The release process varies depending on whether you're cutting a new release or patching an existing one. See the section below on [How to Patch an Existing Release](#publishing-a-patch-release).

### New release

For new releases, you must first close the [related milestone](https://github.com/finos/git-proxy/milestones). This will generate a PR bumping `package.json`, `package-lock.json` and `packages/git-proxy-cli/package.json`. Once that PR is merged into `main`, make a new branch from `main` called `release/X.Y`, where `X` is the major version and `Y` is the minor version to which you want to bump.

This will generate a draft release in our [Releases](https://github.com/finos/git-proxy/releases) page. Edit the release notes if necessary, and then click Publish. This will also automatically publish to NPM.

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).

### Release Drafter
Expand All @@ -72,7 +88,7 @@ Publishing creates a git tag, and triggers the `npm.yml` workflow described belo

### Publish to NPM

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.
This workflow runs whenever you publish a GitHub release. A PR bumping the `package.json` versions will be automatically generated via the [Bumper Workflow](https://github.com/finos/git-proxy/blob/main/.github/workflows/version-bump.yml) when closing the milestone. If for any reason the workflow failed, or got the version wrong, you can make your own PR bumping `./package.json`, `./package-lock.json` and `./packages/git-proxy-cli/package.json`, or [trigger the workflow manually](https://github.com/finos/git-proxy/actions/workflows/version-bump.yml) with your desired version.

As detailed in [`npm.yml`](https://github.com/finos/git-proxy/blob/main/.github/workflows/npm.yml), it:

Expand All @@ -88,7 +104,10 @@ As detailed in [`npm.yml`](https://github.com/finos/git-proxy/blob/main/.github/

For patch releases, the same process applies. The only difference is that first we must perform the cascade-based merge flow from above:

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.
1. Make a PR against the release branch for the **earliest supported version** where the bug can be reproduced. The PR must include the bugfix, and also bump `package.json`, `package-lock.json` and `packages/git-proxy-cli/package.json` to the next patch version. Note that deprecated versions need not inherit the fix.
2. Merge the PR. If you forgot to bump `package.json`, you can generate a patch bump PR by using [this release branch bumper workflow](https://github.com/finos/git-proxy/actions/workflows/release-branch-bump.yml). Just set the branch version you want to target, such as `2.2`.
3. Once the bugfix and the version bump is merged, a draft release will be generated in our [Releases page](https://github.com/finos/git-proxy/releases). **Publish this release** before merging additional PRs to the release branch, or performing the cascade merges. **Don't forget to uncheck the "Set as latest release" default option** when publishing the release.
4. Once a patch has been published, you can cascade-merge the release branch into the next supported version. For example, if the currently supported versions are `2.2` and `3.0`, we will first publish a patch on `2.2` and then merge (AKA cascade) `release/2.2` into `release/3.0`. Then, we repeat the version bumping and publishing process.

### Cheatsheet and Troubleshooting

Expand All @@ -97,7 +116,7 @@ The flows are roughly as follows:
#### Releases

1. You merge new PRs to `main`
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.
2. When a milestone is closed, a PR is autogenerated to bump the GitProxy version on all of the relevant `package.json` files. This is necessary for correctly publishing to NPM.
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
4. Upon creating the branch, a draft release is automatically generated.
5. Review the draft release notes, and click Publish if appropriate. This will automatically build and publish to NPM
Expand All @@ -106,10 +125,10 @@ The flows are roughly as follows:

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`.
2. Fix the bug and make a PR against the relevant version
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`)
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`). If you forgot to bump, you can easily generate a PR for it by using [release-branch-bump.yml](https://github.com/finos/git-proxy/actions/workflows/release-branch-bump.yml)
4. Once the PR is merged, a draft release will be generated to bump the patch version
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**
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`)
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 too** (ex: `2.2.0` -> `2.2.1`)
7. Repeat steps 4-6 until all the supported versions have been published to both GitHub and NPM

#### Troubleshooting
Expand All @@ -122,7 +141,7 @@ Just re-run the `release-drafter.yml` workflow from the related release branch.

##### I published the draft release and forgot to bump the `package.json`s!

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.
Usually, this will fail to publish to NPM at all, though it still generates a bad GitHub release. Just delete the bad release from the [GitHub releases page](https://github.com/finos/git-proxy/releases), delete the old related tag, then use the [release branch bumper workflow](https://github.com/finos/git-proxy/actions/workflows/release-branch-bump.yml) to make a PR against the relevant `release/X.Y` branch bumping the `package.json` versions. Once that 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.

## Questions

Expand Down
Loading