ci: Jenkins pipeline and tests for packaging/release tools
ysj7705/mudfish-decky/pipeline/head There was a failure building this commit
ysj7705/mudfish-decky/pipeline/head There was a failure building this commit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f33bc0a49f
commit
c2841e8f21
3 files changed
+205
No files matched your search
@@ -0,0 +1,135 @@
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
import zipfile
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
def load(name):
|
||||
spec = importlib.util.spec_from_file_location(name, os.path.join(ROOT, "scripts", f"{name}.py"))
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
package = load("package")
|
||||
gitea_release = load("gitea_release")
|
||||
|
||||
|
||||
def make_tree(root, version="0.3.1"):
|
||||
files = {
|
||||
"plugin.json": json.dumps({"name": "Mudfish VPN"}),
|
||||
"package.json": json.dumps({"name": "mudfish-decky", "version": version}),
|
||||
"main.py": "print('x')",
|
||||
"README.md": "# readme",
|
||||
"dist/index.js": "js",
|
||||
"py_modules/mudfish_decky/__init__.py": "",
|
||||
"py_modules/mudfish_decky/nodes.py": "x = 1",
|
||||
"py_modules/mudfish_decky/__pycache__/nodes.cpython-314.pyc": "junk",
|
||||
"tests/test_x.py": "not shipped",
|
||||
"src/index.tsx": "not shipped",
|
||||
}
|
||||
for rel, text in files.items():
|
||||
path = os.path.join(root, rel)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "w") as f:
|
||||
f.write(text)
|
||||
|
||||
|
||||
class PackageTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.src = os.path.join(self.tmp.name, "src")
|
||||
os.makedirs(self.src)
|
||||
make_tree(self.src)
|
||||
|
||||
def tearDown(self):
|
||||
self.tmp.cleanup()
|
||||
|
||||
def test_version_from_package_json(self):
|
||||
self.assertEqual(package.read_version(self.src), "0.3.1")
|
||||
|
||||
def test_zip_has_decky_layout_and_only_runtime_files(self):
|
||||
out = package.build_zip(self.src, os.path.join(self.tmp.name, "out"))
|
||||
self.assertEqual(os.path.basename(out), "mudfish-decky-0.3.1.zip")
|
||||
with zipfile.ZipFile(out) as z:
|
||||
names = sorted(z.namelist())
|
||||
self.assertEqual(names, [
|
||||
"mudfish-decky/README.md",
|
||||
"mudfish-decky/dist/index.js",
|
||||
"mudfish-decky/main.py",
|
||||
"mudfish-decky/package.json",
|
||||
"mudfish-decky/plugin.json",
|
||||
"mudfish-decky/py_modules/mudfish_decky/__init__.py",
|
||||
"mudfish-decky/py_modules/mudfish_decky/nodes.py",
|
||||
])
|
||||
|
||||
def test_missing_build_output_fails(self):
|
||||
os.remove(os.path.join(self.src, "dist", "index.js"))
|
||||
with self.assertRaises(SystemExit):
|
||||
package.build_zip(self.src, os.path.join(self.tmp.name, "out"))
|
||||
|
||||
|
||||
class FakeApi:
|
||||
"""Stands in for the Gitea REST API: (method, path) -> (status, json)."""
|
||||
|
||||
def __init__(self, existing_tag=None):
|
||||
self.existing_tag = existing_tag
|
||||
self.calls = []
|
||||
|
||||
def __call__(self, method, path, body=None, headers=None):
|
||||
self.calls.append((method, path, body, headers))
|
||||
if method == "GET" and path.endswith("/releases/tags/" + (self.existing_tag or "")) and self.existing_tag:
|
||||
return 200, {"id": 7, "tag_name": self.existing_tag}
|
||||
if method == "GET":
|
||||
return 404, {"message": "not found"}
|
||||
if method == "POST" and path.endswith("/releases"):
|
||||
return 201, {"id": 42, "html_url": "https://git.example/r/42"}
|
||||
if method == "POST" and "/assets" in path:
|
||||
return 201, {"id": 1, "browser_download_url": "https://git.example/a/1"}
|
||||
return 500, {}
|
||||
|
||||
|
||||
class GiteaReleaseTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.zip = os.path.join(self.tmp.name, "mudfish-decky-0.3.1.zip")
|
||||
with open(self.zip, "wb") as f:
|
||||
f.write(b"PK-zip-bytes")
|
||||
|
||||
def tearDown(self):
|
||||
self.tmp.cleanup()
|
||||
|
||||
def test_skips_when_release_exists(self):
|
||||
api = FakeApi(existing_tag="v0.3.1")
|
||||
result = gitea_release.publish(api, "ysj7705/mudfish-decky", "0.3.1", "abc123", self.zip)
|
||||
self.assertIsNone(result)
|
||||
self.assertEqual([c[0] for c in api.calls], ["GET"])
|
||||
|
||||
def test_creates_release_and_uploads_zip(self):
|
||||
api = FakeApi()
|
||||
result = gitea_release.publish(api, "ysj7705/mudfish-decky", "0.3.1", "abc123", self.zip)
|
||||
self.assertEqual(result, "https://git.example/a/1")
|
||||
method, path, body, _ = api.calls[1]
|
||||
self.assertEqual((method, path), ("POST", "/api/v1/repos/ysj7705/mudfish-decky/releases"))
|
||||
self.assertEqual(body["tag_name"], "v0.3.1")
|
||||
self.assertEqual(body["target_commitish"], "abc123")
|
||||
method, path, body, headers = api.calls[2]
|
||||
self.assertEqual(method, "POST")
|
||||
self.assertEqual(path, "/api/v1/repos/ysj7705/mudfish-decky/releases/42/assets?name=mudfish-decky-0.3.1.zip")
|
||||
self.assertIn(b"PK-zip-bytes", body)
|
||||
self.assertIn("multipart/form-data; boundary=", headers["Content-Type"])
|
||||
|
||||
def test_api_error_raises(self):
|
||||
def forbidden(method, path, body=None, headers=None):
|
||||
return (404, {}) if method == "GET" else (403, {"message": "token lacks scope"})
|
||||
|
||||
with self.assertRaises(gitea_release.ReleaseError):
|
||||
gitea_release.publish(forbidden, "o/r", "0.3.1", "abc", self.zip)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user