Skip to content

Commit c77dec0

Browse files
authored
tooling: Add Makefile and npm hooks for dev workflow. (#229)
* tooling: Add Makefile and npm hooks for dev workflow. * tooling: Add external config files and Makefile targets. * tooling: Add dynamic test targets, dev dependencies and update docs. * tooling: Address Copilot review feedback. * tooling: Fix dynamic test targets and address Codex review. * tooling: Document naming convention for dynamic test targets. * tooling: Address second round of Copilot review.
1 parent abcb813 commit c77dec0

18 files changed

Lines changed: 10113 additions & 34 deletions

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@ __pycache__
33
*.egg-info
44
*/dist/
55
*.org
6-
*.1
6+
*.1
7+
78e43b91-b6a2-4e0a-99c2-3f6f74828063_ExportBlock-935e0d48-7286-4b74-aa60-ccd18217ac01
8+
node_modules/
9+
CLAUDE.md

.husky/commit-msg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env sh
2+
npx --no-install commitlint --edit "$1"

.husky/pre-commit

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env sh
2+
npx --no-install validate-branch-name && npx --no-install git-precommit-checks && npx --no-install lint-staged

.prettierrc.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"endOfLine": "lf",
3+
"semi": true,
4+
"singleQuote": true,
5+
"tabWidth": 4,
6+
"arrowParens": "avoid",
7+
"trailingComma": "all",
8+
"useTabs": false
9+
}

.releaserc.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"branches": ["main"],
3+
"repositoryUrl": "https://github.com/steamicc/micropython-steami-lib.git",
4+
"debug": false,
5+
"plugins": [
6+
"@semantic-release/commit-analyzer",
7+
"@semantic-release/release-notes-generator",
8+
[
9+
"@semantic-release/changelog",
10+
{
11+
"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."
12+
}
13+
],
14+
[
15+
"@semantic-release/github",
16+
{
17+
"assets": "pack/*.tgz"
18+
}
19+
],
20+
[
21+
"@semantic-release/git",
22+
{
23+
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
24+
}
25+
]
26+
]
27+
}

.validate-branch-namerc.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
pattern: "^(main)$|^(feat|fix|docs|tooling|ci|test|style|chore|refactor)\/([a-z0-9]+-)*[a-z0-9]+$|^release\/v([0-9]+)\\.([0-9]+)\\.([0-9]+)([a-z0-9-]*)$",
3+
errorMsg: "🤨 La branche que tu essaies de pusher ne respecte pas nos conventions, tu peux la renommer avec `git branch -m <nom-actuel> <nouveau-nom>`",
4+
}

CONTRIBUTING.md

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,46 +48,46 @@ lib/<component>/
4848

4949
## Linting
5050

51-
The project uses `ruff` for linting.
52-
5351
```bash
54-
ruff check .
52+
make lint
5553
```
5654

5755
To automatically fix issues:
5856

5957
```bash
60-
ruff check . --fix
58+
make lint-fix
6159
```
6260

6361
## Commit messages
6462

65-
Use the following format:
63+
Commit messages follow the [Conventional Commits](https://www.conventionalcommits.org/) format, enforced by commitlint via a git hook:
6664

6765
```
68-
<scope>: <Description starting with a capital letter ending with a period.>
66+
<type>[(<scope>)]: <Description starting with a capital letter ending with a period.>
6967
```
7068

71-
The scope is the driver name or domain (`hts221`, `ism330dl`, `docs`, `tests`, `ci`...). There is no predefined list of types — the scope is free-form.
69+
**Types**: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `ci`, `build`, `chore`, `perf`, `revert`, `tooling`
70+
71+
**Scopes** (optional): driver names (`hts221`, `ism330dl`, `wsen-pads`...) or domains (`ci`, `docs`, `style`, `tests`, `tooling`). The scope is recommended for driver-specific changes but can be omitted for cross-cutting changes.
7272

7373
### Examples
7474

7575
```
76-
hts221: Fix missing self parameter in get_av method.
77-
ism330dl: Add driver support for temperature reading.
78-
wsen-pads: Correct pressure conversion formula.
76+
fix(hts221): Fix missing self parameter in get_av method.
77+
feat(ism330dl): Add driver support for temperature reading.
78+
fix(wsen-pads): Correct pressure conversion formula.
7979
docs: Update README driver table.
80-
tests: Add mock scenarios for mcp23009e driver.
80+
test(mcp23009e): Add mock scenarios for mcp23009e driver.
8181
```
8282

8383
## Workflow
8484

85-
1. Create a branch from main (`git checkout -b my-new-feature`)
86-
2. Write your code and add tests in `tests/scenarios/<driver>.yaml`
87-
3. Run `ruff check` and `python -m pytest tests/ -v -k mock locally`
88-
4. Commit your changes following the commit message format
89-
5. Push your branch to the repository
90-
6. Open a Pull Request
85+
1. Set up your environment: `make setup`
86+
2. Create a branch from main (format: `feat/`, `fix/`, `docs/`, `tooling/`, `ci/`, `test/`, `style/`, `chore/`, `refactor/`)
87+
3. Write your code and add tests in `tests/scenarios/<driver>.yaml`
88+
4. Run `make ci` to verify everything passes (lint + tests + examples)
89+
5. Commit — the git hooks will automatically check your commit message and run ruff on staged files
90+
6. Push your branch and open a Pull Request
9191

9292
## Continuous Integration
9393

@@ -98,6 +98,7 @@ All pull requests must pass these checks:
9898
| Commit messages | `check-commits.yml` | Validates commit message format |
9999
| Linting | `python-linter.yml` | Runs `ruff check` |
100100
| Mock tests | `tests.yml` | Runs mock driver tests |
101+
| Example validation | `tests.yml` | Validates example files syntax and imports |
101102

102103
## Notes
103104

Makefile

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
.DEFAULT_GOAL := help
2+
3+
.ONESHELL: # Applies to every targets
4+
5+
include env.mk
6+
7+
# --- Setup ---
8+
9+
# npm install is re-run only when package.json changes
10+
node_modules/.package-lock.json: package.json package-lock.json
11+
npm install
12+
@touch $@
13+
14+
.PHONY: prepare
15+
prepare: node_modules/.package-lock.json ## Install git hooks
16+
husky
17+
18+
.PHONY: setup
19+
setup: install prepare ## Full dev environment setup
20+
21+
.PHONY: install
22+
install: node_modules/.package-lock.json ## Install dev tools (pip + npm)
23+
python3 -m pip install -e ".[dev,test]"
24+
25+
# --- Linting ---
26+
27+
.PHONY: lint
28+
lint: ## Run ruff linter
29+
ruff check
30+
31+
.PHONY: lint-fix
32+
lint-fix: ## Auto-fix lint issues
33+
ruff check --fix
34+
35+
# --- Testing ---
36+
37+
# Dynamic per-scenario targets (test-apds9960, test-hts221, etc.)
38+
# Uses 'driver' field for driver scenarios, filename stem for board scenarios.
39+
# Convention: for board scenarios, the YAML 'name' field must match the filename.
40+
SCENARIOS := $(shell python3 -c "import yaml,glob,os; [print(d.get('driver',os.path.basename(f).replace('.yaml',''))) for f in sorted(glob.glob('tests/scenarios/*.yaml')) for d in [yaml.safe_load(open(f))]]" 2>/dev/null)
41+
$(foreach s,$(SCENARIOS),$(eval .PHONY: test-$(s))$(eval test-$(s): ; python3 -m pytest tests/ -v -k "$(s)" --port $$(PORT) -s))
42+
43+
.PHONY: test-mock
44+
test-mock: ## Run mock tests (no hardware needed)
45+
python3 -m pytest tests/ -v -k mock
46+
47+
.PHONY: test
48+
test: test-mock ## Run mock tests (use 'make test-all' for mock + hardware)
49+
@echo ""
50+
@echo "ℹ️ Only mock tests were run. Use 'make test-all' to include hardware tests."
51+
52+
.PHONY: test-hardware
53+
test-hardware: ## Run all hardware tests (needs board on PORT)
54+
python3 -m pytest tests/ -v --port $(PORT) -s -k hardware
55+
56+
.PHONY: test-board
57+
test-board: ## Run board tests only (buttons, LEDs, buzzer, screen)
58+
python3 -m pytest tests/ -v --port $(PORT) -s -k "board_ and hardware"
59+
60+
.PHONY: test-sensors
61+
test-sensors: ## Run sensor driver hardware tests (I2C devices)
62+
python3 -m pytest tests/ -v --port $(PORT) -s -k "hardware and not board_"
63+
64+
.PHONY: test-all
65+
test-all: ## Run all tests (mock + hardware)
66+
python3 -m pytest tests/ -v --port $(PORT) -s
67+
68+
.PHONY: test-examples
69+
test-examples: ## Validate all example files (syntax + imports)
70+
python3 -m pytest tests/test_examples.py -v
71+
72+
# --- CI ---
73+
74+
.PHONY: ci
75+
ci: lint test test-examples ## Run all CI checks (lint + tests + examples)
76+
77+
# --- Build / Package ---
78+
79+
.PHONY: build
80+
build: lint test ## Build (lint + test)
81+
82+
# --- Hardware ---
83+
84+
.PHONY: repl
85+
repl: ## Open MicroPython REPL on the board
86+
mpremote connect $(PORT)
87+
88+
.PHONY: mount
89+
mount: ## Mount lib/ on the board for live testing
90+
mpremote connect $(PORT) mount lib/
91+
92+
# --- Utilities ---
93+
94+
.PHONY: clean
95+
clean: ## Remove build artifacts and caches
96+
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
97+
find . -type d -name .pytest_cache -exec rm -rf {} + 2>/dev/null || true
98+
find . -type d -name .ruff_cache -exec rm -rf {} + 2>/dev/null || true
99+
find . -type d -name .mypy_cache -exec rm -rf {} + 2>/dev/null || true
100+
101+
.PHONY: deepclean
102+
deepclean: clean ## Remove everything including node_modules
103+
rm -rf node_modules
104+
105+
.PHONY: help
106+
help: ## Show this help
107+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
108+
109+
# A useful debug Make Target - found from
110+
# http://lists.gnu.org/archive/html/help-make/2005-08/msg00137.html
111+
.PHONY: printvars
112+
printvars:
113+
@$(foreach V,$(sort $(.VARIABLES)), \
114+
$(if $(filter-out environment% default automatic, \
115+
$(origin $V)),$(warning $V=$($V) ($(value $V)))))
116+
117+
# Affiche toutes les cibles disponibles dans le Makefile
118+
.PHONY: list
119+
list:
120+
@LC_ALL=C $(MAKE) -pRrq -f $(firstword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/(^|\n)# Files(\n|$$)/,/(^|\n)# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | grep -E -v -e '^[^[:alnum:]]' -e '^$@$$'

README.md

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,67 @@ mpremote cp -r lib/ssd1327/ssd1327 :lib/
9191

9292
Once copied, the driver can be imported normally without mounting.
9393

94-
## Testing
94+
## Development
95+
96+
### Setup
97+
98+
```bash
99+
make setup # Install pip + npm dependencies and git hooks
100+
```
101+
102+
### Available commands
103+
104+
Run `make help` to see all available targets:
105+
106+
| Command | Description |
107+
|---------|-------------|
108+
| `make lint` | Run ruff linter |
109+
| `make lint-fix` | Auto-fix lint issues |
110+
| `make test` | Run mock tests (no hardware) |
111+
| `make test-mock` | Run mock tests |
112+
| `make test-hardware` | Run all hardware tests (needs board) |
113+
| `make test-board` | Board tests only (buttons, LEDs, buzzer) |
114+
| `make test-sensors` | Sensor driver hardware tests |
115+
| `make test-all` | All tests (mock + hardware) |
116+
| `make test-examples` | Validate example files |
117+
| `make test-<scenario>` | Test a specific scenario (e.g. `make test-hts221`) |
118+
| `make ci` | Full CI pipeline (lint + tests + examples) |
119+
| `make repl` | Open MicroPython REPL |
120+
| `make mount` | Mount lib/ on the board |
121+
| `make clean` | Remove caches |
122+
| `make deepclean` | Remove everything including node_modules |
123+
124+
Per-scenario targets are generated automatically from `tests/scenarios/*.yaml`.
125+
126+
### Git hooks
127+
128+
Git hooks are managed by [husky](https://typicode.github.io/husky/) and run automatically on commit:
129+
130+
- **commit-msg** — validates commit message format via [commitlint](https://commitlint.js.org/)
131+
- **pre-commit** — branch name validation, content checks (conflict markers, TODO), ruff on staged `.py` files
132+
133+
### Testing
95134

96135
Run the full mock test suite:
97136

98137
```bash
99-
python -m pytest tests/ -v -k mock
138+
make test
139+
```
140+
141+
Run hardware tests (requires a STeaMi board on `/dev/ttyACM0`):
142+
143+
```bash
144+
make test-hardware
100145
```
101146

102-
See full details in [tests/TESTING.md](tests/TESTING.md)
147+
Run tests for a specific driver:
148+
149+
```bash
150+
make test-hts221
151+
```
152+
153+
See full details in [tests/TESTING.md](tests/TESTING.md).
154+
103155
## Contributing
104156

105157
Contributions are welcome! Please follow the project guidelines.

commitlint.config.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
module.exports = {
2+
extends: ['@commitlint/config-conventional'],
3+
parserPreset: {
4+
parserOpts: {
5+
issuePrefixes: ['#'],
6+
},
7+
},
8+
rules: {
9+
'subject-case': [0],
10+
'subject-full-stop': [0],
11+
'header-max-length': [2, 'always', 78],
12+
'scope-enum': [
13+
1,
14+
'always',
15+
[
16+
'apds9960',
17+
'bme280',
18+
'bq27441',
19+
'daplink_flash',
20+
'gc9a01',
21+
'hts221',
22+
'im34dt05',
23+
'ism330dl',
24+
'lis2mdl',
25+
'mcp23009e',
26+
'ssd1327',
27+
'steami_config',
28+
'vl53l1x',
29+
'wsen-hids',
30+
'wsen-pads',
31+
'ci',
32+
'docs',
33+
'style',
34+
'tests',
35+
'tooling',
36+
],
37+
],
38+
'type-enum': [
39+
2,
40+
'always',
41+
[
42+
'build',
43+
'chore',
44+
'ci',
45+
'docs',
46+
'feat',
47+
'fix',
48+
'perf',
49+
'refactor',
50+
'revert',
51+
'style',
52+
'test',
53+
'tooling',
54+
],
55+
],
56+
'body-max-line-length': [1, 'always', 100],
57+
},
58+
};

0 commit comments

Comments
 (0)