~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
123 lines · 5.8 KB Raw History
# Builds the PDS container image on a version tag, and publishes it as the
# `pds.js` release of this repository, which knotless.dev serves as
# `docker pull knotless.dev/chadtmiller.com/pds.js/pds.js:<version>`. It
# copies it to the pdsjs.dev PDS's own registry (@pdsjs/oci) too.
#
# The runner gives a step no docker daemon, so kaniko builds the image. Once
# a build ends, kaniko leaves the image's filesystem at /, and
# scripts/run-image.mjs runs the smoke test in it as the image would run.
# The image goes up as the OCI layout kaniko writes, with `knotless publish
# --oci`, as the repository's owner: RELEASE_PASSWORD is an app password of
# theirs, sealed to ci.knotless.dev for v* tags.
#
# Asked for at a branch, `knotless rerun knotless://chadtmiller.com/pds.js
# --ref refs/heads/<branch> --workflow release-image`, it builds and tests
# the image and publishes nothing.
on:
  push: {refs: [refs/tags/v*]}
  asked: {refs: [refs/heads/*]}
superseded: finish
image: buildpack-deps:trixie-curl
runners: [ci.knotless.dev]
secrets: [RELEASE_PASSWORD, PDSJS_SITE_TOKEN]
env:
  REPO: knotless://chadtmiller.com/pds.js
  PACKAGE: pds.js
  DOCKERFILE: Dockerfile
  KNOTLESS_APPVIEW: https://knotless.dev
  PDS_HOST: pds.pdsjs.dev
  CRANE_VERSION: v0.20.2
  KANIKO_IMAGE: gcr.io/kaniko-project/executor:v1.23.2
  # Where kaniko's own image keeps them. A build replaces the rest of /.
  SSL_CERT_DIR: /kaniko/ssl/certs
  DOCKER_CONFIG: /kaniko/.docker
steps:
  - name: check credentials
    run: |
      case "$KNOTLESS_RUN_REF" in
        refs/tags/v*)
          if [ -z "${RELEASE_PASSWORD:-}" ]; then
            echo "RELEASE_PASSWORD is not granted, so this tag would publish nothing."
            echo "Seal it to ci.knotless.dev with knotless secret. It is an app password for the repository's owner."
            exit 1
          fi ;;
        *) echo "at $KNOTLESS_RUN_REF: building and testing, publishing nothing" ;;
      esac
  # kaniko's own image starts kaniko, not the command the runner keeps a
  # container up with, so the steps take kaniko from its image. They run in
  # Debian, whose /sbin is a link as in the images built: the runner mounts
  # docker-init under /sbin, and a build cannot replace a directory that
  # holds a mount with a link. The tools go under /kaniko, which a build
  # leaves alone.
  - name: tools
    run: |
      mkdir -p /kaniko/tools
      cd /kaniko/tools
      curl -fsSL "https://github.com/google/go-containerregistry/releases/download/$CRANE_VERSION/go-containerregistry_Linux_x86_64.tar.gz" | tar -xzf - crane
      ./crane export "$KANIKO_IMAGE" - | tar -xf - -C / kaniko
      /kaniko/executor version
      v=$(curl -fsSL https://knotless.dev/releases/latest)
      name="knotless-$v-x86_64-unknown-linux-musl"
      curl -fsSLO "https://knotless.dev/releases/$v/$name.tar.gz"
      curl -fsSL -o sum "https://knotless.dev/releases/$v/$name.tar.gz.sha256"
      [ "$(sha256sum "$name.tar.gz" | cut -d' ' -f1)" = "$(cut -d' ' -f1 sum)" ]
      tar -xzf "$name.tar.gz"
      mv "$name/knotless" knotless
  - name: build
    timeout: 45
    run: |
      # kaniko builds over the files of the image the steps run in, and
      # removes them only between stages. A first stage of nothing makes it
      # remove them before the Dockerfile's own stages.
      { echo 'FROM scratch AS empty'; cat "$DOCKERFILE"; } > /kaniko/Dockerfile
      /kaniko/executor --force --context "dir://$PWD" --dockerfile /kaniko/Dockerfile \
        --no-push --oci-layout-path /kaniko/image --snapshot-mode redo
      cat /kaniko/image/index.json
  # A tagged image that cannot boot is worse than no image. Every variable
  # the entrypoint demands is set here: a new one in docker/server.js fails
  # this test until it is added here too.
  - name: smoke test
    run: |
      node scripts/run-image.mjs /kaniko/image \
        PDS_HOSTNAME=pds.example.com JWT_SECRET=smoke-test-secret \
        PDS_PASSWORD=smoke-test-password PDS_ENABLE_SPACES=true > /tmp/server.log 2>&1 &
      server=$!
      health='fetch("http://127.0.0.1:3000/xrpc/_health").then(r=>process.exit(r.ok?0:1),()=>process.exit(1))'
      for _ in $(seq 1 30); do
        if node -e "$health"; then
          kill "$server"
          echo "the image answers its health check"
          exit 0
        fi
        kill -0 "$server" 2>/dev/null || break
        sleep 1
      done
      tail -30 /tmp/server.log
      kill "$server" 2>/dev/null || true
      exit 1
  - name: publish
    run: |
      case "$KNOTLESS_RUN_REF" in refs/tags/v*) ;; *) exit 0 ;; esac
      version="${KNOTLESS_RUN_REF#refs/tags/v}"
      KNOTLESS_PASSWORD="$RELEASE_PASSWORD" /kaniko/tools/knotless publish "$REPO" "$PACKAGE" "$version" --oci /kaniko/image
  # A copy, like a mirror: it never fails a release on its own. A PDS
  # without the feature answers /v2/ with 404 rather than the challenge.
  - name: copy to the PDS
    run: |
      case "$KNOTLESS_RUN_REF" in refs/tags/v*) ;; *) exit 0 ;; esac
      if [ -z "${PDSJS_SITE_TOKEN:-}" ]; then
        echo "PDSJS_SITE_TOKEN is not granted, so the PDS copy is skipped"
        exit 0
      fi
      status=$(node -e "fetch('https://$PDS_HOST/v2/').then((r) => console.log(r.status)).catch(() => console.log(0))")
      if [ "$status" != "401" ]; then
        echo "https://$PDS_HOST/v2/ answered $status, not the challenge, so the PDS registry is off"
        exit 0
      fi
      tag="${KNOTLESS_RUN_REF#refs/tags/}"
      crane=/kaniko/tools/crane
      echo "$PDSJS_SITE_TOKEN" | $crane auth login "$PDS_HOST" -u pdsjs.dev --password-stdin
      if ! $crane push /kaniko/image "$PDS_HOST/$PACKAGE:$tag" || ! $crane tag "$PDS_HOST/$PACKAGE:$tag" latest; then
        echo "the PDS refused the push. A layer can be over the platform's request limit"
        echo "(100 MB on the lowest Cloudflare plan). The release stands on knotless."
      fi