Continuous delivery sample

DevOps is awesome.

A small static site, shipped the way production software should be: built, tested and deployed automatically on every change.

From commit to production

Every merge to master moves through the same four stages.

  1. 01

    Build

    Gradle validates the project and packages the site as a build artifact.

  2. 02

    Test

    Static checks confirm the required files exist and the markup is valid.

  3. 03

    Stage

    The artifact is published to a staging host for a final manual review.

  4. 04

    Release

    After approval the same artifact is promoted to production over SSH.

Architecture

Two pipelines share one repository: GitHub Actions validates every pull request and publishes this site, while Jenkins promotes the packaged artifact to staging and production.

Delivery architecture A developer opens a pull request on GitHub. GitHub Actions builds and validates it, then publishes the site to GitHub Pages after merge. Jenkins builds the artifact, deploys to staging over SSH, waits for manual approval and deploys to production. Developerfeature branch GitHubpull request + review GitHub Actionsbuild, validate HTML GitHub Pagesthis website JenkinsGradle build, artifact Staging (SSH) Production (SSH) manual approval on PR + push
Pull requests run through GitHub Actions. Merges to master publish the site and start the Jenkins release.

Under the hood

The configuration that drives the pipeline, trimmed to the essentials.

build.gradle
tasks.register('verifySite') {
    doLast {
        def missing = requiredFiles.findAll {
            !siteDir.file(it).asFile.exists()
        }
        if (missing) {
            throw new GradleException(
                "Missing site files: ${missing.join(', ')}")
        }
    }
}

tasks.register('siteZip', Zip) {
    from siteDir
    destinationDirectory =
        layout.buildDirectory.dir('distributions')
}
Jenkinsfile
stage('Deploy to production') {
    when { branch 'master' }
    steps {
        input message: 'Does the staging environment look OK?'
        milestone(1)
        deployTo('production')
    }
}
.github/workflows/ci.yml
- uses: gradle/actions/setup-gradle@v4

- name: Build and verify
  run: ./gradlew build --no-daemon

- name: Validate HTML
  run: npx --yes html-validate@8 src/index.html
$ ./gradlew build

> Task :verifySite
> Task :siteZip
> Task :assemble
> Task :check
> Task :build

BUILD SUCCESSFUL
Example output of a local build

Practices behind it

Pull request workflow

Changes land through reviewed pull requests, and the default branch stays releasable.

Build once, deploy many

One artifact is promoted through each environment, so what was tested is what ships.

Manual approval gate

Production releases wait for an explicit sign-off after staging is verified.

Secrets stay out of code

Credentials live in the CI credential store and are never committed to the repository.

Tooling

Technologies used
AreaToolRole
BuildGradle 8.10.2Verifies the site and packages it as a zip
RuntimeJDK 17 (Temurin)Runs Gradle locally and in CI
CIGitHub ActionsBuild, HTML validation, Pages deploy
ReleaseJenkinsStaging, approval gate, production
TransportPublish over SSHCopies the site to each environment
FrontendHTML, CSS, vanilla JSNo framework or build step required