-
Notifications
You must be signed in to change notification settings - Fork 52
Release workflows #2260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Release workflows #2260
Changes from 10 commits
cb6cdcd
122f3d0
0aa0e20
e6b55d0
d6d1aa5
50e8cf1
375d6fa
ce2f7c6
864d029
b23c43b
8c62538
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
|
|
||
| # This workflow builds and pushes a Docker image to Docker Hub whenever a | ||
| # GitHub Release is published, tagging it with the release version and, | ||
| # for stable (non-pre-release) releases, also as `latest`. | ||
| # | ||
| # Requires the DOCKERHUB_USERNAME and DOCKERHUB_TOKEN repository secrets | ||
| # to be configured under Settings -> Secrets and variables -> Actions. | ||
|
|
||
| name: Publish Docker image | ||
|
|
||
| on: | ||
| release: | ||
| types: [published] | ||
| workflow_dispatch: | ||
| inputs: | ||
| tag: | ||
| description: "Git tag to build/push (e.g. v1.0.0). Required when run manually." | ||
| required: false | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| docker-publish: | ||
| runs-on: ubuntu-latest | ||
| # Dedicated environments with protections for publishing are strongly recommended. | ||
| # For more information, see: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#deployment-protection-rules | ||
| environment: | ||
| name: dockerhub | ||
| steps: | ||
| - name: Determine version | ||
| id: version | ||
| run: | | ||
| TAG="${{ github.event.release.tag_name || inputs.tag }}" | ||
| if [ -z "$TAG" ]; then | ||
| echo "No tag available (release.tag_name and inputs.tag are both empty)." >&2 | ||
| exit 1 | ||
| fi | ||
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ steps.version.outputs.tag }} | ||
| fetch-depth: 0 # full history + tags, required for hatch-vcs to resolve the version | ||
|
|
||
| - name: Log in to Docker Hub | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| username: ${{ secrets.DOCKERHUB_USERNAME }} | ||
| password: ${{ secrets.DOCKERHUB_TOKEN }} | ||
|
|
||
| - uses: docker/setup-buildx-action@v3 | ||
|
|
||
| - name: Build and push (versioned tag) | ||
| uses: docker/build-push-action@v6 | ||
| with: | ||
| context: . | ||
| file: ./Dockerfile | ||
| push: true | ||
| tags: lfenergy/flexmeasures:${{ steps.version.outputs.tag }} | ||
|
|
||
| - name: Build and push (latest) | ||
| if: ${{ !github.event.release.prerelease }} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know what a release.prerelease event is
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not an event, it's a field — github.event.release.prerelease is a boolean GitHub sets on the release webhook payload, true/false depending on whether "Set as a pre-release" was checked when the GitHub Release was created. Added a comment explaining this inline. |
||
| uses: docker/build-push-action@v6 | ||
| with: | ||
| context: . | ||
| file: ./Dockerfile | ||
| push: true | ||
| tags: lfenergy/flexmeasures:latest | ||
|
|
||
| - name: Summary | ||
| run: | | ||
| echo "Pushed lfenergy/flexmeasures:${{ steps.version.outputs.tag }}" >> "$GITHUB_STEP_SUMMARY" | ||
| if [ "${{ github.event.release.prerelease }}" != "true" ]; then | ||
| echo "Pushed lfenergy/flexmeasures:latest" >> "$GITHUB_STEP_SUMMARY" | ||
| fi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
|
|
||
| # Manually-triggered QA workflow for release testing. It spins up the local | ||
| # docker compose stack, then runs: | ||
| # - the toy tutorials 1-4, using the runner scripts kept in the | ||
| # FlexMeasures/tsc repo (tsc/scripts/run-tutorial*-in-docker.sh) | ||
| # - the HEMS walkthrough, using the example kept in the | ||
| # FlexMeasures/flexmeasures-client repo (examples/HEMS/HEMS_setup.py), | ||
| # including its CLI-based report generation step (see | ||
| # flexmeasures-client's FLEXMEASURES_CLI_CMD/FLEXMEASURES_CLI_CONFIG_DIR | ||
| # support, used below to run that CLI inside the `server` container) | ||
| # | ||
| # This does not replace manual QA (e.g. UI login/graph checks, tutorial 5, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tutorial 5 can be covered, see other comment
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Resolved via the same change — tutorial 5's runner script is now included, so the QA workflow runs tutorials 1-5. |
||
| # exploratory testing) -- see documentation/dev/release_process.rst. | ||
|
|
||
| name: QA (release) | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| tsc-ref: | ||
| description: "Branch/tag/ref to use from FlexMeasures/tsc" | ||
| required: false | ||
| default: "main" | ||
| flexmeasures-client-ref: | ||
| description: "Branch/tag/ref to use from FlexMeasures/flexmeasures-client" | ||
| required: false | ||
| default: "main" | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| env: | ||
| # Picked up by every `docker compose` invocation in this job, so the HEMS | ||
| # configs bind mount (added via qa-hems-override.yml, see below) applies | ||
| # consistently across the `up`, `exec`, and `down` steps. | ||
| COMPOSE_FILE: docker-compose.yml:qa-hems-override.yml | ||
|
|
||
| jobs: | ||
| qa: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout flexmeasures | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Checkout flexmeasures-client | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| repository: FlexMeasures/flexmeasures-client | ||
| ref: ${{ inputs.flexmeasures-client-ref }} | ||
| path: flexmeasures-client | ||
|
|
||
| - name: Add compose override to mount the HEMS configs directory | ||
| run: | | ||
| cat > qa-hems-override.yml <<EOF | ||
| services: | ||
| server: | ||
| volumes: | ||
| - ${{ github.workspace }}/flexmeasures-client/examples/HEMS/configs:/hems-configs:ro | ||
| EOF | ||
|
|
||
| - name: Build and start the docker compose stack | ||
| run: docker compose up --build --detach --wait --wait-timeout 300 | ||
|
|
||
| - name: Checkout tsc tutorial scripts | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| repository: FlexMeasures/tsc | ||
| ref: ${{ inputs.tsc-ref }} | ||
| sparse-checkout: tsc/scripts | ||
| sparse-checkout-cone-mode: false | ||
| path: tsc | ||
|
|
||
| - name: Run tutorials 1-4 | ||
| run: | | ||
| for script in tsc/tsc/scripts/run-tutorial-in-docker.sh \ | ||
| tsc/tsc/scripts/run-tutorial2-in-docker.sh \ | ||
| tsc/tsc/scripts/run-tutorial3-in-docker.sh \ | ||
| tsc/tsc/scripts/run-tutorial4-in-docker.sh; do | ||
| echo "::group::Running $script" | ||
| # The scripts use `docker exec -it`, which requires a tty we don't have here. | ||
| sed 's/docker exec -it/docker exec -i/' "$script" | bash | ||
| echo "::endgroup::" | ||
| done | ||
|
|
||
| - name: Create HEMS admin account and user | ||
| run: | | ||
| OUTPUT=$(docker compose exec -T server flexmeasures add account --name "HEMS Admin Org") | ||
| echo "$OUTPUT" | ||
| ACCOUNT_ID=$(echo "$OUTPUT" | grep -oP '(?<=ID: )\d+') | ||
| docker compose exec -T server bash -c \ | ||
| "printf 'admin\nadmin\n' | flexmeasures add user --username admin --email admin@admin.com --account $ACCOUNT_ID --roles admin" | ||
|
|
||
| - name: Install uv | ||
| uses: astral-sh/setup-uv@v7 | ||
| with: | ||
| version: "0.10.9" | ||
| enable-cache: true | ||
|
|
||
| - name: Run the HEMS example against the running stack | ||
| working-directory: flexmeasures-client | ||
| env: | ||
| # Absolute -f paths, since this subprocess runs from | ||
| # flexmeasures-client/examples/HEMS, not the workspace root where | ||
| # COMPOSE_FILE's relative paths would otherwise resolve. | ||
| FLEXMEASURES_CLI_CMD: "docker compose -f ${{ github.workspace }}/docker-compose.yml -f ${{ github.workspace }}/qa-hems-override.yml exec -T server flexmeasures" | ||
| FLEXMEASURES_CLI_CONFIG_DIR: "/hems-configs" | ||
| run: | | ||
| uv sync | ||
| cd examples/HEMS | ||
| uv run --project .. python3 HEMS_setup.py | ||
|
|
||
| - name: Show stack logs on failure | ||
| if: failure() | ||
| run: docker compose logs | ||
|
|
||
| - name: Tear down the stack | ||
| if: always() | ||
| run: docker compose down --volumes | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| #!/usr/bin/env bash | ||
| # Lists merged PRs since the given (or latest) git tag, to help a human | ||
| # assemble documentation/changelog.rst / cli/change_log.rst / api/change_log.rst | ||
| # entries. Read-only: does not write to any changelog file or commit anything. | ||
| # | ||
| # Usage: bin/list-merged-prs-since-tag.sh [<since-tag>] | ||
| set -euo pipefail | ||
|
|
||
| SINCE="${1:-$(git describe --tags --abbrev=0)}" | ||
| SINCE_DATE=$(git log -1 --format=%aI "$SINCE") | ||
|
|
||
| echo "Merged PRs since $SINCE ($SINCE_DATE):" | ||
| echo | ||
|
|
||
| gh pr list \ | ||
| --repo FlexMeasures/flexmeasures \ | ||
| --state merged \ | ||
| --search "merged:>=$SINCE_DATE" \ | ||
| --limit 200 \ | ||
| --json number,title,url \ | ||
| --template '{{range .}}* {{.title}} [see `PR #{{.number}} <{{.url}}>`_]{{"\n"}}{{end}}' | ||
|
|
||
| echo | ||
| echo "Review, categorize into New features / Infrastructure / Support / Bugfixes," | ||
| echo "and paste into documentation/changelog.rst (and cli/change_log.rst, api/change_log.rst if relevant)." |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,70 @@ | ||||||
| Release process | ||||||
| ================ | ||||||
|
|
||||||
| This page describes how FlexMeasures releases are cut, and which parts are automated versus manual. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I have never seen "cut" used like this, is that usual?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, fixed — changed to "done". |
||||||
| The canonical, most detailed checklist still lives in `FlexMeasures/tsc RELEASE.md <https://github.com/FlexMeasures/tsc/blob/main/RELEASE.md>`_. | ||||||
| This page summarizes that flow and points out where CI now does the mechanical work. | ||||||
|
|
||||||
| Versioning is derived entirely from git tags via ``hatch-vcs`` (see ``pyproject.toml``'s ``[tool.hatch.version]``), so there is no file where a version string needs to be bumped by hand. | ||||||
|
|
||||||
| 1. Prepare and test (manual) | ||||||
| ----------------------------- | ||||||
|
|
||||||
| * Ensure the corresponding GitHub milestone is complete and the version number choice matches the changes made. | ||||||
| * ``uv sync --group dev --group test`` and ``uv run poe test``. | ||||||
| * For MINOR/MAJOR releases, do QA using the Docker Compose stack, and write the accompanying blog post. | ||||||
|
|
||||||
| Tutorials 1-4 and the HEMS walkthrough can be run via the manually-triggered ``QA (release)`` GitHub Actions workflow (``.github/workflows/docker-qa.yml``). It spins up the local docker compose stack and runs the tutorial runner scripts from `FlexMeasures/tsc <https://github.com/FlexMeasures/tsc/tree/main/tsc/scripts>`_ and the HEMS example from `FlexMeasures/flexmeasures-client <https://github.com/FlexMeasures/flexmeasures-client/tree/main/examples/HEMS>`_ (both kept in their own repos, not duplicated here). Trigger it from the Actions tab; it does not run automatically. This does not replace manual UI login/graph checks, tutorial 5, or exploratory QA. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We moved the tutorial runner scripts into the FM repo, at Maybe they should have been deleted from the TSC repo, I'll do that.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done — switched the QA workflow and docs to use documentation/tut/scripts (including tutorial 5) instead of checking out tsc. |
||||||
|
|
||||||
| 2. Assemble the changelog (semi-automated) | ||||||
| ------------------------------------------- | ||||||
|
|
||||||
| Run: | ||||||
|
|
||||||
| .. code-block:: bash | ||||||
|
|
||||||
| uv run poe changelog-check | ||||||
|
|
||||||
| This lists merged PRs since the last tag (via ``ci/list-merged-prs-since-tag.sh``), pre-formatted as changelog bullets. | ||||||
| It is read-only: it does not edit or commit anything. | ||||||
| Review, categorize each entry into *New features* / *Infrastructure / Support* / *Bugfixes*, | ||||||
| and paste them into ``documentation/changelog.rst`` (and ``documentation/cli/change_log.rst`` / ``documentation/api/change_log.rst`` where relevant). | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should only be done if PR IDs are missing at that point, right? Maybe the task can even look that up (missing PR IDs) and suggest which need to still be added.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea, done — the script now cross-references PR numbers already linked in the changelog files and only lists the ones that are still missing. |
||||||
|
|
||||||
| 3. Commit, tag, and release (manual — requires GPG signing) | ||||||
| -------------------------------------------------------------- | ||||||
|
|
||||||
| .. code-block:: bash | ||||||
|
|
||||||
| git commit -S -sam "changelog updates for v<version>" | ||||||
| git push | ||||||
| git tag -s -a v<version> -m "" | ||||||
| git push --tags | ||||||
|
|
||||||
| Then create the GitHub Release from the new tag. This step is intentionally manual: GPG signing requires the maintainer's personal key, which cannot be delegated to CI without exporting private key material into repository secrets. | ||||||
|
|
||||||
| 4. Automated publishing (CI) | ||||||
| ------------------------------- | ||||||
|
|
||||||
| Publishing the GitHub Release (step 3) triggers two workflows: | ||||||
|
|
||||||
| * ``.github/workflows/pypi-publish.yml`` builds the package and publishes it to PyPI via trusted (OIDC) publishing, then runs a ``pypi-smoke-test`` job that installs the just-published version into a fresh virtual environment and verifies ``flexmeasures.__version__`` matches. | ||||||
| * ``.github/workflows/docker-publish.yml`` builds and pushes the Docker image to Docker Hub, tagged ``lfenergy/flexmeasures:v<version>``, and additionally as ``lfenergy/flexmeasures:latest`` for stable (non-pre-release) releases. | ||||||
|
|
||||||
| Check the Actions tab for both workflow runs to confirm they succeeded; also spot-check that the new version shows up on `PyPI <https://pypi.org/project/flexmeasures>`_, `Docker Hub <https://hub.docker.com/r/lfenergy/flexmeasures>`_, and that the ReadTheDocs build for the new tag completed. | ||||||
|
|
||||||
| .. note:: | ||||||
| The ``docker-publish.yml`` workflow can also be re-run manually (``workflow_dispatch``, with a ``tag`` input) if a run needs to be retried. | ||||||
|
|
||||||
| 5. Announce (manual) | ||||||
| ---------------------- | ||||||
|
|
||||||
| Publish the blog post (MINOR/MAJOR only), and announce on Mastodon, the mailing list, and the LF Energy Slack. | ||||||
|
|
||||||
| 6. Post-release (manual, MINOR/MAJOR only) | ||||||
| --------------------------------------------- | ||||||
|
|
||||||
| * Start the next development cycle: an empty, signed commit ``Start v<next-version>`` and tag ``v<next-version>.dev0``. | ||||||
| * Update the milestone on GitHub, and add changelog placeholders for upcoming releases. | ||||||
| * Open a dependency-upgrade PR tagged ``dependency-hygiene``. | ||||||
|
|
||||||
| These are infrequent, low-risk manual steps and involve a judgment call (what the next version number should be), so they are not automated. | ||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically, we don't need to build again, in the RELEASE file we tag the image from the last step as latest, then push that.
I know that Docker will also use the cached layers so the end result is maybe the same, but tagging seems cleaner (pushing the very same image).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, fixed — now a single build-push-action call with both tags, so the exact same image gets pushed under both, no second build.