Skip to content

Commit 807e33d

Browse files
committed
Modernize
1 parent 3aee23b commit 807e33d

File tree

13 files changed

+390
-36
lines changed

13 files changed

+390
-36
lines changed

.github/workflows/ci.yaml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
name: CI
22

3-
on: push
3+
on:
4+
push:
5+
pull_request:
46

57
jobs:
68
ci:
79
name: CI
810
runs-on: ubuntu-latest
911

1012
steps:
11-
- uses: actions/checkout@v2
13+
- uses: actions/checkout@v4
14+
15+
- name: Setup Bats and bats libs
16+
uses: bats-core/bats-action@3.0.0
1217

1318
- name: CI Bash
1419
run: make ci-bash
1520

16-
- name: CI Python
17-
uses: actions/setup-python@v2
21+
- name: Set up Python
22+
uses: actions/setup-python@v5
1823
with:
1924
python-version: '3.x'
20-
- run: make ci-py
25+
26+
- name: CI Python
27+
run: make ci-py

Makefile

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: fmt format lint test
1+
.PHONY: fmt format lint test update-sha256 clean ci-bash install install-bats local-install ci-py install-py-deps it-test local-install-ci setup
22

33
### Bash
44

@@ -7,49 +7,59 @@ clean:
77
rm /usr/local/bin/filter
88
rm -rf /usr/local/lib/fs
99

10-
ci-bash: lint install-bats test
10+
ci-bash: lint test
1111

1212
fmt: format
1313

1414
format:
15-
shellcheck -x -f diff fs/operations/* | git apply
16-
shellcheck -x -f diff map | git apply
17-
shellcheck -x -f diff filter | git apply
15+
shellcheck -x -f diff fs/operations/* | git apply --allow-empty
16+
shellcheck -x -f diff
17+
shellcheck -x -f diff filter | git apply --allow-empty
1818

1919
install:
2020
./install.sh
2121

22-
install-bats:
23-
git clone https://github.com/bats-core/bats-core.git /tmp/bats-core && \
24-
cd /tmp/bats-core && \
25-
sudo ./install.sh /usr/local
26-
27-
local-install:
28-
@cp -f ./map /usr/local/bin/
29-
@cp -f ./filter /usr/local/bin/
30-
@cp -rf ./fs /usr/local/lib/
31-
3222
lint:
3323
shellcheck -x -e SC1091 map
3424
shellcheck -x -e SC1091 filter
3525
shellcheck -x fs/operations/*
3626
shellcheck -x fs/functions/*
27+
shellcheck -x install.sh
28+
shellcheck -x update-sha256.sh
29+
shellcheck -x it-test-setup.sh
30+
31+
setup:
32+
@echo "Setting up the environment..."
33+
@brew install shellcheck bats parallel
3734

3835
test:
3936
bats -j 15 tests/
4037

38+
update-sha256:
39+
./update-sha256.sh
40+
4141

4242
### Python
4343

4444
ci-py: install-py-deps local-install-ci it-test
4545

46+
setup-py:
47+
poetry env use python3.11
48+
4649
install-py-deps:
47-
pip install -r tests/integration-tests/requirements.txt
50+
poetry install --only=test
4851

49-
it-test:
50-
pytest -s tests/integration-tests/tests.py
52+
it-test: it-test-install
53+
poetry run pytest -s tests/integration-tests/tests.py
54+
./it-test-setup.sh uninstall
5155

5256
local-install-ci:
5357
sudo cp -f ./map /usr/local/bin/
5458
sudo cp -f ./filter /usr/local/bin/
5559
sudo cp -rf ./fs /usr/local/lib/
60+
61+
it-test-install:
62+
./it-test-setup.sh install
63+
64+
it-test-uninstall:
65+
./it-test-setup.sh uninstall

fs/fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/Users/segersand/Documents/Personal/repos_personal/functional-shell/fs

fs/functions/_filter

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ function _filter {
6161

6262
local filter_res
6363
while read -r line; do
64-
filter_res=$(eval "$fn_arg" "'$line'" "'$args'")
65-
if [ "$filter_res" == "true" ]; then
66-
printf "%s\n" "$line"
64+
# Use safer function call instead of eval
65+
if filter_res=$("$fn_arg" "$line" "$args"); then
66+
if [ "$filter_res" == "true" ]; then
67+
printf "%s\n" "$line"
68+
fi
6769
fi
6870
done < /dev/stdin
6971

fs/functions/_map

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ function _map {
77
shift
88
local args="$*"
99

10+
# Validate function name contains only safe characters
11+
if [[ ! "$fn_arg" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then
12+
printf "Error: Invalid function name '%s' for map\n" "$fn_arg" >&2
13+
exit 1
14+
fi
15+
1016
if [ "$(declare -f "$fn_arg" > /dev/null; echo $?)" -gt 0 ]; then
1117
printf "%s %s\n" "$fn_arg" "is not a valid function for map" >&2
1218
exit 1
@@ -22,7 +28,8 @@ function _map {
2228
IFS=''
2329

2430
while read -r line; do
25-
eval "$fn_arg" "'$line'" "'$args'"
31+
# Use safer function call instead of eval
32+
"$fn_arg" "$line" "$args"
2633
done < /dev/stdin
2734

2835
IFS=$old_ifs

fs/operations/file_and_dir

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,11 @@ function has_ext {
9797
printf "%s\n" false
9898
else
9999
local has_ext
100-
has_ext=$(test "$input" != "$actual_ext"; echo $?)
100+
if test "$input" != "$actual_ext"; then
101+
has_ext=0
102+
else
103+
has_ext=1
104+
fi
101105
local has_same_ext
102106
has_same_ext=$(test "$actual_ext" == "$expected_ext"; echo $?)
103107

fs/operations/string

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function substr {
3333

3434
local i=0
3535
for arg in $args; do
36-
split_args[$i]="$arg"
36+
split_args[i]="$arg"
3737
i=$((i + 1))
3838
done
3939

@@ -74,7 +74,7 @@ function replace {
7474

7575
local i=0
7676
for arg in $args; do
77-
split_args[$i]="$arg"
77+
split_args[i]="$arg"
7878
i=$((i + 1))
7979
done
8080

install.sh

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,70 @@
77
# finally removes the cloned repo
88
#
99

10-
set -e
10+
set -euo pipefail
1111

1212
readonly tmp_dir="/tmp/functional-shell"
13+
readonly repo_url="https://github.com/simeg/functional-shell"
14+
readonly expected_sha256="160a085aa649b90a55c704e8a46c1d8c73241fc88fae62f0095baadece0f3c32"
1315

16+
# Check if we need sudo for installation
17+
check_permissions() {
18+
if [[ ! -w /usr/local/bin ]] || [[ ! -w /usr/local/lib ]]; then
19+
echo "-- Root permissions required for installation to /usr/local"
20+
if command -v sudo >/dev/null 2>&1; then
21+
echo "-- Will use sudo for installation"
22+
USE_SUDO="sudo"
23+
else
24+
echo "-- Error: No write permissions and sudo not available" >&2
25+
exit 1
26+
fi
27+
else
28+
USE_SUDO=""
29+
fi
30+
}
31+
32+
# Verify repository integrity
33+
verify_integrity() {
34+
echo "-- Verifying repository integrity..."
35+
36+
if [[ "${expected_sha256}" == "PLACEHOLDER_SHA256" ]]; then
37+
echo "-- Warning: Checksum verification disabled (no expected hash provided)"
38+
return 0
39+
fi
40+
41+
# Calculate SHA256 of the git tree
42+
local actual_sha256
43+
actual_sha256=$(cd "${tmp_dir}" && git ls-tree HEAD | sha256sum | cut -d' ' -f1)
44+
45+
if [[ "${actual_sha256}" != "${expected_sha256}" ]]; then
46+
echo "-- Error: Repository integrity check failed!" >&2
47+
echo "-- Expected: ${expected_sha256}" >&2
48+
echo "-- Actual: ${actual_sha256}" >&2
49+
echo "-- This could indicate tampering or an unexpected version" >&2
50+
exit 1
51+
fi
52+
53+
echo "-- Repository integrity verified ✓"
54+
}
1455

1556
echo "-- Cloning repo..."
1657

17-
git clone https://github.com/simeg/functional-shell ${tmp_dir}
58+
git clone "${repo_url}" "${tmp_dir}"
1859

1960
echo "-- Repo cloned!"
2061

62+
verify_integrity
63+
64+
check_permissions
65+
2166
echo "-- Installing..."
2267

2368
# Install map and filter
24-
cp -f ${tmp_dir}/map /usr/local/bin
25-
cp -f ${tmp_dir}/filter /usr/local/bin
69+
${USE_SUDO} cp -f "${tmp_dir}/map" /usr/local/bin/
70+
${USE_SUDO} cp -f "${tmp_dir}/filter" /usr/local/bin/
2671

2772
# Install operations
28-
cp -rf ${tmp_dir}/fs /usr/local/lib
73+
${USE_SUDO} cp -rf "${tmp_dir}/fs" /usr/local/lib/
2974

3075
echo "-- Cleaning up temp files..."
3176

it-test-setup.sh

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env bash
2+
3+
#
4+
# Integration test setup script using symlinks
5+
# This allows running it-test without needing sudo or copying files
6+
#
7+
# Usage: ./it-test-setup.sh [install|uninstall]
8+
#
9+
10+
set -euo pipefail
11+
12+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13+
readonly script_dir
14+
readonly bin_dir="$HOME/.local/bin"
15+
readonly lib_dir="$HOME/.local/lib"
16+
17+
usage() {
18+
echo "Usage: $0 [install|uninstall]"
19+
echo " install - Create symlinks for integration testing"
20+
echo " uninstall - Remove symlinks created for integration testing"
21+
exit 1
22+
}
23+
24+
install_symlinks() {
25+
echo "-- Setting up symlinks for integration testing..."
26+
27+
# Ensure directories exist
28+
mkdir -p "$bin_dir" "$lib_dir"
29+
30+
# Create symlinks for executables
31+
ln -sf "$script_dir/map" "$bin_dir/map"
32+
ln -sf "$script_dir/filter" "$bin_dir/filter"
33+
34+
# Create symlink for operations library
35+
ln -sf "$script_dir/fs" "$lib_dir/fs"
36+
37+
echo "-- Symlinks created:"
38+
echo " $bin_dir/map -> $script_dir/map"
39+
echo " $bin_dir/filter -> $script_dir/filter"
40+
echo " $lib_dir/fs -> $script_dir/fs"
41+
echo ""
42+
echo "-- Make sure $bin_dir is in your PATH:"
43+
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
44+
}
45+
46+
uninstall_symlinks() {
47+
echo "-- Removing symlinks for integration testing..."
48+
49+
# Remove symlinks if they exist
50+
[ -L "$bin_dir/map" ] && rm "$bin_dir/map" && echo " Removed $bin_dir/map"
51+
[ -L "$bin_dir/filter" ] && rm "$bin_dir/filter" && echo " Removed $bin_dir/filter"
52+
[ -L "$lib_dir/fs" ] && rm "$lib_dir/fs" && echo " Removed $lib_dir/fs"
53+
54+
echo "-- Symlinks removed"
55+
}
56+
57+
# Parse command line argument
58+
case "${1:-install}" in
59+
install)
60+
install_symlinks
61+
;;
62+
uninstall)
63+
uninstall_symlinks
64+
;;
65+
*)
66+
usage
67+
;;
68+
esac

0 commit comments

Comments
 (0)