Sitelet https://github.com/lightspeedwp/.github/pull/3487/files
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b18c516
Clear workflow shellcheck debt and fix consumption examples (#3478)
eleshar Sep 23, 2026
154a49f
Activate organisation reusable workflows (#3480)
eleshar Sep 23, 2026
4acec5e
Read FEEDBACK_RESPONSE.md through the base repository (#3480)
eleshar Sep 23, 2026
bfdcf3d
Only trusted authors can advance phases from PRs (#3480)
eleshar Sep 23, 2026
b9cc8b1
Run Jest on every PR, failing only on new failures (#3479)
eleshar Sep 23, 2026
d6e26a6
Keep the test comparison summary when new failures are found (#3479)
eleshar Sep 23, 2026
a925eb3
Make the collection-duration test deterministic (#3479)
eleshar Sep 23, 2026
370a493
Use inclusive jitter bounds in the backoff test (#3479)
eleshar Sep 23, 2026
4a23711
fix(ci): fail artifact pruning on errors and order by filename (#3478)
eleshar Sep 23, 2026
afb6a4b
Merge branch 'develop' into chore/ci-hygiene-3478
eleshar Sep 23, 2026
b747990
Merge branch 'develop' into chore/ci-hygiene-3478
eleshar Sep 23, 2026
7ad1e8d
Merge branch 'chore/ci-hygiene-3478' into feat/reusable-workflows-3480
eleshar Sep 23, 2026
4700880
Merge branch 'feat/reusable-workflows-3480' into fix/jest-ci-3479
eleshar Sep 23, 2026
3f59e8f
fix(automation): add phase labels before removing the old ones (#3480)
eleshar Sep 24, 2026
17fac70
docs(examples): make the scaffold CI examples runnable on a clean rep…
eleshar Sep 24, 2026
9dc6239
Merge branch 'feat/reusable-workflows-3480' into fix/jest-ci-3479
eleshar Sep 24, 2026
a682d0c
Merge remote-tracking branch 'origin/chore/ci-hygiene-3478' into feat…
eleshar Sep 24, 2026
7da80b7
Merge branch 'feat/reusable-workflows-3480' into fix/jest-ci-3479
eleshar Sep 24, 2026
488cc86
Merge branch 'develop' into chore/ci-hygiene-3478
eleshar Sep 24, 2026
c42e3a7
Merge branch 'chore/ci-hygiene-3478' into feat/reusable-workflows-3480
eleshar Sep 24, 2026
615b7ac
Merge branch 'feat/reusable-workflows-3480' into fix/jest-ci-3479
eleshar Sep 24, 2026
26d754f
Merge branch 'develop' into chore/ci-hygiene-3478
eleshar Sep 24, 2026
321e25f
Merge branch 'chore/ci-hygiene-3478' into feat/reusable-workflows-3480
eleshar Sep 24, 2026
9d71363
Merge branch 'feat/reusable-workflows-3480' into fix/jest-ci-3479
eleshar Sep 24, 2026
d09fee8
Merge branch 'develop' into chore/ci-hygiene-3478
eleshar Sep 24, 2026
e809096
Merge branch 'chore/ci-hygiene-3478' into feat/reusable-workflows-3480
eleshar Sep 24, 2026
ccc4187
Merge branch 'feat/reusable-workflows-3480' into fix/jest-ci-3479
eleshar Sep 24, 2026
9c143a3
chore(changelog): resolve develop merge for #3487
eleshar Sep 24, 2026
ac5abe1
fix(ci): harden Jest pull request gate
eleshar Sep 25, 2026
7069941
fix(ci): keep tested Markdown in Jest
eleshar Sep 25, 2026
d718da1
fix(changelog): shorten Jest gate entry
eleshar Sep 25, 2026
f5cbf08
merge: update Jest branch after #3459
eleshar Sep 25, 2026
0e02962
Merge remote-tracking branch 'origin/develop' into fix/jest-ci-3479
eleshar Sep 25, 2026
1234885
fix: apply CodeRabbit auto-fixes
eleshar Sep 25, 2026
5ece886
fix: reject testless Jest reports
eleshar Sep 25, 2026
a0f623e
test: remove flaky timer lower bound
eleshar Sep 25, 2026
b5fa91c
Merge branch 'develop' into fix/jest-ci-3479
mergify[bot] Sep 25, 2026
20a8189
test: stop two 100ms-boundary timing assertions flaking under load
eleshar Sep 25, 2026
d8f4936
Merge remote-tracking branch 'origin/develop' into fix/jest-ci-3479
eleshar Sep 25, 2026
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
184 changes: 184 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
name: Tests

# Runs the Jest suite on every PR and fails only on failures the PR
# introduces: develop still carries known failures (#3472), so the head and
# base are tested in parallel and compared. Once #3472 clears the baseline,
# this can gate on zero failures instead (#3479).

on:
# No paths filter: this backs a status check, and a filtered-out run
# never reports.
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: tests-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
scope:
name: Detect test scope
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
docs_only: ${{ steps.scope.outputs.docs_only }}
steps:
- name: Checkout changed files
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0

- name: Detect documentation-only changes
id: scope
env:
EVENT_NAME: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
docs_only=false
if [ "$EVENT_NAME" = "pull_request" ]; then
changed_files="$RUNNER_TEMP/tests-changed-files"
git diff --name-only --no-renames -z "$BASE_SHA" HEAD > "$changed_files"
if [ -s "$changed_files" ]; then
docs_only=true
while IFS= read -r -d '' file; do
case "$file" in
docs/LABEL_STRATEGY.md | docs/LABELING.md)
docs_only=false
break
;;
docs/*.md) ;;
*)
docs_only=false
break
;;
esac
done < "$changed_files"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
fi
fi
echo "docs_only=$docs_only" >> "$GITHUB_OUTPUT"

jest:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
name: Jest (${{ matrix.target }})
needs: scope
if: needs.scope.outputs.docs_only != 'true'
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
target: [head, base]
exclude:
# No base to compare against outside a pull request.
- target: ${{ github.event_name != 'pull_request' && 'base' || 'none' }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 1
# head: the PR merge commit (default); base: the PR base commit.
ref: ${{ matrix.target == 'base' && github.event.pull_request.base.sha || '' }}

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .nvmrc
cache: npm

- name: Install dependencies
run: npm ci --ignore-scripts --prefer-offline --no-audit --no-fund

- name: Run Jest
env:
EVENT_NAME: ${{ github.event_name }}
run: |
# Failing tests are expected on develop; the compare job decides.
# A missing or invalid report fails closed there.
jest_status=0
npx jest --config .jest.config.cjs --ci --coverage=false \
--json --outputFile=jest-report.json || jest_status=$?
jq -r '"suites=\(.numTotalTestSuites) failed_suites=\(.numFailedTestSuites) tests=\(.numTotalTests) failed=\(.numFailedTests)"' \
jest-report.json 2>/dev/null || echo "::warning::No Jest report was written"
if [ "$EVENT_NAME" = "workflow_dispatch" ] \
&& { [ "$jest_status" -ne 0 ] || [ ! -s jest-report.json ]; }; then
exit 1
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
fi

- name: Check the suite left the working tree clean
run: |
# Tests must not write into the repository (#3482). Warning only
# until that is fixed.
dirty=$(git status --porcelain -- . ':!jest-report.json')
if [ -n "$dirty" ]; then
echo "::warning::Jest modified the working tree (#3482):"
printf '%s\n' "$dirty"
fi

- name: Upload report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: jest-report-${{ matrix.target }}
path: jest-report.json
if-no-files-found: ignore
retention-days: 7

compare:
name: No new test failures
needs: [scope, jest]
if: always() && github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Report documentation-only skip
if: needs.scope.outputs.docs_only == 'true'
run: |
echo "Documentation-only pull request; Jest comparison skipped." >> "$GITHUB_STEP_SUMMARY"

- name: Checkout comparison script
if: needs.scope.outputs.docs_only != 'true'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 1
sparse-checkout: scripts/validation

- name: Download reports
if: needs.scope.outputs.docs_only != 'true'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: jest-report-*
path: reports

- name: Compare head with base
if: needs.scope.outputs.docs_only != 'true'
run: |
set -o pipefail
for target in head base; do
if ! jq -e '.testResults | type == "array"' "reports/jest-report-$target/jest-report.json" >/dev/null 2>&1; then
echo "::error::Missing or invalid Jest report for $target; failing closed."
exit 1
fi
done
# Both jobs check out to the same workspace path, so one root
# makes their test paths comparable. The default shell is
# bash -e: suspend errexit so a non-zero result still reaches the
# summary below.
set +e
node scripts/validation/compare-jest-failures.cjs \
--head reports/jest-report-head/jest-report.json --head-root "$GITHUB_WORKSPACE" \
--base reports/jest-report-base/jest-report.json --base-root "$GITHUB_WORKSPACE" \
| tee comparison.txt
status=${PIPESTATUS[0]}
set -e
{
echo "## Test comparison (head vs base)"
echo
echo '```'
cat comparison.txt
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
exit "$status"
1 change: 1 addition & 0 deletions .github/workflows/workflow-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,6 @@ jobs:
.github/workflows/orchestrate-phase-progression.yml \
.github/workflows/phase-progression.yml \
.github/workflows/pr-template-routing.yml \
.github/workflows/tests.yml \
.github/workflows/validate-specifications.yml \
.github/workflows/workflow-lint.yml
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- **Shared Review and Phase Workflows** — Other repositories can now reuse the review-feedback check (warnings only by default) and the automatic phase-label workflow, which now really applies labels. (#3480)
- **Test Check on Every Pull Request** — Non-documentation pull requests run the full suite and fail only on new failures; eligible documentation-only pull requests report success without installing dependencies or running Jest. (#3479)
- **Workflow Reachability Guards** — A test now fails if a composite action has no caller, a local `uses:` does not resolve, or a workflow-shaped file sits outside `.github/workflows/`. (#3570)

### Changed
Expand Down
26 changes: 20 additions & 6 deletions agents/metadata-agent/__tests__/api/retry-strategy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,26 @@ describe("RetryStrategy", () => {
const delay1 = strategy.calculateDelay(1); // ~2000ms
const delay2 = strategy.calculateDelay(2); // ~4000ms

expect(delay0).toBeGreaterThan(900);
expect(delay0).toBeLessThan(1100);
expect(delay1).toBeGreaterThan(1800);
expect(delay1).toBeLessThan(2200);
expect(delay2).toBeGreaterThan(3600);
expect(delay2).toBeLessThan(4400);
// Jitter is ±10% and rounded, so the bounds are inclusive; strict
// bounds failed intermittently when a delay landed exactly on one.
expect(delay0).toBeGreaterThanOrEqual(900);
expect(delay0).toBeLessThanOrEqual(1100);
expect(delay1).toBeGreaterThanOrEqual(1800);
expect(delay1).toBeLessThanOrEqual(2200);
expect(delay2).toBeGreaterThanOrEqual(3600);
expect(delay2).toBeLessThanOrEqual(4400);
});

test.each([
[0, 900],
[1 - Number.EPSILON, 1100],
])("stays within the jitter range at the extremes (Math.random = %p)", (random, expected) => {
const randomSpy = jest.spyOn(Math, "random").mockReturnValue(random);
try {
expect(strategy.calculateDelay(0)).toBe(expected);
} finally {
randomSpy.mockRestore();
}
});

test("caps delay at maxDelayMs", () => {
Expand Down
11 changes: 9 additions & 2 deletions scripts/metrics/__tests__/integration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,15 @@ describe("Metrics Agent Phase 2 - Integration Tests", () => {
test("single repository collection should complete efficiently", async () => {
const startTime = Date.now();

// Simulate collection
await new Promise((resolve) => setTimeout(resolve, 100)); // Simulate 100ms work
// Simulate collection. The sleep carries 50ms of headroom above the
// bound asserted below: `elapsed` is `Date.now() - startTime` measured
// across two reads with millisecond granularity, so a sleep sitting
// exactly on the asserted bound lands on 99ms under parallel load and
// failed roughly one full-suite run in four (#3572). The headroom keeps
// the assertion meaningful - it still fails if the work is skipped or
// the elapsed time is not tracked - without depending on timer
// precision. The upper bound is unaffected.
await new Promise((resolve) => setTimeout(resolve, 150));

const elapsed = Date.now() - startTime;

Expand Down
Loading
Loading