supercov 4.24.0 → 4.24.2

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.
@@ -715,8 +715,11 @@ image, as `.:/workspace` in a compose file does: Supercov runs the command
715
715
  in the copy, so the mount is of the copy. It gets the run's settings with
716
716
  each path into the copy moved to where the container sees it, and the
717
717
  project's `node_modules`, which the copy's packages link to, mounted
718
- read-only at its own path. A mount of one directory of the project
719
- (`./src:/app/src`) is not the copy, and is left alone.
718
+ read-only at its own path. The copy's `.supercov`, where the run's evidence
719
+ is written, is mounted writable where the container sees it, so a copy
720
+ mounted read-only (`.:/workspace:ro`) is measured as well. A mount of one
721
+ directory of the project (`./src:/app/src`) is not the copy, and is left
722
+ alone.
720
723
 
721
724
  The tests can run in such a container too. A command `docker exec` (or
722
725
  `docker compose exec`) starts in it, `npx playwright test` say, gets the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supercov",
3
- "version": "4.24.0",
3
+ "version": "4.24.2",
4
4
  "description": "Coverage, security and code quality for coding agents",
5
5
  "keywords": [
6
6
  "coverage",
@@ -101,6 +101,7 @@
101
101
  "check:tooling": "npm run test:release-tooling && npm run test:runtime && npm run test:action && npm run test:rust-assets && npm run test:agent-commands && npm run test:assertions && npm run test:sweep-target && npm run test:skip-guard && npm run test:windows-tls && node scripts/package-preflight.mjs && node scripts/verify-binstall-metadata.mjs --offline",
102
102
  "release:check": "cargo clean && node scripts/sweep-target.mjs && npm run check && npm run test:engine && npm run test:go && npm run test:jvm && npm run test:launcher && npm run test:fixture && npm run test:watchdog && npm run test:engine-contract && npm run test:agent && npm run test:child-attribution && npm run test:host-loader && npm run test:python && npm run test:ruby-coverage && npm run test:rust-public-cargo && npm run test:packed-npx && npm run test:clang-mcdc && npm run benchmark:check",
103
103
  "release:bump": "node scripts/bump-version.mjs",
104
+ "release:verify": "node scripts/verify-release.mjs",
104
105
  "sweep": "node scripts/sweep-target.mjs",
105
106
  "oracle:rust": "cargo build --release -p supercov && node scripts/rust-coverage-oracle.mjs",
106
107
  "prepack": "node scripts/package-preflight.mjs",
@@ -122,14 +123,14 @@
122
123
  "test:windows-tls": "node scripts/windows-tls-test.mjs"
123
124
  },
124
125
  "optionalDependencies": {
125
- "@supercov/cli-darwin-arm64": "4.24.0",
126
- "@supercov/cli-darwin-x64": "4.24.0",
127
- "@supercov/cli-linux-arm64-gnu": "4.24.0",
128
- "@supercov/cli-linux-arm64-musl": "4.24.0",
129
- "@supercov/cli-linux-x64-gnu": "4.24.0",
130
- "@supercov/cli-linux-x64-musl": "4.24.0",
131
- "@supercov/cli-win32-arm64": "4.24.0",
132
- "@supercov/cli-win32-x64": "4.24.0"
126
+ "@supercov/cli-darwin-arm64": "4.24.2",
127
+ "@supercov/cli-darwin-x64": "4.24.2",
128
+ "@supercov/cli-linux-arm64-gnu": "4.24.2",
129
+ "@supercov/cli-linux-arm64-musl": "4.24.2",
130
+ "@supercov/cli-linux-x64-gnu": "4.24.2",
131
+ "@supercov/cli-linux-x64-musl": "4.24.2",
132
+ "@supercov/cli-win32-arm64": "4.24.2",
133
+ "@supercov/cli-win32-x64": "4.24.2"
133
134
  },
134
135
  "peerDependencies": {
135
136
  "@playwright/test": ">=1.55.0",
@@ -1,4 +1,4 @@
1
- import { appendFileSync, existsSync, mkdirSync, openSync, closeSync, fsyncSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
1
+ import { appendFileSync, chmodSync, existsSync, mkdirSync, openSync, closeSync, fsyncSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { dirname, resolve } from "node:path";
4
4
  import { Buffer } from "node:buffer";
@@ -18,6 +18,22 @@ function makeDirectory(directory) {
18
18
  }
19
19
  }
20
20
 
21
+ // And a file made there is readable by every user, whatever the process's
22
+ // mask: a container's root wrote evidence only it could read, and the run,
23
+ // as another user, recorded nothing ("Permission denied", 4.24.1).
24
+ const inContainer = () => process.env.SUPERCOV_CONTAINER === "1";
25
+ const fileMode = () => (inContainer() ? 0o666 : 0o600);
26
+ function openToAll(path) {
27
+ if (!inContainer())
28
+ return;
29
+ try {
30
+ chmodSync(path, 0o666);
31
+ }
32
+ catch {
33
+ // Left as the mask made it: the run says what it could not read.
34
+ }
35
+ }
36
+
21
37
  function fsyncDirectory(path) {
22
38
  try {
23
39
  const directory = openSync(path, "r");
@@ -38,7 +54,8 @@ export function atomicWriteFileSync(path, data, options) {
38
54
  const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
39
55
  let descriptor;
40
56
  try {
41
- descriptor = openSync(temporary, "wx", 0o600);
57
+ descriptor = openSync(temporary, "wx", fileMode());
58
+ openToAll(temporary);
42
59
  writeFileSync(descriptor, data, options);
43
60
  fsyncSync(descriptor);
44
61
  closeSync(descriptor);
@@ -59,7 +76,7 @@ export function atomicWriteFileSync(path, data, options) {
59
76
  export function appendJsonLineDurableSync(path, data) {
60
77
  makeDirectory(dirname(path));
61
78
  const existed = existsSync(path);
62
- const descriptor = openSync(path, "a", 0o600);
79
+ const descriptor = openSync(path, "a", fileMode());
63
80
  try {
64
81
  writeFileSync(descriptor, data.endsWith("\n") ? data : `${data}\n`);
65
82
  fsyncSync(descriptor);
@@ -67,13 +84,18 @@ export function appendJsonLineDurableSync(path, data) {
67
84
  finally {
68
85
  closeSync(descriptor);
69
86
  }
70
- if (!existed)
87
+ if (!existed) {
88
+ openToAll(path);
71
89
  fsyncDirectory(dirname(path));
90
+ }
72
91
  }
73
92
  /** Append a complete local record; process exit closes it before publication. */
74
93
  export function appendJsonLineSync(path, data) {
75
94
  makeDirectory(dirname(path));
76
- appendFileSync(path, data.endsWith("\n") ? data : `${data}\n`, { mode: 0o600 });
95
+ const opening = inContainer() && !existsSync(path);
96
+ appendFileSync(path, data.endsWith("\n") ? data : `${data}\n`, { mode: fileMode() });
97
+ if (opening)
98
+ openToAll(path);
77
99
  }
78
100
  /** Atomically publish a fully prepared file or directory and persist its entry. */
79
101
  export function atomicRenameSync(source, destination) {
@@ -10,7 +10,7 @@
10
10
  // CommonJS on purpose: Jest's module system loads setup files without the
11
11
  // ESM flag, and the runtime it needs is already on the sandbox global.
12
12
  const { createHash, randomUUID } = require("node:crypto");
13
- const { appendFileSync, closeSync, fsyncSync, mkdirSync, openSync, statSync, writeFileSync } = require("node:fs");
13
+ const { appendFileSync, chmodSync, closeSync, fsyncSync, mkdirSync, openSync, statSync, writeFileSync } = require("node:fs");
14
14
  const { relative, resolve, sep } = require("node:path");
15
15
  const { Buffer } = require("node:buffer");
16
16
 
@@ -115,8 +115,13 @@ function writeEvidence(payload) {
115
115
  const writer = (process.env.SUPERCOV_EXECUTION_LOG_SHARD ?? `pid-${process.pid}`).replace(/[^A-Za-z0-9_-]/g, "_");
116
116
  journal = { path: resolve(directory, `jest-worker-${writer}-${randomUUID()}.mcdc.jsonl`), size: 0 };
117
117
  }
118
+ // In a container its root writes the journal, and the run, as another
119
+ // user, reads it: opened to all there, as atomic.mjs opens its files.
120
+ const container = process.env.SUPERCOV_CONTAINER === "1";
121
+ const mode = container ? 0o666 : 0o600;
122
+ const opening = container && journal.size === 0;
118
123
  if (process.env.SUPERCOV_DURABLE_EVIDENCE_EACH_TEST === "1") {
119
- const descriptor = openSync(journal.path, "a", 0o600);
124
+ const descriptor = openSync(journal.path, "a", mode);
120
125
  try {
121
126
  writeFileSync(descriptor, line);
122
127
  fsyncSync(descriptor);
@@ -126,7 +131,15 @@ function writeEvidence(payload) {
126
131
  }
127
132
  }
128
133
  else {
129
- appendFileSync(journal.path, line, { mode: 0o600 });
134
+ appendFileSync(journal.path, line, { mode });
135
+ }
136
+ if (opening) {
137
+ try {
138
+ chmodSync(journal.path, 0o666);
139
+ }
140
+ catch {
141
+ // Left as the mask made it.
142
+ }
130
143
  }
131
144
  journal.size += Buffer.byteLength(line);
132
145
  }
@@ -625,6 +625,7 @@ function flushBackgroundAppends(fs) {
625
625
  // all.
626
626
  function makeEvidenceDirectory(fs, directory) {
627
627
  const first = fs.mkdirSync(directory, { recursive: true });
628
+ markContainerWriting(fs);
628
629
  if (!first || typeof process === "undefined" || !process.env || process.env.SUPERCOV_CONTAINER !== "1")
629
630
  return;
630
631
  let current = first;
@@ -640,6 +641,21 @@ function makeEvidenceDirectory(fs, directory) {
640
641
  }
641
642
  }
642
643
  }
644
+ // A container Supercov's relay measured says, once, that its evidence could
645
+ // be written: one that never does -- a server stopped at its first probe, a
646
+ // directory it may not write -- is named when the run ends.
647
+ function markContainerWriting(fs) {
648
+ const mark = typeof process !== "undefined" && process.env ? process.env.SUPERCOV_CONTAINER_MARK : void 0;
649
+ if (!mark || state.containerMarked)
650
+ return;
651
+ state.containerMarked = true;
652
+ try {
653
+ fs.mkdirSync(mark.slice(0, mark.lastIndexOf("/")), { recursive: true });
654
+ fs.writeFileSync(mark, "", { mode: 0o666 });
655
+ fs.chmodSync(mark, 0o666);
656
+ } catch (e) {
657
+ }
658
+ }
643
659
  function writeBackgroundLines(fs, runId, payload) {
644
660
  var _a8;
645
661
  const directory = backgroundEvidenceDirectory(runId);