# cratly — publish a built site to S3-compatible storage
#
# Canonical URL:  https://cratly.io/ci/v1/deploy-s3.yml
# Documentation:  https://cratly.io/reference/pipeline
# Licence:        MIT
#
# Required masked CI/CD variables:
#   S3_ENDPOINT    service endpoint URL, e.g. https://s3.example.com
#   S3_ACCESS_KEY  access key with read/write permission on the bucket
#   S3_SECRET_KEY  matching secret key
#
# Object storage has no directories and no rename, so there is no staging step
# to be had: each object is replaced individually and a visitor arriving
# mid-deploy can see a mix of both versions. Files are uploaded before the
# removals run, which keeps that window on the harmless side — a page may be
# stale, but its assets are already there.

spec:
  inputs:
    job_name:
      default: "deploy"
    stage:
      default: "deploy"
    needs:
      type: array
      default: [ "build" ]
      description: "Job producing the artifact — the build job's name."
    image:
      default: "quay.io/minio/mc:latest"
      description: >-
        MinIO publishes the client on quay.io; the minio/* repositories on
        Docker Hub are gone. Any image providing `mc` will do.
    artifact_dir:
      default: "site"
      description: "Directory the build job stored the site in."
    bucket:
      description: "Bucket name only, no s3:// prefix."
    prefix:
      default: ""
      description: >-
        Key prefix inside the bucket. Empty means the bucket root — see `remove`
        before leaving it empty.
    remove:
      default: "true"
      options: [ "true", "false" ]
      description: >-
        Delete objects at the target that the build no longer contains. With an
        empty `prefix` this prunes the whole bucket, so only leave both at their
        default when the bucket holds nothing but this site.
    branch:
      default: "$CI_DEFAULT_BRANCH"
      description: "Only deploy commits on this branch."
    environment:
      default: "production"

---

"$[[ inputs.job_name ]]":
  stage: $[[ inputs.stage ]]
  image:
    name: $[[ inputs.image ]]
    entrypoint: [ "" ]
  needs: $[[ inputs.needs ]]
  # A half-finished publication is worse than a slow one: never cancel a deploy.
  interruptible: false
  script:
    - |
      set -eu

      mc alias set deploy "$S3_ENDPOINT" "$S3_ACCESS_KEY" "$S3_SECRET_KEY"

      TARGET="deploy/$[[ inputs.bucket ]]"
      if [ -n "$[[ inputs.prefix ]]" ]; then
        TARGET="$TARGET/$[[ inputs.prefix ]]"
      elif [ "$[[ inputs.remove ]]" = "true" ]; then
        echo "note: mirroring to the bucket root with remove=true — every object in"
        echo "      $[[ inputs.bucket ]] that this build does not contain will be deleted."
      fi

      REMOVE=""
      if [ "$[[ inputs.remove ]]" = "true" ]; then REMOVE="--remove"; fi

      echo "Mirroring $[[ inputs.artifact_dir ]]/ → $TARGET"
      mc mirror --overwrite $REMOVE "$[[ inputs.artifact_dir ]]/" "$TARGET"
  environment:
    name: $[[ inputs.environment ]]
  rules:
    - if: $CI_COMMIT_BRANCH == "$[[ inputs.branch ]]"
