Skip to content

Commit a1e2ba1

Browse files
DeeAjayidunefrogeekygulshaninnoavator
authored
Add workflow to Publish Truefoundry Chart PRs (#1257)
* Add workflow to Publish Truefoundry Chart PRs * update folder name * remove get_realease_notes * update generate diff step * update generate diff step file name * update chart_changes script * [CI] Update charts artifacts manifest files Signed-off-by: DeeAjayi <DeeAjayi@users.noreply.github.com> * update chart version * [CI] Update charts artifacts manifest files Signed-off-by: DeeAjayi <DeeAjayi@users.noreply.github.com> * update parse_image_changes function * debugging * [CI] Update charts artifacts manifest files Signed-off-by: DeeAjayi <DeeAjayi@users.noreply.github.com> * update script * Added 5m as the disruption window for inf machines (#1269) * [CI] Update LLMGateway chart values.yaml * Update README.md with readme-generator-for-helm Signed-off-by: innoavator <innoavator@users.noreply.github.com> * Changelog generation * Modified github token variable name * change the variable name * removed pr action and added tfy-token env * type fix * Fixing the copy loop * [CI] Update charts artifacts manifest files Signed-off-by: dunefro <dunefro@users.noreply.github.com> * [CI] Update charts artifacts manifest files Signed-off-by: DeeAjayi <DeeAjayi@users.noreply.github.com> * Update workflow * update aws role for s3 upload * update ecr role * fix s3 path * update changelog upload step logic * update update-artifcat workflow --------- Signed-off-by: DeeAjayi <DeeAjayi@users.noreply.github.com> Signed-off-by: innoavator <innoavator@users.noreply.github.com> Signed-off-by: dunefro <dunefro@users.noreply.github.com> Co-authored-by: DeeAjayi <DeeAjayi@users.noreply.github.com> Co-authored-by: Vedant Pareek <36420365+dunefro@users.noreply.github.com> Co-authored-by: geekygulshan <geekygulshan@users.noreply.github.com> Co-authored-by: innoavator <innoavator@users.noreply.github.com> Co-authored-by: Vedant Pareek <dunefro@gmail.com> Co-authored-by: dunefro <dunefro@users.noreply.github.com>
1 parent 61d7c55 commit a1e2ba1

File tree

3 files changed

+171
-0
lines changed

3 files changed

+171
-0
lines changed

.github/workflows/update-artifacts.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ on:
1313
- 'charts/tfy-k8s-generic-inframold/**'
1414
- 'scripts/generate-artifacts-manifest/**'
1515
- 'scripts/upload-artifacts/**'
16+
- 'scripts/get-release-notes/**'
1617

1718
jobs:
1819
update-artifacts:
@@ -25,6 +26,7 @@ jobs:
2526
ARTIFACTORY_PASSWORD: ${{ secrets.TRUEFOUNDRY_ARTIFACTORY_PUBLIC_PASSWORD }}
2627
ARTIFACTORY_REPOSITORY_URL: ${{ vars.TRUEFOUNDRY_ARTIFACTORY_PUBLIC_REPOSITORY }}
2728
ARTIFACTORY_HELM_REGISTRY_URL: ${{ vars.TRUEFOUNDRY_ARTIFACTORY_PUBLIC_HELM_REGISTRY }}
29+
CHANGELOG_S3_BUCKET: "tfy-euwe1-production-control-plane-changelog"
2830
steps:
2931
- name: Checkout Code
3032
uses: actions/checkout@v4
@@ -72,6 +74,14 @@ jobs:
7274
pip install -r scripts/upload-artifacts/requirements.txt
7375
pip install -r scripts/generate-artifacts-manifest/requirements.txt
7476
77+
- name: Copy the current artifacts manifest file to old-artifacts.json for changelog
78+
run: |
79+
charts_list=("tfy-k8s-aws-eks-inframold")
80+
for chart in "${charts_list[@]}"
81+
do
82+
cp charts/$chart/artifacts-manifest.json charts/$chart/old-artifacts-manifest.json
83+
done
84+
7585
# Generate artifacts manifest for inframold charts
7686
- name: Generate Artifacts Manifest for Each Chart
7787
run: |
@@ -92,6 +102,43 @@ jobs:
92102
charts/tfy-k8s-aws-eks-inframold/artifacts-manifest.json \
93103
scripts/generate-artifacts-manifest/multi-arch-image-whitelist.json
94104
105+
- name: Generate Changelog
106+
run: |
107+
charts_list=("tfy-k8s-aws-eks-inframold")
108+
for chart in "${charts_list[@]}";
109+
do
110+
python scripts/get-release-notes/chart_changes.py charts/$chart/old-artifacts-manifest.json charts/$chart/artifacts-manifest.json --output charts/$chart/changelog.json
111+
done
112+
env:
113+
TFY_GITHUB_TOKEN: ${{ secrets.TFY_GITHUB_TOKEN }}
114+
115+
- name: Configure AWS credentials for s3 uploads
116+
uses: aws-actions/configure-aws-credentials@v4
117+
with:
118+
role-to-assume: ${{ secrets.S3_IAM_ROLE_ARN }}
119+
aws-region: us-east-1
120+
121+
# Upload Changelog to S3
122+
- name: Upload Changelog to S3
123+
run: |
124+
chart="tfy-k8s-aws-eks-inframold"
125+
changelog_path="charts/$chart/changelog.json"
126+
if [ -f "$changelog_path" ]; then
127+
version=$(grep version charts/$chart/Chart.yaml | awk '{print $2}')
128+
aws s3 cp "$changelog_path" s3://$CHANGELOG_S3_BUCKET/$version/changelog.json
129+
else
130+
echo "changelog.json not found - skipping S3 upload."
131+
fi
132+
133+
- name: Cleanup Old Artifacts Manifest and Changelog
134+
run: |
135+
charts_list=("tfy-k8s-aws-eks-inframold")
136+
for chart in "${charts_list[@]}";
137+
do
138+
rm -rf charts/$chart/old-artifacts-manifest.json
139+
rm -rf charts/$chart/changelog.json
140+
done
141+
95142
# Update the inframold artifacts manifest
96143
- name: Update Artifacts Manifest
97144
run: |
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import json
2+
import os
3+
import re
4+
import requests
5+
import argparse
6+
from typing import Dict, List, Optional
7+
8+
# Read GitHub token from env
9+
TFY_GITHUB_TOKEN = os.getenv("TFY_GITHUB_TOKEN", "")
10+
if not TFY_GITHUB_TOKEN:
11+
raise EnvironmentError("❌ TFY_GITHUB_TOKEN not set in environment variables")
12+
13+
GITHUB_API_HEADERS = {
14+
"Accept": "application/vnd.github+json",
15+
"Authorization": f"Bearer {TFY_GITHUB_TOKEN}"
16+
}
17+
18+
19+
def extract_truefoundry_images(manifest: List[dict]) -> List[str]:
20+
for item in manifest:
21+
if item.get("type") == "helm" and item.get("details", {}).get("chart") == "truefoundry":
22+
return item["details"].get("images", [])
23+
return []
24+
25+
26+
def map_images_by_name(images: List[str]) -> Dict[str, str]:
27+
image_map = {}
28+
for image in images:
29+
if ":" not in image:
30+
print(f"⚠️ Skipping malformed image: {image}")
31+
continue
32+
path, tag = image.rsplit(":", 1)
33+
name = path.split("/")[-1]
34+
image_map[name] = tag
35+
return image_map
36+
37+
38+
def compare_image_tags(old_json_path: str, new_json_path: str) -> List[Dict[str, str]]:
39+
with open(old_json_path) as f1, open(new_json_path) as f2:
40+
old_manifest = json.load(f1)
41+
new_manifest = json.load(f2)
42+
43+
old_images = extract_truefoundry_images(old_manifest)
44+
new_images = extract_truefoundry_images(new_manifest)
45+
46+
old_map = map_images_by_name(old_images)
47+
new_map = map_images_by_name(new_images)
48+
49+
changes = []
50+
for name, new_tag in new_map.items():
51+
old_tag = old_map.get(name)
52+
if old_tag and old_tag != new_tag:
53+
changes.append({
54+
"image": name,
55+
"old_tag": old_tag,
56+
"new_tag": new_tag
57+
})
58+
return changes
59+
60+
61+
def get_commits(repo: str, old_tag: str, new_tag: str) -> List[dict]:
62+
url = f"https://api.github.com/repos/truefoundry/{repo}/compare/{old_tag}...{new_tag}"
63+
try:
64+
resp = requests.get(url, headers=GITHUB_API_HEADERS)
65+
resp.raise_for_status()
66+
return resp.json().get("commits", [])
67+
except requests.RequestException as e:
68+
print(f"❌ Failed to get commits for {repo}: {e}")
69+
return []
70+
71+
72+
def extract_pr_number(message: str) -> Optional[str]:
73+
match = re.search(r"\(#(\d+)\)", message)
74+
return match.group(1) if match else None
75+
76+
77+
def enrich_commit(commit: dict, repo: str) -> Dict:
78+
commit_info = {
79+
"author": commit.get("commit", {}).get("author", {}),
80+
"message": commit.get("commit", {}).get("message", ""),
81+
"commit_url": commit.get("html_url", ""),
82+
"pull_request": ""
83+
}
84+
pr_num = extract_pr_number(commit_info["message"])
85+
if pr_num:
86+
commit_info["pull_request"] = f"https://github.com/truefoundry/{repo}/pull/{pr_num}"
87+
return commit_info
88+
89+
90+
def generate_changelog(changes: List[Dict[str, str]]) -> Dict[str, List[Dict]]:
91+
changelog = {}
92+
for entry in changes:
93+
repo = entry["image"]
94+
old_tag = entry["old_tag"]
95+
new_tag = entry["new_tag"]
96+
97+
print(f"🔍 Fetching commits for {repo}: {old_tag}{new_tag}")
98+
commits = get_commits(repo, old_tag, new_tag)
99+
100+
changelog[repo] = [enrich_commit(commit, repo) for commit in commits]
101+
return changelog
102+
103+
104+
def write_json(data: dict, filename: str) -> None:
105+
with open(filename, "w") as f:
106+
json.dump(data, f, indent=2)
107+
print(f"✅ Saved changelog to {filename}")
108+
109+
110+
if __name__ == "__main__":
111+
parser = argparse.ArgumentParser(description="Compare image tags and get changelogs from truefoundry manifests.")
112+
parser.add_argument("old_manifest", help="Path to old artifacts-manifest.json")
113+
parser.add_argument("new_manifest", help="Path to new artifacts-manifest.json")
114+
parser.add_argument("--output", default="changelog.json", help="Output changelog JSON file (default: changelog.json)")
115+
116+
args = parser.parse_args()
117+
118+
image_changes = compare_image_tags(args.old_manifest, args.new_manifest)
119+
print("📦 Changed images:")
120+
print(json.dumps(image_changes, indent=2))
121+
122+
changelog = generate_changelog(image_changes)
123+
write_json(changelog, args.output)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
requests==2.32.3

0 commit comments

Comments
 (0)