Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Route run artifact listing through api.Client - #14078

Merged
williammartin merged 2 commits into
williammartin-wp-05-release-api-clientfrom
williammartin-route-run-artifacts-api-client
Aug 6, 2026
Merged

Route run artifact listing through api.Client#14078
williammartin merged 2 commits into
williammartin-wp-05-release-api-clientfrom
williammartin-route-run-artifacts-api-client

Conversation

@williammartin

@williammartin williammartin commented Aug 5, 2026

Copy link
Copy Markdown
Member

Part of #13991

Description

This stacked PR routes artifact listing in gh run through api.Client.RESTWithNext. ListArtifacts previously issued requests through a private apiGet helper, decoded responses itself, and parsed Link headers with a package-local regular expression and findNextPage. The API client already provides that pagination behavior, so this removes the duplicate request and link-parsing implementation while preserving per_page=100 and rel="next" traversal.

The initial endpoint is now built as a safe relative URL and passed to RESTWithNext; subsequent absolute URLs returned by the API are passed through unchanged. The existing multi-page test continues to exercise that path. The error-path test also verifies that failures remain CLI api.HTTPError values with the expected status and byte-identical message. This relies on #13988, which is below this PR in the stack.

Three raw http.Client sites remain intentionally and carry TODO(api-client-rollout) comments with their blockers:

  • downloadArtifact streams an artifact ZIP response body to disk.
  • apiLogFetcher.GetLog returns the job log response body as an io.ReadCloser.
  • getLog streams a run log ZIP for archive processing.

api.Client.REST cannot express those operations because it decodes successful responses as JSON rather than exposing the raw response body.

Merged with the PRs below it in the stack, this is a consolidation with no intended user-visible behavior change.

How did you test this change?

Full acceptance test output
williammartin-fantastic-guide git:(williammartin-route-run-artifacts-api-client) GH_ACCEPTANCE_HOST=github.com \
GH_ACCEPTANCE_ORG=gh-acceptance-testing \
GH_ACCEPTANCE_TOKEN="$(gh auth token --hostname github.com)" \
go test -tags=acceptance -count=1 -v -run '^TestWorkflows$' ./acceptance
=== RUN   TestWorkflows
=== RUN   TestWorkflows/cache-list-delete
=== PAUSE TestWorkflows/cache-list-delete
=== RUN   TestWorkflows/cache-list-empty
=== PAUSE TestWorkflows/cache-list-empty
=== RUN   TestWorkflows/run-cancel
=== PAUSE TestWorkflows/run-cancel
=== RUN   TestWorkflows/run-delete
=== PAUSE TestWorkflows/run-delete
=== RUN   TestWorkflows/run-download
=== PAUSE TestWorkflows/run-download
=== RUN   TestWorkflows/run-rerun
=== PAUSE TestWorkflows/run-rerun
=== RUN   TestWorkflows/run-view-log-escape-sequences
=== PAUSE TestWorkflows/run-view-log-escape-sequences
=== RUN   TestWorkflows/run-view
=== PAUSE TestWorkflows/run-view
=== RUN   TestWorkflows/workflow-enable-disable
=== PAUSE TestWorkflows/workflow-enable-disable
=== RUN   TestWorkflows/workflow-list
=== PAUSE TestWorkflows/workflow-list
=== RUN   TestWorkflows/workflow-run
=== PAUSE TestWorkflows/workflow-run
=== RUN   TestWorkflows/workflow-view
=== PAUSE TestWorkflows/workflow-view
=== CONT  TestWorkflows/cache-list-delete
=== CONT  TestWorkflows/run-delete
=== CONT  TestWorkflows/run-rerun
=== CONT  TestWorkflows/run-view
=== CONT  TestWorkflows/workflow-view
=== CONT  TestWorkflows/run-download
=== CONT  TestWorkflows/workflow-run
=== CONT  TestWorkflows/workflow-list
=== CONT  TestWorkflows/workflow-enable-disable
=== CONT  TestWorkflows/run-cancel
=== CONT  TestWorkflows/cache-list-empty
=== CONT  TestWorkflows/run-view-log-escape-sequences
=== NAME  TestWorkflows/cache-list-empty
    cmd.go:376: 
    testscript.go:584: WORK=$WORK
        PATH=/var/folders/z7/869nt6ns29d77xln9h9bm8680000gn/T/testscript-main1557404561/bin:/Users/williammartin/go/pkg/mod/golang.org/[email protected]/bin:/Users/williammartin/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/pkg/env/global/bin:/Users/williammartin/.local/bin
        GOTRACEBACK=system
        HOME=$WORK
        TMPDIR=$WORK/.tmp
        devnull=/dev/null
        /=/
        :=:
        $=$
        exe=
        SCRIPT_NAME=cache_list_empty
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        GH_TOKEN=gho_************************************
        RANDOM_STRING=sHszzGkbXa
        GH_TELEMETRY=false
        
        # It's unclear what we want to do with these acceptance tests beyond our GHEC discovery, so skip new ones by default (0.000s)
        > skip
        
=== NAME  TestWorkflows/workflow-view
    testscript.go:584: WORK=$WORK
        PATH=/var/folders/z7/869nt6ns29d77xln9h9bm8680000gn/T/testscript-main1557404561/bin:/Users/williammartin/go/pkg/mod/golang.org/[email protected]/bin:/Users/williammartin/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/pkg/env/global/bin:/Users/williammartin/.local/bin
        GOTRACEBACK=system
        HOME=$WORK
        TMPDIR=$WORK/.tmp
        devnull=/dev/null
        /=/
        :=:
        $=$
        exe=
        SCRIPT_NAME=workflow_view
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        GH_TOKEN=gho_************************************
        RANDOM_STRING=KmJvsPwfmb
        GH_TELEMETRY=false
        
        # Use gh as a credential helper (0.599s)
        > exec gh auth setup-git
        # Create a repository with a file so it has a default branch (2.220s)
        > exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --add-readme --private
        [stdout]
        https://github.com/gh-acceptance-testing/workflow_view-KmJvsPwfmb
        # Defer repo cleanup (0.000s)
        > defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING
        # Clone the repo (1.519s)
        > exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
        [stderr]
        Cloning into 'workflow_view-KmJvsPwfmb'...
        # commit the workflow file (1.312s)
        > cd $SCRIPT_NAME-$RANDOM_STRING
        $WORK/workflow_view-KmJvsPwfmb
        > mkdir .github/workflows
        > mv ../workflow.yml .github/workflows/workflow.yml
        > exec git add .github/workflows/workflow.yml
        > exec git commit -m 'Create workflow file'
        [stdout]
        [main f53cf9a] Create workflow file
         1 file changed, 24 insertions(+)
         create mode 100644 .github/workflows/workflow.yml
        > exec git push -u origin main
        [stdout]
        branch 'main' set up to track 'origin/main'.
        [stderr]
        To https://github.com/gh-acceptance-testing/workflow_view-KmJvsPwfmb.git
           5f2e3f0..f53cf9a  main -> main
        # Sleep because it takes a second for the workflow to register (1.001s)
        > sleep 1
        # Check the workflow is indeed created (0.555s)
        > exec gh workflow view 'Test Workflow Name'
        [stdout]
        Test Workflow Name - workflow.yml
        ID: 327845210
        
        Total runs 0
        
        To see the YAML for this workflow, try: gh workflow view workflow.yml --yaml
        > stdout 'Test Workflow Name - workflow.yml'
        PASS
        
=== NAME  TestWorkflows/workflow-list
    testscript.go:584: WORK=$WORK
        PATH=/var/folders/z7/869nt6ns29d77xln9h9bm8680000gn/T/testscript-main1557404561/bin:/Users/williammartin/go/pkg/mod/golang.org/[email protected]/bin:/Users/williammartin/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/pkg/env/global/bin:/Users/williammartin/.local/bin
        GOTRACEBACK=system
        HOME=$WORK
        TMPDIR=$WORK/.tmp
        devnull=/dev/null
        /=/
        :=:
        $=$
        exe=
        SCRIPT_NAME=workflow_list
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        GH_TOKEN=gho_************************************
        RANDOM_STRING=ymmbQJCAMe
        GH_TELEMETRY=false
        
        # Use gh as a credential helper (0.595s)
        > exec gh auth setup-git
        # Create a repository with a file so it has a default branch (3.213s)
        > exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --add-readme --private
        [stdout]
        https://github.com/gh-acceptance-testing/workflow_list-ymmbQJCAMe
        # Defer repo cleanup (0.000s)
        > defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING
        # Clone the repo (1.468s)
        > exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
        [stderr]
        Cloning into 'workflow_list-ymmbQJCAMe'...
        # commit the workflow file (1.225s)
        > cd $SCRIPT_NAME-$RANDOM_STRING
        $WORK/workflow_list-ymmbQJCAMe
        > mkdir .github/workflows
        > mv ../workflow.yml .github/workflows/workflow.yml
        > exec git add .github/workflows/workflow.yml
        > exec git commit -m 'Create workflow file'
        [stdout]
        [main 2d19e81] Create workflow file
         1 file changed, 24 insertions(+)
         create mode 100644 .github/workflows/workflow.yml
        > exec git push -u origin main
        [stdout]
        branch 'main' set up to track 'origin/main'.
        [stderr]
        To https://github.com/gh-acceptance-testing/workflow_list-ymmbQJCAMe.git
           92d4206..2d19e81  main -> main
        # Sleep because it takes a second for the workflow to register (1.001s)
        > sleep 1
        # Check the workflow is indeed created (0.346s)
        > exec gh workflow list
        [stdout]
        Test Workflow Name      active  327845222
        > stdout 'Test Workflow Name'
        PASS
        
=== NAME  TestWorkflows/workflow-enable-disable
    testscript.go:584: WORK=$WORK
        PATH=/var/folders/z7/869nt6ns29d77xln9h9bm8680000gn/T/testscript-main1557404561/bin:/Users/williammartin/go/pkg/mod/golang.org/[email protected]/bin:/Users/williammartin/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/pkg/env/global/bin:/Users/williammartin/.local/bin
        GOTRACEBACK=system
        HOME=$WORK
        TMPDIR=$WORK/.tmp
        devnull=/dev/null
        /=/
        :=:
        $=$
        exe=
        SCRIPT_NAME=workflow_enable_disable
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        GH_TOKEN=gho_************************************
        RANDOM_STRING=ZuRxwNElVP
        GH_TELEMETRY=false
        
        # Use gh as a credential helper (0.598s)
        > exec gh auth setup-git
        # Create a repository with a file so it has a default branch (2.529s)
        > exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --add-readme --private
        [stdout]
        https://github.com/gh-acceptance-testing/workflow_enable_disable-ZuRxwNElVP
        # Defer repo cleanup (0.000s)
        > defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING
        # Clone the repo (1.567s)
        > exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
        [stderr]
        Cloning into 'workflow_enable_disable-ZuRxwNElVP'...
        # commit the workflow file (1.407s)
        > cd $SCRIPT_NAME-$RANDOM_STRING
        $WORK/workflow_enable_disable-ZuRxwNElVP
        > mkdir .github/workflows
        > mv ../workflow.yml .github/workflows/workflow.yml
        > exec git add .github/workflows/workflow.yml
        > exec git commit -m 'Create workflow file'
        [stdout]
        [main e2baca9] Create workflow file
         1 file changed, 24 insertions(+)
         create mode 100644 .github/workflows/workflow.yml
        > exec git push -u origin main
        [stdout]
        branch 'main' set up to track 'origin/main'.
        [stderr]
        To https://github.com/gh-acceptance-testing/workflow_enable_disable-ZuRxwNElVP.git
           81d9ba7..e2baca9  main -> main
        # Sleep because it takes a second for the workflow to register (1.001s)
        > sleep 1
        # Check the workflow is indeed created (0.350s)
        > exec gh workflow list
        [stdout]
        Test Workflow Name      active  327845214
        > stdout 'Test Workflow Name'
        # disable the workflow (0.622s)
        > exec gh workflow disable 'Test Workflow Name'
        # Check that the listing shows it is disabled (0.376s)
        > exec gh workflow list --all
        [stdout]
        Test Workflow Name      disabled_manually       327845214
        > stdout 'Test\s+Workflow\s+Name\s+disabled_manually\s+\d+'
        # enable the workflow (0.669s)
        > exec gh workflow enable 'Test Workflow Name'
        # Check the workflow is indeed enabled (0.318s)
        > exec gh workflow list
        [stdout]
        Test Workflow Name      active  327845214
        > stdout 'Test\s+Workflow\s+Name\s+active\s+\d+'
        PASS
        
=== NAME  TestWorkflows/workflow-run
    testscript.go:584: WORK=$WORK
        PATH=/var/folders/z7/869nt6ns29d77xln9h9bm8680000gn/T/testscript-main1557404561/bin:/Users/williammartin/go/pkg/mod/golang.org/[email protected]/bin:/Users/williammartin/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/pkg/env/global/bin:/Users/williammartin/.local/bin
        GOTRACEBACK=system
        HOME=$WORK
        TMPDIR=$WORK/.tmp
        devnull=/dev/null
        /=/
        :=:
        $=$
        exe=
        SCRIPT_NAME=workflow_run
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        GH_TOKEN=gho_************************************
        RANDOM_STRING=XDCiumbcpQ
        GH_TELEMETRY=false
        
        # Use gh as a credential helper (0.592s)
        > exec gh auth setup-git
        # Create a repository with a file so it has a default branch (2.696s)
        > exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --add-readme --private
        [stdout]
        https://github.com/gh-acceptance-testing/workflow_run-XDCiumbcpQ
        # Defer repo cleanup (0.000s)
        > defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING
        # Clone the repo (1.414s)
        > exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
        [stderr]
        Cloning into 'workflow_run-XDCiumbcpQ'...
        # commit the workflow file (1.559s)
        > cd $SCRIPT_NAME-$RANDOM_STRING
        $WORK/workflow_run-XDCiumbcpQ
        > mkdir .github/workflows
        > mv ../workflow.yml .github/workflows/workflow.yml
        > exec git add .github/workflows/workflow.yml
        > exec git commit -m 'Create workflow file'
        [stdout]
        [main 02f5add] Create workflow file
         1 file changed, 24 insertions(+)
         create mode 100644 .github/workflows/workflow.yml
        > exec git push -u origin main
        [stdout]
        branch 'main' set up to track 'origin/main'.
        [stderr]
        To https://github.com/gh-acceptance-testing/workflow_run-XDCiumbcpQ.git
           9586313..02f5add  main -> main
        # Sleep because it takes a second for the workflow to register (1.000s)
        > sleep 1
        # Check the workflow is indeed created (0.364s)
        > exec gh workflow list
        [stdout]
        Test Workflow Name      active  327845216
        > stdout 'Test Workflow Name'
        # Run the workflow (2.012s)
        > exec gh workflow run 'Test Workflow Name'
        [stdout]
        https://github.com/gh-acceptance-testing/workflow_run-XDCiumbcpQ/actions/runs/31011614930
        # It takes some time for a workflow run to register (10.001s)
        > sleep 10
        # Check the workflow run exists (0.716s)
        > exec gh run list
        [stdout]
        queued          Test Workflow Name      Test Workflow Name      main    workflow_dispatch       31011614930     11s     2026-08-05T13:43:48Z
        > stdout 'Test Workflow Name' 
        PASS
        
=== NAME  TestWorkflows/run-view
    testscript.go:584: WORK=$WORK
        PATH=/var/folders/z7/869nt6ns29d77xln9h9bm8680000gn/T/testscript-main1557404561/bin:/Users/williammartin/go/pkg/mod/golang.org/[email protected]/bin:/Users/williammartin/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/pkg/env/global/bin:/Users/williammartin/.local/bin
        GOTRACEBACK=system
        HOME=$WORK
        TMPDIR=$WORK/.tmp
        devnull=/dev/null
        /=/
        :=:
        $=$
        exe=
        SCRIPT_NAME=run_view
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        GH_TOKEN=gho_************************************
        RANDOM_STRING=FoqRkODdAA
        GH_TELEMETRY=false
        
        # Use gh as a credential helper (0.596s)
        > exec gh auth setup-git
        # Create a repository with a file so it has a default branch (3.257s)
        > exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --add-readme --private
        [stdout]
        https://github.com/gh-acceptance-testing/run_view-FoqRkODdAA
        # Defer repo cleanup (0.000s)
        > defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING
        # Clone the repo (1.435s)
        > exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
        [stderr]
        Cloning into 'run_view-FoqRkODdAA'...
        # commit the workflow file (1.297s)
        > cd $SCRIPT_NAME-$RANDOM_STRING
        $WORK/run_view-FoqRkODdAA
        > mkdir .github/workflows
        > mv ../workflow.yml .github/workflows/workflow.yml
        > exec git add .github/workflows/workflow.yml
        > exec git commit -m 'Create workflow file'
        [stdout]
        [main 509dde9] Create workflow file
         1 file changed, 22 insertions(+)
         create mode 100644 .github/workflows/workflow.yml
        > exec git push -u origin main
        [stdout]
        branch 'main' set up to track 'origin/main'.
        [stderr]
        To https://github.com/gh-acceptance-testing/run_view-FoqRkODdAA.git
           be0e194..509dde9  main -> main
        # Sleep because it takes a second for the workflow to register (1.001s)
        > sleep 1
        # Check the workflow is indeed created (0.305s)
        > exec gh workflow list
        [stdout]
        Test Workflow Name      active  327845220
        > stdout 'Test Workflow Name'
        # Run the workflow (2.290s)
        > exec gh workflow run 'Test Workflow Name'
        [stdout]
        https://github.com/gh-acceptance-testing/run_view-FoqRkODdAA/actions/runs/31011615703
        # It takes some time for a workflow run to register (10.001s)
        > sleep 10
        # Get the run ID we want to view (0.676s)
        > exec gh run list --json databaseId --jq '.[0].databaseId'
        [stdout]
        31011615703
        > stdout2env RUN_ID
        # Wait for workflow to complete (0.611s)
        > exec gh run watch $RUN_ID --exit-status
        [stdout]
        Run Test Workflow Name (31011615703) has already completed with 'success'
        # View the workflow run (1.657s)
        > exec gh run view $RUN_ID
        [stdout]
        
        ✓ main Test Workflow Name · 31011615703
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        ✓ build in 3s (ID 92324980883)
        
        For more information about the job, try: gh run view --job=92324980883
        View this run on GitHub: https://github.com/gh-acceptance-testing/run_view-FoqRkODdAA/actions/runs/31011615703
        PASS
        
