-
Notifications
You must be signed in to change notification settings - Fork 167
Add Windows deployment support #252
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
cc @zhichli |
|
cc @aluzzardi, the windows test will keep failing until you add the secrets I asked you via DM 🙏 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Powershell install worked for me with version v0.3.5-test
PATH setting instructions only worked for admin, but then normal user had install directory in PATH
PS C:\Users\Home> [Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\Users\Home\container-use', [EnvironmentVariableTarget]::User)
container-use UI didn't render well in admin terminal of Powershell 7
will love to see winget support too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greptile Summary
This PR adds comprehensive Windows deployment support to the container-use tool, completing the work started in PR #242 which only added Windows compilation. The changes introduce multiple Windows-specific installation methods and build configurations to provide Windows users with the same functionality available on Linux/macOS.
The key additions include:
PowerShell Installer (install.ps1): A secure installation script that mirrors the existing bash installer but uses PowerShell-specific approaches. It includes SHA256 checksum verification, parameter validation to prevent injection attacks, dependency checking for Docker and Git, and safe PATH handling with user profile directory installation.
Build Configuration Updates: The .goreleaser.yaml file now includes Windows-specific packaging with zip format override (while other platforms retain tar.gz) and complete Chocolatey package configuration for automated package publishing to the Windows package manager.
Testing Infrastructure: Windows-specific CI workflow (.github/workflows/test-windows.yml) that uses Chocolatey for Dagger installation and handles Windows-specific PATH management issues in GitHub Actions. The workflow uses Dagger Cloud to avoid WSL virtualization issues.
Integration Test Fixes: The test helpers now include Windows MAX_PATH (260 character) workarounds by using C:\Temp as a shorter base path and truncating test names to avoid filesystem limitations.
Documentation: Updated quickstart guide with dedicated Windows installation instructions, including both PowerShell script and Chocolatey package manager options, with clear platform-specific guidance.
Release Pipeline: The Dagger module and GitHub Actions workflow now support optional Chocolatey API key configuration for automated Windows package publishing during releases.
These changes integrate seamlessly with the existing codebase architecture, extending the cross-platform support without breaking existing Linux/macOS functionality. The Windows-specific implementations follow the same security and usability patterns as the existing tools while adapting to Windows-specific requirements and conventions.
Important Files Changed
Click to expand changed files
| Filename | Score | Overview |
|---|---|---|
.dagger/main.go |
5/5 | Added optional Chocolatey API key parameter to Dagger release function |
.github/workflows/release.yml |
5/5 | Integrated Chocolatey API key into GitHub Actions release workflow |
environment/integration/integration_test.go |
5/5 | Refactored to use Windows-compatible temp directory helper function |
environment/integration/helpers.go |
4/5 | Added Windows MAX_PATH workaround and changed Dagger check from skip to fail |
docs/quickstart.mdx |
5/5 | Added comprehensive Windows installation documentation with PowerShell and Chocolatey options |
.goreleaser.yaml |
4/5 | Added Windows zip format override and complete Chocolatey package configuration |
install.ps1 |
4/5 | New PowerShell installer with security features and Windows-specific handling |
.github/workflows/test-windows.yml |
4/5 | New Windows CI workflow with Chocolatey Dagger installation and PATH handling |
Confidence score: 4/5
- This PR is generally safe to merge with some minor considerations around CI behavior changes and error handling
- Score reflects comprehensive Windows support implementation with good security practices, but some potential issues with dependency checking and test behavior changes
- Pay close attention to
environment/integration/helpers.gofor the test behavior change andinstall.ps1for PowerShell error handling patterns
Sequence Diagram
sequenceDiagram
participant User
participant PowerShell as PowerShell Script
participant GitHub as GitHub API
participant Docker
participant Git
participant ContainerUse as container-use.exe
Note over User, ContainerUse: Windows Installation Flow
User->>PowerShell: Execute install.ps1
PowerShell->>Docker: Check docker version
Docker-->>PowerShell: Version info
PowerShell->>Git: Check git version
Git-->>PowerShell: Version info
PowerShell->>GitHub: Get latest release info
GitHub-->>PowerShell: Release metadata
PowerShell->>GitHub: Download Windows .zip archive
GitHub-->>PowerShell: Binary archive
PowerShell->>GitHub: Download checksums.txt
GitHub-->>PowerShell: Checksum file
PowerShell->>PowerShell: Verify SHA256 checksum
PowerShell->>PowerShell: Extract archive to temp
PowerShell->>PowerShell: Copy to install directory
PowerShell->>PowerShell: Create cu.exe alias
alt AddToPath flag set
PowerShell->>PowerShell: Update user PATH environment
end
PowerShell->>ContainerUse: Verify installation
ContainerUse-->>PowerShell: Version output
PowerShell-->>User: Installation complete
Note over User, ContainerUse: Release Process Flow
User->>GitHub: Push version tag
GitHub->>GitHub: Trigger release workflow
GitHub->>Dagger: Call release function
Dagger->>GoReleaser: Build multi-platform binaries
GoReleaser->>GoReleaser: Create .zip for Windows, .tar.gz for others
GoReleaser->>GitHub: Upload release assets
alt Chocolatey API key provided
GoReleaser->>Chocolatey: Publish Windows package
end
GoReleaser-->>GitHub: Release created
GitHub-->>User: Release notification
Note over User, ContainerUse: Windows Testing Flow
User->>GitHub: Push to main/PR
GitHub->>GitHub: Run test-windows.yml
GitHub->>Chocolatey: Install Dagger via choco
GitHub->>Git: Configure test user
GitHub->>Go: Download dependencies
GitHub->>Go: Run test suite
GitHub->>Go: Build Windows executable
GitHub->>ContainerUse: Run smoke tests
ContainerUse-->>GitHub: Test results
GitHub-->>User: CI results
8 files reviewed, 2 comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 Code Review
Summary
This PR adds comprehensive Windows deployment support including PowerShell installer, GoReleaser Windows configuration, Chocolatey packaging, CI pipeline, and integration test fixes for Windows path limitations.
Assessment
5abccfb to
b103c46
Compare
d972e58 to
98982cc
Compare
1a38f8e to
9c87568
Compare
…repo branches/dispatch Follow‑up to the Windows support work. We can’t rely on nested virtualization on GitHub’s Windows runners, so we exercise everything that happens on the host (CLI, repo/lock/config paths, stdio plumbing) and send container execution to PARC Runner. What this sets up: - Runs `go test -v -race ./...` on `windows-latest`. - Installs Dagger (Chocolatey) and verifies `dagger version` for tests that require it. - Builds `container-use.exe` and smoke‑tests `version`, `--help`, and `completion powershell`. Safety: the workflow runs only on branches in this repository and via `workflow_dispatch`. Forks do not run it. Signed-off-by: Guillaume de Rouville <guillaume@dagger.io>
…ths (Windows); fail fast on missing Dagger Windows exposed three issues: shared caches caused rename/lock contention, deep/unsanitized temp paths tripped path limits, and "Dagger unavailable" was silently skipped. This commit makes the test process self‑contained and honest. Why: - Shared global cache/tmp across test processes produced Windows rename/lock stalls (esp. during Dagger CLI downloads). - Long paths + slashes from subtest names exceeded Windows limits. - Skipping when Dagger is missing hid real CI problems. How: - `TestMain`: per‑process `XDG_CACHE_HOME` and `TMP*` sandbox. - `createTestTempDir`: short roots (e.g., `C:\Temp`), sanitized test names, and shorter prefixes. - Relax write‑perf threshold on Windows only (environmental difference). - Treat Dagger availability as a hard precondition (fail fast instead of skip). Signed-off-by: Guillaume de Rouville <guillaume@dagger.io>
…lize (120s); fix Windows build/run details Stdio tests would hang up to 10 minutes on Windows during Initialize. Root cause: multiple servers sharing a cache/tmp plus Windows file locks during Dagger CLI downloads. Fixes: - Give every MCP server its own cache/tmp sandbox (on top of per‑process isolation). - Bound stdio RPCs with a 120s context to fail fast instead of hanging. - Clean up sandboxes via `t.Cleanup`. - Build helper emits `container-use.exe` on Windows and resolves absolute paths. Result: no hangs, no shared‑cache contention, and deterministic teardown. Signed-off-by: Guillaume de Rouville <guillaume@dagger.io>
…ross-process exclusion After isolating `TMP` per process, our lock files in `os.TempDir()` no longer coordinated across processes. Each process had its own lock root, so git remote edits raced. Change: - Put locks under the container‑use config dir: * Prefer `CONTAINER_USE_CONFIG_DIR`, else the OS default. * Path: <config>/locks/container-use-<repoHash>-<lockType>.lock - Create the dir (0755) and log failures. This restores true cross‑process exclusion without introducing global machine state. Signed-off-by: Guillaume de Rouville <guillaume@dagger.io>
…er initial + worktree commits Windows runners often lack a global git identity. Our tests set identity in the source repo, but commits happen in the fork (bare) repo/worktrees, so worktree commits failed with “Author identity unknown”. Approach: - During `ensureFork` (under `LockTypeForkRepo`), ensure the fork repo has `user.name` and `user.email`. Copy from source if present; otherwise set a local fallback (`container-use` / `container-use@local`). Scope: - Repo‑local only (no global config). Covers the initial empty commit and all subsequent worktree commits. Tests pin both behaviors (copy vs. fallback). Signed-off-by: Guillaume de Rouville <guillaume@dagger.io>
…eds through pipeline Windows users expect .zip artifacts and often install via Chocolatey. Changes: - GoReleaser: publish Windows artifacts as `.zip`. - Chocolatey: add package metadata and enable publish via `CHOCOLATEY_API_KEY`. - Dagger pipeline + release workflow: forward GitHub + Chocolatey creds. - Fix recipe to reference the archive ID (not the build ID). This aligns distribution with Windows norms and makes the package pipeline turnkey once credentials are present. Signed-off-by: Guillaume de Rouville <guillaume@dagger.io>
…cs; relax runtime regex for CI We ship a first‑party Windows installer that’s safe on PS 5.1 and PS 7+ and robust on GitHub runners. Installer: - Validates parameters; forces TLS 1.2; uses `UseBasicParsing` only on PS < 6. - Verifies archive SHA256 against `checksums.txt`. - Installs to a configurable dir, creates `cu.exe`, and updates PATH (User) without duplicates. - Supports `-Repo` for fork testing. Docs: - Replace the multi‑step PowerShell instructions with a one‑liner that works on PS 5.1 and PS 7+. Tests: - Allow “unknown” container runtime version in CI where the runner doesn’t report a version string. Signed-off-by: Guillaume de Rouville <guillaume@dagger.io>
9c87568 to
f9ceb52
Compare
Signed-off-by: Guillaume de Rouville <guillaume@dagger.io>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 things semi blocking:
- answer to install script question (and follow-up actions)
- answer to stdio_test question... did we make that test less useful in order to turn it green? or am i just misreading the intent of the code?
I'd like the build directives in a follow-up PR unless you've got strong arguments as to why we should use runtime conditional logic.
| binaryName := "container-use" | ||
| if runtime.GOOS == "windows" { | ||
| binaryName = "container-use.exe" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style nit, but kinda a big one: use build directives instead of conditional logic -- maybe fix in a follow-up?
|
|
||
| // Make Git allow long paths without touching system/global config | ||
| gitcfg := filepath.Join(root, "gitconfig") | ||
| _ = os.WriteFile(gitcfg, []byte("[core]\nlongpaths = true\n"), 0o644) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL, how long are these paths that they have a special flag? lmao
| "testing" | ||
| ) | ||
|
|
||
| func TestMain(m *testing.M) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the pr description explained why all this faff was necessary on windows, but do we really need to fuck with it on unixes too?
(implicitly, couldn't this be done with build directives?)
| func serverSandboxEnv(baseEnv []string) ([]string, string) { | ||
| root, err := os.MkdirTemp("", "cu-stdio-srv-") | ||
| if err != nil { | ||
| panic(err) | ||
| } | ||
| cache := filepath.Join(root, "cache") | ||
| tmp := filepath.Join(root, "tmp") | ||
| _ = os.MkdirAll(cache, 0o755) | ||
| _ = os.MkdirAll(tmp, 0o755) | ||
|
|
||
| env := append([]string{}, baseEnv...) | ||
| env = append(env, "XDG_CACHE_HOME="+cache) | ||
| if runtime.GOOS == "windows" { | ||
| env = append(env, "TEMP="+tmp, "TMP="+tmp) | ||
| } else { | ||
| env = append(env, "TMPDIR="+tmp) | ||
| } | ||
| return env, root | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how is this different than the logic in TestMain? Are we providing additional per-server isolation?
the intent of these stdio tests in particular is to surface failures that occur when multiple server processes share a single source repo AND fork. If I'm understanding this helper correctly, this change unshares their forks, and therefore this test will no longer repro the data races it's designed to repro?
or is CONTAINER_USE_CONFIG_DIR the only var that matters here?
| setupGitRepo(t, sharedRepoDir) | ||
|
|
||
| sharedConfigDir, err := os.MkdirTemp("", "cu-e2e-shared-config-*") | ||
| sharedConfigDir, err := os.MkdirTemp("", fmt.Sprintf("cu-e2e-%s-config-*", testName)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this makes me think you were hitting real bugs and fixed them by making the test less aggro... it's "fine" to make the test less aggro, but what were the bugs?
| testName := t.Name() | ||
| testName = strings.ReplaceAll(testName, "/", "_") | ||
|
|
||
| if runtime.GOOS == "windows" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit again: prefer build directives. it's quite hard to see what this fn does on either OS-group
| // fork worktrees may run on machines without a global identity (Windows CI). | ||
| // Setting it up at the bare repo ensures consistent authorship for all worktrees | ||
| func (r *Repository) ensureForkIdentity(ctx context.Context) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
related: #256
| --fish <($out/bin/cu completion fish) \ | ||
| --zsh <($out/bin/cu completion zsh) | ||
| # chocolateys: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not working?
| @@ -0,0 +1,307 @@ | |||
| #Requires -Version 5.0 | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we really want a windows install script? can we not rely exclusively on pkg managers?
I don't know powershell well enough to review or maintain this, and i don't know if windows users actually install shit using this method -- in my head, it's more of a linux thing to deal with the fact that there are so many different package ecosystems.
This is a follow-up of #242, which added Windows compilation but no installation method. This PR completes Windows support by adding:
Testing (outdated, will update below)
Validated using fork releases:
Next steps
UPDATE 08/18/2025
This is the follow‑up to the earlier “Windows support” work. That PR added the features; this one wires up a Windows pipeline and hardens the pieces that actually run on the host. Because nested virtualization is sketchy on GitHub’s Windows runners (and we don’t want to enable privileged Docker on a public repo), we only exercise the host‑side behavior on Windows. Container execution happens remotely on our runner (via Dagger), so we still get coverage for the CLI, repo plumbing, MCP stdio wiring, and everything else that a Windows user touches locally.
We enable the workflow only on branches in this repo (not forks) and via workflow dispatch.
Where the flakes came from (and what we changed)
While bringing the suite up on Windows, we hit a handful of issues that only show up under concurrency:
1. Stalls during MCP stdio Initialize (~10m timeouts).
Multiple stdio servers in the same test process fought over a shared cache and temp directory. On Windows, that turns into rename/lock contention when the Dagger CLI is fetched.
2. Cross‑process locking wasn’t actually cross‑process.
Once we sandboxed TMP per process, our flock files living under os.TempDir() no longer coordinated across processes.
3. "Author identity unknown" when committing from a fork worktree.
Runners often lack a global Git identity. We only set user.name/email in the source repo during tests, but worktrees commit from the bare fork and don’t inherit that.
4. Path length and odd test names.
Deep temp paths and slashes in subtest names triggered Windows path errors and Git long‑path problems.
What else is in here
Two small pieces to finish the Windows story:
Logical order of commits
I kept commits logical and readable so you can follow the story:
It might feel over‑isolated, but it’s that combination that made the Windows job green...