Skip to content
Merged
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
155 changes: 127 additions & 28 deletions .github/workflows/cerberus-integration.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,11 @@
name: Cerberus Integration Tests

on:
push:
branches: [ main, development, 'feature/**' ]
paths:
- 'backend/internal/caddy/**'
- 'backend/internal/security/**'
- 'backend/internal/handlers/security*.go'
- 'backend/internal/models/security*.go'
- 'scripts/cerberus_integration.sh'
- 'Dockerfile'
- '.github/workflows/cerberus-integration.yml'
pull_request:
branches: [ main, development ]
paths:
- 'backend/internal/caddy/**'
- 'backend/internal/security/**'
- 'backend/internal/handlers/security*.go'
- 'backend/internal/models/security*.go'
- 'scripts/cerberus_integration.sh'
- 'Dockerfile'
- '.github/workflows/cerberus-integration.yml'
# Allow manual trigger
workflow_run:
workflows: ["Docker Build, Publish & Test"]
types: [completed]
branches: [main, development, 'feature/**'] # Explicit branch filter prevents unexpected triggers
# Allow manual trigger for debugging
workflow_dispatch:

concurrency:
Expand All @@ -33,19 +17,134 @@ jobs:
name: Cerberus Security Stack Integration
runs-on: ubuntu-latest
timeout-minutes: 20
# Only run if docker-build.yml succeeded, or if manually triggered
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}

steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
# Determine the correct image tag based on trigger context
# For PRs: pr-{number}-{sha}, For branches: {sanitized-branch}-{sha}
- name: Determine image tag
id: image
env:
EVENT: ${{ github.event.workflow_run.event }}
REF: ${{ github.event.workflow_run.head_branch }}
SHA: ${{ github.event.workflow_run.head_sha }}
MANUAL_TAG: ${{ inputs.image_tag }}
run: |
# Manual trigger uses provided tag
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
if [[ -n "$MANUAL_TAG" ]]; then
echo "tag=${MANUAL_TAG}" >> $GITHUB_OUTPUT
else
# Default to latest if no tag provided
echo "tag=latest" >> $GITHUB_OUTPUT
fi
echo "source_type=manual" >> $GITHUB_OUTPUT
exit 0
fi

# Extract 7-character short SHA
SHORT_SHA=$(echo "$SHA" | cut -c1-7)

if [[ "$EVENT" == "pull_request" ]]; then
# Use native pull_requests array (no API calls needed)
PR_NUM=$(echo '${{ toJson(github.event.workflow_run.pull_requests) }}' | jq -r '.[0].number')

if [[ -z "$PR_NUM" || "$PR_NUM" == "null" ]]; then
echo "❌ ERROR: Could not determine PR number"
echo "Event: $EVENT"
echo "Ref: $REF"
echo "SHA: $SHA"
echo "Pull Requests JSON: ${{ toJson(github.event.workflow_run.pull_requests) }}"
exit 1
fi

# Immutable tag with SHA suffix prevents race conditions
echo "tag=pr-${PR_NUM}-${SHORT_SHA}" >> $GITHUB_OUTPUT
echo "source_type=pr" >> $GITHUB_OUTPUT
else
# Branch push: sanitize branch name and append SHA
# Sanitization: lowercase, replace / with -, remove special chars
SANITIZED=$(echo "$REF" | \
tr '[:upper:]' '[:lower:]' | \
tr '/' '-' | \
sed 's/[^a-z0-9-._]/-/g' | \
sed 's/^-//; s/-$//' | \
sed 's/--*/-/g' | \
cut -c1-121) # Leave room for -SHORT_SHA (7 chars)

echo "tag=${SANITIZED}-${SHORT_SHA}" >> $GITHUB_OUTPUT
echo "source_type=branch" >> $GITHUB_OUTPUT
fi

echo "sha=${SHORT_SHA}" >> $GITHUB_OUTPUT
echo "Determined image tag: $(cat $GITHUB_OUTPUT | grep tag=)"

# Pull image from registry with retry logic (dual-source strategy)
# Try registry first (fast), fallback to artifact if registry fails
- name: Pull Docker image from registry
id: pull_image
uses: nick-fields/retry@v3
with:
timeout_minutes: 5
max_attempts: 3
retry_wait_seconds: 10
command: |
IMAGE_NAME="ghcr.io/${{ github.repository_owner }}/charon:${{ steps.image.outputs.tag }}"
echo "Pulling image: $IMAGE_NAME"
docker pull "$IMAGE_NAME"
docker tag "$IMAGE_NAME" charon:local
echo "✅ Successfully pulled from registry"
continue-on-error: true

