chore: standardize security and quality foundations
Magent CI/CD / verify (push) Failing after 9m34s
Magent CI/CD / deploy-beta (push) Skipped

This commit is contained in:
2026-09-17 20:03:47 +12:00
parent 5639dbcb83
commit f852e7c941
127 changed files with 17928 additions and 10741 deletions
+12 -4
View File
@@ -6,13 +6,21 @@ cd "$repo_root"
python_bin="${PYTHON_BIN:-python3}"
echo "Installing backend Python requirements"
"$python_bin" -m pip install -r backend/requirements.txt
echo "Installing backend Python requirements and quality tools"
"$python_bin" -m pip install -r backend/requirements-dev.txt
echo "Running Python dependency integrity check"
"$python_bin" -m pip check
echo "Running backend unit tests"
"$python_bin" -m unittest discover -s backend/tests -p "test_*.py" -v
echo "Auditing Python production dependencies"
"$python_bin" -m pip_audit -r backend/requirements.txt --progress-spinner off
echo "Linting backend application code"
"$python_bin" -m ruff check backend/app
echo "Running backend unit tests with coverage"
"$python_bin" -m coverage erase
"$python_bin" -m coverage run -m unittest discover -s backend/tests -p "test_*.py" -v
"$python_bin" -m coverage report
echo "Backend quality gate passed"
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail
container_name="magent-ci-${GITHUB_RUN_ID:-local}-$$"
data_dir="$(mktemp -d)"
chmod 0777 "$data_dir"
cleanup() {
docker rm -f "$container_name" >/dev/null 2>&1 || true
rm -rf "$data_dir"
}
trap cleanup EXIT
docker build --tag magent:ci .
docker run --detach --name "$container_name" \
--read-only --cap-drop ALL --security-opt no-new-privileges:true \
--tmpfs /tmp:rw,noexec,nosuid,size=64m,uid=1000,gid=1000 \
--tmpfs /app/frontend/.next/cache:rw,noexec,nosuid,size=128m,uid=1000,gid=1000 \
--volume "$data_dir:/app/data" \
--env JWT_SECRET=ci-only-secret-with-at-least-32-characters \
--env SETTINGS_ENCRYPTION_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= \
--env ADMIN_PASSWORD=ci-only-bootstrap-password-123 \
magent:ci >/dev/null
deadline=$((SECONDS + 120))
until [ "$(docker inspect --format '{{.State.Health.Status}}' "$container_name")" = "healthy" ]; do
if [ "$SECONDS" -ge "$deadline" ]; then
docker logs "$container_name"
echo "Container did not become healthy within 120 seconds" >&2
exit 1
fi
sleep 2
done
docker exec "$container_name" curl --fail --silent --show-error http://127.0.0.1:8000/health >/dev/null
docker exec "$container_name" curl --fail --silent --show-error http://127.0.0.1:3000/login >/dev/null
+4 -10
View File
@@ -6,7 +6,6 @@ cd "$repo_root"
deploy_host="${DEPLOY_HOST:-AMS-DEV01}"
deploy_user="${DEPLOY_USER:-zak}"
prod_path="${PROD_DEPLOY_PATH:-/home/${deploy_user}/magent}"
deploy_path="${BETA_DEPLOY_PATH:-/home/${deploy_user}/magent-beta}"
beta_frontend_bind="${BETA_FRONTEND_BIND:-10.30.1.32}"
ssh_opts="${DEPLOY_SSH_OPTS:-"-o StrictHostKeyChecking=yes"}"
@@ -32,19 +31,14 @@ git archive --format=tar HEAD | ssh ${ssh_opts} "${remote}" "
done
tar -xf - -C '${deploy_path}'
if [ ! -f '${deploy_path}/.env' ] && [ -f '${prod_path}/.env' ]; then
cp '${prod_path}/.env' '${deploy_path}/.env'
fi
if [ -f '${deploy_path}/.env' ]; then
chmod 600 '${deploy_path}/.env'
if [ ! -f '${deploy_path}/.env' ]; then
echo 'Beta .env is missing. Provision independent beta secrets before deploying.' >&2
exit 1
fi
chmod 600 '${deploy_path}/.env'
mkdir -p '${deploy_path}/data'
chmod 700 '${deploy_path}/data'
if [ ! -f '${deploy_path}/data/magent.db' ] && [ -d '${prod_path}/data' ]; then
cp -a '${prod_path}/data/.' '${deploy_path}/data/'
fi
cd '${deploy_path}'
docker compose -p magent-beta -f docker-compose.beta.yml build
if ! grep -Eq '^[[:space:]]*SETTINGS_ENCRYPTION_KEY=' .env; then
+41
View File
@@ -0,0 +1,41 @@
"""Fail when generated build metadata has drifted from .build_number."""
from __future__ import annotations
import ast
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def backend_build_number() -> str:
tree = ast.parse((ROOT / "backend/app/build_info.py").read_text(encoding="utf-8-sig"))
for node in tree.body:
if isinstance(node, ast.Assign) and any(
isinstance(target, ast.Name) and target.id == "BUILD_NUMBER" for target in node.targets
):
return str(ast.literal_eval(node.value))
raise RuntimeError("backend/app/build_info.py does not define BUILD_NUMBER")
def main() -> None:
expected = (ROOT / ".build_number").read_text(encoding="utf-8-sig").strip()
package = json.loads((ROOT / "frontend/package.json").read_text(encoding="utf-8"))
package_lock = json.loads((ROOT / "frontend/package-lock.json").read_text(encoding="utf-8"))
values = {
"backend/app/build_info.py": backend_build_number(),
"frontend/package.json": str(package.get("version") or ""),
"frontend/package-lock.json": str(package_lock.get("version") or ""),
"frontend/package-lock.json root package": str(package_lock.get("packages", {}).get("", {}).get("version") or ""),
}
drifted = {name: value for name, value in values.items() if value != expected}
if drifted:
details = ", ".join(f"{name}={value!r}" for name, value in drifted.items())
raise SystemExit(f"Build metadata differs from .build_number ({expected!r}): {details}")
print(f"Build metadata is synchronized: {expected}")
if __name__ == "__main__":
main()