Skip to content

Instantly share code, notes, and snippets.

@aaryadev
Last active December 31, 2025 16:40
Show Gist options
  • Select an option

  • Save aaryadev/760e3835cf6e0f4f6a1b1638c7e21011 to your computer and use it in GitHub Desktop.

Select an option

Save aaryadev/760e3835cf6e0f4f6a1b1638c7e21011 to your computer and use it in GitHub Desktop.
deploy the repo using python ( for raspi with AGro tunnel, Apache peroxided deployment)
import http.server
import socketserver
import json
import subprocess
import os
import shutil
import datetime
import sys
import threading
# ==========================================
# CONFIGURATION
# ==========================================
# 1. Server Configuration
PORT = 10000
# 2. Git Configuration
# Use the SSH URL for your repository
REPO_URL = "git@yourdomain.com:username/repository.git"
# Path to your private SSH key (e.g., "/home/user/.ssh/id_rsa")
# If your key is already loaded in the ssh-agent, you can leave this as None
SSH_KEY_PATH = "/path/to/your/private_key"
# 3. Directory Configuration
TEMP_DIR = "./temp_repo_clone" # Where the repo will be cloned temporarily
DESTINATION_DIR = "./production_app" # Where the final files should go
# 4. List of Cleanup/Build Commands
# Add or remove commands here. These run inside the TEMP_DIR after cloning.
COMMANDS_TO_RUN = [
"echo 'Starting build process...'",
# "npm install", # Example: Install node dependencies
# "rm -rf .git", # Example: Remove git history
# "rm .gitignore", # Example: Remove gitignore
"echo 'Cleanup commands completed.'"
]
# ==========================================
# SCRIPT LOGIC
# ==========================================
class WebhookHandler(http.server.BaseHTTPRequestHandler):
def do_POST(self):
"""Handle incoming POST requests."""
# 1. Capture and Store JSON Data
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
try:
json_data = json.loads(post_data)
# Save to a file with a timestamp
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"payload_{timestamp}.json"
with open(filename, "w") as f:
json.dump(json_data, f, indent=4)
print(f"[INFO] Received webhook. JSON saved to {filename}")
# 2. Trigger the Deployment Process in the BACKGROUND
# We create a new thread that targets the run_deployment method
deployment_thread = threading.Thread(target=self.run_deployment)
deployment_thread.start()
# 3. Send Response Immediately
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
# This returns immediately while the thread works in the background
self.wfile.write(b'{"status": "success", "message": "Deployment triggered in background"}')
except json.JSONDecodeError:
print("[ERROR] Failed to decode JSON")
self.send_response(400)
self.end_headers()
self.wfile.write(b'{"status": "error", "message": "Invalid JSON"}')
except Exception as e:
print(f"[ERROR] {str(e)}")
self.send_response(500)
self.end_headers()
self.wfile.write(f'{{"status": "error", "message": "{str(e)}"}}'.encode())
def run_deployment(self):
"""Orchestrates the clone, clean, and copy process."""
print("--- Starting Deployment ---")
# A. Setup SSH Environment
git_env = os.environ.copy()
if SSH_KEY_PATH and os.path.exists(SSH_KEY_PATH):
# This tells Git to use a specific SSH key and disable strict host checking (optional but helpful for scripts)
git_env["GIT_SSH_COMMAND"] = f"ssh -i {SSH_KEY_PATH} -o StrictHostKeyChecking=no"
# B. Clean previous temp directory if it exists
if os.path.exists(TEMP_DIR):
shutil.rmtree(TEMP_DIR)
# C. Clone the Repository
print(f"[CMD] Cloning {REPO_URL} into {TEMP_DIR}...")
try:
subprocess.run(
["git", "clone", REPO_URL, TEMP_DIR],
check=True,
env=git_env
)
except subprocess.CalledProcessError as e:
print(f"[FAIL] Git clone failed: {e}")
return
# D. Run User Defined Commands
print("[CMD] Running cleanup/build commands...")
for cmd in COMMANDS_TO_RUN:
try:
# shell=True allows running complex shell commands strings
subprocess.run(cmd, shell=True, check=True, cwd=TEMP_DIR)
except subprocess.CalledProcessError as e:
print(f"[FAIL] Command '{cmd}' failed. Stopping deployment.")
return
# E. Copy Only Modified Files (using rsync)
# rsync is preferred over python's shutil because it handles 'only modified' logic perfectly.
# -a: archive mode (preserves permissions, times, etc.)
# -v: verbose
# --update: skip files that are newer on the receiver
print(f"[CMD] Syncing files to {DESTINATION_DIR}...")
# Ensure destination exists
if not os.path.exists(DESTINATION_DIR):
os.makedirs(DESTINATION_DIR)
try:
# Note: Putting a slash at the end of source (TEMP_DIR/) is important in rsync
# to copy contents, not the folder itself.
rsync_cmd = ["rsync", "-av", "--update", f"{TEMP_DIR}/", DESTINATION_DIR]
subprocess.run(rsync_cmd, check=True)
print("[SUCCESS] Deployment finished successfully.")
except subprocess.CalledProcessError as e:
print(f"[FAIL] Rsync failed: {e}")
# ==========================================
# MAIN ENTRY POINT
# ==========================================
if __name__ == "__main__":
try:
with socketserver.TCPServer(("", PORT), WebhookHandler) as httpd:
print(f"Serving on port {PORT}...")
print(f"Configured to clone from: {REPO_URL}")
print("Press Ctrl+C to stop.")
httpd.serve_forever()
except KeyboardInterrupt:
print("\nServer stopped.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment