Skip to content

Commit

Permalink
Merge branch 'main' into release/0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
RobPasMue committed Apr 17, 2024
2 parents a4c81e0 + f04ce11 commit d95c474
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 16 deletions.
68 changes: 68 additions & 0 deletions .github/workflows/autoupdate_python_versions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Autoupdate Python Versions
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
# Or if the "scripts" directory is modified
push:
paths:
- 'scripts/**'


env:
MAIN_PYTHON_VERSION: '3.12'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
autoupdate-python-versions:
name: Autoupdate Python Versions
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.PYANSYS_CI_BOT_TOKEN }}

- name: Set up Python ${{ env.MAIN_PYTHON_VERSION }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.MAIN_PYTHON_VERSION }}

- name: Run the autoupdate script
run: |
python -m pip install --upgrade pip
pip install -r scripts/requirements.txt
python scripts/update_python_versions.py
- name: Commit changes (if any)
run: |
# Configure git username & email
git config user.name 'pyansys-ci-bot'
git config user.email '[email protected]'
# Check if there are any changes
diff_code=$(git diff --exit-code)
# If there are changes, verify whether the branch 'feat/update-python-version' exists
# If it does, switch to it, rebase it on top of the main branch, and push the changes
# If it doesn't, create the branch, commit the changes, and push the branch
if [ -n "$diff_code" ]; then
if git show-ref --verify --quiet refs/heads/feat/update-python-version; then
git checkout feat/update-python-version
git rebase main
else
git checkout -b feat/update-python-version
fi
git add .
git commit -m "chore: update Python versions"
git push origin feat/update-python-version
fi
# If there are changes, create a pull request
if [ -n "$diff_code" ]; then
gh pr create --title "chore: update Python versions" --body "This PR updates the Python versions in the CI configuration files." --base main --head feat/update-python-version
fi
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ repos:
- --line-length=88

- repo: https://github.com/psf/black
rev: 24.3.0
rev: 24.4.0
hooks:
- id: black
args:
Expand Down Expand Up @@ -54,7 +54,7 @@ repos:

# this validates our github workflow files
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.28.1
rev: 0.28.2
hooks:
- id: check-github-workflows

Expand Down
2 changes: 2 additions & 0 deletions scripts/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
requests==2.31.0
packaging==23.2
224 changes: 224 additions & 0 deletions scripts/update_python_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
"""Script that updates the Python versions used in the project."""

import os
import re

from packaging.version import Version
import requests


def is_version_string(s: str) -> bool:
"""Check if the string is in accepted version format.
Parameters
----------
s : str
String to check.
Returns
-------
bool
True if the string is in the accepted version format, False otherwise.
"""
pattern = r"^\d+\.\d+\.\d+$"
return bool(re.match(pattern, s))


def get_latest_github_release(user, repo) -> dict:
"""Get the latest release of a GitHub repository.
Parameters
----------
user : str
GitHub username.
repo : str
Repository name.
Returns
-------
dict
JSON response of the latest release.
"""
url = f"https://api.github.com/repos/{user}/{repo}/releases/latest"
response = requests.get(url)
if response.status_code == 200:
return response.json()
else:
print(f"Failed to get releases: {response.content}")
return None


def get_minor_version_sublist_with_greater_patch(list: list[str], current_version: str):
"""Get the sublist of versions with greater patch than the current version."""
major, minor, patch = current_version.split(".")
major, minor, patch = int(major), int(minor), int(patch)
sublist = [version for version in list if version.startswith(f"{major}.{minor}.")]
sublist = [version for version in sublist if int(version.split(".")[2]) > patch]
sublist = sorted(sublist, key=Version, reverse=True)

return sublist


# Get path to the root of the project
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Path to the constants file
CONSTANTS_FILE = os.path.join(
ROOT_DIR, "src", "ansys", "tools", "installer", "constants.py"
)

# Parse the constants file to find the current Python versions
# used in the project. The versions are stored in a tuple
with open(CONSTANTS_FILE, "r") as f:
lines = f.readlines()

# Import the following constants inside the constants file
#
# Example:
#
# VANILLA_PYTHON_VERSIONS = {
# "Python 3.8": "3.8.10",
# "Python 3.9": "3.9.13",
# "Python 3.10": "3.10.11",
# "Python 3.11": "3.11.6",
# "Python 3.12": "3.12.0",
# }
#
# CONDA_PYTHON_VERSION = "23.1.0-4"
#

vanilla_python_versions: dict[str, str] = {}
conda_python_version: str = ""

for line in lines:
if "VANILLA_PYTHON_VERSIONS" in line:
# Store the index of the line where the dictionary starts
start_index = lines.index(line)
break

# Get the dictionary that contains the Python versions
for line in lines[start_index:]:
if "}" in line:
# Store the index of the line where the dictionary ends
end_index = lines.index(line, start_index)
break

# Parse the dictionary to get the Python versions
for line in lines[start_index : end_index + 1]:
if "Python" in line:
# Extract the Python version and the version number
python_version = line.split(":")[0].strip().replace('"', "")
version_number = line.split(":")[1].strip().replace('"', "").replace(",", "")

# Store the Python version and the version number
vanilla_python_versions[python_version] = version_number

# Get the Conda Python version
for line in lines:
if "CONDA_PYTHON_VERSION" in line:
conda_python_version = line.split("=")[1].strip().replace('"', "")

# LOG - Print the current Python versions
print("Current Vanilla Python versions:")
for version in vanilla_python_versions.values():
print(f">>> '{version}'")

print("Current Conda Python version")
print(f">>> '{conda_python_version}'")

# --------------------------------------------------------------------------------------------

print("--- \nUpdating Python versions...\n")

# Check remote Python versions available
PYTHON_FTP = "https://www.python.org/ftp/python"

# List all folders in the Python FTP
response = requests.get(PYTHON_FTP)
text = response.text.split("\n")
ftp_versions = []
for line in text:
tmp = line.strip('<a href="').split('/">')[0]
# Check if the folder is a Python version
if is_version_string(tmp):
ftp_versions.append(tmp)

# For each minor version, get the patch versions available
# greter than the current patch version
for python_version_key, python_version_value in vanilla_python_versions.items():
# Get the minor version of the current Python version
minor_version = ".".join(python_version_value.split(".")[:2])

# Get the patch versions available
patch_versions = get_minor_version_sublist_with_greater_patch(
ftp_versions, python_version_value
)

# Check if the patch versions contain the executable
new_patch_version = None
for patch_version in patch_versions:
# Check if the executable exists
response_1 = requests.get(
f"{PYTHON_FTP}/{patch_version}/Python-{patch_version}.tar.xz"
)
response_2 = requests.get(
f"{PYTHON_FTP}/{patch_version}/python-{patch_version}-amd64.exe"
)
if response_1.status_code == 200 and response_2.status_code == 200:
print(f"Python {patch_version} is available for download")
new_patch_version = patch_version
break

# Update the Python version
if new_patch_version:
vanilla_python_versions[python_version_key] = new_patch_version
else:
print(f"Python {python_version_value} is already the latest version available")

# Get the latest Conda Python version
latest_conda_release = get_latest_github_release("conda-forge", "miniforge")

# Verify that the assets are available
assets = latest_conda_release["assets"]
new_conda_version = None
count = 0
for asset in assets:
if f"Miniforge3-{latest_conda_release['name']}-Linux-x86_64.sh" in asset["name"]:
count += 1
if f"Miniforge3-{latest_conda_release['name']}-Windows-x86_64.exe" in asset["name"]:
count += 1
if count == 2:
new_conda_version = latest_conda_release["name"]
break

# Update the Conda Python version
if new_conda_version:
conda_python_version = new_conda_version
print(f"Conda Python version updated to {conda_python_version}")
else:
print(f"Conda Python version is already the latest version available")

print("\nPython versions updated successfully\n ---")

# --------------------------------------------------------------------------------------------

# LOG - Print the new Python versions
print("New Vanilla Python versions:")
for version in vanilla_python_versions.values():
print(f">>> '{version}'")

print("New Conda Python version:")
print(f">>> '{conda_python_version}'")

# Update the constants file with the new Python versions
# Write the new Python versions to the constants file
with open(CONSTANTS_FILE, "w") as f:
for line in lines[:start_index]:
f.write(line)

f.write("VANILLA_PYTHON_VERSIONS = {\n")
for python_version, version_number in vanilla_python_versions.items():
f.write(f' "{python_version}": "{version_number}",\n')
f.write("}\n\n")

f.write(f'CONDA_PYTHON_VERSION = "{conda_python_version}"\n')
18 changes: 18 additions & 0 deletions src/ansys/tools/installer/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,21 @@

VENV_DEFAULT_PATH = "venv_default_path"
VENV_SEARCH_PATH = "venv_search_path"


###############################################################################
# Python versions
###############################################################################
#
# Do not modify below this section
#

VANILLA_PYTHON_VERSIONS = {
"Python 3.8": "3.8.10",
"Python 3.9": "3.9.13",
"Python 3.10": "3.10.11",
"Python 3.11": "3.11.9",
"Python 3.12": "3.12.3",
}

CONDA_PYTHON_VERSION = "24.1.2-0"
25 changes: 13 additions & 12 deletions src/ansys/tools/installer/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@
ABOUT_TEXT,
ANSYS_FAVICON,
ASSETS_PATH,
CONDA_PYTHON_VERSION,
INSTALL_TEXT,
LOG,
PRE_COMPILED_PYTHON_WARNING,
PYTHON_VERSION_TEXT,
UNABLE_TO_RETRIEVE_LATEST_VERSION_TEXT,
VANILLA_PYTHON_VERSIONS,
)
from ansys.tools.installer.create_virtual_environment import CreateVenvTab
from ansys.tools.installer.installed_table import InstalledTab
Expand Down Expand Up @@ -224,14 +226,14 @@ def __init__(self, show=True):
python_version.setLayout(python_version_layout)

self.python_version_select = QtWidgets.QComboBox()
self.python_version_select.addItem("Python 3.8", "3.8.10")
self.python_version_select.addItem("Python 3.9", "3.9.13")
self.python_version_select.addItem("Python 3.10", "3.10.11")
self.python_version_select.addItem("Python 3.11", "3.11.6")
self.python_version_select.addItem("Python 3.12", "3.12.0")

# Set the default selection to "Python 3.11"
default_index = self.python_version_select.findText("Python 3.11")
for elem_key, elem_value in VANILLA_PYTHON_VERSIONS.items():
self.python_version_select.addItem(elem_key, elem_value)

# Set the default selection to the last Python version
VANILLA_PYTHON_VERSIONS
default_index = self.python_version_select.findText(
list(VANILLA_PYTHON_VERSIONS.keys())[-1]
)
self.python_version_select.setCurrentIndex(default_index)
python_version_layout.addWidget(self.python_version_select)

Expand Down Expand Up @@ -561,14 +563,13 @@ def download_and_install(self):
filename = f"python-{selected_version}-amd64.exe"
LOG.info("Installing vanilla Python %s", selected_version)
else:
conda_version = "23.1.0-4"
# OS based file download
if is_linux_os():
LOG.info("Linux")
url, filename = get_conda_url_and_filename(conda_version)
url, filename = get_conda_url_and_filename(CONDA_PYTHON_VERSION)
else:
url = f"https://github.com/conda-forge/miniforge/releases/download/{conda_version}/Miniforge3-{conda_version}-Windows-x86_64.exe"
filename = f"Miniforge3-{conda_version}-Windows-x86_64.exe"
url = f"https://github.com/conda-forge/miniforge/releases/download/{CONDA_PYTHON_VERSION}/Miniforge3-{CONDA_PYTHON_VERSION}-Windows-x86_64.exe"
filename = f"Miniforge3-{CONDA_PYTHON_VERSION}-Windows-x86_64.exe"
LOG.info("Installing miniconda from %s", url)
try:
self._download(url, filename, when_finished=self._run_install_python)
Expand Down
Loading

0 comments on commit d95c474

Please sign in to comment.