Version Control with Git and GitHub/GitLab

Chapter 10
Git Automation and CI/CD Integration

Diploma in DevOps
Version Control with Git and GitHub/GitLab   •   Diploma

Git Automation and CI/CD Integration

Introduction

Version control becomes substantially more valuable when it does more than record file changes. In a professional DevOps environment, every commit can trigger automated validation, testing, packaging, security checks, release preparation, and deployment. This automation reduces manual effort, shortens feedback cycles, and makes software delivery more repeatable.

Git provides local automation through hooks and repository templates. GitHub and GitLab extend Git with hosted automation platforms: GitHub Actions and GitLab CI/CD. These systems execute workflows on runners, produce artifacts, manage environments, and enforce controls around sensitive deployments.

Automation does not eliminate engineering judgment. Poorly designed pipelines can create false confidence, expose secrets, waste computing resources, or deploy defective software rapidly. Effective CI/CD therefore combines technical automation with suitable branching policies, test strategies, security controls, observability, and human approval where risk requires it.

Learning Objectives

By the end of this chapter, you should be able to:

  • distinguish Git hooks, repository templates, CI, and CD;
  • design validation and testing stages for a software repository;
  • create basic GitHub Actions and GitLab CI/CD workflows;
  • explain runners, jobs, stages, dependencies, caches, and artifacts;
  • manage deployment environments and protected releases;
  • apply secrets-management and least-privilege principles;
  • diagnose common pipeline failures; and
  • evaluate the benefits and limitations of automation in a realistic delivery scenario.

1. Automation in the Git-Based Delivery Model

Continuous integration (CI) is the practice of integrating changes frequently and automatically verifying them. A CI pipeline commonly performs syntax checks, formatting checks, unit tests, integration tests, security scanning, and build operations.

Continuous delivery means that validated software is always kept in a releasable state. Continuous deployment goes further: an approved pipeline automatically deploys a release to a target environment. The terms are related but not identical.

A typical flow is:

Developer commit
      |
      v
Local hooks and push
      |
      v
CI: lint -> test -> scan -> build
      |
      v
Artifact or package
      |
      v
Deploy to test/staging
      |
      v
Approval or policy gate
      |
      v
Production deployment and verification

Automation should provide rapid feedback close to the source of an error. Formatting and basic unit tests are usually fast and should run early. Expensive integration, performance, or end-to-end tests can run later or in parallel when appropriate.

A useful quality principle is that a pipeline should fail for a meaningful reason. A job that is unreliable, excessively slow, or dependent on an unrecorded external service may reduce trust in the entire system.

2. Git Hooks and Repository Templates

2.1 Git hooks

A Git hook is an executable program invoked by Git at a particular point in a local or server-side operation. Hooks are stored in the repository’s .git/hooks directory by default, although core.hooksPath can specify another location.

Common client-side hooks include:

HookTypical use
pre-commitFormatting, linting, and fast checks before a commit is created
commit-msgEnforcing commit-message conventions
pre-pushRunning tests before objects are pushed
prepare-commit-msgSupplying or modifying an initial message

Server-side hooks can run on a Git server. A pre-receive or update hook can reject a push, while a post-receive hook can notify another system. Hosted platforms generally provide their own policy and workflow mechanisms instead of allowing arbitrary server hooks.

Hooks are useful for fast developer feedback, but they are not a sufficient security boundary. A developer can bypass a local hook with git commit --no-verify, and local hooks are not automatically transferred when a repository is cloned. Therefore, important checks must also run in CI.

A simple pre-commit example is:

#!/bin/sh
set -eu

python -m compileall -q src
git diff --check

The first command checks Python source compilation; the second detects whitespace errors in the staged or working-tree diff. In a real project, a hook manager such as pre-commit may install and standardize multiple tools, but the CI pipeline should still repeat authoritative checks.

2.2 Commit-message hooks

A commit-msg hook can enforce a pattern such as a type and short description:

#!/bin/sh
set -eu

message=$(sed -n '1p' "$1")
case "$message" in
  feat:*|fix:*|docs:*|test:*|refactor:*) exit 0 ;;
  *) echo "Commit must begin with feat:, fix:, docs:, test:, or refactor:" >&2; exit 1 ;;
esac

This is a convention, not a substitute for code review. Teams should avoid rules that encourage meaningless messages or make emergency recovery unnecessarily difficult.

2.3 Repository templates

A repository template provides a consistent starting structure for new repositories. It may include:

  • a standard directory layout;
  • .gitignore and .editorconfig files;
  • a README and contribution guide;
  • issue and pull-request templates;
  • starter CI configuration;
  • security and licensing documents; and
  • ownership rules for sensitive directories.

Templates improve consistency, but they can become stale. They should have an owner, a versioning or update strategy, and automated tests of their own. A template should not embed credentials, private keys, or organization-specific secrets.

3. Pipeline Design Principles

A pipeline is a defined sequence or graph of automated jobs. A job is a unit of execution, such as running tests or building a container. A stage groups jobs conceptually; jobs in the same stage may run in parallel, depending on the platform and available runners.

Important design principles include:

  1. Fast feedback: run inexpensive checks early.
  2. Repeatability: pin important tool and action versions and record dependencies.
  3. Isolation: use clean, reproducible runner environments.
  4. Least privilege: grant jobs only the permissions they require.
  5. Traceability: associate commits, artifacts, approvals, and deployments.
  6. Idempotence: repeating a deployment should lead to the same intended state rather than duplicate or corrupt resources.
  7. Fail-fast but informative: stop unsafe progression while preserving useful logs and test reports.
  8. Separation of build and deployment: build once, then promote the same artifact through environments.

A common pipeline graph is:

validate ──┬──> unit-test ──┬──> package ──> deploy-test ──> deploy-production
           └──> security ────┘                         (approval/policy)

Not every job must depend on every previous job. Explicit dependencies allow independent jobs to run concurrently while preserving required order.

3.1 Build once, promote many times

A weak approach rebuilds source separately for test, staging, and production. Different dependency resolution, timestamps, or environment variables can produce different binaries. A stronger approach creates one versioned artifact and promotes that exact artifact.

An artifact is an output retained by the CI system, such as a compiled package, container image reference, test report, or deployment bundle. A cache is different: it is an optimization containing reusable data such as downloaded dependencies. A cache may be deleted or regenerated; an artifact is normally part of the evidence and output of a pipeline.

4. GitHub Actions

GitHub Actions workflows are YAML files normally stored in .github/workflows/. A workflow is triggered by events such as a push, pull request, tag, manual dispatch, or scheduled event. It contains jobs, and each job contains steps.

A minimal workflow is:

name: CI

on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Check out source
        uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - name: Install dependencies
        run: python -m pip install -r requirements.txt
      - name: Run tests
        run: python -m pytest

The on section defines triggers. runs-on selects a hosted or self-hosted runner. uses invokes a reusable action, while run executes shell commands. The permissions declaration limits the workflow token; restricting permissions is safer than relying on broad defaults.

A job can expose outputs, consume secrets, use a matrix, and depend on other jobs. A matrix is useful for testing multiple supported versions:

strategy:
  matrix:
    python-version: ['3.11', '3.12']

The platform creates a job instance for each matrix value. The test suite must therefore be compatible with all declared versions, and the team should control matrix size to avoid unnecessary cost.

4.1 Artifacts and releases in GitHub Actions

An artifact can be uploaded for later jobs or for download after a workflow completes. A release workflow may be triggered by a version tag, for example v2.4.0. A robust release process should verify that the tag points to the intended commit, build the package, generate checksums or provenance where appropriate, and publish only after required checks succeed.

Secrets should be stored in repository, organization, or environment secret stores. They should not be written into YAML, source code, command-line arguments visible in logs, or generated artifacts. Environment protection rules can require reviewers before production secrets become available.

Reusable workflows and composite actions help reduce duplication. They should be reviewed like source code because an action executes with the permissions of its workflow. Pinning actions to a trusted major version is convenient, while pinning to an immutable commit offers stronger supply-chain control but requires deliberate maintenance.

5. GitLab CI/CD

GitLab CI/CD configuration is usually stored in .gitlab-ci.yml. A pipeline defines stages and jobs. Jobs commonly use an image, scripts, variables, dependencies, rules, caches, and artifacts.

Example:

stages:
  - validate
  - test
  - build
  - deploy

default:
  image: python:3.12-slim

lint:
  stage: validate
  script:
    - pip install -r requirements-dev.txt
    - ruff check .

test:
  stage: test
  script:
    - pip install -r requirements.txt
    - pytest --junitxml=reports/junit.xml
  artifacts:
    when: always
    reports:
      junit: reports/junit.xml

package:
  stage: build
  script:
    - python -m build
  artifacts:
    paths:
      - dist/
    expire_in: 14 days

By default, later stages wait for earlier stages to succeed, although the configuration can express more precise dependencies. rules can restrict a job to a branch, tag, merge request, or changed files. Variables should be classified carefully: ordinary configuration variables are not automatically confidential, whereas protected and masked variables are appropriate for sensitive values.

GitLab runners execute jobs. They may be shared, group-specific, or project-specific, and may use shell, Docker, Kubernetes, or other executors. Runner tags can ensure that a job is scheduled only on a suitable runner. A privileged runner requires particular caution because a compromised job could affect the runner host or other workloads.

GitLab artifacts can be passed to later jobs or retained for download. Reports can be interpreted by GitLab, including test and security reports. Caches accelerate dependency installation but should not be treated as evidence of a successful build.

6. Runners, Dependencies, and Execution Security

A runner is the execution agent that performs a CI job. Hosted runners are maintained by the platform provider; self-hosted runners are operated by the organization. Self-hosting can provide network access, specialized hardware, or cost control, but it creates responsibilities for patching, isolation, monitoring, and credential protection.

A job should ideally run in a clean environment. Reusing a mutable workspace can cause hidden dependencies: a test passes because a previous job left a file behind, or an old package remains installed. Containers and ephemeral virtual machines reduce this risk.

Runner governance should address:

  • who can register or administer runners;
  • which projects can schedule jobs on them;
  • whether untrusted pull requests can execute on privileged runners;
  • network access and egress restrictions;
  • operating-system and container patching;
  • log retention and sensitive-data redaction; and
  • capacity, queue time, and cost.

Forked pull requests deserve special attention. Workflows processing untrusted code must not expose production credentials or write access tokens. A malicious change can print environment variables, alter build scripts, or attack reachable services. Use read-only permissions, isolated runners, and protected environments.

7. Testing, Quality Gates, and Security Checks

A pipeline can combine several test levels:

CheckPurposeTypical timing
Formatting/lintingDetect style and common defectsVery early
Unit testsVerify isolated logicEarly
Integration testsVerify component interactionAfter dependencies are available
End-to-end testsVerify user-visible workflowsLater and selectively
Static analysisDetect code and security patternsEarly or parallel
Dependency scanningIdentify vulnerable packagesEvery relevant build
Performance testsMeasure response and capacityScheduled or release-oriented

Quality gates should be explicit. For example, a merge may require successful tests, no critical vulnerability, review by an appropriate owner, and a successful build. A coverage percentage can be informative, but coverage alone does not demonstrate test quality; trivial tests can increase coverage without exercising important behavior.

Security scanning also has limitations. A scanner can produce false positives and may miss business-logic vulnerabilities. Results should be triaged, documented, and linked to risk-based remediation rather than blindly ignored.

8. Environments, Artifacts, and Deployment Controls

An environment is a named target such as development, test, staging, or production. Each environment may have different URLs, credentials, infrastructure, data sensitivity, and approval requirements.

A deployment design should specify:

  • the artifact version being deployed;
  • configuration supplied at deployment time;
  • health checks and acceptance criteria;
  • rollback or roll-forward procedure;
  • ownership and approval authority; and
  • audit records.

