Skip to content

Feature/add symlink and sql flows - #5

Draft
leglesslizard wants to merge 58 commits into
mainfrom
feature/add-symlink-and-sql-flows
Draft

Feature/add symlink and sql flows#5
leglesslizard wants to merge 58 commits into
mainfrom
feature/add-symlink-and-sql-flows

Conversation

@leglesslizard

@leglesslizard leglesslizard commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

When merging in we'll need the following to get notifications to work:

  • Add SMTP_SERVER, SMTP_USERNAME, SMTP_PASSWORD at organisation level (used by the failure notification email)
  • NOTIFY_EMAIL is optional per-repo - defaults to #uptime_alerts if omitted (email is harcoded for now, I assume we'll want this to go to something@pie.co.de)

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

Adds an “Atomic Deploy” flow for WordPress deployments that can (a) run SQL migrations against a copied table prefix and then switch wp-config.php to the new prefix, and (b) atomically re-point plugin/theme symlinks to a release directory for fast rollbacks. This is implemented via new reusable workflow(s), a composite action that runs the remote swap script over SSH, and migration script templates, with README documentation updates.

Changes:

  • Added templates/migrations/swap.sh + templates/migrations/migrate.sh to support prefix-copy migrations and symlink swapping during deploys.
  • Added actions/swap-and-migrate composite action and .github/workflows/atomic-deploy.yaml reusable workflow to run the remote deploy step and send notifications.
  • Expanded README.md documentation for Atomic Deploy, rsync/FTP deploys, and templates.

Reviewed changes

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

Show a summary per file
File Description
templates/migrations/swap.sh Remote deploy script: preflight, migration/prefix-swap, symlink swap, and release pruning logic.
templates/migrations/migrate.sh Migration runner that applies pending SQL files and records them in a tracking table.
actions/swap-and-migrate/action.yml Composite action that SSHes to the server to run swap.sh, then emails on failure based on exit codes.
.github/workflows/atomic-deploy.yaml Reusable workflow wiring SSH setup + swap-and-migrate action with SMTP/notification secrets.
README.md Documents the new Atomic Deploy flow and updates other workflow/action documentation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread templates/migrations/swap.sh Outdated
Comment thread templates/migrations/swap.sh Outdated
Comment thread actions/swap-and-migrate/scripts/migrate.sh
Comment thread actions/swap-and-migrate/action.yml Outdated
Comment thread actions/swap-and-migrate/action.yml
Comment thread actions/swap-and-migrate/action.yml Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated

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 5 out of 5 changed files in this pull request and generated 9 comments.

Comment thread actions/swap-and-migrate/action.yml Outdated
Comment thread .github/workflows/atomic-deploy.yaml
Comment thread templates/migrations/swap.sh Outdated
Comment thread templates/migrations/swap.sh
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md
Comment thread README.md Outdated
@leglesslizard
leglesslizard requested a review from Copilot June 30, 2026 11:30

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 8 out of 9 changed files in this pull request and generated 11 comments.

Comment thread .github/workflows/deploy.yaml
Comment thread .github/workflows/deploy.yaml
Comment thread .github/workflows/deploy.yaml
Comment thread .github/workflows/atomic-deploy.yaml
Comment thread .github/workflows/atomic-deploy.yaml
Comment thread actions/swap-and-migrate/scripts/swap.sh
Comment thread actions/swap-and-migrate/scripts/swap.sh
Comment thread actions/swap-and-migrate/action.yml
Comment thread actions/swap-and-migrate/action.yml Outdated
Comment thread README.md Outdated

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 9 out of 10 changed files in this pull request and generated 4 comments.

Suppressed comments (11)

.github/workflows/deploy.yaml:97

  • This reusable workflow will remain pinned to the feature branch after merge. If that temporary branch is deleted, every deployment fails while resolving the action. Point the action back to the stable main ref.
      - uses: pie/.github/actions/deploy-via-rsync@feature/add-symlink-and-sql-flows

.github/workflows/atomic-deploy.yaml:64

  • The new reusable workflow is pinned to the temporary feature branch. Once that branch is deleted after merge, atomic deployments fail before connecting to the server. Use the stable main ref.
      - uses: pie/.github/actions/add-ssh-config@feature/add-symlink-and-sql-flows

.github/workflows/deploy.yaml:88

  • This reusable workflow will remain pinned to the feature branch after merge. If that temporary branch is deleted, password-based deployments fail while resolving the action. Point the action back to the stable main ref.
      - uses: pie/.github/actions/add-ssh-pass@feature/add-symlink-and-sql-flows

