Skip to content

fix: Package-and-Upload workflow attaches binaries to UI-created releases - #459

Open
jacalata wants to merge 6 commits into
developmentfrom
jac/fix-package-workflow-upload
Open

jacalata wants to merge 6 commits into
developmentfrom
jac/fix-package-workflow-upload

Conversation

@jacalata

@jacalata jacalata commented Aug 14, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Investigating why the v2.0.21 release page shipped with no executables attached, we found two bugs in the Package-and-Upload workflow. v2.0.19 had the same failure mode for the same reason.

Behavior change

Before: the "Upload binaries to release" step was gated on

if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')

Releases created via the GitHub UI create a tag but don't fire the push:tags: event that this workflow depends on. So the workflow only ran manually via workflow_dispatch afterward, built the binaries as workflow artifacts, and skipped the upload-to-release step because the if: guard requires push. The binaries were sitting in the artifact store for 90 days, never on the release.

Separately, both macOS build jobs used name: tabcmd-macos on actions/upload-artifact, so the two same-named artifacts collided in the artifact store and gh run download clobbered one with the other.

After:

  • New release_tag workflow_dispatch input. Set it when dispatching from a branch to attach binaries to a UI-created release. Falls back to github.ref_name when dispatched at a tag ref.
  • Mac artifact collision fixed: matrix.UPLOAD_FILE_NAME (unique per platform) is now the artifact name instead of tabcmd-${{ matrix.TARGET }}.
  • Upload logic split into a new upload_to_release job that depends on buildexe, gated on environment: release. Anyone with dispatch access can trigger a build, but only an approved reviewer can attach binaries to a public release. Mirrors the existing pypi environment gate on this repo (PR security: add environment gate to production PyPI publish job #439).
  • release-on-merge.yml no longer dispatches package.yml. Merging to main only pushes the tag and creates the draft release; publishing it starts the build. The merge-time run targeted a draft, which upload-release-action can't find by tag, and every release needed two release approvals.

Prerequisite before merging

The release environment already exists in this repo's Settings -> Environments with required-reviewer protection. Confirm it's still configured before merging; if it isn't, the upload_to_release job will run unguarded.

Test plan

  • Live-verified today via gh release upload v2.0.21 ... (with the artifacts downloaded from the last workflow_dispatch run) that the platform binaries were the right ones and could be attached to v2.0.21. The v2.0.21 release page now shows all four assets, retroactively fixed.
  • End-to-end verification after merge: for v2.0.22 or a test tag, publish the draft release that the merge creates, or dispatch with --ref <tag> for an already-published release; confirm the approval gate fires and the four binaries end up on the release page.
  • Code review from code-reviewer agent — no blocking findings; added inline comments for the download-artifact@v7 choice (it versions independently of upload-artifact@v7 and reads its artifacts) and the needs: buildexe all-matrix-legs-must-succeed semantics.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Aug 14, 2026 •

Copy link
Copy Markdown

Coverage

Coverage Report
FileStmtsMissCoverMissing
tabcmd
   __main__.py121212 0%
   _version.py111111 0%
   tabcmd.py151515 0%
   version.py1066 40%
tabcmd/commands
   commands.py101010 0%
   constants.py771818 77%
   server.py1351818 87%
tabcmd/commands/auth
   session.py3945050 87%
tabcmd/commands/datasources_and_workbooks
   datasources_and_workbooks_command.py1571818 89%
   datasources_workbooks_views_url_parser.py14255 96%
   delete_command.py702323 67%
   export_command.py1202525 79%
   get_url_command.py1274747 63%
   publish_command.py1341616 88%
   runschedule_command.py2177 67%
tabcmd/commands/extracts
   create_extracts_command.py4288 81%
   decrypt_extracts_command.py2722 93%
   delete_extracts_command.py3766 84%
   encrypt_extracts_command.py2722 93%
   extracts.py2022 90%
   reencrypt_extracts_command.py2722 93%
   refresh_extracts_command.py481010 79%
tabcmd/commands/group
   create_group_command.py2955 83%
   delete_group_command.py2722 93%
tabcmd/commands/project
   create_project_command.py4688 83%
   delete_project_command.py3544 89%
   publish_samples_command.py3044 87%
tabcmd/commands/site
   create_site_command.py3455 85%
   delete_site_command.py2722 93%
   edit_site_command.py661818 73%
   list_command.py771212 84%
   list_sites_command.py2922 93%
tabcmd/commands/user
   add_users_command.py2955 83%
   create_site_users.py581111 81%
   create_users_command.py5999 85%
   delete_site_users_command.py4355 88%
   user_data.py2223131 86%
tabcmd/execution
   _version.py222 0%
   global_options.py14788 95%
   localize.py671111 84%
   logger_config.py6066 90%
   tabcmd_controller.py4277 83%
TOTAL294647084% 

Copilot AI lite review requested due to automatic review settings September 10, 2026 23:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Restore compatible dispatch behavior and resolve the release tag across all supported workflow paths.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Updates the packaging workflow to build platform binaries and upload them to releases through a reviewer-gated job.

Changes:

  • Adds unique artifact names to prevent macOS collisions.
  • Separates release uploads into a gated job.
  • Updates release-trigger and artifact-upload handling.

Review findings:

  • Moderate (3 votes): Removing workflow_dispatch breaks the existing release-on-merge caller and manual repair path.
  • Moderate (1 vote): Hard-coded github.event.release.tag_name is empty for dispatch and tag-ref workflows.
File summaries
File Description
.github/workflows/package.yml Updates workflow triggers, artifact naming, and release uploads.
Review details

Suppressed comments (1)

.github/workflows/package.yml:142

  • The upload job hard-codes github.event.release.tag_name, which is populated for the release event but not for the workflow-dispatch or tag-ref paths described in the PR. In those paths the upload action receives an empty target tag and cannot attach the artifacts to the release. Resolve a validated target tag from the dispatch input, release payload, or tag ref and reuse it for all uploads (and the concurrency group).
        tag: ${{ github.event.release.tag_name }}
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/package.yml
jacalata and others added 3 commits September 18, 2026 18:10
…ases

Two bugs found while investigating why v2.0.21 shipped without
executables attached:

1. The "Upload binaries to release" step was gated on
   `github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')`.
   Releases created via the GitHub UI create a tag but do not fire the
   push:tags: event that this workflow depends on, so v2.0.21 (and v2.0.19)
   ended up as releases with no assets. The only successful run for
   v2.0.21 was a workflow_dispatch afterward that built the binaries as
   artifacts but skipped the upload-to-release step.
2. Both macOS build jobs used `name: tabcmd-macos` on
   `actions/upload-artifact`, so the two same-named artifacts collided in
   the artifact store and downloads clobbered each other.

Fixes:

- New `release_tag` workflow_dispatch input. Set it when dispatching from
  a branch to attach binaries to a UI-created release. Falls back to
  `github.ref_name` when the workflow runs on a tag ref (push or dispatch).
- Fix mac artifact collision: use `matrix.UPLOAD_FILE_NAME` (unique per
  platform) as the artifact name instead of `tabcmd-${{ matrix.TARGET }}`.
- Split the upload into a separate `upload_to_release` job that depends on
  `buildexe`, gated on `environment: release`. The `release` environment
  needs to be created in the repo's Settings -> Environments with
  required-reviewer protection (mirroring the existing `pypi` environment).
  Anyone with dispatch access can trigger a build, but only an approved
  reviewer can attach binaries to a public release.
- Upload gate: `if: startsWith(github.ref, 'refs/tags/') || inputs.release_tag != ''`
  so both the push:tags path and the workflow_dispatch path work.

Live-verified by using `gh release upload` today to fix v2.0.21
retroactively with the artifacts from the last workflow_dispatch run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Switch to release: types: [published] as the trigger (matches
  publish-pypi.yml pattern). Removes the manual-dispatch-with-input
  footgun; release payload provides the tag directly.
- Add explicit permissions: contents: write on the upload job so it
  survives future org-default hardening.
- Add concurrency group keyed on the release tag; prevents overlapping
  runs from stomping each other's approved uploads.
- Pin download-artifact back to v7 to match upload-artifact@v7 until
  v7/v8 interop is verified.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…vent paths

Copilot flagged that removing workflow_dispatch broke the
release-on-merge.yml caller and the documented manual re-attach
path, and that github.event.release.tag_name is empty for anything
other than release:published events.

Restores workflow_dispatch alongside release:published, adds a
"Resolve release tag" step that falls back to github.ref_name, and
switches all four uploaders to the resolved output.
@jacalata
jacalata force-pushed the jac/fix-package-workflow-upload branch from ca9d74b to 4990684 Compare September 19, 2026 01:16
Copilot AI review requested due to automatic review settings September 19, 2026 01:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The workflow lacks the described release_tag workflow_dispatch input and currently resolves tags in a way that can mis-target uploads or unnecessarily block on environment approval for branch-dispatched runs.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 2 Medium severity

Open (2)
Resolved since last review (1)

Comment thread .github/workflows/package.yml Outdated
Comment thread .github/workflows/package.yml
…e-attach

Adds a Publishing-a-release subsection covering the release:published
path fired by publishing the draft release in the UI, the new
`environment: release` reviewer-approval gate that pauses upload_to_release
until a maintainer clicks "Review deployments", and the manual re-attach
command `gh workflow run package.yml --ref <tag>` for existing releases.

Requested during Copilot review of #459.
Copilot AI review requested due to automatic review settings September 19, 2026 01:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The workflow_dispatch path is missing the documented/expected release_tag input (and tag resolution can target the wrong tag when dispatched from a branch), and the new documentation incorrectly states the PyPI gate uses the release environment.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 2 Medium severity · 1 Low severity

Open (3)

Comment thread contributing.md Outdated
… dispatch without one

Address Copilot review on #459: define the release_tag workflow_dispatch
input, include it in the concurrency group, and resolve the upload tag from
the release event, then release_tag, then ref_name only for tag refs. A branch
dispatch with no release_tag now errors instead of using the branch name.
Correct contributing.md to say publish-pypi.yml is gated on the pypi
environment, and document the branch-dispatch form.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings October 23, 2026 20:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Unresolved workflow trigger, artifact-upload sequencing, and contradictory documentation issues remain.

No open issues 🎉

3 previously missed issues

In code that hasn't changed since last review

Medium severity Restore the workflow's tag push trigger

.github/​workflows/​package.yml:17

The workflow no longer has a push trigger, so pushing a tag cannot start this workflow at all. That makes the documented/declared github.ref_name fallback for a tag push unreachable and contradicts the test plan's “either push a tag” path. Retain the tag push trigger (the existing GITHUB_TOKEN tag push from release-on-merge will still be covered by the explicit dispatch), or remove the push path from the comments and release instructions.

Medium severity Use download-artifact v8 for v7 uploads

.github/​workflows/​package.yml:139

This job uploads artifacts with actions/upload-artifact@v7, but the corresponding current download action is actions/download-artifact@v8 (the upload action has no v8 release). Pinning the downloader to v7 leaves this new upload path on the wrong major and can prevent the job from resolving/downloading the build artifacts; use the v8 downloader here.

Low severity Remove or update the conflicting manual release procedure

contributing.md:198

This new procedure leaves the ## Release process section immediately below unchanged, where it still tells maintainers to create releases manually and copy artifacts from Actions. That directly contradicts the merge/publish flow and reviewer-gated automatic attachment documented here, so following this file can still lead to manual uploads; update or remove that legacy procedure as part of this change.

3 resolved since last review

Review effort: Lite

Drop the package.yml dispatch from release-on-merge.yml. The merge-time run
targeted a draft release, which upload-release-action cannot find by tag, and
each release needed two `release` approvals. Merging to main now only pushes
the tag and creates the draft; publishing it starts package.yml and
publish-pypi.yml.

Also rewrite the legacy release-process section of contributing.md and
correct the trigger and download-artifact comments in package.yml.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings October 26, 2026 21:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Critical security issues remain around build-job token permissions and the ability to modify the workflow before dispatch.

2 open issues

Review effort: Lite

Comment on lines 21 to +26
workflow_dispatch:
inputs:
release_tag:
description: 'Release tag to attach binaries to. Required when dispatching from a branch rather than a tag ref.'
required: false
type: string
Comment on lines 34 to +35
buildexe:
name: Build executables and upload them to the existing release
name: Build executables

This branch has not been deployed

No deployments
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.

2 participants