- name: Build Docker image
# Fallback: Download artifact if registry pull failed
- name: Fallback to artifact download
if: steps.pull_image.outcome == 'failure'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SHA: ${{ steps.image.outputs.sha }}
run: |
docker build \
--no-cache \
--build-arg VCS_REF=${{ github.sha }} \
-t charon:local .
echo "⚠️ Registry pull failed, falling back to artifact..."

# Determine artifact name based on source type
if [[ "${{ steps.image.outputs.source_type }}" == "pr" ]]; then
PR_NUM=$(echo '${{ toJson(github.event.workflow_run.pull_requests) }}' | jq -r '.[0].number')
ARTIFACT_NAME="pr-image-${PR_NUM}"
else
ARTIFACT_NAME="push-image"
fi

echo "Downloading artifact: $ARTIFACT_NAME"
gh run download ${{ github.event.workflow_run.id }} \
--name "$ARTIFACT_NAME" \
--dir /tmp/docker-image || {
echo "❌ ERROR: Artifact download failed!"
echo "Available artifacts:"
gh run view ${{ github.event.workflow_run.id }} --json artifacts --jq '.artifacts[].name'
exit 1
}

docker load < /tmp/docker-image/charon-image.tar
docker tag $(docker images --format "{{.Repository}}:{{.Tag}}" | head -1) charon:local
echo "✅ Successfully loaded from artifact"

# Validate image freshness by checking SHA label
- name: Validate image SHA
env:
SHA: ${{ steps.image.outputs.sha }}
run: |
LABEL_SHA=$(docker inspect charon:local --format '{{index .Config.Labels "org.opencontainers.image.revision"}}' | cut -c1-7)
echo "Expected SHA: $SHA"
echo "Image SHA: $LABEL_SHA"

if [[ "$LABEL_SHA" != "$SHA" ]]; then
echo "⚠️ WARNING: Image SHA mismatch!"
echo "Image may be stale. Proceeding with caution..."
else
echo "✅ Image SHA matches expected commit"
fi

- name: Run Cerberus integration tests
id: cerberus-test
Expand Down
159 changes: 127 additions & 32 deletions .github/workflows/crowdsec-integration.yml
Original file line number Diff line number Diff line change
@@ -1,31 +1,11 @@
name: CrowdSec Integration Tests

on:
push:
branches: [ main, development, 'feature/**' ]
paths:
- 'backend/internal/crowdsec/**'
- 'backend/internal/models/crowdsec*.go'
- 'configs/crowdsec/**'
- 'scripts/crowdsec_integration.sh'
- 'scripts/crowdsec_decision_integration.sh'
- 'scripts/crowdsec_startup_test.sh'
- '.github/skills/integration-test-crowdsec*/**'
- 'Dockerfile'
- '.github/workflows/crowdsec-integration.yml'
pull_request:
branches: [ main, development ]
paths:
- 'backend/internal/crowdsec/**'
- 'backend/internal/models/crowdsec*.go'
- 'configs/crowdsec/**'
- 'scripts/crowdsec_integration.sh'
- 'scripts/crowdsec_decision_integration.sh'
- 'scripts/crowdsec_startup_test.sh'
- '.github/skills/integration-test-crowdsec*/**'
- 'Dockerfile'
- '.github/workflows/crowdsec-integration.yml'
# Allow manual trigger
workflow_run:
workflows: ["Docker Build, Publish & Test"]
types: [completed]
branches: [main, development, 'feature/**'] # Explicit branch filter prevents unexpected triggers
# Allow manual trigger for debugging
workflow_dispatch:

concurrency:
Expand All @@ -37,19 +17,134 @@ jobs:
name: CrowdSec Bouncer Integration
runs-on: ubuntu-latest
timeout-minutes: 15
# Only run if docker-build.yml succeeded, or if manually triggered
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}

steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
# Determine the correct image tag based on trigger context
# For PRs: pr-{number}-{sha}, For branches: {sanitized-branch}-{sha}
- name: Determine image tag
id: image
env:
EVENT: ${{ github.event.workflow_run.event }}
REF: ${{ github.event.workflow_run.head_branch }}
SHA: ${{ github.event.workflow_run.head_sha }}
MANUAL_TAG: ${{ inputs.image_tag }}
run: |
# Manual trigger uses provided tag
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
if [[ -n "$MANUAL_TAG" ]]; then
echo "tag=${MANUAL_TAG}" >> $GITHUB_OUTPUT
else
# Default to latest if no tag provided
echo "tag=latest" >> $GITHUB_OUTPUT
fi
echo "source_type=manual" >> $GITHUB_OUTPUT
exit 0
fi

