Skip to content

fix(api): skip stable tags whose GitHub Release is not yet published#277

Open
hydai wants to merge 1 commit into
masterfrom
fix/latest-release-skip-unpublished-tags
Open

fix(api): skip stable tags whose GitHub Release is not yet published#277
hydai wants to merge 1 commit into
masterfrom
fix/latest-release-skip-unpublished-tags

Conversation

@hydai

@hydai hydai commented May 8, 2026

Copy link
Copy Markdown
Member

Summary

Fixes latest_release() to return the latest stable version whose install assets are actually uploaded to the release CDN, not just the latest stable git tag. The two diverge during release-prep windows when a tag is pushed but its archives are still being built / a draft Release exists without published assets.

Root cause

WasmEdge releases happen in two phases:

Phase What happens Visible to
1. Tag push Maintainers push a tag like 0.16.3 to mark a revision and trigger CI. git ls-remote immediately.
2. Release publish CI builds Linux/macOS/Windows archives + the SHA256SUM digest, then a GitHub Release goes live with assets attached. The Release may be a draft during the upload window. releases/tags/<tag> REST endpoint and asset CDN URLs.

latest_release() was reading from phase 1 (releases::get_allgit ls-remote). Whenever phase 2 hadn't completed for the highest tag, callers that subsequently tried to download anything (get_release_checksum, wasmedgeup install latest, the <- latest marker in wasmedgeup list --remote) would fail with ChecksumNotFound.

This bug is independent of the recent gix migration — the previous git2::Remote::list path had the same conflation between "latest tag" and "latest installable release."

Fix: HEAD the CDN, not the API

latest_release() now walks sorted stable candidates newest-first and accepts the first one whose SHA256SUM file is reachable via HEAD <WASMEDGE_RELEASE_BASE_URL>/<tag>/SHA256SUM. A 404 → fall through to the next candidate; other non-2xx → propagate.

Why HEAD-on-CDN rather than GET /repos/.../releases/tags/<tag>: the REST endpoint is gated by GitHub's 60-req/hour unauthenticated rate limit. CI runners share IPs and our other tests already hit that API, so the publication check would land 403 — surfacing as a misleading "release publication check" error rather than as "not published yet." The release-download URL is served by GitHub's CDN with no API rate limit, so the check is both cheaper and more reliable. It also handles the draft case naturally: a draft Release exposes no asset URLs to the public CDN, so the HEAD 404s the same way it does for a tag with no Release at all.

A first iteration of this PR went through the API; CI on macOS-beta hit the rate-limit path and failed with 403. Switched to the HEAD-on-CDN approach in 881be04.

Other changes

  • New private helper is_release_published(tag) keeps the 404-vs-error distinction in one place.
  • tests/install_test.rs::test_install_latest_version previously re-implemented the same buggy "first stable tag" logic locally with releases::get_all(...)[0]. Switched it to pass the literal "latest" so the install command's standard resolve_version path drives the test — exercising the corrected latest_release() end-to-end.

Test plan

  • cargo fmt --all --check clean.
  • cargo clippy --all --all-features --tests -- -D warnings clean.
  • cargo test --tests — 87 pass. Before the fix, test_get_release_checksum and test_install_latest_version both failed because latest_release() returned the in-progress 0.16.3 tag (whose Release was a draft with no uploaded assets).
  • Confirmed against the live remote: HEAD https://github.com/WasmEdge/WasmEdge/releases/download/0.16.3/SHA256SUM → 404; HEAD .../releases/download/0.16.2/SHA256SUM → 302 → 200. With the fix, latest_release() returns 0.16.2.
  • CI pass on the rebased PR — particularly the macOS-beta Test job that previously hit the 60/hr API rate limit.

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

Super-linter summary

Language Validation result
GITLEAKS Pass ✅
GIT_COMMITLINT Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@hydai hydai force-pushed the fix/latest-release-skip-unpublished-tags branch from 5e11420 to 881be04 Compare May 8, 2026 15:04
@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

Super-linter summary

Language Validation result
GITLEAKS Pass ✅
GIT_COMMITLINT Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@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: 881be04f73

ℹ️ 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 src/api/mod.rs Outdated
@hydai hydai force-pushed the fix/latest-release-skip-unpublished-tags branch from 881be04 to a11b1c3 Compare May 8, 2026 15:22
@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

Super-linter summary

Language Validation result
GITLEAKS Pass ✅
GIT_COMMITLINT Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@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: a11b1c348d

ℹ️ 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 src/api/mod.rs Outdated
@hydai hydai force-pushed the fix/latest-release-skip-unpublished-tags branch from a11b1c3 to b888478 Compare May 8, 2026 15:30
@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

Super-linter summary

Language Validation result
GITLEAKS Pass ✅
GIT_COMMITLINT Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

Copilot AI 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.

Pull request overview

This PR fixes how latest_release() determines the “latest” stable WasmEdge version by skipping stable git tags that don’t yet have published release assets on the GitHub CDN (e.g., during the tag-push / draft-release window), preventing downstream install/checksum operations from failing on missing SHA256SUM.

