Skip to content

[VPEX][5b] Add local-env six-phase pipeline orchestrator#5851

Open
rugpanov wants to merge 2 commits into
dbconnect/05a-pkgmanagerfrom
dbconnect/05b-pipeline
Open

[VPEX][5b] Add local-env six-phase pipeline orchestrator#5851
rugpanov wants to merge 2 commits into
dbconnect/05a-pkgmanagerfrom
dbconnect/05b-pipeline

Conversation

@rugpanov

@rugpanov rugpanov commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Why

  • The earlier layers (resolve / fetch / merge) need an orchestrator that runs them in order, reports structured per-phase status, and provisions the environment — without mutating disk in --check dry-run mode.
  • This is the core half of the split described in [VPEX][5a] Add local-env package-manager interface, detection, and preflight #5850: the six-phase pipeline itself, stacked on the package-manager/detection layer.

What

  • pipeline.go — the six-phase orchestrator (preflight → resolve → fetch → merge → provision → validate). It records per-phase status into Result and returns typed PipelineErrors carrying FailurePhase and DiskMutated. Under --check it computes and reports the plan (with a diff) and performs no writes, skipping the writability probe and package-manager availability (neither is needed to compute the plan).
  • Backups are created only when no .bak exists and the original is a genuine os.ErrNotExist-free read; an unreadable or unstattable existing file fails loudly rather than being misread as greenfield and overwritten.

Testing strategy

  • End-to-end unit tests of the phase machine against a fake PackageManager + stub compute + httptest server: --check mutates nothing (even on a read-only dir / without a package manager), greenfield vs. existing, backup safety on unreadable/unstattable files, constraints-only omission, and phase/error attribution (pipeline_test.go).
  • Gates: go build, go test, golangci-lint (0 issues), deadcode, gofmt — all green.

About this stack

Review bottom-up. This PR targets #5850 as its base, so its diff shows only the pipeline layer.

This PR and #5850 together supersede #5828. The libs/localenv tree at the tip of this branch is byte-identical to the tip of #5828's branch, apart from the pyprojectFile const relocation and a small deadcode-guard test added in #5850.

This pull request and its description were written by Isaac.

@rugpanov rugpanov temporarily deployed to test-trigger-is July 8, 2026 09:58 — with GitHub Actions Inactive
@rugpanov rugpanov temporarily deployed to test-trigger-is July 8, 2026 09:58 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Waiting for approval

Could not determine reviewers from git history.
Round-robin suggestion: @renaudhartert-db

Eligible reviewers: @andrewnester, @anton-107, @denik, @janniklasrose, @pietern, @renaudhartert-db, @shreyas-goenka, @simonfaltum

Suggestions based on git history. See OWNERS for ownership rules.

@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 6f31579

Run: 29024040434

Env 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
💚​ aws linux 4 4 230 1069 4:28
💚​ aws windows 4 4 232 1067 3:49
💚​ aws-ucws linux 4 4 314 987 4:51
💚​ aws-ucws windows 4 4 316 985 4:08
💚​ azure linux 4 4 230 1068 4:07
💚​ azure windows 4 4 232 1066 4:06
💚​ azure-ucws linux 4 4 316 984 5:17
💚​ azure-ucws windows 4 4 318 982 5:36
💚​ gcp linux 4 4 229 1070 3:51
💚​ gcp windows 4 4 231 1068 3:50
8 interesting tests: 4 RECOVERED, 4 SKIP
Test Name aws linux aws windows aws-ucws linux aws-ucws windows azure linux azure windows azure-ucws linux azure-ucws windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
💚​ TestFetchRepositoryInfoAPI_FromRepo 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestFetchRepositoryInfoAPI_FromRepo/root 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestFetchRepositoryInfoAPI_FromRepo/subdir 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
Top 5 slowest tests (at least 2 minutes):
duration env testname
4:23 azure-ucws windows TestAccept
3:07 azure windows TestAccept
3:04 aws-ucws windows TestAccept
3:00 gcp windows TestAccept
2:53 aws windows TestAccept

The orchestrator runs the layers in order (preflight → resolve → fetch →
merge → provision → validate), records per-phase status into Result, and
returns typed PipelineErrors carrying FailurePhase and DiskMutated. Under
--check it computes and reports the plan (with a diff) and performs no
writes — skipping the writability probe and package-manager availability,
neither of which is needed to compute the plan.

