@@ -11,7 +11,7 @@ import YAML from "yaml";
1111
1212import { validatePostMergeDocsWorkflowBoundary } from "../tools/post-merge-docs/contract.mts" ;
1313import { publishDocumentation , type Request } from "../tools/post-merge-docs/publish.mts" ;
14- import { executePostMergeDocs } from "../tools/post-merge-docs/run.mts" ;
14+ import { configurePostMergeDocs , executePostMergeDocs } from "../tools/post-merge-docs/run.mts" ;
1515import type { OpenShellTools } from "../tools/openshell-agent/runtime.mts" ;
1616
1717const directories : string [ ] = [ ] ;
@@ -182,6 +182,7 @@ function runnerFixture(phase: "author" | "review") {
182182 GITHUB_REPOSITORY : repository ,
183183 GITHUB_SHA : mainSha ,
184184 HOME : root ,
185+ OPENSHELL_GATEWAY_ENDPOINT : "http://127.0.0.1:8080" ,
185186 PI_IMAGE : "image" ,
186187 POST_MERGE_DOCS_ARTIFACT_DIR : path . join ( root , "artifact" ) ,
187188 POST_MERGE_DOCS_CANDIDATE_DIR : candidate ,
@@ -190,6 +191,7 @@ function runnerFixture(phase: "author" | "review") {
190191 POST_MERGE_DOCS_WORKDIR : path . join ( root , "work" ) ,
191192 RANGE_START_SHA : mainSha ,
192193 RANGE_START_TAG : "v1.0.0" ,
194+ RUNNER_TEMP : path . join ( root , "runner-temp" ) ,
193195 SANDBOX_NAME : `docs-${ phase } ` ,
194196 TRUSTED_CHECKOUT : source ,
195197 } ,
@@ -203,13 +205,19 @@ function runnerTools(
203205 const { env, root } = input ;
204206 const sandbox = path . join ( root , "sandbox" ) ;
205207 const output = path . join ( root , "work/output" ) ;
206- const state = { deleted : false } ;
208+ const state = {
209+ agentArgs : [ ] as readonly string [ ] ,
210+ createArgs : [ ] as readonly string [ ] ,
211+ deleted : false ,
212+ } ;
207213 const handlers : Record < string , ( args : readonly string [ ] ) => unknown > = {
208- create : ( ) => {
214+ create : ( args ) => {
215+ state . createArgs = args ;
209216 fs . cpSync ( path . join ( root , "work/repo" ) , sandbox , { recursive : true } ) ;
210217 expect ( git ( sandbox , [ "rev-parse" , "HEAD" ] ) ) . toBe ( env . GITHUB_SHA ) ;
211218 } ,
212- agent : ( ) => {
219+ agent : ( args ) => {
220+ state . agentArgs = args ;
213221 const agents = {
214222 author : ( ) => fs . writeFileSync ( path . join ( sandbox , "docs/guide.mdx" ) , "authored\n" ) ,
215223 review : ( ) =>
@@ -329,18 +337,71 @@ describe("post-merge documentation publisher", () => {
329337} ) ;
330338
331339describe ( "post-merge documentation runner" , ( ) => {
340+ it ( "enables bind mounts before creating a reviewer sandbox" , async ( ) => {
341+ const input = runnerFixture ( "review" ) ;
342+ const responses = new Map ( [ [ "which" , "/trusted/bin/openshell-sandbox" ] ] ) ;
343+ const tools : OpenShellTools = {
344+ run : vi . fn ( ( command ) => responses . get ( command ) ?? "" ) ,
345+ start : vi . fn ( ) ,
346+ wait : async ( ) => undefined ,
347+ } ;
348+ await configurePostMergeDocs ( input . env , tools ) ;
349+ const config = fs . readFileSync (
350+ path . join ( input . root , "runner-temp/openshell-gateway/gateway.toml" ) ,
351+ "utf8" ,
352+ ) ;
353+ expect ( config ) . toContain ( "enable_bind_mounts = true" ) ;
354+ } ) ;
355+
332356 it ( "authors from the triggering SHA without exposing host credentials" , ( ) => {
333357 const input = runnerFixture ( "author" ) ;
334358 const { state, tools } = runnerTools ( input ) ;
335359 executePostMergeDocs ( input . env , tools ) ;
336360 expect ( fs . readFileSync ( path . join ( input . root , "artifact/docs.patch" ) , "utf8" ) ) . toContain (
337361 "+authored" ,
338362 ) ;
363+ expect ( state . createArgs . filter ( ( argument ) => argument === "--upload" ) ) . toHaveLength ( 3 ) ;
364+ expect ( state . createArgs ) . not . toContain ( "--driver-config-json" ) ;
365+ expect ( state . agentArgs . join ( "\n" ) ) . not . toContain ( "GIT_DIR=" ) ;
339366 expect ( state . deleted ) . toBe ( true ) ;
340367 } ) ;
341368 it ( "records the exact independent approval" , ( ) => {
342369 const input = runnerFixture ( "review" ) ;
343- executePostMergeDocs ( input . env , runnerTools ( input ) . tools ) ;
370+ const { state, tools } = runnerTools ( input ) ;
371+ executePostMergeDocs ( input . env , tools ) ;
372+ const driverConfigIndex = state . createArgs . indexOf ( "--driver-config-json" ) ;
373+ expect ( JSON . parse ( state . createArgs [ driverConfigIndex + 1 ] as string ) ) . toEqual ( {
374+ docker : {
375+ mounts : [
376+ {
377+ read_only : true ,
378+ source : path . join ( input . root , "work/repo" ) ,
379+ target : "/sandbox/repo" ,
380+ type : "bind" ,
381+ } ,
382+ {
383+ read_only : true ,
384+ source : path . join ( input . root , "config" ) ,
385+ target : "/sandbox/config" ,
386+ type : "bind" ,
387+ } ,
388+ ] ,
389+ } ,
390+ } ) ;
391+ expect ( state . createArgs ) . not . toContain ( "--upload" ) ;
392+ expect ( state . createArgs . slice ( - 6 ) ) . toEqual ( [
393+ "--" ,
394+ "/usr/bin/git" ,
395+ "--git-dir=/sandbox/repo/.git" ,
396+ "--work-tree=/sandbox/repo" ,
397+ "status" ,
398+ "--short" ,
399+ ] ) ;
400+ expect ( state . agentArgs ) . toEqual (
401+ expect . arrayContaining ( [ "GIT_DIR=/sandbox/repo/.git" , "GIT_WORK_TREE=/sandbox/repo" ] ) ,
402+ ) ;
403+ expect ( fs . statSync ( path . join ( input . root , "config" ) ) . mode & 0o777 ) . toBe ( 0o755 ) ;
404+ expect ( fs . statSync ( path . join ( input . root , "config/task.txt" ) ) . mode & 0o777 ) . toBe ( 0o444 ) ;
344405 expect (
345406 JSON . parse ( fs . readFileSync ( path . join ( input . root , "artifact/review.json" ) , "utf8" ) ) ,
346407 ) . toEqual ( {
0 commit comments