Developer Workflow
Contributing to ClusterCockpit using GitHub Flow with git rebase.
Overview
ClusterCockpit uses GitHub Flow: main is always deployable, all work
happens on short-lived feature branches, and every change lands via a pull
request. History is kept linear by rebasing — no merge commits from feature
branches.
| Branch | Purpose |
|---|
main | Active development; source for the next release |
release/v1.x | Persistent; created per minor release, receives backported fixes |
External contributors fork the repository and add the upstream as a second
remote. Core team members clone upstream directly and replace upstream
with origin in the commands below.
One-time git configuration
git config --global pull.rebase true
git config --global rebase.autoStash true
Branch Naming
<type>/<issue-number>-<short-description>
| Prefix | Use for |
|---|
feat/ | New features |
fix/ | Bug fixes |
sec/ | Security fixes |
doc/ | Documentation |
fix/backport- | Backports to a release branch |
Examples: feat/123-auto-job-tagging, fix/backport-423-wal-rotation-v1.3
Persistent release branches: release/v1.x (minor version only).
Development Cycle
Sync main before starting:
git fetch upstream && git rebase upstream/main
Branch off main:
git checkout -b feat/123-my-feature main
Commit freely — informal messages are fine during development; they will
be cleaned up before the PR. Reference issue numbers where relevant.
Push, and use --force-with-lease (never --force) after any rebase:
git push -u origin feat/123-my-feature
git push --force-with-lease origin feat/123-my-feature # after rebase
Rebase onto main whenever the base branch has moved:
git fetch upstream && git rebase upstream/main
Interactive Rebase Before Opening a PR
Clean up the branch history so each commit is a logical unit with a proper
prefix message before requesting review:
git rebase -i upstream/main
Squash WIP commits with squash / fixup, polish messages with reword.
See Commit Message Conventions for the
required prefixes. Push with --force-with-lease afterwards.
Pre-PR Checklist
Pull Requests
Target main for new work; target release/v1.x for backports.
- Title: use commit prefix conventions (
feat: …, fix: …) - Description: what and why; how to test; closing keyword (
Fixes #423) - Use GitHub Draft status for work in progress
- Keep PRs small
Reference
1 - Commit Message Conventions
Prefix tags for release notes, issue linking, and goreleaser integration
Introduction
ClusterCockpit uses goreleaser for building and
uploading releases. Release notes are generated automatically from commit
messages using the prefix tags described below. GitHub also parses special
keywords to link commits and PRs to issues.
Before opening a pull request, use interactive rebase
to clean up your branch history and ensure the final commit messages follow
these conventions.
Release Note Prefixes
Commits carrying one of the following prefixes appear in the generated release
notes:
| Prefix | Appears under |
|---|
feat: | New features |
fix: | Bug fixes |
sec: | Security fixes (ClusterCockpit-specific) |
doc: | Documentation updates |
feat dep: or fix dep: | Dependency additions or changes |
Commits without a recognised prefix are not included in the release notes.
Examples:
feat: add automatic job tagging
fix: correct WAL rotation on partial flush (#423)
sec: enforce API token expiry
doc: update rebase workflow guide
feat dep: upgrade to Go 1.22
Referencing Issues
It is good practice to create a GitHub issue for any notable change so that
the motivation and discussion are preserved. Reference an issue in any commit
message or PR description using the #<number> syntax:
This change contributes to #235
Automatically Closing Issues
GitHub closes an issue automatically when a PR containing one of the following
keywords merges into the default branch:
close, closes, closedfix, fixes, fixedresolve, resolves, resolved
The issue is not closed until the commit appears on main. Example:
fix: correct WAL rotation on partial flush
Fixes #423
Place the closing keyword in the PR description rather than a WIP commit
message, since commit messages are often rewritten with interactive rebase
before the PR is merged.
2 - Unit Tests
Go test conventions and how to run the test suite locally
Overview
ClusterCockpit uses the standard Go testing environment. Run the full test
suite locally before pushing to avoid CI failures — this is part of the
pre-PR checklist.
Conventions
- White-box unit tests — tests for internal (unexported) functionality are
placed in the same file as the code under test, within the same package.
- Black-box unit tests — tests for public interfaces are placed in a
separate file named
<package_name>_test.go and belong to the package
<package_name>_test. There is at most one such file per package. - Integration tests — tests that exercise multiple components are also
placed in the
<package_name>_test.go file under the <package_name>_test
package. - Test assets — any required fixture files are placed in a
./testdata/
directory within the package directory.
Running Tests
The project Makefile provides a test target that runs:
go clean -testcache
go build ./...
go vet ./...
go test ./...
Run it with:
You can also run any of the individual commands directly from the command line.
For debugging individual tests, Visual Studio Code has excellent Go test
integration including breakpoint support.
Further Reading
3 - Frontend Development Setup
How to set up cc-backend for fast frontend development iteration
Overview
The cc-backend web frontend is built with Svelte. By default, the compiled
frontend assets are embedded directly in the Go binary. During frontend
development this is impractical — use the setup below to enable fast
rebuild-on-save iteration without recompiling the backend.
For all other aspects of the development process (branching, commits, PRs)
follow the Developer Workflow guide.
Setup
1. Disable embedded static files
In config.json, set:
"embed-static-files": false,
"static-files": "./web/frontend/public/"
This tells cc-backend to serve assets from disk rather than from the embedded
filesystem.
2. Start the frontend build in watch mode
In the ./web/frontend directory:
This starts the Rollup build in listen mode. Whenever you save a source file,
the affected JavaScript targets are rebuilt automatically.
If the output is minified when you expect it not to be, set the production flag
manually in ./web/frontend/rollup.config.mjs:
This should normally be set automatically based on the npm script, but the
override is available if needed.
3. Start cc-backend
From the repository root:
./cc-backend -server -dev
Because assets are served by cc-backend (not a separate dev server), you must
reload the page in your browser manually after each frontend rebuild.
Recommended Terminal Layout
A common setup is three terminals running concurrently:
| Terminal | Directory | Command |
|---|
| 1 | repository root | ./cc-backend -server -dev |
| 2 | ./web/frontend | npm run dev |
| 3 | any | editor / shell for source edits |
4 - Contributing to Documentation
How to set up a local Hugo environment and contribute to the documentation website
The ClusterCockpit documentation is built with Hugo using
the Docsy theme. Pages are written in
Markdown. Hugo wraps them into a static site with navigation, search, and
versioning.
All documentation contributions follow the same branch and PR workflow
described in the Developer Workflow guide. The
repository to fork or clone is
ClusterCockpit/cc-doc.
Local Setup
You need the Hugo extended version (for SCSS support), at minimum Hugo
0.45 — the most recent available version is recommended.
Clone the repository with submodules (required for the Docsy theme):
git clone --recurse-submodules https://github.com/ClusterCockpit/cc-doc.git
cd cc-doc
Start the local development server:
Your site is available at http://localhost:1313/. Hugo watches for file
changes and automatically rebuilds and reloads the page.
Quick Edit via GitHub
For small corrections to an existing page, use the Edit this page link in
the top-right corner of any documentation page. GitHub will prompt you to fork
the repository if you have not already, open the file in edit mode, and guide
you through creating a PR.
Creating an Issue
If you have found a problem but are not ready to fix it yourself, open an issue
in the cc-doc repository.
You can also use the Create Issue button in the top-right corner of any
documentation page.
Useful Resources
5 - Preparing a Release
How to cut new releases and backport fixes using persistent release branches and goreleaser.
Branch Model
ClusterCockpit maintains two types of long-lived branches:
| Branch | Purpose |
|---|
main | Active development; all new features and fixes land here first |
release/v1.x | Persistent branch for a minor release series; receives backported fixes |
A release/v1.x branch is created once when cutting a new minor or major
release and stays open indefinitely. All v1.x.y patch releases are tagged
from this branch. Short-lived fix branches for backports are PRed into the
release branch, not into main.
Every change — including urgent fixes — follows the same feature-branch/PR
workflow described in the Developer Workflow guide.
There is no “hotfix directly to main” shortcut.
Cutting a New Minor or Major Release
1. Prepare main
Ensure all PRs intended for the release are merged and CI is green on main.
2. Create the release branch
git checkout main
git pull --rebase
git checkout -b release/v1.x
git push -u origin release/v1.x
3. Bump the version
On a short-lived branch from release/v1.x, update the version in Makefile
and prepare ReleaseNotes.md:
git checkout -b doc/release-v1.x.0 release/v1.x
# edit Makefile and ReleaseNotes.md
git add Makefile ReleaseNotes.md
git commit -m "doc: prepare release v1.x.0"
git push -u origin doc/release-v1.x.0
Open a PR targeting release/v1.x (not main), get it reviewed, and merge.
4. Tag the release
On a Linux host with repository push access:
git fetch origin
git checkout release/v1.x
git pull --rebase
git tag v1.x.0 -m "release v1.x.0"
git push origin v1.x.0
5. Run goreleaser
Ensure the GITHUB_TOKEN environment variable is set, then:
goreleaser builds the release artifacts, generates release notes from commit
prefixes, and publishes everything to the GitHub Releases page. See
Commit Message Conventions for the prefix
tags that control what appears in the release notes.
6. Post-release
- Verify the release appears correctly on the GitHub Releases page.
- Close the milestone for this release if one was used.
- Announce the release in the appropriate channels.
Patch Releases — Backporting Fixes
Patch releases (v1.x.1, v1.x.2, …) are cut from the release/v1.x
branch after backporting one or more fixes from main.
1. Develop the fix on main first
Follow the standard feature-branch workflow and merge the fix into main via
a PR. Note the commit SHA(s) of the fix once merged.
2. Create a backport branch
git fetch origin
git checkout release/v1.x
git pull --rebase
git checkout -b fix/backport-<issue>-<description>
3. Cherry-pick the fix
git cherry-pick <commit-sha>
If the cherry-pick produces conflicts, resolve them, stage the files, then
continue:
git add <resolved-files>
git cherry-pick --continue
4. Open a PR targeting the release branch
git push -u origin fix/backport-<issue>-<description>
Open the PR on GitHub and set the base branch to release/v1.x, not
main. The PR description should reference the original issue and the commit
SHA that was cherry-picked.
5. Tag the patch release
After the backport PR is merged:
git fetch origin
git checkout release/v1.x
git pull --rebase
git tag v1.x.1 -m "release v1.x.1"
git push origin v1.x.1
goreleaser release
Repeat steps 2–5 for each additional fix that needs to be included in the
patch release before tagging.