Add "Prune Deployed Branches" workflow with Python-based logic#205
Add "Prune Deployed Branches" workflow with Python-based logic#205
Conversation
Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com>
Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com>
|
does this remove the branch history @copilot so that git repo is reduced in size |
Yes — the
After the force-push, all the old deployment commits become unreachable/dangling objects on GitHub's servers. GitHub runs garbage collection automatically on orphaned objects, which permanently removes them and reclaims the storage. There is no way to trigger GitHub's GC manually from a workflow, but the old history is gone from all refs immediately after the push and will be fully purged on the next GC cycle. The net result: |
|
can you also add a prune none - preserves all gh-pages but removes its hustory |
Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com>
Done in commit
|
|
what if there is a branch named NONE ? @copilot |
…anch name collision Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com>
Good catch. Fixed in The three modes remain:
|
✅ Deployment SuccessfulBranch:
Deployment completed successfully! The site is now live at the URL above. |
There was a problem hiding this comment.
Pull request overview
Adds a manually triggered GitHub Actions maintenance workflow to clean up stale branch preview deployments under gh-pages/branches/ and optionally squash the gh-pages history down to a single commit to reduce repository size over time.
Changes:
- Introduces
input/scripts/prune_branches.pyto validate inputs, delete one/allbranches/*previews, and squash+force-pushgh-pages. - Adds
.github/workflows/prune-branches.yml(workflow_dispatch) to run the pruning/squash operation with safety gating (confirm=CONFIRM) and controlled input passing viaenv.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
input/scripts/prune_branches.py |
Implements pruning + orphan-commit squash logic with input validation and git subprocess wrappers. |
.github/workflows/prune-branches.yml |
Adds a workflow_dispatch launcher that checks out gh-pages and runs the pruning script with guarded inputs. |
| "GIT_CONFIG_NOSYSTEM", | ||
| "GIT_CONFIG_COUNT", | ||
| "GIT_CONFIG_KEY_0", | ||
| "GIT_CONFIG_VALUE_0", | ||
| }) |
There was a problem hiding this comment.
The git env sanitization blocklist only removes GIT_CONFIG_KEY_0 / GIT_CONFIG_VALUE_0, but git supports GIT_CONFIG_KEY_<n>/VALUE_<n> for arbitrary indices (controlled by GIT_CONFIG_COUNT) and other config-related env vars (e.g. GIT_CONFIG_PARAMETERS). As written, injected config entries beyond index 0 would still reach git subprocesses. Consider stripping all env vars with a GIT_CONFIG_ prefix (and any other git-redirection vars you intend to defend against), or adjust the security claims in the docstring to match what’s actually blocked.
| - name: Checkout prune script (default branch) | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| sparse-checkout: | | ||
| input/scripts/prune_branches.py |
There was a problem hiding this comment.
This step is labeled as checking out the prune script from the “default branch”, but actions/checkout will use the workflow’s selected ref unless ref: is set. Because workflow_dispatch lets operators choose a branch, this can end up running a different prune_branches.py than intended. Set ref: ${{ github.event.repository.default_branch }} (or explicitly main) for this checkout, and/or prevent dispatches from non-default branches.
| name: Prune Deployed Branches | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: |
There was a problem hiding this comment.
This workflow force-pushes gh-pages, so concurrent manual runs can race and overwrite each other’s result (last writer wins). Add a concurrency group (e.g. keyed on the workflow name) with cancel-in-progress: false to ensure only one prune operation can run at a time.
gh-pages accumulates a commit per deployed branch preview with no mechanism to clean up stale entries. This adds a
workflow_dispatch-triggered workflow that removes one or all subdirectories underbranches/on gh-pages and squashes the branch history down to a single commit.Workflow (
prune-branches.yml)target_branch(optional),squash_only(boolean, default false), andconfirm(must be the literal stringCONFIRMas a safety gate)prune_branches.pyfrom the default branch, then checks outgh-pagesto a separate path — no overlap between the twoenv:block; never interpolated into the shell command stringScript (
input/scripts/prune_branches.py)All logic is in Python; the YAML is a thin launcher.
Three pruning modes:
squash_onlytarget_branchbranches/, squash historymy-featurebranches/my-feature/, squash historybranches/intact, squash history onlyThe
squash_onlymode is a dedicated boolean input rather than a magic sentinel string, so branch names likeNONEare handled normally.Injection / tampering protections:
os.environonly — notsys.argv— so shell metacharacters in workflow inputs are inertGIT_ENV_BLOCKLISTstripsGIT_DIR,GIT_WORK_TREE,GIT_INDEX_FILE, and related vars from everysubprocesscall, preventing git-redirection via injected env varssubprocesscalls use explicit list args withshell=False.., separators) then canonical (os.path.realpath+os.path.commonpathmust stay insidebranches/)validate_current_branch()aborts unlessgit rev-parse --abbrev-ref HEADreturns exactlygh-pagesHistory squash:
Creates an orphan branch from the post-deletion working tree and force-pushes it to
origin/gh-pages, collapsing all accumulated deployment history to a single commit. The squash-only mode usesforce=Trueso the orphan commit is created even when no files were deleted.Dynamic branch listing:
GitHub Actions
workflow_dispatchonly supports statically-definedchoicelists. As the best practical alternative, the script prints every subdirectory ofbranches/at the start of each run so operators can identify valid names before re-running with a specific target.Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.