From 036b388fb062b12483065360a9d755e125abe1ff Mon Sep 17 00:00:00 2001 From: Mahdi Butcher Date: Sat, 22 Aug 2026 08:14:51 +0000 Subject: [PATCH] feat(scripts): add Docker-style script execution header and commit tracking --- .../pasarguard-standalone-release.yml | 2 + .../workflows/pg-node-standalone-release.yml | 2 + install_core.sh | 29 ++++++++++++ iran-sanction/pasarguard-standalone.sh | 1 + iran-sanction/pg-node-standalone.sh | 1 + lib/common.sh | 47 +++++++++++++++++++ pasarguard.sh | 9 +++- pg-node.sh | 11 ++++- tests/unit_lib_common.sh | 33 +++++++++++++ tests/unit_pasarguard.sh | 10 ++++ tests/unit_pgnode.sh | 24 ++++++++++ 11 files changed, 167 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pasarguard-standalone-release.yml b/.github/workflows/pasarguard-standalone-release.yml index 61b50df..4e7fb9c 100644 --- a/.github/workflows/pasarguard-standalone-release.yml +++ b/.github/workflows/pasarguard-standalone-release.yml @@ -51,6 +51,8 @@ jobs: cp dist/pg-node-assets/.env.example "$BUNDLE_ROOT/pg-node-assets/.env.example" cp dist/pasarguard-assets/.env.example "$BUNDLE_ROOT/pasarguard-assets/.env.example" cp iran-sanction/README-pasarguard-standalone.fa.md "$BUNDLE_ROOT/README.md" + COMMIT_SHA="${GITHUB_SHA:-$(git rev-parse HEAD)}" + find "$BUNDLE_ROOT" -type f -name "*.sh" -exec sed -i "s/__SCRIPT_COMMIT_SHA__/${COMMIT_SHA}/g" {} + tar -czf dist/pasarguard-standalone.tar.gz -C dist pasarguard-standalone - name: Upload workflow artifact diff --git a/.github/workflows/pg-node-standalone-release.yml b/.github/workflows/pg-node-standalone-release.yml index f6ee645..9510f3f 100644 --- a/.github/workflows/pg-node-standalone-release.yml +++ b/.github/workflows/pg-node-standalone-release.yml @@ -37,6 +37,8 @@ jobs: cp docker-compose/node.yml "$BUNDLE_ROOT/docker-compose/" cp dist/pg-node-assets/.env.example "$BUNDLE_ROOT/pg-node-assets/.env.example" cp iran-sanction/README-pg-node-standalone.fa.md "$BUNDLE_ROOT/README.md" + COMMIT_SHA="${GITHUB_SHA:-$(git rev-parse HEAD)}" + find "$BUNDLE_ROOT" -type f -name "*.sh" -exec sed -i "s/__SCRIPT_COMMIT_SHA__/${COMMIT_SHA}/g" {} + tar -czf dist/pg-node-standalone.tar.gz -C dist pg-node-standalone - name: Upload workflow artifact diff --git a/install_core.sh b/install_core.sh index 1a885cf..094930a 100644 --- a/install_core.sh +++ b/install_core.sh @@ -1,6 +1,34 @@ #!/usr/bin/env bash set -e +SCRIPT_COMMIT_SHA="${SCRIPT_COMMIT_SHA:-__SCRIPT_COMMIT_SHA__}" +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" + +get_script_commit_sha() { + local baked_sha="${SCRIPT_COMMIT_SHA:-}" + local commit_sha="" + + if [ -n "$baked_sha" ] && [ "$baked_sha" != "__SCRIPT_COMMIT_SHA__" ]; then + printf '%s\n' "$baked_sha" + return 0 + fi + + if [ -n "${PASARGUARD_SCRIPT_COMMIT:-}" ] && [ "$PASARGUARD_SCRIPT_COMMIT" != "__SCRIPT_COMMIT_SHA__" ]; then + printf '%s\n' "$PASARGUARD_SCRIPT_COMMIT" + return 0 + fi + + if command -v git >/dev/null 2>&1; then + commit_sha=$(git -C "$SCRIPT_DIR" rev-parse HEAD 2>/dev/null || true) + if [ -n "$commit_sha" ]; then + printf '%s\n' "$commit_sha" + return 0 + fi + fi + + printf '%s\n' "main" +} + # Download Xray latest RELEASE_TAG="latest" @@ -44,6 +72,7 @@ parse_args() { } parse_args "$@" +printf '# Executing install_core script, commit: %s\n' "$(get_script_commit_sha)" check_if_running_as_root() { # If you want to run as another user, please modify $EUID to be owned by this user diff --git a/iran-sanction/pasarguard-standalone.sh b/iran-sanction/pasarguard-standalone.sh index 52c395e..bf41170 100644 --- a/iran-sanction/pasarguard-standalone.sh +++ b/iran-sanction/pasarguard-standalone.sh @@ -249,6 +249,7 @@ detect_compose() { } install_pasarguard_script() { + print_script_execution_header "pasarguard-standalone" "$SCRIPT_COMMIT_SHA" "install" local target_path="/usr/local/bin/pasarguard" local wrapper_source="$STANDALONE_SCRIPT_DIR/pasarguard-standalone.sh" local installed_wrapper="$STANDALONE_INSTALL_ROOT/iran-sanction/pasarguard-standalone.sh" diff --git a/iran-sanction/pg-node-standalone.sh b/iran-sanction/pg-node-standalone.sh index 14e6d40..9985378 100644 --- a/iran-sanction/pg-node-standalone.sh +++ b/iran-sanction/pg-node-standalone.sh @@ -281,6 +281,7 @@ install_node_service_script() { } install_node_script() { + print_script_execution_header "pg-node-standalone" "$SCRIPT_COMMIT_SHA" "install" local target_path="/usr/local/bin/$APP_NAME" local wrapper_source="$STANDALONE_SCRIPT_DIR/pg-node-standalone.sh" local installed_wrapper="$STANDALONE_INSTALL_ROOT/iran-sanction/pg-node-standalone.sh" diff --git a/lib/common.sh b/lib/common.sh index 1a216f1..15f492d 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -113,3 +113,50 @@ create_temp_file_in_dir() { die "Failed to create temporary file in $dir" } + +get_script_commit_sha() { + local script_dir="${1:-}" + local baked_sha="${2:-}" + local commit_sha="" + + if [ -n "$baked_sha" ] && [ "$baked_sha" != "__SCRIPT_COMMIT_SHA__" ]; then + printf '%s\n' "$baked_sha" + return 0 + fi + + if [ -n "${PASARGUARD_SCRIPT_COMMIT:-}" ] && [ "$PASARGUARD_SCRIPT_COMMIT" != "__SCRIPT_COMMIT_SHA__" ]; then + printf '%s\n' "$PASARGUARD_SCRIPT_COMMIT" + return 0 + fi + + if [ -n "${SCRIPT_COMMIT_SHA:-}" ] && [ "$SCRIPT_COMMIT_SHA" != "__SCRIPT_COMMIT_SHA__" ]; then + printf '%s\n' "$SCRIPT_COMMIT_SHA" + return 0 + fi + + if [ -n "$script_dir" ] && command -v git >/dev/null 2>&1; then + commit_sha=$(git -C "$script_dir" rev-parse HEAD 2>/dev/null || true) + if [ -n "$commit_sha" ]; then + printf '%s\n' "$commit_sha" + return 0 + fi + fi + + printf '%s\n' "main" +} + +print_script_execution_header() { + local script_name="$1" + local baked_sha="${2:-}" + local action_type="${3:-}" + local script_dir="${4:-${SCRIPT_DIR:-}}" + local commit_sha="" + + commit_sha=$(get_script_commit_sha "$script_dir" "$baked_sha") + + if [ -n "$action_type" ]; then + printf '# Executing %s %s script, commit: %s\n' "$script_name" "$action_type" "$commit_sha" + else + printf '# Executing %s script, commit: %s\n' "$script_name" "$commit_sha" + fi +} diff --git a/pasarguard.sh b/pasarguard.sh index 76909ff..fc10f52 100755 --- a/pasarguard.sh +++ b/pasarguard.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash set -e +SCRIPT_COMMIT_SHA="${SCRIPT_COMMIT_SHA:-__SCRIPT_COMMIT_SHA__}" SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" SHARED_LIB_DIR="${SCRIPT_DIR}/lib" REQUIRED_SHARED_LIBS="common.sh system.sh docker.sh github.sh env.sh pasarguard-backup.sh pasarguard-restore.sh" @@ -927,6 +928,7 @@ verify_and_start_container() { } install_pasarguard_script() { + print_script_execution_header "pasarguard" "$SCRIPT_COMMIT_SHA" "install" FETCH_REPO="PasarGuard/scripts" colorized_echo blue "Installing pasarguard script" install_shared_libs_from_repo "$FETCH_REPO" common.sh system.sh docker.sh github.sh env.sh pasarguard-backup.sh pasarguard-restore.sh @@ -1242,6 +1244,7 @@ check_existing_database_volumes() { install_command() { check_running_as_root + print_script_execution_header "pasarguard" "$SCRIPT_COMMIT_SHA" "install" # Default values pasarguard_version="latest" @@ -1891,7 +1894,7 @@ _pasarguard_completions() local cur cmds COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" - cmds="up down restart status logs cli tui install update uninstall install-script install-node backup backup-service restore core-update edit edit-env help completion" + cmds="up down restart status logs cli tui install update uninstall install-script install-node backup backup-service restore core-update edit edit-env version-script script-version help completion" COMPREPLY=( $(compgen -W "$cmds" -- "$cur") ) return 0 } @@ -1945,6 +1948,7 @@ usage() { colorized_echo yellow " restore $(tput sgr0)– Restore database from backup file" colorized_echo yellow " edit $(tput sgr0)– Edit docker-compose.yml (via nano or vi editor)" colorized_echo yellow " edit-env $(tput sgr0)– Edit environment file (via nano or vi editor)" + colorized_echo yellow " version-script $(tput sgr0)– Show script version and commit" colorized_echo yellow " help $(tput sgr0)– Show this help message" echo @@ -2025,6 +2029,9 @@ pasarguard_main() { shift edit_env_command "$@" ;; + version-script | script-version) + print_script_execution_header "pasarguard" "$SCRIPT_COMMIT_SHA" + ;; completion) check_running_as_root install_completion diff --git a/pg-node.sh b/pg-node.sh index f9a5499..0a69c29 100755 --- a/pg-node.sh +++ b/pg-node.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash set -e +SCRIPT_COMMIT_SHA="${SCRIPT_COMMIT_SHA:-__SCRIPT_COMMIT_SHA__}" SCRIPT_DIR="${PG_NODE_SCRIPT_DIR:-$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)}" SHARED_LIB_DIR="${SCRIPT_DIR}/lib" REQUIRED_SHARED_LIBS="common.sh system.sh docker.sh github.sh" @@ -248,6 +249,7 @@ configure_firewall_for_port() { colorized_echo yellow "$hint" } install_node_script() { + print_script_execution_header "pg-node" "$SCRIPT_COMMIT_SHA" "install" colorized_echo blue "Installing node script" TARGET_PATH="/usr/local/bin/$APP_NAME" TEMP_FILE=$(create_temp_file "pg-node-script" ".sh") @@ -1007,6 +1009,7 @@ is_node_up() { } install_command() { check_running_as_root + print_script_execution_header "pg-node" "$SCRIPT_COMMIT_SHA" "install" # Default values node_version="latest" node_version_set="false" @@ -1881,7 +1884,7 @@ _node_completions() local cur cmds COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" - cmds="up down restart status logs install update uninstall install-script uninstall-script core-update geofiles renew-cert edit edit-env completion service-install service-uninstall service-restart service-status service-logs service-update service-start service-stop" + cmds="up down restart status logs install update uninstall install-script uninstall-script core-update geofiles renew-cert version-script script-version edit edit-env completion service-install service-uninstall service-restart service-status service-logs service-update service-start service-stop" COMPREPLY=( $(compgen -W "$cmds" -- "$cur") ) return 0 } @@ -1909,6 +1912,8 @@ commands=( core-update geofiles renew-cert + version-script + script-version edit edit-env completion @@ -1995,6 +2000,7 @@ usage() { colorized_echo yellow " core-update $(tput sgr0)✓ Update/Change Xray core" colorized_echo yellow " geofiles $(tput sgr0)✓ Download geoip and geosite files for specific regions" colorized_echo yellow " renew-cert $(tput sgr0)✓ Regenerate SSL/TLS certificate" + colorized_echo yellow " version-script $(tput sgr0)✓ Show script version and commit" colorized_echo yellow " completion $(tput sgr0)✓ Install bash/zsh tab completion" echo colorized_echo cyan "Restart Options:" @@ -2297,6 +2303,9 @@ pg_node_main() { edit-env) edit_env_command ;; + version-script | script-version) + print_script_execution_header "pg-node" "$SCRIPT_COMMIT_SHA" + ;; completion) check_running_as_root install_completion diff --git a/tests/unit_lib_common.sh b/tests/unit_lib_common.sh index e3043de..05aa0ac 100644 --- a/tests/unit_lib_common.sh +++ b/tests/unit_lib_common.sh @@ -82,6 +82,39 @@ assert_eq "$(stat -c '%a' "$secret_new")" "600" "harden_secret_file: appended co # Empty path is rejected without creating anything. if harden_secret_file ""; then fail "harden_secret_file: rejects empty path"; else pass "harden_secret_file: rejects empty path"; fi +# --- get_script_commit_sha & print_script_execution_header --- +assert_eq "$(get_script_commit_sha "$WORK_DIR" "a23123f03978989e95d257beb9de0c5ad9da6e70")" \ + "a23123f03978989e95d257beb9de0c5ad9da6e70" "get_script_commit_sha: returns baked-in SHA" + +( + export PASARGUARD_SCRIPT_COMMIT="custom_env_commit_sha" + assert_eq "$(get_script_commit_sha "$WORK_DIR" "__SCRIPT_COMMIT_SHA__")" \ + "custom_env_commit_sha" "get_script_commit_sha: respects PASARGUARD_SCRIPT_COMMIT override" +) + +( + export SCRIPT_COMMIT_SHA="custom_script_sha" + assert_eq "$(get_script_commit_sha "$WORK_DIR" "__SCRIPT_COMMIT_SHA__")" \ + "custom_script_sha" "get_script_commit_sha: respects SCRIPT_COMMIT_SHA override" +) + +git_expected=$(git -C "$ROOT_DIR" rev-parse HEAD 2>/dev/null || echo "main") +assert_eq "$(get_script_commit_sha "$ROOT_DIR" "__SCRIPT_COMMIT_SHA__")" \ + "$git_expected" "get_script_commit_sha: resolves commit from git repo" + +no_git_dir="$WORK_DIR/no_git_dir" +mkdir -p "$no_git_dir" +assert_eq "$(get_script_commit_sha "$no_git_dir" "__SCRIPT_COMMIT_SHA__")" \ + "main" "get_script_commit_sha: falls back to 'main' outside git repo" + +assert_eq "$(print_script_execution_header "pasarguard" "test_commit_sha" "install")" \ + "# Executing pasarguard install script, commit: test_commit_sha" \ + "print_script_execution_header: formats install banner correctly" + +assert_eq "$(print_script_execution_header "pg-node" "test_commit_sha")" \ + "# Executing pg-node script, commit: test_commit_sha" \ + "print_script_execution_header: formats general script banner correctly" + echo "" echo "Results: $PASS passed, $FAIL failed" [ "$FAIL" -eq 0 ] || exit 1 diff --git a/tests/unit_pasarguard.sh b/tests/unit_pasarguard.sh index 6b8160c..061fd98 100644 --- a/tests/unit_pasarguard.sh +++ b/tests/unit_pasarguard.sh @@ -448,6 +448,16 @@ OS="openSUSE Tumbleweed"; assert_eq "$(get_cron_package_name)" "cronie" "cron_p OS="Plan 9"; assert_false "cron_pkg: unknown OS -> non-zero" get_cron_package_name OS="$_saved_os" +# ----------------------------------------------------------------------- +# version-script CLI command & completion +# ----------------------------------------------------------------------- +ver_out=$(pasarguard_main version-script) +assert_true "version-script: output contains '# Executing pasarguard script, commit:'" contains "$ver_out" "# Executing pasarguard script, commit:" + +comp_out=$(generate_completion) +assert_true "completion: contains version-script" contains "$comp_out" "version-script" +assert_true "completion: contains script-version" contains "$comp_out" "script-version" + echo "" echo "Results: $PASS passed, $FAIL failed" [ "$FAIL" -eq 0 ] || exit 1 diff --git a/tests/unit_pgnode.sh b/tests/unit_pgnode.sh index ab25ea8..e890ad9 100644 --- a/tests/unit_pgnode.sh +++ b/tests/unit_pgnode.sh @@ -279,6 +279,30 @@ ENV_FILE="/nonexistent/dir/.env" sync_env_ssl_paths pass "sync_env_ssl_paths: no crash when ENV_FILE missing" +# ----------------------------------------------------------------------- +# version-script CLI command & completions +# ----------------------------------------------------------------------- +ver_out=$(pg_node_main version-script) +if [[ "$ver_out" == *"# Executing pg-node script, commit:"* ]]; then + pass "version-script: output contains '# Executing pg-node script, commit:'" +else + fail "version-script: output contains '# Executing pg-node script, commit:'" +fi + +bash_comp_out=$(generate_bash_completion) +if [[ "$bash_comp_out" == *"version-script"* && "$bash_comp_out" == *"script-version"* ]]; then + pass "bash completion: contains version-script and script-version" +else + fail "bash completion: contains version-script and script-version" +fi + +zsh_comp_out=$(generate_zsh_completion) +if [[ "$zsh_comp_out" == *"version-script"* && "$zsh_comp_out" == *"script-version"* ]]; then + pass "zsh completion: contains version-script and script-version" +else + fail "zsh completion: contains version-script and script-version" +fi + echo "" echo "Results: $PASS passed, $FAIL failed" [ "$FAIL" -eq 0 ] || exit 1