Protected branches prevent direct changes to important branches. Protected environments add controls around deployment, such as required reviewers, restricted branches, or deployment windows. These controls reduce the chance that a single compromised account or erroneous commit can deploy directly to production.

Approvals must not become meaningless routine clicks. The approver should have enough information to assess the change: commit identity, test results, security findings, migration impact, and rollback plan. Separation of duties may require that the person who authored a change cannot be its sole production approver.

Deployment strategies include:

  • Rolling deployment: replace instances gradually.
  • Blue-green deployment: maintain two environments and switch traffic between them.
  • Canary deployment: expose a small proportion of users to the new version before wider rollout.
  • Feature flags: deploy code while controlling feature activation separately.

The appropriate method depends on architecture, risk, observability, and rollback capability. A deployment is not successful merely because a command returned exit code zero; application health and user impact must be verified.

9. Secrets, Identity, and Supply-Chain Governance

Secrets include passwords, API tokens, signing keys, cloud credentials, and certificates. Recommended controls include:

  • store secrets in managed secret stores or CI secret variables;
  • grant short-lived, narrowly scoped credentials where possible;
  • avoid printing secrets and redact logs;
  • rotate credentials and revoke unused ones;
  • use workload identity or federated authentication instead of long-lived cloud keys;
  • separate credentials by environment; and
  • audit access and usage.

The software supply chain includes source dependencies, build tools, container base images, CI actions, runners, and package registries. Risks include dependency substitution, malicious packages, compromised actions, and tampered artifacts. Mitigations include lockfiles, trusted registries, dependency review, action or image pinning, signed commits or artifacts where feasible, provenance records, and minimal runner permissions.

Automation also raises ethical and governance questions. Logs may contain personal or customer data, so retention and access must follow organizational policy and applicable privacy requirements. Automated rejection should be explainable, and teams should provide a controlled emergency process without normalizing bypasses.

10. Troubleshooting and Operational Metrics

Pipeline failures can be classified as:

  1. Code failures: tests or builds correctly detect a defect.
  2. Configuration failures: incorrect YAML, permissions, variables, or paths.
  3. Environment failures: unavailable services, runner capacity, or network problems.
  4. Flaky failures: nondeterministic tests or timing assumptions.
  5. Security-policy failures: blocked permissions, vulnerability gates, or protected deployment rules.

Diagnosis should begin with the failing job and exact command, followed by comparison with the last successful run. Reproducing the command locally in a matching container can reveal environment differences. Teams should avoid blindly rerunning failures: reruns can hide a flaky system and may repeat unsafe deployment actions.

Useful delivery metrics include deployment frequency, lead time for changes, change failure rate, and time to restore service. These metrics should be interpreted together. Increasing deployment frequency while increasing failed changes may indicate that speed has been optimized without adequate quality. Metrics should support learning rather than individual blame.

11. Applied Case: Online Bookshop Release Pipeline

This is an illustrative case.

Background

A small online bookshop maintains a Python web service in GitHub. Developers work in short-lived branches and submit pull requests to main. Production runs in a managed container platform.

Problem

The team manually ran tests on laptops and copied a package to production. Two incidents occurred: a developer forgot to apply a database migration, and a package built on one laptop differed from the package tested by another developer. There was no consistent approval record.

Analysis

The main risks were non-reproducible builds, insufficient pre-merge validation, weak deployment traceability, and lack of a rollback procedure. The team also discovered that a local test depended on a developer-only environment variable.

Approach and solution

The team implemented the following workflow:

  1. A pre-commit hook runs formatting and fast lint checks.
  2. Every pull request runs linting, unit tests, dependency scanning, and integration tests against a disposable database.
  3. The workflow builds one container image identified by the commit SHA.
  4. The image is stored in a private registry and promoted unchanged to staging and production.
  5. A staging deployment runs database-migration checks and smoke tests.
  6. Production deployment is available only from the protected main branch and requires an approved reviewer.
  7. Production credentials are environment-scoped and supplied through short-lived identity federation.
  8. A health check and error-rate monitor determine whether the release remains active; otherwise the previous image is restored.
  9. The pipeline records the commit, image digest, test reports, approver, and deployment time.

Outcome

The team removed laptop-specific build differences and gained a searchable deployment history. Developers received failures during pull requests rather than after production deployment. The process did not prevent every defect: a business-rule error still passed the automated tests, demonstrating that test coverage and exploratory review remained necessary.

Learning points

The case illustrates four principles. First, local hooks improve feedback but CI remains authoritative. Second, a single immutable artifact should be promoted rather than rebuilt. Third, production protection combines technical controls and human accountability. Fourth, deployment automation must include observability and recovery, not merely a successful command.

12. Contemporary Developments and Best Practices

Modern CI/CD increasingly incorporates infrastructure as code, policy as code, software bills of materials, artifact signing, provenance attestations, and automated dependency updates. These capabilities improve traceability but also enlarge the system that must be governed.

AI-assisted coding and pipeline generation can accelerate routine work, but generated YAML and scripts must be reviewed. An AI tool may suggest insecure permissions, expose secrets, use an untrusted action, or misunderstand deployment dependencies. Treat generated configuration as untrusted code until tested and reviewed.

A practical implementation checklist is:

  • keep workflows in version control;
  • validate YAML and pipeline configuration before merging;
  • use minimal token permissions;
  • run untrusted code on isolated, non-privileged runners;
  • pin or govern third-party actions and images;
  • distinguish caches from release artifacts;
  • build once and promote the same artifact;
  • protect production branches and environments;
  • record approvals, versions, and deployment outcomes;
  • test rollback and recovery procedures; and
  • review pipeline performance, reliability, and cost periodically.

Chapter Summary

Git automation connects source changes to repeatable engineering actions. Local hooks provide rapid feedback, while CI provides the authoritative and reproducible validation boundary. Repository templates standardize project beginnings but require maintenance.

GitHub Actions and GitLab CI/CD express workflows as jobs and steps or stages, executed by runners. Effective pipelines separate validation, testing, security analysis, packaging, and deployment while using explicit dependencies. Artifacts preserve build outputs; caches merely accelerate execution. Runners must be isolated and governed, especially when processing untrusted code.

Environments, protected branches, protected deployments, approvals, secrets management, and least-privilege identities reduce operational and supply-chain risk. Deployment strategies such as rolling, blue-green, canary, and feature-flagged releases should be selected according to risk and observability. Finally, reliable CI/CD includes monitoring, auditability, rollback, and continuous improvement rather than treating automation as a simple sequence of shell commands.

Key Terms

Git hook: A program invoked by Git during a defined operation.

Repository template: A standardized starting structure for new repositories.

Continuous integration: Frequent integration of changes with automated validation.

Continuous delivery: Keeping validated software ready for release.

Continuous deployment: Automatically deploying validated changes to a target environment.

Workflow: A configured automation process triggered by repository or platform events.

Job: A unit of work executed by a runner.

Runner: An agent or execution environment that performs CI jobs.

Stage: A logical grouping of jobs in a pipeline.

Artifact: A retained output produced by a job, such as a package or test report.

Cache: Reusable data retained to speed up later jobs.

Environment: A named deployment target with its own configuration and controls.

Protected branch: A branch subject to merge, review, or push restrictions.

Protected environment: A deployment target requiring specified controls or approvals.

Matrix: A configuration that creates job variants across parameter combinations.

Idempotence: The property that repeating an operation produces the same intended state.

Least privilege: Granting only the permissions required for a task.

Canary deployment: Releasing to a small proportion of users before wider rollout.

Pipeline gate: A condition that must be satisfied before progression.

Supply-chain security: Protection of source, dependencies, tools, artifacts, and delivery infrastructure.

Review Questions

Short Answer Questions

  1. Why are local Git hooks insufficient as the only validation mechanism?
  2. Distinguish a CI artifact from a CI cache.
  3. What is the role of a runner in GitHub Actions or GitLab CI/CD?
  4. Why should production environments be protected?
  5. State two risks associated with self-hosted runners.

