Check and Windows preview / check (anvil) (push) Successful in 2m5s
Check and Windows preview / check (crucible) (push) Successful in 2m11s
Check and Windows preview / check (lathe) (push) Successful in 2m22s
Check and Windows preview / check (patina) (push) Successful in 2m50s
Check and Windows preview / check (rivet) (push) Successful in 46s
Check and Windows preview / check (temper) (push) Successful in 41s
Check and Windows preview / check (weld) (push) Successful in 39s
Check and Windows preview / previews (push) Failing after 24s
Check and Windows preview / windows (push) Skipped
Set the CLAP vendor of all seven plugins to "Bass Foundry" and name the combined preview ZIP bass-foundry-windows-x86_64.zip. CLAP IDs and saved state stay unchanged. Bring development.md and releases.md up to date with all seven products, the shared crates, the test types, the steps to add a product and the published releases. Add texture problems and seven texture plugin ideas to ideas.md.
68 lines
3.3 KiB
Python
68 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Package a combined preview bundle or an independently versioned plugin."""
|
|
import argparse
|
|
import hashlib
|
|
from pathlib import Path
|
|
import struct
|
|
import zipfile
|
|
from plugin_catalog import ROOT, archive_name, catalog, version
|
|
|
|
def pe_check(name, data):
|
|
if len(data) < 64 or data[:2] != b"MZ":
|
|
raise ValueError(f"{name}: expected Windows PE binary")
|
|
offset = struct.unpack_from("<I", data, 0x3C)[0]
|
|
if offset + 24 > len(data) or data[offset:offset+4] != b"PE\0\0" or struct.unpack_from("<H", data, offset+4)[0] != 0x8664:
|
|
raise ValueError(f"{name}: expected x86-64 Windows DLL")
|
|
if b"clap_entry\0" not in data:
|
|
raise ValueError(f"{name}: CLAP entry export missing")
|
|
|
|
def package(target="x86_64-pc-windows-gnu", plugin=None, root=ROOT):
|
|
products = catalog(root)
|
|
selected = {plugin: products[plugin]} if plugin else products
|
|
folder = root / "dist" / target
|
|
names = ["LICENSE", "docs/development.md", "docs/releases.md"]
|
|
for product in selected.values():
|
|
name = f"{product['name']}.clap"
|
|
pe_check(name, (folder / name).read_bytes())
|
|
names.append(name)
|
|
names.extend(p.relative_to(root).as_posix() for p in (root / product["documentation"]).rglob("*") if p.is_file())
|
|
names.extend(product.get("licenses", {}).values())
|
|
payload = {name: (folder / name).read_bytes() for name in sorted(set(names))}
|
|
if plugin:
|
|
product = selected[plugin]
|
|
release_version = version(plugin, root)
|
|
filename = archive_name(plugin, release_version)
|
|
payload["README.md"] = (
|
|
f"# {product['name']} {release_version}\n\n{product['description']}.\n\n"
|
|
f"Copy {product['name']}.clap into C:\\Program Files\\Common Files\\CLAP\\ "
|
|
"and rescan plugins in FL Studio 2024.1 or newer.\n\n"
|
|
f"See [{product['name']} guide]({product['documentation']}/README.md).\n"
|
|
).encode()
|
|
else:
|
|
filename = "bass-foundry-windows-x86_64.zip"
|
|
payload["README.md"] = (folder / "README.md").read_bytes()
|
|
checksums = "".join(f"{hashlib.sha256(data).hexdigest()} {name}\n" for name, data in sorted(payload.items()))
|
|
payload["SHA256SUMS"] = checksums.encode()
|
|
archive = root / "dist" / filename
|
|
with zipfile.ZipFile(archive, "w", compression=zipfile.ZIP_DEFLATED) as output:
|
|
for name, data in sorted(payload.items()):
|
|
info = zipfile.ZipInfo(name, date_time=(2020, 1, 1, 0, 0, 0))
|
|
info.compress_type = zipfile.ZIP_DEFLATED
|
|
info.external_attr = 0o100644 << 16
|
|
output.writestr(info, data)
|
|
# Stable ZIP metadata makes repeated packaging of identical files reproducible.
|
|
sidecar = archive.with_suffix(".zip.sha256")
|
|
sidecar.write_text(f"{hashlib.sha256(archive.read_bytes()).hexdigest()} {archive.name}\n", encoding="utf-8")
|
|
print(f"Packaged {archive}")
|
|
return archive
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--plugin", choices=catalog(), help="Omit for a combined development bundle")
|
|
parser.add_argument("--target", default="x86_64-pc-windows-gnu", choices=["x86_64-pc-windows-gnu", "x86_64-pc-windows-msvc"])
|
|
args = parser.parse_args()
|
|
package(args.target, args.plugin)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|