Skip to content

Generalize Backup command - #14

Merged
mlaurense merged 4 commits into
mainfrom
feature/backup-output-dir
Aug 28, 2026
Merged

Generalize Backup command#14
mlaurense merged 4 commits into
mainfrom
feature/backup-output-dir

Conversation

@marcepartment

Copy link
Copy Markdown
Collaborator

Add argument options for an output path.

marcepartment and others added 3 commits August 26, 2026 13:52
Lets a caller place the finished archive outside the project, name it, and
keep the restorable directory form in place. Together these are what the
Epartment build server currently needs its own backup command override for.

Volumes are still staged in the project's .roll/backups/ and retention
cleanup still only ever runs against that directory, so pointing
--output-dir at a shared delivery directory cannot delete another
environment's archives. The "latest" symlink is likewise only maintained
when the archive stays in .roll/backups/, since a shared directory would
have every environment fighting over one symlink.

The destination is created and checked for writability before `env down`
and before any volume is tarred, so an unusable path fails in seconds
rather than after a multi-hour backup.

--keep-dir suppresses the post-archive `rm -rf` of the backup directory.
`roll restore` reads the directory form, so a workflow that archives the
whole workspace after a backup only captures a restorable copy while that
directory still exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
backup is on ROLL_CMD_ANYARGS, so roll's parser stops at --help and hands
it to backup.cmd unchanged. The handler called `roll backup --help`,
which re-entered the same branch and never terminated. Render the usage
directly instead.

The same pattern is present in restore.cmd, restore-full.cmd,
duplicate.cmd, env.cmd, db.cmd and svc.cmd and is left untouched here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

This PR generalizes the roll backup command by adding CLI options to control where the final backup archive is written and how it is named, while keeping staging/retention behavior anchored to the project’s .roll/backups directory.

Changes:

  • Add --output-dir, --archive-name, and --keep-dir options to backup and document them in help text.
  • Validate/create the output directory up front and route archive creation to the resolved destination (with conditional latest symlink behavior).
  • Bump the project version to 0.7.1.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
version Version bump to reflect the new backup CLI behavior.
commands/backup.help Documents new output-related flags and clarifies behavior/notes.
commands/backup.cmd Implements output directory resolution, archive naming, and directory retention controls.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread commands/backup.cmd
## Volumes are always staged inside the project: that is where the disk space is budgeted, and
## retention cleanup must never be pointed at a shared drop directory holding other projects'
## archives. --output-dir only moves the finished archive.
BACKUP_BASE_DIR="$(pwd)/.roll/backups"
Comment thread commands/backup.cmd Outdated
Comment on lines +870 to +872
if [[ "${BACKUP_OUTPUT_DIR}" == "${BACKUP_BASE_DIR}" ]]; then
(cd "${BACKUP_BASE_DIR}" && ln -sf "$archive_name" "latest$(getCompressionExtension)")
fi
Comment thread commands/backup.cmd
Comment on lines +43 to +47
## Do NOT re-invoke `roll backup --help` here. `backup` is on roll's ROLL_CMD_ANYARGS
## list, so roll's own parser stops at --help and passes it straight through to this
## script — re-invoking roll lands right back on this branch and recurses forever.
## usage.cmd renders ROLL_CMD_HELP (backup.help) and exits on its own.
source "${ROLL_DIR}/commands/usage.cmd"
Comment thread commands/backup.cmd
Comment on lines 852 to 855
# Create compressed archive for the entire backup
local archive_name="backup_${ROLL_ENV_NAME}_${timestamp}$(getCompressionExtension)"
local archive_name="${BACKUP_ARCHIVE_NAME:-backup_${ROLL_ENV_NAME}_${timestamp}}$(getCompressionExtension)"
local archive_path="${BACKUP_OUTPUT_DIR}/${archive_name}"
logMessage INFO "Creating final backup archive: $archive_name"
Comment thread commands/backup.cmd
Comment on lines 860 to 864
if [[ $BACKUP_OUTPUT_ID -eq 1 ]]; then
(cd "$(pwd)/.roll/backups" && tar -cf - "$timestamp" 2>/dev/null | $(getCompressionCommand) > "$archive_name")
(cd "${BACKUP_BASE_DIR}" && tar -cf - "$timestamp" 2>/dev/null | $(getCompressionCommand) > "$archive_path") || archive_status=$?
else
(cd "$(pwd)/.roll/backups" && tar -cf - "$timestamp" | $(getCompressionCommand) > "$archive_name")
(cd "${BACKUP_BASE_DIR}" && tar -cf - "$timestamp" | $(getCompressionCommand) > "$archive_path") || archive_status=$?
fi
- Set pipefail around the final tar|compress pipeline. roll sets `set -e`
  but never pipefail, so the pipeline reported the compressor's status:
  gzip exits 0 on a truncated stream, so a tar that ran out of disk was
  recorded as a successful backup and the uncompressed copy was then
  deleted. Verified: `(false | cat)` returns 0, with pipefail returns 1.

- Reject a path separator in --archive-name. It names a file inside the
  output directory, so `--archive-name=../x` wrote outside it and past the
  writability checks.

- Decide the "latest" symlink from whether --output-dir was given rather
  than by comparing it to the staging path. The same directory compares
  unequal as a string once a relative argument or a symlinked path is
  involved, which skipped the symlink for a backup that never left the
  project.

- Document why BACKUP_BASE_DIR stays keyed on the working directory: it is
  pre-existing behaviour that restore.cmd shares, and changing it only here
  would make backups land where restore does not look.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

commands/backup.help:52

  • The --keep-dir help text says backups are only restorable when the uncompressed directory is kept, but roll restore supports restoring from the archive as well (it extracts internally). This is misleading documentation; --keep-dir is optional and mainly useful for inspection or leaving extracted contents in place.
  --keep-dir            Keep the uncompressed backup directory in .roll/backups/ as well as
                        the archive (removed by default). 'roll restore' reads the directory
                        form, so this is what leaves a restorable backup in the project.

Comment thread commands/backup.cmd
Comment on lines +120 to +124
--output-dir=*)
BACKUP_OUTPUT_DIR="${1#*=}"
BACKUP_OUTPUT_REDIRECTED=1
shift
;;
Comment thread commands/backup.cmd
Comment on lines +906 to +909
## Clean up the directory version, which the archive already contains. --keep-dir
## retains it for a caller that needs a restorable backup left in place — restore
## reads the directory form, so archiving the workspace after a backup only yields a
## restorable copy while the directory is still there.
@mlaurense
mlaurense merged commit 594efa2 into main Aug 28, 2026
1 of 3 checks passed
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.

3 participants