Skip to content

Conversation

@th0ma7
Copy link
Contributor

@th0ma7 th0ma7 commented Jun 30, 2025

Description

Intent is to automate wheel updates through github-action so a PR can be made for every wheel needing updating as found into our requirement files. IMPORTANT: Said PR would contain a wheel update for all package where it was found.

How it works:

  1. A .github/actions/generate_dependabot.py script generates a .github/dependabot.yml including all detected requirement files such as:
version: 2
updates:
- package-ecosystem: pip
  directory: /spk/python310-wheels/src
  requirements-file: requirements-abi3.txt
  schedule:
    interval: weekly
  groups:
    all-python-deps:
      patterns:
      - '*'
- package-ecosystem: pip
  directory: /spk/python311-wheels/src
  requirements-file: requirements-abi3.txt
  schedule:
    interval: weekly
  groups:
    all-python-deps:
      patterns:
      - '*'
...
  1. A .github/workflows/generate-dependabot.yml calls the generate_dependabot.py script every week.
  2. github-action ends-up checking potential update candidates from the .github/dependabot.yml and creates associated PR

Extra tid-bits:

  • Manages exclusion list namely for pip, Cython and msgpack
  • native/python3* now uses a requirements.txt file and capture pip version at processing time

Relates to : #6619

Checklist

  • Build rule all-supported completed successfully
  • New installation of package completed successfully
  • Package upgrade completed successfully (Manually install the package again)
  • Package functionality was tested
  • Any needed documentation is updated/created

Type of change

  • Bug fix
  • New Package
  • Package update
  • Includes small framework changes
  • This change requires a documentation update (e.g. Wiki)

@th0ma7 th0ma7 requested review from hgy59 and mreid-tt June 30, 2025 15:47
@th0ma7 th0ma7 force-pushed the automate-wheel-pr-update branch from 06aec54 to 73618ed Compare June 30, 2025 19:03
@th0ma7
Copy link
Contributor Author

th0ma7 commented Jun 30, 2025

@hgy59 and @mreid-tt this is a little outside my normal area of work... I'm hoping with this to automate wheel updates. One area I'm unsure it will work ok is in the crossenv definition files as there is a build|cross|wheelhouse: prefix. Also it may require an exclude list such as pip (until fixed), Cython and msgpack...

Your 👀 on this would be appreciated, thnx.

EDIT: Exclude portion now in theory solved... as for the prefix, I'll need to further think about it.

@th0ma7
Copy link
Contributor Author

th0ma7 commented Jul 6, 2025

@mreid-tt and @hgy59 you review on this would be much appreciated, thnx.

@mreid-tt
Copy link
Contributor

@th0ma7 This is outside my area of expertise as well, but if I’m understanding correctly, you're reviewing the requirements for various Python versions and triggering a PR whenever updates are available? That seems a bit heavy; wouldn’t this potentially lead to users installing multiple new Python versions every month? Could you help me understand the benefit of this approach?

@th0ma7
Copy link
Contributor Author

th0ma7 commented Jul 20, 2025

The idea is for the wheels requirements definitions for python to be auto updated. This in turn would test python to confirm it still builds ok. When we are ready to release a new version it becomes straight forward as the requirements have already been tested.

@mreid-tt
Copy link
Contributor

The idea is for the wheels requirements definitions for python to be auto updated. This in turn would test python to confirm it still builds ok. When we are ready to release a new version it becomes straight forward as the requirements have already been tested.

