@exercode/problem-utils 2.0.3 → 2.1.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/dist/helpers/runCommandInTemporaryPackageManagerProject.cjs +1 -1
- package/dist/helpers/runCommandInTemporaryPackageManagerProject.cjs.map +1 -1
- package/dist/helpers/runCommandInTemporaryPackageManagerProject.d.ts +5 -0
- package/dist/helpers/runCommandInTemporaryPackageManagerProject.js +1 -1
- package/dist/helpers/runCommandInTemporaryPackageManagerProject.js.map +1 -1
- package/dist/presets/command.cjs +1 -1
- package/dist/presets/command.cjs.map +1 -1
- package/dist/presets/command.d.ts +5 -0
- package/dist/presets/command.js +1 -1
- package/dist/presets/command.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./sandboxUser.cjs"),n=require("./safeFs.cjs");let r=require("node:fs/promises");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:child_process");a=e.__toESM(a,1);let o=require("node:fs");o=e.__toESM(o,1);let s=require("node:os");s=e.__toESM(s,1);const c={bun:[`package.json`,`bun.lock`,`bun.lockb`],cargo:[`Cargo.toml`,`Cargo.lock`],go:[`go.mod`,`go.sum`],gradle:[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradle.properties`,`gradle.lockfile`,`buildscript-gradle.lockfile`,`gradle`,`gradlew`,`gradlew.bat`],maven:[`pom.xml`,`.mvn`,`mvnw`,`mvnw.cmd`],npm:[`package.json`,`package-lock.json`],pnpm:[`package.json`,`pnpm-lock.yaml`,`pnpm-workspace.yaml`],ruby:[`Gemfile`,`Gemfile.lock`,`.ruby-version`],uv:[`pyproject.toml`,`uv.lock`],yarn:[`package.json`,`yarn.lock`,`.yarnrc`,`.yarnrc.yml`,`.yarn`]},l={bun:h,cargo:g,go:_,gradle:v,maven:y,npm:b,pnpm:x,ruby:S,uv:C,yarn:w},u=M();async function d(e){let n=await r.default.mkdtemp(i.default.join(s.default.tmpdir(),e.tempDirPrefix??`exercode-`));try{await r.default.cp(e.cwd,n,{recursive:!0}),await k({packageManager:e.packageManager,projectDir:e.projectDir,runDir:n,projectFilePaths:e.projectFilePaths}),t.makeAccessibleToSandboxUser(n);let i=e.env?{...process.env,...e.env}:process.env,a=await p(e.packageManager,n),o=typeof e.command==`function`?e.command({runDir:n}):e.command,s=Date.now(),c=e.outputLimitBytes??52428800,l;if(a&&(l=await j(a,{cwd:n,env:i,outputLimitBytes:c,stdin:``,timeLimitSeconds:e.timeLimitSeconds}),m(l)))return f({elapsedTimeSeconds:(Date.now()-s)/1e3,options:e,result:l});let u=e.timeLimitSeconds-(Date.now()-s)/1e3;if(u<=0)return{stdin:e.stdin??``,stdout:l?.stdout??``,stderr:l?.stderr??``,status:0,timeSeconds:e.timeLimitSeconds+.001,memoryBytes:l?.memoryBytes??0,timedOut:!0,signal:l?.signal,outputLimitExceeded:!1};let d=await j(o,{cwd:n,env:i,outputLimitBytes:c,stdin:e.stdin??``,timeLimitSeconds:u}),h=(Date.now()-s)/1e3;return f(l?{elapsedTimeSeconds:h,options:e,result:{...d,timeSeconds:l.timeSeconds+d.timeSeconds,memoryBytes:Math.max(l.memoryBytes,d.memoryBytes)}}:{elapsedTimeSeconds:h,options:e,result:d})}finally{try{t.sandboxUserName&&t.killSandboxUserProcesses()}finally{await t.forceRemove(n)}}}function f(e){return{stdin:e.options.stdin??``,stdout:e.result.stdout,stderr:e.result.stderr,status:e.result.timedOut||e.result.outputLimitExceeded?0:e.result.status,timeSeconds:e.result.timedOut?e.options.timeLimitSeconds+.001:e.result.timeSeconds||e.elapsedTimeSeconds,memoryBytes:e.result.memoryBytes,timedOut:e.result.timedOut,signal:e.result.signal,outputLimitExceeded:e.result.outputLimitExceeded}}function p(e,t){return l[e](t)}function m(e){return e.status!==0||e.timedOut||e.outputLimitExceeded}async function h(e){if(await D(i.default.join(e,`package.json`)))return await E(e,[`bun.lock`,`bun.lockb`])?[`bun`,`install`,`--frozen-lockfile`,`--silent`]:[`bun`,`install`,`--silent`]}async function g(e){if(await D(i.default.join(e,`Cargo.toml`)))return await D(i.default.join(e,`Cargo.lock`))?[`cargo`,`fetch`,`--locked`]:[`cargo`,`fetch`]}async function _(e){if(await D(i.default.join(e,`go.mod`)))return[`go`,`mod`,`download`]}async function v(e){if(!await E(e,[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradlew`,`gradlew.bat`]))return;let t=[`--no-daemon`,`--quiet`,`dependencies`];return process.platform===`win32`?await D(i.default.join(e,`gradlew.bat`))?[`cmd.exe`,`/c`,`gradlew.bat`,...t]:[`gradle`,...t]:await D(i.default.join(e,`gradlew`))?[`sh`,`./gradlew`,...t]:[`gradle`,...t]}async function y(e){if(!await D(i.default.join(e,`pom.xml`)))return;let t=[`-q`,`dependency:go-offline`];return process.platform===`win32`?await D(i.default.join(e,`mvnw.cmd`))?[`cmd.exe`,`/c`,`mvnw.cmd`,...t]:[`mvn`,...t]:await D(i.default.join(e,`mvnw`))?[`sh`,`./mvnw`,...t]:[`mvn`,...t]}async function b(e){if(await D(i.default.join(e,`package.json`)))return await D(i.default.join(e,`package-lock.json`))?[`npm`,`ci`,`--silent`]:[`npm`,`install`,`--silent`]}async function x(e){if(await D(i.default.join(e,`package.json`)))return await D(i.default.join(e,`pnpm-lock.yaml`))?[`pnpm`,`install`,`--frozen-lockfile`,`--silent`]:[`pnpm`,`install`,`--silent`]}async function S(e){if(await D(i.default.join(e,`Gemfile`)))return await D(i.default.join(e,`Gemfile.lock`))?[`bundle`,`install`,`--frozen`,`--quiet`]:[`bundle`,`install`,`--quiet`]}async function C(){}async function w(e){if(!await D(i.default.join(e,`package.json`)))return;let t=await T(e),n=await D(i.default.join(e,`yarn.lock`));return t?n?[`yarn`,`install`,`--immutable`]:[`yarn`,`install`]:n?[`yarn`,`install`,`--frozen-lockfile`,`--silent`]:[`yarn`,`install`,`--silent`]}async function T(e){if(await D(i.default.join(e,`.yarnrc.yml`)))return!0;let t=await O(i.default.join(e,`package.json`)),n=typeof t.packageManager==`string`?t.packageManager:void 0,r=/^yarn@(\d+)/.exec(n??``)?.[1];return r!==void 0&&Number(r)>=2}async function E(e,t){for(let n of t)if(await D(i.default.join(e,n)))return!0;return!1}async function D(e){try{return await r.default.access(e),!0}catch(e){if((typeof e==`object`&&e&&`code`in e?e.code:void 0)!==`ENOENT`)throw e;return!1}}async function O(e){try{let t=JSON.parse(await r.default.readFile(e,`utf8`));return typeof t==`object`&&t&&!Array.isArray(t)?t:{}}catch(e){if(e instanceof SyntaxError||(typeof e==`object`&&e&&`code`in e?e.code:void 0)===`ENOENT`)return{};throw e}}async function k(e){for(let t of e.projectFilePaths??c[e.packageManager])await A(i.default.join(e.projectDir,t),i.default.join(e.runDir,t),e.runDir)}async function A(e,t,a){try{await r.default.lstat(e),await n.createDirectoryWithoutFollowingSymlinks(a,i.default.dirname(t)),await n.copyWithoutFollowingSymlinks(e,t)}catch(e){if((typeof e==`object`&&e&&`code`in e?e.code:void 0)!==`ENOENT`)throw e}}async function j(e,n){let r=u===void 0?void 0:i.default.join(n.cwd,`.exercode-time-result`),o=t.wrapCommandWithSandboxUser(e),s=u===void 0?o:t.prependInsideSandboxWrapper(o,[...u,`--output=${r}`]),c=t.startSandboxTimeoutWatchdog(n.timeLimitSeconds),l=Date.now()+(Math.ceil(n.timeLimitSeconds)+5)*1e3,d;try{d=a.default.spawn(s[0],s.slice(1),{cwd:n.cwd,detached:process.platform!==`win32`,env:{...n.env,...t.getSandboxUserEnvOverrides(n.env)},stdio:[`pipe`,`pipe`,`pipe`]})}catch(e){throw c.cancel(),e}let f=[],p=[],m=0,h=!1,g=!1,_,v,y=e=>{try{N(d,e)}catch(e){_??=e instanceof Error?e:Error(String(e)),v?.()}},b=(e,t)=>{if(m>=n.outputLimitBytes){t.byteLength>0&&(g=!0,y(`SIGKILL`));return}let r=n.outputLimitBytes-m,i=t.byteLength>r?t.subarray(0,r):t;e.push(i),m+=i.byteLength,t.byteLength>r&&(g=!0,y(`SIGKILL`))};d.stdout.on(`data`,e=>b(f,e)),d.stderr.on(`data`,e=>b(p,e));let x=setTimeout(()=>{h=!0,y(`SIGTERM`)},n.timeLimitSeconds*1e3),S=setTimeout(()=>{h&&y(`SIGKILL`)},n.timeLimitSeconds*1e3+1e3);S.unref();let C,w=!1,{status:T,signal:E}=await new Promise((e,t)=>{let r=!1,i,a=e=>{r||(i=e,y(`SIGKILL`),d.pid===void 0&&(r=!0,t(e)))};v=()=>{C??=setTimeout(()=>{r||(r=!0,t(_??Error(`failed to terminate the submission`)))},Math.max(0,l-Date.now())+2e3)},d.on(`error`,a),d.stdin.on(`error`,e=>{e.code!==`EPIPE`&&a(e)}),d.on(`close`,(n,a)=>{if(!r){if(r=!0,w=!0,i){t(i);return}e({status:n??void 0,signal:a??void 0})}}),d.stdin.end(n.stdin)}).finally(()=>{clearTimeout(x),clearTimeout(S),C&&clearTimeout(C),(w||!_)&&c.cancel()}),{timeSeconds:D,memoryBytes:O}=r===void 0?{timeSeconds:0,memoryBytes:0}:await F(r);return{stdout:Buffer.concat(f).toString(),stderr:Buffer.concat(p).toString(),status:T,timeSeconds:D,memoryBytes:O,timedOut:h,signal:E,outputLimitExceeded:g}}function M(){let e=s.default.platform()===`darwin`?`gtime`:`/usr/bin/time`,t=a.default.spawnSync(e,[`--version`],{stdio:`ignore`});if(!(t.error||t.status!==0))return[e,`--format`,`%e %M`]}function N(e,n){if(e.pid!==void 0){if(t.sandboxUserName){t.killSandboxUserProcesses([n===`SIGKILL`?`KILL`:`TERM`]);return}try{if(process.platform===`win32`){e.kill(n);return}process.kill(-e.pid,n)}catch(e){let t=typeof e==`object`&&e&&`code`in e?e.code:void 0;if(t!==`ESRCH`&&t!==`EPERM`)throw e}}}function P(e,t){return typeof e==`object`&&!!e&&`code`in e&&e.code===t}async function F(e){let t,n;try{if(n=await r.default.open(e,o.default.constants.O_RDONLY|o.default.constants.O_NOFOLLOW|o.default.constants.O_NONBLOCK),!(await n.stat()).isFile())throw Error(`${e} is not a regular file`);t=await n.readFile(`utf8`)}catch(t){if(P(t,`ENOENT`))return{timeSeconds:0,memoryBytes:0};throw Error(`failed to read the time measurement at ${e}`,{cause:t})}finally{await n?.close()}let i=/(\d+(?:[.,]\d+)?) (\d+)\s*$/.exec(t);return i?{timeSeconds:Number(i[1].replace(`,`,`.`)),memoryBytes:Number(i[2])*1024}:{timeSeconds:0,memoryBytes:0}}exports.copyPackageManagerProjectFiles=k,exports.runCommandInTemporaryPackageManagerProject=d;
|
|
1
|
+
"use strict";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./sandboxUser.cjs"),n=require("./safeFs.cjs");let r=require("node:fs/promises");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:child_process");a=e.__toESM(a,1);let o=require("node:fs");o=e.__toESM(o,1);let s=require("node:os");s=e.__toESM(s,1);const c={bun:[`package.json`,`bun.lock`,`bun.lockb`],cargo:[`Cargo.toml`,`Cargo.lock`],go:[`go.mod`,`go.sum`],gradle:[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradle.properties`,`gradle.lockfile`,`buildscript-gradle.lockfile`,`gradle`,`gradlew`,`gradlew.bat`],maven:[`pom.xml`,`.mvn`,`mvnw`,`mvnw.cmd`],npm:[`package.json`,`package-lock.json`],pnpm:[`package.json`,`pnpm-lock.yaml`,`pnpm-workspace.yaml`],ruby:[`Gemfile`,`Gemfile.lock`,`.ruby-version`],uv:[`pyproject.toml`,`uv.lock`],yarn:[`package.json`,`yarn.lock`,`.yarnrc`,`.yarnrc.yml`,`.yarn`]},l={bun:h,cargo:g,go:_,gradle:v,maven:y,npm:b,pnpm:x,ruby:S,uv:C,yarn:w},u=M();async function d(e){let n=await r.default.mkdtemp(i.default.join(s.default.tmpdir(),e.tempDirPrefix??`exercode-`));try{await r.default.cp(e.cwd,n,{recursive:!0}),await k({packageManager:e.packageManager,projectDir:e.projectDir,runDir:n,projectFilePaths:e.projectFilePaths}),t.makeAccessibleToSandboxUser(n);let i=e.env?{...process.env,...e.env}:process.env,a=e.prepareDependencies===!1?void 0:await p(e.packageManager,n),o=typeof e.command==`function`?e.command({runDir:n}):e.command,s=Date.now(),c=e.outputLimitBytes??52428800,l;if(a&&(l=await j(a,{cwd:n,env:i,outputLimitBytes:c,stdin:``,timeLimitSeconds:e.timeLimitSeconds}),m(l)))return f({elapsedTimeSeconds:(Date.now()-s)/1e3,options:e,result:l});let u=e.timeLimitSeconds-(Date.now()-s)/1e3;if(u<=0)return{stdin:e.stdin??``,stdout:l?.stdout??``,stderr:l?.stderr??``,status:0,timeSeconds:e.timeLimitSeconds+.001,memoryBytes:l?.memoryBytes??0,timedOut:!0,signal:l?.signal,outputLimitExceeded:!1};let d=await j(o,{cwd:n,env:i,outputLimitBytes:c,stdin:e.stdin??``,timeLimitSeconds:u}),h=(Date.now()-s)/1e3;return f(l?{elapsedTimeSeconds:h,options:e,result:{...d,timeSeconds:l.timeSeconds+d.timeSeconds,memoryBytes:Math.max(l.memoryBytes,d.memoryBytes)}}:{elapsedTimeSeconds:h,options:e,result:d})}finally{try{t.sandboxUserName&&t.killSandboxUserProcesses()}finally{await t.forceRemove(n)}}}function f(e){return{stdin:e.options.stdin??``,stdout:e.result.stdout,stderr:e.result.stderr,status:e.result.timedOut||e.result.outputLimitExceeded?0:e.result.status,timeSeconds:e.result.timedOut?e.options.timeLimitSeconds+.001:e.result.timeSeconds||e.elapsedTimeSeconds,memoryBytes:e.result.memoryBytes,timedOut:e.result.timedOut,signal:e.result.signal,outputLimitExceeded:e.result.outputLimitExceeded}}function p(e,t){return l[e](t)}function m(e){return e.status!==0||e.timedOut||e.outputLimitExceeded}async function h(e){if(await D(i.default.join(e,`package.json`)))return await E(e,[`bun.lock`,`bun.lockb`])?[`bun`,`install`,`--frozen-lockfile`,`--silent`]:[`bun`,`install`,`--silent`]}async function g(e){if(await D(i.default.join(e,`Cargo.toml`)))return await D(i.default.join(e,`Cargo.lock`))?[`cargo`,`fetch`,`--locked`]:[`cargo`,`fetch`]}async function _(e){if(await D(i.default.join(e,`go.mod`)))return[`go`,`mod`,`download`]}async function v(e){if(!await E(e,[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradlew`,`gradlew.bat`]))return;let t=[`--no-daemon`,`--quiet`,`dependencies`];return process.platform===`win32`?await D(i.default.join(e,`gradlew.bat`))?[`cmd.exe`,`/c`,`gradlew.bat`,...t]:[`gradle`,...t]:await D(i.default.join(e,`gradlew`))?[`sh`,`./gradlew`,...t]:[`gradle`,...t]}async function y(e){if(!await D(i.default.join(e,`pom.xml`)))return;let t=[`-q`,`dependency:go-offline`];return process.platform===`win32`?await D(i.default.join(e,`mvnw.cmd`))?[`cmd.exe`,`/c`,`mvnw.cmd`,...t]:[`mvn`,...t]:await D(i.default.join(e,`mvnw`))?[`sh`,`./mvnw`,...t]:[`mvn`,...t]}async function b(e){if(await D(i.default.join(e,`package.json`)))return await D(i.default.join(e,`package-lock.json`))?[`npm`,`ci`,`--silent`]:[`npm`,`install`,`--silent`]}async function x(e){if(await D(i.default.join(e,`package.json`)))return await D(i.default.join(e,`pnpm-lock.yaml`))?[`pnpm`,`install`,`--frozen-lockfile`,`--silent`]:[`pnpm`,`install`,`--silent`]}async function S(e){if(await D(i.default.join(e,`Gemfile`)))return await D(i.default.join(e,`Gemfile.lock`))?[`bundle`,`install`,`--frozen`,`--quiet`]:[`bundle`,`install`,`--quiet`]}async function C(){}async function w(e){if(!await D(i.default.join(e,`package.json`)))return;let t=await T(e),n=await D(i.default.join(e,`yarn.lock`));return t?n?[`yarn`,`install`,`--immutable`]:[`yarn`,`install`]:n?[`yarn`,`install`,`--frozen-lockfile`,`--silent`]:[`yarn`,`install`,`--silent`]}async function T(e){if(await D(i.default.join(e,`.yarnrc.yml`)))return!0;let t=await O(i.default.join(e,`package.json`)),n=typeof t.packageManager==`string`?t.packageManager:void 0,r=/^yarn@(\d+)/.exec(n??``)?.[1];return r!==void 0&&Number(r)>=2}async function E(e,t){for(let n of t)if(await D(i.default.join(e,n)))return!0;return!1}async function D(e){try{return await r.default.access(e),!0}catch(e){if((typeof e==`object`&&e&&`code`in e?e.code:void 0)!==`ENOENT`)throw e;return!1}}async function O(e){try{let t=JSON.parse(await r.default.readFile(e,`utf8`));return typeof t==`object`&&t&&!Array.isArray(t)?t:{}}catch(e){if(e instanceof SyntaxError||(typeof e==`object`&&e&&`code`in e?e.code:void 0)===`ENOENT`)return{};throw e}}async function k(e){for(let t of e.projectFilePaths??c[e.packageManager])await A(i.default.join(e.projectDir,t),i.default.join(e.runDir,t),e.runDir)}async function A(e,t,a){try{await r.default.lstat(e),await n.createDirectoryWithoutFollowingSymlinks(a,i.default.dirname(t)),await n.copyWithoutFollowingSymlinks(e,t)}catch(e){if((typeof e==`object`&&e&&`code`in e?e.code:void 0)!==`ENOENT`)throw e}}async function j(e,n){let r=u===void 0?void 0:i.default.join(n.cwd,`.exercode-time-result`),o=t.wrapCommandWithSandboxUser(e),s=u===void 0?o:t.prependInsideSandboxWrapper(o,[...u,`--output=${r}`]),c=t.startSandboxTimeoutWatchdog(n.timeLimitSeconds),l=Date.now()+(Math.ceil(n.timeLimitSeconds)+5)*1e3,d;try{d=a.default.spawn(s[0],s.slice(1),{cwd:n.cwd,detached:process.platform!==`win32`,env:{...n.env,...t.getSandboxUserEnvOverrides(n.env)},stdio:[`pipe`,`pipe`,`pipe`]})}catch(e){throw c.cancel(),e}let f=[],p=[],m=0,h=!1,g=!1,_,v,y=e=>{try{N(d,e)}catch(e){_??=e instanceof Error?e:Error(String(e)),v?.()}},b=(e,t)=>{if(m>=n.outputLimitBytes){t.byteLength>0&&(g=!0,y(`SIGKILL`));return}let r=n.outputLimitBytes-m,i=t.byteLength>r?t.subarray(0,r):t;e.push(i),m+=i.byteLength,t.byteLength>r&&(g=!0,y(`SIGKILL`))};d.stdout.on(`data`,e=>b(f,e)),d.stderr.on(`data`,e=>b(p,e));let x=setTimeout(()=>{h=!0,y(`SIGTERM`)},n.timeLimitSeconds*1e3),S=setTimeout(()=>{h&&y(`SIGKILL`)},n.timeLimitSeconds*1e3+1e3);S.unref();let C,w=!1,{status:T,signal:E}=await new Promise((e,t)=>{let r=!1,i,a=e=>{r||(i=e,y(`SIGKILL`),d.pid===void 0&&(r=!0,t(e)))};v=()=>{C??=setTimeout(()=>{r||(r=!0,t(_??Error(`failed to terminate the submission`)))},Math.max(0,l-Date.now())+2e3)},d.on(`error`,a),d.stdin.on(`error`,e=>{e.code!==`EPIPE`&&a(e)}),d.on(`close`,(n,a)=>{if(!r){if(r=!0,w=!0,i){t(i);return}e({status:n??void 0,signal:a??void 0})}}),d.stdin.end(n.stdin)}).finally(()=>{clearTimeout(x),clearTimeout(S),C&&clearTimeout(C),(w||!_)&&c.cancel()}),{timeSeconds:D,memoryBytes:O}=r===void 0?{timeSeconds:0,memoryBytes:0}:await F(r);return{stdout:Buffer.concat(f).toString(),stderr:Buffer.concat(p).toString(),status:T,timeSeconds:D,memoryBytes:O,timedOut:h,signal:E,outputLimitExceeded:g}}function M(){let e=s.default.platform()===`darwin`?`gtime`:`/usr/bin/time`,t=a.default.spawnSync(e,[`--version`],{stdio:`ignore`});if(!(t.error||t.status!==0))return[e,`--format`,`%e %M`]}function N(e,n){if(e.pid!==void 0){if(t.sandboxUserName){t.killSandboxUserProcesses([n===`SIGKILL`?`KILL`:`TERM`]);return}try{if(process.platform===`win32`){e.kill(n);return}process.kill(-e.pid,n)}catch(e){let t=typeof e==`object`&&e&&`code`in e?e.code:void 0;if(t!==`ESRCH`&&t!==`EPERM`)throw e}}}function P(e,t){return typeof e==`object`&&!!e&&`code`in e&&e.code===t}async function F(e){let t,n;try{if(n=await r.default.open(e,o.default.constants.O_RDONLY|o.default.constants.O_NOFOLLOW|o.default.constants.O_NONBLOCK),!(await n.stat()).isFile())throw Error(`${e} is not a regular file`);t=await n.readFile(`utf8`)}catch(t){if(P(t,`ENOENT`))return{timeSeconds:0,memoryBytes:0};throw Error(`failed to read the time measurement at ${e}`,{cause:t})}finally{await n?.close()}let i=/(\d+(?:[.,]\d+)?) (\d+)\s*$/.exec(t);return i?{timeSeconds:Number(i[1].replace(`,`,`.`)),memoryBytes:Number(i[2])*1024}:{timeSeconds:0,memoryBytes:0}}exports.copyPackageManagerProjectFiles=k,exports.runCommandInTemporaryPackageManagerProject=d;
|
|
2
2
|
//# sourceMappingURL=runCommandInTemporaryPackageManagerProject.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runCommandInTemporaryPackageManagerProject.cjs","names":["fs","path","os","sandboxUserName","forceRemove","createDirectoryWithoutFollowingSymlinks","copyWithoutFollowingSymlinks","wrapCommandWithSandboxUser","prependInsideSandboxWrapper","startSandboxTimeoutWatchdog","childProcess","getSandboxUserEnvOverrides","nodeFs"],"sources":["../../src/helpers/runCommandInTemporaryPackageManagerProject.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport nodeFs from 'node:fs';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport {\n forceRemove,\n getSandboxUserEnvOverrides,\n killSandboxUserProcesses,\n makeAccessibleToSandboxUser,\n prependInsideSandboxWrapper,\n SANDBOX_WATCHDOG_GRACE_SECONDS,\n sandboxUserName,\n startSandboxTimeoutWatchdog,\n wrapCommandWithSandboxUser,\n} from './sandboxUser.js';\nimport { copyWithoutFollowingSymlinks, createDirectoryWithoutFollowingSymlinks } from './safeFs.js';\n\nexport type PackageManager = 'bun' | 'cargo' | 'go' | 'gradle' | 'maven' | 'npm' | 'pnpm' | 'ruby' | 'uv' | 'yarn';\ntype PackageManagerInstallCommand = readonly [string, ...string[]];\n\nexport interface PackageManagerCommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n timedOut: boolean;\n signal: NodeJS.Signals | undefined;\n outputLimitExceeded: boolean;\n}\n\nexport interface RunCommandInTemporaryPackageManagerProjectOptions {\n cwd: string;\n projectDir: string;\n packageManager: PackageManager;\n command: readonly [string, ...string[]] | ((context: { runDir: string }) => readonly [string, ...string[]]);\n stdin?: string;\n env?: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n outputLimitBytes?: number;\n tempDirPrefix?: string;\n projectFilePaths?: readonly string[];\n}\n\nconst packageManagerProjectFilePaths = {\n bun: ['package.json', 'bun.lock', 'bun.lockb'],\n cargo: ['Cargo.toml', 'Cargo.lock'],\n go: ['go.mod', 'go.sum'],\n gradle: [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradle.properties',\n 'gradle.lockfile',\n 'buildscript-gradle.lockfile',\n 'gradle',\n 'gradlew',\n 'gradlew.bat',\n ],\n maven: ['pom.xml', '.mvn', 'mvnw', 'mvnw.cmd'],\n npm: ['package.json', 'package-lock.json'],\n pnpm: ['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml'],\n ruby: ['Gemfile', 'Gemfile.lock', '.ruby-version'],\n uv: ['pyproject.toml', 'uv.lock'],\n yarn: ['package.json', 'yarn.lock', '.yarnrc', '.yarnrc.yml', '.yarn'],\n} as const satisfies Record<PackageManager, readonly string[]>;\n\nconst packageManagerInstallCommandResolvers = {\n bun: resolveBunInstallCommand,\n cargo: resolveCargoInstallCommand,\n go: resolveGoInstallCommand,\n gradle: resolveGradleInstallCommand,\n maven: resolveMavenInstallCommand,\n npm: resolveNpmInstallCommand,\n pnpm: resolvePnpmInstallCommand,\n ruby: resolveRubyInstallCommand,\n uv: resolveUvInstallCommand,\n yarn: resolveYarnInstallCommand,\n} as const satisfies Record<PackageManager, (runDir: string) => Promise<PackageManagerInstallCommand | undefined>>;\n\nconst defaultOutputLimitBytes = 50 * 1024 * 1024;\nconst killGracePeriodMilliseconds = 1000;\n// Added to the watchdog's own deadline so its kill has time to land and `close` to arrive.\nconst watchdogSettleMarginMilliseconds = 2000;\nconst timeCommand = resolveTimeCommand();\n\n/**\n * Copies a submission directory to a temporary directory, overlays package\n * manager project files from the problem directory, prepares dependencies,\n * runs a command, and then removes the temporary directory.\n *\n * Under `EXERCODE_SANDBOX_USER` delegation, cleanup kills every process of the sandbox user, so do\n * not run multiple invocations concurrently in that mode — one finishing would kill the others.\n */\nexport async function runCommandInTemporaryPackageManagerProject(\n options: RunCommandInTemporaryPackageManagerProjectOptions\n): Promise<PackageManagerCommandRunResult> {\n const runDir = await fs.mkdtemp(path.join(os.tmpdir(), options.tempDirPrefix ?? 'exercode-'));\n try {\n await fs.cp(options.cwd, runDir, { recursive: true });\n await copyPackageManagerProjectFiles({\n packageManager: options.packageManager,\n projectDir: options.projectDir,\n runDir,\n projectFilePaths: options.projectFilePaths,\n });\n // The sandbox user (if any) runs the install/run commands below and must read the copied\n // sources and create outputs (e.g. `node_modules`, `.venv`) next to them.\n makeAccessibleToSandboxUser(runDir);\n\n const env = options.env ? { ...process.env, ...options.env } : process.env;\n const installCommand = await resolveInstallCommand(options.packageManager, runDir);\n const command = typeof options.command === 'function' ? options.command({ runDir }) : options.command;\n const startedAt = Date.now();\n const outputLimitBytes = options.outputLimitBytes ?? defaultOutputLimitBytes;\n let installResult: Awaited<ReturnType<typeof spawnWithInput>> | undefined;\n\n if (installCommand) {\n installResult = await spawnWithInput(installCommand, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: '',\n timeLimitSeconds: options.timeLimitSeconds,\n });\n if (isFailedSpawnResult(installResult)) {\n return toPackageManagerCommandRunResult({\n elapsedTimeSeconds: (Date.now() - startedAt) / 1000,\n options,\n result: installResult,\n });\n }\n }\n\n const remainingTimeLimitSeconds = options.timeLimitSeconds - (Date.now() - startedAt) / 1000;\n if (remainingTimeLimitSeconds <= 0) {\n return {\n stdin: options.stdin ?? '',\n stdout: installResult?.stdout ?? '',\n stderr: installResult?.stderr ?? '',\n status: 0,\n timeSeconds: options.timeLimitSeconds + 1e-3,\n memoryBytes: installResult?.memoryBytes ?? 0,\n timedOut: true,\n signal: installResult?.signal,\n outputLimitExceeded: false,\n };\n }\n\n const result = await spawnWithInput(command, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: options.stdin ?? '',\n timeLimitSeconds: remainingTimeLimitSeconds,\n });\n const elapsedTimeSeconds = (Date.now() - startedAt) / 1000;\n\n if (installResult) {\n return toPackageManagerCommandRunResult({\n elapsedTimeSeconds,\n options,\n result: {\n ...result,\n timeSeconds: installResult.timeSeconds + result.timeSeconds,\n memoryBytes: Math.max(installResult.memoryBytes, result.memoryBytes),\n },\n });\n }\n\n return toPackageManagerCommandRunResult({ elapsedTimeSeconds, options, result });\n } finally {\n try {\n // Daemonized children of the sandboxed command would otherwise outlive the run.\n if (sandboxUserName) killSandboxUserProcesses();\n } finally {\n // Must run even when the sweep fails closed, or the temporary submission copy leaks.\n await forceRemove(runDir);\n }\n }\n}\n\nfunction toPackageManagerCommandRunResult(context: {\n elapsedTimeSeconds: number;\n options: RunCommandInTemporaryPackageManagerProjectOptions;\n result: Awaited<ReturnType<typeof spawnWithInput>>;\n}): PackageManagerCommandRunResult {\n return {\n stdin: context.options.stdin ?? '',\n stdout: context.result.stdout,\n stderr: context.result.stderr,\n status: context.result.timedOut || context.result.outputLimitExceeded ? 0 : context.result.status,\n timeSeconds: context.result.timedOut\n ? context.options.timeLimitSeconds + 1e-3\n : context.result.timeSeconds || context.elapsedTimeSeconds,\n memoryBytes: context.result.memoryBytes,\n timedOut: context.result.timedOut,\n signal: context.result.signal,\n outputLimitExceeded: context.result.outputLimitExceeded,\n };\n}\n\nfunction resolveInstallCommand(\n packageManager: PackageManager,\n runDir: string\n): Promise<PackageManagerInstallCommand | undefined> {\n return packageManagerInstallCommandResolvers[packageManager](runDir);\n}\n\nfunction isFailedSpawnResult(result: Awaited<ReturnType<typeof spawnWithInput>>): boolean {\n return result.status !== 0 || result.timedOut || result.outputLimitExceeded;\n}\n\nasync function resolveBunInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n // Bun supports --silent and it keeps successful preparation output out of judge output buffers.\n return (await hasAnyPath(runDir, ['bun.lock', 'bun.lockb']))\n ? ['bun', 'install', '--frozen-lockfile', '--silent']\n : ['bun', 'install', '--silent'];\n}\n\nasync function resolveCargoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Cargo.toml')))) return undefined;\n return (await pathExists(path.join(runDir, 'Cargo.lock'))) ? ['cargo', 'fetch', '--locked'] : ['cargo', 'fetch'];\n}\n\nasync function resolveGoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'go.mod')))) return undefined;\n return ['go', 'mod', 'download'];\n}\n\nasync function resolveGradleInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (\n !(await hasAnyPath(runDir, [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradlew',\n 'gradlew.bat',\n ]))\n )\n return undefined;\n const args = ['--no-daemon', '--quiet', 'dependencies'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'gradlew.bat')))\n ? ['cmd.exe', '/c', 'gradlew.bat', ...args]\n : ['gradle', ...args];\n }\n return (await pathExists(path.join(runDir, 'gradlew'))) ? ['sh', './gradlew', ...args] : ['gradle', ...args];\n}\n\nasync function resolveMavenInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'pom.xml')))) return undefined;\n const args = ['-q', 'dependency:go-offline'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'mvnw.cmd')))\n ? ['cmd.exe', '/c', 'mvnw.cmd', ...args]\n : ['mvn', ...args];\n }\n return (await pathExists(path.join(runDir, 'mvnw'))) ? ['sh', './mvnw', ...args] : ['mvn', ...args];\n}\n\nasync function resolveNpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'package-lock.json')))\n ? ['npm', 'ci', '--silent']\n : ['npm', 'install', '--silent'];\n}\n\nasync function resolvePnpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'pnpm-lock.yaml')))\n ? ['pnpm', 'install', '--frozen-lockfile', '--silent']\n : ['pnpm', 'install', '--silent'];\n}\n\nasync function resolveRubyInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Gemfile')))) return undefined;\n return (await pathExists(path.join(runDir, 'Gemfile.lock')))\n ? ['bundle', 'install', '--frozen', '--quiet']\n : ['bundle', 'install', '--quiet'];\n}\n\nasync function resolveUvInstallCommand(): Promise<undefined> {\n return undefined;\n}\n\nasync function resolveYarnInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n const isBerry = await isYarnBerryProject(runDir);\n const hasLockfile = await pathExists(path.join(runDir, 'yarn.lock'));\n if (isBerry) return hasLockfile ? ['yarn', 'install', '--immutable'] : ['yarn', 'install'];\n return hasLockfile ? ['yarn', 'install', '--frozen-lockfile', '--silent'] : ['yarn', 'install', '--silent'];\n}\n\nasync function isYarnBerryProject(runDir: string): Promise<boolean> {\n if (await pathExists(path.join(runDir, '.yarnrc.yml'))) return true;\n\n const packageJson = await readJson(path.join(runDir, 'package.json'));\n const packageManager = typeof packageJson.packageManager === 'string' ? packageJson.packageManager : undefined;\n const yarnMajorVersion = /^yarn@(\\d+)/.exec(packageManager ?? '')?.[1];\n return yarnMajorVersion !== undefined && Number(yarnMajorVersion) >= 2;\n}\n\nasync function hasAnyPath(directoryPath: string, relativePaths: readonly string[]): Promise<boolean> {\n for (const relativePath of relativePaths) {\n if (await pathExists(path.join(directoryPath, relativePath))) return true;\n }\n return false;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ENOENT') throw error;\n return false;\n }\n}\n\nasync function readJson(filePath: string): Promise<Record<string, unknown>> {\n try {\n const parsed = JSON.parse(await fs.readFile(filePath, 'utf8')) as unknown;\n return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch (error) {\n if (error instanceof SyntaxError) return {};\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code === 'ENOENT') return {};\n throw error;\n }\n}\n\nexport async function copyPackageManagerProjectFiles(options: {\n packageManager: PackageManager;\n projectDir: string;\n runDir: string;\n projectFilePaths?: readonly string[];\n}): Promise<void> {\n for (const projectFilePath of options.projectFilePaths ?? packageManagerProjectFilePaths[options.packageManager]) {\n await copyPathIfExists(\n path.join(options.projectDir, projectFilePath),\n path.join(options.runDir, projectFilePath),\n options.runDir\n );\n }\n}\n\nasync function copyPathIfExists(sourcePath: string, destinationPath: string, runDir: string): Promise<void> {\n try {\n // Before touching the destination: creating the parent levels replaces whatever the submission\n // put there, which must not happen for a project file the problem does not even ship.\n await fs.lstat(sourcePath);\n // No-follow copy: the destination was seeded from the (sandbox-writable) submission tree, so a\n // planted symlink there — at the destination itself or at any directory level of a nested\n // project file path — must not redirect this trusted project-file overlay outside runDir.\n await createDirectoryWithoutFollowingSymlinks(runDir, path.dirname(destinationPath));\n await copyWithoutFollowingSymlinks(sourcePath, destinationPath);\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ENOENT') throw error;\n }\n}\n\nasync function spawnWithInput(\n command: readonly [string, ...string[]],\n context: {\n cwd: string;\n env: NodeJS.ProcessEnv;\n outputLimitBytes: number;\n stdin: string;\n timeLimitSeconds: number;\n }\n): Promise<{\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n timedOut: boolean;\n signal: NodeJS.Signals | undefined;\n outputLimitExceeded: boolean;\n}> {\n const timeOutputPath = timeCommand === undefined ? undefined : path.join(context.cwd, '.exercode-time-result');\n // `wrapCommandWithSandboxUser` is idempotent, so a command the presets already wrapped is not\n // nested; `time` is then spliced INSIDE that wrapper so it runs as the sandbox user, never as the\n // harness user writing `--output` into this sandbox-writable directory.\n const wrappedCommand = wrapCommandWithSandboxUser(command);\n const spawnedCommand =\n timeCommand === undefined\n ? wrappedCommand\n : prependInsideSandboxWrapper(wrappedCommand, [...timeCommand, `--output=${timeOutputPath}`]);\n // Started BEFORE the submission: `killSubprocessGroup` cannot spawn its `sudo` once a submission\n // has exhausted the PID cgroup, and a submission spawned first could exhaust it before this\n // watchdog exists. It is the deadline of last resort on this path.\n const watchdog = startSandboxTimeoutWatchdog(context.timeLimitSeconds);\n const watchdogDeadlineAt = Date.now() + (Math.ceil(context.timeLimitSeconds) + SANDBOX_WATCHDOG_GRACE_SECONDS) * 1000;\n let subprocess: childProcess.ChildProcessWithoutNullStreams;\n try {\n subprocess = childProcess.spawn(spawnedCommand[0], spawnedCommand.slice(1), {\n cwd: context.cwd,\n detached: process.platform !== 'win32',\n env: { ...context.env, ...getSandboxUserEnvOverrides(context.env) },\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n } catch (error) {\n watchdog.cancel();\n throw error;\n }\n\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let outputBytes = 0;\n let timedOut = false;\n let outputLimitExceeded = false;\n // Set when terminating the submission failed (the cleanup `sudo` could not be spawned). The run\n // then waits for the watchdog to end the submission rather than throwing out of an EventEmitter\n // callback, which would be an uncaught exception that kills the harness before any cleanup.\n let killError: Error | undefined;\n let onKillFailure: (() => void) | undefined;\n const killOrRecordFailure = (signal: NodeJS.Signals): void => {\n try {\n killSubprocessGroup(subprocess, signal);\n } catch (error) {\n killError ??= error instanceof Error ? error : new Error(String(error));\n onKillFailure?.();\n }\n };\n\n const appendOutputChunk = (chunks: Buffer[], chunk: Buffer): void => {\n if (outputBytes >= context.outputLimitBytes) {\n if (chunk.byteLength > 0) {\n outputLimitExceeded = true;\n killOrRecordFailure('SIGKILL');\n }\n return;\n }\n\n const remainingBytes = context.outputLimitBytes - outputBytes;\n const appendedChunk = chunk.byteLength > remainingBytes ? chunk.subarray(0, remainingBytes) : chunk;\n chunks.push(appendedChunk);\n outputBytes += appendedChunk.byteLength;\n\n if (chunk.byteLength > remainingBytes) {\n outputLimitExceeded = true;\n killOrRecordFailure('SIGKILL');\n }\n };\n\n subprocess.stdout.on('data', (chunk: Buffer) => appendOutputChunk(stdoutChunks, chunk));\n subprocess.stderr.on('data', (chunk: Buffer) => appendOutputChunk(stderrChunks, chunk));\n\n const timeout = setTimeout(() => {\n timedOut = true;\n killOrRecordFailure('SIGTERM');\n }, context.timeLimitSeconds * 1000);\n const killTimeout = setTimeout(\n () => {\n if (timedOut) killOrRecordFailure('SIGKILL');\n },\n context.timeLimitSeconds * 1000 + killGracePeriodMilliseconds\n );\n killTimeout.unref();\n // Bounds the wait after a kill failure: the watchdog force-kills the sandbox user at its own\n // deadline, so `close` should arrive; if even that fails, give up rather than hang.\n let killFailureTimeout: ReturnType<typeof setTimeout> | undefined;\n let closeObserved = false;\n\n const { status, signal } = await new Promise<{ status: number | undefined; signal: NodeJS.Signals | undefined }>(\n (resolve, reject) => {\n let settled = false;\n let pendingError: Error | undefined;\n const failAfterClose = (error: Error): void => {\n if (settled) return;\n pendingError = error;\n killOrRecordFailure('SIGKILL');\n if (subprocess.pid === undefined) {\n settled = true;\n reject(error);\n }\n };\n // Terminating the submission failed. Keep waiting: the watchdog (started before the\n // submission) force-kills the sandbox user at its own deadline, so `close` still arrives and\n // the run reports its real verdict. Give up only once that deadline has passed without the\n // submission ending, so the run can neither hang nor abandon a still-running submission\n // while its watchdog could still fire.\n onKillFailure = () => {\n killFailureTimeout ??= setTimeout(\n () => {\n if (settled) return;\n settled = true;\n reject(killError ?? new Error('failed to terminate the submission'));\n },\n Math.max(0, watchdogDeadlineAt - Date.now()) + watchdogSettleMarginMilliseconds\n );\n };\n subprocess.on('error', failAfterClose);\n subprocess.stdin.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code !== 'EPIPE') failAfterClose(error);\n });\n subprocess.on('close', (code, closeSignal) => {\n if (settled) return;\n settled = true;\n // The submission ended, so the watchdog has nothing left to kill and must not outlive this\n // run. A kill that failed once but succeeded on retry (or was superseded by the watchdog)\n // is not an error: the timeout/output-limit verdict below is the correct one to report.\n closeObserved = true;\n if (pendingError) {\n reject(pendingError);\n return;\n }\n resolve({ status: code ?? undefined, signal: closeSignal ?? undefined });\n });\n subprocess.stdin.end(context.stdin);\n }\n ).finally(() => {\n clearTimeout(timeout);\n clearTimeout(killTimeout);\n if (killFailureTimeout) clearTimeout(killFailureTimeout);\n // Cancel unless the submission is still running after a failed kill: the watchdog is then the\n // only remaining mechanism able to stop it, and cancelling would leave it running unchecked.\n if (closeObserved || !killError) watchdog.cancel();\n });\n\n const { timeSeconds, memoryBytes } =\n timeOutputPath === undefined ? { timeSeconds: 0, memoryBytes: 0 } : await readTimeResult(timeOutputPath);\n\n return {\n stdout: Buffer.concat(stdoutChunks).toString(),\n stderr: Buffer.concat(stderrChunks).toString(),\n status,\n timeSeconds,\n memoryBytes,\n timedOut,\n signal,\n outputLimitExceeded,\n };\n}\n\nfunction resolveTimeCommand(): readonly [string, ...string[]] | undefined {\n const command = os.platform() === 'darwin' ? 'gtime' : '/usr/bin/time';\n const result = childProcess.spawnSync(command, ['--version'], { stdio: 'ignore' });\n if (result.error || result.status !== 0) return undefined;\n\n return [command, '--format', '%e %M'];\n}\n\nfunction killSubprocessGroup(subprocess: childProcess.ChildProcess, signal: NodeJS.Signals): void {\n if (subprocess.pid === undefined) return;\n\n // The current user cannot signal the root-owned sudo wrapper nor the sandbox user's processes,\n // so go through sudo with the requested signal only; the callers' existing timers provide the\n // TERM → grace → KILL escalation.\n // Throws when the cleanup `sudo` cannot even be spawned (a submission can exhaust the PID\n // cgroup). Only `killOrRecordFailure` may call this: it records the failure and lets the\n // watchdog (or, past its deadline, a bounded timer) settle the run instead of letting the throw\n // escape an EventEmitter callback.\n if (sandboxUserName) {\n killSandboxUserProcesses([signal === 'SIGKILL' ? 'KILL' : 'TERM']);\n return;\n }\n\n try {\n if (process.platform === 'win32') {\n subprocess.kill(signal);\n return;\n }\n process.kill(-subprocess.pid, signal);\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ESRCH' && code !== 'EPERM') throw error;\n }\n}\n\nfunction isErrorWithCode(error: unknown, code: string): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && (error as { code: unknown }).code === code;\n}\n\nasync function readTimeResult(timeOutputPath: string): Promise<{ timeSeconds: number; memoryBytes: number }> {\n // The submission owns this directory and can put anything at this path, so decide what was\n // opened from the handle itself rather than from a prior `lstat` it could race:\n // - `O_NOFOLLOW` refuses a symlink to a harness-readable file (e.g. the problem's expected\n // outputs), which would otherwise report that file's trailing numbers as the submission's own\n // time and memory usage;\n // - `O_NONBLOCK` keeps a planted FIFO from blocking the harness forever waiting for a writer;\n // - the `fstat` then rejects anything that is not a regular file.\n //\n // Only an absent file means \"no measurement\" (the command was killed before `time` wrote one).\n // Anything else at this path is the submission tampering with its own accounting — reporting zero\n // memory would let it slip past a memory limit — so that fails the run instead.\n let content: string;\n let fileHandle: Awaited<ReturnType<typeof fs.open>> | undefined;\n try {\n fileHandle = await fs.open(\n timeOutputPath,\n nodeFs.constants.O_RDONLY | nodeFs.constants.O_NOFOLLOW | nodeFs.constants.O_NONBLOCK\n );\n const stats = await fileHandle.stat();\n if (!stats.isFile()) throw new Error(`${timeOutputPath} is not a regular file`);\n content = await fileHandle.readFile('utf8');\n } catch (error) {\n if (isErrorWithCode(error, 'ENOENT')) return { timeSeconds: 0, memoryBytes: 0 };\n throw new Error(`failed to read the time measurement at ${timeOutputPath}`, { cause: error });\n } finally {\n await fileHandle?.close();\n }\n\n const match = /(\\d+(?:[.,]\\d+)?) (\\d+)\\s*$/.exec(content);\n if (!match) return { timeSeconds: 0, memoryBytes: 0 };\n\n return { timeSeconds: Number(match[1]!.replace(',', '.')), memoryBytes: Number(match[2]) * 1024 };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runCommandInTemporaryPackageManagerProject.cjs","names":["fs","path","os","sandboxUserName","forceRemove","createDirectoryWithoutFollowingSymlinks","copyWithoutFollowingSymlinks","wrapCommandWithSandboxUser","prependInsideSandboxWrapper","startSandboxTimeoutWatchdog","childProcess","getSandboxUserEnvOverrides","nodeFs"],"sources":["../../src/helpers/runCommandInTemporaryPackageManagerProject.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport nodeFs from 'node:fs';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport {\n forceRemove,\n getSandboxUserEnvOverrides,\n killSandboxUserProcesses,\n makeAccessibleToSandboxUser,\n prependInsideSandboxWrapper,\n SANDBOX_WATCHDOG_GRACE_SECONDS,\n sandboxUserName,\n startSandboxTimeoutWatchdog,\n wrapCommandWithSandboxUser,\n} from './sandboxUser.js';\nimport { copyWithoutFollowingSymlinks, createDirectoryWithoutFollowingSymlinks } from './safeFs.js';\n\nexport type PackageManager = 'bun' | 'cargo' | 'go' | 'gradle' | 'maven' | 'npm' | 'pnpm' | 'ruby' | 'uv' | 'yarn';\ntype PackageManagerInstallCommand = readonly [string, ...string[]];\n\nexport interface PackageManagerCommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n timedOut: boolean;\n signal: NodeJS.Signals | undefined;\n outputLimitExceeded: boolean;\n}\n\nexport interface RunCommandInTemporaryPackageManagerProjectOptions {\n cwd: string;\n projectDir: string;\n packageManager: PackageManager;\n command: readonly [string, ...string[]] | ((context: { runDir: string }) => readonly [string, ...string[]]);\n /**\n * Set to false when `command` prepares the dependencies it needs.\n * Defaults to true.\n */\n prepareDependencies?: boolean;\n stdin?: string;\n env?: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n outputLimitBytes?: number;\n tempDirPrefix?: string;\n projectFilePaths?: readonly string[];\n}\n\nconst packageManagerProjectFilePaths = {\n bun: ['package.json', 'bun.lock', 'bun.lockb'],\n cargo: ['Cargo.toml', 'Cargo.lock'],\n go: ['go.mod', 'go.sum'],\n gradle: [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradle.properties',\n 'gradle.lockfile',\n 'buildscript-gradle.lockfile',\n 'gradle',\n 'gradlew',\n 'gradlew.bat',\n ],\n maven: ['pom.xml', '.mvn', 'mvnw', 'mvnw.cmd'],\n npm: ['package.json', 'package-lock.json'],\n pnpm: ['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml'],\n ruby: ['Gemfile', 'Gemfile.lock', '.ruby-version'],\n uv: ['pyproject.toml', 'uv.lock'],\n yarn: ['package.json', 'yarn.lock', '.yarnrc', '.yarnrc.yml', '.yarn'],\n} as const satisfies Record<PackageManager, readonly string[]>;\n\nconst packageManagerInstallCommandResolvers = {\n bun: resolveBunInstallCommand,\n cargo: resolveCargoInstallCommand,\n go: resolveGoInstallCommand,\n gradle: resolveGradleInstallCommand,\n maven: resolveMavenInstallCommand,\n npm: resolveNpmInstallCommand,\n pnpm: resolvePnpmInstallCommand,\n ruby: resolveRubyInstallCommand,\n uv: resolveUvInstallCommand,\n yarn: resolveYarnInstallCommand,\n} as const satisfies Record<PackageManager, (runDir: string) => Promise<PackageManagerInstallCommand | undefined>>;\n\nconst defaultOutputLimitBytes = 50 * 1024 * 1024;\nconst killGracePeriodMilliseconds = 1000;\n// Added to the watchdog's own deadline so its kill has time to land and `close` to arrive.\nconst watchdogSettleMarginMilliseconds = 2000;\nconst timeCommand = resolveTimeCommand();\n\n/**\n * Copies a submission directory to a temporary directory, overlays package\n * manager project files from the problem directory, prepares dependencies,\n * runs a command, and then removes the temporary directory.\n *\n * Under `EXERCODE_SANDBOX_USER` delegation, cleanup kills every process of the sandbox user, so do\n * not run multiple invocations concurrently in that mode — one finishing would kill the others.\n */\nexport async function runCommandInTemporaryPackageManagerProject(\n options: RunCommandInTemporaryPackageManagerProjectOptions\n): Promise<PackageManagerCommandRunResult> {\n const runDir = await fs.mkdtemp(path.join(os.tmpdir(), options.tempDirPrefix ?? 'exercode-'));\n try {\n await fs.cp(options.cwd, runDir, { recursive: true });\n await copyPackageManagerProjectFiles({\n packageManager: options.packageManager,\n projectDir: options.projectDir,\n runDir,\n projectFilePaths: options.projectFilePaths,\n });\n // The sandbox user (if any) runs the install/run commands below and must read the copied\n // sources and create outputs (e.g. `node_modules`, `.venv`) next to them.\n makeAccessibleToSandboxUser(runDir);\n\n const env = options.env ? { ...process.env, ...options.env } : process.env;\n const installCommand =\n options.prepareDependencies === false ? undefined : await resolveInstallCommand(options.packageManager, runDir);\n const command = typeof options.command === 'function' ? options.command({ runDir }) : options.command;\n const startedAt = Date.now();\n const outputLimitBytes = options.outputLimitBytes ?? defaultOutputLimitBytes;\n let installResult: Awaited<ReturnType<typeof spawnWithInput>> | undefined;\n\n if (installCommand) {\n installResult = await spawnWithInput(installCommand, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: '',\n timeLimitSeconds: options.timeLimitSeconds,\n });\n if (isFailedSpawnResult(installResult)) {\n return toPackageManagerCommandRunResult({\n elapsedTimeSeconds: (Date.now() - startedAt) / 1000,\n options,\n result: installResult,\n });\n }\n }\n\n const remainingTimeLimitSeconds = options.timeLimitSeconds - (Date.now() - startedAt) / 1000;\n if (remainingTimeLimitSeconds <= 0) {\n return {\n stdin: options.stdin ?? '',\n stdout: installResult?.stdout ?? '',\n stderr: installResult?.stderr ?? '',\n status: 0,\n timeSeconds: options.timeLimitSeconds + 1e-3,\n memoryBytes: installResult?.memoryBytes ?? 0,\n timedOut: true,\n signal: installResult?.signal,\n outputLimitExceeded: false,\n };\n }\n\n const result = await spawnWithInput(command, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: options.stdin ?? '',\n timeLimitSeconds: remainingTimeLimitSeconds,\n });\n const elapsedTimeSeconds = (Date.now() - startedAt) / 1000;\n\n if (installResult) {\n return toPackageManagerCommandRunResult({\n elapsedTimeSeconds,\n options,\n result: {\n ...result,\n timeSeconds: installResult.timeSeconds + result.timeSeconds,\n memoryBytes: Math.max(installResult.memoryBytes, result.memoryBytes),\n },\n });\n }\n\n return toPackageManagerCommandRunResult({ elapsedTimeSeconds, options, result });\n } finally {\n try {\n // Daemonized children of the sandboxed command would otherwise outlive the run.\n if (sandboxUserName) killSandboxUserProcesses();\n } finally {\n // Must run even when the sweep fails closed, or the temporary submission copy leaks.\n await forceRemove(runDir);\n }\n }\n}\n\nfunction toPackageManagerCommandRunResult(context: {\n elapsedTimeSeconds: number;\n options: RunCommandInTemporaryPackageManagerProjectOptions;\n result: Awaited<ReturnType<typeof spawnWithInput>>;\n}): PackageManagerCommandRunResult {\n return {\n stdin: context.options.stdin ?? '',\n stdout: context.result.stdout,\n stderr: context.result.stderr,\n status: context.result.timedOut || context.result.outputLimitExceeded ? 0 : context.result.status,\n timeSeconds: context.result.timedOut\n ? context.options.timeLimitSeconds + 1e-3\n : context.result.timeSeconds || context.elapsedTimeSeconds,\n memoryBytes: context.result.memoryBytes,\n timedOut: context.result.timedOut,\n signal: context.result.signal,\n outputLimitExceeded: context.result.outputLimitExceeded,\n };\n}\n\nfunction resolveInstallCommand(\n packageManager: PackageManager,\n runDir: string\n): Promise<PackageManagerInstallCommand | undefined> {\n return packageManagerInstallCommandResolvers[packageManager](runDir);\n}\n\nfunction isFailedSpawnResult(result: Awaited<ReturnType<typeof spawnWithInput>>): boolean {\n return result.status !== 0 || result.timedOut || result.outputLimitExceeded;\n}\n\nasync function resolveBunInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n // Bun supports --silent and it keeps successful preparation output out of judge output buffers.\n return (await hasAnyPath(runDir, ['bun.lock', 'bun.lockb']))\n ? ['bun', 'install', '--frozen-lockfile', '--silent']\n : ['bun', 'install', '--silent'];\n}\n\nasync function resolveCargoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Cargo.toml')))) return undefined;\n return (await pathExists(path.join(runDir, 'Cargo.lock'))) ? ['cargo', 'fetch', '--locked'] : ['cargo', 'fetch'];\n}\n\nasync function resolveGoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'go.mod')))) return undefined;\n return ['go', 'mod', 'download'];\n}\n\nasync function resolveGradleInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (\n !(await hasAnyPath(runDir, [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradlew',\n 'gradlew.bat',\n ]))\n )\n return undefined;\n const args = ['--no-daemon', '--quiet', 'dependencies'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'gradlew.bat')))\n ? ['cmd.exe', '/c', 'gradlew.bat', ...args]\n : ['gradle', ...args];\n }\n return (await pathExists(path.join(runDir, 'gradlew'))) ? ['sh', './gradlew', ...args] : ['gradle', ...args];\n}\n\nasync function resolveMavenInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'pom.xml')))) return undefined;\n const args = ['-q', 'dependency:go-offline'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'mvnw.cmd')))\n ? ['cmd.exe', '/c', 'mvnw.cmd', ...args]\n : ['mvn', ...args];\n }\n return (await pathExists(path.join(runDir, 'mvnw'))) ? ['sh', './mvnw', ...args] : ['mvn', ...args];\n}\n\nasync function resolveNpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'package-lock.json')))\n ? ['npm', 'ci', '--silent']\n : ['npm', 'install', '--silent'];\n}\n\nasync function resolvePnpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'pnpm-lock.yaml')))\n ? ['pnpm', 'install', '--frozen-lockfile', '--silent']\n : ['pnpm', 'install', '--silent'];\n}\n\nasync function resolveRubyInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Gemfile')))) return undefined;\n return (await pathExists(path.join(runDir, 'Gemfile.lock')))\n ? ['bundle', 'install', '--frozen', '--quiet']\n : ['bundle', 'install', '--quiet'];\n}\n\nasync function resolveUvInstallCommand(): Promise<undefined> {\n return undefined;\n}\n\nasync function resolveYarnInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n const isBerry = await isYarnBerryProject(runDir);\n const hasLockfile = await pathExists(path.join(runDir, 'yarn.lock'));\n if (isBerry) return hasLockfile ? ['yarn', 'install', '--immutable'] : ['yarn', 'install'];\n return hasLockfile ? ['yarn', 'install', '--frozen-lockfile', '--silent'] : ['yarn', 'install', '--silent'];\n}\n\nasync function isYarnBerryProject(runDir: string): Promise<boolean> {\n if (await pathExists(path.join(runDir, '.yarnrc.yml'))) return true;\n\n const packageJson = await readJson(path.join(runDir, 'package.json'));\n const packageManager = typeof packageJson.packageManager === 'string' ? packageJson.packageManager : undefined;\n const yarnMajorVersion = /^yarn@(\\d+)/.exec(packageManager ?? '')?.[1];\n return yarnMajorVersion !== undefined && Number(yarnMajorVersion) >= 2;\n}\n\nasync function hasAnyPath(directoryPath: string, relativePaths: readonly string[]): Promise<boolean> {\n for (const relativePath of relativePaths) {\n if (await pathExists(path.join(directoryPath, relativePath))) return true;\n }\n return false;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ENOENT') throw error;\n return false;\n }\n}\n\nasync function readJson(filePath: string): Promise<Record<string, unknown>> {\n try {\n const parsed = JSON.parse(await fs.readFile(filePath, 'utf8')) as unknown;\n return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch (error) {\n if (error instanceof SyntaxError) return {};\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code === 'ENOENT') return {};\n throw error;\n }\n}\n\nexport async function copyPackageManagerProjectFiles(options: {\n packageManager: PackageManager;\n projectDir: string;\n runDir: string;\n projectFilePaths?: readonly string[];\n}): Promise<void> {\n for (const projectFilePath of options.projectFilePaths ?? packageManagerProjectFilePaths[options.packageManager]) {\n await copyPathIfExists(\n path.join(options.projectDir, projectFilePath),\n path.join(options.runDir, projectFilePath),\n options.runDir\n );\n }\n}\n\nasync function copyPathIfExists(sourcePath: string, destinationPath: string, runDir: string): Promise<void> {\n try {\n // Before touching the destination: creating the parent levels replaces whatever the submission\n // put there, which must not happen for a project file the problem does not even ship.\n await fs.lstat(sourcePath);\n // No-follow copy: the destination was seeded from the (sandbox-writable) submission tree, so a\n // planted symlink there — at the destination itself or at any directory level of a nested\n // project file path — must not redirect this trusted project-file overlay outside runDir.\n await createDirectoryWithoutFollowingSymlinks(runDir, path.dirname(destinationPath));\n await copyWithoutFollowingSymlinks(sourcePath, destinationPath);\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ENOENT') throw error;\n }\n}\n\nasync function spawnWithInput(\n command: readonly [string, ...string[]],\n context: {\n cwd: string;\n env: NodeJS.ProcessEnv;\n outputLimitBytes: number;\n stdin: string;\n timeLimitSeconds: number;\n }\n): Promise<{\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n timedOut: boolean;\n signal: NodeJS.Signals | undefined;\n outputLimitExceeded: boolean;\n}> {\n const timeOutputPath = timeCommand === undefined ? undefined : path.join(context.cwd, '.exercode-time-result');\n // `wrapCommandWithSandboxUser` is idempotent, so a command the presets already wrapped is not\n // nested; `time` is then spliced INSIDE that wrapper so it runs as the sandbox user, never as the\n // harness user writing `--output` into this sandbox-writable directory.\n const wrappedCommand = wrapCommandWithSandboxUser(command);\n const spawnedCommand =\n timeCommand === undefined\n ? wrappedCommand\n : prependInsideSandboxWrapper(wrappedCommand, [...timeCommand, `--output=${timeOutputPath}`]);\n // Started BEFORE the submission: `killSubprocessGroup` cannot spawn its `sudo` once a submission\n // has exhausted the PID cgroup, and a submission spawned first could exhaust it before this\n // watchdog exists. It is the deadline of last resort on this path.\n const watchdog = startSandboxTimeoutWatchdog(context.timeLimitSeconds);\n const watchdogDeadlineAt = Date.now() + (Math.ceil(context.timeLimitSeconds) + SANDBOX_WATCHDOG_GRACE_SECONDS) * 1000;\n let subprocess: childProcess.ChildProcessWithoutNullStreams;\n try {\n subprocess = childProcess.spawn(spawnedCommand[0], spawnedCommand.slice(1), {\n cwd: context.cwd,\n detached: process.platform !== 'win32',\n env: { ...context.env, ...getSandboxUserEnvOverrides(context.env) },\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n } catch (error) {\n watchdog.cancel();\n throw error;\n }\n\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let outputBytes = 0;\n let timedOut = false;\n let outputLimitExceeded = false;\n // Set when terminating the submission failed (the cleanup `sudo` could not be spawned). The run\n // then waits for the watchdog to end the submission rather than throwing out of an EventEmitter\n // callback, which would be an uncaught exception that kills the harness before any cleanup.\n let killError: Error | undefined;\n let onKillFailure: (() => void) | undefined;\n const killOrRecordFailure = (signal: NodeJS.Signals): void => {\n try {\n killSubprocessGroup(subprocess, signal);\n } catch (error) {\n killError ??= error instanceof Error ? error : new Error(String(error));\n onKillFailure?.();\n }\n };\n\n const appendOutputChunk = (chunks: Buffer[], chunk: Buffer): void => {\n if (outputBytes >= context.outputLimitBytes) {\n if (chunk.byteLength > 0) {\n outputLimitExceeded = true;\n killOrRecordFailure('SIGKILL');\n }\n return;\n }\n\n const remainingBytes = context.outputLimitBytes - outputBytes;\n const appendedChunk = chunk.byteLength > remainingBytes ? chunk.subarray(0, remainingBytes) : chunk;\n chunks.push(appendedChunk);\n outputBytes += appendedChunk.byteLength;\n\n if (chunk.byteLength > remainingBytes) {\n outputLimitExceeded = true;\n killOrRecordFailure('SIGKILL');\n }\n };\n\n subprocess.stdout.on('data', (chunk: Buffer) => appendOutputChunk(stdoutChunks, chunk));\n subprocess.stderr.on('data', (chunk: Buffer) => appendOutputChunk(stderrChunks, chunk));\n\n const timeout = setTimeout(() => {\n timedOut = true;\n killOrRecordFailure('SIGTERM');\n }, context.timeLimitSeconds * 1000);\n const killTimeout = setTimeout(\n () => {\n if (timedOut) killOrRecordFailure('SIGKILL');\n },\n context.timeLimitSeconds * 1000 + killGracePeriodMilliseconds\n );\n killTimeout.unref();\n // Bounds the wait after a kill failure: the watchdog force-kills the sandbox user at its own\n // deadline, so `close` should arrive; if even that fails, give up rather than hang.\n let killFailureTimeout: ReturnType<typeof setTimeout> | undefined;\n let closeObserved = false;\n\n const { status, signal } = await new Promise<{ status: number | undefined; signal: NodeJS.Signals | undefined }>(\n (resolve, reject) => {\n let settled = false;\n let pendingError: Error | undefined;\n const failAfterClose = (error: Error): void => {\n if (settled) return;\n pendingError = error;\n killOrRecordFailure('SIGKILL');\n if (subprocess.pid === undefined) {\n settled = true;\n reject(error);\n }\n };\n // Terminating the submission failed. Keep waiting: the watchdog (started before the\n // submission) force-kills the sandbox user at its own deadline, so `close` still arrives and\n // the run reports its real verdict. Give up only once that deadline has passed without the\n // submission ending, so the run can neither hang nor abandon a still-running submission\n // while its watchdog could still fire.\n onKillFailure = () => {\n killFailureTimeout ??= setTimeout(\n () => {\n if (settled) return;\n settled = true;\n reject(killError ?? new Error('failed to terminate the submission'));\n },\n Math.max(0, watchdogDeadlineAt - Date.now()) + watchdogSettleMarginMilliseconds\n );\n };\n subprocess.on('error', failAfterClose);\n subprocess.stdin.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code !== 'EPIPE') failAfterClose(error);\n });\n subprocess.on('close', (code, closeSignal) => {\n if (settled) return;\n settled = true;\n // The submission ended, so the watchdog has nothing left to kill and must not outlive this\n // run. A kill that failed once but succeeded on retry (or was superseded by the watchdog)\n // is not an error: the timeout/output-limit verdict below is the correct one to report.\n closeObserved = true;\n if (pendingError) {\n reject(pendingError);\n return;\n }\n resolve({ status: code ?? undefined, signal: closeSignal ?? undefined });\n });\n subprocess.stdin.end(context.stdin);\n }\n ).finally(() => {\n clearTimeout(timeout);\n clearTimeout(killTimeout);\n if (killFailureTimeout) clearTimeout(killFailureTimeout);\n // Cancel unless the submission is still running after a failed kill: the watchdog is then the\n // only remaining mechanism able to stop it, and cancelling would leave it running unchecked.\n if (closeObserved || !killError) watchdog.cancel();\n });\n\n const { timeSeconds, memoryBytes } =\n timeOutputPath === undefined ? { timeSeconds: 0, memoryBytes: 0 } : await readTimeResult(timeOutputPath);\n\n return {\n stdout: Buffer.concat(stdoutChunks).toString(),\n stderr: Buffer.concat(stderrChunks).toString(),\n status,\n timeSeconds,\n memoryBytes,\n timedOut,\n signal,\n outputLimitExceeded,\n };\n}\n\nfunction resolveTimeCommand(): readonly [string, ...string[]] | undefined {\n const command = os.platform() === 'darwin' ? 'gtime' : '/usr/bin/time';\n const result = childProcess.spawnSync(command, ['--version'], { stdio: 'ignore' });\n if (result.error || result.status !== 0) return undefined;\n\n return [command, '--format', '%e %M'];\n}\n\nfunction killSubprocessGroup(subprocess: childProcess.ChildProcess, signal: NodeJS.Signals): void {\n if (subprocess.pid === undefined) return;\n\n // The current user cannot signal the root-owned sudo wrapper nor the sandbox user's processes,\n // so go through sudo with the requested signal only; the callers' existing timers provide the\n // TERM → grace → KILL escalation.\n // Throws when the cleanup `sudo` cannot even be spawned (a submission can exhaust the PID\n // cgroup). Only `killOrRecordFailure` may call this: it records the failure and lets the\n // watchdog (or, past its deadline, a bounded timer) settle the run instead of letting the throw\n // escape an EventEmitter callback.\n if (sandboxUserName) {\n killSandboxUserProcesses([signal === 'SIGKILL' ? 'KILL' : 'TERM']);\n return;\n }\n\n try {\n if (process.platform === 'win32') {\n subprocess.kill(signal);\n return;\n }\n process.kill(-subprocess.pid, signal);\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ESRCH' && code !== 'EPERM') throw error;\n }\n}\n\nfunction isErrorWithCode(error: unknown, code: string): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && (error as { code: unknown }).code === code;\n}\n\nasync function readTimeResult(timeOutputPath: string): Promise<{ timeSeconds: number; memoryBytes: number }> {\n // The submission owns this directory and can put anything at this path, so decide what was\n // opened from the handle itself rather than from a prior `lstat` it could race:\n // - `O_NOFOLLOW` refuses a symlink to a harness-readable file (e.g. the problem's expected\n // outputs), which would otherwise report that file's trailing numbers as the submission's own\n // time and memory usage;\n // - `O_NONBLOCK` keeps a planted FIFO from blocking the harness forever waiting for a writer;\n // - the `fstat` then rejects anything that is not a regular file.\n //\n // Only an absent file means \"no measurement\" (the command was killed before `time` wrote one).\n // Anything else at this path is the submission tampering with its own accounting — reporting zero\n // memory would let it slip past a memory limit — so that fails the run instead.\n let content: string;\n let fileHandle: Awaited<ReturnType<typeof fs.open>> | undefined;\n try {\n fileHandle = await fs.open(\n timeOutputPath,\n nodeFs.constants.O_RDONLY | nodeFs.constants.O_NOFOLLOW | nodeFs.constants.O_NONBLOCK\n );\n const stats = await fileHandle.stat();\n if (!stats.isFile()) throw new Error(`${timeOutputPath} is not a regular file`);\n content = await fileHandle.readFile('utf8');\n } catch (error) {\n if (isErrorWithCode(error, 'ENOENT')) return { timeSeconds: 0, memoryBytes: 0 };\n throw new Error(`failed to read the time measurement at ${timeOutputPath}`, { cause: error });\n } finally {\n await fileHandle?.close();\n }\n\n const match = /(\\d+(?:[.,]\\d+)?) (\\d+)\\s*$/.exec(content);\n if (!match) return { timeSeconds: 0, memoryBytes: 0 };\n\n return { timeSeconds: Number(match[1]!.replace(',', '.')), memoryBytes: Number(match[2]) * 1024 };\n}\n"],"mappings":"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"}
|
|
@@ -17,6 +17,11 @@ export interface RunCommandInTemporaryPackageManagerProjectOptions {
|
|
|
17
17
|
command: readonly [string, ...string[]] | ((context: {
|
|
18
18
|
runDir: string;
|
|
19
19
|
}) => readonly [string, ...string[]]);
|
|
20
|
+
/**
|
|
21
|
+
* Set to false when `command` prepares the dependencies it needs.
|
|
22
|
+
* Defaults to true.
|
|
23
|
+
*/
|
|
24
|
+
prepareDependencies?: boolean;
|
|
20
25
|
stdin?: string;
|
|
21
26
|
env?: NodeJS.ProcessEnv;
|
|
22
27
|
timeLimitSeconds: number;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{forceRemove as e,getSandboxUserEnvOverrides as t,killSandboxUserProcesses as n,makeAccessibleToSandboxUser as r,prependInsideSandboxWrapper as i,sandboxUserName as a,startSandboxTimeoutWatchdog as o,wrapCommandWithSandboxUser as s}from"./sandboxUser.js";import{copyWithoutFollowingSymlinks as c,createDirectoryWithoutFollowingSymlinks as l}from"./safeFs.js";import u from"node:fs/promises";import d from"node:path";import f from"node:child_process";import p from"node:fs";import m from"node:os";const h={bun:[`package.json`,`bun.lock`,`bun.lockb`],cargo:[`Cargo.toml`,`Cargo.lock`],go:[`go.mod`,`go.sum`],gradle:[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradle.properties`,`gradle.lockfile`,`buildscript-gradle.lockfile`,`gradle`,`gradlew`,`gradlew.bat`],maven:[`pom.xml`,`.mvn`,`mvnw`,`mvnw.cmd`],npm:[`package.json`,`package-lock.json`],pnpm:[`package.json`,`pnpm-lock.yaml`,`pnpm-workspace.yaml`],ruby:[`Gemfile`,`Gemfile.lock`,`.ruby-version`],uv:[`pyproject.toml`,`uv.lock`],yarn:[`package.json`,`yarn.lock`,`.yarnrc`,`.yarnrc.yml`,`.yarn`]},g={bun:S,cargo:C,go:w,gradle:T,maven:E,npm:D,pnpm:O,ruby:k,uv:A,yarn:j},_=z();async function v(t){let i=await u.mkdtemp(d.join(m.tmpdir(),t.tempDirPrefix??`exercode-`));try{await u.cp(t.cwd,i,{recursive:!0}),await I({packageManager:t.packageManager,projectDir:t.projectDir,runDir:i,projectFilePaths:t.projectFilePaths}),r(i);let e=t.env?{...process.env,...t.env}:process.env,n=await b(t.packageManager,i),a=typeof t.command==`function`?t.command({runDir:i}):t.command,o=Date.now(),s=t.outputLimitBytes??52428800,c;if(n&&(c=await R(n,{cwd:i,env:e,outputLimitBytes:s,stdin:``,timeLimitSeconds:t.timeLimitSeconds}),x(c)))return y({elapsedTimeSeconds:(Date.now()-o)/1e3,options:t,result:c});let l=t.timeLimitSeconds-(Date.now()-o)/1e3;if(l<=0)return{stdin:t.stdin??``,stdout:c?.stdout??``,stderr:c?.stderr??``,status:0,timeSeconds:t.timeLimitSeconds+.001,memoryBytes:c?.memoryBytes??0,timedOut:!0,signal:c?.signal,outputLimitExceeded:!1};let d=await R(a,{cwd:i,env:e,outputLimitBytes:s,stdin:t.stdin??``,timeLimitSeconds:l}),f=(Date.now()-o)/1e3;return y(c?{elapsedTimeSeconds:f,options:t,result:{...d,timeSeconds:c.timeSeconds+d.timeSeconds,memoryBytes:Math.max(c.memoryBytes,d.memoryBytes)}}:{elapsedTimeSeconds:f,options:t,result:d})}finally{try{a&&n()}finally{await e(i)}}}function y(e){return{stdin:e.options.stdin??``,stdout:e.result.stdout,stderr:e.result.stderr,status:e.result.timedOut||e.result.outputLimitExceeded?0:e.result.status,timeSeconds:e.result.timedOut?e.options.timeLimitSeconds+.001:e.result.timeSeconds||e.elapsedTimeSeconds,memoryBytes:e.result.memoryBytes,timedOut:e.result.timedOut,signal:e.result.signal,outputLimitExceeded:e.result.outputLimitExceeded}}function b(e,t){return g[e](t)}function x(e){return e.status!==0||e.timedOut||e.outputLimitExceeded}async function S(e){if(await P(d.join(e,`package.json`)))return await N(e,[`bun.lock`,`bun.lockb`])?[`bun`,`install`,`--frozen-lockfile`,`--silent`]:[`bun`,`install`,`--silent`]}async function C(e){if(await P(d.join(e,`Cargo.toml`)))return await P(d.join(e,`Cargo.lock`))?[`cargo`,`fetch`,`--locked`]:[`cargo`,`fetch`]}async function w(e){if(await P(d.join(e,`go.mod`)))return[`go`,`mod`,`download`]}async function T(e){if(!await N(e,[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradlew`,`gradlew.bat`]))return;let t=[`--no-daemon`,`--quiet`,`dependencies`];return process.platform===`win32`?await P(d.join(e,`gradlew.bat`))?[`cmd.exe`,`/c`,`gradlew.bat`,...t]:[`gradle`,...t]:await P(d.join(e,`gradlew`))?[`sh`,`./gradlew`,...t]:[`gradle`,...t]}async function E(e){if(!await P(d.join(e,`pom.xml`)))return;let t=[`-q`,`dependency:go-offline`];return process.platform===`win32`?await P(d.join(e,`mvnw.cmd`))?[`cmd.exe`,`/c`,`mvnw.cmd`,...t]:[`mvn`,...t]:await P(d.join(e,`mvnw`))?[`sh`,`./mvnw`,...t]:[`mvn`,...t]}async function D(e){if(await P(d.join(e,`package.json`)))return await P(d.join(e,`package-lock.json`))?[`npm`,`ci`,`--silent`]:[`npm`,`install`,`--silent`]}async function O(e){if(await P(d.join(e,`package.json`)))return await P(d.join(e,`pnpm-lock.yaml`))?[`pnpm`,`install`,`--frozen-lockfile`,`--silent`]:[`pnpm`,`install`,`--silent`]}async function k(e){if(await P(d.join(e,`Gemfile`)))return await P(d.join(e,`Gemfile.lock`))?[`bundle`,`install`,`--frozen`,`--quiet`]:[`bundle`,`install`,`--quiet`]}async function A(){}async function j(e){if(!await P(d.join(e,`package.json`)))return;let t=await M(e),n=await P(d.join(e,`yarn.lock`));return t?n?[`yarn`,`install`,`--immutable`]:[`yarn`,`install`]:n?[`yarn`,`install`,`--frozen-lockfile`,`--silent`]:[`yarn`,`install`,`--silent`]}async function M(e){if(await P(d.join(e,`.yarnrc.yml`)))return!0;let t=await F(d.join(e,`package.json`)),n=typeof t.packageManager==`string`?t.packageManager:void 0,r=/^yarn@(\d+)/.exec(n??``)?.[1];return r!==void 0&&Number(r)>=2}async function N(e,t){for(let n of t)if(await P(d.join(e,n)))return!0;return!1}async function P(e){try{return await u.access(e),!0}catch(e){if((typeof e==`object`&&e&&`code`in e?e.code:void 0)!==`ENOENT`)throw e;return!1}}async function F(e){try{let t=JSON.parse(await u.readFile(e,`utf8`));return typeof t==`object`&&t&&!Array.isArray(t)?t:{}}catch(e){if(e instanceof SyntaxError||(typeof e==`object`&&e&&`code`in e?e.code:void 0)===`ENOENT`)return{};throw e}}async function I(e){for(let t of e.projectFilePaths??h[e.packageManager])await L(d.join(e.projectDir,t),d.join(e.runDir,t),e.runDir)}async function L(e,t,n){try{await u.lstat(e),await l(n,d.dirname(t)),await c(e,t)}catch(e){if((typeof e==`object`&&e&&`code`in e?e.code:void 0)!==`ENOENT`)throw e}}async function R(e,n){let r=_===void 0?void 0:d.join(n.cwd,`.exercode-time-result`),a=s(e),c=_===void 0?a:i(a,[..._,`--output=${r}`]),l=o(n.timeLimitSeconds),u=Date.now()+(Math.ceil(n.timeLimitSeconds)+5)*1e3,p;try{p=f.spawn(c[0],c.slice(1),{cwd:n.cwd,detached:process.platform!==`win32`,env:{...n.env,...t(n.env)},stdio:[`pipe`,`pipe`,`pipe`]})}catch(e){throw l.cancel(),e}let m=[],h=[],g=0,v=!1,y=!1,b,x,S=e=>{try{B(p,e)}catch(e){b??=e instanceof Error?e:Error(String(e)),x?.()}},C=(e,t)=>{if(g>=n.outputLimitBytes){t.byteLength>0&&(y=!0,S(`SIGKILL`));return}let r=n.outputLimitBytes-g,i=t.byteLength>r?t.subarray(0,r):t;e.push(i),g+=i.byteLength,t.byteLength>r&&(y=!0,S(`SIGKILL`))};p.stdout.on(`data`,e=>C(m,e)),p.stderr.on(`data`,e=>C(h,e));let w=setTimeout(()=>{v=!0,S(`SIGTERM`)},n.timeLimitSeconds*1e3),T=setTimeout(()=>{v&&S(`SIGKILL`)},n.timeLimitSeconds*1e3+1e3);T.unref();let E,D=!1,{status:O,signal:k}=await new Promise((e,t)=>{let r=!1,i,a=e=>{r||(i=e,S(`SIGKILL`),p.pid===void 0&&(r=!0,t(e)))};x=()=>{E??=setTimeout(()=>{r||(r=!0,t(b??Error(`failed to terminate the submission`)))},Math.max(0,u-Date.now())+2e3)},p.on(`error`,a),p.stdin.on(`error`,e=>{e.code!==`EPIPE`&&a(e)}),p.on(`close`,(n,a)=>{if(!r){if(r=!0,D=!0,i){t(i);return}e({status:n??void 0,signal:a??void 0})}}),p.stdin.end(n.stdin)}).finally(()=>{clearTimeout(w),clearTimeout(T),E&&clearTimeout(E),(D||!b)&&l.cancel()}),{timeSeconds:A,memoryBytes:j}=r===void 0?{timeSeconds:0,memoryBytes:0}:await H(r);return{stdout:Buffer.concat(m).toString(),stderr:Buffer.concat(h).toString(),status:O,timeSeconds:A,memoryBytes:j,timedOut:v,signal:k,outputLimitExceeded:y}}function z(){let e=m.platform()===`darwin`?`gtime`:`/usr/bin/time`,t=f.spawnSync(e,[`--version`],{stdio:`ignore`});if(!(t.error||t.status!==0))return[e,`--format`,`%e %M`]}function B(e,t){if(e.pid!==void 0){if(a){n([t===`SIGKILL`?`KILL`:`TERM`]);return}try{if(process.platform===`win32`){e.kill(t);return}process.kill(-e.pid,t)}catch(e){let t=typeof e==`object`&&e&&`code`in e?e.code:void 0;if(t!==`ESRCH`&&t!==`EPERM`)throw e}}}function V(e,t){return typeof e==`object`&&!!e&&`code`in e&&e.code===t}async function H(e){let t,n;try{if(n=await u.open(e,p.constants.O_RDONLY|p.constants.O_NOFOLLOW|p.constants.O_NONBLOCK),!(await n.stat()).isFile())throw Error(`${e} is not a regular file`);t=await n.readFile(`utf8`)}catch(t){if(V(t,`ENOENT`))return{timeSeconds:0,memoryBytes:0};throw Error(`failed to read the time measurement at ${e}`,{cause:t})}finally{await n?.close()}let r=/(\d+(?:[.,]\d+)?) (\d+)\s*$/.exec(t);return r?{timeSeconds:Number(r[1].replace(`,`,`.`)),memoryBytes:Number(r[2])*1024}:{timeSeconds:0,memoryBytes:0}}export{I as copyPackageManagerProjectFiles,v as runCommandInTemporaryPackageManagerProject};
|
|
1
|
+
import{forceRemove as e,getSandboxUserEnvOverrides as t,killSandboxUserProcesses as n,makeAccessibleToSandboxUser as r,prependInsideSandboxWrapper as i,sandboxUserName as a,startSandboxTimeoutWatchdog as o,wrapCommandWithSandboxUser as s}from"./sandboxUser.js";import{copyWithoutFollowingSymlinks as c,createDirectoryWithoutFollowingSymlinks as l}from"./safeFs.js";import u from"node:fs/promises";import d from"node:path";import f from"node:child_process";import p from"node:fs";import m from"node:os";const h={bun:[`package.json`,`bun.lock`,`bun.lockb`],cargo:[`Cargo.toml`,`Cargo.lock`],go:[`go.mod`,`go.sum`],gradle:[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradle.properties`,`gradle.lockfile`,`buildscript-gradle.lockfile`,`gradle`,`gradlew`,`gradlew.bat`],maven:[`pom.xml`,`.mvn`,`mvnw`,`mvnw.cmd`],npm:[`package.json`,`package-lock.json`],pnpm:[`package.json`,`pnpm-lock.yaml`,`pnpm-workspace.yaml`],ruby:[`Gemfile`,`Gemfile.lock`,`.ruby-version`],uv:[`pyproject.toml`,`uv.lock`],yarn:[`package.json`,`yarn.lock`,`.yarnrc`,`.yarnrc.yml`,`.yarn`]},g={bun:S,cargo:C,go:w,gradle:T,maven:E,npm:D,pnpm:O,ruby:k,uv:A,yarn:j},_=z();async function v(t){let i=await u.mkdtemp(d.join(m.tmpdir(),t.tempDirPrefix??`exercode-`));try{await u.cp(t.cwd,i,{recursive:!0}),await I({packageManager:t.packageManager,projectDir:t.projectDir,runDir:i,projectFilePaths:t.projectFilePaths}),r(i);let e=t.env?{...process.env,...t.env}:process.env,n=t.prepareDependencies===!1?void 0:await b(t.packageManager,i),a=typeof t.command==`function`?t.command({runDir:i}):t.command,o=Date.now(),s=t.outputLimitBytes??52428800,c;if(n&&(c=await R(n,{cwd:i,env:e,outputLimitBytes:s,stdin:``,timeLimitSeconds:t.timeLimitSeconds}),x(c)))return y({elapsedTimeSeconds:(Date.now()-o)/1e3,options:t,result:c});let l=t.timeLimitSeconds-(Date.now()-o)/1e3;if(l<=0)return{stdin:t.stdin??``,stdout:c?.stdout??``,stderr:c?.stderr??``,status:0,timeSeconds:t.timeLimitSeconds+.001,memoryBytes:c?.memoryBytes??0,timedOut:!0,signal:c?.signal,outputLimitExceeded:!1};let d=await R(a,{cwd:i,env:e,outputLimitBytes:s,stdin:t.stdin??``,timeLimitSeconds:l}),f=(Date.now()-o)/1e3;return y(c?{elapsedTimeSeconds:f,options:t,result:{...d,timeSeconds:c.timeSeconds+d.timeSeconds,memoryBytes:Math.max(c.memoryBytes,d.memoryBytes)}}:{elapsedTimeSeconds:f,options:t,result:d})}finally{try{a&&n()}finally{await e(i)}}}function y(e){return{stdin:e.options.stdin??``,stdout:e.result.stdout,stderr:e.result.stderr,status:e.result.timedOut||e.result.outputLimitExceeded?0:e.result.status,timeSeconds:e.result.timedOut?e.options.timeLimitSeconds+.001:e.result.timeSeconds||e.elapsedTimeSeconds,memoryBytes:e.result.memoryBytes,timedOut:e.result.timedOut,signal:e.result.signal,outputLimitExceeded:e.result.outputLimitExceeded}}function b(e,t){return g[e](t)}function x(e){return e.status!==0||e.timedOut||e.outputLimitExceeded}async function S(e){if(await P(d.join(e,`package.json`)))return await N(e,[`bun.lock`,`bun.lockb`])?[`bun`,`install`,`--frozen-lockfile`,`--silent`]:[`bun`,`install`,`--silent`]}async function C(e){if(await P(d.join(e,`Cargo.toml`)))return await P(d.join(e,`Cargo.lock`))?[`cargo`,`fetch`,`--locked`]:[`cargo`,`fetch`]}async function w(e){if(await P(d.join(e,`go.mod`)))return[`go`,`mod`,`download`]}async function T(e){if(!await N(e,[`build.gradle`,`build.gradle.kts`,`settings.gradle`,`settings.gradle.kts`,`gradlew`,`gradlew.bat`]))return;let t=[`--no-daemon`,`--quiet`,`dependencies`];return process.platform===`win32`?await P(d.join(e,`gradlew.bat`))?[`cmd.exe`,`/c`,`gradlew.bat`,...t]:[`gradle`,...t]:await P(d.join(e,`gradlew`))?[`sh`,`./gradlew`,...t]:[`gradle`,...t]}async function E(e){if(!await P(d.join(e,`pom.xml`)))return;let t=[`-q`,`dependency:go-offline`];return process.platform===`win32`?await P(d.join(e,`mvnw.cmd`))?[`cmd.exe`,`/c`,`mvnw.cmd`,...t]:[`mvn`,...t]:await P(d.join(e,`mvnw`))?[`sh`,`./mvnw`,...t]:[`mvn`,...t]}async function D(e){if(await P(d.join(e,`package.json`)))return await P(d.join(e,`package-lock.json`))?[`npm`,`ci`,`--silent`]:[`npm`,`install`,`--silent`]}async function O(e){if(await P(d.join(e,`package.json`)))return await P(d.join(e,`pnpm-lock.yaml`))?[`pnpm`,`install`,`--frozen-lockfile`,`--silent`]:[`pnpm`,`install`,`--silent`]}async function k(e){if(await P(d.join(e,`Gemfile`)))return await P(d.join(e,`Gemfile.lock`))?[`bundle`,`install`,`--frozen`,`--quiet`]:[`bundle`,`install`,`--quiet`]}async function A(){}async function j(e){if(!await P(d.join(e,`package.json`)))return;let t=await M(e),n=await P(d.join(e,`yarn.lock`));return t?n?[`yarn`,`install`,`--immutable`]:[`yarn`,`install`]:n?[`yarn`,`install`,`--frozen-lockfile`,`--silent`]:[`yarn`,`install`,`--silent`]}async function M(e){if(await P(d.join(e,`.yarnrc.yml`)))return!0;let t=await F(d.join(e,`package.json`)),n=typeof t.packageManager==`string`?t.packageManager:void 0,r=/^yarn@(\d+)/.exec(n??``)?.[1];return r!==void 0&&Number(r)>=2}async function N(e,t){for(let n of t)if(await P(d.join(e,n)))return!0;return!1}async function P(e){try{return await u.access(e),!0}catch(e){if((typeof e==`object`&&e&&`code`in e?e.code:void 0)!==`ENOENT`)throw e;return!1}}async function F(e){try{let t=JSON.parse(await u.readFile(e,`utf8`));return typeof t==`object`&&t&&!Array.isArray(t)?t:{}}catch(e){if(e instanceof SyntaxError||(typeof e==`object`&&e&&`code`in e?e.code:void 0)===`ENOENT`)return{};throw e}}async function I(e){for(let t of e.projectFilePaths??h[e.packageManager])await L(d.join(e.projectDir,t),d.join(e.runDir,t),e.runDir)}async function L(e,t,n){try{await u.lstat(e),await l(n,d.dirname(t)),await c(e,t)}catch(e){if((typeof e==`object`&&e&&`code`in e?e.code:void 0)!==`ENOENT`)throw e}}async function R(e,n){let r=_===void 0?void 0:d.join(n.cwd,`.exercode-time-result`),a=s(e),c=_===void 0?a:i(a,[..._,`--output=${r}`]),l=o(n.timeLimitSeconds),u=Date.now()+(Math.ceil(n.timeLimitSeconds)+5)*1e3,p;try{p=f.spawn(c[0],c.slice(1),{cwd:n.cwd,detached:process.platform!==`win32`,env:{...n.env,...t(n.env)},stdio:[`pipe`,`pipe`,`pipe`]})}catch(e){throw l.cancel(),e}let m=[],h=[],g=0,v=!1,y=!1,b,x,S=e=>{try{B(p,e)}catch(e){b??=e instanceof Error?e:Error(String(e)),x?.()}},C=(e,t)=>{if(g>=n.outputLimitBytes){t.byteLength>0&&(y=!0,S(`SIGKILL`));return}let r=n.outputLimitBytes-g,i=t.byteLength>r?t.subarray(0,r):t;e.push(i),g+=i.byteLength,t.byteLength>r&&(y=!0,S(`SIGKILL`))};p.stdout.on(`data`,e=>C(m,e)),p.stderr.on(`data`,e=>C(h,e));let w=setTimeout(()=>{v=!0,S(`SIGTERM`)},n.timeLimitSeconds*1e3),T=setTimeout(()=>{v&&S(`SIGKILL`)},n.timeLimitSeconds*1e3+1e3);T.unref();let E,D=!1,{status:O,signal:k}=await new Promise((e,t)=>{let r=!1,i,a=e=>{r||(i=e,S(`SIGKILL`),p.pid===void 0&&(r=!0,t(e)))};x=()=>{E??=setTimeout(()=>{r||(r=!0,t(b??Error(`failed to terminate the submission`)))},Math.max(0,u-Date.now())+2e3)},p.on(`error`,a),p.stdin.on(`error`,e=>{e.code!==`EPIPE`&&a(e)}),p.on(`close`,(n,a)=>{if(!r){if(r=!0,D=!0,i){t(i);return}e({status:n??void 0,signal:a??void 0})}}),p.stdin.end(n.stdin)}).finally(()=>{clearTimeout(w),clearTimeout(T),E&&clearTimeout(E),(D||!b)&&l.cancel()}),{timeSeconds:A,memoryBytes:j}=r===void 0?{timeSeconds:0,memoryBytes:0}:await H(r);return{stdout:Buffer.concat(m).toString(),stderr:Buffer.concat(h).toString(),status:O,timeSeconds:A,memoryBytes:j,timedOut:v,signal:k,outputLimitExceeded:y}}function z(){let e=m.platform()===`darwin`?`gtime`:`/usr/bin/time`,t=f.spawnSync(e,[`--version`],{stdio:`ignore`});if(!(t.error||t.status!==0))return[e,`--format`,`%e %M`]}function B(e,t){if(e.pid!==void 0){if(a){n([t===`SIGKILL`?`KILL`:`TERM`]);return}try{if(process.platform===`win32`){e.kill(t);return}process.kill(-e.pid,t)}catch(e){let t=typeof e==`object`&&e&&`code`in e?e.code:void 0;if(t!==`ESRCH`&&t!==`EPERM`)throw e}}}function V(e,t){return typeof e==`object`&&!!e&&`code`in e&&e.code===t}async function H(e){let t,n;try{if(n=await u.open(e,p.constants.O_RDONLY|p.constants.O_NOFOLLOW|p.constants.O_NONBLOCK),!(await n.stat()).isFile())throw Error(`${e} is not a regular file`);t=await n.readFile(`utf8`)}catch(t){if(V(t,`ENOENT`))return{timeSeconds:0,memoryBytes:0};throw Error(`failed to read the time measurement at ${e}`,{cause:t})}finally{await n?.close()}let r=/(\d+(?:[.,]\d+)?) (\d+)\s*$/.exec(t);return r?{timeSeconds:Number(r[1].replace(`,`,`.`)),memoryBytes:Number(r[2])*1024}:{timeSeconds:0,memoryBytes:0}}export{I as copyPackageManagerProjectFiles,v as runCommandInTemporaryPackageManagerProject};
|
|
2
2
|
//# sourceMappingURL=runCommandInTemporaryPackageManagerProject.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runCommandInTemporaryPackageManagerProject.js","names":["childProcess","nodeFs"],"sources":["../../src/helpers/runCommandInTemporaryPackageManagerProject.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport nodeFs from 'node:fs';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport {\n forceRemove,\n getSandboxUserEnvOverrides,\n killSandboxUserProcesses,\n makeAccessibleToSandboxUser,\n prependInsideSandboxWrapper,\n SANDBOX_WATCHDOG_GRACE_SECONDS,\n sandboxUserName,\n startSandboxTimeoutWatchdog,\n wrapCommandWithSandboxUser,\n} from './sandboxUser.js';\nimport { copyWithoutFollowingSymlinks, createDirectoryWithoutFollowingSymlinks } from './safeFs.js';\n\nexport type PackageManager = 'bun' | 'cargo' | 'go' | 'gradle' | 'maven' | 'npm' | 'pnpm' | 'ruby' | 'uv' | 'yarn';\ntype PackageManagerInstallCommand = readonly [string, ...string[]];\n\nexport interface PackageManagerCommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n timedOut: boolean;\n signal: NodeJS.Signals | undefined;\n outputLimitExceeded: boolean;\n}\n\nexport interface RunCommandInTemporaryPackageManagerProjectOptions {\n cwd: string;\n projectDir: string;\n packageManager: PackageManager;\n command: readonly [string, ...string[]] | ((context: { runDir: string }) => readonly [string, ...string[]]);\n stdin?: string;\n env?: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n outputLimitBytes?: number;\n tempDirPrefix?: string;\n projectFilePaths?: readonly string[];\n}\n\nconst packageManagerProjectFilePaths = {\n bun: ['package.json', 'bun.lock', 'bun.lockb'],\n cargo: ['Cargo.toml', 'Cargo.lock'],\n go: ['go.mod', 'go.sum'],\n gradle: [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradle.properties',\n 'gradle.lockfile',\n 'buildscript-gradle.lockfile',\n 'gradle',\n 'gradlew',\n 'gradlew.bat',\n ],\n maven: ['pom.xml', '.mvn', 'mvnw', 'mvnw.cmd'],\n npm: ['package.json', 'package-lock.json'],\n pnpm: ['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml'],\n ruby: ['Gemfile', 'Gemfile.lock', '.ruby-version'],\n uv: ['pyproject.toml', 'uv.lock'],\n yarn: ['package.json', 'yarn.lock', '.yarnrc', '.yarnrc.yml', '.yarn'],\n} as const satisfies Record<PackageManager, readonly string[]>;\n\nconst packageManagerInstallCommandResolvers = {\n bun: resolveBunInstallCommand,\n cargo: resolveCargoInstallCommand,\n go: resolveGoInstallCommand,\n gradle: resolveGradleInstallCommand,\n maven: resolveMavenInstallCommand,\n npm: resolveNpmInstallCommand,\n pnpm: resolvePnpmInstallCommand,\n ruby: resolveRubyInstallCommand,\n uv: resolveUvInstallCommand,\n yarn: resolveYarnInstallCommand,\n} as const satisfies Record<PackageManager, (runDir: string) => Promise<PackageManagerInstallCommand | undefined>>;\n\nconst defaultOutputLimitBytes = 50 * 1024 * 1024;\nconst killGracePeriodMilliseconds = 1000;\n// Added to the watchdog's own deadline so its kill has time to land and `close` to arrive.\nconst watchdogSettleMarginMilliseconds = 2000;\nconst timeCommand = resolveTimeCommand();\n\n/**\n * Copies a submission directory to a temporary directory, overlays package\n * manager project files from the problem directory, prepares dependencies,\n * runs a command, and then removes the temporary directory.\n *\n * Under `EXERCODE_SANDBOX_USER` delegation, cleanup kills every process of the sandbox user, so do\n * not run multiple invocations concurrently in that mode — one finishing would kill the others.\n */\nexport async function runCommandInTemporaryPackageManagerProject(\n options: RunCommandInTemporaryPackageManagerProjectOptions\n): Promise<PackageManagerCommandRunResult> {\n const runDir = await fs.mkdtemp(path.join(os.tmpdir(), options.tempDirPrefix ?? 'exercode-'));\n try {\n await fs.cp(options.cwd, runDir, { recursive: true });\n await copyPackageManagerProjectFiles({\n packageManager: options.packageManager,\n projectDir: options.projectDir,\n runDir,\n projectFilePaths: options.projectFilePaths,\n });\n // The sandbox user (if any) runs the install/run commands below and must read the copied\n // sources and create outputs (e.g. `node_modules`, `.venv`) next to them.\n makeAccessibleToSandboxUser(runDir);\n\n const env = options.env ? { ...process.env, ...options.env } : process.env;\n const installCommand = await resolveInstallCommand(options.packageManager, runDir);\n const command = typeof options.command === 'function' ? options.command({ runDir }) : options.command;\n const startedAt = Date.now();\n const outputLimitBytes = options.outputLimitBytes ?? defaultOutputLimitBytes;\n let installResult: Awaited<ReturnType<typeof spawnWithInput>> | undefined;\n\n if (installCommand) {\n installResult = await spawnWithInput(installCommand, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: '',\n timeLimitSeconds: options.timeLimitSeconds,\n });\n if (isFailedSpawnResult(installResult)) {\n return toPackageManagerCommandRunResult({\n elapsedTimeSeconds: (Date.now() - startedAt) / 1000,\n options,\n result: installResult,\n });\n }\n }\n\n const remainingTimeLimitSeconds = options.timeLimitSeconds - (Date.now() - startedAt) / 1000;\n if (remainingTimeLimitSeconds <= 0) {\n return {\n stdin: options.stdin ?? '',\n stdout: installResult?.stdout ?? '',\n stderr: installResult?.stderr ?? '',\n status: 0,\n timeSeconds: options.timeLimitSeconds + 1e-3,\n memoryBytes: installResult?.memoryBytes ?? 0,\n timedOut: true,\n signal: installResult?.signal,\n outputLimitExceeded: false,\n };\n }\n\n const result = await spawnWithInput(command, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: options.stdin ?? '',\n timeLimitSeconds: remainingTimeLimitSeconds,\n });\n const elapsedTimeSeconds = (Date.now() - startedAt) / 1000;\n\n if (installResult) {\n return toPackageManagerCommandRunResult({\n elapsedTimeSeconds,\n options,\n result: {\n ...result,\n timeSeconds: installResult.timeSeconds + result.timeSeconds,\n memoryBytes: Math.max(installResult.memoryBytes, result.memoryBytes),\n },\n });\n }\n\n return toPackageManagerCommandRunResult({ elapsedTimeSeconds, options, result });\n } finally {\n try {\n // Daemonized children of the sandboxed command would otherwise outlive the run.\n if (sandboxUserName) killSandboxUserProcesses();\n } finally {\n // Must run even when the sweep fails closed, or the temporary submission copy leaks.\n await forceRemove(runDir);\n }\n }\n}\n\nfunction toPackageManagerCommandRunResult(context: {\n elapsedTimeSeconds: number;\n options: RunCommandInTemporaryPackageManagerProjectOptions;\n result: Awaited<ReturnType<typeof spawnWithInput>>;\n}): PackageManagerCommandRunResult {\n return {\n stdin: context.options.stdin ?? '',\n stdout: context.result.stdout,\n stderr: context.result.stderr,\n status: context.result.timedOut || context.result.outputLimitExceeded ? 0 : context.result.status,\n timeSeconds: context.result.timedOut\n ? context.options.timeLimitSeconds + 1e-3\n : context.result.timeSeconds || context.elapsedTimeSeconds,\n memoryBytes: context.result.memoryBytes,\n timedOut: context.result.timedOut,\n signal: context.result.signal,\n outputLimitExceeded: context.result.outputLimitExceeded,\n };\n}\n\nfunction resolveInstallCommand(\n packageManager: PackageManager,\n runDir: string\n): Promise<PackageManagerInstallCommand | undefined> {\n return packageManagerInstallCommandResolvers[packageManager](runDir);\n}\n\nfunction isFailedSpawnResult(result: Awaited<ReturnType<typeof spawnWithInput>>): boolean {\n return result.status !== 0 || result.timedOut || result.outputLimitExceeded;\n}\n\nasync function resolveBunInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n // Bun supports --silent and it keeps successful preparation output out of judge output buffers.\n return (await hasAnyPath(runDir, ['bun.lock', 'bun.lockb']))\n ? ['bun', 'install', '--frozen-lockfile', '--silent']\n : ['bun', 'install', '--silent'];\n}\n\nasync function resolveCargoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Cargo.toml')))) return undefined;\n return (await pathExists(path.join(runDir, 'Cargo.lock'))) ? ['cargo', 'fetch', '--locked'] : ['cargo', 'fetch'];\n}\n\nasync function resolveGoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'go.mod')))) return undefined;\n return ['go', 'mod', 'download'];\n}\n\nasync function resolveGradleInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (\n !(await hasAnyPath(runDir, [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradlew',\n 'gradlew.bat',\n ]))\n )\n return undefined;\n const args = ['--no-daemon', '--quiet', 'dependencies'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'gradlew.bat')))\n ? ['cmd.exe', '/c', 'gradlew.bat', ...args]\n : ['gradle', ...args];\n }\n return (await pathExists(path.join(runDir, 'gradlew'))) ? ['sh', './gradlew', ...args] : ['gradle', ...args];\n}\n\nasync function resolveMavenInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'pom.xml')))) return undefined;\n const args = ['-q', 'dependency:go-offline'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'mvnw.cmd')))\n ? ['cmd.exe', '/c', 'mvnw.cmd', ...args]\n : ['mvn', ...args];\n }\n return (await pathExists(path.join(runDir, 'mvnw'))) ? ['sh', './mvnw', ...args] : ['mvn', ...args];\n}\n\nasync function resolveNpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'package-lock.json')))\n ? ['npm', 'ci', '--silent']\n : ['npm', 'install', '--silent'];\n}\n\nasync function resolvePnpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'pnpm-lock.yaml')))\n ? ['pnpm', 'install', '--frozen-lockfile', '--silent']\n : ['pnpm', 'install', '--silent'];\n}\n\nasync function resolveRubyInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Gemfile')))) return undefined;\n return (await pathExists(path.join(runDir, 'Gemfile.lock')))\n ? ['bundle', 'install', '--frozen', '--quiet']\n : ['bundle', 'install', '--quiet'];\n}\n\nasync function resolveUvInstallCommand(): Promise<undefined> {\n return undefined;\n}\n\nasync function resolveYarnInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n const isBerry = await isYarnBerryProject(runDir);\n const hasLockfile = await pathExists(path.join(runDir, 'yarn.lock'));\n if (isBerry) return hasLockfile ? ['yarn', 'install', '--immutable'] : ['yarn', 'install'];\n return hasLockfile ? ['yarn', 'install', '--frozen-lockfile', '--silent'] : ['yarn', 'install', '--silent'];\n}\n\nasync function isYarnBerryProject(runDir: string): Promise<boolean> {\n if (await pathExists(path.join(runDir, '.yarnrc.yml'))) return true;\n\n const packageJson = await readJson(path.join(runDir, 'package.json'));\n const packageManager = typeof packageJson.packageManager === 'string' ? packageJson.packageManager : undefined;\n const yarnMajorVersion = /^yarn@(\\d+)/.exec(packageManager ?? '')?.[1];\n return yarnMajorVersion !== undefined && Number(yarnMajorVersion) >= 2;\n}\n\nasync function hasAnyPath(directoryPath: string, relativePaths: readonly string[]): Promise<boolean> {\n for (const relativePath of relativePaths) {\n if (await pathExists(path.join(directoryPath, relativePath))) return true;\n }\n return false;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ENOENT') throw error;\n return false;\n }\n}\n\nasync function readJson(filePath: string): Promise<Record<string, unknown>> {\n try {\n const parsed = JSON.parse(await fs.readFile(filePath, 'utf8')) as unknown;\n return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch (error) {\n if (error instanceof SyntaxError) return {};\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code === 'ENOENT') return {};\n throw error;\n }\n}\n\nexport async function copyPackageManagerProjectFiles(options: {\n packageManager: PackageManager;\n projectDir: string;\n runDir: string;\n projectFilePaths?: readonly string[];\n}): Promise<void> {\n for (const projectFilePath of options.projectFilePaths ?? packageManagerProjectFilePaths[options.packageManager]) {\n await copyPathIfExists(\n path.join(options.projectDir, projectFilePath),\n path.join(options.runDir, projectFilePath),\n options.runDir\n );\n }\n}\n\nasync function copyPathIfExists(sourcePath: string, destinationPath: string, runDir: string): Promise<void> {\n try {\n // Before touching the destination: creating the parent levels replaces whatever the submission\n // put there, which must not happen for a project file the problem does not even ship.\n await fs.lstat(sourcePath);\n // No-follow copy: the destination was seeded from the (sandbox-writable) submission tree, so a\n // planted symlink there — at the destination itself or at any directory level of a nested\n // project file path — must not redirect this trusted project-file overlay outside runDir.\n await createDirectoryWithoutFollowingSymlinks(runDir, path.dirname(destinationPath));\n await copyWithoutFollowingSymlinks(sourcePath, destinationPath);\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ENOENT') throw error;\n }\n}\n\nasync function spawnWithInput(\n command: readonly [string, ...string[]],\n context: {\n cwd: string;\n env: NodeJS.ProcessEnv;\n outputLimitBytes: number;\n stdin: string;\n timeLimitSeconds: number;\n }\n): Promise<{\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n timedOut: boolean;\n signal: NodeJS.Signals | undefined;\n outputLimitExceeded: boolean;\n}> {\n const timeOutputPath = timeCommand === undefined ? undefined : path.join(context.cwd, '.exercode-time-result');\n // `wrapCommandWithSandboxUser` is idempotent, so a command the presets already wrapped is not\n // nested; `time` is then spliced INSIDE that wrapper so it runs as the sandbox user, never as the\n // harness user writing `--output` into this sandbox-writable directory.\n const wrappedCommand = wrapCommandWithSandboxUser(command);\n const spawnedCommand =\n timeCommand === undefined\n ? wrappedCommand\n : prependInsideSandboxWrapper(wrappedCommand, [...timeCommand, `--output=${timeOutputPath}`]);\n // Started BEFORE the submission: `killSubprocessGroup` cannot spawn its `sudo` once a submission\n // has exhausted the PID cgroup, and a submission spawned first could exhaust it before this\n // watchdog exists. It is the deadline of last resort on this path.\n const watchdog = startSandboxTimeoutWatchdog(context.timeLimitSeconds);\n const watchdogDeadlineAt = Date.now() + (Math.ceil(context.timeLimitSeconds) + SANDBOX_WATCHDOG_GRACE_SECONDS) * 1000;\n let subprocess: childProcess.ChildProcessWithoutNullStreams;\n try {\n subprocess = childProcess.spawn(spawnedCommand[0], spawnedCommand.slice(1), {\n cwd: context.cwd,\n detached: process.platform !== 'win32',\n env: { ...context.env, ...getSandboxUserEnvOverrides(context.env) },\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n } catch (error) {\n watchdog.cancel();\n throw error;\n }\n\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let outputBytes = 0;\n let timedOut = false;\n let outputLimitExceeded = false;\n // Set when terminating the submission failed (the cleanup `sudo` could not be spawned). The run\n // then waits for the watchdog to end the submission rather than throwing out of an EventEmitter\n // callback, which would be an uncaught exception that kills the harness before any cleanup.\n let killError: Error | undefined;\n let onKillFailure: (() => void) | undefined;\n const killOrRecordFailure = (signal: NodeJS.Signals): void => {\n try {\n killSubprocessGroup(subprocess, signal);\n } catch (error) {\n killError ??= error instanceof Error ? error : new Error(String(error));\n onKillFailure?.();\n }\n };\n\n const appendOutputChunk = (chunks: Buffer[], chunk: Buffer): void => {\n if (outputBytes >= context.outputLimitBytes) {\n if (chunk.byteLength > 0) {\n outputLimitExceeded = true;\n killOrRecordFailure('SIGKILL');\n }\n return;\n }\n\n const remainingBytes = context.outputLimitBytes - outputBytes;\n const appendedChunk = chunk.byteLength > remainingBytes ? chunk.subarray(0, remainingBytes) : chunk;\n chunks.push(appendedChunk);\n outputBytes += appendedChunk.byteLength;\n\n if (chunk.byteLength > remainingBytes) {\n outputLimitExceeded = true;\n killOrRecordFailure('SIGKILL');\n }\n };\n\n subprocess.stdout.on('data', (chunk: Buffer) => appendOutputChunk(stdoutChunks, chunk));\n subprocess.stderr.on('data', (chunk: Buffer) => appendOutputChunk(stderrChunks, chunk));\n\n const timeout = setTimeout(() => {\n timedOut = true;\n killOrRecordFailure('SIGTERM');\n }, context.timeLimitSeconds * 1000);\n const killTimeout = setTimeout(\n () => {\n if (timedOut) killOrRecordFailure('SIGKILL');\n },\n context.timeLimitSeconds * 1000 + killGracePeriodMilliseconds\n );\n killTimeout.unref();\n // Bounds the wait after a kill failure: the watchdog force-kills the sandbox user at its own\n // deadline, so `close` should arrive; if even that fails, give up rather than hang.\n let killFailureTimeout: ReturnType<typeof setTimeout> | undefined;\n let closeObserved = false;\n\n const { status, signal } = await new Promise<{ status: number | undefined; signal: NodeJS.Signals | undefined }>(\n (resolve, reject) => {\n let settled = false;\n let pendingError: Error | undefined;\n const failAfterClose = (error: Error): void => {\n if (settled) return;\n pendingError = error;\n killOrRecordFailure('SIGKILL');\n if (subprocess.pid === undefined) {\n settled = true;\n reject(error);\n }\n };\n // Terminating the submission failed. Keep waiting: the watchdog (started before the\n // submission) force-kills the sandbox user at its own deadline, so `close` still arrives and\n // the run reports its real verdict. Give up only once that deadline has passed without the\n // submission ending, so the run can neither hang nor abandon a still-running submission\n // while its watchdog could still fire.\n onKillFailure = () => {\n killFailureTimeout ??= setTimeout(\n () => {\n if (settled) return;\n settled = true;\n reject(killError ?? new Error('failed to terminate the submission'));\n },\n Math.max(0, watchdogDeadlineAt - Date.now()) + watchdogSettleMarginMilliseconds\n );\n };\n subprocess.on('error', failAfterClose);\n subprocess.stdin.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code !== 'EPIPE') failAfterClose(error);\n });\n subprocess.on('close', (code, closeSignal) => {\n if (settled) return;\n settled = true;\n // The submission ended, so the watchdog has nothing left to kill and must not outlive this\n // run. A kill that failed once but succeeded on retry (or was superseded by the watchdog)\n // is not an error: the timeout/output-limit verdict below is the correct one to report.\n closeObserved = true;\n if (pendingError) {\n reject(pendingError);\n return;\n }\n resolve({ status: code ?? undefined, signal: closeSignal ?? undefined });\n });\n subprocess.stdin.end(context.stdin);\n }\n ).finally(() => {\n clearTimeout(timeout);\n clearTimeout(killTimeout);\n if (killFailureTimeout) clearTimeout(killFailureTimeout);\n // Cancel unless the submission is still running after a failed kill: the watchdog is then the\n // only remaining mechanism able to stop it, and cancelling would leave it running unchecked.\n if (closeObserved || !killError) watchdog.cancel();\n });\n\n const { timeSeconds, memoryBytes } =\n timeOutputPath === undefined ? { timeSeconds: 0, memoryBytes: 0 } : await readTimeResult(timeOutputPath);\n\n return {\n stdout: Buffer.concat(stdoutChunks).toString(),\n stderr: Buffer.concat(stderrChunks).toString(),\n status,\n timeSeconds,\n memoryBytes,\n timedOut,\n signal,\n outputLimitExceeded,\n };\n}\n\nfunction resolveTimeCommand(): readonly [string, ...string[]] | undefined {\n const command = os.platform() === 'darwin' ? 'gtime' : '/usr/bin/time';\n const result = childProcess.spawnSync(command, ['--version'], { stdio: 'ignore' });\n if (result.error || result.status !== 0) return undefined;\n\n return [command, '--format', '%e %M'];\n}\n\nfunction killSubprocessGroup(subprocess: childProcess.ChildProcess, signal: NodeJS.Signals): void {\n if (subprocess.pid === undefined) return;\n\n // The current user cannot signal the root-owned sudo wrapper nor the sandbox user's processes,\n // so go through sudo with the requested signal only; the callers' existing timers provide the\n // TERM → grace → KILL escalation.\n // Throws when the cleanup `sudo` cannot even be spawned (a submission can exhaust the PID\n // cgroup). Only `killOrRecordFailure` may call this: it records the failure and lets the\n // watchdog (or, past its deadline, a bounded timer) settle the run instead of letting the throw\n // escape an EventEmitter callback.\n if (sandboxUserName) {\n killSandboxUserProcesses([signal === 'SIGKILL' ? 'KILL' : 'TERM']);\n return;\n }\n\n try {\n if (process.platform === 'win32') {\n subprocess.kill(signal);\n return;\n }\n process.kill(-subprocess.pid, signal);\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ESRCH' && code !== 'EPERM') throw error;\n }\n}\n\nfunction isErrorWithCode(error: unknown, code: string): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && (error as { code: unknown }).code === code;\n}\n\nasync function readTimeResult(timeOutputPath: string): Promise<{ timeSeconds: number; memoryBytes: number }> {\n // The submission owns this directory and can put anything at this path, so decide what was\n // opened from the handle itself rather than from a prior `lstat` it could race:\n // - `O_NOFOLLOW` refuses a symlink to a harness-readable file (e.g. the problem's expected\n // outputs), which would otherwise report that file's trailing numbers as the submission's own\n // time and memory usage;\n // - `O_NONBLOCK` keeps a planted FIFO from blocking the harness forever waiting for a writer;\n // - the `fstat` then rejects anything that is not a regular file.\n //\n // Only an absent file means \"no measurement\" (the command was killed before `time` wrote one).\n // Anything else at this path is the submission tampering with its own accounting — reporting zero\n // memory would let it slip past a memory limit — so that fails the run instead.\n let content: string;\n let fileHandle: Awaited<ReturnType<typeof fs.open>> | undefined;\n try {\n fileHandle = await fs.open(\n timeOutputPath,\n nodeFs.constants.O_RDONLY | nodeFs.constants.O_NOFOLLOW | nodeFs.constants.O_NONBLOCK\n );\n const stats = await fileHandle.stat();\n if (!stats.isFile()) throw new Error(`${timeOutputPath} is not a regular file`);\n content = await fileHandle.readFile('utf8');\n } catch (error) {\n if (isErrorWithCode(error, 'ENOENT')) return { timeSeconds: 0, memoryBytes: 0 };\n throw new Error(`failed to read the time measurement at ${timeOutputPath}`, { cause: error });\n } finally {\n await fileHandle?.close();\n }\n\n const match = /(\\d+(?:[.,]\\d+)?) (\\d+)\\s*$/.exec(content);\n if (!match) return { timeSeconds: 0, memoryBytes: 0 };\n\n return { timeSeconds: Number(match[1]!.replace(',', '.')), memoryBytes: Number(match[2]) * 1024 };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runCommandInTemporaryPackageManagerProject.js","names":["childProcess","nodeFs"],"sources":["../../src/helpers/runCommandInTemporaryPackageManagerProject.ts"],"sourcesContent":["import childProcess from 'node:child_process';\nimport nodeFs from 'node:fs';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport {\n forceRemove,\n getSandboxUserEnvOverrides,\n killSandboxUserProcesses,\n makeAccessibleToSandboxUser,\n prependInsideSandboxWrapper,\n SANDBOX_WATCHDOG_GRACE_SECONDS,\n sandboxUserName,\n startSandboxTimeoutWatchdog,\n wrapCommandWithSandboxUser,\n} from './sandboxUser.js';\nimport { copyWithoutFollowingSymlinks, createDirectoryWithoutFollowingSymlinks } from './safeFs.js';\n\nexport type PackageManager = 'bun' | 'cargo' | 'go' | 'gradle' | 'maven' | 'npm' | 'pnpm' | 'ruby' | 'uv' | 'yarn';\ntype PackageManagerInstallCommand = readonly [string, ...string[]];\n\nexport interface PackageManagerCommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n timedOut: boolean;\n signal: NodeJS.Signals | undefined;\n outputLimitExceeded: boolean;\n}\n\nexport interface RunCommandInTemporaryPackageManagerProjectOptions {\n cwd: string;\n projectDir: string;\n packageManager: PackageManager;\n command: readonly [string, ...string[]] | ((context: { runDir: string }) => readonly [string, ...string[]]);\n /**\n * Set to false when `command` prepares the dependencies it needs.\n * Defaults to true.\n */\n prepareDependencies?: boolean;\n stdin?: string;\n env?: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n outputLimitBytes?: number;\n tempDirPrefix?: string;\n projectFilePaths?: readonly string[];\n}\n\nconst packageManagerProjectFilePaths = {\n bun: ['package.json', 'bun.lock', 'bun.lockb'],\n cargo: ['Cargo.toml', 'Cargo.lock'],\n go: ['go.mod', 'go.sum'],\n gradle: [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradle.properties',\n 'gradle.lockfile',\n 'buildscript-gradle.lockfile',\n 'gradle',\n 'gradlew',\n 'gradlew.bat',\n ],\n maven: ['pom.xml', '.mvn', 'mvnw', 'mvnw.cmd'],\n npm: ['package.json', 'package-lock.json'],\n pnpm: ['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml'],\n ruby: ['Gemfile', 'Gemfile.lock', '.ruby-version'],\n uv: ['pyproject.toml', 'uv.lock'],\n yarn: ['package.json', 'yarn.lock', '.yarnrc', '.yarnrc.yml', '.yarn'],\n} as const satisfies Record<PackageManager, readonly string[]>;\n\nconst packageManagerInstallCommandResolvers = {\n bun: resolveBunInstallCommand,\n cargo: resolveCargoInstallCommand,\n go: resolveGoInstallCommand,\n gradle: resolveGradleInstallCommand,\n maven: resolveMavenInstallCommand,\n npm: resolveNpmInstallCommand,\n pnpm: resolvePnpmInstallCommand,\n ruby: resolveRubyInstallCommand,\n uv: resolveUvInstallCommand,\n yarn: resolveYarnInstallCommand,\n} as const satisfies Record<PackageManager, (runDir: string) => Promise<PackageManagerInstallCommand | undefined>>;\n\nconst defaultOutputLimitBytes = 50 * 1024 * 1024;\nconst killGracePeriodMilliseconds = 1000;\n// Added to the watchdog's own deadline so its kill has time to land and `close` to arrive.\nconst watchdogSettleMarginMilliseconds = 2000;\nconst timeCommand = resolveTimeCommand();\n\n/**\n * Copies a submission directory to a temporary directory, overlays package\n * manager project files from the problem directory, prepares dependencies,\n * runs a command, and then removes the temporary directory.\n *\n * Under `EXERCODE_SANDBOX_USER` delegation, cleanup kills every process of the sandbox user, so do\n * not run multiple invocations concurrently in that mode — one finishing would kill the others.\n */\nexport async function runCommandInTemporaryPackageManagerProject(\n options: RunCommandInTemporaryPackageManagerProjectOptions\n): Promise<PackageManagerCommandRunResult> {\n const runDir = await fs.mkdtemp(path.join(os.tmpdir(), options.tempDirPrefix ?? 'exercode-'));\n try {\n await fs.cp(options.cwd, runDir, { recursive: true });\n await copyPackageManagerProjectFiles({\n packageManager: options.packageManager,\n projectDir: options.projectDir,\n runDir,\n projectFilePaths: options.projectFilePaths,\n });\n // The sandbox user (if any) runs the install/run commands below and must read the copied\n // sources and create outputs (e.g. `node_modules`, `.venv`) next to them.\n makeAccessibleToSandboxUser(runDir);\n\n const env = options.env ? { ...process.env, ...options.env } : process.env;\n const installCommand =\n options.prepareDependencies === false ? undefined : await resolveInstallCommand(options.packageManager, runDir);\n const command = typeof options.command === 'function' ? options.command({ runDir }) : options.command;\n const startedAt = Date.now();\n const outputLimitBytes = options.outputLimitBytes ?? defaultOutputLimitBytes;\n let installResult: Awaited<ReturnType<typeof spawnWithInput>> | undefined;\n\n if (installCommand) {\n installResult = await spawnWithInput(installCommand, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: '',\n timeLimitSeconds: options.timeLimitSeconds,\n });\n if (isFailedSpawnResult(installResult)) {\n return toPackageManagerCommandRunResult({\n elapsedTimeSeconds: (Date.now() - startedAt) / 1000,\n options,\n result: installResult,\n });\n }\n }\n\n const remainingTimeLimitSeconds = options.timeLimitSeconds - (Date.now() - startedAt) / 1000;\n if (remainingTimeLimitSeconds <= 0) {\n return {\n stdin: options.stdin ?? '',\n stdout: installResult?.stdout ?? '',\n stderr: installResult?.stderr ?? '',\n status: 0,\n timeSeconds: options.timeLimitSeconds + 1e-3,\n memoryBytes: installResult?.memoryBytes ?? 0,\n timedOut: true,\n signal: installResult?.signal,\n outputLimitExceeded: false,\n };\n }\n\n const result = await spawnWithInput(command, {\n cwd: runDir,\n env,\n outputLimitBytes,\n stdin: options.stdin ?? '',\n timeLimitSeconds: remainingTimeLimitSeconds,\n });\n const elapsedTimeSeconds = (Date.now() - startedAt) / 1000;\n\n if (installResult) {\n return toPackageManagerCommandRunResult({\n elapsedTimeSeconds,\n options,\n result: {\n ...result,\n timeSeconds: installResult.timeSeconds + result.timeSeconds,\n memoryBytes: Math.max(installResult.memoryBytes, result.memoryBytes),\n },\n });\n }\n\n return toPackageManagerCommandRunResult({ elapsedTimeSeconds, options, result });\n } finally {\n try {\n // Daemonized children of the sandboxed command would otherwise outlive the run.\n if (sandboxUserName) killSandboxUserProcesses();\n } finally {\n // Must run even when the sweep fails closed, or the temporary submission copy leaks.\n await forceRemove(runDir);\n }\n }\n}\n\nfunction toPackageManagerCommandRunResult(context: {\n elapsedTimeSeconds: number;\n options: RunCommandInTemporaryPackageManagerProjectOptions;\n result: Awaited<ReturnType<typeof spawnWithInput>>;\n}): PackageManagerCommandRunResult {\n return {\n stdin: context.options.stdin ?? '',\n stdout: context.result.stdout,\n stderr: context.result.stderr,\n status: context.result.timedOut || context.result.outputLimitExceeded ? 0 : context.result.status,\n timeSeconds: context.result.timedOut\n ? context.options.timeLimitSeconds + 1e-3\n : context.result.timeSeconds || context.elapsedTimeSeconds,\n memoryBytes: context.result.memoryBytes,\n timedOut: context.result.timedOut,\n signal: context.result.signal,\n outputLimitExceeded: context.result.outputLimitExceeded,\n };\n}\n\nfunction resolveInstallCommand(\n packageManager: PackageManager,\n runDir: string\n): Promise<PackageManagerInstallCommand | undefined> {\n return packageManagerInstallCommandResolvers[packageManager](runDir);\n}\n\nfunction isFailedSpawnResult(result: Awaited<ReturnType<typeof spawnWithInput>>): boolean {\n return result.status !== 0 || result.timedOut || result.outputLimitExceeded;\n}\n\nasync function resolveBunInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n // Bun supports --silent and it keeps successful preparation output out of judge output buffers.\n return (await hasAnyPath(runDir, ['bun.lock', 'bun.lockb']))\n ? ['bun', 'install', '--frozen-lockfile', '--silent']\n : ['bun', 'install', '--silent'];\n}\n\nasync function resolveCargoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Cargo.toml')))) return undefined;\n return (await pathExists(path.join(runDir, 'Cargo.lock'))) ? ['cargo', 'fetch', '--locked'] : ['cargo', 'fetch'];\n}\n\nasync function resolveGoInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'go.mod')))) return undefined;\n return ['go', 'mod', 'download'];\n}\n\nasync function resolveGradleInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (\n !(await hasAnyPath(runDir, [\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n 'gradlew',\n 'gradlew.bat',\n ]))\n )\n return undefined;\n const args = ['--no-daemon', '--quiet', 'dependencies'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'gradlew.bat')))\n ? ['cmd.exe', '/c', 'gradlew.bat', ...args]\n : ['gradle', ...args];\n }\n return (await pathExists(path.join(runDir, 'gradlew'))) ? ['sh', './gradlew', ...args] : ['gradle', ...args];\n}\n\nasync function resolveMavenInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'pom.xml')))) return undefined;\n const args = ['-q', 'dependency:go-offline'] as const;\n if (process.platform === 'win32') {\n return (await pathExists(path.join(runDir, 'mvnw.cmd')))\n ? ['cmd.exe', '/c', 'mvnw.cmd', ...args]\n : ['mvn', ...args];\n }\n return (await pathExists(path.join(runDir, 'mvnw'))) ? ['sh', './mvnw', ...args] : ['mvn', ...args];\n}\n\nasync function resolveNpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'package-lock.json')))\n ? ['npm', 'ci', '--silent']\n : ['npm', 'install', '--silent'];\n}\n\nasync function resolvePnpmInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n return (await pathExists(path.join(runDir, 'pnpm-lock.yaml')))\n ? ['pnpm', 'install', '--frozen-lockfile', '--silent']\n : ['pnpm', 'install', '--silent'];\n}\n\nasync function resolveRubyInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'Gemfile')))) return undefined;\n return (await pathExists(path.join(runDir, 'Gemfile.lock')))\n ? ['bundle', 'install', '--frozen', '--quiet']\n : ['bundle', 'install', '--quiet'];\n}\n\nasync function resolveUvInstallCommand(): Promise<undefined> {\n return undefined;\n}\n\nasync function resolveYarnInstallCommand(runDir: string): Promise<PackageManagerInstallCommand | undefined> {\n if (!(await pathExists(path.join(runDir, 'package.json')))) return undefined;\n const isBerry = await isYarnBerryProject(runDir);\n const hasLockfile = await pathExists(path.join(runDir, 'yarn.lock'));\n if (isBerry) return hasLockfile ? ['yarn', 'install', '--immutable'] : ['yarn', 'install'];\n return hasLockfile ? ['yarn', 'install', '--frozen-lockfile', '--silent'] : ['yarn', 'install', '--silent'];\n}\n\nasync function isYarnBerryProject(runDir: string): Promise<boolean> {\n if (await pathExists(path.join(runDir, '.yarnrc.yml'))) return true;\n\n const packageJson = await readJson(path.join(runDir, 'package.json'));\n const packageManager = typeof packageJson.packageManager === 'string' ? packageJson.packageManager : undefined;\n const yarnMajorVersion = /^yarn@(\\d+)/.exec(packageManager ?? '')?.[1];\n return yarnMajorVersion !== undefined && Number(yarnMajorVersion) >= 2;\n}\n\nasync function hasAnyPath(directoryPath: string, relativePaths: readonly string[]): Promise<boolean> {\n for (const relativePath of relativePaths) {\n if (await pathExists(path.join(directoryPath, relativePath))) return true;\n }\n return false;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ENOENT') throw error;\n return false;\n }\n}\n\nasync function readJson(filePath: string): Promise<Record<string, unknown>> {\n try {\n const parsed = JSON.parse(await fs.readFile(filePath, 'utf8')) as unknown;\n return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch (error) {\n if (error instanceof SyntaxError) return {};\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code === 'ENOENT') return {};\n throw error;\n }\n}\n\nexport async function copyPackageManagerProjectFiles(options: {\n packageManager: PackageManager;\n projectDir: string;\n runDir: string;\n projectFilePaths?: readonly string[];\n}): Promise<void> {\n for (const projectFilePath of options.projectFilePaths ?? packageManagerProjectFilePaths[options.packageManager]) {\n await copyPathIfExists(\n path.join(options.projectDir, projectFilePath),\n path.join(options.runDir, projectFilePath),\n options.runDir\n );\n }\n}\n\nasync function copyPathIfExists(sourcePath: string, destinationPath: string, runDir: string): Promise<void> {\n try {\n // Before touching the destination: creating the parent levels replaces whatever the submission\n // put there, which must not happen for a project file the problem does not even ship.\n await fs.lstat(sourcePath);\n // No-follow copy: the destination was seeded from the (sandbox-writable) submission tree, so a\n // planted symlink there — at the destination itself or at any directory level of a nested\n // project file path — must not redirect this trusted project-file overlay outside runDir.\n await createDirectoryWithoutFollowingSymlinks(runDir, path.dirname(destinationPath));\n await copyWithoutFollowingSymlinks(sourcePath, destinationPath);\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ENOENT') throw error;\n }\n}\n\nasync function spawnWithInput(\n command: readonly [string, ...string[]],\n context: {\n cwd: string;\n env: NodeJS.ProcessEnv;\n outputLimitBytes: number;\n stdin: string;\n timeLimitSeconds: number;\n }\n): Promise<{\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n timedOut: boolean;\n signal: NodeJS.Signals | undefined;\n outputLimitExceeded: boolean;\n}> {\n const timeOutputPath = timeCommand === undefined ? undefined : path.join(context.cwd, '.exercode-time-result');\n // `wrapCommandWithSandboxUser` is idempotent, so a command the presets already wrapped is not\n // nested; `time` is then spliced INSIDE that wrapper so it runs as the sandbox user, never as the\n // harness user writing `--output` into this sandbox-writable directory.\n const wrappedCommand = wrapCommandWithSandboxUser(command);\n const spawnedCommand =\n timeCommand === undefined\n ? wrappedCommand\n : prependInsideSandboxWrapper(wrappedCommand, [...timeCommand, `--output=${timeOutputPath}`]);\n // Started BEFORE the submission: `killSubprocessGroup` cannot spawn its `sudo` once a submission\n // has exhausted the PID cgroup, and a submission spawned first could exhaust it before this\n // watchdog exists. It is the deadline of last resort on this path.\n const watchdog = startSandboxTimeoutWatchdog(context.timeLimitSeconds);\n const watchdogDeadlineAt = Date.now() + (Math.ceil(context.timeLimitSeconds) + SANDBOX_WATCHDOG_GRACE_SECONDS) * 1000;\n let subprocess: childProcess.ChildProcessWithoutNullStreams;\n try {\n subprocess = childProcess.spawn(spawnedCommand[0], spawnedCommand.slice(1), {\n cwd: context.cwd,\n detached: process.platform !== 'win32',\n env: { ...context.env, ...getSandboxUserEnvOverrides(context.env) },\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n } catch (error) {\n watchdog.cancel();\n throw error;\n }\n\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let outputBytes = 0;\n let timedOut = false;\n let outputLimitExceeded = false;\n // Set when terminating the submission failed (the cleanup `sudo` could not be spawned). The run\n // then waits for the watchdog to end the submission rather than throwing out of an EventEmitter\n // callback, which would be an uncaught exception that kills the harness before any cleanup.\n let killError: Error | undefined;\n let onKillFailure: (() => void) | undefined;\n const killOrRecordFailure = (signal: NodeJS.Signals): void => {\n try {\n killSubprocessGroup(subprocess, signal);\n } catch (error) {\n killError ??= error instanceof Error ? error : new Error(String(error));\n onKillFailure?.();\n }\n };\n\n const appendOutputChunk = (chunks: Buffer[], chunk: Buffer): void => {\n if (outputBytes >= context.outputLimitBytes) {\n if (chunk.byteLength > 0) {\n outputLimitExceeded = true;\n killOrRecordFailure('SIGKILL');\n }\n return;\n }\n\n const remainingBytes = context.outputLimitBytes - outputBytes;\n const appendedChunk = chunk.byteLength > remainingBytes ? chunk.subarray(0, remainingBytes) : chunk;\n chunks.push(appendedChunk);\n outputBytes += appendedChunk.byteLength;\n\n if (chunk.byteLength > remainingBytes) {\n outputLimitExceeded = true;\n killOrRecordFailure('SIGKILL');\n }\n };\n\n subprocess.stdout.on('data', (chunk: Buffer) => appendOutputChunk(stdoutChunks, chunk));\n subprocess.stderr.on('data', (chunk: Buffer) => appendOutputChunk(stderrChunks, chunk));\n\n const timeout = setTimeout(() => {\n timedOut = true;\n killOrRecordFailure('SIGTERM');\n }, context.timeLimitSeconds * 1000);\n const killTimeout = setTimeout(\n () => {\n if (timedOut) killOrRecordFailure('SIGKILL');\n },\n context.timeLimitSeconds * 1000 + killGracePeriodMilliseconds\n );\n killTimeout.unref();\n // Bounds the wait after a kill failure: the watchdog force-kills the sandbox user at its own\n // deadline, so `close` should arrive; if even that fails, give up rather than hang.\n let killFailureTimeout: ReturnType<typeof setTimeout> | undefined;\n let closeObserved = false;\n\n const { status, signal } = await new Promise<{ status: number | undefined; signal: NodeJS.Signals | undefined }>(\n (resolve, reject) => {\n let settled = false;\n let pendingError: Error | undefined;\n const failAfterClose = (error: Error): void => {\n if (settled) return;\n pendingError = error;\n killOrRecordFailure('SIGKILL');\n if (subprocess.pid === undefined) {\n settled = true;\n reject(error);\n }\n };\n // Terminating the submission failed. Keep waiting: the watchdog (started before the\n // submission) force-kills the sandbox user at its own deadline, so `close` still arrives and\n // the run reports its real verdict. Give up only once that deadline has passed without the\n // submission ending, so the run can neither hang nor abandon a still-running submission\n // while its watchdog could still fire.\n onKillFailure = () => {\n killFailureTimeout ??= setTimeout(\n () => {\n if (settled) return;\n settled = true;\n reject(killError ?? new Error('failed to terminate the submission'));\n },\n Math.max(0, watchdogDeadlineAt - Date.now()) + watchdogSettleMarginMilliseconds\n );\n };\n subprocess.on('error', failAfterClose);\n subprocess.stdin.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code !== 'EPIPE') failAfterClose(error);\n });\n subprocess.on('close', (code, closeSignal) => {\n if (settled) return;\n settled = true;\n // The submission ended, so the watchdog has nothing left to kill and must not outlive this\n // run. A kill that failed once but succeeded on retry (or was superseded by the watchdog)\n // is not an error: the timeout/output-limit verdict below is the correct one to report.\n closeObserved = true;\n if (pendingError) {\n reject(pendingError);\n return;\n }\n resolve({ status: code ?? undefined, signal: closeSignal ?? undefined });\n });\n subprocess.stdin.end(context.stdin);\n }\n ).finally(() => {\n clearTimeout(timeout);\n clearTimeout(killTimeout);\n if (killFailureTimeout) clearTimeout(killFailureTimeout);\n // Cancel unless the submission is still running after a failed kill: the watchdog is then the\n // only remaining mechanism able to stop it, and cancelling would leave it running unchecked.\n if (closeObserved || !killError) watchdog.cancel();\n });\n\n const { timeSeconds, memoryBytes } =\n timeOutputPath === undefined ? { timeSeconds: 0, memoryBytes: 0 } : await readTimeResult(timeOutputPath);\n\n return {\n stdout: Buffer.concat(stdoutChunks).toString(),\n stderr: Buffer.concat(stderrChunks).toString(),\n status,\n timeSeconds,\n memoryBytes,\n timedOut,\n signal,\n outputLimitExceeded,\n };\n}\n\nfunction resolveTimeCommand(): readonly [string, ...string[]] | undefined {\n const command = os.platform() === 'darwin' ? 'gtime' : '/usr/bin/time';\n const result = childProcess.spawnSync(command, ['--version'], { stdio: 'ignore' });\n if (result.error || result.status !== 0) return undefined;\n\n return [command, '--format', '%e %M'];\n}\n\nfunction killSubprocessGroup(subprocess: childProcess.ChildProcess, signal: NodeJS.Signals): void {\n if (subprocess.pid === undefined) return;\n\n // The current user cannot signal the root-owned sudo wrapper nor the sandbox user's processes,\n // so go through sudo with the requested signal only; the callers' existing timers provide the\n // TERM → grace → KILL escalation.\n // Throws when the cleanup `sudo` cannot even be spawned (a submission can exhaust the PID\n // cgroup). Only `killOrRecordFailure` may call this: it records the failure and lets the\n // watchdog (or, past its deadline, a bounded timer) settle the run instead of letting the throw\n // escape an EventEmitter callback.\n if (sandboxUserName) {\n killSandboxUserProcesses([signal === 'SIGKILL' ? 'KILL' : 'TERM']);\n return;\n }\n\n try {\n if (process.platform === 'win32') {\n subprocess.kill(signal);\n return;\n }\n process.kill(-subprocess.pid, signal);\n } catch (error) {\n const code =\n typeof error === 'object' && error !== null && 'code' in error ? (error as { code: unknown }).code : undefined;\n if (code !== 'ESRCH' && code !== 'EPERM') throw error;\n }\n}\n\nfunction isErrorWithCode(error: unknown, code: string): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && (error as { code: unknown }).code === code;\n}\n\nasync function readTimeResult(timeOutputPath: string): Promise<{ timeSeconds: number; memoryBytes: number }> {\n // The submission owns this directory and can put anything at this path, so decide what was\n // opened from the handle itself rather than from a prior `lstat` it could race:\n // - `O_NOFOLLOW` refuses a symlink to a harness-readable file (e.g. the problem's expected\n // outputs), which would otherwise report that file's trailing numbers as the submission's own\n // time and memory usage;\n // - `O_NONBLOCK` keeps a planted FIFO from blocking the harness forever waiting for a writer;\n // - the `fstat` then rejects anything that is not a regular file.\n //\n // Only an absent file means \"no measurement\" (the command was killed before `time` wrote one).\n // Anything else at this path is the submission tampering with its own accounting — reporting zero\n // memory would let it slip past a memory limit — so that fails the run instead.\n let content: string;\n let fileHandle: Awaited<ReturnType<typeof fs.open>> | undefined;\n try {\n fileHandle = await fs.open(\n timeOutputPath,\n nodeFs.constants.O_RDONLY | nodeFs.constants.O_NOFOLLOW | nodeFs.constants.O_NONBLOCK\n );\n const stats = await fileHandle.stat();\n if (!stats.isFile()) throw new Error(`${timeOutputPath} is not a regular file`);\n content = await fileHandle.readFile('utf8');\n } catch (error) {\n if (isErrorWithCode(error, 'ENOENT')) return { timeSeconds: 0, memoryBytes: 0 };\n throw new Error(`failed to read the time measurement at ${timeOutputPath}`, { cause: error });\n } finally {\n await fileHandle?.close();\n }\n\n const match = /(\\d+(?:[.,]\\d+)?) (\\d+)\\s*$/.exec(content);\n if (!match) return { timeSeconds: 0, memoryBytes: 0 };\n\n return { timeSeconds: Number(match[1]!.replace(',', '.')), memoryBytes: Number(match[2]) * 1024 };\n}\n"],"mappings":"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"}
|
package/dist/presets/command.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../helpers/parseArgs.cjs"),n=require("../helpers/printTestCaseResult.cjs"),r=require("../helpers/sandboxUser.cjs"),i=require("../types/decisionCode.cjs"),a=require("../helpers/printDebugBanner.cjs"),o=require("../helpers/checkProblemDirIsolation.cjs"),s=require("../helpers/cleanWorkingDirectory.cjs"),c=require("../helpers/copyTestCaseFileInput.cjs"),l=require("../helpers/findEntryPointFile.cjs"),u=require("../helpers/findLanguageDefinitionByPath.cjs"),d=require("../helpers/judgeByStaticAnalysis.cjs"),f=require("../helpers/readOutputFiles.cjs"),p=require("../helpers/runCustomRunner.cjs"),m=require("../helpers/readProblemMarkdownFrontMatter.cjs"),h=require("../helpers/readTestCases.cjs"),g=require("../helpers/resolveCwds.cjs"),_=require("../helpers/spawnSyncWithTimeout.cjs");let v=require("node:path");v=e.__toESM(v,1);let y=require("zod");const b=y.z.object({language:y.z.union([y.z.string(),y.z.array(y.z.string())]).optional()});async function x(e,n){let r=t.parseArgs(process.argv),i=b.parse(r.params),{cwds:s,isDebugMode:c}=await g.resolveCwds(e,r.cwd);if(c){let t=s.find(e=>e.expectedResult===`accepted`);if(t){if(!(await o.checkProblemDirIsolation(e,t,i)).passed){process.exitCode=1;return}}else a.printDebugBanner([`[DEBUG MODE] isolated problem directory check skipped`,``,`No accepted model answer is available for checking that the copied judge still accepts a valid submission.`])}for(let t of s){c&&g.printDebugCwdBanner(e,t);let r=await S(e,t.cwd,i,n);c&&!C(t,r)&&(process.exitCode=1,g.printDebugExpectationFailureBanner(e,t))}}async function S(e,t,a,o){r.makeAccessibleToSandboxUser(t);let h=await m.readProblemMarkdownFrontMatter(e),g=await(o.readTestCases??D)(e),_=g[0]?.id??`prebuild`,v={buildTimeoutSeconds:o.limits?.buildTimeoutSeconds??10,maxOutputLength:o.limits?.maxOutputLength??5e4},y=o.runTimeoutSeconds??2,b=await d.judgeByStaticAnalysis(t,h);if(b)return n.printTestCaseResult({testCaseId:_,...b}),{allAccepted:!1};let x=await l.findEntryPointFile(t,a.language);if(!x)return n.printTestCaseResult({testCaseId:_,decisionCode:i.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${a.language?`: language: ${a.language}`:``}`}),{allAccepted:!1};let S=u.findLanguageDefinitionByPath(x);if(!S)return n.printTestCaseResult({testCaseId:_,decisionCode:i.DecisionCode.WRONG_ANSWER,stderr:`unsupported language`}),{allAccepted:!1};let C={...process.env,CI:``,FORCE_COLOR:`0`},E=x;if(S.prebuild)try{await S.prebuild(t);let e=await l.findEntryPointFile(t,a.language);e&&(E=e)}catch(e){return n.printTestCaseResult({testCaseId:_,decisionCode:i.DecisionCode.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)}),{allAccepted:!1}}let
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../helpers/parseArgs.cjs"),n=require("../helpers/printTestCaseResult.cjs"),r=require("../helpers/sandboxUser.cjs"),i=require("../types/decisionCode.cjs"),a=require("../helpers/printDebugBanner.cjs"),o=require("../helpers/checkProblemDirIsolation.cjs"),s=require("../helpers/cleanWorkingDirectory.cjs"),c=require("../helpers/copyTestCaseFileInput.cjs"),l=require("../helpers/findEntryPointFile.cjs"),u=require("../helpers/findLanguageDefinitionByPath.cjs"),d=require("../helpers/judgeByStaticAnalysis.cjs"),f=require("../helpers/readOutputFiles.cjs"),p=require("../helpers/runCustomRunner.cjs"),m=require("../helpers/readProblemMarkdownFrontMatter.cjs"),h=require("../helpers/readTestCases.cjs"),g=require("../helpers/resolveCwds.cjs"),_=require("../helpers/spawnSyncWithTimeout.cjs");let v=require("node:path");v=e.__toESM(v,1);let y=require("zod");const b=y.z.object({language:y.z.union([y.z.string(),y.z.array(y.z.string())]).optional()});async function x(e,n){let r=t.parseArgs(process.argv),i=b.parse(r.params),{cwds:s,isDebugMode:c}=await g.resolveCwds(e,r.cwd);if(c){let t=s.find(e=>e.expectedResult===`accepted`);if(t){if(!(await o.checkProblemDirIsolation(e,t,i)).passed){process.exitCode=1;return}}else a.printDebugBanner([`[DEBUG MODE] isolated problem directory check skipped`,``,`No accepted model answer is available for checking that the copied judge still accepts a valid submission.`])}for(let t of s){c&&g.printDebugCwdBanner(e,t);let r=await S(e,t.cwd,i,n);c&&!C(t,r)&&(process.exitCode=1,g.printDebugExpectationFailureBanner(e,t))}}async function S(e,t,a,o){r.makeAccessibleToSandboxUser(t);let h=await m.readProblemMarkdownFrontMatter(e),g=await(o.readTestCases??D)(e),_=g[0]?.id??`prebuild`,v={buildTimeoutSeconds:o.limits?.buildTimeoutSeconds??10,maxOutputLength:o.limits?.maxOutputLength??5e4},y=o.runTimeoutSeconds??2,b=await d.judgeByStaticAnalysis(t,h);if(b)return n.printTestCaseResult({testCaseId:_,...b}),{allAccepted:!1};let x=await l.findEntryPointFile(t,a.language);if(!x)return n.printTestCaseResult({testCaseId:_,decisionCode:i.DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${a.language?`: language: ${a.language}`:``}`}),{allAccepted:!1};let S=u.findLanguageDefinitionByPath(x);if(!S)return n.printTestCaseResult({testCaseId:_,decisionCode:i.DecisionCode.WRONG_ANSWER,stderr:`unsupported language`}),{allAccepted:!1};let C={...process.env,CI:``,FORCE_COLOR:`0`},E=x;if(o.buildSubmission!==!1){if(S.prebuild)try{await S.prebuild(t);let e=await l.findEntryPointFile(t,a.language);e&&(E=e)}catch(e){return n.printTestCaseResult({testCaseId:_,decisionCode:i.DecisionCode.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)}),{allAccepted:!1}}let e=S.buildCommand?.(E);if(e){let r=w(e,{cwd:t,env:C,testCaseId:_,limits:v});if(r)return n.printTestCaseResult(r),{allAccepted:!1}}}let A=await s.snapshotWorkingDirectory(t);if(g.length===0)return n.printTestCaseResult({testCaseId:`default`,decisionCode:i.DecisionCode.ACCEPTED}),{allAccepted:!0};let j=g.shared?.fileInputPath;for(let e of g){j&&await c.copyTestCaseFileInput(j,t),e.fileInputPath&&await c.copyTestCaseFileInput(e.fileInputPath,t);let a=typeof h.timeLimitMs==`number`?h.timeLimitMs/1e3:y,l=S.command(E),u=e.input??``,d;try{o.resolveInput&&(u=await o.resolveInput({testCase:e,cwd:t,env:C})),d=o.runCommand?await p.runCustomRunner(()=>o.runCommand?.({testCase:e,command:r.wrapCommandWithSandboxUser(l),stdin:u,cwd:t,env:{...C,...r.getSandboxUserEnvOverrides(C)},timeLimitSeconds:a})):O(l,{stdin:u,cwd:t,env:C,timeLimitSeconds:a})}catch(r){return n.printTestCaseResult({testCaseId:e.id,decisionCode:i.DecisionCode.RUNTIME_ERROR,stdin:u,stderr:T(r)}),await s.cleanWorkingDirectory(t,A),{allAccepted:!1}}let m=await f.readOutputFiles(t,h.requiredOutputFilePaths??[]),g={timeLimitSeconds:a,outputLimitLength:v.maxOutputLength,problemMarkdownFrontMatter:{memoryLimitByte:h.memoryLimitByte,requiredOutputFilePaths:h.requiredOutputFilePaths}},_=k({runResult:d,outputFiles:m,context:g}),b=_;if(_.decisionCode===i.DecisionCode.ACCEPTED)try{let t=await o.test?.({testCase:e,runResult:d,outputFiles:m,context:g});t&&(b={decisionCode:t.decisionCode??_.decisionCode,feedbackMarkdown:t.feedbackMarkdown,stderr:t.stderr})}catch(e){b={decisionCode:i.DecisionCode.RUNTIME_ERROR,stderr:T(e)}}if(n.printTestCaseResult({testCaseId:e.id,...b,exitStatus:d.status,stdin:d.stdin,stdout:d.stdout.slice(0,v.maxOutputLength)||void 0,stderr:(b.stderr??d.stderr).slice(0,v.maxOutputLength)||void 0,timeSeconds:d.timeSeconds,memoryBytes:d.memoryBytes,outputFiles:m.length>0?m:void 0}),await s.cleanWorkingDirectory(t,A),b.decisionCode!==i.DecisionCode.ACCEPTED)return{allAccepted:!1}}return{allAccepted:!0}}function C(e,t){return t.allAccepted===(e.expectedResult===`accepted`)}function w(e,t){let n=_.spawnSyncWithTimeout(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,env:t.env},t.limits.buildTimeoutSeconds),r=n.status??void 0;if(n.status!==0)return{testCaseId:t.testCaseId,decisionCode:i.DecisionCode.BUILD_ERROR,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.timeSeconds>t.limits.buildTimeoutSeconds)return{testCaseId:t.testCaseId,decisionCode:i.DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.stdout.length>t.limits.maxOutputLength||n.stderr.length>t.limits.maxOutputLength)return{testCaseId:t.testCaseId,decisionCode:i.DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function T(e){return e instanceof Error?e.message:String(e)}function E(e){return{id:e.id,input:e.input,fileInputPath:e.fileInputPath}}async function D(e){let t=await h.readTestCases(v.default.join(e,`test_cases`)),n=t.map(e=>E(e));return t.shared?.fileInputPath?Object.assign(n,{shared:{fileInputPath:t.shared.fileInputPath}}):n}function O(e,t){let n=_.spawnSyncWithTimeout(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,input:t.stdin,env:t.env},t.timeLimitSeconds);return{stdin:t.stdin,stdout:n.stdout,stderr:n.stderr,status:n.status??void 0,timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function k(e){if(e.runResult.status!==0)return{decisionCode:i.DecisionCode.RUNTIME_ERROR,stderr:e.runResult.stderr};if(e.runResult.timeSeconds>e.context.timeLimitSeconds)return{decisionCode:i.DecisionCode.TIME_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.memoryBytes>(e.context.problemMarkdownFrontMatter.memoryLimitByte??1/0))return{decisionCode:i.DecisionCode.MEMORY_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.outputLimitExceeded||e.runResult.stdout.length>e.context.outputLimitLength||e.runResult.stderr.length>e.context.outputLimitLength)return{decisionCode:i.DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED,stderr:e.runResult.stderr};let t=e.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length??0;return e.outputFiles.length<t?{decisionCode:i.DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR}:{decisionCode:i.DecisionCode.ACCEPTED}}exports.commandJudgePreset=x;
|
|
2
2
|
//# sourceMappingURL=command.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.cjs","names":["z","parseArgs","resolveCwds","checkProblemDirIsolation","readProblemMarkdownFrontMatter","judgeByStaticAnalysis","findEntryPointFile","DecisionCode","findLanguageDefinitionByPath","snapshotWorkingDirectory","copyTestCaseFileInput","runCustomRunner","wrapCommandWithSandboxUser","getSandboxUserEnvOverrides","cleanWorkingDirectory","readOutputFiles","spawnSyncWithTimeout","readFileTestCases","path"],"sources":["../../src/presets/command.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { z } from 'zod';\n\nimport { checkProblemDirIsolation } from '../helpers/checkProblemDirIsolation.js';\nimport { cleanWorkingDirectory, snapshotWorkingDirectory } from '../helpers/cleanWorkingDirectory.js';\nimport { copyTestCaseFileInput } from '../helpers/copyTestCaseFileInput.js';\nimport { findEntryPointFile } from '../helpers/findEntryPointFile.js';\nimport { findLanguageDefinitionByPath } from '../helpers/findLanguageDefinitionByPath.js';\nimport { judgeByStaticAnalysis } from '../helpers/judgeByStaticAnalysis.js';\nimport { parseArgs } from '../helpers/parseArgs.js';\nimport { printDebugBanner } from '../helpers/printDebugBanner.js';\nimport { printTestCaseResult } from '../helpers/printTestCaseResult.js';\nimport { readOutputFiles } from '../helpers/readOutputFiles.js';\nimport { runCustomRunner } from '../helpers/runCustomRunner.js';\nimport {\n getSandboxUserEnvOverrides,\n makeAccessibleToSandboxUser,\n wrapCommandWithSandboxUser,\n} from '../helpers/sandboxUser.js';\nimport { readProblemMarkdownFrontMatter } from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases as readFileTestCases } from '../helpers/readTestCases.js';\nimport {\n printDebugCwdBanner,\n printDebugExpectationFailureBanner,\n resolveCwds,\n type ResolvedCwd,\n} from '../helpers/resolveCwds.js';\nimport { spawnSyncWithTimeout } from '../helpers/spawnSyncWithTimeout.js';\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nconst BUILD_TIMEOUT_SECONDS = 10;\nconst JUDGE_DEFAULT_TIMEOUT_SECONDS = 2;\nconst MAX_STDOUT_LENGTH = 50_000;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\ntype JudgeParams = z.infer<typeof judgeParamsSchema>;\n\ninterface BaseCommandTestCase {\n id: string;\n input?: string;\n fileInputPath?: string;\n}\n\ntype CommandJudgeCaseResult = Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown' | 'stderr'>;\n\nexport interface CommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n outputLimitExceeded?: boolean;\n}\n\ninterface CommandJudgeContext {\n timeLimitSeconds: number;\n outputLimitLength: number;\n problemMarkdownFrontMatter: Pick<ProblemMarkdownFrontMatter, 'memoryLimitByte' | 'requiredOutputFilePaths'>;\n}\n\nexport interface CommandJudgeLimits {\n buildTimeoutSeconds: number;\n maxOutputLength: number;\n}\n\nexport interface CommandJudgePresetOptions<\n TTestCase extends BaseCommandTestCase = BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n> {\n limits?: CommandJudgeLimits;\n runTimeoutSeconds?: number;\n readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;\n /**\n * Runs as the trusted harness user with the submission's `cwd`. Fixture files it creates there\n * must be written with `createDirectoryWithoutFollowingSymlinks`/`writeFileWithoutFollowingSymlinks`\n * (both exported): a submission can plant a symlink at a fixture path, and a plain `fs.writeFile`\n * would follow it into a file only the harness can write.\n */\n resolveInput?: (context: { testCase: TTestCase; cwd: string; env: NodeJS.ProcessEnv }) => Promise<string> | string;\n runCommand?: (context: {\n testCase: TTestCase;\n /**\n * The command to run. Under `EXERCODE_SANDBOX_USER` delegation it is already wrapped so it\n * executes as the sandbox user; spawn it as given (with the supplied `env`) instead of\n * reconstructing it, or the submission runs as the trusted harness user.\n *\n * Its direct child is then a root-owned `sudo` whose descendants belong to the sandbox user, so\n * the handler cannot signal them: enforce `timeLimitSeconds` with `startSandboxTimeoutWatchdog`\n * and `killSandboxUserProcesses` (both exported) rather than `child.kill()` or an outer\n * `timeout`. The preset terminates leftover sandbox processes after the handler returns.\n */\n command: readonly [string, ...string[]];\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }) => Promise<TRunResult> | TRunResult;\n test?: (context: {\n testCase: TTestCase;\n runResult: TRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n }) => Promise<Partial<CommandJudgeCaseResult>> | Partial<CommandJudgeCaseResult> | undefined;\n}\n\n/**\n * A preset function for judging by executable command.\n *\n * Keep problem-specific logic in `resolveInput` and `test`.\n *\n * @example\n * Create `judge.ts`:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await commandJudgePreset(import.meta.dirname, {\n * readTestCases: async () => [\n * { id: '01', input: '1 2' },\n * ],\n * test: ({ runResult }) => {\n * return runResult.stdout.trim() === '3'\n * ? { decisionCode: DecisionCode.ACCEPTED }\n * : { decisionCode: DecisionCode.WRONG_ANSWER };\n * },\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/python '{ \"language\": \"python\" }'\n * ```\n *\n * Run without a cwd argument to judge each `<problemDir>/model_answers/*` directory\n * for debugging. A prominent banner is printed for each run.\n * ```bash\n * bun judge.ts\n * ```\n */\nexport async function commandJudgePreset<\n TTestCase extends BaseCommandTestCase = BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(problemDir: string, options: CommandJudgePresetOptions<TTestCase, TRunResult>): Promise<void> {\n const args = parseArgs(process.argv);\n const params = judgeParamsSchema.parse(args.params);\n\n const { cwds, isDebugMode } = await resolveCwds(problemDir, args.cwd);\n\n if (isDebugMode) {\n const acceptedCwd = cwds.find((cwd) => cwd.expectedResult === 'accepted');\n if (acceptedCwd) {\n const isolationCheckResult = await checkProblemDirIsolation(problemDir, acceptedCwd, params);\n if (!isolationCheckResult.passed) {\n process.exitCode = 1;\n return;\n }\n } else {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check skipped',\n '',\n 'No accepted model answer is available for checking that the copied judge still accepts a valid submission.',\n ]);\n }\n }\n\n for (const resolvedCwd of cwds) {\n if (isDebugMode) printDebugCwdBanner(problemDir, resolvedCwd);\n const result = await runCommandJudgeForCwd<TTestCase, TRunResult>(problemDir, resolvedCwd.cwd, params, options);\n if (isDebugMode && !matchesExpectedResult(resolvedCwd, result)) {\n process.exitCode = 1;\n printDebugExpectationFailureBanner(problemDir, resolvedCwd);\n }\n }\n}\n\nasync function runCommandJudgeForCwd<\n TTestCase extends BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(\n problemDir: string,\n cwd: string,\n params: JudgeParams,\n options: CommandJudgePresetOptions<TTestCase, TRunResult>\n): Promise<{ allAccepted: boolean }> {\n // The sandboxed submission must read its sources and write build/run outputs in its directory.\n makeAccessibleToSandboxUser(cwd);\n\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await (options.readTestCases ?? readCommandTestCases)(problemDir);\n const prebuildTestCaseId = testCases[0]?.id ?? 'prebuild';\n const limits = {\n buildTimeoutSeconds: options.limits?.buildTimeoutSeconds ?? BUILD_TIMEOUT_SECONDS,\n maxOutputLength: options.limits?.maxOutputLength ?? MAX_STDOUT_LENGTH,\n };\n const runTimeoutSeconds = options.runTimeoutSeconds ?? JUDGE_DEFAULT_TIMEOUT_SECONDS;\n\n const staticAnalysisResult = await judgeByStaticAnalysis(cwd, problemMarkdownFrontMatter);\n if (staticAnalysisResult) {\n printTestCaseResult({ testCaseId: prebuildTestCaseId, ...staticAnalysisResult });\n return { allAccepted: false };\n }\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return { allAccepted: false };\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return { allAccepted: false };\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let mainFilePath = originalMainFilePath;\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n const prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n if (prebuiltMainFilePath) mainFilePath = prebuiltMainFilePath;\n } catch (error) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return { allAccepted: false };\n }\n }\n\n const buildCommand = languageDefinition.buildCommand?.(mainFilePath);\n if (buildCommand) {\n const buildResult = runBuild(buildCommand, {\n cwd,\n env,\n testCaseId: prebuildTestCaseId,\n limits,\n });\n if (buildResult) {\n printTestCaseResult(buildResult);\n return { allAccepted: false };\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n return { allAccepted: true };\n }\n\n const sharedFileInputPath = (testCases as { shared?: { fileInputPath?: string } }).shared?.fileInputPath;\n\n for (const testCase of testCases) {\n if (sharedFileInputPath) await copyTestCaseFileInput(sharedFileInputPath, cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, cwd);\n\n const timeLimitSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : runTimeoutSeconds;\n\n const command = languageDefinition.command(mainFilePath);\n let stdin = testCase.input ?? '';\n let runResult: TRunResult;\n try {\n if (options.resolveInput) {\n stdin = await options.resolveInput({ testCase, cwd, env });\n }\n\n runResult = options.runCommand\n ? await runCustomRunner(\n () =>\n // Hand custom runners a command that is already sandbox-wrapped, and the matching\n // environment: they spawn it themselves, so an unwrapped command would run the\n // submission as the trusted harness user and defeat the delegation boundary.\n options.runCommand?.({\n testCase,\n command: wrapCommandWithSandboxUser(command),\n stdin,\n cwd,\n env: { ...env, ...getSandboxUserEnvOverrides(env) },\n timeLimitSeconds,\n }) as Promise<TRunResult>\n )\n : (runCommand(command, {\n stdin,\n cwd,\n env,\n timeLimitSeconds,\n }) as TRunResult);\n } catch (error) {\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stdin,\n stderr: errorToMessage(error),\n });\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n return { allAccepted: false };\n }\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n const judgeContext: CommandJudgeContext = {\n timeLimitSeconds,\n outputLimitLength: limits.maxOutputLength,\n problemMarkdownFrontMatter: {\n memoryLimitByte: problemMarkdownFrontMatter.memoryLimitByte,\n requiredOutputFilePaths: problemMarkdownFrontMatter.requiredOutputFilePaths,\n },\n };\n const baseJudgeResult = evaluateByLimits({ runResult, outputFiles, context: judgeContext });\n let judgeResult = baseJudgeResult;\n if (baseJudgeResult.decisionCode === DecisionCode.ACCEPTED) {\n try {\n const extendedJudgeResult = await options.test?.({ testCase, runResult, outputFiles, context: judgeContext });\n if (extendedJudgeResult) {\n judgeResult = {\n decisionCode: extendedJudgeResult.decisionCode ?? baseJudgeResult.decisionCode,\n feedbackMarkdown: extendedJudgeResult.feedbackMarkdown,\n stderr: extendedJudgeResult.stderr,\n };\n }\n } catch (error) {\n judgeResult = {\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stderr: errorToMessage(error),\n };\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n ...judgeResult,\n exitStatus: runResult.status,\n stdin: runResult.stdin,\n stdout: runResult.stdout.slice(0, limits.maxOutputLength) || undefined,\n stderr: (judgeResult.stderr ?? runResult.stderr).slice(0, limits.maxOutputLength) || undefined,\n timeSeconds: runResult.timeSeconds,\n memoryBytes: runResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n if (judgeResult.decisionCode !== DecisionCode.ACCEPTED) return { allAccepted: false };\n }\n\n return { allAccepted: true };\n}\n\nfunction matchesExpectedResult(resolvedCwd: ResolvedCwd, result: { allAccepted: boolean }): boolean {\n return result.allAccepted === (resolvedCwd.expectedResult === 'accepted');\n}\n\nfunction runBuild(\n buildCommand: readonly [string, ...string[]],\n context: { cwd: string; env: NodeJS.ProcessEnv; testCaseId: string; limits: CommandJudgeLimits }\n): (Omit<TestCaseResult, 'testCaseId'> & { testCaseId: string }) | undefined {\n const spawnResult = spawnSyncWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: context.cwd, encoding: 'utf8', env: context.env },\n context.limits.buildTimeoutSeconds\n );\n const exitStatus = spawnResult.status ?? undefined;\n\n if (spawnResult.status !== 0) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (spawnResult.timeSeconds > context.limits.buildTimeoutSeconds) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (\n spawnResult.stdout.length > context.limits.maxOutputLength ||\n spawnResult.stderr.length > context.limits.maxOutputLength\n ) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n return;\n}\n\nfunction errorToMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction toCommandTestCase(value: {\n id: string;\n input?: string;\n fileInputPath?: string;\n fileOutputPath?: string;\n output?: string;\n}): BaseCommandTestCase {\n return { id: value.id, input: value.input, fileInputPath: value.fileInputPath };\n}\n\nasync function readCommandTestCases<TTestCase extends BaseCommandTestCase = BaseCommandTestCase>(\n problemDir: string\n): Promise<readonly TTestCase[]> {\n const fileTestCases = await readFileTestCases(path.join(problemDir, 'test_cases'));\n const commandTestCases = fileTestCases.map((testCase) => toCommandTestCase(testCase) as TTestCase);\n if (fileTestCases.shared?.fileInputPath) {\n return Object.assign(commandTestCases, { shared: { fileInputPath: fileTestCases.shared.fileInputPath } });\n }\n return commandTestCases;\n}\n\nfunction runCommand(\n command: readonly [string, ...string[]],\n context: {\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }\n): CommandRunResult {\n const spawnResult = spawnSyncWithTimeout(\n command[0],\n command.slice(1),\n { cwd: context.cwd, encoding: 'utf8', input: context.stdin, env: context.env },\n context.timeLimitSeconds\n );\n\n return {\n stdin: context.stdin,\n stdout: spawnResult.stdout,\n stderr: spawnResult.stderr,\n status: spawnResult.status ?? undefined,\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n}\n\nfunction evaluateByLimits(context: {\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n}): CommandJudgeCaseResult {\n if (context.runResult.status !== 0) {\n return { decisionCode: DecisionCode.RUNTIME_ERROR, stderr: context.runResult.stderr };\n }\n\n if (context.runResult.timeSeconds > context.context.timeLimitSeconds) {\n return { decisionCode: DecisionCode.TIME_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.memoryBytes >\n (context.context.problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)\n ) {\n return { decisionCode: DecisionCode.MEMORY_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.outputLimitExceeded ||\n context.runResult.stdout.length > context.context.outputLimitLength ||\n context.runResult.stderr.length > context.context.outputLimitLength\n ) {\n return { decisionCode: DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n const requiredOutputFileCount = context.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0;\n if (context.outputFiles.length < requiredOutputFileCount) {\n return { decisionCode: DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR };\n }\n\n return { decisionCode: DecisionCode.ACCEPTED };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"command.cjs","names":["z","parseArgs","resolveCwds","checkProblemDirIsolation","readProblemMarkdownFrontMatter","judgeByStaticAnalysis","findEntryPointFile","DecisionCode","findLanguageDefinitionByPath","snapshotWorkingDirectory","copyTestCaseFileInput","runCustomRunner","wrapCommandWithSandboxUser","getSandboxUserEnvOverrides","cleanWorkingDirectory","readOutputFiles","spawnSyncWithTimeout","readFileTestCases","path"],"sources":["../../src/presets/command.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { z } from 'zod';\n\nimport { checkProblemDirIsolation } from '../helpers/checkProblemDirIsolation.js';\nimport { cleanWorkingDirectory, snapshotWorkingDirectory } from '../helpers/cleanWorkingDirectory.js';\nimport { copyTestCaseFileInput } from '../helpers/copyTestCaseFileInput.js';\nimport { findEntryPointFile } from '../helpers/findEntryPointFile.js';\nimport { findLanguageDefinitionByPath } from '../helpers/findLanguageDefinitionByPath.js';\nimport { judgeByStaticAnalysis } from '../helpers/judgeByStaticAnalysis.js';\nimport { parseArgs } from '../helpers/parseArgs.js';\nimport { printDebugBanner } from '../helpers/printDebugBanner.js';\nimport { printTestCaseResult } from '../helpers/printTestCaseResult.js';\nimport { readOutputFiles } from '../helpers/readOutputFiles.js';\nimport { runCustomRunner } from '../helpers/runCustomRunner.js';\nimport {\n getSandboxUserEnvOverrides,\n makeAccessibleToSandboxUser,\n wrapCommandWithSandboxUser,\n} from '../helpers/sandboxUser.js';\nimport { readProblemMarkdownFrontMatter } from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases as readFileTestCases } from '../helpers/readTestCases.js';\nimport {\n printDebugCwdBanner,\n printDebugExpectationFailureBanner,\n resolveCwds,\n type ResolvedCwd,\n} from '../helpers/resolveCwds.js';\nimport { spawnSyncWithTimeout } from '../helpers/spawnSyncWithTimeout.js';\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nconst BUILD_TIMEOUT_SECONDS = 10;\nconst JUDGE_DEFAULT_TIMEOUT_SECONDS = 2;\nconst MAX_STDOUT_LENGTH = 50_000;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\ntype JudgeParams = z.infer<typeof judgeParamsSchema>;\n\ninterface BaseCommandTestCase {\n id: string;\n input?: string;\n fileInputPath?: string;\n}\n\ntype CommandJudgeCaseResult = Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown' | 'stderr'>;\n\nexport interface CommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n outputLimitExceeded?: boolean;\n}\n\ninterface CommandJudgeContext {\n timeLimitSeconds: number;\n outputLimitLength: number;\n problemMarkdownFrontMatter: Pick<ProblemMarkdownFrontMatter, 'memoryLimitByte' | 'requiredOutputFilePaths'>;\n}\n\nexport interface CommandJudgeLimits {\n buildTimeoutSeconds: number;\n maxOutputLength: number;\n}\n\nexport interface CommandJudgePresetOptions<\n TTestCase extends BaseCommandTestCase = BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n> {\n /**\n * Set to false when the custom runner builds the submitted source itself.\n * Defaults to true.\n */\n buildSubmission?: boolean;\n limits?: CommandJudgeLimits;\n runTimeoutSeconds?: number;\n readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;\n /**\n * Runs as the trusted harness user with the submission's `cwd`. Fixture files it creates there\n * must be written with `createDirectoryWithoutFollowingSymlinks`/`writeFileWithoutFollowingSymlinks`\n * (both exported): a submission can plant a symlink at a fixture path, and a plain `fs.writeFile`\n * would follow it into a file only the harness can write.\n */\n resolveInput?: (context: { testCase: TTestCase; cwd: string; env: NodeJS.ProcessEnv }) => Promise<string> | string;\n runCommand?: (context: {\n testCase: TTestCase;\n /**\n * The command to run. Under `EXERCODE_SANDBOX_USER` delegation it is already wrapped so it\n * executes as the sandbox user; spawn it as given (with the supplied `env`) instead of\n * reconstructing it, or the submission runs as the trusted harness user.\n *\n * Its direct child is then a root-owned `sudo` whose descendants belong to the sandbox user, so\n * the handler cannot signal them: enforce `timeLimitSeconds` with `startSandboxTimeoutWatchdog`\n * and `killSandboxUserProcesses` (both exported) rather than `child.kill()` or an outer\n * `timeout`. The preset terminates leftover sandbox processes after the handler returns.\n */\n command: readonly [string, ...string[]];\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }) => Promise<TRunResult> | TRunResult;\n test?: (context: {\n testCase: TTestCase;\n runResult: TRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n }) => Promise<Partial<CommandJudgeCaseResult>> | Partial<CommandJudgeCaseResult> | undefined;\n}\n\n/**\n * A preset function for judging by executable command.\n *\n * Keep problem-specific logic in `resolveInput` and `test`.\n *\n * @example\n * Create `judge.ts`:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await commandJudgePreset(import.meta.dirname, {\n * readTestCases: async () => [\n * { id: '01', input: '1 2' },\n * ],\n * test: ({ runResult }) => {\n * return runResult.stdout.trim() === '3'\n * ? { decisionCode: DecisionCode.ACCEPTED }\n * : { decisionCode: DecisionCode.WRONG_ANSWER };\n * },\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/python '{ \"language\": \"python\" }'\n * ```\n *\n * Run without a cwd argument to judge each `<problemDir>/model_answers/*` directory\n * for debugging. A prominent banner is printed for each run.\n * ```bash\n * bun judge.ts\n * ```\n */\nexport async function commandJudgePreset<\n TTestCase extends BaseCommandTestCase = BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(problemDir: string, options: CommandJudgePresetOptions<TTestCase, TRunResult>): Promise<void> {\n const args = parseArgs(process.argv);\n const params = judgeParamsSchema.parse(args.params);\n\n const { cwds, isDebugMode } = await resolveCwds(problemDir, args.cwd);\n\n if (isDebugMode) {\n const acceptedCwd = cwds.find((cwd) => cwd.expectedResult === 'accepted');\n if (acceptedCwd) {\n const isolationCheckResult = await checkProblemDirIsolation(problemDir, acceptedCwd, params);\n if (!isolationCheckResult.passed) {\n process.exitCode = 1;\n return;\n }\n } else {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check skipped',\n '',\n 'No accepted model answer is available for checking that the copied judge still accepts a valid submission.',\n ]);\n }\n }\n\n for (const resolvedCwd of cwds) {\n if (isDebugMode) printDebugCwdBanner(problemDir, resolvedCwd);\n const result = await runCommandJudgeForCwd<TTestCase, TRunResult>(problemDir, resolvedCwd.cwd, params, options);\n if (isDebugMode && !matchesExpectedResult(resolvedCwd, result)) {\n process.exitCode = 1;\n printDebugExpectationFailureBanner(problemDir, resolvedCwd);\n }\n }\n}\n\nasync function runCommandJudgeForCwd<\n TTestCase extends BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(\n problemDir: string,\n cwd: string,\n params: JudgeParams,\n options: CommandJudgePresetOptions<TTestCase, TRunResult>\n): Promise<{ allAccepted: boolean }> {\n // The sandboxed submission must read its sources and write build/run outputs in its directory.\n makeAccessibleToSandboxUser(cwd);\n\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await (options.readTestCases ?? readCommandTestCases)(problemDir);\n const prebuildTestCaseId = testCases[0]?.id ?? 'prebuild';\n const limits = {\n buildTimeoutSeconds: options.limits?.buildTimeoutSeconds ?? BUILD_TIMEOUT_SECONDS,\n maxOutputLength: options.limits?.maxOutputLength ?? MAX_STDOUT_LENGTH,\n };\n const runTimeoutSeconds = options.runTimeoutSeconds ?? JUDGE_DEFAULT_TIMEOUT_SECONDS;\n\n const staticAnalysisResult = await judgeByStaticAnalysis(cwd, problemMarkdownFrontMatter);\n if (staticAnalysisResult) {\n printTestCaseResult({ testCaseId: prebuildTestCaseId, ...staticAnalysisResult });\n return { allAccepted: false };\n }\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return { allAccepted: false };\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return { allAccepted: false };\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let mainFilePath = originalMainFilePath;\n if (options.buildSubmission !== false) {\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n const prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n if (prebuiltMainFilePath) mainFilePath = prebuiltMainFilePath;\n } catch (error) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return { allAccepted: false };\n }\n }\n\n const buildCommand = languageDefinition.buildCommand?.(mainFilePath);\n if (buildCommand) {\n const buildResult = runBuild(buildCommand, {\n cwd,\n env,\n testCaseId: prebuildTestCaseId,\n limits,\n });\n if (buildResult) {\n printTestCaseResult(buildResult);\n return { allAccepted: false };\n }\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n return { allAccepted: true };\n }\n\n const sharedFileInputPath = (testCases as { shared?: { fileInputPath?: string } }).shared?.fileInputPath;\n\n for (const testCase of testCases) {\n if (sharedFileInputPath) await copyTestCaseFileInput(sharedFileInputPath, cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, cwd);\n\n const timeLimitSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : runTimeoutSeconds;\n\n const command = languageDefinition.command(mainFilePath);\n let stdin = testCase.input ?? '';\n let runResult: TRunResult;\n try {\n if (options.resolveInput) {\n stdin = await options.resolveInput({ testCase, cwd, env });\n }\n\n runResult = options.runCommand\n ? await runCustomRunner(\n () =>\n // Hand custom runners a command that is already sandbox-wrapped, and the matching\n // environment: they spawn it themselves, so an unwrapped command would run the\n // submission as the trusted harness user and defeat the delegation boundary.\n options.runCommand?.({\n testCase,\n command: wrapCommandWithSandboxUser(command),\n stdin,\n cwd,\n env: { ...env, ...getSandboxUserEnvOverrides(env) },\n timeLimitSeconds,\n }) as Promise<TRunResult>\n )\n : (runCommand(command, {\n stdin,\n cwd,\n env,\n timeLimitSeconds,\n }) as TRunResult);\n } catch (error) {\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stdin,\n stderr: errorToMessage(error),\n });\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n return { allAccepted: false };\n }\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n const judgeContext: CommandJudgeContext = {\n timeLimitSeconds,\n outputLimitLength: limits.maxOutputLength,\n problemMarkdownFrontMatter: {\n memoryLimitByte: problemMarkdownFrontMatter.memoryLimitByte,\n requiredOutputFilePaths: problemMarkdownFrontMatter.requiredOutputFilePaths,\n },\n };\n const baseJudgeResult = evaluateByLimits({ runResult, outputFiles, context: judgeContext });\n let judgeResult = baseJudgeResult;\n if (baseJudgeResult.decisionCode === DecisionCode.ACCEPTED) {\n try {\n const extendedJudgeResult = await options.test?.({ testCase, runResult, outputFiles, context: judgeContext });\n if (extendedJudgeResult) {\n judgeResult = {\n decisionCode: extendedJudgeResult.decisionCode ?? baseJudgeResult.decisionCode,\n feedbackMarkdown: extendedJudgeResult.feedbackMarkdown,\n stderr: extendedJudgeResult.stderr,\n };\n }\n } catch (error) {\n judgeResult = {\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stderr: errorToMessage(error),\n };\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n ...judgeResult,\n exitStatus: runResult.status,\n stdin: runResult.stdin,\n stdout: runResult.stdout.slice(0, limits.maxOutputLength) || undefined,\n stderr: (judgeResult.stderr ?? runResult.stderr).slice(0, limits.maxOutputLength) || undefined,\n timeSeconds: runResult.timeSeconds,\n memoryBytes: runResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n if (judgeResult.decisionCode !== DecisionCode.ACCEPTED) return { allAccepted: false };\n }\n\n return { allAccepted: true };\n}\n\nfunction matchesExpectedResult(resolvedCwd: ResolvedCwd, result: { allAccepted: boolean }): boolean {\n return result.allAccepted === (resolvedCwd.expectedResult === 'accepted');\n}\n\nfunction runBuild(\n buildCommand: readonly [string, ...string[]],\n context: { cwd: string; env: NodeJS.ProcessEnv; testCaseId: string; limits: CommandJudgeLimits }\n): (Omit<TestCaseResult, 'testCaseId'> & { testCaseId: string }) | undefined {\n const spawnResult = spawnSyncWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: context.cwd, encoding: 'utf8', env: context.env },\n context.limits.buildTimeoutSeconds\n );\n const exitStatus = spawnResult.status ?? undefined;\n\n if (spawnResult.status !== 0) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (spawnResult.timeSeconds > context.limits.buildTimeoutSeconds) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (\n spawnResult.stdout.length > context.limits.maxOutputLength ||\n spawnResult.stderr.length > context.limits.maxOutputLength\n ) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n return;\n}\n\nfunction errorToMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction toCommandTestCase(value: {\n id: string;\n input?: string;\n fileInputPath?: string;\n fileOutputPath?: string;\n output?: string;\n}): BaseCommandTestCase {\n return { id: value.id, input: value.input, fileInputPath: value.fileInputPath };\n}\n\nasync function readCommandTestCases<TTestCase extends BaseCommandTestCase = BaseCommandTestCase>(\n problemDir: string\n): Promise<readonly TTestCase[]> {\n const fileTestCases = await readFileTestCases(path.join(problemDir, 'test_cases'));\n const commandTestCases = fileTestCases.map((testCase) => toCommandTestCase(testCase) as TTestCase);\n if (fileTestCases.shared?.fileInputPath) {\n return Object.assign(commandTestCases, { shared: { fileInputPath: fileTestCases.shared.fileInputPath } });\n }\n return commandTestCases;\n}\n\nfunction runCommand(\n command: readonly [string, ...string[]],\n context: {\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }\n): CommandRunResult {\n const spawnResult = spawnSyncWithTimeout(\n command[0],\n command.slice(1),\n { cwd: context.cwd, encoding: 'utf8', input: context.stdin, env: context.env },\n context.timeLimitSeconds\n );\n\n return {\n stdin: context.stdin,\n stdout: spawnResult.stdout,\n stderr: spawnResult.stderr,\n status: spawnResult.status ?? undefined,\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n}\n\nfunction evaluateByLimits(context: {\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n}): CommandJudgeCaseResult {\n if (context.runResult.status !== 0) {\n return { decisionCode: DecisionCode.RUNTIME_ERROR, stderr: context.runResult.stderr };\n }\n\n if (context.runResult.timeSeconds > context.context.timeLimitSeconds) {\n return { decisionCode: DecisionCode.TIME_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.memoryBytes >\n (context.context.problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)\n ) {\n return { decisionCode: DecisionCode.MEMORY_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.outputLimitExceeded ||\n context.runResult.stdout.length > context.context.outputLimitLength ||\n context.runResult.stderr.length > context.context.outputLimitLength\n ) {\n return { decisionCode: DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n const requiredOutputFileCount = context.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0;\n if (context.outputFiles.length < requiredOutputFileCount) {\n return { decisionCode: DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR };\n }\n\n return { decisionCode: DecisionCode.ACCEPTED };\n}\n"],"mappings":"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"}
|
|
@@ -25,6 +25,11 @@ export interface CommandJudgeLimits {
|
|
|
25
25
|
maxOutputLength: number;
|
|
26
26
|
}
|
|
27
27
|
export interface CommandJudgePresetOptions<TTestCase extends BaseCommandTestCase = BaseCommandTestCase, TRunResult extends CommandRunResult = CommandRunResult> {
|
|
28
|
+
/**
|
|
29
|
+
* Set to false when the custom runner builds the submitted source itself.
|
|
30
|
+
* Defaults to true.
|
|
31
|
+
*/
|
|
32
|
+
buildSubmission?: boolean;
|
|
28
33
|
limits?: CommandJudgeLimits;
|
|
29
34
|
runTimeoutSeconds?: number;
|
|
30
35
|
readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;
|
package/dist/presets/command.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{parseArgs as e}from"../helpers/parseArgs.js";import{printTestCaseResult as t}from"../helpers/printTestCaseResult.js";import{getSandboxUserEnvOverrides as n,makeAccessibleToSandboxUser as r,wrapCommandWithSandboxUser as i}from"../helpers/sandboxUser.js";import{DecisionCode as a}from"../types/decisionCode.js";import{printDebugBanner as o}from"../helpers/printDebugBanner.js";import{checkProblemDirIsolation as s}from"../helpers/checkProblemDirIsolation.js";import{cleanWorkingDirectory as c,snapshotWorkingDirectory as l}from"../helpers/cleanWorkingDirectory.js";import{copyTestCaseFileInput as u}from"../helpers/copyTestCaseFileInput.js";import{findEntryPointFile as d}from"../helpers/findEntryPointFile.js";import{findLanguageDefinitionByPath as f}from"../helpers/findLanguageDefinitionByPath.js";import{judgeByStaticAnalysis as p}from"../helpers/judgeByStaticAnalysis.js";import{readOutputFiles as m}from"../helpers/readOutputFiles.js";import{runCustomRunner as h}from"../helpers/runCustomRunner.js";import{readProblemMarkdownFrontMatter as g}from"../helpers/readProblemMarkdownFrontMatter.js";import{readTestCases as _}from"../helpers/readTestCases.js";import{printDebugCwdBanner as v,printDebugExpectationFailureBanner as y,resolveCwds as b}from"../helpers/resolveCwds.js";import{spawnSyncWithTimeout as x}from"../helpers/spawnSyncWithTimeout.js";import S from"node:path";import{z as C}from"zod";const w=C.object({language:C.union([C.string(),C.array(C.string())]).optional()});async function T(t,n){let r=e(process.argv),i=w.parse(r.params),{cwds:a,isDebugMode:c}=await b(t,r.cwd);if(c){let e=a.find(e=>e.expectedResult===`accepted`);if(e){if(!(await s(t,e,i)).passed){process.exitCode=1;return}}else o([`[DEBUG MODE] isolated problem directory check skipped`,``,`No accepted model answer is available for checking that the copied judge still accepts a valid submission.`])}for(let e of a){c&&v(t,e);let r=await E(t,e.cwd,i,n);c&&!D(e,r)&&(process.exitCode=1,y(t,e))}}async function E(e,o,s,_){r(o);let v=await g(e),y=await(_.readTestCases??j)(e),b=y[0]?.id??`prebuild`,x={buildTimeoutSeconds:_.limits?.buildTimeoutSeconds??10,maxOutputLength:_.limits?.maxOutputLength??5e4},S=_.runTimeoutSeconds??2,C=await p(o,v);if(C)return t({testCaseId:b,...C}),{allAccepted:!1};let w=await d(o,s.language);if(!w)return t({testCaseId:b,decisionCode:a.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${s.language?`: language: ${s.language}`:``}`}),{allAccepted:!1};let T=f(w);if(!T)return t({testCaseId:b,decisionCode:a.WRONG_ANSWER,stderr:`unsupported language`}),{allAccepted:!1};let E={...process.env,CI:``,FORCE_COLOR:`0`},D=w;if(T.prebuild)try{await T.prebuild(o);let e=await d(o,s.language);e&&(D=e)}catch(e){return t({testCaseId:b,decisionCode:a.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)}),{allAccepted:!1}}let
|
|
1
|
+
import{parseArgs as e}from"../helpers/parseArgs.js";import{printTestCaseResult as t}from"../helpers/printTestCaseResult.js";import{getSandboxUserEnvOverrides as n,makeAccessibleToSandboxUser as r,wrapCommandWithSandboxUser as i}from"../helpers/sandboxUser.js";import{DecisionCode as a}from"../types/decisionCode.js";import{printDebugBanner as o}from"../helpers/printDebugBanner.js";import{checkProblemDirIsolation as s}from"../helpers/checkProblemDirIsolation.js";import{cleanWorkingDirectory as c,snapshotWorkingDirectory as l}from"../helpers/cleanWorkingDirectory.js";import{copyTestCaseFileInput as u}from"../helpers/copyTestCaseFileInput.js";import{findEntryPointFile as d}from"../helpers/findEntryPointFile.js";import{findLanguageDefinitionByPath as f}from"../helpers/findLanguageDefinitionByPath.js";import{judgeByStaticAnalysis as p}from"../helpers/judgeByStaticAnalysis.js";import{readOutputFiles as m}from"../helpers/readOutputFiles.js";import{runCustomRunner as h}from"../helpers/runCustomRunner.js";import{readProblemMarkdownFrontMatter as g}from"../helpers/readProblemMarkdownFrontMatter.js";import{readTestCases as _}from"../helpers/readTestCases.js";import{printDebugCwdBanner as v,printDebugExpectationFailureBanner as y,resolveCwds as b}from"../helpers/resolveCwds.js";import{spawnSyncWithTimeout as x}from"../helpers/spawnSyncWithTimeout.js";import S from"node:path";import{z as C}from"zod";const w=C.object({language:C.union([C.string(),C.array(C.string())]).optional()});async function T(t,n){let r=e(process.argv),i=w.parse(r.params),{cwds:a,isDebugMode:c}=await b(t,r.cwd);if(c){let e=a.find(e=>e.expectedResult===`accepted`);if(e){if(!(await s(t,e,i)).passed){process.exitCode=1;return}}else o([`[DEBUG MODE] isolated problem directory check skipped`,``,`No accepted model answer is available for checking that the copied judge still accepts a valid submission.`])}for(let e of a){c&&v(t,e);let r=await E(t,e.cwd,i,n);c&&!D(e,r)&&(process.exitCode=1,y(t,e))}}async function E(e,o,s,_){r(o);let v=await g(e),y=await(_.readTestCases??j)(e),b=y[0]?.id??`prebuild`,x={buildTimeoutSeconds:_.limits?.buildTimeoutSeconds??10,maxOutputLength:_.limits?.maxOutputLength??5e4},S=_.runTimeoutSeconds??2,C=await p(o,v);if(C)return t({testCaseId:b,...C}),{allAccepted:!1};let w=await d(o,s.language);if(!w)return t({testCaseId:b,decisionCode:a.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,stderr:`main file not found${s.language?`: language: ${s.language}`:``}`}),{allAccepted:!1};let T=f(w);if(!T)return t({testCaseId:b,decisionCode:a.WRONG_ANSWER,stderr:`unsupported language`}),{allAccepted:!1};let E={...process.env,CI:``,FORCE_COLOR:`0`},D=w;if(_.buildSubmission!==!1){if(T.prebuild)try{await T.prebuild(o);let e=await d(o,s.language);e&&(D=e)}catch(e){return t({testCaseId:b,decisionCode:a.BUILD_ERROR,stderr:e instanceof Error?e.message:String(e)}),{allAccepted:!1}}let e=T.buildCommand?.(D);if(e){let n=O(e,{cwd:o,env:E,testCaseId:b,limits:x});if(n)return t(n),{allAccepted:!1}}}let A=await l(o);if(y.length===0)return t({testCaseId:`default`,decisionCode:a.ACCEPTED}),{allAccepted:!0};let P=y.shared?.fileInputPath;for(let e of y){P&&await u(P,o),e.fileInputPath&&await u(e.fileInputPath,o);let r=typeof v.timeLimitMs==`number`?v.timeLimitMs/1e3:S,s=T.command(D),l=e.input??``,d;try{_.resolveInput&&(l=await _.resolveInput({testCase:e,cwd:o,env:E})),d=_.runCommand?await h(()=>_.runCommand?.({testCase:e,command:i(s),stdin:l,cwd:o,env:{...E,...n(E)},timeLimitSeconds:r})):M(s,{stdin:l,cwd:o,env:E,timeLimitSeconds:r})}catch(n){return t({testCaseId:e.id,decisionCode:a.RUNTIME_ERROR,stdin:l,stderr:k(n)}),await c(o,A),{allAccepted:!1}}let f=await m(o,v.requiredOutputFilePaths??[]),p={timeLimitSeconds:r,outputLimitLength:x.maxOutputLength,problemMarkdownFrontMatter:{memoryLimitByte:v.memoryLimitByte,requiredOutputFilePaths:v.requiredOutputFilePaths}},g=N({runResult:d,outputFiles:f,context:p}),y=g;if(g.decisionCode===a.ACCEPTED)try{let t=await _.test?.({testCase:e,runResult:d,outputFiles:f,context:p});t&&(y={decisionCode:t.decisionCode??g.decisionCode,feedbackMarkdown:t.feedbackMarkdown,stderr:t.stderr})}catch(e){y={decisionCode:a.RUNTIME_ERROR,stderr:k(e)}}if(t({testCaseId:e.id,...y,exitStatus:d.status,stdin:d.stdin,stdout:d.stdout.slice(0,x.maxOutputLength)||void 0,stderr:(y.stderr??d.stderr).slice(0,x.maxOutputLength)||void 0,timeSeconds:d.timeSeconds,memoryBytes:d.memoryBytes,outputFiles:f.length>0?f:void 0}),await c(o,A),y.decisionCode!==a.ACCEPTED)return{allAccepted:!1}}return{allAccepted:!0}}function D(e,t){return t.allAccepted===(e.expectedResult===`accepted`)}function O(e,t){let n=x(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,env:t.env},t.limits.buildTimeoutSeconds),r=n.status??void 0;if(n.status!==0)return{testCaseId:t.testCaseId,decisionCode:a.BUILD_ERROR,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.timeSeconds>t.limits.buildTimeoutSeconds)return{testCaseId:t.testCaseId,decisionCode:a.BUILD_TIME_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes};if(n.stdout.length>t.limits.maxOutputLength||n.stderr.length>t.limits.maxOutputLength)return{testCaseId:t.testCaseId,decisionCode:a.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,exitStatus:r,stdout:n.stdout.slice(0,t.limits.maxOutputLength),stderr:n.stderr.slice(0,t.limits.maxOutputLength),timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function k(e){return e instanceof Error?e.message:String(e)}function A(e){return{id:e.id,input:e.input,fileInputPath:e.fileInputPath}}async function j(e){let t=await _(S.join(e,`test_cases`)),n=t.map(e=>A(e));return t.shared?.fileInputPath?Object.assign(n,{shared:{fileInputPath:t.shared.fileInputPath}}):n}function M(e,t){let n=x(e[0],e.slice(1),{cwd:t.cwd,encoding:`utf8`,input:t.stdin,env:t.env},t.timeLimitSeconds);return{stdin:t.stdin,stdout:n.stdout,stderr:n.stderr,status:n.status??void 0,timeSeconds:n.timeSeconds,memoryBytes:n.memoryBytes}}function N(e){if(e.runResult.status!==0)return{decisionCode:a.RUNTIME_ERROR,stderr:e.runResult.stderr};if(e.runResult.timeSeconds>e.context.timeLimitSeconds)return{decisionCode:a.TIME_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.memoryBytes>(e.context.problemMarkdownFrontMatter.memoryLimitByte??1/0))return{decisionCode:a.MEMORY_LIMIT_EXCEEDED,stderr:e.runResult.stderr};if(e.runResult.outputLimitExceeded||e.runResult.stdout.length>e.context.outputLimitLength||e.runResult.stderr.length>e.context.outputLimitLength)return{decisionCode:a.OUTPUT_SIZE_LIMIT_EXCEEDED,stderr:e.runResult.stderr};let t=e.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length??0;return e.outputFiles.length<t?{decisionCode:a.MISSING_REQUIRED_OUTPUT_FILE_ERROR}:{decisionCode:a.ACCEPTED}}export{T as commandJudgePreset};
|
|
2
2
|
//# sourceMappingURL=command.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.js","names":["readFileTestCases"],"sources":["../../src/presets/command.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { z } from 'zod';\n\nimport { checkProblemDirIsolation } from '../helpers/checkProblemDirIsolation.js';\nimport { cleanWorkingDirectory, snapshotWorkingDirectory } from '../helpers/cleanWorkingDirectory.js';\nimport { copyTestCaseFileInput } from '../helpers/copyTestCaseFileInput.js';\nimport { findEntryPointFile } from '../helpers/findEntryPointFile.js';\nimport { findLanguageDefinitionByPath } from '../helpers/findLanguageDefinitionByPath.js';\nimport { judgeByStaticAnalysis } from '../helpers/judgeByStaticAnalysis.js';\nimport { parseArgs } from '../helpers/parseArgs.js';\nimport { printDebugBanner } from '../helpers/printDebugBanner.js';\nimport { printTestCaseResult } from '../helpers/printTestCaseResult.js';\nimport { readOutputFiles } from '../helpers/readOutputFiles.js';\nimport { runCustomRunner } from '../helpers/runCustomRunner.js';\nimport {\n getSandboxUserEnvOverrides,\n makeAccessibleToSandboxUser,\n wrapCommandWithSandboxUser,\n} from '../helpers/sandboxUser.js';\nimport { readProblemMarkdownFrontMatter } from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases as readFileTestCases } from '../helpers/readTestCases.js';\nimport {\n printDebugCwdBanner,\n printDebugExpectationFailureBanner,\n resolveCwds,\n type ResolvedCwd,\n} from '../helpers/resolveCwds.js';\nimport { spawnSyncWithTimeout } from '../helpers/spawnSyncWithTimeout.js';\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nconst BUILD_TIMEOUT_SECONDS = 10;\nconst JUDGE_DEFAULT_TIMEOUT_SECONDS = 2;\nconst MAX_STDOUT_LENGTH = 50_000;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\ntype JudgeParams = z.infer<typeof judgeParamsSchema>;\n\ninterface BaseCommandTestCase {\n id: string;\n input?: string;\n fileInputPath?: string;\n}\n\ntype CommandJudgeCaseResult = Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown' | 'stderr'>;\n\nexport interface CommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n outputLimitExceeded?: boolean;\n}\n\ninterface CommandJudgeContext {\n timeLimitSeconds: number;\n outputLimitLength: number;\n problemMarkdownFrontMatter: Pick<ProblemMarkdownFrontMatter, 'memoryLimitByte' | 'requiredOutputFilePaths'>;\n}\n\nexport interface CommandJudgeLimits {\n buildTimeoutSeconds: number;\n maxOutputLength: number;\n}\n\nexport interface CommandJudgePresetOptions<\n TTestCase extends BaseCommandTestCase = BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n> {\n limits?: CommandJudgeLimits;\n runTimeoutSeconds?: number;\n readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;\n /**\n * Runs as the trusted harness user with the submission's `cwd`. Fixture files it creates there\n * must be written with `createDirectoryWithoutFollowingSymlinks`/`writeFileWithoutFollowingSymlinks`\n * (both exported): a submission can plant a symlink at a fixture path, and a plain `fs.writeFile`\n * would follow it into a file only the harness can write.\n */\n resolveInput?: (context: { testCase: TTestCase; cwd: string; env: NodeJS.ProcessEnv }) => Promise<string> | string;\n runCommand?: (context: {\n testCase: TTestCase;\n /**\n * The command to run. Under `EXERCODE_SANDBOX_USER` delegation it is already wrapped so it\n * executes as the sandbox user; spawn it as given (with the supplied `env`) instead of\n * reconstructing it, or the submission runs as the trusted harness user.\n *\n * Its direct child is then a root-owned `sudo` whose descendants belong to the sandbox user, so\n * the handler cannot signal them: enforce `timeLimitSeconds` with `startSandboxTimeoutWatchdog`\n * and `killSandboxUserProcesses` (both exported) rather than `child.kill()` or an outer\n * `timeout`. The preset terminates leftover sandbox processes after the handler returns.\n */\n command: readonly [string, ...string[]];\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }) => Promise<TRunResult> | TRunResult;\n test?: (context: {\n testCase: TTestCase;\n runResult: TRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n }) => Promise<Partial<CommandJudgeCaseResult>> | Partial<CommandJudgeCaseResult> | undefined;\n}\n\n/**\n * A preset function for judging by executable command.\n *\n * Keep problem-specific logic in `resolveInput` and `test`.\n *\n * @example\n * Create `judge.ts`:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await commandJudgePreset(import.meta.dirname, {\n * readTestCases: async () => [\n * { id: '01', input: '1 2' },\n * ],\n * test: ({ runResult }) => {\n * return runResult.stdout.trim() === '3'\n * ? { decisionCode: DecisionCode.ACCEPTED }\n * : { decisionCode: DecisionCode.WRONG_ANSWER };\n * },\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/python '{ \"language\": \"python\" }'\n * ```\n *\n * Run without a cwd argument to judge each `<problemDir>/model_answers/*` directory\n * for debugging. A prominent banner is printed for each run.\n * ```bash\n * bun judge.ts\n * ```\n */\nexport async function commandJudgePreset<\n TTestCase extends BaseCommandTestCase = BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(problemDir: string, options: CommandJudgePresetOptions<TTestCase, TRunResult>): Promise<void> {\n const args = parseArgs(process.argv);\n const params = judgeParamsSchema.parse(args.params);\n\n const { cwds, isDebugMode } = await resolveCwds(problemDir, args.cwd);\n\n if (isDebugMode) {\n const acceptedCwd = cwds.find((cwd) => cwd.expectedResult === 'accepted');\n if (acceptedCwd) {\n const isolationCheckResult = await checkProblemDirIsolation(problemDir, acceptedCwd, params);\n if (!isolationCheckResult.passed) {\n process.exitCode = 1;\n return;\n }\n } else {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check skipped',\n '',\n 'No accepted model answer is available for checking that the copied judge still accepts a valid submission.',\n ]);\n }\n }\n\n for (const resolvedCwd of cwds) {\n if (isDebugMode) printDebugCwdBanner(problemDir, resolvedCwd);\n const result = await runCommandJudgeForCwd<TTestCase, TRunResult>(problemDir, resolvedCwd.cwd, params, options);\n if (isDebugMode && !matchesExpectedResult(resolvedCwd, result)) {\n process.exitCode = 1;\n printDebugExpectationFailureBanner(problemDir, resolvedCwd);\n }\n }\n}\n\nasync function runCommandJudgeForCwd<\n TTestCase extends BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(\n problemDir: string,\n cwd: string,\n params: JudgeParams,\n options: CommandJudgePresetOptions<TTestCase, TRunResult>\n): Promise<{ allAccepted: boolean }> {\n // The sandboxed submission must read its sources and write build/run outputs in its directory.\n makeAccessibleToSandboxUser(cwd);\n\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await (options.readTestCases ?? readCommandTestCases)(problemDir);\n const prebuildTestCaseId = testCases[0]?.id ?? 'prebuild';\n const limits = {\n buildTimeoutSeconds: options.limits?.buildTimeoutSeconds ?? BUILD_TIMEOUT_SECONDS,\n maxOutputLength: options.limits?.maxOutputLength ?? MAX_STDOUT_LENGTH,\n };\n const runTimeoutSeconds = options.runTimeoutSeconds ?? JUDGE_DEFAULT_TIMEOUT_SECONDS;\n\n const staticAnalysisResult = await judgeByStaticAnalysis(cwd, problemMarkdownFrontMatter);\n if (staticAnalysisResult) {\n printTestCaseResult({ testCaseId: prebuildTestCaseId, ...staticAnalysisResult });\n return { allAccepted: false };\n }\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return { allAccepted: false };\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return { allAccepted: false };\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let mainFilePath = originalMainFilePath;\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n const prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n if (prebuiltMainFilePath) mainFilePath = prebuiltMainFilePath;\n } catch (error) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return { allAccepted: false };\n }\n }\n\n const buildCommand = languageDefinition.buildCommand?.(mainFilePath);\n if (buildCommand) {\n const buildResult = runBuild(buildCommand, {\n cwd,\n env,\n testCaseId: prebuildTestCaseId,\n limits,\n });\n if (buildResult) {\n printTestCaseResult(buildResult);\n return { allAccepted: false };\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n return { allAccepted: true };\n }\n\n const sharedFileInputPath = (testCases as { shared?: { fileInputPath?: string } }).shared?.fileInputPath;\n\n for (const testCase of testCases) {\n if (sharedFileInputPath) await copyTestCaseFileInput(sharedFileInputPath, cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, cwd);\n\n const timeLimitSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : runTimeoutSeconds;\n\n const command = languageDefinition.command(mainFilePath);\n let stdin = testCase.input ?? '';\n let runResult: TRunResult;\n try {\n if (options.resolveInput) {\n stdin = await options.resolveInput({ testCase, cwd, env });\n }\n\n runResult = options.runCommand\n ? await runCustomRunner(\n () =>\n // Hand custom runners a command that is already sandbox-wrapped, and the matching\n // environment: they spawn it themselves, so an unwrapped command would run the\n // submission as the trusted harness user and defeat the delegation boundary.\n options.runCommand?.({\n testCase,\n command: wrapCommandWithSandboxUser(command),\n stdin,\n cwd,\n env: { ...env, ...getSandboxUserEnvOverrides(env) },\n timeLimitSeconds,\n }) as Promise<TRunResult>\n )\n : (runCommand(command, {\n stdin,\n cwd,\n env,\n timeLimitSeconds,\n }) as TRunResult);\n } catch (error) {\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stdin,\n stderr: errorToMessage(error),\n });\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n return { allAccepted: false };\n }\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n const judgeContext: CommandJudgeContext = {\n timeLimitSeconds,\n outputLimitLength: limits.maxOutputLength,\n problemMarkdownFrontMatter: {\n memoryLimitByte: problemMarkdownFrontMatter.memoryLimitByte,\n requiredOutputFilePaths: problemMarkdownFrontMatter.requiredOutputFilePaths,\n },\n };\n const baseJudgeResult = evaluateByLimits({ runResult, outputFiles, context: judgeContext });\n let judgeResult = baseJudgeResult;\n if (baseJudgeResult.decisionCode === DecisionCode.ACCEPTED) {\n try {\n const extendedJudgeResult = await options.test?.({ testCase, runResult, outputFiles, context: judgeContext });\n if (extendedJudgeResult) {\n judgeResult = {\n decisionCode: extendedJudgeResult.decisionCode ?? baseJudgeResult.decisionCode,\n feedbackMarkdown: extendedJudgeResult.feedbackMarkdown,\n stderr: extendedJudgeResult.stderr,\n };\n }\n } catch (error) {\n judgeResult = {\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stderr: errorToMessage(error),\n };\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n ...judgeResult,\n exitStatus: runResult.status,\n stdin: runResult.stdin,\n stdout: runResult.stdout.slice(0, limits.maxOutputLength) || undefined,\n stderr: (judgeResult.stderr ?? runResult.stderr).slice(0, limits.maxOutputLength) || undefined,\n timeSeconds: runResult.timeSeconds,\n memoryBytes: runResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n if (judgeResult.decisionCode !== DecisionCode.ACCEPTED) return { allAccepted: false };\n }\n\n return { allAccepted: true };\n}\n\nfunction matchesExpectedResult(resolvedCwd: ResolvedCwd, result: { allAccepted: boolean }): boolean {\n return result.allAccepted === (resolvedCwd.expectedResult === 'accepted');\n}\n\nfunction runBuild(\n buildCommand: readonly [string, ...string[]],\n context: { cwd: string; env: NodeJS.ProcessEnv; testCaseId: string; limits: CommandJudgeLimits }\n): (Omit<TestCaseResult, 'testCaseId'> & { testCaseId: string }) | undefined {\n const spawnResult = spawnSyncWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: context.cwd, encoding: 'utf8', env: context.env },\n context.limits.buildTimeoutSeconds\n );\n const exitStatus = spawnResult.status ?? undefined;\n\n if (spawnResult.status !== 0) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (spawnResult.timeSeconds > context.limits.buildTimeoutSeconds) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (\n spawnResult.stdout.length > context.limits.maxOutputLength ||\n spawnResult.stderr.length > context.limits.maxOutputLength\n ) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n return;\n}\n\nfunction errorToMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction toCommandTestCase(value: {\n id: string;\n input?: string;\n fileInputPath?: string;\n fileOutputPath?: string;\n output?: string;\n}): BaseCommandTestCase {\n return { id: value.id, input: value.input, fileInputPath: value.fileInputPath };\n}\n\nasync function readCommandTestCases<TTestCase extends BaseCommandTestCase = BaseCommandTestCase>(\n problemDir: string\n): Promise<readonly TTestCase[]> {\n const fileTestCases = await readFileTestCases(path.join(problemDir, 'test_cases'));\n const commandTestCases = fileTestCases.map((testCase) => toCommandTestCase(testCase) as TTestCase);\n if (fileTestCases.shared?.fileInputPath) {\n return Object.assign(commandTestCases, { shared: { fileInputPath: fileTestCases.shared.fileInputPath } });\n }\n return commandTestCases;\n}\n\nfunction runCommand(\n command: readonly [string, ...string[]],\n context: {\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }\n): CommandRunResult {\n const spawnResult = spawnSyncWithTimeout(\n command[0],\n command.slice(1),\n { cwd: context.cwd, encoding: 'utf8', input: context.stdin, env: context.env },\n context.timeLimitSeconds\n );\n\n return {\n stdin: context.stdin,\n stdout: spawnResult.stdout,\n stderr: spawnResult.stderr,\n status: spawnResult.status ?? undefined,\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n}\n\nfunction evaluateByLimits(context: {\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n}): CommandJudgeCaseResult {\n if (context.runResult.status !== 0) {\n return { decisionCode: DecisionCode.RUNTIME_ERROR, stderr: context.runResult.stderr };\n }\n\n if (context.runResult.timeSeconds > context.context.timeLimitSeconds) {\n return { decisionCode: DecisionCode.TIME_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.memoryBytes >\n (context.context.problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)\n ) {\n return { decisionCode: DecisionCode.MEMORY_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.outputLimitExceeded ||\n context.runResult.stdout.length > context.context.outputLimitLength ||\n context.runResult.stderr.length > context.context.outputLimitLength\n ) {\n return { decisionCode: DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n const requiredOutputFileCount = context.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0;\n if (context.outputFiles.length < requiredOutputFileCount) {\n return { decisionCode: DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR };\n }\n\n return { decisionCode: DecisionCode.ACCEPTED };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"command.js","names":["readFileTestCases"],"sources":["../../src/presets/command.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { z } from 'zod';\n\nimport { checkProblemDirIsolation } from '../helpers/checkProblemDirIsolation.js';\nimport { cleanWorkingDirectory, snapshotWorkingDirectory } from '../helpers/cleanWorkingDirectory.js';\nimport { copyTestCaseFileInput } from '../helpers/copyTestCaseFileInput.js';\nimport { findEntryPointFile } from '../helpers/findEntryPointFile.js';\nimport { findLanguageDefinitionByPath } from '../helpers/findLanguageDefinitionByPath.js';\nimport { judgeByStaticAnalysis } from '../helpers/judgeByStaticAnalysis.js';\nimport { parseArgs } from '../helpers/parseArgs.js';\nimport { printDebugBanner } from '../helpers/printDebugBanner.js';\nimport { printTestCaseResult } from '../helpers/printTestCaseResult.js';\nimport { readOutputFiles } from '../helpers/readOutputFiles.js';\nimport { runCustomRunner } from '../helpers/runCustomRunner.js';\nimport {\n getSandboxUserEnvOverrides,\n makeAccessibleToSandboxUser,\n wrapCommandWithSandboxUser,\n} from '../helpers/sandboxUser.js';\nimport { readProblemMarkdownFrontMatter } from '../helpers/readProblemMarkdownFrontMatter.js';\nimport { readTestCases as readFileTestCases } from '../helpers/readTestCases.js';\nimport {\n printDebugCwdBanner,\n printDebugExpectationFailureBanner,\n resolveCwds,\n type ResolvedCwd,\n} from '../helpers/resolveCwds.js';\nimport { spawnSyncWithTimeout } from '../helpers/spawnSyncWithTimeout.js';\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nconst BUILD_TIMEOUT_SECONDS = 10;\nconst JUDGE_DEFAULT_TIMEOUT_SECONDS = 2;\nconst MAX_STDOUT_LENGTH = 50_000;\n\nconst judgeParamsSchema = z.object({\n language: z.union([z.string(), z.array(z.string())]).optional(),\n});\n\ntype JudgeParams = z.infer<typeof judgeParamsSchema>;\n\ninterface BaseCommandTestCase {\n id: string;\n input?: string;\n fileInputPath?: string;\n}\n\ntype CommandJudgeCaseResult = Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown' | 'stderr'>;\n\nexport interface CommandRunResult {\n stdin: string;\n stdout: string;\n stderr: string;\n status: number | undefined;\n timeSeconds: number;\n memoryBytes: number;\n outputLimitExceeded?: boolean;\n}\n\ninterface CommandJudgeContext {\n timeLimitSeconds: number;\n outputLimitLength: number;\n problemMarkdownFrontMatter: Pick<ProblemMarkdownFrontMatter, 'memoryLimitByte' | 'requiredOutputFilePaths'>;\n}\n\nexport interface CommandJudgeLimits {\n buildTimeoutSeconds: number;\n maxOutputLength: number;\n}\n\nexport interface CommandJudgePresetOptions<\n TTestCase extends BaseCommandTestCase = BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n> {\n /**\n * Set to false when the custom runner builds the submitted source itself.\n * Defaults to true.\n */\n buildSubmission?: boolean;\n limits?: CommandJudgeLimits;\n runTimeoutSeconds?: number;\n readTestCases?: (problemDir: string) => Promise<readonly TTestCase[]>;\n /**\n * Runs as the trusted harness user with the submission's `cwd`. Fixture files it creates there\n * must be written with `createDirectoryWithoutFollowingSymlinks`/`writeFileWithoutFollowingSymlinks`\n * (both exported): a submission can plant a symlink at a fixture path, and a plain `fs.writeFile`\n * would follow it into a file only the harness can write.\n */\n resolveInput?: (context: { testCase: TTestCase; cwd: string; env: NodeJS.ProcessEnv }) => Promise<string> | string;\n runCommand?: (context: {\n testCase: TTestCase;\n /**\n * The command to run. Under `EXERCODE_SANDBOX_USER` delegation it is already wrapped so it\n * executes as the sandbox user; spawn it as given (with the supplied `env`) instead of\n * reconstructing it, or the submission runs as the trusted harness user.\n *\n * Its direct child is then a root-owned `sudo` whose descendants belong to the sandbox user, so\n * the handler cannot signal them: enforce `timeLimitSeconds` with `startSandboxTimeoutWatchdog`\n * and `killSandboxUserProcesses` (both exported) rather than `child.kill()` or an outer\n * `timeout`. The preset terminates leftover sandbox processes after the handler returns.\n */\n command: readonly [string, ...string[]];\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }) => Promise<TRunResult> | TRunResult;\n test?: (context: {\n testCase: TTestCase;\n runResult: TRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n }) => Promise<Partial<CommandJudgeCaseResult>> | Partial<CommandJudgeCaseResult> | undefined;\n}\n\n/**\n * A preset function for judging by executable command.\n *\n * Keep problem-specific logic in `resolveInput` and `test`.\n *\n * @example\n * Create `judge.ts`:\n * ```ts\n * import { commandJudgePreset } from '@exercode/problem-utils/presets/command';\n * import { DecisionCode } from '@exercode/problem-utils';\n *\n * await commandJudgePreset(import.meta.dirname, {\n * readTestCases: async () => [\n * { id: '01', input: '1 2' },\n * ],\n * test: ({ runResult }) => {\n * return runResult.stdout.trim() === '3'\n * ? { decisionCode: DecisionCode.ACCEPTED }\n * : { decisionCode: DecisionCode.WRONG_ANSWER };\n * },\n * });\n * ```\n *\n * Run with the required parameters:\n * ```bash\n * bun judge.ts model_answers/python '{ \"language\": \"python\" }'\n * ```\n *\n * Run without a cwd argument to judge each `<problemDir>/model_answers/*` directory\n * for debugging. A prominent banner is printed for each run.\n * ```bash\n * bun judge.ts\n * ```\n */\nexport async function commandJudgePreset<\n TTestCase extends BaseCommandTestCase = BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(problemDir: string, options: CommandJudgePresetOptions<TTestCase, TRunResult>): Promise<void> {\n const args = parseArgs(process.argv);\n const params = judgeParamsSchema.parse(args.params);\n\n const { cwds, isDebugMode } = await resolveCwds(problemDir, args.cwd);\n\n if (isDebugMode) {\n const acceptedCwd = cwds.find((cwd) => cwd.expectedResult === 'accepted');\n if (acceptedCwd) {\n const isolationCheckResult = await checkProblemDirIsolation(problemDir, acceptedCwd, params);\n if (!isolationCheckResult.passed) {\n process.exitCode = 1;\n return;\n }\n } else {\n printDebugBanner([\n '[DEBUG MODE] isolated problem directory check skipped',\n '',\n 'No accepted model answer is available for checking that the copied judge still accepts a valid submission.',\n ]);\n }\n }\n\n for (const resolvedCwd of cwds) {\n if (isDebugMode) printDebugCwdBanner(problemDir, resolvedCwd);\n const result = await runCommandJudgeForCwd<TTestCase, TRunResult>(problemDir, resolvedCwd.cwd, params, options);\n if (isDebugMode && !matchesExpectedResult(resolvedCwd, result)) {\n process.exitCode = 1;\n printDebugExpectationFailureBanner(problemDir, resolvedCwd);\n }\n }\n}\n\nasync function runCommandJudgeForCwd<\n TTestCase extends BaseCommandTestCase,\n TRunResult extends CommandRunResult = CommandRunResult,\n>(\n problemDir: string,\n cwd: string,\n params: JudgeParams,\n options: CommandJudgePresetOptions<TTestCase, TRunResult>\n): Promise<{ allAccepted: boolean }> {\n // The sandboxed submission must read its sources and write build/run outputs in its directory.\n makeAccessibleToSandboxUser(cwd);\n\n const problemMarkdownFrontMatter = await readProblemMarkdownFrontMatter(problemDir);\n const testCases = await (options.readTestCases ?? readCommandTestCases)(problemDir);\n const prebuildTestCaseId = testCases[0]?.id ?? 'prebuild';\n const limits = {\n buildTimeoutSeconds: options.limits?.buildTimeoutSeconds ?? BUILD_TIMEOUT_SECONDS,\n maxOutputLength: options.limits?.maxOutputLength ?? MAX_STDOUT_LENGTH,\n };\n const runTimeoutSeconds = options.runTimeoutSeconds ?? JUDGE_DEFAULT_TIMEOUT_SECONDS;\n\n const staticAnalysisResult = await judgeByStaticAnalysis(cwd, problemMarkdownFrontMatter);\n if (staticAnalysisResult) {\n printTestCaseResult({ testCaseId: prebuildTestCaseId, ...staticAnalysisResult });\n return { allAccepted: false };\n }\n\n const originalMainFilePath = await findEntryPointFile(cwd, params.language);\n if (!originalMainFilePath) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n stderr: `main file not found${params.language ? `: language: ${params.language}` : ''}`,\n });\n return { allAccepted: false };\n }\n\n const languageDefinition = findLanguageDefinitionByPath(originalMainFilePath);\n if (!languageDefinition) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.WRONG_ANSWER,\n stderr: 'unsupported language',\n });\n return { allAccepted: false };\n }\n\n // `CI` changes affects Chainlit. `FORCE_COLOR` affects Bun.\n const env = { ...process.env, CI: '', FORCE_COLOR: '0' };\n\n let mainFilePath = originalMainFilePath;\n if (options.buildSubmission !== false) {\n if (languageDefinition.prebuild) {\n try {\n await languageDefinition.prebuild(cwd);\n const prebuiltMainFilePath = await findEntryPointFile(cwd, params.language);\n if (prebuiltMainFilePath) mainFilePath = prebuiltMainFilePath;\n } catch (error) {\n printTestCaseResult({\n testCaseId: prebuildTestCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n stderr: error instanceof Error ? error.message : String(error),\n });\n return { allAccepted: false };\n }\n }\n\n const buildCommand = languageDefinition.buildCommand?.(mainFilePath);\n if (buildCommand) {\n const buildResult = runBuild(buildCommand, {\n cwd,\n env,\n testCaseId: prebuildTestCaseId,\n limits,\n });\n if (buildResult) {\n printTestCaseResult(buildResult);\n return { allAccepted: false };\n }\n }\n }\n\n const cwdSnapshot = await snapshotWorkingDirectory(cwd);\n\n if (testCases.length === 0) {\n printTestCaseResult({ testCaseId: 'default', decisionCode: DecisionCode.ACCEPTED });\n return { allAccepted: true };\n }\n\n const sharedFileInputPath = (testCases as { shared?: { fileInputPath?: string } }).shared?.fileInputPath;\n\n for (const testCase of testCases) {\n if (sharedFileInputPath) await copyTestCaseFileInput(sharedFileInputPath, cwd);\n if (testCase.fileInputPath) await copyTestCaseFileInput(testCase.fileInputPath, cwd);\n\n const timeLimitSeconds =\n typeof problemMarkdownFrontMatter.timeLimitMs === 'number'\n ? problemMarkdownFrontMatter.timeLimitMs / 1000\n : runTimeoutSeconds;\n\n const command = languageDefinition.command(mainFilePath);\n let stdin = testCase.input ?? '';\n let runResult: TRunResult;\n try {\n if (options.resolveInput) {\n stdin = await options.resolveInput({ testCase, cwd, env });\n }\n\n runResult = options.runCommand\n ? await runCustomRunner(\n () =>\n // Hand custom runners a command that is already sandbox-wrapped, and the matching\n // environment: they spawn it themselves, so an unwrapped command would run the\n // submission as the trusted harness user and defeat the delegation boundary.\n options.runCommand?.({\n testCase,\n command: wrapCommandWithSandboxUser(command),\n stdin,\n cwd,\n env: { ...env, ...getSandboxUserEnvOverrides(env) },\n timeLimitSeconds,\n }) as Promise<TRunResult>\n )\n : (runCommand(command, {\n stdin,\n cwd,\n env,\n timeLimitSeconds,\n }) as TRunResult);\n } catch (error) {\n printTestCaseResult({\n testCaseId: testCase.id,\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stdin,\n stderr: errorToMessage(error),\n });\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n return { allAccepted: false };\n }\n\n const outputFiles = await readOutputFiles(cwd, problemMarkdownFrontMatter.requiredOutputFilePaths ?? []);\n const judgeContext: CommandJudgeContext = {\n timeLimitSeconds,\n outputLimitLength: limits.maxOutputLength,\n problemMarkdownFrontMatter: {\n memoryLimitByte: problemMarkdownFrontMatter.memoryLimitByte,\n requiredOutputFilePaths: problemMarkdownFrontMatter.requiredOutputFilePaths,\n },\n };\n const baseJudgeResult = evaluateByLimits({ runResult, outputFiles, context: judgeContext });\n let judgeResult = baseJudgeResult;\n if (baseJudgeResult.decisionCode === DecisionCode.ACCEPTED) {\n try {\n const extendedJudgeResult = await options.test?.({ testCase, runResult, outputFiles, context: judgeContext });\n if (extendedJudgeResult) {\n judgeResult = {\n decisionCode: extendedJudgeResult.decisionCode ?? baseJudgeResult.decisionCode,\n feedbackMarkdown: extendedJudgeResult.feedbackMarkdown,\n stderr: extendedJudgeResult.stderr,\n };\n }\n } catch (error) {\n judgeResult = {\n decisionCode: DecisionCode.RUNTIME_ERROR,\n stderr: errorToMessage(error),\n };\n }\n }\n\n printTestCaseResult({\n testCaseId: testCase.id,\n ...judgeResult,\n exitStatus: runResult.status,\n stdin: runResult.stdin,\n stdout: runResult.stdout.slice(0, limits.maxOutputLength) || undefined,\n stderr: (judgeResult.stderr ?? runResult.stderr).slice(0, limits.maxOutputLength) || undefined,\n timeSeconds: runResult.timeSeconds,\n memoryBytes: runResult.memoryBytes,\n outputFiles: outputFiles.length > 0 ? outputFiles : undefined,\n });\n\n await cleanWorkingDirectory(cwd, cwdSnapshot);\n if (judgeResult.decisionCode !== DecisionCode.ACCEPTED) return { allAccepted: false };\n }\n\n return { allAccepted: true };\n}\n\nfunction matchesExpectedResult(resolvedCwd: ResolvedCwd, result: { allAccepted: boolean }): boolean {\n return result.allAccepted === (resolvedCwd.expectedResult === 'accepted');\n}\n\nfunction runBuild(\n buildCommand: readonly [string, ...string[]],\n context: { cwd: string; env: NodeJS.ProcessEnv; testCaseId: string; limits: CommandJudgeLimits }\n): (Omit<TestCaseResult, 'testCaseId'> & { testCaseId: string }) | undefined {\n const spawnResult = spawnSyncWithTimeout(\n buildCommand[0],\n buildCommand.slice(1),\n { cwd: context.cwd, encoding: 'utf8', env: context.env },\n context.limits.buildTimeoutSeconds\n );\n const exitStatus = spawnResult.status ?? undefined;\n\n if (spawnResult.status !== 0) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_ERROR,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (spawnResult.timeSeconds > context.limits.buildTimeoutSeconds) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_TIME_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n if (\n spawnResult.stdout.length > context.limits.maxOutputLength ||\n spawnResult.stderr.length > context.limits.maxOutputLength\n ) {\n return {\n testCaseId: context.testCaseId,\n decisionCode: DecisionCode.BUILD_OUTPUT_SIZE_LIMIT_EXCEEDED,\n exitStatus,\n stdout: spawnResult.stdout.slice(0, context.limits.maxOutputLength),\n stderr: spawnResult.stderr.slice(0, context.limits.maxOutputLength),\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n }\n\n return;\n}\n\nfunction errorToMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction toCommandTestCase(value: {\n id: string;\n input?: string;\n fileInputPath?: string;\n fileOutputPath?: string;\n output?: string;\n}): BaseCommandTestCase {\n return { id: value.id, input: value.input, fileInputPath: value.fileInputPath };\n}\n\nasync function readCommandTestCases<TTestCase extends BaseCommandTestCase = BaseCommandTestCase>(\n problemDir: string\n): Promise<readonly TTestCase[]> {\n const fileTestCases = await readFileTestCases(path.join(problemDir, 'test_cases'));\n const commandTestCases = fileTestCases.map((testCase) => toCommandTestCase(testCase) as TTestCase);\n if (fileTestCases.shared?.fileInputPath) {\n return Object.assign(commandTestCases, { shared: { fileInputPath: fileTestCases.shared.fileInputPath } });\n }\n return commandTestCases;\n}\n\nfunction runCommand(\n command: readonly [string, ...string[]],\n context: {\n stdin: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeLimitSeconds: number;\n }\n): CommandRunResult {\n const spawnResult = spawnSyncWithTimeout(\n command[0],\n command.slice(1),\n { cwd: context.cwd, encoding: 'utf8', input: context.stdin, env: context.env },\n context.timeLimitSeconds\n );\n\n return {\n stdin: context.stdin,\n stdout: spawnResult.stdout,\n stderr: spawnResult.stderr,\n status: spawnResult.status ?? undefined,\n timeSeconds: spawnResult.timeSeconds,\n memoryBytes: spawnResult.memoryBytes,\n };\n}\n\nfunction evaluateByLimits(context: {\n runResult: CommandRunResult;\n outputFiles: NonNullable<TestCaseResult['outputFiles']>;\n context: CommandJudgeContext;\n}): CommandJudgeCaseResult {\n if (context.runResult.status !== 0) {\n return { decisionCode: DecisionCode.RUNTIME_ERROR, stderr: context.runResult.stderr };\n }\n\n if (context.runResult.timeSeconds > context.context.timeLimitSeconds) {\n return { decisionCode: DecisionCode.TIME_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.memoryBytes >\n (context.context.problemMarkdownFrontMatter.memoryLimitByte ?? Number.POSITIVE_INFINITY)\n ) {\n return { decisionCode: DecisionCode.MEMORY_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n if (\n context.runResult.outputLimitExceeded ||\n context.runResult.stdout.length > context.context.outputLimitLength ||\n context.runResult.stderr.length > context.context.outputLimitLength\n ) {\n return { decisionCode: DecisionCode.OUTPUT_SIZE_LIMIT_EXCEEDED, stderr: context.runResult.stderr };\n }\n\n const requiredOutputFileCount = context.context.problemMarkdownFrontMatter.requiredOutputFilePaths?.length ?? 0;\n if (context.outputFiles.length < requiredOutputFileCount) {\n return { decisionCode: DecisionCode.MISSING_REQUIRED_OUTPUT_FILE_ERROR };\n }\n\n return { decisionCode: DecisionCode.ACCEPTED };\n}\n"],"mappings":"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"}
|