Flowcontrol CI/CD and Portainer Deployment FlowFlowcontrol CI/CD and Portainer Deployment FlowGitHub ActionsGitHub ActionsGitHub ActionsDeveloperGitHub ActionsGitHub ActionsGitHub ActionsGitHub Container RegistryPortainerDeveloperDeveloperGitHub ActionsDevelopment PipelineGitHub ActionsDevelopment PipelineGitHub ActionsProtected Branch CIGitHub ActionsProtected Branch CIGitHub ActionsProtected Branch Image PublishGitHub ActionsProtected Branch Image PublishGitHub Container Registry(GHCR)GitHub Container Registry(GHCR)PortainerPortainerGitHub ActionsGitHub ActionsGitHub ActionsDevelopmentPush to `development`Deploy docs to GitHub PagesRun Java CI matrixRun client CIPrepare development SonarQubeRun dev SonarQube reports(non-blocking)Release validationOpen PR to `staging` or `master`Run Java CI + Jib smoke buildsRun client build + Docker smoke buildValidation only.No image publishing from pull requests.Post-merge publishMerge approved PR into protected branchVerify commit belongs to a merged PRwith an effective approvalalt[Merge into `staging`]Push `staging`, `staging-<sha>`,`<version>-staging`, `<version>-staging-<sha>`Staging auto-deploys staging tagsAuto-deploy staging update[Merge into `master`]Push immutable `<version>` tagsVersion tag available in GHCRManual Portainer updateGitHub Actions stops at build, verification, and image publication.Portainer is the runtime deployment layer.
Skip to content

πŸš€ CI/CD ​

The current Flowcontrol pipeline is split into three concerns:

  • Continuous integration in GitHub Actions
  • Container image publication to GitHub Container Registry (GHCR)
  • Runtime deployment through Portainer

GitHub Actions no longer deploys the application stack directly. The repo-side flow ends after documentation is published and application images are pushed to GHCR. Portainer is the deployment layer that consumes those images for the staging and production stacks.

Important:

  • staging is configured to auto-deploy from the published staging tags
  • production is not auto-deployed from GitHub
  • production is rolled forward manually in Portainer by updating APP_VERSION and IMAGE_TAG and then triggering the stack update

🌿 Branch model ​

  • development is the main integration branch.
  • staging is the protected pre-release branch.
  • master is the protected production branch.

In practice this means:

  • pushes to development run the development pipeline
  • pull requests to staging and master run validation only
  • pushes to staging and master are treated as post-merge release events

πŸ—ΊοΈ Workflow map ​

WorkflowTriggerPurpose
development-pipeline.ymlpush to developmentRuns documentation deploy, Java CI, client CI, and development SonarQube analysis
protected-branch-ci.ymlpull_request to staging or masterValidates release candidates before merge
protected-branch-publish.ymlpush to staging or masterVerifies approved merge state and publishes application images to GHCR
reusable-java-ci.ymlworkflow_callRuns mvn verify for one Java module and smoke-builds its Docker image locally with Jib
reusable-client-ci.ymlworkflow_callInstalls the frontend, builds it, and smoke-builds the client Docker image
documentation-deploy.ymlworkflow_callBuilds the VitePress site and deploys it to GitHub Pages
maintenance.ymlscheduleKeeps the self-hosted runners active

πŸ”„ Pipeline flow ​

The diagram below shows where GitHub Actions stops and where Portainer takes over.

PlantUML source: ci-cd-portainer-flow.puml

πŸ§ͺ Continuous integration ​

πŸ”¨ development ​

The Development Pipeline workflow runs on every push to development.

It currently does the following:

  • builds and deploys the documentation site to GitHub Pages
  • runs Java CI for eureka-service, gateway, article, farmer, maintenance, notification, and transport
  • runs the client CI workflow
  • prepares a development SonarQube instance on a self-hosted runner
  • runs SonarQube analysis for article, farmer, notification, transport, and maintenance

Important details:

  • there is currently no separate pull request workflow for development
  • the SonarQube report jobs on development use continue-on-error: true, so the development quality gate is informative and does not block the branch
  • this workflow does not publish application images

πŸ›‘οΈ staging and master pull requests ​

The Protected Branch CI workflow runs only for pull requests targeting staging or master.

This workflow is validation-only:

  • Java modules run through mvn verify
  • each Java module also smoke-builds its container image locally with Jib
  • the client is installed and built
  • the client Docker image is smoke-built locally
  • no images are pushed from pull request runs

This separation is intentional. Release branches are validated before merge, but publishing only happens after the approved pull request is merged.

πŸ“¦ Continuous deployment ​

🏷️ Image publication ​

The Protected Branch Image Publish workflow runs on pushes to staging and master.

Before any image is published, the workflow verifies that:

  • the pushed commit is associated with a merged pull request into the target branch
  • that pull request has at least one effective APPROVED review

If that verification fails, image publication stops.

When verification succeeds, the workflow publishes GHCR images for:

  • eureka-service
  • gateway-service
  • article-module
  • farmer-module
  • maintenance-module
  • notification-module
  • transport-module
  • flowcontrol-client

πŸ§ͺ Staging tags ​

After a merge into staging, each published image receives these tags:

  • staging
  • staging-<short-sha>
  • <version>-staging
  • <version>-staging-<short-sha>

These tags are intended for the staging stack and allow both stable and traceable staging deployments.

πŸš€ Production tags ​

After a merge into master, each published image receives one immutable version tag:

  • <version>

Production image publication is therefore version-based rather than branch-based.

βš“ Portainer handoff ​

Portainer is now responsible for the actual application rollout.

The important boundary is:

  • GitHub Actions validates code and publishes images
  • Portainer handles the runtime rollout behavior per environment

In practice:

  • staging auto-deploys from the published staging tags
  • production does not auto-deploy after the master publish
  • production is updated manually in Portainer by setting the intended APP_VERSION and IMAGE_TAG and then clicking the update/redeploy action so Portainer pulls the selected image version

Portainer configuration itself is not stored in these workflow files, so the GitHub pipeline documentation should be read as "build and publish in GitHub, deploy in Portainer".

For the runtime stack model itself, see Deployment.

πŸ“š Documentation deployment ​

Documentation is deployed separately from the application stack.

On pushes to development, the reusable documentation-deploy.yml workflow:

  • installs the documentation dependencies in documentation/site
  • builds the VitePress site
  • publishes the static output to GitHub Pages

This means documentation updates go live from development without waiting for a staging or master release.

πŸƒ Runners ​

The pipeline uses a mix of GitHub-hosted and self-hosted runners:

  • ubuntu-latest is used for documentation builds, reusable Java CI, reusable client CI, and GHCR publishing
  • self-hosted runners are used for the development SonarQube setup and analysis jobs
  • maintenance.yml runs daily at 03:00 UTC on actions-runner-1 and actions-runner-2 to keep those runners active

βœ… Typical release flow ​

  1. Feature work is merged into development.
  2. A push to development runs documentation deployment, Java/client CI, and development SonarQube analysis.
  3. A pull request from development to staging runs Protected Branch CI.
  4. After that pull request is approved and merged, the push to staging runs Protected Branch Image Publish.
  5. GHCR receives the staging tags and Portainer auto-deploys the staging environment from those tags.
  6. A pull request from staging to master runs Protected Branch CI.
  7. After that pull request is approved and merged, the push to master publishes immutable version tags to GHCR.
  8. For production, someone updates APP_VERSION and IMAGE_TAG in Portainer and triggers the stack update so the production environment pulls the released version.