Ah! Okay. That would include the requirements we have in the python/* directory? i.e. are we updating the python-wheels as well? I'm not sure updating Python alone is comprehensive enough, but then again doing everything would make for long build times.

@th0ma7
Copy link
Contributor Author

th0ma7 commented Jul 20, 2025

It takes so much time and manipulations to fully update things that automating the wheel requirements process would simplify things greatly.

Personally I'd also automate python versions... But that could be for a later time.

th0ma7 added a commit to th0ma7/spksrc that referenced this pull request Nov 3, 2025
This is taken from PR SynoCommunity#6629 in order to automate wheel updates
using github-action.
th0ma7 added a commit that referenced this pull request Nov 4, 2025
* python314: Initial package for version 3.14.0

* python310: Update from version 3.10.18 to 3.10.19

* python311: Update from version 3.11.13 to 3.11.14

* python312: Update from version 3.12.11 to 3.12.12

* python313: Update from version 3.13.7 to 3.13.8

* python314-wheels: Preliminary package wheel check

* native/python310-314: Use requirement files instead in-Makefile

This is taken from PR #6629 in order to automate wheel updates
using github-action.

* mariadb-connector-c: Enable libmysqlclient compatibility mode

* pydantic-core: Update from version 2.27.2 to 2.41.4

In turn it was migrated to rust and no longer needs its own crossenv

* python314: Add post_install to fix prefix in pkgconfig files

* mariadb-connector-c: Use mysql compat mode and remove old mysql version

* python31*: Update default crossenv pakage versions

* python314: Mark as BROKEN pending PR #6766

* python314-wheels: Mark as BROKEN for now
@th0ma7 th0ma7 force-pushed the automate-wheel-pr-update branch from 09f6d66 to 27ad8b1 Compare November 19, 2025 00:50
@mreid-tt
Copy link
Contributor

hey @th0ma7, did an AI review of this for you...


Detailed Review of PR #6629

Purpose: Automates the generation of Dependabot configuration for Python requirements files in the spksrc repository.

Summary

PR #6629 adds a GitHub Actions workflow and Python script to automatically generate and maintain dependabot.yml configuration for Python package updates. This eliminates the need to manually update the Dependabot config whenever new Python version packages are added or requirements files change.

Files Added

  1. .github/actions/update_python_requirements.py - Python script that:
    • Scans repository for Python requirements files using glob patterns
    • Generates a consolidated dependabot.yml configuration
    • Groups all Python deps together per package
    • Ignores specific packages (pip, Cython, msgpack)
  2. .github/workflows/python-requirements-updater.yml - GitHub Actions workflow that:
    • Runs weekly on Monday at 2 AM UTC
    • Triggers on changes to requirements files or the script itself
    • Creates a PR with the updated dependabot.yml

Strengths

  • Well-documented: Includes HOWTO section with manual testing instructions
  • Clean code structure: Functions are well-separated and documented
  • Proper path handling: Uses leading / for Dependabot directory requirements
  • Sensible defaults: Weekly schedule, grouped updates, reasonable ignore list
  • Self-maintaining: Workflow triggers on its own changes

Issues Found

  1. Missing github-actions ecosystem preservation
    • Current upstream dependabot.yml has:
      • package-ecosystem: "github-actions"
        directory: "/"
        schedule:
        interval: "weekly"
    • The script overwrites this entirely, losing the GitHub Actions updates
    • Fix: Script should either preserve existing entries or explicitly include the github-actions ecosystem
  2. find_repo_root() has redundant logic
    • Checks for spksrc subdirectory, but spksrc is the repo root (not a subdirectory)
    • Lines 51-56 will never match because os.path.isdir(os.path.join(current_path, 'spksrc')) is always false at repo root
  3. Missing requirements patterns
    • Pattern spk/python*/crossenv/requirements-default.txt only catches requirements-default.txt
    • There are also files like requirements-numpy-*.txt, requirements-scipy.txt, etc.
    • These won't get Dependabot updates
  4. No validation of generated YAML
    • Script writes YAML but doesn't validate it matches Dependabot schema
    • Could add schema validation step in workflow
  5. Hardcoded Python version in workflow
    • Uses python-version: '3.11' but could use a matrix or latest stable

Suggestions

  1. Preserve existing ecosystem entries:
   def generate_dependabot_config(repo_root):
       # Read existing config first
       existing_path = os.path.join(repo_root, ".github", "dependabot.yml")
       existing_updates = []
       if os.path.exists(existing_path):
           with open(existing_path) as f:
               existing = yaml.safe_load(f)
               # Keep non-pip ecosystems
               existing_updates = [u for u in existing.get('updates', [])
                                   if u.get('package-ecosystem') != 'pip']
       # ... rest of function
       return {
           "version": 2,
           "updates": existing_updates + updates
       }, requirements_found
  1. Consider per-package requirements files in spk/*/src/requirements-*.txt pattern for packages like ffsync, octoprint, tvheadend that have their own Python requirements
  2. Add workflow run summary showing which files were found/processed

Testing

I ran the script locally and it found 30 requirements files and generated valid YAML. The output structure looks correct for Dependabot.

Verdict

Good addition for reducing manual maintenance. The main issue is losing the github-actions ecosystem entry when regenerating. This should be addressed before merging.

push:
paths:
- 'spk/python*/crossenv/requirements-default.txt'
- 'spk/python*/src/requirements-*.txt'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- 'spk/python*/src/requirements-*.txt'
- 'spk/python*/src/requirements-*.txt'
- 'spk/python*-wheels/src/requirements-*.txt'
- 'spk/*/src/requirements-crossenv.txt'
- 'spk/*/src/requirements-pure.txt'

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
python-version: '3.11'
python-version: '3.x'

