Skip to content

Commit e4d308e

Browse files
committed
fixed test updates
1 parent 029f3cb commit e4d308e

File tree

5 files changed

+105
-10
lines changed

5 files changed

+105
-10
lines changed

.github/workflows/release.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,9 @@ jobs:
216216
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
217217
GITHUB_REPOSITORY: ${{ github.repository }}
218218

219-
# Test tags (e.g. v1.0.3-test1.1) create a draft release: hidden from public
220-
# Releases list; only visible to maintainers at GitHub Releases when logged in.
219+
# All tags create a published release. Test tags (e.g. v1.0.3-test1.1) are
220+
# marked prerelease so assets are public (no 404); website uses releases/latest
221+
# which returns only non-prerelease, so it points to stable only.
221222
- name: Create GitHub Release
222223
uses: softprops/action-gh-release@v1
223224
with:
@@ -229,7 +230,7 @@ jobs:
229230
THIRD_PARTY_LICENSES.txt
230231
SHA256SUMS
231232
body_path: RELEASE_NOTES.md
232-
draft: ${{ contains(github.ref_name, '-test') }}
233+
draft: false
233234
prerelease: ${{ contains(github.ref_name, '-test') }}
234235
env:
235236
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

docs/release/design-two-appcast-feeds-test-stable.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@
4444
| FR4 | Test tags do not run appcast generation. | Add workflow branch for test tag: generate and publish only `updates/*/test/` appcasts. |
4545
| I3, I4 | Not explicitly enforced; workflow only has one appcast path (stable). | Enforce by: (1) stable steps run only on non-test tag; (2) new test steps run only on test tag; (3) test publish does not pass `--index`. |
4646

47-
### 1.5 Test releases remain drafts (no change)
47+
### 1.5 Test releases: published + prerelease (in-app update works)
4848

49-
- **GitHub Release creation** in the release workflow **does not change**: test tags continue to create **draft** (and prerelease) releases; normal tags create published releases.
50-
- **Rationale:** Draft test releases stay hidden from the public Releases list and from `releases/latest`. The **only** addition is the **test appcast feed** on gh-pages, so that test builds can discover updates in-app. The website and “latest” behavior remain unchanged; test releases are still not offered for download on the site.
49+
- **GitHub Release creation:** Test tags create **published** releases with `prerelease: true` (not draft). This makes release assets publicly downloadable so in-app update (e.g. 0.0.3-test → 0.0.4-test) does not get 404.
50+
- **Website:** The site uses `releases/latest`, which returns the latest **non-prerelease** release, and the download page explicitly skips prereleases. So the website continues to offer only normal (stable) releases for download.
51+
- **Releases page:** Test builds appear in the GitHub Releases list with a Pre-release badge; they are visible but clearly marked.
5152

5253
---
5354

gh-pages-root/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,10 +302,11 @@ <h2>Download</h2>
302302
(function () {
303303
var winEl = document.getElementById('win-dl');
304304
var macEl = document.getElementById('mac-dl');
305+
// GitHub releases/latest returns the latest non-prerelease, non-draft release only.
305306
fetch('https://api.github.com/repos/stuchain/CuePoint/releases/latest')
306307
.then(function (r) { return r.json(); })
307308
.then(function (release) {
308-
if (!release.assets || !release.assets.length) return;
309+
if (release.prerelease || !release.assets || !release.assets.length) return;
309310
release.assets.forEach(function (asset) {
310311
var name = asset.name.toLowerCase();
311312
if (name.endsWith('.exe')) winEl.href = asset.browser_download_url;

src/cuepoint/utils/sentry_init.py

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@
66
77
Initializes the Sentry SDK when a DSN is available (env SENTRY_DSN or
88
default below). Captures unhandled exceptions, ERROR/WARNING logs, and
9-
(when consent is given) sends them to Sentry. Use init_sentry_early()
10-
right after the crash handler so import-time and startup errors are also
11-
captured.
9+
(when consent is given) sends them to Sentry with full analytic context:
10+
stack traces, local variables, source context, breadcrumbs, and build/env
11+
tags. Use init_sentry_early() right after the crash handler so import-time
12+
and startup errors are also captured.
1213
"""
1314

1415
import logging
1516
import os
17+
import platform
18+
import sys
1619
from typing import Callable, Optional
1720

1821
logger = logging.getLogger(__name__)
@@ -39,6 +42,30 @@ def _consent_allowed() -> bool:
3942
return False
4043

4144

45+
def _get_analytic_context() -> tuple[dict, dict]:
46+
"""Return (tags, extra) for full analytic context on every Sentry event."""
47+
tags = {
48+
"platform": platform.system(),
49+
"platform_release": platform.release(),
50+
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
51+
"machine": platform.machine(),
52+
"frozen": str(getattr(sys, "frozen", False)),
53+
}
54+
extra = {}
55+
try:
56+
from cuepoint.version import get_build_info
57+
58+
build_info = get_build_info()
59+
extra["build"] = build_info
60+
if build_info.get("commit_sha"):
61+
tags["commit_sha"] = build_info.get("short_commit_sha") or build_info.get("commit_sha", "")[:8]
62+
if build_info.get("build_number"):
63+
tags["build_number"] = str(build_info["build_number"])
64+
except Exception:
65+
pass
66+
return tags, extra
67+
68+
4269
def init_sentry_early() -> bool:
4370
"""Initialize Sentry as early as possible (call right after CrashHandler).
4471
@@ -79,12 +106,24 @@ def init_sentry_early() -> bool:
79106
def before_send(event, hint):
80107
if not _consent_allowed():
81108
return None
109+
tags, extra = _get_analytic_context()
110+
if event.get("tags") is None:
111+
event["tags"] = {}
112+
event["tags"].update(tags)
113+
if event.get("extra") is None:
114+
event["extra"] = {}
115+
event["extra"].update(extra)
82116
return event
83117

84118
sentry_sdk.init(
85119
dsn=dsn,
86120
release=release,
87121
environment=env,
122+
# Full analytic: attach stack traces to all events, local vars, source context
123+
attach_stacktrace=True,
124+
include_local_variables=True,
125+
include_source_context=True,
126+
max_breadcrumbs=200,
88127
traces_sample_rate=0.0,
89128
profiles_sample_rate=0.0,
90129
send_default_pii=False,
@@ -137,14 +176,30 @@ def init_sentry(
137176
level=logging.INFO,
138177
event_level=logging.WARNING,
139178
)
179+
180+
def before_send_with_context(event, hint):
181+
tags, extra = _get_analytic_context()
182+
if event.get("tags") is None:
183+
event["tags"] = {}
184+
event["tags"].update(tags)
185+
if event.get("extra") is None:
186+
event["extra"] = {}
187+
event["extra"].update(extra)
188+
return event
189+
140190
sentry_sdk.init(
141191
dsn=dsn,
142192
release=release or get_version(),
143193
environment=env,
194+
attach_stacktrace=True,
195+
include_local_variables=True,
196+
include_source_context=True,
197+
max_breadcrumbs=200,
144198
traces_sample_rate=0.0,
145199
profiles_sample_rate=0.0,
146200
send_default_pii=False,
147201
integrations=[logging_integration],
202+
before_send=before_send_with_context,
148203
)
149204
logger.info("Sentry error reporting initialized")
150205
return True

src/tests/unit/update/test_two_appcast_channels.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,40 @@ def test_case_insensitive_test_prefix(self):
148148
def test_test_with_suffix_unsigned(self):
149149
"""e.g. 1.0.0-test-unsigned42: prerelease starts with 'test'."""
150150
assert is_test_version("2.1.0-test-unsigned42") is True
151+
152+
153+
@pytest.mark.unit
154+
class TestTestToTestUpdateReturnsDownloadUrl:
155+
"""Test build updating to newer test build gets a valid download_url (no 404 after publishing test releases)."""
156+
157+
def test_test_version_sees_newer_test_in_appcast_with_download_url(self):
158+
"""0.0.3-test sees 0.0.4-test in appcast and gets update with HTTPS download_url."""
159+
# Minimal appcast with one item: 0.0.4-test, GitHub-style enclosure URL
160+
ns = "http://www.andymatuschak.org/xml-namespaces/sparkle"
161+
appcast_xml = f"""<?xml version="1.0" encoding="UTF-8"?>
162+
<rss version="2.0" xmlns:sparkle="{ns}">
163+
<channel>
164+
<item>
165+
<title>CuePoint 0.0.4-test</title>
166+
<sparkle:version>202502111200</sparkle:version>
167+
<sparkle:shortVersionString>0.0.4-test</sparkle:shortVersionString>
168+
<enclosure url="https://github.com/stuchain/CuePoint/releases/download/v0.0.4-test/CuePoint-Setup-0.0.4-test.exe"
169+
length="50000000"
170+
type="application/octet-stream"
171+
sparkle:sha256="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"/>
172+
</item>
173+
</channel>
174+
</rss>"""
175+
checker = UpdateChecker(
176+
feed_url="https://stuchain.github.io/CuePoint/updates",
177+
current_version="0.0.3-test",
178+
channel="test",
179+
)
180+
result = checker.check_update_from_appcast(appcast_xml.encode("utf-8"))
181+
assert result is not None
182+
assert result.get("short_version") == "0.0.4-test"
183+
download_url = result.get("download_url")
184+
assert download_url is not None
185+
assert download_url.startswith("https://")
186+
assert "releases/download" in download_url
187+
assert "0.0.4-test" in download_url

0 commit comments

Comments
 (0)