Skip to content

Commit ee4b0cc

Browse files
authored
ci: Add update dependencies
1 parent d0dae09 commit ee4b0cc

File tree

5 files changed

+383
-0
lines changed

5 files changed

+383
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Update dependency versions
2+
3+
on:
4+
schedule:
5+
# This cron expression runs the job at 00:00 UTC on the first day of every month
6+
- cron: '0 0 1 * *'
7+
workflow_dispatch: # Allows manual triggering of the workflow
8+
9+
jobs:
10+
update-fetch-content:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout repository
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: '3.11'
21+
22+
- name: Install Pipenv
23+
run: pip install pipenv
24+
25+
- name: Install dependencies
26+
run: |
27+
cd tools
28+
pipenv install
29+
30+
- name: Run update scripts
31+
run: |
32+
cd tools
33+
pipenv run python update_dependencies.py .. tmp tools build docs .github
34+
35+
- name: Check for changes
36+
id: check_changes
37+
run: |
38+
git config --local user.name "github-actions"
39+
git config --local user.email "[email protected]"
40+
git add .
41+
git diff --cached --exit-code || echo "changed" >> $GITHUB_ENV
42+
43+
- name: Create Pull Request
44+
if: env.changed == 'changed'
45+
run: |
46+
git commit -m "Update dependency versions"
47+
git push origin HEAD:update-dependency-versions
48+
gh pr create --base main --head update-dependency-versions --title "Update dependency versions" --body "This pull request updates the dependency versions in files."
49+
env:
50+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ maddy uses [semver versioning](https://semver.org/).
1616

1717
* ![**CHANGED**](https://img.shields.io/badge/-CHANGED-%23e90) Updated google test to v1.15.0.
1818
* ![**ADDED**](https://img.shields.io/badge/-ADDED-%23099) clang-format
19+
* ![**ADDED**](https://img.shields.io/badge/-ADDED-%23099) automatic update dependencies ci
1920

2021
## version 1.3.0 2023-08-26
2122

tools/Pipfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[[source]]
2+
url = "https://pypi.org/simple"
3+
verify_ssl = true
4+
name = "pypi"
5+
6+
[packages]
7+
requests = "*"
8+
9+
[dev-packages]
10+
11+
[requires]
12+
python_version = "3.11"

tools/Pipfile.lock

Lines changed: 165 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/update_dependencies.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#!/bin/python
2+
#
3+
# maddy update dependencies
4+
# This project is licensed under the MIT license. For more information see the
5+
# LICENSE file.
6+
import os
7+
import re
8+
import requests
9+
import sys
10+
11+
def get_cmake_files(directory, ignored_dirs=None):
12+
"""
13+
Recursively searches for all CMakeLists.txt files in the specified directory,
14+
ignoring specified subdirectories.
15+
16+
Args:
17+
directory (str): The path to the directory to search for CMakeLists.txt files.
18+
ignored_dirs (list): A list of directory names to ignore during the search.
19+
20+
Returns:
21+
list: A list of full paths to all found CMakeLists.txt files.
22+
"""
23+
cmakelists_paths = []
24+
ignored_dirs = ignored_dirs or []
25+
26+
for root, dirs, files in os.walk(directory):
27+
# Modify dirs in place to skip ignored directories
28+
dirs[:] = [d for d in dirs if d not in ignored_dirs]
29+
30+
if "CMakeLists.txt" in files:
31+
cmakelists_paths.append(os.path.join(root, "CMakeLists.txt"))
32+
33+
return cmakelists_paths
34+
35+
def get_last_dependency_version(url):
36+
"""
37+
Retrieves the latest release version tag from a specified GitHub repository.
38+
39+
Args:
40+
url (str): The URL of the GitHub repository in the format
41+
'https://github.com/owner/repo.git' or 'https://github.com/owner/repo'.
42+
43+
Returns:
44+
str: The latest release version tag (e.g., 'v1.2.3') if found,
45+
or raises an Exception if the repository is not found or
46+
if there is an error in the API request.
47+
48+
Raises:
49+
ValueError: If the provided URL is not in the expected format.
50+
Exception: If there is an error fetching data from the GitHub API.
51+
"""
52+
# Remove the .git suffix if it exists
53+
if url.endswith('.git'):
54+
url = url[:-4]
55+
56+
# Split the URL to extract owner and repository name
57+
parts = url.split('/')
58+
if len(parts) < 5 or parts[2] != 'github.com':
59+
raise ValueError(f"Invalid GitHub repository URL {url}")
60+
61+
owner = parts[3]
62+
repo = parts[4]
63+
64+
# GitHub API endpoint for releases
65+
api_url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
66+
67+
# Make a GET request to the GitHub API
68+
response = requests.get(api_url)
69+
70+
if response.status_code == 200:
71+
release_data = response.json()
72+
return release_data['tag_name'] # Return the latest version tag
73+
else:
74+
raise Exception(f"Error fetching data from GitHub API: {response.status_code} - {response.text}")
75+
76+
def get_current_version_from_fetch_content(cmake_code):
77+
"""
78+
Extracts the currently used Git tag from a FetchContent call in CMake code.
79+
80+
Args:
81+
cmake_code (str): The CMake code containing the FetchContent call.
82+
83+
Returns:
84+
str: The Git tag used in the FetchContent call, or None if not found.
85+
"""
86+
# Regular expression to match FetchContent_Declare and capture the GIT_TAG argument
87+
pattern = r'FetchContent_Declare\s*\(\s*[^)]+\s*GIT_TAG\s+([^\s)]+)'
88+
89+
match = re.search(pattern, cmake_code)
90+
if match:
91+
return match.group(1) # Return the captured Git tag
92+
return None
93+
94+
def update_fetch_content_versions(cmake_file_path):
95+
"""
96+
Updates the FetchContent blocks in a CMakeLists.txt file to use the latest
97+
version from the corresponding GitHub repositories.
98+
99+
Args:
100+
cmake_file_path (str): The path to the CMakeLists.txt file to be updated.
101+
"""
102+
with open(cmake_file_path, 'r') as file:
103+
cmake_code = file.read()
104+
105+
# Regular expression to find FetchContent blocks
106+
fetch_content_pattern = r'FetchContent_Declare\s*\(\s*(.*?)\s*\)'
107+
108+
# Find all FetchContent blocks
109+
fetch_content_blocks = re.findall(fetch_content_pattern, cmake_code, re.DOTALL)
110+
111+
for block in fetch_content_blocks:
112+
# Extract the GIT_REPOSITORY line
113+
repo_pattern = r'GIT_REPOSITORY\s+([^\s]+)'
114+
repo_match = re.search(repo_pattern, block)
115+
116+
if repo_match:
117+
repo_url = repo_match.group(1)
118+
current_version = get_current_version_from_fetch_content(block)
119+
latest_version = get_last_dependency_version(repo_url)
120+
121+
if current_version != latest_version:
122+
# Replace the old version with the new version in the CMake code
123+
new_block = re.sub(r'GIT_TAG\s+([^\s]+)', f'GIT_TAG {latest_version}', block)
124+
cmake_code = cmake_code.replace(block, new_block)
125+
126+
# Write the updated CMake code back to the file
127+
with open(cmake_file_path, 'w', encoding='utf-8', newline='\n') as file:
128+
file.write(cmake_code.replace('\r\n', '\n')) # Ensure LF line
129+
130+
def main():
131+
"""
132+
Main function to update FetchContent versions in CMakeLists.txt files.
133+
It takes a directory path as an argument and processes all CMakeLists.txt files found,
134+
ignoring specified directories.
135+
"""
136+
if len(sys.argv) < 2:
137+
print("Usage: python update_dependencies.py <directory_path> [ignored_dirs...]")
138+
sys.exit(1)
139+
140+
directory_path = sys.argv[1]
141+
ignored_dirs = sys.argv[2:] # Remaining arguments are ignored directories
142+
143+
if not os.path.isdir(directory_path):
144+
print(f"The provided path '{directory_path}' is not a valid directory.")
145+
sys.exit(1)
146+
147+
cmake_files = get_cmake_files(directory_path, ignored_dirs)
148+
149+
for cmake_file in cmake_files:
150+
print(f"Updating {cmake_file}...")
151+
update_fetch_content_versions(cmake_file)
152+
print(f"Updated {cmake_file} successfully.")
153+
154+
if __name__ == "__main__":
155+
main()

0 commit comments

Comments
 (0)