Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .dagger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,73 @@ dagger call publish-image \
--imageTags=v0.1.0,latest
```

### `PublishToWinget(packageId, version, githubToken, installerUrls)`

Automates the submission of Harbor CLI updates to the Windows Package Manager (WinGet) repository. This function uses `wingetcreate` to update the package manifest and automatically submit a pull request to `microsoft/winget-pkgs`.

Before running the command, export your GitHub Personal Access Token (with `public_repo` scope):

```shell
export GITHUB_TOKEN=ghp_yourTokenHere
Comment on lines +99 to +100
Copy link
Collaborator

Choose a reason for hiding this comment

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

is there a possibility of doing this without PATs ??

Copy link
Author

@xenonnn4w xenonnn4w Nov 25, 2025

Choose a reason for hiding this comment

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

Hey there, thanks for the review! I did some research on using the automatic secrets.GITHUB_TOKEN instead of a PAT, but unfortunately wingetcreate requires permissions that the automatic token doesn't have.

Why PAT is Required;

The automatic GITHUB_TOKEN in GitHub Actions:

  • Only has permissions for the current repository
  • Cannot create PRs to external repositories (like microsoft/winget-pkgs)
  • Has restricted permissions by design for security

Wingetcreate needs to:

  • Fork the microsoft/winget-pkgs repository
  • Create a branch in the fork
  • Submit a PR back to microsoft/winget-pkgs

This requires a PAT with public_repo scope to access external repositories, as documented in https://github.com/microsoft/winget-create/blob/main/doc/token.md.

```

**Basic Usage** (auto-detects installer URLs from GitHub releases):

```bash
dagger call publish-to-winget \
--package-id="GoHarbor.Harbor" \
--version="0.0.11" \
--github-token=env:GITHUB_TOKEN
```

**Advanced Usage** (specify custom installer URLs):

```bash
dagger call publish-to-winget \
--package-id="GoHarbor.Harbor" \
--version="0.0.11" \
--github-token=env:GITHUB_TOKEN \
--installer-urls="https://github.com/goharbor/harbor-cli/releases/download/v0.0.11/harbor-cli_0.0.11_windows_amd64.zip" \
--installer-urls="https://github.com/goharbor/harbor-cli/releases/download/v0.0.11/harbor-cli_0.0.11_windows_arm64.zip"
```

This will:
1. Download the latest `wingetcreate` tool
2. Update the WinGet manifest with the new version and installer URLs
3. Automatically submit a PR to the `microsoft/winget-pkgs` repository

**Requirements:**
- GitHub Personal Access Token with `public_repo` scope
- Valid installer URLs (must be publicly accessible)
- Existing package in the WinGet repository
- **Windows Docker host** or Windows container support (for CI/CD, use `runs-on: windows-latest` in GitHub Actions)

**Note:** This function requires Windows containers because `wingetcreate` is a Windows-only tool. On Linux/macOS hosts, this will fail. In GitHub Actions, ensure your workflow uses a Windows runner.

### `PublishToWingetDryRun(packageId, version, installerUrls)`

Test the WinGet publishing logic without requiring Windows containers. This shows exactly what command would be executed.

```bash
dagger call publish-to-winget-dry-run \
--package-id="GoHarbor.Harbor" \
--version="0.0.11"
```

Output example:
```
📦 WinGet Publishing Dry Run
========================================
Package ID: GoHarbor.Harbor
Version: 0.0.11
Installer URLs:
https://github.com/goharbor/harbor-cli/releases/download/v0.0.11/harbor-cli_0.0.11_windows_amd64.zip
https://github.com/goharbor/harbor-cli/releases/download/v0.0.11/harbor-cli_0.0.11_windows_arm64.zip

