GitHub Actions CI/CD Architecture

1. Overview

This guide documents the CI/CD pipeline architecture used across Event and Membership Management projects. The system uses a combination of reusable workflows (defined centrally) and project-specific workflows that consume them.

1.1. Key Principles

  • DRY (Don’t Repeat Yourself) - Common build logic is centralized in reusable workflows

  • Consistency - All projects follow the same patterns and conventions

  • Conditional Execution - Only run tests for changed components (frontend/backend)

  • GitOps - Deployments are managed through ArgoCD and Git-based configuration

2. Workflow Architecture

The CI/CD system consists of two layers:

Reusable Workflows (in christhonie/event/.github/workflows/)

Centrally managed, versioned workflow definitions that encapsulate common operations like testing, building, and deploying.

Project Workflows (in each repository’s .github/workflows/)

Project-specific workflows that orchestrate the reusable workflows and define triggers.

Diagram

3. Path Filtering Strategy

3.1. Excluding Non-Build Directories

Use paths-ignore at the workflow trigger level to prevent unnecessary builds when only documentation or configuration files change:

on:
  push:
    branches:
      - develop
    # NOTE: Keep paths-ignore in sync with push-main.yml and pr-non-main.yml
    paths-ignore:
      - '*.md'
      - '*.adoc'
      - '.devcontainer/**'
      - '.husky/**'
      - '.jhipster/**'
      - 'docs/**'
      - 'src/main/helm/**'
When modifying the paths-ignore list, update it in all workflow files that share this pattern. The sync note comment serves as a reminder.

3.2. Conditional Job Execution

Use the dorny/paths-filter action to conditionally run frontend or backend tests based on what files changed:

