Skip to content
Merged
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
48 changes: 43 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ name: Semantic Release
on:
push:
branches: [main]
# Run manually on an older branch to cut a patch release for that line.
workflow_dispatch:
inputs:
force:
description: 'Force a version bump (auto = derive from commits)'
type: choice
options: [auto, patch, minor, major]
default: auto

jobs:
# The same aggregate a pull request has to pass, so the release runs exactly
Expand All @@ -18,7 +26,6 @@ jobs:
# release half-finished.
needs: [run_ci]
runs-on: ubuntu-latest
if: github.ref_name == 'main'
concurrency:
group: ${{ github.workflow }}-release-${{ github.ref_name }}
cancel-in-progress: false
Expand Down Expand Up @@ -56,6 +63,32 @@ jobs:
# We create it ourselves in the next step so that the distributions
# are attached before the release is published. See that step for why.
vcs_release: "false"
force: ${{ inputs.force != 'auto' && inputs.force || '' }}

# python-semantic-release exits successfully without releasing when the
# branch is not in a release group or there is nothing to release. A
# manual run is always meant to release, so fail it rather than show it
# as green.
- name: Check | Fail a manual run that released nothing
if: github.event_name == 'workflow_dispatch' && steps.release.outputs.released != 'true'
run: |
echo "::error::No release was made. Run this on main or release/<name>, with releasable commits."
exit 1
Comment thread
farhan marked this conversation as resolved.
Resolved

# python-semantic-release also skips the release, and exits successfully,
# when the next version is already tagged on another branch, eg. main's
# next minor taken by a release branch. Fail so the skip doesn't look green.
- name: Check | Fail when the next version is tagged on another branch
if: steps.release.outputs.released != 'true'
env:
TAG: ${{ steps.release.outputs.tag }}
run: |
git fetch --tags --quiet
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null \
&& ! git merge-base --is-ancestor "$TAG" HEAD; then
echo "::error::$TAG is already tagged on another branch, so this release was skipped."
exit 1
fi

# Built before the release is created so that its distributions can be
# attached as release assets too. See the release step for why that
Expand Down Expand Up @@ -89,6 +122,7 @@ jobs:
# publishes it -- all within this one command.
gh release create "$TAG" \
--verify-tag \
--latest=${{ github.ref_name == 'main' }} \
--title "$TAG" \
--notes-file "$RUNNER_TEMP/release_notes.md" \
backend-plugin-sample/dist/* \
Expand Down Expand Up @@ -121,7 +155,7 @@ jobs:
# and it won't require reversing the release.
runs-on: ubuntu-latest
needs: release
if: github.ref_name == 'main' && needs.release.outputs.released == 'true'
if: needs.release.outputs.released == 'true'

permissions:
contents: read
Expand All @@ -143,7 +177,7 @@ jobs:
publish_tutor_plugin_to_pypi:
runs-on: ubuntu-latest
needs: release
if: github.ref_name == 'main' && needs.release.outputs.released == 'true'
if: needs.release.outputs.released == 'true'

permissions:
contents: read
Expand All @@ -164,7 +198,7 @@ jobs:
publish_to_npm:
runs-on: ubuntu-latest
needs: release
if: github.ref_name == 'main' && needs.release.outputs.released == 'true'
if: needs.release.outputs.released == 'true'
permissions:
contents: read
id-token: write
Expand All @@ -181,9 +215,13 @@ jobs:
node-version-file: './frontend-plugin-sample/.nvmrc'

- name: Update the package version and publish
# Only releases from main move the `latest` dist-tag; an older line
# (release/ulmo) is published under its own name (ulmo).
env:
DIST_TAG: ${{ github.ref_name == 'main' && 'latest' || github.ref_name }}
run: |
npm ci
npm version --no-git-tag-version ${{ needs.release.outputs.version }}
npm run build
npm publish
npm publish --tag "${DIST_TAG#release/}"
working-directory: './frontend-plugin-sample'
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ This path assumes you already have edx-platform running locally (bare-metal or d

> TODO: a fully local brand-development flow without Tutor (recompile + serve from disk) is not yet documented.

## Releasing from an older release branch

1. Create a `release/<name>` branch from the release tag, e.g. `git switch -c release/ulmo v3.8.7`, and merge the fix into it as a `fix:` commit.
Only branches matching `release/<name>` can be released.
2. Go to **Actions → Semantic Release → Run workflow** and select that branch.
3. Optionally set `force` to `patch`, `minor` or `major`. The default `auto` derives the bump from the commits.

The version comes from the tags on that branch only, so a `fix:` on `release/ulmo` releases `3.8.8` even if `main` is on `3.9.x`.
Backports should land as `fix:` commits, so the line gets a patch and not the next minor that `main` will want. For the same reason, make `main`'s next release after cutting the branch at least a minor bump (a `feat:` commit, or `force: minor`).
The GitHub Release is not marked as latest, and the npm package is published under the `<name>` dist-tag instead of `latest`.

This works only for branches cut from a release that includes this workflow. Older release branches have to be released manually.

See [Multibranch Releases](https://python-semantic-release.readthedocs.io/en/latest/concepts/multibranch_releases.html) for more details.

## Getting help

- Open edX [community Slack](https://openedx.org/slack) and [discussion forums](https://discuss.openedx.org)
Expand Down
8 changes: 8 additions & 0 deletions backend-plugin-sample/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ where = ["src"]
# code and files we need in this repo.
build_command = "pip install build && SETUPTOOLS_SCM_PRETEND_VERSION=$NEW_VERSION python -m build"

[tool.semantic_release.branches.main]
match = "main"

# Release branches (e.g. release/ulmo) can be released by running release.yml
# manually to patch an older release line. The version comes from that branch's tags.
[tool.semantic_release.branches.maintenance]
match = "^release/[a-z]+$"

[tool.semantic_release.commit_parser_options]
# Because this repo is meant to be an example, docs changes are relevant
# feature changes and so should produces new releases.
Expand Down
Loading