diff --git a/runs/atreboot.sh b/runs/atreboot.sh index 8166249e82..39aea4b82d 100755 --- a/runs/atreboot.sh +++ b/runs/atreboot.sh @@ -337,7 +337,8 @@ chmod 666 "$LOGFILE" echo "load versions..." "$OPENWBBASEDIR/runs/update_available_versions.sh" else - echo "no internet connection, skipping version update" + echo "no internet connection, updating versions from local git data" + "$OPENWBBASEDIR/runs/update_available_versions.sh" --no-fetch fi # set restore dir permissions to allow file upload for apache diff --git a/runs/backup.sh b/runs/backup.sh index 489c0040fd..18438caa6e 100755 --- a/runs/backup.sh +++ b/runs/backup.sh @@ -181,18 +181,31 @@ force_mosquitto_write() { collect_git_info() { echo "collecting git information" - if git branch --no-color --show-current >"$TEMPDIR/GIT_BRANCH"; then + if git -C "$OPENWBBASEDIR" branch --no-color --show-current >"$TEMPDIR/GIT_BRANCH"; then echo "current branch: $(<"$TEMPDIR/GIT_BRANCH")" else echo "failed to collect git branch info" return 1 fi - if git log --pretty='format:%H' -n1 >"$TEMPDIR/GIT_HASH"; then + if git -C "$OPENWBBASEDIR" log --pretty='format:%H' -n1 >"$TEMPDIR/GIT_HASH"; then echo "current commit hash: $(<"$TEMPDIR/GIT_HASH")" else echo "failed to collect git commit hash" return 1 fi + local logicalBranch="" + if [[ -z $(<"$TEMPDIR/GIT_BRANCH") ]]; then + logicalBranch=$(git -C "$OPENWBBASEDIR" config --local --get openwb.updateBranch) || logicalBranch="" + fi + case "$logicalBranch" in + "Release"|"Beta"|"") ;; + *) + echo "invalid logical update branch '$logicalBranch'" + return 1 + ;; + esac + printf '%s\n' "$logicalBranch" >"$TEMPDIR/GIT_UPDATE_BRANCH" + echo "logical update branch: $logicalBranch" return 0 } @@ -216,6 +229,7 @@ create_archive() { "${DB_FILES[@]}" \ "GIT_BRANCH" \ "GIT_HASH" \ + "GIT_UPDATE_BRANCH" \ --directory="/etc/mosquitto/" \ "conf_local.d/" \ --directory="$TARBASEDIR/" \ diff --git a/runs/prepare_restore.sh b/runs/prepare_restore.sh index 184c629dd7..788e98c70e 100755 --- a/runs/prepare_restore.sh +++ b/runs/prepare_restore.sh @@ -4,6 +4,9 @@ RESTORE_DIR="$OPENWB_BASE_DIR/data/restore" SOURCE_FILE="$RESTORE_DIR/restore.tar.gz" WORKING_DIR="/home/openwb/openwb_restore" LOG_FILE="$OPENWB_BASE_DIR/data/log/restore.log" +GIT_REMOTE="origin" + +source "$OPENWB_BASE_DIR/runs/update_version_helpers.sh" || exit 1 declare resultMessage declare resultStatus @@ -12,6 +15,11 @@ declare resultStatus echo "$(date +"%Y-%m-%d %H:%M:%S") Preparation of restore started..." echo "****************************************" echo "Step 1: creating working directory \"$WORKING_DIR\"" + echo "Step 1.1: ensure working directory is empty" + if [[ -d "$WORKING_DIR" ]]; then + echo "Removing existing working directory" + sudo rm -rf "$WORKING_DIR" + fi mkdir -p "$WORKING_DIR" echo "****************************************" echo "Step 2: extract archive to working directory" @@ -54,17 +62,33 @@ declare resultStatus else echo "****************************************" echo "Step 4: sync with git" - GIT_REMOTE="origin" BRANCH=$(<"$WORKING_DIR/GIT_BRANCH") - # TAG=$(<"$WORKING_DIR/GIT_HASH") + HASH=$(<"$WORKING_DIR/GIT_HASH") + UPDATE_BRANCH="" + if [[ -f "$WORKING_DIR/GIT_UPDATE_BRANCH" ]]; then + UPDATE_BRANCH=$(<"$WORKING_DIR/GIT_UPDATE_BRANCH") + fi + metadataStatus=0 + validateRestoreGitMetadata "$BRANCH" "$UPDATE_BRANCH" || metadataStatus=$? echo "Step 4.1: fetch info from git" - if ! git -C "$OPENWB_BASE_DIR" fetch -v "$GIT_REMOTE"; then + if [[ $metadataStatus -eq 1 ]]; then + echo "invalid logical update branch '$UPDATE_BRANCH'" + resultMessage="Der logische Update-Zweig des Archivs ist ungültig." + resultStatus=1 + elif [[ $metadataStatus -eq 2 ]]; then + echo "logical update branch requires detached HEAD" + resultMessage="Das Archiv enthält widersprüchliche Git-Zweige." + resultStatus=1 + elif ! fetchVersionRefs "$OPENWB_BASE_DIR" "$GIT_REMOTE"; then resultMessage="Beim Abgleich mit dem openWB Git ist ein Fehler aufgetreten!" resultStatus=1 else - echo "Step 4.2: verify branch \"$BRANCH\" from archive" - if ! git -C "$OPENWB_BASE_DIR" branch | grep "$BRANCH"; then - resultMessage="Der Entwicklungszweig \"$BRANCH\" des Archivs ist ungültig." + echo "Step 4.2: verify git state from archive" + if ! git -C "$OPENWB_BASE_DIR" rev-parse --verify --quiet "$HASH^{commit}" >/dev/null; then + resultMessage="Der Git-Stand \"$HASH\" des Archivs ist ungültig." + resultStatus=1 + elif [[ -n $BRANCH ]] && ! remoteBranchExists "$OPENWB_BASE_DIR" "$GIT_REMOTE" "$BRANCH"; then + resultMessage="Der Entwicklungszweig \"$GIT_REMOTE/$BRANCH\" des Archivs ist ungültig." resultStatus=1 else echo "Step 4.3: clean mosquitto configuration, will be recreated on next boot" diff --git a/runs/restore.sh b/runs/restore.sh index e928e19115..1bb35321fe 100755 --- a/runs/restore.sh +++ b/runs/restore.sh @@ -7,6 +7,9 @@ MOSQUITTO_DB_DIR="/var/lib/mosquitto" MOSQUITTO_LOCAL_DB_DIR="/var/lib/mosquitto_local" MOSQUITTO_CONF_DIR="/etc/mosquitto" LOG_FILE="$OPENWB_BASE_DIR/data/log/restore.log" +GIT_REMOTE="origin" + +source "$OPENWB_BASE_DIR/runs/update_version_helpers.sh" || exit 1 { echo "$(date +"%Y-%m-%d %H:%M:%S") Restore of backup started..." @@ -22,23 +25,39 @@ LOG_FILE="$OPENWB_BASE_DIR/data/log/restore.log" # give systemctl some time to finish, may be unnecessary? sleep 2 echo "****************************************" - echo "Step 6: checkout matching git branch and tag" + echo "Step 6: checkout matching git branch and hash" BRANCH=$(<"$WORKING_DIR/GIT_BRANCH") - TAG=$(<"$WORKING_DIR/GIT_HASH") - echo "Step 6.1: checkout branch: $BRANCH" - if ! git -C "$OPENWB_BASE_DIR" checkout --force "$BRANCH"; then - echo "checkout of branch \"$BRANCH\" failed! aborting restore" + HASH=$(<"$WORKING_DIR/GIT_HASH") + UPDATE_BRANCH="" + if [[ -f "$WORKING_DIR/GIT_UPDATE_BRANCH" ]]; then + UPDATE_BRANCH=$(<"$WORKING_DIR/GIT_UPDATE_BRANCH") + fi + metadataStatus=0 + validateRestoreGitMetadata "$BRANCH" "$UPDATE_BRANCH" || metadataStatus=$? + if [[ $metadataStatus -eq 1 ]]; then + echo "invalid logical update branch '$UPDATE_BRANCH'! aborting restore" + exit 1 + elif [[ $metadataStatus -eq 2 ]]; then + echo "logical update branch requires detached HEAD! aborting restore" exit 1 fi - echo "Step 6.2: reset to matching version: [$TAG]" - if ! git -C "$OPENWB_BASE_DIR" reset --hard "$TAG"; then - echo "reset to version [$TAG] failed! aborting restore" + if [[ -z $BRANCH ]]; then + echo "checkout detached commit: $HASH" + else + echo "checkout branch from remote: $GIT_REMOTE/$BRANCH" + fi + echo "apply saved version: [$HASH]" + if ! applyVersion "$OPENWB_BASE_DIR" "$GIT_REMOTE" "$BRANCH" "$HASH" "$UPDATE_BRANCH"; then + echo "applying version [$HASH] failed! aborting restore" exit 1 fi echo "****************************************" echo "Step 7: restore contents of backup" # we use cp not mv because of not empty directories in destination - sudo cp -v -p -r "${WORKING_DIR}/openWB/." "${OPENWB_BASE_DIR}/" + if ! sudo cp -v -p -r "${WORKING_DIR}/openWB/." "${OPENWB_BASE_DIR}/"; then + echo "restoring openWB files failed! aborting restore" + exit 1 + fi if [[ -f "$WORKING_DIR/configuration.json" ]]; then sudo mv -v -f "${WORKING_DIR}/configuration.json" "/home/openwb/" else diff --git a/runs/update_available_versions.sh b/runs/update_available_versions.sh index 0f48995fa4..b067c5914c 100755 --- a/runs/update_available_versions.sh +++ b/runs/update_available_versions.sh @@ -1,93 +1,131 @@ #!/bin/bash -OPENWBBASEDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) -LOGFILE="${OPENWBBASEDIR}/ramdisk/versions.log" -GITREMOTE="origin" -YOURCHARGEPREFIX="yc/" +OPENWB_BASE_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +LOG_FILE="${OPENWB_BASE_DIR}/ramdisk/versions.log" +GIT_REMOTE="origin" +YOUR_CHARGE_PREFIX="yc/" +DRY_RUN=0 # set to 1 for testing without writing to files or publishing to MQTT + +source "$OPENWB_BASE_DIR/runs/update_version_helpers.sh" if [ "$(id -u -n)" != "openwb" ]; then echo "this script has to be run as user openwb" exit 1 fi -validateTag() { - branch=$1 - tag=$2 +buildTagJson() { + includeHead=$1 + shift + local -a inputTags=("$@") - case $branch in - "release" | "Release") - if [[ $tag =~ ([Rr][Cc])|([Bb]eta)|([Aa]lpha) ]] || [[ $tag =~ 1.99 ]]; then - return 1 - fi - ;; - "beta" | "Beta") - if [[ $tag =~ [Aa]lpha ]] || [[ $tag =~ 1.99 ]]; then - return 1 + { + for tagLine in "${inputTags[@]}"; do + if [[ -z $tagLine ]]; then + continue + fi + echo "${tagLine%%: *}" + echo "$tagLine" + done + if [[ $includeHead == "true" ]]; then + echo "*HEAD*" + echo "Aktuellster Stand" fi - ;; - *) - return 0 - ;; - esac + } | + jq -n -R -c 'reduce inputs as $key ({}; . + { ($key): (input) })' +} + +mqttPublish() { + if [[ $DRY_RUN -eq 0 ]]; then + mosquitto_pub -p 1886 -t "$1" -r -m "$2" + fi } -{ +writeFile() { + if [[ $DRY_RUN -eq 0 ]]; then + echo "$2" >"$1" + fi +} + +runUpdate() { echo "#### updating available version info ####" # update our local version - currentCommit=$(git -C "$OPENWBBASEDIR" log --pretty='format:%ci [%h]' -n1) + currentCommit=$(git -C "$OPENWB_BASE_DIR" log --pretty='format:%ci [%h]' -n1) echo "current commit: $currentCommit" - mosquitto_pub -p 1886 -t "openWB/system/current_commit" -r -m "\"$currentCommit\"" - echo "$currentCommit" >"$OPENWBBASEDIR/web/lastcommit" + mqttPublish "openWB/system/current_commit" "\"$currentCommit\"" + writeFile "$OPENWB_BASE_DIR/web/lastcommit" "$currentCommit" # fetch data from git - echo "fetching latest data from '$GITREMOTE'..." - git -C "$OPENWBBASEDIR" fetch --verbose --prune --tags --prune-tags --force "$GITREMOTE" && echo "done" + if [[ ${1:-} == "--no-fetch" ]]; then + echo "skipping remote fetch, using locally cached refs" + else + echo "fetching latest data from '$GIT_REMOTE'..." + fetchVersionRefs "$OPENWB_BASE_DIR" "$GIT_REMOTE" && echo "done" + fi - # update branches from $GITREMOTE + # update branches from $GIT_REMOTE echo "branches:" IFS=$'\n' - read -r -d '' -a branches < <(git -C "$OPENWBBASEDIR" branch -r --list "$GITREMOTE/*") + read -r -d '' -a branches < <(git -C "$OPENWB_BASE_DIR" branch -r --list "$GIT_REMOTE/*") declare -A availableBranches declare -A tagsJson for index in "${!branches[@]}"; do if [[ ${branches[$index]} == *"HEAD"* ]]; then unset 'branches[$index]' else - branches[index]="${branches[$index]//*$GITREMOTE\//}" # remove leading whitespace and $GITREMOTE/ - if [[ ${branches[$index]} == *"$YOURCHARGEPREFIX"* ]]; then + branches[index]="${branches[$index]//*$GIT_REMOTE\//}" # remove leading whitespace and $GIT_REMOTE/ + if [[ ${branches[$index]} == *"$YOUR_CHARGE_PREFIX"* ]]; then echo "skipping branch '${branches[$index]}'" unset 'branches[$index]' else - echo -n "checking commit for '$GITREMOTE/${branches[$index]}'..." - availableBranches[${branches[$index]}]=$(git -C "$OPENWBBASEDIR" log --pretty='format:%ci [%h]' -n1 "$GITREMOTE/${branches[$index]}") + echo -n "checking commit for '$GIT_REMOTE/${branches[$index]}'..." + availableBranches[${branches[$index]}]=$(git -C "$OPENWB_BASE_DIR" log --pretty='format:%ci [%h]' -n1 "$GIT_REMOTE/${branches[$index]}") echo "${availableBranches[${branches[$index]}]}" - echo "tags in branch:" - read -r -d '' -a tags < <(git -C "$OPENWBBASEDIR" tag -n --format "%(refname:short): %(subject)" --merged "$GITREMOTE/${branches[$index]}") - echo "${tags[*]}" - tagsJson[${branches[$index]}]=$( - for key in "${!tags[@]}"; do - if validateTag "${branches[$index]}" "${tags[${key}]//: */}"; then - echo "${tags[${key}]//: */}" - echo "${tags[${key}]}" - else - # invalid tag for this branch, skip - continue - fi - # special handling for last element in $tags - if (( key == ${#tags[@]}-1 )); then - # add tag *HEAD* if branch is not "Beta" or "Release" - if [[ ${branches[$index]} != "Beta" ]] && [[ ${branches[$index]} != "Release" ]]; then - echo "*HEAD*" - echo "Aktuellster Stand" - fi - fi - done | - jq -n -R -c 'reduce inputs as $key ({}; . + { ($key): (input) })' - ) + if [[ ${branches[$index]} == "master" ]]; then + echo "tags in branch:" + read -r -d '' -a tags < <(git -C "$OPENWB_BASE_DIR" tag -n --format "%(refname:short): %(subject)" --merged "$GIT_REMOTE/${branches[$index]}" && printf '\0') + echo "${tags[*]}" + tagsJson[${branches[$index]}]=$(buildTagJson true "${tags[@]}") + else + tagsJson[${branches[$index]}]='{"*HEAD*":"Aktuellster Stand"}' + fi echo "${branches[$index]}: ${tagsJson[${branches[$index]}]}" fi fi done + + # Build virtual Release/Beta entries from repository tags + read -r -d '' -a allTags < <(git -C "$OPENWB_BASE_DIR" tag -n --format "%(refname:short): %(subject)" && printf '\0') + declare -a releaseTags + declare -a betaTags + for tagLine in "${allTags[@]}"; do + tagName="${tagLine%%: *}" + if tagMatchesTrain "Release" "$tagName"; then + releaseTags+=("$tagLine") + betaTags+=("$tagLine") + + elif tagMatchesTrain "Beta" "$tagName"; then + betaTags+=("$tagLine") + fi + done + + tagsJson["Release"]=$(buildTagJson false "${releaseTags[@]}") + tagsJson["Beta"]=$(buildTagJson false "${betaTags[@]}") + + latestReleaseTag=$(selectLatestTrainTag "Release") + if [[ -n $latestReleaseTag ]]; then + availableBranches["Release"]=$(git -C "$OPENWB_BASE_DIR" log --pretty='format:%ci [%h]' -n1 "$latestReleaseTag") + else + availableBranches["Release"]=${availableBranches["master"]} + fi + + latestBetaTag=$(selectLatestTrainTag "Beta") + if [[ -n $latestBetaTag ]]; then + availableBranches["Beta"]=$(git -C "$OPENWB_BASE_DIR" log --pretty='format:%ci [%h]' -n1 "$latestBetaTag") + else + availableBranches["Beta"]=${availableBranches["master"]} + fi + echo "Release: ${tagsJson["Release"]}" + echo "Beta: ${tagsJson["Beta"]}" branchJson=$( for key in "${!availableBranches[@]}"; do echo "$key" @@ -97,23 +135,70 @@ validateTag() { jq -n -R 'reduce inputs as $key ({}; . + { ($key): { commit: (input), tags: (input|fromjson) } })' ) echo "$branchJson" - mosquitto_pub -p 1886 -t "openWB/system/available_branches" -r -m "$branchJson" + mqttPublish "openWB/system/available_branches" "$branchJson" # update current branch - currentBranch=$(git -C "$OPENWBBASEDIR" branch --no-color --show-current) + currentBranch=$(git -C "$OPENWB_BASE_DIR" branch --no-color --show-current) + if [[ -z $currentBranch ]]; then + logicalBranch=$(git -C "$OPENWB_BASE_DIR" config --local --get openwb.updateBranch) + if [[ -z $logicalBranch ]]; then + local -a headTags + local train headTag + read -r -d '' -a headTags < <(git -C "$OPENWB_BASE_DIR" tag --points-at HEAD && printf '\0') + for train in Release Beta; do + for headTag in "${headTags[@]}"; do + if jq -e --arg tag "$headTag" 'has($tag)' <<<"${tagsJson[$train]}" >/dev/null; then + logicalBranch="$train" + break 2 + fi + done + done + if [[ -n $logicalBranch ]]; then + echo "inferred update branch '$logicalBranch' from tags at HEAD" + if [[ $DRY_RUN -eq 0 ]]; then + setLogicalUpdateBranch "$OPENWB_BASE_DIR" "$logicalBranch" || return 1 + else + echo "DRY RUN: would persist update branch '$logicalBranch'" + fi + fi + fi + if [[ $logicalBranch == "Release" || $logicalBranch == "Beta" ]]; then + currentBranch="$logicalBranch" + fi + fi echo "currently selected branch: $currentBranch" - mosquitto_pub -p 1886 -t "openWB/system/current_branch" -r -m "\"$currentBranch\"" + mqttPublish "openWB/system/current_branch" "\"$currentBranch\"" - # update $currentBranch commit - currentBranchCommit=$(git -C "$OPENWBBASEDIR" log --pretty='format:%ci [%h]' -n1 "$GITREMOTE/$currentBranch") - echo "last commit in '$currentBranch' branch: $currentBranchCommit" - mosquitto_pub -p 1886 -t "openWB/system/current_branch_commit" -r -m "\"$currentBranchCommit\"" - - # list missing commits + # update $currentBranch commit and list missing commits + remoteCurrentBranch="$GIT_REMOTE/$currentBranch" echo "changes:" - IFS=$'\n' - read -r -d '' -a commitDiff < <(git -C "$OPENWBBASEDIR" log --pretty='format:%ci [%h] - %s' "$currentBranch..$GITREMOTE/$currentBranch") + latestTrainTag="" + case "$currentBranch" in + "Release") latestTrainTag="$latestReleaseTag" ;; + "Beta") latestTrainTag="$latestBetaTag" ;; + esac + if [[ -n $latestTrainTag ]]; then + currentBranchCommit=$(git -C "$OPENWB_BASE_DIR" log --pretty='format:%ci [%h]' -n1 "refs/tags/$latestTrainTag") + echo "last commit in '$currentBranch' train: $currentBranchCommit" + mqttPublish "openWB/system/current_branch_commit" "\"$currentBranchCommit\"" + IFS=$'\n' + read -r -d '' -a commitDiff < <(git -C "$OPENWB_BASE_DIR" log --pretty='format:%ci [%h] - %s' "HEAD..refs/tags/$latestTrainTag" && printf '\0') + elif [[ -n $currentBranch ]] && remoteBranchExists "$OPENWB_BASE_DIR" "$GIT_REMOTE" "$currentBranch"; then + currentBranchCommit=$(git -C "$OPENWB_BASE_DIR" log --pretty='format:%ci [%h]' -n1 "$remoteCurrentBranch") + echo "last commit in '$currentBranch' branch: $currentBranchCommit" + mqttPublish "openWB/system/current_branch_commit" "\"$currentBranchCommit\"" + IFS=$'\n' + read -r -d '' -a commitDiff < <(git -C "$OPENWB_BASE_DIR" log --pretty='format:%ci [%h] - %s' "$currentBranch..$remoteCurrentBranch" && printf '\0') + else + currentBranchCommit=$(git -C "$OPENWB_BASE_DIR" log --pretty='format:%ci [%h]' -n1) + echo "no remote branch '$remoteCurrentBranch' found, using local HEAD commit" + echo "last commit in '$currentBranch' branch: $currentBranchCommit" + mqttPublish "openWB/system/current_branch_commit" "\"$currentBranchCommit\"" + commitDiff=() + fi printf "* %s\n" "${commitDiff[@]}" commitDiffMessage=$(jq --compact-output --null-input '$ARGS.positional' --args -- "${commitDiff[@]}") - mosquitto_pub -p 1886 -t "openWB/system/current_missing_commits" -r -m "$commitDiffMessage" -} >"$LOGFILE" 2>&1 + mqttPublish "openWB/system/current_missing_commits" "$commitDiffMessage" +} + +runUpdate "$@" >"$LOG_FILE" 2>&1 diff --git a/runs/update_self.sh b/runs/update_self.sh index 00b4145214..e2b8a82a46 100755 --- a/runs/update_self.sh +++ b/runs/update_self.sh @@ -1,45 +1,147 @@ #!/bin/bash -OPENWBBASEDIR=$(cd "$(dirname "$0")/../" && pwd) -LOGFILE="${OPENWBBASEDIR}/data/log/update.log" -GITREMOTE="origin" -SELECTEDBRANCH="$1" -DEFAULTTAG="*HEAD*" -SELECTEDTAG=$2 +OPENWB_BASE_DIR=$(cd "$(dirname "$0")/../" && pwd) +LOG_FILE="${OPENWB_BASE_DIR}/data/log/update.log" +GIT_REMOTE="origin" +SELECTED_BRANCH="$1" +DEFAULT_TAG="*HEAD*" +SELECTED_TAG="${2:-$DEFAULT_TAG}" +DRY_RUN=0 # set to 1 for testing without writing to files or publishing to MQTT -echo "#### running update ####" >"$LOGFILE" +source "$OPENWB_BASE_DIR/runs/update_version_helpers.sh" + +validateTag() { + local tag=$1 + if ! git -C "$OPENWB_BASE_DIR" show-ref --verify --quiet "refs/tags/$tag"; then + echo "#### ERROR: tag '$tag' does not exist ####" + return 1 + fi + if ! git -C "$OPENWB_BASE_DIR" rev-parse --verify --quiet "refs/tags/$tag^{commit}" >/dev/null; then + echo "#### ERROR: tag '$tag' does not resolve to a commit ####" + return 1 + fi + if [[ $SELECTED_BRANCH == "Release" || $SELECTED_BRANCH == "Beta" ]]; then + if ! tagMatchesTrain "$SELECTED_BRANCH" "$tag"; then + echo "#### ERROR: tag '$tag' does not belong to train '$SELECTED_BRANCH' ####" + return 1 + fi + fi + return 0 +} + +validateBranch() { + local branch=$1 + if ! remoteBranchExists "$OPENWB_BASE_DIR" "$GIT_REMOTE" "$branch"; then + echo "#### ERROR: remote branch '$GIT_REMOTE/$branch' does not exist ####" + return 1 + fi + return 0 +} + +clearUpdateFlagOnFailure() { + local status=$? + if [[ $status -ne 0 && $DRY_RUN -eq 0 ]]; then + mosquitto_pub -p 1886 -t "openWB/system/update_in_progress" -r -m 'false' || true + fi +} +trap clearUpdateFlagOnFailure EXIT + +echo "#### running update ####" >"$LOG_FILE" { + if [[ -z $SELECTED_BRANCH ]]; then + echo "#### ERROR: no branch selected, aborting update ####" + exit 1 + fi + # fetch new release from GitHub - echo "#### 1. fetching latest data from '$GITREMOTE' ####" - git -C "$OPENWBBASEDIR" fetch -v "$GITREMOTE" && echo "#### done" - - # checkout selected branch - echo "#### 2. checkout selected branch '$SELECTEDBRANCH'" - git -C "$OPENWBBASEDIR" checkout --force "$SELECTEDBRANCH" && echo "#### done" - - # reset to latest revision or selected tag - echo "#### 3. reset working dir ###" - resetTarget="$GITREMOTE/$SELECTEDBRANCH" - echo "SELECTEDTAG: $SELECTEDTAG" - if [[ -n $SELECTEDTAG ]] && [[ $SELECTEDTAG != "$DEFAULTTAG" ]]; then - echo "#### selected tag: '$SELECTEDTAG'" - resetTarget="$SELECTEDTAG" + echo "#### 1. fetching latest data from '$GIT_REMOTE' ####" + fetchVersionRefs "$OPENWB_BASE_DIR" "$GIT_REMOTE" || exit 1 + echo "#### done" + + if [[ -z $SELECTED_TAG ]] || [[ $SELECTED_TAG == "$DEFAULT_TAG" ]]; then + fallbackTag=$(selectLatestTrainTag "$SELECTED_BRANCH") + if [[ -n $fallbackTag ]]; then + echo "#### using fallback tag '$fallbackTag' for branch '$SELECTED_BRANCH' ####" + SELECTED_TAG="$fallbackTag" + fi + fi + + # check if selected tag exists + if [[ -n $SELECTED_TAG ]] && [[ $SELECTED_TAG != "$DEFAULT_TAG" ]]; then + if ! validateTag "$SELECTED_TAG"; then + echo "#### ERROR: selected tag '$SELECTED_TAG' is invalid, aborting update ####" + exit 1 + fi + fi + + versionBranch="" + versionRef="" + logicalTrain="" + if [[ $SELECTED_BRANCH == "Release" || $SELECTED_BRANCH == "Beta" ]]; then + if [[ -n $SELECTED_TAG ]] && [[ $SELECTED_TAG != "$DEFAULT_TAG" ]]; then + echo "#### 2. checkout selected tag '$SELECTED_TAG' for virtual branch '$SELECTED_BRANCH' ####" + versionRef="refs/tags/$SELECTED_TAG" + logicalTrain="$SELECTED_BRANCH" + else + echo "#### ERROR: no tag available for virtual branch '$SELECTED_BRANCH', aborting update ####" + exit 1 + fi + else + echo "#### 2. checkout selected branch ####" + if ! validateBranch "$SELECTED_BRANCH"; then + echo "#### ERROR: selected remote branch '$GIT_REMOTE/$SELECTED_BRANCH' does not exist, aborting update ####" + exit 1 + fi + versionBranch="$SELECTED_BRANCH" + # set Selected_Tag to default if branch is not "master" + if [[ $SELECTED_BRANCH != "master" ]]; then + echo "#### branch '$SELECTED_BRANCH' is not 'master', ignoring selected tag and using default tag '$DEFAULT_TAG' ####" + SELECTED_TAG="$DEFAULT_TAG" + fi + versionRef="refs/remotes/$GIT_REMOTE/$SELECTED_BRANCH" + echo "#### SELECTED_TAG: $SELECTED_TAG ####" + if [[ -n $SELECTED_TAG ]] && [[ $SELECTED_TAG != "$DEFAULT_TAG" ]]; then + echo "#### resetting working dir to selected tag '$SELECTED_TAG' ####" + versionRef="refs/tags/$SELECTED_TAG" + else + echo "#### no tag or default selected, resetting to latest remote revision" + fi + fi + + echo "#### 3. apply selected version '$versionRef' ####" + if ! applyVersion "$OPENWB_BASE_DIR" "$GIT_REMOTE" "$versionBranch" "$versionRef" "$logicalTrain" "$DRY_RUN"; then + echo "#### ERROR: applying version '$versionRef' failed, aborting update ####" + exit 1 + fi + if [[ $DRY_RUN -eq 0 ]]; then + echo "#### done" else - echo "#### no tag or default selected, resetting to latest revision" + echo "DRY RUN: would apply version '$versionRef' with branch '$versionBranch' and train '$logicalTrain'" fi - git -C "$OPENWBBASEDIR" reset --hard "$resetTarget" && echo "#### done" # clean mosquitto configuration directory to remove possibly outdated files - echo "#### 4. clean mosquitto configuration, will be recreated on next boot" - sudo rm -v -f /etc/mosquitto/conf.d/openwb-*.conf + echo "#### 4. clean mosquitto configuration, will be recreated on next boot ####" + if [[ $DRY_RUN -eq 0 ]]; then + sudo rm -v -f /etc/mosquitto/conf.d/openwb-*.conf + else + echo "DRY RUN: would remove /etc/mosquitto/conf.d/openwb-*.conf" + fi # notify system # set boot_done first to prevent flickering in gui - mosquitto_pub -p 1886 -t "openWB/system/boot_done" -r -m 'false' - mosquitto_pub -p 1886 -t "openWB/system/update_in_progress" -r -m 'false' + if [[ $DRY_RUN -eq 0 ]]; then + mosquitto_pub -p 1886 -t "openWB/system/boot_done" -r -m 'false' + mosquitto_pub -p 1886 -t "openWB/system/update_in_progress" -r -m 'false' + else + echo "DRY RUN: would publish to MQTT topics 'openWB/system/boot_done' and 'openWB/system/update_in_progress'" + fi sleep 1 # now reboot system echo "#### 5. rebooting system ####" - sudo reboot now & -} >>"$LOGFILE" 2>&1 + if [[ $DRY_RUN -eq 0 ]]; then + sudo reboot now & + else + echo "DRY RUN: would reboot system now" + fi +} >>"$LOG_FILE" 2>&1 diff --git a/runs/update_version_helpers.sh b/runs/update_version_helpers.sh new file mode 100644 index 0000000000..6b44678ffb --- /dev/null +++ b/runs/update_version_helpers.sh @@ -0,0 +1,143 @@ +#!/bin/bash + +fetchVersionRefs() { + local baseDir=$1 + local remote=$2 + git -C "$baseDir" fetch --verbose --prune --tags --prune-tags --force "$remote" +} + +remoteBranchExists() { + local baseDir=$1 + local remote=$2 + local branch=$3 + git -C "$baseDir" show-ref --verify --quiet "refs/remotes/$remote/$branch" +} + +setLogicalUpdateBranch() { + local baseDir=$1 + local train=$2 + case "$train" in + "Release"|"Beta") + git -C "$baseDir" config --local openwb.updateBranch "$train" + ;; + "") + local configStatus=0 + git -C "$baseDir" config --local --unset-all openwb.updateBranch || configStatus=$? + if [[ $configStatus -eq 5 ]]; then + return 0 + fi + return "$configStatus" + ;; + *) + return 1 + ;; + esac +} + +validateRestoreGitMetadata() { + local branch=$1 + local train=$2 + case "$train" in + "Release"|"Beta"|"") ;; + *) return 1 ;; + esac + if [[ -n $branch && -n $train ]]; then + return 2 + fi + return 0 +} + +applyVersion() { + local baseDir=$1 + local remote=$2 + local branch=$3 + local targetRef=$4 + local train=$5 + local dryRun=${6:-0} + local targetCommit + + validateRestoreGitMetadata "$branch" "$train" || return $? + if [[ -n $branch ]]; then + remoteBranchExists "$baseDir" "$remote" "$branch" || return 1 + fi + targetCommit=$(git -C "$baseDir" rev-parse --verify --quiet "$targetRef^{commit}") || return 1 + if [[ $dryRun == "1" ]]; then + return 0 + elif [[ $dryRun != "0" ]]; then + return 1 + fi + + if [[ -z $branch ]]; then + git -C "$baseDir" checkout --detach --force "$targetCommit" || return 1 + else + git -C "$baseDir" checkout --force -B "$branch" --track "refs/remotes/$remote/$branch" || return 1 + fi + git -C "$baseDir" reset --hard "$targetCommit" || return 1 + setLogicalUpdateBranch "$baseDir" "$train" +} + +getTrainTagPattern() { + local branch=$1 + case "$branch" in + "Release") + echo '^([2-9]|[1-9][0-9]+)\.[0-9]+\.[0-9]+(-Patch\.[0-9]+)?$' + ;; + "Beta") + echo '^([2-9]|[1-9][0-9]+)\.[0-9]+\.[0-9]+(-Patch\.[0-9]+|-Beta\.[0-9]+|-[Rr][Cc]\.[0-9]+)?$' + ;; + *) + return 1 + ;; + esac +} + +tagMatchesTrain() { + local branch=$1 + local tag=$2 + local pattern + pattern=$(getTrainTagPattern "$branch") || return 1 + [[ $tag =~ $pattern ]] +} + +latestTagByPattern() { + local pattern=$1 + git -C "$OPENWB_BASE_DIR" tag --sort=-version:refname | grep -E -m1 "$pattern" +} + +extractBaseVersion() { + local tag=$1 + echo "$tag" | sed -E 's/-Patch\.[0-9]+$//; s/-Beta\.[0-9]+$//; s/-[Rr][Cc]\.[0-9]+$//' +} + +highestBaseByPattern() { + local pattern=$1 + local tag + tag=$(latestTagByPattern "$pattern") || return 1 + extractBaseVersion "$tag" +} + +selectLatestTrainTag() { + local branch=$1 + local baseVersion + local basePattern + local trainPattern + trainPattern=$(getTrainTagPattern "$branch") || return 1 + baseVersion=$(highestBaseByPattern "$trainPattern") || return 1 + basePattern=${baseVersion//./\\.} + + case "$branch" in + "Release") + latestTagByPattern "^${basePattern}-Patch\\.[0-9]+$" || + latestTagByPattern "^${basePattern}$" + ;; + "Beta") + latestTagByPattern "^${basePattern}-Patch\\.[0-9]+$" || + latestTagByPattern "^${basePattern}$" || + latestTagByPattern "^${basePattern}-[Rr][Cc]\\.[0-9]+$" || + latestTagByPattern "^${basePattern}-Beta\\.[0-9]+$" + ;; + *) + return 1 + ;; + esac +}