jobs:
  paths-filter:
    runs-on: ubuntu-latest
    outputs:
      frontend: ${{ steps.filter.outputs.frontend }}
      backend: ${{ steps.filter.outputs.backend }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v2
        id: filter
        with:
          filters: |
            frontend:
              - '.github/workflows/**'
              - 'package*.json'
              - 'angular.json'
              - 'tsconfig*.json'
              - 'webpack/**'
              - 'src/main/webapp/**'
            backend:
              - '.github/workflows/**'
              - 'pom.xml'
              - 'src/main/java/**'
              - 'src/main/resources/**'
              - 'src/test/java/**'
              - 'src/test/resources/**'

  test-fe:
    needs: paths-filter
    if: needs.paths-filter.outputs.frontend == 'true'
    uses: christhonie/event/.github/workflows/test-fe.yml@main
    # ...

  test-be:
    needs: paths-filter
    if: needs.paths-filter.outputs.backend == 'true'
    uses: christhonie/event/.github/workflows/test-be.yml@main
    # ...

4. Reusable Workflows Reference

The following reusable workflows are available in christhonie/event/.github/workflows/:

Workflow Purpose Inputs Secrets Outputs

test-be.yml

Run backend tests using Maven

maven-profiles, jdk-version

MAVEN_PASSWORD

-

test-fe.yml

Run frontend tests using npm

node-version

NPM_PASSWORD

-

maven-package.yml

Build and cache Maven artifacts

maven-profiles, jdk-version, require-npm, node-version

MAVEN_PASSWORD

cached target

maven-deploy.yml

Deploy JAR to GitHub Packages

maven-profiles, jdk-version

MAVEN_PASSWORD

-

docker-build.yml

Build and push Docker image via JIB

maven-profiles, jdk-version

DOCKER_PAT

image-tag

helm-build.yml

Package and push Helm charts

maven-profiles, jdk-version

DOCKER_PAT

-

argocd-update.yml

Update ArgoCD deployment manifests

environment, service-name, image-tag

ARGOCD_REPO_TOKEN

-

dependencygraph.yml

Submit dependency graph to GitHub

maven-profiles, jdk-version

-

-

publish.yml

Upload build artifacts

jdk-version

-

-

gitflow-release-finish.yml

Complete GitFlow release process

jdk-version

-

-

4.1. Workflow Naming Convention

Workflow names use emoji prefixes for quick visual identification:

🚀

Deployment and push workflows

Testing workflows

📦

Packaging workflows

🐋

Docker workflows

Helm/Kubernetes workflows

GitFlow release workflows

📰

Publishing workflows

🔄

Synchronization workflows

5. Pipeline Flows

5.1. Pull Request Pipeline

When a PR is created or updated against non-main branches:

PR created/updated
       │
       ▼
  paths-filter
       │
       ├──► test-fe (if frontend changed)
       │
       └──► test-be (if backend changed)
       │
       ▼
  All tests must pass for merge

5.2. Develop Branch Pipeline

When code is pushed to the develop branch:

Push to develop
       │
       ▼
  paths-filter
       │
       ├──► test-fe ──┐
       │              │
       └──► test-be ──┤
                      │
                      ▼
                  package
                      │
       ┌──────────────┼──────────────┐
       │              │              │
       ▼              ▼              ▼
   publish     docker-build   dependencygraph

5.3. Main Branch Pipeline (Release)

When code is pushed to the main branch (typically after a release merge):

Push to main
       │
       ▼
    package (prod profile)
       │
       ├──────────────┬──────────────┐
       │              │              │
       ▼              ▼              ▼
   publish     docker-build     helm-build
                                     │
                                     ▼
                          gitflow-release-finish

5.4. Helm-Only Pipeline

Changes to src/main/helm/** trigger a dedicated Helm build without full Java/Angular builds:

on:
  push:
    branches:
      - main
      - develop
    paths:
      - 'src/main/helm/**'
  workflow_dispatch:

6. Consuming Workflows

6.1. Referencing Reusable Workflows

Project workflows reference reusable workflows using the uses keyword:

jobs:
  test-be:
    uses: christhonie/event/.github/workflows/test-be.yml@main
    with:
      maven-profiles: dev
      jdk-version: '17'
    secrets:
      MAVEN_PASSWORD: ${{ secrets.EVENT_PACKAGE_REPO_TOKEN }}

6.2. Versioning Strategy

Recommended: Use @main to stay current with the latest reusable workflow improvements.

uses: christhonie/event/.github/workflows/test-be.yml@main

Alternative: Pin to a specific tag for stability when needed:

uses: christhonie/event/.github/workflows/[email protected]

7. Job Dependencies and Caching

7.1. Sequential Execution with needs

Use needs to define job dependencies:

jobs:
  package:
    uses: christhonie/event/.github/workflows/maven-package.yml@main
    # ...

  docker-build:
    needs: [package]  # Wait for package to complete
    uses: christhonie/event/.github/workflows/docker-build.yml@main
    # ...

7.2. Caching Strategy

The maven-package workflow caches the target/ directory, which is then reused by:

  • docker-build - For building Docker images

  • helm-build - For packaging Helm charts

  • publish - For uploading artifacts

  • dependencygraph - For dependency analysis

7.3. Running Jobs After Skipped Dependencies

Use if: always() to run jobs even when dependencies were skipped:

docker-build:
  needs: [package]
  if: always()  # Run even if tests were skipped
  uses: christhonie/event/.github/workflows/docker-build.yml@main

8. Environment Variables Pattern

Centralize fallback logic at the job level to avoid repetition:

jobs:
  helm-build:
    runs-on: ubuntu-latest
    env:
      JDK_VERSION: ${{ inputs.jdk-version || '17' }}
      MAVEN_PROFILES: ${{ inputs.maven-profiles || 'dev' }}

    steps:
      - name: Set up JDK
        uses: actions/setup-java@v3
        with:
          java-version: ${{ env.JDK_VERSION }}
          # ...

      - name: Package Helm
        run: mvn helm:package -P${{ env.MAVEN_PROFILES }}

9. Secrets Configuration

9.1. Required Secrets

Secret Purpose Used By

GITHUB_TOKEN

Built-in token for repository operations

Git operations, GitHub API

EVENT_PACKAGE_REPO_TOKEN

Standard secret name for GitHub Packages access (Maven/NPM)

maven-package, test-be, test-fe, maven-deploy

DOCKER_PAT

Docker Hub Personal Access Token

docker-build, helm-build

ARGOCD_REPO_TOKEN

Access to ArgoCD configuration repository

argocd-update

9.2. Secret Variable Mapping

The EVENT_PACKAGE_REPO_TOKEN secret is internally mapped to workflow-specific variables:

# In workflow steps
secrets:
  MAVEN_PASSWORD: ${{ secrets.EVENT_PACKAGE_REPO_TOKEN }}
  NPM_PASSWORD: ${{ secrets.EVENT_PACKAGE_REPO_TOKEN }}

9.3. Maven Repository Configuration

The setup-java action creates a settings.xml file with credentials:

- name: Set up JDK
  uses: actions/setup-java@v3
  with:
    java-version: '17'
    distribution: 'temurin'
    cache: maven
    server-id: github-christhonie
    server-username: MAVEN_USERNAME
    server-password: MAVEN_PASSWORD

- name: Build with Maven
  run: mvn --batch-mode package
  env:
    MAVEN_USERNAME: christhonie
    MAVEN_PASSWORD: ${{ secrets.EVENT_PACKAGE_REPO_TOKEN }}

The generated settings.xml will contain:

<servers>
  <server>
    <id>github-christhonie</id>
    <username>${env.MAVEN_USERNAME}</username>
    <password>${env.MAVEN_PASSWORD}</password>
  </server>
</servers>

10. Artifact Management

10.1. Build Artifacts

Build artifacts (JAR files) are uploaded to GitHub Actions artifacts for investigation:

- name: Upload Artifacts
  uses: actions/upload-artifact@v4
  with:
    name: build-artifacts
    path: target/*.jar
    retention-days: 14
Artifacts are automatically purged after the retention period.

10.2. Dependency Graph (SBOM)

The dependencygraph workflow submits the Maven dependency graph to GitHub’s dependency graph feature, enabling:

  • Security vulnerability scanning

  • Software Bill of Materials (SBOM) generation

  • Dependency alerts

11. Docker Compose Version Synchronization

11.1. Pattern Overview

Projects can automatically synchronize Docker Compose file versions with POM versions:

  1. Store image versions in pom.xml properties (<revision>, <admin-service.version>)

  2. Workflow triggers when pom.xml changes on develop branch

  3. Extract versions using grep/sed

  4. Update Docker Compose files (dev.yml, admin-service.yml)

  5. Auto-commit changes with bot attribution

11.2. Example Implementation

Reference: event-admin-ui/.github/workflows/sync-docker-versions.yml

name: '🔄 Sync Docker Compose Versions'

on:
  push:
    branches:
      - develop
    paths:
      - 'pom.xml'

env:
  DOCKER_REGISTRY: christhonie
  GATEWAY_IMAGE_NAME: event-admin-ui
  ADMIN_IMAGE_NAME: event-admin-service

jobs:
  sync-versions:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Extract versions from POM
        id: versions
        run: |
          REVISION=$(grep -oP '(?<=<revision>)[^<]+' pom.xml | head -1)
          echo "revision=$REVISION" >> $GITHUB_OUTPUT

      - name: Update Docker Compose files
        run: |
          sed -i "s|${DOCKER_REGISTRY}/${GATEWAY_IMAGE_NAME}:[^[:space:]]*|${DOCKER_REGISTRY}/${GATEWAY_IMAGE_NAME}:${{ steps.versions.outputs.revision }}|g" src/main/docker/dev.yml

      - name: Commit and push
        run: |
          git config user.name "github-actions[bot]"
          git commit -am "[AutoTask] Sync Docker image versions"
          git push

TODO: Standardize sync-docker-versions.yml into a reusable workflow to avoid duplication across projects.

12. Version Strategy for Docker/Helm Artifacts

TODO: Implement build number conversion for SNAPSHOT versions:

  • Docker containers and Helm charts ending in -SNAPSHOT should include a build number

  • Pattern: 1.2.3-SNAPSHOT1.2.3-SNAPSHOT-{buildNumber} or 1.2.3-{buildNumber}

  • Ensures each build has a unique, retrievable version identifier

  • Build number options: GitHub run number, timestamp, or short commit SHA