mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-12 22:13:05 +00:00
simplify(OPS86): gate fail-fast no unmapped-risk, smoke fallback no manifesto, pins de sync do wrapper, docs AGENT-OPS (Related #846)
This commit is contained in:
parent
9af4b92851
commit
9c5cd7830b
13 changed files with 152 additions and 32 deletions
4
.github/workflows/ci-pr.yml
vendored
4
.github/workflows/ci-pr.yml
vendored
|
|
@ -121,11 +121,11 @@ jobs:
|
|||
- name: Fail on unmapped risk paths
|
||||
if: steps.scope.outputs.e2e_mode == 'unmapped-risk'
|
||||
run: |
|
||||
echo "::error::Risk-area files touched by this PR have no e2e manifest mapping:"
|
||||
echo "::error::Risk-area files touched by this PR have no e2e manifest mapping (fail-closed). Unmapped files:"
|
||||
for file in ${{ steps.scope.outputs.e2e_unmapped }}; do
|
||||
echo "::error::- $file"
|
||||
done
|
||||
echo "::error::Add an entry in scripts/lib/e2e-affected-manifest.mjs (E2E_AFFECTED_MANIFEST) covering these paths."
|
||||
echo "::error::Add entries in scripts/lib/e2e-affected-manifest.mjs (E2E_AFFECTED_MANIFEST) covering the risk-area paths."
|
||||
exit 1
|
||||
|
||||
# Content guards are PR-scoped and cheap — run them before the suite so
|
||||
|
|
|
|||
|
|
@ -74,14 +74,14 @@ Arquivos de "lista" que N agentes escrevem em paralelo (plans, migrações, cata
|
|||
|
||||
## CI por alvo (GitHub Actions, OPS71)
|
||||
|
||||
| Workflow | Trigger | Banco | Passos |
|
||||
| ------------------------------------ | --------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `ci-pr.yml` | PR → `main` (same-repo) | 2× service Postgres 17 | Job único `checks` sequencial (OPS62 X1): guards → lint→format→typecheck→knip→cycles→unit→int→build→e2e; skips via `ci-scope.mjs` (step `scope`); **fail-fast estrutural** — step vermelho interrompe o job, o run falha; `concurrency` nativo cancela o run em push novo; e2e **apenas blast radius** (`selected`, nunca `full` — OPS72) em 1 processo com `PLAYWRIGHT_WORKERS=4`; 2 services por nome na rede do job (`postgres-int`/`postgres-build`, sem publish host); fork-PR excluída no `if:` do job (repo público — não queima minutes) |
|
||||
| `deploy.yml` | **`workflow_dispatch` manual** | 2× service Postgres 17 | **Dois jobs:** `verify` (hosted, suíte **full** sem skips — lint→format→typecheck→knip→cycles→unit→int→build→e2e full) → `deploy` (`needs: [verify]`, `runs-on: [self-hosted, homeserver]`) executa `scripts/deploy-homeserver.sh <sha>` **localmente** no homeserver (sem SSH; idempotente; HEAD guard; flock; migrator→migrate→runner→rollout→smoke) |
|
||||
| `issue-done-on-main-merge.yml` | PR merged → `main` (same-repo) + **dispatch manual (recovery)** | — | `Closes`/`Fixes` → `done` + `in-prod` + **close** **no GitHub** (token `GITHUB_TOKEN`; body do PR via `PR_BODY` do evento GitHub — no dispatch, busca via `gh` com o mesmo contrato: merged + main + same-repo) |
|
||||
| `plan-issue-ready-on-main-merge.yml` | PR merged → `main` (same-repo) + **dispatch manual (recovery)** | — | `Related #N` aguardando plano → `ready` **no GitHub** (OPS18; soft-skip; mesmo token) |
|
||||
| `agent-pr-ready-automerge.yml` | PR same-repo → `main` (open/sync/…) | — | Safety net via `github-pr-automerge.mjs` (plain Node, REST+GraphQL): draft `cursor/*` → Ready (PATCH); arm `enablePullRequestAutoMerge` (rebase) **com o `AUTOMERGE_PAT`** (fail-closed — nunca o `GITHUB_TOKEN` nativo, cujo merge como `github-actions[bot]` não cria runs no `closed`; OPS71-FLIP) — o **servidor** espera o required check `CI (PR) / checks` (o OPS64 "rollup pode mentir" é estrutural aqui: job único posta um check-run honesto); draft não-`cursor/*` = veto, skip |
|
||||
| `archive-cursor-agent.yml` | dispatch manual (ops) | — | Helper: cancela run + arquiva um agente Cursor Cloud (`CURSOR_API_KEY`) — **dormente (OPS65)** como o pool |
|
||||
| Workflow | Trigger | Banco | Passos |
|
||||
| ------------------------------------ | --------------------------------------------------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `ci-pr.yml` | PR → `main` (same-repo) | 2× service Postgres 17 | Job único `checks` sequencial (OPS62 X1): guards → lint→format→typecheck→knip→cycles→unit→int→build→e2e; skips via `ci-scope.mjs` (step `scope`); **fail-fast estrutural** — step vermelho interrompe o job, o run falha; `concurrency` nativo cancela o run em push novo; e2e **apenas blast radius** — `selected` ou, em high-risk, o conjunto `curated` (nunca `full` — OPS72/OPS86); área de risco sem mapping falha `unmapped-risk`; unit/int `changed` com seleção vazia cai em full (`vitest-changed-or-full.mjs`) — em 1 processo com `PLAYWRIGHT_WORKERS=4`; 2 services por nome na rede do job (`postgres-int`/`postgres-build`, sem publish host); fork-PR excluída no `if:` do job (repo público — não queima minutes) |
|
||||
| `deploy.yml` | **`workflow_dispatch` manual** | 2× service Postgres 17 | **Dois jobs:** `verify` (hosted, suíte **full** sem skips — lint→format→typecheck→knip→cycles→unit→int→build→e2e full) → `deploy` (`needs: [verify]`, `runs-on: [self-hosted, homeserver]`) executa `scripts/deploy-homeserver.sh <sha>` **localmente** no homeserver (sem SSH; idempotente; HEAD guard; flock; migrator→migrate→runner→rollout→smoke) |
|
||||
| `issue-done-on-main-merge.yml` | PR merged → `main` (same-repo) + **dispatch manual (recovery)** | — | `Closes`/`Fixes` → `done` + `in-prod` + **close** **no GitHub** (token `GITHUB_TOKEN`; body do PR via `PR_BODY` do evento GitHub — no dispatch, busca via `gh` com o mesmo contrato: merged + main + same-repo) |
|
||||
| `plan-issue-ready-on-main-merge.yml` | PR merged → `main` (same-repo) + **dispatch manual (recovery)** | — | `Related #N` aguardando plano → `ready` **no GitHub** (OPS18; soft-skip; mesmo token) |
|
||||
| `agent-pr-ready-automerge.yml` | PR same-repo → `main` (open/sync/…) | — | Safety net via `github-pr-automerge.mjs` (plain Node, REST+GraphQL): draft `cursor/*` → Ready (PATCH); arm `enablePullRequestAutoMerge` (rebase) **com o `AUTOMERGE_PAT`** (fail-closed — nunca o `GITHUB_TOKEN` nativo, cujo merge como `github-actions[bot]` não cria runs no `closed`; OPS71-FLIP) — o **servidor** espera o required check `CI (PR) / checks` (o OPS64 "rollup pode mentir" é estrutural aqui: job único posta um check-run honesto); draft não-`cursor/*` = veto, skip |
|
||||
| `archive-cursor-agent.yml` | dispatch manual (ops) | — | Helper: cancela run + arquiva um agente Cursor Cloud (`CURSOR_API_KEY`) — **dormente (OPS65)** como o pool |
|
||||
|
||||
`agent-pool.yml` foi **removido no OPS65** (pool dormente; o tick `*/10` custava 144 `pnpm install`/dia na workstation). O verificador de main (`ci.yml`) foi **eliminado no OPS71** (gate 2026-08-19): só o `verify` do dispatch manual roda a suíte full.
|
||||
|
||||
|
|
|
|||
60
docs/plans/escala-dry-pos-ops86.md
Normal file
60
docs/plans/escala-dry-pos-ops86.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# OPS86+: curated não engole unmapped-risk sem rastro
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-08-24
|
||||
Issue: #846
|
||||
Intenção: docs/plans/ops86-blast-radius-testes-prs.md (débito pós-entrega)
|
||||
Priority: P2
|
||||
Impeccable: A — N/A sem UI
|
||||
Appetite: ~2h eng
|
||||
|
||||
## Origem
|
||||
|
||||
Triagem do simplify da entrega OPS86 (#832): o cheque high-risk de
|
||||
`selectE2eSpecs` (`scripts/lib/test-affected-core.mjs`) precede a computação
|
||||
de `unmappedRisk`. Um diff que toca uma migração (high-risk) **e** um arquivo
|
||||
novo de área de risco (ex. `src/utilities/access/novo.ts`) cai em `curated`
|
||||
com `unmapped: []` — o arquivo de risco desaparece sem rastro no `reason`.
|
||||
Segurança mantida (o conjunto curado inclui as specs de RBAC/visibilidade),
|
||||
mas o fail-closed é bypassado sem pista de diagnóstico exatamente no caso que
|
||||
o OPS86 existe para caçar.
|
||||
|
||||
## Fase única
|
||||
|
||||
1. **Núcleo + pin** — reordenar `selectE2eSpecs` para computar os unmapped de
|
||||
área de risco **antes** do early-return de high-risk, e incluí-los no
|
||||
`reason` do curated (ex.: "curated + risk files without mapping: …"), sem
|
||||
mudar o mode (`curated` continua vencendo — a curadoria cobre as
|
||||
superfícies de risco). Alternativa rejeitada: fazer `unmapped-risk` vencer
|
||||
o high-risk — piora o falso negativo (a spec nova de RBAC poderia não
|
||||
existir e o curated é a rede mais forte). Pins: `testAffected.unit.spec.ts`
|
||||
ganha caso "high-risk + risco não-mapeado → curated com unmapped no
|
||||
reason"; `ciSkipInvariants`/`e2eAffectedManifest` intactos.
|
||||
Verificação: `pnpm test:unit` (pins), `pnpm gate:fast`, classificador
|
||||
exercitado à mão no caso combinado.
|
||||
|
||||
## Já resolvido no simplify/critique (não reabrir)
|
||||
|
||||
- gate-ci falha em `unmapped-risk` (espelho fail-fast do CI); pin do wrapper
|
||||
espelhando `package.json`; `E2E_SMOKE_FALLBACK_SPEC` movido para o
|
||||
manifesto; labels risk-aware no `ci-scope`/`e2e-affected`; wording do passo
|
||||
de falha no `ci-pr.yml`; switch no `run-e2e-affected`; `AGENT-OPS.md`
|
||||
atualizado.
|
||||
|
||||
## Explicitamente fora (defer/descartes desta triagem)
|
||||
|
||||
- **Descartado:** pin do ci-pr.yml casa 1 ocorrência de `selected || curated`
|
||||
(S6) — remover `curated` de um passo isolado quebra loud no CI (sem
|
||||
browser/build); pin redundante aceitável.
|
||||
- **Descartado:** replicação da lista curada/risco em docs (S10) — o pin de
|
||||
teste é o guard real; changelog é registro histórico por convenção.
|
||||
- **Descartado:** 10 specs órfãs full-only (G1) — gap já registrado no
|
||||
changelog da entrega; dono natural é o OPS87 (#833).
|
||||
- **Defer + gatilho:** `startsWith('src/utilities/ai')` sem trailing slash
|
||||
over-seleciona um futuro `src/utilities/ai*` (S7) — direção segura
|
||||
(fail-closed); gatilho: surgir o 1º módulo irmão `src/utilities/ai*` fora
|
||||
de `ai/`.
|
||||
- **Defer + gatilho:** parse manual de argv no `vitest-changed-or-full.mjs`
|
||||
diverge do `scripts/lib/cli.mjs` (3º estilo de parsing no repo) (S9) —
|
||||
DRY com 1 call site de 2 flags; gatilho: 3ª flag ou 2º script duplicando o
|
||||
loop.
|
||||
|
|
@ -61,7 +61,7 @@ if (e2e.unmapped.length > 0) {
|
|||
const label =
|
||||
e2e.mode === 'unmapped-risk'
|
||||
? '[ci-scope] RISK-AREA paths with no e2e manifest mapping (CI will fail closed):'
|
||||
: '[ci-scope] src/ paths with no e2e manifest mapping (running the home smoke):'
|
||||
: '[ci-scope] src/ paths with no e2e manifest mapping (selection may not cover them):'
|
||||
console.error(`${label}\n ${e2e.unmapped.join('\n ')}`)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,8 +49,10 @@ const files = raw
|
|||
|
||||
const result = { ...selectE2eSpecs(files, E2E_AFFECTED_MANIFEST), base }
|
||||
if (result.unmapped.length > 0) {
|
||||
console.error(
|
||||
`[e2e-affected] src/ paths with no manifest mapping:\n ${result.unmapped.join('\n ')}`,
|
||||
)
|
||||
const label =
|
||||
result.mode === 'unmapped-risk'
|
||||
? '[e2e-affected] RISK-AREA paths with no manifest mapping (CI fails closed; running curated locally):'
|
||||
: '[e2e-affected] src/ paths with no manifest mapping (the selection may not cover them):'
|
||||
console.error(`${label}\n ${result.unmapped.join('\n ')}`)
|
||||
}
|
||||
console.log(JSON.stringify(result))
|
||||
|
|
|
|||
|
|
@ -84,6 +84,15 @@ const main = async () => {
|
|||
`[gate:ci] scope base=${scope.base} code=${scope.code.mode} build=${scope.build.mode} test=${scope.test.mode} e2e=${scope.e2e.mode} (e2e not in this gate — OPS72; local affected is a skill step)`,
|
||||
)
|
||||
|
||||
// Fail-fast mirror of the CI fail-closed step: risk-area files without a
|
||||
// manifest entry fail the job — catch it before running the whole suite.
|
||||
if (scope.e2e.mode === 'unmapped-risk') {
|
||||
console.error(
|
||||
`\n[gate:ci] ✗ e2e: RISK-AREA files without a manifest mapping — CI fails closed on this, so the local mirror fails too:\n ${scope.e2e.unmapped.join('\n ')}\n Add an e2e manifest entry (scripts/lib/e2e-affected-manifest.mjs) covering them.`,
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// --- Phase 1 (cheap) ---
|
||||
run('check-test-locations', 'node', ['scripts/check-test-locations.mjs'])
|
||||
run('lint', 'pnpm', ['lint'])
|
||||
|
|
@ -163,9 +172,9 @@ const main = async () => {
|
|||
console.log(
|
||||
`\n[gate:ci] ▶ e2e: diff is high-risk — CI runs the curated cross-section (${scope.e2e.specs.join(', ')}) — not in this gate (OPS72); run \`pnpm test:e2e:affected\` locally (runs full); deploy verify runs full before publishing`,
|
||||
)
|
||||
} else if (scope.e2e.mode === 'unmapped-risk') {
|
||||
} else {
|
||||
console.log(
|
||||
`\n[gate:ci] ✗ e2e: RISK-AREA files without a manifest mapping — CI WILL FAIL on:\n ${scope.e2e.unmapped.join('\n ')}\n Add an e2e manifest entry for them before pushing.`,
|
||||
`\n[gate:ci] ⚠ e2e: unexpected mode "${scope.e2e.mode}" — review scripts/ci-scope.mjs (contract drift?)`,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,14 @@ export const E2E_RISK_PREFIXES = [
|
|||
'src/utilities/ai',
|
||||
]
|
||||
|
||||
/**
|
||||
* Generic e2e smoke for unmapped non-risk src/ files (OPS86 fallback): the
|
||||
* home spec renders the campaign shell (login + home) with console-error
|
||||
* fail-fast. Lives here (not in the core) so the manifest entries below can
|
||||
* reference the same constant without a cycle.
|
||||
*/
|
||||
export const E2E_SMOKE_FALLBACK_SPEC = 'campaignHomeActions'
|
||||
|
||||
export const E2E_AFFECTED_MANIFEST = [
|
||||
{
|
||||
prefixes: ['src/app/(payload)'],
|
||||
|
|
@ -201,7 +209,7 @@ export const E2E_AFFECTED_MANIFEST = [
|
|||
// Web Push client — the opt-in toast mounts on the campaign shell, so a
|
||||
// push diff wakes the home smoke (console-error fail-fast covers it).
|
||||
prefixes: ['src/utilities/campaignPushClient.ts'],
|
||||
specs: ['campaignHomeActions'],
|
||||
specs: [E2E_SMOKE_FALLBACK_SPEC],
|
||||
},
|
||||
{
|
||||
prefixes: ['src/app/(campaign)/campanha/agenda/ical', 'src/utilities/calendarFeed'],
|
||||
|
|
@ -244,6 +252,6 @@ export const E2E_AFFECTED_MANIFEST = [
|
|||
`${CAMPAIGN_APP}/perfil`,
|
||||
`${CAMPAIGN_APP}/quadro`,
|
||||
],
|
||||
specs: ['campaignHomeActions'],
|
||||
specs: [E2E_SMOKE_FALLBACK_SPEC],
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@
|
|||
* file wakes the home smoke.
|
||||
*/
|
||||
|
||||
import { E2E_CURATED_SPECS, E2E_RISK_PREFIXES } from './e2e-affected-manifest.mjs'
|
||||
|
||||
/** Generic e2e smoke for unmapped non-risk src/ files (OPS86 fallback). */
|
||||
export const E2E_SMOKE_FALLBACK_SPEC = 'campaignHomeActions'
|
||||
import {
|
||||
E2E_CURATED_SPECS,
|
||||
E2E_RISK_PREFIXES,
|
||||
E2E_SMOKE_FALLBACK_SPEC,
|
||||
} from './e2e-affected-manifest.mjs'
|
||||
|
||||
/**
|
||||
* Paths whose blast radius is the whole app: schema, test harness, lockfile,
|
||||
|
|
|
|||
|
|
@ -78,12 +78,18 @@ if (process.env.E2E_PROD === '1') {
|
|||
}
|
||||
|
||||
const curatedSpecPaths = E2E_CURATED_SPECS.map((name) => `tests/e2e/${name}.e2e.spec.ts`)
|
||||
const scopeSpecPaths =
|
||||
scope.mode === 'selected'
|
||||
? scope.specs.map((name) => `tests/e2e/${name}.e2e.spec.ts`)
|
||||
: scope.mode === 'unmapped-risk'
|
||||
? curatedSpecPaths
|
||||
: [] // curated/full → run the whole suite locally
|
||||
let scopeSpecPaths
|
||||
switch (scope.mode) {
|
||||
case 'selected':
|
||||
scopeSpecPaths = scope.specs.map((name) => `tests/e2e/${name}.e2e.spec.ts`)
|
||||
break
|
||||
case 'unmapped-risk':
|
||||
scopeSpecPaths = curatedSpecPaths
|
||||
break
|
||||
default:
|
||||
// curated / full → run the whole suite locally (OPS72 local contract).
|
||||
scopeSpecPaths = []
|
||||
}
|
||||
const playwrightArgs = buildPlaywrightE2eArgs({
|
||||
scopeSpecPaths,
|
||||
passthroughArgs,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
* Exit code is the test run's — the wrapper never turns red into green.
|
||||
*
|
||||
* The env/config pairs mirror the `test:unit` / `test:int` package.json
|
||||
* scripts; keep them in sync (both are pinned by ciSkipInvariants).
|
||||
* scripts; the sync is pinned by ciSkipInvariants.unit.spec.ts.
|
||||
*/
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import path from 'node:path'
|
||||
|
|
@ -58,7 +58,15 @@ if (listResult.status !== 0) {
|
|||
console.error(`[vitest-changed-or-full] ✗ vitest list failed (exit ${listResult.status ?? 1})`)
|
||||
process.exit(listResult.status ?? 1)
|
||||
}
|
||||
const changedSpecs = JSON.parse(listResult.stdout)
|
||||
let changedSpecs
|
||||
try {
|
||||
changedSpecs = JSON.parse(listResult.stdout)
|
||||
} catch {
|
||||
console.error(
|
||||
`[vitest-changed-or-full] ✗ vitest list emitted non-JSON on stdout:\n${listResult.stdout.slice(0, 500)}`,
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const run = (label, argv, env = process.env) => {
|
||||
console.log(`\n[vitest-changed-or-full] ▶ ${args.suite} ${label}`)
|
||||
|
|
|
|||
|
|
@ -152,4 +152,21 @@ describe('ciSkipInvariants', () => {
|
|||
// The PR never runs e2e full.
|
||||
expect(ciPr).not.toContain("e2e_mode == 'full'")
|
||||
})
|
||||
|
||||
it('vitest-changed-or-full mirrors the package.json test scripts (config + unit DB guard)', () => {
|
||||
// The wrapper re-declares the vitest config paths and the unit
|
||||
// invalid-DATABASE_URL guard from the `test:unit`/`test:int` scripts; a
|
||||
// drift changes what `--changed` detects and is silent. Pin the sync.
|
||||
const pkg = JSON.parse(readFileSync(join(repoRoot, 'package.json'), 'utf8'))
|
||||
const wrapper = readFileSync(join(repoRoot, 'scripts/vitest-changed-or-full.mjs'), 'utf8')
|
||||
|
||||
expect(pkg.scripts['test:unit']).toContain('./vitest.unit.config.mts')
|
||||
expect(pkg.scripts['test:int']).toContain('./vitest.config.mts')
|
||||
expect(wrapper).toContain('./vitest.unit.config.mts')
|
||||
expect(wrapper).toContain('./vitest.config.mts')
|
||||
|
||||
const invalidUnitDbUrl = 'postgresql://invalid:invalid@127.0.0.1:1/unit_tests_must_not_connect'
|
||||
expect(pkg.scripts['test:unit']).toContain(invalidUnitDbUrl)
|
||||
expect(wrapper).toContain(invalidUnitDbUrl)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
E2E_AFFECTED_MANIFEST,
|
||||
E2E_CURATED_SPECS,
|
||||
E2E_RISK_PREFIXES,
|
||||
E2E_SMOKE_FALLBACK_SPEC,
|
||||
} from '../../scripts/lib/e2e-affected-manifest.mjs'
|
||||
|
||||
const E2E_DIR = join(__dirname, '../e2e')
|
||||
|
|
@ -72,4 +73,10 @@ describe('E2E_AFFECTED_MANIFEST (OPS5)', () => {
|
|||
expect(mappedPrefixes.has(prefix), prefix).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it('the unmapped fallback smoke spec exists on disk (OPS86 never-zero)', () => {
|
||||
// A rename of the smoke spec would turn the fallback into a CI
|
||||
// "No tests found" failure.
|
||||
expect(new Set(specNamesOnDisk()).has(E2E_SMOKE_FALLBACK_SPEC)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { E2E_CURATED_SPECS } from '../../scripts/lib/e2e-affected-manifest.mjs'
|
||||
import {
|
||||
E2E_CURATED_SPECS,
|
||||
E2E_SMOKE_FALLBACK_SPEC,
|
||||
} from '../../scripts/lib/e2e-affected-manifest.mjs'
|
||||
import {
|
||||
classifyBuildScope,
|
||||
classifyStaticScope,
|
||||
classifyTestScope,
|
||||
E2E_SMOKE_FALLBACK_SPEC,
|
||||
HIGH_RISK_EXACT,
|
||||
HIGH_RISK_PREFIXES,
|
||||
selectE2eSpecs,
|
||||
|
|
|
|||
Loading…
Reference in a new issue