Motivation
We have multiple production-critical bash scripts, e.g. in https://github.com/os-autoinst/os-autoinst-scripts/, getting more and more complicated. We have good tests but we are lacking other helpful development features like coverage analysis, easier mocking in test, good style checks, etc. So likely the best choice is to rewrite some bash scripts in Python.
Goals
- G1: Some usual bash scripts rewritten in Python with according tests
Execution
- Pick any or multiple scripts from https://github.com/os-autoinst/os-autoinst-scripts/ and rewrite using AI or without
- Transfer ruff style checks and formatting from https://github.com/openSUSE/qem-bot/
Progress
Day 1
As I started some research work using only my smart phone using the Google Gemini webchat https://gemini.google.com/app/ I learned about industry best practices and prepared a migration plan. A migration plan was generated as a markdown document. When I was at my work computer I put that document into the local working space of the project https://github.com/os-autoinst/os-autoinst-scripts/ and slightly adapted it:
```
Role
You are a Senior Python Engineer specializing in Linux systems, DevOps automation, and high-performance I/O. Your goal is to refactor legacy Bash scripts into modern, production-grade Python 3.11+ code, ensuring high quality via automated checks.
Project Architecture
- Package Name:
os-autoinst-scripts - Source Location:
src/os-autoinst-scripts/ - Tests Location:
tests/ - Dependency Manager:
pyproject.toml(Standard PEP 621)
Tech Stack & Libraries
CLI Framework: - MUST use
typer.- Use
typer.Optionandtyper.Argumentwith explicit type hints. - Add
richfor colored output (viatyper[all]). - Forbidden:
argparse,optparse,click(unless via Typer).
- Use
Shell Operations:
- Simple commands: Use
subprocess.run(..., check=True, capture_output=True, text=True). - Complex pipes/logic: Use the
shlibrary (e.g.,from sh import git, rpm). - Forbidden:
os.system,subprocess.call(without checks).
- Simple commands: Use
HTTP/Network (Performance Critical):
- MUST use
httpx. - Concurrency Rule: If the script iterates over a list (e.g., job IDs, tickets) and makes API calls:
- You MUST use
asyncioandhttpx.AsyncClient(http2=True). - Use
asyncio.gather(*tasks)to run requests in parallel.
- You MUST use
- Single Request: Standard
httpx.get(sync) is acceptable for simple, one-off checks. - Forbidden:
requests,urllib.
- MUST use
File Handling:
- MUST use
pathlib.Path. - Forbidden:
os.path,open()(usePath.read_text()/Path.write_text()where possible).
- MUST use
Coding Standards (Enforced by Ruff)
- Type Hints: REQUIRED for all function arguments and return values.
- Docstrings: Google-style docstrings for important high-level functions. Do not over-document or document obvious functionality.
- Error Handling: - Catch specific exceptions (e.g.,
httpx.HTTPError).- Use
sys.exit(1)with a clear error message (viatyper.secho(..., fg=typer.colors.RED)).
- Use
- Style:
- Max line length: 120.
- Use all ruff checks as defined in https://github.com/openSUSE/qem-bot/blob/master/pyproject.toml
- Take over test and check calls from https://github.com/openSUSE/qem-bot/blob/master/Makefile
- Sort imports (standard library -> third party -> local).
Workflow & Verification Guidelines
You must follow this iterative process for every script:
- Atomic Changes: Refactor ONE script at a time. Do not combine multiple migrations.
- Implementation:
- Create the Python script in
src/os-autoinst-scripts/. - Create a corresponding test file in
tests/test_.py.
- Create the Python script in
- Verification (The "Quality Gate"):
- Run style checks and tests:
make test, same as in https://github.com/openSUSE/qem-bot/blob/master/Makefile - Check Coverage:
pytest --cov=src/os-autoinst-scripts tests/test_.pyormake test-with-coverage - CRITICAL: If any step fails, fix the code and re-run verification. Do not proceed until tests pass.
- Run style checks and tests:
- Commit Strategy:
- Once verification passes, generate a git commit.
- Message Format:
refactor(): Convert bash to python - Description: Briefly list technical changes (e.g., "Used async httpx for API calls", "Added unit tests").
Definition of Done
A task is complete only when:
1. Legacy Bash script logic is fully ported.
2. New Python script passes ruff.
3. New Python script has passing pytest coverage.
4. Changes are committed to git.
TASK: Batch Migration of os-autoinst-scripts
I have prepared a list of legacy scripts that need to be migrated to Python. Please act as an autonomous developer and process this list item by item following the rules as stated above.
Inputs
- Work Queue: Read
MIGRATION_TASKS.txt(List of files to process).
Process Instructions
Iterate through every file listed in MIGRATION_TASKS.txt. For each file:
- Analyze: Read the content of the legacy Bash script.
- Plan: Determine the new Python filename (e.g.,
src/os-autoinst-scripts/.py) and Test filename (tests/test_.py). - Implement:
- Write the Python implementation using
typer,httpx(async if needed), andshas per the Context rules. - Write a comprehensive
pytestfile usingrespxfor mocking.
- Write the Python implementation using
- Verify (Quality Gate):
- Run style checks and tests
- IF tests fail: Analyze the error, fix the code, and re-run tests. Do not proceed until green.
- Commit:
- Remove the old Bash script (unless it's a critical entry point that needs a shim).
- Create a git commit
Constraints
- One Commit Per Script: Do not bunch changes. I want a clean history.
- Stop on Error: If you cannot fix a test after 3 attempts, stop and ask for human intervention.
- Preserve Logic: Ensure command-line arguments and flags match the original intent (unless they were "bash-isms" that Typer handles better).
- Keep tests intact: Multiple scripts already have a corresponding test file in test/ . Create new test implementations based on those existing files
- Prevent data loss: Never call "git reset" or "git restore". If in doubt ask for human intervention to ensure a clean state.
```
then I primed gemini-cli with
Read MIGRATION_CONTEXT.md and execute the migration tasks defined in there. Create a TODO-List so that I can follow progress.
Day 2
I realized that if I want to apply all the ruff rules and best practices that I recently adopted for https://github.com/openSUSE/qem-bot/ I first need to adapt all already existing python files to that standard. Otherwise I am just piling up style and test failures and will loose overview. The same applies to AI agents. As gemini-cli would mostly be able to fix encountered style issues but actually at a slow pace compared to me sitting down and focussing for some time to do it myself I mostly applied changes myself bringing all current python scripts in os-autoinst-scripts to a current standard.
Day 3
Similar to what I did in qem-bot and os-autoinst-scripts as I found a colleague working on https://github.com/openSUSE/git-sha-verify I quickly added there also my recently learned best practices also bringing that mini-project to a good standard, good enforced style, quick dependency management with uv and a helpful Makefile.
For os-autoinst-scripts I have sorted out all "fix all already existing Python files" into commits that should come first before continuing with migrating bash scripts to Python. I have created https://github.com/os-autoinst/os-autoinst-scripts/pull/493 for the first changes. That PR needs fixing to provide "uv" in dependencies as visible in both the GHA as well as OBS checks.
Day 4
Similar as to what others have observed limited quotas for AI usage as well as deficiencies in non-pro models makes progress slow so I am stretching out the work over multiple days. But that topic is already being discussed and alternatives have been mentioned that will likely help us in the near future. In the meantime I realized I actually forgot some practices which I already applied years ago in https://github.com/os-autoinst/openqa_review/, e.g. using pytest-mock instead of a weird mix of monkeypatch and unittest-mock. So I also incorporated that into the current work as well as patching up minor issues found in https://github.com/os-autoinst/openqa_review/ itself.
Day 5
With the end of hackweek approaching I am coming up with a plan for the various changes I have started but could not finish yet.
Results
Open points
- To have Python files in a "proper package" path all Python scripts are now in src/osautoinstscripts but that means those are not immediate replacements of the former bash scripts. I wonder if I should reconsider this decision and move those scripts back into the top folder. Also, is "src/osautoinstscripts/" redundant? Should it just be "src" or just "osautoinstscripts"? -> Those separate directories are certainly best practice but a symlink in the top folder can provide a good middle ground
- Maybe the dependency on "typer+httpx+sh" is seen as too much and we might want to limit ourselves to base libraries
- os-autoinst-scripts was created so that we have a common location to store "useful scripts and snippets" with less requirements on quality compared to other projects in the scope of os-autoinst. With the additional increased code quality requirements the original goal might not be easily fulfillable anymore -> What we can do though is to apply file specific rule exclusions. Alternatively provide a separate directory like "contrib/" for low-quality content.
Verdict
I have initial states of Python scripts that should be able to replace all bash scripts in os-autoinst-scripts that look reasonably correct. Most are more verbose with more lines of code but considering that they have arguably easier to read code and internal documentation and an easier command line interface with help texts and easier to read and faster tests that should also be easier to maintain I call this progress and success on the original goal.
No Hackers yet
Looking for hackers with the skills:
Nothing? Add some keywords!
This project is part of:
Hack Week 25
Activity
Comments
Be the first to comment!
Similar Projects
This project is one of its kind!