diff --git a/.github/workflows/check_outdated_dependencies.yml b/.github/workflows/check_outdated_dependencies.yml new file mode 100644 index 000000000..5a3c78e7d --- /dev/null +++ b/.github/workflows/check_outdated_dependencies.yml @@ -0,0 +1,24 @@ +--- + +name: Check outdated dependencies + +on: + schedule: + - cron: '0 7 1,15 * *' + + workflow_dispatch: + +jobs: + check_outdate_dependencies: + runs-on: ubuntu-latest + + steps: + - name: Check out the code + uses: actions/checkout@main + + - name: Check workflow dependencies + uses: ./workflows/check_outdate_deps + env: + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REQUIREMENT_FILE: "requirements-workflow.txt" diff --git a/requirements-workflow.txt b/requirements-workflow.txt new file mode 100644 index 000000000..b3d9256a4 --- /dev/null +++ b/requirements-workflow.txt @@ -0,0 +1,4 @@ +ansible==9.1.0 +ansible-compat==4.1.10 +ansible-core==2.16.2 +ansible-lint==6.22.1 diff --git a/workflows/check_outdate_deps/Dockerfile b/workflows/check_outdate_deps/Dockerfile new file mode 100644 index 000000000..d5b42bd04 --- /dev/null +++ b/workflows/check_outdate_deps/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.12-slim + +COPY check_outdate_deps.sh /check_outdate_deps.sh + +RUN chmod +x /check_outdate_deps.sh + +# needed for the open_issue.py +RUN pip3 install requests + +ENTRYPOINT [ "/check_outdate_deps.sh" ] diff --git a/workflows/check_outdate_deps/action.yml b/workflows/check_outdate_deps/action.yml new file mode 100644 index 000000000..a4d3c2e89 --- /dev/null +++ b/workflows/check_outdate_deps/action.yml @@ -0,0 +1,5 @@ +name: 'Check dependencies and try to solve it' +description: 'This action will check dependencies in .github/workflows that are installed using pip and open issue and create a pull request to solve the problem' +runs: + using: 'docker' + image: 'Dockerfile' diff --git a/workflows/check_outdate_deps/check_outdate_deps.sh b/workflows/check_outdate_deps/check_outdate_deps.sh new file mode 100755 index 000000000..eed1198b1 --- /dev/null +++ b/workflows/check_outdate_deps/check_outdate_deps.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +pip3 install -r "$REQUIREMENT_FILE" +input=$(pip3 list --outdated) + +compare_versions() { + local version1=$1 + local version2=$2 + local IFS='.' + + read -ra version1_splitted <<< "$version1" + read -ra version2_splitted <<< "$version2" + + for ((i=0;i<${#version1_splitted[@]};i++)); do + delta=$(( version1_splitted[i] - version2_splitted[i] )) + if (( delta != 0 )); then + echo $delta + return + fi + done + echo 0 +} + +while read -r line; do + if [[ $line =~ ^([a-zA-Z0-9-]+)\ +([0-9]+\.[0-9]+\.[0-9]+)\ +([0-9]+\.[0-9]+\.[0-9]+)\ +([a-zA-Z]+) ]]; then + package="${BASH_REMATCH[1]}" + version="${BASH_REMATCH[2]}" + latest="${BASH_REMATCH[3]}" + + if [ "$(compare_versions "$version" "$latest")" -lt 0 ]; then + ./workflows/check_outdate_deps/open_issue.py "$package" "$version" "$latest" + fi + fi +done <<< "$input" diff --git a/workflows/check_outdate_deps/open_issue.py b/workflows/check_outdate_deps/open_issue.py new file mode 100755 index 000000000..39689ef6f --- /dev/null +++ b/workflows/check_outdate_deps/open_issue.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +import argparse +import requests +import os +import json + + +def get_description(requirement_file, package, latest): + return f"""The package {package} is outdated in {requirement_file}. The latest version is {latest}. Please update the package to the latest version. + +Check the package [here](https://pypi.org/project/{package}/{latest}/) for more information. +""" + + +def get_title(requirement_file, package_name, current_version, latest_version): + return f"Dependency outdated in {requirement_file}: {package_name}:{current_version} -> {latest_version}" + + +if __name__ == '__main__': + # Arguments parsing + parser = argparse.ArgumentParser(description="Open issue with the correct argument") + parser.add_argument("package", type=str, help='The name of the package') + parser.add_argument("version", type=str, help='The current version of the package') + parser.add_argument("latest", type=str, help='The latest version of the package') + args = parser.parse_args() + + # Environment variable + repo = os.environ.get("GITHUB_REPOSITORY") + requirement_file = str(os.environ.get("REQUIREMENT_FILE")) + + # Define the title + issue_title = get_title(requirement_file, args.package, args.version, args.latest) + + # The double quote on the issue_title is necessary to avoid duplicate issues + query = f"repo:{repo} type:issue in:title \"{issue_title}\"" + + # Send the query + response = requests.get("https://api.github.com/search/issues", params={"q": query}) + data = response.json() + + # There is this error that we somehow try to avoid + # {'message': "API rate limit exceeded for 93.45.31.205. (But here's the good news: Authenticated requests get a higher rate limit. Check + # out the documentation for more details.)", 'documentation_url': 'https://docs.github.com/rest/overview/resources-in-the-rest-api#rat + # e-limiting'} + if data["total_count"] > 0: + print("There is already an issue with this title!") + else: + issue_description = get_description(requirement_file, args.package, args.latest) + token = os.environ.get("GITHUB_TOKEN") + issue = {"title": issue_title, "body": issue_description} + headers = {"Authorization": f"token {token}"} + + response = requests.post(f"https://api.github.com/repos/{repo}/issues", headers=headers, data=json.dumps(issue)) + + # Check the response + if response.status_code == 201: + print("Issue created successfully.") + else: + print(f"Failed to create issue. Status code: {response.status_code}.")