Skip to content

Commit 317e011

Browse files
authored
build: auto-update SKILL.md based on release diffs (#886)
* feat: auto-update SKILL.md based on release diffs This adds a GitHub workflow and Python script to use the Gemini API to update the SKILL.md documentation instructions whenever a new release is published. * ci: create PR instead of committing directly to main for skill updates Replaces the manual branch checkout and commit process in the update-skill workflow with the `peter-evans/create-pull-request` action. This ensures changes are reviewed via a Pull Request before merging to main. * fix: remove invalid yaml escape sequence from workflow body string
1 parent ca66e98 commit 317e011

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed

.github/scripts/update_skill.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import os
16+
import json
17+
import urllib.request
18+
import sys
19+
20+
def get_gemini_response(api_key, prompt):
21+
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={api_key}"
22+
headers = {'Content-Type': 'application/json'}
23+
data = {
24+
"contents": [{
25+
"parts": [{"text": prompt}]
26+
}]
27+
}
28+
29+
req = urllib.request.Request(url, data=json.dumps(data).encode('utf-8'), headers=headers)
30+
try:
31+
with urllib.request.urlopen(req) as response:
32+
res_data = json.loads(response.read().decode('utf-8'))
33+
return res_data['candidates'][0]['content']['parts'][0]['text']
34+
except urllib.error.HTTPError as e:
35+
print(f"Gemini API Error ({e.code}): {e.reason}", file=sys.stderr)
36+
try:
37+
error_body = e.read().decode('utf-8')
38+
print(f"Error details: {error_body}", file=sys.stderr)
39+
except:
40+
pass
41+
return None
42+
except Exception as e:
43+
print(f"Error calling Gemini API: {e}", file=sys.stderr)
44+
return None
45+
46+
def main():
47+
api_key = os.getenv("GEMINI_API_KEY")
48+
diff_file = os.getenv("DIFF_FILE", "release_diff.patch")
49+
skill_file = os.getenv("SKILL_FILE", ".gemini/skills/android-maps-compose/SKILL.md")
50+
51+
if not api_key:
52+
print("GEMINI_API_KEY not found", file=sys.stderr)
53+
sys.exit(1)
54+
55+
if not os.path.exists(diff_file):
56+
print(f"Diff file {diff_file} not found.", file=sys.stderr)
57+
sys.exit(1)
58+
59+
if not os.path.exists(skill_file):
60+
print(f"Skill file {skill_file} not found.", file=sys.stderr)
61+
sys.exit(1)
62+
63+
with open(diff_file, "r") as f:
64+
diff_content = f.read()
65+
66+
with open(skill_file, "r") as f:
67+
skill_content = f.read()
68+
69+
prompt = f"""
70+
You are an expert technical writer and Android developer.
71+
Your task is to update the Gemini CLI skill instructions for an SDK based on the latest release changes.
72+
73+
Here is the current `SKILL.md` file:
74+
```markdown
75+
{skill_content}
76+
```
77+
78+
Here is the git diff representing the changes introduced in this release:
79+
```diff
80+
{diff_content}
81+
```
82+
83+
Please analyze the diff to identify any new APIs, deprecated functions, structural changes, or changes in implementation best practices.
84+
Update the `SKILL.md` content to incorporate these new concepts or deprecations.
85+
86+
CRITICAL REQUIREMENTS:
87+
- Preserve the overall markdown structure and formatting of the existing `SKILL.md`.
88+
- Ensure you keep the YAML frontmatter intact at the top of the file (between `---`).
89+
- Do NOT remove the `// x-release-please-version` comments in the Gradle dependencies, as they are required by our release process.
90+
- Return ONLY the raw updated markdown content. Do NOT wrap it in ```markdown...``` code blocks, just return the exact file content so it can be directly saved.
91+
"""
92+
93+
print("Requesting update from Gemini...", file=sys.stderr)
94+
response_text = get_gemini_response(api_key, prompt)
95+
if response_text:
96+
# Clean up response text if the model wrapped it in markdown code blocks despite instructions
97+
if response_text.startswith("```markdown"):
98+
response_text = response_text.replace("```markdown\n", "", 1)
99+
if response_text.endswith("```"):
100+
response_text = response_text[:-3]
101+
elif response_text.startswith("```"):
102+
response_text = response_text.replace("```\n", "", 1)
103+
if response_text.endswith("```"):
104+
response_text = response_text[:-3]
105+
106+
# Trim whitespace at the very beginning or end
107+
response_text = response_text.strip() + "\n"
108+
109+
with open(skill_file, "w") as f:
110+
f.write(response_text)
111+
print(f"Successfully updated {skill_file}", file=sys.stderr)
112+
else:
113+
sys.exit(1)
114+
115+
if __name__ == "__main__":
116+
main()

.github/workflows/update-skill.yml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: Update Skill
16+
17+
on:
18+
release:
19+
types: [published]
20+
21+
jobs:
22+
update-skill:
23+
runs-on: ubuntu-latest
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@v4
27+
with:
28+
fetch-depth: 0 # Fetch all history for tags and diffs
29+
token: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO }}
30+
31+
- name: Setup Python
32+
uses: actions/setup-python@v5
33+
with:
34+
python-version: '3.11'
35+
36+
- name: Generate Diff
37+
run: |
38+
# The new release tag is GITHUB_REF_NAME when triggered on release
39+
# Find the tag immediately preceding this one
40+
PREVIOUS_TAG=$(git describe --tags --abbrev=0 ${GITHUB_REF_NAME}^ 2>/dev/null || echo "")
41+
42+
if [ -z "$PREVIOUS_TAG" ]; then
43+
# If no previous tag, diff against the initial commit
44+
INITIAL_COMMIT=$(git rev-list --max-parents=0 HEAD)
45+
git diff $INITIAL_COMMIT..HEAD > release_diff.patch
46+
else
47+
git diff $PREVIOUS_TAG..$GITHUB_REF_NAME > release_diff.patch
48+
fi
49+
50+
- name: Update Skill via Gemini
51+
env:
52+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
53+
run: python .github/scripts/update_skill.py
54+
55+
- name: Create Pull Request
56+
uses: peter-evans/create-pull-request@v7
57+
with:
58+
token: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO }}
59+
commit-message: "docs: update SKILL.md based on release ${{ github.ref_name }}"
60+
title: "docs: update SKILL.md for release ${{ github.ref_name }}"
61+
body: "Automatically generated PR to update `SKILL.md` for the latest release `${{ github.ref_name }}`."
62+
branch: "update-skill-for-${{ github.ref_name }}"
63+
base: main
64+
labels: |
65+
docs
66+
automerge

0 commit comments

Comments
 (0)