Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 2 additions & 2 deletions .github/rulesets/main-merge-queue.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
"type": "pull_request",
"parameters": {
"required_approving_review_count": 1,
"dismiss_stale_reviews_on_push": false,
"dismiss_stale_reviews_on_push": true,
"require_code_owner_review": false,
"require_last_push_approval": false,
"require_last_push_approval": true,
"required_review_thread_resolution": false,
"allowed_merge_methods": ["squash", "rebase"]
}
Expand Down
10 changes: 8 additions & 2 deletions .github/workflows/cut-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,11 @@ jobs:

- name: Stop after dry-run preview
if: ${{ inputs.dry_run == true || inputs.dry_run == 'true' }}
env:
NEXT_VERSION: ${{ steps.preview.outputs.next }}
run: |
echo "dry_run=true — not bumping or tagging."
echo "Re-run with dry_run unchecked to cut v${{ steps.preview.outputs.next }}."
echo "Re-run with dry_run unchecked to cut v${NEXT_VERSION}."

- name: Install actionlint
if: ${{ inputs.dry_run != true && inputs.dry_run != 'true' }}
Expand Down Expand Up @@ -180,7 +182,9 @@ jobs:

- name: Add actionlint to PATH
if: ${{ inputs.dry_run != true && inputs.dry_run != 'true' }}
run: echo "${{ github.workspace }}/.githooks/bin" >> "$GITHUB_PATH"
env:
HOOKS_BIN: ${{ github.workspace }}/.githooks/bin
run: echo "${HOOKS_BIN}" >> "$GITHUB_PATH"

- name: Configure git identity
if: ${{ inputs.dry_run != true && inputs.dry_run != 'true' }}
Expand All @@ -192,6 +196,8 @@ jobs:
if: ${{ inputs.dry_run != true && inputs.dry_run != 'true' }}
env:
MESH_CLIENT_RELEASE_YES: '1'
# Never inherit a repo/org PARSE_ONLY var — that would green-exit with no tag.
MESH_CLIENT_RELEASE_PARSE_ONLY: ''
RELEASE_BUMP: ${{ steps.preview.outputs.bump }}
SKIP_DEP_UPDATE: ${{ inputs.skip_dep_update }}
run: |
Expand Down
30 changes: 29 additions & 1 deletion .github/workflows/third-party-licenses.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ jobs:
name: Update third-party licenses
runs-on: ubuntu-latest
steps:
# persist-credentials: false avoids Duplicate Authorization when CPR
# configures its own http.*.extraheader (actions/checkout@v6 default helper).
- uses: actions/checkout@v6
with:
persist-credentials: false

- name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271
Expand All @@ -42,20 +46,44 @@ jobs:
- name: Generate third-party licenses
run: pnpm run docs:licenses

- name: Detect license doc changes
id: licenses_diff
run: |
if git diff --quiet -- docs/third-party-licenses.md; then
echo 'changed=false' >> "$GITHUB_OUTPUT"
else
echo 'changed=true' >> "$GITHUB_OUTPUT"
fi

- name: Require RELEASE_PUSH_TOKEN for PR checks
if: steps.licenses_diff.outputs.changed == 'true'
env:
RELEASE_PUSH_TOKEN: ${{ secrets.RELEASE_PUSH_TOKEN }}
run: |
if [ -z "${RELEASE_PUSH_TOKEN}" ]; then
echo '::error::Set repository secret RELEASE_PUSH_TOKEN (admin PAT: contents+workflows+pull_requests).'
echo 'Default GITHUB_TOKEN PRs do not auto-run required checks under the merge-queue ruleset.'
exit 1
fi

# Open a PR instead of pushing to main — the merge-queue ruleset blocks
# direct pushes, and GitHub Actions cannot be added as a bypass actor
# on this org (“must be part of the ruleset source or owner organization”).
# RELEASE_PUSH_TOKEN (not GITHUB_TOKEN) so pull_request workflows run.
# Unique branch per run avoids thrashing one tip under concurrent main pushes.
- name: Create pull request
if: steps.licenses_diff.outputs.changed == 'true'
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
with:
token: ${{ secrets.RELEASE_PUSH_TOKEN }}
commit-message: 'docs: update third-party licenses'
title: 'docs: update third-party licenses'
body: |
Regenerated `docs/third-party-licenses.md` after dependency changes.