# Extract 7-character short SHA
SHORT_SHA=$(echo "$SHA" | cut -c1-7)

if [[ "$EVENT" == "pull_request" ]]; then
# Use native pull_requests array (no API calls needed)
PR_NUM=$(echo '${{ toJson(github.event.workflow_run.pull_requests) }}' | jq -r '.[0].number')

if [[ -z "$PR_NUM" || "$PR_NUM" == "null" ]]; then
echo "❌ ERROR: Could not determine PR number"
echo "Event: $EVENT"
echo "Ref: $REF"
echo "SHA: $SHA"
echo "Pull Requests JSON: ${{ toJson(github.event.workflow_run.pull_requests) }}"
exit 1
fi

# Immutable tag with SHA suffix prevents race conditions
echo "tag=pr-${PR_NUM}-${SHORT_SHA}" >> $GITHUB_OUTPUT
echo "source_type=pr" >> $GITHUB_OUTPUT
else
# Branch push: sanitize branch name and append SHA
# Sanitization: lowercase, replace / with -, remove special chars
SANITIZED=$(echo "$REF" | \
tr '[:upper:]' '[:lower:]' | \
tr '/' '-' | \
sed 's/[^a-z0-9-._]/-/g' | \
sed 's/^-//; s/-$//' | \
sed 's/--*/-/g' | \
cut -c1-121) # Leave room for -SHORT_SHA (7 chars)

echo "tag=${SANITIZED}-${SHORT_SHA}" >> $GITHUB_OUTPUT
echo "source_type=branch" >> $GITHUB_OUTPUT
fi

echo "sha=${SHORT_SHA}" >> $GITHUB_OUTPUT
echo "Determined image tag: $(cat $GITHUB_OUTPUT | grep tag=)"

# Pull image from registry with retry logic (dual-source strategy)
# Try registry first (fast), fallback to artifact if registry fails
- name: Pull Docker image from registry
id: pull_image
uses: nick-fields/retry@v3
with:
timeout_minutes: 5
max_attempts: 3
retry_wait_seconds: 10
command: |
IMAGE_NAME="ghcr.io/${{ github.repository_owner }}/charon:${{ steps.image.outputs.tag }}"
echo "Pulling image: $IMAGE_NAME"
docker pull "$IMAGE_NAME"
docker tag "$IMAGE_NAME" charon:local
echo "✅ Successfully pulled from registry"
continue-on-error: true

- name: Build Docker image
# Fallback: Download artifact if registry pull failed
- name: Fallback to artifact download
if: steps.pull_image.outcome == 'failure'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SHA: ${{ steps.image.outputs.sha }}
run: |
docker build \
--no-cache \
--build-arg VCS_REF=${{ github.sha }} \
-t charon:local .
echo "⚠️ Registry pull failed, falling back to artifact..."

# Determine artifact name based on source type
if [[ "${{ steps.image.outputs.source_type }}" == "pr" ]]; then
PR_NUM=$(echo '${{ toJson(github.event.workflow_run.pull_requests) }}' | jq -r '.[0].number')
ARTIFACT_NAME="pr-image-${PR_NUM}"
else
ARTIFACT_NAME="push-image"
fi

echo "Downloading artifact: $ARTIFACT_NAME"
gh run download ${{ github.event.workflow_run.id }} \
--name "$ARTIFACT_NAME" \
--dir /tmp/docker-image || {
echo "❌ ERROR: Artifact download failed!"
echo "Available artifacts:"
gh run view ${{ github.event.workflow_run.id }} --json artifacts --jq '.artifacts[].name'
exit 1
}

docker load < /tmp/docker-image/charon-image.tar
docker tag $(docker images --format "{{.Repository}}:{{.Tag}}" | head -1) charon:local
echo "✅ Successfully loaded from artifact"

# Validate image freshness by checking SHA label
- name: Validate image SHA
env:
SHA: ${{ steps.image.outputs.sha }}
run: |
LABEL_SHA=$(docker inspect charon:local --format '{{index .Config.Labels "org.opencontainers.image.revision"}}' | cut -c1-7)
echo "Expected SHA: $SHA"
echo "Image SHA: $LABEL_SHA"

if [[ "$LABEL_SHA" != "$SHA" ]]; then
echo "⚠️ WARNING: Image SHA mismatch!"
echo "Image may be stale. Proceeding with caution..."
else
echo "✅ Image SHA matches expected commit"
fi

- name: Run CrowdSec integration tests
id: crowdsec-test
Expand Down
Loading
Loading