Skip to content

Conversation

@gmondello
Copy link
Collaborator

🚀 Performance Improvements & New Features

This PR implements comprehensive performance optimizations and adds automatic budget creation capabilities for cost centers.

✨ Key Features

Performance Optimizations

  • Cost Center Caching: 24-hour TTL intelligent caching system (33,000x+ speedup for cached operations)
  • Bulk API Operations: Safe path now uses bulk membership checking + targeted individual checks
  • Dramatically Reduced API Calls: ~60% reduction in API calls for safe path mode
  • Near-Zero Performance Penalty: Safe path now performs as fast as fast path

Budget Creation (Unreleased API)

  • Automatic Budget Provisioning: --create-budgets flag to auto-create budgets for new cost centers
  • Cost Center Integration: Seamlessly integrated with teams mode cost center creation
  • Zero-Dollar Budgets: Creates budgets with $0 limit and prevent_further_usage enabled
  • Uses Unreleased APIs: Requires GitHub Enterprise Budgets API (not yet publicly available)

📊 Performance Results

Tested with avocado-corp enterprise (124 users, 18 cost centers):

  • Safe path execution: 2+ minutes → 4 seconds (30x faster, 96.7% improvement)
  • API call reduction: ~165 calls → ~65 calls (60% reduction)
  • Cache hit rates: 100% in production testing
  • No performance penalty: Safe path equals fast path performance

🔧 Technical Changes

Caching System:

  • CostCenterCache class with atomic operations
  • 24-hour TTL with automatic cleanup
  • Cache statistics and management CLI options
  • JSON-based persistence

API Optimizations:

  • Updated _make_request() to support POST/DELETE methods
  • Bulk membership checking in add_users_to_cost_center()
  • Smart filtering: bulk check → targeted individual verification
  • Only checks users not already in target cost center

Budget Creation:

  • create_cost_center_budget() method in GitHubCopilotManager
  • Uses cost center UUID (not name) for proper association
  • Integrated into teams cost center provisioning flow
  • Graceful failure handling with logging

CLI Updates:

  • --create-budgets: Enable automatic budget creation
  • --cache-stats: Show cache performance metrics
  • --clear-cache: Clear cost center cache
  • --cache-cleanup: Remove expired cache entries

🏗️ Infrastructure

  • GitHub Actions workflow with cache support
  • Performance comparison utilities
  • Comprehensive logging for cache operations
  • Bug fixes: config_manager.py indentation in try/except blocks

⚠️ Important Notes

  1. Budget Creation: Requires unreleased GitHub Enterprise Budgets API - not documented yet
  2. Backward Compatible: All existing functionality preserved
  3. Cache Location: exports/.cost_center_cache.json (configurable via export_dir)
  4. Safe by Default: Budget creation is opt-in via --create-budgets flag

🧪 Testing

Thoroughly tested with:

  • avocado-corp enterprise (124 users, 18 teams)
  • Cache performance validation (100% hit rates)
  • Budget creation verification (20 budgets successfully created)
  • API call reduction confirmation (~60% fewer calls)
  • Safe path performance validation (4-second execution)

📝 Related

This builds on the previous teams mode improvements and terminology updates.

…et creation

Performance improvements:
- Add cost center caching with 24-hour TTL
- Implement bulk membership checking in safe path mode
- Significantly reduce API calls through bulk operations
- Dramatically improve safe path execution time

Core changes:
- Add CostCenterCache class for intelligent caching
- Optimize add_users_to_cost_center() with bulk operations
- Add cache management CLI options (--cache-stats, --clear-cache)
- Include performance testing and monitoring scripts
- Add GitHub Actions workflow with caching support
- Preserve copilot seats incremental run functionality
- Fix config_manager.py indentation issues in try/except block

New features:
- Add --create-budgets flag for automatic budget creation
- Integrate budget creation with cost center provisioning
- Support for GitHub Enterprise Budgets API (unreleased)
- Use cost center UUID for budget entity identification

The safe path now performs as fast as fast path while maintaining
full safety checks through optimized bulk API patterns. Budget
creation feature allows automatic budget provisioning for new cost
centers (requires unreleased GitHub Enterprise APIs).
@gmondello gmondello requested a review from a team as a code owner October 14, 2025 23:31
Copilot AI review requested due to automatic review settings October 14, 2025 23:31
Comment on lines +25 to +92
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

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

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Restore cost center cache
uses: actions/cache@v4
with:
path: .cache/
key: cost-center-cache-${{ github.repository }}-${{ hashFiles('**/config.yaml') }}
restore-keys: |
cost-center-cache-${{ github.repository }}-
cost-center-cache-
- name: Show cache statistics (before)
run: |
python main.py --cache-stats
- name: Clear cache if requested
if: ${{ inputs.clear_cache == 'true' }}
run: |
echo "Clearing cost center cache as requested..."
python main.py --clear-cache
- name: Clean up expired cache entries
run: |
python main.py --cache-cleanup
- name: Run cost center sync (teams mode)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_ENTERPRISE: ${{ vars.GITHUB_ENTERPRISE }}
run: |
MODE="${{ inputs.mode || 'plan' }}"
echo "Running cost center sync in $MODE mode..."
if [ "$MODE" = "apply" ]; then
python main.py --teams-mode --assign-cost-centers --mode apply --yes
else
python main.py --teams-mode --assign-cost-centers --mode plan
fi
- name: Show cache statistics (after)
if: always()
run: |
python main.py --cache-stats
- name: Upload cache statistics as artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: cache-stats-${{ github.run_number }}
path: |
.cache/
retention-days: 7

performance-report:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 13 days ago

To fix the problem, you should add a permissions block to restrict the permissions of the GITHUB_TOKEN. The most secure and simplest approach is to add the following block at the top level of the workflow (before jobs:), which will restrict all jobs in this workflow to only have read access to repository contents:

permissions:
  contents: read

This setting will ensure the jobs do not have unnecessary write access. If a job (now or in the future) requires additional permissions (e.g., to open issues or PRs, or write to contents), you must explicitly grant only those permissions to the specific jobs needing it. At present, based on the steps in both jobs, only reading contents is necessary (for checkout and artifact actions), so contents: read suffices.

The change should go in .github/workflows/cost-center-sync-cached.yml, inserted between the workflow name: and the on: block (i.e., between lines 1 and 3).

Suggested changeset 1
.github/workflows/cost-center-sync-cached.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/cost-center-sync-cached.yml b/.github/workflows/cost-center-sync-cached.yml
--- a/.github/workflows/cost-center-sync-cached.yml
+++ b/.github/workflows/cost-center-sync-cached.yml
@@ -1,5 +1,7 @@
 name: Cost Center Sync with Caching
 
+permissions:
+  contents: read
 on:
   schedule:
     # Run every 6 hours (can be adjusted based on needs)
EOF
@@ -1,5 +1,7 @@
name: Cost Center Sync with Caching

permissions:
contents: read
on:
schedule:
# Run every 6 hours (can be adjusted based on needs)
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +93 to +136
runs-on: ubuntu-latest
needs: sync-cost-centers
if: always()

steps:
- name: Checkout repository
uses: actions/checkout@v4

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

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Download cache artifacts
uses: actions/download-artifact@v4
with:
name: cache-stats-${{ github.run_number }}
path: .cache/

- name: Generate performance report
run: |
echo "# Cost Center Sync Performance Report" > performance-report.md
echo "" >> performance-report.md
echo "**Run:** ${{ github.run_number }}" >> performance-report.md
echo "**Date:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> performance-report.md
echo "**Mode:** ${{ inputs.mode || 'plan' }}" >> performance-report.md
echo "" >> performance-report.md
echo "## Cache Performance" >> performance-report.md
echo "" >> performance-report.md
echo '```' >> performance-report.md
python main.py --cache-stats >> performance-report.md 2>&1 || true
echo '```' >> performance-report.md
- name: Upload performance report
uses: actions/upload-artifact@v4
with:
name: performance-report-${{ github.run_number }}
path: performance-report.md
retention-days: 30 No newline at end of file

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 13 days ago

To fix this problem, an explicit permissions block should be added to the workflow, ideally at the top/root level so that it applies to all jobs by default. According to the principle of least privilege, the default can usually be set to contents: read, which covers common operations like artifact upload/download and source checkout. If any step requires additional permissions (e.g., opening PRs, commenting on issues), those can be set at the job or step level. For the provided workflow, no operation appears to require more than contents: read, so this setting suffices. The block should be added after the name: field and before on:.


Suggested changeset 1
.github/workflows/cost-center-sync-cached.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/cost-center-sync-cached.yml b/.github/workflows/cost-center-sync-cached.yml
--- a/.github/workflows/cost-center-sync-cached.yml
+++ b/.github/workflows/cost-center-sync-cached.yml
@@ -1,4 +1,6 @@
 name: Cost Center Sync with Caching
+permissions:
+  contents: read
 
 on:
   schedule:
EOF
@@ -1,4 +1,6 @@
name: Cost Center Sync with Caching
permissions:
contents: read

on:
schedule:
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements a comprehensive performance optimization system with automatic budget creation for cost centers. The key focus is on dramatically reducing API calls through intelligent caching and bulk operations while adding budget provisioning capabilities using unreleased GitHub Enterprise APIs.

Key changes include:

  • Cost center caching system with 24-hour TTL providing 33,000x+ performance improvements
  • Bulk API optimizations in safe path mode with smart filtering to reduce API calls by ~60%
  • Automatic budget creation with --create-budgets flag for seamless cost center provisioning

Reviewed Changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/teams_cost_center_manager.py Integrated caching system, budget creation, and performance metrics
src/github_api.py Enhanced API methods with bulk operations and budget creation endpoints
src/cost_center_cache.py New caching implementation with TTL and statistics
src/logger_setup.py Added broken pipe handler for graceful CLI termination
main.py Cache management CLI options and signal handling
src/config_manager.py Reorganized configuration structure
scripts/performance_comparison.py Performance demonstration script
TEAMS_QUICKSTART.md Updated documentation for new features
.github/workflows/cost-center-sync-cached.yml GitHub Actions workflow with cache support
Comments suppressed due to low confidence (1)

main.py:1

  • Invalid Unicode character in the string. The lock emoji '🔒' appears to be corrupted.
#!/usr/bin/env python3

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

IMPORTANT: Users can only belong to ONE cost center. If a user is in multiple teams,
they will be assigned to the LAST team's cost center that is processed.
assignment depends on their current cost center status (existing assignments are preserved by default).
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

The comment describes current behavior but is unclear. Consider clarifying what 'existing assignments are preserved by default' means in practice - does it skip users already in cost centers, or does it move them based on team membership?

Suggested change
assignment depends on their current cost center status (existing assignments are preserved by default).
their existing cost center assignment is preserved by default; users already assigned to a cost center are skipped and not reassigned based on team membership.

Copilot uses AI. Check for mistakes.
self.logger.warning(
f"⚠️ Found {len(multi_team_users)} users who are members of multiple teams. "
"Each user can only belong to ONE cost center - the LAST team processed will determine their assignment."
"Each user can only belong to ONE cost center - assignment depends on their current cost center status."
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

This message is vague about what 'depends on their current cost center status' means. Users need to understand the actual assignment logic - whether existing assignments are preserved, overwritten, or how conflicts are resolved.

Copilot uses AI. Check for mistakes.
# User is in a different cost center
existing_cost_center_name = existing_membership.get('cost_center_name', existing_membership.get('cost_center_id'))
users_in_other_cost_center.append((username, existing_cost_center_name))
self.logger.info(f"Skipping {username} - already in cost center '{existing_cost_center_name}' (use --check-current-cost-center to override)")
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

The flag name in the error message is incorrect. Based on the main.py file, the actual flag is '--check-current-cost-center', but the logic suggests it should reference '--ignore-current-cost-center' to override the skipping behavior.

Suggested change
self.logger.info(f"Skipping {username} - already in cost center '{existing_cost_center_name}' (use --check-current-cost-center to override)")
self.logger.info(f"Skipping {username} - already in cost center '{existing_cost_center_name}' (use --ignore-current-cost-center to override)")

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +28
def __init__(self, cache_file: str = ".cache/cost_centers.json", cache_ttl_hours: int = 24):
"""
Initialize the cost center cache.
Args:
cache_file: Path to the cache file
cache_ttl_hours: Time-to-live for cache entries in hours
"""
self.logger = logging.getLogger(__name__)
self.cache_file = Path(cache_file)
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

The default cache file path '.cache/cost_centers.json' is hardcoded and may not align with the export directory configuration. Consider making the cache path configurable through the config manager or at least use a path relative to the export directory.

Suggested change
def __init__(self, cache_file: str = ".cache/cost_centers.json", cache_ttl_hours: int = 24):
"""
Initialize the cost center cache.
Args:
cache_file: Path to the cache file
cache_ttl_hours: Time-to-live for cache entries in hours
"""
self.logger = logging.getLogger(__name__)
self.cache_file = Path(cache_file)
def __init__(self, cache_file: str = ".cache/cost_centers.json", cache_ttl_hours: int = 24, export_dir: Optional[str] = None):
"""
Initialize the cost center cache.
Args:
cache_file: Path to the cache file (relative to export_dir if not absolute)
cache_ttl_hours: Time-to-live for cache entries in hours
export_dir: Base directory for cache file (default: current working directory)
"""
self.logger = logging.getLogger(__name__)
# If cache_file is not absolute, resolve it relative to export_dir (if provided)
cache_path = Path(cache_file)
if not cache_path.is_absolute():
base_dir = Path(export_dir) if export_dir is not None else Path.cwd()
cache_path = base_dir / cache_path
self.cache_file = cache_path

Copilot uses AI. Check for mistakes.
logger.warning("No users to sync")
else:
results = github_manager.bulk_update_cost_center_assignments(cost_center_groups)
results = github_manager.bulk_update_cost_center_assignments(cost_center_groups, not args.check_current_cost_center)
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

The parameter logic is inverted. The method expects 'ignore_current_cost_center' parameter, but passing 'not args.check_current_cost_center' means when --check-current-cost-center is True, ignore_current_cost_center becomes False, which is the opposite of the intended behavior.

Suggested change
results = github_manager.bulk_update_cost_center_assignments(cost_center_groups, not args.check_current_cost_center)
results = github_manager.bulk_update_cost_center_assignments(cost_center_groups, ignore_current_cost_center=not args.check_current_cost_center)

Copilot uses AI. Check for mistakes.
@gmondello gmondello merged commit 8840e81 into main Oct 15, 2025
3 checks passed
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.

2 participants