Thanks for your interest in structalign! This document covers the development workflow, commit conventions, and the release process.
- Go 1.25+ (the floor set by
golang.org/x/tools). git-cliff— to regenerateCHANGELOG.md.goreleaser— to validate/build releases locally.- Python 3 + Pillow — only needed to regenerate the README screenshot.
main.go (module root) is a thin entrypoint; the implementation lives in
pkg/common (contracts) and internal/ (loader, align, layout, ui, app, …).
_example/ holds sample structs used for manual testing and in the docs. The repo
uses Task; the Makefile just delegates to task.
task build # -> ./structalign
task ci # lint, build, test, and a smoke test against ./_example (what CI runs)
task --list # list all tasksRun task ci before pushing — it mirrors the CI job. CI also runs the matrix
across Go 1.25.0 and stable.
When you change diff or inspect output, refresh the README screenshot:
task screenshot # regenerates docs/diff.png (needs python3 + pillow)Commits follow Conventional Commits. This is what drives the changelog, so the prefix matters:
| Prefix | Changelog section |
|---|---|
feat: |
Added |
fix: |
Fixed |
perf: / refactor: / style: / revert: |
Changed |
deprecate: |
Deprecated |
remove: |
Removed |
security: |
Security |
docs: |
Documentation |
chore: / ci: / build: / test: |
(excluded — not user-facing) |
Mark breaking changes with a ! (e.g. feat!:) or a BREAKING CHANGE: footer.
- Branch off
devel(e.g.feature/<topic>); never targetmaindirectly. - Make your change with tests, and run
task ciuntil it's green. - Use Conventional Commits (see above) so the changelog stays accurate.
- Open the PR against
develand fill in the pull-request template. Keep it focused — one logical change per PR. - CI (lint, build + test across the Go matrix, CodeQL, dependency review) must pass; a maintainer then reviews and merges.
main— production; every commit is a tagged release.devel— integration branch and the default branch; day-to-day work targets it.feature/*,release/*,hotfix/*— the usual git-flow temporaries.
Do not commit directly to main.
Releases use git-flow to cut the version and
GoReleaser (triggered by the tag push) to build the
cross-platform binaries and publish the GitHub Release. Example for v0.1.0:
# 1. Start the release branch off devel.
git flow release start v0.1.0
# 2. Stamp the changelog for this version and commit it.
task release TAG=v0.1.0
git add CHANGELOG.md
git commit -m "chore(release): v0.1.0"
# 3. (Recommended) dry-run the release build locally — no publish.
task release-check # validate .goreleaser.yaml
task snapshot # build all artifacts into dist/
# 4. Finish: merges release -> main, tags v0.1.0, back-merges into devel.
git flow release finish v0.1.0
# 5. Push everything, including the tag (the tag is what triggers the release).
git push origin main devel --tags
# 6. Verify.
gh run list --workflow=release.yml
gh release view v0.1.0Notes:
- The tag (
vX.Y.Z) is what fires.github/workflows/release.yml; if you forget--tags, nothing happens. The version is derived from the tag and embedded in the binary (structalign -version). - Release notes are GitHub's native generated notes
(
changelog.use: github-nativein.goreleaser.yaml). The in-repoCHANGELOG.mdis git-cliff-generated and bundled into the release archives. GITHUB_TOKENis provided automatically by Actions — no secret to configure for a binaries-only release.