Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
13 changes: 13 additions & 0 deletions .github/workflows/release-binaries.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,16 @@ jobs:
uses: ./.github/workflows/homebrew-formula.yaml
with:
tag: ${{ github.event.release.tag_name }}

winget-manifests:
name: WinGet manifests
needs: build-binaries
permissions:
contents: read
uses: ./.github/workflows/winget-manifests.yaml
with:
release_tag: ${{ github.event.release.tag_name }}
publish: true
secrets:
PROJECT_APP_ID: ${{ secrets.PROJECT_APP_ID }}
PROJECT_APP_KEY: ${{ secrets.PROJECT_APP_KEY }}
232 changes: 232 additions & 0 deletions .github/workflows/winget-manifests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
# Copyright AI-Catalog Contributors (https://github.com/Agent-Card/ai-catalog-cli)
# Copyright AGNTCY Contributors (https://github.com/agntcy)
# SPDX-License-Identifier: Apache-2.0

---
name: winget-manifests

on:
workflow_call:
inputs:
release_tag:
description: Release tag to render WinGet manifests for
required: true
type: string
publish:
description: Push the manifests to the winget-pkgs fork
required: false
type: boolean
default: false
secrets:
PROJECT_APP_ID:
required: false
PROJECT_APP_KEY:
required: false
workflow_dispatch:
inputs:
release_tag:
description: Release tag to render WinGet manifests for
required: true
type: string
publish:
description: Push the manifests to the winget-pkgs fork
required: false
type: boolean
default: false

concurrency:
group: ${{ github.workflow }}-${{ inputs.release_tag }}
cancel-in-progress: false

jobs:
render:
name: Render manifests
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
manifest_rel_dir: ${{ steps.render.outputs.manifest_rel_dir }}
package_identifier: ${{ steps.render.outputs.package_identifier }}
package_version: ${{ steps.render.outputs.package_version }}
release_url: ${{ steps.render.outputs.release_url }}
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Setup Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"

- name: Render WinGet manifests
id: render
env:
GITHUB_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.release_tag }}
run: |
python3 scripts/render_winget_manifests.py \
--tag "$RELEASE_TAG" \
--output-dir dist/winget \
--github-output "$GITHUB_OUTPUT"

- name: Upload manifests
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: winget-manifests-${{ steps.render.outputs.package_version }}
path: dist/winget/manifests

validate:
name: Validate manifests
needs: render
runs-on: windows-2025
permissions:
contents: read
steps:
- name: Download rendered manifests
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: winget-manifests-${{ needs.render.outputs.package_version }}
path: manifests

- name: Validate and install from the manifest
shell: pwsh
env:
MANIFEST_REL_DIR: ${{ needs.render.outputs.manifest_rel_dir }}
run: |
function Assert-LastExit($what) {
if ($LASTEXITCODE -ne 0) { throw "$what failed with exit $LASTEXITCODE" }
}

$dir = Join-Path "manifests" ($env:MANIFEST_REL_DIR -replace '^manifests/', '')
Get-ChildItem $dir | ForEach-Object { Write-Host $_.Name }

winget --version
winget validate --manifest $dir --ignore-warnings
Assert-LastExit "winget validate"

winget settings --enable LocalManifestFiles
Assert-LastExit "winget settings --enable LocalManifestFiles"

