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
78 changes: 62 additions & 16 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,47 @@
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
#
# `target-branch: "develop"` on every ecosystem entry routes Dependabot's
# scheduled version-update PRs to the integration branch instead of the
# repo default (`main`). That keeps dep bumps in the
# `feature → develop → main` flow described in AGENTS.md: each bump joins
# the bot-triggered develop prerelease for early-warning testing, then
# bundles into the next develop → main merge-commit alongside feature work.
# This is also what keeps develop from falling behind main — bumps land on
# develop first.
# Every ecosystem appears **twice**: once with `target-branch: "main"`
# and once with `target-branch: "develop"`. Dependabot will open
# parallel PRs against each branch, so both stay current on
# dependency versions independently of the develop → main release
# cadence.
#
# Caveat: `target-branch` only redirects scheduled version updates.
# Dependabot *security update PRs* (the CVE-driven ones Dependabot opens
# in response to security alerts) are opened against the default branch
# (`main`) and do not honor `target-branch`. The merge-bot's `case`
# statement in .github/workflows/merge-bot-pull-request.yml handles
# either base correctly (squash for develop, merge for main), and a
# maintainer can retarget manually from the PR UI if a one-off needs
# the other branch.
# Why dual-target and not develop-only:
# - `develop` is the integration branch and ships content forward to
# `main` through merge-commit releases, but the time between releases
# can be long (a feature branch may sit on develop for weeks).
# - Push-distribution channels (e.g. HACS for Home Assistant
# integrations, distros that pull from main) consume `main` directly.
# If `main` only got dependency bumps via the next develop → main
# release, those channels would ship outdated code in the interim.
# - Codegen workflows take the same dual-target shape for the same
# reason — see .github/workflows/run-codegen-pull-request-task.yml.
#
# The merge-bot's `case` statement in
# .github/workflows/merge-bot-pull-request.yml dispatches the merge
# method per base ref (squash on develop, merge on main) so both bases
# auto-merge cleanly. `develop` remains strictly forward-only: there
# are no main → develop back-merges; each branch absorbs its own
# Dependabot PRs and codegen PRs independently.
#
# Security update PRs (CVE-driven) are opened by Dependabot against
# the repo default branch (`main`) regardless of any `target-branch`
# config — the `case` statement handles them in the same code path.
version: 2
updates:

# ----- nuget -----

- package-ecosystem: "nuget"
target-branch: "main"
directory: "/"
schedule:
interval: "daily"
groups:
nuget-deps:
patterns:
- "*"

- package-ecosystem: "nuget"
target-branch: "develop"
directory: "/"
Expand All @@ -30,6 +52,18 @@ updates:
patterns:
- "*"

# ----- github-actions -----

- package-ecosystem: "github-actions"
target-branch: "main"
directory: "/"
schedule:
interval: "daily"
groups:
actions-deps:
patterns:
- "*"

- package-ecosystem: "github-actions"
target-branch: "develop"
directory: "/"
Expand All @@ -40,6 +74,18 @@ updates:
patterns:
- "*"

# ----- uv (PyPiLibrary) -----

- package-ecosystem: "uv"
target-branch: "main"
directory: "/PyPiLibrary"
schedule:
interval: "daily"
groups:
pypi-deps:
patterns:
- "*"

- package-ecosystem: "uv"
target-branch: "develop"
directory: "/PyPiLibrary"
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/build-nugetlibrary-task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ jobs:
get-version:
name: Get version information job
uses: ./.github/workflows/get-version-task.yml
secrets: inherit

build-nugetlibrary:
name: Build NuGet library project job
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/get-version-task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ jobs:
with:
fetch-depth: 0

