Skip to content

Fix winget-publish: use gh CLI instead of wingetcreate --submit - #254

Merged
fernandotonon merged 2 commits into
masterfrom
fix/winget-publish
Apr 8, 2026
Merged

Fix winget-publish: use gh CLI instead of wingetcreate --submit#254
fernandotonon merged 2 commits into
masterfrom
fix/winget-publish

Conversation

@fernandotonon

@fernandotonon fernandotonon commented Apr 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • wingetcreate --submit consistently fails with "Failed to connect to GitHub" on Actions runners
  • Replace with a two-step approach: generate manifest with wingetcreate, submit PR via gh CLI
  • Switch to ubuntu-latest (wingetcreate available as .NET tool, gh CLI pre-installed)
  • Add continue-on-error: true so submission failures don't block releases

Test plan

  • Verify manifest generation works with wingetcreate update --out
  • Verify gh CLI can fork winget-pkgs, create branch, and open PR
  • Re-run the release workflow on 2.21.0 to confirm submission succeeds

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Made WinGet publication non-blocking so release workflow continues even if package publication steps fail.
    • Modernized the Windows package deployment flow: moved to a cross-platform execution, automates manifest generation, and creates or updates the package repository via automated pull requests.

wingetcreate's built-in --submit consistently fails with "Failed to
connect to GitHub" on Actions runners. Replace with a two-step approach:

1. Generate manifest with wingetcreate (no --submit)
2. Fork winget-pkgs, create branch, commit manifest, open PR via gh CLI

Also:
- Switch to ubuntu-latest (wingetcreate available as .NET tool)
- Add continue-on-error: true so submission failures don't block releases
- Use gh CLI which authenticates more reliably with the WINGET_TOKEN PAT

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 8, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The winget-publish GitHub Actions job was moved from windows-latest to ubuntu-latest, changed to install wingetcreate as a .NET global tool, generate manifests locally, and submit updates by forking/cloning microsoft/winget-pkgs, committing manifests to a versioned branch, and opening a PR; PR creation is allowed to fail without failing the release.

Changes

Cohort / File(s) Summary
WinGet Publication Workflow
​.github/workflows/deploy.yml
winget-publish runner switched to ubuntu-latest; adds checkout, installs wingetcreate as a .NET tool, waits/downloads release ZIP, generates manifests via wingetcreate update ... --out manifests, then handles submission by forking/cloning microsoft/winget-pkgs, creating/reusing a versioned branch, copying manifests to manifests/f/.../<version>, committing/pushing (no-op exit on no diff), and creating/updating a PR with gh pr create; continue-on-error: true applied to the PR step. Removed Windows PowerShell wingetcreate.exe + --submit flow and per-attempt retry loop.

Sequence Diagram(s)

sequenceDiagram
  participant Runner as CI Runner (ubuntu)
  participant Tool as wingetcreate (.NET tool)
  participant Release as GitHub Release (ZIP asset)
  participant Fork as User Fork (git)
  participant upstream as microsoft/winget-pkgs
  participant GH as GitHub API (gh)

  Runner->>Tool: install dotnet tool (wingetcreate)
  Runner->>Release: poll/download release ZIP
  Runner->>Tool: run `wingetcreate update ... --out manifests`
  Runner->>Fork: git clone authenticated fork
  Runner->>Fork: create/reuse versioned branch
  Runner->>Fork: copy manifests -> manifests/f/.../<version>
  Runner->>Fork: git commit & push (exit if no diff)
  Runner->>GH: check for existing PR
  alt no existing PR
    Runner->>GH: create PR targeting upstream
  else PR exists
    Runner->>GH: update PR branch
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
I hopped from Windows to Linux land,
Crafting manifests with careful hand,
I fork, I branch, I push, I plea,
A PR blooms for all to see —
May CI cheer and pipelines stand!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: replacing wingetcreate --submit with gh CLI for manifest submission.
Description check ✅ Passed The description includes the required Summary and Technical Details sections with clear bullets explaining the problem, solution, and testing approach.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/winget-publish

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7f15b15ffb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/deploy.yml Outdated
RAW_TAG="${{ github.event.release.tag_name }}"
VER="${RAW_TAG#v}"
BRANCH="qtmesheditor-${VER}"
FORK_OWNER="${{ github.repository_owner }}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Derive fork owner from authenticated GH account

gh repo fork microsoft/winget-pkgs creates the fork under the account tied to GH_TOKEN, but this workflow hardcodes FORK_OWNER to github.repository_owner. If the secret token belongs to a bot/user different from the repository owner (a common CI setup), gh repo clone "${FORK_OWNER}/winget-pkgs" and the --head "${FORK_OWNER}:${BRANCH}" PR reference will target a repo/branch that does not exist, so submission fails every run in that environment.

Useful? React with 👍 / 👎.

git config user.email "github-actions[bot]@users.noreply.github.com"
git add .
git commit -m "Update FernandoTonon.QtMeshEditor to ${VER}"
git push -u origin "$BRANCH"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Configure git auth before pushing winget fork branch

This job exports GH_TOKEN for gh commands but then uses plain git push without configuring git credentials (for example via gh auth setup-git or a tokenized remote URL). On fresh runners, that push can fail authentication even though the preceding gh API calls succeed, which prevents opening the WinGet PR.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/deploy.yml:
- Around line 1736-1744: The workflow hands the new release ZIP URL (built from
RAW_TAG/VER/URL) straight to the wingetcreate command, which can fail if the
asset hasn't propagated; modify the section around RAW_TAG/VER/URL and the
wingetcreate update FernandoTonon.QtMeshEditor invocation to first poll the
constructed URL (URL) with a short retry loop (e.g., attempt curl -f or HEAD
requests), sleeping between attempts and capping retries, and only call
wingetcreate once the URL returns success; ensure the retry logic logs attempts
and ultimately fails the job if the URL never becomes reachable.
- Around line 1767-1784: The workflow currently always runs git checkout -b
"$BRANCH", commits, pushes and creates a PR (gh pr create) which fails or
duplicates when rerun for the same ${VER}; make the flow idempotent by detecting
and reusing existing branch/PR: check for an existing remote branch named
"$BRANCH" (or a matching PR for "${FORK_OWNER}:${BRANCH}" / title "Update
FernandoTonon.QtMeshEditor to ${VER}") before creating a new branch, and if it
exists, update its contents (git fetch/origin, git checkout "$BRANCH", git reset
--hard origin/"$BRANCH", copy manifests, git add/commit/push) or update the
existing PR via gh pr edit instead of gh pr create; use the variables BRANCH,
FORK_OWNER, VER, RAW_TAG, and the gh/git commands from the snippet to implement
the checks and conditional create-or-update logic so reruns either no-op or
update the existing PR.
- Around line 1725-1726: Remove the job-level continue-on-error: true (currently
adjacent to runs-on: ubuntu-latest) so failures in earlier steps like dotnet
tool install and wingetcreate update are not masked, and instead add
continue-on-error: true to the specific step named "Submit PR to winget-pkgs" so
only the PR submission tolerates failure; locate the job block that contains
runs-on: ubuntu-latest and the step with the display name "Submit PR to
winget-pkgs" and move the flag accordingly.
- Around line 1756-1763: Replace deriving FORK_OWNER from the static
github.repository_owner with the authenticated GH CLI user; call gh api user
--jq .login to get the token owner's login and assign that to FORK_OWNER before
running gh repo fork/clone, so gh repo fork creates and subsequent gh repo
clone/PR operations target the actual authenticated account (update the variable
assignment for FORK_OWNER used around the gh repo fork and gh repo clone steps).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dc13cae9-3165-4132-a5e7-dc412f1e9342

📥 Commits

Reviewing files that changed from the base of the PR and between 1591129 and 7f15b15.

📒 Files selected for processing (1)
  • .github/workflows/deploy.yml

Comment thread .github/workflows/deploy.yml Outdated
Comment thread .github/workflows/deploy.yml
Comment thread .github/workflows/deploy.yml Outdated
Comment thread .github/workflows/deploy.yml Outdated
- Derive FORK_OWNER from authenticated token via `gh api user --jq .login`
  instead of hardcoding github.repository_owner (fixes bot/service account)
- Add `gh auth setup-git` before git push for proper authentication
- Move continue-on-error from job-level to submission step only
- Add retry loop waiting for release ZIP to be downloadable (CDN propagation)
- Handle reruns: reuse existing branch, skip PR creation if one exists

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
.github/workflows/deploy.yml (1)

1796-1806: ⚠️ Potential issue | 🟠 Major

Keep PR creation reachable on no-diff reruns.

Line 1800 exits before the PR lookup. If a previous run already pushed ${BRANCH} but failed during gh pr create, the rerun sees no diff here and becomes a no-op, so the missing PR is never created.

💡 Proposed fix
           git config user.name "github-actions[bot]"
           git config user.email "github-actions[bot]@users.noreply.github.com"
           git add .
-          git diff --cached --quiet && echo "No changes to commit" && exit 0
-          git commit -m "Update FernandoTonon.QtMeshEditor to ${VER}"
-          git push -u origin "$BRANCH"
+          if git diff --cached --quiet; then
+            echo "No changes to commit"
+          else
+            git commit -m "Update FernandoTonon.QtMeshEditor to ${VER}"
+            git push -u origin "$BRANCH"
+          fi

           # Open PR (or skip if one already exists for this branch)
           EXISTING_PR="$(gh pr list --repo microsoft/winget-pkgs --head "${FORK_OWNER}:${BRANCH}" --json number --jq '.[0].number' 2>/dev/null || true)"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/deploy.yml around lines 1796 - 1806, The script exits
early when no staged changes are found (the `git diff --cached --quiet && echo
"No changes to commit" && exit 0` line), which prevents the subsequent PR
lookup/creation (`EXISTING_PR`, `gh pr list`, `gh pr create`) from running on
reruns; change the flow so a no-diff case skips commit/push but does not exit
the job — e.g., remove the `exit 0` or branch the logic so when `git diff
--cached` returns no changes you still proceed to check `EXISTING_PR`/call `gh
pr create` (and ensure `git push -u origin "$BRANCH"` is only run when a commit
was made or the branch already exists remotely).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In @.github/workflows/deploy.yml:
- Around line 1796-1806: The script exits early when no staged changes are found
(the `git diff --cached --quiet && echo "No changes to commit" && exit 0` line),
which prevents the subsequent PR lookup/creation (`EXISTING_PR`, `gh pr list`,
`gh pr create`) from running on reruns; change the flow so a no-diff case skips
commit/push but does not exit the job — e.g., remove the `exit 0` or branch the
logic so when `git diff --cached` returns no changes you still proceed to check
`EXISTING_PR`/call `gh pr create` (and ensure `git push -u origin "$BRANCH"` is
only run when a commit was made or the branch already exists remotely).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: acd8c74e-241b-4f6d-b7a6-c36af2da61a4

📥 Commits

Reviewing files that changed from the base of the PR and between 7f15b15 and 70b0e18.

📒 Files selected for processing (1)
  • .github/workflows/deploy.yml

@sonarqubecloud

sonarqubecloud Bot commented Apr 8, 2026

Copy link
Copy Markdown

@fernandotonon
fernandotonon merged commit 34fe349 into master Apr 8, 2026
31 of 33 checks passed
@fernandotonon
fernandotonon deleted the fix/winget-publish branch April 8, 2026 06:37
fernandotonon added a commit that referenced this pull request Apr 9, 2026
- Version 2.22.0 → 2.23.0
- 14 new tests: group_nodes, ungroup_node, reparent_node,
  set/get_pivot_mode, resample_animation (valid + error cases)
- AllToolNamesAreRecognized updated to 51 tools
- Winget fix already included (gh CLI approach from PR #254)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fernandotonon added a commit that referenced this pull request Apr 9, 2026
…271)

* Add animation keyframe resampling and decimation — Phase 1, item 8

Resample animations to reduce keyframe density (e.g., Mixamo's 200+
keyframes down to 30) or keep every Nth keyframe for lighter files.

Core:
- AnimationMerger::resampleAnimation(): evaluate animation at N
  evenly-spaced times via getInterpolatedKeyFrame, rebuild with
  smooth interpolated keyframes
- AnimationMerger::decimateAnimation(): keep every Nth keyframe
  plus always the last, discard the rest (no interpolation)

CLI:
- qtmesh anim model.fbx --resample 30 -o optimized.fbx
- qtmesh anim model.fbx --decimate-step 5 -o lighter.fbx
- qtmesh anim model.fbx --resample 30 --animation "Walk" -o out.fbx

MCP: resample_animation tool (target_keyframes or decimate_step)

Tests: 8 new tests (resample basic + interpolation, decimate basic +
keeps-last, null safety, missing animation, step validation)

Part of #256 (Phase 1: Scene Editing Power Tools)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add Node Grouping (Ctrl+G / Ctrl+Shift+G) — Phase 1, item 5

Group selected nodes under a shared parent for organized scene hierarchy.
Ogre SceneNode parenting propagates transforms automatically.

Core:
- Manager::groupNodes(): create empty "Group" node at centroid of
  selection, reparent children preserving world transforms
- Manager::ungroupNode(): reparent children to root, destroy empty group
- Manager::isGroupNode(): detect group nodes (no entities, has children)
- SceneTreeModel: shows "Group" type label, recursive child traversal
- GroupCommand/UngroupCommand: full undo/redo support
- Ctrl+G (group) and Ctrl+Shift+G (ungroup) in Edit menu
- MCP tools: group_nodes, ungroup_node
- 8 unit tests

Part of #256 (Phase 1: Scene Editing Power Tools)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Rename Group → Group Nodes, enable only with 2+ nodes selected

- Renamed menu items to "Group Nodes" and "Ungroup Nodes" for clarity
- Both actions start disabled; enabled/disabled via selectionChanged:
  Group Nodes requires 2+ selected nodes, Ungroup Nodes requires
  exactly 1 selected group node

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add Pivot Point Editing (P key) — Phase 1, item 4

Three pivot modes for the transform gizmo: Center (default), Bottom
(floor level), and Origin (node local origin). Rotation and scale
operate around the active pivot point.

Core:
- TransformOperator: PivotMode enum, getPivotPoint() computes pivot
  based on mode and selection bounding box, QSettings persistence
- P key cycles through modes; QML Inspector shows 3 toggle buttons
- PropertiesPanelController: Q_PROPERTY bridge for pivot mode
- MCP tools: set_pivot_mode, get_pivot_mode
- Sentry breadcrumbs for mode changes

Part of #256 (Phase 1: Scene Editing Power Tools)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add Undo History Panel — Phase 1, item 9

Collapsible "Undo History" section in the Inspector panel showing the
full QUndoStack. Click any entry to jump to that state.

- PropertiesPanelController: undoHistory property (QVariantList of
  {text, isCurrent}), undoIndex, undoToIndex(int), clearUndoHistory()
- QML: ListView with highlighted current position, dimmed redo zone,
  "Initial State" entry, hover effects, "Clear History" button
- Sentry breadcrumbs for history jumps and clear

Part of #256 (Phase 1: Scene Editing Power Tools)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add Drag-and-Drop Node Reparenting — Phase 1, item 2

Drag scene nodes in the tree to reparent them under other nodes.
World-space transforms are preserved by recomputing local transforms.

Core:
- Manager::reparentNode(): removes from old parent, adds to new parent,
  recalculates local transform to preserve world position. Validates
  against cycles (node into own subtree).
- Manager::isDescendantOf(): static cycle detection helper
- ReparentCommand: stores old/new parent names and local transforms
  for full undo/redo
- SceneTreeModel: canReparent() validation, reparentNode() with undo
- SceneTreeNode.qml: Drag/DropArea on Node and Group items, drag proxy
  label, blue highlight on valid drop targets
- PropertiesPanel.qml: "Scene (Root)" drop zone for reparenting to root
- MCP tool: reparent_node (node_name, new_parent_name)
- Sentry breadcrumbs

Part of #256 (Phase 1: Scene Editing Power Tools)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix drag-and-drop reparenting: use shared property instead of mime data

QML Drag.Internal mode doesn't pass mimeData to DropArea handlers.
Replaced with a draggedNodeName property on PropertiesPanelController
that's set on mouse press and read by DropAreas on enter/drop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Replace broken QML drag-and-drop with button-based reparenting

QML Drag/DropArea in QQuickWidget inside QDockWidget doesn't deliver
events properly across recursive tree components. Replaced with:
- "↑ to Root" button next to Scene (Root) header, visible when a
  nested node is selected
- Grouping (Ctrl+G) for creating parent-child relationships
- MCP reparent_node tool for programmatic access

Removed all Drag/DropArea code from SceneTreeNode.qml and the root
drop zone from PropertiesPanel.qml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Auto-delete empty group nodes after reparenting

When the last child is moved out of a group via reparentNode(), the
empty parent is now automatically destroyed instead of persisting as
an orphaned "Node" in the scene tree.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Cascade-delete empty group ancestors after reparenting

When moving the last child out of a nested group, now walks up the
parent chain and destroys ALL empty ancestors (not just the immediate
parent). Fixes nested groups leaving orphaned empty nodes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Bump version to 2.23.0, add tests for Phase 1 remaining MCP tools

- Version 2.22.0 → 2.23.0
- 14 new tests: group_nodes, ungroup_node, reparent_node,
  set/get_pivot_mode, resample_animation (valid + error cases)
- AllToolNamesAreRecognized updated to 51 tools
- Winget fix already included (gh CLI approach from PR #254)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai coderabbitai Bot mentioned this pull request Apr 17, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant