Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,16 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Install BATS
run: |
sudo apt-get update
sudo apt-get install -y bats

- name: Run validation
run: tests/validate.sh

- name: Run BATS tests
run: |
bats \
cli/bash/commands/caff/tests/caff.bats \
cli/bash/commands/sort-in-place/tests/sort-in-place.bats
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to Base Platform Tools will be documented in this file.

### Added

- Migrated the `caff` and `sort-in-place` Bash utility CLIs from
`codeforester/base`.
- Added the initial public repository scaffold for Base Platform Tools.
- Added the Base-managed project manifest and validation contract.
- Added the tooling boundary documentation.
Expand Down
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,10 @@ basectl test base-platform-tools
```

Use more specific tests once real tool implementations are added.

For migrated Bash tools, run the BATS coverage:

```bash
bats cli/bash/commands/caff/tests/caff.bats
bats cli/bash/commands/sort-in-place/tests/sort-in-place.bats
```
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,11 @@ The boundary is intentional:

## Current Status

This repository is in its initial scaffold stage. It has the initial CLI layout
for future Bash and Python tools, but it does not yet contain production utility
CLIs.
This repository is in its early stage. It has the initial CLI layout and hosts
the first migrated Bash utility CLIs from Base:

The existing `caff` and `sort` utilities remain in `codeforester/base` for now.
They can be migrated here later through separate issues and pull requests once
this repository baseline is stable.
- `caff`
- `sort-in-place`

## Getting Started

Expand Down Expand Up @@ -68,6 +66,13 @@ List the commands declared by this repository:
basectl run base-platform-tools --list
```

Run the migrated utilities through Base:

```bash
basectl run base-platform-tools caff -- --help
basectl run base-platform-tools sort-in-place -- --help
```

## What Belongs Here

Good candidates for this repository include:
Expand Down Expand Up @@ -100,12 +105,17 @@ Keep these outside Base Platform Tools:
```text
.
├── bin/
│ └── README.md
│ ├── README.md
│ ├── caff
│ └── sort-in-place
├── base_manifest.yaml
├── cli/
│ ├── README.md
│ ├── bash/
│ │ └── README.md
│ │ ├── README.md
│ │ └── commands/
│ │ ├── caff/
│ │ └── sort-in-place/
│ └── python/
│ ├── README.md
│ └── base_platform_tools/
Expand Down
2 changes: 2 additions & 0 deletions base_manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ test:
command: tests/validate.sh

commands:
caff: bin/caff
cli-check: tests/validate.sh
sort-in-place: bin/sort-in-place
validate: tests/validate.sh
19 changes: 19 additions & 0 deletions bin/caff
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash

repo_root="$(cd -- "$(dirname -- "$0")/.." && pwd -P)" || exit 1
command_path="$repo_root/cli/bash/commands/caff/caff.sh"

if [[ -n "${BASE_HOME:-}" ]]; then
if [[ ! -x "$BASE_HOME/bin/basectl" ]]; then
printf 'ERROR: BASE_HOME is set but basectl was not found at %s/bin/basectl.\n' "$BASE_HOME" >&2
exit 1
fi
exec "$BASE_HOME/bin/basectl" "$command_path" "$@"
fi

if command -v basectl >/dev/null 2>&1; then
exec basectl "$command_path" "$@"
fi

printf 'ERROR: BASE_HOME is not set and basectl was not found on PATH.\n' >&2
exit 1
19 changes: 19 additions & 0 deletions bin/sort-in-place
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash

repo_root="$(cd -- "$(dirname -- "$0")/.." && pwd -P)" || exit 1
command_path="$repo_root/cli/bash/commands/sort-in-place/sort-in-place.sh"

if [[ -n "${BASE_HOME:-}" ]]; then
if [[ ! -x "$BASE_HOME/bin/basectl" ]]; then
printf 'ERROR: BASE_HOME is set but basectl was not found at %s/bin/basectl.\n' "$BASE_HOME" >&2
exit 1
fi
exec "$BASE_HOME/bin/basectl" "$command_path" "$@"
fi

if command -v basectl >/dev/null 2>&1; then
exec basectl "$command_path" "$@"
fi

printf 'ERROR: BASE_HOME is not set and basectl was not found on PATH.\n' >&2
exit 1
11 changes: 11 additions & 0 deletions cli/bash/commands/caff/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# `caff`

Caffeinate a named process by finding its PID and running macOS `caffeinate`
against that process.

Public invocation is exposed by the launcher at `bin/caff`; the implementation
lives here so command code, documentation, and tests stay together.

This utility was migrated from `codeforester/base`. It belongs in Base Platform
Tools because it is a small operational helper, not part of Base's core
workspace orchestration surface.
136 changes: 136 additions & 0 deletions cli/bash/commands/caff/caff.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#!/usr/bin/env bash

#
# caff: call caffeinate for a named process.
#

caff_show_help() {
cat <<'EOF'
Caffeinate a named process.

Usage:
caff [-s] <process-name>

Options:
-s Suppress the already-caffeinating message.
-h, --help Show this help text.
EOF
}

caff_describe() {
printf '%s\n' "Caffeinate a named process"
}

caff_first_pgrep_match() {
local pattern="$1"
local pgrep_output status

pgrep_output="$(pgrep "$pattern" 2>/dev/null)"
status=$?
if [[ "$status" -eq 0 ]]; then
[[ -n "$pgrep_output" ]] || return 1
printf '%s\n' "$pgrep_output" | sed -n '1p'
return 0
fi
if [[ "$status" -eq 1 ]]; then
return 1
fi

print_error "Unable to query process list for '$pattern'."
return 2
}

caff_wait_pid_from_caffeinate_args() {
local args_line="$1"
local arg rest
local waiting_for_pid=false
local args=()

read -r -a args <<< "$args_line"
for arg in "${args[@]:1}"; do
if [[ "$waiting_for_pid" == true ]]; then
printf '%s\n' "$arg"
return 0
fi

case "$arg" in
-w)
waiting_for_pid=true
;;
-w[0-9]*)
printf '%s\n' "${arg#-w}"
return 0
;;
-*w*)
rest="${arg#*w}"
if [[ -n "$rest" ]]; then
printf '%s\n' "$rest"
return 0
fi
waiting_for_pid=true
;;
esac
done

return 1
}

main() {
local silent=0
local process_name
local target_pid
local caffeinate_pid
local caffeinated_pid
local pgrep_status

case "${1:-}" in
-h|--help)
caff_show_help
return 0
;;
--describe)
caff_describe
return 0
;;
-s)
silent=1
shift
;;
esac

if ! command -v caffeinate >/dev/null 2>&1; then
print_error "There is no caffeinate command on your system."
return 1
fi

if (($# != 1)); then
print_error "A process name is required."
caff_show_help >&2
return 2
fi

process_name="$1"
target_pid="$(caff_first_pgrep_match "$process_name")"
pgrep_status=$?
if [[ "$pgrep_status" -eq 1 ]]; then
print_warn "'$process_name' process is not running."
return 1
fi
[[ "$pgrep_status" -eq 0 ]] || return 1

caffeinate_pid="$(caff_first_pgrep_match caffeinate)"
pgrep_status=$?
if [[ "$pgrep_status" -eq 2 ]]; then
return 1
fi
if [[ "$pgrep_status" -eq 0 ]]; then
caffeinated_pid="$(ps -o args= -p "$caffeinate_pid" | while IFS= read -r line; do caff_wait_pid_from_caffeinate_args "$line" && break; done)"
if [[ "$caffeinated_pid" == "$target_pid" ]]; then
((silent)) || printf '%s\n' "Already caffeinating: $process_name pid=$target_pid, caffeinate pid=$caffeinate_pid"
return 0
fi
fi

printf '%s\n' "Caffeinating PID $target_pid"
caffeinate -iw "$target_pid" & disown
}
Loading
Loading