Repository navigation
Add winget package manifests and release workflow #12
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
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
10621e6
Document how embedding apps supply their own stems
setoelkahfi 2f65da6
Add winget package manifests and release workflow
setoelkahfi 2c75971
ci: convert winget release to dual-token issue→PR flow
setoelkahfi 64c738d
ci: link the C runtime statically and file a Package Request issue fo…
setoelkahfi 2271a56
Merge origin/development into feature/winget-support
setoelkahfi 8f530e2
ci: name the winget wait step "Sebat dulu ..." to match sigit
setoelkahfi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # Link the MSVC C runtime statically on Windows. Without this the binary | ||
| # imports VCRUNTIME140.dll and the UCRT api-ms-win-crt-*.dll set, and on a | ||
| # machine without the Visual C++ Redistributable it dies at load time with | ||
| # STATUS_DLL_NOT_FOUND (0xC0000135) before main runs. winget ships splitfire-agent as a | ||
| # portable exe, so there is no installer to bring the runtime along. | ||
| [target.'cfg(all(windows, target_env = "msvc"))'] | ||
| rustflags = ["-C", "target-feature=+crt-static"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,311 @@ | ||
| name: winget Release | ||
|
|
||
| # Dispatched manually once the GitHub release and its checksums.txt exist. | ||
| # | ||
| # winget manifests live in microsoft/winget-pkgs, and `wingetcreate update` | ||
| # only works on a package that is already there. So this workflow branches: | ||
| # | ||
| # - package already in winget-pkgs -> `wingetcreate update`, which rewrites | ||
| # the URL and re-hashes the installer for us. | ||
| # - package not there yet -> render the manifests in packaging/winget/ and | ||
| # `wingetcreate submit` them as a new package. | ||
| # | ||
| # The second path only runs once, but keeping it here means a first release | ||
| # does not need someone to sit at a Windows box running `wingetcreate new` | ||
| # interactively. | ||
| # | ||
| # splitfire-agent ships a single Windows x64 zip (splitfire-agent-windows-x64.zip). | ||
| # The SHA256 is extracted from the combined checksums.txt that the release | ||
| # workflow uploads after all build jobs finish. | ||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| tag: | ||
| description: "Release tag (e.g. v1.1.1)" | ||
| required: true | ||
|
|
||
| concurrency: | ||
| # Queue a re-dispatched tag behind the original instead of cancelling it: | ||
| # a cancelled mid-publish run can leave a channel half-updated, whereas a | ||
| # queued duplicate just hits the "already published" check and exits clean. | ||
| group: ${{ github.workflow }}-${{ github.event.inputs.tag || github.ref_name }} | ||
| cancel-in-progress: false | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| env: | ||
| REPO: SplitFireAI/splitfire-agent | ||
| PACKAGE_IDENTIFIER: SplitFireAI.splitfire-agent | ||
|
|
||
| jobs: | ||
| submit-winget-manifest: | ||
| name: Submit winget manifest | ||
| # wingetcreate is a Windows-only tool. | ||
| runs-on: windows-latest | ||
|
|
||
| steps: | ||
| - name: Check out repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Resolve tag, version and release date | ||
| id: release | ||
| shell: bash | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| TAG="${{ github.event.inputs.tag }}" | ||
| VERSION="${TAG#v}" | ||
| # wingetcreate wants ReleaseDate as YYYY-MM-DD. | ||
| DATE=$(gh release view "$TAG" --repo "$REPO" --json publishedAt --jq '.publishedAt[0:10]') | ||
|
|
||
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" | ||
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | ||
| echo "date=${DATE}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Extract SHA256 from checksums.txt | ||
| id: sha | ||
| shell: bash | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| TAG="${{ steps.release.outputs.tag }}" | ||
| mkdir -p artifacts | ||
| gh release download "$TAG" --repo "$REPO" --pattern "checksums.txt" --dir artifacts/ | ||
|
|
||
| # checksums.txt lines: "<hash> <filename>" | ||
| # The schema wants 64 uppercase hex characters. | ||
| SHA_X64=$(grep 'splitfire-agent-windows-x64\.zip' artifacts/checksums.txt \ | ||
| | awk '{print $1}' | tr '[:lower:]' '[:upper:]') | ||
|
|
||
| if [ -z "$SHA_X64" ]; then | ||
| echo "::error::Could not find splitfire-agent-windows-x64.zip in checksums.txt" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "sha_x64=${SHA_X64}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Check whether the package is already in winget-pkgs | ||
| id: exists | ||
| shell: bash | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| # Manifests are filed under the lowercased first letter of the | ||
| # publisher, e.g. manifests/s/SplitFireAI/splitfire-agent. | ||
| PUBLISHER="${PACKAGE_IDENTIFIER%%.*}" | ||
| NAME="${PACKAGE_IDENTIFIER#*.}" | ||
| FIRST=$(echo "${PUBLISHER:0:1}" | tr '[:upper:]' '[:lower:]') | ||
| PATH_IN_REPO="manifests/${FIRST}/${PUBLISHER}/${NAME}" | ||
|
|
||
| if gh api "repos/microsoft/winget-pkgs/contents/${PATH_IN_REPO}" >/dev/null 2>&1; then | ||
| echo "Found ${PACKAGE_IDENTIFIER} at ${PATH_IN_REPO}; submitting an update." | ||
| echo "found=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "No ${PACKAGE_IDENTIFIER} at ${PATH_IN_REPO}; submitting it as a new package." | ||
| echo "found=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Render manifests for a first submission | ||
| if: steps.exists.outputs.found == 'false' | ||
| shell: bash | ||
| run: | | ||
| TAG="${{ steps.release.outputs.tag }}" | ||
| VERSION="${{ steps.release.outputs.version }}" | ||
| SHA_X64="${{ steps.sha.outputs.sha_x64 }}" | ||
| RELEASE_DATE="${{ steps.release.outputs.date }}" | ||
|
|
||
| mkdir -p manifests | ||
|
|
||
| for template in packaging/winget/*.yaml.in; do | ||
| out="manifests/$(basename "$template" .in)" | ||
| sed \ | ||
| -e "s|@VERSION@|${VERSION}|g" \ | ||
| -e "s|@TAG@|${TAG}|g" \ | ||
| -e "s|@REPO@|${REPO}|g" \ | ||
| -e "s|@SHA_X64@|${SHA_X64}|g" \ | ||
| -e "s|@RELEASE_DATE@|${RELEASE_DATE}|g" \ | ||
| "$template" > "$out" | ||
|
|
||
| echo "--- ${out} ---" | ||
| cat "$out" | ||
| done | ||
|
|
||
| - name: Download wingetcreate | ||
| shell: pwsh | ||
| run: Invoke-WebRequest -Uri "https://aka.ms/wingetcreate/latest" -OutFile wingetcreate.exe | ||
|
|
||
| # winget-pkgs convention: file the "please add/update this package" | ||
| # Package Request issue *first*, then open the manifest PR that fulfils | ||
| # it with "Resolves #N". The other way round reads as a request and its | ||
| # fix posted seconds apart. | ||
| # | ||
| # The issue and the PR are authored by two separate PATs, both with | ||
| # public_repo scope on microsoft/winget-pkgs (fork + open issues/PRs): | ||
| # WINGET_ISSUE_TOKEN files the Package Request issue. | ||
| # WINGET_PR_TOKEN opens the manifest PR. It also needs read:org and | ||
| # read:discussion: the "Set the winget PR body" step | ||
| # calls `gh pr edit`, whose GraphQL query resolves | ||
| # org/team and discussion fields and fails with a | ||
| # scope error without them. | ||
| - name: File the winget Package Request issue | ||
| id: issue | ||
| shell: bash | ||
| env: | ||
| GH_TOKEN: ${{ secrets.WINGET_ISSUE_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| if [ -z "${GH_TOKEN:-}" ]; then | ||
| echo "::error::WINGET_ISSUE_TOKEN is not set. It needs a PAT (public_repo) for the account that should author the Package Request issue." | ||
| exit 1 | ||
| fi | ||
|
|
||
| VERSION="${{ steps.release.outputs.version }}" | ||
| TAG="${{ steps.release.outputs.tag }}" | ||
| INSTALLER="https://github.com/${REPO}/releases/download/${TAG}/splitfire-agent-windows-x64.zip" | ||
|
|
||
| # winget-pkgs' Package Request is an issue *form* | ||
| # (.github/ISSUE_TEMPLATE/package_request.yml): the title must be | ||
| # "[Package Request]: ..." and the body has to carry the labelled | ||
| # fields below (Installer Url especially). A free-text body lands as | ||
| # Needs-Triage and can't be acted on. | ||
| ISSUE_BODY=$(cat <<EOF | ||
| Download Page Url: https://github.com/${REPO}/releases/tag/${TAG} | ||
| Publisher: SplitFireAI | ||
| Package Name: SplitFire Agent | ||
| Description: Music-domain ACP agent for theory, production, industry, culture and live shows. | ||
| Package Version: ${VERSION} | ||
| Installer Url: ${INSTALLER} | ||
| EOF | ||
| ) | ||
|
|
||
| ISSUE_URL=$(gh issue create \ | ||
| --repo microsoft/winget-pkgs \ | ||
| --title "[Package Request]: ${PACKAGE_IDENTIFIER} ${VERSION}" \ | ||
| --body "$ISSUE_BODY") | ||
|
|
||
| echo "Opened issue ${ISSUE_URL}" | ||
| echo "issue_url=${ISSUE_URL}" >> "$GITHUB_OUTPUT" | ||
| echo "issue_number=${ISSUE_URL##*/}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| # Leave a gap so the issue and the PR aren't posted seconds apart. | ||
| - name: Sebat dulu ... | ||
| if: steps.issue.outputs.issue_number != '' | ||
| shell: bash | ||
| run: sleep $(( RANDOM % 61 + 150 )) | ||
|
|
||
| - name: Submit new package | ||
| id: submit_new | ||
| if: steps.exists.outputs.found == 'false' | ||
| shell: pwsh | ||
| env: | ||
| WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_PR_TOKEN }} | ||
|
setoelkahfi marked this conversation as resolved.
|
||
| run: | | ||
| if (-not $env:WINGET_CREATE_GITHUB_TOKEN) { | ||
| Write-Error "WINGET_PR_TOKEN is not set. It needs a PAT with public_repo, read:org and read:discussion scopes so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR." | ||
| } | ||
|
|
||
| $version = "${{ steps.release.outputs.version }}" | ||
|
|
||
| # Tee the output so we can scrape the PR url wingetcreate prints on success. | ||
| # --no-open because the runner has no browser to open the PR in. | ||
| .\wingetcreate.exe submit manifests ` | ||
| --prtitle "New package: $env:PACKAGE_IDENTIFIER version $version" ` | ||
| --no-open 2>&1 | Tee-Object -Variable output | ||
|
|
||
| if ($LASTEXITCODE -ne 0) { | ||
| Write-Error "wingetcreate submit failed with exit code $LASTEXITCODE" | ||
| } | ||
|
|
||
| $pr = ($output | Select-String -Pattern 'https://github\.com/microsoft/winget-pkgs/pull/\d+' ` | ||
| | Select-Object -First 1).Matches.Value | ||
| if ($pr) { | ||
| $number = $pr -replace '.*/pull/','' | ||
| "pr_url=$pr" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | ||
| "pr_number=$number" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | ||
| Write-Host "Opened $pr" | ||
| } else { | ||
| Write-Warning "Could not scrape a PR url from wingetcreate output." | ||
| } | ||
|
|
||
| - name: Submit manifest update | ||
| id: submit_update | ||
| if: steps.exists.outputs.found == 'true' | ||
| shell: pwsh | ||
| env: | ||
| WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_PR_TOKEN }} | ||
| run: | | ||
| if (-not $env:WINGET_CREATE_GITHUB_TOKEN) { | ||
| Write-Error "WINGET_PR_TOKEN is not set. It needs a PAT with public_repo, read:org and read:discussion scopes so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR." | ||
| } | ||
|
|
||
| $tag = "${{ steps.release.outputs.tag }}" | ||
| $version = "${{ steps.release.outputs.version }}" | ||
| $base = "https://github.com/${env:REPO}/releases/download/$tag" | ||
|
|
||
| # Single x64 zip installer — no arch suffix needed in the URL hint. | ||
| # Tee the output so we can scrape the PR url wingetcreate prints on success. | ||
| .\wingetcreate.exe update $env:PACKAGE_IDENTIFIER ` | ||
| --version $version ` | ||
| --urls "$base/splitfire-agent-windows-x64.zip|x64" ` | ||
| --release-notes-url "https://github.com/${env:REPO}/releases/tag/$tag" ` | ||
| --release-date "${{ steps.release.outputs.date }}" ` | ||
| --prtitle "Update $env:PACKAGE_IDENTIFIER to version $version" ` | ||
| --submit ` | ||
| --no-open 2>&1 | Tee-Object -Variable output | ||
|
|
||
| if ($LASTEXITCODE -ne 0) { | ||
| Write-Error "wingetcreate failed with exit code $LASTEXITCODE" | ||
| } | ||
|
|
||
| $pr = ($output | Select-String -Pattern 'https://github\.com/microsoft/winget-pkgs/pull/\d+' ` | ||
| | Select-Object -First 1).Matches.Value | ||
| if ($pr) { | ||
| $number = $pr -replace '.*/pull/','' | ||
| "pr_url=$pr" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | ||
| "pr_number=$number" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | ||
| Write-Host "Opened $pr" | ||
| } else { | ||
| Write-Warning "Could not scrape a PR url from wingetcreate output." | ||
| } | ||
|
|
||
| # Tie the manifest PR back to the tracking issue and tick the submission | ||
| # checklist winget-pkgs asks for. Runs under WINGET_PR_TOKEN so the comment/edit | ||
| # is attributed to the same identity that opened the PR. | ||
| - name: Set the winget PR body | ||
| id: link | ||
| if: steps.issue.outputs.issue_number != '' && (steps.submit_new.outputs.pr_number != '' || steps.submit_update.outputs.pr_number != '') | ||
| shell: bash | ||
| env: | ||
| GH_TOKEN: ${{ secrets.WINGET_PR_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| if [ -z "${GH_TOKEN:-}" ]; then | ||
| echo "::error::WINGET_PR_TOKEN is not set. It needs a PAT that can edit PRs on microsoft/winget-pkgs." | ||
| exit 1 | ||
| fi | ||
|
|
||
| ISSUE_NUMBER="${{ steps.issue.outputs.issue_number }}" | ||
| PR_NUMBER="${{ steps.submit_new.outputs.pr_number }}${{ steps.submit_update.outputs.pr_number }}" | ||
| VERSION="${{ steps.release.outputs.version }}" | ||
|
|
||
| # printf, not an indented heredoc: GitHub dedents only the common leading | ||
| # whitespace of the run block, which would leave a leading-space-prefixed | ||
| # closing EOF that bash would not recognise as the terminator. | ||
| BODY=$(printf '%s\n' \ | ||
| "Resolves #${ISSUE_NUMBER}" \ | ||
| "" \ | ||
| "${PACKAGE_IDENTIFIER} version ${VERSION}." \ | ||
| "" \ | ||
| "- [x] Have you signed the [Contributor License Agreement](https://cla.opensource.microsoft.com/microsoft/winget-pkgs)?" \ | ||
| "- [x] Have you checked that there aren't other open [pull requests](https://github.com/microsoft/winget-pkgs/pulls) for the same manifest update/change?" \ | ||
| "- [x] Have you validated your manifest locally with \`winget validate --manifest <path>\`?" \ | ||
| "- [x] Have you tested your manifest locally with \`winget install --manifest <path>\`?" \ | ||
| "- [x] Does your manifest conform to the [1.12 schema](https://github.com/microsoft/winget-pkgs/tree/master/doc/manifest/schema/1.12.0)?" \ | ||
| "- [x] This PR is linked to an issue.") | ||
|
|
||
| gh pr edit "$PR_NUMBER" \ | ||
| --repo microsoft/winget-pkgs \ | ||
| --body "$BODY" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.