diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5dc3bf4..bbd2d1b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,11 +8,12 @@ on: # Allow manual trigger workflow_dispatch: -env: - SNAP: envtester_${{ github.run_id}}.snap - jobs: build-and-test: + strategy: + fail-fast: false + matrix: + implementation: [bash, rust] outputs: snap: ${{ steps.snapcraft.outputs.snap }} runs-on: ubuntu-latest @@ -20,8 +21,11 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Implementation + run: "echo ::notice::env-exporter implementation: ${{ matrix.implementation }}" + - name: Initialize tests - run: sudo ./tests/initialize + run: sudo ./tests/initialize -i ${{ matrix.implementation }} - name: Build snap uses: snapcore/action-build@v1 @@ -33,7 +37,7 @@ jobs: - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: ${{ env.SNAP }} + name: envtester_${{ github.run_id }}_${{ matrix.implementation }}.snap path: ${{ steps.snapcraft.outputs.snap }} if-no-files-found: error diff --git a/env-exporter.sh b/env-exporter.sh index ea471e6..e0b3589 100755 --- a/env-exporter.sh +++ b/env-exporter.sh @@ -127,6 +127,7 @@ json_to_hash_table() { local nested=$(catch_nested_json "$json_input") err "Nested snap options keys aren't supported:\n$nested" json_input=$(strip_nested_json_keys "$json_input") + fi json_input=$(echo "$json_input" | sed 's/[{}]//g' | tr -d '[:space:]') diff --git a/tests/common b/tests/common new file mode 100755 index 0000000..5726340 --- /dev/null +++ b/tests/common @@ -0,0 +1,131 @@ + +declare -A impl_map=( + [bash]="env-exporter.sh" + [rust]="env-exporter" +) + +SNAP=envtester +SNAP_COMMON=/var/snap/"${SNAP}"/common/ + +check_syntax() { + local snap_app="$1" + local env_value="$2" + if eval "$snap_app" | grep -q "^${env_value}="; then + echo -e "\n[ERROR] Value '$env_name' SHOULD NOT BE set.\n" + return 1 + fi + return 0 +} + +check_env_exist() { + local snap_app="$1" + local env_name="$2" + + if ! eval "$snap_app" | grep -q "^${env_name}="; then + echo -e "\n[ERROR] Environment variable '$env_name' is not set, for the app: $snap_app.\n" + return 1 + fi +} + +check_env_not_exist() { + + local snap_app="$1" + local env_name="$2" + + if eval "$snap_app" | grep -q "^${env_name}="; then + echo -e "\n[ERROR] Environment variable '$env_name' SHOULD NOT be set, for the app: $snap_app.\n" + return 1 + fi +} + +check_env_value() { + local snap_app="$1" + local env_name="$2" + local exp_value="$3" + local actual_value + + if [ -z "$env_name" ]; then + echo -e "\n\nHERE HERE\n\n " + empty=$("$snap_app" | grep "=${exp_value}") + [ -z "$empty" ] || return 1 + fi + actual_value=$("$snap_app" | grep "^${env_name}=" | cut -d'=' -f2-) + if [ "$actual_value" != "$exp_value" ]; then + echo -e "\n[ERROR] Environment variable '$env_name' does not match the expected value, for the app: $snap_app" + echo -e "[ERROR] Expected: '$env_name=$exp_value', but got: '$env_name=$actual_value'\n" + return 1 + fi +} + +check_root() { + if [ "$USER" != "root" ]; then + echo -e "Please run as root.\n" + exit 1 + fi +} + +clean() { + echo "Cleaning..." + snap remove --purge "${SNAP}" + git restore snap/snapcraft.yaml + sudo chown $SUDO_USER:$SUDO_USER snap/snapcraft.yaml + + rm -rf squashfs-root +} + +fail() { + clean + exit 1 +} + +inject_test_app() { + clean + sudo snap install yq + + if [[ -z "${impl_map[$1]}" ]]; then + echo "Error: unknown implementation '$1'" + help + fi + + local exporter="${impl_map[$1]}" + + # Create app1 + sudo -u "$SUDO_USER" yq ".apps.app1 = { + \"environment\": { + \"env_alias\": \"app1\" + }, + \"command-chain\": [ + \"bin/${exporter}\" + ], + \"command\": \"bin/env.sh\" + }" -i "snap/snapcraft.yaml" + + # Create app2 + sudo -u "$SUDO_USER" yq ".apps.app2 = { + \"environment\": { + \"env_alias\": \"app2\" + }, + \"command-chain\": [ + \"bin/${exporter}\" + ], + \"command\": \"bin/env.sh\" + }" -i "snap/snapcraft.yaml" + +} + +check_impl() { + if [[ "$1" != "bash" && "$1" != "rust" ]]; then + echo "Error: --impl/-i must be either 'bash' or 'rust'" >&2 + help + fi +} + +init_tests() { + set +u + + if [ -z "${GITHUB_ACTIONS}" ]; then + set -u + snapcraft -o "${SNAP}".snap + snap install "${SNAP}".snap --dangerous + fi +} \ No newline at end of file diff --git a/tests/initialize b/tests/initialize index e2cceef..975ba41 100755 --- a/tests/initialize +++ b/tests/initialize @@ -1,100 +1,53 @@ #!/bin/bash -check_syntax() { - local snap_app="$1" - local env_value="$2" - if eval "$snap_app" | grep -q "^${env_value}="; then - echo -e "\n[ERROR] Value '$env_name' SHOULD NOT BE set.\n" - return 1 - fi - return 0 -} - -check_env_exist() { - local snap_app="$1" - local env_name="$2" +set -x - if ! eval "$snap_app" | grep -q "^${env_name}="; then - echo -e "\n[ERROR] Environment variable '$env_name' is not set, for the app: $snap_app.\n" - return 1 - fi -} +BASEDIR=$(dirname "$(realpath "$0")") +PROJECT_ROOT="$BASEDIR/.." -check_env_not_exist() { +. $PROJECT_ROOT/tests/common - local snap_app="$1" - local env_name="$2" +help() { + cat < + $0 --impl= - if eval "$snap_app" | grep -q "^${env_name}="; then - echo -e "\n[ERROR] Environment variable '$env_name' SHOULD NOT be set, for the app: $snap_app.\n" - return 1 - fi -} +Options: + -i, --impl Specify the implementation of the env-exporter program. + Must be either 'bash' or 'rust'. -check_env_value() { - local snap_app="$1" - local env_name="$2" - local exp_value="$3" - local actual_value +Examples: + $0 -i bash + $0 --impl=rust - if [ -z "$env_name" ]; then - echo -e "\n\nHERE HERE\n\n " - empty=$("$snap_app" | grep "=${exp_value}") - [ -z "$empty" ] || return 1 - fi - actual_value=$("$snap_app" | grep "^${env_name}=" | cut -d'=' -f2-) - if [ "$actual_value" != "$exp_value" ]; then - echo -e "\n[ERROR] Environment variable '$env_name' does not match the expected value, for the app: $snap_app" - echo -e "[ERROR] Expected: '$env_name=$exp_value', but got: '$env_name=$actual_value'\n" - return 1 - fi -} - -check_root() { - if [ "$USER" != "root" ]; then - echo -e "Please run as root.\n" - exit 1 - fi -} +Description: + The 'impl' option selects which implementation of the env-exporter program to use. + - 'bash': Uses the Bash-based implementation. + - 'rust': Uses the Rust-based implementation. -clean() { - echo "Cleaning..." - snap remove --purge "${SNAP}" - git restore snap/snapcraft.yaml - sudo chown $SUDO_USER:$SUDO_USER snap/snapcraft.yaml - - rm -rf squashfs-root -} - -fail() { - clean +EOF exit 1 } -inject_test_app() { - sudo snap install yq - sudo -u "$SUDO_USER" yq '.apps.app-rust-2 = { - "environment": { - "env_alias": "app-rust-2" - }, - "command-chain": [ - "bin/env-exporter" - ], - "command": "bin/env.sh" - }' -i "snap/snapcraft.yaml" -} -init_tests() { - set +u - if [ -z "${GITHUB_ACTIONS}" ]; then - set -u - inject_test_app - snapcraft -o "${SNAP}".snap - snap install "${SNAP}".snap --dangerous - fi -} +impl="" + +while [[ $# -gt 0 ]]; do + case "$1" in + -i=* | --impl=*) + impl="${1#*=}" + shift + ;; + -i | --impl) + impl="$2" + shift 2 + ;; + *) + help + ;; + esac +done -SNAP=envtester -SNAP_COMMON=/var/snap/"${SNAP}"/common/ +check_impl "$impl" -inject_test_app +inject_test_app "$impl" diff --git a/tests/test-snappyenv b/tests/test-snappyenv index b36c586..71e6d60 100755 --- a/tests/test-snappyenv +++ b/tests/test-snappyenv @@ -1,35 +1,23 @@ #!/bin/bash -set -eu +set -eux BASEDIR=$(dirname "$(realpath "$0")") PROJECT_ROOT="$BASEDIR/.." -. $PROJECT_ROOT/tests/initialize +. $PROJECT_ROOT/tests/common check_root -clean init_tests echo "SNAP NAME: ${SNAP}" pushd "$PROJECT_ROOT" || exit 1 -# 'sudo -u "$RUN_AS"' prevents a permission denied error when using yq as snap. -sudo -u "$SUDO_USER" yq '.apps.app-rust-2 = { - "environment": { - "env_alias": "app-rust-2" - }, - "command-chain": [ - "bin/env-exporter" - ], - "command": "bin/env.sh" -}' -i "snap/snapcraft.yaml" - unsquashfs "${PROJECT_ROOT}/${SNAP}.snap" echo "Check that the env-exporter program is present" -[ -f squashfs-root/bin/env-exporter ] || fail +[ -f squashfs-root/bin/env-exporter ] || [ -f squashfs-root/bin/env-exporter.sh ] || fail echo "Check that the exec-env script is present" [ -f squashfs-root/bin/env.sh ] || fail @@ -40,27 +28,27 @@ echo 'HELLO_WORLD="Hello World"' >>"$SNAP_COMMON"/global.env echo "Load global envfile" snap set "${SNAP}" envfile="$SNAP_COMMON"/global.env -echo "[TEST] - Check if the global envfile is loaded the app using Rust snappy-env programs" -check_env_exist "${SNAP}.app-rust" "HELLO_WORLD" || fail -check_env_value "${SNAP}.app-rust" "HELLO_WORLD" "Hello World" || fail +echo "[TEST] - Check if the global envfile is loaded the app using snappy-env programs" +check_env_exist "${SNAP}.app1" "HELLO_WORLD" || fail +check_env_value "${SNAP}.app1" "HELLO_WORLD" "Hello World" || fail -check_env_exist "${SNAP}.app-rust-2" "HELLO_WORLD" || fail -check_env_value "${SNAP}.app-rust-2" "HELLO_WORLD" "Hello World" || fail +check_env_exist "${SNAP}.app2" "HELLO_WORLD" || fail +check_env_value "${SNAP}.app2" "HELLO_WORLD" "Hello World" || fail echo -e "\n[envtester] Creating app-specific envfile" -echo 'SCOPED=RustVersion' >>"$SNAP_COMMON"/rust.env -echo 'SCOPED=Rust2Version' >>"$SNAP_COMMON"/rust-2.env +echo 'SCOPED=AppVersion' >>"$SNAP_COMMON"/envs.env +echo 'SCOPED=App2Version' >>"$SNAP_COMMON"/envs2.env echo "Load app-specific envfile" -snap set "${SNAP}" apps.app-rust.envfile="$SNAP_COMMON"/rust.env -snap set "${SNAP}" apps.app-rust-2.envfile="$SNAP_COMMON"/rust-2.env +snap set "${SNAP}" apps.app1.envfile="$SNAP_COMMON"/envs.env +snap set "${SNAP}" apps.app2.envfile="$SNAP_COMMON"/envs2.env echo "[TEST] - Check if the app-specific envfile is loaded for the apps" -check_env_exist "${SNAP}.app-rust" "SCOPED" || fail -check_env_value "${SNAP}.app-rust" "SCOPED" "RustVersion" || fail +check_env_exist "${SNAP}.app1" "SCOPED" || fail +check_env_value "${SNAP}.app1" "SCOPED" "AppVersion" || fail -check_env_exist "${SNAP}.app-rust-2" "SCOPED" || fail -check_env_value "${SNAP}.app-rust-2" "SCOPED" "Rust2Version" || fail +check_env_exist "${SNAP}.app2" "SCOPED" || fail +check_env_value "${SNAP}.app2" "SCOPED" "App2Version" || fail echo -e "\n[envtester] Setting global env variable" @@ -68,44 +56,44 @@ echo "Set env vars: Global" snap set "${SNAP}" env.global="World" echo "[TEST] - Check if the global env var is set for all apps" -check_env_exist "${SNAP}.app-rust" "GLOBAL" || fail -check_env_value "${SNAP}.app-rust" "GLOBAL" "World" || fail +check_env_exist "${SNAP}.app1" "GLOBAL" || fail +check_env_value "${SNAP}.app1" "GLOBAL" "World" || fail -check_env_exist "${SNAP}.app-rust-2" "GLOBAL" || fail -check_env_value "${SNAP}.app-rust-2" "GLOBAL" "World" || fail +check_env_exist "${SNAP}.app2" "GLOBAL" || fail +check_env_value "${SNAP}.app2" "GLOBAL" "World" || fail echo -e "\n[envtester] Setting app-specific env variable" echo "Set env vars: specific to each app" -snap set "${SNAP}" apps.app-rust.env.hello="Hello" -snap set "${SNAP}" apps.app-rust-2.env.specific="City" +snap set "${SNAP}" apps.app1.env.hello="Hello" +snap set "${SNAP}" apps.app2.env.specific="City" -echo "[TEST] - Check if the app-specific env var IS SET for the app 'app-rust'" -check_env_exist "${SNAP}.app-rust" "HELLO" || fail -check_env_value "${SNAP}.app-rust" "HELLO" "Hello" || fail +echo "[TEST] - Check if the app-specific env var IS SET for the app 'app1'" +check_env_exist "${SNAP}.app1" "HELLO" || fail +check_env_value "${SNAP}.app1" "HELLO" "Hello" || fail -echo -e "\n[TEST] - Check if the app-specific env var IS NOT SET for the app 'app-rust-2'" -check_env_not_exist "${SNAP}.app-rust-2" "HELLO" || fail +echo -e "\n[TEST] - Check if the app-specific env var IS NOT SET for the app 'app2'" +check_env_not_exist "${SNAP}.app2" "HELLO" || fail -echo -e "\n[TEST] - Check if the app-specific env var IS SET for the app 'app-rust-2'" -check_env_exist "${SNAP}.app-rust-2" "SPECIFIC" || fail -check_env_value "${SNAP}.app-rust-2" "SPECIFIC" "City" || fail +echo -e "\n[TEST] - Check if the app-specific env var IS SET for the app 'app2'" +check_env_exist "${SNAP}.app2" "SPECIFIC" || fail +check_env_value "${SNAP}.app2" "SPECIFIC" "City" || fail -echo -e "\n[TEST] - Check if the app-specific env var IS NOT SET for the app 'app-rust'" -check_env_not_exist "${SNAP}.app-rust" "SPECIFIC" || fail +echo -e "\n[TEST] - Check if the app-specific env var IS NOT SET for the app 'app1'" +check_env_not_exist "${SNAP}.app1" "SPECIFIC" || fail echo -e "\n[envtester] Testing order of env vars" echo 'ORDER="From envfile"' >>"$SNAP_COMMON"/local.env -snap set "${SNAP}" apps.app-rust.env.order="from snap option" -snap set "${SNAP}" apps.app-rust.envfile="$SNAP_COMMON"/local.env +snap set "${SNAP}" apps.app1.env.order="from snap option" +snap set "${SNAP}" apps.app1.envfile="$SNAP_COMMON"/local.env echo "[TEST] - Check if local overrides global" -check_env_exist "${SNAP}.app-rust" "ORDER" || fail -check_env_value "${SNAP}.app-rust" "ORDER" "from snap option" || fail +check_env_exist "${SNAP}.app1" "ORDER" || fail +check_env_value "${SNAP}.app1" "ORDER" "from snap option" || fail echo -e "\n[envtester] Testing options syntax" snap set "${SNAP}" env.word.dot="wrong" echo "[TEST] - Check if the key with dot was ignored" -check_syntax "${SNAP}.app-rust" "wrong" || fail +check_syntax "${SNAP}.app1" "wrong" || fail snap unset "${SNAP}" env.word.dot clean