aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml18
-rw-r--r--Makefile50
-rw-r--r--README.md33
-rwxr-xr-xlinks-chmod3
-rwxr-xr-xlinks-remove3
-rwxr-xr-xlinks-update3
-rw-r--r--src/db.sh5
-rw-r--r--test/docker-compose.yml13
-rw-r--r--test/docker/Dockerfile9
-rwxr-xr-xtest/integration/test.sh91
-rw-r--r--test/unit/dest/1.txt (renamed from test/dest/1.txt)0
-rw-r--r--test/unit/dest/bar/3.txt (renamed from test/dest/bar/3.txt)0
-rw-r--r--test/unit/src/%DEST%/1.txt (renamed from test/src/%DEST%/1.txt)0
-rw-r--r--test/unit/src/%DEST%/bar/3.txt (renamed from test/src/%DEST%/bar/3.txt)0
-rw-r--r--test/unit/src/%DEST%/bar/baz/4.txt (renamed from test/src/%DEST%/bar/baz/4.txt)0
-rw-r--r--test/unit/src/%DEST%/foo/2.txt (renamed from test/src/%DEST%/foo/2.txt)0
-rwxr-xr-xtest/unit/test.sh (renamed from test/test.sh)23
17 files changed, 233 insertions, 18 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f4954c4..2bfb964 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,7 +6,7 @@ on:
workflow_dispatch:
jobs:
- test:
+ test_local:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
@@ -26,4 +26,18 @@ jobs:
# with ancient utilities that come with the actual macOS.
- name: Test
- run: ./test/test.sh
+ run: make test
+
+ test_docker:
+ strategy:
+ matrix:
+ # Keep in sync with those in Makefile:
+ distro: [xenial, focal]
+ runs-on: ubuntu-latest
+ name: 'Test / Docker / ${{ matrix.distro }}'
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Test
+ run: make 'test/docker/${{ matrix.distro }}'
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..054e75b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,50 @@
+MAKEFLAGS += --no-builtin-rules --no-builtin-variables --warn-undefined-variables
+unexport MAKEFLAGS
+.DEFAULT_GOAL := all
+.DELETE_ON_ERROR:
+.SUFFIXES:
+SHELL := bash
+.SHELLFLAGS := -eu -o pipefail -c
+
+escape = $(subst ','\'',$(1))
+
+define noexpand
+ifeq ($$(origin $(1)),environment)
+ $(1) := $$(value $(1))
+endif
+ifeq ($$(origin $(1)),environment override)
+ $(1) := $$(value $(1))
+endif
+ifeq ($$(origin $(1)),command line)
+ override $(1) := $$(value $(1))
+endif
+endef
+
+.PHONY: DO
+DO:
+
+.PHONY: all
+all: test
+
+.PHONY: test
+test: test/local
+
+.PHONY: test/all
+test/all: test/local test/docker
+
+.PHONY: test/local
+test/local:
+ ./test/unit/test.sh
+
+test/docker/%: DO
+ cd test && \
+ DISTRO='$*' docker-compose build --pull && \
+ docker-compose run --rm test && \
+ docker-compose down -v
+
+# Xenial has bash 4.3, which doesn't support inherit_errexit, which is a good
+# thing to test against.
+#
+# Keep the list repositories synced with the GitHub actions workflow.
+.PHONY: test/docker
+test/docker: test/docker/xenial test/docker/focal
diff --git a/README.md b/README.md
index 4247989..9c04c05 100644
--- a/README.md
+++ b/README.md
@@ -53,16 +53,16 @@ To remove all symlinks, use `links-remove`.
usage: links-remove [-h|--help] [-d|--database PATH] [-s|--shared-dir DIR] [-n|--dry-run]
```
-In this example, symlinks to files in "../src" must appear in "/test/dest".
+In this example, symlinks to files in "/test/src" must appear in "/test/dest".
```
-> tree /test/dest/
+$ tree /test/dest/
/test/dest/
0 directories, 0 files
-> tree ../src/
-../src/
+$ tree /test/src/
+/test/src/
└── %DEST%
├── a
│   └── b
@@ -74,21 +74,21 @@ In this example, symlinks to files in "../src" must appear in "/test/dest".
6 directories, 2 files
-> echo "$DEST"
+$ echo "$DEST"
/test/dest
-> ./links-update --shared-dir ../src/
+$ ./links-update --shared-dir /test/src/
...
-> tree /test/dest/
+$ tree /test/dest/
/test/dest/
├── a
│   └── b
│   └── c
-│   └── test.txt -> /cygdrive/d/workspace/personal/src/%DEST%/a/b/c/test.txt
+│   └── test.txt -> /test/src/%DEST%/a/b/c/test.txt
└── foo
└── bar
- └── baz -> /cygdrive/d/workspace/personal/src/%DEST%/foo/bar/baz
+ └── baz -> /test/src/%DEST%/foo/bar/baz
5 directories, 2 files
```
@@ -101,6 +101,21 @@ For my personal real-life usage examples, see
[my dotfiles]: https://github.com/egor-tensin/linux-home
[Windows apps]: https://github.com/egor-tensin/windows-home
+Development
+-----------
+
+Run local tests using
+
+ make test
+
+Run Docker tests using
+
+ make test/docker
+
+Run all tests using
+
+ make test/all
+
Limitations
-----------
diff --git a/links-chmod b/links-chmod
index f722b95..c0cf71e 100755
--- a/links-chmod
+++ b/links-chmod
@@ -8,7 +8,8 @@
# usage: ./links-chmod [-h|--help] [-d|--database PATH] [-s|--shared-dir DIR] [-n|--dry-run] MODE
set -o errexit -o nounset -o pipefail
-shopt -s inherit_errexit lastpipe
+shopt -s inherit_errexit 2> /dev/null || true
+shopt -s lastpipe
script_name="$( basename -- "${BASH_SOURCE[0]}" )"
readonly script_name
diff --git a/links-remove b/links-remove
index c378485..2efc46d 100755
--- a/links-remove
+++ b/links-remove
@@ -8,7 +8,8 @@
# usage: ./links-remove [-h|--help] [-d|--database PATH] [-s|--shared-dir DIR] [-n|--dry-run]
set -o errexit -o nounset -o pipefail
-shopt -s inherit_errexit lastpipe
+shopt -s inherit_errexit 2> /dev/null || true
+shopt -s lastpipe
script_name="$( basename -- "${BASH_SOURCE[0]}" )"
readonly script_name
diff --git a/links-update b/links-update
index 5b15f1b..322cd49 100755
--- a/links-update
+++ b/links-update
@@ -20,7 +20,8 @@
# usage: ./links-update [-h|--help] [-d|--database PATH] [-s|--shared-dir DIR] [-m|--mode MODE] [-n|--dry-run]
set -o errexit -o nounset -o pipefail
-shopt -s inherit_errexit lastpipe
+shopt -s inherit_errexit 2> /dev/null || true
+shopt -s lastpipe
script_name="$( basename -- "${BASH_SOURCE[0]}" )"
readonly script_name
diff --git a/src/db.sh b/src/db.sh
index 201ad25..94a4a7d 100644
--- a/src/db.sh
+++ b/src/db.sh
@@ -192,6 +192,7 @@ shared_file_present() {
}
link_all_entries() {
+ local -a shared_var_dirs=()
local shared_var_dir
find "$shared_root_dir" \
@@ -201,6 +202,10 @@ link_all_entries() {
-regex ".*/$var_name_regex\$" \
-printf '%P\0' |
while IFS= read -d '' -r shared_var_dir; do
+ shared_var_dirs+=("$shared_var_dir")
+ done
+
+ for shared_var_dir in ${shared_var_dirs[@]+"${shared_var_dirs[@]}"}; do
dump "shared directory: $shared_root_dir$shared_var_dir"
local shared_path
diff --git a/test/docker-compose.yml b/test/docker-compose.yml
new file mode 100644
index 0000000..76546c4
--- /dev/null
+++ b/test/docker-compose.yml
@@ -0,0 +1,13 @@
+version: '3'
+services:
+ test:
+ build:
+ context: docker/
+ args:
+ DISTRO: "${DISTRO:-xenial}"
+ volumes:
+ - ../:/src
+ command:
+ - sh
+ - -c
+ - /src/test/unit/test.sh && /src/test/integration/test.sh
diff --git a/test/docker/Dockerfile b/test/docker/Dockerfile
new file mode 100644
index 0000000..5d4d016
--- /dev/null
+++ b/test/docker/Dockerfile
@@ -0,0 +1,9 @@
+ARG DISTRO=xenial
+
+FROM ubuntu:$DISTRO
+
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive \
+ apt-get install -y --no-install-recommends ca-certificates git
+
+RUN git config --global --add safe.directory /src
diff --git a/test/integration/test.sh b/test/integration/test.sh
new file mode 100755
index 0000000..f4b1028
--- /dev/null
+++ b/test/integration/test.sh
@@ -0,0 +1,91 @@
+#!/usr/bin/env bash
+
+set -o errexit -o nounset -o pipefail
+shopt -s inherit_errexit 2> /dev/null || true
+shopt -s lastpipe
+
+script_dir="$( dirname -- "${BASH_SOURCE[0]}" )"
+script_dir="$( cd -- "$script_dir" && pwd )"
+readonly script_dir
+script_name="$( basename -- "${BASH_SOURCE[0]}" )"
+readonly script_name
+
+root_dir="$( git -C "$script_dir" rev-parse --show-toplevel )"
+readonly root_dir
+
+src_name='linux-home'
+readonly src_name
+src_url="https://github.com/egor-tensin/$src_name.git"
+readonly src_url
+
+test_root_dir=
+test_src_dir=
+
+new_test() {
+ local test_name=
+ [ "${#FUNCNAME[@]}" -gt 1 ] && test_name="${FUNCNAME[1]}"
+ [ "$#" -gt 0 ] && test_name="$1"
+
+ echo
+ echo ======================================================================
+ echo "New test: $test_name"
+
+ test_root_dir="$( mktemp -d )"
+ # mktemp returns /var/..., which is actually in /private/var/... on macOS.
+ test_root_dir="$( readlink -e -- "$test_root_dir" )"
+ test_src_dir="$test_root_dir/$src_name"
+
+ echo "Root directory: $test_root_dir"
+ echo "Shared directory: $test_src_dir"
+ echo ======================================================================
+
+ git clone -q -- "$src_url" "$test_src_dir"
+ cd -- "$test_src_dir"
+}
+
+call_bin_script() {
+ echo
+ echo -n 'Executing script:'
+
+ printf -- ' %q' "$@" --shared-dir "$test_src_dir" --database "$test_root_dir/links.bin"
+ printf -- '\n'
+
+ echo
+ "$@" --shared-dir "$test_src_dir" --database "$test_root_dir/links.bin"
+}
+
+call_update() {
+ call_bin_script "$root_dir/links-update" "$@"
+}
+
+call_remove() {
+ call_bin_script "$root_dir/links-remove"
+}
+
+call_chmod() {
+ call_bin_script "$root_dir/links-chmod" "$@"
+}
+
+test_my_dotfiles_work() {
+ new_test
+ call_update
+ # Again:
+ call_update
+}
+
+show_env() {
+ echo
+ echo ======================================================================
+ echo Environment
+ echo ======================================================================
+
+ bash --version
+}
+
+main() {
+ show_env
+
+ test_my_dotfiles_work
+}
+
+main
diff --git a/test/dest/1.txt b/test/unit/dest/1.txt
index 3a2e3f4..3a2e3f4 100644
--- a/test/dest/1.txt
+++ b/test/unit/dest/1.txt
diff --git a/test/dest/bar/3.txt b/test/unit/dest/bar/3.txt
index a83d1d5..a83d1d5 100644
--- a/test/dest/bar/3.txt
+++ b/test/unit/dest/bar/3.txt
diff --git a/test/src/%DEST%/1.txt b/test/unit/src/%DEST%/1.txt
index d00491f..d00491f 100644
--- a/test/src/%DEST%/1.txt
+++ b/test/unit/src/%DEST%/1.txt
diff --git a/test/src/%DEST%/bar/3.txt b/test/unit/src/%DEST%/bar/3.txt
index 00750ed..00750ed 100644
--- a/test/src/%DEST%/bar/3.txt
+++ b/test/unit/src/%DEST%/bar/3.txt
diff --git a/test/src/%DEST%/bar/baz/4.txt b/test/unit/src/%DEST%/bar/baz/4.txt
index b8626c4..b8626c4 100644
--- a/test/src/%DEST%/bar/baz/4.txt
+++ b/test/unit/src/%DEST%/bar/baz/4.txt
diff --git a/test/src/%DEST%/foo/2.txt b/test/unit/src/%DEST%/foo/2.txt
index 0cfbf08..0cfbf08 100644
--- a/test/src/%DEST%/foo/2.txt
+++ b/test/unit/src/%DEST%/foo/2.txt
diff --git a/test/test.sh b/test/unit/test.sh
index 05c5d65..f693aa1 100755
--- a/test/test.sh
+++ b/test/unit/test.sh
@@ -1,7 +1,8 @@
#!/usr/bin/env bash
set -o errexit -o nounset -o pipefail
-shopt -s inherit_errexit lastpipe
+shopt -s inherit_errexit 2> /dev/null || true
+shopt -s lastpipe
script_dir="$( dirname -- "${BASH_SOURCE[0]}" )"
script_dir="$( cd -- "$script_dir" && pwd )"
@@ -9,6 +10,9 @@ readonly script_dir
script_name="$( basename -- "${BASH_SOURCE[0]}" )"
readonly script_name
+root_dir="$( git -C "$script_dir" rev-parse --show-toplevel )"
+readonly root_dir
+
readonly src_dir_name='src'
readonly dest_dir_name='dest'
readonly alt_dest_dir_name='alt_dest'
@@ -62,15 +66,15 @@ call_bin_script() {
}
call_update() {
- call_bin_script "$script_dir/../links-update" "$@"
+ call_bin_script "$root_dir/links-update" "$@"
}
call_remove() {
- call_bin_script "$script_dir/../links-remove"
+ call_bin_script "$root_dir/links-remove"
}
call_chmod() {
- call_bin_script "$script_dir/../links-chmod" "$@"
+ call_bin_script "$root_dir/links-chmod" "$@"
}
verify_output() {
@@ -413,7 +417,18 @@ $test_dest_dir/foo/2.txt->$test_src_dir/%DEST%/foo/2.txt"
verify_mode "$expected_mode" "$test_src_dir/%DEST%/1.txt"
}
+show_env() {
+ echo
+ echo ======================================================================
+ echo Environment
+ echo ======================================================================
+
+ bash --version
+}
+
main() {
+ show_env
+
test_update_works
test_remove_works
test_remove_does_not_overwrite_files