commit 81b7bbeae26b1ab361025495c56bac2afc8042ac
parent dd31eb76fe236f4b5eceb6f17e648b4abb0226eb
Author: Joris Hartog <jorishartog@hotmail.com>
Date: Thu, 13 Aug 2026 09:37:35 +0200
Improve deployment script
Diffstat:
| M | deployment.sh | | | 69 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- |
1 file changed, 64 insertions(+), 5 deletions(-)
diff --git a/deployment.sh b/deployment.sh
@@ -1,6 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
+cd "$(dirname "${BASH_SOURCE[0]}")"
+
usage() {
echo "Usage: $0 <version>" >&2
echo "Example: $0 0.2.48" >&2
@@ -11,6 +13,25 @@ die() {
exit 1
}
+require_command() {
+ local command_name="$1"
+
+ command -v "$command_name" >/dev/null 2>&1 || die "missing required command: ${command_name}"
+}
+
+confirm() {
+ local prompt="$1"
+ local answer
+
+ if ! read -r -p "$prompt" answer || [[ ! "$answer" =~ ^[Yy]$ ]]; then
+ return 1
+ fi
+}
+
+escape_sed_replacement() {
+ printf '%s' "$1" | sed -e 's/[\/&]/\\&/g'
+}
+
replace_in_file() {
local file="$1"
local pattern="$2"
@@ -19,12 +40,26 @@ replace_in_file() {
}
ensure_clean_worktree() {
+ require_command git
+
if ! git diff --quiet || ! git diff --cached --quiet || [ -n "$(git ls-files --others --exclude-standard)" ]; then
die "worktree is not clean; commit or stash changes before releasing"
fi
}
+ensure_head_matches_tag() {
+ local tag="$1"
+ local head_commit
+ local tag_commit
+
+ head_commit="$(git rev-parse HEAD)"
+ tag_commit="$(git rev-parse "${tag}^{commit}")"
+ [ "$head_commit" = "$tag_commit" ] || die "${tag} exists, but HEAD is not at ${tag}; checkout ${tag} before redeploying it"
+}
+
ensure_tauri_cli() {
+ require_command cargo
+
if ! cargo tauri --version >/dev/null 2>&1; then
cargo install tauri-cli --locked --version "^2"
fi
@@ -33,6 +68,9 @@ ensure_tauri_cli() {
update_versions() {
local version="$1"
+ require_command cargo
+ require_command perl
+
replace_in_file Cargo.toml '(\[package\]\nname = "iuna"\nversion = ")[^"]+' "\${1}${version}"
replace_in_file src-tauri/Cargo.toml '(\[package\]\nname = "iuna-desktop"\nversion = ")[^"]+' "\${1}${version}"
replace_in_file src-tauri/tauri.conf.json '("version": ")[^"]+' "\${1}${version}"
@@ -49,6 +87,8 @@ commit_and_tag() {
local version="$1"
local tag="v${version}"
+ require_command git
+
git add Cargo.toml Cargo.lock src-tauri/Cargo.toml src-tauri/Cargo.lock src-tauri/tauri.conf.json README.md
git commit -m "Release ${tag}"
git tag -a "$tag" -m "Release ${tag}"
@@ -62,6 +102,8 @@ build_macos_desktop_if_possible() {
[ "$(uname -s)" = "Darwin" ] || return 0
[ "$(uname -m)" = "arm64" ] || die "macOS desktop artifact requires Apple silicon; expected ${artifact}"
+ require_command codesign
+ require_command ditto
ensure_tauri_cli
cargo build --release --locked
mkdir -p src-tauri/binaries downloads
@@ -178,6 +220,8 @@ build_linux_cli_archives() {
mkdir -p downloads
[ -f "downloads/${linux_x86_64_package}.tar.gz" ] && [ -f "downloads/${linux_aarch64_package}.tar.gz" ] && return 0
+ require_command docker
+
docker run --rm --platform=linux/amd64 \
-e "IUNA_VERSION=${version}" \
-e "HOST_UID=$(id -u)" \
@@ -266,6 +310,8 @@ build_docker_image() {
local www_image="${IUNA_WWW_IMAGE:-iuna-www:v${version}}"
local node_image="${IUNA_NODE_IMAGE:-iuna-node:v${version}}"
+ require_command docker
+
docker build --platform=linux/amd64 --progress=plain -t "$www_image" .
docker build --platform=linux/amd64 --progress=plain -t "$node_image" -f Dockerfile.node .
echo "Built Docker images: ${www_image}, ${node_image}"
@@ -278,6 +324,10 @@ import_image_to_k3s() {
local remote_file="${image//[:\/]/_}.tar"
local image_file="${tmp_folder}/${remote_file}"
+ require_command docker
+ require_command scp
+ require_command ssh
+
docker save "$image" -o "$image_file"
scp "$image_file" "${remote_host}:~/"
ssh "$remote_host" "sudo k3s ctr -n k8s.io images import ~/${remote_file} && rm ~/${remote_file}"
@@ -287,10 +337,15 @@ render_manifest() {
local www_image="$1"
local node_image="$2"
local output="$3"
+ local escaped_www_image
+ local escaped_node_image
+
+ escaped_www_image="$(escape_sed_replacement "$www_image")"
+ escaped_node_image="$(escape_sed_replacement "$node_image")"
sed \
- -e "s|\${IUNA_WWW_IMAGE}|${www_image}|g" \
- -e "s|\${IUNA_NODE_IMAGE}|${node_image}|g" \
+ -e "s|\${IUNA_WWW_IMAGE}|${escaped_www_image}|g" \
+ -e "s|\${IUNA_NODE_IMAGE}|${escaped_node_image}|g" \
config/deployment.yml > "$output"
}
@@ -301,6 +356,8 @@ deploy_docker_image() {
local kubectl_context="${IUNA_KUBECTL_CONTEXT:-jhx-app}"
local tmp_folder
+ require_command kubectl
+
tmp_folder="$(mktemp -d)"
trap 'rm -rf "$tmp_folder"' RETURN
@@ -330,18 +387,20 @@ main() {
# Check if the tag already exists; if it does, only deploy
if git rev-parse --verify "v${version}" >/dev/null 2>&1; then
- echo "Tag v${version} already exists; skipping build and deploying only"
- if ! read -p "Are you sure you want to deploy v${version}? (y/N) " answer || [[ ! "$answer" =~ ^[Yy]$ ]]; then
+ ensure_head_matches_tag "v${version}"
+ echo "Tag v${version} already exists; rebuilding Docker images and deploying"
+ if ! confirm "Are you sure you want to deploy v${version}? (y/N) "; then
echo "Aborting deployment"
exit 1
fi
+ build_docker_image "$version"
deploy_docker_image "$version"
exit 0
fi
update_versions "$version"
- commit_and_tag "$version"
build_versions "$version"
+ commit_and_tag "$version"
build_docker_image "$version"
deploy_docker_image "$version"
}