Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
266 changes: 266 additions & 0 deletions .github/workflows/test-github-oidc-integration.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
# ------------------------------------------------------------
# Copyright 2023 The Radius Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ------------------------------------------------------------

# This workflow tests the GitHub OIDC credential backend (pkg/github/).
# It runs unit tests, builds the server binary, and executes an integration
# test against the real GitHub API using the workflow's GITHUB_TOKEN.
#
# The integration test creates a temporary GitHub Environment, sets variables,
# reads them back, and cleans up — verifying the full credential setup flow.

name: "Test: GitHub OIDC Integration"

on:
workflow_dispatch:
pull_request:
paths:
- "pkg/github/**"
- "cmd/github-app/**"
- ".github/workflows/test-github-oidc-integration.yaml"

permissions:
contents: read

jobs:
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Run unit tests
run: |
echo "=== Running pkg/github unit tests ==="
go test -v -race -count=1 ./pkg/github/... 2>&1 | tee test-output.txt
echo ""
echo "=== Test Summary ==="
grep -E "^(ok|FAIL|---)" test-output.txt || true

- name: Build server binary
run: |
echo "=== Building cmd/github-app ==="
go build -o /dev/null ./cmd/github-app/...
echo "✅ Server binary builds successfully"

integration-test:
name: Integration Test (GitHub API)
runs-on: ubuntu-latest
# This job needs write access to create/delete environments and variables.
# id-token: write is needed for Azure OIDC federated login verification.
permissions:
contents: read
id-token: write
# Note: The GITHUB_TOKEN in pull_request events from forks does NOT have
# environment write access. This job will only fully pass on:
# - workflow_dispatch (manual trigger)
# - pull_request from the same repo (not forks)
outputs:
azure_env_name: ${{ steps.set-env-name.outputs.azure_env_name }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Generate Azure environment name
id: set-env-name
run: |
# Use a fixed name so the verify workflow can reference it statically.
AZURE_ENV_NAME="radius-oidc-verify"
echo "azure_env_name=${AZURE_ENV_NAME}" >> "$GITHUB_OUTPUT"

- name: Run integration test
env:
GITHUB_TOKEN: ${{ secrets.INTEGRATION_TEST_TOKEN }}
TEST_GITHUB_OWNER: ${{ github.repository_owner }}
TEST_GITHUB_REPO: ${{ github.event.repository.name }}
TEST_AZURE_APP_ID: ${{ secrets.AZURE_APP_ID }}
TEST_AZURE_SUB_ID: ${{ secrets.AZURE_SUB_ID }}
TEST_AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
TEST_AZURE_ENV_NAME: ${{ steps.set-env-name.outputs.azure_env_name }}
run: |
echo "=== Running GitHub API integration test ==="
echo "Repository: ${TEST_GITHUB_OWNER}/${TEST_GITHUB_REPO}"
echo ""
go test -v -race -count=1 -tags=integration \
-run TestIntegration \
./pkg/github/environment/... ./pkg/github/credential/... 2>&1 | tee integration-output.txt

echo ""
echo "=== Integration Test Summary ==="
grep -E "^(ok|FAIL|---)" integration-output.txt || true

credential-flow-test:
name: Credential Flow Test (End-to-End)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Build and start server
env:
GITHUB_TOKEN: ${{ secrets.INTEGRATION_TEST_TOKEN }}
run: |
echo "=== Building server ==="
go build -o github-app ./cmd/github-app/

echo "=== Starting server in background ==="
GITHUB_TOKEN="${GITHUB_TOKEN}" \
GITHUB_CLIENT_ID="test" \
GITHUB_CLIENT_SECRET="test" \
PORT=8080 \
./github-app &
SERVER_PID=$!
echo "Server PID: ${SERVER_PID}"

# Wait for server to be ready
for i in $(seq 1 10); do
if curl -sf http://localhost:8080/healthz > /dev/null 2>&1; then
echo "✅ Server is ready"
break
fi
echo "Waiting for server... (attempt $i)"
sleep 1
done

echo ""
echo "=== Testing health endpoint ==="
curl -sf http://localhost:8080/healthz
echo ""
echo "✅ Health check passed"

echo ""
echo "=== Testing unauthenticated request returns 401 ==="
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/api/repos/test/repo/environments)
if [ "$HTTP_CODE" = "401" ]; then
echo "✅ Unauthenticated request correctly returned 401"
else
echo "❌ Expected 401, got ${HTTP_CODE}"
kill $SERVER_PID 2>/dev/null || true
exit 1
fi

echo ""
echo "=== Testing API with mock session ==="
# The server validates sessions via the session store which is in-memory.
# We can't easily inject a session from curl, so we verify the 401 behavior
# which proves the auth middleware is working.

echo ""
echo "=== All server tests passed ==="
kill $SERVER_PID 2>/dev/null || true

- name: Verify verification workflow generation
run: |
echo "=== Testing verification workflow generation ==="
go test -v -race -count=1 \
-run "TestGenerateVerificationWorkflow" \
./pkg/github/credential/... 2>&1

echo ""
echo "=== Verification workflow generation tests passed ==="

# This job verifies that the Azure credentials stored on the GitHub Environment
# by the integration test can actually be used by a runner to authenticate.
verify-azure-access:
name: Verify Azure Access (Runner)
needs: [integration-test]
if: needs.integration-test.outputs.azure_env_name != ''
runs-on: ubuntu-latest
environment: ${{ needs.integration-test.outputs.azure_env_name }}
permissions:
contents: read
id-token: write
steps:
- name: Verify environment variables are set
run: |
echo "AZURE_CLIENT_ID is set: ${{ vars.AZURE_CLIENT_ID != '' }}"
echo "AZURE_TENANT_ID is set: ${{ vars.AZURE_TENANT_ID != '' }}"
echo "AZURE_SUBSCRIPTION_ID is set: ${{ vars.AZURE_SUBSCRIPTION_ID != '' }}"

- name: Azure Login via OIDC
uses: azure/login@v2

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium test

Unpinned 3rd party Action 'Test: GitHub OIDC Integration' step
Uses Step
uses 'azure/login' with ref 'v2', not a pinned commit hash
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

- name: Confirm authentication
run: |
echo "Successfully authenticated to Azure using environment credentials"

# Clean up the Azure test environment after verification.
cleanup-azure-env:
name: Cleanup Azure Environment
needs: [integration-test, verify-azure-access]
if: always() && needs.integration-test.outputs.azure_env_name != ''
runs-on: ubuntu-latest
steps:
- name: Delete test environment
env:
GITHUB_TOKEN: ${{ secrets.INTEGRATION_TEST_TOKEN }}
REPO: ${{ github.repository }}
ENV_NAME: ${{ needs.integration-test.outputs.azure_env_name }}
run: |
curl -sf -X DELETE \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${REPO}/environments/${ENV_NAME}" \
&& echo "Environment deleted" \
|| echo "Environment may already be deleted"

summary:
name: Test Summary
runs-on: ubuntu-latest
needs: [unit-tests, integration-test, credential-flow-test, verify-azure-access, cleanup-azure-env]
if: always()
steps:
- name: Report results
run: |
echo "## GitHub OIDC Integration Test Results"
echo ""
echo "| Job | Status |"
echo "|-----|--------|"
echo "| Unit Tests | ${{ needs.unit-tests.result }} |"
echo "| Integration Test | ${{ needs.integration-test.result }} |"
echo "| Verify Azure Access | ${{ needs.verify-azure-access.result }} |"
echo "| Credential Flow Test | ${{ needs.credential-flow-test.result }} |"
echo "| Cleanup Azure Env | ${{ needs.cleanup-azure-env.result }} |"
echo ""

if [ "${{ needs.unit-tests.result }}" != "success" ] || \
[ "${{ needs.credential-flow-test.result }}" != "success" ]; then
echo "❌ Some tests failed"
exit 1
fi

echo "✅ All required tests passed"
Loading
Loading