Opened automatically by `third-party-licenses.yaml` (direct pushes to
`main` are blocked by the merge-queue ruleset).
branch: chore/third-party-licenses
branch: chore/third-party-licenses-${{ github.run_id }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
delete-branch: true
add-paths: |
docs/third-party-licenses.md
18 changes: 10 additions & 8 deletions docs/ci-cd.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Mesh-Client uses GitHub Actions for continuous integration and deployment.
| `flatpak.yaml` | Version tags (`v*`), manual | Build Flatpak (+ schema compare vs last official); publish to release on tags |
| `cut-release.yaml` | Manual `workflow_dispatch` | **Primary** release cut in Actions (needs admin `RELEASE_PUSH_TOKEN`) |
| `docs.yml` | Push to `main` | Deploy MkDocs to GitHub Pages |
| `third-party-licenses.yaml` | Path-filtered push to `main` + dispatch | Regenerate `docs/third-party-licenses.md` after dependency changes |
| `third-party-licenses.yaml` | Path-filtered push to `main` + dispatch | Regenerate licenses doc and open a PR (needs `RELEASE_PUSH_TOKEN`) |

---

Expand Down Expand Up @@ -140,14 +140,14 @@ On **version tag pushes**, a `publish` job waits for the Electron `prepare-githu

After merges to `main` that change `package.json`, `pnpm-lock.yaml`, the generator script, or this workflow (and on `workflow_dispatch`):

1. Checkout code
1. Checkout with `persist-credentials: false` (avoids Duplicate Authorization with create-pull-request)
2. Setup pnpm + Node 22
3. Install dependencies (`pnpm install --frozen-lockfile`)
4. Audit licenses (`pnpm run check:licenses`)
5. Regenerate `docs/third-party-licenses.md` (`pnpm run docs:licenses`)
6. Open a PR via `peter-evans/create-pull-request` when the file changed (branch `chore/third-party-licenses`)
6. Open a PR via `peter-evans/create-pull-request` when the file changed (branch `chore/third-party-licenses-<run_id>`)

Direct pushes to `main` are blocked by the merge-queue ruleset, and GitHub Actions cannot bypass it on this org — hence the PR flow.
**Secret:** reuse **`RELEASE_PUSH_TOKEN`** (admin PAT with **contents**, **workflows**, and **pull requests** write). Default `GITHUB_TOKEN` PRs do not auto-run required checks, so they cannot enter the merge queue cleanly. Direct pushes to `main` remain blocked by the merge-queue ruleset.

---

Expand Down Expand Up @@ -326,22 +326,24 @@ Each required check is pinned with `integration_id` **15368** (GitHub Actions) i

- **Repository admins** (`RepositoryRole` id 5) — emergency hotfixes and local `pnpm run release` (direct push of bump commit + tag to `main`)

GitHub Actions **cannot** be added as a bypass actor on this organization (“must be part of the ruleset source or owner organization”). [`third-party-licenses.yaml`](../.github/workflows/third-party-licenses.yaml) therefore opens a PR instead of pushing to `main`.
GitHub Actions **cannot** be added as a bypass actor on this organization (“must be part of the ruleset source or owner organization”). [`third-party-licenses.yaml`](../.github/workflows/third-party-licenses.yaml) therefore opens a PR (via `RELEASE_PUSH_TOKEN`) instead of pushing to `main`.

**Pull request gate:** one approving review, **dismiss stale reviews on push**, and **require last push approval** so an approved PR cannot enter the merge queue after unreviewed follow-up commits.

### Applying / updating the ruleset

Canonical JSON lives at [`.github/rulesets/main-merge-queue.json`](../.github/rulesets/main-merge-queue.json) (live ruleset id **20821455**).
Canonical JSON lives at [`.github/rulesets/main-merge-queue.json`](../.github/rulesets/main-merge-queue.json) (live ruleset id **20821455**). Vitest contract: `scripts/main-merge-queue-ruleset.test.mjs` (pinned checks + review gates). After changing the JSON, **PUT the live ruleset** or drift will remain until someone syncs:

```bash
# Update
# Update live ruleset from canonical JSON
gh api repos/Colorado-Mesh/mesh-client/rulesets/20821455 \
--method PUT \
--input .github/rulesets/main-merge-queue.json
```

`gh api --input` can hit HTTP/2 content-length issues on create; if that fails, POST the JSON body with Python `urllib` (same payload).

**Rollout:** merge the PR that adds `merge_group` triggers to `ci.yaml` / `tests.yaml` **before** flipping this ruleset to `enforcement: active`. Enabling the queue without those triggers leaves required checks pending forever.
The ruleset is already **active** with `merge_group` triggers on `ci.yaml` / `tests.yaml`. Keep those triggers if you ever recreate the ruleset — enabling the queue without them leaves required checks pending forever.

---

Expand Down
15 changes: 8 additions & 7 deletions docs/release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ Documentation deploys separately: [`docs.yml`](../.github/workflows/docs.yml) ru
4. The workflow sets `MESH_CLIENT_RELEASE_YES=1` (non-interactive). Locally use `pnpm run release -- --yes` or the same env var.
5. Wait for `release.yaml` + `flatpak.yaml` to attach draft artifacts, then **Publish** on GitHub.

**Secret:** `RELEASE_PUSH_TOKEN` — fine-grained PAT (or GitHub App installation token) owned by a repo **admin** (so the merge-queue ruleset bypass applies), with **contents: write** and **workflows: write**. Plain `GITHUB_TOKEN` cannot trigger tag workflows and cannot push past the ruleset.
**Secret:** `RELEASE_PUSH_TOKEN` — fine-grained PAT (or GitHub App installation token) owned by a repo **admin** (so the merge-queue ruleset bypass applies), with **contents: write**, **workflows: write**, and **pull requests: write** (also used by `third-party-licenses.yaml` so bot PRs run required checks). Plain `GITHUB_TOKEN` cannot trigger tag workflows, cannot push past the ruleset, and cannot open check-running license PRs.

Version detection lives in [`scripts/detectReleaseBump.mjs`](../scripts/detectReleaseBump.mjs) (handles scoped Conventional Commits such as `feat(rrc): …`).
Version detection lives in [`scripts/detectReleaseBump.mjs`](../scripts/detectReleaseBump.mjs) (handles scoped Conventional Commits such as `feat(rrc): …`). Do not set `MESH_CLIENT_RELEASE_PARSE_ONLY` in Actions (test-only hook; Cut release clears it).

---

Expand Down Expand Up @@ -81,9 +81,10 @@ pnpm run release --finish # complete a mid-release after
pnpm run release -- --yes # non-interactive (skip both confirmation prompts)
pnpm run release -- --yes --skip-dep-update patch # CI-style: no pnpm update
MESH_CLIENT_RELEASE_YES=1 pnpm run release # same as --yes (avoids pnpm's own -y)
# Invalid: --auto cannot be combined with patch|minor|major|x.x.x
```

The script prompts twice by default (start pre-flight, then confirm after checks pass). Pass **`-- --yes`** after `pnpm run release` (or set `MESH_CLIENT_RELEASE_YES=1`) to skip those prompts — useful for automation. Use `--` so pnpm does not swallow `-y`/`--yes`. **Expect several minutes** for the full validation chain.
The script prompts twice by default (start pre-flight, then confirm after checks pass). Pass **`-- --yes`** after `pnpm run release` (or set `MESH_CLIENT_RELEASE_YES=1`) to skip those prompts — useful for automation. Use `--` so pnpm does not swallow `-y`/`--yes`. **`--auto` plus an explicit bump is rejected.** **Expect several minutes** for the full validation chain.

**Full suite only:** Release must never use `test:staged`, `test:changed`, or `vitest related`. Pre-commit may run a staged subset for speed; release matches PR CI by running the unrestricted `pnpm run test:run` (`vitest run`) and does not soft-skip actionlint/yamllint when those tools are missing.

Expand Down Expand Up @@ -235,13 +236,13 @@ When artifacts and notes look correct:

## Version naming

Follow [Semantic Versioning](https://semver.org/):
Follow [Semantic Versioning](https://semver.org/). Auto-detect is implemented in [`scripts/detectReleaseBump.mjs`](../scripts/detectReleaseBump.mjs) (called from `release.sh` / Cut release):

- **Major (X.0.0):** Breaking changes (`BREAKING CHANGE:` footer or `feat!:` / `fix!:`)
- **Minor (0.X.0):** New features (`feat:`), backward compatible
- **Major (X.0.0):** `type!:` / `type(scope)!:`, or a line-anchored `BREAKING CHANGE:` / `BREAKING-CHANGE:` footer in a commit body
- **Minor (0.X.0):** New features (`feat:` or `feat(scope):`), backward compatible
- **Patch (0.0.X):** Fixes and other conventional commits without `feat:`

`release.sh` applies these rules when auto-detecting the bump.
Release notes “Breaking Changes” use the same subject bang + footer rules (not subject-only).

---

Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,12 @@
"update": "bash scripts/update.sh"
},
"dependencies": {
"@bufbuild/protobuf": "^2.13.0",
"@bufbuild/protobuf": "^2.14.0",
"@meshtastic/protobufs": "npm:@jsr/meshtastic__protobufs@^2.7.26",
"@stoprocent/noble": "^2.7.1",
"@xterm/addon-fit": "^0.11.0",
"@xterm/xterm": "^6.0.0",
"@zip.js/zip.js": "^2.8.43",
"@zip.js/zip.js": "^2.8.47",
"builder-util-runtime": "9.7.0",
"dompurify": "^3.4.13",
"electron-updater": "^6.8.9",
Expand Down Expand Up @@ -229,7 +229,7 @@
"vite": "^8.2.1",
"vitest": "^4.1.10",
"vitest-axe": "1.0.0-pre.5",
"zustand": "^5.0.14"
"zustand": "^5.0.15"
},
"packageManager": "pnpm@11.21.0+sha512.521705bce689924eac72f5a3587122f362689ef6571e55ba80076fd637c11132ecffada26fad4ea79c485bfddbfd3d5a2a5b05805a77e893de71ec8a6cca3bb1",
"engines": {
Expand Down
46 changes: 23 additions & 23 deletions pnpm-lock.yaml

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

Loading