Treating SAP CCO Plugins as Real Software: CI/CD, Code Analysis and Dependency Management

How are SAP Customer Checkout plugins built and delivered? Some partners have proper CI/CD pipelines in place - but certainly not everyone. A plugin is quickly treated as a one-off jar file: built on someone's laptop, tested by clicking around a POS once, zipped and mailed to the customer. That works - until the plugin misbehaves in a store, nobody can reproduce the build, and the jar that is running in production does not match any commit in any repository.

Plugins run inside a point of sale system. If anything deserves proper software engineering, it is the code that sits between your customer and their revenue. Over the past weeks I have moved many of my CCO plugin repositories to a shared setup with CI/CD, code analysis, security scanning and automated dependency updates. This post describes that setup as a blueprint - most of it applies to any SAP partner building CCO plugins, and the running costs are modest.

I use GitLab, but every pattern here maps 1:1 to GitHub Actions or any other CI system.

1. Reproducible builds start with dependencies

The first thing to fix is the ENV.jar problem. Every CCO plugin compiles against the ENV library that ships with the CCO installer. A common approach - and there is no official distribution channel that would suggest otherwise - is to either commit the jar (or a whole lib/ folder) into the repository, or to have every developer keep their own extracted ENV.jar somewhere on their machine. The first option bloats the repo and makes version upgrades opaque; the second means no two machines are guaranteed to build against the same thing.

Instead, I publish the ENV library of each CCO version into a private Maven repository once, with a small script that extracts it from the official installer ZIP. Plugin POMs then declare a normal dependency - here for the Cloud Edition:

<dependency>
    <groupId>com.sap.customercheckout.pos</groupId>
    <artifactId>ENV</artifactId>
    <version>3.2602.2</version>
    <scope>provided</scope>
</dependency>

For the repository itself I use JFrog Artifactory Cloud (around 150 EUR/month) - but there are free alternatives: your Git host's built-in package registry, a self-hosted Nexus or Artifactory OSS all work just as well for this.

Two details that took some trial and error:

The payoff: any developer (or CI runner) can build any plugin with nothing but a repository checkout and Maven credentials. Upgrading a plugin to a new feature pack becomes a one-line version bump with a reviewable diff.

2. Versioning and releases: the pom is the single source of truth

Nothing erodes trust faster than a jar whose version nobody can verify. My rules:

This is boring, standard release engineering - which is exactly the point. Boring is what you want from the process that produces POS software.

3. Central CI templates: quality and security gates for every repo

With more than one plugin repository, copy-pasted CI configuration drifts immediately. I keep all shared jobs in a central ci-templates repository that plugin repos include:

include:
  - project: my-group/ci-templates
    ref: main
    file:
      - quality.yml
      - security.yml

quality.yml contains:

security.yml contains:

One Trivy trap worth knowing: by default it skips dev/test dependencies. One of my test suites' package-lock.json was silently unscanned until I added --include-dev-deps to the reporting scan. Decide consciously which scan gates the pipeline and which one feeds reports.

4. Static code analysis with SonarQube

For deeper analysis I run a self-hosted SonarQube Community Build on a small VPS - the Community edition is free, and for a handful of plugin repositories a 2-core machine with Postgres behind a reverse proxy is plenty. The sonar.yml CI template runs the analysis via the Maven scanner without touching any plugin pom, and JaCoCo feeds coverage into it.

Know the Community edition's main limitation up front: no branch or merge request analysis. Analysis runs on the default branch only - if you scan a feature branch, it overwrites your main analysis. So the sonar job runs on main after merge, not as an MR gate. For my team size that trade-off is fine; the MR gates are the quality/security jobs above. If MR analysis becomes important, upgrading to the commercial Developer Edition later is a straightforward path - the CI wiring stays the same.

A nice bonus for customer-facing work: the cnes-report plugin exports the full analysis as a Word/Excel document straight from CI - useful when a customer asks "how do you ensure code quality?" and you can answer with a generated report instead of a slide.

5. Automated dependency updates with Renovate

Dependency updates are the chore that never happens manually - until a security advisory forces a panicked upgrade. Renovate automates this: it opens merge requests for outdated dependencies, grouped and rate-limited, with lockfile updates included.

Since the hosted Renovate app targets GitHub, on GitLab I run it as a scheduled pipeline in a small runner repository that auto-discovers all projects in my group. Every night it opens MRs where needed; I review and merge them with a green pipeline.

The configuration is where the CCO-specific thinking lives:

{
  "packageRules": [
    {
      "matchPackageNames": ["com.sap.customercheckout.pos:ENV"],
      "enabled": false
    },
    {
      "matchPackageNames": ["org.junit.jupiter:{/,}**"],
      "allowedVersions": "<6"
    }
  ]
}

And one review lesson: an MR pipeline being green does not mean the update is tested. Some of my jobs only run on main or manually on branches - so for updates that touch that infrastructure (a Docker-in-Docker bump, for instance), I trigger those jobs manually before merging. Know what your MR pipeline actually covers.

6. Deliver evidence, not just a jar

The final piece ties it together: on every release tag, CI assembles a customer report bundle next to the jar:

None of this requires manual work once set up - it is a zip attached to the release. When a customer's IT department asks about vulnerabilities, licenses or code quality, the answer is already on the release page.

Outlook: one-command test environments and automated e2e tests

This one is more advanced, and I have a first version of it working - it deserves a post of its own.

Each plugin repository gets a checked-in docker/ folder with a docker-compose.yml that starts a POS with the plugin deployed, paired against a shared CCO Manager (one per feature pack). The repository also contains an exported configuration XML, so the environment comes up fully configured - articles, users, quick selections, everything the plugin needs:

git clone <plugin-repo>
cd plugin-repo/docker
docker compose up

A new developer, a colleague reproducing a bug, or a CI runner all get the identical environment. For plugins that also target the Cloud Edition, a second docker/cloud/ variant pairs the POS against a Cloud Edition tenant.

On top of this sit automated end-to-end tests in CI: a headless browser drives the actual POS UI - tap article, trigger the plugin, complete the receipt - and then asserts on the outcome. For a gift receipt plugin, for example, the test captures the actual printout and asserts that the article description is present and no price appears anywhere. That assertion found a real bug: one template branch still printed prices for a specific receipt type. No amount of manual clicking would have caught that reliably on every future change.

Expect a deeper dive on this setup in a future post.

Summary

Practice Tooling Cost
Dependency management Private Maven repo for ENV/SAP libs free options exist; I use JFrog Cloud
Versioning & releases pom-driven, tag-gated CI releases 0
Formatting & lint gates Spotless (palantir), node --check 0
Security scanning Trivy, secret detection, SAST 0
Code analysis Self-hosted SonarQube Community ~a small VPS
Dependency updates Renovate (self-scheduled) 0
Customer deliverables SBOM, licenses, scan + analysis reports 0
Test environments & e2e (outlook) Checked-in docker-compose, headless browser 0

None of this is exotic - it is ordinary software engineering hygiene, applied to a niche where it is not yet universal. The whole setup runs mostly on free tooling, and every piece pays for itself the first time a bug is caught before it reaches a store, or a customer asks a question you can answer with a generated report.

What's next?

If you are an SAP partner building CCO plugins - or a customer running them - I am curious how you handle this today. Do your plugin vendors deliver from CI? Can you trace the jar on your POS back to a commit? Share your setup or your questions in the comments.

If this was useful, a like or share helps other people working on SAP CCO find it too.

ccosapSAPCustomerCheckoutpluginci-cddevopsbest-practicesrenovatesonarqube