Logo

Your Personal Sysadmin

Deployment scenario: NextJs Static site, separate content repo

This scenario describes a static site being built from two separate repositories, one containing a NextJS based source and a second content repository containing media files, text and necessary configuration / glue files.

A common use case is a static site being rebuilt based on content added through a repository interface like github or gitlab. this is a simpler equivalent to a full blown cms. like so the future content editor only needs to be trained to interface with a common and widely used and supported system.

Infrastructure parts

  • client repository site
  • client repository content
  • mirror repository site
  • mirror repository content
  • ci system
  • container registry
  • web server

Deployment pipeline

App updates

  1. commit is pushed to client app repo
  2. client app repo is pushed to mirror app repo
  3. ci executes pipeline on mirror app repo
    1. clones app code from mirror
    2. clones content from mirror
    3. builds container image with web server
      1. copy files into container
      2. builds site and merges content
      3. configures webserver
    4. pushes image to registry
    5. triggers deploy on infrastructure
      1. pulls container image
      2. restarts service

Content updates

  1. commit is pushed to client content repo
  2. client content repo is pushed to mirror content repo
  3. ci executes pipeline on mirror content repo
    1. clones mirror app repo
    2. updates version file with commit hash (stage) or tag (prod) from content repo
    3. commits change and pushes to client app repo, thereby triggering a run of the App Update pipeline above

Configuration files

  • ci pipeline .woodpecker.yml on the site repo
  • example deploys the built artefact to a caprover-managed server and notifies via a telegram bot
steps:
  clone_content_repo:
    image: ubuntu
    secrets: [ssh_pubkey_with_read_access]
    commands:
      - apt-get update -y && apt-get install openssh-client git git-lfs -y
      - eval $(ssh-agent -s)
      - echo "$ssh_pubkey_with_read_access" | tr -d '\r' | ssh-add -
      - mkdir -p ~/.ssh
      - chmod 700 ~/.ssh
      - ssh-keyscan -p9235 git_server > ~/.ssh/known_hosts
      - env
      - git clone --depth=1 ssh://git@git_server/${CI_REPO}-content content
      - cd content
      - git lfs pull
      - rm -rf .git
      - cd ..
      - rm -rf .git
    when:
      - event: push
        branch: [master, main]
      - event: tag

  build_stage_image:
    image: woodpeckerci/plugin-docker-buildx
    settings:
      registry: package_server
      repo: git_server/${CI_REPO}
      tag: ${CI_COMMIT_SHA}
      username: wellhoster
      password:
        from_secret: gitea_pass
    when:
      - event: push
        branch: [master, main]
        status: success

  build_prod_image:
    image: woodpeckerci/plugin-docker-buildx
    settings:
      registry: package_server
      repo: git_server/${CI_REPO}
      tag: ${CI_COMMIT_TAG}
      username: wellhoster
      password:
        from_secret: gitea_pass
    when:
      - event: tag
        branch: [master, main]
        status: success

  deploy_prod_site:
    image: caprover/cli-caprover:v2.1.1
    environment:
      - CAPROVER_URL=https://caprover_domain
      - CAPROVER_APP=appname
      - "CONTAINER_FULL_IMAGE_NAME_WITH_TAG=package_server/${CI_REPO}:${CI_COMMIT_TAG}"
    secrets: [ caprover_pass ]
    commands:
      - caprover deploy --caproverUrl $CAPROVER_URL --caproverPassword $CAPROVER_PASS --caproverApp $CAPROVER_APP --imageName $CONTAINER_FULL_IMAGE_NAME_WITH_TAG
    when:
      - event: tag
        status: success

  deploy_stage_site:
    image: caprover/cli-caprover:v2.1.1
    environment:
      - CAPROVER_URL=https://caprover_domain
      - CAPROVER_APP=appname
      - "CONTAINER_FULL_IMAGE_NAME_WITH_TAG=package_server/${CI_REPO}:${CI_COMMIT_SHA}"
    secrets: [ caprover_pass ]
    commands:
      - caprover deploy --caproverUrl $CAPROVER_URL --caproverPassword $CAPROVER_PASS --caproverApp $CAPROVER_APP --imageName $CONTAINER_FULL_IMAGE_NAME_WITH_TAG
    when:
      - event: push
        branch: [master, main]
        status: success

  notify:
    image: appleboy/drone-telegram
    settings:
      token:
        from_secret: telegram_token
      to: 393273328
      message: >
        
          ✅ Building `${CI_REPO_NAME}` triggered by `${CI_PIPELINE_EVENT}`
          

            This went to prod.
          

          📝 Commit by ${CI_COMMIT_AUTHOR} on `${CI_COMMIT_BRANCH}`:

          `${CI_COMMIT_MESSAGE}`
        
          ❌ Build ${CI_PIPELINE_EVENT} of `${CI_REPO_NAME}` has status `${CI_PIPELINE_STATUS}`.
          

            This tried to go to prod.
          

          📝 Commit by ${CI_COMMIT_AUTHOR} on `${CI_COMMIT_BRANCH}`:

          `${CI_COMMIT_MESSAGE}`
        
    when:
      event:
        - tag
        - push
      status:
        - success
        - failure

Hey! I'll happily receive your comments via email

Andreas Wagner
Freelance System Administrator from Tallinn, Estonia.