Repository navigation
[Sprint] sprint-loop-7 - #9
Merged
Merged
Conversation
Closes the loop on SUR-1829's deferred namespace bullet by adding a -n NAMESPACE option that, when set, threads -n into both the pod-listing k8s::ctl get pods call and the per-pod k8s::ctl logs call. Default behaviour (current-context namespace) is unchanged when -n is omitted.
…1855)
Follow-up to SUR-1832. Two defects from "Other defects in the same file"
that the merged PR did not address:
- getRs() captured an unused itemType local; the call site
`getRs "$ns" replicaset` and the relayed `getItems → getRs "$@"` both
passed a literal "replicaset" that the function never consumed. Drop
the unused local and tighten the call sites so getRs's $2 means
"selector" consistently.
- The script never called options::standard/options::add/options::parse,
so it had no -h, no flag validation, and consumed the cluster name from
a bare positional. Wire in the standard options boilerplate with a
mandatory -c CLUSTER and keep the bare positional as a backward-compat
fallback (CLUSTER=${CLUSTER:-${1:-}}). Use NO_SYNTAX_EXIT during
parse_available so positional-only invocations don't trigger the
no-args help-and-exit path.
Two changes that together remove a footgun every new contributor hits: - log.sh: add a top-of-file comment block documenting the LOG_LEVEL -> enabled-levels mapping (0..4), and call log::level "$LOG_LEVEL" once at source time so the LOG_DISABLE_* flags are deterministic. Without that call, the disable flags were unset; the test [ "$LOG_DISABLE_INFO" = "false" ] then evaluated false (empty != "false") and log::info silently no-oped at the default LOG_LEVEL=0. The init is guarded so callers that pre-set any LOG_DISABLE_* flag before sourcing keep their values (mitigates risk R4 in the sprint plan). - options.sh: extend options::standard's @doc annotation to point at the LOG_LEVEL mapping in log.sh.
…tch (SUR-1833)
Three correctness/security defects in bash/bb:
- pr-data redeclared its `repo` parameter via `local project repo`,
clobbering "$1" to empty. Every API call ended up
`projects//repos//pull-requests?...`, so pr-count and pr-list (which
delegate through pr-data) always returned empty. Rename the parameter
to `slug` and use distinct `project` / `repo_name` locals.
- The script sourced "$HOME/.${CONFIG}-config" with no validation of
the user-controlled CONFIG. Reject names that don't match
^[a-zA-Z0-9_-]+$ before sourcing so '..' and '/' can't escape $HOME.
- The trailing `"$@"` was unbounded function dispatch — any name visible
in the shell (including anything from the sourced config file) was
callable. Replace it with an explicit allow-list case over
pr-data | pr-count | pr-list and error::exit on anything else.
…d (SUR-1827)
Three bugs in create_package that produced incomplete or mangled
support tarballs:
- The archive pipeline piped `find -print0 | sed | xargs -0 tar`, but
sed is line-oriented and stripped/garbled the NUL delimiters between
`find -print0` and `xargs -0`. xargs then received one giant blob
and tar silently missed files. Replace with `cd "$OUT_DIR" && find .
-type f -print0 | tar --null --files-from=- -czf "$file_abs"`, which
preserves the NUL framing and produces a tarball whose paths are
relative to OUT_DIR (no $OUT_DIR/ prefix).
- `${file/\//_}` collapses only the first slash, so cluster contexts
like `arn:aws:eks:.../cluster/my-cluster` ended up with embedded
slashes that tar interpreted as directories. Switch to `${file//\//_}`.
- `find … -exec shred {} \;` ran one shred per file. Switch to `+`-form.
Smoke (synthetic OUT_DIR with paths containing spaces and multiple
slashes) confirms `tar -tzf | wc -l` matches `find -type f | wc -l`
and no path inside the tarball begins with $OUT_DIR/.
…821) Three defects in the PagerDuty alerting path that combined to make pagerduty-alert silently drop messages: - pagerduty.sh::send_incident called `_curl POST --header ...`. Without `-X`, curl consumed `POST` as a positional URL arg, leaving the request method as the default GET. Add -X. - pagerduty.sh::send_event had its entire _curl body commented out and still logged "Sent PagerDuty event", so `pagerduty-alert -i event` returned 0 having sent nothing — the worst class of bug for an alerting tool. Until someone implements the Events API v2 call (which uses a different endpoint, auth model, and payload), error::exit so the failure is loud. - pagerduty-alert's `event)` arm was entirely commented out, so even fixing send_event wouldn't have surfaced anything. Un-comment it, route through pagerduty::send_event, and add a *) arm rejecting unknown alert types. Also rename send_incident/send_event to pagerduty::send_incident / pagerduty::send_event per AGENTS.md namespacing, with deprecated shims so any out-of-tree callers still using the bare names keep working.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sprint Plan — 2026-04-28 sprint-loop-7
Sprint Goal
Knock down a coherent batch of correctness defects and small improvements across
bash/command scripts and libraries: fix two silently-broken integrations (PagerDuty alerting,bbBitbucket PR queries), repair thek8s-support-collectorarchive pipeline, finish two follow-ups left dangling from the previous sprint (fetch-all-pod-logs -n,plantuml-from-k8soptions/help), and document/initialise thelog.shlevel system so default-verbosity scripts behave predictably. Every selected item is a single- or two-file edit with static verification (shellcheck, shfmt,bash -n,-hsmoke) — fits a short sprint and reduces operational risk in tools that SREs actually rely on.Selected Issues
1. SUR-1856 — Follow-up to SUR-1829: fetch-all-pod-logs — add
-n NAMESPACEflag-n NAMESPACEso the script works outside the kubectl current-context default namespace. Suggested skeleton usesoptions::add -o n -d "namespace …" -a -e namespaceand threads(-n "$namespace")into bothk8s::ctl get podsandk8s::ctl logscalls.bash/fetch-all-pod-logs -hlists-n NAMESPACE.k8s::ctl get podsandk8s::ctl logscalls receive-n "$namespace"when supplied.-nis omitted (current-context namespace).pre-commit run shellcheck shfmt --files bash/fetch-all-pod-logsis clean.2. SUR-1855 — Follow-up to SUR-1832: plantuml-from-k8s —
getRsunused arg + missingoptions::parse/helpgetRsis invoked with an unusedreplicasetliteral (copy-paste leftover from agetItems "$ns" replicasetpattern); (b) the script never callsoptions::standard/options::add/options::parse, so it has no-h, no flag validation, and consumes the cluster name from a bare positional. Suggested: drop the unused arg or thread it throughgetRs, and convert the positional cluster arg into a mandatory-c CLUSTER(or keep positional but add the standard option boilerplate).plantuml-from-k8sin line with the rest of the repo's command-script convention (every other command usesoptions::*) and removes a confusing dead argument.getRsno longer receives an unused argument (or accepts and uses it consistently).options::standard/options::add/options::parse.bash/plantuml-from-k8s -hproduces help text.bash -n bash/plantuml-from-k8ssucceeds.pre-commit run shellcheck shfmt --files bash/plantuml-from-k8sis clean.3. SUR-1847 — Docs/Improvement:
log.shdefaultLOG_LEVEL=0silently disables WARNING/INFO/DEBUG/TRACEbash/log.shinitialisesLOG_LEVEL=${LOG_LEVEL:-0}but never callslog::levelat source time, so theLOG_DISABLE_*flags remain unset. Scripts that don't pass-vend up with empty disable flags; the test[ "$LOG_DISABLE_INFO" = "false" ]then evaluates false (empty ≠ "false"), solog::infoand below silently no-op. The level→enabled-set mapping is documented nowhere. Suggested fix: (1) document the level table at the top oflog.sh; (2) calllog::level "$LOG_LEVEL"once at source time so the disable flags are deterministic; (3) mention the contract inoptions.sh::options::standardand/or the README.bash/log.shcallslog::level "$LOG_LEVEL"at source time after theLOG_LEVEL=${LOG_LEVEL:-0}line.log.shdocuments the LOG_LEVEL→enabled-levels table.options::standard's@docannotation inbash/options.shreferences the level mapping (or links back tolog.sh).pre-commit run shellcheck shfmt --files bash/log.sh bash/options.shis clean.4. SUR-1833 — Bug:
bbshadows function argrepowithlocal project repo; sources untrusted CONFIG filebash/bb: (1)pr-dataredeclares itsrepoparameter vialocal project repo, clobbering$1to empty — every API call ends upprojects//repos//pull-requests?..., sopr-countandpr-list(which delegate throughpr-data) always return empty; (2) the script sources$HOME/.${CONFIG}-configfrom a user-controlled$CONFIGvalue with no validation, allowing path-separator escapes; (3) the trailing"$@"is unbounded function dispatch — any name visible in the shell (including those sourced from the config file) is callable. Suggested: drop the redeclaration and use a distinctrepo_namelocal; validate$CONFIGagainst^[a-zA-Z0-9_-]+$; replace"$@"with an explicit allow-listcaseoverpr-data|pr-count|pr-list.bbscript to working order (currently broken in the most-used path) and tightens a real security smell. Single-file change.pr-datano longer shadows itsrepoargument; HTTP path includes a non-empty project and repo.$CONFIGis validated against a sane identifier regex before sourcing; sourcing rejects names with/,.., etc.pr-data,pr-count,pr-listonly; unknown subcommandserror::exitcleanly.pre-commit run shellcheck shfmt --files bash/bbis clean.bb -c <name> pr-list <project>/<repo>returns a non-empty body for a known repo (or, where API access is unavailable, the call URL is correct inset -x).5. SUR-1827 — Bug:
k8s-support-collector—sedin null-delimited pipe and single-slash filename replacebash/k8s-support-collectorthat produce mangled support tarballs: (1)find … -print0 | sed -e "s@$OUT_DIR/@@g" | xargs -0 tar cfz—sedis line-oriented and strips/garbles the NUL delimiters betweenfind -print0andxargs -0, soxargsreceives one giant blob andtarsilently misses files or fails; (2)file=${file/\//_}uses single replace (only fires on the first/), so cluster contexts containing multiple slashes (e.g.arn:aws:eks:.../cluster/my-cluster) end up with embedded slashes thattarinterprets as directories. Bonus:find … -exec shred {} \;runs oneshredper file instead of+-form. Suggested fix: move path stripping totar --transformand pipefind -print0straight totar --null --files-from=-; switch to${file//\//_}; switch to-exec shred {} +.sedbetweenfind -print0andxargs -0; usestar --null --files-from=- --transform(orcd+ relative paths).${file//\//_}.shredinvocation uses-exec … +.pre-commit run shellcheck shfmt --files bash/k8s-support-collectoris clean.$OUT_DIR(verified bytar -tzf | wc -lvs.find $OUT_DIR -type f | wc -lin a dry run).6. SUR-1821 — Bug:
pagerduty-alertevent path is fully commented out andsend_incidentmisses-X POSTbash/pagerduty.sh::send_eventhas its entire_curlbody commented out, but the function still logs "Sent PagerDuty event" — invokingpagerduty-alert -i event …returns 0 having sent nothing, a silent-failure mode of an alerting tool; (2)bash/pagerduty.sh::send_incidentcalls_curl POST --header …rather than_curl -X POST --header …—POSTis consumed as a positional URL argument and the request method is wrong. Thepagerduty-alertscript'seventarm is also commented out, so even fixingsend_eventrequires un-commenting the call site. Suggested: fix-X POSTfor incidents; either properly implementsend_eventagainst the PagerDuty Events API v2 (different endpoint and payload) or have iterror::exit "send_event not implemented"so callers fail loudly; un-comment thepagerduty-alerteventarm to match; namespace the functions topagerduty::send_incident/pagerduty::send_eventper AGENTS.md.pagerduty.sh::send_incident(or its renamedpagerduty::send_incident) callscurl -X POST(not barePOST).pagerduty.sh::send_eventeither implements the Events API v2 call or fails loudly (error::exit) — never silently returns 0.pagerduty-alert'sevent)arm is no longer commented out — it either calls the implementedsend_eventor routes through the loud-fail stub.pagerduty::send_incident/pagerduty::send_event(AGENTS.md namespacing); back-compat shims viaannotations::deprecatedif any in-tree caller still uses the bare names.pre-commit run shellcheck shfmt --files bash/pagerduty.sh bash/pagerduty-alertis clean.Risks + Mitigations
bbsmoke verification needs Bitbucket credentials: Thepr-datafix is provable by inspecting the resolved URL viaset -x, but a true integration smoke needs a Bitbucket token. Mitigation: validate the URL string in a capturedset -xtrace; do not require live API access in DoD.k8s-support-collectorsmoke requires a populated$OUT_DIR: The pipeline fix is best validated end-to-end against a real collected bundle. Mitigation: synthesise a small fake$OUT_DIRwith a few files (including paths containing spaces and multiple slashes in the leading prefix) and verify the produced tarball roundtrips.send_eventPagerDuty Events API v2 implementation is non-trivial: The Events API uses a different endpoint (events.pagerduty.com/v2/enqueue), a different auth model (routing key, not API token), and a different payload schema. Mitigation: scope the sprint deliverable to the loud-fail stub plus the-X POSTfix; track full Events-API implementation as a follow-up issue if no driver exists.log::levelat source time changes default behaviour for callers that already manipulateLOG_DISABLE_*directly: Callinglog::level "$LOG_LEVEL"at source time will overwrite anyLOG_DISABLE_*a caller pre-set before sourcinglog.sh. Mitigation: only calllog::levelif theLOG_DISABLE_*flags are unset (if [ -z "${LOG_DISABLE_NOTICE+set}" ]; then log::level "$LOG_LEVEL"; fi), or document the precedence change explicitly.plantuml-from-k8sswitching from positional cluster arg to-c CLUSTERis a breaking API change: Anyone withplantuml-from-k8s my-clusterin a wrapper will break. Mitigation: keep the positional accepted as a fallback (parse-cfirst, then fall back to${1:-}); or commit the breaking change but call it out in the conventional-commit body and changelog."$@"dispatch) overlaps SUR-1833's bonus item; touchingbash/bbmay tempt tackling SUR-1842 too. Mitigation: hold the line — fix only thebbinstance under SUR-1833; deferkind,minikubeto SUR-1842.pre-commit'sno-commit-to-branchenforces^(fix|feature|refactor)/[a-zA-Z0-9\-]+$and rejects underscores/dots. Mitigation: branch names usefix/sur-XXXX-short-slugorfeature/sur-XXXX-short-slugonly.pre-commit(shellcheck, shfmt) plus targeted manual smokes documented in the implementation plan.Out of Scope
set -x, exit-trap scope, java arg arrays, Ctrl-C trap) — separate file, separate sprint."$COMMAND"/"$@"dispatch inkind/minikube/bb) —bbis covered by SUR-1833; defer thekind/minikubeinstances.kubectl … | grep -v NAME | awk '{print $1}'parsing) — touches five command scripts and needs a shared helper; defer.docker::cp; release-images iterates extras) — separate refactor; defer.-X POSTfix.plantuml-from-k8sto a non-backward-compatible CLI (see R5) — sprint keeps the positional fallback.Linear Evidence
Surinis(idce9ebfde-ff2b-4f54-90f1-c388591ca110)shell-scripts(ida43901a0-b02b-4009-aae1-a6e8903d127d); project's owning team isSurinis— match confirmed.list_issues(team="Surinis", project="shell-scripts", state="Backlog", limit=100)plus per-candidateget_issue(includeRelations=true),list_issues(parentId=…)for sub-issues,list_comments(issueId=…)for comment threads, andlist_issue_labels(team="Surinis", name="manual")to enumerate manual-labelled issues.hasNextPage=false).manuallabel exists in the workspace (id91344244-aedf-462c-ae9e-c68f8622bcf7) but no Backlog issue in theshell-scriptsproject currently carries it. All 14 Backlog issues had label sets in{Improvement, Bug, testing, documentation, 3 - medium}only.Sub-issue Status
None of the six selected parents has any sub-issues. Verified by
list_issues(parentId=<each>)returning an empty array for all six. No parent was skipped due to incomplete sub-issues.Linear State Transitions