From de52ff46827f30f86c1db8aa8f16a17adae33b0f Mon Sep 17 00:00:00 2001 From: Yohei Kamitsukasa <35333687+paper2@users.noreply.github.com> Date: Sun, 29 Jun 2025 03:49:09 +0000 Subject: [PATCH 1/2] Add validate-action-output.yml reusable workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This workflow provides a reusable template for testing the GitHub Action with OpenTelemetry collector validation. It includes: - OpenTelemetry collector setup with health checks - Telemetry data collection and validation - Structure comparison with expected test data - Artifact upload for debugging 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .github/workflows/validate-action-output.yml | 214 +++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 .github/workflows/validate-action-output.yml diff --git a/.github/workflows/validate-action-output.yml b/.github/workflows/validate-action-output.yml new file mode 100644 index 00000000..8e677e7c --- /dev/null +++ b/.github/workflows/validate-action-output.yml @@ -0,0 +1,214 @@ +name: Validate Action Output +# Reusable workflow for testing the GitHub Action with OpenTelemetry collector validation +# This workflow can be used to test different trigger events (push, workflow_run, etc.) +# +# To update test data for any workflow using this reusable workflow: +# 1. Run the calling workflow (by pushing changes, creating PR, or triggering workflow_run) +# 2. After workflow completion, download the artifact from the GitHub Actions run page +# 3. Extract the artifact and copy traces.json and metrics.json to the appropriate .github/test-data/ directory +# 4. Commit and push the updated test data files + +on: + workflow_call: + inputs: + test-data-directory: + description: + 'Directory name under .github/test-data/ for expected test data' + required: true + type: string + artifact-name: + description: 'Name for the collector logs artifact' + required: false + type: string + default: 'collector-logs' + retention-days: + description: 'Number of days to retain the artifact' + required: false + type: number + default: 5 + +jobs: + test-action: + name: GitHub Actions Test + runs-on: ubuntu-latest + + steps: + - name: Checkout + id: checkout + uses: actions/checkout@v4 + + # Create directory with proper permissions + - name: Create log directory with proper permissions + run: | + mkdir -p collector-logs + chmod 777 collector-logs + + # Start OpenTelemetry Collector manually after checkout + - name: Start OpenTelemetry Collector + # port 13133 is used for health checks, 4318 for OTLP HTTP endpoint + run: | + echo "Starting OpenTelemetry Collector with custom configuration..." + docker run -d \ + --name otel-collector \ + -p 13133:13133 \ + -p 4318:4318 \ + -v ${{ github.workspace }}/.github/configs/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml:ro \ + -v ${{ github.workspace }}/collector-logs:/collector-logs \ + --health-cmd "wget --no-verbose --tries=1 --spider http://localhost:13133/ || exit 1" \ + --health-interval 10s \ + --health-timeout 5s \ + --health-retries 3 \ + otel/opentelemetry-collector-contrib:0.115.1 + + # Wait for collector to be healthy and ready + - name: Wait for collector to be ready + run: | + echo "Waiting for OpenTelemetry Collector to be ready..." + timeout 60s bash -c 'until docker ps | grep -q "healthy.*otel-collector"; do sleep 2; echo "Waiting for collector health check..."; done' + echo "Collector is ready" + + # Define JQ filters for normalization (setting dynamic fields to fixed values) + - name: Set JQ filters for normalization + id: set-jq-filters + run: | + # JQ filter for trace normalization + cat > /tmp/normalize-traces.jq << 'EOF' + .resourceSpans[]?.scopeSpans[]?.spans[]? |= ( + .traceId = "00000000000000000000000000000000" | + .spanId = "0000000000000000" | + .parentSpanId = "0000000000000000" | + .startTimeUnixNano = "0000000000000000000" | + .endTimeUnixNano = "0000000000000000000" + ) | + .resourceSpans[]?.scopeSpans[]?.spans[]?.attributes[]? |= ( + if .key == "run_id" then .value = {"intValue": "0"} + elif .key == "job.id" then .value = {"intValue": "0"} + elif .key == "url" then .value = {"stringValue": "https://example.com/actions/runs/0"} + elif .key == "runner.name" then .value = {"stringValue": "GitHub Actions 0"} + else . + end + ) + EOF + + # JQ filter for metrics normalization + cat > /tmp/normalize-metrics.jq << 'EOF' + .resourceMetrics[]?.scopeMetrics[]?.metrics[]?.gauge?.dataPoints[]? |= ( + .startTimeUnixNano = "0000000000000000000" | + .timeUnixNano = "0000000000000000000" | + .asDouble = 0 + ) + EOF + + # Execute the GitHub Action which sends telemetry data to the collector + - name: Test Local Action + id: test-action + uses: ./ + env: + OTEL_SERVICE_NAME: github-actions-opentelemetry + # Point to local collector service (not external endpoint) + OTEL_EXPORTER_OTLP_ENDPOINT: http://localhost:4318 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Upload collector logs as GitHub artifact for debugging + - name: Upload collector logs as artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.artifact-name }} + path: collector-logs/ + retention-days: ${{ inputs.retention-days }} + + # Validate that collector generated the expected JSON log files with correct content + - name: Validate collector logs (Traces) + run: | + echo "=== Validating traces data ===" + if [ -f "collector-logs/traces.json" ]; then + echo "✓ Traces file found" + echo "Traces content preview (first 5 lines):" + head -5 collector-logs/traces.json + + # Check if traces file has content + if [ -s "collector-logs/traces.json" ]; then + echo "✓ Traces data received" + + # Validate traces structure matches expected format by comparing with test data + echo "=== Validating trace structure ===" + if [ -f ".github/test-data/${{ inputs.test-data-directory }}/traces.json" ]; then + # Apply jq transformation to remove dynamic fields from collected traces + jq -f /tmp/normalize-traces.jq collector-logs/traces.json > collector-logs/traces-normalized.json + + # Apply same transformation to expected test data + jq -f /tmp/normalize-traces.jq .github/test-data/${{ inputs.test-data-directory }}/traces.json > collector-logs/traces-expected.json + + # Compare normalized traces + if diff collector-logs/traces-normalized.json collector-logs/traces-expected.json > /dev/null; then + echo "✓ Trace structure matches expected format" + else + echo "✗ Trace structure differs from expected format" + echo "=== Differences ===" + diff collector-logs/traces-normalized.json collector-logs/traces-expected.json || true + exit 1 + fi + else + echo "⚠ Test data file not found, skipping structure validation" + fi + else + echo "⚠ Traces file is empty" + fi + else + echo "⚠ Traces file not found (no trace data received)" + fi + + - name: Validate collector logs (Metrics) + run: | + echo "=== Validating metrics data ===" + if [ -f "collector-logs/metrics.json" ]; then + echo "✓ Metrics file found" + echo "Metrics content preview (first 5 lines):" + head -5 collector-logs/metrics.json + + # Check if metrics file has content + if [ -s "collector-logs/metrics.json" ]; then + echo "✓ Metrics data received" + + # Validate metrics structure matches expected format by comparing with test data + echo "=== Validating metrics structure ===" + if [ -f ".github/test-data/${{ inputs.test-data-directory }}/metrics.json" ]; then + # Apply jq transformation to remove dynamic fields from collected metrics + jq -f /tmp/normalize-metrics.jq collector-logs/metrics.json > collector-logs/metrics-normalized.json + + # Apply same transformation to expected test data + jq -f /tmp/normalize-metrics.jq .github/test-data/${{ inputs.test-data-directory }}/metrics.json > collector-logs/metrics-expected.json + + # Compare normalized metrics + if diff collector-logs/metrics-normalized.json collector-logs/metrics-expected.json > /dev/null; then + echo "✓ Metrics structure matches expected format" + else + echo "✗ Metrics structure differs from expected format" + echo "=== Differences ===" + diff collector-logs/metrics-normalized.json collector-logs/metrics-expected.json || true + exit 1 + fi + else + echo "⚠ Test data file not found, skipping structure validation" + fi + else + echo "⚠ Metrics file is empty" + fi + else + echo "⚠ Metrics file not found (no metrics data received)" + fi + + # Show final collector status and logs for debugging + - name: Show collector debug info + if: always() + run: | + echo "=== Final collector status ===" + docker ps -a | grep otel-collector || echo "Collector container not found" + + echo "=== Complete collector logs ===" + docker logs otel-collector || echo "Could not retrieve collector logs" + + echo "=== All generated files ===" + find collector-logs -type f -exec echo "File: {}" \; -exec head -10 {} \; -exec echo "---" \; 2>/dev/null || echo "No files found" From 3c1fb8325e23ce540fdc3fcc5ca72ac8c6b6e39b Mon Sep 17 00:00:00 2001 From: Yohei Kamitsukasa <35333687+paper2@users.noreply.github.com> Date: Sun, 29 Jun 2025 03:55:44 +0000 Subject: [PATCH 2/2] Remove branch restrictions from example workflows and update workflow_run tests to include all example workflows --- .github/workflows/example-workflow-01.yml | 3 --- .github/workflows/example-workflow-02.yml | 3 --- .github/workflows/example-workflow-03.yml | 3 --- .github/workflows/workflow-run-tests.yml | 5 ++++- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/example-workflow-01.yml b/.github/workflows/example-workflow-01.yml index 17b2f1b0..73ca2059 100644 --- a/.github/workflows/example-workflow-01.yml +++ b/.github/workflows/example-workflow-01.yml @@ -2,9 +2,6 @@ name: Example Workflow 01 on: push: - branches: - - main - - getting-started jobs: example-app: diff --git a/.github/workflows/example-workflow-02.yml b/.github/workflows/example-workflow-02.yml index 9f886540..b739760f 100644 --- a/.github/workflows/example-workflow-02.yml +++ b/.github/workflows/example-workflow-02.yml @@ -2,9 +2,6 @@ name: Example Workflow 02 on: push: - branches: - - main - - getting-started jobs: example-app: diff --git a/.github/workflows/example-workflow-03.yml b/.github/workflows/example-workflow-03.yml index 6c207477..65c64f21 100644 --- a/.github/workflows/example-workflow-03.yml +++ b/.github/workflows/example-workflow-03.yml @@ -2,9 +2,6 @@ name: Example Workflow 03 on: push: - branches: - - main - - getting-started jobs: example-app: diff --git a/.github/workflows/workflow-run-tests.yml b/.github/workflows/workflow-run-tests.yml index 3680de9b..d0b3f3d3 100644 --- a/.github/workflows/workflow-run-tests.yml +++ b/.github/workflows/workflow-run-tests.yml @@ -4,7 +4,10 @@ name: Workflow Run Tests on: workflow_run: - workflows: ['Push Tests'] + workflows: + - Example Workflow 01 + - Example Workflow 02 + - Example Workflow 03 types: - completed