You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

286 lines
8.2KB

  1. import sys
  2. import os
  3. import glob
  4. import json
  5. import time
  6. import re
  7. import tempfile
  8. import random
  9. import functools
  10. import common
  11. import update_modulargrid
  12. import update_cache
  13. TOOLCHAIN_DIR = "../toolchain-v2"
  14. PACKAGES_DIR = "../packages"
  15. MANIFESTS_DIR = "manifests"
  16. RACK_SYSTEM_DIR = "../Rack2"
  17. RACK_USER_DIR = "$HOME/.local/share/Rack2"
  18. SCREENSHOTS_DIR = os.path.join(RACK_USER_DIR, "screenshots")
  19. PLUGIN_DIR = os.path.join(RACK_USER_DIR, "plugins-lin-x64")
  20. FILES_DIR = "../files/packages"
  21. def version_part_less(a, b):
  22. try:
  23. ai = int(a)
  24. except ValueError:
  25. ai = None
  26. try:
  27. bi = int(b)
  28. except ValueError:
  29. bi = None
  30. if ai is not None and bi is not None:
  31. return ai < bi
  32. elif ai is not None:
  33. return False
  34. elif bi is not None:
  35. return True
  36. else:
  37. return a < b
  38. def version_less(a, b):
  39. a_parts = a.split(".")
  40. b_parts = b.split(".")
  41. for a_part, b_part in zip(a_parts, b_parts):
  42. if a_part == b_part:
  43. continue
  44. return version_part_less(a_part, b_part)
  45. return len(a_parts) < len(b_parts)
  46. def package_exists(slug, version, os_, cpu):
  47. if os.path.isfile(os.path.join(PACKAGES_DIR, f"{slug}-{version}-{os_}-{cpu}.vcvplugin")):
  48. return True
  49. if cpu == 'x64':
  50. if os.path.isfile(os.path.join(PACKAGES_DIR, f"{slug}-{version}-{os_}.vcvplugin")):
  51. return True
  52. return False
  53. # Get missing Mac ARM64 slugs
  54. def print_missing_mac_arm64():
  55. emails = set()
  56. for manifest_path in glob.glob("manifests/*.json"):
  57. with open(manifest_path, "r") as f:
  58. manifest = json.load(f)
  59. slug = manifest['slug']
  60. version = manifest['version']
  61. author = manifest['author']
  62. license = manifest.get('license', "")
  63. email = manifest.get('authorEmail', "")
  64. if not package_exists(slug, version, 'mac', 'x64'):
  65. continue
  66. if package_exists(slug, version, 'mac', 'arm64'):
  67. continue
  68. print(f"{slug} {version}\t\t{email}\t\t{license}")
  69. # Update git before continuing
  70. common.system("git pull")
  71. common.system("git submodule sync --recursive --quiet")
  72. common.system("git submodule update --init --recursive")
  73. plugin_paths = sys.argv[1:]
  74. # Default to all repos, so all out-of-date repos are built
  75. if not plugin_paths:
  76. plugin_paths = glob.glob("repos/*")
  77. plugin_paths.sort()
  78. # Randomize repo order
  79. # plugin_paths = random.sample(plugin_paths, len(plugin_paths))
  80. manifest_versions = {}
  81. for plugin_path in plugin_paths:
  82. plugin_path = os.path.abspath(plugin_path)
  83. (plugin_basename, plugin_ext) = os.path.splitext(os.path.basename(plugin_path))
  84. # Get manifest
  85. # Source dir
  86. if os.path.isdir(plugin_path):
  87. manifest_filename = os.path.join(plugin_path, "plugin.json")
  88. try:
  89. # Read manifest
  90. with open(manifest_filename, "r") as f:
  91. manifest = json.load(f)
  92. except IOError:
  93. # Skip plugins without plugin.json
  94. continue
  95. slug = manifest['slug']
  96. version = manifest['version']
  97. # Plugin package
  98. elif plugin_ext == ".vcvplugin":
  99. m = re.match(r'^(.*)-(\d\..*?)-(.*?)-(.*?)$', plugin_basename)
  100. if not m:
  101. raise Exception(f"Filename {plugin_path} invalid format")
  102. slug = m[1]
  103. version = m[2]
  104. os_ = m[3]
  105. cpu = m[4]
  106. # Extract ZIP to temp dir
  107. try:
  108. tempdir = tempfile.mkdtemp()
  109. common.system(f'zstd -d < "{plugin_path}" | tar -x -C "{tempdir}"')
  110. # Read manifest
  111. manifest_filename = os.path.join(tempdir, slug, "plugin.json")
  112. with open(manifest_filename) as f:
  113. manifest = json.load(f)
  114. if manifest['slug'] != slug:
  115. raise Exception(f"Manifest slug {manifest['slug']} does not match filename slug {slug}")
  116. if manifest['version'] != version:
  117. raise Exception(f"Manifest version {manifest['version']} does not match filename version {version}")
  118. finally:
  119. common.system(f'rm -rf "{tempdir}"')
  120. else:
  121. raise Exception(f"Plugin {plugin_path} is not a valid format")
  122. # Slug blacklist
  123. # if slug == 'questionablemodules':
  124. # continue
  125. # Get library manifest
  126. library_manifest_filename = os.path.join(MANIFESTS_DIR, f"{slug}.json")
  127. library_manifest = None
  128. try:
  129. with open(library_manifest_filename, "r") as f:
  130. library_manifest = json.load(f)
  131. except IOError:
  132. # Warn if manifest is new
  133. # print(f"Manifest {slug} is new, press enter to approve.")
  134. # if input() != '':
  135. # continue
  136. pass
  137. # Ensure that no modules are removed from library
  138. if library_manifest:
  139. library_modules = set(module['slug'] for module in library_manifest['modules'])
  140. modules = set(module['slug'] for module in manifest['modules'])
  141. removed_modules = library_modules - modules
  142. if removed_modules:
  143. raise Exception(f"Plugin {slug} removes modules {", ".join(removed_modules)}")
  144. # Source dir
  145. if os.path.isdir(plugin_path):
  146. # Ensure that version is higher than library version
  147. if library_manifest:
  148. # if not version_less(library_manifest['version'], version):
  149. if library_manifest['version'] == version:
  150. continue
  151. print()
  152. print(f"Building {slug}")
  153. try:
  154. # Clean repo
  155. common.system(f'cd "{TOOLCHAIN_DIR}" && make plugin-build-clean')
  156. # Build repo for each arch
  157. common.system(f'cd "{TOOLCHAIN_DIR}" && make -j2 plugin-build-mac-arm64 PLUGIN_DIR="{plugin_path}"')
  158. common.system(f'cd "{TOOLCHAIN_DIR}" && make -j2 plugin-build-mac-x64 PLUGIN_DIR="{plugin_path}"')
  159. common.system(f'cd "{TOOLCHAIN_DIR}" && make -j2 plugin-build-win-x64 PLUGIN_DIR="{plugin_path}"')
  160. common.system(f'cd "{TOOLCHAIN_DIR}" && make -j2 plugin-build-lin-x64 PLUGIN_DIR="{plugin_path}"')
  161. # Copy package to packages dir
  162. common.system(f'cp -v "{TOOLCHAIN_DIR}"/plugin-build/* "{PACKAGES_DIR}"/')
  163. # Copy Mac packages to files dir for testing
  164. # common.system(f'cp -v "{TOOLCHAIN_DIR}"/plugin-build/*-mac-x64.vcvplugin "{FILES_DIR}"/')
  165. # common.system(f'cp -v "{TOOLCHAIN_DIR}"/plugin-build/*-mac-arm64.vcvplugin "{FILES_DIR}"/')
  166. # Install Linux package for testing
  167. common.system(f'cp -v "{TOOLCHAIN_DIR}"/plugin-build/*-lin-x64.vcvplugin "{PLUGIN_DIR}"/')
  168. except Exception as e:
  169. print(e)
  170. print(f"{slug} build failed")
  171. input()
  172. continue
  173. finally:
  174. common.system(f'cd "{TOOLCHAIN_DIR}" && make plugin-build-clean')
  175. # Open plugin issue thread
  176. os.system(f"xdg-open 'https://github.com/VCVRack/library/issues?q=is%3Aissue+sort%3Aupdated-desc+in%3Atitle+{slug}' &")
  177. # Plugin package
  178. elif plugin_ext == ".vcvplugin":
  179. # Review manifest for errors
  180. print(json.dumps(manifest, indent=" "))
  181. print("Press enter to approve manifest")
  182. input()
  183. # Copy package
  184. common.system(f'cp "{plugin_path}" "{PACKAGES_DIR}/"')
  185. # Update file timestamp
  186. package_filename = os.path.basename(plugin_path)
  187. common.system(f'touch "{PACKAGES_DIR}/{package_filename}"')
  188. # Copy Mac ARM64 package to files dir for testing
  189. # if os_ == 'mac' and cpu == 'arm64':
  190. # common.system(f'cp -v "{plugin_path}" "{FILES_DIR}"/')
  191. # Install Linux package for testing
  192. if os_ == 'lin':
  193. common.system(f'cp "{plugin_path}" "{PLUGIN_DIR}/"')
  194. # Copy manifest
  195. with open(library_manifest_filename, "w") as f:
  196. json.dump(manifest, f, indent=" ")
  197. # Delete screenshot cache
  198. screenshots_dir = os.path.join(SCREENSHOTS_DIR, slug)
  199. common.system(f'rm -rf "{screenshots_dir}"')
  200. manifest_versions[slug] = version
  201. manifest_versions_str = ", ".join(map(lambda pair: pair[0] + " to " + pair[1], manifest_versions.items()))
  202. if not manifest_versions:
  203. print("Nothing to build")
  204. exit(0)
  205. update_cache.update()
  206. update_modulargrid.update()
  207. # Test plugins
  208. print()
  209. print(f"Press enter to launch Rack and test the following packages: {manifest_versions_str}")
  210. input()
  211. try:
  212. common.system(f"cd {RACK_SYSTEM_DIR} && ./Rack")
  213. common.system(f"cd {RACK_USER_DIR} && grep -P '\\bwarn|debug\\b' log.txt || true")
  214. except:
  215. print(f"Rack failed! Enter to continue if desired")
  216. print(f"Press enter to generate screenshots, upload packages, upload screenshots, and commit/push the library repo.")
  217. input()
  218. # Generate screenshots
  219. try:
  220. common.system(f"cd {RACK_SYSTEM_DIR} && ./Rack -t 4")
  221. except:
  222. print(f"Rack failed! Enter to continue if desired")
  223. common.system("cd ../screenshots && make -j$(nproc)")
  224. # Upload packages
  225. common.system("cd ../packages && make upload")
  226. # Upload screenshots
  227. common.system("cd ../screenshots && make upload")
  228. # Commit git repo
  229. common.system("git add manifests")
  230. common.system("git add manifests-cache.json ModularGrid-VCVLibrary.json")
  231. common.system(f"git commit -m 'Update manifest {manifest_versions_str}'")
  232. common.system("git push")
  233. print()
  234. print(f"Updated {manifest_versions_str}")