Backups are created only when no .bak exists and the original is a genuine
os.ErrNotExist-free read; an unreadable or unstattable existing file fails
loudly rather than being misread as greenfield and overwritten.

Stacked on the package-manager/detection layer; provisions through the
PackageManager interface against a fake in tests.

Co-authored-by: Isaac
@rugpanov rugpanov force-pushed the dbconnect/05a-pkgmanager branch from cb166cb to f24c320 Compare July 8, 2026 14:44
@rugpanov rugpanov force-pushed the dbconnect/05b-pipeline branch from af9d41b to c2e8b69 Compare July 8, 2026 14:44
@rugpanov rugpanov temporarily deployed to test-trigger-is July 8, 2026 14:44 — with GitHub Actions Inactive
@rugpanov rugpanov temporarily deployed to test-trigger-is July 8, 2026 14:44 — with GitHub Actions Inactive
Comment thread libs/localenv/pipeline.go
}
if err := os.WriteFile(dst, data, 0o644); err != nil {
return fmt.Errorf("write %s: %w", dst, err)
}

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.

copyFile creates the .bak at a hardcoded 0o644. If the user's pyproject.toml is locked down (e.g. 0o600 because it carries a private index URL or token), the backup silently becomes world-readable.

The main-file write at applyMerge is fine only incidentally — os.WriteFile ignores the mode arg when the file already exists, so the existing pyproject keeps its perms. But the backup is freshly created here, so 0o644 actually applies.

Suggest either preserving the source mode (os.Stat(src) → pass info.Mode()) or, if 0o644 is deliberate, a one-line comment saying so (the repo's comment-the-why rule).


This comment was generated with GitHub MCP.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — fixed in 6f31579. copyFile now stats the source and creates the backup with info.Mode().Perm(), so a 0o600 pyproject (private index URL/token) is no longer widened to world-readable. Added TestCopyFilePreservesMode to lock it in.

Comment thread libs/localenv/pipeline.go
// Phase: preflight — manager detection, writability, package-manager availability.
// P0 supports only uv; any other detected manager is a clean, non-blaming exit.
if m := detectManager(p.ProjectDir); m != managerUv {
return p.fail(PhasePreflight, false, NewError(ErrManagerUnsupported, nil, "%s", managerGuidance(m)))

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.

Two preflight exits look untested at the pipeline level:

  • this unsupported-manager path → E_MANAGER_UNSUPPORTED, and
  • the EnsureAvailable failure below → E_UV_MISSING (line ~113).

detect_test.go covers detectManager in isolation, but not the pipeline's wiring of it into a clean preflight failure (correct phase attribution, DiskMutated=false, later phases still pending). The noProvisionPM fixture already exists — a test that drops an environment.yml and asserts the E_MANAGER_UNSUPPORTED / PhasePreflight result would close the gap cheaply.


This comment was generated with GitHub MCP.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 6f31579. Added TestPipelineManagerUnsupportedFailsAtPreflight (drops an environment.ymlE_MANAGER_UNSUPPORTED) and TestPipelineUvMissingFailsAtPreflight (new uvMissingPM fixture → E_UV_MISSING). Both assert phase attribution (PhasePreflight), DiskMutated=false, and that all later phases stay pending, via a shared assertPreflightFailure helper.

Address review on the pipeline PR:

- copyFile created the .bak at a hardcoded 0o644, silently widening a
  locked-down pyproject.toml (e.g. 0o600 carrying a private index URL) to
  world-readable. Stat the source and create the backup with its permission
  bits; os.WriteFile applies the mode only when creating the file, which is
  always the case for the fresh .bak.
- Add pipeline-level tests for the two preflight exits that were only
  covered in isolation before: E_MANAGER_UNSUPPORTED (a non-uv project) and
  E_UV_MISSING (package manager unavailable), asserting phase attribution,
  DiskMutated=false, and that later phases stay pending. Add a direct
  copyFile mode-preservation test.

Co-authored-by: Isaac
@rugpanov rugpanov temporarily deployed to test-trigger-is July 9, 2026 14:06 — with GitHub Actions Inactive
@rugpanov rugpanov temporarily deployed to test-trigger-is July 9, 2026 14:06 — with GitHub Actions Inactive
@rugpanov rugpanov requested a review from anton-107 July 10, 2026 11:26
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