Unit 3: Continuous Integration (CI) & Build Automation
I. CI Fundamentals and Benefits
Continuous Integration (originating with Grady Booch, 1991; popularised by Kent Beck in Extreme Programming, late 1990s) is the practice of merging every developer's work into a shared mainline many times a day, with each merge verified by an automated build and test run. The whole unit assumes this feedback loop.
- Core definition: developers push small commits to a shared repository; an automated server builds and tests each commit, rejecting anything that breaks the build.
- Trunk-based working: short-lived branches merged into
mainfrequently — ideally at least once per day per developer — to minimise divergence. - Fail fast: a broken build stops the line; fixing it is the team's top priority before new work proceeds.
- Automation over manual gates: compilation, testing and packaging run without human steps, triggered by a version-control event.
- Benefits:
- Early defect detection: integration bugs surface within minutes, not at a release-day "big-bang" merge.
- Reduced merge conflict cost: small, frequent merges shrink the diff each developer must reconcile.
- Always-releasable mainline: a green build means the codebase is a candidate for deployment at any time.
- Faster feedback: developers learn of failures while the change is still fresh in memory.
II. CI Pipeline Architecture
A CI pipeline is the ordered set of automated stages a commit passes through from source change to verified artifact.
A. Trigger and Source Stage
The pipeline begins when version control signals a change.
- Webhook trigger: the SCM (e.g. GitHub) sends an HTTP POST to the CI server on
pushorpull_requestevents. - Checkout: the runner clones the exact commit SHA so the build is reproducible.
- Polling (fallback): the server queries the repo on a schedule (
H/5 * * * *) when webhooks are unavailable.
B. Build and Test Stages
The commit is compiled, packaged and validated.
- Build stage: compile sources, resolve dependencies, produce a binary or bundle.
- Test stage: run unit tests, then integration tests, gating progression on pass/fail.
- Artifact stage: store the packaged output (JAR, Docker image, tarball) in an artifact registry keyed by build number.
C. Runners, Agents and Feedback
Execution is distributed and results reported back.
- Agents/runners: worker nodes (persistent or ephemeral containers) that execute stage steps in isolation.
- Fan-out parallelism: independent jobs (e.g. lint, unit test, build) run concurrently to shorten wall-clock time.
- Status reporting: the pipeline posts a commit status (green/red) back to the SCM and notifies via chat or email.
III. Popular CI Tools
Modern CI platforms differ chiefly in hosting model, configuration syntax and ecosystem integration.
A. Jenkins
An open-source, self-hosted automation server with a controller/agent topology.
- Extensibility: 1,800+ plugins cover SCM, build tools, cloud and notifications.
- Configuration: pipelines defined in a
Jenkinsfile(Groovy) stored with the code. - Trade-off: maximum flexibility and control, but the team maintains the infrastructure and plugin compatibility.
B. GitHub Actions
CI/CD built into GitHub, configured by YAML in .github/workflows/.
- Event model: workflows triggered by repository events (
on: push,on: pull_request). - Reusable units: community "actions" from the Marketplace (e.g.
actions/checkout) compose steps. - Hosted runners: GitHub-provided Linux/Windows/macOS VMs, billed by the minute; self-hosted runners also supported.
C. GitLab CI
CI/CD native to GitLab, configured by a .gitlab-ci.yml at the repo root.
- Stages and jobs:
stages:defines order; jobs assigned to a stage run in parallel within it. - GitLab Runner: an agent executing jobs via Docker, shell or Kubernetes executors.
- Integration: merge-request pipelines and built-in container registry tie CI tightly to the SCM.
D. CircleCI
A cloud-first CI platform configured by .circleci/config.yml.
- Orbs: shareable, versioned config packages that bundle jobs and commands.
- Executors: choice of
docker,machineormacosenvironments per job. - Caching and workspaces: explicit
save_cache/restore_cachespeed dependency-heavy builds.
IV. Build Tools Overview
Build tools automate dependency resolution, compilation and packaging so the CI server runs a single reproducible command.
A. Maven
A declarative Java build tool driven by a pom.xml.
- Convention over configuration: a fixed lifecycle —
validate → compile → test → package → verify → install → deploy. - Dependency management: coordinates (
groupId:artifactId:version) resolved from Maven Central. - Command:
mvn clean packagecompiles, tests and produces a JAR/WAR intarget/.
B. Gradle
A JVM build tool using a Groovy/Kotlin DSL (build.gradle).
- Task graph: a directed acyclic graph of tasks executed in dependency order.
- Incremental builds: up-to-date checks and a build cache skip unchanged work, faster than Maven on large projects.
- Command:
gradle buildrunscompileJava,testandassemble.
C. npm
The Node.js package manager and script runner (package.json).
- Dependency files:
dependenciesanddevDependencies;package-lock.jsonpins exact versions. - Scripts:
"scripts": { "build": "...", "test": "..." }invoked withnpm run <name>. - Command:
npm ciinstalls from the lockfile for reproducible CI installs.
V. Writing Jenkins Declarative Pipelines
The declarative pipeline is a structured, opinionated syntax for defining a Jenkinsfile around a mandatory pipeline block.
A. Structure and Directives
Every declarative pipeline follows a fixed skeleton.
agent: where the pipeline runs (any,none,docker,label).stages/stage: the ordered logical phases shown on the Jenkins UI.steps: the concrete commands inside a stage (sh,bat,echo).environment/post: variables shared across stages;postruns cleanup or notification onalways,successorfailure.
B. Worked Example
pipeline {
agent any
stages {
stage('Build') {
steps { sh 'mvn clean package' }
}
stage('Test') {
steps { sh 'mvn test' }
}
}
post {
failure { echo 'Build failed — notifying team' }
}
}agent any: run on any available agent.stage('Build'): compiles and packages via Maven; a non-zero exit fails the stage.post { failure }: executes only when a prior stage errored.
VI. Automated Builds
An automated build converts source into a deployable artifact with no manual intervention, triggered by each integration.
- Deterministic output: identical inputs (commit + pinned dependencies) yield identical artifacts; lockfiles enforce this.
- Clean environment: builds run in fresh containers to avoid "works on my machine" state leakage.
- Single command: the pipeline invokes one entry point (
mvn package,gradle build,npm run build) so behaviour matches local runs. - Artifact versioning: outputs tagged with the build number or commit SHA for traceability and rollback.
- Fast build principle: keep the commit build under ~10 minutes; offload slow steps to later pipeline stages.
VII. Automated Testing
Automated testing runs the suite on every build so functional regressions block the pipeline rather than reaching production.
A. The Test Pyramid
Tests are layered by scope, speed and quantity.
- Unit tests (base): many, fast, isolated — the bulk of the suite.
- Integration tests (middle): fewer, verify component interaction (DB, API).
- End-to-end tests (top): fewest, slowest, exercise the whole system through the UI.
B. Placement in the Pipeline
Tests gate progression by cost.
- Fast tests first: unit tests run in the commit stage for sub-minute feedback.
- Slow tests later: integration and E2E tests run in downstream stages, often in parallel.
- Fail-fast gating: any red test halts the pipeline and marks the commit as broken.
VIII. Test Automation Basics
Test automation basics establish how correctness is measured and how thoroughly the tests exercise the code.
A. Unit Testing
A unit test verifies the smallest testable piece of code in isolation.
- Arrange–Act–Assert: set up inputs, invoke the unit, assert the outcome.
- Isolation: dependencies replaced with mocks or stubs so failures localise to one unit.
- Frameworks: JUnit (Java), pytest (Python), Jest (JavaScript).
- Example:
JAVA@Test void addsTwoNumbers() { assertEquals(5, Calculator.add(2, 3)); }assertEquals(5, ...): the test fails ifaddreturns anything but 5.
B. Coverage
Coverage measures the proportion of code executed by the test suite.
- Line coverage: percentage of source lines run at least once.
- Branch coverage: percentage of decision outcomes (both
ifandelse) exercised — stricter than line coverage. - Tools: JaCoCo (Java), Istanbul/nyc (JavaScript), coverage.py (Python).
- Interpretation: high coverage shows code is exercised, not that it is correct; a threshold gate (e.g. fail below 80%) prevents regression in test discipline without treating 100% as the goal.
Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →