Skip to content

fix(workbench): default git clone repo, accurate skips, and R readback#490

Merged
ian-flores merged 3 commits into
mainfrom
workbench-git-skip-gate
Jul 17, 2026
Merged

fix(workbench): default git clone repo, accurate skips, and R readback#490
ian-flores merged 3 commits into
mainfrom
workbench-git-skip-gate

Conversation

@ian-flores

Copy link
Copy Markdown
Collaborator

Summary

Fixes a cluster of Workbench git-ops issues found validating a live
Workbench 2026.06 deployment. Three related fixes, each validated:

Live validation (dev.demo.posit.team, config-less, interactive auth)

Not in this PR

Test plan

  • ruff check + format clean; selftests green (config defaults, feature
    step-order guard, skip-message wording, readback regression tests)
  • git-ops scenarios still collect (6: clone/push × rstudio/vscode/positron)

Demo

Fix: workbench git-ops skip reasons + default public clone repo

This branch fixes two related Workbench git-ops bugs.

#479 — misleading auth skip when git config is absent. Every scenario in test_git_ops.feature checked the auth-session login gate BEFORE the git-config gate. Because Workbench OIDC auth is flaky under the shared-account serial run, whether a git test skipped with the config reason or the auth reason was non-deterministic — push tests could report a misleading 'Workbench session not established' error when the real reason was 'no git config / read-only'. Fixed by reordering the Gherkin steps so the config gate (and, for push scenarios, the pushing gate) resolves before the login attempt.

#483 — public-repo cloning wrongly appeared to require VIP_GIT_TOKEN. There was no default [workbench.git_test] config, so clone scenarios always skipped without one, and the skip message read as if a token was required even for anonymous public clones. Fixed by shipping a default public clone target (https://github.com/posit-dev/posit-cli.git, auth_method="none") that WorkbenchConfig.from_dict synthesizes when the block is absent, and by rewording the skip messages to make clear a token is only needed for push/private-repo scenarios.

Fix #479: config gate now precedes login in every scenario

Before this fix, step 1 was the login attempt and step 2 was the config check. Now the config check runs first:

grep -n -A4 "Scenario: Clone a Git repository in RStudio" src/vip_tests/workbench/test_git_ops.feature
11:  Scenario: Clone a Git repository in RStudio terminal
12-    Given the Git test config is available
13-    And Workbench is accessible and I am logged in
14-    When I launch an RStudio session
15-    And I clone the repository in the RStudio terminal

Fix #483: reworded skip message makes clear public clones need no token

The config-missing skip message now explicitly says an anonymous public-repo clone needs only clone_url + auth_method='none' (no token), and a token is only required for push/private-repo scenarios:

sed -n "219,225p" src/vip_tests/workbench/test_git_ops.py
        pytest.skip(
            "Git test config is not configured. "
            "Cloning a public repo needs only clone_url and auth_method='none' "
            "in a [workbench.git_test] block of vip.toml (no token required). "
            "Push/private-repo scenarios additionally need auth_method='https-token' "
            "with VIP_GIT_TOKEN set in the environment."
        )

New selftests pass: default-config synthesis, step-order guard, skip-message wording

env -u UV_PROJECT uv run --frozen --no-sync --project . pytest selftests/test_config.py selftests/test_workbench_git_ops.py -q 2>&1 | grep -E "passed|failed|error" | sed "s/ in [0-9.]*s//"
111 passed, 1 warning

