Skip to content

Conversation

@grouville
Copy link
Member

@grouville grouville commented Jul 24, 2025

This is a follow-up of #242, which added Windows compilation but no installation method. This PR completes Windows support by adding:

  • PowerShell installer with checksum verification
  • GoReleaser configuration for Windows .zip archives
  • Chocolatey packaging setup
  • Documentation updates

Testing (outdated, will update below)

Validated using fork releases:

# Test with fork release
(Invoke-WebRequest "https://raw.githubusercontent.com/grouville/container-use/windows-deployment/install.ps1").Content -replace
'dagger/container-use', 'grouville/container-use' | Out-File test-install.ps1
.\test-install.ps1 -Version v0.3.5-test

Next steps

  1. Next release includes Windows binaries
  2. PowerShell installer immediately available
  3. Chocolatey pending API key configuration

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.

Fix: give each test process its own cache/tmp sandbox, and then give each MCP server its own sub‑sandbox on top of that. We also bound stdio RPCs with a 120s context so they fail fast instead of hanging.

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.

Fix: move locks under the container‑use config dir (/locks/...). That restores true cross‑process exclusion regardless of how TMP is isolated.

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.

Fix: when we create/ensure the fork, set user.name/email in the bare repo. Copy from the source repo if present, else fall back to container-use / container-use@local. Scope is repo‑local, no global config changes.

4. Path length and odd test names.

Deep temp paths and slashes in subtest names triggered Windows path errors and Git long‑path problems.

Fix: create temporary roots under a short base (e.g. C:\Temp), sanitize test names, and in cmd/ tests set a temporary global config with core.longpaths=true. We also relaxed a single performance threshold from 2s → 5s on Windows to account for runner slowness.

What else is in here

Two small pieces to finish the Windows story:

  • Installer + docs: a hardened PowerShell installer (checksums, PS 5.1/7 compatibility, safe PATH updates) and a concise one‑liner in the docs. We also accept “unknown” as a runtime version in a test to match runner reality.
  • Release ergonomics: ship Windows artifacts as .zip; wire optional Chocolatey publishing.

Logical order of commits

I kept commits logical and readable so you can follow the story:

  • CI wiring first, then test isolation (process → server → paths), then repository fixes (locks, fork identity), and finally installer/release bits.

It might feel over‑isolated, but it’s that combination that made the Windows job green...

@grouville
Copy link
Member Author

cc @zhichli

@grouville
Copy link
Member Author

cc @aluzzardi, the windows test will keep failing until you add the secrets I asked you via DM 🙏

DaggerAssistant

This comment was marked as outdated.

DaggerAssistant

This comment was marked as outdated.

DaggerAssistant

This comment was marked as outdated.

DaggerAssistant

This comment was marked as outdated.

DaggerAssistant

This comment was marked as outdated.

DaggerAssistant

This comment was marked as outdated.

DaggerAssistant

This comment was marked as outdated.

DaggerAssistant

This comment was marked as outdated.

DaggerAssistant

This comment was marked as outdated.

DaggerAssistant

This comment was marked as outdated.

DaggerAssistant

This comment was marked as outdated.

DaggerAssistant

This comment was marked as outdated.

@jpadams jpadams self-requested a review August 1, 2025 02:46
Copy link
Contributor

@jpadams jpadams left a 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

Copy link

@greptile-apps greptile-apps bot left a 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.go for the test behavior change and install.ps1 for 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
Loading

8 files reviewed, 2 comments

Edit Code Review Bot Settings | Greptile

Copy link
Contributor

@DaggerAssistant DaggerAssistant left a 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

⚠️ The PR is well-structured and adds solid Windows support, but there are several security concerns and technical issues that should be addressed before merging. The PowerShell installer has some security vulnerabilities, and there are missing error handling patterns throughout. The Windows-specific path handling is clever but could be more robust.

@grouville grouville force-pushed the windows-deployment branch 2 times, most recently from 5abccfb to b103c46 Compare August 14, 2025 21:53
@grouville grouville force-pushed the windows-deployment branch 2 times, most recently from d972e58 to 98982cc Compare August 15, 2025 23:47
@grouville grouville force-pushed the windows-deployment branch 4 times, most recently from 1a38f8e to 9c87568 Compare August 18, 2025 22:10
…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>
@grouville grouville requested a review from cwlbraa August 19, 2025 00:52
Signed-off-by: Guillaume de Rouville <guillaume@dagger.io>
Copy link
Contributor

@cwlbraa cwlbraa left a 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:

  1. answer to install script question (and follow-up actions)
  2. 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.

Comment on lines +24 to +27
binaryName := "container-use"
if runtime.GOOS == "windows" {
binaryName = "container-use.exe"
}
Copy link
Contributor

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)
Copy link
Contributor

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) {
Copy link
Contributor

@cwlbraa cwlbraa Aug 19, 2025

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?)

Comment on lines +87 to +105
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
}
Copy link
Contributor

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))
Copy link
Contributor

@cwlbraa cwlbraa Aug 19, 2025

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" {
Copy link
Contributor

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

Comment on lines +475 to +477
// 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 {
Copy link
Contributor

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:
Copy link
Contributor

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
Copy link
Contributor

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.

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.

5 participants