diff --git a/README.md b/README.md index 66bb9a6..22be32e 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,7 @@ Targets are supplied after the code directory (or via `default_targets` in the m | `build-if-missing` | No-op when the image already exists; otherwise delegates to `build`. | | `rebuild` | Rebuild the image from scratch (`--no-cache`). | | `run` | Default goal. Runs the container with Codex as entrypoint and passes `codex_args`. | +| `resume ` | Continues a Codex session by running `codex resume ` inside the container (builds if needed). | | `login` | Starts Codex in login mode to bootstrap `${HOME}/.codex`. | | `shell` | Launches `/bin/bash` inside the container for debugging. | | `stop` | Best-effort stop of the running container (if any). | diff --git a/sloptrap b/sloptrap index b97888d..5a523e1 100755 --- a/sloptrap +++ b/sloptrap @@ -30,6 +30,8 @@ print_styled() { for arg in "$@"; do colored_args+=("$COLOR_HIGHLIGHT$arg$color") done + # Format strings are defined within sloptrap, not user input. + # shellcheck disable=SC2059 printf "$fmt" "${colored_args[@]}" else printf '%b' "$fmt" @@ -50,6 +52,8 @@ print_styled_err() { for arg in "$@"; do colored_args+=("$COLOR_HIGHLIGHT$arg$color") done + # Format strings are defined within sloptrap, not user input. + # shellcheck disable=SC2059 printf "$fmt" "${colored_args[@]}" >&2 else printf '%b' "$fmt" >&2 @@ -121,6 +125,7 @@ usage() { info_line "\n" info_line "Example targets:\n" comment_line " run Build if needed, then launch Codex\n" + comment_line " resume Build if needed, then run 'codex resume '\n" comment_line " shell Drop into an interactive /bin/bash session\n" comment_line " clean Remove the project container/image cache\n" comment_line " prune Remove dangling/unused sloptrap images\n" @@ -1026,17 +1031,25 @@ prune_sloptrap_images() { run_or_print "${cmd[@]}" } -run_codex() { - if ! $DRY_RUN; then - status_line "Running %s\n" "$SLOPTRAP_IMAGE_NAME" - fi +run_codex_command() { + local -a extra_args=("$@") local -a cmd=("${BASE_CONTAINER_CMD[@]}" "$SLOPTRAP_IMAGE_NAME") if [[ ${#CODEX_ARGS_ARRAY[@]} -gt 0 ]]; then cmd+=("${CODEX_ARGS_ARRAY[@]}") fi + if [[ ${#extra_args[@]} -gt 0 ]]; then + cmd+=("${extra_args[@]}") + fi run_or_print "${cmd[@]}" } +run_codex() { + if ! $DRY_RUN; then + status_line "Running %s\n" "$SLOPTRAP_IMAGE_NAME" + fi + run_codex_command +} + run_login_target() { if ! $DRY_RUN; then status_line "Login %s\n" "$SLOPTRAP_IMAGE_NAME" @@ -1053,6 +1066,23 @@ run_shell_target() { run_or_print "${cmd[@]}" } +run_resume_target() { + local session_id=$1 + if ! $DRY_RUN; then + status_line "Resume %s (%s)\n" "$SLOPTRAP_IMAGE_NAME" "$session_id" + fi + run_codex_command resume "$session_id" +} + +process_resume_target() { + local session_id=$1 + if [[ -z $session_id ]]; then + error "target 'resume' requires a session identifier" + fi + build_if_missing + run_resume_target "$session_id" +} + dispatch_target() { local target=$1 case "$target" in @@ -1267,6 +1297,17 @@ if $AUTO_LOGIN; then dispatch_target login fi -for target in "${TARGETS[@]}"; do - dispatch_target "$target" +target_index=0 +while (( target_index < ${#TARGETS[@]} )); do + current_target="${TARGETS[$target_index]}" + if [[ $current_target == "resume" ]]; then + if (( target_index + 1 >= ${#TARGETS[@]} )); then + error "target 'resume' requires a session identifier" + fi + process_resume_target "${TARGETS[$((target_index + 1))]}" + ((target_index+=2)) + continue + fi + dispatch_target "$current_target" + ((target_index+=1)) done diff --git a/tests/README.md b/tests/README.md index d920614..b45adda 100644 --- a/tests/README.md +++ b/tests/README.md @@ -10,3 +10,4 @@ Current scenarios: - `manifest_injection/` — ensures disallowed `make.*` overrides abort parsing. - `helper_symlink/` — ensures `.sloptrap-ignores` cannot be a symlink to directories outside the project. - `secret_mask/` — verifies masked files remain hidden even when sloptrap remaps the workspace mount. +- `resume_target/` — verifies the resume target passes the requested session identifier to Codex. diff --git a/tests/resume_target/README.md b/tests/resume_target/README.md new file mode 100644 index 0000000..e8ad893 --- /dev/null +++ b/tests/resume_target/README.md @@ -0,0 +1,4 @@ +# Resume Target Scenario + +Empty project used by the regression suite to ensure the `resume` target +invokes `codex resume ` inside the container. diff --git a/tests/run_tests.sh b/tests/run_tests.sh index f7bae3a..57b9e40 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -289,6 +289,23 @@ run_secret_mask() { teardown_stub_env } +run_resume_target() { + local scenario_dir="$TEST_ROOT/resume_target" + printf '==> resume_target\n' + setup_stub_env + local session_id="019a81b7-32d2-7622-8639-6698c6579625" + if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" \ + "$SLOPTRAP_BIN" "$scenario_dir" resume "$session_id" >/dev/null 2>&1; then + record_failure "resume_target: sloptrap exited non-zero" + teardown_stub_env + return + fi + if ! grep -q -- "resume $session_id" "$STUB_LOG"; then + record_failure "resume_target: resume invocation missing" + fi + teardown_stub_env +} + run_shellcheck run_mount_injection run_root_target @@ -296,6 +313,7 @@ run_symlink_escape run_manifest_injection run_helper_symlink run_secret_mask +run_resume_target if [[ ${#failures[@]} -gt 0 ]]; then printf '\nTest failures:\n'