name: safe-migrate
description: Produce JSON and Markdown PostgreSQL migration-review artifacts without synchronizing a database.
author: safe-migrate contributors
inputs:
mode:
description: Analyze one migration (`lint`) or an ordered migration directory (`lint-chain`).
required: false
default: lint-chain
path:
description: Migration file for `lint` or directory for `lint-chain`.
required: true
cache:
description: Reviewed local cache path to read when no-cache is false. The action never refreshes it.
required: false
default: .safe-migrate.cache
config:
description: Explicitly trusted safe-migrate TOML path. Empty uses built-in defaults and never reads workspace configuration.
required: false
default: ""
no-cache:
description: Run with an unavailable baseline instead of reading a cache. Defaults to true because repository caches are PR-controlled unless explicitly supplied from a trusted workflow artifact.
required: false
default: "true"
output-dir:
description: Directory for `safe-migrate-report.json` and `safe-migrate-report.md`.
required: false
default: safe-migrate-artifacts
outputs:
json-report:
description: Path to the generated JSON report.
value: ${{ steps.report.outputs.json-report }}
markdown-report:
description: Path to the generated Markdown report.
value: ${{ steps.report.outputs.markdown-report }}
exit-code:
description: "Analyzer exit status: 0 completed, 1 operational failure, 2 blocking findings."
value: ${{ steps.report.outputs.exit-code }}
diagnostic-log:
description: Path to analyzer diagnostics, including the reason for operational failures.
value: ${{ steps.report.outputs.diagnostic-log }}
runs:
using: composite
steps:
- name: Install Rust
uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4
- name: Analyze migrations
id: report
shell: bash
env:
INPUT_MODE: ${{ inputs.mode }}
INPUT_PATH: ${{ inputs.path }}
INPUT_CACHE: ${{ inputs.cache }}
INPUT_CONFIG: ${{ inputs.config }}
INPUT_NO_CACHE: ${{ inputs.no-cache }}
INPUT_OUTPUT_DIR: ${{ inputs.output-dir }}
run: |
set -euo pipefail
case "$INPUT_MODE" in
lint)
command=(lint --file "$INPUT_PATH")
;;
lint-chain)
command=(lint-chain --dir "$INPUT_PATH")
;;
*)
echo "mode must be lint or lint-chain, got: $INPUT_MODE" >&2
exit 1
;;
esac
command+=(--cache "$INPUT_CACHE")
if [ -n "$INPUT_CONFIG" ]; then
command+=(--config "$INPUT_CONFIG")
else
default_config="$(mktemp "${RUNNER_TEMP}/safe-migrate-default.XXXXXX")"
command+=(--config "$default_config")
fi
if [ "$INPUT_NO_CACHE" = "true" ]; then
command+=(--no-cache)
fi
mkdir -p "$INPUT_OUTPUT_DIR"
install_root="${RUNNER_TEMP}/safe-migrate-action"
cargo install --path "$GITHUB_ACTION_PATH" --locked --root "$install_root"
binary="${install_root}/bin/safe-migrate"
if [ "$RUNNER_OS" = "Windows" ]; then
binary="${binary}.exe"
fi
json_report="${INPUT_OUTPUT_DIR}/safe-migrate-report.json"
markdown_report="${INPUT_OUTPUT_DIR}/safe-migrate-report.md"
diagnostic_log="${INPUT_OUTPUT_DIR}/safe-migrate-diagnostics.log"
: > "$diagnostic_log"
# Database synchronization is intentionally outside the Action's
# default path. A separate workflow step can prepare a reviewed cache.
set +e
env -u DATABASE_URL "$binary" "${command[@]}" --json \
> "$json_report" 2> >(tee -a "$diagnostic_log" >&2)
json_status=$?
env -u DATABASE_URL "$binary" "${command[@]}" --markdown \
> "$markdown_report" 2> >(tee -a "$diagnostic_log" >&2)
markdown_status=$?
set -e
final_status="$json_status"
if [ "$json_status" -ne "$markdown_status" ]; then
final_status=1
echo "JSON and Markdown runs returned different statuses: ${json_status} and ${markdown_status}" \
| tee -a "$diagnostic_log" >&2
fi
if [ "$final_status" -eq 1 ]; then
if [ ! -s "$diagnostic_log" ]; then
echo "safe-migrate failed without emitting a diagnostic" \
| tee -a "$diagnostic_log" >&2
fi
printf '%s\n' \
'{' \
' "artifact_schema_version": 1,' \
' "status": "operational_error",' \
' "exit_code": 1,' \
' "message": "safe-migrate did not produce an analysis report; see safe-migrate-diagnostics.log"' \
'}' > "$json_report"
printf '%s\n' \
'# safe-migrate operational error' \
'' \
'Analysis did not complete. See `safe-migrate-diagnostics.log` for the underlying error.' \
> "$markdown_report"
fi
echo "json-report=${json_report}" >> "$GITHUB_OUTPUT"
echo "markdown-report=${markdown_report}" >> "$GITHUB_OUTPUT"
echo "diagnostic-log=${diagnostic_log}" >> "$GITHUB_OUTPUT"
echo "exit-code=${final_status}" >> "$GITHUB_OUTPUT"
exit "$final_status"