Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package 
python-comfyui-workflow-templates-core for openSUSE:Factory checked in at 
2026-10-15 12:25:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-comfyui-workflow-templates-core 
(Old)
 and      
/work/SRC/openSUSE:Factory/.python-comfyui-workflow-templates-core.new.3698736 
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-comfyui-workflow-templates-core"

Thu Oct 15 12:25:38 2026 rev:18 rq:1384887 version:0.3.373

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-comfyui-workflow-templates-core/python-comfyui-workflow-templates-core.changes
    2026-10-08 13:28:27.417476628 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-comfyui-workflow-templates-core.new.3698736/python-comfyui-workflow-templates-core.changes
       2026-10-15 12:25:45.597177493 +0200
@@ -1,0 +2,19 @@
+Thu Oct 15 04:52:59 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to version 0.3.373: the manifest now lists 618
+  templates (was 614)
+  * New, all owned by the media-assets-02 bundle: FishAudio Drama
+    3 dialogue and directed, Higgsfield motion transfer, and Qwen
+    Image 2.1 Turbo image edit plus text to image
+  * Drop the retired Luma Ray 2 image-to-video and text-to-video
+    entries from the media-api bundle
+  * The bump crosses 0.3.372, which added the six entries and
+    dropped the Luma pair; 0.3.373 only fixes the Qwen multiangle
+    entry id. Described together here as only 0.3.373 is submitted
+- No code or dependency change: loader.py is byte-identical,
+  the sdist still declares no runtime requirements, and the
+  json >= 0.1.102 and media-assets-02 >= 0.1.9 Requires floors
+  are still satisfied (the six new entries previews need
+  media-assets-02 0.1.14, not packaged yet)
+
+-------------------------------------------------------------------

Old:
----
  comfyui_workflow_templates_core-0.3.369.tar.gz

New:
----
  comfyui_workflow_templates_core-0.3.373.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-comfyui-workflow-templates-core.spec ++++++
--- /var/tmp/diff_new_pack.oNxmaT/_old  2026-10-15 12:25:46.375210049 +0200
+++ /var/tmp/diff_new_pack.oNxmaT/_new  2026-10-15 12:25:46.377210133 +0200
@@ -16,7 +16,7 @@
 #
 
 Name:           python-comfyui-workflow-templates-core
-Version:        0.3.369
+Version:        0.3.373
 Release:        0
 Summary:        Core helpers for ComfyUI workflow templates
 # Legal-Review-Notice: sdist ships no LICENSE file; upstream

++++++ comfyui_workflow_templates_core-0.3.369.tar.gz -> 
comfyui_workflow_templates_core-0.3.373.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/comfyui_workflow_templates_core-0.3.369/PKG-INFO 
new/comfyui_workflow_templates_core-0.3.373/PKG-INFO
--- old/comfyui_workflow_templates_core-0.3.369/PKG-INFO        2026-10-07 
18:01:12.141493300 +0200
+++ new/comfyui_workflow_templates_core-0.3.373/PKG-INFO        2026-10-13 
19:13:47.441332800 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: comfyui-workflow-templates-core
-Version: 0.3.369
+Version: 0.3.373
 Summary: Core helpers for ComfyUI workflow templates
 License-Expression: MIT
 Requires-Python: >=3.9
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/comfyui_workflow_templates_core-0.3.369/pyproject.toml 
new/comfyui_workflow_templates_core-0.3.373/pyproject.toml
--- old/comfyui_workflow_templates_core-0.3.369/pyproject.toml  2026-10-07 
18:00:41.900971000 +0200
+++ new/comfyui_workflow_templates_core-0.3.373/pyproject.toml  2026-10-13 
19:13:18.663047800 +0200
@@ -4,7 +4,7 @@
 
 [project]
 name = "comfyui-workflow-templates-core"
-version = "0.3.369"
+version = "0.3.373"
 description = "Core helpers for ComfyUI workflow templates"
 readme = {text = "Core helpers for ComfyUI workflow templates.", content-type 
= "text/plain"}
 requires-python = ">=3.9"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/comfyui_workflow_templates_core-0.3.369/src/comfyui_workflow_templates_core/manifest.json
 
new/comfyui_workflow_templates_core-0.3.373/src/comfyui_workflow_templates_core/manifest.json
--- 
old/comfyui_workflow_templates_core-0.3.369/src/comfyui_workflow_templates_core/manifest.json
       2026-10-07 18:01:09.568760000 +0200
+++ 
new/comfyui_workflow_templates_core-0.3.373/src/comfyui_workflow_templates_core/manifest.json
       2026-10-13 19:13:44.954892400 +0200
