// The server deployment kit: how a turnkey reaches a customer's own server and // leaves it again. install.sh and uninstall.sh are product-independent (they // read the product from release.env and its launch parameters from the stored // template), so they are canonical copies of conventions/server-*.sh. What is // this product's is the image the release packs. import type { ShapeContext } from "./create-product.ts"; export function packageReleaseSh(ctx: ShapeContext): string { return `#!/usr/bin/env bash # Build a release for a server: the product image as a portable tar, install.sh # and uninstall.sh, and release.env naming what they install. The image is in # no registry; the server pulls the media and Studio images and norsk-ctl # itself. # # bun run package-release [-- ] # default release/ # SKIP_BUILD=1 IMAGE_TAG= bun run package-release # an existing image # # Then copy the directory to the server and run install.sh there (its header # says how), e.g. # tar -C release -czf - . | ssh 'mkdir -p ${ctx.name}-release && tar -C ${ctx.name}-release -xzf -' set -euo pipefail repo="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)" out="\${1:-\${repo}/release}" cd "\${repo}" commit="$(git describe --always --dirty)" # Tagged by commit: install.sh tells releases apart by tag, and a reused tag # (\`:dev\`) would leave a server on the build it already has. The licence names # the repository, not the tag. export IMAGE_TAG="\${IMAGE_TAG:-${ctx.name}:\${commit}}" [ -n "\${SKIP_BUILD:-}" ] || bun run build:image # The tested norsk-ctl is the one the dev shell pins. ctl_version="$(sed -n 's/^ *ctlVersion = "\\(.*\\)";/\\1/p' flake.nix)" [ -n "\${ctl_version}" ] || { echo "no ctlVersion in flake.nix" >&2 exit 1 } rm -rf "\${out}" mkdir -p "\${out}" docker save "\${IMAGE_TAG}" | gzip -1 >"\${out}/image.tar.gz" cp deployment/install.sh deployment/uninstall.sh "\${out}/" { printf 'PRODUCT=%q\\n' ${ctx.name} printf 'IMAGE=%q\\n' "\${IMAGE_TAG}" printf 'IMAGE_ARCH=%q\\n' "$(docker image inspect -f '{{.Architecture}}' "\${IMAGE_TAG}")" printf 'IMAGE_TAR=%q\\n' image.tar.gz printf 'CTL_VERSION=%q\\n' "\${ctl_version}" printf 'RELEASE_COMMIT=%q\\n' "\${commit}" } >"\${out}/release.env" echo "release in \${out}" `; } export function shipImageSh(ctx: ShapeContext): string { return `#!/usr/bin/env bash # Build the ${ctx.name} image and load it into a remote host's Docker, for a # host that cannot pull it (the image is in no registry). norsk-ctl on that host # then adds and launches it from the local copy: \`product add\` and the launch # both treat a failed pull as "use the local image". For a customer's server, # package-release.sh is the whole kit; this is the quick route for a dev box. # # bun run ship-image -- # build, then ship # SKIP_BUILD=1 bun run ship-image -- # ship the existing image set -euo pipefail host="\${1:?usage: ship-image.sh }" export IMAGE_TAG="\${IMAGE_TAG:-${ctx.name}:dev}" cd "$(dirname "\${BASH_SOURCE[0]}")/.." [ -n "\${SKIP_BUILD:-}" ] || bun run build:image # An image built for another architecture loads without complaint and then # fails at launch, so refuse it here. local_arch="$(docker image inspect -f '{{.Architecture}}' "\${IMAGE_TAG}")" case "$(ssh "\${host}" uname -m)" in x86_64) remote_arch=amd64 ;; aarch64 | arm64) remote_arch=arm64 ;; *) remote_arch=unknown ;; esac if [ "\${local_arch}" != "\${remote_arch}" ]; then echo "\${IMAGE_TAG} is \${local_arch} but \${host} is \${remote_arch}: build on a \${remote_arch} machine" >&2 exit 1 fi echo "shipping \${IMAGE_TAG} to \${host}" docker save "\${IMAGE_TAG}" | gzip | ssh "\${host}" 'gunzip | docker load' echo "loaded. On \${host}: norsk-ctl product add --image \${IMAGE_TAG} --license-file " `; } export const SERVER_KIT_GITIGNORE = `# A release (bun run package-release): the image tar and the scripts, built for # a server, never source. release/ `;