mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-13 14:30:47 +01:00
Add deploy command: ff the live checkout
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (mypy, mypy) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (pylint, pylint) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (rest, ) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (shellcheck, shellcheck) (push) Waiting to run
Python tests / test (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / lib-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (mypy, mypy) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (pylint, pylint) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (rest, ) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (shellcheck, shellcheck) (push) Waiting to run
Python tests / test (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / lib-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
testsAndMisc had no '- deploy:' line, so Ship reported 'pushed, not deployed'. scripts/deploy.sh fast-forwards ~/src/testsAndMisc to origin/main, keeps unrelated local edits, and health-checks HEAD = origin/main plus the tracked hooks. bats: 7/7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
13db6e8116
commit
f75fb60687
@ -172,6 +172,7 @@ number of error codes, so it is weaker than the `strict = true` in the config.
|
||||
## Commands
|
||||
|
||||
- run: n/a: monorepo of independent scripts and apps, no single entry point (see Repository Layout)
|
||||
- deploy: `scripts/deploy.sh` (fast-forwards the live `~/src/testsAndMisc` checkout to origin/main, keeping unrelated local edits; exits non-zero unless HEAD = origin/main and the tracked hooks are wired)
|
||||
- test: `python3 -m pytest -q -o addopts="--strict-markers --strict-config"`
|
||||
- test-changed: `scripts/test_changed.sh`
|
||||
- lint: `pre-commit run --hook-stage pre-commit`
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
{
|
||||
"intent": "The i3blocks shutdown countdown shows and enforces minute-precision shutdown times (e.g. 18:30) now that /etc/shutdown-schedule.conf carries *_MINUTES keys, while still reading pre-migration hour-only files.",
|
||||
"scope": [
|
||||
"linux_configuration/i3blocks/shutdown_countdown.sh",
|
||||
"linux_configuration/tests/test_i3blocks_efficiency.sh",
|
||||
"Non-goal: the writers (screen-locker helper, digital-wellbeing installer) live in their own repos"
|
||||
],
|
||||
"changes": [
|
||||
"Parse MON_WED_MINUTES/THU_SUN_MINUTES/MORNING_END_MINUTES, prefer them, fall back to *_HOUR * 60; reject values outside 0-1440",
|
||||
"Shutdown epoch and window thresholds computed from minutes; still builtins only (no forks)",
|
||||
"Regression test: 18:30 minutes config shows '⏻ 18:30' at 18:15 and '⏻ SHUTDOWN' at 18:31 while the legacy HOUR copy says 18"
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "bash linux_configuration/tests/test_i3blocks_efficiency.sh",
|
||||
"result": "pass",
|
||||
"evidence": "All i3blocks efficiency regression tests passed (fork count <= 1 for the countdown)"
|
||||
},
|
||||
{
|
||||
"command": "SHUTDOWN_CONFIG=<dual|legacy fixture> NOW_EPOCH=<Tue 17:00/18:29/18:31, Thu 22:00> bash shutdown_countdown.sh",
|
||||
"result": "pass",
|
||||
"evidence": "dual: 18:30, 18:30, SHUTDOWN, SHUTDOWN; legacy (19/23): 19:00, 19:00, 19:00, 23:00"
|
||||
},
|
||||
{
|
||||
"command": "bash ~/.config/i3blocks/shutdown_countdown.sh (live, symlinked)",
|
||||
"result": "pass",
|
||||
"evidence": "⏻ 23:00 against the host's current hour-only /etc/shutdown-schedule.conf"
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"A config with only *_MINUTES and malformed values shows INVALID TIME instead of a countdown (display only; enforcement is the check script)"
|
||||
],
|
||||
"rollback": [
|
||||
"git revert the commit; the old script reads the *_HOUR keys, which every writer still emits",
|
||||
"Re-run the live block and confirm it shows the hour-rounded time"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
{
|
||||
"intent": "testsAndMisc declares a deploy command, so Ship can deploy a pushed change instead of reporting 'pushed, not deployed'.",
|
||||
"scope": [
|
||||
"scripts/deploy.sh (new), CLAUDE.md Commands, linux_configuration/tests/test_deploy.bats (new)",
|
||||
"Non-goal: refreshing copies the sudo installers put under /usr/local or /etc"
|
||||
],
|
||||
"changes": [
|
||||
"scripts/deploy.sh fast-forwards ~/src/testsAndMisc to origin/main (pull --ff-only), keeps unrelated local edits, refuses on collision",
|
||||
"Health check: on main, HEAD = origin/main, core.hooksPath = linux_configuration/.githooks; exits non-zero otherwise",
|
||||
"CLAUDE.md Commands gains '- deploy: `scripts/deploy.sh`'"
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "bats linux_configuration/tests/test_deploy.bats",
|
||||
"result": "pass",
|
||||
"evidence": "red first (deploy.sh missing, exit 127); then 7/7 ok: ff, idempotent, keeps WIP, refuses collision, hooks unwired fails, off-main fails, CLAUDE.md line"
|
||||
},
|
||||
{
|
||||
"command": "shellcheck scripts/deploy.sh",
|
||||
"result": "pass",
|
||||
"evidence": "no findings"
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"Live checkout carries kuhy's uncommitted edits; an incoming commit touching them makes deploy fail rather than overwrite"
|
||||
],
|
||||
"rollback": [
|
||||
"git revert this commit; the live checkout stays where the last deploy left it"
|
||||
]
|
||||
}
|
||||
85
linux_configuration/tests/test_deploy.bats
Executable file
85
linux_configuration/tests/test_deploy.bats
Executable file
@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env bats
|
||||
# Tests for scripts/deploy.sh against a scratch origin + "live" clone, so the
|
||||
# real ~/src/testsAndMisc is never touched.
|
||||
#
|
||||
# bats linux_configuration/tests/test_deploy.bats
|
||||
|
||||
bats_require_minimum_version 1.5.0
|
||||
|
||||
setup() {
|
||||
ROOT="$(cd -- "$(dirname -- "$BATS_TEST_FILENAME")/../.." && pwd)"
|
||||
SCRIPT="$ROOT/scripts/deploy.sh"
|
||||
T="$BATS_TEST_TMPDIR"
|
||||
export GIT_AUTHOR_NAME=t GIT_AUTHOR_EMAIL=t@example.com
|
||||
export GIT_COMMITTER_NAME=t GIT_COMMITTER_EMAIL=t@example.com
|
||||
git init -q --bare -b main "$T/origin.git"
|
||||
git clone -q "$T/origin.git" "$T/dev" 2>/dev/null
|
||||
mkdir -p "$T/dev/linux_configuration/.githooks"
|
||||
echo a >"$T/dev/file.txt"
|
||||
touch "$T/dev/linux_configuration/.githooks/pre-commit"
|
||||
git -C "$T/dev" add -A
|
||||
git -C "$T/dev" commit -q -m base
|
||||
git -C "$T/dev" push -q origin HEAD:main
|
||||
git clone -q "$T/origin.git" "$T/live"
|
||||
git -C "$T/live" config core.hooksPath linux_configuration/.githooks
|
||||
export TESTSANDMISC_DIR="$T/live"
|
||||
}
|
||||
|
||||
push_change() {
|
||||
echo "$1" >"$T/dev/${2:-file.txt}"
|
||||
git -C "$T/dev" commit -qam "$1"
|
||||
git -C "$T/dev" push -q origin HEAD:main
|
||||
}
|
||||
|
||||
@test "CLAUDE.md declares scripts/deploy.sh as the deploy command" {
|
||||
local tick="\`" n=0 line
|
||||
while IFS= read -r line; do
|
||||
[[ "$line" == "- deploy: ${tick}scripts/deploy.sh${tick}"* ]] && n=$((n + 1))
|
||||
done <"$ROOT/CLAUDE.md"
|
||||
[ "$n" -eq 1 ]
|
||||
[ -x "$SCRIPT" ]
|
||||
}
|
||||
|
||||
@test "fast-forwards the live checkout to origin/main" {
|
||||
push_change b
|
||||
run "$SCRIPT"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(git -C "$T/live" rev-parse HEAD)" = "$(git -C "$T/origin.git" rev-parse main)" ]
|
||||
}
|
||||
|
||||
@test "is idempotent: a second run is a no-op that still passes" {
|
||||
push_change b
|
||||
"$SCRIPT"
|
||||
run "$SCRIPT"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "keeps unrelated uncommitted work and still deploys" {
|
||||
echo wip >"$T/live/other.txt"
|
||||
git -C "$T/live" add other.txt
|
||||
push_change b
|
||||
run "$SCRIPT"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(cat "$T/live/other.txt")" = wip ]
|
||||
}
|
||||
|
||||
@test "refuses when uncommitted work collides with the update" {
|
||||
echo mine >"$T/live/file.txt"
|
||||
push_change theirs
|
||||
run "$SCRIPT"
|
||||
[ "$status" -ne 0 ]
|
||||
[ "$(cat "$T/live/file.txt")" = mine ]
|
||||
}
|
||||
|
||||
@test "fails the health check when hooks are not wired" {
|
||||
git -C "$T/live" config --unset core.hooksPath
|
||||
run "$SCRIPT"
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *hooksPath* ]]
|
||||
}
|
||||
|
||||
@test "fails when the live checkout is not on main" {
|
||||
git -C "$T/live" switch -q -c feature
|
||||
run "$SCRIPT"
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
56
scripts/deploy.sh
Executable file
56
scripts/deploy.sh
Executable file
@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================================================
|
||||
# Deploy testsAndMisc: fast-forward the live checkout (~/src/testsAndMisc) to
|
||||
# origin/main. Everything this repo runs on the machine (i3 config, systemd
|
||||
# units, symlinked scripts, git hooks) executes straight from that checkout,
|
||||
# so updating it is the deploy.
|
||||
#
|
||||
# scripts/deploy.sh
|
||||
#
|
||||
# Idempotent. Uncommitted work in the live checkout is kept: git refuses the
|
||||
# fast-forward (and this exits non-zero) only when an incoming commit touches
|
||||
# a file with local edits. Copies installed under /usr/local or /etc by the
|
||||
# sudo installers are not refreshed here; rerun that installer by hand.
|
||||
#
|
||||
# Exits non-zero unless the health check passes: the checkout is on main, its
|
||||
# HEAD equals origin/main, and core.hooksPath points at the tracked hooks.
|
||||
# Env: TESTSANDMISC_DIR (default ~/src/testsAndMisc).
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
readonly REPO="${TESTSANDMISC_DIR:-${HOME}/src/testsAndMisc}"
|
||||
readonly HOOKS_PATH=linux_configuration/.githooks
|
||||
|
||||
die() {
|
||||
echo "deploy: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
update_checkout() {
|
||||
[[ -d "${REPO}/.git" ]] || die "${REPO} is not a git checkout"
|
||||
local branch
|
||||
branch="$(git -C "$REPO" rev-parse --abbrev-ref HEAD)"
|
||||
[[ "$branch" == main ]] || die "${REPO} is on '${branch}', not main"
|
||||
git -C "$REPO" pull --ff-only --quiet origin main ||
|
||||
die "git pull --ff-only failed in ${REPO} (diverged, or local edits collide)"
|
||||
}
|
||||
|
||||
health_check() {
|
||||
local head remote hooks
|
||||
head="$(git -C "$REPO" rev-parse HEAD)"
|
||||
remote="$(git -C "$REPO" rev-parse origin/main)"
|
||||
[[ "$head" == "$remote" ]] || die "HEAD ${head:0:8} != origin/main ${remote:0:8}"
|
||||
hooks="$(git -C "$REPO" config --get core.hooksPath || true)"
|
||||
[[ "$hooks" == "$HOOKS_PATH" && -d "${REPO}/${HOOKS_PATH}" ]] ||
|
||||
die "core.hooksPath is '${hooks}', want ${HOOKS_PATH} (run meta/scripts/install_hooks.sh)"
|
||||
echo "deploy: ${REPO} at ${head:0:8} = origin/main, hooks wired"
|
||||
}
|
||||
|
||||
main() {
|
||||
update_checkout
|
||||
health_check
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Loading…
Reference in New Issue
Block a user