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
14 changes: 14 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ jobs:
cs50/cli:amd64
cs50/cli:canary-amd64

- name: Check out tests
uses: actions/checkout@v7

- name: Smoke-test linux/amd64 build
run: |
bash tests/smoke.sh cs50/cli:canary-amd64

- name: Push linux/amd64 build to Docker Hub
if: ${{ github.ref == 'refs/heads/main' }}
run: |
Expand Down Expand Up @@ -68,6 +75,13 @@ jobs:
cs50/cli:arm64
cs50/cli:canary-arm64

- name: Check out tests
uses: actions/checkout@v7

- name: Smoke-test linux/arm64 build
run: |
bash tests/smoke.sh cs50/cli:canary-arm64

- name: Push linux/arm64 build to Docker Hub
if: ${{ github.ref == 'refs/heads/main' }}
run: |
Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ RUN apt update && \
apt install --no-install-recommends --no-install-suggests --yes \
astyle \
bash-completion \
bsdextrautils `# For col, for help50` \
build-essential `# dpkg-dev, libc, gcc, g++, make, etc.` \
ca-certificates \
clang \
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ rebuild:
docker build --build-arg VCS_REF=$(shell git rev-parse HEAD) --no-cache --tag $(IMAGE) .

run:
docker run --env LANG=$(LANG) --env LOCAL_WORKSPACE_FOLDER="$(PWD)" --env WORKDIR=/mnt --interactive --publish-all --rm --security-opt seccomp=unconfined --tty --volume "$(PWD)":/mnt --volume /var/run/docker.sock:/var/run/docker-host.sock --workdir /mnt cs50/cli bash --login || true
docker run --env LANG=$(LANG) --env LOCAL_WORKSPACE_FOLDER="$(PWD)" --env WORKDIR=/mnt --interactive --publish-all --rm --security-opt seccomp=unconfined --tty --volume "$(PWD)":/mnt --volume /var/run/docker.sock:/var/run/docker-host.sock --workdir /mnt $(IMAGE) bash --login || true

smoke:
bash tests/smoke.sh $(IMAGE)

squash: depends
docker-squash --tag $(IMAGE) $(IMAGE)
4 changes: 2 additions & 2 deletions etc/profile.d/cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ if [ `id -u` -ne 0 ]; then
# Valgrind
export VALGRIND_OPTS="--memcheck:leak-check=full --memcheck:show-leak-kinds=all --memcheck:track-origins=yes"

# Start help50 if enabled
if help50 is-enabled > /dev/null; then
# Start help50 if enabled (only for interactive shells with a terminal, else `script` would hang, e.g., in `bash --login -c`)
if [[ $- == *i* && -t 0 ]] && help50 is-enabled > /dev/null; then
help50 start
fi
fi
6 changes: 4 additions & 2 deletions etc/profile.d/help50.sh
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ function _help50() {
if [[ -n "$help" ]]; then # If helpful
_helpful "$help"
elif [[ $status -ne 0 ]]; then # If helpless
_helpless "$typescript"

# Cap what's relayed downstream (e.g., ddb50 rejects > 10,000 characters), keeping the end, where errors tend to be
_helpless "$(echo "$typescript" | tail -c 8192)"
fi
else
_helped
Expand All @@ -113,7 +115,7 @@ if ! type _helpful >/dev/null 2>&1; then

# Intercept accidental invocation of `yes` and `n`, which are actual programs
for name in n no y yes; do
alias $name=_rhetocial
alias $name=_rhetorical
done

# Output help
Expand Down
4 changes: 3 additions & 1 deletion opt/cs50/bin/valgrind
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#!/bin/bash

. /opt/cs50/lib/cli

# If run on Python program
if [[ "$1" == "python" || "$1" == *.py ]]; then
echo "$(_help "Afraid \`valgrind\` does not support Python programs!")"
_alert "$(_ansi "Afraid \`valgrind\` does not support Python programs!")"
exit 1
fi

Expand Down
13 changes: 7 additions & 6 deletions opt/cs50/lib/cli
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ function _alert() {

function _ansi() {

# If command-line arguments
if [[ -t 0 ]]; then
# If command-line arguments (don't test -t 0, else arguments are ignored whenever stdin is redirected)
if [[ $# -gt 0 ]]; then
input="$*"

# If standard input
Expand Down Expand Up @@ -48,17 +48,18 @@ function _find() {

function _fold() {

# If command-line arguments
if [[ -t 0 ]]; then
# If command-line arguments (don't test -t 0, else arguments are ignored whenever stdin is redirected)
if [[ $# -gt 0 ]]; then
input="$*"

# If standard input
else
input=$(cat)
fi

# Wrap long lines
local cols=$(tput cols)
# Wrap long lines (default to 80 columns if no terminal, e.g., when piped)
local cols=$(tput cols 2> /dev/null)
[[ "$cols" =~ ^[0-9]+$ ]] || cols=80
echo "$input" | fold --spaces --width=$cols
}

Expand Down
11 changes: 6 additions & 5 deletions opt/cs50/lib/help50/python
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ fi
regex="python: can't open file '(.*\.py)': \[Errno 2\] No such file or directory"
if [[ "$output" =~ $regex ]]; then

# Relative path from $PWD
path=$(realpath --relative-to=. "${BASH_REMATCH[1]}")
# Relative path from $PWD (file doesn't exist, so don't require it to)
path=$(realpath --canonicalize-missing --relative-to=. "${BASH_REMATCH[1]}")

# If command was `python baz.py` (i.e., without a dirname)
if [[ -n "$path" && "$path" == $(basename "$path") ]]; then
Expand All @@ -51,11 +51,12 @@ if [[ "$output" =~ $regex ]]; then
fi

# If command was `python bar/baz.py` (i.e., with a dirname)
if [[ -n "$path" && "$path" == $(basename "$path") ]]; then
dir=$(_find -type f "$path")
if [[ -n "$path" ]]; then
base=$(basename "$path")
dir=$(_find -type f "$base")
echo -n "There isn't a file called \`$path\` in your current directory."
if [[ ! -z "$dir" ]]; then
echo " Did you mean to \`cd $dir\` first?"
echo " Did you mean to run \`python $dir/$base\` instead?"
else
echo
fi
Expand Down
28 changes: 28 additions & 0 deletions tests/smoke.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash
# Smoke-tests a built cs50/cli image. Usage: tests/smoke.sh [IMAGE]
# Each check has a timeout so that a regression that hangs the shell fails loudly.

set -o errexit -o nounset -o pipefail

IMAGE="${1:-cs50/cli}"
run() { timeout 60 docker run --rm "$@"; }

echo "Checking $IMAGE"

echo "- non-interactive login shell exits (help50 must not start without a terminal)"
run "$IMAGE" bash --login -c 'echo ok' | grep -qx ok
echo true | run --interactive "$IMAGE" bash --login

echo "- runtime dependencies of help50 are installed"
run "$IMAGE" bash --login -c 'for c in ansi2txt col file script fold; do command -v "$c" > /dev/null || { echo "missing $c" >&2; exit 1; }; done'

echo "- help50 controller is the Bash version, on PATH, and enabled by default"
run "$IMAGE" bash --login -c 'test "$(type -P help50)" = /opt/cs50/bin/help50 && help50 is-enabled && test "$(help50 status)" = stopped' > /dev/null

echo "- wrappers print their message even when stdin is redirected"
run "$IMAGE" bash --login -c 'valgrind python x.py < /dev/null; test $? -eq 1' 2>&1 | grep -q 'does not support Python'

echo "- _fold wraps without a terminal"
run "$IMAGE" bash --login -c '. /opt/cs50/lib/cli; TERM= _fold "$(printf "a %.0s" {1..100})"' | head -n 1 | grep -qE '^.{1,80}$'

echo "OK"
Loading