Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/merge-bot-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:

merge-bot:
name: Merge bot pull request job
uses: ptr727/ProjectTemplate/.github/workflows/merge-bot-task.yml@098016c83723e384c9da0088c13a00ca14e02fd5 # 2.0.1004
uses: ptr727/ProjectTemplate/.github/workflows/merge-bot-task.yml@4b10111c29a2b6200cf270e44e39846614f4a1c0 # 2.0.1120
secrets:
CODEGEN_APP_CLIENT_ID: ${{ secrets.CODEGEN_APP_CLIENT_ID }}
CODEGEN_APP_PRIVATE_KEY: ${{ secrets.CODEGEN_APP_PRIVATE_KEY }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
plan:
name: Plan release job
needs: [assert-dispatch-ref]
uses: ptr727/ProjectTemplate/.github/workflows/publish-plan-task.yml@098016c83723e384c9da0088c13a00ca14e02fd5 # 2.0.1004
uses: ptr727/ProjectTemplate/.github/workflows/publish-plan-task.yml@4b10111c29a2b6200cf270e44e39846614f4a1c0 # 2.0.1120
with:
event_name: ${{ github.event_name }}
actor: ${{ github.actor }}
Expand All @@ -38,7 +38,7 @@ jobs:
name: Validate sources job
needs: [plan]
if: ${{ needs.plan.outputs.publish == 'true' }}
uses: ptr727/ProjectTemplate/.github/workflows/validate-task.yml@098016c83723e384c9da0088c13a00ca14e02fd5 # 2.0.1004
uses: ptr727/ProjectTemplate/.github/workflows/validate-task.yml@4b10111c29a2b6200cf270e44e39846614f4a1c0 # 2.0.1120
permissions:
contents: read
secrets:
Expand All @@ -48,7 +48,7 @@ jobs:
name: Publish project release job
needs: [plan, validate]
if: ${{ needs.plan.outputs.publish == 'true' && needs.validate.result == 'success' }}
uses: ptr727/ProjectTemplate/.github/workflows/build-release-task.yml@098016c83723e384c9da0088c13a00ca14e02fd5 # 2.0.1004
uses: ptr727/ProjectTemplate/.github/workflows/build-release-task.yml@4b10111c29a2b6200cf270e44e39846614f4a1c0 # 2.0.1120
permissions:
contents: write
actions: write
Expand Down
46 changes: 38 additions & 8 deletions .github/workflows/test-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: Test pull request action
# Source lint plus a change-gated compile test, run on the ptr727/esphome-nonroot image the repo deploys.
# - template-compile-test builds the example devices, which between them compose every published template.
# - test/easystart.yaml also covers the easystart external component.
# - Each leg then lints the build it compiled, failing on a compiler warning in this repository's own code.
# It costs minutes of esp-idf build, so it runs only when what it covers moves.
# Operational model.
# - develop takes direct signed commits, where push runs advisory CI.
Expand Down Expand Up @@ -32,7 +33,7 @@ jobs:

validate:
name: Validate sources job
uses: ptr727/ProjectTemplate/.github/workflows/validate-task.yml@098016c83723e384c9da0088c13a00ca14e02fd5 # 2.0.1004
uses: ptr727/ProjectTemplate/.github/workflows/validate-task.yml@4b10111c29a2b6200cf270e44e39846614f4a1c0 # 2.0.1120
permissions:
contents: read
secrets:
Expand Down Expand Up @@ -80,7 +81,8 @@ jobs:
fi
WORKFLOW='\.github/workflows/test-pull-request\.yml$'
# The test/ tree hosts the example devices, and test/easystart.yaml compiles easystart/components.
if grep -qE "^(templates/|test/|easystart/components/|$WORKFLOW)" <<<"$DIFF"; then
# The compile lint runs in the same legs, so a change to it reruns them too.
if grep -qE "^(templates/|test/|easystart/components/|tools/compile-lint/|$WORKFLOW)" <<<"$DIFF"; then
echo "template-test=true" >> "$GITHUB_OUTPUT"
else
echo "template-test=false" >> "$GITHUB_OUTPUT"
Expand All @@ -105,6 +107,15 @@ jobs:
echo "Templates that no example device in test/ includes: ${missing[*]}"
exit 1
fi
# A flashed example device runs on a bench with no Home Assistant and maybe no network, so it must not reboot for lack of either.
rebooting=()
for path in test/*.yaml; do
grep -qE "^ api_reboot_timeout: [\"']?0s[\"']?( *#.*)?\$" "$path" && grep -qE "^ wifi_reboot_timeout: [\"']?0s[\"']?( *#.*)?\$" "$path" || rebooting+=("$path")
done
if (( ${#rebooting[@]} > 0 )); then
echo "Example devices not setting api_reboot_timeout and wifi_reboot_timeout to 0s: ${rebooting[*]}"
exit 1
fi
CONFIGS="$(basename -a -s .yaml test/*.yaml | jq -Rsc 'split("\n") | map(select(length > 0))')"
echo "Template matrix: $CONFIGS"
echo "template-configs=$CONFIGS" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -140,10 +151,12 @@ jobs:
key="$(python3 -c 'import base64, os; print(base64.b64encode(os.urandom(32)).decode())')"
sed -i "s|REPLACE_WITH_BASE64_32_BYTE_KEY|$key|" secrets.yaml

# The cache sits at a fixed path, and the log there names the build path, so the lint step finds the build.
- name: Compile template test config step
run: |
set -Eeuo pipefail
cache="$(mktemp -d)"
cache="$RUNNER_TEMP/esphome-cache"
mkdir -p "$cache"
chmod -R 0777 "$cache" .
docker run --rm --user 1000:1000 \
--volume "$PWD":/config \
Expand All @@ -153,7 +166,24 @@ jobs:
set -Eeuo pipefail
/entrypoint/cache.sh
esphome config /config/test/${{ matrix.config }}.yaml > /dev/null
esphome compile /config/test/${{ matrix.config }}.yaml
esphome compile /config/test/${{ matrix.config }}.yaml 2>&1 | tee /cache/compile.log
'

# Recompile the generated main.cpp syntax-only with stricter warnings, keeping what lands in this repository's code.
# A finding annotates its YAML lambda line or component file, and fails the leg, per OPERATIONS.md "Compile Lint".
# It runs as the runner's own user, so it can append to the step summary file mounted into the container.
- name: Lint compiled code step
run: |
set -Eeuo pipefail
docker run --rm --user "$(id -u):$(id -g)" \
--volume "$PWD":/config \
--volume "$RUNNER_TEMP/esphome-cache":/cache \
--volume "$GITHUB_STEP_SUMMARY":/summary.md \
--env PYTHONPATH=/config/tools/compile-lint/src \
ptr727/esphome-nonroot:latest \
bash -c '
set -Eeuo pipefail
python3 -m compile_lint --config-root /config --annotate --summary /summary.md --from-log /cache/compile.log
Comment thread
ptr727 marked this conversation as resolved.
'

# GitHub Actions does not support required status checks on conditional jobs.
Expand All @@ -177,9 +207,9 @@ jobs:
exit 1
fi
# The compile job is skipped when the change gate says nothing it covers moved.
# Only a real failure blocks, a skip must still pass.
# The matrix aggregates to a single result, failure if any leg failed.
if [[ "${{ needs.template-compile-test.result }}" == "failure" ]]; then
echo "Job 'template-compile-test' failed; refusing to pass."
# A skip must still pass, and anything else short of success blocks, a cancelled leg included.
result="${{ needs.template-compile-test.result }}"
if [[ "$result" != "success" && "$result" != "skipped" ]]; then
echo "Job 'template-compile-test' did not succeed ($result); refusing to pass."
exit 1
fi
36 changes: 36 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,42 @@
"clear": false
}
},
{
// The compile above, then the compile lint over the build it produced, per OPERATIONS.md "Compile Lint".
// A warning in a lambda or component lands in the Problems pane on its YAML or source line.
"label": "ESPHome: Compile and Lint",
"type": "process",
"command": "docker",
"args": [
"exec",
"--env",
"PYTHONPATH=/config/tools/compile-lint/src",
"esphome",
"python3",
"-m",
"compile_lint",
"--config-root",
"/config",
"--compile",
"/config/${relativeFile}"
],
"problemMatcher": {
"owner": "compile-lint",
"fileLocation": ["relative", "${workspaceFolder}"],
"pattern": {
"regexp": "^[^:\\s]+: (.+):(\\d+):(\\d+): (warning|error): (.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
},
"presentation": {
"showReuseMessage": false,
"clear": false
}
},
{
"label": "ESPHome: Clean",
"type": "process",
Expand Down
5 changes: 3 additions & 2 deletions CODESTYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ This is packaged as the `python-codestyle` Skill at `.agents/skills/python-codes

### This Repository's Python

All Python in this repository lives in one **uv workspace** rooted at the repository root, and every project in it takes the `build` profile. The root [`pyproject.toml`][root-pyproject] is a virtual workspace root with no `[project]` of its own: it lists the member projects, holds the shared `dev` dependency group, and carries the one `[tool.ruff]`, `[tool.pyright]`, and `[tool.pytest.ini_options]` configuration every member uses. The single root `uv.lock` pins every member's dependencies and the dev tools together. The one member today is [`easystart/python`][easystart-python], the EasyStart BLE monitor, a `src`-layout project with its own `[project]`, `[build-system]`, and an `easystart-monitor` console script.
All Python in this repository lives in one **uv workspace** rooted at the repository root, and every project in it takes the `build` profile. The root [`pyproject.toml`][root-pyproject] is a virtual workspace root with no `[project]` of its own: it lists the member projects, holds the shared `dev` dependency group, and carries the one `[tool.ruff]`, `[tool.pyright]`, and `[tool.pytest.ini_options]` configuration every member uses. The single root `uv.lock` pins every member's dependencies and the dev tools together. The members are [`easystart/python`][easystart-python], the EasyStart BLE monitor, and [`tools/compile-lint`][compile-lint], the compile lint. Each is a `src`-layout project with its own `[project]`, `[build-system]`, and console script. The compile lint also runs inside the ESPHome image, so it needs only the standard library and ESPHome's own PyYAML.

- **uv is the only supported toolchain.** Run everything from the repository root with `uv run`, which syncs the workspace first: `uv run ruff check`, `uv run ruff format --check`, `uv run pyright`, `uv run pytest`, and a member's own script such as `uv run easystart-monitor`. There is no pip or `requirements.txt` path.
- **The hub validator is the CI gate.** A root `pyproject.toml`, `uv.lock`, and `tests/` are what its tree detection keys on, so it runs `uv sync --all-groups --frozen`, ruff, pyright, and pytest with coverage uploaded to Codecov, with no repository-specific CI step. The Codecov statuses are informational, per `codecov.yml`.
Expand All @@ -80,7 +80,7 @@ The C++ here is a small amount of hand-written code that an ESPHome build compil

The style derives from ESPHome upstream (LLVM-based, 2-space indent, 120-column limit) so the committed sources format clean and do not churn against upstream conventions. A repository's `.h` is read as C++ by context, which is what ESPHome and Arduino headers are.

Semantic and static analysis is deliberately out of scope. The downstream ESPHome build compiles this code and does the compile-time checking, and it holds the compile database that a `clang-tidy` pass would need. Adding a second, weaker analysis here would duplicate that without the context to be correct.
Semantic checking runs on the generated code rather than on the YAML. A lambda only compiles inside the `main.cpp` an ESPHome build produces. The compile lint rechecks that build with stricter warnings, per `OPERATIONS.md` "Compile Lint". Checking the YAML text directly would mean reimplementing ESPHome's code generation.

### Conventions

Expand All @@ -90,6 +90,7 @@ Semantic and static analysis is deliberately out of scope. The downstream ESPHom
<!-- Repo -->

[clang-format]: ./.clang-format
[compile-lint]: ./tools/compile-lint/
[cspell]: ./cspell.json
[devices]: ./DEVICES.md
[easystart-python]: ./easystart/python/
Expand Down
1 change: 1 addition & 0 deletions GOVERNANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ Every repo's GitHub repository details (the About panel) follow a fixed conventi
- [`test/`](./test/), example device configs that between them compose every published template, each overlay on its board's example. CI compiles these against the current ESPHome release, so a published template is known to build, and they double as usage samples.
- [`easystart/`](./easystart/), the Micro-Air EasyStart soft-starter project: the ESPHome external component, its protocol and reverse-engineering notes, and the standalone BLE monitor utility under `easystart/python`.
- [`archive/`](./archive/), retired device configs kept for reference, neither built nor validated.
- [`tools/`](./tools/), repository tooling. [`tools/compile-lint`](./tools/compile-lint/) is the compile lint the compile test runs over each build, per [`OPERATIONS.md`](./OPERATIONS.md) "Compile Lint".
- This repo carries no local `repo-config/` or `spec/secrets.json`: the hub hosts the branch ruleset and settings payloads and the secret-name ground truth, and [`AUDIT.md`](./AUDIT.md) "General Settings and Rulesets" and "Secrets" check against the hub's checkout rather than a local copy.
- [`host-tools.json`](./host-tools.json), what a development host needs beyond the fleet baseline, layered over it tighten-only. Empty here, because every tool this repo's procedures need is already declared at its own floor.
- [`.devcontainer/`](./.devcontainer/), the offline debugging container described above.
Expand Down
10 changes: 10 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ ESPHome configuration templates and projects.

## Release History

- Version 2.4:
- Added M5Stack CoreS3 support, with an LVGL touch screen example device.
- Added M5Stack GC0308 camera overlay support.
- Added M5Stack Dial support, with an LVGL and rotary encoder example device.
- Changed the example devices to disable the API and WiFi reboot timeouts, so a flashed one runs on a bench without Home Assistant.
- Added a compile lint for compiler warnings in a lambda or the EasyStart component, so a warning surfaces while developing and at pull request time.
- Added a VS Code task that compiles the open config and lists each lint finding in the Problems pane on its YAML or source line.
- Added a pull request check that fails a compile leg on a lint finding, annotating its YAML or source line.
- Added a pull request check that fails an example device that does not disable both reboot timeouts.
- Fixed the float math warnings the compile lint finds in the templates and the EasyStart component.
- Version 2.3:
- Changed OTA to encryption only, using the API key, so only the upgrade bridge build still needs the `ota_password` secret.
- Fixed the compile warnings the templates and the EasyStart component can address.
Expand Down
Loading
Loading