fix: keep release downloads on configured origin
Signed Release / release (push) Successful in 7m18s

This commit is contained in:
Qiufeng
2026-08-05 06:23:39 +08:00
parent 5d7112ce31
commit c172350200
9 changed files with 76 additions and 47 deletions
@@ -50,7 +50,6 @@ public class SystemUpdateService {
private static final int MAX_RELEASE_HISTORY = 20;
private static final int MAX_RELEASE_ASSETS = 32;
private static final int MAX_ASSET_NAME_CHARS = 255;
private static final int MAX_ASSET_URL_CHARS = 4_096;
private final UpdateProperties properties;
private final ObjectMapper objectMapper;
@@ -266,6 +265,16 @@ public class SystemUpdateService {
+ encode(repository[1]) + "/releases" + suffix);
}
private URI releaseAssetUri(String tag, String name) {
String[] repository = properties.getRepository().split("/", 2);
if (repository.length != 2 || repository[0].isBlank() || repository[1].isBlank()) {
throw new ApiException(500, "更新仓库配置无效");
}
String base = properties.getGiteaBaseUrl().replaceAll("/+$", "");
return URI.create(base + "/" + encode(repository[0]) + "/" + encode(repository[1])
+ "/releases/download/" + encode(tag) + "/" + encode(name));
}
private JsonNode fetchReleaseJson(URI uri) {
HttpRequest.Builder builder = HttpRequest.newBuilder(uri)
.timeout(Duration.ofSeconds(Math.max(1, properties.getRequestTimeoutSeconds())))
@@ -327,15 +336,12 @@ public class SystemUpdateService {
List<ReleaseAsset> assets = new ArrayList<>();
for (JsonNode node : root.path("assets")) {
String name = text(node, "name");
String downloadUrl = text(node, "browser_download_url");
if (name.isBlank() || name.length() > MAX_ASSET_NAME_CHARS
|| downloadUrl.isBlank() || downloadUrl.length() > MAX_ASSET_URL_CHARS) {
if (name.isBlank() || name.length() > MAX_ASSET_NAME_CHARS) {
throw new ApiException(502, "Release 文件信息无效");
}
validateAssetUrl(downloadUrl);
assets.add(new ReleaseAsset(
name,
downloadUrl,
releaseAssetUri(tag, name).toString(),
node.path("size").asLong(0)
));
}
@@ -43,8 +43,7 @@ class SystemUpdateServiceTest {
void checksLatestGiteaReleaseAndRequiresSignedAssetSet() throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/api/v1/repos/awaioi/ERP/releases/latest", exchange -> {
String origin = "http://127.0.0.1:" + exchange.getLocalAddress().getPort();
byte[] body = ("""
byte[] body = """
{
"tag_name": "v0.2.0",
"body": "PostgreSQL production release",
@@ -52,12 +51,12 @@ class SystemUpdateServiceTest {
"prerelease": false,
"published_at": "2026-08-03T10:00:00Z",
"assets": [
{"name":"kaidi-erp-0.2.0.tar.gz","browser_download_url":"%s/app","size":123},
{"name":"SHA256SUMS","browser_download_url":"%s/sums","size":64},
{"name":"SHA256SUMS.sig","browser_download_url":"%s/sig","size":64}
{"name":"kaidi-erp-0.2.0.tar.gz","browser_download_url":"http://legacy.invalid/app","size":123},
{"name":"SHA256SUMS","browser_download_url":"http://legacy.invalid/sums","size":64},
{"name":"SHA256SUMS.sig","browser_download_url":"http://legacy.invalid/sig","size":64}
]
}
""").formatted(origin, origin, origin).getBytes(StandardCharsets.UTF_8);
""".getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().add("Content-Type", "application/json");
exchange.sendResponseHeaders(200, body.length);
exchange.getResponseBody().write(body);
@@ -75,6 +74,9 @@ class SystemUpdateServiceTest {
assertThat(status.latestVersion()).isEqualTo("0.2.0");
assertThat(status.assets()).extracting(SystemUpdateService.ReleaseAsset::name)
.containsExactly("kaidi-erp-0.2.0.tar.gz", "SHA256SUMS", "SHA256SUMS.sig");
assertThat(status.assets()).extracting(SystemUpdateService.ReleaseAsset::downloadUrl)
.allMatch(url -> url.startsWith("http://127.0.0.1:" + server.getAddress().getPort()
+ "/awaioi/ERP/releases/download/v0.2.0/"));
} finally {
server.stop(0);
}
@@ -198,7 +200,7 @@ class SystemUpdateServiceTest {
}
@Test
void rejectsReleaseAssetsFromAnotherOrigin() throws Exception {
void ignoresReleaseAssetOriginsAndBuildsTrustedDownloadUrls() throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/api/v1/repos/awaioi/ERP/releases/latest", exchange -> {
byte[] body = """
@@ -214,9 +216,11 @@ class SystemUpdateServiceTest {
server.start();
try {
SystemUpdateService service = service(configuredProperties(server.getAddress().getPort()), "0.3.0");
assertThatThrownBy(service::check)
.isInstanceOf(ApiException.class)
.hasMessageContaining("未受信任");
SystemUpdateService.UpdateStatus status = service.check();
assertThat(status.assets()).extracting(SystemUpdateService.ReleaseAsset::downloadUrl)
.allMatch(url -> url.startsWith("http://127.0.0.1:" + server.getAddress().getPort()
+ "/awaioi/ERP/releases/download/v0.4.0/"));
} finally {
server.stop(0);
}