Skip to content

Commit 616b884

Browse files
ci(DATAGO-113775): introduce versioning and release pipeline (#83)
* ci(DATAGO-113775): introduce versioning and release pipeline * fix: run readiness after release please * ci: update release readiness check order * add check runs for release readiness * pin actions * fix required inputs * add plugin sync action * use script for the action as well * scan final plugin with fossa
1 parent 1f55e52 commit 616b884

File tree

9 files changed

+1639
-0
lines changed

9 files changed

+1639
-0
lines changed

.github/pr_labeler.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,7 @@ sam-sql-database-tool:
6565
sam-webhook-gateway:
6666
- changed-files:
6767
- any-glob-to-any-file: sam-webhook-gateway/**
68+
69+
sam-mcp-server-gateway-adapter:
70+
- changed-files:
71+
- any-glob-to-any-file: sam-mcp-server-gateway-adapter/**
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Add missing plugins to all configuration files.
4+
5+
This script scans the repository for plugin directories (sam-*) and adds
6+
any missing plugins to:
7+
- .github/workflows/build-plugin.yaml
8+
- .github/workflows/sync-plugin-configs.yaml (paths exclusions)
9+
- .release-please-manifest.json
10+
- release-please-config.json
11+
- .github/pr_labeler.yaml
12+
"""
13+
14+
from __future__ import annotations
15+
16+
import json
17+
import re
18+
import sys
19+
from pathlib import Path
20+
from typing import Set
21+
22+
23+
def get_plugin_directories(repo_root: Path) -> Set[str]:
24+
"""Get all plugin directories (directories starting with 'sam-')."""
25+
plugins = set()
26+
for item in repo_root.iterdir():
27+
if item.is_dir() and item.name.startswith("sam-"):
28+
# Check if it's a valid plugin (has pyproject.toml)
29+
if (item / "pyproject.toml").exists():
30+
plugins.add(item.name)
31+
return plugins
32+
33+
34+
def get_package_name(plugin_dir: str) -> str:
35+
"""Convert plugin directory name to package name."""
36+
# sam-foo-bar -> solace_agent_mesh_foo_bar
37+
suffix = plugin_dir.replace("sam-", "").replace("-", "_")
38+
return f"solace_agent_mesh_{suffix}"
39+
40+
41+
def update_build_workflow(repo_root: Path, plugins: Set[str]) -> bool:
42+
"""Update .github/workflows/build-plugin.yaml with missing plugins."""
43+
workflow_path = repo_root / ".github" / "workflows" / "build-plugin.yaml"
44+
45+
if not workflow_path.exists():
46+
print(f" ⚠️ {workflow_path} does not exist, skipping")
47+
return False
48+
49+
content = workflow_path.read_text()
50+
51+
# Find existing plugins in options (lines like " - sam-foo")
52+
existing_plugins = set()
53+
for match in re.finditer(r"^\s*-\s*(sam-[\w-]+)\s*$", content, re.MULTILINE):
54+
existing_plugins.add(match.group(1))
55+
56+
missing = plugins - existing_plugins
57+
if not missing:
58+
print(" ✅ build-plugin.yaml already has all plugins")
59+
return False
60+
61+
# Find the last plugin option and add after it
62+
lines = content.split("\n")
63+
last_option_idx = -1
64+
indent = " " # Default indent
65+
66+
for i, line in enumerate(lines):
67+
match = re.match(r"^(\s*)-\s*sam-[\w-]+\s*$", line)
68+
if match:
69+
indent = match.group(1)
70+
last_option_idx = i
71+
72+
if last_option_idx < 0:
73+
print(" ⚠️ Could not find options block in build-plugin.yaml")
74+
return False
75+
76+
# Insert missing plugins after the last option
77+
for plugin in sorted(missing, reverse=True):
78+
lines.insert(last_option_idx + 1, f"{indent}- {plugin}")
79+
80+
workflow_path.write_text("\n".join(lines))
81+
print(f" ✅ Added {len(missing)} plugins to build-plugin.yaml:")
82+
for plugin in sorted(missing):
83+
print(f" + {plugin}")
84+
return True
85+
86+
87+
def update_sync_workflow(repo_root: Path, plugins: Set[str]) -> bool:
88+
"""Update .github/workflows/sync-plugin-configs.yaml paths exclusions with missing plugins."""
89+
workflow_path = repo_root / ".github" / "workflows" / "sync-plugin-configs.yaml"
90+
91+
if not workflow_path.exists():
92+
print(f" ⚠️ {workflow_path} does not exist, skipping")
93+
return False
94+
95+
content = workflow_path.read_text()
96+
97+
# Find existing plugins in paths exclusions (! prefix)
98+
existing_plugins = set()
99+
for match in re.finditer(r'^\s*-\s*"!(sam-[\w-]+)/\*\*"', content, re.MULTILINE):
100+
existing_plugins.add(match.group(1))
101+
102+
missing = plugins - existing_plugins
103+
if not missing:
104+
print(" ✅ sync-plugin-configs.yaml already has all plugins")
105+
return False
106+
107+
# Find the last exclusion pattern (lines with "!sam-*/**") and add after it
108+
lines = content.split("\n")
109+
last_exclusion_idx = -1
110+
indent = " " # Default indent
111+
112+
for i, line in enumerate(lines):
113+
match = re.match(r'^(\s*)-\s*"!sam-[\w-]+/\*\*"', line)
114+
if match:
115+
indent = match.group(1)
116+
last_exclusion_idx = i
117+
118+
if last_exclusion_idx < 0:
119+
print(" ⚠️ Could not find paths exclusions in sync-plugin-configs.yaml")
120+
return False
121+
122+
# Insert missing plugins after the last exclusion
123+
for plugin in sorted(missing, reverse=True):
124+
lines.insert(last_exclusion_idx + 1, f'{indent}- "!{plugin}/**"')
125+
126+
workflow_path.write_text("\n".join(lines))
127+
print(f" ✅ Added {len(missing)} plugins to sync-plugin-configs.yaml:")
128+
for plugin in sorted(missing):
129+
print(f" + {plugin}")
130+
return True
131+
132+
133+
def update_manifest(repo_root: Path, plugins: Set[str]) -> bool:
134+
"""Update .release-please-manifest.json with missing plugins."""
135+
manifest_path = repo_root / ".release-please-manifest.json"
136+
137+
if manifest_path.exists():
138+
with open(manifest_path) as f:
139+
manifest = json.load(f)
140+
else:
141+
manifest = {}
142+
143+
missing = plugins - set(manifest.keys())
144+
if not missing:
145+
print(" ✅ .release-please-manifest.json already has all plugins")
146+
return False
147+
148+
# Add missing plugins with initial version
149+
for plugin in sorted(missing):
150+
manifest[plugin] = "0.1.0"
151+
152+
# Sort the manifest by key
153+
sorted_manifest = dict(sorted(manifest.items()))
154+
155+
with open(manifest_path, "w") as f:
156+
json.dump(sorted_manifest, f, indent=2)
157+
f.write("\n")
158+
159+
print(f" ✅ Added {len(missing)} plugins to .release-please-manifest.json:")
160+
for plugin in sorted(missing):
161+
print(f" + {plugin}")
162+
return True
163+
164+
165+
def update_release_config(repo_root: Path, plugins: Set[str]) -> bool:
166+
"""Update release-please-config.json with missing plugins."""
167+
config_path = repo_root / "release-please-config.json"
168+
169+
if config_path.exists():
170+
with open(config_path) as f:
171+
config = json.load(f)
172+
else:
173+
config = {
174+
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
175+
"release-type": "python",
176+
"include-component-in-tag": True,
177+
"include-v-in-tag": False,
178+
"separate-pull-requests": False,
179+
"packages": {},
180+
}
181+
182+
packages = config.get("packages", {})
183+
missing = plugins - set(packages.keys())
184+
185+
if not missing:
186+
print(" ✅ release-please-config.json already has all plugins")
187+
return False
188+
189+
# Add missing plugins
190+
for plugin in sorted(missing):
191+
packages[plugin] = {
192+
"package-name": get_package_name(plugin),
193+
"changelog-path": "CHANGELOG.md",
194+
}
195+
196+
# Sort packages by key
197+
config["packages"] = dict(sorted(packages.items()))
198+
199+
with open(config_path, "w") as f:
200+
json.dump(config, f, indent=2)
201+
f.write("\n")
202+
203+
print(f" ✅ Added {len(missing)} plugins to release-please-config.json:")
204+
for plugin in sorted(missing):
205+
print(f" + {plugin}")
206+
return True
207+
208+
209+
def update_pr_labeler(repo_root: Path, plugins: Set[str]) -> bool:
210+
"""Update .github/pr_labeler.yaml with missing plugins."""
211+
labeler_path = repo_root / ".github" / "pr_labeler.yaml"
212+
213+
if labeler_path.exists():
214+
content = labeler_path.read_text()
215+
else:
216+
content = """# PR Labeler configuration
217+
# This file automatically labels PRs based on changed files.
218+
219+
"""
220+
221+
# Find existing plugins
222+
existing_plugins = set()
223+
for match in re.finditer(r"^(sam-[\w-]+):", content, re.MULTILINE):
224+
existing_plugins.add(match.group(1))
225+
226+
missing = plugins - existing_plugins
227+
if not missing:
228+
print(" ✅ pr_labeler.yaml already has all plugins")
229+
return False
230+
231+
# Add missing plugins at the end
232+
new_entries = []
233+
for plugin in sorted(missing):
234+
new_entries.append(f"""{plugin}:
235+
- changed-files:
236+
- any-glob-to-any-file: {plugin}/**
237+
238+
""")
239+
240+
content = content.rstrip() + "\n" + "".join(new_entries)
241+
242+
labeler_path.write_text(content)
243+
244+
print(f" ✅ Added {len(missing)} plugins to pr_labeler.yaml:")
245+
for plugin in sorted(missing):
246+
print(f" + {plugin}")
247+
return True
248+
249+
250+
def main():
251+
# Find repository root
252+
script_dir = Path(__file__).parent
253+
repo_root = script_dir.parent.parent
254+
255+
print(f"Repository root: {repo_root}")
256+
print()
257+
258+
# Get all plugin directories
259+
actual_plugins = get_plugin_directories(repo_root)
260+
print(f"Found {len(actual_plugins)} plugin directories:")
261+
for plugin in sorted(actual_plugins):
262+
print(f" - {plugin}")
263+
print()
264+
265+
# Update each config file
266+
updated_any = False
267+
268+
print("Updating build-plugin.yaml...")
269+
if update_build_workflow(repo_root, actual_plugins):
270+
updated_any = True
271+
print()
272+
273+
print("Updating sync-plugin-configs.yaml...")
274+
if update_sync_workflow(repo_root, actual_plugins):
275+
updated_any = True
276+
print()
277+
278+
print("Updating .release-please-manifest.json...")
279+
if update_manifest(repo_root, actual_plugins):
280+
updated_any = True
281+
print()
282+
283+
print("Updating release-please-config.json...")
284+
if update_release_config(repo_root, actual_plugins):
285+
updated_any = True
286+
print()
287+
288+
print("Updating pr_labeler.yaml...")
289+
if update_pr_labeler(repo_root, actual_plugins):
290+
updated_any = True
291+
print()
292+
293+
# Summary
294+
if updated_any:
295+
print("=" * 50)
296+
print("✅ Configuration files updated successfully!")
297+
print(" Please review the changes and commit them.")
298+
else:
299+
print("✅ All configuration files are already up to date!")
300+
301+
return 0
302+
303+
304+
if __name__ == "__main__":
305+
sys.exit(main())

0 commit comments

Comments
 (0)