diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2249490..e2e5913 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ on: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: test: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 with: @@ -28,7 +28,7 @@ jobs: - name: Run tests run: test/test.sh coverage: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 with: diff --git a/backup.sh b/backup.sh index af4ad58..84a90e1 100755 --- a/backup.sh +++ b/backup.sh @@ -24,6 +24,7 @@ RESTIC_HOSTNAME="" # Leave empty to use system hostname LOCK_FILE="" # Optional lock file to acquire to ensure two backups don't run at once LOCK_FILE_TIMEOUT="" # Optional lock file wait timeout (in seconds) WINDOW_MANAGER="screen" # Choices: screen, tmux, RCON +SAVE_ALL_WAIT_SECONDS="" # Delay in seconds after save-all flush (default: 5, 0 if rcon) # Other Variables (do not modify) DATE_FORMAT="%F_%H-%M-%S" @@ -41,7 +42,7 @@ debug-log () { fi } -while getopts 'a:cd:e:f:hH:i:l:m:o:p:qr:s:t:u:vw:x' FLAG; do +while getopts 'a:cd:e:f:hH:i:l:m:o:p:qr:s:t:u:vw:xz:' FLAG; do case $FLAG in a) COMPRESSION_ALGORITHM=$OPTARG ;; c) ENABLE_CHAT_MESSAGES=true ;; @@ -69,6 +70,7 @@ while getopts 'a:cd:e:f:hH:i:l:m:o:p:qr:s:t:u:vw:x' FLAG; do echo "-u Lock file timeout seconds (empty = unlimited)" echo "-v Verbose mode" echo "-w Window manager: screen (default), tmux, RCON" + echo "-z Delay in seconds after save-all flush (default: 5, 0 if rcon)" exit 0 ;; H) RESTIC_HOSTNAME=$OPTARG ;; @@ -84,6 +86,7 @@ while getopts 'a:cd:e:f:hH:i:l:m:o:p:qr:s:t:u:vw:x' FLAG; do u) LOCK_FILE_TIMEOUT=$OPTARG ;; v) DEBUG=true ;; w) WINDOW_MANAGER=$OPTARG ;; + z) SAVE_ALL_WAIT_SECONDS=$OPTARG ;; *) log-fatal "Invalid option -$FLAG"; exit 1 ;; esac done @@ -290,6 +293,25 @@ message-players-color () { execute-command "tellraw @a [\"\",{\"text\":\"[$PREFIX] \",\"color\":\"gray\",\"italic\":true},{\"text\":\"$MESSAGE\",\"color\":\"$COLOR\",\"italic\":true,\"hoverEvent\":{\"action\":\"show_text\",\"value\":{\"text\":\"\",\"extra\":[{\"text\":\"$HOVER_MESSAGE\"}]}}}]" fi } +save-all-flush-delay-seconds () { + if [[ -n "$SAVE_ALL_WAIT_SECONDS" ]]; then + echo "$SAVE_ALL_WAIT_SECONDS" + return + fi + + case "$WINDOW_MANAGER" in + # RCON already waits for response + "RCON"|"rcon"|"docker-rcon") echo "0" + ;; + *) echo "5" # default value for all other cases + ;; + esac +} +execute-save-all-flush () { + execute-command "save-all" # for versions <1.16 + execute-command "save-all flush" + sleep "$(save-all-flush-delay-seconds)" +} # Parse file timestamp to one readable by "date" parse-file-timestamp () { @@ -489,9 +511,12 @@ do-backup () { # Notify players of start message-players "Starting backup..." "$ARCHIVE_PATH" - # Disable world autosaving + # Disable world autosaving, still allows save-all execute-command "save-off" + # Trigger save now to get most up-to-date data + execute-save-all-flush + # Backup world START_TIME=$(date +"%s") diff --git a/test/data/test-chat-messages.txt b/test/data/test-chat-messages.txt index 65aaeb4..75615f1 100644 --- a/test/data/test-chat-messages.txt +++ b/test/data/test-chat-messages.txt @@ -1,5 +1,7 @@ tellraw @a ["",{"text":"[Backup] ","color":"gray","italic":true},{"text":"Starting backup...","color":"gray","italic":true,"hoverEvent":{"action":"show_text","value":{"text":"","extra":[{"text":"test/tmp/backups/2021-01-01_00-00-00.tar.gz"}]}}}] save-off +save-all +save-all flush save-on save-all tellraw @a ["",{"text":"[Backup] ","color":"gray","italic":true},{"text":"Backup complete!","color":"green","italic":true,"hoverEvent":{"action":"show_text","value":{"text":"","extra":[{"text":"0 s, 4.0K/4.0K, 316%"}]}}}] diff --git a/test/data/test-chat-prefix.txt b/test/data/test-chat-prefix.txt index 33d6856..4b512e6 100644 --- a/test/data/test-chat-prefix.txt +++ b/test/data/test-chat-prefix.txt @@ -1,5 +1,7 @@ tellraw @a ["",{"text":"[Hello] ","color":"gray","italic":true},{"text":"Starting backup...","color":"gray","italic":true,"hoverEvent":{"action":"show_text","value":{"text":"","extra":[{"text":"test/tmp/backups/2021-01-01_00-00-00.tar.gz"}]}}}] save-off +save-all +save-all flush save-on save-all tellraw @a ["",{"text":"[Hello] ","color":"gray","italic":true},{"text":"Hello complete!","color":"green","italic":true,"hoverEvent":{"action":"show_text","value":{"text":"","extra":[{"text":"0 s, 4.0K/4.0K, 316%"}]}}}] diff --git a/test/test.sh b/test/test.sh index 42a2991..7083909 100755 --- a/test/test.sh +++ b/test/test.sh @@ -346,7 +346,7 @@ test-nonzero-exit-warning () { test-screen-interface () { TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")" ./backup.sh -i "$TEST_TMP/server/world" -o "$TEST_TMP/backups" -s "$SCREEN_TMP" -f "$TIMESTAMP" - EXPECTED_CONTENTS=$(echo -e "save-off\nsave-on\nsave-all") + EXPECTED_CONTENTS=$(echo -e "save-off\nsave-all\nsave-all flush\nsave-on\nsave-all") SCREEN_CONTENTS="$(cat "$TEST_TMP/screen-output")" assertEquals "$EXPECTED_CONTENTS" "$SCREEN_CONTENTS" } @@ -354,7 +354,7 @@ test-screen-interface () { test-tmux-interface () { TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")" ./backup.sh -w tmux -i "$TEST_TMP/server/world" -o "$TEST_TMP/backups" -s "$SCREEN_TMP" -f "$TIMESTAMP" - EXPECTED_CONTENTS=$(echo -e "save-off\nsave-on\nsave-all") + EXPECTED_CONTENTS=$(echo -e "save-off\nsave-all\nsave-all flush\nsave-on\nsave-all") SCREEN_CONTENTS="$(cat "$TEST_TMP/tmux-output")" assertEquals "$EXPECTED_CONTENTS" "$SCREEN_CONTENTS" } @@ -362,8 +362,8 @@ test-tmux-interface () { test-rcon-interface () { TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01")" ./backup.sh -w rcon -i "$TEST_TMP/server/world" -o "$TEST_TMP/backups" -s "localhost:$RCON_PORT:$RCON_PASSWORD" -f "$TIMESTAMP" - EXPECTED_CONTENTS=$(echo -e "save-off\nsave-on\nsave-all") - SCREEN_CONTENTS="$(head -n3 "$TEST_TMP/rcon-output")" + EXPECTED_CONTENTS=$(echo -e "save-off\nsave-all\nsave-all flush\nsave-on\nsave-all") + SCREEN_CONTENTS="$(head -n5 "$TEST_TMP/rcon-output")" assertEquals "$EXPECTED_CONTENTS" "$SCREEN_CONTENTS" }