diff --git a/action.yml b/action.yml index d5ddb45c..1ccfe287 100644 --- a/action.yml +++ b/action.yml @@ -20,8 +20,10 @@ inputs: context: description: Directory in the repository where the Dockerfile or start command is located required: false + default: "./" dockerfile: description: Path to the Dockerfile (optional) + required: false default: "./Dockerfile" exposed_port: description: Port to expose in the container diff --git a/entrypoint.sh b/entrypoint.sh index 1e07a37c..7c08ed54 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -3,104 +3,22 @@ # Exit immediately if a command exits with a non-zero status. set -e -function handle_error { - comment "Failed ❌" "#" && exit 1 -} - -# Set up trap to handle errors -trap 'handle_error' ERR - -comment() { - local status_message=$1 - local preview_url=$2 - - echo $status_message - - local comment_body=$(jq -n --arg body "Here are the latest updates on your deployment. Explore the action and ⭐ star our project for more insights! 🔍 - - - - - - - - - - - - - - - - - -
Deployed ByStatusPreview URLUpdated At (UTC)
PR Deploy${status_message}Visit Preview$(date +'%b %d, %Y %I:%M%p')
" '{body: $body}') - - if [ -z "$COMMENT_ID" ]; then - # Create a new comment - COMMENT_ID=$(curl -s -H "Authorization: token $GITHUB_TOKEN" -X POST \ - -d "$comment_body" \ - -H "Accept: application/vnd.github.v3+json" \ - "https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/issues/${PR_NUMBER}/comments" | jq -r '.id') - - elif [ "$COMMENT_ID" == "null" ]; then - # Create a new comment - COMMENT_ID=$(curl -s -H "Authorization: token $GITHUB_TOKEN" -X POST \ - -d "$comment_body" \ - -H "Accept: application/vnd.github.v3+json" \ - "https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/issues/${PR_NUMBER}/comments" | jq -r '.id') - else - # Update an existing comment - curl -s -H "Authorization: token $GITHUB_TOKEN" -X PATCH \ - -d "$comment_body" \ - -H "Accept: application/vnd.github.v3+json" \ - "https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/issues/comments/${COMMENT_ID}" > /dev/null - fi -} - -REPO_ID=$(curl -L \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer $GITHUB_TOKEN" \ - https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME} | jq -r '.id') - -# Checks if the action is opened -if [ "$PR_ACTION" == "opened" ]; then - comment "Deploying ⏳" "#" -fi - -# Formats the environment variables into a comma-separated string -ENV_ARGS=$(echo "$ENVS" | tr '\n' ',' ) - -# Copy the pr-deploy.sh script to the remote server. +# Copy the script to the remote server. sshpass -p "$SERVER_PASSWORD" scp -o StrictHostKeyChecking=no -P $SERVER_PORT pr-deploy.sh $SERVER_USERNAME@$SERVER_HOST:/srv/pr-deploy.sh -# Run the pr-deploy.sh script on the remote server and capture the output from the remote script -REMOTE_OUTPUT=$(sshpass -p "$SERVER_PASSWORD" ssh -o StrictHostKeyChecking=no -p $SERVER_PORT $SERVER_USERNAME@$SERVER_HOST bash /srv/pr-deploy.sh $CONTEXT $DOCKERFILE $EXPOSED_PORT $REPO_URL $REPO_ID $GITHUB_HEAD_REF $PR_ACTION $PR_NUMBER "${ENV_ARGS}" $COMMENT_ID | tail -n 1) - -# Ensure the output is valid JSON by escaping problematic characters -SANITIZED_OUTPUT=$(echo "$REMOTE_OUTPUT" | sed 's/[[:cntrl:]]//g') - -# Parse the sanitized JSON -COMMENT_ID=$(echo "$SANITIZED_OUTPUT" | jq -r '.COMMENT_ID') -DEPLOYED_URL=$(echo "$SANITIZED_OUTPUT" | jq -r '.DEPLOYED_URL') - -if [ "$COMMENT_ID" == "null" ]; then - # Checks if the action is opened - if [[ "$PR_ACTION" == "opened" || "$PR_ACTION" == "synchronize" || "$PR_ACTION" == "reopened" ]]; then - comment "Deploying ⏳" "#" - elif [ "$PR_ACTION" == "closed" ]; then - comment "Terminated 🛑" "#" - fi - - # Run the pr-deploy.sh script on the remote server and capture the output from the remote script - NEW_REMOTE_OUTPUT=$(sshpass -p "$SERVER_PASSWORD" ssh -o StrictHostKeyChecking=no -p $SERVER_PORT $SERVER_USERNAME@$SERVER_HOST bash /srv/pr-deploy.sh $CONTEXT $DOCKERFILE $EXPOSED_PORT $REPO_URL $REPO_ID $GITHUB_HEAD_REF $PR_ACTION $PR_NUMBER "${ENV_ARGS}" $COMMENT_ID | tail -n 1) - exit 0 -fi - -if [ -z "$DEPLOYED_URL" ]; then - if [ "$PR_ACTION" == "closed" ]; then - comment "Terminated 🛑" "#" && exit 0 - fi - comment "Failed ❌" "#" && exit 1 -fi -comment "Deployed 🎉" $DEPLOYED_URL +# Run the script on the remote server with environment variables. +sshpass -p "$SERVER_PASSWORD" ssh -o StrictHostKeyChecking=no -p $SERVER_PORT $SERVER_USERNAME@$SERVER_HOST \ + "GITHUB_TOKEN='$GITHUB_TOKEN' \ + CONTEXT='$CONTEXT' \ + DOCKERFILE='$DOCKERFILE' \ + EXPOSED_PORT='$EXPOSED_PORT' \ + ENVS='$ENVS' \ + REPO_OWNER='$REPO_OWNER' \ + REPO_NAME='$REPO_NAME' \ + REPO_URL='$REPO_URL' \ + REPO_ID='$REPO_ID' \ + BRANCH='$GITHUB_HEAD_REF' \ + PR_ACTION='$PR_ACTION' \ + PR_NUMBER='$PR_NUMBER' \ + COMMENT_ID='$COMMENT_ID' \ + bash -c 'echo \"$SERVER_PASSWORD\" | sudo -SE bash /srv/pr-deploy.sh'" \ No newline at end of file diff --git a/pr-deploy.sh b/pr-deploy.sh index dc4684d0..d67d4f40 100644 --- a/pr-deploy.sh +++ b/pr-deploy.sh @@ -1,65 +1,103 @@ #!/bin/bash set -e +trap 'comment "Failed ❌" "#"; exit 1' ERR -CONTEXT=$1 -DOCKERFILE=$2 -EXPOSED_PORT=$3 -REPO_URL=$4 -REPO_ID=$5 -BRANCH=$6 -PR_ACTION=$7 -PR_NUMBER=$8 -ENVS="${9}" -COMMENT_ID="${10}" -PR_ID="pr_${REPO_ID}${PR_NUMBER}" -# JSON file to store PIDs +DEPLOY_FOLDER="/srv/pr-deploy" PID_FILE="/srv/pr-deploy/nohup.json" COMMENT_ID_FILE="/srv/pr-deploy/comments.json" -DEPLOY_FOLDER="/srv/pr-deploy" -function handle_error { - echo "{\"COMMENT_ID\": \"$COMMENT_ID\", \"DEPLOYED_URL\": \"\"}" - exit 1 +comment() { + local status_message=$1 + local preview_url=$2 + echo $status_message + + local comment_body=$(jq -n --arg body "Here are the latest updates on your deployment. Explore the action and ⭐ star our project for more insights! 🔍 + + + + + + + + + + + + + + + + + +
Deployed ByStatusPreview URLUpdated At (UTC)
PR Deploy${status_message}Visit Preview$(date +'%b %d, %Y %I:%M%p')
" '{body: $body}') + + if [ -z "$COMMENT_ID" ]; then + # Create a new comment + COMMENT_ID=$(curl -s -H "Authorization: token $GITHUB_TOKEN" -X POST \ + -d "$comment_body" \ + -H "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/issues/${PR_NUMBER}/comments" | jq -r '.id') + jq --arg pr_id "$PR_ID" --arg cid "$COMMENT_ID" '.[$pr_id] = $cid' "$COMMENT_ID_FILE" > "${PID_FILE}.tmp" && mv "${PID_FILE}.tmp" "$COMMENT_ID_FILE" + else + # Update the existing comment + curl -s -H "Authorization: token $GITHUB_TOKEN" -X PATCH \ + -d "$comment_body" \ + -H "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/issues/comments/${COMMENT_ID}" > /dev/null + fi } -# This helps to kill the process created by nohup using the process id -function kill_process_with_pid() { - # serveo - local key=$1 - ID=$(jq -r --arg key "$key" '.[$key]' "${PID_FILE}") - if [ -n $ID ]; then - kill -9 $ID - jq --arg key "$key" 'del(.[$key])' "${PID_FILE}" > tmp && mv tmp "${PID_FILE}" +cleanup() { + PID=$(jq -r --arg key "$PR_ID" '.[$key] // ""' "${PID_FILE}") + + if [ -n "$PID" ]; then + kill -9 "$PID" || true + jq --arg key "$PR_ID" 'del(.[$key])' "${PID_FILE}" > "${PID_FILE}.tmp" && mv "${PID_FILE}.tmp" "${PID_FILE}" fi + + CONTAINER_ID=$(docker ps -aq --filter "name=${PR_ID}") + [ -n "$CONTAINER_ID" ] && sudo docker stop -t 0 "$CONTAINER_ID" && sudo docker rm -f "$CONTAINER_ID" + + IMAGE_ID=$(docker images -q --filter "reference=${PR_ID}") + [ -n "$IMAGE_ID" ] && sudo docker rmi -f "$IMAGE_ID" + sleep 1 } +REPO_ID=$(curl -L \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME} | jq -r '.id') + +PR_ID="pr_${REPO_ID}${PR_NUMBER}" + # Setup directory -mkdir -p ${DEPLOY_FOLDER}/ +mkdir -p ${DEPLOY_FOLDER} # Initialize the JSON file for nohup if it doesn't exist if [ ! -f "$PID_FILE" ]; then - echo "{}" > "$PID_FILE" + echo {} > $PID_FILE fi # Initialize the JSON file for comment if it doesn't exist if [ ! -f "$COMMENT_ID_FILE" ]; then - echo "{}" > "$COMMENT_ID_FILE" + echo {} > $COMMENT_ID_FILE fi -# Set up trap to handle errors -trap 'handle_error' ERR +# Handle COMMENT_ID +COMMENT_ID=$(jq -r --arg key $PR_ID '.[$key] // ""' ${COMMENT_ID_FILE}) +comment "Deploying ⏳" "#" # Ensure docker is installed if [ ! command -v docker &> /dev/null ]; then - sudo apt-get update - sudo apt-get install docker.io -y + apt-get update + apt-get install docker.io -y fi # Ensure python is installed if [ ! command -v python3 &> /dev/null ]; then - sudo apt-get update - sudo apt-get install python3 -y + apt-get update + apt-get install python3 -y fi # Free port @@ -68,63 +106,33 @@ FREE_PORT=$(python3 -c 'import socket; s = socket.socket(); s.bind(("", 0)); pri cd ${DEPLOY_FOLDER} rm -rf $PR_ID -# Handle COMMENT_ID -if [ -n "$COMMENT_ID" ]; then - # echo "$COMMENT_ID" > "${PR_ID}.txt" - jq --arg pr_id "$PR_ID" --arg cid "$COMMENT_ID" '.[$pr_id] = $cid' "$COMMENT_ID_FILE" > tmp.$$.json && mv tmp.$$.json "$COMMENT_ID_FILE" -else - if [ -f "$COMMENT_ID_FILE" ]; then - COMMENT_ID=$(jq -r --arg key "$PR_ID" '.[$key]' "${COMMENT_ID_FILE}") - - else - COMMENT_ID="" - fi -fi - -# Get container and image IDs -CONTAINER_ID=$(docker ps -aq --filter "name=${PR_ID}") -IMAGE_ID=$(docker images -q --filter "reference=${PR_ID}") - # Handle different PR actions case $PR_ACTION in reopened | synchronize | closed) - # Stop and force remove containers if they exist - [ -n "$CONTAINER_ID" ] && sudo docker stop -t 0 $CONTAINER_ID && sudo docker rm -f $CONTAINER_ID - - # Force remove images if they exist - [ -n "$IMAGE_ID" ] && sudo docker rmi -f $IMAGE_ID - - # Exit early for 'closed' action - [ "$PR_ACTION" == "closed" ] && echo "{\"COMMENT_ID\": \"$COMMENT_ID\", \"DEPLOYED_URL\": \"\"}" && kill_process_with_pid $PR_ID && exit 0 + cleanup + [ "$PR_ACTION" == "closed" ] && comment "Terminated 🛑" "#" && exit 0 ;; esac # Git clone and Docker operations -echo "Git Clone ..." git clone -b $BRANCH $REPO_URL $PR_ID -cd $PR_ID - -echo "Building docker image..." -sudo docker build -t $PR_ID -f $DOCKERFILE . +cd $PR_ID/$CONTEXT -echo "Running docker container..." -echo "$ENVS" | tr ',' '\n' > .env -sudo docker run -d --env-file .env -p $FREE_PORT:$EXPOSED_PORT --name $PR_ID $PR_ID +# Build and run Docker Container +docker build -t $PR_ID -f $DOCKERFILE . +echo $ENVS > ${PR_ID}.env +docker run -d --env-file ${PR_ID}.env -p $FREE_PORT:$EXPOSED_PORT --name $PR_ID $PR_ID -echo "Start SSH session..." +# Start SSH Tunnel nohup ssh -tt -o StrictHostKeyChecking=no -R 80:localhost:$FREE_PORT serveo.net > serveo_output.log 2>&1 & SERVEO_PID=$! sleep 3 - -# Check if Serveo tunnel was set up successfully DEPLOYED_URL=$(grep "Forwarding HTTP traffic from" serveo_output.log | tail -n 1 | awk '{print $5}') # update the nohup ids -if [ -n DEPLOYED_URL ]; then - # jq --arg pid "$SERVEO_PID" '.serveo = $pid' "$PID_FILE" > tmp.$$.json && mv tmp.$$.json "$PID_FILE" - jq --arg pr_id "$PR_ID" --arg pid "$SERVEO_PID" '.[$pr_id] = $pid' "$PID_FILE" > tmp.$$.json && mv tmp.$$.json "$PID_FILE" +jq --arg pr_id "$PR_ID" --arg pid "$SERVEO_PID" '.[$pr_id] = $pid' "$PID_FILE" > "${PID_FILE}.tmp" && mv "${PID_FILE}.tmp" "$PID_FILE" +if [ -z "$DEPLOYED_URL" ]; then + comment "Failed ❌" "#" && exit 1 fi - -# Output the final JSON -echo "{\"COMMENT_ID\": \"$COMMENT_ID\", \"DEPLOYED_URL\": \"$DEPLOYED_URL\"}" +comment "Deployed 🎉" $DEPLOYED_URL \ No newline at end of file