Add channel-aware release tooling and updater recovery
This commit is contained in:
643
release_tests/test_gitea_publisher.py
Normal file
643
release_tests/test_gitea_publisher.py
Normal file
@@ -0,0 +1,643 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from scripts.release_lib import verify_signature
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
MOCK_CURL = r'''#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
METHOD="GET"
|
||||
OUTPUT=""
|
||||
FORM=""
|
||||
DATA_FILE=""
|
||||
URL=""
|
||||
while (( $# > 0 ))
|
||||
do
|
||||
case "$1" in
|
||||
--request)
|
||||
METHOD="$2"
|
||||
shift 2
|
||||
;;
|
||||
--output)
|
||||
OUTPUT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--form)
|
||||
FORM="$2"
|
||||
shift 2
|
||||
;;
|
||||
--data-binary)
|
||||
DATA_FILE="${2#@}"
|
||||
shift 2
|
||||
;;
|
||||
--connect-timeout|--max-time|--header|--write-out)
|
||||
shift 2
|
||||
;;
|
||||
--silent|--show-error)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
URL="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
release_json()
|
||||
{
|
||||
local assets
|
||||
assets="$(jq -s '.' "${MOCK_STATE_DIR}"/asset-*.json)"
|
||||
jq -n \
|
||||
--arg html_url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/tag/${RELEASE_TAG}" \
|
||||
--arg published_at "2026-09-08T12:00:00Z" \
|
||||
--arg tag "$RELEASE_TAG" \
|
||||
--arg name "PristonTale EU Launcher ${MOCK_VERSION}" \
|
||||
--arg target_commitish "$RELEASE_COMMIT" \
|
||||
--argjson assets "$assets" \
|
||||
--argjson prerelease "$RELEASE_PRERELEASE" \
|
||||
'{id:71,tag_name:$tag,name:$name,draft:false,prerelease:$prerelease,target_commitish:$target_commitish,published_at:$published_at,created_at:$published_at,html_url:$html_url,assets:$assets}'
|
||||
}
|
||||
|
||||
case "${METHOD} ${URL}" in
|
||||
"GET "*"/releases/tags/"*)
|
||||
if [[ "${MOCK_TIMEOUT_STAGE:-}" == "existing" ]]
|
||||
then
|
||||
printf 'curl: (28) simulated release lookup timeout\n' >&2
|
||||
exit 28
|
||||
fi
|
||||
if [[ -n "${MOCK_EXISTING_STATUS:-}" ]]
|
||||
then
|
||||
printf '%s\n' '{"message":"simulated release lookup failure"}' > "$OUTPUT"
|
||||
printf '%s' "$MOCK_EXISTING_STATUS"
|
||||
exit 0
|
||||
fi
|
||||
if [[ -f "${MOCK_STATE_DIR}/created" ]]
|
||||
then
|
||||
release_json > "$OUTPUT"
|
||||
printf '200'
|
||||
else
|
||||
printf '%s\n' '{"message":"not found"}' > "$OUTPUT"
|
||||
printf '404'
|
||||
fi
|
||||
;;
|
||||
"POST "*"/releases")
|
||||
if [[ "${MOCK_TIMEOUT_STAGE:-}" == "create" ]]
|
||||
then
|
||||
printf 'curl: (28) simulated release creation timeout\n' >&2
|
||||
exit 28
|
||||
fi
|
||||
if [[ -n "${MOCK_CREATE_STATUS:-}" ]]
|
||||
then
|
||||
printf '%s\n' '{"message":"simulated release creation failure"}' > "$OUTPUT"
|
||||
printf '%s' "$MOCK_CREATE_STATUS"
|
||||
exit 0
|
||||
fi
|
||||
cp -- "$DATA_FILE" "${MOCK_STATE_DIR}/release-payload.json"
|
||||
: > "${MOCK_STATE_DIR}/created"
|
||||
printf '%s\n' '{"id":71}' > "$OUTPUT"
|
||||
printf '201'
|
||||
;;
|
||||
"POST "*"/assets?name="*)
|
||||
if [[ "${MOCK_TIMEOUT_STAGE:-}" == "asset" ]]
|
||||
then
|
||||
printf 'curl: (28) simulated attachment timeout\n' >&2
|
||||
exit 28
|
||||
fi
|
||||
if [[ -n "${MOCK_ASSET_STATUS:-}" ]]
|
||||
then
|
||||
printf '%s\n' '{"message":"simulated attachment failure"}' > "$OUTPUT"
|
||||
printf '%s' "$MOCK_ASSET_STATUS"
|
||||
exit 0
|
||||
fi
|
||||
name="${URL##*?name=}"
|
||||
file="${FORM#attachment=@}"
|
||||
file="${file%%;*}"
|
||||
size="$(stat -c '%s' -- "$file")"
|
||||
jq -n \
|
||||
--arg name "$name" \
|
||||
--arg url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/${name}" \
|
||||
--argjson size "$size" \
|
||||
'{name:$name,size:$size,browser_download_url:$url}' > "$OUTPUT"
|
||||
cp -- "$OUTPUT" "${MOCK_STATE_DIR}/asset-${name}.json"
|
||||
printf '201'
|
||||
;;
|
||||
"GET "*"/releases?limit=100&page=1")
|
||||
if [[ "${MOCK_RELEASE_PAGINATION:-}" == "1" ]]
|
||||
then
|
||||
jq -n '[range(0;100) as $index | {draft:true,tag_name:("ignored-" + ($index|tostring))}]' > "$OUTPUT"
|
||||
else
|
||||
release_json | jq -s '.' > "$OUTPUT"
|
||||
fi
|
||||
printf '200'
|
||||
;;
|
||||
"GET "*"/releases?limit=100&page=2")
|
||||
release_json | jq -s '.' > "$OUTPUT"
|
||||
printf '200'
|
||||
;;
|
||||
"GET "*"/contents/"*)
|
||||
printf '%s\n' '{"message":"not found"}' > "$OUTPUT"
|
||||
printf '404'
|
||||
;;
|
||||
"POST "*"/contents")
|
||||
cp -- "$DATA_FILE" "${MOCK_STATE_DIR}/channel-payload.json"
|
||||
printf '%s\n' '{"commit":{"sha":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}}' > "$OUTPUT"
|
||||
printf '201'
|
||||
;;
|
||||
*)
|
||||
printf 'Unexpected mock curl request: %s %s\n' "$METHOD" "$URL" >&2
|
||||
exit 90
|
||||
;;
|
||||
esac
|
||||
'''
|
||||
|
||||
|
||||
MOCK_HISTORY_CURL = r'''#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
METHOD="GET"
|
||||
OUTPUT=""
|
||||
DATA_FILE=""
|
||||
URL=""
|
||||
while (( $# > 0 ))
|
||||
do
|
||||
case "$1" in
|
||||
--request)
|
||||
METHOD="$2"
|
||||
shift 2
|
||||
;;
|
||||
--output)
|
||||
OUTPUT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--data-binary)
|
||||
DATA_FILE="${2#@}"
|
||||
shift 2
|
||||
;;
|
||||
--connect-timeout|--max-time|--header|--write-out)
|
||||
shift 2
|
||||
;;
|
||||
--silent|--show-error)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
URL="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "${METHOD} ${URL}" in
|
||||
"GET "*"/releases?limit=100&page=1")
|
||||
cp -- "$MOCK_RELEASES" "$OUTPUT"
|
||||
printf '200'
|
||||
;;
|
||||
"GET "*"/manifest.json.sig")
|
||||
cp -- "$MOCK_MANIFEST_SIGNATURE" "$OUTPUT"
|
||||
printf '200'
|
||||
;;
|
||||
"GET "*"/manifest.json")
|
||||
cp -- "$MOCK_MANIFEST" "$OUTPUT"
|
||||
printf '200'
|
||||
;;
|
||||
"GET "*"/contents/"*)
|
||||
printf '%s\n' '{"message":"not found"}' > "$OUTPUT"
|
||||
printf '404'
|
||||
;;
|
||||
"POST "*"/contents")
|
||||
cp -- "$DATA_FILE" "$MOCK_HISTORY_PAYLOAD"
|
||||
printf '%s\n' '{"commit":{"sha":"cccccccccccccccccccccccccccccccccccccccc"}}' > "$OUTPUT"
|
||||
printf '201'
|
||||
;;
|
||||
*)
|
||||
printf 'Unexpected history mock request: %s %s\n' "$METHOD" "$URL" >&2
|
||||
exit 90
|
||||
;;
|
||||
esac
|
||||
'''
|
||||
|
||||
|
||||
def _publisher_manifest(version: str = "1.8.0") -> bytes:
|
||||
archive_name = f"PristonTale-EU-{version}.tar.gz"
|
||||
archive_url = (
|
||||
f"https://gitea.example.test/owner/project/releases/download/"
|
||||
f"v{version}/{archive_name}"
|
||||
)
|
||||
return (
|
||||
json.dumps(
|
||||
{
|
||||
"application": "PristonTale-EU",
|
||||
"archive": {
|
||||
"name": archive_name,
|
||||
"sha256": "a" * 64,
|
||||
"size": 123,
|
||||
"url": archive_url,
|
||||
},
|
||||
"archive_sha256": "a" * 64,
|
||||
"archive_size": 123,
|
||||
"archive_url": archive_url,
|
||||
"channel": "stable",
|
||||
"compatibility": {
|
||||
"config_schema": 1,
|
||||
"manifest_schema": 1,
|
||||
"minimum_config_schema": 1,
|
||||
"minimum_updater_version": "0.5.0",
|
||||
},
|
||||
"minimum_updater_version": "0.5.0",
|
||||
"notes_url": (
|
||||
f"https://gitea.example.test/owner/project/releases/tag/v{version}"
|
||||
),
|
||||
"published_at": "2026-09-08T12:00:00Z",
|
||||
"schema": 1,
|
||||
"status": "active",
|
||||
"tag": f"v{version}",
|
||||
"version": version,
|
||||
},
|
||||
indent=2,
|
||||
sort_keys=True,
|
||||
)
|
||||
+ "\n"
|
||||
).encode()
|
||||
|
||||
|
||||
class GiteaPublisherTests(unittest.TestCase):
|
||||
def test_publishes_assets_and_updates_only_the_selected_channel(self) -> None:
|
||||
openssl = shutil.which("openssl")
|
||||
jq = shutil.which("jq")
|
||||
if openssl is None or jq is None:
|
||||
self.skipTest("OpenSSL and jq are required")
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
output = root / "dist"
|
||||
state = root / "state"
|
||||
fake_bin = root / "bin"
|
||||
for directory in (output, state, fake_bin):
|
||||
directory.mkdir()
|
||||
archive = output / "PristonTale-EU-0.9.8.tar.gz"
|
||||
archive.write_bytes(b"archive fixture")
|
||||
(output / f"{archive.name}.sha256").write_text(
|
||||
f"{'a' * 64} {archive.name}\n",
|
||||
encoding="ascii",
|
||||
)
|
||||
(output / "manifest.json").write_text('{"schema":1}\n', encoding="utf-8")
|
||||
(output / "manifest.json.sig").write_bytes(b"manifest signature")
|
||||
(output / "release-notes.md").write_text("Release notes.\n", encoding="utf-8")
|
||||
private_key = root / "private.pem"
|
||||
public_key = root / "public.pem"
|
||||
subprocess.run(
|
||||
[
|
||||
openssl,
|
||||
"genpkey",
|
||||
"-quiet",
|
||||
"-algorithm",
|
||||
"RSA",
|
||||
"-pkeyopt",
|
||||
"rsa_keygen_bits:2048",
|
||||
"-out",
|
||||
str(private_key),
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
subprocess.run(
|
||||
[
|
||||
openssl,
|
||||
"pkey",
|
||||
"-in",
|
||||
str(private_key),
|
||||
"-pubout",
|
||||
"-out",
|
||||
str(public_key),
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
fake_curl = fake_bin / "curl"
|
||||
fake_curl.write_text(MOCK_CURL, encoding="utf-8")
|
||||
fake_curl.chmod(0o755)
|
||||
summary = root / "summary.md"
|
||||
environment = {
|
||||
**os.environ,
|
||||
"PATH": f"{fake_bin}:{os.environ['PATH']}",
|
||||
"GITHUB_API_URL": "https://gitea.example.test/api/v1",
|
||||
"GITHUB_REPOSITORY": "owner/project",
|
||||
"GITHUB_SERVER_URL": "https://gitea.example.test",
|
||||
"GITHUB_STEP_SUMMARY": str(summary),
|
||||
"MOCK_STATE_DIR": str(state),
|
||||
"MOCK_RELEASE_PAGINATION": "1",
|
||||
"MOCK_VERSION": "0.9.8",
|
||||
"PTLINUX_PYTHON": sys.executable,
|
||||
"PTLINUX_SIGNING_KEY": str(private_key),
|
||||
"PTLINUX_UPDATE_PUBLIC_KEY": str(public_key),
|
||||
"RELEASE_BRANCH": "main",
|
||||
"RELEASE_COMMIT": "a" * 40,
|
||||
"RELEASE_PRERELEASE": "false",
|
||||
"RELEASE_TAG": "v0.9.8",
|
||||
"RELEASE_TOKEN": "mock-release-token",
|
||||
}
|
||||
result = subprocess.run(
|
||||
[
|
||||
str(PROJECT_ROOT / "scripts/publish-gitea-release.sh"),
|
||||
"0.9.8",
|
||||
"stable",
|
||||
str(output),
|
||||
str(output / "release-notes.md"),
|
||||
],
|
||||
cwd=PROJECT_ROOT,
|
||||
env=environment,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
release_payload = json.loads(
|
||||
(state / "release-payload.json").read_text(encoding="utf-8")
|
||||
)
|
||||
self.assertFalse(release_payload["draft"])
|
||||
self.assertFalse(release_payload["prerelease"])
|
||||
self.assertEqual(release_payload["tag_name"], "v0.9.8")
|
||||
self.assertEqual(release_payload["body"], "Release notes.\n")
|
||||
self.assertEqual(len(list(state.glob("asset-*.json"))), 5)
|
||||
channel_payload = json.loads(
|
||||
(state / "channel-payload.json").read_text(encoding="utf-8")
|
||||
)
|
||||
paths = {operation["path"] for operation in channel_payload["files"]}
|
||||
self.assertIn("updates/stable.json", paths)
|
||||
self.assertIn("updates/stable.json.sig", paths)
|
||||
self.assertNotIn("updates/prerelease.json", paths)
|
||||
self.assertIn("updates/history.json", paths)
|
||||
self.assertIn("Status | Published", summary.read_text(encoding="utf-8"))
|
||||
self.assertNotIn("mock-release-token", result.stdout + result.stderr)
|
||||
|
||||
second = subprocess.run(
|
||||
[
|
||||
str(PROJECT_ROOT / "scripts/publish-gitea-release.sh"),
|
||||
"0.9.8",
|
||||
"stable",
|
||||
str(output),
|
||||
str(output / "release-notes.md"),
|
||||
],
|
||||
cwd=PROJECT_ROOT,
|
||||
env=environment,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
self.assertNotEqual(second.returncode, 0)
|
||||
self.assertIn("already exists", second.stderr)
|
||||
|
||||
def test_status_publication_rebuilds_signed_channel_metadata(self) -> None:
|
||||
openssl = shutil.which("openssl")
|
||||
jq = shutil.which("jq")
|
||||
if openssl is None or jq is None:
|
||||
self.skipTest("OpenSSL and jq are required")
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
fake_bin = root / "bin"
|
||||
fake_bin.mkdir()
|
||||
private_key = root / "private.pem"
|
||||
public_key = root / "public.pem"
|
||||
subprocess.run(
|
||||
[
|
||||
openssl,
|
||||
"genpkey",
|
||||
"-quiet",
|
||||
"-algorithm",
|
||||
"RSA",
|
||||
"-pkeyopt",
|
||||
"rsa_keygen_bits:2048",
|
||||
"-out",
|
||||
str(private_key),
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
subprocess.run(
|
||||
[
|
||||
openssl,
|
||||
"pkey",
|
||||
"-in",
|
||||
str(private_key),
|
||||
"-pubout",
|
||||
"-out",
|
||||
str(public_key),
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
manifest = root / "manifest.json"
|
||||
manifest.write_bytes(_publisher_manifest())
|
||||
manifest_signature = root / "manifest.json.sig"
|
||||
subprocess.run(
|
||||
[
|
||||
openssl,
|
||||
"dgst",
|
||||
"-sha256",
|
||||
"-sign",
|
||||
str(private_key),
|
||||
"-out",
|
||||
str(manifest_signature),
|
||||
str(manifest),
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
releases = root / "releases.json"
|
||||
releases.write_text(
|
||||
json.dumps(
|
||||
[
|
||||
{
|
||||
"assets": [
|
||||
{
|
||||
"browser_download_url": (
|
||||
"https://gitea.example.test/owner/project/"
|
||||
"releases/download/v1.8.0/manifest.json"
|
||||
),
|
||||
"name": "manifest.json",
|
||||
},
|
||||
{
|
||||
"browser_download_url": (
|
||||
"https://gitea.example.test/owner/project/"
|
||||
"releases/download/v1.8.0/manifest.json.sig"
|
||||
),
|
||||
"name": "manifest.json.sig",
|
||||
},
|
||||
],
|
||||
"draft": False,
|
||||
"html_url": (
|
||||
"https://gitea.example.test/owner/project/releases/tag/v1.8.0"
|
||||
),
|
||||
"id": 71,
|
||||
"name": "PristonTale EU Launcher 1.8.0",
|
||||
"prerelease": False,
|
||||
"published_at": "2026-09-08T12:00:00Z",
|
||||
"tag_name": "v1.8.0",
|
||||
}
|
||||
]
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
statuses = root / "statuses.json"
|
||||
statuses.write_text(
|
||||
(
|
||||
'{"releases":{"v1.8.0":{"config_schema":2,'
|
||||
'"minimum_config_schema":1,"status":"withdrawn"}},"schema":1}\n'
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
mock_curl = fake_bin / "curl"
|
||||
mock_curl.write_text(MOCK_HISTORY_CURL, encoding="utf-8")
|
||||
mock_curl.chmod(0o755)
|
||||
payload = root / "history-payload.json"
|
||||
summary = root / "summary.md"
|
||||
environment = {
|
||||
**os.environ,
|
||||
"PATH": f"{fake_bin}:{os.environ['PATH']}",
|
||||
"GITHUB_API_URL": "https://gitea.example.test/api/v1",
|
||||
"GITHUB_REPOSITORY": "owner/project",
|
||||
"GITHUB_SERVER_URL": "https://gitea.example.test",
|
||||
"GITHUB_STEP_SUMMARY": str(summary),
|
||||
"MOCK_HISTORY_PAYLOAD": str(payload),
|
||||
"MOCK_MANIFEST": str(manifest),
|
||||
"MOCK_MANIFEST_SIGNATURE": str(manifest_signature),
|
||||
"MOCK_RELEASES": str(releases),
|
||||
"PTLINUX_PYTHON": sys.executable,
|
||||
"PTLINUX_SIGNING_KEY": str(private_key),
|
||||
"PTLINUX_UPDATE_PUBLIC_KEY": str(public_key),
|
||||
"RELEASE_BRANCH": "main",
|
||||
"RELEASE_STATUS_FILE": str(statuses),
|
||||
"RELEASE_TOKEN": "mock-release-token",
|
||||
}
|
||||
result = subprocess.run(
|
||||
[str(PROJECT_ROOT / "scripts/publish-release-history.sh"), str(root / "dist")],
|
||||
cwd=PROJECT_ROOT,
|
||||
env=environment,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
published = json.loads(payload.read_text(encoding="utf-8"))
|
||||
files = {item["path"]: item for item in published["files"]}
|
||||
self.assertEqual(
|
||||
set(files),
|
||||
{
|
||||
"updates/history.json",
|
||||
"updates/history.json.sig",
|
||||
"updates/stable.json",
|
||||
"updates/stable.json.sig",
|
||||
},
|
||||
)
|
||||
stable = json.loads(base64.b64decode(files["updates/stable.json"]["content"]))
|
||||
history = json.loads(base64.b64decode(files["updates/history.json"]["content"]))
|
||||
self.assertEqual(stable["status"], "withdrawn")
|
||||
self.assertEqual(history["releases"][0]["status"], "withdrawn")
|
||||
self.assertEqual(
|
||||
history["releases"][0]["compatibility"],
|
||||
{"config_schema": 2, "minimum_config_schema": 1},
|
||||
)
|
||||
self.assertEqual(
|
||||
stable["compatibility"]["config_schema"],
|
||||
2,
|
||||
)
|
||||
published_stable = root / "published-stable.json"
|
||||
published_stable_signature = root / "published-stable.json.sig"
|
||||
published_stable.write_bytes(
|
||||
base64.b64decode(files["updates/stable.json"]["content"])
|
||||
)
|
||||
published_stable_signature.write_bytes(
|
||||
base64.b64decode(files["updates/stable.json.sig"]["content"])
|
||||
)
|
||||
verify_signature(published_stable, published_stable_signature, public_key)
|
||||
self.assertIn("stable channel: `1.8.0`", summary.read_text(encoding="utf-8"))
|
||||
self.assertNotIn("mock-release-token", result.stdout + result.stderr)
|
||||
|
||||
def test_api_failures_never_report_publication_success(self) -> None:
|
||||
if shutil.which("jq") is None:
|
||||
self.skipTest("jq is required")
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
output = root / "dist"
|
||||
state = root / "state"
|
||||
fake_bin = root / "bin"
|
||||
for directory in (output, state, fake_bin):
|
||||
directory.mkdir()
|
||||
archive = output / "PristonTale-EU-0.9.8.tar.gz"
|
||||
archive.write_bytes(b"archive fixture")
|
||||
(output / f"{archive.name}.sha256").write_text(
|
||||
f"{'a' * 64} {archive.name}\n",
|
||||
encoding="ascii",
|
||||
)
|
||||
for name in ("manifest.json", "manifest.json.sig", "release-notes.md"):
|
||||
(output / name).write_text("fixture\n", encoding="utf-8")
|
||||
fake_curl = fake_bin / "curl"
|
||||
fake_curl.write_text(MOCK_CURL, encoding="utf-8")
|
||||
fake_curl.chmod(0o755)
|
||||
base_environment = {
|
||||
**os.environ,
|
||||
"PATH": f"{fake_bin}:{os.environ['PATH']}",
|
||||
"GITHUB_API_URL": "https://gitea.example.test/api/v1",
|
||||
"GITHUB_REPOSITORY": "owner/project",
|
||||
"GITHUB_SERVER_URL": "https://gitea.example.test",
|
||||
"MOCK_STATE_DIR": str(state),
|
||||
"MOCK_VERSION": "0.9.8",
|
||||
"RELEASE_BRANCH": "main",
|
||||
"RELEASE_COMMIT": "a" * 40,
|
||||
"RELEASE_PRERELEASE": "false",
|
||||
"RELEASE_TAG": "v0.9.8",
|
||||
"RELEASE_TOKEN": "mock-release-token",
|
||||
}
|
||||
command = [
|
||||
str(PROJECT_ROOT / "scripts/publish-gitea-release.sh"),
|
||||
"0.9.8",
|
||||
"stable",
|
||||
str(output),
|
||||
str(output / "release-notes.md"),
|
||||
]
|
||||
|
||||
cases = [
|
||||
("MOCK_EXISTING_STATUS", "401"),
|
||||
("MOCK_EXISTING_STATUS", "403"),
|
||||
("MOCK_EXISTING_STATUS", "500"),
|
||||
("MOCK_CREATE_STATUS", "404"),
|
||||
("MOCK_CREATE_STATUS", "409"),
|
||||
("MOCK_CREATE_STATUS", "422"),
|
||||
("MOCK_CREATE_STATUS", "500"),
|
||||
("MOCK_ASSET_STATUS", "500"),
|
||||
("MOCK_TIMEOUT_STAGE", "existing"),
|
||||
("MOCK_TIMEOUT_STAGE", "create"),
|
||||
("MOCK_TIMEOUT_STAGE", "asset"),
|
||||
]
|
||||
for variable, value in cases:
|
||||
with self.subTest(variable=variable, value=value):
|
||||
(state / "created").unlink(missing_ok=True)
|
||||
environment = {**base_environment, variable: value}
|
||||
result = subprocess.run(
|
||||
command,
|
||||
cwd=PROJECT_ROOT,
|
||||
env=environment,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertNotIn("Published stable release", result.stdout)
|
||||
self.assertNotIn("mock-release-token", result.stdout + result.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user