tewip-ai 0.51.0 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,79 @@
1
+ // Which files in a project are migrations at all.
2
+ //
3
+ // Split out of schema-command.ts in Phase 44, when the corpus harness needed to ask the question
4
+ // across a whole checkout rather than across a diff and that file went past the 300-line rule.
5
+ // Keeping it in one place is the point: the alternative was a copy of these patterns in a script,
6
+ // and two definitions of "is this a migration" drift the moment one of them is fixed.
7
+ import { readdirSync, statSync } from 'node:fs';
8
+ import { join, relative } from 'node:path';
9
+ // A directory called `migration/` says what is probably in it, not what every file in it is. This
10
+ // matched *anything* underneath one, so Keycloak's `docs/guides/migration/index.adoc` and its 113
11
+ // `migration/migrators/MigrateTo12_0_0.java` classes - application code that moves data between
12
+ // Keycloak versions, nothing to do with a schema file - were selected as migrations and then listed
13
+ // as ones Tewip could not read. 205 files chosen in order to read 89. A "could not read" list that
14
+ // is mostly documentation teaches the reader to skip the list.
15
+ //
16
+ // So the directory decides *where*, and the extension decides *whether it could hold DDL at all*.
17
+ const MIGRATION_DIR = /(^|\/)(migrations?|migration|db\/migrate|prisma\/migrations|alembic|flyway|changelogs?|sql)\//i;
18
+ const COULD_HOLD_DDL = /\.(sql|py|rb|js|mjs|cjs|ts|php|cs|xml|ya?ml|json|go|exs?)$/i;
19
+ // Flyway's Java migrations are real, and they are named the way Flyway requires: `V2__add_index.java`
20
+ // or `R__rebuild_views.java`. Keycloak's `MigrateTo12_0_0.java` is not one. Taking `.java` on the
21
+ // strength of its directory alone is what filled that list with source files.
22
+ const FLYWAY_JAVA = /(^|\/)[VR][\w.]*__[\w.-]+\.(java|kt|scala)$/;
23
+ export const MIGRATION = {
24
+ test: (path) => MIGRATION_DIR.test(path) && (COULD_HOLD_DDL.test(path) || FLYWAY_JAVA.test(path)),
25
+ };
26
+ // A changelog is often not in a directory that says so. OpenMRS keeps 44 Liquibase changelogs in
27
+ // `api/src/main/resources/` named `liquibase-*.xml`, and the path filter saw none of them - so a
28
+ // real healthcare system with 44 migration files was told it had none. The name counts too.
29
+ //
30
+ // And a changelog is named for what it is, but hardly ever *only* that. Phase 42 taught this filter
31
+ // to read `liquibase-update-to-latest.xml` and it still missed Keycloak's 83, every one called
32
+ // `jpa-changelog-<version>.xml`: the word had to *begin* the name. Prefixes are the norm once a
33
+ // project has more than one database. Letting a release-notes `CHANGELOG.xml` through costs nothing,
34
+ // because `dialectOf` still has to find `<databaseChangeLog` in it before it is read as a migration.
35
+ export const NAMED_MIGRATION = /(^|\/)[\w.-]*(liquibase|changelog)[\w.-]*\.(xml|ya?ml|json)$/i;
36
+ export const SCHEMA_FILE = /\.(sql)$|(^|\/)(migrations?|migration|db\/migrate)\/.*\.(py|rb|js|ts|php|cs)$/i;
37
+ /**
38
+ * Every migration in a whole repository, by the same filters a diff is read through.
39
+ *
40
+ * `schemaFiles` answers "which of these changed files are migrations", which is the question a pull
41
+ * request asks. "What migrations does this project have at all" is a different question, and the
42
+ * corpus harness needs it to re-derive a verified number across a pinned checkout.
43
+ */
44
+ export function migrationsUnder(root, limit = 20_000) {
45
+ const skip = new Set(['node_modules', '.git', 'dist', 'build', 'target', 'vendor', '__pycache__', '.venv', 'venv', '.next', '.cache']);
46
+ const found = [];
47
+ const walk = (where, depth) => {
48
+ if (depth > 12 || found.length >= limit)
49
+ return;
50
+ let entries;
51
+ try {
52
+ entries = readdirSync(where);
53
+ }
54
+ catch {
55
+ return;
56
+ }
57
+ for (const entry of entries) {
58
+ if (skip.has(entry) || entry.startsWith('.'))
59
+ continue;
60
+ const full = join(where, entry);
61
+ let info;
62
+ try {
63
+ info = statSync(full);
64
+ }
65
+ catch {
66
+ continue;
67
+ }
68
+ if (info.isDirectory()) {
69
+ walk(full, depth + 1);
70
+ continue;
71
+ }
72
+ const rel = relative(root, full).replaceAll(String.fromCharCode(92), '/');
73
+ if (SCHEMA_FILE.test(rel) || MIGRATION.test(rel) || NAMED_MIGRATION.test(rel))
74
+ found.push(rel);
75
+ }
76
+ };
77
+ walk(root, 0);
78
+ return found.sort();
79
+ }
@@ -13,18 +13,8 @@ import { dirname, resolve } from 'node:path';
13
13
  import { changeSummary } from '../quality/grounding.js';
14
14
  import { dialectOf, forwardOnly, irreversible, readFramework } from './frameworks.js';
15
15
  import { readSql } from './statements.js';
16
- const MIGRATION = /(^|\/)(migrations?|migration|db\/migrate|prisma\/migrations|alembic|flyway|changelogs?|sql)\//i;
17
- // A changelog is often not in a directory that says so. OpenMRS keeps 44 Liquibase changelogs in
18
- // `api/src/main/resources/` named `liquibase-*.xml`, and the path filter saw none of them - so a
19
- // real healthcare system with 44 migration files was told it had none. The name counts too.
20
- // A changelog is named for what it is, but hardly ever *only* that. Phase 42 taught this filter to
21
- // read `liquibase-update-to-latest.xml` and it still missed Keycloak's 83 files, every one of them
22
- // called `jpa-changelog-<version>.xml`: the name was required to *begin* with the word. Prefixes are
23
- // the norm - a project with more than one database names each changelog after its module. So the
24
- // word may sit anywhere in the name. Letting a release-notes `CHANGELOG.xml` through costs nothing,
25
- // because `dialectOf` still has to find `<databaseChangeLog` in it before it is read as a migration.
26
- const NAMED_MIGRATION = /(^|\/)[\w.-]*(liquibase|changelog)[\w.-]*\.(xml|ya?ml|json)$/i;
27
- const SCHEMA_FILE = /\.(sql)$|(^|\/)(migrations?|migration|db\/migrate)\/.*\.(py|rb|js|ts|php|cs)$/i;
16
+ import { MIGRATION, NAMED_MIGRATION, SCHEMA_FILE, migrationsUnder } from './migration-files.js';
17
+ export { migrationsUnder };
28
18
  /** The schema files in this change. */
29
19
  export function schemaFiles(cmd) {
30
20
  const named = cmd.files !== undefined && cmd.files.length > 0;
@@ -30,6 +30,13 @@ const CHECKS = [
30
30
  /\bpytest\s*\.\s*(raises|warns|approx|fail)\b/,
31
31
  /\bnp\s*\.\s*testing\s*\.\s*assert/,
32
32
  /\bassert_(frame|series|index)_equal\b/,
33
+ // A bare `fail(...)`, which is how JUnit, TestNG and Go spell "this must not be reached" once the
34
+ // assertion is statically imported. `Assert.fail(...)` was already read by the pattern below;
35
+ // `fail(...)` on its own was not, so OpenMRS's RefByUuidTest - whose only real check is a `fail()`
36
+ // in the catch, beside a deliberate `assertTrue(true)` in the try - was called a tautology that
37
+ // "no change to the code can falsify". Make `getRefByUuid` throw and it falsifies immediately.
38
+ // The leading class keeps `shouldFail(` and `canFail(` out, and the paren must come next.
39
+ /(^|[^\w.])(?:Assert(?:ions)?\s*\.\s*)?fail\s*\(/,
33
40
  // Java, Kotlin, C#, Scala.
34
41
  /\bAssert(ions)?\s*\./,
35
42
  /\bassertThat\s*\(/,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tewip-ai",
3
- "version": "0.51.0",
3
+ "version": "0.52.0",
4
4
  "mcpName": "io.github.hmamut39/tewip",
5
5
  "description": "Tewip finds why each failing test failed - real bug, flaky test, environment, test data or changed selector - for any stack, from the command line, CI, or your editor.",
6
6
  "keywords": [
@@ -63,7 +63,9 @@
63
63
  "typecheck": "tsc --noEmit",
64
64
  "build": "tsc -p tsconfig.build.json",
65
65
  "prepack": "npm run build",
66
- "tewip": "node cli/tewip.ts"
66
+ "tewip": "node cli/tewip.ts",
67
+ "version": "node scripts/sync-version.ts && git add server.json",
68
+ "corpus": "node scripts/corpus.ts"
67
69
  },
68
70
  "license": "Apache-2.0",
69
71
  "devDependencies": {