diff --git a/.githooks/post-receive b/.githooks/post-receive index a3f097f..638a096 100644 --- a/.githooks/post-receive +++ b/.githooks/post-receive @@ -2,13 +2,46 @@ set -euo pipefail # -e: exit on error, -u: exit on unset var, -o pipefail: catch errors in pipes TIMESTAMP=$(date +%Y%m%d-%H%M%S) -MAIN_APP_BARE_REPO="/srv/jasonpoage.com/expressjs-blog.git" + +get_deploy_env_file() { + branch="$1" + echo "/srv/jasonpoage.com/${branch}.env" +} + +get_deploy_path() { + branch="$1" + echo "/srv/jasonpoage.com/expressjs-blog-${branch}" +} + +get_log_file_path() { + echo "/srv/jasonpoage.com/logs/receive-${TIMESTAMP}.log" +} + +copy_env_file() { + local branch="$1" + # Copy the .env file for the test environment + cp "$(get_deploy_env_file "$branch")" "$tmpdir/.env" || { + echo "Error: Failed to copy .env file for test environment." + return 1 + } +} + +clone_branch() { + local branch="$1" + local tmpdir="$2" + # This creates a proper working tree at $tmpdir, which is essential for submodules. + git clone --branch "$branch" "$GIT_DIR" "$tmpdir" || { + echo "Error: Failed to clone main application for tests to $tmpdir" + return 1 + } +} # Function to wait for a service to become available -wait_for_service() { +_wait_for_service() { local url="$1" local timeout=30 # seconds - local start_time=$(date +%s) + local start_time + start_time=$(date +%s) echo "Waiting for service at $url to become available (timeout: ${timeout}s)..." while :; do if curl --silent --fail "$url" >/dev/null; then @@ -25,152 +58,47 @@ } deploy_expressjs_blog() { - local oldrev="$1" - local newrev="$2" - local ref="$3" - local custom_path="${4:-}" # If custom_path is provided, this is a test deployment - local custom_env="${5:-}" - local branch="${ref#refs/heads/}" + local branch="$1" + local test_dir="$2" # Define deployment path and env file path - local path="${custom_path:-/srv/jasonpoage.com/expressjs-blog-$branch}" - local envfile="${custom_env:-/srv/jasonpoage.com/$branch.env}" - - # set -x # Enable command tracing for debugging + local deploy_path envfile + deploy_path=$(get_deploy_path "$branch") + envfile=$(get_deploy_env_file "$branch") if [[ "$branch" == "production" || "$branch" == "main" || "$branch" == "testing" ]]; then + echo "Moving tested deployment from '$test_dir' to '$deploy_path'..." + [[ -d "$deploy_path" ]] && rm -rf "$deploy_path" + mv "$test_dir" "$deploy_path" + ln -f "$envfile" "$deploy_path/.env" - # Ensure the deployment directory exists and is clean for a fresh clone - if [[ -d "$path" ]]; then - echo "Deployment path '$path' exists, removing for fresh clone..." - rm -rf "$path" - fi - mkdir -p "$(dirname "$path")" # Ensure parent directory exists - - echo "Cloning main application from bare repo '$MAIN_APP_BARE_REPO' to '$path'..." - - git clone --branch "$branch" "$MAIN_APP_BARE_REPO" "$path" || { - echo "Error: Failed to clone main application to $path" - return 1 - } - - cd "$path" || { - echo "Error: Could not change directory to $path" - return 1 - } - - echo "Ensuring content submodule is initialized and updated." - git --work-tree="$path" submodule update --init --remote --depth=1 --checkout --recursive || { - echo "Error: Failed to update submodules." - return 1 - } - - # Handle .env file - ln -f "$envfile" "$path/.env" - - update_node_pkgs || return 1 - combine_css "$oldrev" "$newrev" || return 1 - - # Restart service (only for actual deployments) - [[ -z "$custom_path" ]] && systemctl --user restart express-blog@"$branch".service - - fi # End of branch check - # set +x # Disable command tracing -} - -deploy_tested_expressjs_blog() { - local oldrev="$1" - local newrev="$2" - local ref="$3" - local custom_path="${4:-}" - local custom_env="${5:-}" - local branch="${ref#refs/heads/}" - - # Define deployment path and env file path - local path="${custom_path:-/srv/jasonpoage.com/expressjs-blog-$branch}" - local envfile="${custom_env:-/srv/jasonpoage.com/$branch.env}" - - # set -x # Enable command tracing for debugging - - if [[ "$branch" == "production" || "$branch" == "main" || "$branch" == "testing" ]]; then - - echo "Moving tested deployment from '$test_dir' to '$path'..." - [[ -d "$path" ]] && rm -rf "$path" - mv "$test_dir" "$path" - - cd "$path" || { - echo "Error: Could not change directory to $path" - return 1 - } - - # Handle .env file - ln -f "$envfile" "$path/.env" - - # Restart service (only for actual deployments) - [[ -z "$custom_path" ]] && systemctl --user restart express-blog@"$branch".service - - fi # End of branch check - # set +x # Disable command tracing -} - -update_node_pkgs() { - # Yarn install logic - local install_required=true - if [[ -z "$custom_path" && -n "$oldrev" && "$oldrev" != "0000000000000000000000000000000000000000" ]]; then - # This block only runs for actual deployments (not test runs) - # Use --git-dir for this diff against the bare repo's commits - if ! git --git-dir="$MAIN_APP_BARE_REPO" diff-tree --name-only -r "$oldrev..$newrev" | grep -qE "(package\.json|yarn\.lock)$"; then - echo "No changes detected in package.json or yarn.lock. Skipping yarn install." - install_required=false - else - echo "Changes detected in package.json or yarn.lock. Running yarn install." - fi - else - echo "Running yarn install (initial deployment or test environment)." - fi - - if [[ "$install_required" == "true" ]]; then - yarn || { - echo "Error: yarn install failed." - return 1 - } + systemctl --user restart express-blog@"$branch".service fi } + run_postreceive_tests() { local branch="$1" local tmpdir pidfile logfile - tmpdir=$(mktemp -d) + tmpdir="$2" pidfile="$tmpdir/test.pid" - logfile="/srv/jasonpoage.com/logs/receive-$TIMESTAMP.log" + logfile=$(get_log_file_path) # Trap to ensure cleanup even if script exits unexpectedly - trap "kill $(cat "$pidfile" 2>/dev/null) 2>/dev/null || true; rm -rf \"$tmpdir\"" EXIT + trap "kill \"$(cat "$pidfile" 2>/dev/null)\" 2>/dev/null || true; rm -f \"$pidfile\"" EXIT + echo "Running post-receive tests for branch '$branch' in temporary environment." - # This creates a proper working tree at $tmpdir, which is essential for submodules. - git clone --branch "$branch" "$GIT_DIR" "$tmpdir" || { - echo "Error: Failed to clone main application for tests to $tmpdir" - return 1 - } + clone_branch "$branch" "$tmpdir" || return 1 cd "$tmpdir" || { echo "Error: Could not change directory to $tmpdir" return 1 } - echo "Initializing and updating submodules for test environment..." - git --work-tree="$tmpdir" submodule update --init --recursive || { - ls "$tmpdir" -a - echo "Error: Failed to initialize/update submodules for test environment." - return 1 - } + copy_env_file "$branch" || return 1 - # Copy the .env file for the test environment - cp "/srv/jasonpoage.com/${branch}.env" "$tmpdir/.env" || { - echo "Error: Failed to copy .env file for test environment." - return 1 - } + initialize_submodules "$tmpdir" || return 1 export TEST_PORT=4123 export NODE_ENV=testing @@ -183,22 +111,43 @@ nohup yarn start >>"$logfile" 2>&1 & echo $! >"$pidfile" - # Wait for the application to become responsive - if ! wait_for_service "http://127.0.0.1:$TEST_PORT"; then - echo "Application did not start or respond for tests. Check logs in $logfile:" - cat "$logfile" # Display logs on failure - return 1 - fi + set +x + wait_for_service "$logfile" + set -x echo "Running tests..." - run_tests || return 1 + run_tests "$branch" "$pidfile" "$logfile" || return 1 kill "$(cat "$pidfile")" 2>/dev/null || true + unset TEST_PORT NODE_ENV + echo "Tests passed for branch '$branch' in temporary environment." return 0 } +initialize_submodules() { + local tmpdir="$1" + echo "Initializing and updating submodules for test environment..." + git --work-tree="$tmpdir" submodule update --init --recursive || { + ls "$tmpdir" -a + echo "Error: Failed to initialize/update submodules for test environment." + return 1 + } +} +wait_for_service() { + local logfile="$1" + # Wait for the application to become responsive + if ! _wait_for_service "http://127.0.0.1:$TEST_PORT"; then + echo "Application did not start or respond for tests. Check logs in $logfile:" + cat "$logfile" # Display logs on failure + return 1 + fi +} + run_tests() { + branch="$1" + pidfile="$2" + logfile="$3" echo "Running npm tests..." if ! npm run test:postreceive; then kill "$(cat "$pidfile")" 2>/dev/null || true @@ -209,34 +158,25 @@ } combine_css() { - set +u - oldrev="$1" - newrev="$2" - set -u - - if [[ -z "$oldrev" && -z "$newrev" ]]; then - yarn --production=false combine:css || { - echo "Error: yarn combine:css failed." - return 1 - } - else - yarn --production=false combine:css "$oldrev" "$newrev" "$MAIN_APP_BARE_REPO" || { - echo "Error: yarn combine:css with args failed." - return 1 - } - fi + yarn --production=false combine:css || { + echo "Error: yarn combine:css failed." + return 1 + } } +set -x # Main script execution loop while read -r oldrev newrev ref; do + tmpdir=$(mktemp -d) branch="${ref#refs/heads/}" echo "--- Processing push for branch: $branch (from $oldrev to $newrev) ---" - if run_postreceive_tests "$branch"; then + if run_postreceive_tests "$branch" "$tmpdir"; then echo "Tests passed for $branch. Proceeding with deployment." - deploy_tested_expressjs_blog "$oldrev" "$newrev" "$ref" + deploy_expressjs_blog "$branch" "$tmpdir" echo "Deployment of $branch complete." else echo "Post-receive tests failed for $branch. Deployment aborted." exit 1 fi done +set +x diff --git a/src/app.js b/src/app.js index 2ed0a2c..d9b02e7 100644 --- a/src/app.js +++ b/src/app.js @@ -1,6 +1,7 @@ // src/app.js require("dotenv").config(); +const net = require("net"); const setupMiddleware = require("./middleware"); const { manualLogger } = require("./utils/logging"); const { startTokenCleanup } = require("./utils/tokenCleanup"); @@ -27,10 +28,26 @@ const app = setupMiddleware(); -app.listen(SERVER_PORT, () => { - console.log(SERVER_LISTEN_LOG(SERVER_PORT)); - console.log(NODE_ENV_LOG); +const server = net.createServer(); +server.once("error", (err) => { + if (err.code === "EADDRINUSE") { + console.error(`Port ${SERVER_PORT} is already in use.`); + process.exit(1); + } else { + throw err; + } }); +server.once("listening", () => { + server.close(); + + app.listen(SERVER_PORT, () => { + console.log(SERVER_LISTEN_LOG(SERVER_PORT)); + console.log(NODE_ENV_LOG); + }); +}); + +server.listen(SERVER_PORT); + process.on("uncaughtException", handleUncaughtException); process.on("unhandledRejection", handleUnhandledRejection);