.github/workflows/atomic-deploy.yaml:72

  • The core atomic-deploy action is pinned to the temporary feature branch, so callers of the merged workflow will fail to resolve it if the branch is deleted. Use the stable main ref.
      - uses: pie/.github/actions/swap-and-migrate@feature/add-symlink-and-sql-flows

.github/workflows/deploy.yaml:79

  • This reusable workflow will remain pinned to the feature branch after merge. If that temporary branch is deleted, every rsync deployment fails while resolving the action. Point the action back to the stable main ref.
      - uses: pie/.github/actions/add-ssh-config@feature/add-symlink-and-sql-flows

actions/swap-and-migrate/scripts/swap.sh:341

  • Without pending migrations, maintenance mode is never enabled and SAFE_TO_RECOVER remains true. A failure after moving one live component (or between these two mv calls) can therefore leave a missing or mixed-version deployment, yet cleanup neither restores .previous nor marks the state urgent. Keep prior directories until all swaps succeed and add rollback handling for partially completed component swaps (or otherwise enter a protected maintenance state before the first live rename).
    if [ -e "$LIVE_PATH" ] || [ -L "$LIVE_PATH" ]; then
        mv "$LIVE_PATH" "$OLD_PATH"
    fi
    mv "$STAGING_PATH" "$LIVE_PATH"
    rm -rf "$OLD_PATH"

actions/swap-and-migrate/scripts/swap.sh:272

  • Each table is copied independently with no transaction or table lock. Maintenance mode does not stop in-flight requests, WP-CLI jobs, or other database clients, so concurrent writes can produce a new prefix containing an inconsistent cross-table snapshot before it becomes live. Take the clone from a consistent snapshot or hold appropriate locks for the copy.
        wp db query "INSERT INTO \`$NEW_TABLE\` SELECT * FROM \`$TABLE\`" --path="$WP_ROOT"

actions/swap-and-migrate/scripts/swap.sh:25

  • The 53-character cap does not account for the table prefix or the 11-character _migrations suffix. For a 53-character repository slug, even a one-character live prefix produces a 65-character identifier, exceeding MySQL's 64-character table-name limit; the new SHA prefix is longer still. Derive the maximum slug length from the active/new prefixes and reject or deterministically shorten longer names.
REPO_SLUG="$(printf '%s' "$REPO_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/_/g' | cut -c1-53)"

actions/swap-and-migrate/action.yml:102

  • Only exit codes 1 and 2 have notification steps. Other normal failure statuses—such as rsync's 23 or SSH's 255—skip both emails even though the job fails, so the promised failure notification is absent and remote state may be unknown. Normalize all script failures into defined states or add a third notification path for unclassified failures.
    - name: Send failure notification — site recovered
      if: steps.swap.outcome == 'failure' && steps.swap.outputs.ssh_exit == '1' && inputs.smtp-server != '' && inputs.smtp-username != '' && inputs.smtp-password != '' && inputs.notify-email != ''

.github/workflows/atomic-deploy.yaml:30

  • The implementation defaults to $WP_ROOT/releases, which is a subdirectory inside wp-root, not a sibling. This public input description can make callers configure access controls or paths incorrectly.
        description: "Absolute path to the releases directory on the server. Defaults to a 'releases' sibling of wp-root when not set."

README.md:332

  • This path uses the full 40-character SHA, but atomic-deploy.yaml computes an 8-character SHA and swap.sh looks under that short-SHA directory. Following this instruction makes the atomic step fail with “Release directory ... not found.” Use the setup workflow output shown in the complete example above.
The calling workflow should rsync `migrations/` to `releases/${{ github.sha }}/migrations` and pass the component list to the `atomic-deploy` workflow:

Comment thread actions/swap-and-migrate/scripts/swap.sh Outdated
Comment thread actions/swap-and-migrate/scripts/swap.sh
Comment thread actions/swap-and-migrate/scripts/swap.sh
Comment thread actions/swap-and-migrate/action.yml
leglesslizard and others added 5 commits August 18, 2026 16:02
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…ithub into feature/add-symlink-and-sql-flows

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 9 out of 10 changed files in this pull request and generated no new comments.

Suppressed comments (10)

.github/workflows/deploy.yaml:79

  • These reusable workflows reference actions on a feature branch ref (@feature/add-symlink-and-sql-flows). After the PR merges, that branch ref may be deleted or moved, breaking downstream repos that use this workflow. Other workflows in this repo pin to @main (e.g. .github/workflows/synchronise.yaml:34).
      - uses: pie/.github/actions/add-ssh-config@feature/add-symlink-and-sql-flows

.github/workflows/deploy.yaml:88

  • These reusable workflows reference actions on a feature branch ref (@feature/add-symlink-and-sql-flows). After the PR merges, that branch ref may be deleted or moved, breaking downstream repos that use this workflow. Other workflows in this repo pin to @main (e.g. .github/workflows/synchronise.yaml:34).
      - uses: pie/.github/actions/add-ssh-pass@feature/add-symlink-and-sql-flows

.github/workflows/deploy.yaml:97

  • These reusable workflows reference actions on a feature branch ref (@feature/add-symlink-and-sql-flows). After the PR merges, that branch ref may be deleted or moved, breaking downstream repos that use this workflow. Other workflows in this repo pin to @main (e.g. .github/workflows/synchronise.yaml:34).
      - uses: pie/.github/actions/deploy-via-rsync@feature/add-symlink-and-sql-flows

.github/workflows/atomic-deploy.yaml:64

  • This workflow references actions on a feature branch ref (@feature/add-symlink-and-sql-flows). Once merged, that ref may not exist, breaking callers. Prefer a stable ref like @main (consistent with .github/workflows/synchronise.yaml).
      - uses: pie/.github/actions/add-ssh-config@feature/add-symlink-and-sql-flows

.github/workflows/atomic-deploy.yaml:72

  • This workflow references actions on a feature branch ref (@feature/add-symlink-and-sql-flows). Once merged, that ref may not exist, breaking callers. Prefer a stable ref like @main (or a tag).
      - uses: pie/.github/actions/swap-and-migrate@feature/add-symlink-and-sql-flows

.github/workflows/atomic-deploy.yaml:48

  • This secret is described as defaulting to the #uptime_alerts Slack channel, but the workflow actually hardcodes a Slack channel email address when the secret is unset (see the notify-email: default below). Update the description so callers know they must provide an email address.
      NOTIFY_EMAIL:
        description: "Override recipient email (defaults to #uptime_alerts Slack channel)"
        required: false

actions/swap-and-migrate/action.yml:173

  • Failure notifications are only sent when swap.sh exits with code 1 or 2. Many realistic failures (e.g. rsync/mv errors during component swap, missing release directory, wp-cli failures) will exit with other codes and send no notification even though the deploy failed.

    - name: Fail the job
      if: steps.swap.outcome == 'failure'
      shell: bash
      run: exit 1

.github/workflows/atomic-deploy.yaml:32

  • The releases-dir input description says it defaults to a releases sibling of wp-root, but the swap-and-migrate action (and README) default to wp-root/releases (i.e., inside wp-root). This mismatch is likely to confuse callers.

This issue also appears on line 46 of the same file.

      releases-dir:
        type: string
        description: "Absolute path to the releases directory on the server. Defaults to a 'releases' sibling of wp-root when not set."
        required: false
        default: ""

README.md:75

  • Docs say NOTIFY_EMAIL defaults to the #uptime_alerts Slack channel, but the implementation uses an email recipient (a Slack channel email address) and the action requires an email address for to:. Clarify this to avoid users passing a channel name that can't work with SMTP.
- `SSH_PRIVATE_KEY`: SSH private key. Required.
- `SMTP_SERVER`: SMTP server for failure notifications. Optional — set at organisation level.
- `SMTP_USERNAME`: SMTP username. Optional — set at organisation level.
- `SMTP_PASSWORD`: SMTP password. Optional — set at organisation level.
- `NOTIFY_EMAIL`: Override the notification recipient. Optional — defaults to `#uptime_alerts` Slack channel.

actions/swap-and-migrate/action.yml:78

  • scp uses the unescaped $MIGRATIONS_DIR in the remote destination path. If wp-root/releases-dir ever contains spaces or shell-special characters, the upload step will fail even though you already computed an escaped $MIGRATIONS_DIR_Q for SSH commands.

This issue also appears on line 169 of the same file.

        MIGRATIONS_DIR="$RELEASES_DIR/$GIT_SHA/migrations"
        MIGRATIONS_DIR_Q=$(printf '%q' "$MIGRATIONS_DIR")
        ssh server "mkdir -p $MIGRATIONS_DIR_Q"
        scp "$GITHUB_ACTION_PATH/scripts/swap.sh" "server:$MIGRATIONS_DIR/swap.sh"
        scp "$GITHUB_ACTION_PATH/scripts/migrate.sh" "server:$MIGRATIONS_DIR/migrate.sh"

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.

2 participants