Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions modules/pastebin_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import requests

def create_paste(
*,
developer_key: str,
content: str,
timeout_seconds: float = 15.0,
) -> str:
# Make sure the API key is not empty
if not developer_key.strip():
raise ValueError("Pastebin developer key cannot be empty")

# Make sure there is actually something to upload
if not content.strip():
raise ValueError("Paste content cannot be empty")

# Data Pastebin expects
payload = {
"api_dev_key": developer_key.strip(),
"api_option": "paste",
"api_paste_code": content,
}

# Send the POST request to Pastebin
response = requests.post(
"https://pastebin.com/api/api_post.php",
data=payload,
timeout=timeout_seconds,
)

response.raise_for_status()

# Pastebin returns either:
# - a Pastebin URL on success
# - text starting with "Bad API request" on failure
response_body = response.text.strip()

if response_body.startswith("Bad API request"):
raise RuntimeError(
f"Pastebin rejected the request: {response_body}"
)

return response_body
48 changes: 43 additions & 5 deletions server.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from fastapi import BackgroundTasks, FastAPI, Request, Response
from fastapi.middleware.cors import CORSMiddleware
from prometheus_client import generate_latest
from dotenv import load_dotenv
import requests
import uvicorn
import websocket
Expand All @@ -24,7 +25,10 @@
from metrics import MetricsHandler
from modules.args import get_args
from modules.commit_status_helpers import CommitStatus, push_commit_status
from modules.pastebin_helpers import create_paste

load_dotenv()
PASTEBIN_DEV_API_KEY = os.getenv("PASTEBIN_DEV_API_KEY")

args = get_args()
logging.basicConfig(
Expand Down Expand Up @@ -57,7 +61,6 @@
CICD_DISCORD_WEBHOOK_URL = None
GITHUB_TOKEN = None


@dataclasses.dataclass
class RepoConfig:
name: str
Expand Down Expand Up @@ -141,6 +144,20 @@ def run_command(command_args: list, cwd: str) -> ExecutionResult:
logger.exception(f"Failed to execute {cmd_str}")
return ExecutionResult(command=cmd_str)

def build_execution_log(
step_title: str,
execution_result: ExecutionResult,
) -> str:
return (
f"Step: {step_title}\n"
f"Command: {execution_result.command}\n"
f"Exit Code: {execution_result.exit_code}\n"
f"Success: {execution_result.success}\n"
f"\nSTDOUT:\n"
f"{execution_result.stdout or '(empty)'}\n"
f"\nSTDERR:\n"
f"{execution_result.stderr or '(empty)'}\n"
)

def push_github_commit_status(status: DeploymentStatus):
if status.is_dev:
Expand All @@ -165,8 +182,29 @@ def push_github_commit_status(status: DeploymentStatus):

for step_title, status_field_name in execution_results:
execution_result = getattr(status, status_field_name, None)
deployment_failed = execution_result is not None and not execution_result.success

if execution_result is None:
continue

deployment_failed = not execution_result.success

paste_url = None

if not PASTEBIN_DEV_API_KEY:
logger.warning("Pastebin API key missing")
else:
try:
paste_url = create_paste(
developer_key = PASTEBIN_DEV_API_KEY,
content = build_execution_log(
step_title,
execution_result,
),
)
logger.info(f"{step_title} logs uploaded to Pastebin")
except Exception:
logger.exception(
f"Failed to upload {step_title} logs to Pastebin"
)

commit_status = CommitStatus(
state = "failure" if deployment_failed else "success",
Expand All @@ -176,6 +214,7 @@ def push_github_commit_status(status: DeploymentStatus):
else "Deployment successful"
),
context = f"[sce-cicd] {step_title}",
target_url = paste_url,
)

try:
Expand Down Expand Up @@ -461,7 +500,7 @@ def should_skip_deployment(files_changed: List[str], ignore_patterns: List[str])
SMEE2_URL = data.get("smee2_url")
SMEE2_API_KEY = data.get("smee2_api_key")
CICD_DISCORD_WEBHOOK_URL = data.get("cicd_discord_webhook_url")
GITHUB_TOKEN = data.get("github_token")
GITHUB_TOKEN = data.get("github_token") or os.getenv("GITHUB_TOKEN")
for r in raw_repos:
# make a new entry into the result dictionary
# the key is a tuple of the repo name and branch
Expand Down Expand Up @@ -697,6 +736,5 @@ def smee_listen():
get_docker_images_disk_usage_bytes()
smee_listen()


if __name__ == "__main__":
uvicorn.run("server:app", port=args.port, reload=True)