Implement Linastorage project backups
This commit is contained in:
@@ -168,13 +168,12 @@ Major version 0.01 work already completed:
|
|||||||
Remaining version 0.01 cleanup before moving deeper into new gameplay:
|
Remaining version 0.01 cleanup before moving deeper into new gameplay:
|
||||||
|
|
||||||
- [ ] Decide whether to keep current Unreal template variants or remove unused starter variants.
|
- [ ] Decide whether to keep current Unreal template variants or remove unused starter variants.
|
||||||
- [!] Create protected `main` branch. Blocked while the repo remains private on the current GitHub plan; GitHub API reports this requires GitHub Pro or making the repository public.
|
|
||||||
- [x] Decide whether to create/use a long-lived `dev` branch. Decision: do not use one yet; use `main` plus short-lived task branches until team size or release channels require a staging branch.
|
- [x] Decide whether to create/use a long-lived `dev` branch. Decision: do not use one yet; use `main` plus short-lived task branches until team size or release channels require a staging branch.
|
||||||
- [x] Finish branch naming conventions.
|
- [x] Finish branch naming conventions.
|
||||||
- [x] Finish commit message conventions.
|
- [x] Finish commit message conventions.
|
||||||
- [x] Define GitHub/LFS free-tier storage guardrails.
|
- [x] Define GitHub/LFS free-tier storage guardrails.
|
||||||
- [x] Define backup expectations for NAS and repository.
|
- [x] Define backup expectations for NAS and repository.
|
||||||
- [ ] Implement Linastorage incremental project backup job.
|
- [x] Implement Linastorage incremental project backup job.
|
||||||
- [ ] Implement quiesced VM backup job for Windows-Builder and Ubuntu-Codex.
|
- [ ] Implement quiesced VM backup job for Windows-Builder and Ubuntu-Codex.
|
||||||
- [ ] Create repeatable dedicated server build instructions.
|
- [ ] Create repeatable dedicated server build instructions.
|
||||||
- [~] Finish required plugin documentation.
|
- [~] Finish required plugin documentation.
|
||||||
@@ -196,12 +195,11 @@ Goal: Prepare the project so all future development is controlled, recoverable,
|
|||||||
- [x] Create `.gitignore` for Unreal Engine.
|
- [x] Create `.gitignore` for Unreal Engine.
|
||||||
- [x] Ensure `Binaries/`, `Intermediate/`, `Saved/`, and `DerivedDataCache/` are excluded unless intentionally needed.
|
- [x] Ensure `Binaries/`, `Intermediate/`, `Saved/`, and `DerivedDataCache/` are excluded unless intentionally needed.
|
||||||
- [x] Commit clean baseline project.
|
- [x] Commit clean baseline project.
|
||||||
- [!] Create protected `main` branch. Blocked while the repo remains private on the current GitHub plan; GitHub API reports this requires GitHub Pro or making the repository public.
|
|
||||||
- [x] Create `dev` branch if we want staging before main. Decision: do not create a long-lived `dev` branch yet.
|
- [x] Create `dev` branch if we want staging before main. Decision: do not create a long-lived `dev` branch yet.
|
||||||
- [x] Define branch naming conventions.
|
- [x] Define branch naming conventions.
|
||||||
- [x] Define commit message conventions.
|
- [x] Define commit message conventions.
|
||||||
- [x] Define GitHub/LFS free-tier storage guardrails.
|
- [x] Define GitHub/LFS free-tier storage guardrails.
|
||||||
- [ ] Define backup expectations for NAS and repo.
|
- [x] Define backup expectations for NAS and repo.
|
||||||
- [x] Confirm this roadmap file is committed or otherwise backed up.
|
- [x] Confirm this roadmap file is committed or otherwise backed up.
|
||||||
|
|
||||||
## 0.2 Engine And Tooling Decisions
|
## 0.2 Engine And Tooling Decisions
|
||||||
@@ -314,7 +312,7 @@ Current tooling decisions:
|
|||||||
- [ ] Stabilize Windows-Builder network/RDP behavior under GPU passthrough.
|
- [ ] Stabilize Windows-Builder network/RDP behavior under GPU passthrough.
|
||||||
- [x] Decide and document VM snapshot cadence before major engine/tool changes.
|
- [x] Decide and document VM snapshot cadence before major engine/tool changes.
|
||||||
- [x] Define Unraid share backup policy.
|
- [x] Define Unraid share backup policy.
|
||||||
- [ ] Implement Linastorage incremental project backup job with deleted-file retention.
|
- [x] Implement Linastorage incremental project backup job with deleted-file retention.
|
||||||
- [ ] Implement quiesced VM backup job for Windows-Builder and Ubuntu-Codex.
|
- [ ] Implement quiesced VM backup job for Windows-Builder and Ubuntu-Codex.
|
||||||
- [ ] Add recurring restore-test log for project and VM backups.
|
- [ ] Add recurring restore-test log for project and VM backups.
|
||||||
- [ ] Define GitHub branch protection and review rules.
|
- [ ] Define GitHub branch protection and review rules.
|
||||||
@@ -1266,6 +1264,7 @@ These tracks run across all phases and must not be left as afterthoughts.
|
|||||||
- [ ] Add one-command Linux dedicated server build wrapper.
|
- [ ] Add one-command Linux dedicated server build wrapper.
|
||||||
- [x] Define investor-demo build trigger at version milestone completion.
|
- [x] Define investor-demo build trigger at version milestone completion.
|
||||||
- [x] Add smoke-test command for build artifacts.
|
- [x] Add smoke-test command for build artifacts.
|
||||||
|
- [ ] Enable protected `main` branch once revenue or a paid GitHub plan justifies the cost.
|
||||||
|
|
||||||
## L. Storefront Development Distribution
|
## L. Storefront Development Distribution
|
||||||
|
|
||||||
@@ -1410,13 +1409,12 @@ Current version: `0.01 Foundation Baseline`
|
|||||||
|
|
||||||
Earliest incomplete foundation items:
|
Earliest incomplete foundation items:
|
||||||
|
|
||||||
- [!] Create protected `main` branch. Blocked while the repo remains private on the current GitHub plan; GitHub API reports this requires GitHub Pro or making the repository public.
|
|
||||||
- [x] Decide whether to create/use a long-lived `dev` branch. Decision: no long-lived `dev` branch yet.
|
- [x] Decide whether to create/use a long-lived `dev` branch. Decision: no long-lived `dev` branch yet.
|
||||||
- [x] Finish branch naming conventions.
|
- [x] Finish branch naming conventions.
|
||||||
- [x] Finish commit message conventions.
|
- [x] Finish commit message conventions.
|
||||||
- [x] Define GitHub/LFS free-tier storage guardrails.
|
- [x] Define GitHub/LFS free-tier storage guardrails.
|
||||||
- [x] Define backup expectations for NAS and repository.
|
- [x] Define backup expectations for NAS and repository.
|
||||||
- [ ] Implement Linastorage incremental project backup job.
|
- [x] Implement Linastorage incremental project backup job.
|
||||||
- [ ] Implement quiesced VM backup job for Windows-Builder and Ubuntu-Codex.
|
- [ ] Implement quiesced VM backup job for Windows-Builder and Ubuntu-Codex.
|
||||||
- [ ] Create repeatable dedicated server build instructions.
|
- [ ] Create repeatable dedicated server build instructions.
|
||||||
- [~] Finish required plugin documentation.
|
- [~] Finish required plugin documentation.
|
||||||
@@ -1435,4 +1433,4 @@ Earliest incomplete foundation items:
|
|||||||
|
|
||||||
Immediate next item:
|
Immediate next item:
|
||||||
|
|
||||||
- [ ] Implement Linastorage incremental project backup job.
|
- [ ] Implement quiesced VM backup job for Windows-Builder and Ubuntu-Codex.
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# Agrarian Project Backup Runbook
|
||||||
|
|
||||||
|
Agrarian project backups run from the Codex/dev host and write encrypted,
|
||||||
|
deduplicated restic snapshots to Linastorage.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Source: `/mnt/projects/AgrarianGameBulid`
|
||||||
|
- NAS mount: `/mnt/backups/linastorage`
|
||||||
|
- Backup root: `/mnt/backups/linastorage/backups/agrarian-game/project`
|
||||||
|
- Restic repository: `/mnt/backups/linastorage/backups/agrarian-game/project/restic-repository`
|
||||||
|
- State files: `/mnt/backups/linastorage/backups/agrarian-game/project/state`
|
||||||
|
- Password file: `/root/.backup-secrets/agrarian-project-restic.password`
|
||||||
|
- Script: `/usr/local/sbin/agrarian-project-backup`
|
||||||
|
- Service: `agrarian-project-backup.service`
|
||||||
|
- Timer: `agrarian-project-backup.timer`
|
||||||
|
|
||||||
|
## Schedule
|
||||||
|
|
||||||
|
The timer runs every two hours with a small randomized delay. The script exits
|
||||||
|
without creating a snapshot when there are no changes compared to the latest
|
||||||
|
snapshot.
|
||||||
|
|
||||||
|
The first implementation attempt used `rsync --link-dest`, but Linastorage over
|
||||||
|
SMB did not support the hard-link operations required for that model. The active
|
||||||
|
job uses restic instead. Restic stores encrypted, deduplicated chunks as normal
|
||||||
|
files, which is a better fit for this SMB target.
|
||||||
|
|
||||||
|
Manual snapshots can be created before risky work:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo /usr/local/sbin/agrarian-project-backup --manual
|
||||||
|
```
|
||||||
|
|
||||||
|
Force a scheduled-style snapshot even if no changes are detected:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo /usr/local/sbin/agrarian-project-backup --force
|
||||||
|
```
|
||||||
|
|
||||||
|
Check whether a snapshot would be created:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo /usr/local/sbin/agrarian-project-backup --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Included
|
||||||
|
|
||||||
|
- Unreal project files.
|
||||||
|
- Git metadata, including unpushed local history when practical.
|
||||||
|
- Local source data under `Data/`, including raw terrain source inputs excluded
|
||||||
|
from GitHub.
|
||||||
|
- Scripts, docs, configs, plugins, content, and project metadata.
|
||||||
|
|
||||||
|
## Excluded
|
||||||
|
|
||||||
|
- `DerivedDataCache/`
|
||||||
|
- `Intermediate/`
|
||||||
|
- `Saved/`
|
||||||
|
- `Binaries/`
|
||||||
|
- `Builds/`
|
||||||
|
- transient Git LFS temp files
|
||||||
|
- local Visual Studio temp files
|
||||||
|
|
||||||
|
## Retention
|
||||||
|
|
||||||
|
The script uses restic retention:
|
||||||
|
|
||||||
|
- All snapshots within 7 days.
|
||||||
|
- Daily snapshots for 30 days.
|
||||||
|
- Weekly snapshots for 12 weeks.
|
||||||
|
- Monthly snapshots for 12 months.
|
||||||
|
|
||||||
|
The script records a source signature and skips scheduled runs when no project
|
||||||
|
changes are detected.
|
||||||
|
|
||||||
|
## Restore Test
|
||||||
|
|
||||||
|
Restore one file to a temporary path:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /tmp/agrarian-project-restore-test
|
||||||
|
sudo sh -c 'RESTIC_PASSWORD_FILE=/root/.backup-secrets/agrarian-project-restic.password \
|
||||||
|
restic -r /mnt/backups/linastorage/backups/agrarian-game/project/restic-repository \
|
||||||
|
dump latest /mnt/projects/AgrarianGameBulid/AGRARIAN_DEVELOPMENT_ROADMAP.md \
|
||||||
|
> /tmp/agrarian-project-restore-test/AGRARIAN_DEVELOPMENT_ROADMAP.md'
|
||||||
|
diff -q /mnt/projects/AgrarianGameBulid/AGRARIAN_DEVELOPMENT_ROADMAP.md \
|
||||||
|
/tmp/agrarian-project-restore-test/AGRARIAN_DEVELOPMENT_ROADMAP.md
|
||||||
|
```
|
||||||
|
|
||||||
|
List snapshots:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo RESTIC_PASSWORD_FILE=/root/.backup-secrets/agrarian-project-restic.password \
|
||||||
|
restic -r /mnt/backups/linastorage/backups/agrarian-game/project/restic-repository \
|
||||||
|
snapshots --tag agrarian-game --tag project
|
||||||
|
```
|
||||||
|
|
||||||
|
Record restore tests in the handoff log with the snapshot name and result.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Agrarian project incremental backup to Linastorage
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target mnt-backups-linastorage.automount
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/sbin/agrarian-project-backup
|
||||||
|
Nice=10
|
||||||
|
IOSchedulingClass=best-effort
|
||||||
|
IOSchedulingPriority=7
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Run Agrarian project backups every two hours when changes exist
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=*-*-* 00/2:13:00
|
||||||
|
Persistent=true
|
||||||
|
RandomizedDelaySec=10m
|
||||||
|
Unit=agrarian-project-backup.service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
Executable
+220
@@ -0,0 +1,220 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
umask 077
|
||||||
|
|
||||||
|
SOURCE_DIR="${AGRARIAN_PROJECT_SOURCE:-/mnt/projects/AgrarianGameBulid}"
|
||||||
|
MOUNT_POINT="${AGRARIAN_BACKUP_MOUNT:-/mnt/backups/linastorage}"
|
||||||
|
BACKUP_ROOT="${AGRARIAN_BACKUP_ROOT:-$MOUNT_POINT/backups/agrarian-game/project}"
|
||||||
|
RESTIC_REPOSITORY="${AGRARIAN_RESTIC_REPOSITORY:-$BACKUP_ROOT/restic-repository}"
|
||||||
|
RESTIC_PASSWORD_FILE="${AGRARIAN_RESTIC_PASSWORD_FILE:-/root/.backup-secrets/agrarian-project-restic.password}"
|
||||||
|
STATE_DIR="${AGRARIAN_BACKUP_STATE_DIR:-$BACKUP_ROOT/state}"
|
||||||
|
LOCK_FILE="${AGRARIAN_BACKUP_LOCK:-/run/lock/agrarian-project-backup.lock}"
|
||||||
|
LOG_TAG="${AGRARIAN_BACKUP_LOG_TAG:-agrarian-project-backup}"
|
||||||
|
|
||||||
|
DRY_RUN=0
|
||||||
|
FORCE=0
|
||||||
|
MANUAL=0
|
||||||
|
|
||||||
|
EXCLUDES=(
|
||||||
|
--exclude "$SOURCE_DIR/DerivedDataCache"
|
||||||
|
--exclude "$SOURCE_DIR/Intermediate"
|
||||||
|
--exclude "$SOURCE_DIR/Saved"
|
||||||
|
--exclude "$SOURCE_DIR/Binaries"
|
||||||
|
--exclude "$SOURCE_DIR/Builds"
|
||||||
|
--exclude "$SOURCE_DIR/.git/index.lock"
|
||||||
|
--exclude "$SOURCE_DIR/.git/lfs/tmp"
|
||||||
|
--exclude "$SOURCE_DIR/.git/logs"
|
||||||
|
--exclude "$SOURCE_DIR/.vs"
|
||||||
|
--exclude "$SOURCE_DIR/AGRARIAN_BACKUP_MANIFEST.txt"
|
||||||
|
--exclude "$SOURCE_DIR/AGRARIAN_CRITICAL_SHA256SUMS"
|
||||||
|
--exclude "$SOURCE_DIR/AGRARIAN_RSYNC_LOG.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
log() {
|
||||||
|
printf '[%s] %s\n' "$(date -Is)" "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die() {
|
||||||
|
log "ERROR: $*"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'USAGE'
|
||||||
|
Usage: agrarian_project_backup.sh [--force] [--manual] [--dry-run]
|
||||||
|
|
||||||
|
Creates an incremental deduplicated restic backup of the Agrarian Unreal project
|
||||||
|
on Linastorage. Generated Unreal cache/build folders are excluded; Git metadata
|
||||||
|
and local source data are included.
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args() {
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--force)
|
||||||
|
FORCE=1
|
||||||
|
;;
|
||||||
|
--manual)
|
||||||
|
MANUAL=1
|
||||||
|
FORCE=1
|
||||||
|
;;
|
||||||
|
--dry-run)
|
||||||
|
DRY_RUN=1
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
die "Unknown argument: $1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
restic_cmd() {
|
||||||
|
RESTIC_PASSWORD_FILE="$RESTIC_PASSWORD_FILE" \
|
||||||
|
restic -r "$RESTIC_REPOSITORY" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
require_environment() {
|
||||||
|
[[ -d "$SOURCE_DIR" ]] || die "Project source directory not found: $SOURCE_DIR"
|
||||||
|
[[ -f "$SOURCE_DIR/AgrarianGame.uproject" ]] || die "Missing AgrarianGame.uproject under $SOURCE_DIR"
|
||||||
|
findmnt -rn "$MOUNT_POINT" >/dev/null || die "$MOUNT_POINT is not mounted"
|
||||||
|
mkdir -p "$BACKUP_ROOT" "$STATE_DIR" "$(dirname "$RESTIC_PASSWORD_FILE")"
|
||||||
|
[[ -w "$BACKUP_ROOT" ]] || die "$BACKUP_ROOT is not writable"
|
||||||
|
command -v restic >/dev/null || die "restic is required"
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_password_file() {
|
||||||
|
if [[ -f "$RESTIC_PASSWORD_FILE" ]]; then
|
||||||
|
chmod 0600 "$RESTIC_PASSWORD_FILE"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Creating root-owned restic password file: $RESTIC_PASSWORD_FILE"
|
||||||
|
openssl rand -base64 48 > "$RESTIC_PASSWORD_FILE"
|
||||||
|
chmod 0600 "$RESTIC_PASSWORD_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_repository() {
|
||||||
|
if [[ -f "$RESTIC_REPOSITORY/config" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Initializing restic repository: $RESTIC_REPOSITORY"
|
||||||
|
mkdir -p "$RESTIC_REPOSITORY"
|
||||||
|
restic_cmd init
|
||||||
|
}
|
||||||
|
|
||||||
|
source_signature() {
|
||||||
|
local tmp
|
||||||
|
tmp="$(mktemp)"
|
||||||
|
|
||||||
|
{
|
||||||
|
git -C "$SOURCE_DIR" rev-parse HEAD 2>/dev/null || true
|
||||||
|
git -C "$SOURCE_DIR" status --porcelain=v1 2>/dev/null || true
|
||||||
|
find "$SOURCE_DIR" \
|
||||||
|
\( -path "$SOURCE_DIR/DerivedDataCache" \
|
||||||
|
-o -path "$SOURCE_DIR/Intermediate" \
|
||||||
|
-o -path "$SOURCE_DIR/Saved" \
|
||||||
|
-o -path "$SOURCE_DIR/Binaries" \
|
||||||
|
-o -path "$SOURCE_DIR/Builds" \
|
||||||
|
-o -path "$SOURCE_DIR/.git/lfs/tmp" \
|
||||||
|
-o -path "$SOURCE_DIR/.git/logs" \
|
||||||
|
-o -path "$SOURCE_DIR/.vs" \) -prune \
|
||||||
|
-o -type f -printf '%P\t%s\t%T@\n' 2>/dev/null \
|
||||||
|
| sort
|
||||||
|
} > "$tmp"
|
||||||
|
|
||||||
|
sha256sum "$tmp" | awk '{print $1}'
|
||||||
|
rm -f -- "$tmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
write_manifest() {
|
||||||
|
local signature="$1"
|
||||||
|
local manifest="$STATE_DIR/LATEST_MANIFEST.txt"
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "backup_timestamp=$(date -Is)"
|
||||||
|
echo "host=$(hostname -f 2>/dev/null || hostname)"
|
||||||
|
echo "source=$SOURCE_DIR"
|
||||||
|
echo "mount=$MOUNT_POINT"
|
||||||
|
echo "repository=$RESTIC_REPOSITORY"
|
||||||
|
echo "signature=$signature"
|
||||||
|
echo "backup_mode=$([[ "$MANUAL" == "1" ]] && echo manual || echo scheduled)"
|
||||||
|
echo "git_head=$(git -C "$SOURCE_DIR" rev-parse HEAD 2>/dev/null || echo unavailable)"
|
||||||
|
echo
|
||||||
|
echo "[excluded]"
|
||||||
|
printf '%s\n' "${EXCLUDES[@]}"
|
||||||
|
} > "$manifest"
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_project() {
|
||||||
|
local signature="$1"
|
||||||
|
local mode_tag="scheduled"
|
||||||
|
[[ "$MANUAL" == "1" ]] && mode_tag="manual"
|
||||||
|
|
||||||
|
log "Starting restic backup for Agrarian project"
|
||||||
|
restic_cmd backup \
|
||||||
|
--one-file-system \
|
||||||
|
--tag agrarian-game \
|
||||||
|
--tag project \
|
||||||
|
--tag "$mode_tag" \
|
||||||
|
"${EXCLUDES[@]}" \
|
||||||
|
"$SOURCE_DIR"
|
||||||
|
|
||||||
|
write_manifest "$signature"
|
||||||
|
printf '%s\n' "$signature" > "$STATE_DIR/LAST_SOURCE_SIGNATURE"
|
||||||
|
|
||||||
|
log "Applying restic retention policy"
|
||||||
|
restic_cmd forget \
|
||||||
|
--tag agrarian-game \
|
||||||
|
--tag project \
|
||||||
|
--keep-within 7d \
|
||||||
|
--keep-daily 30 \
|
||||||
|
--keep-weekly 12 \
|
||||||
|
--keep-monthly 12 \
|
||||||
|
--prune
|
||||||
|
|
||||||
|
restic_cmd snapshots --tag agrarian-game --tag project > "$STATE_DIR/LATEST_SNAPSHOTS.txt"
|
||||||
|
log "Project backup completed"
|
||||||
|
logger -t "$LOG_TAG" "restic project backup completed successfully"
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
exec 9>"$LOCK_FILE"
|
||||||
|
if ! flock -n 9; then
|
||||||
|
log "Another Agrarian project backup is already running; skipping this trigger"
|
||||||
|
logger -t "$LOG_TAG" "another project backup is already running; skipped"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
require_environment
|
||||||
|
ensure_password_file
|
||||||
|
ensure_repository
|
||||||
|
|
||||||
|
local signature previous_signature
|
||||||
|
signature="$(source_signature)"
|
||||||
|
previous_signature="$(cat "$STATE_DIR/LAST_SOURCE_SIGNATURE" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ "$FORCE" != "1" && "$signature" == "$previous_signature" ]]; then
|
||||||
|
log "No project changes detected since latest restic backup; skipping"
|
||||||
|
logger -t "$LOG_TAG" "no project changes detected; skipped"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == "1" ]]; then
|
||||||
|
log "Dry run: project changes detected; restic snapshot would be created"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
backup_project "$signature"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user