Skip to content

Update self-update flow to use CDN release metadata#123

Merged
F16shen merged 1 commit intoAI-Shell-Team:mainfrom
F16shen:fix/update-command-cdn-source
Apr 22, 2026
Merged

Update self-update flow to use CDN release metadata#123
F16shen merged 1 commit intoAI-Shell-Team:mainfrom
F16shen:fix/update-command-cdn-source

Conversation

@F16shen
Copy link
Copy Markdown
Collaborator

@F16shen F16shen commented Apr 22, 2026

Summary

Switch the stable aish update flow to the CDN release metadata and download path used by the installer.

What changed

  • Read the stable latest version from the CDN latest endpoint instead of the GitHub latest release API.
  • Build update bundle download URLs from the CDN base URL instead of GitHub release tag paths.
  • Honor AISH_DOWNLOAD_BASE_URL and AISH_LATEST_URL, while keeping AISH_REPO_URL as a compatibility alias.
  • Update the update manager tests to cover latest metadata parsing, CDN download URLs, and download base overrides.

Why

The installer now resolves releases from the CDN, so the update command needs to follow the same distribution path. This keeps install and update behavior aligned and avoids depending on the old GitHub release download layout for stable updates.

Notes

  • Stable updates now use CDN metadata and CDN bundle downloads.
  • The pre-release lookup path still uses the GitHub releases list because GitHub's latest endpoint excludes prereleases.

Validation

  • /home/lixin/workspace/aishell/aish/.venv/bin/python -m pytest tests/test_update_manager.py

Summary by CodeRabbit

Release Notes

  • New Features

    • Update system now supports configuration via environment variables (AISH_DOWNLOAD_BASE_URL, AISH_REPO_URL, AISH_LATEST_URL) to customize CDN and metadata sources.
  • Chores

    • Simplified update download process; releases now download directly from configured CDN without fallback mechanisms.

Switch stable update checks from the GitHub latest release API to the CDN latest metadata endpoint so the update command follows the new release distribution path used by the installer.

Update archive downloads to use the CDN bundle base URL and honor AISH_DOWNLOAD_BASE_URL, AISH_LATEST_URL, and the legacy AISH_REPO_URL override for compatibility with existing deployment setups.

Refresh update manager tests to cover stable latest metadata parsing, CDN download URLs, and environment-based download base overrides.

Validation: /home/lixin/workspace/aishell/aish/.venv/bin/python -m pytest tests/test_update_manager.py
@github-actions
Copy link
Copy Markdown
Contributor

Thanks for the pull request. A maintainer will review it when available.

Please keep the PR focused, explain the why in the description, and make sure local checks pass before requesting review.

Contribution guide: https://github.com/AI-Shell-Team/aish/blob/main/CONTRIBUTING.md

@github-actions
Copy link
Copy Markdown
Contributor

This pull request description looks incomplete. Please update the missing sections below before review.

Missing items:

  • User-visible Changes
  • Compatibility
  • Testing
  • Change Type
  • Scope

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

📝 Walkthrough

Walkthrough

The update_manager module transitions from GitHub API-based version checking to configurable CDN-based resolution. Environment variables now control the CDN download base URL and latest version metadata endpoint, replacing hardcoded GitHub constants. Version data is fetched as plain text from CDN rather than GitHub JSON API, with new validation logic introduced.

Changes

Cohort / File(s) Summary
CDN Configuration & Version Resolution
src/aish/cli/update_manager.py
Added get_download_base_url() and get_latest_version_url() methods for environment-driven URL resolution; introduced VERSION_PATTERN regex and normalize_tag() static method for version validation; updated constants (DEFAULT_DOWNLOAD_BASE_URL, removed GITHUB_RELEASES_BASE/FALLBACK_MIRROR).
Release Fetching & Download Logic
src/aish/cli/update_manager.py
Refactored get_latest_release() to fetch plain text from CDN instead of GitHub API JSON, synthesizing response fields; simplified download_release() to use resolved CDN base URL directly without GitHub/mirror fallback logic.
Test Fixtures & Assertions
tests/test_update_manager.py
Replaced GitHub JSON fixtures with plain text CDN metadata ("0.3.0\n"); updated mocks to use .text instead of .json; added test for download base URL environment variable override; adjusted assertions for CDN-based behavior and URL patterns.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 We hail the CDN's embrace so bright,
No GitHub chains to hold us tight!
Environment winds guide our way,
To faster downloads, hip hooray! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: switching the self-update flow to use CDN release metadata instead of GitHub releases for stable updates.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
tests/test_update_manager.py (2)

107-152: ⚠️ Potential issue | 🟠 Major

Make the download tests deterministic across runners.

These assertions hard-code linux-amd64, but download_release() builds the filename from detect_platform(). They will fail on darwin/arm64 runners, and the default-path case can also be skewed by ambient AISH_DOWNLOAD_BASE_URL / AISH_REPO_URL overrides.

Suggested fix pattern
-def test_download_release_success(mock_client_class, update_manager, tmp_path):
+def test_download_release_success(
+    mock_client_class, update_manager, tmp_path, monkeypatch
+):
+    monkeypatch.delenv("AISH_DOWNLOAD_BASE_URL", raising=False)
+    monkeypatch.delenv("AISH_REPO_URL", raising=False)
+
     mock_response = Mock()
     mock_response.raise_for_status = Mock()
     mock_response.iter_bytes = Mock(return_value=[b"test data"])
@@
-    with patch.object(update_manager, "client", mock_client_instance):
+    with (
+        patch.object(update_manager, "client", mock_client_instance),
+        patch.object(update_manager, "detect_platform", return_value=("linux", "amd64")),
+    ):
         result = update_manager.download_release("v0.3.0", dest_dir=tmp_path)

Use the same detect_platform() stub in the override test as well.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_update_manager.py` around lines 107 - 152, The tests hard-code the
platform string causing failures on non-linux-amd64 runners and can be
influenced by ambient env vars; in the test function
test_download_release_respects_download_base_override stub detect_platform() to
return the same deterministic platform used in test_download_release (e.g.,
"linux-amd64"), and ensure AISH_DOWNLOAD_BASE_URL/AISH_REPO_URL are set/cleared
via monkeypatch to known values before calling update_manager.download_release;
keep the existing mocks (mock_client_instance.stream) and assert against the
resulting stream_url as done currently.

38-55: ⚠️ Potential issue | 🟠 Major

Clear AISH_* overrides in the default latest-version tests.

These tests assert the default /latest URL, but get_latest_release() now honors AISH_LATEST_URL, AISH_DOWNLOAD_BASE_URL, and AISH_REPO_URL. If any of those are present in the runner environment, the assertions fail for the wrong reason.

Suggested fix pattern
 def test_get_latest_release_success(
-    mock_client_class, update_manager, mock_latest_version_text
+    mock_client_class, update_manager, mock_latest_version_text, monkeypatch
 ):
+    for var in ("AISH_LATEST_URL", "AISH_DOWNLOAD_BASE_URL", "AISH_REPO_URL"):
+        monkeypatch.delenv(var, raising=False)
+
     mock_response = Mock()
     mock_response.raise_for_status = Mock()
     mock_response.text = mock_latest_version_text

Apply the same setup to the other default-path update-check tests in this block.

Also applies to: 71-100

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_update_manager.py` around lines 38 - 55, The test
test_get_latest_release_success (and the other default-path update-check tests
in the same block) fail when AISH_LATEST_URL, AISH_DOWNLOAD_BASE_URL or
AISH_REPO_URL are set in the environment; update the tests to clear those env
overrides before calling update_manager.get_latest_release() — e.g. use
patch.dict(os.environ, {"AISH_LATEST_URL":"", "AISH_DOWNLOAD_BASE_URL":"",
"AISH_REPO_URL":""}, clear=False) or monkeypatch.delenv for each key so the
function uses its default "/latest" path; apply the same change to the other
tests in the block (lines ~71-100) to ensure the mocked client.get assertion
that URL endswith("/latest") is stable.
src/aish/cli/update_manager.py (1)

135-153: ⚠️ Potential issue | 🟠 Major

