Skip to content

Fix Pantheon backup freshness check comparing epoch to formatted date - #97

Open
sgenini wants to merge 2 commits into
docksal:masterfrom
sgenini:fix/pantheon-backup-ttl-epoch-comparison
Open

Fix Pantheon backup freshness check comparing epoch to formatted date#97
sgenini wants to merge 2 commits into
docksal:masterfrom
sgenini:fix/pantheon-backup-ttl-epoch-comparison

Conversation

@sgenini

@sgenini sgenini commented Aug 12, 2026

Copy link
Copy Markdown

Fixes #96

Problem

provider_pull_pantheon_db() compares a raw Unix epoch timestamp (from terminus backup:list --field=date) directly against a formatted Y-m-d H:M:S string ($yesterday). Since epoch timestamps for any date after 2001 start with 1 and formatted dates for 2000-2099 start with 2, the string comparison is always true — so it force-creates a new backup on the live environment on every pull, never reusing a same-day one.

Fix

Convert the epoch timestamp to the same formatted string before comparing, mirroring what provider_pull_acquia_db already does a few hundred lines down in the same file:

last_date=$(_exec date --date=@${last_date} "+%Y-%m-%d %H:%M:%S")

Also fixes a related typo in the same condition: [[ "last_backup" == "" ]] was missing the $ sigil, so it compared against the literal string "last_backup" instead of the variable, meaning the "no backup exists yet" case never matched either.

Testing

Verified against a real Pantheon site:

  • Before: last_backup=1786491893.1987 (raw epoch) vs yesterday=2026-08-10 19:21:58 (formatted) → < always true → always creates a new backup.
  • After: last_backup converts to 2026-08-11 16:44:53, correctly compares as newer than yesterday, and the existing backup is reused instead of creating a new one.
  • Re-verified that when a backup genuinely is >24h old (or doesn't exist), the fixed comparison still correctly falls through to terminus backup:create.

Summary by CodeRabbit

  • Bug Fixes
    • Improved database backup timestamp handling for more accurate freshness checks.
    • Fixed backup creation when no previous backup timestamp is available.

provider_pull_pantheon_db() reads the last backup's date via
`terminus backup:list ... --field=date`, which returns a raw Unix
epoch timestamp (e.g. 1786491893.1987), and compares it directly
against `$yesterday`, a formatted "Y-m-d H:M:S" string.

Since epoch timestamps for any date after 2001 start with the digit
"1" and formatted "YYYY-..." dates for 2000-2099 start with "2", the
string comparison `"${last_backup}" < "${yesterday}"` is always true
regardless of how recent the backup actually is. This forces a brand
new `terminus backup:create` against the live/production environment
on every single pull, even when a backup from minutes ago already
exists.

The Acquia provider (provider_pull_acquia_db) already converts its
epoch timestamp to the same formatted string before comparing (see
`date --date=@${last_date} ...`); this applies the same pattern here.

Also fixes a secondary bug in the same condition: the "no backup
exists" check compared the literal string "last_backup" instead of
the variable "${last_backup}", so it never actually detected a
missing backup.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@sgenini, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e5f586b-17c3-494d-985b-109bd10716fc

📥 Commits

Reviewing files that changed from the base of the PR and between 59f3f1e and 62fe9d6.

📒 Files selected for processing (1)
  • pull/pull
📝 Walkthrough

Walkthrough

Pantheon database backup handling converts epoch timestamps to the formatted timestamp used for freshness checks. The backup condition now detects a missing timestamp and creates a backup only when no backup exists, the backup is older than one day, or creation is forced.

Changes

Pantheon backup handling

Layer / File(s) Summary
Normalize and evaluate backup timestamp
pull/pull
The raw Terminus backup timestamp is normalized before comparison. The condition correctly handles missing, expired, and forced backups.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: addons-owners

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the Pantheon backup freshness comparison bug and matches the main change.
Linked Issues check ✅ Passed The change converts the epoch timestamp and fixes the empty-backup variable check required by issue #96.
Out of Scope Changes check ✅ Passed All reported changes directly support the Pantheon backup freshness requirements in issue #96.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pull/pull`:
- Around line 540-542: Update the last_backup conversion logic in pull so it
captures and checks the date command’s exit status before evaluating the backup
condition; handle conversion failures explicitly rather than allowing an empty
value to trigger backup creation. Pass the timestamp to date as the single
quoted argument --date="@${last_backup%%.*}", while preserving the existing
freshness and force checks for successful conversions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f211679a-dab9-4809-9388-4d7e9cf0e1f9

📥 Commits

Reviewing files that changed from the base of the PR and between f115ec4 and 59f3f1e.

📒 Files selected for processing (1)
  • pull/pull

Comment thread pull/pull Outdated
Comment on lines +540 to +542
last_backup=$(_exec date --date=@${last_backup%%.*} "+%Y-%m-%d %H:%M:%S")
fi
if [[ "${last_backup}" == "" ]] || [[ "${last_backup}" < "${yesterday}" ]] || [[ "$force" == "force" ]]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 '(^|[[:space:]])_exec[[:space:]]*\(|function[[:space:]]+_exec' .

Repository: docksal/addons

Length of output: 152


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)pull(/|$)|(^|/)pull$' || true

printf '%s\n' '--- relevant symbols ---'
rg -n -C 5 '_exec|if_failed_error|last_backup|backup:create' . || true

Repository: docksal/addons

Length of output: 22204


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- tracked top-level files ---'
git ls-files | sed -n '1,160p'

printf '%s\n' '--- pull/pull outline and header ---'
wc -l pull/pull
sed -n '1,120p' pull/pull
sed -n '528,552p' pull/pull

printf '%s\n' '--- helper references and definitions ---'
rg -n '(^|[[:space:]])(_exec|if_failed_error|run_cli)[[:space:](=]' --glob '!pull/pull' . || true
rg -n '_exec|if_failed_error' pull/README.md README.md 2>/dev/null || true

Repository: docksal/addons

Length of output: 8913


🌐 Web query:

Docksal fin shell function _exec if_failed_error source

💡 Result:

In the Docksal fin script, if_failed_error is a shell function defined to handle command failures by outputting an error message and exiting the script [1][2][3]. The function is defined as follows: if_failed_error { if [! $? -eq 0 ]; then echo-error "$@" exit 1 fi } It checks the exit status of the previously executed command ($?). If the status is non-zero (indicating failure), it uses the echo-error function to display the provided arguments and then terminates the process with an exit code of 1 [1][3]. It is commonly used throughout the fin script to ensure that if a critical operation—such as a file download or a system command—fails, the execution stops immediately [3]. The _exec function is a separate internal helper in the fin script used to handle command execution within the cli service container (e.g., via docker exec). While _exec manages the actual command execution, if_failed_error is often called immediately after to handle any potential errors resulting from that execution [4][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

bash <<'BASH'
set -u
_exec() {
  printf 'argc=%s\n' "$#"
  local i=1
  for arg in "$@"; do
    printf 'arg[%s]=<%s>\n' "$i" "$arg"
    i=$((i + 1))
  done
}

last_backup='1700000000.123'
_exec date --date=@${last_backup%%.*} "+%Y-%m-%d %H:%M:%S"
_exec date --date="@${last_backup%%.*}" "+%Y-%m-%d %H:%M:%S"

last_backup='1700000000 1800000000'
_exec date --date=@${last_backup%%.*} "+%Y-%m-%d %H:%M:%S"
_exec date --date="@${last_backup%%.*}" "+%Y-%m-%d %H:%M:%S"
BASH

Repository: docksal/addons

Length of output: 491


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
curl -fsSL https://raw.githubusercontent.com/docksal/docksal/develop/bin/fin -o "$tmp"

printf '%s\n' '--- helper definitions ---'
rg -n -A 18 -B 3 '(^|[[:space:]])(_exec|if_failed_error)[[:space:](]' "$tmp" || true

Repository: docksal/addons

Length of output: 23433


🏁 Script executed:

#!/bin/bash
set -euo pipefail

bash <<'BASH'
set -u

_exec() {
  "$@"
}

if_failed_error() {
  local status=$?
  printf 'if_failed_error previous_status=%s\n' "$status"
}

run_conversion() {
  local last_backup="$1"
  last_backup=$(_exec date --date=@${last_backup%%.*} "+%Y-%m-%d %H:%M:%S")
  local conversion_status=$?
  printf 'input=<%s> status=%s output=<%s>\n' "$1" "$conversion_status" "$last_backup"
  if [[ "$last_backup" == "" ]]; then
    printf 'backup_branch=entered\n'
  else
    printf 'backup_branch=not-entered\n'
  fi
}

run_conversion 'not-an-epoch'
run_conversion '1700000000.123'
BASH

Repository: docksal/addons

Length of output: 336


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
curl -fsSL https://raw.githubusercontent.com/docksal/docksal/develop/bin/fin -o "$tmp"
sed -n '5360,5445p' "$tmp"

Repository: docksal/addons

Length of output: 3263


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
curl -fsSL https://raw.githubusercontent.com/docksal/docksal/develop/bin/fin -o "$tmp"
sed -n '5440,5505p' "$tmp"

Repository: docksal/addons

Length of output: 2544


Handle date-conversion failures before creating a backup.

If date fails, last_backup becomes empty and the backup branch runs. Check the conversion status before evaluating freshness. Quote the date argument as --date="@${last_backup%%.*}" to preserve it as one argument.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pull/pull` around lines 540 - 542, Update the last_backup conversion logic in
pull so it captures and checks the date command’s exit status before evaluating
the backup condition; handle conversion failures explicitly rather than allowing
an empty value to trigger backup creation. Pass the timestamp to date as the
single quoted argument --date="@${last_backup%%.*}", while preserving the
existing freshness and force checks for successful conversions.

Addresses CodeRabbit review feedback on docksal#97: pass the timestamp to
`date` as a single quoted argument (--date="@${last_backup%%.*}")
instead of leaving it unquoted.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fin pull db: Pantheon backup freshness check always creates a new backup (epoch vs formatted-date comparison bug)

1 participant