autobuild/autobuild-setup
rail5 736313e53e
Autobuild now creates a new Debian repo for the user
Rather than relying on the user already owning one, we just go ahead and create one (incl. signing key, etc)
This repo can be shipped either as a GitHub Pages site, or via some web server on the host machine, etc. We just create the files
This closes #35 and #27
2024-06-01 16:32:02 +08:00

335 lines
11 KiB
Bash
Executable file

#!/usr/bin/env bash
# autobuild setup
# Copyright (C) 2024 rail5
# Free software (GNU Affero GPL v3)
if [[ "$(whoami)" != _autobuild ]]; then
echo "Setup must be run via autobuild -s"
exit 1
fi
local_storage_directory="/var/autobuild"
autobuild_directory="/usr/share/autobuild"
CONFIG_FILE="$local_storage_directory/config.toml"
window_title="Autobuild setup"
# Copy any updated files to $local_storage_directory/build-farm in case an upgrade took place
mkdir -p "$local_storage_directory/build-farm" 2>/dev/null
cp -ru --preserve=timestamps "${autobuild_directory:?}/build-farm/"* "${local_storage_directory:?}/build-farm/"
BUILDFARM_SCRIPTS_FILE="$autobuild_directory/build-farm/scripts/scripts.sh"
# shellcheck source=./build-farm/scripts/scripts.sh
. "$BUILDFARM_SCRIPTS_FILE"
function setup_single_vm() {
if [[ $# != 1 ]]; then
echo "bag args to setup_single_vm" && exit
fi
local ARCH="$1"
ARCH_DIRECTORY="$local_storage_directory/build-farm/debian-stable-$ARCH"
cd "$ARCH_DIRECTORY" || (echo ""; echo "Local storage directory is not properly configured"; exit)
rm -f "./image.qcow"
rm -f "./preseed.cfg"
cp ../preseed.cfg ./preseed.cfg
download_vm_image "$ARCH" "$ARCH_DIRECTORY" 2>/dev/null | dialog --title "$window_title" \
--progressbox "Downloading $ARCH image... (Please wait)" 15 55
preseed_vm_image "$ARCH" "$ARCH_DIRECTORY" 2>/dev/null | dialog --title "$window_title" \
--progressbox "Preparing $ARCH image... (Please wait)" 15 55
install_vm "$ARCH" "$ARCH_DIRECTORY" 2>/dev/null | dialog --title "$window_title" \
--progressbox "Installing $ARCH VM... (Please wait, this may take a while)" 15 55
rm -f "./"*.iso
}
function setup_build_farm() {
amd64_vm_is_configured=$(test -f "$local_storage_directory/build-farm/debian-stable-amd64/image.qcow" && echo "true" || echo "false")
i386_vm_is_configured=$(test -f "$local_storage_directory/build-farm/debian-stable-i386/image.qcow" && echo "true" || echo "false")
arm64_vm_is_configured=$(test -f "$local_storage_directory/build-farm/debian-stable-arm64/image.qcow" && echo "true" || echo "false")
amd64_info_string="Not installed"
i386_info_string="Not installed"
arm64_info_string="Not installed"
if [[ $amd64_vm_is_configured == true ]]; then
amd64_info_string="Installed"
fi
if [[ $i386_vm_is_configured == true ]]; then
i386_info_string="Installed"
fi
if [[ $arm64_vm_is_configured == true ]]; then
arm64_info_string="Installed"
fi
# --checklist "prompt" width height list_height \
# "Option Tag" "Option comment" "Status"
# "Second option name" "Second option comment" "Status"
# The option is marked with '*' by default if status = "on"
{ user_choice="$(dialog --title "$window_title" \
--checklist "Which build farm VMs do you want to install?" 15 55 3 \
"amd64" "$amd64_info_string" off \
"i386" "$i386_info_string" off \
"arm64" "$arm64_info_string" off \
2>&1 1>&3 3>&- )"; } 3>&1 # Capture stderr output into user_choice variable
if [[ "$user_choice" != "" ]]; then
dialog --title "$window_title" \
--yesno "Are you sure you want to build VM(s) for $user_choice?\nThis will permanently erase & overwrite the currently-existing VMs for those architectures if they are already installed" 15 55
if [[ $? -eq 0 ]]; then
for choice in $user_choice; do
setup_single_vm "$choice"
done
else
dialog --title "$window_title" \
--infobox "Cancelled" 15 55; sleep 3
fi
fi
}
function create_new_debian_repository() {
# Ask the user for the name and email to use for the package signing key
local user_email="" user_name="" signing_key_fingerprint="" repo_url="" repo_friendlyname="" default_index_html_code="" repo_will_be_ghpages=false
{ user_email="$(dialog --title "$window_title" \
--inputbox "Input the EMAIL to be associated with the package GPG signing key" 15 55 \
2>&1 1>&3 3>&- )"; } 3>&1
echo ""
echo ""
echo "$user_email"
# Check if we've already made a key with that email
if gpg --list-secret-keys "$user_email"; then
key_exists_already=true
else
key_exists_already=false
fi
use_old_key=$key_exists_already
if [[ $key_exists_already == true ]]; then
dialog --title "$window_title" \
--yesno "A signing key already exists under '$user_email'.\n\nWould you like to DELETE this one and create a new signing key?" 15 55
case $? in
0)
use_old_key=false
;;
1)
use_old_key=true
user_name=$(gpg --with-colons -k "$user_email" | awk -F: '$1=="uid" {print $10}' | sed 's/<.\+>//')
;;
esac
fi
if [[ $use_old_key == false ]]; then
{ user_name="$(dialog --title "$window_title" \
--inputbox "Input the NAME to be associated with the package GPG signing key" 15 55 \
2>&1 1>&3 3>&- )"; } 3>&1
if [[ $key_exists_already == true ]]; then
# Delete the old key before creating the new key
old_fingerprint=$(gpg -K --with-colons "$user_email" | awk -F: '$1=="fpr" {print $10}' | head -n 1)
gpg --batch --yes --delete-secret-keys "$old_fingerprint"
gpg --batch --yes --delete-keys "$user_email"
fi
# Create the new key
gpg --batch --gen-key <<EOF
%no-protection
Key-Type:1
Key-Length:4096
Subkey-Type:1
Subkey-Length:4096
Name-Real: $user_name
Name-Email: $user_email
Expire-Date:0
EOF
fi
# Get the signing key's fingerprint!
signing_key_fingerprint=$(gpg -K --with-colons "$user_email" | awk -F: '$1=="fpr" {print $10}' | head -n 1)
# Delete any old repository which may or may not exist
rm -rf "${local_storage_directory:?}/repo/"
# Prepare the new repository for reprepro
mkdir -p "${local_storage_directory:?}/repo/conf"
cat > "${local_storage_directory:?}/repo/conf/distributions" <<EOF
Origin: $user_name
Label: $user_name
Codename: unstable
Architectures: source amd64 i386 arm64
Components: main
Description: Apt repository for $user_name
SignWith: $signing_key_fingerprint
EOF
# Ask the user for the repo URL
{ repo_url="$(dialog --title "$window_title" \
--inputbox "Input the FULL URL of the Debian Repository\nFor example: https://my.site.com/path/to/repo" 15 55 \
2>&1 1>&3 3>&- )"; } 3>&1
# Get the repo "friendly name":
## Remove non-alphanumeric characters from the user's name
## And convert the remainder to lowercase
# shellcheck disable=SC2001
repo_friendlyname=$(sed 's/[^A-Za-z0-9\-]//g' <<<"$user_name" | tr '[:upper:]' '[:lower:]')
# Export the public key to a file
gpg --export "$signing_key_fingerprint" > "${local_storage_directory:?}/repo/${repo_friendlyname:?}-signing-key.gpg"
# Create the repo .list file
cat > "${local_storage_directory:?}/repo/${repo_friendlyname:?}.list" <<EOF
deb $repo_url unstable main
deb-src $repo_url unstable main
EOF
# Create the default index.html file for the repo
default_index_html_code=$(cat "${autobuild_directory:?}/repository/default-index.html")
default_index_html_code="${default_index_html_code//\%REPO_FRIENDLYNAME\%/"$repo_friendlyname"}"
default_index_html_code="${default_index_html_code//\%REPO_URL\%/"$repo_url"}"
cat > "${local_storage_directory:?}/repo/index.html" <<<"$default_index_html_code"
cp "${autobuild_directory:?}/repository/run_prettify.js" "${local_storage_directory:?}/repo/run_prettify.js"
# Will this be some web server, or GitHub Pages?
# If it's GitHub Pages, we'll have to push changes etc rather than just change files
dialog --title "$window_title" \
--yesno "Will this repository be served via GitHub Pages?" 15 55
case $? in
0)
repo_will_be_ghpages=true
;;
1)
repo_will_be_ghpages=false
;;
esac
local repo_conf_file="${local_storage_directory:?}/repo/autobuild_repo.conf"
echo "[repo]" > "${repo_conf_file:?}"
if [[ $repo_will_be_ghpages == true ]]; then
# Parse autobuild config.toml to get GitHub Credentials
local config_json=""
config_json=$(toml2json "$CONFIG_FILE")
GITHUB_OWNER=$(jq -r ".github.repo_owner" <<<"$config_json")
GITHUB_EMAIL=$(jq -r ".github.email" <<<"$config_json")
GITHUB_ACCESS_TOKEN=$(jq -r ".github.access_token" <<<"$config_json")
if [[ "$GITHUB_OWNER" == "" || "$GITHUB_EMAIL" == "" || "$GITHUB_ACCESS_TOKEN" == "" ]]; then
echo "ghpages = false" >> "${repo_conf_file:?}"
dialog --title "$window_title" \
--infobox "Error: Your GitHub credentials are not properly configured in /var/autobuild/config.toml\n\nBefore trying again, please ensure that that file contains:\n - Your GitHub username\n - Your GitHub email\n - Your GitHub Access Token" 15 55; sleep 7
return
fi
echo "ghpages = true" >> "${repo_conf_file:?}"
# Ask the user for the Github URL
{ ghpages_url="$(dialog --title "$window_title" \
--inputbox "Input the FULL URL of the GitHub Repository that will be used\nFor example: https://github.com/user/repository.git" 15 55 \
2>&1 1>&3 3>&- )"; } 3>&1
echo "ghpages_url = \"$ghpages_url\"" >> "${repo_conf_file:?}"
# Initialize the git repo, set origin, and push
cd "${local_storage_directory:?}/repo" || (echo ""; echo "Confusing and fatal error in setting up GitHub Pages repository"; exit)
# Add our necessary credentials to the URL to push (https://user:pass@github.com/etc)
local push_url="${ghpages_url/:\/\//:\/\/$GITHUB_OWNER:$GITHUB_ACCESS_TOKEN@}"
local temporary_repo_changes_directory=""
temporary_repo_changes_directory=$(mktemp --tmpdir -d)
mv -f "./"* "${temporary_repo_changes_directory:?}/"
git init
git config user.email "$GITHUB_EMAIL"
git config user.name "$GITHUB_OWNER"
git remote add origin "$ghpages_url"
git pull "$push_url" -q
mv -f "${temporary_repo_changes_directory:?}/"* "./"
rm -rf "${temporary_repo_changes_directory:?}"
git add --all
git commit -m "Initialized Autobuild GitHub Pages Debian Repository"
git branch -M main
git push "$push_url" --all # Push changes
else
echo "ghpages = false" >> "${repo_conf_file:?}"
fi
dialog --title "$window_title" \
--infobox "Your Debian Repo is now configured\n\nYou can check the welcome page at $repo_url\nIf your repo is managed via GitHub Pages, it may take a few moments to publish." 15 55; sleep 7
}
function setup_debian_repo() {
# First, check: do we have one already?
debian_repository_already_exists=$(test -f /var/autobuild/repo/autobuild_repo.conf && echo "true" || echo "false")
if [[ $debian_repository_already_exists == true ]]; then
dialog --title "$window_title" \
--yesno "It looks like a repository already exists in /var/autobuild/repo.\nWould you like to DELETE this repository and create a new one?" 15 55
case $? in
0)
create_new_debian_repository
return ;;
1)
return ;;
esac
else
create_new_debian_repository
return
fi
}
function clear_builds_directory() {
rm -rf "${local_storage_directory:?}/builds/"*
dialog --title "$window_title" \
--infobox "Cleared $local_storage_directory/builds" 15 55; sleep 3
}
function setup_main_menu() {
{ next_page="$(dialog --title "$window_title" \
--menu "This menu can be reached anytime with 'autobuild -s'" 15 55 \
4 \
1 "Install build farm" \
2 "Configure Debian Repository" \
3 "Clear 'builds' directory" \
4 "Exit" \
2>&1 1>&3 3>&- )"; } 3>&1 # Capture stderr output into next_page variable
# 'dialog' writes user responses to stderr
case $next_page in
"" | 4) # User pressed "cancel" or "Exit"
exit ;;
1)
setup_build_farm
setup_main_menu
;;
2)
setup_debian_repo
setup_main_menu
;;
3)
clear_builds_directory
setup_main_menu
;;
esac
}
setup_main_menu