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
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
38 changes: 37 additions & 1 deletion .github/workflows/third-party-licenses.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,23 @@ on:
- .github/workflows/third-party-licenses.yaml
workflow_dispatch:

# Coalesce overlapping regenerations (path-filtered pushes can stack) so we do not
# leave multiple stale license PRs from canceled/superseded runs.
concurrency:
group: third-party-licenses
cancel-in-progress: true

jobs:
update:
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:
ref: main
persist-credentials: false

- name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271
Expand All @@ -42,20 +53,45 @@ 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
base: main
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