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
18 changes: 18 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!--
The PR TITLE must follow Conventional Commits — it becomes the squash-merge commit on `main`
and drives the version bump + changelog. Examples:
feat: add Tailwind v4 exporter → minor
fix: clamp K-Means seeds to bounds → patch
feat!: drop the net6.0 target → major (breaking)
chore: tidy usings → no release
See CONTRIBUTING.md → "Commit messages".
-->

## What & why


## Checklist

- [ ] PR title is a Conventional Commit (`feat:`, `fix:`, `docs:`, …)
- [ ] Tests added/updated for new behavior
- [ ] New/changed public APIs are documented
36 changes: 36 additions & 0 deletions .github/workflows/pr-title-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: pr-title-lint

# Enforce that every PR title is a valid Conventional Commit. PRs are squash-merged, so the PR
# title becomes the single commit on main that release-please reads to compute the next version
# and changelog — keeping it valid here is what keeps releases working.
#
# pull_request_target (not pull_request) so the check also runs on PRs from forks. This is safe:
# the job only reads the PR title via the API and never checks out or runs the PR's code.
on:
pull_request_target:
types: [opened, edited, reopened, synchronize]

permissions:
pull-requests: write # post/update a comment explaining a failing title

jobs:
validate:
name: Validate PR title (Conventional Commits)
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Allowed types — mirrors the Conventional Commits spec and what release-please understands.
types: |
feat
fix
perf
refactor
docs
test
build
ci
chore
revert
108 changes: 69 additions & 39 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,53 @@
name: release

# Trunk-based releases via release-please (Conventional Commits):
# 1. Every push to main updates a "release PR" that bumps the version + CHANGELOG.
# 2. Merging that release PR creates the GitHub Release + tag, which flips
# release_created=true and runs the publish job below in the same workflow run.
# Because publish is a downstream job (not a separate workflow keyed off the tag),
# the default GITHUB_TOKEN is sufficient — no PAT required.
on:
push:
tags: [ 'v*' ]
workflow_dispatch:
inputs:
version:
description: 'Version to publish (e.g. 1.2.3)'
required: true
branches: [ main ]

permissions:
contents: write # create the GitHub Release
packages: write # push to GitHub Packages
# Serialize so two quick merges can't race the release PR or a publish.
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

jobs:
release:
release-please:
name: Release Please
runs-on: ubuntu-latest
permissions:
contents: write # create the GitHub Release + tag
pull-requests: write # open/update the release PR
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
version: ${{ steps.release.outputs.version }}
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
token: ${{ secrets.GITHUB_TOKEN }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

publish:
name: Pack & publish (OverTone + DI)
needs: release-please
if: ${{ needs.release-please.outputs.release_created == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: write # attach package assets to the GitHub Release
packages: write # push to GitHub Packages

steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-depth: 0 # full history -> deterministic Source Link

- name: Setup .NET (8 + 10)
uses: actions/setup-dotnet@v5
Expand All @@ -31,50 +56,55 @@ jobs:
8.0.x
10.0.x

- name: Determine version
id: version
shell: bash
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ github.event.inputs.version }}"
else
VERSION="${GITHUB_REF_NAME#v}"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Publishing OverTone $VERSION"
- name: Restore
run: dotnet restore

# Build with the release version so the embedded assembly version matches the package.
- name: Build
run: >
dotnet build --configuration Release --no-restore
-p:Version=${{ needs.release-please.outputs.version }}

# Gate the publish on a green test run — a bad NuGet.org push can't be unpublished.
- name: Test (net8.0 + net10.0)
run: dotnet test --configuration Release --no-build --verbosity normal

# Packs every IsPackable project: OverTone and OverTone.Extensions.DependencyInjection.
- name: Pack
run: >
dotnet pack --configuration Release
-p:Version=${{ steps.version.outputs.version }}
dotnet pack --configuration Release --no-build
-p:Version=${{ needs.release-please.outputs.version }}
--output ${{ runner.temp }}/nupkg

- name: Push to NuGet.org
if: ${{ env.NUGET_API_KEY != '' }}
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: >
dotnet nuget push "${{ runner.temp }}/nupkg/*.nupkg"
--api-key "$NUGET_API_KEY"
--source https://api.nuget.org/v3/index.json
--skip-duplicate
run: |
if [ -z "$NUGET_API_KEY" ]; then
echo "NUGET_API_KEY secret not set — skipping NuGet.org push."
exit 0
fi
dotnet nuget push "${{ runner.temp }}"/nupkg/*.nupkg \
--api-key "$NUGET_API_KEY" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate

- name: Push to GitHub Packages
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: >
dotnet nuget push "${{ runner.temp }}/nupkg/*.nupkg"
dotnet nuget push "${{ runner.temp }}"/nupkg/*.nupkg
--api-key "$GH_TOKEN"
--source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
--skip-duplicate
--no-symbols

- name: Create GitHub Release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v3
with:
generate_release_notes: true
files: |
${{ runner.temp }}/nupkg/*.nupkg
${{ runner.temp }}/nupkg/*.snupkg
# Attach the .nupkg/.snupkg to the release release-please just created.
- name: Attach packages to the GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: >
gh release upload "${{ needs.release-please.outputs.tag_name }}"
"${{ runner.temp }}"/nupkg/*.nupkg
"${{ runner.temp }}"/nupkg/*.snupkg
--clobber
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "1.1.0"
}
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
This file covers both published packages — **OverTone** and
**OverTone.Extensions.DependencyInjection** — which are versioned together.

## [Unreleased]
## [1.1.0] - 2026-06-06

### Added
- **One-call theming from an image**: `Palette.GetThemeAsync` / `GetThemePairAsync` and the matching
Expand Down Expand Up @@ -47,5 +47,5 @@ This file covers both published packages — **OverTone** and
- Multi-targeting for **.NET 8.0** and **.NET 10.0**.
- Source Link, deterministic builds, a package icon, and symbol packages (`.snupkg`).

[Unreleased]: https://github.com/ChocoStout/OverTone/compare/v1.0.0...HEAD
[1.1.0]: https://github.com/ChocoStout/OverTone/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/ChocoStout/OverTone/releases/tag/v1.0.0
39 changes: 34 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,43 @@ cross-platform and builds on Windows, macOS, and Linux.
- Add tests for new behavior.
- NuGet versions are centralized in `Directory.Packages.props` (Central Package Management) —
add or change versions there, not in individual `.csproj` files.
- Versions follow [Semantic Versioning](https://semver.org/). Update `CHANGELOG.md` and bump
`VersionPrefix` in `Directory.Build.props`.
- Versions follow [Semantic Versioning](https://semver.org/), derived automatically from commit
messages — **don't** hand-edit `CHANGELOG.md` or `VersionPrefix`; release-please owns both. See
[Commit messages](#commit-messages) and [Releasing](#releasing-maintainers).

## Commit messages

OverTone uses [Conventional Commits](https://www.conventionalcommits.org/). The release automation
([release-please](https://github.com/googleapis/release-please)) reads them to pick the next version
and to write `CHANGELOG.md`, so the prefix matters:

| Prefix | Example | Effect |
|---|---|---|
| `feat:` | `feat: add Tailwind v4 exporter` | minor bump |
| `fix:` | `fix: clamp K-Means seeds to image bounds` | patch bump |
| `feat!:` / `fix!:`, or a `BREAKING CHANGE:` footer | `feat!: drop the net6.0 target` | major bump |
| `docs:` `test:` `refactor:` `perf:` `build:` `ci:` `chore:` | `chore: tidy usings` | no release |

Only `feat`, `fix`, and breaking changes cut a release; other types just record history. A commit whose
subject doesn't start with one of these types is ignored by the changelog.

PRs are **squash-merged**, so the **pull-request title becomes the commit on `main`** and must itself be
a valid Conventional Commit. The `pr-title-lint` workflow enforces this on every PR.

## Releasing (maintainers)

Pushing a `v*` tag (for example `v1.1.0`) triggers `.github/workflows/release.yml`, which packs both
packages with the tag's version and publishes them to NuGet.org and GitHub Packages. The NuGet.org
push requires a `NUGET_API_KEY` repository secret.
Releases are automated with [release-please](https://github.com/googleapis/release-please) — there are
no manual tags or version bumps:

1. Merging Conventional Commits to `main` makes release-please open (and keep updating) a **release PR**
titled `chore(main): release X.Y.Z` that bumps `VersionPrefix` in `Directory.Build.props` and
updates `CHANGELOG.md`.
2. **Merge that release PR** when you're ready to ship. release-please creates the GitHub Release and the
`vX.Y.Z` tag, which runs the `publish` job: it builds, tests, packs both packages, and pushes them to
NuGet.org and GitHub Packages (attaching the `.nupkg`/`.snupkg` to the release).

The NuGet.org push requires a `NUGET_API_KEY` repository secret (GitHub Packages uses the built-in
token). Configuration lives in `release-please-config.json` and `.release-please-manifest.json`.

## License

Expand Down
8 changes: 5 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
<RepositoryUrl>https://github.com/ChocoStout/OverTone</RepositoryUrl>
<RepositoryType>git</RepositoryType>

<!-- Shared version — bump here to release all packages together.
CI release builds override this from the pushed git tag (see release.yml). -->
<VersionPrefix>1.1.0</VersionPrefix>
<!-- Version is managed by release-please — do NOT hand-edit. It derives the next version
from Conventional Commits, bumps the line below in its release PR, and publishing
happens when that PR is merged (see release.yml + release-please-config.json).
The trailing marker tells release-please's generic updater which value to bump. -->
<VersionPrefix>1.1.0</VersionPrefix> <!-- x-release-please-version -->

<!-- Build quality -->
<Nullable>enable</Nullable>
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,9 +497,11 @@ dotnet run --project OverTone.Web
1. Fork the repository
2. Create a feature branch: `git checkout -b feature/my-extractor`
3. Add your extractor implementing `IColorPaletteExtractor`
4. Open a pull request
4. Open a pull request with a [Conventional Commits](https://www.conventionalcommits.org/) title
(e.g. `feat: add my extractor`) — it drives the version bump and changelog

All contributions welcome — new algorithms, bug fixes, performance improvements, docs.
See [CONTRIBUTING.md](CONTRIBUTING.md) for commit conventions and the release process.

---

Expand Down
15 changes: 15 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"include-component-in-tag": false,
"last-release-sha": "a216562fbbf39a840d6d76b8962e2f750c9bc897",
"packages": {
".": {
"release-type": "simple",
"package-name": "OverTone",
"changelog-path": "CHANGELOG.md",
"extra-files": [
{ "type": "generic", "path": "Directory.Build.props" }
]
}
}
}
Loading