Command that would be executed:
wingetcreate.exe update GoHarbor.Harbor --version 0.0.11 --urls "..." --submit --token $env:GITHUB_TOKEN
```

---

## ⚙️ Configuration Constants
Expand Down
92 changes: 92 additions & 0 deletions .dagger/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,3 +477,95 @@ func (m *HarborCli) Sign(ctx context.Context,
"--timeout", "1m",
}).Stdout(ctx)
}

// PublishToWingetDryRun shows what commands would be executed for WinGet publishing
// This is useful for testing and validation without requiring Windows containers
func (m *HarborCli) PublishToWingetDryRun(
ctx context.Context,
packageId string,
version string,
// +optional
// URL(s) to the installer(s) - multiple URLs can be provided for different architectures
installerUrls []string,
) string {
// Construct installer URLs from GitHub release if not provided
if len(installerUrls) == 0 {
baseUrl := fmt.Sprintf("https://github.com/goharbor/harbor-cli/releases/download/v%s", version)
installerUrls = []string{
fmt.Sprintf("%s/harbor-cli_%s_windows_amd64.zip", baseUrl, version),
fmt.Sprintf("%s/harbor-cli_%s_windows_arm64.zip", baseUrl, version),
}
}

urlsParam := strings.Join(installerUrls, " ")

output := fmt.Sprintf(` WinGet Publishing Dry Run
========================================
Package ID: %s
Version: %s
Installer URLs:
%s

Command that would be executed:
wingetcreate.exe update %s --version %s --urls "%s" --submit --token $env:GITHUB_TOKEN

Note: This is a dry run. To actually publish, use publish-to-winget on a Windows host.
`, packageId, version, strings.Join(installerUrls, "\n"), packageId, version, urlsParam)

return output
}

// PublishToWinget updates the WinGet package manifest and submits a PR to microsoft/winget-pkgs
// This automates the process of keeping the Harbor CLI package up-to-date in the Windows Package Manager
// NOTE: Requires Windows containers - must run on Windows host or GitHub Actions windows-latest runner
func (m *HarborCli) PublishToWinget(
ctx context.Context,
packageId string,
version string,
githubToken *dagger.Secret,
// +optional
// URL(s) to the installer(s) - multiple URLs can be provided for different architectures
// If not provided, will attempt to construct from GitHub release
installerUrls []string,
) (string, error) {
fmt.Println(" Publishing to WinGet...")

// Construct installer URLs from GitHub release if not provided
if len(installerUrls) == 0 {
// Default to common Windows installer patterns
baseUrl := fmt.Sprintf("https://github.com/goharbor/harbor-cli/releases/download/v%s", version)
installerUrls = []string{
fmt.Sprintf("%s/harbor-cli_%s_windows_amd64.zip", baseUrl, version),
fmt.Sprintf("%s/harbor-cli_%s_windows_arm64.zip", baseUrl, version),
}
fmt.Printf("Using default installer URLs: %v\n", installerUrls)
}

urlsParam := strings.Join(installerUrls, " ")

// Use a Windows container with PowerShell to run wingetcreate
container := dag.Container().
From("mcr.microsoft.com/windows/servercore:ltsc2022").
WithSecretVariable("GITHUB_TOKEN", githubToken).
WithExec([]string{
"powershell", "-Command",
"Invoke-WebRequest -Uri https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe",
}).
// Run wingetcreate update with submit flag
WithExec([]string{
"powershell", "-Command",
fmt.Sprintf(
"./wingetcreate.exe update %s --version %s --urls \"%s\" --submit --token $env:GITHUB_TOKEN",
packageId, version, urlsParam,
),
})

output, err := container.Stdout(ctx)
if err != nil {
stderr, _ := container.Stderr(ctx)
return "", fmt.Errorf("failed to publish to WinGet: %v\nStderr: %s", err, stderr)
}

fmt.Printf("WinGet publication completed:\n%s\n", output)
return output, nil
}
28 changes: 28 additions & 0 deletions .github/workflows/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,31 @@ jobs:
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
REGISTRY_ADDRESS: ${{ vars.REGISTRY_ADDRESS }}
REGISTRY_USERNAME: ${{ vars.REGISTRY_USERNAME }}

publish-to-winget:
needs:
- publish-release
runs-on: windows-latest
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/tags/'))
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Extract version from tag
id: extract_version
shell: bash
run: |
VERSION=${{ github.ref_name }}
VERSION_WITHOUT_V=${VERSION#v}
echo "version=${VERSION_WITHOUT_V}" >> $GITHUB_OUTPUT

- name: Publish to WinGet
uses: dagger/dagger-for-github@v7
env:
GITHUB_TOKEN: ${{ secrets.WINGET_GITHUB_TOKEN }}
with:
version: "latest"
verb: call
args: "publish-to-winget --package-id=GoHarbor.Harbor --version=${{ steps.extract_version.outputs.version }} --github-token=env:GITHUB_TOKEN"