Changes:

  • Update latest_release() to iterate stable tags newest-first and pick the first tag whose SHA256SUM is reachable via CDN (HEAD with GET fallback).
  • Add a private helper is_release_published(tag) encapsulating the publication check and its 404-vs-error semantics.
  • Update test_install_latest_version to exercise the real "latest" resolution path end-to-end.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/api/mod.rs Implements CDN-based “published assets” detection and updates latest_release() selection logic.
tests/install_test.rs Adjusts integration test to install "latest" via the standard resolution path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/api/mod.rs
Comment thread src/api/mod.rs
@hydai hydai force-pushed the fix/latest-release-skip-unpublished-tags branch from b888478 to 4dec523 Compare May 8, 2026 15:55
@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

Super-linter summary

Language Validation result
GITLEAKS Pass ✅
GIT_COMMITLINT Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment thread src/error.rs
Comment thread src/api/mod.rs Outdated
Comment thread src/api/mod.rs
@hydai hydai force-pushed the fix/latest-release-skip-unpublished-tags branch from 4dec523 to 07a93db Compare May 8, 2026 16:10
@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

Super-linter summary

Language Validation result
GITLEAKS Pass ✅
GIT_COMMITLINT Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@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: 07a93db568

ℹ️ 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 src/api/mod.rs Outdated
`latest_release()` was returning whichever stable-formatted git tag had
the highest semver, regardless of whether the corresponding GitHub
Release had its install assets uploaded.

That worked when WasmEdge's tag-push and release-publish steps
happened atomically, but the actual release flow has two phases:

  1. Maintainers push a tag (e.g. 0.16.3) to mark the revision and
     trigger CI to start building cross-platform archives.
  2. CI builds the archives plus the SHA256SUM digest file and
     uploads them; then the GitHub Release goes live with assets
     attached. The Release may be a *draft* during the upload window.

Between phases 1 and 2 the tag exists in `git ls-remote` but the
SHA256SUM file 404s. During that window `latest_release()` would
report the in-progress tag as latest, and any caller that tried to
download an asset (`get_release_checksum`,
`wasmedgeup install latest`) would fail with `ChecksumNotFound`.

This is independent of the recent gix migration — the previous
`git2::Remote::list` path had the same logical conflation between
"latest tag" and "latest installable release" and would fail the
same way.

Why HEAD the CDN file instead of the GitHub REST API
----------------------------------------------------
A first attempt at this fix used `GET
/repos/WasmEdge/WasmEdge/releases/tags/<tag>` to determine
publication. That endpoint is gated by GitHub's 60-req/hour
unauthenticated rate limit, which CI runners on shared IPs hit
easily — manifesting as a 403 that gets reported as an unrelated
error instead of as "not published yet."

The release-download URL on github.com is served by GitHub's CDN
with no API rate limit, so the check is both cheaper and more
reliable. It also incidentally handles the draft case: a draft
Release exposes no asset URLs to the public CDN, so the HEAD
naturally 404s.

Changes
-------
- `WasmEdgeApiClient::latest_release` now walks the sorted stable
  candidates newest-first and returns the first one whose SHA256SUM
  file is reachable via HEAD on
  `<WASMEDGE_RELEASE_BASE_URL>/<tag>/SHA256SUM`. A 404 means the
  tag's install assets have not been uploaded — fall through to the
  next candidate. Other non-2xx statuses propagate so transient
  outages surface as errors rather than as misleading
  `NoReleasesFound`.
- New private helper `is_release_published(tag)` keeps the
  404-vs-error distinction in one place.
- `tests/install_test.rs::test_install_latest_version` previously
  re-implemented the buggy "first stable tag" logic with
  `releases::get_all(...)[0]`. Switched it to pass the literal
  `"latest"` so the install command's standard `resolve_version`
  path drives the test — exercising the corrected
  `latest_release()` end-to-end.

Verification
------------
- `cargo test --tests` — 87 pass. Before this change,
  `test_get_release_checksum` and `test_install_latest_version`
  both failed because `latest_release()` was returning the
  in-progress `0.16.3` tag (Release exists as draft, SHA256SUM not
  yet uploaded).
- Confirmed against the live remote at the time of this commit:
  `HEAD https://github.com/WasmEdge/WasmEdge/releases/download/0.16.3/SHA256SUM`
  returns 404; the same HEAD on `0.16.2/SHA256SUM` returns 302→200.
  After the fix, `latest_release()` returns `0.16.2`.

Signed-off-by: hydai <z54981220@gmail.com>
@hydai hydai force-pushed the fix/latest-release-skip-unpublished-tags branch from 07a93db to 5f5af3d Compare May 8, 2026 16:17
@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

Super-linter summary

Language Validation result
GITLEAKS Pass ✅
GIT_COMMITLINT Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread src/api/mod.rs
// candidate would pay full TCP+TLS handshake cost.
let client = self.http_client()?;
for candidate in candidates {
if Self::is_release_published(&client, &candidate.to_string()).await? {
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