Full selftest suite is green (excluding selftests/test_load_engine.py, which has pre-existing timing-flaky tests unrelated to this change; run with -n0 since the repo's default -n auto xdist parallelism is itself intermittently flaky, unrelated to this change)

UV_FROZEN=1 env -u UV_PROJECT uv run --frozen --no-sync --project . pytest selftests/ -q --ignore=selftests/test_load_engine.py -n0 2>&1 | grep -E "passed|failed|error" | sed "s/ in [0-9.]*s//"
950 passed, 11 warnings

Lint and format check (just check equivalent — run directly with the project-scoped uv wrapper since just's recipes invoke bare uv, which reroots to the parent ptd-workspace in this environment)

env -u UV_PROJECT uv run --frozen --no-sync --project . ruff check src/ src/vip_tests/ selftests/ examples/
All checks passed!
env -u UV_PROJECT uv run --frozen --no-sync --project . ruff format --check src/ src/vip_tests/ selftests/ examples/
159 files already formatted

The 6 git-ops scenarios still collect after the feature-file reorder

Workbench product tests deselect entirely without a configured URL, so this confirms collection with a minimal [workbench] config:

printf "[workbench]\nurl = \"https://workbench.example.com\"\n" > "$TMPDIR/vip_demo.toml" && env -u UV_PROJECT uv run --frozen --no-sync --project . pytest --collect-only -q --vip-config "$TMPDIR/vip_demo.toml" src/vip_tests/workbench/test_git_ops.py 2>/dev/null | sed "s/ in [0-9.]*s//"; rm -f "$TMPDIR/vip_demo.toml"
src/vip_tests/workbench/test_git_ops.py::test_clone_rstudio[chromium]
src/vip_tests/workbench/test_git_ops.py::test_push_rstudio[chromium]
src/vip_tests/workbench/test_git_ops.py::test_clone_vscode[chromium]
src/vip_tests/workbench/test_git_ops.py::test_push_vscode[chromium]
src/vip_tests/workbench/test_git_ops.py::test_clone_positron[chromium]
src/vip_tests/workbench/test_git_ops.py::test_push_positron[chromium]

6 tests collected

Follow-up fix: R file-readback quoting bug (surfaced once #483 made clone run by default)

Live validation of #483's default clone against dev.demo.posit.team exposed a third bug: read_file()'s R branches evaluated a bare paste(readLines(...), collapse="\\n") expression. The R REPL auto-prints that as a quoted, backslash-escaped character vector, which appended a stray " to the terminal_run done-marker line (...:0\"). _parse_done_marker then rejected the exit code as non-numeric, so terminal_run spun until its 120s timeout even though the underlying command had finished in ~1.5s — this is what made test_clone_rstudio time out.

The fix wraps the read in cat() via a new _read_file_r_expr helper, so R emits raw bytes instead of an auto-printed quoted vector, and drops the now-unnecessary _strip_r_index call on those reads (both the RStudio and Positron R-console readback paths used the same buggy expression).

env -u UV_PROJECT uv run --frozen --no-sync --project . pytest "selftests/test_workbench_exec.py::TestReadFileRExpr" "selftests/test_workbench_exec.py::TestParseDoneMarker::test_raw_exit_code_parses_but_quoted_does_not" -q 2>&1 | grep -E "passed|failed|error" | sed "s/ in [0-9.]*s//"
3 passed
grep -n "cat(paste(readLines" src/vip_tests/workbench/exec.py
97:    return f'cat(paste(readLines("{path}"), collapse="\\n"))'

Live validation on dev.demo.posit.team

Not re-runnable in this document (requires a live deployment + interactive browser auth), so recorded here as prose instead of an exec block:

Running a config-less --interactive-auth pass against dev.demo.posit.team (a real 2026.06-class deployment):

Two related Workbench git-ops bugs surfaced validating a live 2026.06
deployment:

- #479: push_* tests reported a misleading "Workbench session not
  established by --interactive-auth" skip when the real reason was a
  missing [workbench.git_test] block. The auth-session gate ran before
  the config gate, and flaky OIDC auth made the surfaced reason
  non-deterministic. Reorder the Gherkin steps so the config (and, for
  push, the read-only) gate runs before the login step, surfacing the
  real reason deterministically without depending on a login attempt.

- #483: cloning a public repo wrongly appeared to require VIP_GIT_TOKEN.
  Ship a default public clone repo (posit-dev/posit-cli, auth_method
  "none") so the anonymous clone scenarios run out-of-the-box with no
  config, and reword the skip messages so public cloning is shown to
  need only clone_url + auth_method "none" (no token); a token is
  required only for push/private-repo scenarios.

Fixes #479
Fixes #483
read_file()'s R branches evaluated a bare paste(readLines(...)) expression,
which the R REPL auto-prints as a quoted, backslash-escaped character vector.
That appended a stray '"' to the terminal_run done-marker line (...:0"), so
_parse_done_marker rejected the exit code as non-numeric and terminal_run spun
until its 120s timeout even though the command had finished in ~1.5s.

Wrap the read expression in cat() (new _read_file_r_expr helper) so R emits raw
bytes, and drop the now-needless _strip_r_index on those reads. Fixes the
RStudio git-ops clone readback (surfaced end-to-end once #483 made clone run by
default) and the identical Positron R-console readback path -- the readback
half of #390. The Positron launch gate (#477) is tracked separately and needs a
Positron-enabled deployment to validate.

Refs #390
Refs #477
Copilot AI review requested due to automatic review settings July 17, 2026 03:29
@ian-flores
ian-flores marked this pull request as ready for review July 17, 2026 03:30

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 PR fixes a set of Workbench git-ops test reliability and usability issues by (1) making clone scenarios runnable out-of-the-box via a default public repo, (2) ensuring skip reasons are deterministic by gating config/read-only checks before login, and (3) fixing an R console readback bug that could prevent done-marker parsing and cause timeouts.

Changes:

  • Synthesize a default [workbench.git_test] config (public repo, auth_method="none") when the block is absent, and update skip messaging accordingly.
  • Reorder Gherkin steps so config/push gating runs before the (potentially flaky) interactive login step; add selftests guarding step order and skip text.
  • Fix R readback by wrapping readLines() output in cat() via a shared helper, preventing R auto-print quoting from corrupting the done-marker exit code.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
vip.toml.example Updates example docs to reflect optional git_test block + default anonymous clone behavior.
validation_docs/demo-issue-479-483-git-skip-gate.md Adds Showboat demo documenting validation steps and outcomes.
src/vip/config.py Adds default public clone URL constant and synthesizes default git_test config in WorkbenchConfig.from_dict.
src/vip_tests/workbench/test_git_ops.py Updates module docs and improves skip messaging for public-clone vs token-required scenarios.
src/vip_tests/workbench/test_git_ops.feature Reorders scenario steps so config/push gates run before login.
src/vip_tests/workbench/exec.py Adds _read_file_r_expr() and routes R readback through cat() to avoid quoted done-marker corruption.
selftests/test_workbench_git_ops.py Adds selftests for feature step-order guard and skip-message wording.
selftests/test_workbench_exec.py Adds regression tests for quoted vs raw done-marker parsing and _read_file_r_expr behavior.
selftests/test_config.py Updates expectations to confirm default git_test synthesis and token handling when block is absent.

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

Comment on lines 792 to +795
if ide == "vscode":
return read_file_via_vscode_editor(page, path, timeout=timeout)
# RStudio (and unknown — fall back to RStudio R path)
if lang.lower() == "r":
expr = f'paste(readLines("{path}"), collapse="\\n")'
return _strip_r_index(rstudio_eval(page, expr, timeout=timeout))
expr = f'paste(readLines("{path}"), collapse="\\n")'
return _strip_r_index(rstudio_eval(page, expr, timeout=timeout))
return rstudio_eval(page, _read_file_r_expr(path), timeout=timeout)
@posit-vip-triage

Copy link
Copy Markdown
Contributor

📸 Preview Screenshots

7 screenshots captured from the PR preview deployments.

Website Preview (pr-preview-site/pr-490)

Page URL
Home https://posit-dev.github.io/vip/pr-preview-site/pr-490/
Getting Started https://posit-dev.github.io/vip/pr-preview-site/pr-490/getting-started/
Test Inventory https://posit-dev.github.io/vip/pr-preview-site/pr-490/tests/
Feature Matrix https://posit-dev.github.io/vip/pr-preview-site/pr-490/feature-matrix/
Example Report https://posit-dev.github.io/vip/pr-preview-site/pr-490/report/

website home

website getting-started

website tests

website feature-matrix

website report


Quarto Report Preview (pr-preview/pr-490)

Page URL
Report Home https://posit-dev.github.io/vip/pr-preview/pr-490/
Detailed Results https://posit-dev.github.io/vip/pr-preview/pr-490/details.html

report home

report details


Notes: The report home full-page screenshot timed out (JS-heavy page); a viewport-only screenshot was captured instead. All other pages rendered successfully.

Warning

Firewall blocked 2 domains

The following domains were blocked by the firewall during workflow execution:

  • fonts.gstatic.com
  • mtalk.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "fonts.gstatic.com"
    - "mtalk.google.com"

See Network Configuration for more information.

Generated by Capture preview screenshots for PRs · 56.6 AIC · ⌖ 6.42 AIC · ⊞ 6.1K ·

@ian-flores
ian-flores merged commit e68d9e8 into main Jul 17, 2026
34 checks passed
@ian-flores
ian-flores deleted the workbench-git-skip-gate branch July 17, 2026 03:54
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-07-17 03:55 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants