staysfixed 0.9.1 → 0.11.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.
- package/CHANGELOG.md +182 -0
- package/README.md +17 -5
- package/docs/getting-started.md +10 -0
- package/docs/how-v2-works.md +5 -2
- package/package.json +2 -2
- package/src/guard/api.js +107 -3
- package/src/guard/run.js +154 -20
- package/src/report/console.js +235 -17
- package/src/report/html.js +75 -19
- package/src/types.js +5 -0
- package/src/v2/adapters/android-driver.js +62 -12
- package/src/v2/adapters/contract.js +18 -4
- package/src/v2/adapters/electron.js +96 -14
- package/src/v2/adapters/http.js +264 -23
- package/src/v2/adapters/ios-driver.js +22 -4
- package/src/v2/adapters/ios.js +5 -2
- package/src/v2/adapters/isolate.js +78 -5
- package/src/v2/adapters/process.js +350 -92
- package/src/v2/adapters/web-driver.js +23 -1
- package/src/v2/adapters/web.js +42 -3
- package/src/v2/adapters/windows.js +32 -15
- package/src/v2/check.js +526 -19
- package/src/v2/cli.js +345 -3
- package/src/v2/cluster.js +112 -4
- package/src/v2/coverage.js +293 -8
- package/src/v2/detect.js +182 -9
- package/src/v2/doctor.js +253 -30
- package/src/v2/init.js +102 -10
- package/src/v2/mcp/server.js +4 -1
- package/src/v2/mcp/tools.js +291 -24
- package/src/v2/normalise.js +11 -0
- package/src/v2/observation.js +57 -5
- package/src/v2/reference.js +133 -14
- package/src/v2/refusal.js +389 -0
- package/src/v2/remote.js +24 -3
- package/src/v2/run.js +306 -16
- package/src/v2/sealed.js +14 -2
- package/src/v2/ship.js +286 -22
- package/src/v2/store.js +101 -2
- package/src/v2/types.js +5 -0
- package/src/v2/waiver.js +9 -2
- package/src/watch/panel.js +12 -1
|
@@ -79,6 +79,9 @@ import {
|
|
|
79
79
|
trimForStorage,
|
|
80
80
|
} from './contract.js';
|
|
81
81
|
import { RemoteLinkLost, remoteRunner } from '../remote.js';
|
|
82
|
+
// Every wait here has a limit and every limit says what it was waiting for; the pieces are in
|
|
83
|
+
// process.js so there is one of each rather than one per adapter.
|
|
84
|
+
import { endOfChild, letGoOf } from './process.js';
|
|
82
85
|
|
|
83
86
|
/** @typedef {import('./contract.js').Build} Build */
|
|
84
87
|
/** @typedef {import('./contract.js').PreparedBuild} PreparedBuild */
|
|
@@ -814,21 +817,35 @@ export function asList(value) {
|
|
|
814
817
|
*/
|
|
815
818
|
export async function pushBuild(host, localDir, remoteDir) {
|
|
816
819
|
const started = Date.now();
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
820
|
+
// `ConnectTimeout` because ssh with no answer at the far end will sit on a half-open socket
|
|
821
|
+
// for as long as the network lets it, and the whole point of this pass is that nothing here
|
|
822
|
+
// is allowed to wait without a clock on it.
|
|
823
|
+
const tar = spawn('tar', ['-cf', '-', '-C', path.dirname(localDir), path.basename(localDir)]);
|
|
824
|
+
const ssh = spawn('ssh', ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=15', host, `mkdir -p '${remoteDir}' && tar -xf - -C '${remoteDir}'`]);
|
|
825
|
+
let trouble = '';
|
|
826
|
+
tar.stdout.pipe(ssh.stdin);
|
|
827
|
+
ssh.stderr.on('data', (d) => { trouble += String(d); });
|
|
828
|
+
tar.stderr.on('data', (d) => { trouble += String(d); });
|
|
829
|
+
// Nothing wants what ssh prints on its way through, and that is exactly why it has to be
|
|
830
|
+
// read. A pipe nobody empties fills up, and a full pipe blocks its writer for ever — the
|
|
831
|
+
// same hang as an unclosed one, arriving from the other direction.
|
|
832
|
+
ssh.stdout?.resume();
|
|
833
|
+
// A pipe with nobody reading it fills up and blocks the writer for ever, so if ssh is gone
|
|
834
|
+
// tar has to be told rather than left leaning on a dead pipe.
|
|
835
|
+
ssh.on('error', () => { try { tar.kill('SIGKILL'); } catch { /* already gone */ } });
|
|
836
|
+
tar.on('error', () => { try { ssh.kill('SIGKILL'); } catch { /* already gone */ } });
|
|
837
|
+
|
|
838
|
+
// Thirty minutes for a whole build over a network, which is far longer than it has ever
|
|
839
|
+
// taken and still a limit. A copy that never finishes and never says so is the shape of the
|
|
840
|
+
// hang this whole pass exists to remove.
|
|
841
|
+
const ended = await endOfChild(ssh, { limitMs: 30 * 60_000, what: `the copy of this build to ${host}` });
|
|
842
|
+
try { tar.kill('SIGKILL'); } catch { /* it finished on its own */ }
|
|
843
|
+
letGoOf(tar);
|
|
844
|
+
|
|
845
|
+
const ms = Date.now() - started;
|
|
846
|
+
if (ended.gaveUp) return { ok: false, ms, why: `${ended.why} ${trouble.trim().slice(0, 200)}`.trim() };
|
|
847
|
+
if (ended.code === 0) return { ok: true, ms, why: `Copied to ${host} in ${timeBucket(ms)}.` };
|
|
848
|
+
return { ok: false, ms, why: `Copying to ${host} failed: ${trouble.trim().slice(0, 300) || `the copy ended with ${ended.code ?? ended.signal}`}` };
|
|
832
849
|
}
|
|
833
850
|
|
|
834
851
|
// ---------------------------------------------------------------------------
|