winget install --manifest $dir `
--accept-package-agreements --accept-source-agreements --disable-interactivity
Assert-LastExit "winget install"

$env:PATH = "$env:LOCALAPPDATA\Microsoft\WinGet\Links;$env:SystemRoot\System32;$env:SystemRoot"
ai-catalog --help
Assert-LastExit "ai-catalog --help"
Write-Host "OK - installed via winget and the binary runs"

publish:
name: Publish to winget-pkgs
needs: [render, validate]
if: inputs.publish == true || inputs.publish == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
env:
APP_AUTH_CONFIGURED: ${{ secrets.PROJECT_APP_ID != '' && secrets.PROJECT_APP_KEY != '' }}
steps:
- name: Download rendered manifests
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: winget-manifests-${{ needs.render.outputs.package_version }}
path: dist/winget/manifests

- name: Authenticate as the organization bot
if: env.APP_AUTH_CONFIGURED == 'true'
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.PROJECT_APP_ID }}
private-key: ${{ secrets.PROJECT_APP_KEY }}
owner: ${{ github.repository_owner }}

- name: Resolve bot user id
if: ${{ steps.app-token.outputs.token != '' }}
id: app-user
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
APP_SLUG: ${{ steps.app-token.outputs.app-slug }}
run: echo "user_id=$(gh api "/users/$APP_SLUG[bot]" --jq .id)" >> "$GITHUB_OUTPUT"

- name: Push manifests to the winget-pkgs fork
env:
WINGET_PKGS_TOKEN: ${{ steps.app-token.outputs.token }}
FORK_OWNER: ${{ github.repository_owner }}
GIT_AUTHOR_NAME: ${{ steps.app-token.outputs.app-slug }}[bot]
GIT_AUTHOR_EMAIL: ${{ steps.app-user.outputs.user_id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com
MANIFEST_DIR: ${{ github.workspace }}/dist/winget/${{ needs.render.outputs.manifest_rel_dir }}
MANIFEST_REL_DIR: ${{ needs.render.outputs.manifest_rel_dir }}
PACKAGE_IDENTIFIER: ${{ needs.render.outputs.package_identifier }}
PACKAGE_VERSION: ${{ needs.render.outputs.package_version }}
RELEASE_TAG: ${{ inputs.release_tag }}
RELEASE_URL: ${{ needs.render.outputs.release_url }}
run: |
if [ -z "$WINGET_PKGS_TOKEN" ]; then
echo "::notice::PROJECT_APP_ID / PROJECT_APP_KEY are not configured; the manifests are available as a workflow artifact only."
exit 0
fi

export GH_TOKEN="$WINGET_PKGS_TOKEN"

if ! gh repo view "$FORK_OWNER/winget-pkgs" >/dev/null 2>&1; then
echo "::error::No fork at $FORK_OWNER/winget-pkgs. Fork microsoft/winget-pkgs into the organization before publishing."
exit 1
fi

gh auth setup-git >/dev/null
rm -rf /tmp/winget-pkgs
gh repo clone "$FORK_OWNER/winget-pkgs" /tmp/winget-pkgs -- --depth 1

cd /tmp/winget-pkgs
git remote add upstream https://github.com/microsoft/winget-pkgs.git 2>/dev/null ||
git remote set-url upstream https://github.com/microsoft/winget-pkgs.git
git fetch --depth 1 upstream master

branch="automation/winget-ai-catalog-$PACKAGE_VERSION"
git config user.name "$GIT_AUTHOR_NAME"
git config user.email "$GIT_AUTHOR_EMAIL"
git checkout -B "$branch" upstream/master

if [ ! -d "$MANIFEST_DIR" ]; then
echo "::error::rendered manifests are not at $MANIFEST_DIR"
exit 1
fi

mkdir -p "$(dirname "$MANIFEST_REL_DIR")"
rm -rf "$MANIFEST_REL_DIR"
cp -R "$MANIFEST_DIR" "$(dirname "$MANIFEST_REL_DIR")/"
git add --all "$MANIFEST_REL_DIR"

if git diff --cached --quiet -- "$MANIFEST_REL_DIR"; then
echo "WinGet manifests are already up to date for $RELEASE_TAG"
exit 0
fi

git commit -s -m "New version: $PACKAGE_IDENTIFIER version $PACKAGE_VERSION"
git push --force --set-upstream origin "$branch"

pr_number=$(gh pr list --repo microsoft/winget-pkgs --head "$FORK_OWNER:$branch" \
--base master --state open --json number --jq 'first(.[].number) // ""' 2>/dev/null || true)

{
echo "### WinGet manifests for $PACKAGE_IDENTIFIER $PACKAGE_VERSION"
echo
if [ -n "$pr_number" ]; then
echo "Pushed \`$branch\`, updating the open upstream PR:"
echo
echo "https://github.com/microsoft/winget-pkgs/pull/$pr_number"
else
echo "Pushed \`$branch\`. Open the upstream PR to publish it:"
echo
echo "https://github.com/microsoft/winget-pkgs/compare/master...$FORK_OWNER:$branch?expand=1"
echo
echo "The App token cannot open that PR: an App only acts on"
echo "repositories it is installed on, and microsoft/winget-pkgs"
echo "is not one of them."
fi
echo
echo "Upstream release: $RELEASE_URL"
} >> "$GITHUB_STEP_SUMMARY"
Loading