mcpField
P
post-deploy

post-deploy

github.com/fellowship-devCloud & DevOps3122 installsUpdated 2w ago
Repository

About post-deploy

post-deploy is a post-deployment pipeline skill that matches changed PR files against team-configured rules and runs corresponding checks or actions. It supports ECR image rebuilds, config reload verification, smoke tests, and custom commands.

Key features

  1. 01Match files against team rules
  2. 02Rebuild and push ECR images
  3. 03Verify config reloads and executors
  4. 04Run smoke tests or custom commands
  5. 05Comment on PRs and write reports

Use cases

  1. 01Run targeted checks after deployment confirmation
  2. 02Rebuild container images after code changes
  3. 03Check whether updated configuration has loaded
  4. 04Summarize post-deploy actions on a PR

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/fellowship-dev/dogfooded-skills --skill post-deploy

After installing, type/post-deployin Claude Code to confirm it loaded.

Details

From the project README

post-deploy

Stage 3 of the autonomous deployment pipeline. Triggered by the deployed label. Reads the PR's changed files, matches them against configured patterns, and runs targeted actions: ECR image rebuilds, config reload verification, smoke tests, or custom scripts.

When to Use

  • Triggered by deployed label via event-rules (after deployment-checker confirms live deploy)
  • Manual: /post-deploy PR_NUMBER org/repo

Invocation

/post-deploy 42 fellowship-dev/pylot

Runbook

Step 0: Dedup Gate
PR=$1
REPO=$2

# Check if post-deploy already ran (look for a post-deploy comment)
EXISTING=$(gh pr view $PR --repo $REPO --json comments \
  --jq '.comments[].body' | grep "Post-deploy actions" || true)
if [ -n "$EXISTING" ]; then
  echo "[post-deploy] outcome=\"already complete — post-deploy comment found\" status=success"
  exit 0
fi
Step 1: Read PR Context
PR_DATA=$(gh pr view $PR --repo $REPO --json number,title,url,files,mergeCommit,baseRefName)
PR_TITLE=$(echo "$PR_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin)['title'])")
PR_URL=$(echo "$PR_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin)['url'])")
CHANGED_FILES=$(echo "$PR_DATA" | python3 -c "
import json,sys
d = json.load(sys.stdin)
print('\n'.join(f['path'] for f in d['files']))
")

echo "PR: $PR — $PR_TITLE"
echo "Changed files:"
echo "$CHANGED_FILES"
Step 2: Read Team Post-Deploy Configuration

Read the team's CLAUDE.md for post_deploy: rules. Each rule maps a file glob pattern to an action:

# Example in team CLAUDE.md:
post_deploy:
  - match: "Dockerfile*"
    action: ecr-rebuild
    description: "Rebuild and push ECR image"
  - match: "docker-entrypoint.sh"
    action: ecr-rebuild
  - match: "crew.yml"
    action: config-reload-verify
    description: "Verify gateway reloaded updated config"
  - match: "scripts/*.sh"
    action: executor-verify
    description: "Spot-check executor behavior"
  - match: "event-rules.yml"
    action: config-reload-verify
  - match: "**"
    action: smoke-test
    optional: true
    description: "Optional smoke test for any PR"

Action types:

ActionDescription
ecr-rebuildRebuild Docker image and push to ECR
config-reload-verifyVerify the process/gateway has reloaded the updated config
executor-verifySpot-check executor or script behavior
smoke-testRun team-defined smoke test
customRun command field as shell command
Step 3: Match Files to Actions
# Pseudocode — implement in bash with Python for glob matching
import fnmatch

changed = CHANGED_FILES.splitlines()
triggered_actions = []

for rule in post_deploy_rules:
    pattern = rule['match']
    matching = [f for f in changed if fnmatch.fnmatch(f, pattern)]
    if matching:
        triggered_actions.append({
            'action': rule['action'],
            'description': rule.get('description', rule['action']),
            'matched_files': matching,
            'optional': rule.get('optional', False)
        })
Step 4: Execute Actions

Run each triggered action. Document result for each.

Action: ecr-rebuild
# Rebuild and push ECR image
PYLOT_DIR="${PYLOT_DIR:-$HOME/projects/fellowship-dev/pylot}"
ECR_URI="${ECR_URI:-$(grep 'fargate_ecr_uri' $PYLOT_DIR/crew.yml | awk '{print $2}')}"

cd "$PYLOT_DIR"
bash scripts/build-worker-image.sh 2>&1
ECR_EXIT=$?
[ $ECR_EXIT -eq 0 ] && echo "ecr-rebuild: success" || echo "ecr-rebuild: failed exit=$ECR_EXIT"
Action: config-reload-verify
# Verify the executor/gateway has reloaded the updated config
# For Pylot: check that crew.yml timestamp matches what executor loaded
PYLOT_DIR="${PYLOT_DIR:-$HOME/projects/fellowship-dev/pylot}"

# Check if executor is running and has loaded recent config
CREW_MTIME=$(stat -c %Y "$PYLOT_DIR/crew.yml" 2>/dev/null || stat -f %m "$PYLOT_DIR/crew.yml" 2>/dev/null)
EXECUTOR_PID=$(pgrep -f "executor.sh" || true)

if [ -n "$EXECUTOR_PID" ]; then
  echo "config-reload-verify: executor running (pid=$EXECUTOR_PID). crew.yml mtime=$CREW_MTIME."
  echo "config-reload-verify: success — executor will pick up config on next poll cycle"
else
  echo "config-reload-verify: WARNING — executor not running. Config will load on restart."
fi
Action: executor-verify
# Spot-check executor behavior — run a quick test
PYLOT_DIR="${PYLOT_DIR:-$HOME/projects/fellowship-dev/pylot}"

# Syntax-check the modified scripts
for script in $MATCHED_SCRIPTS; do
  bash -n "$PYLOT_DIR/$script" && echo "executor-verify: $script syntax OK" || echo "executor-verify: $script SYNTAX ERROR"
done
Action: smoke-test

Run the team's configured smoke test. Optional by default.

# Team-specific smoke test — read from CLAUDE.md smoke_test.command
# Example for Pylot: run a dry-run event dispatch
PYLOT_DIR="${PYLOT_DIR:-$HOME/projects/fellowship-dev/pylot}"
bash "$PYLOT_DIR/event-router.sh" --dry-run 2>&1 | head -20
echo "smoke-test: event-router dry-run complete"
Step 5: Post Summary Comment
# Build table rows before heredoc (<<'EOF' prevents expansion — pre-build instead)
ACTION_TABLE=""
for action_entry in $TRIGGERED_ACTION_ENTRIES; do
  ACTION_NAME=$(echo "$action_entry" | cut -d: -f1)
  ACTION_FILES=$(echo "$action_entry" | cut -d: -f2)
  ACTION_RESULT=$(echo "$action_entry" | cut -d: -f3)
  ACTION_TABLE="${ACTION_TABLE}| ${ACTION_NAME} | ${ACTION_FILES} | ${ACTION_RESULT} |\n"
done
[ -z "$ACTION_TABLE" ] && ACTION_TABLE="_No file-match rules triggered for this PR's changed files._\n"

gh pr comment $PR --repo $REPO --body "$(printf '%s' "## Post-deploy actions

| Action | Matched Files | Result |
|--------|--------------|--------|
$(printf '%b' "$ACTION_TABLE")
**Pipeline complete.** This PR is live and verified.")"
Step 6: Write Report
PYLOT_DIR="${PYLOT_DIR:-$HOME/projects/fellowship-dev/pylot}"
REPORT="$PYLOT_DIR/reports/$(date +%Y-%m-%d)-post-deploy-$(echo $REPO | tr '/' '-')-pr${PR}.md"

cat > "$REPORT" <<EOF
# Post-Deploy: $REPO PR #$PR — $PR_TITLE

**Date**: $(date +%Y-%m-%d)
**PR**: [$REPO#$PR]($PR_URL)

## Changed Files
$CHANGED_FILES

## Actions Triggered
$TRIGGERED_ACTIONS_SUMMARY

## Results
$ACTIONS_RESULTS
EOF

Default File-Match Rules (Pylot)

PatternActionWhen
Dockerfile*ecr-rebuildDocker image changed
docker-entrypoint.shecr-rebuildEntrypoint script changed
crew.ymlconfig-reload-verifyCrew config changed
event-rules.ymlconfig-reload-verifyEvent routing changed
scripts/*.shexecutor-verifyShell scripts changed
dispatch.shexecutor-verifyDispatch logic changed
executor.shexecutor-verifyExecutor logic changed

Notes

  • No matched rules = no actions. A PR touching only Markdown files does nothing. This is correct behavior.
  • Optional actions (smoke-test) run but do not affect the summary verdict.
  • ecr-rebuild failure should be surfaced as a comment but does not re-apply deploy-failed — the deploy itself was verified. File a follow-up issue instead.
  • Team config is the authority. Generic skill + team CLAUDE.md = full configurability without modifying the skill.
  • Monorepo: match files against per-target rules if the team has multiple deploy targets.

Comments

Markdown supported