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
32 changes: 32 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Release

on:
push:
branches: [main]

permissions:
contents: write
issues: write
pull-requests: write

jobs:
release:
name: Semantic Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false

- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- run: npm ci

- name: Run semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release
59 changes: 33 additions & 26 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
{
"branches": ["main"],
"repositoryUrl": "https://github.com/steamicc/micropython-steami-lib.git",
"debug": false,
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/changelog",
{
"changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file. See\n[Conventional Commits](https://conventionalcommits.org) for commit guidelines."
}
],
[
"@semantic-release/github",
{
"assets": "pack/*.tgz"
}
],
[
"@semantic-release/git",
{
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}
"branches": ["main"],
"repositoryUrl": "https://github.com/steamicc/micropython-steami-lib.git",
"tagFormat": "v${version}",
"plugins": [
["@semantic-release/commit-analyzer", {
"releaseRules": [
{"type": "feat", "release": "minor"},
{"type": "fix", "release": "patch"},
{"type": "perf", "release": "patch"},
{"type": "refactor", "release": "patch"},
{"type": "tooling", "release": "patch"},
{"type": "docs", "release": false},
{"type": "style", "release": false},
{"type": "test", "release": false},
{"type": "ci", "release": false},
{"type": "build", "release": false},
{"type": "chore", "release": false}
]
}],
"@semantic-release/release-notes-generator",
["@semantic-release/changelog", {
"changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://conventionalcommits.org) for commit guidelines."
}],
["@semantic-release/exec", {
"prepareCmd": "sed -i 's/^version = \".*\"/version = \"${nextRelease.version}\"/' pyproject.toml"
}],
["@semantic-release/git", {
"assets": ["CHANGELOG.md", "pyproject.toml"],
"message": "chore(release): v${nextRelease.version}. [skip ci]\n\n${nextRelease.notes}"
}],
"@semantic-release/github"
]
}
19 changes: 19 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,25 @@ All pull requests must pass these checks:
| Mock tests | `tests.yml` | Runs mock driver tests |
| Example validation | `tests.yml` | Validates example files syntax and imports |

## Releasing

Releases are handled automatically by [semantic-release](https://semantic-release.gitbook.io/) when commits are pushed to `main`. The version is determined from commit messages:

- `fix:` → patch bump (v1.0.0 → v1.0.1)
- `feat:` → minor bump (v1.0.0 → v1.1.0)
- `BREAKING CHANGE:` in commit body → major bump (v1.0.0 → v2.0.0)
- `docs:`, `style:`, `test:`, `ci:`, `chore:` → no release

semantic-release automatically updates `pyproject.toml`, generates `CHANGELOG.md`, creates a git tag, and publishes a GitHub release.

To force a specific version manually (override):

```bash
make bump # patch: v1.0.0 → v1.0.1
make bump PART=minor # minor: v1.0.1 → v1.1.0
make bump PART=major # major: v1.1.0 → v2.0.0
```

## Notes

* Keep implementations simple and readable
Expand Down
42 changes: 42 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,48 @@ repl: ## Open MicroPython REPL on the board
mount: ## Mount lib/ on the board for live testing
mpremote connect $(PORT) mount lib/

# --- Release ---

PART ?= patch

.PHONY: bump
bump: ## Create a version tag (PART=patch|minor|major, default: patch)
@echo "Note: releases are normally handled by semantic-release in CI."
@echo "Use 'make bump' only to force a specific version.\n"
@if [ "$$(git symbolic-ref --short HEAD)" != "main" ]; then \
echo "Error: bump must be run on the main branch."; exit 1; \
fi
@if [ -n "$$(git status --porcelain)" ]; then \
echo "Error: working tree is not clean. Commit or stash changes first."; exit 1; \
fi
@set -e; \
LAST=$$(git tag --sort=-v:refname | head -1); \
if [ -z "$$LAST" ]; then \
NEXT="v1.0.0"; \
else \
if ! echo "$$LAST" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$$'; then \
echo "Error: latest tag '$$LAST' is not in supported format v<major>.<minor>.<patch>."; exit 1; \
fi; \
MAJOR=$$(echo "$$LAST" | sed 's/^v//' | cut -d. -f1); \
MINOR=$$(echo "$$LAST" | sed 's/^v//' | cut -d. -f2); \
PATCH=$$(echo "$$LAST" | sed 's/^v//' | cut -d. -f3); \
case "$(PART)" in \
major) MAJOR=$$((MAJOR + 1)); MINOR=0; PATCH=0 ;; \
minor) MINOR=$$((MINOR + 1)); PATCH=0 ;; \
patch) PATCH=$$((PATCH + 1)) ;; \
*) echo "Error: PART must be patch, minor or major."; exit 1 ;; \
esac; \
NEXT="v$$MAJOR.$$MINOR.$$PATCH"; \
Comment on lines +114 to +123
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bump logic assumes the latest tag is exactly v<major>.<minor>.<patch>. If the repo ever has non-semver tags (e.g., v1.2.3-rc1) or tags without a leading v, MAJOR/MINOR/PATCH parsing and arithmetic can fail. Add a validation step (regex match) and error out with a clear message when LAST isn't a supported format.

