diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 7473c7b2..07dbe1c2 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -511,15 +511,19 @@ jobs: run: npx playwright install --with-deps chromium # Журнал пойманных свидетелей (#481). Отменённый или упавший по таймауту # прогон не пропадает: журнал пишется после каждого пойманного мутанта и - # сохраняется при любом исходе шага, а следующий пуш начинает с того - # места, где предыдущий остановился. Мутант с тем же отпечатком входов + # сохраняется при любом исходе шага, а следующая попытка или пуш начинает + # с того места, где предыдущий остановился. Мутант с тем же отпечатком входов # (файлы патча и гарда, без строки версии) повторно не гоняется. - name: Журнал свидетелей (последний по шарду) uses: actions/cache/restore@v6 with: path: artifacts/mutation-ledger - key: mutation-ledger-${{ matrix.shard }}-${{ github.run_id }} - restore-keys: mutation-ledger-${{ matrix.shard }}- + # Cache immutable: rerun с тем же run_id обязан получить новый key, + # иначе post-save не сможет записать прогресс поверх первой попытки. + key: mutation-ledger-${{ matrix.shard }}-${{ github.run_id }}-${{ github.run_attempt }} + restore-keys: | + mutation-ledger-${{ matrix.shard }}-${{ github.run_id }}- + mutation-ledger-${{ matrix.shard }}- - name: Затронутые мутанты ловятся env: EVENT_NAME: ${{ github.event_name }} @@ -550,14 +554,15 @@ jobs: --ledger="artifacts/mutation-ledger/shard-$SHARD.json" # Сохраняется всегда: и после красного шага (пойманные до отказа уже # записаны), и при отмене прогона — post-шаг cache/save исполняется, - # пока job не убита целиком. Ключ уникален на прогон: cache не - # перезаписывает существующий ключ, а restore-keys берёт самый новый. + # пока job не убита целиком. Ключ уникален на попытку: cache не + # перезаписывает существующий ключ, а restore-keys берёт предыдущую + # попытку этого run либо самый новый журнал шарда. - name: Сохранить журнал свидетелей if: always() uses: actions/cache/save@v6 with: path: artifacts/mutation-ledger - key: mutation-ledger-${{ matrix.shard }}-${{ github.run_id }} + key: mutation-ledger-${{ matrix.shard }}-${{ github.run_id }}-${{ github.run_attempt }} frontend: name: "Фронтенд: типы, юниты, мутанты, синхрон бандла" diff --git a/test/validate-workflow.test.mjs b/test/validate-workflow.test.mjs index 71638298..3e51b5f7 100644 --- a/test/validate-workflow.test.mjs +++ b/test/validate-workflow.test.mjs @@ -400,17 +400,18 @@ test('ручной/ночной полный прогон не делит concur assert.match(text, /group: validate-\$\{\{ github\.event_name == 'workflow_dispatch' && 'dispatch-' \|\| '' \}\}/); }); -test('журнал свидетелей changed_mutants: restore по шарду, ledger в команде, save при любом исходе (#481 AC5)', () => { +test('журнал свидетелей changed_mutants: rerun продолжает предыдущую попытку, save работает при любом исходе (#481 AC5, #499)', () => { const workflow = read('validate.yml'); const start = workflow.indexOf('\n changed_mutants:\n'); const job = workflow.slice(start, workflow.indexOf('\n frontend:\n', start)); const restore = job.slice(job.indexOf('actions/cache/restore@v6'), job.indexOf('name: Затронутые мутанты ловятся')); - assert.match(restore, /key: mutation-ledger-\$\{\{ matrix\.shard \}\}-\$\{\{ github\.run_id \}\}/); - assert.match(restore, /restore-keys: mutation-ledger-\$\{\{ matrix\.shard \}\}-/, 'без префикса журнал прошлого прогона не найдётся'); + assert.match(restore, /key: mutation-ledger-\$\{\{ matrix\.shard \}\}-\$\{\{ github\.run_id \}\}-\$\{\{ github\.run_attempt \}\}/); + assert.match(restore, /mutation-ledger-\$\{\{ matrix\.shard \}\}-\$\{\{ github\.run_id \}\}-/, 'rerun обязан восстановить предыдущую попытку того же run'); + assert.match(restore, /^\s+mutation-ledger-\$\{\{ matrix\.shard \}\}-\s*$/m, 'новый run обязан найти последний журнал шарда'); assert.match(job, /--changed="\$base\.\.\$HEAD_SHA" --shard="\$SHARD\/3" \\\n\s+--ledger="artifacts\/mutation-ledger\/shard-\$SHARD\.json"/); const save = job.slice(job.indexOf('name: Сохранить журнал свидетелей')); assert.match(save, /if: always\(\)/, 'красный или отменённый шард обязан сохранить уже пойманное'); assert.match(save, /actions\/cache\/save@v6/); - assert.match(save, /key: mutation-ledger-\$\{\{ matrix\.shard \}\}-\$\{\{ github\.run_id \}\}/); + assert.match(save, /key: mutation-ledger-\$\{\{ matrix\.shard \}\}-\$\{\{ github\.run_id \}\}-\$\{\{ github\.run_attempt \}\}/); assert.ok(job.indexOf('name: Сохранить журнал свидетелей') > job.indexOf('--ledger='), 'save идёт после шага прогона'); });