-
Notifications
You must be signed in to change notification settings - Fork 2
feat: implement catalog workflow automation with standalone PR creation #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ChristopherJHart
merged 23 commits into
master
from
feature/catalog-workflow-automation
Dec 20, 2025
Merged
feat: implement catalog workflow automation with standalone PR creation #25
ChristopherJHart
merged 23 commits into
master
from
feature/catalog-workflow-automation
Dec 20, 2025
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Implements automated catalog contribution workflow that creates PRs against catalog repository with proper directory structure and writes metadata back to test case files. **Issue #22: Add catalog workflow CLI flags** - Added --catalog-workflow flag to enable catalog workflow mode - Added --catalog-repo option (default: US-PS-SVS/catalog) - Added --test-cases-dir option (default: workspace/test_cases/) - Threaded flags through driver.py to override target repo when catalog mode enabled - Built catalog repository URL from github_api_url for metadata writeback **Issue #23: Implement PR metadata writeback** - Created github_ops_manager/processing/test_cases_processor.py module - Implemented find_test_cases_files() to locate test_cases.yaml files - Implemented load_test_cases_yaml() and save_test_cases_yaml() with format preservation - Implemented find_test_case_by_filename() to match test cases by generated_script_path - Implemented update_test_case_with_pr_metadata() to add PR fields - Added write_pr_metadata_to_test_cases() async function in pull_requests.py - Writes catalog_pr_git_url, catalog_pr_number, catalog_pr_url, catalog_pr_branch back to YAML **Issue #24: Handle robot file copy to catalog structure** - Added OS_TO_CATALOG_DIR_MAP with 15+ OS mappings (ios-xe, nxos, iosxr, etc.) - Implemented normalize_os_to_catalog_dir() for OS name translation - Implemented extract_os_from_robot_filename() to parse filenames - Updated get_desired_pull_request_file_content() to transform paths for catalog - Robot files now placed in catalog/<OS_NAME>/ directory structure - Updated commit_files_to_branch() to accept and pass catalog_workflow flag - Updated sync_github_pull_request() to accept catalog parameters and call writeback - Updated sync_github_pull_requests() to thread catalog parameters **Files Changed:** - github_ops_manager/processing/test_cases_processor.py (NEW) - github_ops_manager/configuration/cli.py (CLI flags) - github_ops_manager/synchronize/driver.py (parameter threading, repo override) - github_ops_manager/synchronize/pull_requests.py (path transformation, writeback) **Benefits:** - Eliminates manual catalog contribution friction - Automatic PR creation with proper catalog directory structure - Full metadata trail from generation → PR → catalog integration - Test automation available via PR branch while under review 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Refactors catalog workflow to be data-driven by reading catalog_destined attribute from individual test cases rather than using a global --catalog-workflow flag. This allows mixing catalog and non-catalog PRs in the same run. **Key Changes:** **CLI (configuration/cli.py:200-254)** - Removed --catalog-workflow flag - Kept --catalog-repo and --test-cases-dir as configuration options - Updated help text to indicate use with catalog_destined=true test cases - Added docstring explaining automatic detection behavior **Driver (synchronize/driver.py:19-159)** - Removed catalog_workflow parameter from run_process_issues_workflow() - Removed conditional repo override logic (now handled per-PR) - Always passes catalog config and auth parameters to sync_github_pull_requests() - Builds catalog_repo_url unconditionally for potential use **Pull Requests (synchronize/pull_requests.py:401-534)** - sync_github_pull_requests() now accepts auth parameters - Detects catalog-destined issues via getattr(issue, 'catalog_destined', False) - Creates catalog adapter only if catalog-destined issues exist - Fetches catalog repository state (issues, PRs, default branch) - Routes each PR to appropriate adapter based on catalog_destined attribute - Catalog workflow features (path transformation, metadata writeback) applied per-issue **Benefits:** - Granular control: Mix catalog and non-catalog in same test_cases.yaml - Data-driven design: Test cases declare their own intent - Single workflow run handles both types - Cleaner separation: tac-quicksilver sets flag, github-ops-manager respects it - More flexible and maintainable **Breaking Change:** - --catalog-workflow CLI flag removed (replaced by per-issue catalog_destined field) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Changes default catalog repository from US-PS-SVS/catalog to Testing-as-Code/tac-catalog across all configuration points. Updated in: - github_ops_manager/configuration/cli.py (CLI default) - github_ops_manager/synchronize/driver.py (function parameter) - github_ops_manager/synchronize/pull_requests.py (function parameter) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Improves reliability of OS detection by parsing the os:<os> tag from the Test Tags section in robot files rather than inferring from filenames. **Why This Is Better:** - Test Tags are structured metadata intentionally placed by tac-quicksilver - More reliable than filename parsing (filenames can vary) - Uses regex pattern to find os:<os> tag in robot file content - Falls back to filename parsing if Test Tags parsing fails **Implementation:** - Added extract_os_from_robot_content() with regex pattern `os:([a-zA-Z0-9_-]+)` - Updated extract_os_from_robot_filename() docstring to note it's a fallback - Modified get_desired_pull_request_file_content() to: 1. First try Test Tags extraction (preferred) 2. Fall back to filename parsing if needed 3. Log which extraction method succeeded **Example:** ```robot Test Tags ... os:ios-xe ... category:foundations ... feature:interfaces ``` Extracts "ios-xe" from Test Tags → maps to "catalog/IOS-XE/" directory **Files Changed:** - github_ops_manager/processing/test_cases_processor.py (new function) - github_ops_manager/synchronize/pull_requests.py (updated extraction logic) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Changed pattern from [a-zA-Z0-9_-]+ to \S+ for more flexible matching. \S+ matches any non-whitespace character, which is simpler and handles more edge cases than the explicit character class. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Added [project.scripts] section to pyproject.toml to expose the CLI as a console script. This allows the package to be invoked via: - uv run github-ops-manager - github-ops-manager (after installation) Without this entry point, the CLI was not accessible as a command after package installation. Entry point: github-ops-manager -> github_ops_manager.configuration.cli:typer_app 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Changed find_test_cases_files() to use non-recursive globbing (*.yaml
instead of **/*.yaml) to avoid picking up backup files in subdirectories
like .backups/
**Problem:**
Recursive glob pattern would find files like:
- workspace/test_cases/test_cases.yaml ✓
- workspace/test_cases/.backups/test_cases_old.yaml ✗ (unwanted)
**Solution:**
Only search immediate directory, not subdirectories:
- Before: test_cases_dir.glob('**/*.yaml') # Recursive
- After: test_cases_dir.glob('*.yaml') # Non-recursive
This prevents processing stale/backup test case files that could cause
weird behavior or duplicate PR creation attempts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
This refactoring addresses the architectural issue where catalog-destined test cases were incorrectly trying to create issues+PRs in the catalog repo. The new architecture creates standalone PRs for catalog without issues. Changes: - Add load_catalog_destined_test_cases() to read test_cases.yaml directly - Add create_catalog_pull_requests() for standalone catalog PR creation - Update driver.py to call catalog PR function after issues workflow - Simplify sync_github_pull_requests() to filter out catalog-destined - Fix base_directory path resolution (use test_cases_dir.parent) Workflow now supports: 1. Non-catalog test cases: Create issues+PRs in project repo 2. Catalog-destined test cases: Create standalone PRs in catalog repo Fixes #22 #23 #24 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
When github_api_url ends with a trailing slash (e.g., 'https://wwwin-github.cisco.com/api/v3/'), the URL construction was creating double slashes like: https://wwwin-github.cisco.com//Testing-as-Code/tac-catalog Fixed by using .rstrip('/') to remove trailing slashes from base_url before constructing the catalog_repo_url. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Changed catalog branch naming from:
catalog/{os_name}/{script_name}
To:
feat/add-{script_name}
This follows Git best practices with conventional commit/branch naming patterns.
Example:
Before: catalog/ios-xe/verify-iosxe-error-disable-detection-reason-presence
After: feat/add-verify-iosxe-error-disable-detection-reason-presence
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
Changed catalog branch naming to include OS for better organization:
feat/{os_name}/add-{script_name}
This groups branches by operating system, making it easier to manage
catalog contributions in the repository.
Example:
feat/ios-xe/add-verify-iosxe-error-disable-detection-reason-presence
feat/nxos/add-verify-nxos-vlan-configuration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
- Add new CLI parameters --create-tracking-issues and --tracking-issue-labels - Create synchronize/tracking_issues.py module with issue creation logic - Add Jinja2 template for tracking issue body (templates/tracking_issue.j2) - Update create_catalog_pull_requests() to return PR metadata - Add writeback of project_issue_number and project_issue_url to test_cases.yaml - Update driver.py to conditionally create tracking issues after catalog PRs - Add update_test_case_with_issue_metadata() helper function This enables automatic creation of project repository issues that track catalog PR review and parameter learning tasks, with full traceability via metadata writeback to test_cases.yaml files.
Test case titles in test_cases.yaml may include OS tag prefixes like [IOS-XE] or [NX-OS], but these tags are stripped when creating test case groups in cxtm.yaml. The tracking issue template now uses the clean title (without OS tags) in CLI commands so that scripts learn and scripts run commands target the correct test case group names. - Add strip_os_tag_from_title() helper function - Pass both original and clean titles to template - Update template to use clean title in CLI commands - Keep original title for display purposes
Compute and display a suggested project repository branch name in tracking issues by replacing 'feat/' or 'feature/' prefix with 'learn/' from the catalog branch name. This provides users with a consistent naming convention for parameter learning branches. Examples: feat/nx-os/add-verify-nxos-module-port-number -> learn/nx-os/add-verify-nxos-module-port-number - Add compute_project_branch_name() helper function - Pass suggested_project_branch to template - Display suggestion in first task item
Replace inline code blocks with fenced code blocks (triple backticks) for all CLI commands in the tracking issue template. This enables GitHub's copy button feature, making it easier for users to copy and paste: - git checkout command for suggested branch name - tac-tools scripts learn command - tac-tools scripts run command Each command is now in its own fenced block with bash syntax highlighting.
Fixes a critical bug where test_cases.yaml files could be wiped out when writing PR or issue metadata back to files. The bug occurred when a test case's _source_file metadata pointed to a file that no longer contained that test case (e.g., after the test case was moved to another file). The code would load the file, fail to find the matching test case, but still save the file anyway - overwriting it with whatever was loaded (often just `test_cases: []`). Changes: - Add test_case_found flag to track if matching test case was located - Only save file if a matching test case was actually found and updated - Add warning logs when test case not found to aid debugging This prevents accidental data loss in criteria_needs_review.yaml and other test case files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Adds an additional safety check in save_test_cases_yaml() that refuses to save a file if: 1. The file exists and has test cases 2. The new data would replace it with test_cases: [] This provides defense-in-depth against data loss, complementing the test_case_found checks added in commit 2d333eb. If this check triggers, it logs an error with the existing and new test case counts to aid debugging. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
CRITICAL FIX: The previous implementation had a fatal flaw where open(filepath, "w") immediately truncates the file to 0 bytes BEFORE yaml.dump() is called. If yaml.dump() failed or data was invalid, the file would be left completely empty. This commit implements atomic file writing: 1. Write to a temporary file in the same directory 2. Only if yaml.dump() succeeds, atomically rename temp to target 3. os.replace() is atomic on POSIX - either complete or not at all 4. If anything fails, original file remains untouched This prevents the exact scenario where files are being blanked out (not even containing test_cases: []) as reported in CI runs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
CRITICAL FIX: The fetch-files command was creating 0-byte files when fetching files larger than 1MB because GitHub API's get_content endpoint returns content=None for large files and provides a download_url instead. The criteria_needs_review.yaml file is 2.7MB, which exceeds the 1MB limit, causing it to be fetched as empty and then overwriting the good file during mv operations in CI. Changes: - Check if response.parsed_data.content is None or empty - If so, use the download_url to fetch raw file content via httpx - Raise error if no download_url is available - Add logging for large file downloads This fixes the root cause of files being blanked out in CI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
…est_cases" This reverts commit fb37bf4.
Enhanced tracking issues created for catalog PRs to include a "Test Requirement" section with key metadata extracted from the test case definition: - purpose - commands (list only, no outputs) - pass_criteria - sample_parameters - parameters_to_parsed_data_mapping This provides reviewers with immediate visibility into what the test requirement is designed to do without needing to navigate to the catalog PR or test case files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
- Use literal block scalars (|) for purpose, pass_criteria, and parameters_to_parsed_data_mapping fields to properly handle multi-line strings and special characters - Quote command strings to handle pipes and other special characters - Ensures generated YAML in tracking issues is always valid 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
This PR implements the catalog workflow automation feature that enables automatic contribution of generated test automation back to the catalog repository. The implementation supports a dual-workflow approach where catalog-destined test cases create standalone PRs while non-catalog test cases follow the traditional issue+PR workflow.
Key Features
1. Dual Workflow Support
catalog_destinedorfalse): Create issues+PRs in project repositorycatalog_destined: true): Create standalone PRs in catalog repository (no issues)2. Smart OS Detection
(?:^|\s)os:(\S+)verify_ios_xe_*.robot)3. Path Transformation
iosxe/verify_*.robot→catalog/IOS-XE/verify_*.robot4. PR Metadata Writeback
test_cases.yamlfilescatalog_pr_git_url,catalog_pr_number,catalog_pr_url,catalog_pr_branch5. Conventional Branch Naming
feat/{os_name}/add-{script_name}feat/ios-xe/add-verify-iosxe-error-disable-detection-reason-presenceArchitecture Changes
New Functions
load_catalog_destined_test_cases()(test_cases_processor.py)catalog_destined=truetest casescreate_catalog_pull_requests()(pull_requests.py)Modified Functions
run_process_issues_workflow()(driver.py)create_catalog_pull_requests()after issues workflowbase_directorypath resolution usingtest_cases_dir.parentsync_github_pull_requests()(pull_requests.py)Bug Fixes
.backups/directoryTesting-as-Code/tac-catalogUsage
github-ops-manager repo US-PS-SVS/project process-issues \ --github-api-url="https://wwwin-github.cisco.com/api/v3/" \ --testing-as-code-workflow \ --test-cases-dir="workspace/jobfiles/test_cases" \ issues.yamlSingle command execution handles:
Files Changed
github_ops_manager/processing/test_cases_processor.py: +45 linesgithub_ops_manager/synchronize/driver.py: +44 linesgithub_ops_manager/synchronize/pull_requests.py: +181 lines, -71 linesgithub_ops_manager/configuration/cli.py: Updated help textpyproject.toml: Added console script entry pointTest Cases
Example test case with
catalog_destinedfield:Closes
Fixes #22
Fixes #23
Fixes #24
🤖 Generated with Claude Code