=== NAME  TestWorkflows/run-view-log-escape-sequences
    testscript.go:584: WORK=$WORK
        PATH=/var/folders/z7/869nt6ns29d77xln9h9bm8680000gn/T/testscript-main1557404561/bin:/Users/williammartin/go/pkg/mod/golang.org/[email protected]/bin:/Users/williammartin/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/pkg/env/global/bin:/Users/williammartin/.local/bin
        GOTRACEBACK=system
        HOME=$WORK
        TMPDIR=$WORK/.tmp
        devnull=/dev/null
        /=/
        :=:
        $=$
        exe=
        SCRIPT_NAME=run_view_log_escape_sequences
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        GH_TOKEN=gho_************************************
        RANDOM_STRING=qiJPkvzOmB
        GH_TELEMETRY=false
        
        # This test ensures that a malicious workflow which emit terminal control sequences (ESC, OSC, CSI) in
        # its log output does not result in terminal injection when logs are displayed using `gh run view --log`
        # Use gh as a credential helper (0.593s)
        > exec gh auth setup-git
        # Create a repository with a file so it has a default branch (3.146s)
        > exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --add-readme --private
        [stdout]
        https://github.com/gh-acceptance-testing/run_view_log_escape_sequences-qiJPkvzOmB
        # Defer repo cleanup (0.000s)
        > defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING
        # Clone the repo (1.502s)
        > exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
        [stderr]
        Cloning into 'run_view_log_escape_sequences-qiJPkvzOmB'...
        # Commit the workflow file (1.629s)
        > cd $SCRIPT_NAME-$RANDOM_STRING
        $WORK/run_view_log_escape_sequences-qiJPkvzOmB
        > mkdir .github/workflows
        > mv ../workflow.yml .github/workflows/workflow.yml
        > exec git add .github/workflows/workflow.yml
        > exec git commit -m 'Create workflow with escape sequences'
        [stdout]
        [main 350b60e] Create workflow with escape sequences
         1 file changed, 17 insertions(+)
         create mode 100644 .github/workflows/workflow.yml
        > exec git push -u origin main
        [stdout]
        branch 'main' set up to track 'origin/main'.
        [stderr]
        To https://github.com/gh-acceptance-testing/run_view_log_escape_sequences-qiJPkvzOmB.git
           2815fce..350b60e  main -> main
        # Sleep because it takes a second for the workflow to register (1.001s)
        > sleep 1
        # Run the workflow (2.291s)
        > exec gh workflow run 'Escape Sequence PoC'
        [stdout]
        https://github.com/gh-acceptance-testing/run_view_log_escape_sequences-qiJPkvzOmB/actions/runs/31011615608
        # It takes some time for a workflow run to register (10.000s)
        > sleep 10
        # Get the run ID we want to view (0.690s)
        > exec gh run list --json databaseId --jq '.[0].databaseId'
        [stdout]
        31011615608
        > stdout2env RUN_ID
        # Wait for workflow to complete (2.103s)
        > exec gh run watch $RUN_ID --exit-status
        [stdout]
        ✓ main Escape Sequence PoC · 31011615608
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        ✓ emit-escape-sequences in 4s (ID 92324980650)
          ✓ Set up job
          ✓ Emit terminal escape sequences
          ✓ Complete job
        # View the logs and check that raw ESC bytes (0x1b) are NOT present in output.
        # If this assertion fails, it means terminal escape sequences from the workflow
        # log are being passed through to the user's terminal unsanitised. (1.723s)
        > exec gh run view $RUN_ID --log
        [stdout]
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7179221Z Current runner version: '2.336.0'
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7215375Z ##[group]Runner Image Provisioner
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7217279Z Hosted Compute Agent
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7218290Z Version: 20260707.563
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7219302Z Commit: 02667638d2b423fbc733a8e32a88b44996a3ba6e
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7220601Z Build Date: 2026-07-07T19:33:50Z
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7221747Z Worker ID: {941b2953-059d-441d-bdf6-ece706f1e0ee}
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7222966Z Azure Region: westus
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7224688Z ##[endgroup]
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7227211Z ##[group]Operating System
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7228423Z Ubuntu
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7229393Z 24.04.4
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7230527Z LTS
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7231650Z ##[endgroup]
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7232655Z ##[group]Runner Image
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7233686Z Image: ubuntu-24.04
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7235086Z Version: 20260720.247.2
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7236995Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20260720.247/images/ubuntu/Ubuntu2404-Readme.md
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7240478Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20260720.247
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7242367Z ##[endgroup]
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7245005Z ##[group]GITHUB_TOKEN Permissions
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7247968Z Contents: read
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7249136Z Metadata: read
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7250117Z Packages: read
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7251013Z ##[endgroup]
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7254478Z Secret source: Actions
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7256250Z Prepare workflow directory
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7778037Z Prepare all required actions
        emit-escape-sequences   Set up job      2026-08-05T13:43:56.7938356Z Complete job name: emit-escape-sequences
        emit-escape-sequences   Emit terminal escape sequences  2026-08-05T13:43:56.9716802Z ##[group]Run # OSC title set: \x1b]0;TITLE\x07
        emit-escape-sequences   Emit terminal escape sequences  2026-08-05T13:43:56.9718157Z ^[[36;1m# OSC title set: \x1b]0;TITLE\x07^[[0m
        emit-escape-sequences   Emit terminal escape sequences  2026-08-05T13:43:56.9719818Z ^[[36;1mprintf 'ESCAPE_MARKER_START \033]0;HIJACKED_TITLE\007 ESCAPE_MARKER_END\n'^[[0m
        emit-escape-sequences   Emit terminal escape sequences  2026-08-05T13:43:56.9721367Z ^[[36;1m# CSI color: \x1b[31m ... \x1b[0m^[[0m
        emit-escape-sequences   Emit terminal escape sequences  2026-08-05T13:43:56.9723070Z ^[[36;1mprintf 'ESCAPE_MARKER_START \033[31mRED_TEXT\033[0m ESCAPE_MARKER_END\n'^[[0m
        emit-escape-sequences   Emit terminal escape sequences  2026-08-05T13:43:56.9725457Z ^[[36;1m# Screen title set (from original PoC): \x1bk ... \x1b\\^[[0m
        emit-escape-sequences   Emit terminal escape sequences  2026-08-05T13:43:56.9727271Z ^[[36;1mprintf 'ESCAPE_MARKER_START \033k;malicious command;\033\\ ESCAPE_MARKER_END\n'^[[0m
        emit-escape-sequences   Emit terminal escape sequences  2026-08-05T13:43:57.0469550Z shell: /usr/bin/bash -e {0}
        emit-escape-sequences   Emit terminal escape sequences  2026-08-05T13:43:57.0471054Z ##[endgroup]
        emit-escape-sequences   Emit terminal escape sequences  2026-08-05T13:43:57.1677855Z ESCAPE_MARKER_START ^[]0;HIJACKED_TITLE^G ESCAPE_MARKER_END
        emit-escape-sequences   Emit terminal escape sequences  2026-08-05T13:43:57.1678943Z ESCAPE_MARKER_START ^[[31mRED_TEXT^[[0m ESCAPE_MARKER_END
        emit-escape-sequences   Emit terminal escape sequences  2026-08-05T13:43:57.1679969Z ESCAPE_MARKER_START ^[k;malicious command;^[\ ESCAPE_MARKER_END
        emit-escape-sequences   Complete job    2026-08-05T13:43:57.1893225Z Cleaning up orphan processes
        # The output should contain the safe/visible text but not raw ESC bytes.
        # \x1b is the ESC byte - it must not appear in the output. (0.000s)
        > ! stdout '\x1b'
        # The log output should still contain the non-escape parts of the log lines. (0.000s)
        > stdout 'ESCAPE_MARKER_START'
        > stdout 'ESCAPE_MARKER_END'
        PASS
        
=== NAME  TestWorkflows/run-delete
    testscript.go:584: WORK=$WORK
        PATH=/var/folders/z7/869nt6ns29d77xln9h9bm8680000gn/T/testscript-main1557404561/bin:/Users/williammartin/go/pkg/mod/golang.org/[email protected]/bin:/Users/williammartin/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/pkg/env/global/bin:/Users/williammartin/.local/bin
        GOTRACEBACK=system
        HOME=$WORK
        TMPDIR=$WORK/.tmp
        devnull=/dev/null
        /=/
        :=:
        $=$
        exe=
        SCRIPT_NAME=run_delete
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        GH_TOKEN=gho_************************************
        RANDOM_STRING=kMODrjNlQv
        GH_TELEMETRY=false
        
        # Use gh as a credential helper (0.596s)
        > exec gh auth setup-git
        # Create a repository with a file so it has a default branch (2.996s)
        > exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --add-readme --private
        [stdout]
        https://github.com/gh-acceptance-testing/run_delete-kMODrjNlQv
        # Defer repo cleanup (0.000s)
        > defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING
        # Clone the repo (1.707s)
        > exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
        [stderr]
        Cloning into 'run_delete-kMODrjNlQv'...
        # commit the workflow file (1.329s)
        > cd $SCRIPT_NAME-$RANDOM_STRING
        $WORK/run_delete-kMODrjNlQv
        > mkdir .github/workflows
        > mv ../workflow.yml .github/workflows/workflow.yml
        > exec git add .github/workflows/workflow.yml
        > exec git commit -m 'Create workflow file'
        [stdout]
        [main 0f1dd76] Create workflow file
         1 file changed, 22 insertions(+)
         create mode 100644 .github/workflows/workflow.yml
        > exec git push -u origin main
        [stdout]
        branch 'main' set up to track 'origin/main'.
        [stderr]
        To https://github.com/gh-acceptance-testing/run_delete-kMODrjNlQv.git
           0d7fcfe..0f1dd76  main -> main
        # Sleep because it takes a second for the workflow to register (1.000s)
        > sleep 1
        # Check the workflow is indeed created (0.308s)
        > exec gh workflow list
        [stdout]
        Test Workflow Name      active  327845226
        > stdout 'Test Workflow Name'
        # Run the workflow (2.230s)
        > exec gh workflow run 'Test Workflow Name'
        [stdout]
        https://github.com/gh-acceptance-testing/run_delete-kMODrjNlQv/actions/runs/31011615579
        # It takes some time for a workflow run to register (10.000s)
        > sleep 10
        # Get the run ID we want to watch & delete (0.790s)
        > exec gh run list --json databaseId --jq '.[0].databaseId'
        [stdout]
        31011615579
        > stdout2env RUN_ID
        # Wait for workflow to complete (2.004s)
        > exec gh run watch $RUN_ID --exit-status
        [stdout]
        ✓ main Test Workflow Name · 31011615579
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        ✓ build in 4s (ID 92324982137)
          ✓ Set up job
          ✓ Run a one-line script
          ✓ Complete job
        # Delete the workflow run (1.055s)
        > exec gh run delete $RUN_ID
        [stdout]
        ✓ Request to delete workflow run submitted.
        > stdout '✓ Request to delete workflow run submitted.'
        # It takes some time for a workflow run to be deleted (5.000s)
        > sleep 5
        # Check the workflow run is cancelled, which is implied by an empty list (0.347s)
        > exec gh run list
        > stdout ''
        PASS
        
=== NAME  TestWorkflows/cache-list-delete
    testscript.go:584: WORK=$WORK
        PATH=/var/folders/z7/869nt6ns29d77xln9h9bm8680000gn/T/testscript-main1557404561/bin:/Users/williammartin/go/pkg/mod/golang.org/[email protected]/bin:/Users/williammartin/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/pkg/env/global/bin:/Users/williammartin/.local/bin
        GOTRACEBACK=system
        HOME=$WORK
        TMPDIR=$WORK/.tmp
        devnull=/dev/null
        /=/
        :=:
        $=$
        exe=
        SCRIPT_NAME=cache_list_delete
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        GH_TOKEN=gho_************************************
        RANDOM_STRING=KKfevSMuVL
        GH_TELEMETRY=false
        
        # Use gh as a credential helper (0.596s)
        > exec gh auth setup-git
        # Create a repository with a file so it has a default branch (3.004s)
        > exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --add-readme --private
        [stdout]
        https://github.com/gh-acceptance-testing/cache_list_delete-KKfevSMuVL
        # Defer repo cleanup (0.000s)
        > defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING
        # Clone the repo (1.684s)
        > exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
        [stderr]
        Cloning into 'cache_list_delete-KKfevSMuVL'...
        # commit the workflow file (1.460s)
        > cd $SCRIPT_NAME-$RANDOM_STRING
        $WORK/cache_list_delete-KKfevSMuVL
        > mkdir .github/workflows
        > mv ../workflow.yml .github/workflows/workflow.yml
        > exec git add .github/workflows/workflow.yml
        > exec git commit -m 'Create workflow file'
        [stdout]
        [main e5984e7] Create workflow file
         1 file changed, 21 insertions(+)
         create mode 100644 .github/workflows/workflow.yml
        > exec git push -u origin main
        [stdout]
        branch 'main' set up to track 'origin/main'.
        [stderr]
        To https://github.com/gh-acceptance-testing/cache_list_delete-KKfevSMuVL.git
           a3f76a5..e5984e7  main -> main
        # Sleep because it takes a second for the workflow to register (1.001s)
        > sleep 1
        # Check the workflow is indeed created (0.348s)
        > exec gh workflow list
        [stdout]
        Test Workflow Name      active  327845225
        > stdout 'Test Workflow Name'
        # Run the workflow (1.926s)
        > exec gh workflow run 'Test Workflow Name'
        [stdout]
        https://github.com/gh-acceptance-testing/cache_list_delete-KKfevSMuVL/actions/runs/31011615515
        # It takes some time for a workflow run to register (10.001s)
        > sleep 10
        # Get the run ID we want to watch (0.663s)
        > exec gh run list --json databaseId --jq '.[0].databaseId'
        [stdout]
        31011615515
        > stdout2env RUN_ID
        # Wait for workflow to complete (9.723s)
        > exec gh run watch $RUN_ID --exit-status
        [stdout]
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011615515
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        * build (ID 92324980436)
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011615515
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        * build (ID 92324980436)
          ✓ Set up job
          ✓ Run actions/checkout@v4
          ✓ Cache values
          ✓ Generate values file
          ✓ Post Cache values
          * Post Run actions/checkout@v4
        ✓ main Test Workflow Name · 31011615515
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        ✓ build in 6s (ID 92324980436)
          ✓ Set up job
          ✓ Run actions/checkout@v4
          ✓ Cache values
          ✓ Generate values file
          ✓ Post Cache values
          ✓ Post Run actions/checkout@v4
          ✓ Complete job
        # List the cache (0.348s)
        > exec gh cache list
        [stdout]
        6366610571      Linux-values    193 B   2026-08-05T13:44:04Z    2026-08-05T13:44:04Z
        > stdout 'Linux-values'
        # Delete the cache (0.404s)
        > exec gh cache delete 'Linux-values'
        PASS
        
=== NAME  TestWorkflows/run-download
    testscript.go:584: WORK=$WORK
        PATH=/var/folders/z7/869nt6ns29d77xln9h9bm8680000gn/T/testscript-main1557404561/bin:/Users/williammartin/go/pkg/mod/golang.org/[email protected]/bin:/Users/williammartin/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/pkg/env/global/bin:/Users/williammartin/.local/bin
        GOTRACEBACK=system
        HOME=$WORK
        TMPDIR=$WORK/.tmp
        devnull=/dev/null
        /=/
        :=:
        $=$
        exe=
        SCRIPT_NAME=run_download
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        GH_TOKEN=gho_************************************
        RANDOM_STRING=VBWojiykmu
        GH_TELEMETRY=false
        
        # Set up env (0.000s)
        > env REPO=${SCRIPT_NAME}-${RANDOM_STRING}
        # Use gh as a credential helper (0.595s)
        > exec gh auth setup-git
        # Create a repository with a file so it has a default branch (2.512s)
        > exec gh repo create ${ORG}/${REPO} --add-readme --private
        [stdout]
        https://github.com/gh-acceptance-testing/run_download-VBWojiykmu
        # Defer repo cleanup (0.000s)
        > defer gh repo delete --yes ${ORG}/${REPO}
        # Clone the repo (1.401s)
        > exec gh repo clone ${ORG}/${REPO}
        [stderr]
        Cloning into 'run_download-VBWojiykmu'...
        # commit the workflow file (1.357s)
        > cd ${REPO}
        $WORK/run_download-VBWojiykmu
        > mkdir .github/workflows
        > mv ../workflow.yml .github/workflows/workflow.yml
        > exec git add .github/workflows/workflow.yml
        > exec git commit -m 'Create workflow file'
        [stdout]
        [main 13ced61] Create workflow file
         1 file changed, 25 insertions(+)
         create mode 100644 .github/workflows/workflow.yml
        > exec git push -u origin main
        [stdout]
        branch 'main' set up to track 'origin/main'.
        [stderr]
        To https://github.com/gh-acceptance-testing/run_download-VBWojiykmu.git
           8b23347..13ced61  main -> main
        # Sleep because it takes a second for the workflow to register (1.001s)
        > sleep 1
        # Check the workflow is indeed created (0.344s)
        > exec gh workflow list
        [stdout]
        Test Workflow Name      active  327845211
        > stdout 'Test Workflow Name'
        # Run the workflow (2.138s)
        > exec gh workflow run 'Test Workflow Name'
        [stdout]
        https://github.com/gh-acceptance-testing/run_download-VBWojiykmu/actions/runs/31011614465
        # It takes some time for a workflow run to register (10.001s)
        > sleep 10
        # Get the run ID we want to watch (0.802s)
        > exec gh run list --json databaseId --jq '.[0].databaseId'
        [stdout]
        31011614465
        > stdout2env RUN_ID
        # Wait for workflow to complete (13.631s)
        > exec gh run watch ${RUN_ID} --exit-status
        [stdout]
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011614465
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        * build (ID 92324979671)
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011614465
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        * build (ID 92324979671)
          * Set up job
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011614465
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        ✓ build in 5s (ID 92324979671)
          ✓ Set up job
          ✓ Run mkdir -p ./parent/child
          ✓ Run actions/upload-artifact@v4
          ✓ Complete job
        ✓ main Test Workflow Name · 31011614465
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        ✓ build in 5s (ID 92324979671)
          ✓ Set up job
          ✓ Run mkdir -p ./parent/child
          ✓ Run actions/upload-artifact@v4
          ✓ Complete job
        # Download the artifact to current dir (1.090s)
        > exec gh run download ${RUN_ID}
        # Check that we downloaded the artifact and extracted into a dir with the name of the artifact (0.000s)
        > exists ./my-artifact/child/world.txt
        # Remove the artifact (0.005s)
        > rm ./my-artifact
        # Download the artifact via name to current dir (0.994s)
        > exec gh run download -n 'my-artifact' ${RUN_ID}
        # Check that we downloaded the artifact and extracted into the current dir (0.000s)
        > exists ./child/world.txt
        # Download the artifact via name to a specific dir (1.244s)
        > exec gh run download -n 'my-artifact' ${RUN_ID} --dir '..'
        # Check that we downloaded the artifact and extracted into the specified dir (0.000s)
        > exists ../child/world.txt
        PASS
        
=== NAME  TestWorkflows/run-rerun
    testscript.go:584: WORK=$WORK
        PATH=/var/folders/z7/869nt6ns29d77xln9h9bm8680000gn/T/testscript-main1557404561/bin:/Users/williammartin/go/pkg/mod/golang.org/[email protected]/bin:/Users/williammartin/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/pkg/env/global/bin:/Users/williammartin/.local/bin
        GOTRACEBACK=system
        HOME=$WORK
        TMPDIR=$WORK/.tmp
        devnull=/dev/null
        /=/
        :=:
        $=$
        exe=
        SCRIPT_NAME=run_rerun
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        GH_TOKEN=gho_************************************
        RANDOM_STRING=wsVIpZtDkB
        GH_TELEMETRY=false
        
        # Use gh as a credential helper (0.599s)
        > exec gh auth setup-git
        # Create a repository with a file so it has a default branch (3.044s)
        > exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --add-readme --private
        [stdout]
        https://github.com/gh-acceptance-testing/run_rerun-wsVIpZtDkB
        # Defer repo cleanup (0.000s)
        > defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING
        # Clone the repo (1.694s)
        > exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
        [stderr]
        Cloning into 'run_rerun-wsVIpZtDkB'...
        # commit the workflow file (1.303s)
        > cd $SCRIPT_NAME-$RANDOM_STRING
        $WORK/run_rerun-wsVIpZtDkB
        > mkdir .github/workflows
        > mv ../workflow.yml .github/workflows/workflow.yml
        > exec git add .github/workflows/workflow.yml
        > exec git commit -m 'Create workflow file'
        [stdout]
        [main 05d51f7] Create workflow file
         1 file changed, 22 insertions(+)
         create mode 100644 .github/workflows/workflow.yml
        > exec git push -u origin main
        [stdout]
        branch 'main' set up to track 'origin/main'.
        [stderr]
        To https://github.com/gh-acceptance-testing/run_rerun-wsVIpZtDkB.git
           335c500..05d51f7  main -> main
        # Sleep because it takes a second for the workflow to register (1.001s)
        > sleep 1
        # Check the workflow is indeed created (0.305s)
        > exec gh workflow list
        [stdout]
        Test Workflow Name      active  327845221
        > stdout 'Test Workflow Name'
        # Run the workflow (2.247s)
        > exec gh workflow run 'Test Workflow Name'
        [stdout]
        https://github.com/gh-acceptance-testing/run_rerun-wsVIpZtDkB/actions/runs/31011615705
        # It takes some time for a workflow run to register (10.001s)
        > sleep 10
        # Get the run ID we want to rerun (0.732s)
        > exec gh run list --json databaseId --jq '.[0].databaseId'
        [stdout]
        31011615705
        > stdout2env RUN_ID
        # Wait for workflow to complete (9.985s)
        > exec gh run watch $RUN_ID --exit-status
        [stdout]
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011615705
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        * build (ID 92324982512)
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011615705
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        * build (ID 92324982512)
          ✓ Set up job
          ✓ Run a one-line script
          ✓ Complete job
        ✓ main Test Workflow Name · 31011615705
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        ✓ build in 2s (ID 92324982512)
          ✓ Set up job
          ✓ Run a one-line script
          ✓ Complete job
        # Rerun the workflow run (0.934s)
        > exec gh run rerun $RUN_ID
        # It takes some time for a workflow run to register (10.001s)
        > sleep 10
        # Wait for workflow to complete (0.615s)
        > exec gh run watch $RUN_ID --exit-status
        [stdout]
        Run Test Workflow Name (31011615705) has already completed with 'success'
        PASS
        
=== NAME  TestWorkflows/run-cancel
    testscript.go:584: WORK=$WORK
        PATH=/var/folders/z7/869nt6ns29d77xln9h9bm8680000gn/T/testscript-main1557404561/bin:/Users/williammartin/go/pkg/mod/golang.org/[email protected]/bin:/Users/williammartin/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/pkg/env/global/bin:/Users/williammartin/.local/bin
        GOTRACEBACK=system
        HOME=$WORK
        TMPDIR=$WORK/.tmp
        devnull=/dev/null
        /=/
        :=:
        $=$
        exe=
        SCRIPT_NAME=run_cancel
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        GH_TOKEN=gho_************************************
        RANDOM_STRING=YDxxuQUapO
        GH_TELEMETRY=false
        
        # Use gh as a credential helper (0.596s)
        > exec gh auth setup-git
        # Create a repository with a file so it has a default branch (3.088s)
        > exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --add-readme --private
        [stdout]
        https://github.com/gh-acceptance-testing/run_cancel-YDxxuQUapO
        # Defer repo cleanup (0.000s)
        > defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING
        # Clone the repo (1.596s)
        > exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
        [stderr]
        Cloning into 'run_cancel-YDxxuQUapO'...
        # commit the workflow file (1.258s)
        > cd $SCRIPT_NAME-$RANDOM_STRING
        $WORK/run_cancel-YDxxuQUapO
        > mkdir .github/workflows
        > mv ../workflow.yml .github/workflows/workflow.yml
        > exec git add .github/workflows/workflow.yml
        > exec git commit -m 'Create workflow file'
        [stdout]
        [main dfca559] Create workflow file
         1 file changed, 24 insertions(+)
         create mode 100644 .github/workflows/workflow.yml
        > exec git push -u origin main
        [stdout]
        branch 'main' set up to track 'origin/main'.
        [stderr]
        To https://github.com/gh-acceptance-testing/run_cancel-YDxxuQUapO.git
           53977af..dfca559  main -> main
        # Sleep because it takes a second for the workflow to register (1.001s)
        > sleep 1
        # Check the workflow is indeed created (0.343s)
        > exec gh workflow list
        [stdout]
        Test Workflow Name      active  327845219
        > stdout 'Test Workflow Name'
        # Run the workflow (1.975s)
        > exec gh workflow run 'Test Workflow Name'
        [stdout]
        https://github.com/gh-acceptance-testing/run_cancel-YDxxuQUapO/actions/runs/31011615284
        # It takes some time for a workflow run to register (10.001s)
        > sleep 10
        # Get the run ID we want to cancel (0.637s)
        > exec gh run list --json databaseId --jq '.[0].databaseId'
        [stdout]
        31011615284
        > stdout2env RUN_ID
        # cancel the workflow run (0.931s)
        > exec gh run cancel $RUN_ID
        [stdout]
        ✓ Request to cancel workflow 31011615284 submitted.
        > stdout '✓ Request to cancel workflow [0-9]+ submitted.'
        # Wait for workflow to complete (26.387s)
        > exec gh run watch $RUN_ID
        [stdout]
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011615284
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        * build (ID 92324983228)
          ✓ Set up job
          ✓ Run actions/checkout@v4
          * Run a one-line script
          * Post Run actions/checkout@v4
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011615284
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        * build (ID 92324983228)
          ✓ Set up job
          ✓ Run actions/checkout@v4
          * Run a one-line script
          * Post Run actions/checkout@v4
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011615284
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        * build (ID 92324983228)
          ✓ Set up job
          ✓ Run actions/checkout@v4
          * Run a one-line script
          * Post Run actions/checkout@v4
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011615284
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        * build (ID 92324983228)
          ✓ Set up job
          ✓ Run actions/checkout@v4
          * Run a one-line script
          * Post Run actions/checkout@v4
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011615284
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        * build (ID 92324983228)
          ✓ Set up job
          ✓ Run actions/checkout@v4
          * Run a one-line script
          * Post Run actions/checkout@v4
        Refreshing run status every 3 seconds. Press Ctrl+C to quit.
        
        * main Test Workflow Name · 31011615284
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        * build (ID 92324983228)
          ✓ Set up job
          ✓ Run actions/checkout@v4
          X Run a one-line script
          * Post Run actions/checkout@v4
        X main Test Workflow Name · 31011615284
        Triggered via workflow_dispatch less than a minute ago
        
        JOBS
        X build in 28s (ID 92324983228)
          ✓ Set up job
          ✓ Run actions/checkout@v4
          X Run a one-line script
          ✓ Post Run actions/checkout@v4
          ✓ Complete job
        
        ANNOTATIONS
        X The run was canceled by @williammartin.
        build: .github#1
        
        ! Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
        build: .github#3
        
        X The operation was canceled.
        build: .github#5
        
        # Check the workflow run is cancelled (0.625s)
        > exec gh run list --json conclusion --jq '.[0].conclusion'
        [stdout]
        cancelled
        > stdout 'cancelled'
        PASS
        
--- PASS: TestWorkflows (0.00s)
    --- SKIP: TestWorkflows/cache-list-empty (0.03s)
    --- PASS: TestWorkflows/workflow-view (7.82s)
    --- PASS: TestWorkflows/workflow-list (8.45s)
    --- PASS: TestWorkflows/workflow-enable-disable (9.99s)
    --- PASS: TestWorkflows/workflow-run (21.03s)
    --- PASS: TestWorkflows/run-view (23.72s)
    --- PASS: TestWorkflows/run-view-log-escape-sequences (25.31s)
    --- PASS: TestWorkflows/run-delete (29.99s)
    --- PASS: TestWorkflows/cache-list-delete (31.76s)
    --- PASS: TestWorkflows/run-download (37.70s)
    --- PASS: TestWorkflows/run-rerun (43.04s)
    --- PASS: TestWorkflows/run-cancel (49.02s)
PASS
ok      github.com/cli/cli/v2/acceptance        49.628s

Key points

None

Notes for reviewers

None.

Authorship and follow-up

Who wrote this:

  • A human wrote it.
  • An agent wrote it under close human direction.
  • An agent wrote it independently, and no human has guided the implementation beyond the initial prompt.

Who answers review comments:

  • @williammartin will read and reply directly. Name the account.
  • An agent will draft replies and @username will read them before they are posted.
  • Nobody has explicitly committed to replying.

williammartin and others added 2 commits August 5, 2026 14:15
Co-authored-by: Copilot App <[email protected]>
Copilot-Session: 476e1749-e4c0-4476-878b-6b47eae09eed
Co-authored-by: Copilot App <[email protected]>
Copilot-Session: 476e1749-e4c0-4476-878b-6b47eae09eed
@williammartin
williammartin marked this pull request as ready for review August 5, 2026 14:45
@williammartin
williammartin requested a review from a team as a code owner August 5, 2026 14:45
@williammartin
williammartin requested review from sergiou87 and a lite review from Copilot and removed request for a team August 5, 2026 14:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request continues the API client consolidation work by routing workflow run artifact listing in gh run through api.Client.RESTWithNext, removing duplicate pagination and Link-header parsing logic from the run command’s shared artifacts helper.

Changes:

  • Refactors shared.ListArtifacts to use api.Client.RESTWithNext for pagination instead of a package-local apiGet + Link parsing.
  • Adds a regression test to ensure error paths still surface as api.HTTPError with the expected status and message.
  • Adds TODO(api-client-rollout) breadcrumbs on remaining raw http.Client call sites that must stream ZIP/log bodies.
Show a summary per file
File Description
pkg/cmd/run/shared/artifacts.go Switches artifact listing to api.Client.RESTWithNext and removes duplicate HTTP + pagination helpers.
pkg/cmd/run/shared/artifacts_test.go Adds coverage asserting RESTWithNext error behavior and preserves multi-page behavior coverage.
pkg/cmd/run/view/view.go Documents why run-log ZIP streaming still uses raw http.Client.
pkg/cmd/run/view/logs.go Documents why job log fetching still returns an io.ReadCloser via raw http.Client.
pkg/cmd/run/download/http.go Documents why artifact ZIP download/streaming still uses raw http.Client.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread pkg/cmd/run/shared/artifacts.go
glslang added a commit to glslang/agent-skills that referenced this pull request Aug 5, 2026
Round 4 review on #9: one Codex P2, applied structurally.

A human judge who was asked and has not answered has no row in §2's fold,
so "their verdict isn't CHANGES_REQUESTED" was true of them, and §9
reported the PR clear — potentially merging past a required reviewer who
had not looked at it. Verified the mechanism on cli/cli#14078: a reviewer
sits in reviewRequests while others have already approved, and GitHub
drops them from that list only once they submit.

Rounds 3 and 4 both found a missing case in the same bullet (DISMISSED,
then never-answered), so patching a third one invites a fourth. The two
overlapping judge bullets are now a single table that enumerates every
state a judge can be in — pending request, CHANGES_REQUESTED, DISMISSED,
APPROVED, COMMENTED-only, and the four bot states from §6. The section is
shorter than what it replaces, and a missing case is now visible as a
missing row rather than as an unstated assumption.

The underlying rule, stated once: pending and absent are different, in
both directions. Treat pending as satisfied and you merge past a review
that was coming; treat absent as pending and you wait for one that isn't.
"Not CHANGES_REQUESTED" distinguishes neither.

§2 now collects reviewRequests alongside the fold, since the fold covers
only people who actually reviewed.

Co-Authored-By: Claude Opus 5 <[email protected]>
@williammartin
williammartin merged commit 28ebaf1 into williammartin-wp-05-release-api-client Aug 6, 2026
44 checks passed
@williammartin
williammartin deleted the williammartin-route-run-artifacts-api-client branch August 6, 2026 05:00
glslang added a commit to glslang/agent-skills that referenced this pull request Aug 6, 2026
Round 4 review on #9: one Codex P2, applied structurally.

A human judge who was asked and has not answered has no row in §2's fold,
so "their verdict isn't CHANGES_REQUESTED" was true of them, and §9
reported the PR clear — potentially merging past a required reviewer who
had not looked at it. Verified the mechanism on cli/cli#14078: a reviewer
sits in reviewRequests while others have already approved, and GitHub
drops them from that list only once they submit.

Rounds 3 and 4 both found a missing case in the same bullet (DISMISSED,
then never-answered), so patching a third one invites a fourth. The two
overlapping judge bullets are now a single table that enumerates every
state a judge can be in — pending request, CHANGES_REQUESTED, DISMISSED,
APPROVED, COMMENTED-only, and the four bot states from §6. The section is
shorter than what it replaces, and a missing case is now visible as a
missing row rather than as an unstated assumption.

The underlying rule, stated once: pending and absent are different, in
both directions. Treat pending as satisfied and you merge past a review
that was coming; treat absent as pending and you wait for one that isn't.
"Not CHANGES_REQUESTED" distinguishes neither.

§2 now collects reviewRequests alongside the fold, since the fold covers
only people who actually reviewed.

Co-Authored-By: Claude Opus 5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants