Skip to content

Publish installers to current repo for cross-repo tag releases; mark branch releases as pre-releases - #478

Merged
shannah merged 5 commits into
masterfrom
claude/jdeploy-release-artifacts-oe9gll
Sep 8, 2026
Merged

Publish installers to current repo for cross-repo tag releases; mark branch releases as pre-releases#478
shannah merged 5 commits into
masterfrom
claude/jdeploy-release-artifacts-oe9gll

Conversation

@shannah

@shannah shannah commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Summary

Two fixes to the GitHub action's release publishing when target_repository differs from the repository running the workflow (the private-repo → public-repo setup).

1. Tag releases now also get installers in the current repository

Previously, when target_repository was a different repository, all release artifacts went there and nothing was added to the current repository's release. For versioned (tag) releases we already have a release in the current repo, so it now also receives:

  • A copy of the installer artifacts only — the remaining release files (platform bundles, package-info.json, icons, jdeploy-files.zip, npm tarball) stay in the target repository, which remains the publication channel.
  • A release body with download links pointing at the current repository, injected into the existing body via the same <!-- JDEPLOY BEGIN --> / <!-- JDEPLOY END --> markers used by the same-repo path.

The CLI installation links in that body keep pointing at target_repository, since that is where the package metadata (package-info.json) and the jdeploy.com download page live.

Branch releases are unchanged — they still publish only to the target repository.

2. Branch releases in the target repository are marked as pre-releases

The cross-repo path used gh release create with no --prerelease, so a branch snapshot was promoted to "Latest" in the target repository. The same-repo path already passes prerelease: true. Branch builds now pass --prerelease; tag builds still create full releases.

Changes

action.yml only (plus a README line). No Java changes — see below.

  • Upload files to Github Release in Target Repository (renamed from "...Snapshot Release for Branch", since it also handles tags): adds --prerelease when github.ref_type == 'branch'.
  • New step Upload Installers to Github Release for Tag in Current Repository: adds the installers to the release that already exists for the tag, via gh release upload --clobber.
  • New step Update release body for Tag in Current Repository: rewrites the download links to this repository and PATCHes the release body.

Both new steps are gated on target_repository != github.repository && ref_type == 'tag', so they cannot affect single-repo workflows.

Three things live testing caught

This started out larger and got corrected twice by real cross-repo deploys. Recording it here since the reasoning matters more than the diff.

1. It depended on CLI commands that ship separately from the action. The first version added two jdeploy subcommands (github-installer-files, github-release-notes) and called them. But action.yml is consumed from a git ref while the CLI comes from npm at whatever version the caller pins — they're versioned independently, so the steps failed for any jdeploy_version predating the commands, including this action's own default. An unknown subcommand prints help and exits 1, and because the step captured stdout in a command substitution, it failed with no error message at all.

Both jobs are now done with what every jdeploy version already provides:

  • Installer list — derived from the jdeploy/installers directory that github-prepare-release writes, mapped to the release-file names it copies them to (spaces replaced by dots), with a fallback to the unmodified name.
  • Download links — a literal bash substitution (${NOTES//"$from"/"$to"}, deliberately not a regex, so a repository name containing a dot cannot match the wrong text) on the https://github.com/<target>/releases/download/ prefix in the already-generated notes. The CLI install links are jdeploy.com/gh/<repo> URLs, so the rewrite leaves them on the target repository, which is what we want.

The two new subcommands and the GithubReleaseNotesMutator refactor became unused and were reverted, which is why this PR touches no Java.

2. It used the wrong token. Both steps used inputs.github_token, which is the credential for the target repository. When the target is a different repository that token generally cannot write to the current one — and GitHub answers an unauthorized write with 404, not 403, so it surfaced as a baffling HTTP 404: Not Found on /repos/<current>/releases. They now use ${{ github.token }}, the calling workflow's own token, which is the right credential for the caller's own release.

3. It tried to create a release it should only add to. A tag build runs against a release that already exists here, so the step only ever uploads assets now. The create fallback was reached solely because the wrong token made the preceding gh release view 404, turning a permissions error into a misleading one — and creating a release in the caller's own repository is a surprising side effect (the target repo's release is jdeploy-managed and gets deleted/recreated; the caller's is theirs).

Both steps are also non-fatal now. The installers are already published to the target repository — the actual distribution channel — by the time these run, so a missing permission produces a warning annotation naming what's needed rather than failing an otherwise-successful release.

Testing

  • Verified the installer selection and the link rewrite against a fixture mirroring a real run's exact artifact set: installers selected (including the legacy -win-/-linux- duplicates), and no package-info.json, package.json, icon.png, jdeploy-files.zip, or .tgz bundle. The rewrite moved the download links to the current repository and left the jdeploy.com/gh/<target> link untouched.
  • Test Action Scripts static suite: 10/10 passing locally and in CI.
  • action.yml validated as YAML.
  • No Java changed, so the shared/cli suites are identical to master.

Still unverified

The cross-repo path can't be exercised from CI here — it needs a real private/public pair. Failures 2 and 3 above were both found that way, so the token/permission seam is worth one more trial deploy before this is relied on. If the permission is wrong the step should now say so in a warning rather than failing the release.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KQuAnJXfX5ZvXTgnHWdPqs

When target_repository points at a different repository (typically a public
repo fronting a private one), tag releases now also get a copy of the
installer artifacts, plus a release body with download links, in the
repository running the workflow, which already has a release for the tag.
Only the installers are copied; the remaining release files (platform
bundles, package-info.json, icons, etc..) stay in the target repository,
which remains the publication channel. The CLI installation links in the
copied release notes continue to point at the target repository, since that
is where the package metadata lives.

Also mark branch releases in the target repository as pre-releases. They
were being promoted to "Latest" because `gh release create` defaults to a
full release, unlike the same-repo path which already passes prerelease.

Adds two CLI commands used by the action:
- github-installer-files: lists the installer artifacts in the prepared
  release files directory, one path per line.
- github-release-notes: regenerates the release notes, optionally pointing
  the download links at a different repository than the CLI install links
  via JDEPLOY_RELEASE_NOTES_DOWNLOAD_REPOSITORY.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KQuAnJXfX5ZvXTgnHWdPqs
The two steps added for cross-repo tag releases called `jdeploy
github-installer-files` and `jdeploy github-release-notes`, which exist
only in this branch. action.yml is consumed from a git ref while the CLI
comes from npm at the version the caller pins, so those steps failed for
any jdeploy_version predating the commands - including the action's own
default. An unknown subcommand prints help and exits 1, and with the
output captured by a command substitution the step failed with no
message at all.

Derive the installer list from the jdeploy/installers directory that
github-prepare-release already writes, mapped to the release file names
it copies them to, and rewrite the download-link prefix in the generated
release notes with a literal bash substitution. Both work with every
jdeploy version, so the action no longer depends on CLI features that
ship separately from it.

Reverts the github-installer-files and github-release-notes commands and
the GithubReleaseNotesMutator refactor, which are now unused.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KQuAnJXfX5ZvXTgnHWdPqs
The two steps that copy installers into the current repository's release
used inputs.github_token, which is the credential for the target
repository. When the target is a different repository that token
generally cannot write here, and GitHub answers an unauthorized write
with 404 rather than 403, so it surfaced as "HTTP 404: Not Found" on the
releases endpoint.

Use github.token, the calling workflow's own token, for both steps.

Also stop failing the release when that copy does not go through. The
installers are already published to the target repository, which is the
distribution channel, so a missing permission here is reported as a
warning annotation naming what is needed rather than failing an
otherwise successful release. The body update skips with a warning when
no release exists to update.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KQuAnJXfX5ZvXTgnHWdPqs
A tag build runs against a release that already exists in this
repository, so the step only ever adds assets to it. The create fallback
was reached only because the wrong token made the preceding lookup fail,
turning a permissions error into a confusing one, and creating a release
in the caller's own repository is a surprising side effect. Report and
skip instead when the release cannot be read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KQuAnJXfX5ZvXTgnHWdPqs
@shannah
shannah marked this pull request as ready for review September 8, 2026 19:21
@shannah
shannah merged commit 420d17d into master Sep 8, 2026
20 checks passed
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