Suggested change
MAJOR=$$(echo "$$LAST" | sed 's/^v//' | cut -d. -f1); \
MINOR=$$(echo "$$LAST" | sed 's/^v//' | cut -d. -f2); \
PATCH=$$(echo "$$LAST" | sed 's/^v//' | cut -d. -f3); \
case "$(PART)" in \
major) MAJOR=$$((MAJOR + 1)); MINOR=0; PATCH=0 ;; \
minor) MINOR=$$((MINOR + 1)); PATCH=0 ;; \
patch) PATCH=$$((PATCH + 1)) ;; \
*) echo "Error: PART must be patch, minor or major."; exit 1 ;; \
esac; \
NEXT="v$$MAJOR.$$MINOR.$$PATCH"; \
if echo "$$LAST" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$$'; then \
MAJOR=$$(echo "$$LAST" | sed 's/^v//' | cut -d. -f1); \
MINOR=$$(echo "$$LAST" | sed 's/^v//' | cut -d. -f2); \
PATCH=$$(echo "$$LAST" | sed 's/^v//' | cut -d. -f3); \
case "$(PART)" in \
major) MAJOR=$$((MAJOR + 1)); MINOR=0; PATCH=0 ;; \
minor) MINOR=$$((MINOR + 1)); PATCH=0 ;; \
patch) PATCH=$$((PATCH + 1)) ;; \
*) echo "Error: PART must be patch, minor or major."; exit 1 ;; \
esac; \
NEXT="v$$MAJOR.$$MINOR.$$PATCH"; \
else \
echo "Error: latest tag '$$LAST' is not in supported format v<major>.<minor>.<patch>."; exit 1; \
fi; \

Copilot uses AI. Check for mistakes.
fi; \
echo "$$LAST → $$NEXT"; \
VERSION=$${NEXT#v}; \
python3 -c "import re, pathlib; p=pathlib.Path('pyproject.toml'); p.write_text(re.sub(r'^version = \".*\"', 'version = \"$$VERSION\"', p.read_text(), count=1, flags=re.MULTILINE))"; \
git add pyproject.toml; \
git commit -m "chore: Bump version to $$NEXT."; \
git tag -a "$$NEXT" -m "Release $$NEXT"; \
git push origin main "$$NEXT"; \
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Push branch and tag atomically in bump target

The git push origin main "$$NEXT" call can partially update the remote: if main is accepted but the tag is rejected (for example because the tag already exists remotely or tag creation is restricted), the bump commit is published without its release tag. git push only guarantees all-or-nothing behavior when --atomic is used, so this target can leave the repository in an inconsistent release state even though it reports failure.

Useful? React with 👍 / 👎.

echo "Tag $$NEXT pushed to origin."
Comment on lines +131 to +132
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail bump target when git push errors

The bump recipe runs all release steps in one .ONESHELL block and ends with echo, so a failed git push (e.g., auth issue, network problem, or non-fast-forward) is masked and make bump still exits successfully. This can leave a local commit/tag that was never published while printing a success message, which is risky for release automation.

Useful? React with 👍 / 👎.


# --- Utilities ---

.PHONY: clean
Expand Down
190 changes: 190 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
"@commitlint/cz-commitlint": "^20.5.0",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/commit-analyzer": "^13.0.0",
"@semantic-release/exec": "^7.1.0",
"@semantic-release/git": "^10.0.1",
"@semantic-release/github": "^11.0.0",
"commitizen": "^4.2.4",
"conventional-changelog-conventionalcommits": "^9.3.0",
"cross-env": "^7.0.3",
"git-precommit-checks": "^3.1.0",
"husky": "^9.1.7",
Expand Down
Loading