Skip to content

ci: fix broken workflows, tighten permissions, add community health files - #1374

Draft
Hunter275 wants to merge 1 commit into
meshtastic:mainfrom
Hunter275:claude-github-fixes
Draft

ci: fix broken workflows, tighten permissions, add community health files#1374
Hunter275 wants to merge 1 commit into
meshtastic:mainfrom
Hunter275:claude-github-fixes

Conversation

@Hunter275

@Hunter275 Hunter275 commented Aug 1, 2026

Copy link
Copy Markdown
Member

Description

An audit of the GitHub Actions setup and repository metadata. Several workflows
were silently not doing what they appeared to do; this fixes those, tightens
workflow permissions, and adds the missing community health files.

No application code is touched — this is entirely .github/ plus three
root-level docs.

Changes Made

Things that were silently broken

  • Crowdin source upload never fired. The path filter in
    crowdin-upload-sources.yml was /apps/web/public/i18n/locales/en/*.json.
    Path filters are relative to the repo root and must not start with /, so the
    glob never matched and English source strings were never auto-pushed to
    Crowdin on merge.
  • Formatting was never enforced. pr.yml ran pnpm run lint and then
    pnpm run check — but check is lint && format, so the linter ran twice
    and the only step that checked formatting carried continue-on-error: true.
  • Nothing typechecked. apps/web has a typecheck script, but no workflow
    called it, and build is bare vite build, which transpiles without
    checking types.
  • GHCR images weren't linked to the repo. Both release-web.yml and
    nightly.yml set org.opencontainers.image.source to owner/repo. That
    label must be a URL for GHCR to attach the package to the repository.
  • A dead concurrency group. update-stable-from-master.yml used
    group: update-stable-${{ github.run_id }}. run_id is unique per run, so
    every run got its own group and overlapping releases never serialized before
    force-pushing stable.
  • Removed a leftover debug step in pr.yml that grepped the lockfile for
    hardcoded versions 2.7.18 / 2.7.20.

Security

  • release-protobufs.yml: permissions: write-allcontents: read
    (publish-jsr already scoped itself correctly).
  • ci.yml: dropped contents: write + packages: write from a job that only
    builds.
  • Added explicit permissions blocks to e2e.yml and the three Crowdin
    workflows, which previously inherited the repository default.
  • Moved all untrusted interpolation out of run: bodies into env:
    (release tags, workflow_dispatch inputs), and added validation for the
    protobufs version and packages bump inputs.
  • Pinned bufbuild/buf-setup-action@main@v1 and
    denoland/setup-deno@main with deno-version: rc@v2 / v2.x. A
    publishing workflow shouldn't track a moving branch or an RC toolchain.
  • Added workflow-lint.yml running actionlint (YAML/expression/shell
    errors) and zizmor (workflow security audit, SARIF uploaded to code
    scanning) on changes under .github/.
  • npm publish now passes --provenance; the job already had
    id-token: write for JSR.

Reliability and cost

  • timeout-minutes on every job — previously only e2e had one, so a hung job
    could burn the full 6-hour default.
  • Concurrency groups added to ci, e2e, nightly, release-web,
    release-packages, release-protobufs, stale, and the Crowdin workflows.
  • e2e is the most expensive workflow in the repo and triggers on
    packages/**. It gained a draft filter, cancel-in-progress, a Playwright
    browser cache keyed on the resolved Playwright version (with install-deps
    on cache hit), and pip caching.
  • nightly moved from redhat-actions/buildah to the same buildx path
    release-web uses, so the nightly actually exercises the release image build
    instead of a parallel toolchain. Added a gate that skips scheduled runs when
    nothing landed in the last 24h (manual runs always build).
  • release-packages now validates every target before mutating the tree, and
    uses a per-run release/version-bump-<run_id> branch — the fixed
    version_bump branch made the second run fail on git checkout -b.
  • stale moved from every 4 hours to daily, and only-issue-labels was
    "Bug" while ISSUE_TEMPLATE/bug.yml applies bug; aligned, and added
    exempt labels.
  • dependabot groups github-actions and dev dependencies into single PRs
    to cut churn.
  • pnpm run testpnpm exec vitest run in CI, so it doesn't depend on
    implicit CI=true watch-mode detection.

Repository metadata

.github/ had no CODEOWNERS, SECURITY.md, CODE_OF_CONDUCT.md, or
ISSUE_TEMPLATE/config.yml. Added:

  • SECURITY.md — private disclosure via GitHub Security Advisories, with scope
    notes for a client that handles channel PSKs and node private keys.
  • ISSUE_TEMPLATE/config.ymlblank_issues_enabled: false plus contact
    links to Discord, the forum, docs, and the firmware repo, to divert support
    questions before they become issues.
  • CODE_OF_CONDUCT.md — points at the existing org-wide Meshtastic CoC rather
    than duplicating it.
  • Root CONTRIBUTING.md — a pointer to apps/web/CONTRIBUTING.md, since GitHub
    only surfaces the file from the root, .github/, or docs/.
  • CODEOWNERSall rules are commented out, see below.

Needs a maintainer decision

CODEOWNERS handles. The file is in place with proposed path rules
(workflows, infra, transports, protobufs, i18n sources) but every rule is
commented out, because I couldn't determine the right handles or team slugs. A
rule naming a nonexistent user or team is silently ignored by GitHub, which is
worse than no file. Someone with org context should fill in the owners and
uncomment.

Formatting and typecheck are non-blocking. Both steps are added with
continue-on-error: true and a TODO(ci) comment, because both currently fail
on main for pre-existing reasons:

  • oxfmt reports 611 of 624 files as unformatted — drift that looks like it
    came from the oxfmt 0.60 bump. Needs a standalone pnpm run format:fix
    sweep commit, which would conflict with every open branch, so it didn't belong
    in this PR.
  • tsc --noEmit reports ~25 errors, in two clusters:
    • better-result no longer exports ResultType (widespread across sdk,
      sdk-react, and transport-web-serial)
    • two copies of the protobufs types resolve simultaneously — the workspace
      packages/protobufs build and @jsr/meshtastic__protobufs@2.7.20 — so
      structurally identical enums are treated as unrelated types. Notably
      HardwareModel has diverged (HELTEC_MESH_NODE_T096 is missing from the
      JSR copy). This is the same conflict the removed debug step was grepping
      for.

Both should be flipped to blocking once those are addressed. Happy to split
either into its own PR.

Testing Done

Validated with actionlint v1.7.12 — 0 errors across all 13 workflows
(previously and after). YAML syntax of every workflow, dependabot.yml, and
both issue templates verified separately.

No runtime verification of the workflows themselves — they need a merge to
exercise. The changes most worth a careful look on first run are the nightly
buildx switch and the nightly skip-if-no-commits gate.

Checklist

  • Code follows project style guidelines
  • Documentation has been updated or added
  • Tests have been added or updated — n/a, no application code changed
  • All i18n translation labels have been added — n/a

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Added contribution guidance, code of conduct expectations, security reporting instructions, and review ownership documentation.
    • Improved issue and pull request guidance, including support channels and validation checklists.
  • Chores

    • Hardened automation with safer permissions, concurrency controls, timeouts, and workflow validation.
    • Improved release and nightly build workflows, including change detection, multi-architecture publishing, and input validation.
    • Added caching to end-to-end test setup and refined scheduled issue maintenance.

…iles

Audit of the GitHub Actions setup and repository metadata.

Silent breakages:
- crowdin-upload-sources: path filter had a leading slash, so the glob never
  matched and English source strings were never auto-uploaded to Crowdin.
- pr.yml: `pnpm run check` is `lint && format`, so the linter ran twice while
  the only formatting check carried `continue-on-error`. Formatting was never
  enforced. Replaced with a direct `format` call, and added a typecheck step
  (`vite build` transpiles without checking types, so nothing typechecked).
- release-web / nightly: `org.opencontainers.image.source` was set to
  `owner/repo` rather than a URL, so GHCR never linked the image to the repo.
- update-stable-from-master: concurrency group keyed on `github.run_id`, which
  is unique per run, so overlapping releases never serialized before
  force-pushing `stable`.
- Removed a leftover debug step grepping the lockfile for hardcoded versions.

Security:
- release-protobufs: `permissions: write-all` -> `contents: read`.
- ci.yml: dropped unused `contents: write` / `packages: write`.
- Added explicit `permissions` to e2e and the three crowdin workflows.
- Moved all untrusted interpolation out of `run:` bodies into `env:`, and
  added input validation for the protobufs `version` and packages `bump`.
- Pinned bufbuild/buf-setup-action@main -> @v1 and
  denoland/setup-deno@main (deno-version: rc) -> @v2 / v2.x.
- Added workflow-lint.yml running actionlint and zizmor with SARIF upload.
- npm publish now emits build provenance.

Reliability and cost:
- `timeout-minutes` on every job; concurrency groups where they were missing.
- e2e: draft filter, cancel-in-progress, Playwright browser cache keyed on the
  resolved version, and pip caching.
- nightly: moved from buildah/podman to the same buildx path releases use, so
  it actually exercises the release image build; added a gate that skips
  scheduled runs when nothing landed in the last day.
- release-packages: validate all targets before mutating the tree, and use a
  per-run bump branch instead of a fixed `version_bump` that broke on rerun.
- stale: daily instead of every 4h, and `only-issue-labels` corrected to `bug`
  to match the label the issue template actually applies.
- dependabot: grouped github-actions and dev-dependencies to cut PR churn.
- `vitest` -> `vitest run` in CI.

Repository metadata:
- Added SECURITY.md, CODE_OF_CONDUCT.md, a root CONTRIBUTING.md pointer, and
  ISSUE_TEMPLATE/config.yml with community contact links.
- Added CODEOWNERS with all rules commented out pending real handles.

Formatting and typecheck steps are intentionally non-blocking for now; each
carries a TODO(ci) explaining the pre-existing failures to fix before flipping.

Validated with actionlint v1.7.12: 0 errors across all 13 workflows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 1, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the Meshtastic Team on Vercel.

A member of the Team first needs to authorize it.

@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Comment thread .github/workflows/e2e.yml

- name: Cache Playwright browsers
id: pw-cache
uses: actions/cache@v4
should-build: ${{ steps.check.outputs.should-build }}
steps:
- name: Checkout code
uses: actions/checkout@v7
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.created=${{ steps.meta.outputs.created }}
- name: Set up Buildx
uses: docker/setup-buildx-action@v4
id: push-to-registry
uses: redhat-actions/push-to-registry@v3
- name: Login to GHCR
uses: docker/login-action@v4
# Same builder as release-web.yml, so the nightly actually exercises the
# release image build path instead of a parallel buildah/podman stack.
- name: Build and push image (multi-arch)
uses: docker/build-push-action@v7
Comment on lines +28 to +29
- name: Checkout code
uses: actions/checkout@v7
- name: Print image URLs
run: |
echo "Moving tag: ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.moving_tag }}"
echo "Moving tag: ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.moving_tag }}"
- name: Print image URLs
run: |
echo "Moving tag: ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.moving_tag }}"
echo "Moving tag: ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.moving_tag }}"
Comment on lines +32 to +33
- name: Checkout code
uses: actions/checkout@v7
Comment on lines +47 to +48
- name: Checkout code
uses: actions/checkout@v7
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request adds repository governance documents and updates GitHub configuration. It standardizes permissions, concurrency, timeouts, validation, dependency grouping, localization automation, issue processing, container publishing, and package, protobuf, web, and stable-branch releases.

Changes

Repository automation

Layer / File(s) Summary
Governance and contributor guidance
.github/CODEOWNERS, .github/ISSUE_TEMPLATE/config.yml, .github/SECURITY.md, CODE_OF_CONDUCT.md, CONTRIBUTING.md, .github/pull_request_template.md
Adds repository ownership guidance, issue routing, security reporting, conduct guidance, contribution instructions, and explicit pull request validation commands.
Dependency update grouping
.github/dependabot.yml
Groups GitHub Actions and development dependencies and applies dependency labels.
Continuous validation workflows
.github/workflows/ci.yml, .github/workflows/pr.yml, .github/workflows/e2e.yml, .github/workflows/nightly.yml, .github/workflows/workflow-lint.yml
Adds permission limits, concurrency controls, timeouts, validation steps, caching, change detection, workflow linting, security auditing, and Docker Buildx publishing.
Localization and issue automation
.github/workflows/crowdin-*.yml, .github/workflows/inactive-issue.yml
Updates Crowdin permissions, path filters, concurrency, and timeouts. Adjusts inactive issue scheduling, labels, and exclusions.
Package release workflow
.github/workflows/release-packages.yml
Validates release inputs and targets, uses environment-based metadata, creates run-specific pull requests, and enables npm provenance.
Protobuf release workflow
.github/workflows/release-protobufs.yml
Adds scoped permissions, release concurrency, timeouts, version validation, and pinned stable tool actions.
Web image and stable branch releases
.github/workflows/release-web.yml, .github/workflows/update-stable-from-master.yml
Adds release controls, environment-based shell inputs, updated OCI metadata, and explicit source SHA handling.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: danditomaso

Poem

A rabbit checks each workflow line,
While guarded permissions neatly align.
Dependencies group in a row,
Release paths know where to go.
Tests and lint now hop with care—
Secure little carrots everywhere.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main workflow, permission, and repository metadata changes.
Description check ✅ Passed The description covers the required sections and provides detailed changes, testing, and checklist information.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Hunter275
Hunter275 marked this pull request as draft August 1, 2026 04:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🧹 Nitpick comments (2)
.github/workflows/workflow-lint.yml (1)

36-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pin the actionlint Docker image to a specific version, not :latest.

docker://rhysd/actionlint:latest floats to whatever image is current at run time. A newer image release can change actionlint behavior without any change in this repository and undermines reproducible CI. Use a fixed stable tag, such as v1.7.12, for this runner.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/workflow-lint.yml around lines 36 - 38, Update the
actionlint Docker image reference in the workflow step using rhysd/actionlint so
it uses a fixed stable version tag such as v1.7.12 instead of :latest,
preserving the existing args configuration.
.github/workflows/release-protobufs.yml (1)

145-149: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pin Deno to an exact version for JSR publication.

deno-version: v2.x is a SemVer range, so a rerun can resolve to a later stable patch. Use the repository-declared exact Deno version or add a version file, and set deno-version/deno-version-file accordingly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-protobufs.yml around lines 145 - 149, Update the
Set up Deno step to use the repository-declared exact Deno version instead of
the floating v2.x range, using deno-version or deno-version-file as appropriate.
Ensure reruns of the release-protobufs workflow consistently resolve the same
Deno version for JSR publication.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/dependabot.yml:
- Around line 24-25: Update the npm ecosystem configuration’s labels list to
include both the existing dependencies label and the default npm ecosystem
label, preserving both labels on Dependabot npm update pull requests.

In @.github/pull_request_template.md:
- Around line 46-48: Update .github/pull_request_template.md lines 46-48 to
state that lint/format validation and web typechecking were run, while noting
both are currently non-blocking. Update CONTRIBUTING.md lines 24-33 to document
lint/format checks, unit tests, and web typechecking as required contributor
checks; distinguish pnpm run check:fix from CI validation and link to the task
tracking the non-blocking formatting and typecheck status.

In @.github/SECURITY.md:
- Around line 19-21: Add a private fallback security-report contact alongside
the GitHub Security Advisories URL in .github/SECURITY.md lines 19-21, and add
the same fallback contact in .github/ISSUE_TEMPLATE/config.yml lines 15-17 when
the advisory form is unavailable; preserve the existing advisory guidance.

In @.github/workflows/crowdin-download.yml:
- Around line 8-11: Pin the actions used by the Crowdin workflow, specifically
actions/checkout and crowdin/github-action, to reviewed full-length commit SHAs
instead of the mutable v7 and v2 tags. Keep the existing write permissions and
action configuration unchanged.

In @.github/workflows/release-packages.yml:
- Around line 122-138: Update the release workflow after the version-bump PR
creation in the publish job so JSR and npm publishing runs only after that PR is
merged; otherwise commit the version bump directly before publishing. Ensure the
existing PR creation flow does not proceed to registry publishing while its
branch remains unmerged.
- Around line 140-150: Update the package build invocations in the “Build
selected packages” step and the corresponding commands at the referenced lines
to use the declared build:npm script instead of build, ensuring all package
selections—including transport-web-bluetooth—build successfully before
publishing.
- Around line 61-72: Update the target validation loop in the release workflow
to build an allowlist from direct package directories under packages and reject
each requested TARGETS entry unless it exactly matches one of those paths. Do
not rely solely on checking "$dir/package.json", so inputs such as "../" or
packages/../ cannot pass validation; preserve the existing fail-fast error
behavior for non-allowlisted targets.

In @.github/workflows/release-protobufs.yml:
- Around line 20-22: Update the concurrency group around the release workflow’s
version input so equivalent values with and without the optional “v” resolve to
the same canonical key, matching the normalization performed before publication.
Ensure all protobuf releases for a given package version serialize through one
concurrency group while preserving the existing publish behavior.
- Around line 44-59: Update the version validation in the release workflow’s
shell step to reject any newline or other multiline input by requiring the
entire INPUT_VERSION value to match the semver pattern, not merely one line. Use
a fixed invalid-version error message without interpolating VERSION before
writing it to GITHUB_ENV or emitting any workflow command, while preserving
valid version handling.

In @.github/workflows/release-web.yml:
- Around line 26-28: Update the workflow’s concurrency group for the release
image publishing job so all runs that promote mutable tags such as latest,
major, and major.minor use one shared group instead of a release-specific
expression. Keep cancel-in-progress disabled so releases queue and publish in
order.
- Around line 150-168: Update the release output block to print each generated
image reference from PUSHED_TAGS as a docker pull example, instead of
constructing references from REGISTRY_IMAGE, PULL_TAG, and SHORTSHA. Preserve
the existing latest example condition for non-prerelease releases if applicable,
but ensure all generated tags from steps.meta.outputs.tags are used so
normalized image names and tags match the pushed images.

In @.github/workflows/update-stable-from-master.yml:
- Around line 10-14: Update the workflow’s concurrency-protected release flow to
perform a post-lock staleness check before checking out or force-pushing the
release tag. Derive the current latest release/source commit and compare it with
the run’s release source and current stable target; skip or cancel the run when
its source is older, while allowing the newest release to proceed.

In @.github/workflows/workflow-lint.yml:
- Around line 56-67: Update the “Upload SARIF” step’s if condition to verify
that zizmor.sarif contains content rather than only checking its existence via
hashFiles. Preserve the existing behavior of skipping upload when the
redirection-created file is empty after a failed “Run zizmor” step.

---

Nitpick comments:
In @.github/workflows/release-protobufs.yml:
- Around line 145-149: Update the Set up Deno step to use the
repository-declared exact Deno version instead of the floating v2.x range, using
deno-version or deno-version-file as appropriate. Ensure reruns of the
release-protobufs workflow consistently resolve the same Deno version for JSR
publication.

In @.github/workflows/workflow-lint.yml:
- Around line 36-38: Update the actionlint Docker image reference in the
workflow step using rhysd/actionlint so it uses a fixed stable version tag such
as v1.7.12 instead of :latest, preserving the existing args configuration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1775c352-9e70-488f-a712-5d5212cfb870

📥 Commits

Reviewing files that changed from the base of the PR and between 9a47442 and 7cb9096.

📒 Files selected for processing (20)
  • .github/CODEOWNERS
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/SECURITY.md
  • .github/dependabot.yml
  • .github/pull_request_template.md
  • .github/workflows/ci.yml
  • .github/workflows/crowdin-download.yml
  • .github/workflows/crowdin-upload-sources.yml
  • .github/workflows/crowdin-upload-translations.yml
  • .github/workflows/e2e.yml
  • .github/workflows/inactive-issue.yml
  • .github/workflows/nightly.yml
  • .github/workflows/pr.yml
  • .github/workflows/release-packages.yml
  • .github/workflows/release-protobufs.yml
  • .github/workflows/release-web.yml
  • .github/workflows/update-stable-from-master.yml
  • .github/workflows/workflow-lint.yml
  • CODE_OF_CONDUCT.md
  • CONTRIBUTING.md

Comment thread .github/dependabot.yml
Comment on lines +24 to +25
labels:
- "dependencies"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== dependabot.yml =="
cat -n .github/dependabot.yml 2>/dev/null || true

echo
echo "== repo automation references to npm label =="
rg -n '"npm"|: "npm"|github-actions/npm|npm|dependencies' .github workflows . 2>/dev/null | head -200 || true

echo
echo "== docs excerpt for labels =="
python3 - <<'PY'
try:
    import urllib.request
    url = "https://docs.github.com/en/code-security/tutorials/secure-your-dependencies/customizing-dependabot-prs"
    req = urllib.request.Request(url, headers={"User-Agent":"CodeRabbit-verifier"})
    with urllib.request.urlopen(req, timeout=20) as resp:
        text = resp.read().decode("utf-8","replace")
    for token in ["labels:", "npm", "dependencies", "github-actions"]:
        print(f"-- occurrences of [{token}] --")
        hits = [line.strip() for line in text.splitlines() if token in line]
        print("\n".join(hits[:40]))
except Exception as e:
    print(f"DOC_FETCH_ERROR: {e}")
PY

Repository: meshtastic/web

Length of output: 13752


🌐 Web query:

GitHub Dependent PR labels default labels npm package ecosystem labels only configuration

💡 Result:

By default, GitHub Dependabot automatically applies labels to pull requests: a dependencies label, an ecosystem-specific label (e.g., npm, java), and SemVer labels if they exist in the repository [1][2]. These default labels are created automatically if they do not exist [1][2]. You can override this behavior by using the labels option within your dependabot.yml file for a specific package ecosystem [1][3]. When you define the labels option: 1. The labels you specify replace the default labels entirely for that package ecosystem [1][4]. 2. SemVer labels (major, minor, patch) will continue to be applied automatically, in addition to your custom labels, if they exist in your repository [1][4]. 3. The custom labels you specify must already exist in your repository; if a label is not defined, Dependabot will ignore it [1][2]. 4. To remove all labels, including defaults, you can set an empty list: labels: [] [1][4]. There is no configuration setting to explicitly restrict Dependabot to only apply ecosystem labels (e.g., npm) while keeping other defaults; you must manually define the specific set of labels you want to use (including the ecosystem label if you wish to keep it) in the configuration file [1][2].

Citations:


Preserve the npm ecosystem label on npm updates.

labels replaces Dependabot’s default labels entirely for that ecosystem, so this npm config currently applies only dependencies instead of also applying the default npm label. Add npm if you want Dependabot PRs to keep the ecosystem label.

Proposed fix
 labels:
   - "dependencies"
+  - "npm"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
labels:
- "dependencies"
labels:
- "dependencies"
- "npm"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/dependabot.yml around lines 24 - 25, Update the npm ecosystem
configuration’s labels list to include both the existing dependencies label and
the default npm ecosystem label, preserving both labels on Dependabot npm update
pull requests.

Comment on lines +46 to +48
- [ ] `pnpm run check` passes (lint + format)
- [ ] `pnpm --filter ./apps/web run typecheck` passes
- [ ] `pnpm exec vitest run` passes

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
rg -n -C 3 \
  'check:fix|pnpm run check|typecheck|continue-on-error' \
  --glob 'package.json' \
  --glob '.github/workflows/*.yml' \
  --glob '.github/workflows/*.yaml' \
  --glob 'CONTRIBUTING.md' \
  --glob '.github/pull_request_template.md'

Repository: meshtastic/web

Length of output: 152


🏁 Script executed:

#!/usr/bin/envbash
set -u

printf 'Tracked relevant files:\n'
git ls-files | rg '(^package\.json$|CONTRIBUTING\.md$|\.github/pull_request_template\.md$|\.github/workflows/.*\.ya?ml$)' || true

printf '\npackage.json scripts relevant to check/lint/typecheck/tests:\n'
if [ -f package.json ]; then
  python3 - <<'PY'
import json
from pathlib import Path
p=Path('package.json')
data=json.loads(p.read_text())
for k,v in data.get('scripts',{}).items():
    s_k=k.lower()
    s_v=str(v).lower()
    if 'check' in s_k or any(x in ('check','lint','format','typecheck','test','vitest') for x in (k,v.split(maxsplit=3)) if str(x).lower()):
        print(f'{k}: {v}')
PY
fi

printf '\nSearch all tracked text matching keywords:\n'
rg -n -C 2 \
  'check:fix|`pnpm run check|pnpm run check|typecheck|continue-on-error|CI runs|green PR|non-blocking|formatting' . || true

Repository: meshtastic/web

Length of output: 4654


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- .github/pull_request_template.md ---\n'
cat -n .github/pull_request_template.md | sed -n '1,90p'

printf '\n--- CONTRIBUTING.md ---\n'
cat -n CONTRIBUTING.md | sed -n '1,50p'

printf '\n--- .github/pr.yml relevant validation blocks ---\n'
rg -n -C 4 'check|typecheck|format|lint|continue-on-error|pnpm run|pnpm exec|checks' .github/workflows/pr.yml .github/workflows/ci.yml package.json apps/web/package.json || true

printf '\n--- PR-NOTS.md objective context ---\n'
cat -n PR-NOTES.md | sed -n '45,70p'

Repository: meshtastic/web

Length of output: 243


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- .github/pull_request_template.md ---'
cat -n .github/pull_request_template.md | sed -n '1,90p'

printf '%s\n' ''
printf '%s\n' '--- CONTRIBUTING.md ---'
cat -n CONTRIBUTING.md | sed -n '1,50p'

printf '%s\n' ''
printf '%s\n' '--- .github/pr.yml relevant validation blocks ---'
rg -n -C 4 'check|typecheck|format|lint|continue-on-error|pnpm run|pnpm exec|checks' .github/workflows/pr.yml .github/workflows/ci.yml package.json apps/web/package.json || true

printf '%s\n' ''
printf '%s\n' '--- PR-NOTES.md objective context ---'
cat -n PR-NOTES.md | sed -n '45,70p'

Repository: meshtastic/web

Length of output: 11774


Align contributor guidance with the blocking checks.

.github/pr.yml makes formatting and typechecking non-blocking with continue-on-error, but the PR checklist and quick start present those same checks as required for merge/green PR.

  • .github/pull_request_template.md#L46-L48: note that formatting and typechecking were run and are currently non-blocking.
  • CONTRIBUTING.md#L24-L33: document that lint/format check, unit tests, and web typecheck are required; clarify that formatting and typecheck are currently non-blocking with a fix-task link and distinguish pnpm run check:fix from CI validation.
📍 Affects 2 files
  • .github/pull_request_template.md#L46-L48 (this comment)
  • CONTRIBUTING.md#L24-L33
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/pull_request_template.md around lines 46 - 48, Update
.github/pull_request_template.md lines 46-48 to state that lint/format
validation and web typechecking were run, while noting both are currently
non-blocking. Update CONTRIBUTING.md lines 24-33 to document lint/format checks,
unit tests, and web typechecking as required contributor checks; distinguish
pnpm run check:fix from CI validation and link to the task tracking the
non-blocking formatting and typecheck status.

Comment thread .github/SECURITY.md
Comment on lines +19 to +21
Report privately through GitHub Security Advisories:

<https://github.com/meshtastic/web/security/advisories/new>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
status="$(gh api repos/meshtastic/web \
  --jq '.security_and_analysis.private_vulnerability_reporting.status')"
printf 'private_vulnerability_reporting=%s\n' "$status"
test "$status" = enabled

Repository: meshtastic/web

Length of output: 185


🌐 Web query:

GitHub private vulnerability reporting enabled feature request reporting secrets advisory form repository security_advisories new

💡 Result:

GitHub's private vulnerability reporting (PVR) is an opt-in feature that allows security researchers to privately disclose vulnerabilities to repository maintainers directly on the platform [1][2]. For Repository Maintainers To enable this feature, repository owners or administrators can navigate to the repository's security settings [3]: 1. Go to the repository's main page on GitHub. 2. Navigate to Settings and then to Code security and analysis [3]. 3. Under the Advanced Security section, locate Private vulnerability reporting and click Enable [3]. Once enabled, a Report a vulnerability button appears on the repository's Security tab (specifically under the Advisories section), providing a structured form for researchers to submit findings [3]. When a report is submitted, administrators and security managers are notified [3][4]. Maintainers can then review the report, request additional information, or accept the report to initiate a private, collaborative fix—which can include creating a temporary private fork for code development [4]. For Security Researchers When a repository has PVR enabled, you can privately report a vulnerability using the following steps [3][5]: 1. Navigate to the repository on GitHub. 2. Click the Security tab and then click Advisories [3]. 3. Click the Report a vulnerability button to access the private reporting form [3]. 4. Fill out the required details regarding the vulnerability [2]. If a repository does not have PVR enabled, you should follow the repository's documented security policy (typically found in a SECURITY.md file) or contact the maintainers directly [6]. Additionally, GitHub provides a REST API that allows researchers to privately report vulnerabilities programmatically [3][6].

Citations:


Add a private fallback contact for security reports.

Private vulnerability reporting is not enabled for this repository, so both .github/SECURITY.md#L19-L21 and .github/ISSUE_TEMPLATE/config.yml#L15-L17 should add another private contact if this URL does not provide the advisory form.

📍 Affects 2 files
  • .github/SECURITY.md#L19-L21 (this comment)
  • .github/ISSUE_TEMPLATE/config.yml#L15-L17
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/SECURITY.md around lines 19 - 21, Add a private fallback
security-report contact alongside the GitHub Security Advisories URL in
.github/SECURITY.md lines 19-21, and add the same fallback contact in
.github/ISSUE_TEMPLATE/config.yml lines 15-17 when the advisory form is
unavailable; preserve the existing advisory guidance.

Comment on lines +8 to +11
# Needed to push the translation branch and open the PR.
permissions:
contents: write
pull-requests: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

file=".github/workflows/crowdin-download.yml"

if rg -nP '^\s*uses:\s+(crowdin/github-action|actions/checkout)@(?![0-9a-f]{40}\s*$)' "$file"; then
  echo "Mutable action reference found" >&2
  exit 1
fi

Repository: meshtastic/web

Length of output: 262


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

file=".github/workflows/crowdin-download.yml"
echo "== File exists and relevant lines =="
wc -l "$file"
sed -n '1,70p' "$file" | cat -n

echo
echo "== Action refs in workflows =="
rg -n '^\s*uses:\s+' .github/workflows || true

Repository: meshtastic/web

Length of output: 5717


Pin the Crowdin and checkout actions before granting write permissions.

Lines 24 and 27 use actions/checkout@v7 and crowdin/github-action@v2, both mutable tags. This job has contents: write, pull-requests: write, and exposes GITHUB_TOKEN, CROWDIN_PROJECT_ID, and CROWDIN_PERSONAL_TOKEN. Use reviewed full-length commit SHAs for these workflow actions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/crowdin-download.yml around lines 8 - 11, Pin the actions
used by the Crowdin workflow, specifically actions/checkout and
crowdin/github-action, to reviewed full-length commit SHAs instead of the
mutable v7 and v2 tags. Keep the existing write permissions and action
configuration unchanged.

Comment on lines +61 to +72
IFS=',' read -ra TARGETS <<< "${INPUT_PACKAGES}"
TARGETS=("${TARGETS[@]/#/packages/}")
fi

# Validate every target before anything mutates the tree, so a typo
# fails fast instead of half-publishing.
for dir in "${TARGETS[@]}"; do
if [ ! -f "$dir/package.json" ]; then
echo "::error::No package.json at '$dir' - check the 'packages' input." >&2
exit 1
fi
done

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Restrict targets to an exact package allowlist.

INPUT_PACKAGES="../" becomes packages/../. Line 68 accepts that path because the repository root has package.json. The workflow can then modify or release a target outside the intended direct packages/* set.

Build an allowlist from direct package directories. Reject each requested target unless it exactly matches an allowlisted path.

Proposed fix
-          if [ "${INPUT_PACKAGES}" = "all" ] || [ -z "${INPUT_PACKAGES}" ]; then
-            mapfile -t TARGETS < <(ls -d packages/*)
+          mapfile -t ALLOWED_TARGETS < <(
+            find packages -mindepth 1 -maxdepth 1 -type d \
+              -exec test -f '{}/package.json' \; -print | sort
+          )
+
+          if [ "${INPUT_PACKAGES}" = "all" ] || [ -z "${INPUT_PACKAGES}" ]; then
+            TARGETS=("${ALLOWED_TARGETS[@]}")
           else
             IFS=',' read -ra TARGETS <<< "${INPUT_PACKAGES}"
             TARGETS=("${TARGETS[@]/#/packages/}")
           fi
@@
           for dir in "${TARGETS[@]}"; do
-            if [ ! -f "$dir/package.json" ]; then
-              echo "::error::No package.json at '$dir' - check the 'packages' input." >&2
+            if ! printf '%s\n' "${ALLOWED_TARGETS[@]}" | grep -Fqx -- "$dir"; then
+              echo "::error::Invalid package target '$dir'." >&2
               exit 1
             fi
           done
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
IFS=',' read -ra TARGETS <<< "${INPUT_PACKAGES}"
TARGETS=("${TARGETS[@]/#/packages/}")
fi
# Validate every target before anything mutates the tree, so a typo
# fails fast instead of half-publishing.
for dir in "${TARGETS[@]}"; do
if [ ! -f "$dir/package.json" ]; then
echo "::error::No package.json at '$dir' - check the 'packages' input." >&2
exit 1
fi
done
mapfile -t ALLOWED_TARGETS < <(
find packages -mindepth 1 -maxdepth 1 -type d \
-exec test -f '{}/package.json' \; -print | sort
)
if [ "${INPUT_PACKAGES}" = "all" ] || [ -z "${INPUT_PACKAGES}" ]; then
TARGETS=("${ALLOWED_TARGETS[@]}")
else
IFS=',' read -ra TARGETS <<< "${INPUT_PACKAGES}"
TARGETS=("${TARGETS[@]/#/packages/}")
fi
# Validate every target before anything mutates the tree, so a typo
# fails fast instead of half-publishing.
for dir in "${TARGETS[@]}"; do
if ! printf '%s\n' "${ALLOWED_TARGETS[@]}" | grep -Fqx -- "$dir"; then
echo "::error::Invalid package target '$dir'." >&2
exit 1
fi
done
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-packages.yml around lines 61 - 72, Update the
target validation loop in the release workflow to build an allowlist from direct
package directories under packages and reject each requested TARGETS entry
unless it exactly matches one of those paths. Do not rely solely on checking
"$dir/package.json", so inputs such as "../" or packages/../ cannot pass
validation; preserve the existing fail-fast error behavior for non-allowlisted
targets.

Comment on lines +44 to +59
# Workflow inputs are untrusted text; never interpolate into the script body.
env:
INPUT_VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
if [ -n "${{ inputs.version }}" ]; then
VERSION="${{ inputs.version }}"
if [ -n "${INPUT_VERSION}" ]; then
VERSION="${INPUT_VERSION}"
else
echo "No 'version' input. Provide inputs.version." >&2
exit 1
fi
# Reject anything that isn't a plain semver-ish tag before it reaches jq/git.
if ! printf '%s' "$VERSION" | grep -qE '^v?[0-9]+\.[0-9]+\.[0-9]+([-+.][0-9A-Za-z.-]+)?$'; then
echo "::error::Invalid version '$VERSION' (expected e.g. v1.2.3)." >&2
exit 1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

pattern='^v?[0-9]+\.[0-9]+\.[0-9]+([-+.][0-9A-Za-z.-]+)?$'
version=$'1.2.3\nINJECTED=1'

if printf '%s' "$version" | grep -qE "$pattern"; then
  echo "Current validation accepts multiline input."
  printf 'VERSION=%s\n' "$version"
fi

Repository: meshtastic/web

Length of output: 221


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Repository files matching workflow name:"
fd -a 'release-protobufs\.yml$' .github/workflows || true

echo
echo "Workflow excerpt:"
if [ -f .github/workflows/release-protobufs.yml ]; then
  nl -ba .github/workflows/release-protobufs.yml | sed -n '1,120p'
fi

echo
echo "Relevant input definitions/usages in workflows:"
rg -n "inputs\.version|version:|GITHUB_ENV|::error::|GITHUB_OUTPUT|github\.set-output|echo ::error|Invalid version" .github/workflows || true

echo
echo "Programmatic line-wise grep probe with same version values:"
python3 - <<'PY'
import re
pattern = re.compile(r'^v?[0-9]+\.[0-9]+\.[0-9]+([-+.][0-9A-Za-z.-]+)?$')
tests = [
    "1.2.3",
    "v1.2.3",
    "1.2.3\nINJECTED=1",
    "v1.2.3\nINJECTED=1\nNEXT=2",
    "1.2.3\rINJECTED=1",
    "1.2.3\n1.2.3",
    "1.2.3#comment",
]
for value in tests:
    lines = value.splitlines()
    has_line_match = any(pattern.match(line) for line in lines)
    complete_match = bool(pattern.match(value))
    print(repr(value), "-> line_match=", has_line_match, ", complete_match=", complete_match)
PY

Repository: meshtastic/web

Length of output: 312


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Workflow excerpt using awk:"
awk '{printf "%5d\t%s\n", NR, $0}' .github/workflows/release-protobufs.yml | sed -n '1,120p'

echo
echo "Relevant input definitions/usages in workflows:"
rg -n "inputs\.version|version:|GITHUB_ENV|::error::|GITHUB_OUTPUT|github\.set-output|echo ::error|Invalid version" .github/workflows || true

echo
echo "Programmatic line-wise grep probe with same version values:"
python3 - <<'PY'
import re
pattern = re.compile(r'^v?[0-9]+\.[0-9]+\.[0-9]+([-+.][0-9A-Za-z.-]+)?$')
tests = [
    "1.2.3",
    "v1.2.3",
    "1.2.3\nINJECTED=1",
    "v1.2.3\nINJECTED=1\nNEXT=2",
    "1.2.3\rINJECTED=1",
    "1.2.3\n1.2.3",
    "1.2.3#comment",
]
for value in tests:
    lines = value.splitlines()
    has_line_match = any(pattern.match(line) for line in lines)
    complete_match = bool(pattern.match(value))
    print(repr(value), "-> line_match=", has_line_match, ", complete_match=", complete_match)
PY

Repository: meshtastic/web

Length of output: 7866


Reject multiline versions before workflow output.

inputs.version is used directly in GITHUB_ENV and workflow ::error:: output. The current line-based grep accepts values like 1.2.3\nINJECTED=1, so GITHUB_ENV can receive an injected INJECTED=1 record. Validate the full string and use a fixed error message before writing to GITHUB_ENV or emitting workflow commands.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-protobufs.yml around lines 44 - 59, Update the
version validation in the release workflow’s shell step to reject any newline or
other multiline input by requiring the entire INPUT_VERSION value to match the
semver pattern, not merely one line. Use a fixed invalid-version error message
without interpolating VERSION before writing it to GITHUB_ENV or emitting any
workflow command, while preserving valid version handling.

Comment on lines +26 to +28
concurrency:
group: release-web-${{ github.event.release.tag_name || inputs.tag_name || github.ref }}
cancel-in-progress: false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repository files matching workflow =="
git ls-files | rg '(^|/)\.github/workflows/' || true

echo
echo "== target workflow excerpt =="
if [ -f .github/workflows/release-web.yml ]; then
  nl -ba .github/workflows/release-web.yml | sed -n '1,220p'
else
  echo "missing .github/workflows/release-web.yml"
fi

echo
echo "== search for latest/major mutable tag usage in workflows =="
rg -n "latest|major|minor|tag_name|pulls.*latest|imagoi|image|tag|alias|concurrency" .github/workflows -S || true

echo
echo "== changed files/stat =="
git diff --stat || true
git diff -- .github/workflows/release-web.yml || true

Repository: meshtastic/web

Length of output: 938


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== target workflow excerpt =="
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/release-web.yml | sed -n '1,240p'

echo
echo "== relevant workflow search =="
rg -n "latest|major|minor|tag_name|pulls|imagoi|image|tag|alias|concurrency|docker|push" .github/workflows/release-web.yml .github/workflows -S || true

Repository: meshtastic/web

Length of output: 28575


Serialize release image pushes that promote mutable tags.

release-web-${{ github.event.release.tag_name || inputs.tag_name || github.ref }} creates a separate concurrency group per release. Full releases also push latest, major, and major.minor tags, so two releases can share mutable image tags concurrently and an older build can overwrite a newer build. Use one shared publish group for mutable alias promotion, or update immutable releases without pushing shared tags unless an explicit queue coordinates these runs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-web.yml around lines 26 - 28, Update the
workflow’s concurrency group for the release image publishing job so all runs
that promote mutable tags such as latest, major, and major.minor use one shared
group instead of a release-specific expression. Keep cancel-in-progress disabled
so releases queue and publish in order.

Comment on lines +150 to +168
shell: bash
# Release tags/titles are user-supplied; keep them out of the script body.
env:
PUSHED_TAGS: ${{ steps.meta.outputs.tags }}
IS_PRERELEASE: ${{ github.event.release.prerelease }}
PULL_TAG: ${{ github.event.release.tag_name || inputs.tag_name || format('adhoc-{0}', steps.gen.outputs.shortsha) }}
SHORTSHA: ${{ steps.gen.outputs.shortsha }}
run: |
set -euo pipefail
echo "🖼️ Pushed tags:"
echo "${{ steps.meta.outputs.tags }}"
echo "${PUSHED_TAGS}"
echo
echo "Pull examples:"
if [[ "${{ github.event_name }}" == "release" && "${{ github.event.release.prerelease }}" == "false" ]]; then
echo " docker pull ${{ env.REGISTRY_IMAGE }}:latest"
if [[ "${GITHUB_EVENT_NAME}" == "release" && "${IS_PRERELEASE}" == "false" ]]; then
echo " docker pull ${REGISTRY_IMAGE}:latest"
fi
# Always available:
echo " docker pull ${{ env.REGISTRY_IMAGE }}:${{ github.event.release.tag_name || inputs.tag_name || format('adhoc-{0}', steps.gen.outputs.shortsha) }}"
echo " docker pull ${{ env.REGISTRY_IMAGE }}:sha-${{ steps.gen.outputs.shortsha }}"
echo " docker pull ${REGISTRY_IMAGE}:${PULL_TAG}"
echo " docker pull ${REGISTRY_IMAGE}:sha-${SHORTSHA}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow file excerpt =="
if [ -f .github/workflows/release-web.yml ]; then
  nl -ba .github/workflows/release-web.yml | sed -n '1,230p'
else
  echo "missing .github/workflows/release-web.yml"
  fd -a 'release-web\.ya?ml$' .
fi

echo
echo "== docker metadata action usage and related vars =="
rg -n "docker/metadata-action|steps\.meta\.outputs|outputs:|tags:" .github/workflows .github -g '*.yml' -g '*.yaml' || true

echo
echo "== repository references to PULL_TAG / RELEASE_TAG / SHORTSHA =="
rg -n "PULL_TAG|SHORTSHA|REGISTRY_IMAGE|PUSHED_TAGS|steps\.meta\.outputs\.tags|docker/metadata-action" . -g '!node_modules' -g '!dist' -g '!build' || true

Repository: meshtastic/web

Length of output: 220


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow file excerpt =="
if [ -f .github/workflows/release-web.yml ]; then
  awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/release-web.yml | sed -n '1,230p'
else
  echo "missing .github/workflows/release-web.yml"
  find . -name 'release-web.y*ml' -print
fi

echo
echo "== docker metadata action usage and related vars =="
grep -RIn "docker/metadata-action\|steps\.meta\.outputs\|outputs:\|tags:" .github/workflows .github 2>/dev/null | grep -E '\.(yml|yaml):' || true

echo
echo "== repository references to PULL_TAG / RELEASE_TAG / SHORTSHA =="
grep -RIn "PULL_TAG|SHORTSHA|REGISTRY_IMAGE|PUSHED_TAGS|steps\.meta\.outputs\.tags|docker/metadata-action" . 2>/dev/null | grep -v node_modules | grep -v dist | grep -v build || true

Repository: meshtastic/web

Length of output: 11393


🌐 Web query:

docker/metadata-action outputs.tags normalizes image name invalid characters docker build-push-action build

💡 Result:

The docker/metadata-action automatically normalizes image names and tags to ensure compliance with Docker's reference specifications [1][2][3]. For image names, the action enforces lowercase and ensures components follow naming rules (such as not starting or ending with separators like dots or dashes) [1][2]. For tags, the action performs several normalization steps: - Lowercase transformation: The action ensures the image name is lowercased [1][3]. - Invalid character replacement: Sequences of invalid characters within tags are automatically replaced with a hyphen (-) [1][4]. - Leading character stripping: The action strips leading periods (.) and hyphens (-) from tags to comply with the rule that tags cannot start with these characters [5]. When using the outputs from docker/metadata-action in the docker/build-push-action, you can pass the generated tags directly to the tags input [6]. Because the metadata action produces these sanitized, ready-to-use strings, it is the recommended way to prevent build errors related to invalid reference formats (e.g., repository names containing uppercase letters or invalid tag characters) [6]. If you encounter issues with using the output in shell commands (such as newline characters interfering with execution), you can adjust the separator using the sep-tags input or utilize the version output instead of the full tags list [7].

Citations:


Print the generated image references.

docker/metadata-action normalizes image names and tag characters, so the raw REGISTRY_IMAGE and raw release/input tag may not match the refs pushed through steps.meta.outputs.tags. If a release tag like release/1.2.3 is provided, the printed docker pull ghcr.io/...:release/1.2.3 may not exist. Print each value from PUSHED_TAGS as the pull example.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-web.yml around lines 150 - 168, Update the release
output block to print each generated image reference from PUSHED_TAGS as a
docker pull example, instead of constructing references from REGISTRY_IMAGE,
PULL_TAG, and SHORTSHA. Preserve the existing latest example condition for
non-prerelease releases if applicable, but ensure all generated tags from
steps.meta.outputs.tags are used so normalized image names and tags match the
pushed images.

Comment on lines +10 to +14
# A static group so overlapping releases serialize instead of racing to
# force-push `stable`. Never cancel: a half-run leaves `stable` inconsistent.
concurrency:
group: update-stable-${{ github.run_id }}
cancel-in-progress: true
group: update-stable
cancel-in-progress: false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file outline/size =="
wc -l .github/workflows/update-stable-from-master.yml
ast-grep outline .github/workflows/update-stable-from-master.yml || true

echo "== workflow contents =="
cat -n .github/workflows/update-stable-from-master.yml

echo "== git diff stat/name =="
git diff --stat HEAD~1..HEAD 2>/dev/null || git diff --stat
git diff -- .github/workflows/update-stable-from-master.yml 2>/dev/null || true

Repository: meshtastic/web

Length of output: 6246


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import io

# Read-only parser-ish checker: validate whether the workflow is ordered by release order semantics.
# This mirrors the documented facts: release workflows can queue; concurrency serialization does not enforce
# release order; a later serialized run for an older release can overwrite stable with its source.
payload = Path(".github/workflows/update-stable-from-master.yml").read_text()

checks = {
    "has_release_trigger": "on:\n  release:" in payload,
    "static_concurrency": "group: update-stable" in payload and "cancel-in-progress: false" in payload,
    "resolves_source_from_event": "${{ github.event.release.tag_name }}" in payload and "${{ github.event.release.target_commitish }}" in payload,
    "no_post_lock_staleness_check": "steps.meta.outputs.sha" in payload and "git checkout -B stable \"${SOURCE_SHA}\"" in payload and "current-stable" not in payload and "latest-release" not in payload.lower() and "latest release" not in payload.lower(),
    "force_with_league_after_local_checkout": payload.index("Create/reset local stable to SHA") > payload.index("Determine source SHA") and payload.index("Push stable (force-with-lease)") > payload.index("Create/reset local stable to SHA"),
}

print("\n".join(f"{k}: {v}" for k, v in checks.items()))

# Deterministic interleaving model:
# Run A queued first, run B queued later; concurrency lock allows only one at a time; A runs and finishes after B started.
models = [
    {"order": ["B.acquire", "A.activate", "A.resolve", "A.checkout", "A.push", "B.resolve", "B.checkout", "B.push"],
     "expected_result": "stable points to A release source after B checkout because B checkout does not recheck latest release/source age"},
    {"order": ["A.acquire", "A.activate", "A.resolve", "A.checkout", "A.push", "B.acquire", "B.resolve", "B.checkout", "B.push"],
     "expected_result": "stable points to B release source after ordered execution"},
]
for m in models:
    stable = "initial"
    current_latest_release_index = 1  # A
    latest_release_source_index = 1   # A
    log = []
    for event in m["order"]:
        actor = event.split(".")[0]
        action = event.split(".", 1)[1]
        step_prefix = 10 if actor == "A" else 30
        old_remote = stable
        if action == "resolve":
            actor_source = current_latest_release_index if actor == "A" else latest_release_release_source_index if actor == "B" else actor
        if action == "checkout":
            stable = "release_source:" + actor + "_index:" + ("1" if current_latest_release_index != 1 else "30")
        if action == "push":
            latest_release_release_source_index = 30
            log.append(f"{event}: stable={stable} from old={old_remote}")
    print({"scenario": "stale run later", "result": "stable can point to earlier release source"}), 200
PY

echo "== GitHub Actions concurrency docs evidence request not needed as deterministic queue scenario matches stated concern =="

Repository: meshtastic/web

Length of output: 614


🌐 Web query:

GitHub Actions concurrency run-level concurrency workflow file group cancel-in-progress documentation

💡 Result:

In GitHub Actions, the concurrency key is used to manage and limit the execution of workflows or jobs by placing them into a defined concurrency group [1][2][3]. This ensures that only a single job or workflow with a specific group identifier runs at any given time [1][3]. Configuration options for concurrency include: Group: Defines the identifier for the concurrency group. It can be a hard-coded string or a dynamic expression using context variables such as github, inputs, and vars [1][3]. Concurrency groups are unique across a repository; to restrict cancellation to only the current workflow, you should include the workflow name in the group definition (e.g., group: ${{ github.workflow }}-${{ github.ref }}) [1][4]. Cancel-in-progress: When set to true, this option automatically cancels any currently running job or workflow within the same concurrency group when a new one is queued [1][4]. This can also be defined as a conditional expression [1][4]. Queueing: By default, if a job or workflow is running, new ones are marked as pending, and any existing pending jobs in the same group are canceled [1][4]. To allow multiple runs to queue instead of being canceled, you can set queue: max, which permits up to 100 jobs or workflow runs to wait in line [3]. Concurrency can be applied at either the workflow level (the top level of the YAML file) or the job level (within a specific job's configuration) [3][5]. If a job or workflow with the same concurrency group is already in progress, the new instance will enter a pending state until the previous one completes, unless cancel-in-progress is configured to stop it [1][3][5].

Citations:


Prevent stale release runs from resetting stable.

The static concurrency group only serializes runs; it does not preserve the order of queued releases. If an older release run acquires the lock after a newer release run has updated stable, this workflow checks out the older release tag and push force-with-lease succeeds because the remote has not changed since the stale run resolved it. Add a post-lock staleness check: derive the current latest release or source commit, then cancel/skip if the run’s release source is older than the current stable target.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/update-stable-from-master.yml around lines 10 - 14, Update
the workflow’s concurrency-protected release flow to perform a post-lock
staleness check before checking out or force-pushing the release tag. Derive the
current latest release/source commit and compare it with the run’s release
source and current stable target; skip or cancel the run when its source is
older, while allowing the newest release to proceed.

Comment on lines +56 to +67
- name: Run zizmor
run: uvx zizmor --persona=regular --format=sarif . > zizmor.sarif
continue-on-error: true

# Skip the upload if zizmor crashed outright and left an empty file,
# rather than failing the job on a malformed SARIF.
- name: Upload SARIF
if: ${{ hashFiles('zizmor.sarif') != '' }}
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: zizmor.sarif
category: zizmor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

hashFiles guard does not detect a crashed/empty zizmor run.

The comment states the intent is to "skip the upload if zizmor crashed outright and left an empty file." Shell redirection (> zizmor.sarif) creates the file before uvx zizmor runs, so zizmor.sarif exists — and hashFiles('zizmor.sarif') returns a non-empty hash — even when the command crashes and leaves the file empty or malformed. github/codeql-action/upload-sarif@v4 has no continue-on-error, so a malformed SARIF can fail the job instead of being skipped as intended.

🐛 Proposed fix: check for non-empty file content, not mere existence
+      - name: Check SARIF output
+        id: check-sarif
+        if: always()
+        run: |
+          if [ -s zizmor.sarif ]; then
+            echo "has-output=true" >> "$GITHUB_OUTPUT"
+          else
+            echo "has-output=false" >> "$GITHUB_OUTPUT"
+          fi
+
       - name: Upload SARIF
-        if: ${{ hashFiles('zizmor.sarif') != '' }}
+        if: steps.check-sarif.outputs.has-output == 'true'
         uses: github/codeql-action/upload-sarif@v4
         with:
           sarif_file: zizmor.sarif
           category: zizmor
🧰 Tools
🪛 GitHub Check: zizmor

[failure] 64-64:
unpinned action reference: action is not pinned to a hash (required by blanket policy)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/workflow-lint.yml around lines 56 - 67, Update the “Upload
SARIF” step’s if condition to verify that zizmor.sarif contains content rather
than only checking its existence via hashFiles. Preserve the existing behavior
of skipping upload when the redirection-created file is empty after a failed
“Run zizmor” step.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants