-
-
Notifications
You must be signed in to change notification settings - Fork 9
feat: Add CI workflow for publishing .NET tool to NuGet and update Homebrew and WinGet manifests #71
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
feat: Add CI workflow for publishing .NET tool to NuGet and update Homebrew and WinGet manifests #71
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -560,6 +560,34 @@ jobs: | |||||||||||||||||
| cache-from: type=registry,ref=ghcr.io/${{ needs.build-base.outputs.repo_name }}:${{ matrix.variant }},mode=max | ||||||||||||||||||
| cache-to: type=inline | ||||||||||||||||||
|
|
||||||||||||||||||
| # Publish .NET Tool to NuGet | ||||||||||||||||||
| publish-nuget: | ||||||||||||||||||
| name: Publish NuGet | ||||||||||||||||||
| needs: [test-cli-integration, test-shell-functions, test-airlock] | ||||||||||||||||||
| runs-on: ubuntu-24.04 | ||||||||||||||||||
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | ||||||||||||||||||
| steps: | ||||||||||||||||||
| - name: Checkout repository | ||||||||||||||||||
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | ||||||||||||||||||
|
|
||||||||||||||||||
| - name: Setup .NET | ||||||||||||||||||
| uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 | ||||||||||||||||||
| with: | ||||||||||||||||||
| global-json-file: global.json | ||||||||||||||||||
|
|
||||||||||||||||||
| - name: Extract version from shell script | ||||||||||||||||||
| id: version | ||||||||||||||||||
| run: | | ||||||||||||||||||
| VERSION=$(grep -m1 "^# Version:" copilot_here.sh | sed 's/# Version: //') | ||||||||||||||||||
| echo "version=$VERSION" >> $GITHUB_OUTPUT | ||||||||||||||||||
|
|
||||||||||||||||||
| - name: Pack as .NET tool | ||||||||||||||||||
| run: dotnet pack app/CopilotHere.csproj -c Release -p:PackAsTool=true -p:CopilotHereVersion=${{ steps.version.outputs.version }} --nologo | ||||||||||||||||||
|
|
||||||||||||||||||
| - name: Push to NuGet | ||||||||||||||||||
| if: ${{ secrets.NUGET_API_KEY != '' }} | ||||||||||||||||||
| run: dotnet nuget push "app/bin/Release/*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate | ||||||||||||||||||
|
|
||||||||||||||||||
| # Summary job | ||||||||||||||||||
| publish-summary: | ||||||||||||||||||
| name: Publish Summary | ||||||||||||||||||
|
|
@@ -570,6 +598,7 @@ jobs: | |||||||||||||||||
| build-variants, | ||||||||||||||||||
| build-compound-variants, | ||||||||||||||||||
| release-cli, | ||||||||||||||||||
| publish-nuget, | ||||||||||||||||||
| ] | ||||||||||||||||||
| if: always() && (needs.build-base.result != 'skipped' || needs.release-cli.result != 'skipped') | ||||||||||||||||||
| runs-on: ubuntu-24.04 | ||||||||||||||||||
|
|
@@ -612,6 +641,15 @@ jobs: | |||||||||||||||||
| echo "- linux-x64, linux-arm64" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||
| echo "- osx-x64, osx-arm64" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||
| echo "- win-x64, win-arm64" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||
| echo "" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||
| echo "**Package Managers:**" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||
| echo "- Homebrew tap updated" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||
| echo "- WinGet manifest submitted" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||
| fi | ||||||||||||||||||
|
|
||||||||||||||||||
| if [[ "${{ needs.publish-nuget.result }}" == "success" ]]; then | ||||||||||||||||||
| echo "" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||
| echo "**.NET Tool:** Published to nuget.org" >> $GITHUB_STEP_SUMMARY | ||||||||||||||||||
|
||||||||||||||||||
| echo "**.NET Tool:** Published to nuget.org" >> $GITHUB_STEP_SUMMARY | |
| echo "**.NET Tool:** NuGet publish job completed (push may be skipped if NUGET_API_KEY is not set)" >> $GITHUB_STEP_SUMMARY |
Copilot
AI
Mar 3, 2026
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 Homebrew tap update disables SSH host key checking (StrictHostKeyChecking=no) even though known_hosts is populated. This weakens protection against MITM and is avoidable here; prefer relying on a pinned host key in known_hosts (or at least remove StrictHostKeyChecking=no) so the deploy key cannot be used against an unexpected host.
| export GIT_SSH_COMMAND="ssh -i ~/.ssh/homebrew_tap_key -o StrictHostKeyChecking=no" | |
| export GIT_SSH_COMMAND="ssh -i ~/.ssh/homebrew_tap_key" |
Copilot
AI
Mar 3, 2026
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 tap update assumes /tmp/homebrew-tap/Formula/ already exists. If the tap repo is newly created or doesn't have that directory, the cat > /tmp/homebrew-tap/Formula/copilot_here.rb step will fail. Consider adding a mkdir -p /tmp/homebrew-tap/Formula before writing the file to make the workflow robust.
| # Ensure Formula directory exists | |
| mkdir -p /tmp/homebrew-tap/Formula |
Copilot
AI
Mar 3, 2026
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 Homebrew tap commit/push will fail the workflow when there are no changes (e.g., rerunning the workflow for the same version/tag), because git commit exits non-zero on a clean index. Consider checking git diff --quiet (and skipping commit/push) or allowing a no-op commit step so reruns don’t break publishing.
| git commit -m "Update copilot_here to $VERSION" | |
| git push | |
| if git diff --cached --quiet; then | |
| echo "No changes to commit in Homebrew tap; skipping commit and push." | |
| else | |
| git commit -m "Update copilot_here to $VERSION" | |
| git push | |
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| # Package Manager Distribution Setup | ||
|
|
||
| This document describes the external setup steps required to enable automated package manager distribution. | ||
|
|
||
| ## 1. NuGet (.NET Tool - Issue #50) | ||
|
|
||
| The CI workflow publishes the CLI as a .NET global tool to nuget.org. | ||
|
|
||
| ### Setup steps | ||
|
|
||
| - [ ] Create/verify a nuget.org account at https://www.nuget.org/ | ||
| - [ ] Generate an API key at https://www.nuget.org/account/apikeys | ||
| - Scope: **Push new packages and package versions** | ||
| - Glob pattern: `copilot_here` | ||
| - [ ] Add GitHub Actions secret on the `copilot_here` repo: | ||
| - Go to **Settings > Secrets and variables > Actions > New repository secret** | ||
| - Name: `NUGET_API_KEY` | ||
| - Value: the API key from nuget.org | ||
|
|
||
| ### Usage | ||
|
|
||
| ```bash | ||
| dotnet tool install -g copilot_here | ||
| copilot_here --version | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## 2. Homebrew Tap (Issue #51) | ||
|
|
||
| The CI workflow updates a Homebrew formula in a separate tap repository after each release. | ||
|
|
||
| ### Setup steps | ||
|
|
||
| - [ ] Create a public repository: [`GordonBeeming/homebrew-tap`](https://github.com/GordonBeeming/homebrew-tap) | ||
| - [ ] Copy the formula template from `packaging/homebrew/Formula/copilot_here.rb` to the new repo at `Formula/copilot_here.rb` | ||
| - [ ] Generate an SSH deploy key pair: | ||
| ```bash | ||
| ssh-keygen -t ed25519 -C "homebrew-tap-deploy" -f homebrew_tap_key -N "" | ||
| ``` | ||
| - [ ] Add the **public** key (`homebrew_tap_key.pub`) as a deploy key on the `homebrew-tap` repo: | ||
| - Go to `homebrew-tap` repo **Settings > Deploy keys > Add deploy key** | ||
| - Title: `copilot_here CI` | ||
| - Check **Allow write access** | ||
| - [ ] Add the **private** key (`homebrew_tap_key`) as a GitHub Actions secret on the `copilot_here` repo: | ||
| - Name: `HOMEBREW_TAP_DEPLOY_KEY` | ||
| - Value: the full contents of the private key file | ||
|
|
||
| ### Usage | ||
|
|
||
| ```bash | ||
| brew tap gordonbeeming/tap | ||
| brew install copilot_here | ||
| copilot_here --version | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## 3. WinGet (Issue #52) | ||
|
|
||
| The CI workflow auto-submits manifest updates to the `microsoft/winget-pkgs` repository using `wingetcreate`. | ||
|
|
||
| ### Setup steps | ||
|
|
||
| - [ ] Generate a Personal Access Token (classic) at https://github.com/settings/tokens | ||
| - Scope: `public_repo` (needs to submit PRs to `microsoft/winget-pkgs`) | ||
| - [ ] Add GitHub Actions secret on the `copilot_here` repo: | ||
| - Name: `WINGET_PAT` | ||
| - Value: the PAT | ||
| - [ ] **First submission note:** After code changes are merged and a release is created, the CI will auto-submit the first PR to `microsoft/winget-pkgs`. This initial PR requires manual review/approval by Microsoft maintainers (typically takes 1-3 days). Subsequent version updates are auto-approved. | ||
|
|
||
| ### Usage | ||
|
|
||
| ```powershell | ||
| winget install GordonBeeming.CopilotHere | ||
| copilot_here --version | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Summary of required GitHub secrets | ||
|
|
||
| | Secret Name | Purpose | Where to get it | | ||
| |---|---|---| | ||
| | `NUGET_API_KEY` | Push .NET tool to nuget.org | https://www.nuget.org/account/apikeys | | ||
| | `HOMEBREW_TAP_DEPLOY_KEY` | Update Homebrew formula | SSH deploy key (write access) on `homebrew-tap` repo | | ||
| | `WINGET_PAT` | Submit WinGet manifest PRs | https://github.com/settings/tokens (scope: `public_repo`) | |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,49 @@ | ||||||||||||||||
| # typed: false | ||||||||||||||||
| # frozen_string_literal: true | ||||||||||||||||
|
|
||||||||||||||||
| class CopilotHere < Formula | ||||||||||||||||
| desc "Run GitHub Copilot CLI in a sandboxed Docker container" | ||||||||||||||||
| homepage "https://github.com/GordonBeeming/copilot_here" | ||||||||||||||||
| version "VERSION_PLACEHOLDER" | ||||||||||||||||
| license "FSL-1.1-MIT" | ||||||||||||||||
|
|
||||||||||||||||
| on_macos do | ||||||||||||||||
| if Hardware::CPU.arm? | ||||||||||||||||
| url "https://github.com/GordonBeeming/copilot_here/releases/download/TAG_PLACEHOLDER/copilot_here-osx-arm64.tar.gz" | ||||||||||||||||
| sha256 "SHA256_OSX_ARM64_PLACEHOLDER" | ||||||||||||||||
| else | ||||||||||||||||
| url "https://github.com/GordonBeeming/copilot_here/releases/download/TAG_PLACEHOLDER/copilot_here-osx-x64.tar.gz" | ||||||||||||||||
| sha256 "SHA256_OSX_X64_PLACEHOLDER" | ||||||||||||||||
| end | ||||||||||||||||
| end | ||||||||||||||||
|
|
||||||||||||||||
| on_linux do | ||||||||||||||||
| if Hardware::CPU.arm? | ||||||||||||||||
| url "https://github.com/GordonBeeming/copilot_here/releases/download/TAG_PLACEHOLDER/copilot_here-linux-arm64.tar.gz" | ||||||||||||||||
| sha256 "SHA256_LINUX_ARM64_PLACEHOLDER" | ||||||||||||||||
| else | ||||||||||||||||
| url "https://github.com/GordonBeeming/copilot_here/releases/download/TAG_PLACEHOLDER/copilot_here-linux-x64.tar.gz" | ||||||||||||||||
| sha256 "SHA256_LINUX_X64_PLACEHOLDER" | ||||||||||||||||
| end | ||||||||||||||||
| end | ||||||||||||||||
|
|
||||||||||||||||
| depends_on "docker" => :recommended | ||||||||||||||||
|
|
||||||||||||||||
| def install | ||||||||||||||||
| bin.install "copilot_here" | ||||||||||||||||
| end | ||||||||||||||||
|
|
||||||||||||||||
| def caveats | ||||||||||||||||
| <<~EOS | ||||||||||||||||
| To enable the shell function wrapper, run: | ||||||||||||||||
| copilot_here --install-shells | ||||||||||||||||
|
|
||||||||||||||||
| Or manually source the shell script in your profile: | ||||||||||||||||
| Bash/Zsh: source "$(brew --prefix)/share/copilot_here/copilot_here.sh" | ||||||||||||||||
|
Comment on lines
+38
to
+42
|
||||||||||||||||
| To enable the shell function wrapper, run: | |
| copilot_here --install-shells | |
| Or manually source the shell script in your profile: | |
| Bash/Zsh: source "$(brew --prefix)/share/copilot_here/copilot_here.sh" | |
| To enable the shell function wrappers for your shell, run: | |
| copilot_here --install-shells |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| PackageIdentifier: GordonBeeming.CopilotHere | ||
| PackageVersion: 2026.02.19 | ||
| InstallerType: zip | ||
| NestedInstallerType: portable | ||
| NestedInstallerFiles: | ||
| - RelativeFilePath: copilot_here.exe | ||
| Installers: | ||
| - Architecture: x64 | ||
| InstallerUrl: https://github.com/GordonBeeming/copilot_here/releases/download/TAG_PLACEHOLDER/copilot_here-win-x64.zip | ||
| InstallerSha256: SHA256_WIN_X64_PLACEHOLDER | ||
| - Architecture: arm64 | ||
| InstallerUrl: https://github.com/GordonBeeming/copilot_here/releases/download/TAG_PLACEHOLDER/copilot_here-win-arm64.zip | ||
| InstallerSha256: SHA256_WIN_ARM64_PLACEHOLDER | ||
| ManifestType: installer | ||
| ManifestVersion: 1.6.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.
The workflow summary currently prints "Homebrew tap updated" and "WinGet manifest submitted" whenever
release-clisucceeds, but both updates are conditional on secrets and may be skipped. This can produce misleading summaries; consider checking the relevant secrets or step outcomes (or exporting an output from those steps) before claiming they ran.