@@ -598,24 +598,6 @@
       }
     },
     {
-      "id": "api_luma_i2v",
-      "bundle": "media-api",
-      "version": "0.0.0",
-      "assets": [
-        {
-          "filename": "api_luma_i2v.json",
-          "sha256": 
"0c660c05d325a7fb6fbefcc106ceedac79ac59796c26949ae174c331290be8dd"
-        },
-        {
-          "filename": "api_luma_i2v-1.webp",
-          "sha256": 
"e4d981fa4e3f5359375de871b60d1e81fa46e51531add655b66ef4b8f84503fa"
-        }
-      ],
-      "cdn": {
-        "path": "media-api/api_luma_i2v/"
-      }
-    },
-    {
       "id": "api_luma_photon_i2i",
       "bundle": "media-api",
       "version": "0.0.0",
@@ -660,24 +642,6 @@
       }
     },
     {
-      "id": "api_luma_t2v",
-      "bundle": "media-api",
-      "version": "0.0.0",
-      "assets": [
-        {
-          "filename": "api_luma_t2v.json",
-          "sha256": 
"681de365515c93102b6abced05e86de093193162e2a83a3bdde327cbe78e8808"
-        },
-        {
-          "filename": "api_luma_t2v-1.webp",
-          "sha256": 
"b7a70af0a918c1f3ea9fb4c8216363fa2bf0f91b66509f2e2d27dbfb53f2ddbd"
-        }
-      ],
-      "cdn": {
-        "path": "media-api/api_luma_t2v/"
-      }
-    },
-    {
       "id": "api_meshy_image_to_model",
       "bundle": "media-api",
       "version": "0.0.0",
@@ -2476,7 +2440,7 @@
       "assets": [
         {
           "filename": "api_quiver_image_to_svg.json",
-          "sha256": 
"516d6219b353f82071832c5963e218c886119ee1bc624f16784944b1e56909fc"
+          "sha256": 
"d02146d03cbddb471bf4cfb37291e327a49363ab3d25e130e7172dae44aa516e"
         },
         {
           "filename": "api_quiver_image_to_svg-1.webp",
@@ -2494,7 +2458,7 @@
       "assets": [
         {
           "filename": "api_quiver_text_to_svg.json",
-          "sha256": 
"3e02c8c2c3f1436a396f1acdd069b5efd834e283618bd7eb3a7ce041ec75d2cc"
+          "sha256": 
"4c2bd3bee35502fb35d93053993867038e4c2495200f3dfe82ab67712223e595"
         },
         {
           "filename": "api_quiver_text_to_svg-1.webp",
@@ -5924,7 +5888,7 @@
       "assets": [
         {
           "filename": "api_tripo3_1_multiview_to_model.json",
-          "sha256": 
"057605bd9ae9a91f768f93319df5d3f636c341b3517067714f3e61a02737824a"
+          "sha256": 
"95ed72c82cb3c5be64e751c3debacbf350f856cdbde250520f7af6110508e25c"
         },
         {
           "filename": "api_tripo3_1_multiview_to_model-1.webp",
@@ -5942,7 +5906,7 @@
       "assets": [
         {
           "filename": "api_tripo3_1_text_to_model.json",
-          "sha256": 
"bd71b7e1e28e42ca0c88f3493d30fc5d9b1171aa3e99ee070696d34336d7ebc0"
+          "sha256": 
"72428f7c10598ef89db328c77a74f8b7a8c65c845e3ca797d508418089291e5c"
         },
         {
           "filename": "api_tripo3_1_text_to_model-1.webp",
@@ -6190,7 +6154,7 @@
       "assets": [
         {
           "filename": "index.json",
-          "sha256": 
"34211034f887adffb17ffdbcd31ec0aab854ed8b01bcaf75634a09421a437e1e"
+          "sha256": 
"8b584653f46e5c6efff6ef6728397c0a978a1db6176f0bd9cc03838847f87d78"
         }
       ],
       "cdn": {
@@ -6204,7 +6168,7 @@
       "assets": [
         {
           "filename": "index.ar.json",
-          "sha256": 
"ab610746cb6c73c8e79a81c4b07873901fd062639be9265f4f6433e65ee1f038"
+          "sha256": 
"619e811e1b5aff02af63a0452a3779baa9f94740708229150efa53c92cea413b"
         }
       ],
       "cdn": {
@@ -6218,7 +6182,7 @@
       "assets": [
         {
           "filename": "index.es.json",
-          "sha256": 
"e186fad112ebb1a886bec7e6e8ca6af3f4aee9c73d9f04834e131dd8f3869ccf"
+          "sha256": 
"da76405afbe29176dbad44c06be27f330b0ba416c77f5b2f3598c07d6114ee89"
         }
       ],
       "cdn": {
@@ -6232,7 +6196,7 @@
       "assets": [
         {
           "filename": "index.fa.json",
-          "sha256": 
"f4c33d0f2e4979329a009678fbc2b81c89dec63a56cbfc009a012428e4bdd03d"
+          "sha256": 
"29ff743109de0ac6df9e431ad05e7364f376d746a288a7a8406ae65b99c03529"
         }
       ],
       "cdn": {
@@ -6246,7 +6210,7 @@
       "assets": [
         {
           "filename": "index.fr.json",
-          "sha256": 
"4d556d971e8915b80ad5112af0f0ca5ca53860a2f7f25fdd726e0c9517ec7b9e"
+          "sha256": 
"8335e39b4f2c98e6f9c83e385d4eedd09d8225ae772063387efc37433402bf56"
         }
       ],
       "cdn": {
@@ -6260,7 +6224,7 @@
       "assets": [
         {
           "filename": "index.ja.json",
-          "sha256": 
"47f5b26870c7d1f3df91b28ab5493806084f309a4b358f5ed8ab8eaaba7b3a00"
+          "sha256": 
"148acd41b740fa08f1c6cc8eaaf911d1ddebb489864d8c84df5fe7ffe9fa0c6e"
         }
       ],
       "cdn": {
@@ -6274,7 +6238,7 @@
       "assets": [
         {
           "filename": "index.ko.json",
-          "sha256": 
"a35d8c9297a04ed843c7ca8ddc4723791b4b2a5b4cbc9e821bdfac915f7d2ea8"
+          "sha256": 
"43e7dce67e5a03fc8a66ec1f20f2769bd2a15f8616f7df0caf76558ed6b04a0a"
         }
       ],
       "cdn": {
@@ -6288,7 +6252,7 @@
       "assets": [
         {
           "filename": "index.mcp.json",
-          "sha256": 
"32dde137d4f291eda117549d8a799b451c3781d34beb8f592b01a81c969a8e36"
+          "sha256": 
"069abf7ee27c418d66eb47f9dc25b0a3d6a81648512e6d1517b0f1eb84fcbe0e"
         }
       ],
       "cdn": {
@@ -6302,7 +6266,7 @@
       "assets": [
         {
           "filename": "index.pt-BR.json",
-          "sha256": 
"b778efb324be90015174d2fdcdc7bedc1ab944e95e94537686974923a1534e05"
+          "sha256": 
"4fedf5149ed32c4a2729e820ea3220f352632a815f5d956865c245b872f041d1"
         }
       ],
       "cdn": {
@@ -6316,7 +6280,7 @@
       "assets": [
         {
           "filename": "index.ru.json",
-          "sha256": 
"cc129b169fd1ea25dbe7819aaf0a083476508b7dbac7175567247a81580d4525"
+          "sha256": 
"e3be40b1113057bc3e9ef9d64aac2182893b23d3af0097204c8132b832a3b48b"
         }
       ],
       "cdn": {
@@ -6344,7 +6308,7 @@
       "assets": [
         {
           "filename": "index.tr.json",
-          "sha256": 
"069b04940eaa7b6e4f96ed10fe320af2aa48e86460398d8b10867c872c7957cc"
+          "sha256": 
"67e44cc6e1208a2faf323a4d57f1ff0ca75fd1799c8c9507d8827c0affa2de61"
         }
       ],
       "cdn": {
@@ -6358,7 +6322,7 @@
       "assets": [
         {
           "filename": "index.zh.json",
-          "sha256": 
"c0d66f68a3a66c8432cb8ff584773507eca351a1e858051b585613d521294e3a"
+          "sha256": 
"aef34bb284de6753627d5fbae77ecd26f530f10a949180a3378b1675ae784d83"
         }
       ],
       "cdn": {
@@ -6372,7 +6336,7 @@
       "assets": [
         {
           "filename": "index.zh-TW.json",
-          "sha256": 
"8693fe44f0055721c200b4782464050a2b452c32319d427dc2cf059b00c17b04"
+          "sha256": 
"ccbdb3fdf1749bd9806d76cc888a8682f6949db46f90625867e4b7161790fac6"
         }
       ],
       "cdn": {
@@ -6386,7 +6350,7 @@
       "assets": [
         {
           "filename": "index_logo.json",
-          "sha256": 
"071dc2dd15d1f0b29db655d679f6a1569c3e64ec564df15b24a8082f384815c6"
+          "sha256": 
"50311fb442b664ef0a61f6b1a700bb8244c5aee79d9e2b0669ccf546fd859ef3"
         },
         {
           "filename": "logo/anthropic.webp",
@@ -6435,6 +6399,11 @@
           "bundle": "media-assets-02"
         },
         {
+          "filename": "logo/higgsfield.png",
+          "sha256": 
"22e061f4c2779478b03e7526747b6591a2090c45f43bf4c4bf7da3acbfb64457",
+          "bundle": "media-assets-02"
+        },
+        {
           "filename": "logo/hitpaw.png",
           "sha256": 
"b0f08587a2a7de1d1edb70e5aa07c621b93a9f4411702a84a0d47f144c468a3a"
         },
@@ -10978,7 +10947,7 @@
       "assets": [
         {
           "filename": "video_wan_animate2.json",
-          "sha256": 
"c7e67adc03ce0414c11554caf155aa81fb47bf29e8184135768419f91182c845"
+          "sha256": 
"951fb2140c55ef8011a1ddc60639b0c76a5bd99a77212a7ef587af1b7df74652"
         },
         {
           "filename": "video_wan_animate2-1.webp",
@@ -11192,6 +11161,42 @@
       }
     },
     {
+      "id": "api_fishaudio_drama3_dialogue",
+      "bundle": "media-assets-02",
+      "version": "0.0.0",
+      "assets": [
+        {
+          "filename": "api_fishaudio_drama3_dialogue.json",
+          "sha256": 
"04ada6ece59b0da654e76913c69e887708ac2fd7f992260f3823c4be29db656a"
+        },
+        {
+          "filename": "api_fishaudio_drama3_dialogue-1.webp",
+          "sha256": 
"a1b6cc6cdfdb6cdfbe9ddd083f74924ff8b77bc31f5a0592b28fe095542f9b53"
+        }
+      ],
+      "cdn": {
+        "path": "media-assets-02/api_fishaudio_drama3_dialogue/"
+      }
+    },
+    {
+      "id": "api_fishaudio_drama3_directed",
+      "bundle": "media-assets-02",
+      "version": "0.0.0",
+      "assets": [
+        {
+          "filename": "api_fishaudio_drama3_directed.json",
+          "sha256": 
"1c08755afe00350353d3db87c94e493126c3761bc7031f0ac4f3e5178befc190"
+        },
+        {
+          "filename": "api_fishaudio_drama3_directed-1.webp",
+          "sha256": 
"cc57d7afd4f35ed101c7957b1b5a78fb604f10d17a3699785d956a29d6e69235"
+        }
+      ],
+      "cdn": {
+        "path": "media-assets-02/api_fishaudio_drama3_directed/"
+      }
+    },
+    {
       "id": "api_grok_imagine_video_1_5_lite_i2v",
       "bundle": "media-assets-02",
       "version": "0.0.0",
@@ -11282,6 +11287,24 @@
       }
     },
     {
+      "id": "api_higgsfield_genjutsu_motion_transfer",
+      "bundle": "media-assets-02",
+      "version": "0.0.0",
+      "assets": [
+        {
+          "filename": "api_higgsfield_genjutsu_motion_transfer.json",
+          "sha256": 
"0e07dbc509eb7be12eabaf1104eb88721fec6dec9c489f01bb223d3ecd457690"
+        },
+        {
+          "filename": "api_higgsfield_genjutsu_motion_transfer-1.webp",
+          "sha256": 
"fba1ea8748d98209e343b44b436776bbd0ca77ce7e086fdc11a1bd4fcfc111e3"
+        }
+      ],
+      "cdn": {
+        "path": "media-assets-02/api_higgsfield_genjutsu_motion_transfer/"
+      }
+    },
+    {
       "id": "api_ideogram_v4_5_image_edit",
       "bundle": "media-assets-02",
       "version": "0.0.0",
@@ -11498,7 +11521,7 @@
       "assets": [
         {
           "filename": "api_quiver_arrow2_image_to_svg.json",
-          "sha256": 
"a706b115c58167b63b5f658ad6079760d73dc5a4915aedc13316c80efeb78cb4"
+          "sha256": 
"99bd727308518a48deb99745115157eca7edf717b39734230483d97be04142f0"
         },
         {
           "filename": "api_quiver_arrow2_image_to_svg-1.webp",
@@ -11520,7 +11543,7 @@
       "assets": [
         {
           "filename": "api_quiver_arrow2_text_to_svg.json",
-          "sha256": 
"f6b1a5aa4808f27129e90c32b15897d4389d7fbc1d796e0f3a4065725ac034d7"
+          "sha256": 
"bbf10674003489b0696577fa31ad57434448bfe98369c072a780e83ae62f8ff1"
         },
         {
           "filename": "api_quiver_arrow2_text_to_svg-1.webp",
@@ -11862,6 +11885,64 @@
       }
     },
     {
+      "id": "image_qwen_image_2_1_turbo_image_edit",
+      "bundle": "media-assets-02",
+      "version": "0.0.0",
+      "assets": [
+        {
+          "filename": "image_qwen_image_2_1_turbo_image_edit.json",
+          "sha256": 
"5a9622d550866800414afcb9c70523ec5c6965eb171476e401e243a7fe8d5a4e"
+        },
+        {
+          "filename": "image_qwen_image_2_1_turbo_image_edit-1.webp",
+          "sha256": 
"93b2d2fc22eb91efd34e9d0c5e07505727629377c20852c15bd23de5e16ab0fc"
+        },
+        {
+          "filename": "image_qwen_image_2_1_turbo_image_edit-2.webp",
+          "sha256": 
"289cc45cb3c7c315d7c0bf5d6b75de9d4b89dd77f54647908e030fc3f1226c82"
+        }
+      ],
+      "cdn": {
+        "path": "media-assets-02/image_qwen_image_2_1_turbo_image_edit/"
+      }
+    },
+    {
+      "id": "image_qwen_image_2_1_turbo_t2i",
+      "bundle": "media-assets-02",
+      "version": "0.0.0",
+      "assets": [
+        {
+          "filename": "image_qwen_image_2_1_turbo_t2i.json",
+          "sha256": 
"3eb9cef2866c0186fbe30172701f112b35e574cf3c6575c6e4a6b5dd1e76a403"
+        },
+        {
+          "filename": "image_qwen_image_2_1_turbo_t2i-1.webp",
+          "sha256": 
"d16ac0f6130e6a4d3ada442d02c3b81db678031cfcdfdd0ca608b86222947d08"
+        }
+      ],
+      "cdn": {
+        "path": "media-assets-02/image_qwen_image_2_1_turbo_t2i/"
+      }
+    },
+    {
+      "id": "template_qwen_multiangle.app",
+      "bundle": "media-assets-02",
+      "version": "0.0.0",
+      "assets": [
+        {
+          "filename": "template_qwen_multiangle.app.json",
+          "sha256": 
"ee47cfe4a9a7885bd6a99bccb468a5e8b6595477e30a2849114d35657ecd5847"
+        },
+        {
+          "filename": "template_qwen_multiangle.app-1.webp",
+          "sha256": 
"ee4db80e6f4682744b47e1b7f7d9fce7a6ac3270b94c68d5c25ab4d97a6de356"
+        }
+      ],
+      "cdn": {
+        "path": "media-assets-02/template_qwen_multiangle.app/"
+      }
+    },
+    {
       "id": "utility_moge3_geometry_estimation",
       "bundle": "media-assets-02",
       "version": "0.0.0",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/comfyui_workflow_templates_core-0.3.369/src/comfyui_workflow_templates_core.egg-info/PKG-INFO
 
new/comfyui_workflow_templates_core-0.3.373/src/comfyui_workflow_templates_core.egg-info/PKG-INFO
--- 
old/comfyui_workflow_templates_core-0.3.369/src/comfyui_workflow_templates_core.egg-info/PKG-INFO
   2026-10-07 18:01:12.127464300 +0200
+++ 
new/comfyui_workflow_templates_core-0.3.373/src/comfyui_workflow_templates_core.egg-info/PKG-INFO
   2026-10-13 19:13:47.426462200 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: comfyui-workflow-templates-core
-Version: 0.3.369
+Version: 0.3.373
 Summary: Core helpers for ComfyUI workflow templates
 License-Expression: MIT
 Requires-Python: >=3.9
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/comfyui_workflow_templates_core-0.3.369/tests/test_ci_version_manager.py 
new/comfyui_workflow_templates_core-0.3.373/tests/test_ci_version_manager.py
--- 
old/comfyui_workflow_templates_core-0.3.369/tests/test_ci_version_manager.py    
    2026-10-07 18:00:41.903253600 +0200
+++ 
new/comfyui_workflow_templates_core-0.3.373/tests/test_ci_version_manager.py    
    2026-10-13 19:13:18.664749000 +0200
@@ -1,8 +1,11 @@
 """Regression tests for release-time package version detection."""
 
 import importlib.util
+import json
+import os
 import subprocess
 import sys
+import tempfile
 import unittest
 from pathlib import Path
 from unittest.mock import patch
@@ -28,53 +31,204 @@
         ci_version_manager._committed_files_cache.clear()
         ci_version_manager._unstaged_files_cache = None
 
-    def test_release_looks_before_pr_merge_base_for_package_version(self):
-        """A version-only release PR must see unreleased changes already on 
main."""
+    def test_release_uses_tag_and_bumps_core_json_once(self):
+        """The release snapshot includes changes already merged to main."""
         calls = []
 
         def fake_run_git(args):
             calls.append(args)
-            if args[0] == "log":
-                return "version-intro\nolder-version"
-            if args[:2] == ["show", 
"version-intro:packages/core/pyproject.toml"]:
-                return '[project]\nversion = "1.2.3"\n'
-            if args[:2] == ["show", 
"older-version:packages/core/pyproject.toml"]:
-                return '[project]\nversion = "1.2.2"\n'
-            raise AssertionError(f"Unexpected git invocation: {args}")
+            if args[0] == "tag":
+                return "v1.0.0"
+            if args[0] == "show":
+                return '{"templates": []}'
+            if args[0] == "diff":
+                return ""
+            raise AssertionError(args)
 
         with (
             patch.object(ci_version_manager, "run_git", 
side_effect=fake_run_git),
-            patch.object(ci_version_manager, "get_current_version", 
return_value="1.2.3"),
+            patch.object(ci_version_manager, "get_merge_base", 
return_value="merge-base"),
+            patch.object(ci_version_manager, "get_frozen_packages", 
return_value=set()),
+            patch.object(ci_version_manager, "_blocked_frozen_media_updates", 
return_value=set()),
+            patch.object(ci_version_manager, "get_current_version", 
return_value="1.0.0"),
+            patch.object(ci_version_manager, "get_version_at_ref", 
return_value="1.0.0"),
+            patch.object(Path, "read_text", return_value='{"templates": []}'),
         ):
-            since = ci_version_manager.get_since_commit_for_package(
-                "core", "pr-merge-base"
-            )
+            result = ci_version_manager.get_changed_packages()
+
+        self.assertEqual(result, {"core", "json", "meta"})
+        self.assertFalse(any(args[0] == "log" for args in calls))
 
-        self.assertEqual(since, "version-intro")
-        log_call = calls[0]
-        self.assertEqual(log_call[:2], ["log", "--format=%H"])
-        self.assertNotIn("pr-merge-base..HEAD", log_call)
-        self.assertEqual(log_call[-1], "packages/core/pyproject.toml")
+    def test_already_bumped_packages_are_not_bumped_again(self):
+        with (
+            patch.object(
+                ci_version_manager, "run_git", side_effect=["v1.0.0", 
'{"templates": []}']
+            ),
+            patch.object(ci_version_manager, "get_merge_base", 
return_value="merge-base"),
+            patch.object(ci_version_manager, "get_frozen_packages", 
return_value=set()),
+            patch.object(ci_version_manager, "_blocked_frozen_media_updates", 
return_value=set()),
+            patch.object(ci_version_manager, "get_current_version", 
return_value="1.0.1"),
+            patch.object(ci_version_manager, "get_version_at_ref", 
return_value="1.0.0"),
+            patch.object(Path, "read_text", return_value='{"templates": []}'),
+        ):
+            self.assertEqual(ci_version_manager.get_changed_packages(), set())
 
-    def test_latest_auto_bump_commit_becomes_the_next_detection_boundary(self):
-        """A rerun after CI commits a bump must not inspect changes before 
that bump."""
+    def test_replacing_existing_media_asset_bumps_its_bundle(self):
+        """bundles.json need not change when an existing thumbnail is 
replaced."""
+        before = (
+            '{"templates": [{"bundle": "media-assets-02", '
+            '"assets": [{"filename": "a.webp", "sha256": "old"}]}]}'
+        )
+        after = before.replace('"old"', '"new"')
 
         def fake_run_git(args):
-            if args[0] == "log":
-                return "auto-bump-commit\nprevious-version"
-            if args[:2] == ["show", 
"auto-bump-commit:packages/json/pyproject.toml"]:
-                return '[project]\nversion = "2.0.1"\n'
-            raise AssertionError(f"Unexpected git invocation: {args}")
+            if args[0] == "tag":
+                return "v1.0.0"
+            if args[0] == "show":
+                return before
+            if args[0] == "diff":
+                return ""
+            raise AssertionError(args)
 
         with (
             patch.object(ci_version_manager, "run_git", 
side_effect=fake_run_git),
-            patch.object(ci_version_manager, "get_current_version", 
return_value="2.0.1"),
+            patch.object(ci_version_manager, "get_merge_base", 
return_value="merge-base"),
+            patch.object(
+                ci_version_manager, "get_frozen_packages",
+                return_value={
+                    "media_api", "media_video", "media_image", "media_other", 
"media_assets_01"
+                },
+            ),
+            patch.object(ci_version_manager, "_blocked_frozen_media_updates", 
return_value=set()),
+            patch.object(ci_version_manager, "get_current_version", 
return_value="1.0.0"),
+            patch.object(ci_version_manager, "get_version_at_ref", 
return_value="1.0.0"),
+            patch.object(Path, "read_text", return_value=after),
         ):
-            since = ci_version_manager.get_since_commit_for_package(
-                "json", "pr-merge-base"
-            )
+            result = ci_version_manager.get_changed_packages()
+
+        self.assertEqual(result, {"core", "json", "media_assets_02", "meta"})
+
+    def test_missing_release_tag_fails_without_bumping(self):
+        with (
+            patch.object(ci_version_manager, "run_git", return_value=""),
+            patch.object(ci_version_manager, "get_merge_base", 
return_value="merge-base"),
+            patch.object(ci_version_manager, "get_frozen_packages", 
return_value=set()),
+            patch.object(ci_version_manager, "_blocked_frozen_media_updates", 
return_value=set()),
+        ):
+            with self.assertRaisesRegex(SystemExit, "No reachable release 
tag"):
+                ci_version_manager.get_changed_packages()
+
+    def test_media_package_source_change_bumps_without_asset_changes(self):
+        def fake_run_git(args):
+            if args[0] == "tag":
+                return "v1.0.0"
+            if args[0] == "show":
+                return '{"templates": []}'
+            if args[0] == "diff":
+                if "packages/media_assets_02/" in args:
+                    return "packages/media_assets_02/src/example/__init__.py"
+                return ""
+            raise AssertionError(args)
+
+        with (
+            patch.object(ci_version_manager, "run_git", 
side_effect=fake_run_git),
+            patch.object(ci_version_manager, "get_merge_base", 
return_value="merge-base"),
+            patch.object(ci_version_manager, "get_frozen_packages", 
return_value=set()),
+            patch.object(ci_version_manager, "_blocked_frozen_media_updates", 
return_value=set()),
+            patch.object(ci_version_manager, "get_current_version", 
return_value="1.0.0"),
+            patch.object(ci_version_manager, "get_version_at_ref", 
return_value="1.0.0"),
+            patch.object(Path, "read_text", return_value='{"templates": []}'),
+        ):
+            self.assertEqual(ci_version_manager.get_changed_packages(), {
+                "core", "json", "media_assets_02", "meta"
+            })
+
+    def test_prerelease_tag_is_not_used_as_release_baseline(self):
+        def fake_run_git(args):
+            if args[0] == "tag":
+                return "v1.1.0-rc.1\nv1.0.0"
+            if args[0] == "show":
+                self.assertTrue(args[1].startswith("v1.0.0:"))
+                return '{"templates": []}'
+            if args[0] == "diff":
+                return ""
+            raise AssertionError(args)
 
-        self.assertEqual(since, "auto-bump-commit")
+        with (
+            patch.object(ci_version_manager, "run_git", 
side_effect=fake_run_git),
+            patch.object(ci_version_manager, "get_merge_base", 
return_value="merge-base"),
+            patch.object(ci_version_manager, "get_frozen_packages", 
return_value=set()),
+            patch.object(ci_version_manager, "_blocked_frozen_media_updates", 
return_value=set()),
+            patch.object(ci_version_manager, "get_current_version", 
return_value="1.0.0"),
+            patch.object(ci_version_manager, "get_version_at_ref", 
return_value="1.0.0"),
+            patch.object(Path, "read_text", return_value='{"templates": []}'),
+        ):
+            self.assertEqual(ci_version_manager.get_changed_packages(), 
{"core", "json", "meta"})
+
+    def test_only_prerelease_tags_fail_without_bumping(self):
+        with (
+            patch.object(ci_version_manager, "run_git", 
return_value="v1.0.0-rc.1"),
+            patch.object(ci_version_manager, "get_merge_base", 
return_value="merge-base"),
+            patch.object(ci_version_manager, "get_frozen_packages", 
return_value=set()),
+            patch.object(ci_version_manager, "_blocked_frozen_media_updates", 
return_value=set()),
+        ):
+            with self.assertRaisesRegex(SystemExit, "No reachable release 
tag"):
+                ci_version_manager.get_changed_packages()
+
+    def test_required_baseline_version_read_failure_is_not_zero(self):
+        with patch.object(
+            ci_version_manager, "run_git",
+            side_effect=subprocess.CalledProcessError(128, ["git", "show"]),
+        ):
+            with self.assertRaisesRegex(RuntimeError, "Cannot read package 
version for core"):
+                ci_version_manager.get_version_at_ref("core", "v1.0.0", 
required=True)
+
+    def test_required_baseline_version_must_have_project_version(self):
+        with patch.object(ci_version_manager, "run_git", 
return_value="[project]\n"):
+            with self.assertRaisesRegex(RuntimeError, "Cannot read package 
version for core"):
+                ci_version_manager.get_version_at_ref("core", "v1.0.0", 
required=True)
+
+    def test_missing_package_at_valid_ref_is_distinct_from_git_failure(self):
+        with patch.object(ci_version_manager, "run_git", return_value="") as 
run_git:
+            self.assertEqual(ci_version_manager.get_version_at_ref(
+                "core", "base", required=True, allow_missing=True
+            ), "0.0.0")
+        run_git.assert_called_once_with(["ls-tree", "base", "--", 
"packages/core/pyproject.toml"])
+        with patch.object(ci_version_manager, "run_git", 
side_effect=subprocess.CalledProcessError(
+            128, ["git", "ls-tree"], stderr=b"fatal: invalid object name"
+        )):
+            with self.assertRaisesRegex(RuntimeError, "invalid object name"):
+                ci_version_manager.get_version_at_ref(
+                    "core", "base", required=True, allow_missing=True
+                )
+
+    def test_frozen_updates_stop_detection_before_tag_queries(self):
+        with (
+            patch.object(ci_version_manager, "get_merge_base", 
return_value="base"),
+            patch.object(ci_version_manager, "get_frozen_packages", 
return_value={"media_api"}),
+            patch.object(
+                ci_version_manager, "_blocked_frozen_media_updates", 
return_value={"media_api"}
+            ),
+            patch.object(ci_version_manager, "run_git") as run_git,
+        ):
+            with self.assertRaisesRegex(SystemExit, "Frozen media packages 
need an update"):
+                ci_version_manager.get_changed_packages()
+        run_git.assert_not_called()
+
+    def test_detection_git_error_is_reported_without_a_traceback(self):
+        with (
+            patch.object(ci_version_manager, "_parse_args", return_value=type(
+                "Args", (), {"list_publish_packages": False}
+            )()),
+            patch.object(ci_version_manager, "root_version_changed", 
return_value=True),
+            patch.object(ci_version_manager, "get_changed_packages", 
side_effect=(
+                subprocess.CalledProcessError(128, ["git", "tag"], 
stderr=b"fatal: bad ref")
+            )),
+        ):
+            with self.assertRaisesRegex(
+                SystemExit, "Release change detection failed: fatal: bad ref"
+            ):
+                ci_version_manager.main()
 
     def test_update_dependencies_uses_provided_package_set(self):
         """Pin updates must not re-run the full change scan."""
@@ -94,32 +248,6 @@
         get_changed.assert_not_called()
         self.assertTrue(write_text.called)
 
-    def test_frozen_packages_skip_version_intro_scan(self):
-        """Frozen packages must not trigger months-long history walks."""
-        with (
-            patch.object(ci_version_manager, "get_merge_base", 
return_value="merge-base"),
-            patch.object(
-                ci_version_manager,
-                "get_frozen_packages",
-                return_value={"media_api", "media_assets_01"},
-            ),
-            patch.object(
-                ci_version_manager, "get_since_commit_for_package"
-            ) as get_since,
-            patch.object(
-                ci_version_manager, "get_files_affecting_package", 
return_value=[]
-            ),
-            patch.object(
-                ci_version_manager, "_blocked_frozen_media_updates", 
return_value=set()
-            ),
-            patch.object(ci_version_manager, "get_current_version", 
return_value="0.0.1"),
-        ):
-            result = ci_version_manager.get_changed_packages()
-
-        self.assertEqual(result, set())
-        analyzed = {call.args[0] for call in get_since.call_args_list}
-        self.assertNotIn("media_api", analyzed)
-        self.assertNotIn("media_assets_01", analyzed)
 
     def test_blocked_frozen_media_uses_merge_base_not_version_intro(self):
         calls = []
@@ -200,5 +328,127 @@
         self.assertEqual(len(calls), 2)
 
 
+class ReleaseIntegrationTests(unittest.TestCase):
+    def test_release_includes_main_changes_and_rerun_does_not_bump_again(self):
+        self._exercise_release()
+
+    def test_stale_tag_does_not_skip_versions_already_advanced_on_main(self):
+        self._exercise_release(stale_tag=True)
+
+    def test_package_added_on_main_after_tag_can_be_released(self):
+        self._exercise_release(new_package=True)
+
+    def 
test_package_added_on_pr_keeps_initial_version_and_is_publishable(self):
+        self._exercise_release(new_on_pr=True)
+
+    def test_blueprint_core_manifest_change_is_detected(self):
+        self._exercise_release(manifest_only=True)
+
+    def _exercise_release(
+        self, *, stale_tag=False, new_package=False, new_on_pr=False, 
manifest_only=False
+    ):
+        """Exercise real tags, package versions and content across a release 
PR."""
+        with tempfile.TemporaryDirectory() as directory:
+            root = Path(directory)
+
+            def git(*args):
+                return subprocess.check_output(
+                    ["git", *args], cwd=root, stderr=subprocess.PIPE
+                ).decode().strip()
+
+            def write(path, content):
+                target = root / path
+                target.parent.mkdir(parents=True, exist_ok=True)
+                target.write_text(content)
+
+            def commit(message):
+                git("add", ".")
+                git("commit", "-m", message)
+
+            git("init", "-b", "main")
+            git("config", "user.name", "Release test")
+            git("config", "user.email", "[email protected]")
+            for pkg in ("core", "json", "media_assets_02", "blueprints"):
+                if (new_package or new_on_pr) and pkg == "media_assets_02":
+                    continue
+                write(f"packages/{pkg}/pyproject.toml", '[project]\nversion = 
"1.0.0"\n')
+            write("pyproject.toml", '[project]\nversion = "1.0.0"\n')
+            manifest_path = 
"packages/core/src/comfyui_workflow_templates_core/manifest.json"
+            manifest = {"templates": [{"bundle": "media-assets-02", "assets": [
+                {"filename": "a.webp", "sha256": "old"}
+            ]}]}
+            write(manifest_path, json.dumps(manifest))
+            write("blueprints/example.json", "{}")
+            blueprint_manifest = (
+                
"packages/core/src/comfyui_workflow_templates_core/blueprints_manifest.json"
+            )
+            write(blueprint_manifest, '{"blueprints": []}')
+            commit("Published release")
+            git("tag", "v1.0.0")
+
+            if stale_tag:
+                for pkg in ("core", "json", "media_assets_02", "blueprints"):
+                    write(f"packages/{pkg}/pyproject.toml", 
'[project]\nversion = "1.0.1"\n')
+                write("pyproject.toml", '[project]\nversion = "1.0.1"\n')
+                commit("Previous release succeeded but its tag was not 
created")
+            if new_package:
+                write("packages/media_assets_02/pyproject.toml", 
'[project]\nversion = "1.0.1"\n')
+            if manifest_only:
+                write(blueprint_manifest, '{"blueprints": [{"id": 
"example"}]}')
+            else:
+                manifest["templates"][0]["assets"][0]["sha256"] = "new"
+                write(manifest_path, json.dumps(manifest))
+                write("blueprints/example.json", '{"changed": true}')
+            commit("Merge template and blueprint changes into main")
+            git("update-ref", "refs/remotes/origin/main", "HEAD")
+            git("checkout", "-b", "release")
+            if new_on_pr:
+                write("packages/media_assets_02/pyproject.toml", 
'[project]\nversion = "1.0.1"\n')
+            # A reachable prerelease tag must not become the published 
baseline.
+            git("tag", "v1.1.0-rc.1")
+            release_version = "1.0.2" if stale_tag else "1.0.1"
+            write("pyproject.toml", f'[project]\nversion = 
"{release_version}"\n')
+            commit("Start next release")
+
+            previous_cwd = Path.cwd()
+            try:
+                os.chdir(root)
+                with (
+                    patch.object(ci_version_manager, "ROOT", root),
+                    patch.object(ci_version_manager, "get_frozen_packages", 
return_value=set()),
+                    patch.object(
+                        ci_version_manager, "_blocked_frozen_media_updates", 
return_value=set()
+                    ),
+                    patch.object(ci_version_manager, "ALL_PACKAGE_IDS", (
+                        "core", "json", "media_assets_02", "blueprints", "meta"
+                    )),
+                ):
+                    self.assertTrue(ci_version_manager.root_version_changed())
+                    changed = ci_version_manager.get_changed_packages()
+                    expected = {"core", "json", "blueprints", "meta"}
+                    if not manifest_only and not new_on_pr:
+                        expected.add("media_assets_02")
+                    self.assertEqual(changed, expected)
+                    ci_version_manager.bump_versions(changed)
+                    if "media_assets_02" in expected:
+                        expected_version = "1.0.2" if stale_tag or new_package 
else "1.0.1"
+                        self.assertEqual(
+                            
ci_version_manager.get_current_version("media_assets_02"),
+                            expected_version,
+                        )
+                    if new_on_pr:
+                        self.assertEqual(
+                            
ci_version_manager.get_current_version("media_assets_02"), "1.0.1"
+                        )
+                        self.assertIn("media_assets_02", 
ci_version_manager.get_publish_package_ids(
+                            "origin/main"
+                        ))
+                    commit("Auto-bump package versions")
+                    self.assertTrue(ci_version_manager.root_version_changed())
+                    
self.assertEqual(ci_version_manager.get_changed_packages(), set())
+            finally:
+                os.chdir(previous_cwd)
+
+
 if __name__ == "__main__":
     unittest.main()

Reply via email to