Use Gitea Releases for installation and ship VS Code highlighting
This commit is contained in:
1 parent
a46bd6e132
commit
201bdee372
243 files changed
+3289
-12521
No files matched your search
@@ -88,6 +88,15 @@ class InstallerTests(unittest.TestCase):
|
||||
def create_assets(self, output):
|
||||
return assets.create(self.release, output, Path("/usr/bin/zstd"))
|
||||
|
||||
def release_record(self, directory, version="1.2.3"):
|
||||
return {
|
||||
"tag_name": f"v{version}", "draft": False, "prerelease": False,
|
||||
"assets": [{"name": path.name, "size": path.stat().st_size,
|
||||
"browser_download_url":
|
||||
f"https://git.dever.cc/shemic/dever/releases/download/v{version}/{path.name}"}
|
||||
for path in sorted(directory.iterdir())],
|
||||
}
|
||||
|
||||
def archive_payloads(self, path):
|
||||
contents = subprocess.run(["/usr/bin/zstd", "-q", "-d", "-c", str(path)],
|
||||
env={}, capture_output=True, check=True, timeout=15).stdout
|
||||
@@ -212,10 +221,14 @@ class InstallerTests(unittest.TestCase):
|
||||
assets_path = self.root / "assets"
|
||||
self.create_assets(assets_path)
|
||||
calls = []
|
||||
record = self.release_record(assets_path)
|
||||
with mock.patch.object(installer, "download", return_value=io.BytesIO(json.dumps(record).encode())):
|
||||
version, attachments = installer.resolve_release("latest")
|
||||
self.assertEqual(version, "1.2.3")
|
||||
|
||||
def download(address):
|
||||
self.assertTrue(address.startswith(
|
||||
f"https://git.dever.cc/shemic/dever/dist/1.2.3/dever-{self.platform}."))
|
||||
f"https://git.dever.cc/shemic/dever/releases/download/v1.2.3/dever-{self.platform}."))
|
||||
return (assets_path / address.rsplit("/", 1)[-1]).open("rb")
|
||||
|
||||
def extract(command, **kwargs):
|
||||
@@ -233,8 +246,8 @@ class InstallerTests(unittest.TestCase):
|
||||
|
||||
with mock.patch.object(installer, "download", side_effect=download), \
|
||||
mock.patch.object(installer.subprocess, "run", side_effect=extract):
|
||||
installer.payload(self.directory("network"), None, "1.2.3", self.platform,
|
||||
self.catalog, self.public)
|
||||
installer.payload(self.directory("network"), None, self.platform,
|
||||
self.catalog, self.public, attachments)
|
||||
self.assertEqual(len(calls), 1)
|
||||
self.assertEqual(list(self.root.glob(".extract-*")), [])
|
||||
|
||||
@@ -244,7 +257,7 @@ class InstallerTests(unittest.TestCase):
|
||||
with mock.patch.object(installer, "download", side_effect=download), \
|
||||
mock.patch.object(installer.subprocess, "run") as execute, \
|
||||
self.assertRaises(ValueError):
|
||||
installer.payload(self.release, None, "1.2.3", self.platform, self.catalog, self.public)
|
||||
installer.payload(self.release, None, self.platform, self.catalog, self.public, attachments)
|
||||
execute.assert_not_called()
|
||||
|
||||
def test_download_archive_is_bounded(self):
|
||||
@@ -255,19 +268,19 @@ class InstallerTests(unittest.TestCase):
|
||||
|
||||
def test_local_payload_rejects_symlink_and_source_directory(self):
|
||||
copied = self.directory("copied")
|
||||
installer.payload(copied, self.release, "1.2.3", self.platform, self.catalog)
|
||||
installer.payload(copied, self.release, self.platform, self.catalog)
|
||||
self.assertEqual((copied / "dever-core").read_bytes(), self.payloads["dever-core"])
|
||||
core = self.release / "dever-core"
|
||||
core.unlink()
|
||||
core.symlink_to(self.public)
|
||||
with self.assertRaisesRegex(ValueError, "符号链接"):
|
||||
installer.payload(self.directory("symlink"), self.release, "1.2.3", self.platform, self.catalog)
|
||||
installer.payload(self.directory("symlink"), self.release, self.platform, self.catalog)
|
||||
with self.assertRaisesRegex(ValueError, "symlinks"):
|
||||
self.create_assets(self.root / "invalid-assets")
|
||||
core.unlink()
|
||||
core.mkdir()
|
||||
with self.assertRaisesRegex(ValueError, "类型"):
|
||||
installer.payload(self.directory("directory"), self.release, "1.2.3", self.platform, self.catalog)
|
||||
installer.payload(self.directory("directory"), self.release, self.platform, self.catalog)
|
||||
|
||||
def test_image_install_authenticates_before_mock_bootstrap_and_cleans_owned_staging(self):
|
||||
image = self.directory("image")
|
||||
@@ -302,27 +315,83 @@ class InstallerTests(unittest.TestCase):
|
||||
self.assertEqual(len(calls), 1)
|
||||
self.assertEqual(list(image.iterdir()), [])
|
||||
|
||||
def test_metadata_uses_latest_or_exact_directory_and_never_a_github_fallback(self):
|
||||
for version in ("latest", "1.2.3"):
|
||||
destination = self.directory(version)
|
||||
def test_metadata_pins_latest_or_exact_release_before_downloading_matching_assets(self):
|
||||
assets_path = self.root / "assets"
|
||||
self.create_assets(assets_path)
|
||||
record = self.release_record(assets_path)
|
||||
for requested, endpoint in (("latest", "latest"), ("1.2.3", "tags/v1.2.3")):
|
||||
destination = self.directory(requested)
|
||||
addresses = []
|
||||
|
||||
def download(address):
|
||||
addresses.append(address)
|
||||
return io.BytesIO(b"metadata")
|
||||
if address == f"{installer.RELEASES}/{endpoint}":
|
||||
return io.BytesIO(json.dumps(record).encode())
|
||||
return (assets_path / address.rsplit("/", 1)[-1]).open("rb")
|
||||
|
||||
with mock.patch.object(installer, "download", side_effect=download):
|
||||
installer.metadata(destination, None, version, self.platform)
|
||||
self.assertEqual(addresses, [
|
||||
f"https://git.dever.cc/shemic/dever/dist/{version}/dever-{self.platform}.{suffix}"
|
||||
selected, attachments = installer.resolve_release(requested)
|
||||
installer.metadata(destination, None, attachments, self.platform)
|
||||
authenticated, _, _ = installer.authenticate(destination, self.public, self.scratch,
|
||||
self.platform, selected)
|
||||
self.assertEqual(authenticated, "1.2.3")
|
||||
self.assertEqual(addresses, [f"{installer.RELEASES}/{endpoint}"] + [
|
||||
f"https://git.dever.cc/shemic/dever/releases/download/v1.2.3/dever-{self.platform}.{suffix}"
|
||||
for suffix in ("manifest.json", "manifest.sig")
|
||||
])
|
||||
|
||||
def test_release_metadata_rejects_unpublished_ambiguous_and_foreign_assets(self):
|
||||
directory = self.root / "assets"
|
||||
self.create_assets(directory)
|
||||
valid = self.release_record(directory)
|
||||
mutations = [
|
||||
lambda record: record.update(draft=True),
|
||||
lambda record: record.update(prerelease=True),
|
||||
lambda record: record.update(tag_name="v01.2.3"),
|
||||
lambda record: record.update(tag_name="v1.2.4"),
|
||||
lambda record: record["assets"].append(record["assets"][0]),
|
||||
lambda record: record["assets"][0].update(size=True),
|
||||
lambda record: record["assets"][0].update(browser_download_url="https://evil.example/asset"),
|
||||
lambda record: record.update(assets=[]),
|
||||
lambda record: record.update(assets=[record["assets"][0]] * 513),
|
||||
]
|
||||
for mutate in mutations:
|
||||
record = json.loads(json.dumps(valid))
|
||||
mutate(record)
|
||||
with self.subTest(record=record["tag_name"]), \
|
||||
mock.patch.object(installer, "download", return_value=io.BytesIO(json.dumps(record).encode())), \
|
||||
self.assertRaises(ValueError):
|
||||
installer.resolve_release("1.2.3")
|
||||
with mock.patch.object(installer, "MAX_METADATA", 10), \
|
||||
mock.patch.object(installer, "download", return_value=io.BytesIO(b" " * 11)), \
|
||||
self.assertRaisesRegex(ValueError, "大小限制"):
|
||||
installer.resolve_release("latest")
|
||||
|
||||
def test_latest_tag_must_match_signed_manifest_and_required_assets_must_exist(self):
|
||||
directory = self.root / "assets"
|
||||
self.create_assets(directory)
|
||||
record = self.release_record(directory, "1.2.4")
|
||||
with mock.patch.object(installer, "download", return_value=io.BytesIO(json.dumps(record).encode())):
|
||||
selected, attachments = installer.resolve_release("latest")
|
||||
with self.assertRaisesRegex(ValueError, "版本与请求不匹配"):
|
||||
installer.authenticate(self.release, self.public, self.scratch, self.platform, selected)
|
||||
with self.assertRaisesRegex(ValueError, "缺少附件"):
|
||||
installer.asset(attachments, self.platform, "missing.tar.zst")
|
||||
with self.assertRaisesRegex(ValueError, "大小限制"):
|
||||
installer.asset(attachments, self.platform, "manifest.json", 1)
|
||||
|
||||
def test_metadata_rejects_truncated_api_attachment(self):
|
||||
name = f"dever-{self.platform}.manifest.json"
|
||||
attachments = {name: {"address": "https://git.dever.cc/asset", "bytes": 20}}
|
||||
with mock.patch.object(installer, "download", return_value=io.BytesIO(b"short")), \
|
||||
self.assertRaisesRegex(ValueError, "长度不匹配"):
|
||||
installer.metadata(self.directory("truncated"), None, attachments, self.platform)
|
||||
|
||||
def test_redirects_remain_on_the_official_https_site(self):
|
||||
handler = installer.OfficialRedirect()
|
||||
current = "https://git.dever.cc/shemic/dever/dist/latest/a"
|
||||
current = "https://git.dever.cc/shemic/dever/releases/download/v1.2.3/a"
|
||||
request = installer.urllib.request.Request(current)
|
||||
allowed = "https://git.dever.cc/shemic/dever/dist/1.2.3/a"
|
||||
allowed = "https://git.dever.cc/attachments/fixture"
|
||||
redirected = handler.redirect_request(request, None, 302, "Found", {}, allowed)
|
||||
self.assertEqual(redirected.full_url, allowed)
|
||||
for address in (
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
@@ -198,18 +199,20 @@ def install_assets(assets, image, manifest):
|
||||
# Only transport is local: signature verification, full tar extraction and
|
||||
# the final bootstrap executable are the real first-install implementation.
|
||||
installer = load_installer()
|
||||
stem = f"dever-{manifest['platform']}"
|
||||
base = installer.RELEASES
|
||||
routes = {
|
||||
f"{base}/latest/{stem}.manifest.json": assets / f"{stem}.manifest.json",
|
||||
f"{base}/latest/{stem}.manifest.sig": assets / f"{stem}.manifest.sig",
|
||||
f"{base}/{manifest['version']}/{stem}.tar.zst": assets / f"{stem}.tar.zst",
|
||||
base = f"https://git.dever.cc/shemic/dever/releases/download/v{manifest['version']}"
|
||||
routes = {f"{base}/{path.name}": path for path in assets.iterdir() if path.is_file()}
|
||||
record = {
|
||||
"tag_name": f"v{manifest['version']}", "draft": False, "prerelease": False,
|
||||
"assets": [{"name": path.name, "size": path.stat().st_size,
|
||||
"browser_download_url": address} for address, path in routes.items()],
|
||||
}
|
||||
for artifact in manifest["artifacts"]:
|
||||
if artifact["path"] == "bootstrap/dever" or artifact["path"].startswith("bootstrap/lib/"):
|
||||
name = f"{stem}.blob-{artifact['sha256']}"
|
||||
routes[f"{base}/{manifest['version']}/{name}"] = assets / name
|
||||
installer.download = lambda address: routes[address].open("rb")
|
||||
|
||||
def download(address):
|
||||
if address == f"{installer.RELEASES}/latest":
|
||||
return io.BytesIO(json.dumps(record).encode())
|
||||
return routes[address].open("rb")
|
||||
|
||||
installer.download = download
|
||||
installer.install(argparse.Namespace(
|
||||
version="latest", release=None, system_root=image, no_start=True,
|
||||
trusted_key=WORKSPACE / "skills/assets/release.pub",
|
||||
|
||||
Reference in new issue
Block a user