Long Answer Questions

  1. Explain how a pipeline can be structured from commit validation through production deployment. Justify the order of its stages.
  2. Compare GitHub Actions and GitLab CI/CD in terms of configuration, jobs, runners, artifacts, and environment controls.
  3. Discuss the security implications of executing pull-request code on a privileged runner.
  4. Explain the principle of building once and promoting many times, including its benefits for traceability and rollback.
  5. Evaluate the role of human approval in an automated deployment process.

Analytical / Application Questions

  1. A team’s tests pass locally but fail intermittently in CI. Develop a systematic investigation plan that considers dependencies, runner state, timing, external services, and test isolation.
  2. Design a CI/CD pipeline for a containerized service that must support development, staging, and production. Specify validation, artifact handling, approvals, secrets, health checks, and rollback.
  3. A proposed workflow grants write access to repository contents and exposes production credentials to every pull-request job. Analyze the risks and propose a least-privilege redesign.

Multiple Choice Questions with Answers

Question 1

Which statement best describes the relationship between local Git hooks and CI?

A. Local hooks replace the need for CI because they run before every commit

B. CI is unnecessary when a repository uses a commit-message hook

C. Hooks provide local feedback, but authoritative checks should also run in CI

D. CI can run only after a server-side Git hook rejects a push

Correct Answer: C. Hooks provide local feedback, but authoritative checks should also run in CI

Question 2

What is the main difference between a CI artifact and a cache?

A. An artifact is a retained output, while a cache is reusable data intended to speed execution

B. A cache must contain a production binary, while an artifact contains only logs

C. Artifacts are always secret, while caches are always public

D. There is no operational difference between them

Correct Answer: A. An artifact is a retained output, while a cache is reusable data intended to speed execution

Question 3

In GitHub Actions, what does the runs-on field normally specify?

A. The branch that receives the deployment

B. The runner environment on which the job executes

C. The artifact retention period

D. The required pull-request reviewers

Correct Answer: B. The runner environment on which the job executes

Question 4

Which practice most directly supports the principle of build once and promote many times?

A. Rebuilding the package independently in every environment

B. Using a single versioned artifact for staging and production

C. Allowing developers to replace the production binary manually

D. Deleting artifacts after each test job

Correct Answer: B. Using a single versioned artifact for staging and production

Question 5

What is a primary security concern with running untrusted pull-request code on a privileged self-hosted runner?

A. The code may change the runner or access credentials and other resources

B. The pull request will automatically receive a higher test-coverage score

C. The runner will be unable to execute shell commands

D. The repository will lose all commit history

Correct Answer: A. The code may change the runner or access credentials and other resources

Question 6

Which GitLab CI/CD feature is commonly used to group jobs into an execution order?

A. stages

B. README

C. gitignore

D. LICENSE

Correct Answer: A. stages

Question 7

Which deployment approach exposes a small proportion of users to a new release before wider rollout?

A. Blue-green deployment

B. Canary deployment

C. Full replacement deployment

D. Local deployment

Correct Answer: B. Canary deployment

Question 8

Which control best follows the principle of least privilege for a workflow token?

A. Granting administrator access to every job

B. Using the broadest permissions to avoid failures

C. Declaring only the repository permissions required by the workflow

D. Storing the token in the source repository

Correct Answer: C. Declaring only the repository permissions required by the workflow

Question 9

Why should a deployment include health checks after the deployment command succeeds?

A. A successful command necessarily proves that users experience no errors

B. Health checks verify application behavior rather than only command execution

C. Health checks replace the need for automated tests

D. Health checks make rollback impossible

Correct Answer: B. Health checks verify application behavior rather than only command execution

Question 10

Which statement about production approval is most appropriate?

A. Approval should be an unexplained routine click

B. Approval should be based on evidence such as test results, artifact identity, and rollback information

C. Approval is never required for high-risk systems

D. Only the deployment script should approve its own execution

Correct Answer: B. Approval should be based on evidence such as test results, artifact identity, and rollback information

Version Control with Git and GitHub/GitLab
Chapter 10 of 14