chore(ops): 16-day deps gate, weekly CVE scan, overrides, release helper
- Dependency job is now 16 days (service env + script default); a separate weekly scan-only timer reports npm/OSV findings without changing the lockfile. - TimeoutStartSec=30min on the deps job and 20min on the deploy job (systemd's 90s default could kill a legitimate run); both scripts resolve the newest nvm Node instead of hardcoding v22.23.1. - npm override pins @tanstack/query-core to $@tanstack/react-query so a blanket npm update cannot create a second copy (the Oct 26 tsc failure). - Failed runs keep test/smoke logs + attempted package files in failures/ (rotated, 8 kept) and a last-failure marker; reports rotate (12 kept). - scripts/release.sh: notes check -> npm test -> bump -> Android build -> zsp publish -> relay verify -> commit/push + tag. - Deploy/deps systemd units are tracked under scripts/systemd/.
This commit is contained in:
parent
d53b8cd0cc
commit
ac5ad39515
12 changed files with 233 additions and 13 deletions
19
AGENTS.md
19
AGENTS.md
|
|
@ -2340,7 +2340,7 @@ Rules:
|
||||||
apply). `zapstore.yaml` license was `custom` (the listing showed "custom");
|
apply). `zapstore.yaml` license was `custom` (the listing showed "custom");
|
||||||
now **`AGPL-3.0-only`** (re-published in 1.6.8, verified on the 32267 tag).
|
now **`AGPL-3.0-only`** (re-published in 1.6.8, verified on the 32267 tag).
|
||||||
On **failure** the job restores the lockfile and schedules a retry in **~3 days**
|
On **failure** the job restores the lockfile and schedules a retry in **~3 days**
|
||||||
(a failed run no longer burns the whole 20-day window); every notification now
|
(a failed run no longer burns the whole 16-day window); every notification now
|
||||||
includes the **OSV total** ("N packages affected by M known vulnerabilities").
|
includes the **OSV total** ("N packages affected by M known vulnerabilities").
|
||||||
**Final security scan (2026-10-26):** researched the app-relevant advisories and
|
**Final security scan (2026-10-26):** researched the app-relevant advisories and
|
||||||
fixed them — **zod 3.25 → 4.6.7** (only `AppProvider` imports zod; no
|
fixed them — **zod 3.25 → 4.6.7** (only `AppProvider` imports zod; no
|
||||||
|
|
@ -2363,6 +2363,23 @@ Rules:
|
||||||
`whitespace-nowrap` keeps the trailing **More** reachable, the mobile bottom
|
`whitespace-nowrap` keeps the trailing **More** reachable, the mobile bottom
|
||||||
nav + burger append the full catalog, dropdowns have max-height + internal
|
nav + burger append the full catalog, dropdowns have max-height + internal
|
||||||
scroll, and interactive targets meet the 44px rule. No other menu bugs found.
|
scroll, and interactive targets meet the 44px rule. No other menu bugs found.
|
||||||
|
**Automation hardening (2026-10-27):** the dependency interval is now
|
||||||
|
**16 days** (`DEPS_INTERVAL_DAYS=16` in
|
||||||
|
`scripts/systemd/skatespots-deps.service` and the script default), plus a
|
||||||
|
separate **weekly scan-only** timer (`skatespots-deps-scan.timer`, Mon 06:00)
|
||||||
|
that reports npm/OSV findings without touching the lockfile. The deps service
|
||||||
|
has `TimeoutStartSec=30min` (the 90s default can kill a legitimate
|
||||||
|
update/test run) and both maintenance scripts resolve the newest nvm Node
|
||||||
|
instead of hard-coding `v22.23.1`. `@tanstack/query-core` is pinned with an
|
||||||
|
npm **override** to `$@tanstack/react-query`, so `npm update` cannot create a
|
||||||
|
second copy — the Oct 26 failure was duplicate `query-core` breaking tsc.
|
||||||
|
Failed runs now keep `test.log`/`smoke.log` + the attempted package files in
|
||||||
|
`~/.local/state/skatespots-deps/failures/` (8 kept, rotated) and a
|
||||||
|
`last-failure` marker; reports keep 12. Releases are one command via
|
||||||
|
**`scripts/release.sh`** (notes check → `npm test` → bump → Android build →
|
||||||
|
`zsp publish` → relay verification → commit/push + tag). The deploy timer has
|
||||||
|
`TimeoutStartSec=20min`, and all deploy/deps units are tracked in
|
||||||
|
`scripts/systemd/`.
|
||||||
|
|
||||||
### Community relay — self-hosted Newlay (2026-10-12)
|
### Community relay — self-hosted Newlay (2026-10-12)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -175,5 +175,8 @@
|
||||||
"vite": "^6.4.5",
|
"vite": "^6.4.5",
|
||||||
"vitest": "^5.0.5",
|
"vitest": "^5.0.5",
|
||||||
"web-push": "^3.6.7"
|
"web-push": "^3.6.7"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"@tanstack/query-core": "$@tanstack/react-query"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,13 @@
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
REPO="/home/sovereignsk8/SkateSpots"
|
REPO="/home/sovereignsk8/SkateSpots"
|
||||||
# systemd runs with a minimal PATH; node/npm live under nvm here.
|
# systemd runs with a minimal PATH; resolve the newest nvm Node so a Node
|
||||||
export PATH="/home/sovereignsk8/.nvm/versions/node/v22.23.1/bin:$PATH"
|
# upgrade doesn't silently break deploys.
|
||||||
|
if [ -d "$HOME/.nvm/versions/node" ]; then
|
||||||
|
NODE_BIN="$(ls -d "$HOME"/.nvm/versions/node/*/bin 2>/dev/null | sort -V | tail -1)"
|
||||||
|
[ -n "$NODE_BIN" ] && export PATH="$NODE_BIN:$PATH"
|
||||||
|
fi
|
||||||
|
export PATH="$HOME/go/bin:$PATH"
|
||||||
|
|
||||||
STATE_DIR="$HOME/.local/state/skatespots-deploy"
|
STATE_DIR="$HOME/.local/state/skatespots-deploy"
|
||||||
SHA_FILE="$STATE_DIR/last-deployed-sha"
|
SHA_FILE="$STATE_DIR/last-deployed-sha"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# Install the dependency-update + vulnerability-scan systemd user timer.
|
# Install the dependency-update + vulnerability-scan systemd user timers.
|
||||||
# scripts/install-deps-timer.sh
|
# scripts/install-deps-timer.sh
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
|
@ -10,7 +10,9 @@ mkdir -p "$DEST"
|
||||||
|
|
||||||
cp "$SRC/skatespots-deps.service" "$DEST/"
|
cp "$SRC/skatespots-deps.service" "$DEST/"
|
||||||
cp "$SRC/skatespots-deps.timer" "$DEST/"
|
cp "$SRC/skatespots-deps.timer" "$DEST/"
|
||||||
|
cp "$SRC/skatespots-deps-scan.service" "$DEST/"
|
||||||
|
cp "$SRC/skatespots-deps-scan.timer" "$DEST/"
|
||||||
|
|
||||||
systemctl --user daemon-reload
|
systemctl --user daemon-reload
|
||||||
systemctl --user enable --now skatespots-deps.timer
|
systemctl --user enable --now skatespots-deps.timer skatespots-deps-scan.timer
|
||||||
systemctl --user list-timers skatespots-deps.timer --no-pager
|
systemctl --user list-timers skatespots-deps.timer skatespots-deps-scan.timer --no-pager
|
||||||
|
|
|
||||||
129
scripts/release.sh
Executable file
129
scripts/release.sh
Executable file
|
|
@ -0,0 +1,129 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# One-command SkateSpots release: bump → test → build APK → publish to
|
||||||
|
# Zapstore → verify → commit/push (+ optional git tag).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# scripts/release.sh patch|minor|major [--yes]
|
||||||
|
# scripts/release.sh 1.7.1 [--yes]
|
||||||
|
#
|
||||||
|
# Before running:
|
||||||
|
# 1. write the new section at the TOP of release-notes.txt (do not commit it —
|
||||||
|
# this script commits it with the version bump);
|
||||||
|
# 2. provide the release signer WITHOUT putting it in shell history:
|
||||||
|
# export SIGN_WITH=nsec1... # current shell only
|
||||||
|
# or point at a 0600 file:
|
||||||
|
# export SKATES_RELEASE_ENV=$HOME/.config/skatespots/release.env
|
||||||
|
# # file contains: SIGN_WITH=nsec1...
|
||||||
|
#
|
||||||
|
# What it does NOT do: deploy the web (the skatespots-deploy.timer does that
|
||||||
|
# within ~3 min, 24h throttle) or touch the relay.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO="/home/sovereignsk8/SkateSpots"
|
||||||
|
ZSP="${ZSP:-$HOME/go/bin/zsp}"
|
||||||
|
export ANDROID_HOME="${ANDROID_HOME:-$HOME/Android/Sdk}"
|
||||||
|
SPEC="${1:-}"
|
||||||
|
YES=0
|
||||||
|
shift || true
|
||||||
|
for a in "$@"; do [ "$a" = "--yes" ] && YES=1; done
|
||||||
|
|
||||||
|
die() { echo "✗ $*" >&2; exit 1; }
|
||||||
|
log() { echo "• $*"; }
|
||||||
|
|
||||||
|
[ -n "$SPEC" ] || die "usage: scripts/release.sh patch|minor|major|<x.y.z> [--yes]"
|
||||||
|
[ -x "$ZSP" ] || die "zsp not found/executable at $ZSP (set ZSP=...)"
|
||||||
|
[ -d "$ANDROID_HOME" ] || die "Android SDK not found at $ANDROID_HOME (set ANDROID_HOME=...)"
|
||||||
|
|
||||||
|
# Optional protected env file (never commit it).
|
||||||
|
if [ -z "${SIGN_WITH:-}" ] && [ -n "${SKATES_RELEASE_ENV:-}" ]; then
|
||||||
|
[ -f "$SKATES_RELEASE_ENV" ] || die "SKATES_RELEASE_ENV does not exist: $SKATES_RELEASE_ENV"
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
. "$SKATES_RELEASE_ENV"
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
[ -n "${SIGN_WITH:-}" ] || die "SIGN_WITH is not set (export it or use SKATES_RELEASE_ENV)"
|
||||||
|
|
||||||
|
# Resolve the newest nvm Node (the old hard-coded path breaks after upgrades).
|
||||||
|
if [ -d "$HOME/.nvm/versions/node" ]; then
|
||||||
|
NODE_BIN="$(ls -d "$HOME"/.nvm/versions/node/*/bin 2>/dev/null | sort -V | tail -1)"
|
||||||
|
[ -n "$NODE_BIN" ] && export PATH="$NODE_BIN:$PATH"
|
||||||
|
fi
|
||||||
|
export PATH="$HOME/go/bin:$PATH"
|
||||||
|
|
||||||
|
cd "$REPO"
|
||||||
|
|
||||||
|
CURRENT="$(node -p "require('./package.json').version")"
|
||||||
|
log "current version: $CURRENT"
|
||||||
|
|
||||||
|
# The notes must be the only uncommitted change, so the release commit is clean.
|
||||||
|
if git diff --quiet -- release-notes.txt; then
|
||||||
|
die "release-notes.txt is unchanged — write the new section first (and don't commit it)"
|
||||||
|
fi
|
||||||
|
DIRTY="$(git status --porcelain | grep -vE '^ M release-notes\.txt$' || true)"
|
||||||
|
[ -z "$DIRTY" ] || die "working tree has other changes:
|
||||||
|
$DIRTY"
|
||||||
|
|
||||||
|
git fetch --quiet origin main
|
||||||
|
[ "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" ] || \
|
||||||
|
die "local main is not in sync with origin/main — push or pull first"
|
||||||
|
|
||||||
|
if [ "$YES" -eq 0 ]; then
|
||||||
|
[ -t 0 ] || die "non-interactive: pass --yes to confirm"
|
||||||
|
printf 'Release %s from %s? [y/N] ' "$SPEC" "$CURRENT"
|
||||||
|
read -r ans
|
||||||
|
case "$ans" in y|Y|yes|YES) ;; *) die "aborted" ;; esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "running the full test gate (this also builds the web bundle)…"
|
||||||
|
npm test
|
||||||
|
|
||||||
|
log "bumping version ($SPEC)…"
|
||||||
|
npm version "$SPEC" --no-git-tag-version >/dev/null
|
||||||
|
VERSION="$(node -p "require('./package.json').version")"
|
||||||
|
[ "$VERSION" != "$CURRENT" ] || die "version did not change"
|
||||||
|
log "version: $VERSION"
|
||||||
|
|
||||||
|
log "building the signed release APK…"
|
||||||
|
npm run android:build
|
||||||
|
|
||||||
|
APK="android/app/build/outputs/apk/release/app-release.apk"
|
||||||
|
[ -f "$APK" ] || die "APK not found at $APK"
|
||||||
|
SHA="$(sha256sum "$APK" | awk '{print $1}')"
|
||||||
|
log "APK: $APK (sha256 ${SHA:0:16}…)"
|
||||||
|
|
||||||
|
log "publishing to Zapstore…"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
"$ZSP" publish --quiet zapstore.yaml
|
||||||
|
|
||||||
|
log "verifying the Zapstore release event…"
|
||||||
|
FOUND=0
|
||||||
|
for _ in 1 2 3 4 5 6; do
|
||||||
|
if command -v nak >/dev/null 2>&1; then
|
||||||
|
OUT="$(timeout 15 nak req -k 30063 -d "org.skatespots.app@$VERSION" wss://relay.zapstore.dev 2>/dev/null || true)"
|
||||||
|
if printf '%s' "$OUT" | grep -q "\[\"version\",\"$VERSION\"\]"; then FOUND=1; break; fi
|
||||||
|
else
|
||||||
|
log "nak not installed — skipping relay verification"
|
||||||
|
FOUND=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 4
|
||||||
|
done
|
||||||
|
[ "$FOUND" -eq 1 ] || log "warning: the release event did not appear on the relay yet (it usually propagates shortly)"
|
||||||
|
|
||||||
|
log "committing the release…"
|
||||||
|
git add package.json package-lock.json release-notes.txt
|
||||||
|
git commit -m "release: $VERSION" >/dev/null
|
||||||
|
git tag -a "v$VERSION" -m "SkateSpots $VERSION"
|
||||||
|
git push origin main
|
||||||
|
if ! git push origin "v$VERSION" 2>/dev/null; then
|
||||||
|
log "warning: tag v$VERSION was created locally but the push failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "✓ SkateSpots $VERSION published"
|
||||||
|
echo " APK: $APK"
|
||||||
|
echo " SHA256: $SHA"
|
||||||
|
echo " Web: the deploy timer picks up the commit within ~3 min (24h throttle)"
|
||||||
|
echo " Zapstore: https://zapstore.dev/apps/org.skatespots.app"
|
||||||
10
scripts/systemd/skatespots-deploy.service
Normal file
10
scripts/systemd/skatespots-deploy.service
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Auto-deploy SkateSpots web app to Netlify when origin/main changes
|
||||||
|
Documentation=file:///home/sovereignsk8/SkateSpots/scripts/auto-deploy.sh
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
WorkingDirectory=/home/sovereignsk8/SkateSpots
|
||||||
|
# npm ci + vite build + smoke test can exceed systemd's 90s default.
|
||||||
|
TimeoutStartSec=20min
|
||||||
|
ExecStart=/home/sovereignsk8/SkateSpots/scripts/auto-deploy.sh
|
||||||
11
scripts/systemd/skatespots-deploy.timer
Normal file
11
scripts/systemd/skatespots-deploy.timer
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Poll SkateSpots origin/main and deploy to Netlify on change
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnBootSec=2min
|
||||||
|
OnUnitActiveSec=3min
|
||||||
|
AccuracySec=30s
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
7
scripts/systemd/skatespots-deps-scan.service
Normal file
7
scripts/systemd/skatespots-deps-scan.service
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
[Unit]
|
||||||
|
Description=SkateSpots weekly vulnerability scan (report only, no lockfile changes)
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
TimeoutStartSec=15min
|
||||||
|
ExecStart=%h/SkateSpots/scripts/update-deps.sh --scan-only
|
||||||
10
scripts/systemd/skatespots-deps-scan.timer
Normal file
10
scripts/systemd/skatespots-deps-scan.timer
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Weekly SkateSpots npm/OSV vulnerability scan
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=Mon 06:00
|
||||||
|
Persistent=true
|
||||||
|
RandomizedDelaySec=30min
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
|
|
@ -3,4 +3,8 @@ Description=SkateSpots scheduled dependency update + vulnerability scan
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
Environment=DEPS_INTERVAL_DAYS=16
|
||||||
|
# npm update + typecheck + tests + smoke can take several minutes; the default
|
||||||
|
# 90s systemd start timeout would kill a legitimate run.
|
||||||
|
TimeoutStartSec=30min
|
||||||
ExecStart=%h/SkateSpots/scripts/update-deps.sh
|
ExecStart=%h/SkateSpots/scripts/update-deps.sh
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Daily tick for the SkateSpots dependency job (self-gated to ~20 days)
|
Description=Daily tick for the SkateSpots dependency job (self-gated to ~16 days)
|
||||||
|
|
||||||
[Timer]
|
[Timer]
|
||||||
OnCalendar=daily
|
OnCalendar=daily
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# Scheduled dependency update + vulnerability scan (every ~20 days).
|
# Scheduled dependency update + vulnerability scan (every ~16 days).
|
||||||
#
|
#
|
||||||
# Driven by the systemd user timer `skatespots-deps.timer` (a daily tick; this
|
# Driven by the systemd user timer `skatespots-deps.timer` (a daily tick; this
|
||||||
# script self-gates to one real run per INTERVAL_DAYS). What it does:
|
# script self-gates to one real run per INTERVAL_DAYS). A second weekly timer
|
||||||
|
# (`skatespots-deps-scan.timer`) runs this with --scan-only for earlier CVE
|
||||||
|
# visibility without touching the lockfile. What it does:
|
||||||
# 1. bails if the working tree is dirty (never mixes with your own changes);
|
# 1. bails if the working tree is dirty (never mixes with your own changes);
|
||||||
# 2. scans `npm outdated` + `npm audit` into a dated report;
|
# 2. scans `npm outdated` + `npm audit` into a dated report;
|
||||||
# 3. applies NON-breaking updates (`npm update` + `npm audit fix` — never --force);
|
# 3. applies NON-breaking updates (`npm update` + `npm audit fix` — never --force);
|
||||||
|
|
@ -20,14 +22,22 @@
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
REPO="/home/sovereignsk8/SkateSpots"
|
REPO="/home/sovereignsk8/SkateSpots"
|
||||||
export PATH="/home/sovereignsk8/.nvm/versions/node/v22.23.1/bin:$PATH"
|
# Resolve the newest nvm Node so a Node upgrade doesn't silently break the
|
||||||
|
# timer (the old hard-coded v22.23.1 path did).
|
||||||
|
if [ -d "$HOME/.nvm/versions/node" ]; then
|
||||||
|
NODE_BIN="$(ls -d "$HOME"/.nvm/versions/node/*/bin 2>/dev/null | sort -V | tail -1)"
|
||||||
|
[ -n "$NODE_BIN" ] && export PATH="$NODE_BIN:$PATH"
|
||||||
|
fi
|
||||||
|
export PATH="$HOME/go/bin:$PATH"
|
||||||
cd "$REPO"
|
cd "$REPO"
|
||||||
|
|
||||||
INTERVAL_DAYS="${DEPS_INTERVAL_DAYS:-20}"
|
INTERVAL_DAYS="${DEPS_INTERVAL_DAYS:-16}"
|
||||||
STATE_DIR="${DEPS_STATE_DIR:-$HOME/.local/state/skatespots-deps}"
|
STATE_DIR="${DEPS_STATE_DIR:-$HOME/.local/state/skatespots-deps}"
|
||||||
mkdir -p "$STATE_DIR"
|
mkdir -p "$STATE_DIR"
|
||||||
LAST="$STATE_DIR/last-run"
|
LAST="$STATE_DIR/last-run"
|
||||||
REPORT="$STATE_DIR/report-$(LC_ALL=C date +%F).txt"
|
REPORT="$STATE_DIR/report-$(LC_ALL=C date +%F).txt"
|
||||||
|
FAILURE_FILE="$STATE_DIR/last-failure"
|
||||||
|
FAILURE_DIR_BASE="$STATE_DIR/failures"
|
||||||
# OSV-Scanner (broader than npm audit: queries the OSV database for the lockfile).
|
# OSV-Scanner (broader than npm audit: queries the OSV database for the lockfile).
|
||||||
# Installed with: go install github.com/google/osv-scanner/v2/cmd/osv-scanner@latest
|
# Installed with: go install github.com/google/osv-scanner/v2/cmd/osv-scanner@latest
|
||||||
OSV_BIN="${OSV_BIN:-$HOME/go/bin/osv-scanner}"
|
OSV_BIN="${OSV_BIN:-$HOME/go/bin/osv-scanner}"
|
||||||
|
|
@ -81,9 +91,14 @@ fi
|
||||||
OSV_SUMMARY="$(grep -E '^Total [0-9]+ packages affected' "$REPORT" | tail -1)"
|
OSV_SUMMARY="$(grep -E '^Total [0-9]+ packages affected' "$REPORT" | tail -1)"
|
||||||
[ -n "$OSV_SUMMARY" ] || OSV_SUMMARY="OSV scan: no summary (scanner missing?)"
|
[ -n "$OSV_SUMMARY" ] || OSV_SUMMARY="OSV scan: no summary (scanner missing?)"
|
||||||
|
|
||||||
|
# Rotate diagnostics so the state dir cannot grow forever.
|
||||||
|
ls -1t "$STATE_DIR"/report-*.txt 2>/dev/null | tail -n +13 | xargs -r rm -f
|
||||||
|
ls -1dt "$FAILURE_DIR_BASE"/* 2>/dev/null | tail -n +9 | xargs -r rm -rf
|
||||||
|
|
||||||
if [ "$SCAN_ONLY" -eq 1 ]; then
|
if [ "$SCAN_ONLY" -eq 1 ]; then
|
||||||
log "scan-only; report: $REPORT"
|
log "scan-only; report: $REPORT"
|
||||||
echo "$OSV_SUMMARY"
|
echo "$OSV_SUMMARY"
|
||||||
|
notify "Vulnerability scan: $OSV_SUMMARY (report: $REPORT)"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -133,15 +148,22 @@ if npm test >"$STATE_DIR/test.log" 2>&1 && node scripts/smoke-test.mjs >"$STATE_
|
||||||
notify "Dependency scan complete: nothing to update. ${OSV_SUMMARY}"
|
notify "Dependency scan complete: nothing to update. ${OSV_SUMMARY}"
|
||||||
fi
|
fi
|
||||||
finish_timer
|
finish_timer
|
||||||
|
rm -f "$FAILURE_FILE"
|
||||||
log "done"
|
log "done"
|
||||||
else
|
else
|
||||||
log "tests FAILED — restoring the previous lockfile"
|
log "tests FAILED — saving diagnostics and restoring the previous lockfile"
|
||||||
|
FAIL_DIR="$FAILURE_DIR_BASE/$(date +%F-%H%M)"
|
||||||
|
mkdir -p "$FAIL_DIR"
|
||||||
|
cp "$STATE_DIR/test.log" "$STATE_DIR/smoke.log" "$FAIL_DIR"/ 2>/dev/null || true
|
||||||
|
cp package.json package-lock.json "$FAIL_DIR"/ 2>/dev/null || true
|
||||||
|
printf '%s\n' "tests failed at $(date -Is)" "see $FAIL_DIR/test.log" > "$FAIL_DIR/summary.txt"
|
||||||
|
echo "$(date -Is) tests failed — $FAIL_DIR" > "$FAILURE_FILE"
|
||||||
cp "$STATE_DIR/package.json.bak" package.json 2>/dev/null || true
|
cp "$STATE_DIR/package.json.bak" package.json 2>/dev/null || true
|
||||||
cp "$STATE_DIR/package-lock.json.bak" package-lock.json 2>/dev/null || true
|
cp "$STATE_DIR/package-lock.json.bak" package-lock.json 2>/dev/null || true
|
||||||
npm i --silent >/dev/null 2>&1 || true
|
npm i --silent >/dev/null 2>&1 || true
|
||||||
# A failed run must NOT consume the whole interval: retry in ~3 days instead.
|
# A failed run must NOT consume the whole interval: retry in ~3 days instead.
|
||||||
RETRY_DAYS=3; [ "$INTERVAL_DAYS" -gt 4 ] || RETRY_DAYS=1
|
RETRY_DAYS=3; [ "$INTERVAL_DAYS" -gt 4 ] || RETRY_DAYS=1
|
||||||
echo $(( $(date +%s) - (INTERVAL_DAYS - RETRY_DAYS) * 86400 )) > "$LAST"
|
echo $(( $(date +%s) - (INTERVAL_DAYS - RETRY_DAYS) * 86400 )) > "$LAST"
|
||||||
notify "Dependency update REVERTED — tests failed (see $STATE_DIR/test.log). ${OSV_SUMMARY}"
|
notify "Dependency update REVERTED — tests failed (see $FAIL_DIR). ${OSV_SUMMARY}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue