Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
152 changes: 123 additions & 29 deletions .claude/skills/ship-release/SKILL.md

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ jobs:
run: git config --global core.autocrlf false

- name: Checkout code
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0

- name: Setup Node 24.11.1
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24.11.1'

- name: Setup node_modules cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
Expand All @@ -60,6 +60,7 @@ jobs:
env:
CSC_LINK: ${{ matrix.os == 'macos-latest' && secrets.MAC_CSC_LINK || '' }}
CSC_KEY_PASSWORD: ${{ matrix.os == 'macos-latest' && secrets.MAC_CSC_KEY_PASSWORD || '' }}
NODE_OPTIONS: --max-old-space-size=6144
run: yarn test

- name: Build app/
Expand All @@ -71,13 +72,13 @@ jobs:

- name: Setup gcloud (Windows)
if: ${{ matrix.os == 'windows-latest' }}
uses: google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f # v2.2.1
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
with:
version: '>=536.0.0'

- name: Authenticate to Google Cloud (Windows)
if: ${{ matrix.os == 'windows-latest' }}
uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2.1.13
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
credentials_json: ${{ secrets.GCP_SA_JSON }}

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/powershell-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install PSScriptAnalyzer
shell: pwsh
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/pull-request-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,17 @@ jobs:
run: git config --global core.autocrlf false

- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
Comment on lines +40 to 42

Copy link
Copy Markdown
Contributor

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:

#!/bin/bash
set -eu

for f in .github/workflows/pull-request-build.yml .github/workflows/powershell-lint.yml; do
  echo "===== $f ====="
  wc -l "$f"
  cat -n "$f"
done

echo "===== Git-related references in workflows ====="
rg -n -i 'git |git$|github_token|token|persist-credentials|credentials|pull-requests:|permissions:|uses: actions/checkout' .github/workflows

echo "===== actions/checkout tag and pin resolution ====="
if command -v gh >/dev/null 2>&1; then
  ref_json="$(gh api repos/actions/checkout/git/refs/tags/v7.0.1)"
  printf '%s\n' "$ref_json" | jq .
  type="$(printf '%s\n' "$ref_json" | jq -r '.object.type')"
  sha="$(printf '%s\n' "$ref_json" | jq -r '.object.sha')"
  if [ "$type" = tag ]; then
    sha="$(gh api "repos/actions/checkout/git/tags/$sha" --jq '.object.sha')"
  fi
  echo "dereferenced commit: $sha"
  gh api "repos/actions/checkout/commits/$sha" --jq '.sha'
  gh api repos/actions/checkout/contents/action.yml?ref=v7.0.1 --jq '.content' | base64 -d | sed -n '1,180p'
else
  echo "gh is unavailable"
fi

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 45050


🏁 Script executed:

#!/bin/bash
set -eu

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

files = [
    Path(".github/workflows/pull-request-build.yml"),
    Path(".github/workflows/powershell-lint.yml"),
]

for path in files:
    lines = path.read_text().splitlines()
    checkout = next(i for i, line in enumerate(lines) if "uses: actions/checkout@" in line)
    end = next(
        (i for i in range(checkout + 1, len(lines))
         if re.match(r"^\s{6}- name:|^\s{6}- uses:", lines[i])),
        len(lines),
    )
    block = lines[checkout:end]

    print(f"FILE={path}")
    print(f"CHECKOUT_LINE={checkout + 1}")
    print("CHECKOUT_HAS_PERSIST_FALSE=" + str(any("persist-credentials: false" in line for line in block)))
    print("CHECKOUT_WITH_LINES=" + " | ".join(line.strip() for line in block[1:8] if line.strip()))

    permission_lines = [
        line.strip() for line in lines
        if re.match(r"^\s{4}(contents|pull-requests):", line)
    ]
    print("JOB_PERMISSIONS=" + " | ".join(permission_lines))

    later_git = [
        (i + 1, line.strip()) for i, line in enumerate(lines[checkout + 1:], checkout + 1)
        if re.search(r"(^|\s)git(\s|$)", line, re.IGNORECASE)
    ]
    print("LATER_GIT_REFERENCES=" + " | ".join(f"{n}:{text}" for n, text in later_git) or "<none>")

    later_steps = [
        line.strip() for line in block
        if re.match(r"^\s*(run:|uses:)", line)
    ]
    print("STEPS_AFTER_CHECKOUT=" + " | ".join(later_steps))
