governance: consolidate perfops-consulting-site into the monorepo (site/)#31
Conversation
…ocate prod deploy) - WORKDIR guardrail before rm -rf (refuse empty / / / $HOME) - RUNNER_TRACKING_ID="" so the background smoke server survives the step
gh api does not expand flat bracket-notation keys into nested JSON; the branch-protection endpoint requires a nested body, so pass it on stdin.
There was a problem hiding this comment.
Code Review
This pull request consolidates the perfops-consulting-site repository into the monorepo under site/, adds a manual deployment workflow, updates harden.sh to use a nested JSON body for branch protection API calls, and introduces WORKDIR guardrails in merge.sh. The review feedback identifies several critical issues in the newly generated workflow, including the use of non-existent GitHub Action versions (actions/checkout@v5 and actions/setup-node@v6), incomplete directory copying in appleboy/scp-action due to missing wildcards, and an unused variable. Additionally, it is recommended to expand the WORKDIR guardrails in both scripts to explicitly block relative paths like . and .. to prevent accidental deletions.
| esac | ||
|
|
||
| - name: Checkout | ||
| uses: actions/checkout@v5 |
There was a problem hiding this comment.
| run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v6 |
There was a problem hiding this comment.
| working-directory: site | ||
| steps: | ||
| - name: Checkout tested commit | ||
| uses: actions/checkout@v5 |
There was a problem hiding this comment.
| port: ${{ secrets.VPS_PORT }} | ||
| # source paths are repo-root relative; strip the leading site/ so the | ||
| # VPS tree under target stays identical to the standalone-repo layout. | ||
| source: "site/app,site/components,site/data,site/deploy,site/lib,site/public,site/scripts,site/types,site/package.json,site/package-lock.json,site/next.config.ts,site/postcss.config.mjs,site/tsconfig.json,site/next-env.d.ts,site/eslint.config.mjs,site/proxy.ts,site/README.md,site/.env.example" |
There was a problem hiding this comment.
In appleboy/scp-action, specifying directory names directly (e.g., site/app) only copies the directory itself (often as an empty directory) rather than recursively copying its contents. To ensure all files and subdirectories are uploaded to the VPS, you should append /** to each directory path.
| source: "site/app,site/components,site/data,site/deploy,site/lib,site/public,site/scripts,site/types,site/package.json,site/package-lock.json,site/next.config.ts,site/postcss.config.mjs,site/tsconfig.json,site/next-env.d.ts,site/eslint.config.mjs,site/proxy.ts,site/README.md,site/.env.example" | |
| source: "site/app/**,site/components/**,site/data/**,site/deploy/**,site/lib/**,site/public/**,site/scripts/**,site/types/**,site/package.json,site/package-lock.json,site/next.config.ts,site/postcss.config.mjs,site/tsconfig.json,site/next-env.d.ts,site/eslint.config.mjs,site/proxy.ts,site/README.md,site/.env.example" |
|
|
||
| db="$(gh api "/repos/$OWNER/$SRC" --jq '.default_branch' 2>/dev/null || echo main)" | ||
| # Guardrail: never rm -rf an empty, root, or $HOME path (e.g. WORKDIR mis-set). | ||
| if [ -z "${WORKDIR:-}" ] || [ "$WORKDIR" = "/" ] || [ "$WORKDIR" = "$HOME" ]; then |
There was a problem hiding this comment.
The guardrail check for WORKDIR prevents dangerous deletions of / or $HOME, but it does not check for . or .. (or ./ and ../). If WORKDIR is accidentally set to . or .., rm -rf "$WORKDIR" will delete the current or parent directory, which can be disastrous. Consider expanding the guardrail to explicitly block these relative paths.
| if [ -z "${WORKDIR:-}" ] || [ "$WORKDIR" = "/" ] || [ "$WORKDIR" = "$HOME" ]; then | |
| if [ -z "${WORKDIR:-}" ] || [ "$WORKDIR" = "/" ] || [ "$WORKDIR" = "$HOME" ] || [ "$WORKDIR" = "." ] || [ "$WORKDIR" = ".." ] || [ "$WORKDIR" = "./" ] || [ "$WORKDIR" = "../" ]; then |
| HEALTH_RESULT="ok" | ||
| fi | ||
| BASE="${PROD_BASE_URL:-https://maslinka.ohbah.com:8443/}" | ||
| CONTRACT_PATHS="$(node scripts/ci/print-prod-smoke-paths.mjs | tr -d '\n')" |
|
|
||
| if [ "$APPLY" = "true" ]; then | ||
| # Guardrail: never rm -rf an empty, root, or $HOME path (e.g. WORKDIR mis-set). | ||
| if [ -z "${WORKDIR:-}" ] || [ "$WORKDIR" = "/" ] || [ "$WORKDIR" = "$HOME" ]; then |
There was a problem hiding this comment.
The guardrail check for WORKDIR prevents dangerous deletions of / or $HOME, but it does not check for . or .. (or ./ and ../). If WORKDIR is accidentally set to . or .., rm -rf "$WORKDIR" will delete the current or parent directory, which can be disastrous. Consider expanding the guardrail to explicitly block these relative paths.
| if [ -z "${WORKDIR:-}" ] || [ "$WORKDIR" = "/" ] || [ "$WORKDIR" = "$HOME" ]; then | |
| if [ -z "${WORKDIR:-}" ] || [ "$WORKDIR" = "/" ] || [ "$WORKDIR" = "$HOME" ] || [ "$WORKDIR" = "." ] || [ "$WORKDIR" = ".." ] || [ "$WORKDIR" = "./" ] || [ "$WORKDIR" = "../" ]; then |
…RACT_PATHS Action versions (checkout@v5, setup-node@v6) and bare-dir scp source are kept: both verified valid and copied as-is from the live prod-deploy workflow.
|
Thanks @gemini-code-assist — addressed the review: Applied
Not applied (verified false positives)
This is a faithful relocation of an already-running prod workflow; trigger stays Generated by Claude Code |
|
Thanks for the detailed update, @muntianus. The |
Clean replacement for #30 (that branch was the stale post-squash consolidation
branch and conflicted with
mainby re-adding 20 already-merged files). Thisbranch is cut fresh from
main, so the only diff is the new script + two reviewfixes — no conflict.
New:
governance/operations/collapse-consulting-site.shFull consolidation of the public Next.js site into the monorepo (run it in your
shell — MCP cannot
git subtree):git subtree add --prefix=site— mergesperfops-consulting-siteintosite/, full history preserved..github/workflows/deploy-site.yml, path-adjusted forsite/(
working-directory: site,cache-dependency-path: site/package-lock.json,scp
source:prefixed withsite/+strip_components: 1). VPS layout,service (
perfops-site.service) and health port (3001) unchanged; triggerstays
workflow_dispatchonly.Review fixes (from gemini-code-assist on #30)
gh api --input -(flatkey[sub]=...form is not expanded byghand theendpoint 422s).
RUNNER_TRACKING_ID=""before the backgroundsmoke server so the runner's process reaper doesn't kill it between steps.
rm -rf "$WORKDIR"against empty /
//$HOME.All scripts shellcheck-clean. Nothing is deleted or deployed by this PR — the
script never deletes; delete the source only after a green monorepo deploy.
Generated by Claude Code