44from __future__ import annotations
55
66import argparse
7+ import json
78import shutil
89from dataclasses import dataclass
910from pathlib import Path
1011
12+ from pydevices_package_metadata import PYDEVICES_DESCRIPTIONS
13+
1114
1215@dataclass (frozen = True )
1316class Profile :
@@ -76,16 +79,12 @@ class Profile:
7679 ),
7780}
7881
79- PROFILE_REPOSITORIES = {
80- "palettes" : "PyDevices/palettes" ,
81- "pdwidgets" : "PyDevices/pdwidgets" ,
82- "pygraphics" : "PyDevices/pygraphics" ,
83- "pydevices" : "PyDevices/pydevices" ,
84- # Moved from PyDevices/audioif in the audioif/audiocomponents split
85- # (audiocomponents#2, 2026-09-03); audioif publishes the core only now.
86- "audioinstruments" : "PyDevices/audiocomponents" ,
87- "audioeffects" : "PyDevices/audiocomponents" ,
88- }
82+ # The source repository for each profile is pydevices-lock.json in the MIP
83+ # checkout, not a second map in this script. reusable-synchronize-mip-package.yml
84+ # already keeps that lockfile on the runner; a hardcoded PROFILE_REPOSITORIES
85+ # table disagreed with it after the audioif/audiocomponents split and blocked
86+ # every publication until a new publishing-tools tag (#35).
87+ LOCKFILE_NAME = "pydevices-lock.json"
8988
9089# No internal dependency table: lib/ ships as a single MIP package, so the graph
9190# between its components is imports rather than package requirements. It was
@@ -98,6 +97,26 @@ class Profile:
9897PYDEVICES_DESKTOP_DIR = "board_configs/desktop"
9998
10099
100+ def lockfile_repository (mip_root : Path , profile : str ) -> str :
101+ """Return the GitHub repository the MIP lockfile names for *profile*.
102+
103+ A new profile is added to the lockfile deliberately, not auto-created.
104+ """
105+ lockfile = mip_root / LOCKFILE_NAME
106+ if not lockfile .is_file ():
107+ raise SystemExit (f"{ lockfile } is missing; add { profile !r} to { LOCKFILE_NAME } before publishing" )
108+ try :
109+ lock = json .loads (lockfile .read_text (encoding = "utf-8" ))
110+ except json .JSONDecodeError as exc :
111+ raise SystemExit (f"{ lockfile } is not valid JSON: { exc } " ) from exc
112+ if not isinstance (lock , dict ) or profile not in lock :
113+ raise SystemExit (f"{ profile !r} is not in { lockfile } ; add it before publishing" )
114+ entry = lock [profile ]
115+ if not isinstance (entry , dict ) or not entry .get ("repository" ):
116+ raise SystemExit (f"{ profile !r} in { lockfile } has no repository" )
117+ return str (entry ["repository" ])
118+
119+
101120def ignore_debris (_directory : str , names : list [str ]) -> set [str ]:
102121 # publishable() gates the top level; this gates everything nested inside a
103122 # package directory, which copytree would otherwise take wholesale. That is
@@ -155,9 +174,13 @@ def copy_component(source: Path, destination: Path) -> None:
155174
156175
157176def render_pydevices_manifest (name : str , version : str , requirements : tuple [str , ...], payloads : tuple [str , ...] = ()) -> str :
177+ try :
178+ description = PYDEVICES_DESCRIPTIONS [name ]
179+ except KeyError :
180+ raise SystemExit (f"no shared description for { name !r} " ) from None
158181 lines = [
159182 "metadata(" ,
160- f' description="PyDevices { name } ",' ,
183+ f" description={ description !r } ," ,
161184 f' version="{ version } ",' ,
162185 ' author="Brad Barnett",' ,
163186 ' license="MIT",' ,
@@ -223,15 +246,14 @@ def main() -> None:
223246 parser .add_argument ("--version" , required = True )
224247 args = parser .parse_args ()
225248
226- expected_repository = PROFILE_REPOSITORIES [args .profile ]
249+ source_repository = args .source_repository .resolve ()
250+ mip_root = args .mip_repository .resolve ()
251+ expected_repository = lockfile_repository (mip_root , args .profile )
227252 if args .source_repository_name != expected_repository :
228253 raise SystemExit (
229254 f"profile { args .profile !r} requires { expected_repository } , "
230255 f"not { args .source_repository_name } "
231256 )
232-
233- source_repository = args .source_repository .resolve ()
234- mip_root = args .mip_repository .resolve ()
235257 if args .profile == "pydevices" :
236258 synchronize_pydevices (source_repository , mip_root , args .version )
237259 return
0 commit comments