# `dotnet/nbgv` is intentionally floated on `master` rather than
# pinned to a commit SHA — a deliberate deviation from the
# AGENTS.md "pin third-party actions to a commit SHA" rule,
# documented here so the deviation isn't accidentally "fixed" by
# a future reviewer. Justification:
# - The upstream tag stream is effectively dormant — the latest
# tag `v0.5.1` lags well behind `master` and fixes accumulate
# on `master` between tag bumps.
# - Dependabot's GitHub Actions ecosystem tracks tagged
# releases. A SHA pinned to a post-`v0.5.1` `master` commit
# would either receive no Dependabot updates (silently stale)
# or get an attempted downgrade PR to `v0.5.1`'s SHA. Neither
# beats just floating on `master`.
# - Upstream owner is Microsoft (`dotnet/`), so the
# "tag-/branch-retargeting risk" the AGENTS.md rule guards
# against is materially lower than for a random author.
# Revisit if `dotnet/nbgv` resumes regular tagged releases.
- name: Run Nerdbank.GitVersioning tool step
id: nbgv
uses: dotnet/nbgv@master
65 changes: 45 additions & 20 deletions .github/workflows/merge-bot-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ name: Merge bot pull request action
# PRs created under `GITHUB_TOKEN` to nudge the auto-merge workflow.
#
# Merge method:
# Each merge step picks `--squash` or `--merge` from the PR's base ref so
# the form matches that branch's ruleset (`develop` allows only squash,
# `main` allows only merge commits — see AGENTS.md "Branching Model").
# A mismatch fails `enablePullRequestAutoMerge` with "Merge method ... is
# not allowed on this repository". Codegen PRs always target `main` so
# they always merge-commit; Dependabot PRs default to `develop` but
# security update PRs open against `main`, so the `case` statement
# handles both bases.
# Each merge step picks `--squash` or `--merge` from the PR's base ref
# so the form matches that branch's ruleset (`develop` allows only
# squash, `main` allows only merge commits — see AGENTS.md "Branching
# Model"). A mismatch fails `enablePullRequestAutoMerge` with "Merge
# method ... is not allowed on this repository". Both Dependabot and
# codegen open parallel PRs against `main` and `develop` (see the
# AGENTS.md "Branching Model" dual-target bot section), so both jobs
# below use a `case` statement to dispatch the merge method by base
# ref. Dependabot security update PRs (always against `main`) flow
# through the same code path.

on:
pull_request:
Expand Down Expand Up @@ -84,20 +86,33 @@ jobs:
merge-codegen:
name: Merge codegen pull request job
runs-on: ubuntu-latest
# Restrict to codegen PRs that originate from the App in this repository.
# Codegen always opens PRs against `main` from the `codegen` branch.
# Both the PR author AND the event actor must be the App: the author
# check stops human-opened PRs that happen to target the `codegen`
# branch from auto-merging; the actor check stops a maintainer
# pushing extra commits to the App's `codegen` branch (a
# `synchronize` event the human triggered) from auto-merging
# unintended changes through the App PR.
# Restrict to codegen PRs that originate from the App in this
# repository. Codegen runs in a matrix over `main` and `develop`,
# so two head refs are valid: `codegen-main` (always targets `main`)
# and `codegen-develop` (always targets `develop`). The head/base
# pairing is enforced strictly so a misconfigured workflow can't,
# for example, sneak a `codegen-develop` branch into `main`.
# Both the PR author AND the event actor must be the App: the
# author check stops human-opened PRs that happen to target a
# `codegen-*` branch from auto-merging; the actor check stops
# this job from re-invoking `gh pr merge --auto` on a
# `synchronize` event a maintainer triggered.
#
# Limitation worth knowing: the actor check does NOT disable
# auto-merge if it was already enabled by the initial bot-driven
# `opened` event. Once auto-merge is on, every commit that
# passes CI will land — including a maintainer's. To edit a
# codegen PR safely, run `gh pr merge --disable-auto <PR>` (or
# click "Disable auto-merge" in the GitHub UI) BEFORE pushing,
# then re-enable it manually when ready.
if: >-
github.event.pull_request.user.login == 'ptr727-codegen[bot]' &&
github.actor == 'ptr727-codegen[bot]' &&
github.event.pull_request.head.ref == 'codegen' &&
github.event.pull_request.base.ref == 'main' &&
github.event.pull_request.head.repo.full_name == github.repository
github.event.pull_request.head.repo.full_name == github.repository &&
(
(github.event.pull_request.head.ref == 'codegen-main' && github.event.pull_request.base.ref == 'main') ||
(github.event.pull_request.head.ref == 'codegen-develop' && github.event.pull_request.base.ref == 'develop')
)
permissions:
contents: write
pull-requests: write
Expand All @@ -112,7 +127,17 @@ jobs:
private-key: ${{ secrets.CODEGEN_APP_PRIVATE_KEY }}

- name: Merge pull request step
run: gh pr merge --auto --merge "$PR_URL"
run: |
set -euo pipefail
case "${{ github.event.pull_request.base.ref }}" in
develop) method=--squash ;;
main) method=--merge ;;
*)
echo "::error::Unsupported base branch: ${{ github.event.pull_request.base.ref }}"
exit 1
;;
esac
gh pr merge --auto "$method" "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
27 changes: 23 additions & 4 deletions .github/workflows/run-codegen-pull-request-task.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
name: Run codegen and pull request task

