Skip to content
44 changes: 15 additions & 29 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -110,53 +110,39 @@ jobs:
GOFLAGS: '-buildvcs=false'

release:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
runs-on: windows-latest
needs: [build-and-test, sast-scan]
if: startsWith(github.ref, 'refs/tags/')

steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Install GPG
run: sudo apt-get update && sudo apt-get install -y gnupg

- name: Install Aqua
uses: aquaproj/aqua-installer@5e54e5cee8a95ee2ce7c04cb993da6dfad13e59c # v3.1.2
with:
aqua_version: v2.51.1

- name: Install tools
run: aqua install

- name: Cache Go Modules
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-

- name: Install Dependencies
run: go install ./...

- name: Import GPG key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }}

- name: Check if prerelease
id: prerelease
run: |
if [[ "${{ github.ref }}" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+- ]]; then
echo "prerelease=true" >> $GITHUB_OUTPUT
else
echo "prerelease=false" >> $GITHUB_OUTPUT
fi
shell: bash

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
with:
version: "~> v2"
args: release --clean
args: release --clean ${{ steps.prerelease.outputs.prerelease == 'true' && '--skip=chocolatey,homebrew' || '' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GPG_FINGERPRINT: ${{ env.GPG_FINGERPRINT }}
HOMEBREW_CLI_WRITE_PAT: ${{ secrets.HOMEBREW_CLI_WRITE_PAT }}
GITHUB_SHA: ${{ github.sha }}
SKIP_HOMEBREW: ${{ steps.prerelease.outputs.prerelease == 'true' }}
HOMEBREW_CLI_WRITE_PAT: ${{ secrets.HOMEBREW_CLI_WRITE_PAT }}
CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }}
57 changes: 53 additions & 4 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ builds:
# Archive configuration
archives:
- id: windsor
formats: ["tar.gz"]
formats:
- tar.gz
format_overrides:
- goos: windows
format: zip

changelog:
sort: asc
Expand Down Expand Up @@ -61,14 +65,59 @@ signs:

brews:
- name: windsor
directory: Formula
skip_upload: "{{ eq .Env.SKIP_HOMEBREW \"true\" }}"
repository:
owner: windsorcli
name: homebrew-cli
branch: main
token: "{{ .Env.HOMEBREW_CLI_WRITE_PAT }}"
homepage: "https://windsorcli.github.io"
commit_author:
name: goreleaserbot
email: bot@goreleaser.com
homepage: "https://windsorcli.github.io"
description: "The Windsor Command Line Interface"
license: "MPL-2.0"
skip_upload: auto
download_strategy: GithubPrivateRepositoryReleaseDownloadStrategy
custom_require: "lib/custom_download_strategy"
install: |
bin.install "windsor"
# Install shell completions
output = Utils.safe_popen_read("#{bin}/windsor", "completion", "bash")
(bash_completion/"windsor").write output
output = Utils.safe_popen_read("#{bin}/windsor", "completion", "zsh")
(zsh_completion/"_windsor").write output
output = Utils.safe_popen_read("#{bin}/windsor", "completion", "fish")
(fish_completion/"windsor.fish").write output

chocolateys:
- name: windsor
ids:
- windsor
package_source_url: https://github.com/windsorcli/cli
owners: Windsor CLI
title: Windsor CLI
authors: Windsor CLI Team
project_url: https://windsorcli.github.io
icon_url: https://windsorcli.github.io/icon.png
copyright: "2025 Windsor CLI Team"
license_url: https://github.com/windsorcli/cli/blob/main/LICENSE
require_license_acceptance: false
project_source_url: https://github.com/windsorcli/cli
docs_url: https://windsorcli.github.io
bug_tracker_url: https://github.com/windsorcli/cli/issues
tags: "cli windows command-line"
summary: "The Windsor Command Line Interface"
description: |
The Windsor CLI assists your cloud native development workflow.
This package provides the Windows installer for Windsor CLI.

After installation, add the following line to your PowerShell profile to enable shell integration:
```powershell
Invoke-Expression (& windsor hook powershell)
```

Your PowerShell profile is located at: $PROFILE
release_notes: "https://github.com/windsorcli/cli/releases/tag/v{{ .Version }}"
api_key: "{{ .Env.CHOCOLATEY_API_KEY }}"
source_repo: "https://push.chocolatey.org/"
url_template: "https://github.com/windsorcli/cli/releases/download/v{{ .Version }}/windsor_{{ .Version }}_windows_amd64.zip"
74 changes: 64 additions & 10 deletions pkg/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2238,16 +2238,19 @@ func TestBaseController_createConfigComponent(t *testing.T) {
// Clear any existing config handler
mocks.Injector.Register("configHandler", nil)

// Set environment variable to force config load
oldEnv := os.Getenv("WINDSORCONFIG")
os.Setenv("WINDSORCONFIG", "test.yaml")
defer func() {
if oldEnv != "" {
os.Setenv("WINDSORCONFIG", oldEnv)
} else {
os.Unsetenv("WINDSORCONFIG")
}
}()
// Create temporary project directory
projectRoot := t.TempDir()
mockShell := shell.NewMockShell()
mockShell.GetProjectRootFunc = func() (string, error) {
return projectRoot, nil
}
mocks.Injector.Register("shell", mockShell)

// Create config file
configPath := filepath.Join(projectRoot, "windsor.yaml")
if err := os.WriteFile(configPath, []byte("test"), 0644); err != nil {
t.Fatalf("Failed to create test config file: %v", err)
}

// When creating the config component
err := controller.createConfigComponent(Requirements{})
Expand Down Expand Up @@ -2285,6 +2288,57 @@ func TestBaseController_createConfigComponent(t *testing.T) {
t.Errorf("Expected no error, got %v", err)
}
})

t.Run("CreatesConfigComponent", func(t *testing.T) {
// Given a controller with a config handler
controller, mocks := setup(t)
mockConfigHandler := config.NewMockConfigHandler()
controller.constructors.NewConfigHandler = func(di.Injector) config.ConfigHandler {
return mockConfigHandler
}

// Clear any existing config handler
mocks.Injector.Register("configHandler", nil)

// When creating the config component
err := controller.createConfigComponent(Requirements{})

// Then no error should be returned
if err != nil {
t.Errorf("Expected no error, got: %v", err)
}

// And the config handler should be registered
if mocks.Injector.Resolve("configHandler") != mockConfigHandler {
t.Error("Expected config handler to be registered")
}
})

t.Run("HandlesConfigHandlerError", func(t *testing.T) {
// Given a controller with a failing config handler
controller, mocks := setup(t)
mockConfigHandler := config.NewMockConfigHandler()
mockConfigHandler.InitializeFunc = func() error {
return fmt.Errorf("config handler error")
}
controller.constructors.NewConfigHandler = func(di.Injector) config.ConfigHandler {
return mockConfigHandler
}

// Clear any existing config handler
mocks.Injector.Register("configHandler", nil)

// When creating the config component
err := controller.createConfigComponent(Requirements{})

// Then an error should be returned
if err == nil {
t.Error("Expected error, got nil")
}
if !strings.Contains(err.Error(), "config handler error") {
t.Errorf("Expected error about config handler, got: %v", err)
}
})
}

func TestBaseController_createSecretsComponents(t *testing.T) {
Expand Down
Loading