Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 107 additions & 78 deletions overlay/usr/local/sbin/tkldev-setup
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ $(basename "$0"): [-h|--help] [APP] [... APP]
Tool to initialize and maintain TKLDev.

Relevant git repos are downloaded/updated (see below), bootstrap is downloaded
if needed and the latest 'fab' package will be installed when relevant.
if needed (or built locally if not available for download) and the latest
'fab' package will be installed when relevant.

Options:
APP [APP ...] Appliance name/s - defaults if unset: 'core'
-f|--force Attempt checkout of default branch (see below) if alternate
branch checked out; dirty repos still skipped
-n|--full-clone Full clone (rather than default shallow clone)*
-s|--strict Fail rather than skip non-fatal steps
-s|--strict Fail rather than skip non-fatal steps (including a missing
bootstrap download, rather than building it locally)
-t|--transition Transition build - no fail on missing bootstrap download**
-h|--help Show this help and exit

Expand All @@ -33,7 +35,7 @@ branches will be skipped. Default git repos, branches and local paths are:
remote repo branch local path
------------------------------------------------------
turnkeylinux/buildtasks <ver>.x $BT_PATH
turnkeylinux/tklbam-profiles master /turnkey/tklbam-profiles
turnkeylinux/tklbam-profiles master $TKLBAM_PATH
turnkeylinux/cdroots <ver>.x $FAB_PATH/cdroots (amd64 only)
turnkeylinux/common <ver>.x $FAB_PATH/common
turnkeylinux-apps/APP master $FAB_PATH/products/APP
Expand All @@ -43,6 +45,7 @@ Env vars:
FAB_PATH Fab PATH (default: $FAB_PATH)
FAB_ARCH Fab architecture (default: $FAB_ARCH)
BT_PATH Buildtasks PATH (default: $BT_PATH)
TKLBAM_PATH tklbam-profiles PATH (default: $TKLBAM_PATH)
RELEASE Release to target - e.g. debian/bookworm (default: $RELEASE)
ARCH Arch to target (default: $ARCH)
BRANCH Preferred series ref (default: $BRANCH)
Expand All @@ -62,50 +65,6 @@ EOF
exit $exit_code
}

# check for BT_DEBUG as well as bt scripts call tkldev-setup
if [[ -n "$DEBUG" ]] || [[ -n "$BT_DEBUG" ]]; then
set -x
fi
[[ $(id -u) -eq 0 ]] || fatal "Root user required."

GIT_DEPTH=${GIT_DEPTH:-1}

distro="$(lsb_release -si)"
if [[ "${distro,,}" == "turnkey" ]] || [[ "${distro,,}" == "debian" ]]; then
distro="debian"
fi
tkl_ver=$(turnkey-version -t)
unset FORCE STRICT TRANSITION

export RELEASE="${RELEASE:-${distro}/$(lsb_release -s -c)}"
CODENAME="$(basename "$RELEASE")"
export CODENAME
export FAB_ARCH="${FAB_ARCH:-$(dpkg --print-architecture)}"
export BRANCH="${tkl_ver::2}.x"
IMAGES="http://mirror.turnkeylinux.org/turnkeylinux/images"
FAB_PATH="${FAB_PATH:-/turnkey/fab}"
BOOTSTRAP_NAME="bootstrap-${CODENAME}-${FAB_ARCH}"
BOOTSTRAP_PATH="$FAB_PATH/bootstraps/${CODENAME}-${FAB_ARCH}"
BT_PATH="${BT_PATH:-/turnkey/buildtasks}"
BT_VERIFY="$BT_PATH/bin/signature-verify"

read -ra BT_REFS <<< "${BT_REFS:-$BRANCH ${BRANCH}-dev master}"
read -ra TKLBAM_REFS <<< "${TKLBAM_REFS:-$BRANCH ${BRANCH}-dev master}"
read -ra COMMON_REFS <<< "${COMMON_REFS:-$BRANCH ${BRANCH}-dev master}"
read -ra APP_REFS <<< "${APP_REFS:-$BRANCH ${BRANCH}-dev master}"
read -ra CDROOTS_REFS <<< "${CDROOTS_REFS:-$BRANCH ${BRANCH}-dev master}"

GIT_REMOTE_URL="${GIT_REMOTE_URL:-https://github.com/turnkeylinux}"
GIT_REMOTE_NAME="${GIT_REMOTE_NAME:-origin}"

# remove trailing '/' if needed
GIT_REMOTE_URL="${GIT_REMOTE_URL%/}"
# Append '-apps' to git remote URL for apps if default org is 'turnkeylinux'
APP_GIT_REMOTE_URL="$GIT_REMOTE_URL"
if [[ "$GIT_REMOTE_URL" =~ [:/]turnkeylinux ]]; then
APP_GIT_REMOTE_URL="$GIT_REMOTE_URL-apps"
fi

tkl_remote() {
local dir=$1
local git_remote
Expand Down Expand Up @@ -244,6 +203,105 @@ clone_or_update() {
fi
}

build_bootstrap() {
local src
src="$(dirname "$FAB_PATH")/bootstrap"
clone_or_update bootstrap "$src" master
info "Building $BOOTSTRAP_NAME locally (from $src)"
make -C "$src" clean
make -C "$src" install \
FAB_PATH="$FAB_PATH" RELEASE="$RELEASE" FAB_ARCH="$FAB_ARCH"
if [[ ! -f "$BOOTSTRAP_PATH/bin/bash" ]]; then
fatal "Local build of $BOOTSTRAP_NAME did not create $BOOTSTRAP_PATH"
fi
}

setup_bootstrap() {
if [[ -d "$BOOTSTRAP_PATH" ]]; then
if [[ ! -f "$BOOTSTRAP_PATH/bin/bash" ]]; then
fatal "$BOOTSTRAP_PATH exists, but does not appear to be a bootstrap"
fi
info "$BOOTSTRAP_PATH exists, skipping download."
else
info "Downloading $BOOTSTRAP_NAME"
mkdir -p "$(dirname "$BOOTSTRAP_PATH")"
cd "$(dirname "$BOOTSTRAP_PATH")"
bootstrap_tar="$BOOTSTRAP_NAME.tar.gz"
if wget -nc -nv "$IMAGES/bootstrap/$bootstrap_tar" \
&& wget -nc -nv "$IMAGES/bootstrap/$bootstrap_tar.hash"; then
if [[ -f "$BT_VERIFY" ]]; then
bootstrap_tar_path="$(dirname "$BOOTSTRAP_PATH")/$bootstrap_tar"
BT_DEBUG=$DEBUG $BT_VERIFY \
--force-gpg "$bootstrap_tar_path" "$bootstrap_tar_path.hash"
else
fatal "$BT_VERIFY script not found, unable to verify downloaded files."
fi
info "Unpacking $BOOTSTRAP_NAME"
mkdir "$BOOTSTRAP_PATH"
tar -zxf "$BOOTSTRAP_NAME.tar.gz" -C "$BOOTSTRAP_PATH"
elif [[ -n $TRANSITION ]]; then
warning "Failed download: $bootstrap_tar - skipping due to transition"
elif [[ -n $STRICT ]]; then
fatal "Failed download: $bootstrap_tar"
else
warning "Failed download: $bootstrap_tar - building it locally instead"
rm -f "$bootstrap_tar" "$bootstrap_tar.hash"
build_bootstrap
fi
fi
}

# When sourced (as the tests do) stop here: the functions above are defined
# and nothing below runs.
if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then
return 0
fi

# check for BT_DEBUG as well as bt scripts call tkldev-setup
if [[ -n "$DEBUG" ]] || [[ -n "$BT_DEBUG" ]]; then
set -x
fi
[[ $(id -u) -eq 0 ]] || fatal "Root user required."

GIT_DEPTH=${GIT_DEPTH:-1}

distro="$(lsb_release -si)"
if [[ "${distro,,}" == "turnkey" ]] || [[ "${distro,,}" == "debian" ]]; then
distro="debian"
fi
tkl_ver=$(turnkey-version -t)
unset FORCE STRICT TRANSITION

export RELEASE="${RELEASE:-${distro}/$(lsb_release -s -c)}"
CODENAME="$(basename "$RELEASE")"
export CODENAME
export FAB_ARCH="${FAB_ARCH:-$(dpkg --print-architecture)}"
export BRANCH="${tkl_ver::2}.x"
IMAGES="http://mirror.turnkeylinux.org/turnkeylinux/images"
FAB_PATH="${FAB_PATH:-/turnkey/fab}"
BOOTSTRAP_NAME="bootstrap-${CODENAME}-${FAB_ARCH}"
BOOTSTRAP_PATH="$FAB_PATH/bootstraps/${CODENAME}-${FAB_ARCH}"
BT_PATH="${BT_PATH:-/turnkey/buildtasks}"
TKLBAM_PATH="${TKLBAM_PATH:-/turnkey/tklbam-profiles}"
BT_VERIFY="$BT_PATH/bin/signature-verify"

read -ra BT_REFS <<< "${BT_REFS:-$BRANCH ${BRANCH}-dev master}"
read -ra TKLBAM_REFS <<< "${TKLBAM_REFS:-$BRANCH ${BRANCH}-dev master}"
read -ra COMMON_REFS <<< "${COMMON_REFS:-$BRANCH ${BRANCH}-dev master}"
read -ra APP_REFS <<< "${APP_REFS:-$BRANCH ${BRANCH}-dev master}"
read -ra CDROOTS_REFS <<< "${CDROOTS_REFS:-$BRANCH ${BRANCH}-dev master}"

GIT_REMOTE_URL="${GIT_REMOTE_URL:-https://github.com/turnkeylinux}"
GIT_REMOTE_NAME="${GIT_REMOTE_NAME:-origin}"

# remove trailing '/' if needed
GIT_REMOTE_URL="${GIT_REMOTE_URL%/}"
# Append '-apps' to git remote URL for apps if default org is 'turnkeylinux'
APP_GIT_REMOTE_URL="$GIT_REMOTE_URL"
if [[ "$GIT_REMOTE_URL" =~ [:/]turnkeylinux ]]; then
APP_GIT_REMOTE_URL="$GIT_REMOTE_URL-apps"
fi

APPS=()
while [[ "$#" -gt 0 ]]; do
case $1 in
Expand Down Expand Up @@ -272,7 +330,7 @@ done
[[ -n "${APPS[*]}" ]] || APPS=(core)

clone_or_update buildtasks "$BT_PATH" "${BT_REFS[@]}"
clone_or_update tklbam-profiles /turnkey/tklbam-profiles "${TKLBAM_REFS[@]}"
clone_or_update tklbam-profiles "$TKLBAM_PATH" "${TKLBAM_REFS[@]}"
clone_or_update common "$FAB_PATH/common" "${COMMON_REFS[@]}"
for app in "${APPS[@]}"; do
clone_or_update _APP_ "$app" "$FAB_PATH/products/$app" "${APP_REFS[@]}"
Expand All @@ -297,34 +355,5 @@ apt-get update -qq
info "Installing newer fab version (if available)"
DEBIAN_FRONTEND=noninteractive apt-get install -y fab

if [[ -d "$BOOTSTRAP_PATH" ]]; then
if [[ ! -f "$BOOTSTRAP_PATH/bin/bash" ]]; then
fatal "$BOOTSTRAP_PATH exists, but does not appear to be a bootstrap"
fi
info "$BOOTSTRAP_PATH exists, skipping download."
else
if [[ -n $TRANSITION ]]; then
fail() { warning "Failed download: $* - skipping due to transition"; }
else
fail() { fatal "Failed download: $*"; }
fi
info "Downloading $BOOTSTRAP_NAME"
mkdir -p "$(dirname "$BOOTSTRAP_PATH")"
cd "$(dirname "$BOOTSTRAP_PATH")"
bootstrap_tar="$BOOTSTRAP_NAME.tar.gz"
wget -nc -nv "$IMAGES/bootstrap/$bootstrap_tar" \
|| fail "$bootstrap_tar"
wget -nc -nv "$IMAGES/bootstrap/$bootstrap_tar.hash" \
|| fail "$bootstrap_tar.hash"
if [[ -f "$BT_VERIFY" ]]; then
bootstrap_tar_path=$(dirname "$BOOTSTRAP_PATH/$bootstrap_tar")
BT_DEBUG=$DEBUG $BT_VERIFY \
--force-gpg "$bootstrap_tar_path" "$bootstrap_tar_path.hash"
else
fatal "$BT_VERIFY script not found, unable to verify downloaded files."
fi
info "Unpacking $BOOTSTRAP_NAME"
mkdir "$BOOTSTRAP_PATH"
tar -zxf "$BOOTSTRAP_NAME.tar.gz" -C "$BOOTSTRAP_PATH"
fi
setup_bootstrap
info "$(basename "$0") complete."
37 changes: 37 additions & 0 deletions tests/coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/bash
# Line coverage of overlay/usr/local/sbin/tkldev-setup under the bats suite
# in this directory, measured with kcov. Exits 1 when the covered share of
# the script is below the threshold (default 95), 2 when a tool is missing.
#
# tests/coverage.sh [THRESHOLD] (or COVERAGE_THRESHOLD in the environment)
#
# COVERAGE_DIR keeps the kcov report (default: a temporary directory).
# Needs the Debian packages kcov and bats.
set -euo pipefail

here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
target="$(cd "$here/.." && pwd)/overlay/usr/local/sbin/tkldev-setup"
threshold="${1:-${COVERAGE_THRESHOLD:-95}}"

for tool in kcov bats; do
if ! command -v "$tool" >/dev/null; then
echo "$tool not found (apt-get install $tool)" >&2
exit 2
fi
done

report="${COVERAGE_DIR:-$(mktemp -d)}"
kcov --include-path="$target" "$report" bats "$here"

# kcov names the sub directory after the command; with --include-path the
# report holds one file, so its first entry is ours
json="$(ls -t "$report"/*/coverage.json | grep -v /kcov-merged/ | head -1)"
percent="$(grep -o '"percent_covered": "[0-9.]*"' "$json" | head -1 | grep -o '[0-9.]*')"
covered="$(grep -o '"covered_lines": "[0-9]*"' "$json" | head -1 | grep -o '[0-9]*')"
total="$(grep -o '"total_lines": "[0-9]*"' "$json" | head -1 | grep -o '[0-9]*')"

echo "tkldev-setup: $percent percent ($covered of $total lines) covered, threshold $threshold"
if ! awk -v p="$percent" -v t="$threshold" 'BEGIN { exit !(p + 0 >= t + 0) }'; then
echo "coverage below threshold (report: $report)" >&2
exit 1
fi
3 changes: 3 additions & 0 deletions tests/stubs/apt-get
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
# Stand-in for apt-get: records the call only.
echo "apt-get $*" >> "${STUB_LOG:?}"
4 changes: 4 additions & 0 deletions tests/stubs/dpkg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
# Stand-in for dpkg --print-architecture: STUB_ARCH sets the answer.
echo "dpkg $*" >> "${STUB_LOG:?}"
echo "${STUB_ARCH:-amd64}"
41 changes: 41 additions & 0 deletions tests/stubs/git
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash
# Stand-in for git used by the tests: records the call in STUB_LOG and
# answers from STUB_* variables instead of touching any repository.
# STUB_REFS refs that "exist" on the remote (ls-remote)
# STUB_GIT_STATUS output of "git status -s" (empty = clean)
# STUB_GIT_BRANCH output of "git branch --show-current"
# STUB_GIT_FAIL subcommands that fail: fetch pull clone checkout, or
# checkout:<branch> to fail one branch only
echo "git $*" >> "${STUB_LOG:?}"
if [[ $1 == -C ]]; then
shift 2
fi
sub=$1
shift
fails=" ${STUB_GIT_FAIL:-} "
case $sub in
ls-remote)
ref="${*: -1}"
[[ " ${STUB_REFS:-} " == *" $ref "* ]]
;;
remote)
printf '%s\t%s (fetch)\n' origin https://github.com/turnkeylinux/repo.git
printf '%s\t%s (push)\n' origin https://github.com/turnkeylinux/repo.git
;;
status)
[[ -z ${STUB_GIT_STATUS:-} ]] || echo "$STUB_GIT_STATUS"
;;
branch)
echo "${STUB_GIT_BRANCH:-19.x}"
;;
checkout)
[[ $fails != *" checkout "* && $fails != *" checkout:$1 "* ]]
;;
clone)
[[ $fails != *" clone "* ]] || exit 128
mkdir -p "${*: -1}/.git"
;;
fetch|pull)
[[ $fails != *" $sub "* ]]
;;
esac
4 changes: 4 additions & 0 deletions tests/stubs/id
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
# Stand-in for id -u: STUB_UID sets the answer (default root).
echo "id $*" >> "${STUB_LOG:?}"
echo "${STUB_UID:-0}"
7 changes: 7 additions & 0 deletions tests/stubs/lsb_release
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash
# Stand-in for lsb_release: STUB_DISTRO and STUB_CODENAME set the answers.
echo "lsb_release $*" >> "${STUB_LOG:?}"
case " $* " in
*" -si "*) echo "${STUB_DISTRO:-TurnKey}" ;;
*" -c "*) echo "${STUB_CODENAME:-trixie}" ;;
esac
19 changes: 19 additions & 0 deletions tests/stubs/make
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash
# Stand-in for make: records the call and, on "install", creates the
# bootstrap where the FAB_PATH/RELEASE/FAB_ARCH arguments point, unless
# STUB_MAKE_NO_BOOTSTRAP is set.
echo "make $*" >> "${STUB_LOG:?}"
fab_path= release= arch= install=
for arg in "$@"; do
case $arg in
FAB_PATH=*) fab_path="${arg#*=}" ;;
RELEASE=*) release="${arg#*=}" ;;
FAB_ARCH=*) arch="${arg#*=}" ;;
install) install=y ;;
esac
done
if [[ -n $install && -z ${STUB_MAKE_NO_BOOTSTRAP:-} ]]; then
dst="$fab_path/bootstraps/$(basename "$release")-$arch"
mkdir -p "$dst/bin"
echo '#!/bin/sh' > "$dst/bin/bash"
fi
5 changes: 5 additions & 0 deletions tests/stubs/signature-verify
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
# Stand-in for buildtasks/bin/signature-verify: STUB_VERIFY_RC is its exit
# code. The test installs it under BT_PATH/bin.
echo "signature-verify $*" >> "${STUB_LOG:?}"
exit "${STUB_VERIFY_RC:-0}"
4 changes: 4 additions & 0 deletions tests/stubs/turnkey-version
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
# Stand-in for turnkey-version: STUB_TKL_VERSION sets the answer.
echo "turnkey-version $*" >> "${STUB_LOG:?}"
echo "${STUB_TKL_VERSION:-19.0}"
19 changes: 19 additions & 0 deletions tests/stubs/wget
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash
# Stand-in for wget: records the call and creates the requested file in the
# current directory (a real tarball with bin/bash for *.tar.gz).
# STUB_WGET_FAIL "all" fails every download, "hash" only the *.hash one
echo "wget $*" >> "${STUB_LOG:?}"
url="${*: -1}"
name="$(basename "$url")"
fails=" ${STUB_WGET_FAIL:-} "
[[ $fails != *" all "* ]] || exit 8
if [[ $name == *.hash ]]; then
[[ $fails != *" hash "* ]] || exit 8
echo "stub hash for $name" > "$name"
exit 0
fi
work="$(mktemp -d)"
mkdir -p "$work/bin"
echo '#!/bin/sh' > "$work/bin/bash"
tar -C "$work" -zcf "$name" bin
rm -rf "$work"
Loading
Loading