# Runs codegen against `main` and `develop` in parallel via a matrix,
# opens a PR against each base (`codegen-main` branch → main,
# `codegen-develop` branch → develop). The merge-bot auto-merges
# either PR independently. This keeps both branches current on
# generated content (date stamps, API-derived data, etc.) without
# either branch falling behind the other and without main → develop
# back-merges (see AGENTS.md "Branching Model" for the forward-only
# develop invariant).

on:
workflow_call:
secrets:
Expand All @@ -15,11 +24,21 @@ on:
jobs:

codegen:
name: Run codegen and pull request job
name: Run ${{ matrix.target.ref }} codegen and pull request job
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
strategy:
# Each branch gets its own parallel codegen run + PR. If one
# branch's PR fails (CI, conflicts, etc.) the other is unaffected.
fail-fast: false
matrix:
target:
- ref: main
branch: codegen-main
- ref: develop
branch: codegen-develop

steps:

Expand All @@ -42,7 +61,7 @@ jobs:
- name: Checkout code step
uses: actions/checkout@v6
with:
ref: main
ref: ${{ matrix.target.ref }}
token: ${{ steps.app-token.outputs.token }}

- name: Run codegen step
Expand All @@ -65,8 +84,8 @@ jobs:
with:
# App token: triggers pull_request workflow events directly, creates verified commits as the app
token: ${{ steps.app-token.outputs.token }}
base: main
branch: codegen
base: ${{ matrix.target.ref }}
branch: ${{ matrix.target.branch }}
title: 'Update codegen files'
body: 'This PR updates the codegen files.'
commit-message: 'Update codegen files'
Expand Down
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ Treat this file as authoritative for everything else; don't restate its rules el
- `develop` is the integration branch. Feature branches → `develop` is **squash-only**; develop is kept linear.
- `develop` → `main` is **merge-commit only** (no squash, no rebase). Merge commits preserve develop's commit list as a real second-parent reference on main, which is what makes the "release on every push" model attribute releases to the develop commits that produced them. Branch protection enforces this: the develop ruleset allows only `squash`, the main ruleset allows only `merge`.
- All commits on both branches must be cryptographically signed (SSH or GPG). Squash and merge commits created via the GitHub UI are signed by GitHub's web-flow key.
- **Dependabot scheduled updates target `develop`** (see `target-branch` in [`.github/dependabot.yml`](./.github/dependabot.yml)) so develop stays ahead of main; bumps land on develop first and bundle into the next develop → main merge-commit. Security update PRs from Dependabot open against `main` directly (Dependabot doesn't honor `target-branch` for those). The merge-bot ([`.github/workflows/merge-bot-pull-request.yml`](./.github/workflows/merge-bot-pull-request.yml)) picks `--squash` or `--merge` from each PR's base ref so the form matches the ruleset on either base.
- **`develop` is forward-only — no `main → develop` back-merges.** The develop ruleset's squash-only setting physically blocks merge commits on develop. Historical back-merge commits visible in `git log` (`b9b0447`, `410ba56`, `ffb9e64`, `5ce95cf`, etc.) predate this rule and must not be repeated.
- **Bots (Dependabot and codegen) target both `main` and `develop` in parallel.** [`.github/dependabot.yml`](./.github/dependabot.yml) duplicates every ecosystem entry (one per branch) and [`.github/workflows/run-codegen-pull-request-task.yml`](./.github/workflows/run-codegen-pull-request-task.yml) runs as a matrix over both branches with branch names `codegen-main` and `codegen-develop`. Each branch absorbs its own bot PRs independently, so neither falls behind, and the forward-only rule still holds (nothing is back-merged from main to develop — both branches receive their updates directly). The merge-bot ([`.github/workflows/merge-bot-pull-request.yml`](./.github/workflows/merge-bot-pull-request.yml)) dispatches `--squash` or `--merge` from each PR's base ref via a `case` statement so the form matches the ruleset on either base. Dependabot **security** PRs (CVE-driven) always open against the repo default branch (`main`) regardless of `target-branch` — the same `case` statement covers them.
- **Why parallel dual-target rather than develop-only with eventual flow-through:** push-distribution channels (HACS for Home Assistant integrations, Linux distros that vendor from `main`, etc.) consume `main` directly. A develop-only model would leave `main` running stale code during long-running develop features. Codegen content can also be production-critical (live API-derived data, language lists, build catalogs) rather than just sample/demo content, so both branches need fresh codegen on their own cadence.

## Pull Request Title and Commit Message Conventions

Expand Down
Loading
Loading