PY

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 688


🏁 Script executed:

#!/bin/bash
set -eu

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

files = [
    Path(".github/workflows/pull-request-build.yml"),
    Path(".github/workflows/powershell-lint.yml"),
]

step_start = re.compile(r"^(?P<indent>\s*)-\s+(?:name|uses):")
for path in files:
    lines = path.read_text().splitlines()
    starts = [(i, len(m.group("indent"))) for i, line in enumerate(lines)
              if (m := step_start.match(line))]
    checkout_i, checkout_indent = next(
        (i, indent) for i, indent in starts
        if "uses: actions/checkout@" in lines[i]
    )
    checkout_end = next(
        (i for i, indent in starts if i > checkout_i and indent == checkout_indent),
        len(lines),
    )
    later_end = [
        line.strip() for line in lines[checkout_end:]
        if re.match(r"^\s+(?:run|uses):", line)
    ]
    later_git = [
        (i + 1, line.strip()) for i, line in enumerate(lines[checkout_i + 1:], checkout_i + 1)
        if re.search(r"(^|\s)git(\s|$)", line, re.IGNORECASE)
    ]
    permissions = [
        line.strip() for line in lines
        if re.match(r"^\s{6}(?:contents|pull-requests):", line)
    ]
    checkout_block = lines[checkout_i:checkout_end]

    print(f"FILE={path}")
    print(f"CHECKOUT_LINE={checkout_i + 1}")
    print("CHECKOUT_BLOCK=" + " | ".join(line.strip() for line in checkout_block))
    print("JOB_PERMISSIONS=" + " | ".join(permissions) or "<none>")
    print("LATER_RUN_OR_USES=" + " | ".join(later_end) or "<none>")
    print("LATER_GIT_REFERENCES=" + " | ".join(
        f"{line_no}:{text}" for line_no, text in later_git
    ) or "<none>")
PY

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 257


🏁 Script executed:

#!/bin/bash
set -eu

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

for path in [
    Path(".github/workflows/pull-request-build.yml"),
    Path(".github/workflows/powershell-lint.yml"),
]:
    lines = path.read_text().splitlines()
    checkout_i = next(i for i, line in enumerate(lines)
                      if "uses: actions/checkout@" in line)
    checkout_indent = len(lines[checkout_i]) - len(lines[checkout_i].lstrip())
    step_i = max(
        i for i in range(checkout_i + 1)
        if re.match(r"^\s*-\s+name:", lines[i])
        and len(lines[i]) - len(lines[i].lstrip()) == checkout_indent
    )
    next_step = next(
        (i for i in range(checkout_i + 1, len(lines))
         if re.match(r"^\s*-\s+name:", lines[i])
         and len(lines[i]) - len(lines[i].lstrip()) == checkout_indent),
        len(lines),
    )

    permissions = [
        line.strip() for line in lines
        if re.match(r"^\s{6}(?:contents|pull-requests):", line)
    ]
    later_steps = [
        line.strip() for line in lines[next_step:]
        if re.match(r"^\s+(?:run|uses):", line)
    ]
    later_git = [
        (i + 1, line.strip()) for i, line in enumerate(lines[checkout_i + 1:], checkout_i + 1)
        if re.search(r"(^|\s)git(\s|$)", line, re.IGNORECASE)
    ]

    print(f"FILE={path}")
    print(f"CHECKOUT_STEP_LINE={step_i + 1}")
    print("CHECKOUT_BLOCK=" + " | ".join(
        line.strip() for line in lines[step_i:next_step]
    ))
    print("JOB_PERMISSIONS=" + " | ".join(permissions) or "<none>")
    print("LATER_RUN_OR_USES=" + " | ".join(later_steps) or "<none>")
    print("LATER_GIT_REFERENCES=" + " | ".join(
        f"{line_no}:{text}" for line_no, text in later_git
    ) or "<none>")
PY

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 286


🏁 Script executed:

#!/bin/bash
set -eu

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

step_re = re.compile(r"^(?P<indent>\s*)-\s+name:")
for path in (
    Path(".github/workflows/pull-request-build.yml"),
    Path(".github/workflows/powershell-lint.yml"),
):
    lines = path.read_text().splitlines()
    checkout_i = next(i for i, line in enumerate(lines)
                      if "uses: actions/checkout@" in line)
    step_i = max(
        i for i in range(checkout_i + 1)
        if step_re.match(lines[i])
    )
    step_indent = len(step_re.match(lines[step_i]).group("indent"))
    next_step = next(
        (i for i in range(checkout_i + 1, len(lines))
         if (m := step_re.match(lines[i])) and len(m.group("indent")) == step_indent),
        len(lines),
    )

    permissions = [
        line.strip() for line in lines
        if re.match(r"^\s{6}(?:contents|pull-requests):", line)
    ]
    later_steps = [
        line.strip() for line in lines[next_step:]
        if re.match(r"^\s+(?:run|uses):", line)
    ]
    later_git = [
        (i + 1, line.strip()) for i, line in enumerate(lines[checkout_i + 1:], checkout_i + 1)
        if re.search(r"(^|\s)git(\s|$)", line, re.IGNORECASE)
    ]

    print(f"FILE={path}")
    print(f"CHECKOUT_STEP_LINE={step_i + 1}")
    print("CHECKOUT_BLOCK=" + " | ".join(
        line.strip() for line in lines[step_i:next_step]
    ))
    print("JOB_PERMISSIONS=" + " | ".join(permissions) or "<none>")
    print("LATER_RUN_OR_USES=" + " | ".join(later_steps) or "<none>")
    print("LATER_GIT_REFERENCES=" + " | ".join(
        f"{line_no}:{text}" for line_no, text in later_git
    ) or "<none>")
PY

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 1680


Disable checkout credential persistence in both workflows.

actions/checkout defaults persist-credentials to true and stores github.token in the local Git configuration. No later authenticated Git operation requires this token. The pull-request workflow runs PR-controlled commands with pull-requests: write, which increases exposure impact.

  • Add persist-credentials: false beside fetch-depth: 0 in .github/workflows/pull-request-build.yml.
  • Add with: persist-credentials: false in .github/workflows/powershell-lint.yml.
🧰 Tools
🪛 zizmor (1.29.0)

[warning] 39-42: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

📍 Affects 2 files
  • .github/workflows/pull-request-build.yml#L40-L42 (this comment)
  • .github/workflows/powershell-lint.yml#L29-L30
🤖 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/pull-request-build.yml around lines 40 - 42, Disable
checkout credential persistence in both workflows: add persist-credentials:
false beside fetch-depth in .github/workflows/pull-request-build.yml (lines
40-42), and add a with block containing persist-credentials: false for
actions/checkout in .github/workflows/powershell-lint.yml (lines 29-30).

Source: Linters/SAST tools


- name: Setup Node 24.11.1
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24.11.1'

- name: Setup node_modules cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
Expand Down Expand Up @@ -81,7 +81,7 @@ jobs:

- name: Cache Google Cloud KMS CNG provider (Windows)
if: ${{ matrix.os == 'windows-latest' }}
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
id: cache-kms-cng
with:
path: build/installers/google-cloud-kms-cng-provider.msi
Expand Down Expand Up @@ -259,13 +259,13 @@ jobs:

- name: Setup gcloud (Windows)
if: ${{ matrix.os == 'windows-latest' }}
uses: google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f # v2.2.1
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
with:
version: '>=536.0.0'

- name: Authenticate to Google Cloud (Windows)
if: ${{ matrix.os == 'windows-latest' }}
uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2.1.13
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
credentials_json: ${{ secrets.GCP_SA_JSON }}

