Continuous Integration, Delivery and AutomationIntroductionModern software is produced by teams that change a shared codebase continuously. Every change introduces an opportunity to improve the product and a risk that existing behaviour will be damaged. Continuous integration (CI), continuous delivery (CD), and automation provide disciplined ways to obtain rapid feedback, package software consistently, and release it safely. GitHub Actions is GitHub’s workflow-automation platform. A workflow is a YAML-defined process stored in .github/workflows. It responds to repository events, schedules, or manual invocations. A workflow contains jobs; jobs contain steps; steps execute commands or actions on runners. This model allows a full-stack team to automate linting, unit and integration testing, application builds, security checks, artifact publication, deployment, and release management. Automation is not simply a matter of writing YAML. Effective automation requires decisions about trust boundaries, permissions, reproducibility, failure handling, secrets, environments, cost, and the level of evidence required before software reaches users. Learning ObjectivesBy the end of this chapter, you should be able to: - distinguish continuous integration, continuous delivery, and continuous deployment;
- explain GitHub Actions events, workflows, jobs, steps, runners, actions, artifacts, and environments;
- design workflows using dependencies, matrices, caching, and reusable workflows;
- automate testing, linting, building, releases, and deployments;
- manage secrets, permissions, environments, and untrusted pull-request code;
- improve workflow reliability, observability, security, and cost;
- evaluate an automation design in a realistic software-development case.
1. The Delivery Pipeline as a Feedback SystemContinuous integrationContinuous integration means that developers integrate changes into a shared repository frequently and that each integration is automatically built and tested. CI reduces integration risk by discovering problems while the change is still small and its author still has context. A CI pipeline commonly performs the following sequence: - obtain the revision;
- install a known runtime and dependencies;
- run formatting and lint checks;
- execute unit and integration tests;
- build the application;
- publish reports or build outputs for inspection.
CI does not guarantee that software is defect-free. It creates repeatable evidence about selected quality properties. Continuous delivery and continuous deploymentContinuous delivery means that the main branch is kept in a releasable state. A successful pipeline produces a deployable package, but a person or an explicit approval may authorize production deployment. Continuous deployment extends this idea: every change that passes the required controls is deployed automatically to production. Continuous deployment can reduce release batches and manual work, but it requires strong testing, monitoring, rollback, and organizational confidence. | Practice | Primary question | Typical release decision |
|---|
| CI | Does this change integrate and meet automated checks? | Merge or reject the change | | Continuous delivery | Is a tested version ready to release? | Human or policy approval | | Continuous deployment | Should this passing version go live now? | Automated policy |
Pipeline stages and evidenceA pipeline is best understood as a chain of evidence rather than merely a list of commands. A lint result gives evidence about style and selected static errors; a test result gives evidence about specified behaviours; a build gives evidence that source can be transformed into a package. None of these proves every property of the system. A useful design principle is to fail early on inexpensive checks and reserve slower or costly checks for later stages. However, excessive parallelism can increase cost, and a fast but weak pipeline may provide false confidence. 2. GitHub Actions ArchitectureA workflow file is expressed in YAML. The following minimal example runs tests when changes are pushed or when a pull request targets main. name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Check out source
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm testEventsThe on key defines triggers. Common events include: push, for commits pushed to selected branches;pull_request, for pull-request activity;workflow_dispatch, for a manually started workflow and optional inputs;schedule, for cron-based maintenance;release, for release publication events;workflow_call, which allows another workflow to invoke a reusable workflow.
Branch and path filters reduce unnecessary execution. For example, documentation-only changes may not require a full application build. Filters must be designed carefully: omitting a required path can allow an untested change to merge. Pull-request workflows require special care. A workflow triggered by pull_request generally runs with the pull request’s merge context and is safer for untrusted contributions. pull_request_target runs in the context of the base repository and may access elevated secrets; checking out and executing untrusted pull-request code in that context can expose those secrets. It should be used only with a carefully reviewed design, and never as a casual replacement for pull_request. Jobs and dependenciesJobs are independent by default and may run in parallel. The needs property establishes dependency ordering: jobs:
test:
runs-on: ubuntu-latest
steps:
- run: npm ci
- run: npm test
build:
needs: test
runs-on: ubuntu-latest
steps:
- run: npm run buildA job that depends on test starts only after test succeeds, unless conditional expressions intentionally alter that behaviour. Job boundaries are useful for isolation, permissions, runner selection, and visible status reporting. RunnersA runner is the machine or execution environment that performs a job. GitHub-hosted runners provide disposable virtual machines with common operating systems. Self-hosted runners are managed by an organization and may provide specialized hardware, private-network access, or predictable internal tooling. Hosted runners reduce maintenance but may have usage limits, queue delays, and network restrictions. Self-hosted runners provide control but create responsibilities for patching, hardening, credential protection, job isolation, and capacity management. A self-hosted runner that executes untrusted code can become a route into the organization’s network. Steps and actionsA run step executes shell commands. A uses step invokes a packaged action. Actions can implement common operations such as checking out code, setting up a runtime, uploading artifacts, or authenticating with a cloud provider. Third-party actions are executable supply-chain dependencies. Pinning an action to a full commit SHA provides stronger immutability than using a mutable tag, although it increases update-management work. Organizations should review action ownership, permissions, release history, maintenance, and transitive dependencies. 3. Matrices, Artifacts, Caching, and ReuseMatrix testingA matrix expresses combinations of configuration values without duplicating job definitions. It is useful for testing supported versions, operating systems, or database variants. jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
node: [18, 20]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm ci
- run: npm testThis creates four combinations. A matrix increases coverage but also increases runner minutes and queue demand. Use it for supported compatibility claims, not for every conceivable combination. include can add special cases, and exclude can remove unsupported combinations. ArtifactsArtifacts are files retained by a workflow run, such as compiled bundles, test reports, screenshots, coverage reports, or packaged binaries. They allow one job to publish output and another job to download it without rebuilding. - name: Upload build
uses: actions/upload-artifact@v4
with:
name: web-dist
path: dist/
retention-days: 7An artifact is not automatically a deployment. Its provenance, retention, access controls, and contents must be considered. Do not upload secrets, private keys, or unnecessary source files. Artifact names should identify the product, revision, and purpose when several outputs coexist. CachingCaching stores reusable dependencies or intermediate files to accelerate later runs. A cache is an optimization, not an authoritative build output. Builds must remain correct when a cache is absent or invalid. Cache keys should include relevant inputs such as operating-system and lockfile identity. Stale caches can cause confusing failures; broad keys can cause incorrect reuse. Dependency caching is often provided by setup actions. For example, setup-node can cache package-manager data based on the lockfile. The use of npm ci, rather than an unconstrained installation command, ensures that the lockfile controls the dependency set. Reusable workflows and composite actionsA reusable workflow is invoked through workflow_call. It can standardize organization-wide jobs such as security scanning or deployment. Inputs and secrets should be explicitly declared. A composite action groups steps into a reusable action, usually for a focused operation such as installing project tooling. Reusable workflows reduce duplication and policy drift, but excessive abstraction can hide important behaviour. Document inputs, outputs, permissions, supported runners, and failure semantics. Version reusable components deliberately and test changes as carefully as application code. 4. Automating a Full-Stack ApplicationA practical full-stack pipeline may contain separate checks for a frontend, backend, and infrastructure configuration. A representative sequence is: name: Application pipeline
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test -- --coverage
build:
needs: quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: application-${{ github.sha }}
path: dist/
deploy-staging:
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
needs: build
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/download-artifact@v4
with:
name: application-${{ github.sha }}
path: dist/
- run: ./scripts/deploy-staging.sh dist/In a real project, the deployment job would authenticate to the hosting platform using a short-lived identity or tightly scoped secret. It should deploy the artifact produced by the tested commit, rather than rebuilding from an ambiguous branch state. Tests should be layered. Unit tests are fast and isolate functions or components. Integration tests examine interactions with databases, queues, or services. End-to-end tests exercise user journeys and are valuable but often slower and more brittle. A balanced pipeline uses fast checks on every change and schedules or selectively triggers expensive tests when appropriate. 5. Environments, Secrets, and PermissionsEnvironmentsGitHub environments represent deployment targets such as staging and production. They can provide environment-specific secrets, protection rules, required reviewers, and deployment history. A production environment can therefore act as a governance boundary rather than merely a label. A sensible promotion model is: - build once from a known commit;
- deploy the same immutable artifact to staging;
- run smoke or acceptance checks;
- obtain required approval;
- promote that artifact to production;
- monitor and retain rollback information.
SecretsSecrets include cloud credentials, signing keys, database passwords, and API tokens. Store them in GitHub repository, organization, or environment secrets rather than in source code. Avoid printing them; masking reduces accidental display but is not a substitute for careful command design. Secrets should be least-privilege, rotated, monitored, and scoped to the smallest environment and operation possible. Open pull requests from forks generally should not receive repository secrets. A safer alternative for cloud deployment is OpenID Connect (OIDC), in which the workflow obtains a short-lived cloud credential based on an identity assertion instead of storing a long-lived cloud key. PermissionsThe permissions block controls the GITHUB_TOKEN capabilities. A strong default is to grant no unnecessary permissions and explicitly enable only those required: permissions:
contents: read
packages: write A job can narrow permissions further. Write access should be reserved for jobs that genuinely create releases, publish packages, or update deployment state. A compromised dependency or action can abuse whatever authority the job possesses, so permissions are part of the threat model. 6. Reliability, Security, and CostReliable workflow designReliable workflows are deterministic, observable, and recoverable. Important practices include: - pin runtime versions and use lockfiles;
- make scripts fail on errors and avoid silently ignoring commands;
- use timeouts for jobs that could hang;
- separate transient network failures from reproducible test failures;
- retry only idempotent operations and avoid hiding defects with unlimited retries;
- publish test reports and logs that explain failure;
- use concurrency controls to cancel obsolete pull-request runs;
- design deployment scripts to be idempotent, so repeating them converges on the desired state;
- provide a tested rollback procedure.
For pull requests, concurrency can reduce waste: concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: trueCancellation is appropriate for superseded validation runs, but it should not interrupt a production deployment without deliberate safeguards. Security risksCI/CD systems have a high concentration of privileges. Threats include malicious pull requests, dependency compromise, action compromise, secret leakage, artifact tampering, and unsafe shell interpolation. Avoid constructing shell commands directly from untrusted issue titles, branch names, or user input. Quote variables and validate allowed values. Use dependency review, lockfiles, software composition analysis, secret scanning, code scanning, and signed or attestable build outputs where the project’s risk justifies them. Review workflow changes as security-sensitive code. A workflow file can alter what code executes and what credentials it can access. Cost and performanceCost is influenced by runner duration, matrix size, frequency, operating-system choice, artifact storage, and self-hosted infrastructure. Performance improvements should preserve coverage and trust. Common approaches include caching dependencies, parallelizing independent jobs, using path filters carefully, selecting a smaller matrix for pull requests, and running comprehensive compatibility tests on a schedule. A useful engineering measure is not merely elapsed pipeline time but time to trustworthy feedback. A fast pipeline that omits essential checks may increase downstream debugging and incident costs. 7. Releases and Deployment StrategiesA release workflow may be triggered by a version tag, a published release, or a manual dispatch with a validated version input. It can generate release notes, package binaries, publish a container image, and attach artifacts. Versioning rules should be documented and automated consistently; the release should identify the exact commit and build inputs. Deployment strategies include: - Rolling deployment: replace instances gradually. It uses resources efficiently but may expose mixed application versions.
- Blue-green deployment: maintain two environments and switch traffic. Rollback is rapid, but the infrastructure cost is higher.
- Canary deployment: send a small percentage of traffic to the new version before wider promotion. It requires traffic control and meaningful monitoring.
- Feature flags: deploy code while controlling feature exposure. Flags require lifecycle management and can create complexity.
The deployment mechanism must match the application’s data-migration constraints. A database migration that is incompatible with the previous application version can make rollback unsafe. Expand-and-contract migrations—adding compatible schema elements, deploying code, migrating usage, then removing obsolete elements—reduce this risk. 8. Illustrative Case: CampusMarketBackgroundCampusMarket is an illustrative online marketplace developed by a student team. It has a React frontend, a Node.js API, a PostgreSQL database, and a containerized deployment to a cloud platform. The team previously tested locally and copied files manually to a server. ProblemReleases were inconsistent. A developer occasionally forgot to build the frontend, tests were run against different Node.js versions, and production credentials had been stored in a shared script. A failed deployment was difficult to reverse. AnalysisThe main risks were not only defective code. They included non-reproducible environments, excessive credential exposure, lack of artifact identity, and no promotion or rollback model. The team decided that every pull request must receive quality feedback, while only the protected main branch could produce a staging deployment. Approach and solutionThe team implemented these controls: - A pull-request workflow runs formatting, linting, unit tests, API integration tests, and a small Node.js compatibility matrix.
npm ci and a committed lockfile make dependency installation reproducible.- A build job runs only after quality checks and uploads an artifact named with the commit SHA.
- The staging environment receives the artifact, not a fresh rebuild.
- Production requires a protected environment with an approved reviewer.
- Cloud authentication uses OIDC and a role restricted to the application deployment.
- Deployment scripts include health checks and retain the previous artifact for rollback.
- Concurrency cancels obsolete pull-request runs, while production deployments are never casually cancelled.
- Logs, test reports, and deployment records are retained according to the project’s operational needs.
OutcomeThe team gained rapid feedback and a traceable relationship between source commit, test results, artifact, and deployed version. The pipeline did not eliminate defects, but it made failures earlier, more visible, and easier to recover from. The team also discovered that a slow end-to-end suite was unsuitable for every matrix combination; it was moved to staging smoke validation and scheduled nightly coverage. Learning pointsThe case demonstrates that automation is socio-technical. YAML alone did not solve the problem. The team had to define what constituted acceptable evidence, protect credentials, establish ownership for approvals, and test rollback. Continuous delivery is therefore both an engineering architecture and a governance practice. 9. Governance, Ethics, and Contemporary PracticeAutomation affects developers, operations staff, users, and sometimes the public. Automated rejection should be explainable through logs and reports. Teams should avoid treating a green pipeline as an unquestionable judgment: tests encode assumptions and can omit accessibility, privacy, security, or social impacts. Deployment records and logs may contain personal data, tokens, user identifiers, or proprietary code. Retention should be proportionate to operational and legal requirements. Access should follow least privilege. Organizations should define who may approve production changes, who responds to failures, and how emergency changes are recorded. Contemporary practices increasingly include infrastructure as code, policy as code, artifact attestations, provenance tracking, dependency automation, ephemeral preview environments, and AI-assisted code generation. AI-generated workflow or application code still requires human review: generated configurations may overgrant permissions, misuse secrets, omit failure handling, or create insecure shell commands. AI can assist with test generation and log summarization, but responsibility for release decisions remains with the engineering organization. Challenges and LimitationsCI/CD has important limits. Flaky tests reduce trust and may encourage unsafe reruns. Hosted runners can fail because of external service outages. Secrets and third-party actions create supply-chain exposure. Large matrices increase cost and delay. End-to-end tests may be difficult to maintain. Deployment automation cannot compensate for unclear requirements, poor observability, incompatible data migrations, or an organization that lacks ownership of production systems. The correct response is not to automate everything indiscriminately. Automate repeatable, valuable controls; measure failure modes; review exceptions; and keep humans involved where judgment, risk acceptance, or ethical accountability is required. Best-Practice Checklist- Keep workflow files under version control and review them as privileged code.
- Use explicit triggers, permissions, environments, and dependency relationships.
- Pin runtimes and lock dependencies.
- Prefer least privilege and short-lived credentials.
- Do not expose secrets to untrusted pull-request code.
- Build once and promote an identifiable artifact.
- Use matrices selectively and caches only as performance optimizations.
- Publish useful reports and retain appropriate evidence.
- Make deployment and rollback procedures repeatable.
- Monitor production and connect deployment records to incidents.
- Review third-party actions and update pinned versions deliberately.
Chapter SummaryContinuous integration provides rapid, automated evidence that changes integrate with the shared codebase. Continuous delivery keeps tested software ready for release, while continuous deployment releases qualifying changes automatically. GitHub Actions implements these ideas through event-triggered workflows containing jobs, steps, actions, runners, matrices, artifacts, environments, and reusable components. A professional workflow is more than a sequence of commands. It defines dependencies, permissions, secrets, trust boundaries, reproducibility, observability, cost controls, deployment approvals, and rollback behaviour. Matrices increase compatibility coverage; artifacts preserve tested outputs; caches improve speed without becoming sources of truth; environments support controlled promotion; and OIDC can reduce long-lived credential exposure. Effective automation improves feedback and reliability while recognizing that tests are evidence, not proof, and that governance remains essential. Key TermsContinuous integration: Frequent integration of changes with automated build and test validation. Continuous delivery: Keeping software in a releasable state through automated preparation and verification. Continuous deployment: Automatically deploying qualifying changes to production. Workflow: A YAML-defined GitHub Actions automation process. Event: A repository or schedule occurrence that starts a workflow. Job: A group of steps executed on a runner. Step: An individual command or action invocation within a job. Runner: The machine or environment that executes a job. Action: A reusable packaged automation component. Matrix: A set of parameter combinations used to run a job repeatedly. Artifact: A retained file produced or consumed by a workflow. Cache: Stored reusable data intended to accelerate later runs. Environment: A named deployment target with protection rules and configuration. Secret: Protected sensitive configuration such as a token or password. OIDC: An identity mechanism that can provide short-lived cloud credentials to workflows. Least privilege: Granting only the access required for a task. Idempotence: A property in which repeating an operation produces the same intended state. Canary deployment: Gradually exposing a new version to a limited portion of traffic. Reusable workflow: A workflow designed to be invoked by other workflows. Rollback: Returning a service to a previously known-good version. Review QuestionsShort Answer Questions- What is the principal difference between continuous delivery and continuous deployment?
- Why should a cache not be treated as an authoritative build output?
- What purpose does the
needs property serve in a GitHub Actions workflow? - Why are repository workflow files considered security-sensitive code?
- State two advantages and one risk of using a matrix strategy.
Long Answer Questions- Explain how events, jobs, steps, runners, and actions combine to execute a GitHub Actions workflow.
- Describe a secure promotion pipeline from a pull request to production, including artifacts and environments.
- Compare hosted and self-hosted runners in terms of control, maintenance, security, and cost.
- Explain how secrets, permissions, OIDC, and pull-request trust boundaries affect workflow design.
- Discuss why testing, observability, rollback, and database migration strategy must be considered together in continuous delivery.
Analytical / Application Questions- A team has a four-operating-system by three-runtime matrix that takes several hours and frequently queues. Propose a revised pull-request and scheduled-testing strategy, and justify the trade-offs.
- A workflow triggered by
pull_request_target checks out the contributor’s branch and uses a production deployment token. Identify the security risks and redesign the workflow. - Design a GitHub Actions pipeline for a frontend and API that builds once, deploys to staging, requires production approval, and supports rollback. Specify job dependencies, artifacts, permissions, and environments.
Multiple Choice Questions with AnswersQuestion 1Which statement best describes continuous delivery? A. Every commit is automatically deployed to production without approval B. Software is kept in a releasable state, with release authorization possibly remaining manual C. Developers never integrate changes until a feature is complete D. Only infrastructure code is tested automatically Correct Answer: B. Software is kept in a releasable state, with release authorization possibly remaining manual Question 2What is the main purpose of needs in a GitHub Actions job? A. To install a programming language B. To define an artifact retention period C. To establish a dependency relationship between jobs D. To encrypt repository secrets Correct Answer: C. To establish a dependency relationship between jobs Question 3Which practice provides the strongest protection against a mutable third-party action tag changing unexpectedly? A. Using the latest tag without review B. Pinning the action to a full commit SHA C. Copying the action name into a README D. Running the action only on weekends Correct Answer: B. Pinning the action to a full commit SHA Question 4What is a GitHub Actions artifact? A. A repository branch protection rule B. A retained file produced or consumed by a workflow C. A runner operating system D. A secret used for cloud authentication Correct Answer: B. A retained file produced or consumed by a workflow Question 5Why is pull_request_target potentially dangerous when combined with checking out and executing untrusted code? A. It disables all workflow logs B. It always runs on a self-hosted runner C. It may provide the base repository’s elevated context or secrets to untrusted code D. It prevents tests from running Correct Answer: C. It may provide the base repository’s elevated context or secrets to untrusted code Question 6A matrix has 2 operating systems and 3 runtime versions, with no exclusions. How many job combinations are created? A. 2 B. 3 C. 5 D. 6 Correct Answer: D. 6 Question 7Which approach best supports deploying exactly the version that was tested? A. Rebuild from the current branch during production deployment B. Build once, upload an identifiable artifact, and promote that artifact C. Allow each developer to copy files manually D. Use an unrestricted dependency installation command Correct Answer: B. Build once, upload an identifiable artifact, and promote that artifact Question 8What is the principle of least privilege? A. Giving every job administrator access to avoid failures B. Granting only the permissions required for a task C. Allowing permissions to increase automatically over time D. Disabling all authentication in staging Correct Answer: B. Granting only the permissions required for a task Question 9Which deployment strategy gradually exposes a new version to a limited percentage of traffic? A. Canary deployment B. Big-bang deployment C. Source checkout D. Dependency caching Correct Answer: A. Canary deployment Question 10Which statement about workflow caching is correct? A. A build must fail whenever a cache is unavailable B. Caches should replace lockfiles C. Caching is an optimization, and the workflow should remain correct without the cache D. Caches should contain production secrets Correct Answer: C. Caching is an optimization, and the workflow should remain correct without the cache
|