mirror of
https://github.com/kellymichels/zscripts-token-savers
synced 2026-10-08 07:28:18 +00:00
Compare commits
No commits in common. "master" and "v1.0.0.0.24" have entirely different histories.
master
...
v1.0.0.0.2
14
.gitattributes
vendored
14
.gitattributes
vendored
@ -4,18 +4,8 @@
|
||||
*.ps1 text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
|
||||
# Every .txt here is either a generated twin or a manifest, and all of them
|
||||
# want LF. plaintext_twins.py writes LF and git stores LF, but without this
|
||||
# pin checkout applied core.autocrlf and handed the working tree CRLF - so
|
||||
# every regeneration rewrote the file to LF, `git status` reported a change,
|
||||
# and `git add` normalized it straight back to nothing (#88). Pinning the
|
||||
# checkout makes all three agree.
|
||||
*.txt text eol=lf
|
||||
|
||||
# Kept explicit even though *.txt already covers it: the checksum manifest
|
||||
# must stay LF because `sha256sum -c` treats a trailing CR as part of the
|
||||
# filename and reports every entry as missing. That is a broken verification,
|
||||
# not a cosmetic diff, and it should not depend on a glob above it.
|
||||
# The checksum manifest must stay LF: `sha256sum -c` treats a trailing CR as
|
||||
# part of the filename and reports every entry as missing.
|
||||
CHECKSUMS.txt text eol=lf
|
||||
releases/*.sha256 text eol=lf
|
||||
|
||||
|
||||
51
.github/workflows/rerun-timed-out.yml
vendored
51
.github/workflows/rerun-timed-out.yml
vendored
@ -1,51 +0,0 @@
|
||||
# Re-runs a test run once when one of its jobs ran out of time
|
||||
# (evo.testsuites#25, part 2).
|
||||
#
|
||||
# A timeout usually means a stuck runner or a network stall rather than a
|
||||
# broken test, so the first one gets a second chance. A second timeout stays
|
||||
# red, so a real hang still reaches a person. A run cancelled by hand, or by a
|
||||
# newer push, is left alone: only a job whose own record says it "exceeded the
|
||||
# maximum execution time" counts.
|
||||
#
|
||||
# Costs nothing on an ordinary run. The job's `if` is false for every run that
|
||||
# ended any other way, and a job skipped by its `if` never starts a runner.
|
||||
name: re-run a timed-out test run
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["tests"]
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
checks: read
|
||||
|
||||
jobs:
|
||||
rerun:
|
||||
if: >-
|
||||
github.event.workflow_run.run_attempt == 1 &&
|
||||
(github.event.workflow_run.conclusion == 'cancelled' ||
|
||||
github.event.workflow_run.conclusion == 'timed_out')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Re-run it if a job ran out of time
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
RUN: ${{ github.event.workflow_run.id }}
|
||||
run: |
|
||||
timed_out=""
|
||||
for job in $(gh api "repos/$REPO/actions/runs/$RUN/jobs" \
|
||||
--jq '.jobs[] | select(.conclusion == "cancelled" or .conclusion == "timed_out") | .id'); do
|
||||
if gh api "repos/$REPO/check-runs/$job/annotations" --jq '.[].message' \
|
||||
| grep -q "exceeded the maximum execution time"; then
|
||||
timed_out="$job"
|
||||
fi
|
||||
done
|
||||
if [ -n "$timed_out" ]; then
|
||||
echo "Job $timed_out ran out of time. Re-running run $RUN once."
|
||||
gh api -X POST "repos/$REPO/actions/runs/$RUN/rerun"
|
||||
else
|
||||
echo "Run $RUN was cancelled, but not by a timeout. Leaving it."
|
||||
fi
|
||||
80
.github/workflows/tests.yml
vendored
80
.github/workflows/tests.yml
vendored
@ -1,80 +0,0 @@
|
||||
# The Pester suite, on every push to master and every PR.
|
||||
#
|
||||
# This repo is one of the twelve on the fleet board
|
||||
# (evomedia.net/testsuites.html) and was one of three with no CI at all, so the
|
||||
# only thing ever running these tests was a workstation at 04:00. That is a
|
||||
# poor place for the only copy of a check to live.
|
||||
#
|
||||
# windows-latest, not ubuntu, even though Pester runs on Linux: these scripts
|
||||
# deploy from a Windows workstation and the suite reads like it - paths,
|
||||
# executables, the shell itself. Proving them on Linux would be proving
|
||||
# something nobody runs. Windows minutes bill at double, which this suite's
|
||||
# size affords.
|
||||
name: tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
|
||||
# Read-only: this job builds nothing and publishes nothing, so the default
|
||||
# write-capable token is more than it needs.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: tests-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
# Actions pinned to a commit, not a moving tag: a tag can be repointed
|
||||
# by whoever owns it, and this token, read-only though it is, still sees
|
||||
# the repository.
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
|
||||
# Pester pinned to 5.x: the suite is written against the v5 configuration
|
||||
# API (New-PesterConfiguration), and Windows images still carry a v3 in
|
||||
# the module path that would be picked ahead of it. PSScriptAnalyzer is
|
||||
# the PowerShell linter; there is no typecheck for PowerShell, so the
|
||||
# analyzer is the whole of that half.
|
||||
- name: Install Pester 5 and PSScriptAnalyzer
|
||||
shell: powershell
|
||||
run: |
|
||||
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
|
||||
Install-Module Pester -MinimumVersion 5.5.0 -MaximumVersion 5.99.99 `
|
||||
-Force -SkipPublisherCheck -Scope CurrentUser
|
||||
Install-Module PSScriptAnalyzer -Force -Scope CurrentUser
|
||||
Import-Module Pester -MinimumVersion 5.5.0
|
||||
'Pester ' + (Get-Module Pester).Version + ', PSScriptAnalyzer ' + (Get-Module -ListAvailable PSScriptAnalyzer | Select-Object -First 1).Version
|
||||
|
||||
# Errors fail the job; warnings are printed and do not. The repo was
|
||||
# written without the analyzer, and turning every style warning into a
|
||||
# red build on day one would make the gate something to disable rather
|
||||
# than something to keep. PSAvoidUsingWriteHost is excluded outright:
|
||||
# these are command-line tools whose Write-Host output IS the interface.
|
||||
- name: Lint (PSScriptAnalyzer)
|
||||
shell: powershell
|
||||
run: |
|
||||
$r = Invoke-ScriptAnalyzer -Path . -Recurse -ExcludeRule PSAvoidUsingWriteHost
|
||||
$warn = @($r | Where-Object Severity -eq Warning)
|
||||
$err = @($r | Where-Object Severity -eq Error)
|
||||
if ($warn) {
|
||||
Write-Host ("{0} warning(s), not failing the build:" -f $warn.Count)
|
||||
$warn | Format-Table RuleName, ScriptName, Line -AutoSize | Out-String | Write-Host
|
||||
}
|
||||
if ($err) {
|
||||
$err | Format-Table RuleName, ScriptName, Line, Message -AutoSize -Wrap | Out-String | Write-Host
|
||||
throw ("PSScriptAnalyzer: {0} error(s)" -f $err.Count)
|
||||
}
|
||||
Write-Host "no errors"
|
||||
|
||||
# -CI sets the exit code from the result, which is the whole point here:
|
||||
# Invoke-Pester on its own reports failures and still exits 0, so the
|
||||
# job would go green with a red suite.
|
||||
- name: Tests
|
||||
shell: powershell
|
||||
run: Invoke-Pester -Path tests -CI
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -19,6 +19,3 @@ md/
|
||||
|
||||
# Pester coverage output (regenerated; never committed)
|
||||
coverage/
|
||||
|
||||
# Generated by scripts/plaintext_twins.py
|
||||
__pycache__/
|
||||
|
||||
57
CHANGELOG.md
57
CHANGELOG.md
@ -1,68 +1,15 @@
|
||||
<!--
|
||||
evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Created by Kelly Michels · dev@evomedia.net
|
||||
Licensed under the MIT License. See LICENSE.
|
||||
-->
|
||||
|
||||
# Changelog
|
||||
|
||||
Notable changes to the evomedia.net Token Savers.
|
||||
Notable changes to the Evomedia.net Token Savers.
|
||||
|
||||
## Unreleased
|
||||
|
||||
## v1.0.0.0.28 - 2026-09-22
|
||||
|
||||
### Changed
|
||||
|
||||
- **`zec2` and `zec2online` comments now say what they mean without
|
||||
naming private detail.** The container-side version read is described
|
||||
by what it is - an endpoint that is not public on every project - rather
|
||||
than by a product's own wording, and `zec2` records why it needed its
|
||||
own ssh: the read referenced three variables the script never defined,
|
||||
and because it sits inside a try/catch the failure was silent and looked
|
||||
exactly like a service that could not be reached.
|
||||
|
||||
## v1.0.0.0.26 - 2026-09-14
|
||||
|
||||
### Fixed
|
||||
|
||||
- **`zdeploy` on a docker stack built from source shipped nothing after the
|
||||
first deploy.** The docker kind ran `docker compose pull` and then
|
||||
`docker compose up -d`, which is right for a stack of published images and
|
||||
wrong for one built from a `Dockerfile` in the tree: there is nothing to
|
||||
pull, and `up -d` builds only when the image is *missing*. So the first
|
||||
deploy worked and every one after it uploaded the new code, started the old
|
||||
image, and reported success — worse than an error, because the deploy is
|
||||
green and the container is healthy. A project now opts into building with
|
||||
`"deploy": { "build": true }`, which runs `docker compose build --pull` so
|
||||
the base image is refreshed at the same time. Stacks that pull are
|
||||
unaffected.
|
||||
|
||||
## v1.0.0.0.25 - 2026-09-12
|
||||
|
||||
### Added
|
||||
|
||||
- **`zmerge`** — merge every pull request across the org that is genuinely
|
||||
ready (`MERGEABLE` / `CLEAN`, not a draft), re-checking each one immediately
|
||||
before and after every merge, because merging into a default branch can
|
||||
conflict a sibling PR in the same repository. Dry run by default;
|
||||
`-Execute` (or `-e`) merges.
|
||||
- **`zpull`** — `zmerge`, then `git pull --ff-only` in every checkout the
|
||||
merges affected. Skips a checkout that is dirty or is not on its default
|
||||
branch rather than guessing.
|
||||
|
||||
### Changed
|
||||
|
||||
- **`-e` is an alias for `-Execute`** on both of the above, the way `-s`
|
||||
already works for `-Scan`.
|
||||
- **`zmerge` discovers repositories instead of listing them.** It asked a
|
||||
hand-kept list, which had fallen well behind the org - so a scan covered
|
||||
about half of it and reported "Nothing open to merge" while a ready pull
|
||||
request sat in a repository the list had never heard of. It now asks GitHub,
|
||||
and throws rather than returning an empty list if that fails: a tool that
|
||||
quietly scans nothing prints the same reassuring line as one that scanned
|
||||
everything, and the two must not be confusable.
|
||||
|
||||
## v1.0.0.0.24 - 2026-09-08
|
||||
|
||||
### Added
|
||||
|
||||
@ -1,75 +1,15 @@
|
||||
evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Created by Kelly Michels · dev@evomedia.net
|
||||
Licensed under the MIT License. See LICENSE.
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Notable changes to the evomedia.net Token Savers.
|
||||
Notable changes to the Evomedia.net Token Savers.
|
||||
|
||||
Unreleased
|
||||
----------
|
||||
|
||||
v1.0.0.0.28 - 2026-09-22
|
||||
------------------------
|
||||
|
||||
Changed
|
||||
-------
|
||||
|
||||
- **zec2 and zec2online comments now say what they mean without
|
||||
naming private detail.** The container-side version read is described
|
||||
by what it is - an endpoint that is not public on every project - rather
|
||||
than by a product's own wording, and zec2 records why it needed its
|
||||
own ssh: the read referenced three variables the script never defined,
|
||||
and because it sits inside a try/catch the failure was silent and looked
|
||||
exactly like a service that could not be reached.
|
||||
|
||||
v1.0.0.0.26 - 2026-09-14
|
||||
------------------------
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
||||
- **zdeploy on a docker stack built from source shipped nothing after the
|
||||
first deploy.** The docker kind ran docker compose pull and then
|
||||
docker compose up -d, which is right for a stack of published images and
|
||||
wrong for one built from a Dockerfile in the tree: there is nothing to
|
||||
pull, and up -d builds only when the image is missing. So the first
|
||||
deploy worked and every one after it uploaded the new code, started the old
|
||||
image, and reported success — worse than an error, because the deploy is
|
||||
green and the container is healthy. A project now opts into building with
|
||||
"deploy": { "build": true }, which runs docker compose build --pull so
|
||||
the base image is refreshed at the same time. Stacks that pull are
|
||||
unaffected.
|
||||
|
||||
v1.0.0.0.25 - 2026-09-12
|
||||
------------------------
|
||||
|
||||
Added
|
||||
-----
|
||||
|
||||
- zmerge — merge every pull request across the org that is genuinely
|
||||
ready (MERGEABLE / CLEAN, not a draft), re-checking each one immediately
|
||||
before and after every merge, because merging into a default branch can
|
||||
conflict a sibling PR in the same repository. Dry run by default;
|
||||
-Execute (or -e) merges.
|
||||
- zpull — zmerge, then git pull --ff-only in every checkout the
|
||||
merges affected. Skips a checkout that is dirty or is not on its default
|
||||
branch rather than guessing.
|
||||
|
||||
Changed
|
||||
-------
|
||||
|
||||
- -e is an alias for -Execute on both of the above, the way -s
|
||||
already works for -Scan.
|
||||
- zmerge discovers repositories instead of listing them. It asked a
|
||||
hand-kept list, which had fallen well behind the org - so a scan covered
|
||||
about half of it and reported "Nothing open to merge" while a ready pull
|
||||
request sat in a repository the list had never heard of. It now asks GitHub,
|
||||
and throws rather than returning an empty list if that fails: a tool that
|
||||
quietly scans nothing prints the same reassuring line as one that scanned
|
||||
everything, and the two must not be confusable.
|
||||
|
||||
v1.0.0.0.24 - 2026-09-08
|
||||
------------------------
|
||||
|
||||
|
||||
@ -1,45 +1,42 @@
|
||||
6e95736007b3906950d95a830705ac1118ebfc11836d2c717d6dd49cb3157966 setup_backup_schedule.ps1
|
||||
d6ab2ddb273a8ca870bd0673cffdd0907f16f4718367b4413a3e34bb1f1769c8 token-count.ps1
|
||||
30ccf43c371ae95cecd5b443d9214ac8ea71ec83be94bc15bcf359beefb8dee1 zbackup.cmd
|
||||
b3549b346c90a8ae5a05c4c91b7ac370f72467c4cb0d019edcd0ea80b994393e zbackup.ps1
|
||||
b6e5e15f8e2b128219c7b8b9db3e9b7804eee60fd6aed43f184210048e518f3c zbackup_and_sync.ps1
|
||||
b407ef5b298cd6fe94af492e9254b4447e34333d22f8c29d3e9d4d2881651cf3 zbackup_ec2.cmd
|
||||
55df5f2e19e81317b1e33b7b030b252fc4d6f49c0c77229d9e4e86affeb3b8ef zbackup_ec2.ps1
|
||||
64bc3148b09e422ffe4890339fade2354a31f2c7ba004f2728c29a0a39c32f08 zchecksums.cmd
|
||||
3e1c573e446238cae494d22d25376f278333dc25fdbc0a5abbdaa9349722b438 zchecksums.ps1
|
||||
049f8e79fc8c3d8d96ae8ccb1155d191e2dbf2753f4d4d73ceca00aa1ab0481b zdeploy.cmd
|
||||
d2ead96436507c8efedc3c72045e623137a1f844940c4ff57c9c7ebe67645511 zdeploy.ps1
|
||||
30918a9260cb6220d6e2140edcf319fc0fa1e75b7fe36d0efe68b0d10cdde241 zec2.cmd
|
||||
695ec87ea6518933ee64524d369f1d0d5128bf6e54db31f555a888bdddcb9326 zec2.ps1
|
||||
ce3209ed8eaab242286e76aeb11b1249239e2804e3de456a1609cc7caa780b43 zec2_rotatekeys.cmd
|
||||
f58277779b5c54814c29dda55ab567960e305fa9ef515b1206076be97f623093 zec2_rotatekeys.ps1
|
||||
30ed73711eeddb518e02eb5e1968b7cc1dad51f27ecf42155b515b097487c3f3 zec2online.cmd
|
||||
1e8814928910ff4f3da59478511feb75280ba2e12111840fb47d2c5504741f0f zec2online.ps1
|
||||
4ea81dc9e3cafc514b1e8224e633bf22bd88bfdc6e53545ff88a2a2d33715b4f ZHelpers.ps1
|
||||
d97f9b5c68526c3295796b3042ee86194721eacab56b0efa0f3fdf16c881b0ea zkill.cmd
|
||||
587de0b176788de917e713915ac468e44bf00cf9c9c7d6ea9b88aaec41ad6ea7 zkill.ps1
|
||||
66556f55688d1f31e890d2b36143d3d0e2f5fcdded562e3d0a9591c02e827cda ZKiller.ps1
|
||||
2c9e0fa5dabb1544b6600cb60e5f66e89c787f217db246553b830152ab976ec3 ZKillOnly.ps1
|
||||
479d01a4c962eb1dba2dbfee913c3704048f29581a0a06d068c0fb9145c3a51c zmerge.ps1
|
||||
dd1cfde33aec53fc0df4a34e45704a59bf48e094fcafcc0ec49e9d13aa442b6f zpull.cmd
|
||||
9e746ad18b92ee7344061847b38c870f280421cf55d31e36a72a1514772e53a2 zpull.ps1
|
||||
4a57e270fc75ae5419bd27cf01b7dd6d8965be8dd58e0c80af985cb7bf27bdc5 zrelease.cmd
|
||||
a6906e118f13442340cc4e0b14d7523d63e85d2bdb0eff8fd3a7c25567af8962 zrelease.ps1
|
||||
58e5b604cc260af7e644412094ff6e3bf799f80f9e52b9a83a044299f7704107 zrepair.cmd
|
||||
186f7b0fb72808466ece328ea1a4e3a97bb2d4bae9f204ff7055e8b7f009614b zrepair.ps1
|
||||
d97c9cf55b94c53dba49471d13f5c9870f2f50627ac5732ac4b60222da936244 zrestart.cmd
|
||||
32a8484d356c5f740d7a12f18bf0dd51a0c7da66010f6a6f553d976484315eef zrestart.ps1
|
||||
2ebf37f72323cac3963a4a95d1ae414fecf4da1e1e618147663c6cf94d1ece24 zrestartd.cmd
|
||||
0a15ad0d341a5efdcd4c14e22e91d4b45f2bf87d0c5608fcf985200f6797bab4 zsetup.cmd
|
||||
0d3042c5e44c3edf396005d0177f744c0c29536c25a50743e93a92156aa2a96c zsetup.ps1
|
||||
498ad7f8a7a56b332fede299e7172211ba360e76d9244c6c93f7cceae8a58619 zsetup_mail.ps1
|
||||
46b3782d9b05649bdb1bcbd2a007bf6abc889abee420ac2f6e75530fa45a4694 zstart.cmd
|
||||
4b50cadf761f285f4fe655b143e04ec6b3565646ee8fb1ed165c234a201802b7 zstart.ps1
|
||||
2d74dcca3d4f51a28c84fbef5cc134126b61dfcc8ffb35e2d72f9596bf685809 zstart_docker.cmd
|
||||
5016654d9ad68e11b85594be5e05318e19c5c6154174f734c43251970bc2086f zstart_docker.ps1
|
||||
1e1c5990e1dba41a165e797790f73cd33fcee5035eec65dd0d2610ea8b56a023 zstartd.cmd
|
||||
a894876ad9fa1bfac6cacfec2a837914debb773e05b975d9b3cdf56589dd8ed2 zstop.ps1
|
||||
2e165d35d9915099b98a14329c9eb2774b2bf2979c937bc3843d95b5b16400e7 zsync.cmd
|
||||
5d008cc252b978a9f873101139054988dff24562655bdfcd22e781aa5c14029a zsync.ps1
|
||||
d55020eeb926e7190d06cc3de3e4a0d0656e8c73e8a89032c06ffe29c0b661eb zversion.cmd
|
||||
25de4c34219edb98b583be639b798b27c353e978a81f0480d49ba9d965f70f0b zversion.ps1
|
||||
9b3e7daf69a8295679b79072ea3e044c28f29b3ebeacdffe72c4878aaae34b08 setup_backup_schedule.ps1
|
||||
196364f1ff68608e4f7427f6b4e9e91811361eaad8d520db85075136bbe6e506 token-count.ps1
|
||||
4037dd7fb40f91b9f7be380a771d0bdaac51e4a262e1120d38e4f53d4974e942 zbackup.cmd
|
||||
85fd3b754a069302dc08e33101fae3beb96cd16bc505eedc1f44c1af7bdf7636 zbackup.ps1
|
||||
06cde8cd7332a42f25c11666e759ab8002690c00261df9cd7010a5f29e26edda zbackup_and_sync.ps1
|
||||
29bd3b062a8f02c51eb6abfa50e506e304d85ddffe1f870e2ba3ff11fda6077d zbackup_ec2.cmd
|
||||
5833f9c685581879f669a9e19d6c791d876fc91a316c89a8d60fda77dd35de75 zbackup_ec2.ps1
|
||||
f1fb8fb35ec468cf9c084e28f77398b59479daab06fe96909c6c2e7e8ea1a561 zchecksums.cmd
|
||||
7f967d89feeeaf4ca1fbea93a3d013a1a7b0141e11ec6f2888b6d4ac0bdb3485 zchecksums.ps1
|
||||
6126973cbc0ccb22785340354afb9a1495f3d9798f60d3e6fc1c3d3a92285d67 zdeploy.cmd
|
||||
bd4868914e022bb1cf4a4e755d69d76a42eddbcfbce5ae441f08969233a4004d zdeploy.ps1
|
||||
1c2c99cbb986fa8993d47404eaba89ae05b854b6396a541783b46ee22ba359e9 zec2.cmd
|
||||
6b427881c670e2c1ba2ce95bee1ee446be770da85cde1fd91c36cbf40c39cff0 zec2.ps1
|
||||
0e4e9cbd19fdd151bdc07df8d72aee577e446e2280dbc7afa0039d503364e991 zec2_rotatekeys.cmd
|
||||
25068fa51dddd221c0b05628d78c7fa14f1d8c853c6c3f94ec760430122a1589 zec2_rotatekeys.ps1
|
||||
408d43e37a6febf3543dd74f449b00d6cdb4cfa6d5ae6ab48b6b840bbfd63ecb zec2online.cmd
|
||||
f1c3a0f911da86e018f9b3d4d7c9be048210aa56c016548570edfbab79fed8ef zec2online.ps1
|
||||
832ff9fb1f9b318b0868272ba64194f6f7bb4259dea2e6d0417787cec990f2c1 ZHelpers.ps1
|
||||
1e1dcdc76250d57b2b393a650fa853452f4cb271b58a05a7dc8c01f436958424 zkill.cmd
|
||||
1eb4c23bdc0ed1a82dc495c623b49e5dcd4e342f026b4d896e32c79d592667db zkill.ps1
|
||||
1c908b69fb9200610e080ac6bb8f4c15d3d7edf402d2e119c2405158e1986a52 ZKiller.ps1
|
||||
558dfdfc7b4d12231e476c14a00c678e2e536140c4b7abbc05364bf214562291 ZKillOnly.ps1
|
||||
36b01f2cd1d5967dc3ec1313fc4f82ada2ea669d0529a0d313300e21a5b38d04 zrelease.cmd
|
||||
e48b994605b1ad9f793f95d653f50df41db628a6b2c6976415b7d03e3900a373 zrelease.ps1
|
||||
02635351847f84d0f644ffa9c0073804e480d360fcabcd6a8b793cffd6f0026f zrepair.cmd
|
||||
ffb544517ca8613ea2babc61dad19352a06840b1cb8c692e3f4b890f1e214297 zrepair.ps1
|
||||
d2d80e9f063284cb4c4a0605e5e4aa4636f1d2bdb8bce2bd038961ae95515501 zrestart.cmd
|
||||
2107fdd0e25ed6694eb992e706590da0f6fb0461e9cef300df5ca8ae568d27b3 zrestart.ps1
|
||||
5b5a54a82c126552624af4a35b3f0da222b64e614b639877950dbd4d12cfb64f zrestartd.cmd
|
||||
00eb0665d23d700170267b23be494c63f0ffaea420813e153aabfe49e5594d0f zsetup.cmd
|
||||
ed73481327cb3fbf2a2eff85fbe1dc723c4f26a2f6b9e54c16719be6ce96fd8f zsetup.ps1
|
||||
46695f5009dee0dd425fccc3993c6c368882257bc03e91111f69ee8a6250e342 zsetup_mail.ps1
|
||||
98ac29e9d767d5173de828fba46bb0d82bc57999f2da83eec50079c934d44119 zstart.cmd
|
||||
70381eacf132ac3af60655e72fd6e40db2c3be2c5978ef451207b4597191f712 zstart.ps1
|
||||
d7ef72318b6764e2948272deae539a65904c6b5e06ef5c2aaf0e435da3d025d2 zstart_docker.cmd
|
||||
293f2d9a77c3657f7a40f4a03ddbc723adf5871b3b6a1c614911cf0222984069 zstart_docker.ps1
|
||||
f37fcf3b49f7af8a39507293c4f96fdbc3cc460318acae9fdae13b4ac89688e0 zstartd.cmd
|
||||
50537f209f3152f5a9afef6c28ad1b3bac37bca70bc5ee5c7b54236ed7acb01b zstop.ps1
|
||||
d51df3f48a18c7e2225e1a24b5db8c984eee196d58038e015ac915e1e2384d33 zsync.cmd
|
||||
fee5782dc503ba3bbd3a987698277ba467bbdce5ea0527f8f42fb396e3ef3db7 zsync.ps1
|
||||
2056ffca049697a186015eec3da2b6634153482e294659fa550c6e9d693395a3 zversion.cmd
|
||||
6656725cee68f9082a0932e15c24a08eddc24247e387a9f8ae5dd24413836188 zversion.ps1
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Created by Kelly Michels · dev@evomedia.net
|
||||
Licensed under the MIT License. See LICENSE.
|
||||
-->
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Created by Kelly Michels · dev@evomedia.net
|
||||
Licensed under the MIT License. See LICENSE.
|
||||
|
||||
Elevator Pitch
|
||||
==============
|
||||
|
||||
The one-liner
|
||||
-------------
|
||||
|
||||
AI coding agents waste thousands of tokens a day on infrastructure orchestration. Token Savers gives you one-word commands to run those parts yourself — so your agent spends tokens on code, not on SSH.
|
||||
|
||||
The 30-second version
|
||||
---------------------
|
||||
|
||||
Every time your AI coding agent runs your infrastructure for you — a deploy, a restart, a health check — the full output lands in its context window: Docker layers, SSH banners, health-check chatter. We measured it per run — at a typical active-day cadence that's ~26,500 tokens of pure script output through the agent, and a single full Docker rebuild adds ~35,000 more. The dollars are small; the context is not — every line of infrastructure noise crowds out the code your agent is supposed to be reasoning about.
|
||||
|
||||
Token Savers collapses the infrastructure side into short, one-word commands you run yourself: zdeploy myapp, zrepair myapp, zstart myapp. Describe each project once in zconfig.json — where it lives, what kind it is, where it deploys — and every command just knows. You run the deploy; your agent edits the code. You run the health check; your agent reads the result and fixes whatever's wrong.
|
||||
|
||||
Measured per-run; ~26,500 tokens of script output per active development day at a typical cadence — kept out of your agent's context entirely when you run the commands yourself. See TOKEN_SAVINGS.md (TOKEN_SAVINGS.md) for the per-script measurements and method.
|
||||
|
||||
Why it's different
|
||||
------------------
|
||||
|
||||
- Built around the AI-agent workflow. The commands are short on purpose — fewer keystrokes for you, fewer tokens when an agent invokes them. But the real saving is the operations you don't hand to the agent at all.
|
||||
- The project name IS the command. zstart blog, zdeploy api, zbackup store — no flags to memorize, no switches to wire up.
|
||||
- One config file, zero secrets in git. Server IP, SSH key, paths, and project definitions live in one gitignored JSON. Clone it anywhere, drop in your config, go.
|
||||
- It verifies the deploy actually landed. Not "did the server return 200" (a stale cache does that too) — it checks that the build number went live, so you know the code you just shipped is the code that's running.
|
||||
|
||||
Who it's for
|
||||
------------
|
||||
|
||||
Solo developers and small teams running several containerized web apps (Python, Vite, Next.js, plus edge proxies and stock Docker images) on a single VPS or EC2 box, from a Windows dev machine, over SSH — and using AI coding agents to write the code.
|
||||
|
||||
The tagline
|
||||
-----------
|
||||
|
||||
Fewer keystrokes. Fewer tokens. One config to rule your fleet.
|
||||
22
README.md
22
README.md
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Created by Kelly Michels · dev@evomedia.net
|
||||
Licensed under the MIT License. See LICENSE.
|
||||
-->
|
||||
@ -136,8 +136,6 @@ The `.cmd` wrappers are the everyday interface. Every command takes one or more
|
||||
| `zchecksums [-Update]` | Verify every script against `CHECKSUMS.txt` (SHA-256) |
|
||||
| `zversion [bump \| bump-stage <s> \| set <v>]` | Show or advance the toolkit version (stamps every header) |
|
||||
| `zrelease [-Verify]` | Package the current version as `releases/zscripts-<version>.zip` + `.sha256` |
|
||||
| `zmerge [-Execute\|-e]` | Merge every pull request across the org that is genuinely ready |
|
||||
| `zpull [-Execute\|-e]` | `zmerge`, then bring every affected local checkout current |
|
||||
|
||||
### Local development
|
||||
|
||||
@ -411,24 +409,6 @@ Verification walks its channels in trust order — docker-network `viaProxy`, th
|
||||
|
||||
Each release is packaged as a zip in [`releases/`](releases/) — grab the latest `zscripts-v*.zip`, check it, unzip, done:
|
||||
|
||||
**Windows · PowerShell** — these commands are a PowerShell toolkit, so this is
|
||||
most people's path. `sha256sum` and `unzip` are not Windows commands:
|
||||
|
||||
```powershell
|
||||
$zip = "zscripts-v1.0.0.0.0.zip"
|
||||
(Get-FileHash $zip -Algorithm SHA256).Hash -eq (Get-Content "$zip.sha256").Split()[0] # True = good
|
||||
Expand-Archive $zip -DestinationPath zscripts
|
||||
cd zscripts
|
||||
.\zchecksums.cmd # verify the contents
|
||||
```
|
||||
|
||||
`Get-FileHash` prints the hash in **upper** case and the `.sha256` file holds it
|
||||
in lower — they look different side by side and are not. `-eq` on strings is
|
||||
case-insensitive in PowerShell, so the comparison above is right; trust the
|
||||
`True`, not your eyes.
|
||||
|
||||
**macOS · Linux · Git Bash · WSL**
|
||||
|
||||
```bash
|
||||
sha256sum -c zscripts-v1.0.0.0.0.zip.sha256 # verify the download
|
||||
unzip zscripts-v1.0.0.0.0.zip -d zscripts # extract
|
||||
|
||||
20
README.txt
20
README.txt
@ -1,4 +1,4 @@
|
||||
evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Created by Kelly Michels · dev@evomedia.net
|
||||
Licensed under the MIT License. See LICENSE.
|
||||
|
||||
@ -133,8 +133,6 @@ The .cmd wrappers are the everyday interface. Every command takes one or more pr
|
||||
| zchecksums [-Update] | Verify every script against CHECKSUMS.txt (SHA-256) |
|
||||
| zversion [bump \| bump-stage <s> \| set <v>] | Show or advance the toolkit version (stamps every header) |
|
||||
| zrelease [-Verify] | Package the current version as releases/zscripts-<version>.zip + .sha256 |
|
||||
| zmerge [-Execute\|-e] | Merge every pull request across the org that is genuinely ready |
|
||||
| zpull [-Execute\|-e] | zmerge, then bring every affected local checkout current |
|
||||
|
||||
Local development
|
||||
-----------------
|
||||
@ -384,22 +382,6 @@ Downloading without cloning
|
||||
|
||||
Each release is packaged as a zip in releases/ (releases/) — grab the latest zscripts-v*.zip, check it, unzip, done:
|
||||
|
||||
Windows · PowerShell — these commands are a PowerShell toolkit, so this is
|
||||
most people's path. sha256sum and unzip are not Windows commands:
|
||||
|
||||
$zip = "zscripts-v1.0.0.0.0.zip"
|
||||
(Get-FileHash $zip -Algorithm SHA256).Hash -eq (Get-Content "$zip.sha256").Split()[0] # True = good
|
||||
Expand-Archive $zip -DestinationPath zscripts
|
||||
cd zscripts
|
||||
.\zchecksums.cmd # verify the contents
|
||||
|
||||
Get-FileHash prints the hash in upper case and the .sha256 file holds it
|
||||
in lower — they look different side by side and are not. -eq on strings is
|
||||
case-insensitive in PowerShell, so the comparison above is right; trust the
|
||||
True, not your eyes.
|
||||
|
||||
macOS · Linux · Git Bash · WSL
|
||||
|
||||
sha256sum -c zscripts-v1.0.0.0.0.zip.sha256 # verify the download
|
||||
unzip zscripts-v1.0.0.0.0.zip -d zscripts # extract
|
||||
cd zscripts && sha256sum -c CHECKSUMS.txt # verify the contents
|
||||
|
||||
68
SECURITY.md
68
SECURITY.md
@ -1,68 +0,0 @@
|
||||
# Security
|
||||
|
||||
How to report a vulnerability, what to expect, and what is in scope:
|
||||
**[the evomedia-net security policy](https://github.com/evomedia-net/.github/blob/main/SECURITY.md)**.
|
||||
Short version — email [dev@evomedia.net](mailto:dev@evomedia.net), not a public
|
||||
issue.
|
||||
|
||||
What follows is particular to this repository, which is unusual in one way
|
||||
worth stating plainly.
|
||||
|
||||
## This is a mirror, and the interesting bug is a leak
|
||||
|
||||
These scripts are published from a private tree. The copy here is sanitised:
|
||||
placeholder hosts, example configuration, dummy data. So the most valuable
|
||||
thing anyone can report about this repository is not a crash — it is
|
||||
**something real that should not be here**:
|
||||
|
||||
- a credential, key, token, or private-key block
|
||||
- an internal hostname, a product domain, or an operator's path
|
||||
- an identifier that names a private project or a private-only script
|
||||
|
||||
If you find one, treat it as a live secret and mail
|
||||
[dev@evomedia.net](mailto:dev@evomedia.net) rather than opening an issue. A
|
||||
public issue about a leaked secret publishes it a second time and pins it to
|
||||
the top of the page.
|
||||
|
||||
**The automated check is a denylist.** `tests/Sanitization.Tests.ps1` and
|
||||
`tests/sanitization-patterns.psd1` hold the patterns this repository must never
|
||||
contain, and CI enforces them. A denylist proves the absence of *known*
|
||||
patterns, not the absence of secrets — it is a regression net for a specific
|
||||
recurring mistake, not a substitute for reading what is published. That is why
|
||||
a report here is worth sending even though the tests are green.
|
||||
|
||||
## They are automation scripts, so read them before running them
|
||||
|
||||
Everything here drives real infrastructure: archives a working tree, uploads
|
||||
it, rebuilds containers, restarts services. That is the purpose, and it means
|
||||
the ordinary rules for running someone else's shell scripts apply with more
|
||||
force than usual.
|
||||
|
||||
- **Read a script before the first run**, and run it against something you can
|
||||
afford to break.
|
||||
- **Nothing here is a sandbox.** There is no dry-run guarantee unless a script
|
||||
documents one; the flag that exists on one command may not exist on the next.
|
||||
- **The configuration is yours.** The example config carries placeholders, and
|
||||
every host, key path and target in it has to be replaced with your own before
|
||||
anything is pointed at real infrastructure.
|
||||
- Addresses in examples use the ranges reserved for documentation, and
|
||||
loopback. They are placeholders, not somewhere to send anything.
|
||||
|
||||
Scripts that destroy or overwrite state are the ones to read twice. A report
|
||||
that one of them does something destructive **without saying so** is a good
|
||||
report; a report that a script named after a destructive act performs it is
|
||||
not.
|
||||
|
||||
## Release integrity
|
||||
|
||||
Releases carry checksums. They are an **integrity check, not a signature** —
|
||||
they catch a truncated download, a corrupted mirror and an accidental edit,
|
||||
and they do not catch a forger, because whoever can change an archive can
|
||||
change the manifest that travels with it.
|
||||
|
||||
## Not a finding here
|
||||
|
||||
- **Placeholder credentials and example configuration.** Fake values are the
|
||||
sanitisation working, not a leak.
|
||||
- **The private tree.** Only what is published here is in scope; the internal
|
||||
original is not public and cannot be reviewed.
|
||||
73
SECURITY.txt
73
SECURITY.txt
@ -1,73 +0,0 @@
|
||||
Security
|
||||
========
|
||||
|
||||
How to report a vulnerability, what to expect, and what is in scope:
|
||||
the evomedia-net security policy (https://github.com/evomedia-net/.github/blob/main/SECURITY.md).
|
||||
Short version — email dev@evomedia.net (mailto:dev@evomedia.net), not a public
|
||||
issue.
|
||||
|
||||
What follows is particular to this repository, which is unusual in one way
|
||||
worth stating plainly.
|
||||
|
||||
This is a mirror, and the interesting bug is a leak
|
||||
---------------------------------------------------
|
||||
|
||||
These scripts are published from a private tree. The copy here is sanitised:
|
||||
placeholder hosts, example configuration, dummy data. So the most valuable
|
||||
thing anyone can report about this repository is not a crash — it is
|
||||
something real that should not be here:
|
||||
|
||||
- a credential, key, token, or private-key block
|
||||
- an internal hostname, a product domain, or an operator's path
|
||||
- an identifier that names a private project or a private-only script
|
||||
|
||||
If you find one, treat it as a live secret and mail
|
||||
dev@evomedia.net (mailto:dev@evomedia.net) rather than opening an issue. A
|
||||
public issue about a leaked secret publishes it a second time and pins it to
|
||||
the top of the page.
|
||||
|
||||
The automated check is a denylist. tests/Sanitization.Tests.ps1 and
|
||||
tests/sanitization-patterns.psd1 hold the patterns this repository must never
|
||||
contain, and CI enforces them. A denylist proves the absence of known
|
||||
patterns, not the absence of secrets — it is a regression net for a specific
|
||||
recurring mistake, not a substitute for reading what is published. That is why
|
||||
a report here is worth sending even though the tests are green.
|
||||
|
||||
They are automation scripts, so read them before running them
|
||||
-------------------------------------------------------------
|
||||
|
||||
Everything here drives real infrastructure: archives a working tree, uploads
|
||||
it, rebuilds containers, restarts services. That is the purpose, and it means
|
||||
the ordinary rules for running someone else's shell scripts apply with more
|
||||
force than usual.
|
||||
|
||||
- Read a script before the first run, and run it against something you can
|
||||
afford to break.
|
||||
- Nothing here is a sandbox. There is no dry-run guarantee unless a script
|
||||
documents one; the flag that exists on one command may not exist on the next.
|
||||
- The configuration is yours. The example config carries placeholders, and
|
||||
every host, key path and target in it has to be replaced with your own before
|
||||
anything is pointed at real infrastructure.
|
||||
- Addresses in examples use the ranges reserved for documentation, and
|
||||
loopback. They are placeholders, not somewhere to send anything.
|
||||
|
||||
Scripts that destroy or overwrite state are the ones to read twice. A report
|
||||
that one of them does something destructive without saying so is a good
|
||||
report; a report that a script named after a destructive act performs it is
|
||||
not.
|
||||
|
||||
Release integrity
|
||||
-----------------
|
||||
|
||||
Releases carry checksums. They are an integrity check, not a signature —
|
||||
they catch a truncated download, a corrupted mirror and an accidental edit,
|
||||
and they do not catch a forger, because whoever can change an archive can
|
||||
change the manifest that travels with it.
|
||||
|
||||
Not a finding here
|
||||
------------------
|
||||
|
||||
- Placeholder credentials and example configuration. Fake values are the
|
||||
sanitisation working, not a leak.
|
||||
- The private tree. Only what is published here is in scope; the internal
|
||||
original is not public and cannot be reviewed.
|
||||
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Created by Kelly Michels · dev@evomedia.net
|
||||
Licensed under the MIT License. See LICENSE.
|
||||
-->
|
||||
|
||||
@ -1,296 +0,0 @@
|
||||
evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
Created by Kelly Michels · dev@evomedia.net
|
||||
Licensed under the MIT License. See LICENSE.
|
||||
|
||||
Token Savings: Why You Should Run These Scripts Yourself
|
||||
========================================================
|
||||
|
||||
Running these scripts manually keeps their output out of your AI coding agent's
|
||||
context window. Every line the agent doesn't have to read is a token you don't
|
||||
pay for — and a token the agent can spend on the actual problem instead of on
|
||||
deployment sequencing, SSH output, and Docker health checks.
|
||||
|
||||
This document reports two baselines side by side:
|
||||
|
||||
- You run it → Claude runs the script. What Claude ingests if it invokes the
|
||||
z-script as a single command. These figures are measured (see method below).
|
||||
- You run it → Claude orchestrates raw. What Claude would ingest if the scripts
|
||||
didn't exist and it drove scp / ssh / docker compose step by step itself.
|
||||
These figures are estimates — the same command output plus the agent's
|
||||
reasoning and retry logic across every discrete step.
|
||||
|
||||
The savings from running a script yourself is the first column: if you run it,
|
||||
Claude ingests zero. The extra value of having the scripts at all is the gap
|
||||
between the two columns.
|
||||
|
||||
Dollar equivalents use a blended input/output rate: Sonnet 5 ≈ $9/1M | Opus 4.8 ≈ $15/1M | Fable 5 ≈ $30/1M
|
||||
|
||||
> Measurement note: "Measured" figures come from token-count.ps1, which runs
|
||||
> each script under Start-Transcript and counts output characters ÷ 3.5
|
||||
> chars/token. Captured in Claude Code (Sonnet 4.6) against the sp project.
|
||||
> Strictly speaking that's measured output volume with estimated tokenization:
|
||||
> ÷3.5 is a prose heuristic, and code-heavy output (paths, JSON, container IDs)
|
||||
> fragments into more tokens per character under a real BPE tokenizer — so the
|
||||
> token figures here are likely conservative. "Estimated (raw)" figures are not
|
||||
> measured — they approximate manual orchestration and are marked est.
|
||||
> throughout. Other models/interfaces tokenize differently.
|
||||
|
||||
---
|
||||
|
||||
Measured per-run output (script-run baseline)
|
||||
---------------------------------------------
|
||||
|
||||
The bold figures below are real captures from token-count.ps1 sp; rows flagged est. are not:
|
||||
|
||||
| Script | Measured tokens/run | Notes |
|
||||
|--------|--------------------:|-------|
|
||||
| zec2online | 267 | reachability + version check |
|
||||
| zec2 | 331 | EC2 TCP/HTTP + build match |
|
||||
| zbackup_ec2 | 334 | pull server backup |
|
||||
| zrepair | 364 | clean audit; more if it restarts containers |
|
||||
| zkill | 377 | free the dev port |
|
||||
| zbackup | 436 | local project snapshot |
|
||||
| zrestart | 724 | kill + restart (detached) |
|
||||
| zstart | 762 | start dev server (detached) |
|
||||
| zsync | 769 | mirror backups offsite |
|
||||
| zdeploy (cached) | ~810 | 53s deploy, layers cached |
|
||||
| zdeploy (full rebuild) | ~34,600 est. | packages changed; streams full docker build |
|
||||
| zstart_docker | not measured | est. ~500–1,500 |
|
||||
|
||||
Cache state is what drives zdeploy. A cached deploy is ~810 tokens; the
|
||||
large number only appears on a full rebuild (dependencies changed), which streams
|
||||
the entire docker build. During rapid deploy → test → fix iteration almost every run
|
||||
is cached, so ~810 is the realistic per-run cost — with occasional spikes when you
|
||||
change packages.
|
||||
|
||||
---
|
||||
|
||||
Local Development Control
|
||||
-------------------------
|
||||
|
||||
zstart — Start dev servers
|
||||
--------------------------
|
||||
Measured: ~762 tokens/run | est. raw orchestration: ~1,500–3,000 | typical 2–3 runs/day
|
||||
|
||||
Run it yourself and Claude sees none of the version-bump, MOTD, and startup output.
|
||||
If Claude started the server raw, it would also wait on health checks and confirm
|
||||
the port is listening — reasoning the script does deterministically.
|
||||
|
||||
zstart viteapp # start Vite dev server on its configured port
|
||||
zstart pyapp -Port 3000 # override the port
|
||||
zstart nextapp -Detached # start in background, prompt returns
|
||||
|
||||
---
|
||||
|
||||
zkill — Stop dev servers
|
||||
------------------------
|
||||
Measured: ~377 tokens/run | est. raw orchestration: ~1,000–2,000 | typical 2–3 runs/day
|
||||
|
||||
Raw, Claude would enumerate processes, kill them, and re-check the port is free.
|
||||
The script collapses that to one command.
|
||||
|
||||
zkill viteapp
|
||||
zkill pyapp nextapp
|
||||
|
||||
---
|
||||
|
||||
zrestart — Restart in one command
|
||||
---------------------------------
|
||||
Measured: ~724 tokens/run | est. raw orchestration: ~2,500–4,500 | typical 10–15 runs/day
|
||||
|
||||
The most-used command during rapid iteration. Raw, it's stop → wait → start with
|
||||
error handling at each hop — several tool calls and their reasoning. As one script
|
||||
it's a single call, and the -Detached switch now propagates correctly through the
|
||||
kill→restart chain so the server backgrounds cleanly.
|
||||
|
||||
zrestart viteapp
|
||||
zrestart pyapp -Detached
|
||||
|
||||
---
|
||||
|
||||
Build & Deployment
|
||||
------------------
|
||||
|
||||
zdeploy — Deploy to EC2
|
||||
-----------------------
|
||||
Measured: ~810 tokens/run cached (spikes to ~34,600 on a full rebuild) | est. raw orchestration: ~5,000–12,000 cached, ~35,000+ full rebuild | typical 10–15 runs/day
|
||||
|
||||
The biggest lever — and the one where cache state matters most. The script streams
|
||||
the docker/SSH output whether Claude runs it or not, so a cached deploy really is only
|
||||
~810 tokens even through Claude. The raw-orchestration cost is higher not because of
|
||||
extra output but because Claude would reason between ~15 discrete steps (zip, preflight
|
||||
cleanup, scp, unzip, build, up, version bump, restart, verify) and handle retries
|
||||
itself. Running it yourself zeroes out all of that.
|
||||
|
||||
Measured cached: three runs at 808 / 858 / 808 tokens (53–54s each). The full-rebuild
|
||||
figure (~34,600) is an estimate for package-change deploys — treat it as the upper
|
||||
bound.
|
||||
|
||||
zdeploy pyapp -Note "Fix nav alignment"
|
||||
zdeploy edge # reload edge nginx config
|
||||
zdeploy all -Note "weekly release"
|
||||
|
||||
---
|
||||
|
||||
zstart_docker — Start local Docker stack
|
||||
----------------------------------------
|
||||
Not measured (est. ~500–1,500 tokens/run) | typical 1 run/day
|
||||
|
||||
One-time setup per session; doesn't need agent involvement.
|
||||
|
||||
---
|
||||
|
||||
Backup & Sync
|
||||
-------------
|
||||
|
||||
zbackup — Backup projects locally
|
||||
---------------------------------
|
||||
Measured: ~436 tokens/run | est. raw orchestration: ~1,200–2,500 | typical 1–2 runs/day
|
||||
|
||||
Raw, Claude enumerates files, decides exclusions, compresses, and stamps timestamps.
|
||||
You decide when to snapshot.
|
||||
|
||||
zbackup # everything + scripts folder
|
||||
zbackup pyapp -Tag "pre-refactor"
|
||||
|
||||
---
|
||||
|
||||
zsync — Sync backups offsite
|
||||
----------------------------
|
||||
Measured: ~769 tokens/run | est. raw orchestration: ~1,500–3,000 | typical 1 run/day
|
||||
|
||||
Raw, Claude tracks file diffs, runs robocopy, and verifies the copy. You manage
|
||||
cadence independently.
|
||||
|
||||
zsync
|
||||
zsync viteapp # build + mirror dist to $env:ZSYNC_DEST
|
||||
|
||||
---
|
||||
|
||||
zbackup_ec2 — Pull backups from the server
|
||||
------------------------------------------
|
||||
Measured: ~334 tokens/run | est. raw orchestration: ~1,000–2,000 | typical 1 run/day
|
||||
|
||||
Separates database/app backup from code changes. Claude focuses on code; you manage
|
||||
infrastructure snapshots.
|
||||
|
||||
zbackup_ec2
|
||||
|
||||
---
|
||||
|
||||
Diagnostics & Troubleshooting
|
||||
-----------------------------
|
||||
|
||||
zec2 — Check EC2 reachability
|
||||
-----------------------------
|
||||
Measured: ~331 tokens/run (zec2online: ~267) | est. raw orchestration: ~1,000–2,000 | typical 5–8 runs/day
|
||||
|
||||
When a deploy fails you run this first to confirm EC2 is reachable and the right
|
||||
build is live — before asking Claude to debug. Raw, that's blind network diagnostics
|
||||
over SSH. Runs frequently alongside zdeploy.
|
||||
|
||||
zec2 viteapp
|
||||
zec2 # check all projects
|
||||
zec2online sp # lightweight HTTP-only variant
|
||||
|
||||
---
|
||||
|
||||
zrepair — Audit & repair container routing
|
||||
------------------------------------------
|
||||
Measured: ~364 tokens/run (clean audit) | est. raw orchestration: ~2,000–4,000 | typical 1–2 runs/day
|
||||
|
||||
When a page 502s, this isolates routing vs. DNS vs. app logic across several
|
||||
containers — rather than handing Claude an SSH session to figure out blind. The
|
||||
364-token figure is a healthy run with nothing to repair; a run that actually
|
||||
restarts containers emits more. Raw, Claude would SSH per container and reason
|
||||
across each check.
|
||||
|
||||
zrepair viteapp
|
||||
|
||||
---
|
||||
|
||||
Daily Token Savings Summary
|
||||
---------------------------
|
||||
|
||||
Per-run × runs/day. The per-run figures are measured; the daily totals multiply
|
||||
them by assumed typical run counts (midpoints) — zdeploy and zrestart at
|
||||
10–15/day dominate the sum, so scale the total to your own cadence. The est. raw
|
||||
column approximates what Claude would burn orchestrating the same work with no
|
||||
scripts.
|
||||
|
||||
| Script | Measured/run | Runs/day | Measured/day | Est. raw/day |
|
||||
|--------|-------------:|:--------:|-------------:|-------------:|
|
||||
| zstart | 762 | 2–3 | ~1,900 | ~3,800–9,000 |
|
||||
| zkill | 377 | 2–3 | ~940 | ~2,500–6,000 |
|
||||
| zrestart | 724 | 10–15 | ~9,050 | ~31,000–68,000 |
|
||||
| zdeploy (cached) | ~810 | 10–15 | ~10,100 | ~62,000–180,000 |
|
||||
| zec2 (+online) | ~330 | 5–8 | ~2,200 | ~6,500–16,000 |
|
||||
| zbackup | 436 | 1–2 | ~650 | ~1,800–5,000 |
|
||||
| zsync | 769 | 1 | ~770 | ~1,500–3,000 |
|
||||
| zbackup_ec2 | 334 | 1 | ~330 | ~1,000–2,000 |
|
||||
| zrepair | 364 | 1–2 | ~550 | ~3,000–6,000 |
|
||||
| Total (active dev day) | | | ~26,500 | ~115,000–295,000 est. |
|
||||
|
||||
The ~26,500/day figure is measured per-run at an assumed typical cadence —
|
||||
reproducible on the per-run side, workflow-specific on the multiplier. It reflects an
|
||||
active tool-development day of mostly cached deploys. The
|
||||
~115k–295k est. upper figure is what it would cost to have Claude drive the raw
|
||||
ssh/docker sequences instead — dominated by per-step reasoning on zdeploy and
|
||||
zrestart, not by output volume. Treat that column as an **upper bound, not a
|
||||
prediction**: a capable agent asked to deploy might well write its own wrapper
|
||||
script and ingest very little — the counterfactual depends entirely on how the
|
||||
agent chooses to work. A day with several full-rebuild deploys pushes the measured
|
||||
figure higher too, since each rebuild streams ~34,600 tokens.
|
||||
|
||||
Daily dollar savings during active tool development:
|
||||
|
||||
Script output the agent ingests is billed at input rates, so the measured column
|
||||
uses input pricing. The est.-raw column keeps the blended rate, because raw
|
||||
orchestration also generates agent output (reasoning and tool calls between steps).
|
||||
|
||||
| Model | Measured/day @ input rate | Est. raw/day @ blended rate |
|
||||
|-------|--------------------------:|----------------------------:|
|
||||
| Sonnet 5 | ~$0.08 ($3/1M) | ~$1.04–$2.66 ($9/1M) |
|
||||
| Opus 4.8 | ~$0.13 ($5/1M) | ~$1.73–$4.43 ($15/1M) |
|
||||
| Fable 5 | ~$0.27 ($10/1M) | ~$3.45–$8.85 ($30/1M) |
|
||||
|
||||
One-time ingest slightly understates the true cost: tokens that enter the context are
|
||||
re-sent on every later turn of the session (at cheaper cache-read rates when prompt
|
||||
caching applies), so the cumulative figure is somewhat higher than a single ingest.
|
||||
|
||||
Over a ~22-day working month, the measured savings run ~$2–$6/mo (Sonnet →
|
||||
Fable); the raw-orchestration estimate runs ~$23–$195/mo. The honest dollar
|
||||
figure is small — the real currency is context: every infrastructure token kept
|
||||
out of the window is context your agent keeps for the actual problem, and that's
|
||||
worth more than the dollars suggest.
|
||||
|
||||
---
|
||||
|
||||
Claude Model Token Costs (July 2026)
|
||||
------------------------------------
|
||||
|
||||
| Model | Input | Output | Typical use |
|
||||
|-------|-------|--------|-------------|
|
||||
| Haiku 4.5 | $1/1M | $5/1M | Quick edits, small changes |
|
||||
| Sonnet 5 | $3/1M | $15/1M | Daily coding, medium complexity |
|
||||
| Opus 4.8 | $5/1M | $25/1M | Complex reasoning, multi-file refactors |
|
||||
| Fable 5 | $10/1M | $50/1M | Advanced reasoning, agentic workflows |
|
||||
|
||||
---
|
||||
|
||||
When to Run Scripts Yourself vs. Ask the Agent
|
||||
----------------------------------------------
|
||||
|
||||
Run yourself when:
|
||||
- ✅ You know exactly what action is needed
|
||||
- ✅ The script is deterministic (same input = same output)
|
||||
- ✅ You want to parallelize — run zstart while asking Claude for code
|
||||
- ✅ You're troubleshooting and need fast feedback loops
|
||||
|
||||
Ask the agent when:
|
||||
- ❌ You need conditional logic ("if this test fails, try X")
|
||||
- ❌ You're chaining operations that depend on each other's output
|
||||
- ❌ You want the agent to interpret script output and decide next steps
|
||||
|
||||
Bottom line: These scripts are optimized for you to run directly. Use them. Save
|
||||
tokens. Let Claude focus on coding.
|
||||
69
ZHelpers.ps1
69
ZHelpers.ps1
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# ZHelpers.ps1 — shared library dot-sourced by every z script. Not run directly.
|
||||
|
||||
@ -105,33 +105,6 @@ function Get-ZEdgeProject {
|
||||
}
|
||||
|
||||
# Remote compose directory for a project: remote.composeDir if set, else remote.path.
|
||||
# How a docker stack gets its images: built here, or pulled from a registry.
|
||||
#
|
||||
# `docker compose pull` is right for a stack of published images and wrong for
|
||||
# one built from a Dockerfile in the tree - there is nothing to pull. The trap
|
||||
# is what happens next: `docker compose up -d` builds only when the image is
|
||||
# MISSING. So the FIRST deploy of a build-from-source stack works, and every
|
||||
# one after it uploads the new code, starts the old image, and reports success.
|
||||
# That is worse than an error, because nothing looks wrong: the deploy is
|
||||
# green, the container is up, and the change simply is not in it.
|
||||
#
|
||||
# deploy.build opts a project into building instead. --pull refreshes the base
|
||||
# image at the same time, so a rebuild also picks up its security updates
|
||||
# rather than pinning whatever happened to be on the box the first time.
|
||||
function Get-DockerImageStep {
|
||||
param($Proj, [Parameter(Mandatory)][string]$RemotePath)
|
||||
if ($Proj -and $Proj.deploy -and $Proj.deploy.build) {
|
||||
return @{
|
||||
Label = 'docker compose build'
|
||||
Command = "cd $RemotePath && sudo docker compose build --pull"
|
||||
}
|
||||
}
|
||||
return @{
|
||||
Label = 'docker compose pull'
|
||||
Command = "cd $RemotePath && sudo docker compose pull"
|
||||
}
|
||||
}
|
||||
|
||||
function Get-RemoteComposeDir {
|
||||
param([Parameter(Mandatory)][string]$Key)
|
||||
$proj = Get-ZProject -Key $Key
|
||||
@ -590,50 +563,14 @@ function Get-LabelFromVersionJson {
|
||||
}
|
||||
|
||||
function Get-LabelFromBuildJsonObj {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
The build label out of a parsed build-version.json, or $null.
|
||||
|
||||
.DESCRIPTION
|
||||
Three stamp shapes, and this has to read all of them because deploy
|
||||
verification runs BOTH sides through it - the local stamp and the one read
|
||||
back from the running container. A shape it cannot read does not fail
|
||||
loudly; it collapses both sides to the same wrong string and the comparison
|
||||
passes unconditionally, which is worse than having no check at all.
|
||||
|
||||
{ "major":1, "rc":0, "beta":0, "alpha":0, "build":98 } object form
|
||||
{ "version": "v1.0.0.0.98" } string form
|
||||
{ "productVersion": "1.2", "buildNumber": 7 } legacy form
|
||||
|
||||
The string form is what the versioning scheme specifies and what zbump
|
||||
writes, so it is checked FIRST - a stamp carrying both an explicit version
|
||||
and stray numeric fields means the version it states.
|
||||
|
||||
Earned the hard way: the string form used to fall through to the legacy
|
||||
branch, where productVersion is null ("") and buildNumber is null (0), so
|
||||
EVERY string-form stamp became "v.0". A site verified `expect v.0` against
|
||||
a live `v.0` and reported PASS while serving whatever it liked.
|
||||
#>
|
||||
param($obj)
|
||||
if (-not $obj) { return $null }
|
||||
|
||||
# String form: the version is stated, so state it back. Trimmed, and given
|
||||
# the leading v the other branches add, so all three shapes are comparable.
|
||||
if (-not [string]::IsNullOrWhiteSpace([string]$obj.version)) {
|
||||
$v = ([string]$obj.version).Trim()
|
||||
return $(if ($v -match '^[vV]') { 'v' + $v.Substring(1) } else { "v$v" })
|
||||
}
|
||||
|
||||
# Object form: v{major}.{rc}.{beta}.{alpha}.{build}
|
||||
# Five-segment scheme: v{major}.{rc}.{beta}.{alpha}.{build}
|
||||
if ($null -ne $obj.build -or $null -ne $obj.alpha) {
|
||||
$alpha = if ($null -ne $obj.alpha) { [int]$obj.alpha } else { 1 }
|
||||
return "v$([int]$obj.major).$([int]$obj.rc).$([int]$obj.beta).$alpha.$([int]$obj.build)"
|
||||
}
|
||||
|
||||
# Legacy two-part stamp (projects not yet migrated): v{productVersion}.{buildNumber}
|
||||
# Only reached when there is something to build it from; otherwise $null, so
|
||||
# a caller sees "no label" instead of a label that matches everything.
|
||||
if ([string]::IsNullOrWhiteSpace([string]$obj.productVersion) -and $null -eq $obj.buildNumber) { return $null }
|
||||
return "v$([string]$obj.productVersion).$([int]$obj.buildNumber)"
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# ZKillOnly.ps1 — stop local dev server listeners for any project in zconfig.json.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# ZKiller.ps1 — kill then restart dev servers for any project in zconfig.json.
|
||||
#
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
{
|
||||
"version": "v1.0.0.0.28"
|
||||
"version": "v1.0.0.0.24"
|
||||
}
|
||||
|
||||
Binary file not shown.
@ -1 +0,0 @@
|
||||
6f6f1bf1b46e014e0518ef4ffa2b64e455e894681d5b337ffdd947b0efd0d538 zscripts-v1.0.0.0.25.zip
|
||||
Binary file not shown.
@ -1 +0,0 @@
|
||||
dfaa85f53e4dd16789ac0f1f8c9d7e506b56c0c068f4c5f8e2c4d1cce7db8d95 zscripts-v1.0.0.0.26.zip
|
||||
Binary file not shown.
@ -1 +0,0 @@
|
||||
f5b940344ffd832032b0c62e65e77f94bc2de294e690c9fd68362deb99d21e82 zscripts-v1.0.0.0.27.zip
|
||||
Binary file not shown.
@ -1 +0,0 @@
|
||||
5eca5198ba500bb0e1ceb1e5000cfb405d5fb5024fa500daa8f64f9c012c1291 zscripts-v1.0.0.0.28.zip
|
||||
@ -1,4 +1,4 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
|
||||
@ -28,20 +28,11 @@ from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
def pairs() -> tuple[tuple[str, str], ...]:
|
||||
"""Every root-level markdown file, with the twin it owes.
|
||||
|
||||
Discovered rather than listed. The list was hand-kept, and two files had
|
||||
quietly outgrown it - ELEVATOR_PITCH.md and TOKEN_SAVINGS.md had no twin
|
||||
at all, because adding a document and remembering to add it here are two
|
||||
separate acts and the second one is the one that gets skipped. Discovery
|
||||
makes them one act.
|
||||
"""
|
||||
return tuple((f.name, f.with_suffix(".txt").name) for f in sorted(ROOT.glob("*.md")))
|
||||
|
||||
|
||||
#: Kept as a name because the Pester suite reads it to know what to check.
|
||||
PAIRS = pairs()
|
||||
# Every markdown file that owes the repo a plain-text twin.
|
||||
PAIRS = (
|
||||
("README.md", "README.txt"),
|
||||
("CHANGELOG.md", "CHANGELOG.txt"),
|
||||
)
|
||||
|
||||
|
||||
def _inline(text: str) -> str:
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# setup_backup_schedule.ps1 — create a scheduled task for daily backups + OneDrive sync
|
||||
#
|
||||
|
||||
108
site/index.html
108
site/index.html
@ -1,108 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>zscripts — one-word dev commands</title>
|
||||
<meta name="description" content="One-word commands for AI-assisted development. Routine start, deploy, and backup chores become scripts — saving tokens and keeping the AI's context window focused on real work.">
|
||||
<link rel="canonical" href="https://zscripts.evomedia.net/">
|
||||
|
||||
<!-- Open Graph — LinkedIn, Slack and the rest build link previews from
|
||||
these. The URLs must be absolute: a relative og:image is dropped by the
|
||||
strict crawlers and the card renders as bare text. -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://zscripts.evomedia.net/">
|
||||
<meta property="og:site_name" content="evomedia.net">
|
||||
<meta property="og:title" content="zscripts — one-word dev commands">
|
||||
<meta property="og:description" content="One-word commands for AI-assisted development. Routine start, deploy and backup chores become scripts, keeping the AI's context window on real work.">
|
||||
<meta property="og:image" content="https://zscripts.evomedia.net/og-card.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630">
|
||||
<meta property="og:image:alt" content="zscripts — one-word dev commands, over a terminal showing zdeploy, zbackup and zrestart.">
|
||||
|
||||
<!-- Twitter/X card, reusing the same image. -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="zscripts — one-word dev commands">
|
||||
<meta name="twitter:description" content="One-word commands for AI-assisted development. Routine start, deploy and backup chores become scripts, keeping the AI's context window on real work.">
|
||||
<meta name="twitter:image" content="https://zscripts.evomedia.net/og-card.png">
|
||||
<style>
|
||||
:root{
|
||||
--bg:#0d1117; --panel:#161b22; --border:#2a313c;
|
||||
--fg:#e6edf3; --muted:#9aa7b4; --accent:#4ea1ff; --accent2:#3fb950;
|
||||
--mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
|
||||
--sans:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
html,body{margin:0;padding:0}
|
||||
body{background:var(--bg);color:var(--fg);font-family:var(--sans);line-height:1.55;
|
||||
-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
|
||||
.wrap{max-width:760px;margin:0 auto;padding:64px 24px 80px}
|
||||
.eyebrow{font-family:var(--mono);font-size:.8rem;letter-spacing:.08em;text-transform:uppercase;color:var(--accent)}
|
||||
h1{font-size:2.6rem;line-height:1.1;margin:.4rem 0 .2rem;font-weight:700}
|
||||
h1 .z{color:var(--accent)}
|
||||
.tag{font-size:1.2rem;color:var(--muted);margin:0 0 2rem}
|
||||
.lead{font-size:1.05rem;color:var(--fg);margin:0 0 2rem}
|
||||
.card{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:20px 22px;margin:0 0 18px}
|
||||
.card h2{font-size:.95rem;margin:0 0 12px;color:var(--muted);font-weight:600;letter-spacing:.02em;text-transform:uppercase}
|
||||
pre{margin:0;font-family:var(--mono);font-size:.92rem;overflow-x:auto;color:var(--fg)}
|
||||
pre .c{color:var(--muted)}
|
||||
pre .g{color:var(--accent2)}
|
||||
.cmds{display:grid;grid-template-columns:auto 1fr;gap:6px 18px;font-size:.95rem}
|
||||
.cmds code{font-family:var(--mono);color:var(--accent);white-space:nowrap}
|
||||
.cmds span{color:var(--muted)}
|
||||
.actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:6px}
|
||||
a.btn{display:inline-block;text-decoration:none;font-weight:600;font-size:.95rem;
|
||||
padding:11px 20px;border-radius:9px;border:1px solid var(--border)}
|
||||
a.primary{background:var(--accent);color:#04121f;border-color:var(--accent)}
|
||||
a.ghost{color:var(--fg)}
|
||||
a.primary:hover{filter:brightness(1.08)}
|
||||
a.ghost:hover{border-color:var(--accent);color:var(--accent)}
|
||||
footer{margin-top:44px;padding-top:20px;border-top:1px solid var(--border);color:var(--muted);font-size:.85rem}
|
||||
footer a{color:var(--muted)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="wrap">
|
||||
<p class="eyebrow">evomedia.net · developer tools</p>
|
||||
<h1><span class="z">z</span>scripts</h1>
|
||||
<p class="tag">Short, one-word commands for multi-project development.</p>
|
||||
|
||||
<p class="lead">
|
||||
A small suite of PowerShell commands built for AI-assisted
|
||||
development. Routine start / deploy / backup chores become single
|
||||
words a coding agent can run without reasoning through them — which
|
||||
saves tokens, but the real win is keeping the AI's context window
|
||||
focused on the actual work instead of housekeeping.
|
||||
</p>
|
||||
|
||||
<div class="card">
|
||||
<h2>The idea</h2>
|
||||
<pre><span class="c"># every chore the agent doesn't narrate is context kept free</span>
|
||||
<span class="g">zstart</span> sp <span class="c"># launch a project's dev server</span>
|
||||
<span class="g">zdeploy</span> sp <span class="c"># package + ship it</span>
|
||||
<span class="g">zbackup</span> all <span class="c"># snapshot the databases</span></pre>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>What's in the box</h2>
|
||||
<div class="cmds">
|
||||
<code>zstart</code><span>run a project's dev server locally</span>
|
||||
<code>zdeploy</code><span>build and deploy to the server</span>
|
||||
<code>zbackup</code><span>back up project databases</span>
|
||||
<code>zrestart</code><span>restart a running dev server</span>
|
||||
<code>zkill</code><span>stop a dev server cleanly</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<a class="btn primary" href="https://github.com/evomedia-net/evo.zscripts">View on GitHub</a>
|
||||
<a class="btn ghost" href="https://github.com/evomedia-net/evo.zscripts#readme">Read the docs</a>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
An <a href="https://evomedia.net">evomedia.net</a> project ·
|
||||
open source, sanitized for public use.
|
||||
</footer>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
BIN
site/og-card.png
BIN
site/og-card.png
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
@ -1,7 +0,0 @@
|
||||
# zscripts.evomedia.net — the public page for this toolkit.
|
||||
#
|
||||
# Deliberately the opposite of the candidate pages on this estate
|
||||
# (cardiff, opensesame, kelly, unify), which disallow everything. This one
|
||||
# is an open-source project page: being found is the point.
|
||||
User-agent: *
|
||||
Allow: /
|
||||
@ -1,4 +1,4 @@
|
||||
# evomedia.net Token Savers - https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers - https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels - dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
|
||||
@ -73,30 +73,14 @@ BeforeAll {
|
||||
$fixture | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath (Join-Path $script:Install "zconfig.json") -Encoding UTF8
|
||||
|
||||
# Run a script in a child process; capture merged output and exit code.
|
||||
#
|
||||
# Memoised on the exact command. Several checks deliberately assert
|
||||
# different things about the SAME invocation - that running bare exits
|
||||
# non-zero, that its usage lists the project keys, and that the usage
|
||||
# never mentions the underscore comment key are three checks of one run.
|
||||
# Starting a Windows PowerShell costs about 1.7 s, so re-running the same
|
||||
# command to ask it a second question is the single most expensive thing
|
||||
# this file does. The scripts reached here either refuse their input or
|
||||
# inspect an unused fixture port, so none of them has a side effect a
|
||||
# second run would reveal.
|
||||
$script:zRuns = @{}
|
||||
|
||||
function Invoke-ZScript {
|
||||
param([string]$Script, [string[]]$ScriptArgs = @())
|
||||
$key = @($Script) + $ScriptArgs -join "`n"
|
||||
if ($script:zRuns.ContainsKey($key)) { return $script:zRuns[$key] }
|
||||
$path = Join-Path $script:Install $Script
|
||||
$out = & powershell -NoProfile -ExecutionPolicy Bypass -File $path @ScriptArgs 2>&1 | ForEach-Object { "$_" }
|
||||
$result = [pscustomobject]@{
|
||||
return [pscustomobject]@{
|
||||
ExitCode = $LASTEXITCODE
|
||||
Output = ($out -join "`n")
|
||||
}
|
||||
$script:zRuns[$key] = $result
|
||||
return $result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# evomedia.net Token Savers - https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers - https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels - dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
|
||||
|
||||
@ -1,82 +0,0 @@
|
||||
# How a docker stack gets its images, and the deploy that shipped nothing.
|
||||
#
|
||||
# Invoke-Pester .\tests
|
||||
#
|
||||
# `docker compose pull` is right for a stack of published images - Prometheus,
|
||||
# Grafana, docker-mailserver - and wrong for one built from a Dockerfile in the
|
||||
# tree, where there is nothing to pull.
|
||||
#
|
||||
# The trap is what happens after. `docker compose up -d` builds only when the
|
||||
# image is MISSING, so the first deploy of a build-from-source stack works and
|
||||
# every one after it uploads the new code, starts the OLD image, and reports
|
||||
# success. Green deploy, healthy container, and the change is not in it. That
|
||||
# is the failure this helper exists to prevent, and it is worse than an error
|
||||
# because nothing about it looks wrong.
|
||||
#
|
||||
# ZHelpers.ps1 is dot-sourced rather than zdeploy.ps1: zdeploy executes its
|
||||
# main flow on load, helpers only define functions.
|
||||
|
||||
BeforeAll {
|
||||
. (Join-Path (Split-Path -Parent $PSScriptRoot) "ZHelpers.ps1")
|
||||
|
||||
function New-Proj { param($Build)
|
||||
if ($null -eq $Build) { return [pscustomobject]@{ deploy = [pscustomobject]@{ gitPull = $true } } }
|
||||
return [pscustomobject]@{ deploy = [pscustomobject]@{ build = $Build } }
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Get-DockerImageStep - build here, or pull from a registry' {
|
||||
|
||||
It 'pulls by default, so every existing docker stack is unaffected' {
|
||||
$step = Get-DockerImageStep -Proj (New-Proj $null) -RemotePath '/home/u/stack/x'
|
||||
$step.Command | Should -BeLike '*docker compose pull*'
|
||||
$step.Command | Should -Not -BeLike '*build*'
|
||||
}
|
||||
|
||||
It 'pulls for a project with no deploy block at all' {
|
||||
$step = Get-DockerImageStep -Proj ([pscustomobject]@{}) -RemotePath '/home/u/stack/x'
|
||||
$step.Command | Should -BeLike '*docker compose pull*'
|
||||
}
|
||||
|
||||
It 'builds when the project asks to be built' {
|
||||
$step = Get-DockerImageStep -Proj (New-Proj $true) -RemotePath '/home/u/stack/x'
|
||||
$step.Command | Should -BeLike '*docker compose build*'
|
||||
$step.Command | Should -Not -BeLike '*compose pull*'
|
||||
}
|
||||
|
||||
It 'still pulls when build is explicitly false' {
|
||||
$step = Get-DockerImageStep -Proj (New-Proj $false) -RemotePath '/home/u/stack/x'
|
||||
$step.Command | Should -BeLike '*docker compose pull*'
|
||||
}
|
||||
|
||||
It 'refreshes the base image on a build, so a rebuild is not pinned to the first one' {
|
||||
$step = Get-DockerImageStep -Proj (New-Proj $true) -RemotePath '/home/u/stack/x'
|
||||
$step.Command | Should -BeLike '*--pull*'
|
||||
}
|
||||
|
||||
It 'runs in the project directory: <Build>' -ForEach @(
|
||||
@{ Build = $true }
|
||||
@{ Build = $false }
|
||||
) {
|
||||
$step = Get-DockerImageStep -Proj (New-Proj $Build) -RemotePath '/home/u/stack/ablecamera'
|
||||
$step.Command | Should -BeLike 'cd /home/u/stack/ablecamera &&*'
|
||||
}
|
||||
|
||||
It 'labels the step with what it actually does: <Build>' -ForEach @(
|
||||
@{ Build = $true; Expected = 'docker compose build' }
|
||||
@{ Build = $false; Expected = 'docker compose pull' }
|
||||
) {
|
||||
(Get-DockerImageStep -Proj (New-Proj $Build) -RemotePath '/x').Label | Should -Be $Expected
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'the docker deploy uses it' {
|
||||
|
||||
It 'no longer hardcodes compose pull' {
|
||||
$text = Get-Content -Raw (Join-Path (Split-Path -Parent $PSScriptRoot) "zdeploy.ps1")
|
||||
$body = $text.Substring($text.IndexOf('function Invoke-DockerDeploy'))
|
||||
$body = $body.Substring(0, $body.IndexOf('function Invoke-ZTokensPublish'))
|
||||
$body | Should -Match 'Get-DockerImageStep'
|
||||
$body | Should -Not -Match '"docker compose pull"'
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
# evomedia.net Token Savers - https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels - dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
|
||||
# LinkPreview.Tests.ps1 - the public page renders a card, not a bare URL.
|
||||
#
|
||||
# Invoke-Pester .\tests
|
||||
#
|
||||
# Pasting zscripts.evomedia.net into LinkedIn, Slack or a message builds a card
|
||||
# from the page's og:* tags. The page had a title and a description and nothing
|
||||
# else, so it pasted as a bare URL.
|
||||
#
|
||||
# None of that is visible from here. The page is correct, the site is up, and
|
||||
# the only symptom is a card somewhere else - which is why the rule is asserted
|
||||
# rather than remembered.
|
||||
#
|
||||
# The last test is the one that earned its place: the first draft of the card
|
||||
# showed `ztests`, which is not a command in this repo. A card is public copy,
|
||||
# and public copy must not advertise a script that does not exist.
|
||||
|
||||
BeforeAll {
|
||||
$script:RepoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$script:Page = Join-Path $script:RepoRoot "site\index.html"
|
||||
$script:Card = Join-Path $script:RepoRoot "site\og-card.png"
|
||||
$script:Html = [IO.File]::ReadAllText($script:Page)
|
||||
|
||||
function Get-Meta {
|
||||
param([string]$Key)
|
||||
$pattern = '<meta (?:property|name)="' + [regex]::Escape($Key) + '" content="([^"]*)">'
|
||||
$m = [regex]::Match($script:Html, $pattern)
|
||||
if ($m.Success) { return $m.Groups[1].Value }
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
Describe "zscripts.evomedia.net link preview" {
|
||||
|
||||
It "carries the tags a card is built from" {
|
||||
foreach ($key in @('og:type', 'og:url', 'og:title', 'og:description', 'og:image')) {
|
||||
Get-Meta $key | Should -Not -BeNullOrEmpty -Because "$key is what the card is made of"
|
||||
}
|
||||
# Without it X renders the small square variant instead of a card.
|
||||
Get-Meta 'twitter:card' | Should -Be 'summary_large_image'
|
||||
}
|
||||
|
||||
It "gives absolute URLs, which the strict crawlers require" {
|
||||
foreach ($key in @('og:url', 'og:image', 'twitter:image')) {
|
||||
Get-Meta $key | Should -Match '^https://'
|
||||
}
|
||||
}
|
||||
|
||||
It "points og:url at the same place as the canonical" {
|
||||
$canonical = [regex]::Match($script:Html, '<link rel="canonical" href="([^"]*)">')
|
||||
$canonical.Success | Should -BeTrue
|
||||
Get-Meta 'og:url' | Should -Be $canonical.Groups[1].Value
|
||||
}
|
||||
|
||||
It "publishes the card beside the page" {
|
||||
# site/ is copied to the server wholesale; a card outside it is a 404
|
||||
# and the preview falls back to text.
|
||||
Test-Path -LiteralPath $script:Card | Should -BeTrue
|
||||
}
|
||||
|
||||
It "serves a card that is the size the tags claim" {
|
||||
# PNG header: width and height are big-endian at offsets 16 and 20.
|
||||
$bytes = [IO.File]::ReadAllBytes($script:Card)[0..23]
|
||||
$width = [int]$bytes[16] * 16777216 + [int]$bytes[17] * 65536 + [int]$bytes[18] * 256 + [int]$bytes[19]
|
||||
$height = [int]$bytes[20] * 16777216 + [int]$bytes[21] * 65536 + [int]$bytes[22] * 256 + [int]$bytes[23]
|
||||
$width | Should -Be 1200
|
||||
$height | Should -Be 630
|
||||
Get-Meta 'og:image:width' | Should -Be '1200'
|
||||
Get-Meta 'og:image:height' | Should -Be '630'
|
||||
}
|
||||
|
||||
It "does not advertise a command this repo does not ship" {
|
||||
$alt = Get-Meta 'og:image:alt'
|
||||
$alt | Should -Not -BeNullOrEmpty
|
||||
|
||||
$named = [regex]::Matches($alt, '\bz[a-z_]+\b') | ForEach-Object { $_.Value } | Sort-Object -Unique
|
||||
$named.Count | Should -BeGreaterThan 0 -Because 'the alt text names the commands on the card'
|
||||
|
||||
foreach ($cmd in $named) {
|
||||
if ($cmd -eq 'zscripts') { continue } # the toolkit, not a command
|
||||
$exists = @('.ps1', '.cmd') | Where-Object {
|
||||
Test-Path -LiteralPath (Join-Path $script:RepoRoot "$cmd$_")
|
||||
}
|
||||
$exists | Should -Not -BeNullOrEmpty -Because "$cmd is on the card but is not in this repo"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
|
||||
|
||||
@ -38,19 +38,14 @@ Describe "plain-text twins" {
|
||||
Test-Path -LiteralPath $script:Generator | Should -BeTrue
|
||||
}
|
||||
|
||||
# Asks the REPOSITORY what markdown it has, not the generator what it was
|
||||
# told about. Scraping the generator's own list could only ever prove the
|
||||
# list was self-consistent - a document nobody added to it was invisible to
|
||||
# the check, which is how ELEVATOR_PITCH.md and TOKEN_SAVINGS.md sat here
|
||||
# with no twin while this test passed.
|
||||
It "every .md at the repository root has a twin" {
|
||||
$mds = Get-ChildItem -LiteralPath $script:RepoRoot -Filter *.md -File
|
||||
It "every .md that owes a twin has one" {
|
||||
$pairs = Select-String -Path $script:Generator -Pattern '^\s*\("([^"]+\.md)",\s*"([^"]+\.txt)"\),' |
|
||||
ForEach-Object { [pscustomobject]@{ Md = $_.Matches[0].Groups[1].Value; Txt = $_.Matches[0].Groups[2].Value } }
|
||||
|
||||
$mds.Count | Should -BeGreaterThan 0 -Because "the repo documents itself in markdown"
|
||||
foreach ($md in $mds) {
|
||||
$txt = [IO.Path]::ChangeExtension($md.FullName, ".txt")
|
||||
Test-Path -LiteralPath $txt |
|
||||
Should -BeTrue -Because "$($md.Name) owes a twin at $(Split-Path -Leaf $txt)"
|
||||
$pairs.Count | Should -BeGreaterThan 0 -Because "PAIRS in plaintext_twins.py is what this suite checks"
|
||||
foreach ($p in $pairs) {
|
||||
Test-Path -LiteralPath (Join-Path $script:RepoRoot $p.Md) | Should -BeTrue -Because "$($p.Md) is listed in PAIRS"
|
||||
Test-Path -LiteralPath (Join-Path $script:RepoRoot $p.Txt) | Should -BeTrue -Because "$($p.Md) owes a twin at $($p.Txt)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
|
||||
|
||||
@ -23,21 +23,15 @@
|
||||
#>
|
||||
@{
|
||||
Denied = @(
|
||||
# The retired EHS name is split with a one-character class in both rules
|
||||
# below (Smart[P]lant, smart[p]lantehs). The regex is identical - a class of
|
||||
# one matches exactly that character - but the literal no longer appears in
|
||||
# this file, which is public. The private tree still carries that name in
|
||||
# ~20 places, so these rules are still load-bearing: do not delete them,
|
||||
# and do not un-split them.
|
||||
@{ Name = 'private project name'; Pattern = '\b(EvoCivilCode|EvoPlatform|DocketMail|Smart[P]lant\w*|ProvenSheet|evoehs|evoproven|evoaicc|evolocate|evoplatform)\b' }
|
||||
@{ Name = 'private project name'; Pattern = '\b(EvoCivilCode|EvoPlatform|DocketMail|SmartPlant\w*|ProvenSheet|evoehs|evoproven|evoaicc|evolocate|evoplatform)\b' }
|
||||
# The current spellings, which the line above never saw: a dot or a
|
||||
# hyphen breaks the word and an underscore hides the boundary, so
|
||||
# evo.ehs, evo-ai and evoehs_app all passed (evo.scripts#138 in the
|
||||
# private tree). Internal issue references travel with them.
|
||||
@{ Name = 'current product name'; Pattern = '(?i)\bevo[.-](ehs|ai|edge|locate|proven|platform|civilcode|scripts)\b|\bevoehs' }
|
||||
@{ Name = 'internal issue reference'; Pattern = 'evo\.scripts#\d+' }
|
||||
@{ Name = 'private product domain'; Pattern = '\b(smart[p]lantehs\.com|provensheet\.com|evoehs\.com|civilcode\.evomedia\.net|dashboard\.evomedia\.net|webmail\.evomedia\.net|mail-admin\.evomedia\.net|docketmail\.evomedia\.net|cardiff\.evomedia\.net|platform\.evomedia\.net|ai\.evomedia\.net|git\.evomedia\.net|analytics\.evomedia\.net)\b' }
|
||||
@{ Name = 'private-only script'; Pattern = '\b(register_civilcode|register_docketmail|sp_seed_demo_prod|zpublish_stats|zcoverage|zresume|swag_set_owner|provision_demo|apply_platform_config_fixes)\b' }
|
||||
@{ Name = 'private product domain'; Pattern = '\b(smartplantehs\.com|provensheet\.com|evoehs\.com|civilcode\.evomedia\.net|dashboard\.evomedia\.net|webmail\.evomedia\.net|mail-admin\.evomedia\.net|docketmail\.evomedia\.net|cardiff\.evomedia\.net|platform\.evomedia\.net|ai\.evomedia\.net|git\.evomedia\.net|analytics\.evomedia\.net)\b' }
|
||||
@{ Name = 'private-only script'; Pattern = '\b(register_civilcode|register_docketmail|sp_seed_demo_prod|zpublish_stats|zcoverage|zmerge|zpull|zresume|swag_set_owner|provision_demo|apply_platform_config_fixes)\b' }
|
||||
@{ Name = 'local drive path'; Pattern = '[A-Za-z]:\\\\?evomedia\.net' }
|
||||
@{ Name = 'operator home path'; Pattern = '/home/ubuntu/' }
|
||||
@{ Name = 'real pem key name'; Pattern = 'evomedia-prod\.pem' }
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# token-count.ps1 — measure script output volume to estimate AI agent token costs.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zbackup.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zbackup.ps1 — local backups: zip project sources (plus a Postgres dump when the
|
||||
# project's .env has a DATABASE_URL) into the backups folder.
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zbackup_and_sync.ps1 — run backups, then sync the backups folder offsite.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zbackup_ec2.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zbackup_ec2.ps1 — pull backups down from the server: a Postgres dump for projects
|
||||
# with a "db" config block, plus a zip of server-side data dirs (uploads/archive/dist).
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zchecksums.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zchecksums.ps1 - verify (or regenerate) SHA-256 checksums for the scripts.
|
||||
#
|
||||
|
||||
@ -122,7 +122,6 @@
|
||||
"analytics": {
|
||||
"label": "Analytics (any docker compose app)",
|
||||
"kind": "docker",
|
||||
"_build_note": "Images pulled from a registry need nothing here. If the image is BUILT from a Dockerfile in this tree, add \"deploy\": { \"build\": true } \u2014 without it a redeploy uploads the new code and restarts the OLD image, and reports success.",
|
||||
"localRoot": "C:\\YourRoot\\analytics",
|
||||
"domain": "analytics.yourdomain.com",
|
||||
"remote": {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zdeploy.ps1" %*
|
||||
|
||||
38
zdeploy.ps1
38
zdeploy.ps1
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zdeploy.ps1 — deploy any project defined in zconfig.json to the server.
|
||||
# Each project runs its own docker compose stack; the handler is picked by the
|
||||
@ -546,16 +546,6 @@ function Wait-VerifyStaticBuild {
|
||||
# advances deliberately — one version bump per release — so "is the
|
||||
# build I just packed live?" means an exact match.
|
||||
$expectedLabel = Get-LabelFromBuildJsonObj $PreZipBuildState
|
||||
# A stamp we cannot read is not a version to check against. Comparing an
|
||||
# unreadable local label to an unreadable remote one is how a verification
|
||||
# once passed while the container served anything it liked, so refuse to
|
||||
# run rather than run a comparison that cannot fail.
|
||||
if ([string]::IsNullOrWhiteSpace($expectedLabel)) {
|
||||
Write-Host "`n--- [$Key version] NOT VERIFIED - build-version.json is present but unreadable ---" -ForegroundColor Yellow
|
||||
Write-Host " Got: $(($PreZipBuildState | ConvertTo-Json -Compress -Depth 4))" -ForegroundColor DarkGray
|
||||
Write-Host " Expected one of: {`"version`":`"v1.0.0.0.0`"} | {major,rc,beta,alpha,build} | {productVersion,buildNumber}" -ForegroundColor DarkGray
|
||||
return
|
||||
}
|
||||
Write-Host "`n--- [$Key] Live build verification (expect $expectedLabel) ---" -ForegroundColor Cyan
|
||||
$containerName = $Proj.remote.containerName
|
||||
$deadline = (Get-Date).AddSeconds(45)
|
||||
@ -575,7 +565,7 @@ function Wait-VerifyStaticBuild {
|
||||
}
|
||||
if ($r) {
|
||||
$remoteLabel = Get-LabelFromBuildJsonObj $r
|
||||
if ($remoteLabel -and $remoteLabel -eq $expectedLabel) {
|
||||
if ($remoteLabel -eq $expectedLabel) {
|
||||
Write-Host " PASS - live build $remoteLabel matches expected." -ForegroundColor Green
|
||||
return
|
||||
}
|
||||
@ -1176,8 +1166,8 @@ function Invoke-StaticDeploy {
|
||||
param([string]$Key, $Proj)
|
||||
|
||||
# Plain static sites - no build, no container of their own. The landing
|
||||
# container serves them straight off disk, one directory per host, so
|
||||
# shipping the files IS the deploy: there is nothing to restart afterwards.
|
||||
# container serves them straight off disk out of /srv/$host, so shipping
|
||||
# the files IS the deploy: there is nothing to restart afterwards.
|
||||
$root = Join-Path $Proj.localRoot $Proj.siteDir
|
||||
$remotePath = $Proj.remote.path
|
||||
|
||||
@ -1254,10 +1244,7 @@ function Invoke-DockerDeploy {
|
||||
if ($LASTEXITCODE -ne 0) { throw "SCP failed for $($d.Name) (exit $LASTEXITCODE)" }
|
||||
}
|
||||
|
||||
# Built here or pulled from a registry - see Get-DockerImageStep for why
|
||||
# a build-from-source stack cannot use `pull` and silently ships nothing.
|
||||
$imageStep = Get-DockerImageStep -Proj $Proj -RemotePath $remotePath
|
||||
Invoke-Ec2Step $imageStep.Label $imageStep.Command
|
||||
Invoke-Ec2Step "docker compose pull" "cd $remotePath && sudo docker compose pull"
|
||||
Invoke-Ec2Step "docker compose up -d" "cd $remotePath && sudo docker compose up -d"
|
||||
|
||||
Invoke-Ec2PostDeployCleanup -Label $Key
|
||||
@ -1304,16 +1291,6 @@ function Invoke-ZTokensPublish {
|
||||
|
||||
# ── Dispatch ─────────────────────────────────────────────────────────────────
|
||||
|
||||
# pip, uv and docker draw progress bars with box-drawing characters: "━" is
|
||||
# the bytes E2 94 81. PowerShell 5.1 decodes a native command's stdout - ssh's,
|
||||
# here - with [Console]::OutputEncoding, which on Windows is the OEM code page
|
||||
# (437 on this machine), where those three bytes read "Γöü". Forty per bar.
|
||||
# Decode the box's output as the UTF-8 it is for the duration of the deploy,
|
||||
# and put the console back in the finally so a deploy that throws does not
|
||||
# leave the session changed.
|
||||
$prevConsoleEncoding = [Console]::OutputEncoding
|
||||
[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
|
||||
try {
|
||||
foreach ($key in $Projects) {
|
||||
# 'ztokens' matches the tool it runs (ztokens.cmd / ztokens.ps1). The old
|
||||
# singular 'ztoken' still works so existing habits and any script that
|
||||
@ -1331,9 +1308,6 @@ try {
|
||||
default { throw "No deploy handler for kind '$($proj.kind)' (project '$key'). Add an Invoke-<Kind>Deploy function in zdeploy.ps1." }
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
[Console]::OutputEncoding = $prevConsoleEncoding
|
||||
}
|
||||
# The timestamp goes through Stop-ZTracking as the FinalNote so it lands after
|
||||
# the tracking footer and before the trailing blank lines - the last thing on
|
||||
# screen, which is the point: scroll to the bottom and you can see how long ago
|
||||
|
||||
2
zec2.cmd
2
zec2.cmd
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zec2.ps1" %*
|
||||
|
||||
18
zec2.ps1
18
zec2.ps1
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zec2.ps1 — quick reachability check (TCP + HTTP + live build version) for deployed projects.
|
||||
#
|
||||
@ -22,11 +22,9 @@ Start-ZTracking
|
||||
|
||||
$cfg = Get-ZConfig
|
||||
if (-not $HostName) { $HostName = $cfg.ec2.ip }
|
||||
# Needed by the container-side version read below. zec2 had no ssh of its own
|
||||
# before that, so the read referenced three variables this script never
|
||||
# defined -- and because it sits inside a try/catch, the failure was silent:
|
||||
# it fell through to the HTTP call and reported nothing once that endpoint
|
||||
# stopped being public. Same names zec2online.ps1 uses.
|
||||
# Needed by the container-side version read below; same names zec2online.ps1
|
||||
# uses. Without them that read throws inside its try/catch and falls through
|
||||
# silently, which looks identical to a service that cannot be reached.
|
||||
$PemKey = $cfg.ec2.pemKey
|
||||
$SshTarget = Get-Ec2Target
|
||||
|
||||
@ -105,9 +103,9 @@ function Show-Zec2LiveVersion {
|
||||
$r = Invoke-RestMethod -Uri "http://${HostName}/build-version.json" -Headers $headers -TimeoutSec 10 -ErrorAction Stop
|
||||
if ($r) { Write-Host " Live build: $(Get-LabelFromBuildJsonObj $r)" -ForegroundColor Gray }
|
||||
} else {
|
||||
# Container-side first where the project configures it: the
|
||||
# endpoint is not public on every project, and asking the edge
|
||||
# answers from whichever vhost matches the Host header.
|
||||
# Container-side first where the project configures it: a build
|
||||
# stamp is not public on every site, and asking the proxy answers
|
||||
# from whichever vhost matches the Host header.
|
||||
$execCmd = Get-ServerSideVersionCommand -Proj $Proj
|
||||
$label = $null
|
||||
if ($execCmd -and (Test-Path $PemKey)) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zec2_rotatekeys.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zec2_rotatekeys.ps1 - rotate / reset secret keys in a project's SERVER-SIDE
|
||||
# .env, in place, without the values ever passing through this machine's shell
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zec2online.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zec2online.ps1 — deep health check: verify apps are live AND running the expected
|
||||
# build; auto-start downed stacks via docker compose and stream diagnostics.
|
||||
@ -86,10 +86,9 @@ function Get-RemoteVersionLabel {
|
||||
param($Proj)
|
||||
$headers = @{}
|
||||
if ($Proj.domain) { $headers['Host'] = $Proj.domain }
|
||||
# Container-side first where the project configures it. The endpoint is
|
||||
# not public on every project, and the edge answers from whichever vhost
|
||||
# matches the Host header -- which is how a check reads another
|
||||
# product's version.
|
||||
# Container-side first where the project configures it. A build stamp is
|
||||
# not public on every site, and the proxy answers from whichever vhost
|
||||
# matches the Host header - which is how a check reads another service.
|
||||
$execCmd = Get-ServerSideVersionCommand -Proj $Proj
|
||||
if ($execCmd -and (Test-Path $PemKey)) {
|
||||
try {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0ZKillOnly.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zkill.ps1 — alias for ZKillOnly.ps1 (kept so both names work). All args pass through.
|
||||
& (Join-Path $PSScriptRoot "ZKillOnly.ps1") @args
|
||||
|
||||
251
zmerge.ps1
251
zmerge.ps1
@ -1,251 +0,0 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
|
||||
# zmerge.ps1 - merge the fleet's ready pull requests in one pass.
|
||||
#
|
||||
# Usage:
|
||||
# zmerge dry run: list every open PR and its verdict
|
||||
# zmerge -Execute merge everything that is genuinely ready
|
||||
# zmerge -e the same; -e is an alias, as -s is for -Scan
|
||||
# zmerge -Exclude 431 skip PRs by number (repeatable)
|
||||
# zmerge -Repo <name> limit to one repo
|
||||
# zmerge -Only 68,67 merge just these
|
||||
# zmerge -Execute -Yes skip the confirmation prompt
|
||||
#
|
||||
# WHY THIS EXISTS
|
||||
# ---------------
|
||||
# Eleven ready PRs across three repos is eleven trips through the GitHub UI, and
|
||||
# the failure mode is not the clicking - it is that `gh pr create` and the merge
|
||||
# button will both happily accept a PR that cannot actually merge. Mergeability
|
||||
# is computed asynchronously, so a PR reports UNKNOWN for a few seconds after any
|
||||
# push and CONFLICTING only later. Merging by hand, the tenth PR is the one that
|
||||
# gets rubber-stamped.
|
||||
#
|
||||
# So this refuses to merge anything it has not just re-checked, and it re-checks
|
||||
# after every merge, because merging one PR can conflict another in the same
|
||||
# repo.
|
||||
#
|
||||
# WHAT IT WILL NOT DO
|
||||
# -------------------
|
||||
# * merge a PR that is not MERGEABLE/CLEAN at the moment it is reached
|
||||
# * merge a draft, or one with a failing required check
|
||||
# * bump versions - each repo stamps differently (zbump for zscripts,
|
||||
# bump_build_version.mjs for www), and a wrong stamp is worse than none.
|
||||
# The follow-up commands are printed instead.
|
||||
# * deploy anything. Deploys are run by hand, deliberately.
|
||||
#
|
||||
# ON UNKNOWN
|
||||
# ----------
|
||||
# GitHub returns mergeable=UNKNOWN while it computes, which is indistinguishable
|
||||
# from trouble if you only look once. Each PR is polled up to $PollTries times
|
||||
# before being treated as not ready, so a slow answer does not read as a failure
|
||||
# and a real CONFLICTING never reads as "probably fine".
|
||||
|
||||
param(
|
||||
[Alias('e')][switch]$Execute,
|
||||
[switch]$Yes,
|
||||
[int[]]$Exclude = @(),
|
||||
[int[]]$Only = @(),
|
||||
[string]$Repo,
|
||||
[int]$PollTries = 6,
|
||||
[int]$PollDelaySeconds = 4
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Two blank lines at the end of a run, matching every other z-script, so output
|
||||
# is separated from the next prompt. Local copy rather than ZHelpers: this
|
||||
# script does not dot-source it.
|
||||
function Write-ZTrailer { Write-Host ""; Write-Host "" }
|
||||
|
||||
# Every repository in the org, asked of GitHub rather than remembered here.
|
||||
#
|
||||
# Local to this script for the same reason Write-ZTrailer is: zmerge needs gh
|
||||
# and nothing else, and dot-sourcing 1,200 lines of deploy helpers for one
|
||||
# function would trade that away.
|
||||
#
|
||||
# THROWS rather than returning an empty list when gh fails. A merge tool that
|
||||
# quietly scans nothing prints exactly the same reassuring line as one that
|
||||
# scanned everything and found nothing, and those two must never be
|
||||
# confusable - which is precisely how the list this replaced hid its own rot.
|
||||
function Get-FleetRepos {
|
||||
param([Parameter(Mandatory)][string]$Org)
|
||||
$raw = & gh repo list $Org --limit 200 --json name,isArchived 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "gh repo list $Org failed ($LASTEXITCODE): $($raw -join ' ')"
|
||||
}
|
||||
try { $all = $raw | ConvertFrom-Json } catch {
|
||||
throw "gh repo list $Org did not return JSON: $($raw -join ' ')"
|
||||
}
|
||||
if (-not $all) { throw "gh repo list $Org returned no repositories" }
|
||||
$names = @($all | Where-Object { -not $_.isArchived } |
|
||||
ForEach-Object { $_.name } | Sort-Object)
|
||||
if ($names.Count -eq 0) { throw "every repository in $Org is archived?" }
|
||||
return $names
|
||||
}
|
||||
|
||||
|
||||
|
||||
$ORG = "evomedia-net"
|
||||
# Discovered, never listed. The list this replaced had fallen fourteen
|
||||
# repositories behind: a scan covered sixteen of thirty and said "Nothing open
|
||||
# to merge" while a ready PR sat in one of the fourteen it could not see.
|
||||
$REPOS = Get-FleetRepos -Org $ORG
|
||||
|
||||
# How each repo advances its build stamp after a merge. Printed as follow-up,
|
||||
# never run: see the header.
|
||||
$BUMP = @{
|
||||
"evo.zscripts" = "zbump"
|
||||
"evo.www" = "node scripts/bump_build_version.mjs bump (on main, then push)"
|
||||
}
|
||||
|
||||
function Invoke-Gh {
|
||||
param([string[]]$GhArgs, [switch]$AllowFail)
|
||||
$prev = $ErrorActionPreference
|
||||
$ErrorActionPreference = "Continue"
|
||||
try {
|
||||
$out = & gh @GhArgs 2>&1 | ForEach-Object { "$_" }
|
||||
$code = $LASTEXITCODE
|
||||
} finally { $ErrorActionPreference = $prev }
|
||||
if ($code -ne 0 -and -not $AllowFail) {
|
||||
throw "gh $($GhArgs -join ' ') failed ($code): $($out -join "`n")"
|
||||
}
|
||||
return [pscustomobject]@{ Output = ($out -join "`n"); Code = $code }
|
||||
}
|
||||
|
||||
function Get-OpenPrs {
|
||||
param([string]$RepoName)
|
||||
$r = Invoke-Gh @("pr", "list", "-R", "$ORG/$RepoName", "--state", "open",
|
||||
"--limit", "100", "--json", "number,title,isDraft,headRefName") -AllowFail
|
||||
if ($r.Code -ne 0 -or -not $r.Output) { return @() }
|
||||
return @($r.Output | ConvertFrom-Json)
|
||||
}
|
||||
|
||||
# Re-checked immediately before every merge, and again after each one, because
|
||||
# merging into the default branch can conflict a sibling PR in the same repo.
|
||||
function Get-Readiness {
|
||||
param([string]$RepoName, [int]$Number)
|
||||
for ($i = 1; $i -le $PollTries; $i++) {
|
||||
$r = Invoke-Gh @("pr", "view", "$Number", "-R", "$ORG/$RepoName",
|
||||
"--json", "mergeable,mergeStateStatus,state,isDraft") -AllowFail
|
||||
if ($r.Code -ne 0) { return [pscustomobject]@{ Ready = $false; Why = "cannot read PR" } }
|
||||
$j = $r.Output | ConvertFrom-Json
|
||||
if ($j.state -ne "OPEN") { return [pscustomobject]@{ Ready = $false; Why = "state is $($j.state)" } }
|
||||
if ($j.isDraft) { return [pscustomobject]@{ Ready = $false; Why = "draft" } }
|
||||
if ($j.mergeable -eq "MERGEABLE" -and $j.mergeStateStatus -eq "CLEAN") {
|
||||
return [pscustomobject]@{ Ready = $true; Why = "MERGEABLE/CLEAN" }
|
||||
}
|
||||
if ($j.mergeable -eq "CONFLICTING") {
|
||||
return [pscustomobject]@{ Ready = $false; Why = "CONFLICTING - rebase it" }
|
||||
}
|
||||
# UNKNOWN, or a non-CLEAN state such as BLOCKED/BEHIND: give GitHub a
|
||||
# moment, since it computes mergeability asynchronously.
|
||||
if ($j.mergeable -ne "UNKNOWN" -and $j.mergeStateStatus -ne "UNKNOWN") {
|
||||
return [pscustomobject]@{ Ready = $false; Why = "$($j.mergeable)/$($j.mergeStateStatus)" }
|
||||
}
|
||||
Start-Sleep -Seconds $PollDelaySeconds
|
||||
}
|
||||
return [pscustomobject]@{ Ready = $false; Why = "still UNKNOWN after $PollTries tries" }
|
||||
}
|
||||
|
||||
$targets = if ($Repo) { @($Repo) } else { $REPOS }
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Scanning $($targets.Count) repo(s) for open pull requests..." -ForegroundColor Cyan
|
||||
|
||||
$queue = @()
|
||||
foreach ($r in $targets) {
|
||||
foreach ($pr in (Get-OpenPrs -RepoName $r)) {
|
||||
if ($Exclude -contains $pr.number) { continue }
|
||||
if ($Only.Count -gt 0 -and $Only -notcontains $pr.number) { continue }
|
||||
$queue += [pscustomobject]@{ Repo = $r; Number = $pr.number; Title = $pr.title; Draft = $pr.isDraft }
|
||||
}
|
||||
}
|
||||
|
||||
if ($queue.Count -eq 0) { Write-Host "Nothing open to merge." -ForegroundColor Yellow; Write-ZTrailer; exit 0 }
|
||||
|
||||
Write-Host ""
|
||||
foreach ($p in $queue) {
|
||||
$v = Get-Readiness -RepoName $p.Repo -Number $p.Number
|
||||
$p | Add-Member -NotePropertyName Ready -NotePropertyValue $v.Ready -Force
|
||||
$p | Add-Member -NotePropertyName Why -NotePropertyValue $v.Why -Force
|
||||
$mark = if ($v.Ready) { "OK " } else { "SKIP" }
|
||||
$col = if ($v.Ready) { "Green" } else { "Yellow" }
|
||||
Write-Host (" {0} {1,-14} #{2,-4} {3}" -f $mark, $p.Repo, $p.Number, $p.Title) -ForegroundColor $col
|
||||
if (-not $v.Ready) { Write-Host (" -> {0}" -f $v.Why) -ForegroundColor DarkYellow }
|
||||
}
|
||||
|
||||
$ready = @($queue | Where-Object { $_.Ready })
|
||||
Write-Host ""
|
||||
Write-Host "$($ready.Count) of $($queue.Count) ready to merge." -ForegroundColor Cyan
|
||||
|
||||
if (-not $Execute) {
|
||||
Write-Host ""
|
||||
Write-Host "Dry run. Re-run with -Execute (or -e) to merge." -ForegroundColor Yellow
|
||||
Write-ZTrailer
|
||||
exit 0
|
||||
}
|
||||
if ($ready.Count -eq 0) { Write-ZTrailer; exit 1 }
|
||||
|
||||
if (-not $Yes) {
|
||||
Write-Host ""
|
||||
$answer = Read-Host "Squash-merge these $($ready.Count) PRs and delete their branches? (y/N)"
|
||||
if ($answer -notmatch '^(y|yes)$') { Write-Host "Aborted." -ForegroundColor Yellow; Write-ZTrailer; exit 1 }
|
||||
}
|
||||
|
||||
$merged = @(); $failed = @()
|
||||
foreach ($p in $ready) {
|
||||
# Re-check: an earlier merge in this same repo may have conflicted this one.
|
||||
$v = Get-Readiness -RepoName $p.Repo -Number $p.Number
|
||||
if (-not $v.Ready) {
|
||||
Write-Host (" SKIP {0} #{1} - {2}" -f $p.Repo, $p.Number, $v.Why) -ForegroundColor Yellow
|
||||
$failed += [pscustomobject]@{ Repo = $p.Repo; Number = $p.Number; Why = $v.Why }
|
||||
continue
|
||||
}
|
||||
$r = Invoke-Gh @("pr", "merge", "$($p.Number)", "-R", "$ORG/$($p.Repo)",
|
||||
"--squash", "--delete-branch") -AllowFail
|
||||
if ($r.Code -eq 0) {
|
||||
Write-Host (" MERGED {0} #{1}" -f $p.Repo, $p.Number) -ForegroundColor Green
|
||||
$merged += $p
|
||||
} else {
|
||||
Write-Host (" FAILED {0} #{1}" -f $p.Repo, $p.Number) -ForegroundColor Red
|
||||
Write-Host (" {0}" -f $r.Output) -ForegroundColor DarkRed
|
||||
$failed += [pscustomobject]@{ Repo = $p.Repo; Number = $p.Number; Why = $r.Output }
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "merged $($merged.Count), failed/skipped $($failed.Count)" -ForegroundColor Cyan
|
||||
|
||||
# Verify rather than trust the exit codes - a merge can report success and leave
|
||||
# the PR in an unexpected state.
|
||||
if ($merged.Count -gt 0) {
|
||||
Write-Host ""
|
||||
Write-Host "Verifying:" -ForegroundColor Cyan
|
||||
foreach ($p in $merged) {
|
||||
$r = Invoke-Gh @("pr", "view", "$($p.Number)", "-R", "$ORG/$($p.Repo)",
|
||||
"--json", "state,mergedAt") -AllowFail
|
||||
$j = if ($r.Code -eq 0) { $r.Output | ConvertFrom-Json } else { $null }
|
||||
$state = if ($j) { $j.state } else { "unreadable" }
|
||||
$col = if ($state -eq "MERGED") { "Green" } else { "Red" }
|
||||
Write-Host (" {0,-14} #{1,-4} {2}" -f $p.Repo, $p.Number, $state) -ForegroundColor $col
|
||||
}
|
||||
|
||||
# Follow-up, printed not run: ONE build bump per release - not one per
|
||||
# merged PR - on the default branch, and then a deploy. Both deliberately
|
||||
# by hand. This used to print one bump per PR, which is how a single
|
||||
# release came to be stamped as two builds.
|
||||
Write-Host ""
|
||||
Write-Host "Follow-up (not run):" -ForegroundColor Cyan
|
||||
foreach ($grp in ($merged | Group-Object Repo)) {
|
||||
$how = if ($BUMP.ContainsKey($grp.Name)) { $BUMP[$grp.Name] } else { "bump this repo's build stamp" }
|
||||
Write-Host (" {0,-14} {1} merged -> 1 build bump for the release: {2}" -f $grp.Name, $grp.Count, $how)
|
||||
}
|
||||
Write-Host " then deploy each project you want live (zdeploy, by hand)"
|
||||
}
|
||||
|
||||
if ($failed.Count -gt 0) { Write-ZTrailer; exit 1 }
|
||||
|
||||
Write-ZTrailer
|
||||
@ -1,6 +0,0 @@
|
||||
@echo off
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zpull.ps1" %*
|
||||
359
zpull.ps1
359
zpull.ps1
@ -1,359 +0,0 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
|
||||
# zpull.ps1 - merge the fleet's ready PRs, then bring the local checkouts current.
|
||||
#
|
||||
# Usage:
|
||||
# zpull dry run: what would merge, what would pull
|
||||
# zpull -Execute merge ready PRs, then pull every affected checkout
|
||||
# zpull -e the same; -e is an alias, as -s is for -Scan
|
||||
# zpull -Repo <name> limit to one repo
|
||||
# zpull -Only 65 merge just these PR numbers
|
||||
# zpull -PullOnly skip merging; only bring checkouts up to date
|
||||
# zpull -Execute -Yes skip zmerge's confirmation prompt
|
||||
#
|
||||
# WHY THIS EXISTS
|
||||
# ---------------
|
||||
# zmerge stops at the merge, deliberately - deploys are run by hand. But the
|
||||
# tooling repos are not deployed anywhere at all: they run
|
||||
# from the local checkout. For those, "deployed" just means "pulled". Merging a
|
||||
# zdeploy.ps1 fix and then forgetting the pull leaves you running the old file
|
||||
# while GitHub says the bug is fixed - which is its own kind of lie.
|
||||
#
|
||||
# So: merge (via zmerge, which owns all the mergeability safety), then pull.
|
||||
#
|
||||
# WHAT IT WILL NOT DO
|
||||
# -------------------
|
||||
# * pull over uncommitted work. It reports and skips. Twice this month a
|
||||
# checkout sat on a feature branch or held unstaged edits, and anything that
|
||||
# "helpfully" resolved that would have destroyed real work.
|
||||
# * pull anything but a fast-forward. A diverged local main is a decision,
|
||||
# not something a sync script should guess at.
|
||||
# * deploy to a server. Still by hand. This only touches local checkouts.
|
||||
#
|
||||
# HOW CHECKOUTS ARE FOUND
|
||||
# -----------------------
|
||||
# By reading each candidate directory's `origin` remote and matching the repo
|
||||
# name, not from a hardcoded table - a table drifts the moment a directory is
|
||||
# renamed, and this fleet renames directories.
|
||||
|
||||
[CmdletBinding(PositionalBinding = $false)]
|
||||
param(
|
||||
[Alias('e')][switch]$Execute,
|
||||
[switch]$Yes,
|
||||
[switch]$PullOnly,
|
||||
# Sweep only the repos with no zdeploy target - the ones where a pull is
|
||||
# the whole job. Tooling, archives, libraries.
|
||||
[switch]$ReposOnly,
|
||||
[string]$Repo,
|
||||
[int[]]$Only = @(),
|
||||
[int[]]$Exclude = @(),
|
||||
# PowerShell binds --help to -Help on its own (it tolerates the extra
|
||||
# dash), so this one switch answers --help, -help and -h. The bare words
|
||||
# land in $Rest below and are handled there.
|
||||
[Alias('h')][switch]$Help,
|
||||
# Catches anything unmatched. Without it, PositionalBinding=$false makes an
|
||||
# unknown argument a raw PowerShell binding error - a wall of red that does
|
||||
# not say what the valid arguments are. Owning the message means a typo
|
||||
# gets the usage block instead.
|
||||
[Parameter(ValueFromRemainingArguments = $true)][string[]]$Rest = @()
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Two blank lines at the end of a run, matching every other z-script, so output
|
||||
# is separated from the next prompt. Local copy rather than ZHelpers: this
|
||||
# script does not dot-source it.
|
||||
function Write-ZTrailer { Write-Host ""; Write-Host "" }
|
||||
|
||||
$FLEET_ROOT = Split-Path -Parent $PSScriptRoot
|
||||
$ORG = "evomedia-net"
|
||||
|
||||
function Show-ZPullUsage {
|
||||
Write-Host ""
|
||||
Write-Host "zpull - merge the fleet's ready PRs, then bring local checkouts current." -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Usage: zpull [-Execute|-e] [-Yes] [-PullOnly] [-ReposOnly] [-Repo <name>] [-Only <n,n>] [-Exclude <n,n>]" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host " (no args) dry run - what would merge, what would pull. Changes nothing." -ForegroundColor Gray
|
||||
Write-Host " -Execute, -e actually merge ready PRs, then pull every affected checkout" -ForegroundColor Gray
|
||||
Write-Host " -PullOnly skip merging entirely; only bring checkouts up to date" -ForegroundColor Gray
|
||||
Write-Host " -Repo <name> limit to one repo, by its GitHub name" -ForegroundColor Gray
|
||||
Write-Host " -Only <n,n> merge just these PR numbers" -ForegroundColor Gray
|
||||
Write-Host " -Exclude <n,n> merge everything ready except these PR numbers" -ForegroundColor Gray
|
||||
Write-Host " -ReposOnly only repos with no deploy target (pull = done)" -ForegroundColor Gray
|
||||
Write-Host " -Yes skip zmerge's confirmation prompt (needs -Execute)" -ForegroundColor Gray
|
||||
Write-Host " --help, -h this text" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host "What each result line means:" -ForegroundColor Yellow
|
||||
Write-Host " ok already current - nothing to do" -ForegroundColor Gray
|
||||
Write-Host " PULLED fast-forwarded to the new tip" -ForegroundColor Gray
|
||||
Write-Host " SKIP deliberately left alone: uncommitted work, not on the default" -ForegroundColor Gray
|
||||
Write-Host " branch, or diverged. Never resolved automatically." -ForegroundColor Gray
|
||||
Write-Host " FAIL the repo could not be read or fetched. The sweep continues;" -ForegroundColor Gray
|
||||
Write-Host " that one repo is simply not current." -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host "Each line is marked with what the repo still owes:" -ForegroundColor Yellow
|
||||
Write-Host " [repo] nothing runs from a server - the pull is the whole job" -ForegroundColor Gray
|
||||
Write-Host " [zdeploy <key>] a pull leaves the server on the old build" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host "It will not pull over uncommitted work, will not do anything but a" -ForegroundColor DarkGray
|
||||
Write-Host "fast-forward, and will not deploy. Deploys stay manual." -ForegroundColor DarkGray
|
||||
Write-Host ""
|
||||
Write-Host "Checkouts are found by reading each directory's origin remote under" -ForegroundColor DarkGray
|
||||
Write-Host "$FLEET_ROOT, not from a hardcoded list." -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
# Bare-word help too, matching the rest of the toolkit (zdeploy myapp, zkill all).
|
||||
$helpWords = @('help', '?', '/?', '--help', '-help')
|
||||
if ($Help -or @($Rest | Where-Object { $helpWords -contains $_.ToLowerInvariant() }).Count -gt 0) {
|
||||
Show-ZPullUsage
|
||||
Write-ZTrailer
|
||||
exit 0
|
||||
}
|
||||
if ($Rest.Count -gt 0) {
|
||||
Write-Host ""
|
||||
Write-Host "ERROR: unrecognised argument(s): $($Rest -join ', ')" -ForegroundColor Red
|
||||
Show-ZPullUsage
|
||||
Write-ZTrailer
|
||||
exit 1
|
||||
}
|
||||
|
||||
function Get-DeployTargetsByPath {
|
||||
# Checkout path -> the zdeploy keys that ship from it.
|
||||
#
|
||||
# Read from zconfig rather than listed here: a second table would drift the
|
||||
# first time a target is added, and drift in THIS table is the failure it
|
||||
# exists to prevent - a repo quietly reported as "done at the pull" while a
|
||||
# server runs the old build.
|
||||
#
|
||||
# Matched by containment, not equality, because a target's localRoot is
|
||||
# often a subdirectory of its checkout (a service may ship from
|
||||
# <checkout>\<subdir>), and one checkout can carry several targets
|
||||
# (vidplayer ships cardiff, opensesame and kelly).
|
||||
$map = @{}
|
||||
# Read here rather than via ZHelpers' Get-ZConfig: this script is
|
||||
# standalone by design, and that helper exits the process when the config
|
||||
# is missing - which would turn "no zconfig" into a dead sweep instead of
|
||||
# a sweep that simply knows of no deploy targets.
|
||||
$configPath = if ($env:ZCONFIG) { $env:ZCONFIG } else { Join-Path $PSScriptRoot "zconfig.json" }
|
||||
if (-not (Test-Path -LiteralPath $configPath)) { return $map }
|
||||
try {
|
||||
$cfg = Get-Content -LiteralPath $configPath -Raw | ConvertFrom-Json
|
||||
} catch {
|
||||
Write-Host " (zconfig.json unreadable - every repo will report as [repo])" -ForegroundColor Yellow
|
||||
return $map
|
||||
}
|
||||
if (-not $cfg.projects) { return $map }
|
||||
foreach ($key in $cfg.projects.PSObject.Properties.Name) {
|
||||
if ($key -like '_*') { continue } # underscore keys are comments
|
||||
$root = $cfg.projects.$key.localRoot
|
||||
if (-not $root) { continue }
|
||||
try { $map[$key] = [System.IO.Path]::GetFullPath($root).TrimEnd('\') } catch { }
|
||||
}
|
||||
return $map
|
||||
}
|
||||
|
||||
function Get-DeployKeysFor {
|
||||
param([string]$Path, [hashtable]$Targets)
|
||||
$full = [System.IO.Path]::GetFullPath($Path).TrimEnd('\')
|
||||
$hits = @()
|
||||
foreach ($key in $Targets.Keys) {
|
||||
$t = $Targets[$key]
|
||||
if ($t -eq $full -or $t.StartsWith($full + '\', [StringComparison]::OrdinalIgnoreCase)) {
|
||||
$hits += $key
|
||||
}
|
||||
}
|
||||
return @($hits | Sort-Object)
|
||||
}
|
||||
|
||||
function Get-LocalCheckouts {
|
||||
# repo name -> local path, discovered from origin remotes.
|
||||
$map = @{}
|
||||
$candidates = @(Get-ChildItem -LiteralPath $FLEET_ROOT -Directory -ErrorAction SilentlyContinue)
|
||||
# One level deeper too: some projects keep theirs nested.
|
||||
foreach ($d in @($candidates)) {
|
||||
$candidates += @(Get-ChildItem -LiteralPath $d.FullName -Directory -ErrorAction SilentlyContinue)
|
||||
}
|
||||
foreach ($d in $candidates) {
|
||||
if (-not (Test-Path (Join-Path $d.FullName ".git"))) { continue }
|
||||
# A pruned worktree leaves a .git FILE pointing at an admin dir that no
|
||||
# longer exists, so Test-Path above passes and git then fails. Same
|
||||
# redirect trap as everywhere else, so keep this on Continue and judge
|
||||
# by exit code.
|
||||
$prev = $ErrorActionPreference
|
||||
$ErrorActionPreference = "Continue"
|
||||
$url = (git -C $d.FullName remote get-url origin 2>$null)
|
||||
$ok = ($LASTEXITCODE -eq 0)
|
||||
$ErrorActionPreference = $prev
|
||||
if (-not $ok -or -not $url) { continue }
|
||||
if ($url -match "[:/]$ORG/([^/]+?)(\.git)?$") {
|
||||
$name = $Matches[1]
|
||||
if (-not $map.ContainsKey($name)) { $map[$name] = $d.FullName }
|
||||
}
|
||||
}
|
||||
return $map
|
||||
}
|
||||
|
||||
function Get-DefaultBranch {
|
||||
# origin/HEAD is a LOCAL cache of the remote's default branch. It is written
|
||||
# at clone time, and repos created some other way (git init + remote add,
|
||||
# which is how the *-stack and hostops checkouts here were made) simply do
|
||||
# not have it. `git symbolic-ref` then fails with
|
||||
# fatal: ref refs/remotes/origin/HEAD is not a symbolic ref
|
||||
# and - because this script runs under ErrorActionPreference='Stop' - PS 5.1
|
||||
# turns that redirected stderr into a TERMINATING NativeCommandError. The
|
||||
# 2>$null does not prevent it; it is the redirect itself that wraps each
|
||||
# stderr line in an ErrorRecord. So drop to Continue for the native calls.
|
||||
param([string]$Path)
|
||||
$prev = $ErrorActionPreference
|
||||
$ErrorActionPreference = "Continue"
|
||||
try {
|
||||
$d = (git -C $Path symbolic-ref --short refs/remotes/origin/HEAD 2>$null) -replace '^origin/', ''
|
||||
if (-not $d) {
|
||||
# Repair the cache from the remote, then re-ask. Costs one network
|
||||
# round-trip on first run per repo and is permanent afterwards.
|
||||
git -C $Path remote set-head origin --auto 2>$null | Out-Null
|
||||
$d = (git -C $Path symbolic-ref --short refs/remotes/origin/HEAD 2>$null) -replace '^origin/', ''
|
||||
}
|
||||
if (-not $d) {
|
||||
# Offline, or no such remote. Believe the remote-tracking refs that
|
||||
# exist rather than assuming "main" - zscripts is on master, and
|
||||
# guessing wrong makes this script skip the repo with a misleading
|
||||
# "on 'master', not 'main'".
|
||||
foreach ($c in @('main', 'master')) {
|
||||
git -C $Path rev-parse --verify --quiet "refs/remotes/origin/$c" 2>$null | Out-Null
|
||||
if ($LASTEXITCODE -eq 0) { $d = $c; break }
|
||||
}
|
||||
}
|
||||
if (-not $d) { $d = (git -C $Path rev-parse --abbrev-ref HEAD 2>$null) }
|
||||
if (-not $d) { $d = "main" }
|
||||
return $d
|
||||
}
|
||||
finally { $ErrorActionPreference = $prev }
|
||||
}
|
||||
|
||||
function Sync-Checkout {
|
||||
param([string]$Name, [string]$Path, [bool]$DoIt, [string[]]$DeployKeys = @())
|
||||
|
||||
# Everything below judges git by $LASTEXITCODE, so drop to Continue for the
|
||||
# whole function (scoped, auto-reverts on exit).
|
||||
#
|
||||
# This is not tidiness. Under the script's ErrorActionPreference='Stop', a
|
||||
# stderr REDIRECT on a native command makes PS 5.1 wrap each stderr line in
|
||||
# a terminating ErrorRecord - so `git fetch origin 2>$null` against one
|
||||
# repo with an unreachable remote killed the ENTIRE sweep mid-list, leaving
|
||||
# every repo after it unvisited and unreported. A fleet sweep must survive
|
||||
# one bad repo; that repo gets a FAIL row and the run continues.
|
||||
$prev = $ErrorActionPreference
|
||||
$ErrorActionPreference = "Continue"
|
||||
try {
|
||||
Sync-CheckoutCore -Name $Name -Path $Path -DoIt $DoIt -DeployKeys $DeployKeys
|
||||
}
|
||||
catch {
|
||||
Write-Host (" {0,-18} FAIL {1}" -f $Name, $_.Exception.Message) -ForegroundColor Red
|
||||
}
|
||||
finally { $ErrorActionPreference = $prev }
|
||||
}
|
||||
|
||||
function Sync-CheckoutCore {
|
||||
param([string]$Name, [string]$Path, [bool]$DoIt, [string[]]$DeployKeys = @())
|
||||
|
||||
# Appended to every line: the point is that you never have to remember
|
||||
# which kind of repo you are looking at.
|
||||
$mark = if ($DeployKeys.Count -gt 0) { " [zdeploy $($DeployKeys -join ', ')]" } else { " [repo]" }
|
||||
|
||||
$branch = (git -C $Path rev-parse --abbrev-ref HEAD 2>$null)
|
||||
if ($LASTEXITCODE -ne 0 -or -not $branch) {
|
||||
Write-Host (" {0,-18} FAIL not a usable git checkout: {1}{2}" -f $Name, $Path, $mark) -ForegroundColor Red
|
||||
return
|
||||
}
|
||||
$dirty = @(git -C $Path status --porcelain --untracked-files=no 2>$null)
|
||||
$default = Get-DefaultBranch -Path $Path
|
||||
|
||||
if ($dirty) {
|
||||
Write-Host (" {0,-18} SKIP uncommitted changes ({1} file(s)) - commit or stash first{2}" -f $Name, $dirty.Count, $mark) -ForegroundColor Yellow
|
||||
return
|
||||
}
|
||||
if ($branch -ne $default) {
|
||||
Write-Host (" {0,-18} SKIP on '{1}', not '{2}'{3}" -f $Name, $branch, $default, $mark) -ForegroundColor Yellow
|
||||
return
|
||||
}
|
||||
|
||||
git -C $Path fetch origin --quiet 2>$null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
# Unreachable remote, renamed repo, dead credential. Say so and move on
|
||||
# - continuing would compare against stale remote-tracking refs and
|
||||
# report "already current" about a repo we could not actually reach.
|
||||
Write-Host (" {0,-18} FAIL cannot fetch origin - check the remote{1}" -f $Name, $mark) -ForegroundColor Red
|
||||
return
|
||||
}
|
||||
$behind = (git -C $Path rev-list --count "HEAD..origin/$default" 2>$null)
|
||||
$ahead = (git -C $Path rev-list --count "origin/$default..HEAD" 2>$null)
|
||||
|
||||
if ([int]$ahead -gt 0) {
|
||||
Write-Host (" {0,-18} SKIP local '{1}' is {2} commit(s) ahead - diverged, resolve by hand{3}" -f $Name, $default, $ahead, $mark) -ForegroundColor Yellow
|
||||
return
|
||||
}
|
||||
if ([int]$behind -eq 0) {
|
||||
Write-Host (" {0,-18} ok already current{1}" -f $Name, $mark) -ForegroundColor DarkGray
|
||||
return
|
||||
}
|
||||
if (-not $DoIt) {
|
||||
Write-Host (" {0,-18} would pull {1} commit(s){2}" -f $Name, $behind, $mark) -ForegroundColor Cyan
|
||||
return
|
||||
}
|
||||
git -C $Path merge --ff-only "origin/$default" --quiet 2>$null
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host (" {0,-18} PULLED {1} commit(s) -> {2}{3}" -f $Name, $behind, (git -C $Path rev-parse --short HEAD), $mark) -ForegroundColor Green
|
||||
# The whole reason the marker exists. A tooling repo is finished here;
|
||||
# a deployable one now has a checkout ahead of its own server, which is
|
||||
# the state that gets forgotten.
|
||||
foreach ($k in $DeployKeys) {
|
||||
Write-Host (" {0,-18} still on the old build - run: zdeploy {1}" -f "", $k) -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host (" {0,-18} FAILED to fast-forward{1}" -f $Name, $mark) -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
# ── 1. Merge ─────────────────────────────────────────────────────
|
||||
if (-not $PullOnly) {
|
||||
Write-Host "`n=== Merging ready PRs (via zmerge) ===" -ForegroundColor Cyan
|
||||
# Hashtable splatting, not an array. Array splatting passes elements
|
||||
# positionally, so @("-Repo","<name>") fed "-Repo" into zmerge's
|
||||
# [int[]]$Exclude and died on the type conversion.
|
||||
$zm = @{}
|
||||
if ($Execute) { $zm.Execute = $true }
|
||||
if ($Yes) { $zm.Yes = $true }
|
||||
if ($Repo) { $zm.Repo = $Repo }
|
||||
if ($Only) { $zm.Only = $Only }
|
||||
if ($Exclude) { $zm.Exclude = $Exclude }
|
||||
& (Join-Path $PSScriptRoot "zmerge.ps1") @zm
|
||||
}
|
||||
|
||||
# ── 2. Pull ──────────────────────────────────────────────────────
|
||||
Write-Host "`n=== Bringing local checkouts current ===" -ForegroundColor Cyan
|
||||
if (-not $Execute) {
|
||||
Write-Host " (dry run - nothing will be pulled; add -Execute or -e)" -ForegroundColor DarkGray
|
||||
}
|
||||
$checkouts = Get-LocalCheckouts
|
||||
if ($Repo) {
|
||||
if ($checkouts.ContainsKey($Repo)) { $checkouts = @{ $Repo = $checkouts[$Repo] } }
|
||||
else { Write-Host " no local checkout found for '$Repo'" -ForegroundColor Yellow; $checkouts = @{} }
|
||||
}
|
||||
$targets = Get-DeployTargetsByPath
|
||||
if ($ReposOnly) {
|
||||
Write-Host " (-ReposOnly: repos with a zdeploy target are not listed)" -ForegroundColor DarkGray
|
||||
}
|
||||
$shown = 0
|
||||
foreach ($name in ($checkouts.Keys | Sort-Object)) {
|
||||
$keys = Get-DeployKeysFor -Path $checkouts[$name] -Targets $targets
|
||||
if ($ReposOnly -and $keys.Count -gt 0) { continue }
|
||||
$shown++
|
||||
Sync-Checkout -Name $name -Path $checkouts[$name] -DoIt:$Execute -DeployKeys $keys
|
||||
}
|
||||
if ($shown -eq 0) { Write-Host " nothing matched" -ForegroundColor DarkGray }
|
||||
Write-ZTrailer
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zrelease.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zrelease.ps1 - package the current version as a downloadable zip.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zrepair.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zrepair.ps1 — audit and repair container/proxy routing on the server, then smoke test.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0ZKiller.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zrestart.ps1 — alias for ZKiller.ps1 (kept so both names work). All args pass through.
|
||||
& (Join-Path $PSScriptRoot "ZKiller.ps1") @args
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0ZKiller.ps1" -Detached %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zsetup.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zsetup.ps1 — prepare a project for local dev: create its Python venv and install
|
||||
# dependencies (python kind), or run `npm install` (vite/nextjs). Idempotent -
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zsetup_mail.ps1 — create admin@ and noreply@ mailboxes in a docker-mailserver
|
||||
# container on the server, and print the DNS records + SMTP/IMAP settings to use.
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0ZStart.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zstart.ps1 — start local dev servers for any project defined in zconfig.json.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zstart_docker.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zstart_docker.ps1 — bring up a local docker compose stack from <scriptsRoot>\docker\.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0ZStart.ps1" -Detached %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zstop.ps1 — stop docker compose stacks on the server without removing data or files.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zsync.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net Token Savers — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zsync.ps1 — copy new backup files offsite; or build + mirror a vite project's dist.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
REM Evomedia.net — https://github.com/evomedia-net/evo.zscripts
|
||||
REM Created by Kelly Michels · dev@evomedia.net
|
||||
REM Licensed under the MIT License. See LICENSE.
|
||||
REM Version: v1.0.0.0.28
|
||||
REM Version: v1.0.0.0.24
|
||||
|
||||
@echo off
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zversion.ps1" %*
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# evomedia.net — https://github.com/evomedia-net/evo.zscripts
|
||||
# Evomedia.net — https://github.com/evomedia-net/evo.zscripts
|
||||
# Created by Kelly Michels · dev@evomedia.net
|
||||
# Licensed under the MIT License. See LICENSE.
|
||||
# Version: v1.0.0.0.28
|
||||
# Version: v1.0.0.0.24
|
||||
|
||||
# zversion.ps1 - manage the toolkit version.
|
||||
#
|
||||
|
||||
Loading…
Reference in New Issue
Block a user