Pick the newest version instead of returning releases[0].

When include_pre_release=True, this path now trusts the first entry from GitHub’s releases list. That list can contain both stable and prerelease entries, so --pre-release becomes API-order dependent and can miss a newer beta if an older stable release happens to be first. Parse the tags and select the highest version before returning it.

Suggested fix
             if include_pre_release:
                 # /releases/latest excludes pre-releases, use list endpoint instead
                 response = self.client.get(GITHUB_API_LIST)
                 response.raise_for_status()
                 releases = response.json()
-                if not releases:
-                    return None
-                data = releases[0]
-                tag_name = data.get("tag_name")
-                if not tag_name:
+                candidates = []
+                for release in releases:
+                    tag_name = release.get("tag_name")
+                    if not tag_name or release.get("draft"):
+                        continue
+                    candidates.append((version.parse(tag_name.lstrip("v")), release))
+
+                if not candidates:
                     return None
+
+                _, data = max(candidates, key=lambda item: item[0])
+                tag_name = data["tag_name"]
 
                 return {
                     "tag_name": tag_name,
                     "name": data.get("name"),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/aish/cli/update_manager.py` around lines 135 - 153, The code returning
releases[0] when include_pre_release=True can miss the newest semantic version;
in the method handling the GITHUB_API_LIST response (the branch using
include_pre_release in update_manager.py), iterate all items in the releases
list, extract each data.get("tag_name"), parse versions with
packaging.version.parse (or pkg_resources.parse_version) to compare them, pick
the release with the highest parsed version, and then return that release's
fields ("tag_name","name","body","html_url","assets"); if no tag_name is
parseable, fall back to the existing behavior (e.g., first non-empty tag_name)
to avoid breaking the flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/aish/cli/update_manager.py`:
- Around line 135-153: The code returning releases[0] when
include_pre_release=True can miss the newest semantic version; in the method
handling the GITHUB_API_LIST response (the branch using include_pre_release in
update_manager.py), iterate all items in the releases list, extract each
data.get("tag_name"), parse versions with packaging.version.parse (or
pkg_resources.parse_version) to compare them, pick the release with the highest
parsed version, and then return that release's fields
("tag_name","name","body","html_url","assets"); if no tag_name is parseable,
fall back to the existing behavior (e.g., first non-empty tag_name) to avoid
breaking the flow.

In `@tests/test_update_manager.py`:
- Around line 107-152: The tests hard-code the platform string causing failures
on non-linux-amd64 runners and can be influenced by ambient env vars; in the
test function test_download_release_respects_download_base_override stub
detect_platform() to return the same deterministic platform used in
test_download_release (e.g., "linux-amd64"), and ensure
AISH_DOWNLOAD_BASE_URL/AISH_REPO_URL are set/cleared via monkeypatch to known
values before calling update_manager.download_release; keep the existing mocks
(mock_client_instance.stream) and assert against the resulting stream_url as
done currently.
- Around line 38-55: The test test_get_latest_release_success (and the other
default-path update-check tests in the same block) fail when AISH_LATEST_URL,
AISH_DOWNLOAD_BASE_URL or AISH_REPO_URL are set in the environment; update the
tests to clear those env overrides before calling
update_manager.get_latest_release() — e.g. use patch.dict(os.environ,
{"AISH_LATEST_URL":"", "AISH_DOWNLOAD_BASE_URL":"", "AISH_REPO_URL":""},
clear=False) or monkeypatch.delenv for each key so the function uses its default
"/latest" path; apply the same change to the other tests in the block (lines
~71-100) to ensure the mocked client.get assertion that URL endswith("/latest")
is stable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 56fde727-fe1e-4e36-88e2-3e10a08f4c55

📥 Commits

Reviewing files that changed from the base of the PR and between 16e38df and d12de98.

📒 Files selected for processing (2)
  • src/aish/cli/update_manager.py
  • tests/test_update_manager.py

@F16shen F16shen merged commit e560955 into AI-Shell-Team:main Apr 22, 2026
12 checks passed
@F16shen F16shen deleted the fix/update-command-cdn-source branch April 23, 2026 06:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant