From e64d7e6e6d0e6ae4832061e968d8bf8d4f68c922 Mon Sep 17 00:00:00 2001 From: Dave Arnold Date: Thu, 1 May 2025 09:57:49 -0700 Subject: [PATCH] Refactor GitHub Actions workflows: consolidate token refresh job and remove init-cluster PR workflow; add integration tests for GitHub client functionality --- .github/workflows/gh-token.yml | 24 ++- .github/workflows/init-cluster-pr.yml | 78 ---------- .../test_github_client_integration.py | 143 ++++++++++++++++++ 3 files changed, 154 insertions(+), 91 deletions(-) delete mode 100644 .github/workflows/init-cluster-pr.yml create mode 100644 tests/integration/test_github_client_integration.py diff --git a/.github/workflows/gh-token.yml b/.github/workflows/gh-token.yml index 04aaa9e..cd5e7f9 100644 --- a/.github/workflows/gh-token.yml +++ b/.github/workflows/gh-token.yml @@ -10,17 +10,15 @@ permissions: id-token: write jobs: - build: - runs-on: ubuntu-latest + refresh-token: + name: Refresh GitHub Token if: ${{ github.server_url != 'https://github.com' }} - steps: - - name: Refresh GitHub Token - uses: CSVD/centralized-actions/.github/workflows/upload-github-token.yml@main - with: - aws_region: 'us-gov-west-1' - secret_name: '/eks-cluster-deployment/github_token' # This matches the SECRET_NAME in app.py - github_app_id: ${{ vars.GH_APP_ID }} - github_app_installation_id: ${{ vars.GH_APP_INSTALLATION_ID }} - use_ecs_credentials: true - secrets: - github_app_pem_file: ${{ secrets.GH_APP_PEM_FILE }} + uses: CSVD/centralized-actions/.github/workflows/upload-github-token.yml@main + with: + aws_region: 'us-gov-west-1' + secret_name: '/eks-cluster-deployment/github_token' # This matches the SECRET_NAME in app.py + github_app_id: ${{ vars.GH_APP_ID }} + github_app_installation_id: ${{ vars.GH_APP_INSTALLATION_ID }} + use_ecs_credentials: true + secrets: + github_app_pem_file: ${{ secrets.GH_APP_PEM_FILE }} diff --git a/.github/workflows/init-cluster-pr.yml b/.github/workflows/init-cluster-pr.yml deleted file mode 100644 index df42149..0000000 --- a/.github/workflows/init-cluster-pr.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Initialize Cluster Configuration - -on: - pull_request: - branches: [ main ] - types: [ opened, synchronize, reopened ] - -jobs: - determine-environment: - runs-on: ubuntu-latest - if: github.head_ref == 'init-cluster' - outputs: - aws_account: ${{ steps.get-account.outputs.aws_account }} - environment: ${{ steps.get-account.outputs.environment }} - - steps: - - uses: actions/checkout@v4 - - - name: Get AWS account from config - id: get-account - run: | - AWS_ACCOUNT=$(jq -r '.aws_account' config.json) - ENVIRONMENT=$(jq -r '.environment' config.json) - echo "aws_account=${AWS_ACCOUNT}" >> $GITHUB_OUTPUT - echo "environment=${ENVIRONMENT}" >> $GITHUB_OUTPUT - - terraform-plan: - needs: [ determine-environment ] - runs-on: [ "${{ needs.determine-environment.outputs.aws_account }}" ] - - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - - - name: Setup Terraform - uses: hashicorp/setup-terraform@v3.1.2 - with: - terraform_version: 1.9.1 - terraform_wrapper: false - - - name: Setup Terragrunt - run: | - wget -O terragrunt https://github.com/gruntwork-io/terragrunt/releases/download/v0.45.0/terragrunt_linux_amd64 - chmod +x terragrunt - sudo mv terragrunt /usr/local/bin/ - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: arn:aws:iam::${{ needs.determine-environment.outputs.aws_account }}:role/GitHubActionsRole - aws-region: us-east-1 - - - name: Terragrunt Plan - working-directory: environment/region/vpc/cluster - run: | - terragrunt init - terragrunt plan -no-color -out=tfplan 2>&1 | tee plan.txt - - - name: Comment Plan on PR - uses: actions/github-script@v7 - if: github.event_name == 'pull_request' - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const fs = require('fs'); - const plan = fs.readFileSync('environment/region/vpc/cluster/plan.txt', 'utf8'); - const comment = `### Terraform Plan Results - \`\`\` - ${plan} - \`\`\` - `; - github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: comment - }); diff --git a/tests/integration/test_github_client_integration.py b/tests/integration/test_github_client_integration.py new file mode 100644 index 0000000..b3744d9 --- /dev/null +++ b/tests/integration/test_github_client_integration.py @@ -0,0 +1,143 @@ +import os +import pytest +from template_automation.github_client import GitHubClient +from github import GithubException + +# Configuration from environment variables +GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") +GITHUB_API_URL = os.getenv("GITHUB_API_URL", "https://api.github.com") +GITHUB_ORG = os.getenv("GITHUB_ORG", "test-organization") +TEST_REPO_PREFIX = "test-automation-" + +def is_integration_test_enabled(): + """Check if integration tests should run based on environment variables.""" + return bool(GITHUB_TOKEN and GITHUB_ORG) + +@pytest.fixture(scope="session") +def github_client(): + """Create a GitHub client for testing.""" + if not is_integration_test_enabled(): + pytest.skip("Integration tests disabled - missing required environment variables") + + return GitHubClient( + api_base_url=GITHUB_API_URL, + token=GITHUB_TOKEN, + org_name=GITHUB_ORG + ) + +@pytest.fixture(autouse=True) +def cleanup_test_repos(github_client): + """Cleanup test repositories before and after tests.""" + if not is_integration_test_enabled(): + return + + # Cleanup before test + try: + repos = github_client.org.get_repos() + for repo in repos: + if repo.name.startswith(TEST_REPO_PREFIX): + repo.delete() + except GithubException as e: + print(f"Cleanup warning: {e}") + + yield + + # Cleanup after test + try: + repos = github_client.org.get_repos() + for repo in repos: + if repo.name.startswith(TEST_REPO_PREFIX): + repo.delete() + except GithubException as e: + print(f"Cleanup warning: {e}") + +@pytest.mark.integration +def test_repository_creation(github_client): + """Test basic repository creation and deletion.""" + repo_name = f"{TEST_REPO_PREFIX}basic" + + # Test repository creation + repo = github_client.get_repository(repo_name, create=True) + assert repo.name == repo_name + assert repo.private is True + + # Verify repository exists + repo = github_client.get_repository(repo_name) + assert repo.name == repo_name + +@pytest.mark.integration +def test_branch_operations(github_client): + """Test branch creation and management.""" + repo_name = f"{TEST_REPO_PREFIX}branches" + branch_name = "test-branch" + + # Create repository and branch + repo = github_client.get_repository(repo_name, create=True) + github_client.create_branch(repo_name, branch_name) + + # Verify branch exists + repo = github_client.get_repository(repo_name) + branch = repo.get_branch(branch_name) + assert branch.name == branch_name + +@pytest.mark.integration +def test_file_operations(github_client): + """Test file creation, reading, and updating.""" + repo_name = f"{TEST_REPO_PREFIX}files" + test_file = "test.txt" + initial_content = "Hello, World!" + updated_content = "Updated content" + + # Create repository and file + repo = github_client.get_repository(repo_name, create=True) + github_client.write_file(repo, test_file, initial_content) + + # Read and verify content + content = github_client.read_file(repo, test_file) + assert content == initial_content + + # Update and verify + github_client.write_file(repo, test_file, updated_content) + content = github_client.read_file(repo, test_file) + assert content == updated_content + +@pytest.mark.integration +def test_pull_request_workflow(github_client): + """Test pull request creation workflow.""" + repo_name = f"{TEST_REPO_PREFIX}pr" + branch_name = "feature-branch" + + # Setup repository and branch + repo = github_client.get_repository(repo_name, create=True) + github_client.create_branch(repo_name, branch_name) + + # Create PR + pr = github_client.create_pull_request( + repo_name=repo_name, + title="Test PR", + body="Testing pull request creation", + head_branch=branch_name + ) + + assert pr.title == "Test PR" + assert pr.head.ref == branch_name + assert pr.base.ref == "main" + +@pytest.mark.integration +def test_team_permissions(github_client): + """Test team permission management.""" + repo_name = f"{TEST_REPO_PREFIX}team-perms" + team_name = os.getenv("GITHUB_TEST_TEAM") + + if not team_name: + pytest.skip("Skipping team permission test - GITHUB_TEST_TEAM not set") + + # Create repository + repo = github_client.get_repository(repo_name, create=True) + + # Set and verify team permissions + github_client.set_team_permission(repo_name, team_name, "admin") + + # Verify team has access (this will raise an exception if access is not granted) + team = github_client.org.get_team_by_slug(team_name) + assert team.has_in_repos(repo)