Expand Down Expand Up @@ -617,7 +617,7 @@ jobs:
# Get Artifact URLs using actions/github-script (only specified file extensions)
- name: Get Artifact URLs
id: get-artifact-urls
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const fs = require('fs');
Expand Down
12 changes: 7 additions & 5 deletions .github/workflows/validate-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ jobs:
- name: Disable git core.autocrlf
run: git config --global core.autocrlf false
- name: Checkout code
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node 24.11.1
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24.11.1'
- name: Setup node_modules cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
Expand All @@ -42,18 +42,20 @@ jobs:
- name: Lint
run: yarn lint
- name: Test
env:
NODE_OPTIONS: --max-old-space-size=6144
run: yarn test:coverage
- name: Upload coverage to Codecov
if: ${{ always() && matrix.os == 'ubuntu-latest' }}
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
flags: unit
verbose: true
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
- name: Upload coverage report (artifact fallback)
if: ${{ always() && matrix.os == 'ubuntu-latest' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-report
path: coverage/
Expand Down
38 changes: 38 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,44 @@ yarn workspaces:build # Build all workspaces
and the date of its last bump (`git log -p --follow -- electron-builder.json`)
before incrementing — do not guess an arbitrary increment.

## Releases And Tagging

- **Always create release tags with `yarn release:tag`** — never hand-rolled
`git tag` + `git push`. The script (`scripts/release-tag.ts`) reads the
version from `package.json`, fetches `origin/master` and tags, and fails
closed (exit 1) on every unsafe case. Tagging by hand skips all of it.
- Guards, and how to override each:

| Guard | Override |
|---|---|
| Invalid semver in `package.json` | none |
| HEAD not contained in `origin/master` | `--allow-detached-from-master` |
| Tag already exists | none — not even `--force` |
| Version not greater than latest tag **in its channel** | `--force` |

- Channel (stable / alpha / beta / candidate) is detected from the version and
compared only within itself, so an alpha never blocks a stable or vice versa.
- Flags: `--yes` / `-y` skips the confirmation prompt (also skipped
automatically when `CI=true`); `--help` lists everything. Without `--yes` the
prompt reads a real TTY, so piping `echo y` is unreliable — use the flag.
- Tag the **squashed merge commit on master**, never the pre-merge bump commit
on the release branch — that ships the wrong tree. The origin/master guard
enforces this; if it fires, fix the checkout rather than overriding it.
- A fresh worktree has no `node_modules`, so the script dies with
`Couldn't find the node_modules state file (findPackageLocation)`. Run
`yarn install` in the worktree — never work around it by tagging by hand.
- If a guard fires, treat it as a real finding: report it and fix the cause.
Do not reach for `--force` or `--allow-detached-from-master` without the
release owner explicitly agreeing.
- Tag names are the bare version (`4.16.0`, no `v` prefix). `build-release.yml`
triggers on any tag push, and the auto-updater feed derives from
`package.json`'s version, which MUST match the tag.
- Pure tag/channel logic lives in `scripts/releaseTag.lib.ts` and is unit
tested (`scripts/releaseTag.lib.spec.ts`); `release-tag.ts` keeps the I/O.
Script specs run under their own Jest project (`testEnvironment: 'node'`).
- The full end-to-end flow (notes, bump PR, merge, tag, CI, asset matrix, Jira
release sync) is driven by the `ship-release` skill.

## UI Work

- Use Fuselage components from `@rocket.chat/fuselage` for UI work unless the
Expand Down
31 changes: 31 additions & 0 deletions docs/KNOWN_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,34 @@
- Affected files: src/ui/components/TopBar/DownloadsIndicator.spec.tsx.
- Reference: PR #3443 (introduced the animated percentage slot), PR #3449 (documented after CI
runs surfaced the divergence).

## Jest test run sits near Node's default 2 GB heap ceiling

- Status: Mitigated, not fixed. The ceiling is raised; the underlying accumulation remains.
- Symptom: The `Test` step dies with
`FATAL ERROR: Ineffective mark-compacts near heap limit - JavaScript heap out of memory`
and `exit code 255`, typically on `check (macos-latest)` first, after most or all suites
have already reported PASS and none have FAILED. Observed at 2038.0/2051.6 MB with
`current mu = 0.040` (V8 spending ~96% of its time in GC before giving up).
- Root cause: `@kayahr/jest-electron-runner` spawns a detached Electron child per test
target and registers a teardown closure for each one in a module-level `DISPOSABLES` set
that is only drained at the very end of the run. With `--maxWorkers=1` (required by the
runner) every suite shares one process, so nothing is reclaimed by worker recycling, and
each retained closure keeps its child's context reachable. `--coverage` inflates each
suite's retained set further, which is why `yarn test:coverage` (validate-pr) tips over
before `yarn test` (build-release) does. macOS fails first: slowest arm64 runner, largest
retained set.
Comment on lines +138 to +139

Copy link
Copy Markdown
Contributor

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

Replace subjective wording with measurable statements.

Lines 138-139 describe the runner as “slowest” and having the “largest retained set” without measured comparisons. Lines 145-147 use “buys headroom” and “real fix”. State the observed result and required action directly.

Proposed wording
-  macOS fails first: slowest arm64 runner, largest retained set.
+  In the referenced run, `macOS-latest` failed first. The observed heap values are listed above.
...
-  raising the ceiling buys headroom, it does not stop the accumulation.
+  increasing the heap limit does not stop the accumulation.
-  the real fix is upstream disposing each child
+  the required upstream fix is to dispose each child

As per coding guidelines: **/*.{md,mdx} must avoid subjective descriptors and use measurable descriptions.

Also applies to: 145-147

🤖 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 `@docs/KNOWN_ISSUES.md` around lines 138 - 139, Update the documentation around
the macOS runner explanation and the lines covering “buys headroom” and “real
fix” to remove subjective descriptors. Replace them with directly observable
results, measured comparisons, or explicit required actions, while preserving
the existing build and test context.

Source: Coding guidelines

- Workaround: `NODE_OPTIONS: --max-old-space-size=6144` on the `Test` step in
`.github/workflows/validate-pr.yml` and `.github/workflows/build-release.yml`
(GitHub-hosted runners have >= 7 GB), and `--detectOpenHandles` removed from the `test` /
Comment on lines +140 to +142

Copy link
Copy Markdown
Contributor

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:

#!/bin/bash
set -eu

printf '%s\n' '--- relevant documentation ---'
cat -n docs/KNOWN_ISSUES.md | sed -n '125,160p'

printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/*' | sort

printf '%s\n' '--- runner declarations and memory references ---'
rg -n -i --glob '.github/workflows/**' \
  'runs-on|ubuntu-|windows-|macos-|max-old-space-size|7 GB|memory|runner' \
  .github/workflows || true

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 7956


🌐 Web query:

GitHub-hosted runners standard runner specifications memory ubuntu-latest macos-latest windows-latest 7 GB

💡 Result:

The memory specifications for standard GitHub-hosted runners vary depending on whether the repository is public or private, and which operating system is used. As of August 11, 2026, the specifications are as follows [1][2]: Public Repositories: - Ubuntu-latest (Linux): 16 GB RAM (4 CPUs) [1][2] - Windows-latest (Windows): 16 GB RAM (4 CPUs) [1][2] - Macos-latest (macOS M1): 7 GB RAM (3 vCPUs) [1][2] Private Repositories: - Ubuntu-latest (Linux): 8 GB RAM (2 CPUs) [1][2] - Windows-latest (Windows): 8 GB RAM (2 CPUs) [1][2] - Macos-latest (macOS M1): 7 GB RAM (3 vCPUs) [1][2] The 7 GB RAM figure specifically applies to the standard GitHub-hosted macOS (M1/arm64) runners, which provide this amount of memory for both public and private repositories [1][2]. For Linux and Windows, the memory allocation is 16 GB for public repositories and 8 GB for private ones [1][2]. Larger memory options are available through GitHub-hosted large runners [3].

Citations:


Cite and qualify the runner-memory claim.

The workflows use standard ubuntu-latest, macos-latest, and windows-latest runners. GitHub documents at least 7 GB of memory for these runners, with macOS arm64 providing 7 GB. Add the GitHub runner-specifications link and limit the statement to these runners.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~140-~140: The official name of this software platform is spelled with a capital “H”.
Context: ...-space-size=6144on theTeststep in .github/workflows/validate-pr.ymland.github...

(GITHUB)


[uncategorized] ~141-~141: The official name of this software platform is spelled with a capital “H”.
Context: ....github/workflows/validate-pr.yml and .github/workflows/build-release.yml (GitHub-...

(GITHUB)

🤖 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 `@docs/KNOWN_ISSUES.md` around lines 140 - 142, Update the runner-memory
qualification in the workaround entry to state that the documented memory
applies to the standard ubuntu-latest, macos-latest, and windows-latest
GitHub-hosted runners, and add the GitHub runner specifications link as the
citation. Remove the unqualified “GitHub-hosted runners have >= 7 GB” claim
while preserving the existing NODE_OPTIONS guidance.

Source: Coding guidelines

`test:coverage` scripts — it retains async resource references by design in order to
report them. It is still available as `yarn test:debug` when actually debugging handles.
- Expect this to recur as the suite grows: raising the ceiling buys headroom, it does not
stop the accumulation. If it returns, do NOT just raise the number again — the real fix is
upstream disposing each child as its target finishes instead of at end-of-run. Note that
`workerIdleMemoryLimit` does NOT help: Jest recycles *worker processes* after a test, and
at `--maxWorkers=1` with this custom runner there is no worker to recycle.
- Affected files: .github/workflows/validate-pr.yml, .github/workflows/build-release.yml,
package.json, patches/@kayahr+jest-electron-runner+29.14.0.patch.
- Reference: PR #3452. Failing run 31515022528 (`check (macos-latest)`); green after the fix
in run 31515403323 (macOS 16m32s, 1721 tests, 0 failures).
6 changes: 6 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,11 @@ module.exports = {
],
setupFilesAfterEnv: ['./src/.jest/setup.ts'],
},
{
preset: 'ts-jest',
errorOnDeprecated: true,
testEnvironment: 'node',
testMatch: ['<rootDir>/scripts/**/*.(spec|test).ts'],
},
],
};
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@
"build-assets-win": "ts-node -O \"{\\\"module\\\":\\\"commonjs\\\"}\" src/buildAssets.ts",
"release": "yarn electron-builder --publish onTagOrDraft --x64",
"install-app-deps": "electron-builder install-app-deps",
"test": "xvfb-maybe jest --forceExit --detectOpenHandles --maxWorkers=1",
"test:coverage": "xvfb-maybe jest --coverage --forceExit --detectOpenHandles --maxWorkers=1",
"test": "xvfb-maybe jest --forceExit --maxWorkers=1",
"test:coverage": "xvfb-maybe jest --coverage --forceExit --maxWorkers=1",
"test:debug": "xvfb-maybe jest --forceExit --detectOpenHandles --maxWorkers=1",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"lint": "run-s .:lint:eslint .:lint:tsc",
".:lint:eslint": "eslint .",
Expand Down
8 changes: 5 additions & 3 deletions patches/@kayahr+jest-electron-runner+29.14.0.patch
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ diff --git a/node_modules/@kayahr/jest-electron-runner/lib/main/electron/TestRun
index 4485862..f34ba6a 100644
--- a/node_modules/@kayahr/jest-electron-runner/lib/main/electron/TestRunner.js
+++ b/node_modules/@kayahr/jest-electron-runner/lib/main/electron/TestRunner.js
@@ -66,14 +66,19 @@ async function startWorker(rootDir, target, config) {
@@ -66,14 +66,21 @@ async function startWorker(rootDir, target, config) {
DISPOSABLES.add(() => {
if (child.pid != null) {
try {
Expand All @@ -18,8 +18,10 @@ index 4485862..f34ba6a 100644
+ // Kill whole process group with negative PID (See `man kill`)
+ process.kill(-child.pid, 'SIGKILL');
+ }
+ } catch (error) {
+ console.error('Failed to kill process:', error);
+ } catch {
+ // Ignored: the process/process group is already dead by the time
+ // we get here (this catch only guards the primary kill attempt;
+ // the fallback child.kill('SIGKILL') below is always redundant).
}
}
- child.kill("SIGKILL");
Expand Down
Loading
Loading