body: |
## Python Requirements Configuration Update

This PR updates the `.github/dependabot.yml` configuration file based on the current Python requirements files in the repository.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This PR updates the `.github/dependabot.yml` configuration file based on the current Python requirements files in the repository.
This PR updates the \`.github/dependabot.yml\` configuration file based on the current Python requirements files in the repository.

Comment on lines +48 to +50
- `spk/python*/crossenv/requirements-default.txt`
- `spk/python*/src/requirements-*.txt`
- `native/python*/src/requirements.txt`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- `spk/python*/crossenv/requirements-default.txt`
- `spk/python*/src/requirements-*.txt`
- `native/python*/src/requirements.txt`
- \`spk/python*/crossenv/requirements-default.txt\`
- \`spk/python*/src/requirements-*.txt\`
- \`spk/python*-wheels/src/requirements-*.txt\`
- \`spk/*/src/requirements-crossenv.txt\`
- \`spk/*/src/requirements-pure.txt\`
- \`native/python*/src/requirements.txt\`

### Configuration
- **Schedule**: Weekly updates
- **Grouping**: All Python dependencies grouped together per package
- **Ignored packages**: pip, Cython, msgpack
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- **Ignored packages**: pip, Cython, msgpack
- **Ignored packages**: pip, Cython, msgpack
- **Preserved**: Non-pip ecosystems (github-actions)

"""
Generate Dependabot configuration for Python requirements files.
Scans the repository for requirements.txt files and creates a consolidated
dependabot.yml configuration with grouped updates.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
dependabot.yml configuration with grouped updates.
dependabot.yml configuration with grouped updates while preserving
non-pip ecosystem entries (e.g., github-actions).


Check that:
- Paths start with "/" (e.g., /spk/python311/src)
- All expected requirements files are included
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- All expected requirements files are included
- All expected requirements files are included
- Non-pip ecosystems (github-actions) are preserved

Comment on lines +41 to +42
Find the repository root by looking for common indicators
like .git directory or spksrc directory.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Find the repository root by looking for common indicators
like .git directory or spksrc directory.
Find the repository root by looking for .git directory.

Comment on lines +48 to +56
return current_path

if os.path.isdir(os.path.join(current_path, 'spksrc')):
return current_path

for indicator in ['.gitignore', 'README.md', 'LICENSE']:
if os.path.isfile(os.path.join(current_path, indicator)):
if os.path.isdir(os.path.join(current_path, 'spksrc')):
return current_path
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return current_path
if os.path.isdir(os.path.join(current_path, 'spksrc')):
return current_path
for indicator in ['.gitignore', 'README.md', 'LICENSE']:
if os.path.isfile(os.path.join(current_path, indicator)):
if os.path.isdir(os.path.join(current_path, 'spksrc')):
return current_path
return current_path

"spk/python*/src/requirements-abi3.txt",
"spk/python*/src/requirements-crossenv.txt",
"spk/python*/src/requirements-pure.txt",
"native/python*/src/requirements.txt"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"native/python*/src/requirements.txt"
"spk/python*-wheels/src/requirements-abi3.txt",
"spk/python*-wheels/src/requirements-crossenv.txt",
"spk/python*-wheels/src/requirements-pure.txt",
"native/python*/src/requirements.txt",
"spk/*/src/requirements-crossenv.txt",
"spk/*/src/requirements-pure.txt",

return ignore_list


def generate_dependabot_config(repo_root):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def generate_dependabot_config(repo_root):
def load_existing_config(config_path):
"""
Load existing dependabot.yml and extract non-pip ecosystem entries.
Returns:
list: Non-pip ecosystem update entries to preserve
"""
if not os.path.exists(config_path):
return []
with open(config_path) as f:
existing = yaml.safe_load(f)
if not existing or 'updates' not in existing:
return []
return [u for u in existing['updates'] if u.get('package-ecosystem') != 'pip']
def generate_dependabot_config(repo_root, preserve_ecosystems=None):

def generate_dependabot_config(repo_root):
"""
Generate Dependabot configuration by scanning for Python requirements files.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Args:
repo_root: Path to repository root
preserve_ecosystems: List of existing non-pip ecosystem entries to preserve

Generate Dependabot configuration by scanning for Python requirements files.

Returns:
dict: Dependabot configuration structure
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
dict: Dependabot configuration structure
tuple: (Dependabot configuration dict, list of requirements files found)


Returns:
dict: Dependabot configuration structure
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""
"""
if preserve_ecosystems is None:
preserve_ecosystems = []

for pattern in REQUIREMENTS_PATTERNS
)

updates = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
updates = []
# Start with preserved non-pip ecosystems
updates = list(preserve_ecosystems)

)

updates = []
requirements_found = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
requirements_found = []
requirements_found = []
seen_dirs = set()


# CRITICAL: Dependabot requires relative path from repo root with leading "/"
relative_dir = "/" + os.path.relpath(os.path.dirname(req_file), repo_root)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Avoid duplicate directory+file combinations
dir_file_key = (relative_dir, filename)
if dir_file_key in seen_dirs:
continue
seen_dirs.add(dir_file_key)
requirements_found.append(req_file)

repo_root = find_repo_root(script_dir)

print(f"Repository root: {repo_root}")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Load existing config to preserve non-pip ecosystems
config_path = os.path.join(repo_root, ".github", "dependabot.yml")
preserved = load_existing_config(config_path)
print(f"Preserving {len(preserved)} non-pip ecosystem entries")

print(f"Repository root: {repo_root}")

# Generate configuration
dependabot_config, requirements_found = generate_dependabot_config(repo_root)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
dependabot_config, requirements_found = generate_dependabot_config(repo_root)
dependabot_config, requirements_found = generate_dependabot_config(repo_root, preserved)

Comment on lines +151 to +152
output_path = os.path.join(repo_root, ".github", "dependabot.yml")
os.makedirs(os.path.dirname(output_path), exist_ok=True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
output_path = os.path.join(repo_root, ".github", "dependabot.yml")
os.makedirs(os.path.dirname(output_path), exist_ok=True)
os.makedirs(os.path.dirname(config_path), exist_ok=True)

output_path = os.path.join(repo_root, ".github", "dependabot.yml")
os.makedirs(os.path.dirname(output_path), exist_ok=True)

with open(output_path, "w") as f:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
with open(output_path, "w") as f:
with open(config_path, "w") as f:

yaml.dump(dependabot_config, f, sort_keys=False, default_flow_style=False, indent=2)

print(f"Generated dependabot.yml with {len(dependabot_config['updates'])} update configurations")
print(f"Output: {output_path}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
print(f"Output: {output_path}")
print(f"Output: {config_path}")

@mreid-tt
Copy link
Contributor

I've added the suggested changes from the AI as follows:


Suggested improvements for the Python requirements updater:

  1. Preserve non-pip ecosystem entries
    • The current implementation overwrites the entire dependabot.yml, which removes any github-actions ecosystem entries
    • Added load_existing_config() to read and preserve non-pip ecosystems before regenerating
  2. Expanded requirements patterns
    • Added spk/python*-wheels/src/requirements-*.txt for wheels packages
    • Added spk/*/src/requirements-crossenv.txt and requirements-pure.txt to catch per-package Python requirements
  3. Deduplication
    • Multiple glob patterns can match the same file, causing duplicate entries in dependabot.yml
    • Added tracking of seen (directory, filename) pairs to avoid duplicates
  4. Consistent output
    • Sorted paths before processing for reproducible, diff-friendly results
  5. Simplified find_repo_root()
    • Removed redundant checks for spksrc subdirectory - the .git directory check is sufficient
  6. Workflow update
    • Changed Python version from hardcoded 3.11 to 3.x (latest stable)

@hgy59
Copy link
Contributor

hgy59 commented Jan 18, 2026

I highly recommend to restrict this automation to python and python-wheels packages.
Those packages are under our control.

For all other python packages you can't just process the requirements files. You need to take the project.toml file of each wheel into concern to resolve the dependencies over all.
And you NEVER are allowed to update requirements files of homeassistant.

Since wheel_name==version is defined in the requirements files, this is an agreement that must not be broken and prohibits an automatic update of all packages.

It would need a redesign to use original requirements like wheel_name>=min_version;<=max_version to automate anything.
And those requirements must be adjusted on new package versions.
This could probably be automated (but again, this does not apply to homeassistant).

And do we really want to update wheels in python packages without updating the package version?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants