fix: normalize Gitea release asset URLs
Release / release (push) Failing after 8s

This commit is contained in:
Qiufeng
2026-08-17 16:42:03 +08:00
parent 7394c9e7e4
commit f9912ade7e
8 changed files with 109 additions and 62 deletions
+14 -12
View File
@@ -46,8 +46,10 @@ npm run build
代码仓库和 Release 已公开,Linux 服务器不需要 Gitea Token。安装器读取
`https://git.awaioi.com/api/v1/repos/ERP-Team/kaidi/releases/latest`,生产机不执行 `git pull` 或现场编译。
安装脚本内置固定的发布公钥指纹,下载的应用包、清单、SBOM 和更新脚本仍须通过 RSA 签名与 SHA-256 校验。
Gitea API 只用于确认 Release tag 和资产名称;下载地址由受信 API 域名、仓库路径和 tag 重新构造,不采用
Gitea 响应中可能错误指向内网地址的 `browser_download_url`。
本版只支持由运维人员预先准备的外部 MySQL 8.4.x;安装器不会安装 MySQL、创建数据库容器或修改现有
PostgreSQL 18。PostgreSQL 兼容开发已冻结,不属于本次 Preview.10 发布范围。
PostgreSQL 18。PostgreSQL 兼容开发已冻结,不属于本次 Preview.11 发布范围。
执行命令的机器需预装 `bash`、`sudo`、`curl`、`mktemp` 和 `sha256sum`,并能访问目标 Gitea;`jq`、Java
和数据库客户端由安装器补齐。应用固定安装到 `/opt/kaidi`、`/var/lib/kaidi`、
@@ -56,14 +58,14 @@ PostgreSQL 18。PostgreSQL 兼容开发已冻结,不属于本次 Preview.10
### 直接 curl 安装
```bash
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.10/install.sh | sudo bash
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.11/install.sh | sudo bash
```
这条命令会提示填写 Java 应用端口,直接回车使用 `18080`;随后安装最新签名 Release,并默认进入 `/setup`
安装向导。Java 默认只监听 `127.0.0.1:所选端口`,前端页面、API 和健康检查均由同一端口提供。无人值守安装可直接指定:
```bash
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.10/install.sh \
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.11/install.sh \
| sudo env KAIDI_APP_PORT=19090 bash
```
@@ -71,8 +73,8 @@ curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-previe
时才设置 `KAIDI_SERVER_ADDRESS=0.0.0.0`,通常应保持默认回环绑定并由本机反向代理访问。需要在执行前独立校验安装脚本时使用:
```bash
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.10/install.sh -o /tmp/kaidi-install.sh
printf '%s %s\n' cc9ae48fc3e36fbeb13b0660b0d91e59bd099a1386d49df699f214285c1afc0a /tmp/kaidi-install.sh | sha256sum -c -
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.11/install.sh -o /tmp/kaidi-install.sh
printf '%s %s\n' 78bd7a89acc71fc016d1c55816abd466173194bfd71a8f095c73f6deb86babd9 /tmp/kaidi-install.sh | sha256sum -c -
sudo bash /tmp/kaidi-install.sh
rm -f /tmp/kaidi-install.sh
```
@@ -83,7 +85,7 @@ rm -f /tmp/kaidi-install.sh
包装器会在 `sudo` 前校验 `deploy/install.sh` 的固定 SHA-256,再按同一公钥信任链安装最新签名 Release。
```bash
git clone --branch v1.0.0-preview.10 --depth 1 https://git.awaioi.com/ERP-Team/kaidi.git kaidi-preview
git clone --branch v1.0.0-preview.11 --depth 1 https://git.awaioi.com/ERP-Team/kaidi.git kaidi-preview
cd kaidi-preview
./deploy/install-from-git.sh
```
@@ -138,7 +140,7 @@ sudo cat /root/kaidi-first-login.txt
32 位服务端镜像,因此 32 位主机需要预先连接一台 MySQL 8.4 数据库,之后仍然只执行一个安装命令:
```bash
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.10/install.sh | sudo bash
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.11/install.sh | sudo bash
```
32 位主机不能运行安装器自动创建的 MySQL 容器,因此在打开向导前,需要预先创建 `kaidi_finance`,并授予安装账号该库的
@@ -158,7 +160,7 @@ GRANT ALL PRIVILEGES ON kaidi_finance.* TO 'kaidi'@'KAIDI_SERVER_IP';
管理员数据和运维人员新增的环境变量:
```bash
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.10/install.sh \
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.11/install.sh \
| sudo env KAIDI_REINSTALL=true KAIDI_SETUP_WIZARD=false bash
```
@@ -168,7 +170,7 @@ curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-previe
如果安装器已完成但向导尚未提交,可执行下面的命令重新生成一次性安装码;该恢复路径只接受仍处于向导模式且未锁定的安装,正式模式不会被覆盖。
```bash
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.10/install.sh \
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.11/install.sh \
| sudo env KAIDI_REINSTALL=true bash
```
@@ -204,8 +206,8 @@ act_runner 提供 `ubuntu-24.04` 标签,并在 tag 发布时执行后端、前
Preview 属性由 SemVer 版本名表达。之后推送 tag 即会构建、测试、签名并发布:
```bash
git tag v1.0.0-preview.10
git push origin v1.0.0-preview.10
git tag v1.0.0-preview.11
git push origin v1.0.0-preview.11
```
在线更新使用独立的 TDesign 页面:隔离的系统管理员进入“系统治理 → 系统更新”。权限与配置页只管理用户、角色、数据范围、表单模板和参数版本,不配置系统名称或域名。
@@ -256,7 +258,7 @@ cat /var/lib/kaidi-update/status.json
```bash
KAIDI_RELEASE_SIGNING_KEY=/secure/release-signing-private.pem \
KAIDI_TRUSTED_RELEASE_PUBLIC_KEY_SHA256=807c6aec1dc3f7ce494db16aa9d763c66f292033c38f328afd0390d2715a8cd9 \
./scripts/package-release.sh 1.0.0-preview.10
./scripts/package-release.sh 1.0.0-preview.11
KAIDI_TRUSTED_RELEASE_PUBLIC_KEY_SHA256=807c6aec1dc3f7ce494db16aa9d763c66f292033c38f328afd0390d2715a8cd9 \
./scripts/verify-release.sh dist/release
```
@@ -15,6 +15,7 @@ import com.kaidi.finance.update.api.SystemUpdateView;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
@@ -52,6 +53,9 @@ public class SystemUpdateApplicationService {
+ "(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$");
private static final Pattern ARTIFACT = Pattern.compile("^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}\\.tar\\.gz$");
private static final Pattern SHA256 = Pattern.compile("^[0-9a-fA-F]{64}$");
private static final Pattern GITEA_RELEASE_API_PATH = Pattern.compile(
"^(.*/)?api/v1/repos/([A-Za-z0-9._-]+)/([A-Za-z0-9._-]+)/releases/(?:latest|tags/[^/?#]+)$");
private static final Pattern GITEA_RELEASE_TAG = Pattern.compile("^v[0-9A-Za-z][0-9A-Za-z._+-]{0,127}$");
private static final List<String> BUSY_STATES = List.of(
"QUEUED", "DOWNLOAD_QUEUED", "INSTALL_QUEUED", "VERIFYING", "DOWNLOADING", "BACKING_UP",
"INSTALLING", "RUNNING");
@@ -195,20 +199,42 @@ public class SystemUpdateApplicationService {
URI api = releaseApiUri();
try {
JsonNode release = objectMapper.readTree(fetchReleaseBytes(api, MAX_RELEASE_API_BYTES, "Gitea Release"));
boolean assetPresent = false;
for (JsonNode asset : release.path("assets")) {
if (!assetName.equals(asset.path("name").asText())) continue;
URI uri = validatedReleaseUri(asset.path("browser_download_url").asText(null), "Release 资源地址");
if (!sameOrigin(api, uri)) {
throw releaseUnavailable("Gitea Release 资源必须与 API 使用同一来源");
if (assetName.equals(asset.path("name").asText())) {
assetPresent = true;
break;
}
return uri;
}
throw releaseUnavailable("Gitea Release 缺少 " + assetName);
if (!assetPresent) throw releaseUnavailable("Gitea Release 缺少 " + assetName);
String tag = release.path("tag_name").asText(null);
if (tag == null || !GITEA_RELEASE_TAG.matcher(tag).matches()) {
throw releaseUnavailable("Gitea Release tag 格式无效");
}
return trustedGiteaAssetUri(api, tag, assetName);
} catch (IOException exception) {
throw releaseUnavailable("Gitea Release 响应读取失败");
}
}
private URI trustedGiteaAssetUri(URI api, String tag, String assetName) {
if (api.getRawQuery() != null || api.getRawFragment() != null) {
throw releaseUnavailable("Gitea Release API 地址格式无效");
}
Matcher matcher = GITEA_RELEASE_API_PATH.matcher(api.getPath());
if (!matcher.matches()) {
throw releaseUnavailable("Gitea Release API 地址必须指向仓库 Release 端点");
}
String prefix = matcher.group(1) == null ? "/" : matcher.group(1);
String path = prefix + matcher.group(2) + "/" + matcher.group(3)
+ "/releases/download/" + tag + "/" + assetName;
try {
return new URI(api.getScheme(), null, api.getHost(), api.getPort(), path, null, null);
} catch (URISyntaxException exception) {
throw releaseUnavailable("Gitea Release 资源地址格式无效");
}
}
private byte[] fetchReleaseBytes(URI uri, int maximumBytes, String resourceName) {
String token = releaseToken();
URI current = uri;
@@ -47,18 +47,17 @@ class SystemUpdateApplicationServiceTest {
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/api/v1/repos/ERP-Team/kaidi/releases/latest", exchange -> {
assertEquals("token read-only-token", exchange.getRequestHeaders().getFirst("Authorization"));
String assetUrl = "http://127.0.0.1:" + server.getAddress().getPort()
+ "/assets/release-manifest.json";
byte[] body = ("""
{"tag_name":"v1.0.0-preview.2","assets":[
{"name":"release-manifest.json","browser_download_url":"%s"}
]}
""").formatted(assetUrl).getBytes(StandardCharsets.UTF_8);
""").formatted("http://10.0.0.8:3000/internal/release-manifest.json")
.getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, body.length);
exchange.getResponseBody().write(body);
exchange.close();
});
server.createContext("/assets/release-manifest.json", exchange -> {
server.createContext("/ERP-Team/kaidi/releases/download/v1.0.0-preview.2/release-manifest.json", exchange -> {
assertEquals("token read-only-token", exchange.getRequestHeaders().getFirst("Authorization"));
byte[] body = """
{"version":"1.0.0-preview.2","artifact":"kaidi-finance-1.0.0-preview.2.tar.gz",
@@ -98,7 +97,7 @@ class SystemUpdateApplicationServiceTest {
}
@Test
void rejectsPrivateGiteaAssetOnAnotherOriginWithoutSendingToken() throws Exception {
void ignoresCrossOriginBrowserAssetUrlWithoutSendingTokenThere() throws Exception {
AtomicInteger assetRequests = new AtomicInteger();
HttpServer assetServer = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
assetServer.createContext("/release-manifest.json", exchange -> {
@@ -112,12 +111,25 @@ class SystemUpdateApplicationServiceTest {
String assetUrl = "http://127.0.0.1:" + assetServer.getAddress().getPort()
+ "/release-manifest.json";
byte[] body = ("""
{"assets":[{"name":"release-manifest.json","browser_download_url":"%s"}]}
{"tag_name":"v1.0.0-preview.2",
"assets":[{"name":"release-manifest.json","browser_download_url":"%s"}]}
""").formatted(assetUrl).getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, body.length);
exchange.getResponseBody().write(body);
exchange.close();
});
apiServer.createContext(
"/ERP-Team/kaidi/releases/download/v1.0.0-preview.2/release-manifest.json", exchange -> {
assertEquals("token read-only-token", exchange.getRequestHeaders().getFirst("Authorization"));
byte[] body = """
{"version":"1.0.0-preview.2","artifact":"kaidi-finance-1.0.0-preview.2.tar.gz",
"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"publishedAt":"2026-08-16T00:00:00Z","releaseNotes":"Gitea Preview update"}
""".getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, body.length);
exchange.getResponseBody().write(body);
exchange.close();
});
assetServer.start();
apiServer.start();
try {
@@ -125,7 +137,7 @@ class SystemUpdateApplicationServiceTest {
SystemUpdateApplicationService service = serviceForGitea(apiServer, inbox,
tempDir.resolve("cross-origin-status.json"));
assertThrows(BusinessException.class, service::check);
assertEquals("1.0.0-preview.2", service.check().latestVersion());
assertEquals(0, assetRequests.get());
} finally {
apiServer.stop(0);
+1 -1
View File
@@ -5,7 +5,7 @@ umask 077
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
INSTALLER="$ROOT/deploy/install.sh"
INSTALLER_SHA256=${KAIDI_INSTALLER_SHA256:-cc9ae48fc3e36fbeb13b0660b0d91e59bd099a1386d49df699f214285c1afc0a}
INSTALLER_SHA256=${KAIDI_INSTALLER_SHA256:-78bd7a89acc71fc016d1c55816abd466173194bfd71a8f095c73f6deb86babd9}
RELEASE_API_URL=${KAIDI_RELEASE_API_URL:-https://git.awaioi.com/api/v1/repos/ERP-Team/kaidi/releases/latest}
PUBLIC_KEY_SHA256=${KAIDI_RELEASE_PUBLIC_KEY_SHA256:-807c6aec1dc3f7ce494db16aa9d763c66f292033c38f328afd0390d2715a8cd9}
TOKEN_FILE=${KAIDI_RELEASE_TOKEN_FILE:-}
+12 -9
View File
@@ -126,17 +126,20 @@ download_release_url() {
}
release_asset_url() {
local asset_name=$1 asset_url api_origin
local asset_name=$1 api_origin repo_path release_tag encoded_tag encoded_asset
if [ -n "$RELEASE_API_URL" ]; then
asset_url=$(jq -er --arg name "$asset_name" \
'.assets[] | select(.name == $name) | .browser_download_url
| strings | select(startswith("https://") or startswith("http://"))' \
"$WORK_DIR/release-api.json" | head -n 1) || die "Release asset $asset_name is missing"
jq -e --arg name "$asset_name" 'any(.assets[]?; .name == $name)' \
"$WORK_DIR/release-api.json" >/dev/null || die "Release asset $asset_name is missing"
release_tag=$(jq -er '.tag_name | strings
| select(test("^v[0-9A-Za-z][0-9A-Za-z._+-]{0,127}$"))' \
"$WORK_DIR/release-api.json") || die "Gitea Release tag is invalid"
api_origin=$(printf '%s' "$RELEASE_API_URL" | sed -E 's#^(https?://[^/]+).*$#\1#')
case "$asset_url" in
"$api_origin"/*) printf '%s\n' "$asset_url" ;;
*) die "Release asset $asset_name must use the Gitea API host" ;;
esac
repo_path=$(printf '%s' "$RELEASE_API_URL" | sed -nE \
's#^https?://[^/]+/api/v1/repos/([A-Za-z0-9._-]+/[A-Za-z0-9._-]+)/releases/(latest|tags/[^/?#]+)$#\1#p')
[ -n "$repo_path" ] || die "KAIDI_RELEASE_API_URL must be a Gitea repository Release API endpoint"
encoded_tag=$(jq -rn --arg value "$release_tag" '$value | @uri')
encoded_asset=$(jq -rn --arg value "$asset_name" '$value | @uri')
printf '%s/%s/releases/download/%s/%s\n' "$api_origin" "$repo_path" "$encoded_tag" "$encoded_asset"
else
printf '%s/%s\n' "${RELEASE_BASE_URL%/}" "$asset_name"
fi
+11 -8
View File
@@ -124,15 +124,18 @@ download() {
release_asset_url() {
asset_name=$1
if [ -n "$RELEASE_API_URL" ]; then
asset_url=$(jq -er --arg name "$asset_name" \
'.assets[] | select(.name == $name) | .browser_download_url
| strings | select(startswith("https://") or startswith("http://"))' \
"$WORK_DIR/release-api.json" | head -n 1) || return 1
jq -e --arg name "$asset_name" 'any(.assets[]?; .name == $name)' \
"$WORK_DIR/release-api.json" >/dev/null || return 1
release_tag=$(jq -er '.tag_name | strings
| select(test("^v[0-9A-Za-z][0-9A-Za-z._+-]{0,127}$"))' \
"$WORK_DIR/release-api.json") || return 1
api_origin=$(printf '%s' "$RELEASE_API_URL" | sed -E 's#^(https?://[^/]+).*$#\1#')
case "$asset_url" in
"$api_origin"/*) printf '%s\n' "$asset_url" ;;
*) return 1 ;;
esac
repo_path=$(printf '%s' "$RELEASE_API_URL" | sed -nE \
's#^https?://[^/]+/api/v1/repos/([A-Za-z0-9._-]+/[A-Za-z0-9._-]+)/releases/(latest|tags/[^/?#]+)$#\1#p')
[ -n "$repo_path" ] || return 1
encoded_tag=$(jq -rn --arg value "$release_tag" '$value | @uri')
encoded_asset=$(jq -rn --arg value "$asset_name" '$value | @uri')
printf '%s/%s/releases/download/%s/%s\n' "$api_origin" "$repo_path" "$encoded_tag" "$encoded_asset"
else
printf '%s/%s\n' "${RELEASE_BASE_URL%/}" "$asset_name"
fi
+8 -7
View File
@@ -165,7 +165,7 @@ RELEASE_AUTH_HEADER_FILE=$WORK/release-auth-header
printf 'Authorization: token %s\n' "$RELEASE_TOKEN" > "$RELEASE_AUTH_HEADER_FILE"
chmod 0600 "$RELEASE_AUTH_HEADER_FILE"
cat > "$WORK/release-api.json" <<'JSON'
{"assets":[
{"tag_name":"v1.0.0-preview.11","assets":[
{"name":"release-manifest.json","browser_download_url":"https://gitea.fixture.invalid/assets/release-manifest.json"}
]}
JSON
@@ -188,12 +188,13 @@ curl() {
done
[ "$header_file" = "$RELEASE_AUTH_HEADER_FILE" ] || return 90
grep -Fqx "Authorization: token $RELEASE_TOKEN" "$header_file" || return 91
[ "$url" = https://gitea.fixture.invalid/assets/release-manifest.json ] || return 92
[ "$url" = https://gitea.fixture.invalid/ERP-Team/kaidi/releases/download/v1.0.0-preview.11/release-manifest.json ] \
|| return 92
cp "$WORK/release-manifest.fixture" "$output"
}
[ "$(release_asset_url release-manifest.json)" = \
https://gitea.fixture.invalid/assets/release-manifest.json ] \
|| fail 'installer did not resolve the private Gitea release asset'
https://gitea.fixture.invalid/ERP-Team/kaidi/releases/download/v1.0.0-preview.11/release-manifest.json ] \
|| fail 'installer did not construct the trusted Gitea release asset URL'
download_release_asset release-manifest.json "$WORK/downloaded-manifest.json"
cmp -s "$WORK/release-manifest.fixture" "$WORK/downloaded-manifest.json" \
|| fail 'installer did not download the private Gitea release asset'
@@ -205,9 +206,9 @@ cmp -s "$WORK/release-manifest.fixture" "$WORK/downloaded-manifest.json" \
jq '.assets[0].browser_download_url = "https://assets.fixture.invalid/release-manifest.json"' \
"$WORK/release-api.json" > "$WORK/release-api.cross-origin.json"
mv "$WORK/release-api.cross-origin.json" "$WORK/release-api.json"
if release_asset_url release-manifest.json >/dev/null 2>&1; then
fail 'installer accepted a cross-origin Gitea release asset'
fi
[ "$(release_asset_url release-manifest.json)" = \
https://gitea.fixture.invalid/ERP-Team/kaidi/releases/download/v1.0.0-preview.11/release-manifest.json ] \
|| fail 'installer trusted the cross-origin browser download URL'
# shellcheck disable=SC2016 # Match literal installer source.
grep -Fq '[ "$APP_ROOT" = /opt/kaidi ]' "$ROOT/deploy/install.sh" \
+12 -12
View File
@@ -154,10 +154,12 @@ build_release() {
build_gitea_release_index() {
local fixture=$1
local origin=${2:-https://gitea.fixture.invalid}
local artifact
local artifact version tag
artifact=$(jq -er '.artifact' "$fixture/release/release-manifest.json")
jq -n --arg origin "$origin" --arg artifact "$artifact" \
'{assets:[
version=$(jq -er '.version' "$fixture/release/release-manifest.json")
tag="v$version"
jq -n --arg origin "$origin" --arg artifact "$artifact" --arg tag "$tag" \
'{tag_name:$tag,assets:[
{name:"release-manifest.json",browser_download_url:($origin + "/assets/release-manifest.json")},
{name:"release-manifest.sig",browser_download_url:($origin + "/assets/release-manifest.sig")},
{name:$artifact,browser_download_url:($origin + "/assets/" + $artifact)}
@@ -267,7 +269,7 @@ assert_private_gitea_release_case() {
|| fail 'authenticated private Gitea requests unexpectedly enabled redirects'
}
assert_cross_origin_gitea_asset_rejected() {
assert_cross_origin_gitea_browser_url_ignored() {
local fixture="$WORK/cross-origin-gitea"
local version='1.0.0-preview.2'
mkdir -p "$fixture"
@@ -276,18 +278,16 @@ assert_cross_origin_gitea_asset_rejected() {
build_gitea_release_index "$fixture" https://assets.fixture.invalid
prepare_installation "$fixture" "$version"
if FIXTURE_RELEASE_BASE_URL='' \
FIXTURE_RELEASE_BASE_URL='' \
FIXTURE_RELEASE_API_URL=https://gitea.fixture.invalid/api/v1/repos/ERP-Team/kaidi/releases/latest \
FIXTURE_RELEASE_ORIGIN=https://gitea.fixture.invalid \
FIXTURE_RELEASE_TOKEN=fixture-read-only-token \
MOCK_CURL_LOG="$fixture/curl.log" \
run_update "$fixture" success > "$fixture/update.log" 2>&1; then
fail 'cross-origin Gitea asset unexpectedly succeeded'
fi
grep -q 'Release manifest asset is missing' "$fixture/update.log" \
|| fail 'cross-origin Gitea asset rejection was not reported'
run_update "$fixture" success
[ "$(jq -r '.state' "$fixture/state/status.json")" = READY ] \
|| fail 'misconfigured browser download URL prevented trusted same-origin download'
! grep -Fq 'assets.fixture.invalid' "$fixture/curl.log" \
|| fail 'cross-origin Gitea asset was requested'
|| fail 'cross-origin browser download URL was requested'
}
assert_success_case() {
@@ -430,5 +430,5 @@ assert_database_failure_case
assert_symlink_request_rejected
assert_install_without_verified_cache_rejected
assert_private_gitea_release_case
assert_cross_origin_gitea_asset_rejected
assert_cross_origin_gitea_browser_url_ignored
printf 'Online update download, confirmation, success, rollback, failure, and unsafe-request fixtures passed\n'