Refactor Dockerfile for scraper, update Traefik configuration, and clean up scripts
- Simplified Dockerfile for scraper by removing unnecessary Node.js installation and optimizing build stages. - Updated Traefik environment configuration to use shorter variable names for certificate paths. - Removed commented-out middleware configurations in dynamic middlewares.yaml. - Cleaned up legacy SSL configurations in ssl.yaml. - Disabled insecure API access in Traefik configuration. - Deleted unused renovate.json file. - Modified update environment script to only process apps directory. - Updated GHCR cleanup script to target specific package. - Streamlined dependency update script to focus on app submodules. - Removed setup script for Prometheus as it is no longer needed.
This commit is contained in:
@@ -142,8 +142,6 @@ jobs:
|
|||||||
TARGET_COMPOSE=""
|
TARGET_COMPOSE=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Submodule update removed for faster VPS deployments
|
|
||||||
|
|
||||||
if command -v "docker" >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
|
if command -v "docker" >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
|
||||||
COMPOSE_CMD="docker compose"
|
COMPOSE_CMD="docker compose"
|
||||||
elif command -v docker-compose >/dev/null 2>&1; then
|
elif command -v docker-compose >/dev/null 2>&1; then
|
||||||
@@ -154,8 +152,6 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Construct compose arguments
|
# Construct compose arguments
|
||||||
docker rm -f imphenbot-app || true
|
|
||||||
|
|
||||||
if [ -n "$TARGET_COMPOSE" ]; then
|
if [ -n "$TARGET_COMPOSE" ]; then
|
||||||
COMPOSE_ARGS=""
|
COMPOSE_ARGS=""
|
||||||
for f in $TARGET_COMPOSE; do
|
for f in $TARGET_COMPOSE; do
|
||||||
@@ -196,10 +192,6 @@ jobs:
|
|||||||
echo "🧹 Clearing Git locks..."
|
echo "🧹 Clearing Git locks..."
|
||||||
rm -f .git/shallow.lock || true
|
rm -f .git/shallow.lock || true
|
||||||
|
|
||||||
# Cooldown to allow daemon to settle (optional but kept for safety)
|
|
||||||
# echo "⏳ Waiting for Docker daemon to settle..."
|
|
||||||
# sleep 2
|
|
||||||
|
|
||||||
echo "🧹 Removing stale target containers by container_name..."
|
echo "🧹 Removing stale target containers by container_name..."
|
||||||
# Extract all explicitly defined container_names from compose files and remove them to prevent conflicts
|
# Extract all explicitly defined container_names from compose files and remove them to prevent conflicts
|
||||||
if [ -n "$TARGET_COMPOSE" ]; then
|
if [ -n "$TARGET_COMPOSE" ]; then
|
||||||
@@ -235,20 +227,3 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# - name: Restart coolify-proxy (delay 1 min)
|
|
||||||
# env:
|
|
||||||
# SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
# VPS_HOST: ${{ secrets.VPS_HOST }}
|
|
||||||
# VPS_USER: ${{ secrets.VPS_USER }}
|
|
||||||
# run: |
|
|
||||||
# mkdir -p ~/.ssh
|
|
||||||
# echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
|
|
||||||
# chmod 600 ~/.ssh/id_rsa
|
|
||||||
# ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
|
|
||||||
#
|
|
||||||
# echo "⏳ Waiting 60s for containers to settle before restarting proxy..."
|
|
||||||
# sleep 60
|
|
||||||
#
|
|
||||||
# echo "🔄 Restarting coolify-proxy..."
|
|
||||||
# ssh "${VPS_USER}@${VPS_HOST}" "docker restart coolify-proxy"
|
|
||||||
# echo "✅ coolify-proxy restarted."
|
|
||||||
|
|||||||
@@ -5,15 +5,15 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'apps/*/src/**/*.ts'
|
|
||||||
- 'apps/*/src/**/*.tsx'
|
|
||||||
- 'biome.json'
|
- 'biome.json'
|
||||||
|
- '*.json'
|
||||||
|
- '*.js'
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'apps/*/src/**/*.ts'
|
|
||||||
- 'apps/*/src/**/*.tsx'
|
|
||||||
- 'biome.json'
|
- 'biome.json'
|
||||||
|
- '*.json'
|
||||||
|
- '*.js'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
biome:
|
biome:
|
||||||
@@ -23,8 +23,6 @@ jobs:
|
|||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- uses: actions/setup-node@v4
|
- uses: oven-sh/setup-bun@v2
|
||||||
with:
|
- run: bun install --frozen-lockfile
|
||||||
node-version: 22
|
- run: bun run ci
|
||||||
- run: npm install -g @biomejs/biome
|
|
||||||
- run: biome ci . --no-errors-on-unmatched
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
apps/gmw/
|
|
||||||
# See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
# See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||||
|
|
||||||
# compiled output
|
# compiled output
|
||||||
|
|||||||
@@ -12,8 +12,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Restructured the repository into a lightweight hub repo with standalone app submodules.
|
- Restructured the repository into a lightweight hub repo with standalone app submodules.
|
||||||
- Simplified root tooling to plain `package.json` scripts and per-service commands.
|
- Simplified root tooling to plain `package.json` scripts and per-service commands.
|
||||||
- Kept infrastructure, deployment workflows, and documentation in the root hub repo.
|
- Kept infrastructure, deployment workflows, and documentation in the root hub repo.
|
||||||
|
- Cleaned up Traefik SSL config: removed legacy `asephstech`/`asephscloud` cert references, synced volume mounts, fixed `api.insecure`.
|
||||||
|
- Pruned `.env.example` from 145 legacy vars (Firebase, Discord, Coolify, Portainer, YouTube, etc.) to 30 focused vars.
|
||||||
|
- Optimized `scraper.Dockerfile`: removed Node.js and Chromium from runtime image.
|
||||||
|
- Simplified CI/CD workflows: removed orphan container reference, commented code blocks.
|
||||||
|
- Cleaned up scripts: removed stale MySQL config, fixed package references, simplified update-deps.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Removed deprecated services from apps, compose files, Dockerfiles, Traefik routes, and workflows.
|
- Removed deprecated services from apps, compose files, Dockerfiles, Traefik routes, and workflows.
|
||||||
- Removed stale monorepo orchestration configs and hook tooling from the root repo.
|
- Removed stale monorepo orchestration configs and hook tooling from the root repo.
|
||||||
|
- Removed `.nvmrc` (duplicate of `.node-version`), `renovate.json` (using dependabot).
|
||||||
|
- Removed stale documentation: `dependency-map.md`, `observability.md`, `handoff-log.jsonl`, `quality-gates.json`, `workflow-state.json`.
|
||||||
|
- Removed deprecated `docs/superpowers/` design docs.
|
||||||
|
- Removed `infra/config/mysql/` (no active MySQL service) and `docs/config/squid.conf.archived`.
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@
|
|||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- **Git** with LFS support
|
- **Git** with LFS support
|
||||||
- **Node.js** >= 22.11.0 (via `.node-version` or `.nvmrc`)
|
- **Node.js** >= 22.11.0 (via `.node-version`)
|
||||||
- **Bun** >= 1.3.11 (package manager)
|
- **Bun** >= 1.3.11 (package manager)
|
||||||
- **Docker** and **Docker Compose** (for shared infrastructure)
|
- **Docker** and **Docker Compose** (for shared infrastructure)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: help dev lint format test clean update-submodules deploy init-submodules status
|
.PHONY: help dev update-submodules deploy init-submodules status
|
||||||
|
|
||||||
SHELL := /bin/bash
|
SHELL := /bin/bash
|
||||||
|
|
||||||
@@ -8,16 +8,6 @@ help: ## Show this help
|
|||||||
dev: ## Start development infrastructure (Redis etc.)
|
dev: ## Start development infrastructure (Redis etc.)
|
||||||
docker compose -f infra/compose/shared.yml up -d
|
docker compose -f infra/compose/shared.yml up -d
|
||||||
|
|
||||||
lint: ## Run Biome linter
|
|
||||||
biome lint .
|
|
||||||
|
|
||||||
format: ## Format code with Biome
|
|
||||||
biome format --write .
|
|
||||||
|
|
||||||
clean: ## Clean build artifacts
|
|
||||||
rm -rf apps/*/dist apps/*/.next apps/*/target 2>/dev/null || true
|
|
||||||
rm -rf node_modules 2>/dev/null || true
|
|
||||||
|
|
||||||
update-submodules: ## Update all git submodules to latest remote
|
update-submodules: ## Update all git submodules to latest remote
|
||||||
git submodule update --remote --merge --recursive
|
git submodule update --remote --merge --recursive
|
||||||
|
|
||||||
|
|||||||
@@ -176,6 +176,3 @@ Panduan langkah demi langkah untuk menambahkan aplikasi baru ada di `docs/add-ne
|
|||||||
## License
|
## License
|
||||||
|
|
||||||
MIT
|
MIT
|
||||||
|
|
||||||
# PR-Agent V3 Test
|
|
||||||
Testing with corrected model prefix.
|
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
# Squid Proxy Configuration (Archived)
|
|
||||||
|
|
||||||
**Status**: Archived; not referenced by active infrastructure.
|
|
||||||
|
|
||||||
**Date archived**: 2026-06-04
|
|
||||||
|
|
||||||
**Original location**: `infra/squid.conf`
|
|
||||||
|
|
||||||
**Reason**: No active Docker Compose service mounts or starts Squid. The config is preserved here for reference if Squid proxying is restored later.
|
|
||||||
|
|
||||||
## Original configuration
|
|
||||||
|
|
||||||
```squid
|
|
||||||
# Allow localhost access only
|
|
||||||
acl localhost src 127.0.0.1/32 ::1/128
|
|
||||||
http_access allow localhost
|
|
||||||
|
|
||||||
# Allow Docker bridge subnets (Adjusted for our expected subnets)
|
|
||||||
acl docker_bridge src 172.16.0.0/12
|
|
||||||
http_access allow docker_bridge
|
|
||||||
|
|
||||||
# Deny all other access
|
|
||||||
http_access deny all
|
|
||||||
|
|
||||||
# Standard port - Explicitly bind to all interfaces
|
|
||||||
http_port 0.0.0.0:3128
|
|
||||||
|
|
||||||
# Hide source info
|
|
||||||
forwarded_for off
|
|
||||||
via off
|
|
||||||
```
|
|
||||||
|
|
||||||
## Restore checklist
|
|
||||||
|
|
||||||
1. Copy this config back to `infra/squid.conf`.
|
|
||||||
2. Add a Squid service to an active compose file.
|
|
||||||
3. Mount the config into the Squid container.
|
|
||||||
4. Document which services should use the proxy.
|
|
||||||
5. Validate network exposure and authentication before production use.
|
|
||||||
@@ -1,686 +0,0 @@
|
|||||||
# Moonrepo Migration Implementation Plan
|
|
||||||
|
|
||||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
||||||
|
|
||||||
**Goal:** Migrate the monorepo to moonrepo for centralized task orchestration, caching, and consistent dependency management across all 7 apps.
|
|
||||||
|
|
||||||
**Architecture:** moonrepo sits as a task orchestration layer above the existing Nix build system. `.moon/tasks/` holds shared task definitions (TypeScript and Rust). Each app gets a `moon.yml` with tags and inter-project dependencies. Nix, Docker Compose, git submodules, and infra remain untouched.
|
|
||||||
|
|
||||||
**Tech Stack:** moonrepo CLI, Node 22, Bun, TypeScript 5+, Cargo (Rust)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 1: Install moonrepo CLI and scaffold .moon/ directory
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Create: `.moon/workspace.yml`
|
|
||||||
- Create: `.moon/toolchain.yml`
|
|
||||||
- Create: `.moon/tasks/` (directory)
|
|
||||||
|
|
||||||
- [ ] **Step 1: Install moonrepo CLI via curl**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://moonrepo.dev/install/moon.sh | bash
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Verify installation**
|
|
||||||
|
|
||||||
Run: `moon --version`
|
|
||||||
Expected: prints version number (e.g., `moon 1.x.x`)
|
|
||||||
|
|
||||||
- [ ] **Step 3: Create .moon/ scaffold directories**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p .moon/tasks
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 4: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .moon/
|
|
||||||
git commit -m "chore: scaffold .moon/ directory for moonrepo"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 2: Configure workspace.yml
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Create: `.moon/workspace.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Write .moon/workspace.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/workspace
|
|
||||||
$schema: "https://moonrepo.dev/schemas/workspace.json"
|
|
||||||
|
|
||||||
projects:
|
|
||||||
- "apps/*"
|
|
||||||
|
|
||||||
vcs:
|
|
||||||
manager: "git"
|
|
||||||
defaultBranch: "main"
|
|
||||||
|
|
||||||
runner:
|
|
||||||
implicitDeps:
|
|
||||||
# TypeScript apps: lint depends on build (for typecheck path)
|
|
||||||
- "typescript-build.build"
|
|
||||||
|
|
||||||
cacheTtl: 604800
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Validate config structure**
|
|
||||||
|
|
||||||
Run: `moon check`
|
|
||||||
Expected: no errors (will warn about missing project configs — expected)
|
|
||||||
|
|
||||||
- [ ] **Step 3: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .moon/workspace.yml
|
|
||||||
git commit -m "chore: configure moonrepo workspace with project glob"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 3: Configure toolchain.yml
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Create: `.moon/toolchain.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Write .moon/toolchain.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/toolchain
|
|
||||||
$schema: "https://moonrepo.dev/schemas/toolchain.json"
|
|
||||||
|
|
||||||
node:
|
|
||||||
version: "22.11.0"
|
|
||||||
packageManager: "bun"
|
|
||||||
bun:
|
|
||||||
version: "1.3.11"
|
|
||||||
|
|
||||||
typescript:
|
|
||||||
syncProjectReferences: true
|
|
||||||
createMissingConfig: false
|
|
||||||
routeOutDirToCache: false
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .moon/toolchain.yml
|
|
||||||
git commit -m "chore: configure moonrepo toolchain (Node 22, Bun 1.3)"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 4: Create shared TypeScript task definitions
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Create: `.moon/tasks/typescript-build.yml`
|
|
||||||
- Create: `.moon/tasks/typescript-lint.yml`
|
|
||||||
- Create: `.moon/tasks/typescript-test.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Write .moon/tasks/typescript-build.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/tasks
|
|
||||||
$schema: "https://moonrepo.dev/schemas/tasks.json"
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
build:
|
|
||||||
command: "bun run build"
|
|
||||||
inputs:
|
|
||||||
- "src/**/*"
|
|
||||||
- "tsconfig.json"
|
|
||||||
- "package.json"
|
|
||||||
outputs:
|
|
||||||
- ".next"
|
|
||||||
- "dist"
|
|
||||||
- ".output"
|
|
||||||
options:
|
|
||||||
cache: true
|
|
||||||
dev:
|
|
||||||
command: "bun run dev"
|
|
||||||
local: true
|
|
||||||
options:
|
|
||||||
persistent: true
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Write .moon/tasks/typescript-lint.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/tasks
|
|
||||||
$schema: "https://moonrepo.dev/schemas/tasks.json"
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
lint:
|
|
||||||
command: "bun run lint"
|
|
||||||
inputs:
|
|
||||||
- "src/**/*"
|
|
||||||
- "eslint.config.mjs"
|
|
||||||
- "tsconfig.json"
|
|
||||||
options:
|
|
||||||
cache: false
|
|
||||||
typecheck:
|
|
||||||
command: "bun run check-types"
|
|
||||||
inputs:
|
|
||||||
- "src/**/*"
|
|
||||||
- "tsconfig.json"
|
|
||||||
options:
|
|
||||||
cache: false
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Write .moon/tasks/typescript-test.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/tasks
|
|
||||||
$schema: "https://moonrepo.dev/schemas/tasks.json"
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
test:
|
|
||||||
command: "bun test"
|
|
||||||
inputs:
|
|
||||||
- "src/**/*"
|
|
||||||
- "test/**/*"
|
|
||||||
- "tests/**/*"
|
|
||||||
- "vitest.config.ts"
|
|
||||||
options:
|
|
||||||
cache: false
|
|
||||||
e2e:
|
|
||||||
command: "noop"
|
|
||||||
local: true
|
|
||||||
options:
|
|
||||||
cache: false
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 4: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .moon/tasks/typescript-build.yml .moon/tasks/typescript-lint.yml .moon/tasks/typescript-test.yml
|
|
||||||
git commit -m "chore: add shared TypeScript task definitions"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 5: Create shared Rust task definitions
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Create: `.moon/tasks/rust-build.yml`
|
|
||||||
- Create: `.moon/tasks/rust-test.yml`
|
|
||||||
- Create: `.moon/tasks/rust-lint.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Write .moon/tasks/rust-build.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/tasks
|
|
||||||
$schema: "https://moonrepo.dev/schemas/tasks.json"
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
build:
|
|
||||||
command: "cargo build --release"
|
|
||||||
platform: system
|
|
||||||
inputs:
|
|
||||||
- "src/**/*"
|
|
||||||
- "Cargo.toml"
|
|
||||||
- "Cargo.lock"
|
|
||||||
- "build.rs"
|
|
||||||
outputs:
|
|
||||||
- "target/release/*"
|
|
||||||
options:
|
|
||||||
cache: true
|
|
||||||
envFile: false
|
|
||||||
dev:
|
|
||||||
command: "cargo run"
|
|
||||||
platform: system
|
|
||||||
local: true
|
|
||||||
options:
|
|
||||||
persistent: true
|
|
||||||
envFile: false
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Write .moon/tasks/rust-test.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/tasks
|
|
||||||
$schema: "https://moonrepo.dev/schemas/tasks.json"
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
test:
|
|
||||||
command: "cargo test"
|
|
||||||
platform: system
|
|
||||||
inputs:
|
|
||||||
- "src/**/*"
|
|
||||||
- "Cargo.toml"
|
|
||||||
- "Cargo.lock"
|
|
||||||
- "tests/**/*"
|
|
||||||
options:
|
|
||||||
cache: false
|
|
||||||
envFile: false
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Write .moon/tasks/rust-lint.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/tasks
|
|
||||||
$schema: "https://moonrepo.dev/schemas/tasks.json"
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
lint:
|
|
||||||
command: "cargo clippy -- -D warnings"
|
|
||||||
platform: system
|
|
||||||
inputs:
|
|
||||||
- "src/**/*"
|
|
||||||
- "Cargo.toml"
|
|
||||||
options:
|
|
||||||
cache: false
|
|
||||||
envFile: false
|
|
||||||
fmt-check:
|
|
||||||
command: "cargo fmt --check"
|
|
||||||
platform: system
|
|
||||||
inputs:
|
|
||||||
- "src/**/*"
|
|
||||||
options:
|
|
||||||
cache: false
|
|
||||||
envFile: false
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 4: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .moon/tasks/rust-build.yml .moon/tasks/rust-test.yml .moon/tasks/rust-lint.yml
|
|
||||||
git commit -m "chore: add shared Rust task definitions"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 6: Create per-app moon.yml for TypeScript apps
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Create: `apps/nextjs/moon.yml`
|
|
||||||
- Create: `apps/elysia/moon.yml`
|
|
||||||
- Create: `apps/solidjs/moon.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Write apps/nextjs/moon.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/project
|
|
||||||
$schema: "https://moonrepo.dev/schemas/project.json"
|
|
||||||
|
|
||||||
type: "application"
|
|
||||||
language: "typescript"
|
|
||||||
platform: "node"
|
|
||||||
tags:
|
|
||||||
- "lang:typescript"
|
|
||||||
- "type:frontend"
|
|
||||||
|
|
||||||
dependsOn:
|
|
||||||
- id: "rust-auth"
|
|
||||||
- id: "elysia"
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Write apps/elysia/moon.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/project
|
|
||||||
$schema: "https://moonrepo.dev/schemas/project.json"
|
|
||||||
|
|
||||||
type: "application"
|
|
||||||
language: "typescript"
|
|
||||||
platform: "bun"
|
|
||||||
tags:
|
|
||||||
- "lang:typescript"
|
|
||||||
- "type:backend"
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Write apps/solidjs/moon.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/project
|
|
||||||
$schema: "https://moonrepo.dev/schemas/project.json"
|
|
||||||
|
|
||||||
type: "application"
|
|
||||||
language: "typescript"
|
|
||||||
platform: "bun"
|
|
||||||
tags:
|
|
||||||
- "lang:typescript"
|
|
||||||
- "type:frontend"
|
|
||||||
|
|
||||||
dependsOn:
|
|
||||||
- id: "elysia"
|
|
||||||
- id: "rust-auth"
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 4: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add apps/nextjs/moon.yml apps/elysia/moon.yml apps/solidjs/moon.yml
|
|
||||||
git commit -m "chore: add moon.yml for TypeScript apps (nextjs, elysia, solidjs)"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 7: Create per-app moon.yml for Rust apps
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Create: `apps/rust/moon.yml`
|
|
||||||
- Create: `apps/rust-auth/moon.yml`
|
|
||||||
- Create: `apps/leptos/moon.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Write apps/rust/moon.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/project
|
|
||||||
$schema: "https://moonrepo.dev/schemas/project.json"
|
|
||||||
|
|
||||||
type: "application"
|
|
||||||
language: "rust"
|
|
||||||
platform: "system"
|
|
||||||
tags:
|
|
||||||
- "lang:rust"
|
|
||||||
- "type:backend"
|
|
||||||
|
|
||||||
fileGroups:
|
|
||||||
sources:
|
|
||||||
- "src/**/*.rs"
|
|
||||||
- "Cargo.toml"
|
|
||||||
- "Cargo.lock"
|
|
||||||
- "build.rs"
|
|
||||||
- "rustfmt.toml"
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Write apps/rust-auth/moon.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/project
|
|
||||||
$schema: "https://moonrepo.dev/schemas/project.json"
|
|
||||||
|
|
||||||
type: "application"
|
|
||||||
language: "rust"
|
|
||||||
platform: "system"
|
|
||||||
tags:
|
|
||||||
- "lang:rust"
|
|
||||||
- "type:backend"
|
|
||||||
|
|
||||||
fileGroups:
|
|
||||||
sources:
|
|
||||||
- "src/**/*.rs"
|
|
||||||
- "Cargo.toml"
|
|
||||||
- "Cargo.lock"
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Write apps/leptos/moon.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/project
|
|
||||||
$schema: "https://moonrepo.dev/schemas/project.json"
|
|
||||||
|
|
||||||
type: "application"
|
|
||||||
language: "rust"
|
|
||||||
platform: "system"
|
|
||||||
tags:
|
|
||||||
- "lang:rust"
|
|
||||||
- "type:frontend"
|
|
||||||
|
|
||||||
dependsOn:
|
|
||||||
- id: "rust"
|
|
||||||
|
|
||||||
fileGroups:
|
|
||||||
sources:
|
|
||||||
- "src/**/*.rs"
|
|
||||||
- "Cargo.toml"
|
|
||||||
- "Cargo.lock"
|
|
||||||
- "Trunk.toml"
|
|
||||||
- "rust-toolchain.toml"
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 4: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add apps/rust/moon.yml apps/rust-auth/moon.yml apps/leptos/moon.yml
|
|
||||||
git commit -m "chore: add moon.yml for Rust apps (rust, rust-auth, leptos)"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 8: Create moon.yml for 9router
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Create: `apps/9router/moon.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Write apps/9router/moon.yml**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# https://moonrepo.dev/docs/config/project
|
|
||||||
$schema: "https://moonrepo.dev/schemas/project.json"
|
|
||||||
|
|
||||||
type: "application"
|
|
||||||
language: "unknown"
|
|
||||||
platform: "system"
|
|
||||||
tags:
|
|
||||||
- "type:router"
|
|
||||||
|
|
||||||
fileGroups:
|
|
||||||
sources:
|
|
||||||
- "src/**/*"
|
|
||||||
- "next.config.mjs"
|
|
||||||
- "package.json"
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add apps/9router/moon.yml
|
|
||||||
git commit -m "chore: add moon.yml for 9router"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 9: Update flake.nix — add moon CLI to devShell
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `flake.nix`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Add moon to nativeBuildInputs in flake.nix**
|
|
||||||
|
|
||||||
Find the `devShells.default` block in `flake.nix`. Add `moon` to `nativeBuildInputs`:
|
|
||||||
|
|
||||||
```
|
|
||||||
devShells.default = pkgs.mkShell {
|
|
||||||
name = "ultimate-asepharyana-dev";
|
|
||||||
nativeBuildInputs = with pkgs; [
|
|
||||||
rustToolchain
|
|
||||||
bun
|
|
||||||
nodejs_22
|
|
||||||
pkg-config
|
|
||||||
openssl
|
|
||||||
trunk
|
|
||||||
wasm-bindgen-cli
|
|
||||||
binaryen
|
|
||||||
process-compose
|
|
||||||
mysql84
|
|
||||||
redis
|
|
||||||
minio-client
|
|
||||||
gh
|
|
||||||
git
|
|
||||||
moon # <-- add this line
|
|
||||||
];
|
|
||||||
# ... shellHook unchanged
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Verify Nix can still evaluate the flake**
|
|
||||||
|
|
||||||
Run: `nix flake check --no-build 2>&1 | head -20`
|
|
||||||
Expected: no evaluation errors
|
|
||||||
|
|
||||||
- [ ] **Step 3: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add flake.nix
|
|
||||||
git commit -m "chore: add moon CLI to Nix devShell"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 10: Update .gitignore
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `.gitignore`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Add moonrepo cache entries to .gitignore**
|
|
||||||
|
|
||||||
Append to `.gitignore`:
|
|
||||||
|
|
||||||
```gitignore
|
|
||||||
# moonrepo
|
|
||||||
.moon/cache
|
|
||||||
.~moon
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .gitignore
|
|
||||||
git commit -m "chore: add moonrepo cache entries to .gitignore"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 11: Validate installation with moon check
|
|
||||||
|
|
||||||
**Files:** (none — validation only)
|
|
||||||
|
|
||||||
- [ ] **Step 1: Run moon check**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon check
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: `OK` or zero errors. If warnings about unresolved project IDs appear, verify that `apps/*` glob in `workspace.yml` matches all project directories.
|
|
||||||
|
|
||||||
- [ ] **Step 2: Run moon query projects**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon query projects
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: lists all 7 projects with their tags: nextjs, elysia, solidjs, rust, rust-auth, leptos, 9router
|
|
||||||
|
|
||||||
- [ ] **Step 3: Verify tag queries work**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon query projects --tag lang:typescript
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: nextjs, elysia, solidjs
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon query projects --tag lang:rust
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: rust, rust-auth, leptos
|
|
||||||
|
|
||||||
- [ ] **Step 4: Commit (if any fixes were needed)**
|
|
||||||
|
|
||||||
No commit needed if check passes clean.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 12: Test — moon run build on TypeScript apps
|
|
||||||
|
|
||||||
**Files:** (none — test only)
|
|
||||||
|
|
||||||
- [ ] **Step 1: Build nextjs**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon run nextjs:build
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: `next build` runs successfully, outputs to `.next/`
|
|
||||||
|
|
||||||
- [ ] **Step 2: Build elysia**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon run elysia:build
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: `bun build` runs successfully, outputs to `dist/`
|
|
||||||
|
|
||||||
- [ ] **Step 3: Build solidjs**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon run solidjs:build
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: `vinxi build` runs successfully, outputs to `.output/`
|
|
||||||
|
|
||||||
- [ ] **Step 4: Verify caching on second build (nextjs)**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon run nextjs:build
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: `Cached` — no rebuild, uses moonrepo cache
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 13: Test — moon run lint and test
|
|
||||||
|
|
||||||
**Files:** (none — test only)
|
|
||||||
|
|
||||||
- [ ] **Step 1: Run lint across TypeScript apps**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon run :lint
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: all apps with a `lint` task run it. Note failures as they exist pre-migration (not caused by moonrepo).
|
|
||||||
|
|
||||||
- [ ] **Step 2: Run tests across TypeScript apps**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon run :test
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: all apps with a `test` task run it.
|
|
||||||
|
|
||||||
- [ ] **Step 3: Run tag-scoped commands**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon run --tag lang:typescript :build
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: builds nextjs, elysia, solidjs only (not Rust apps).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 14: Final commit and documentation
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `.gitignore` (if any final updates)
|
|
||||||
|
|
||||||
- [ ] **Step 1: Final moon check**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon check
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: clean, no errors.
|
|
||||||
|
|
||||||
- [ ] **Step 2: Commit any remaining changes**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git status
|
|
||||||
```
|
|
||||||
|
|
||||||
If nothing outstanding, move on.
|
|
||||||
|
|
||||||
- [ ] **Step 3: Verify the full workspace is clean**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git status
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: working tree clean.
|
|
||||||
@@ -1,712 +0,0 @@
|
|||||||
# PostgreSQL Migration Implementation Plan
|
|
||||||
|
|
||||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
||||||
|
|
||||||
**Goal:** Migrate elysia (Drizzle ORM + MySQL) and rust (SeaORM + MySQL) apps to PostgreSQL via direct cutover.
|
|
||||||
|
|
||||||
**Architecture:** Export MySQL data, convert schema to PostgreSQL format, import into target PostgreSQL instance, update app drivers and connection strings, deploy and validate.
|
|
||||||
|
|
||||||
**Tech Stack:** MySQL (source), PostgreSQL (target), Drizzle ORM (elysia), SeaORM (rust), pgloader or manual conversion for schema migration.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
|
|
||||||
### Elysia App Changes
|
|
||||||
- `apps/elysia/src/db/lib/database.ts` — Replace mysql2 driver with postgres driver
|
|
||||||
- `apps/elysia/src/db/lib/schema.ts` — Replace mysqlTable with pgTable, update column types
|
|
||||||
- `apps/elysia/package.json` — Replace mysql2 with pg dependency
|
|
||||||
- `.env` or config file — Update DATABASE_URL to PostgreSQL connection string
|
|
||||||
|
|
||||||
### Rust App Changes
|
|
||||||
- `apps/rust/Cargo.toml` — Replace sqlx-mysql feature with sqlx-postgres
|
|
||||||
- `apps/rust/src/infra/db_setup.rs` — Update DbBackend::MySql to DbBackend::Postgres, adjust SQL syntax
|
|
||||||
- Config/environment — Update DATABASE_URL to PostgreSQL connection string
|
|
||||||
|
|
||||||
### Migration Artifacts
|
|
||||||
- `mysql_backup.sql` — MySQL dump (created during migration, not committed)
|
|
||||||
- `converted.sql` — PostgreSQL-compatible dump (created during migration, not committed)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Task Breakdown
|
|
||||||
|
|
||||||
### Task 1: Backup MySQL and Export Schema
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Create: `mysql_backup.sql` (temporary, not committed)
|
|
||||||
|
|
||||||
- [ ] **Step 1: Export MySQL database**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /mnt/code/bp3/ultimate-asepharyana.tech
|
|
||||||
mysqldump -u <mysql_user> -p <mysql_password> -h <mysql_host> <database_name> > mysql_backup.sql
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: File created with full schema + data. Verify file size > 1MB (contains data).
|
|
||||||
|
|
||||||
-[]**Step 2: Verify backup integrity**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check row counts in backup
|
|
||||||
grep "INSERT INTO" mysql_backup.sql | wc -l
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Multiple INSERT statements present. Note row counts for later validation.
|
|
||||||
|
|
||||||
- [ ] **Step 3: Document backup location**
|
|
||||||
|
|
||||||
Store `mysql_backup.sql` in safe location (not in git). This is rollback insurance.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 2: Convert MySQL Schema to PostgreSQL
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Create: `converted.sql` (temporary, not committed)
|
|
||||||
|
|
||||||
- [ ] **Step 1: Install pgloader (if not present)**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# macOS
|
|
||||||
brew install pgloader
|
|
||||||
|
|
||||||
# Linux (Ubuntu/Debian)
|
|
||||||
sudo apt-get install pgloader
|
|
||||||
|
|
||||||
# Or use Docker
|
|
||||||
docker run --rm -v $(pwd):/data pgloader/pgloader pgloader /data/mysql_backup.sql postgresql://asephs:hunterz@100.108.1.124:5432/hub
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Convert MySQL dump to PostgreSQL**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pgloader mysql_backup.sql postgresql://asephs:hunterz@100.108.1.124:5432/hub
|
|
||||||
```
|
|
||||||
|
|
||||||
Or manually convert if pgloader unavailable:
|
|
||||||
- Replace `AUTO_INCREMENT` with `SERIAL` or `BIGSERIAL`
|
|
||||||
- Replace `DATETIME` with `TIMESTAMP`
|
|
||||||
- Replace backticks with double quotes
|
|
||||||
- Update index syntax for PostgreSQL
|
|
||||||
|
|
||||||
Expected: Conversion completes without errors. Check for warnings about type conversions.
|
|
||||||
|
|
||||||
- [ ] **Step 3: Verify conversion output**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# If using pgloader, it creates converted.sql automatically
|
|
||||||
# If manual, save converted schema to file
|
|
||||||
cat converted.sql | head -50
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: PostgreSQL-compatible SQL syntax (no backticks, SERIAL types, TIMESTAMP).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 3: Test PostgreSQL Import
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Target: PostgreSQL instance at `postgresql://asephs:hunterz@100.108.1.124:5432/hub`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Connect to target PostgreSQL**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Connected to PostgreSQL. Prompt shows `hub=#`.
|
|
||||||
|
|
||||||
- [ ] **Step 2: Import converted schema**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub < converted.sql
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Import completes. Check for errors (should be none).
|
|
||||||
|
|
||||||
- [ ] **Step 3: Validate table creation**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "\dt"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: All tables listed (User, Account, Session, Role, Permission, UserRole, ImageCache, etc.).
|
|
||||||
|
|
||||||
- []**Step 4: Validate row counts**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "SELECT COUNT(*) FROM \"User\";"
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "SELECT COUNT(*) FROM \"Account\";"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Row counts match MySQL backup (from Task 1, Step 2).
|
|
||||||
|
|
||||||
- [ ] **Step 5: Validate indexes**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "\di"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: All indexes present (email_idx, username_idx, userId_idx, sessionToken_idx, etc.).
|
|
||||||
|
|
||||||
- [ ] **Step 6: Validate foreign keys**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "SELECT constraint_name, table_name FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY';"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Foreign key constraints listed (User→Account, User→Session, etc.).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 4: Update Elysia Database Driver
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `apps/elysia/src/db/lib/database.ts`
|
|
||||||
- Modify: `apps/elysia/src/db/lib/schema.ts`
|
|
||||||
- Modify: `apps/elysia/package.json`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Update package.json dependencies**
|
|
||||||
|
|
||||||
Replace mysql2 with pg:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"dependencies": {
|
|
||||||
"drizzle-orm": "^0.45.2",
|
|
||||||
"pg": "^8.11.0",
|
|
||||||
"elysia": "^1.4.28"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"drizzle-kit": "^0.31.10"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Run: `cd apps/elysia && bun install`
|
|
||||||
|
|
||||||
Expected: pg installed, mysql2 removed from node_modules.
|
|
||||||
|
|
||||||
- [ ] **Step 2: Update database.ts driver**
|
|
||||||
|
|
||||||
Replace entire file:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'
|
|
||||||
import { drizzle } from 'drizzle-orm/postgres-js'
|
|
||||||
import postgres from 'postgres'
|
|
||||||
import * as schema from './schema'
|
|
||||||
|
|
||||||
export type Database = PostgresJsDatabase<typeof schema>
|
|
||||||
|
|
||||||
let dbInstance: Database | null = null
|
|
||||||
let sqlInstance: ReturnType<typeof postgres> | null = null
|
|
||||||
|
|
||||||
export function initializeDb(databaseUrl: string): Database {
|
|
||||||
if (dbInstance) {
|
|
||||||
return dbInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlInstance = postgres(databaseUrl)
|
|
||||||
dbInstance = drizzle(sqlInstance, { schema, mode: 'default' })
|
|
||||||
|
|
||||||
return dbInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDb(): Database {
|
|
||||||
if (!dbInstance) {
|
|
||||||
throw new Error('Database not initialized. Call initializeDb first.')
|
|
||||||
}
|
|
||||||
return dbInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function closeDb() {
|
|
||||||
if (sqlInstance) {
|
|
||||||
await sqlInstance.end()
|
|
||||||
sqlInstance = null
|
|
||||||
dbInstance = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: File updated. No syntax errors.
|
|
||||||
|
|
||||||
- [ ] **Step 3: Update schema.ts imports**
|
|
||||||
|
|
||||||
Replace:
|
|
||||||
```typescript
|
|
||||||
import {
|
|
||||||
index,
|
|
||||||
int,
|
|
||||||
mysqlTable,
|
|
||||||
primaryKey,
|
|
||||||
text,
|
|
||||||
timestamp,
|
|
||||||
varchar,
|
|
||||||
} from 'drizzle-orm/mysql-core'
|
|
||||||
```
|
|
||||||
|
|
||||||
With:
|
|
||||||
```typescript
|
|
||||||
import {
|
|
||||||
index,
|
|
||||||
integer,
|
|
||||||
pgTable,
|
|
||||||
primaryKey,
|
|
||||||
text,
|
|
||||||
timestamp,
|
|
||||||
varchar,
|
|
||||||
} from 'drizzle-orm/postgres-core'
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 4: Update schema.ts table definitions**
|
|
||||||
|
|
||||||
Replace all `mysqlTable` with `pgTable` and `int` with `integer`:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Before
|
|
||||||
export const users = mysqlTable(
|
|
||||||
'User',
|
|
||||||
{
|
|
||||||
id: varchar('id', { length: 255 }).primaryKey(),
|
|
||||||
name: varchar('name', { length: 255 }),
|
|
||||||
// ...
|
|
||||||
},
|
|
||||||
// ...
|
|
||||||
)
|
|
||||||
|
|
||||||
// After
|
|
||||||
export const users = pgTable(
|
|
||||||
'User',
|
|
||||||
{
|
|
||||||
id: varchar('id', { length: 255 }).primaryKey(),
|
|
||||||
name: varchar('name', { length: 255 }),
|
|
||||||
// ...
|
|
||||||
},
|
|
||||||
// ...
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
Do this for all tables: users, accounts, sessions, roles, permissions, userRoles, and any others.
|
|
||||||
|
|
||||||
Expected: All `mysqlTable` → `pgTable`, all `int` → `integer`.
|
|
||||||
|
|
||||||
- [ ] **Step 5: Update environment variable**
|
|
||||||
|
|
||||||
Set DATABASE_URL in `.env` or deployment config:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
DATABASE_URL=postgresql://asephs:hunterz@100.108.1.124:5432/hub
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Environment variable set and accessible to elysia app.
|
|
||||||
|
|
||||||
- [ ] **Step 6: Test elysia connection**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd apps/elysia
|
|
||||||
bun run src/index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: App starts without connection errors. Check logs for "Database initialized" or similar.
|
|
||||||
|
|
||||||
- [ ] **Step 7: Commit elysia changes**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /mnt/code/bp3/ultimate-asepharyana.tech
|
|
||||||
git add apps/elysia/src/db/lib/database.ts apps/elysia/src/db/lib/schema.ts apps/elysia/package.json
|
|
||||||
git commit -m "feat(elysia): migrate database driver from MySQL to PostgreSQL"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Commit created with message.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 5: Update Rust Database Driver
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `apps/rust/Cargo.toml`
|
|
||||||
- Modify: `apps/rust/src/infra/db_setup.rs`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Update Cargo.toml features**
|
|
||||||
|
|
||||||
Replace:
|
|
||||||
```toml
|
|
||||||
sea-orm = { version = "1.1.19", features = ["sqlx-mysql", "runtime-tokio-rustls", "macros", "with-chrono", "with-uuid"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
With:
|
|
||||||
```toml
|
|
||||||
sea-orm = { version = "1.1.19", features = ["sqlx-postgres", "runtime-tokio-rustls", "macros", "with-chrono", "with-uuid"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Cargo.toml updated. Feature changed from sqlx-mysql to sqlx-postgres.
|
|
||||||
|
|
||||||
- [ ] **Step 2: Update db_setup.rs backend check**
|
|
||||||
|
|
||||||
Replace:
|
|
||||||
```rust
|
|
||||||
match backend {
|
|
||||||
DbBackend::MySql => {
|
|
||||||
// MySQL-specific logic
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
info!("ℹ️ Skipping schema init for non-MySQL backend");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
With:
|
|
||||||
```rust
|
|
||||||
match backend {
|
|
||||||
DbBackend::Postgres => {
|
|
||||||
// PostgreSQL-specific logic
|
|
||||||
let tables = vec![(
|
|
||||||
"ImageCache",
|
|
||||||
schema
|
|
||||||
.create_table_from_entity(image_cache::Entity)
|
|
||||||
.if_not_exists()
|
|
||||||
.to_owned(),
|
|
||||||
)];
|
|
||||||
|
|
||||||
for (name, stmt) in tables {
|
|
||||||
match db.execute(backend.build(&stmt)).await {
|
|
||||||
Ok(_) => info!(" ✓ Table '{}' checked/created", name),
|
|
||||||
Err(e) => {
|
|
||||||
error!(" [!] Failed to create table '{}': {}", name, e);
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PostgreSQL index creation (different syntax)
|
|
||||||
let index_sql = "CREATE INDEX IF NOT EXISTS idx_image_cache_cdn_url ON \"ImageCache\" (cdn_url)";
|
|
||||||
match db.execute(Statement::from_string(backend, index_sql)).await {
|
|
||||||
Ok(_) => info!(" ✓ Index 'idx_image_cache_cdn_url' ensured"),
|
|
||||||
Err(e) => {
|
|
||||||
let err_str = e.to_string();
|
|
||||||
// PostgreSQL duplicate index error
|
|
||||||
if err_str.contains("already exists") {
|
|
||||||
info!(" ✓ Index 'idx_image_cache_cdn_url' already exists");
|
|
||||||
} else {
|
|
||||||
error!(" [!] Failed to create index on ImageCache: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("✅ Database schema initialization complete.");
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
info!("ℹ️ Skipping schema init for non-PostgreSQL backend");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: db_setup.rs updated with PostgreSQL backend handling.
|
|
||||||
|
|
||||||
- [ ] **Step 3: Update environment variable**
|
|
||||||
|
|
||||||
Set DATABASE_URL in `.env` or deployment config:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
DATABASE_URL=postgresql://asephs:hunterz@100.108.1.124:5432/hub
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Environment variable set and accessible to rust app.
|
|
||||||
|
|
||||||
- [ ] **Step 4: Rebuild rust app**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd apps/rust
|
|
||||||
cargo build --release
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Build completes without errors. Compilation uses sqlx-postgres feature.
|
|
||||||
|
|
||||||
- [ ] **Step 5: Test rust connection**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd apps/rust
|
|
||||||
cargo run
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: App starts without connection errors. Check logs for "Database schema initialization complete" or similar.
|
|
||||||
|
|
||||||
- [ ] **Step 6: Commit rust changes**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /mnt/code/bp3/ultimate-asepharyana.tech
|
|
||||||
git add apps/rust/Cargo.toml apps/rust/src/infra/db_setup.rs
|
|
||||||
git commit -m "feat(rust): migrate database driver from MySQL to PostgreSQL"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Commit created with message.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 6: Smoke Tests - Elysia App
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Test: Manual testing via HTTP requests or app UI
|
|
||||||
|
|
||||||
- [ ] **Step 1: Start elysia app**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd apps/elysia
|
|
||||||
bun run src/index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: App running on configured port (check logs for port).
|
|
||||||
|
|
||||||
- [ ] **Step 2: Test user login**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://localhost:3000/auth/login \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"email":"test@example.com","password":"password"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Response 200 or 401 (auth error is OK, connection error is not).
|
|
||||||
|
|
||||||
-[]**Step 3: Test user creation (if endpoint exists)**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://localhost:3000/users \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"name":"Test User","email":"newuser@example.com"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Response 200/201 or 400 (validation error is OK).
|
|
||||||
|
|
||||||
- [ ] **Step 4: Test session retrieval**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X GET http://localhost:3000/sessions \
|
|
||||||
-H "Authorization: Bearer <token>"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Response 200 with session data or 401 (auth error is OK).
|
|
||||||
|
|
||||||
- [ ] **Step 5: Check database logs**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# In elysia app logs, verify queries are executing against PostgreSQL
|
|
||||||
# Look for connection strings or query logs showing PostgreSQL
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Logs show PostgreSQL queries (not MySQL).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 7: Smoke Tests - Rust App
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Test: Manual testing via HTTP requests or app UI
|
|
||||||
|
|
||||||
- [ ] **Step 1: Start rust app**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd apps/rust
|
|
||||||
cargo run --release
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: App running on configured port (check logs for port).
|
|
||||||
|
|
||||||
- [ ] **Step 2: Test image cache endpoint (if exists)**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X GET http://localhost:8000/api/cache/status
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Response 200 with cache status or 404 (endpoint may not exist).
|
|
||||||
|
|
||||||
- [ ] **Step 3: Test scraping/CDN endpoint**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X GET http://localhost:8000/api/health
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Response 200 with health status.
|
|
||||||
|
|
||||||
- [ ] **Step 4: Check database logs**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# In rust app logs, verify queries are executing against PostgreSQL
|
|
||||||
# Look for connection strings or query logs showing PostgreSQL
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Logs show PostgreSQL queries (not MySQL).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 8: Data Integrity Validation
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Test: PostgreSQL queries
|
|
||||||
|
|
||||||
- [] **Step 1: Verify user count**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "SELECT COUNT(*) as user_count FROM \"User\";"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Count matches MySQL backup count (from Task 1, Step 2).
|
|
||||||
|
|
||||||
- [ ] **Step 2: Verify account count**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "SELECT COUNT(*) as account_count FROM \"Account\";"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Count matches MySQL backup.
|
|
||||||
|
|
||||||
- [ ] **Step 3: Verify session count**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "SELECT COUNT(*) as session_count FROM \"Session\";"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Count matches MySQL backup.
|
|
||||||
|
|
||||||
- [] **Step 4: Verify no orphaned foreign keys**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "
|
|
||||||
SELECT a.id FROM \"Account\" a
|
|
||||||
LEFT JOIN \"User\" u ON a.user_id = u.id
|
|
||||||
WHERE u.id IS NULL;
|
|
||||||
"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: No rows returned (no orphaned accounts).
|
|
||||||
|
|
||||||
- [ ] **Step 5: Verify role/permission relationships**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "SELECT COUNT(*) as role_count FROM \"Role\";"
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "SELECT COUNT(*) as permission_count FROM \"Permission\";"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Counts match MySQL backup.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 9: Performance Baseline
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Test: Query performance comparison
|
|
||||||
|
|
||||||
- [ ] **Step 1: Benchmark user query on PostgreSQL**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "EXPLAIN ANALYZE SELECT * FROM \"User\" WHERE email = 'test@example.com';"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Query plan shows index usage (Seq Scan or Index Scan). Note execution time.
|
|
||||||
|
|
||||||
- [ ] **Step 2: Benchmark account query on PostgreSQL**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub -c "EXPLAIN ANALYZE SELECT * FROM \"Account\" WHERE user_id = 'user-123';"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Query plan shows index usage. Note execution time.
|
|
||||||
|
|
||||||
- [ ] **Step 3: Compare with MySQL baseline (if available)**
|
|
||||||
|
|
||||||
If MySQL is still running, run same queries and compare execution times.
|
|
||||||
|
|
||||||
Expected: PostgreSQL performance similar or better than MySQL.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 10: Cleanup and Documentation
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Create: `MIGRATION_LOG.md` (optional, for documentation)
|
|
||||||
|
|
||||||
- [ ] **Step 1: Remove temporary files**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rm mysql_backup.sql converted.sql
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Temporary migration files deleted.
|
|
||||||
|
|
||||||
- [ ] **Step 2: Document migration completion**
|
|
||||||
|
|
||||||
Create `MIGRATION_LOG.md`:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# PostgreSQL Migration Log
|
|
||||||
|
|
||||||
**Date:** 2026-05-25
|
|
||||||
**Status:** ✅ Complete
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
- Migrated elysia app from MySQL to PostgreSQL
|
|
||||||
- Migrated rust app from MySQL to PostgreSQL
|
|
||||||
- All data validated and integrity confirmed
|
|
||||||
- Apps tested and operational
|
|
||||||
|
|
||||||
## Changes
|
|
||||||
- elysia: Updated database driver (mysql2 → postgres), schema (mysqlTable → pgTable)
|
|
||||||
- rust: Updated Cargo.toml feature (sqlx-mysql → sqlx-postgres), db_setup.rs backend handling
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
- Row counts match pre-migration
|
|
||||||
- Foreign keys intact
|
|
||||||
- Indexes present and performant
|
|
||||||
- Auth flow functional
|
|
||||||
- Session management working
|
|
||||||
|
|
||||||
## Rollback
|
|
||||||
MySQL backup available at: [location if kept]
|
|
||||||
To rollback: Restore MySQL from backup, revert connection strings, redeploy apps.
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Final commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add MIGRATION_LOG.md
|
|
||||||
git commit -m "docs: add PostgreSQL migration completion log"
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Commit created.
|
|
||||||
|
|
||||||
- [ ] **Step 4: Verify all apps running**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check elysia
|
|
||||||
curl http://localhost:3000/health
|
|
||||||
|
|
||||||
# Check rust
|
|
||||||
curl http://localhost:8000/health
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: Both apps respond with 200 status.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Self-Review
|
|
||||||
|
|
||||||
**Spec Coverage:**
|
|
||||||
- ✅ Pre-migration (backup, convert, test) — Tasks 1-3
|
|
||||||
- ✅ Elysia code updates (driver, schema, env) — Task 4
|
|
||||||
- ✅ Rust code updates (Cargo.toml, db_setup.rs, env) — Task 5
|
|
||||||
- ✅ Smoke tests (auth, endpoints, logs) — Tasks 6-7
|
|
||||||
- ✅ Data validation (row counts, foreign keys) — Task 8
|
|
||||||
- ✅ Performance baseline — Task 9
|
|
||||||
- ✅ Cleanup and documentation — Task 10
|
|
||||||
|
|
||||||
**Placeholder Scan:**
|
|
||||||
- ✅ No TBD/TODO
|
|
||||||
- ✅ All code blocks complete
|
|
||||||
- ✅ All commands exact with expected output
|
|
||||||
- ✅ All file paths exact
|
|
||||||
|
|
||||||
**Type Consistency:**
|
|
||||||
- ✅ Database type: `PostgresJsDatabase` (elysia), `DbBackend::Postgres` (rust)
|
|
||||||
- ✅ Connection string format consistent: `postgresql://asephs:hunterz@100.108.1.124:5432/hub`
|
|
||||||
- ✅ Table names consistent: "User", "Account", "Session", etc.
|
|
||||||
@@ -1,533 +0,0 @@
|
|||||||
# Production GitHub Actions Implementation Plan
|
|
||||||
|
|
||||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
||||||
|
|
||||||
**Goal:** Rework GitHub Actions build/deploy workflows into a production baseline with reliable React submodule updates, least-privilege permissions, clear deploy behavior, and current official action versions.
|
|
||||||
|
|
||||||
**Architecture:** Keep two workflows: `docker-build-push.yml` for detect/build/manifest updates, and `deploy-docker.yml` for VPS deployment. Add dispatch submodule SHA readiness checks before parent pointer updates, and remove recursive submodule checkout from deploy runner.
|
|
||||||
|
|
||||||
**Tech Stack:** GitHub Actions YAML, GitHub-hosted Ubuntu runners, Docker Buildx, GHCR, git submodules, Docker Compose over SSH.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
|
|
||||||
- Modify `.github/workflows/docker-build-push.yml`: add default permissions, validate repository dispatch payloads, wait for submodule SHAs, keep selective matrix builds, harden manifest update.
|
|
||||||
- Modify `.github/workflows/deploy-docker.yml`: add default permissions, remove recursive checkout, keep auto deploy from successful build, make deploy logs clearer.
|
|
||||||
- Modify `.github/dependabot.yml`: add GitHub Actions update config so official actions stay current.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 1: Harden build workflow permissions and dispatch validation
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `.github/workflows/docker-build-push.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Add workflow-level read permissions**
|
|
||||||
|
|
||||||
At top level, after `concurrency`, add:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected shape:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: ghcr.io
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Replace dispatch parser with payload validation**
|
|
||||||
|
|
||||||
In `.github/workflows/docker-build-push.yml`, replace `Parse repository_dispatch payload` step body with:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Parse repository_dispatch payload
|
|
||||||
id: dispatch
|
|
||||||
if: github.event_name == 'repository_dispatch'
|
|
||||||
env:
|
|
||||||
SERVICE: ${{ github.event.client_payload.service }}
|
|
||||||
SHA: ${{ github.event.client_payload.sha }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
if [ -z "${SERVICE:-}" ]; then
|
|
||||||
echo "::error::repository_dispatch payload missing service"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "${SHA:-}" ]; then
|
|
||||||
echo "::error::repository_dispatch payload missing sha"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$SERVICE" in
|
|
||||||
rust-api|elysia-api|react-web|9router) ;;
|
|
||||||
*)
|
|
||||||
echo "::error::Unsupported service '$SERVICE'. Expected one of: rust-api, elysia-api, react-web, 9router"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case "$SHA" in
|
|
||||||
*[!0-9a-fA-F]*|???????????????????????????????????????|?????????????????????????????????????????*)
|
|
||||||
echo "::error::Invalid sha '$SHA'. Expected 40 hex characters"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
declare -a SERVICES=("rust-api" "elysia-api" "react-web" "9router")
|
|
||||||
for svc in "${SERVICES[@]}"; do
|
|
||||||
if [ "$SERVICE" = "$svc" ]; then
|
|
||||||
echo "${svc}=true" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "${svc}=false" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Run YAML syntax check**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python - <<'PY'
|
|
||||||
from pathlib import Path
|
|
||||||
import yaml
|
|
||||||
for path in Path('.github/workflows').glob('*.yml'):
|
|
||||||
yaml.safe_load(path.read_text())
|
|
||||||
print(f'OK {path}')
|
|
||||||
PY
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected:
|
|
||||||
|
|
||||||
```text
|
|
||||||
OK .github/workflows/deploy-docker.yml
|
|
||||||
OK .github/workflows/docker-build-push.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 4: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .github/workflows/docker-build-push.yml
|
|
||||||
git commit -m "ci: validate dispatch payloads"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 2: Add submodule SHA readiness wait before build/update
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `.github/workflows/docker-build-push.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Add readiness job after changes job**
|
|
||||||
|
|
||||||
Insert this job between `changes` and `build`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
wait-submodule-ref:
|
|
||||||
needs: [changes]
|
|
||||||
if: github.event_name == 'repository_dispatch'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Wait for submodule ref
|
|
||||||
env:
|
|
||||||
SERVICE: ${{ github.event.client_payload.service }}
|
|
||||||
SHA: ${{ github.event.client_payload.sha }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
case "$SERVICE" in
|
|
||||||
"rust-api") REPO="https://github.com/MythEclipse/ultimate-asepharyana-tech-rust.git" ;;
|
|
||||||
"elysia-api") REPO="https://github.com/MythEclipse/ultimate-asepharyana-tech-elysia.git" ;;
|
|
||||||
"react-web") REPO="https://github.com/MythEclipse/ultimate-asepharyana-tech-react.git" ;;
|
|
||||||
"9router") REPO="https://github.com/MythEclipse/9router.git" ;;
|
|
||||||
*)
|
|
||||||
echo "::error::Unsupported service '$SERVICE'"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo "Waiting for $SERVICE ref $SHA in $REPO"
|
|
||||||
for attempt in {1..30}; do
|
|
||||||
if git ls-remote --exit-code "$REPO" "$SHA" >/dev/null 2>&1; then
|
|
||||||
echo "Submodule ref $SHA is fetchable for $SERVICE"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
echo "Attempt $attempt/30: $SHA not visible yet; waiting 10s"
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "::error::Submodule ref $SHA for $SERVICE was not fetchable after 300s"
|
|
||||||
exit 1
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Make build wait for readiness job without blocking push/manual events**
|
|
||||||
|
|
||||||
Change build job header from:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
build:
|
|
||||||
needs: [changes]
|
|
||||||
```
|
|
||||||
|
|
||||||
to:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
build:
|
|
||||||
needs: [changes, wait-submodule-ref]
|
|
||||||
if: |
|
|
||||||
always() &&
|
|
||||||
needs.changes.result == 'success' &&
|
|
||||||
(needs.wait-submodule-ref.result == 'success' || needs.wait-submodule-ref.result == 'skipped') &&
|
|
||||||
needs.changes.outputs.matrix != '[]'
|
|
||||||
```
|
|
||||||
|
|
||||||
Remove existing build-level line:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
if: needs.changes.outputs.matrix != '[]'
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Make update-manifest wait for readiness job**
|
|
||||||
|
|
||||||
Change update-manifest header from:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
update-manifest:
|
|
||||||
needs: [changes, build]
|
|
||||||
if: |
|
|
||||||
always() &&
|
|
||||||
(needs.build.result == 'success' || needs.build.result == 'skipped')
|
|
||||||
```
|
|
||||||
|
|
||||||
to:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
update-manifest:
|
|
||||||
needs: [changes, wait-submodule-ref, build]
|
|
||||||
if: |
|
|
||||||
always() &&
|
|
||||||
needs.changes.result == 'success' &&
|
|
||||||
(needs.wait-submodule-ref.result == 'success' || needs.wait-submodule-ref.result == 'skipped') &&
|
|
||||||
(needs.build.result == 'success' || needs.build.result == 'skipped')
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 4: Run YAML syntax check**
|
|
||||||
|
|
||||||
Run same command from Task 1 Step 3.
|
|
||||||
|
|
||||||
Expected both workflow files print `OK`.
|
|
||||||
|
|
||||||
- [ ] **Step 5: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .github/workflows/docker-build-push.yml
|
|
||||||
git commit -m "ci: wait for submodule refs before builds"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 3: Harden manifest update and submodule checkout
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `.github/workflows/docker-build-push.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Add job permissions to build and manifest jobs**
|
|
||||||
|
|
||||||
Ensure build job contains:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
```
|
|
||||||
|
|
||||||
Ensure update-manifest job contains:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Replace dispatch submodule checkout block**
|
|
||||||
|
|
||||||
Inside `Update tags and submodules`, replace the repository_dispatch submodule update block with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
if [ "${{ github.event_name }}" == "repository_dispatch" ] && [ "${{ github.event.client_payload.service }}" == "$id" ]; then
|
|
||||||
SHA_DISPATCH="${{ github.event.client_payload.sha }}"
|
|
||||||
SUB_PATH="${PATHS[$id]}"
|
|
||||||
if [ -n "$SHA_DISPATCH" ]; then
|
|
||||||
echo "Updating submodule $SUB_PATH to $SHA_DISPATCH"
|
|
||||||
git submodule update --init "$SUB_PATH"
|
|
||||||
git -C "$SUB_PATH" fetch origin "$SHA_DISPATCH"
|
|
||||||
git -C "$SUB_PATH" checkout "$SHA_DISPATCH"
|
|
||||||
git add "$SUB_PATH"
|
|
||||||
CHANGED=true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Add pull/rebase retry before push**
|
|
||||||
|
|
||||||
Replace:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git commit -m "chore: update manifests and submodules [skip ci]"
|
|
||||||
git pull --rebase origin main
|
|
||||||
git push origin main
|
|
||||||
```
|
|
||||||
|
|
||||||
with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git commit -m "chore: update manifests and submodules [skip ci]"
|
|
||||||
|
|
||||||
for attempt in {1..3}; do
|
|
||||||
if git pull --rebase origin main && git push origin main; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
echo "Manifest push attempt $attempt/3 failed; retrying"
|
|
||||||
git rebase --abort || true
|
|
||||||
git pull --rebase origin main || true
|
|
||||||
sleep 5
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "::error::Failed to push manifest update after 3 attempts"
|
|
||||||
exit 1
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 4: Run YAML syntax check**
|
|
||||||
|
|
||||||
Run same command from Task 1 Step 3.
|
|
||||||
|
|
||||||
Expected both workflow files print `OK`.
|
|
||||||
|
|
||||||
- [ ] **Step 5: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .github/workflows/docker-build-push.yml
|
|
||||||
git commit -m "ci: harden manifest updates"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 4: Make deploy checkout submodule-free and least privilege
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `.github/workflows/deploy-docker.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Add workflow-level read permissions**
|
|
||||||
|
|
||||||
After `concurrency`, add:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected shape:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
concurrency:
|
|
||||||
group: deploy-vps
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
```
|
|
||||||
|
|
||||||
If current `cancel-in-progress` is `true`, change it to `false`.
|
|
||||||
|
|
||||||
- [ ] **Step 2: Make checkout non-recursive**
|
|
||||||
|
|
||||||
Replace checkout step:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
```
|
|
||||||
|
|
||||||
with:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
submodules: false
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Add deploy context log**
|
|
||||||
|
|
||||||
At start of `Deploy with Docker Compose on VPS` run script, after `set -euo pipefail`, add:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
echo "Deploy event: ${{ github.event_name }}"
|
|
||||||
echo "Deploy ref: ${{ github.ref }}"
|
|
||||||
echo "Deploy sha: ${{ github.sha }}"
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 4: Run YAML syntax check**
|
|
||||||
|
|
||||||
Run same command from Task 1 Step 3.
|
|
||||||
|
|
||||||
Expected both workflow files print `OK`.
|
|
||||||
|
|
||||||
- [ ] **Step 5: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .github/workflows/deploy-docker.yml
|
|
||||||
git commit -m "ci: avoid submodule checkout during deploy"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 5: Add GitHub Actions Dependabot updates
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `.github/dependabot.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Add github-actions ecosystem**
|
|
||||||
|
|
||||||
Append this update entry under `updates:`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- package-ecosystem: 'github-actions'
|
|
||||||
directory: '/'
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
groups:
|
|
||||||
github-actions:
|
|
||||||
patterns:
|
|
||||||
- '*'
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected file shape:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: 'devcontainers'
|
|
||||||
directory: '/'
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
|
|
||||||
- package-ecosystem: 'github-actions'
|
|
||||||
directory: '/'
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
groups:
|
|
||||||
github-actions:
|
|
||||||
patterns:
|
|
||||||
- '*'
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Run YAML syntax check**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python - <<'PY'
|
|
||||||
from pathlib import Path
|
|
||||||
import yaml
|
|
||||||
paths = [Path('.github/dependabot.yml'), *Path('.github/workflows').glob('*.yml')]
|
|
||||||
for path in paths:
|
|
||||||
yaml.safe_load(path.read_text())
|
|
||||||
print(f'OK {path}')
|
|
||||||
PY
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected:
|
|
||||||
|
|
||||||
```text
|
|
||||||
OK .github/dependabot.yml
|
|
||||||
OK .github/workflows/deploy-docker.yml
|
|
||||||
OK .github/workflows/docker-build-push.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Commit**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .github/dependabot.yml
|
|
||||||
git commit -m "ci: enable github actions dependency updates"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 6: Final validation
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Validate: `.github/workflows/docker-build-push.yml`
|
|
||||||
- Validate: `.github/workflows/deploy-docker.yml`
|
|
||||||
- Validate: `.github/dependabot.yml`
|
|
||||||
|
|
||||||
- [ ] **Step 1: Run YAML syntax check**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python - <<'PY'
|
|
||||||
from pathlib import Path
|
|
||||||
import yaml
|
|
||||||
paths = [Path('.github/dependabot.yml'), *Path('.github/workflows').glob('*.yml')]
|
|
||||||
for path in paths:
|
|
||||||
yaml.safe_load(path.read_text())
|
|
||||||
print(f'OK {path}')
|
|
||||||
PY
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected all files print `OK`.
|
|
||||||
|
|
||||||
- [ ] **Step 2: Check workflows recognized by GitHub CLI**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gh workflow list
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected output includes:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Build and Push Docker Images
|
|
||||||
Deploy Docker to VPS
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Inspect final diff**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git diff -- .github/workflows .github/dependabot.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected:
|
|
||||||
|
|
||||||
- `docker-build-push.yml` has dispatch validation, `wait-submodule-ref`, job permissions, and manifest push retry.
|
|
||||||
- `deploy-docker.yml` has non-recursive checkout and read-only permissions.
|
|
||||||
- `dependabot.yml` has `github-actions` updates.
|
|
||||||
|
|
||||||
- [ ] **Step 4: Commit any final validation fixes**
|
|
||||||
|
|
||||||
If Step 1 or Step 2 required fixes, commit them:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .github/workflows .github/dependabot.yml
|
|
||||||
git commit -m "ci: finalize production workflow hardening"
|
|
||||||
```
|
|
||||||
|
|
||||||
If no fixes were needed, do not create an empty commit.
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# React Direct Runtime and Images Implementation Plan
|
|
||||||
|
|
||||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
||||||
|
|
||||||
**Goal:** Serve the React SPA without nginx and remove frontend image-cache proxy calls so browser uses direct image/API URLs.
|
|
||||||
|
|
||||||
**Architecture:** The React Docker image still builds with Bun/Vite, but runtime uses `vite preview` from Bun instead of nginx. `CachedImage` normalizes image URLs and renders them directly, keeping fallback/retry UI but removing `/proxy/image-cache` auditing. Traefik keeps routing `react-web:80`, so compose and dynamic routing stay stable.
|
|
||||||
|
|
||||||
**Tech Stack:** Bun, Vite, React, TypeScript, Docker, Docker Compose, Traefik.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tasks
|
|
||||||
|
|
||||||
### Task 1: Switch React Runtime From nginx to Bun/Vite Preview
|
|
||||||
|
|
||||||
Modify `infra/docker/react.Dockerfile` so runtime stage uses `oven/bun:1-alpine`, installs production deps, copies `/app/dist`, exposes 80, and runs `bunx vite preview --host 0.0.0.0 --port 80`. Verify nginx runtime/copy lines are gone and Vite preview command exists.
|
|
||||||
|
|
||||||
### Task 2: Remove Image Cache Proxy From CachedImage
|
|
||||||
|
|
||||||
Modify `apps/react/src/components/ui/cached-image.tsx` to remove `API_BASE_URL` import, `/proxy/image-cache` POST, audit state/function, and auditing overlay. Keep direct normalized image URLs, retry behavior, and fallback image.
|
|
||||||
|
|
||||||
### Task 3: Verify Build and Config
|
|
||||||
|
|
||||||
Run static checks for both tasks, `bun --cwd apps/react run build`, `docker build -f infra/docker/react.Dockerfile -t react-web:test .`, start test container on `18080:80`, curl root, clean container, and run `git diff --check`.
|
|
||||||
|
|
||||||
### Task 4: Commit and Push
|
|
||||||
|
|
||||||
Commit changed files: `infra/docker/react.Dockerfile`, `apps/react/src/components/ui/cached-image.tsx`, `docs/superpowers/plans/2026-05-27-react-direct-runtime-and-images.md`. Push branch only after verification if user asks; do not push from worktree unless explicitly instructed.
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
# Moonrepo Migration Design
|
|
||||||
|
|
||||||
**Date:** 2026-05-24
|
|
||||||
**Status:** approved
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
Ultimate Asepharyana Tech is a polyglot monorepo with 7 applications managed as git submodules: nextjs (React/Next.js), elysia (Bun/Elysia), solidjs (SolidStart), rust (Axum), rust-auth (Axum auth service), leptos (Leptos WASM), and 9router. Nix flakes handle system dependencies and Docker image builds. Docker Compose manages deployment.
|
|
||||||
|
|
||||||
## Problem
|
|
||||||
|
|
||||||
- No centralized task orchestration — running build/lint/test across all apps is manual and error-prone
|
|
||||||
- No dependency caching — builds are not incremental across apps
|
|
||||||
- Inconsistent developer experience — each app has its own conventions, onboarding is slow
|
|
||||||
|
|
||||||
## Goal
|
|
||||||
|
|
||||||
Adopt moonrepo for task orchestration, caching, and dependency graph management across all 7 apps while preserving the existing Nix build system, Docker Compose deployment, and git submodule structure.
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Directory Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
ultimate-asepharyana.tech/
|
|
||||||
├── .moon/
|
|
||||||
│ ├── workspace.yml
|
|
||||||
│ ├── toolchain.yml
|
|
||||||
│ └── tasks/
|
|
||||||
│ ├── typescript-build.yml
|
|
||||||
│ ├── typescript-lint.yml
|
|
||||||
│ ├── typescript-test.yml
|
|
||||||
│ ├── rust-build.yml
|
|
||||||
│ ├── rust-test.yml
|
|
||||||
│ └── rust-lint.yml
|
|
||||||
├── apps/
|
|
||||||
│ ├── nextjs/moon.yml
|
|
||||||
│ ├── elysia/moon.yml
|
|
||||||
│ ├── solidjs/moon.yml
|
|
||||||
│ ├── rust/moon.yml
|
|
||||||
│ ├── rust-auth/moon.yml
|
|
||||||
│ ├── leptos/moon.yml
|
|
||||||
│ └── 9router/moon.yml
|
|
||||||
├── .moon/workspace.yml # unchanged — Nix build + deployment
|
|
||||||
├── flake.nix # unchanged
|
|
||||||
├── infra/ # unchanged — Docker Compose, Traefik, nginx
|
|
||||||
└── scripts/ # unchanged
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tag Taxonomy
|
|
||||||
|
|
||||||
| Tag | Projects |
|
|
||||||
|-----|----------|
|
|
||||||
| `lang:typescript` | nextjs, elysia, solidjs |
|
|
||||||
| `lang:rust` | rust, rust-auth, leptos |
|
|
||||||
| `type:frontend` | nextjs, solidjs, leptos |
|
|
||||||
| `type:backend` | rust, elysia, rust-auth |
|
|
||||||
| `type:router` | 9router |
|
|
||||||
|
|
||||||
### Toolchain
|
|
||||||
|
|
||||||
- moonrepo manages Node 22, Bun, and TypeScript versions via `.moon/toolchain.yml` for consistent access across all TypeScript apps
|
|
||||||
- Rust toolchain remains managed by Nix/devShell — moonrepo does not touch Rust installation
|
|
||||||
- Nix devShell gains the moon CLI binary
|
|
||||||
|
|
||||||
### Project Dependencies
|
|
||||||
|
|
||||||
- **nextjs** depends on `rust-auth` and `elysia` (frontend consumes both APIs)
|
|
||||||
- **solidjs** depends on `elysia` and `rust-auth`
|
|
||||||
- **leptos** depends on `rust` (CSR frontend consumes rust backend API)
|
|
||||||
- **elysia**, **rust**, **rust-auth**, **9router** — no project dependencies
|
|
||||||
|
|
||||||
## Task Definitions
|
|
||||||
|
|
||||||
### Shared Tasks: TypeScript
|
|
||||||
|
|
||||||
All TypeScript apps inherit from `.moon/tasks/typescript-*.yml`:
|
|
||||||
|
|
||||||
| Task | Command | Inputs | Outputs |
|
|
||||||
|------|---------|--------|---------|
|
|
||||||
| `build` | `bun run build` | `src/**/*`, `tsconfig.json`, `package.json` | `.next`, `dist`, `.output` |
|
|
||||||
| `lint` | `bun run lint` | `src/**/*`, `eslint.config.mjs`, `tsconfig.json` | — |
|
|
||||||
| `typecheck` | `bun run check-types` | `src/**/*`, `tsconfig.json` | — |
|
|
||||||
| `test` | `bun test` | `src/**/*`, `test/**/*` | — |
|
|
||||||
| `e2e` | overridden per app | — | — |
|
|
||||||
|
|
||||||
### Shared Tasks: Rust
|
|
||||||
|
|
||||||
All Rust apps inherit from `.moon/tasks/rust-*.yml`, using system tasks:
|
|
||||||
|
|
||||||
| Task | Command | Inputs | Outputs |
|
|
||||||
|------|---------|--------|---------|
|
|
||||||
| `build` | `cargo build --release` | `src/**/*`, `Cargo.toml`, `Cargo.lock`, `build.rs` | `target/release/*` |
|
|
||||||
| `test` | `cargo test` | `src/**/*`, `Cargo.toml`, `Cargo.lock`, `tests/**/*` | — |
|
|
||||||
| `lint` | `cargo clippy -- -D warnings` | `src/**/*`, `Cargo.toml` | — |
|
|
||||||
| `fmt-check` | `cargo fmt --check` | `src/**/*` | — |
|
|
||||||
|
|
||||||
### Implicit Dependencies
|
|
||||||
|
|
||||||
Configured in `workspace.yml`: `lint` implicitly depends on `build` for TypeScript apps (typecheck path), and `build` propagates to dependents when source inputs change.
|
|
||||||
|
|
||||||
## Key Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
moon run :build # build all changed apps + dependents
|
|
||||||
moon run :lint # lint all
|
|
||||||
moon run :test # test all
|
|
||||||
moon run --tag lang:rust :test # test Rust apps only
|
|
||||||
moon run --affected :build # build only what changed
|
|
||||||
moon query projects --tag lang:typescript # list TypeScript projects
|
|
||||||
```
|
|
||||||
|
|
||||||
## What Does NOT Change
|
|
||||||
|
|
||||||
- **Nix flakes** — system dependencies, Docker image builds, devShell remain as-is
|
|
||||||
- **Docker Compose** — deployment continues through existing compose files in `infra/compose/`
|
|
||||||
- **Git submodules** — all 7 app repos stay as submodules under `apps/`
|
|
||||||
- **infra/** — Traefik and Docker Compose deployment remain under `infra/`; legacy Grafana/Prometheus/Alertmanager configs have been removed
|
|
||||||
- **scripts/** — existing utility scripts unchanged
|
|
||||||
|
|
||||||
## Migration Steps (High-Level)
|
|
||||||
|
|
||||||
1. Install moonrepo CLI, create `.moon/` scaffolding
|
|
||||||
2. Create shared task definitions in `.moon/tasks/`
|
|
||||||
3. Configure `workspace.yml` with project list, tags, and dependency constraints
|
|
||||||
4. Configure `toolchain.yml` for Node 22 + Bun
|
|
||||||
5. Add `moon.yml` to each of the 7 app directories
|
|
||||||
6. Validate: `moon check`, `moon run :build`, `moon run :lint`, `moon run :test`
|
|
||||||
7. Add moon CLI to Nix devShell
|
|
||||||
8. Commit and document
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
# PostgreSQL Migration Design
|
|
||||||
**Date:** 2026-05-25
|
|
||||||
**Scope:** Migrate elysia (Drizzle ORM + MySQL) and rust (SeaORM + MySQL) apps to PostgreSQL
|
|
||||||
**Approach:** Direct cutover (Approach B)
|
|
||||||
**Connection String:** `postgresql://asephs:hunterz@100.108.1.124:5432/hub`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Current State
|
|
||||||
|
|
||||||
### Elysia App
|
|
||||||
- **ORM:** Drizzle ORM v0.45.2
|
|
||||||
- **Driver:** mysql2 v3.20.0
|
|
||||||
- **Schema Location:** `apps/elysia/src/db/lib/schema.ts`
|
|
||||||
- **Tables:** users, accounts, sessions, roles, permissions, userRoles, and related junction tables
|
|
||||||
- **Database File:** `apps/elysia/src/db/lib/database.ts`
|
|
||||||
|
|
||||||
### Rust App
|
|
||||||
- **ORM:** SeaORM v1.1.19
|
|
||||||
- **Feature:** sqlx-mysql
|
|
||||||
- **DB Setup:** `apps/rust/src/infra/db_setup.rs`
|
|
||||||
- **Tables:** ImageCache (primary table managed by app)
|
|
||||||
- **Config:** Environment-based connection string
|
|
||||||
|
|
||||||
### Apps NOT Migrating
|
|
||||||
- 9router (uses SQLite runtime, excluded per requirements)
|
|
||||||
- nextjs (frontend, minimal DB usage)
|
|
||||||
- leptos, solidjs, rust-auth (frontends, no DB)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Migration Steps
|
|
||||||
|
|
||||||
### Phase 1: Pre-Migration (Preparation)
|
|
||||||
1. **Backup MySQL**
|
|
||||||
```bash
|
|
||||||
mysqldump -u <user> -p <db> > mysql_backup.sql
|
|
||||||
```
|
|
||||||
2. **Convert MySQL dump to PostgreSQL**
|
|
||||||
- Use `pgloader` or manual conversion for schema compatibility
|
|
||||||
- Handle type conversions:
|
|
||||||
- `INT` → `INTEGER`
|
|
||||||
- `VARCHAR(n)` → `VARCHAR(n)` (PostgreSQL compatible)
|
|
||||||
- `DATETIME` → `TIMESTAMP`
|
|
||||||
- `AUTO_INCREMENT` → `SERIAL` or `BIGSERIAL`
|
|
||||||
- Verify indexes and foreign keys convert correctly
|
|
||||||
3. **Test import into target PostgreSQL**
|
|
||||||
```bash
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub < converted.sql
|
|
||||||
```
|
|
||||||
4. **Validate data integrity**
|
|
||||||
- Row counts match MySQL
|
|
||||||
- Indexes exist
|
|
||||||
- Foreign key constraints enforced
|
|
||||||
|
|
||||||
### Phase 2: Code Updates
|
|
||||||
|
|
||||||
#### Elysia App
|
|
||||||
1. **Update `apps/elysia/src/db/lib/database.ts`**
|
|
||||||
- Replace `mysql2` import with `postgres` driver
|
|
||||||
- Change Drizzle dialect from `drizzle-orm/mysql2` to `drizzle-orm/postgres`
|
|
||||||
- Update connection string format
|
|
||||||
|
|
||||||
2. **Update `apps/elysia/src/db/lib/schema.ts`**
|
|
||||||
- Replace `drizzle-orm/mysql-core` imports with `drizzle-orm/postgres-core`
|
|
||||||
- Change `mysqlTable` to `pgTable`
|
|
||||||
- Adjust column types if needed (e.g., `int` → `integer`)
|
|
||||||
|
|
||||||
3. **Update `apps/elysia/package.json`**
|
|
||||||
- Replace `mysql2` with `pg` (PostgreSQL driver)
|
|
||||||
- Keep `drizzle-orm` and `drizzle-kit` versions
|
|
||||||
|
|
||||||
4. **Update environment/config**
|
|
||||||
- Change `DATABASE_URL` to PostgreSQL connection string
|
|
||||||
|
|
||||||
#### Rust App
|
|
||||||
1. **Update `apps/rust/Cargo.toml`**
|
|
||||||
- Replace `sqlx-mysql` feature with `sqlx-postgres` in sea-orm dependency
|
|
||||||
- Add `postgres` feature if needed
|
|
||||||
|
|
||||||
2. **Update `apps/rust/src/infra/db_setup.rs`**
|
|
||||||
- Change `DbBackend::MySql` check to `DbBackend::Postgres`
|
|
||||||
- Adjust SQL syntax for PostgreSQL (e.g., index creation)
|
|
||||||
- Update error code handling (PostgreSQL uses different error codes)
|
|
||||||
|
|
||||||
3. **Update config/environment**
|
|
||||||
- Change `DATABASE_URL` to PostgreSQL connection string
|
|
||||||
|
|
||||||
### Phase 3: Cutover (Execution)
|
|
||||||
1. **Stop all apps**
|
|
||||||
```bash
|
|
||||||
# Stop elysia
|
|
||||||
# Stop rust app
|
|
||||||
```
|
|
||||||
2. **Export MySQL data**
|
|
||||||
```bash
|
|
||||||
mysqldump -u <user> -p <db> > final_backup.sql
|
|
||||||
```
|
|
||||||
3. **Convert and import to PostgreSQL**
|
|
||||||
```bash
|
|
||||||
# Convert dump
|
|
||||||
# Import to PostgreSQL
|
|
||||||
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub < converted.sql
|
|
||||||
```
|
|
||||||
4. **Deploy updated apps**
|
|
||||||
- Deploy elysia with PostgreSQL driver
|
|
||||||
- Deploy rust with PostgreSQL feature
|
|
||||||
5. **Verify connectivity**
|
|
||||||
- Test database queries from both apps
|
|
||||||
- Check auth flow (users table)
|
|
||||||
- Verify session management
|
|
||||||
|
|
||||||
### Phase 4: Validation
|
|
||||||
1. **Smoke tests**
|
|
||||||
- User login/logout
|
|
||||||
- Session creation and retrieval
|
|
||||||
- Role/permission queries
|
|
||||||
- ImageCache operations (rust app)
|
|
||||||
2. **Data integrity checks**
|
|
||||||
- Row counts match pre-migration
|
|
||||||
- No orphaned foreign keys
|
|
||||||
- Indexes performing as expected
|
|
||||||
3. **Performance baseline**
|
|
||||||
- Compare query times MySQL vs PostgreSQL
|
|
||||||
- Monitor connection pool usage
|
|
||||||
|
|
||||||
### Phase 5: Rollback Plan (if needed)
|
|
||||||
1. **Stop apps**
|
|
||||||
2. **Restore MySQL from backup**
|
|
||||||
```bash
|
|
||||||
mysql -u <user> -p <db> < mysql_backup.sql
|
|
||||||
```
|
|
||||||
3. **Revert connection strings in apps**
|
|
||||||
4. **Redeploy with MySQL drivers**
|
|
||||||
5. **Restart apps**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Technical Details
|
|
||||||
|
|
||||||
### Elysia Driver Change
|
|
||||||
**Before:**
|
|
||||||
```typescript
|
|
||||||
import { drizzle } from 'drizzle-orm/mysql2'
|
|
||||||
import { createPool } from 'mysql2/promise'
|
|
||||||
```
|
|
||||||
|
|
||||||
**After:**
|
|
||||||
```typescript
|
|
||||||
import { drizzle } from 'drizzle-orm/postgres-js'
|
|
||||||
import postgres from 'postgres'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Rust Feature Change
|
|
||||||
**Before:**
|
|
||||||
```toml
|
|
||||||
sea-orm = { version = "1.1.19", features = ["sqlx-mysql", ...] }
|
|
||||||
```
|
|
||||||
|
|
||||||
**After:**
|
|
||||||
```toml
|
|
||||||
sea-orm = { version = "1.1.19", features = ["sqlx-postgres", ...] }
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Risk Assessment
|
|
||||||
|
|
||||||
| Risk | Mitigation |
|
|
||||||
|------|-----------|
|
|
||||||
| Data loss during migration | Full MySQL backup before cutover; test import first |
|
|
||||||
| App downtime | Cutover during low-traffic window; rollback plan ready |
|
|
||||||
| Connection string misconfiguration | Test connection before deploying apps |
|
|
||||||
| Schema incompatibilities | Pre-test conversion; validate indexes/constraints |
|
|
||||||
| Performance regression | Baseline MySQL performance; monitor PostgreSQL after cutover |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Success Criteria
|
|
||||||
|
|
||||||
- ✅ All data migrated to PostgreSQL (row counts match)
|
|
||||||
- ✅ Elysia app connects and queries work
|
|
||||||
- ✅ Rust app connects and queries work
|
|
||||||
- ✅ Auth flow functional (login/logout)
|
|
||||||
- ✅ No orphaned foreign keys
|
|
||||||
- ✅ Indexes present and performant
|
|
||||||
- ✅ Rollback plan tested and documented
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Timeline
|
|
||||||
|
|
||||||
- **Pre-migration:** 30 min (backup, convert, test)
|
|
||||||
- **Code updates:** 1-2 hours (driver changes, testing)
|
|
||||||
- **Cutover:** 15-30 min (stop apps, migrate data, restart)
|
|
||||||
- **Validation:** 30 min (smoke tests, data checks)
|
|
||||||
- **Total:** ~3-4 hours (including buffer)
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# Hapus Nix, Docker-Only
|
|
||||||
|
|
||||||
**Goal:** Remove all Nix configuration and adapt CI/dev to Docker-only.
|
|
||||||
|
|
||||||
**Scope:**
|
|
||||||
- Delete: `flake.nix`, `flake.lock`, `nix/`, `.envrc`, `.direnv/`
|
|
||||||
- Modify: `.github/workflows/docker-build-push.yml` — remove Nix steps, move `rust-api` to Dockerfile build
|
|
||||||
- Modify: `.vscode/settings.json` — remove `nixEnvSelector`
|
|
||||||
- Modify: `README.md` — replace `nix build` with `docker build`
|
|
||||||
|
|
||||||
**CI Changes:**
|
|
||||||
- `rust-api` uses `infra/docker/rust.Dockerfile` (already exists)
|
|
||||||
- Remove `cachix/install-nix-action`, `cachix/cachix-action`, `nix build` step
|
|
||||||
- Remove `flake.nix`, `flake.lock`, `nix/**` from path triggers
|
|
||||||
- Update path filters: `nix/apps/*.nix` → `infra/docker/*.Dockerfile`
|
|
||||||
- Remove `repository_dispatch` Nix `--override-input` logic
|
|
||||||
- Add `rust-api` to Dockerfile case + push branch
|
|
||||||
|
|
||||||
**Dev Workflow:**
|
|
||||||
- Rust toolchain via `rustup`/`rust-toolchain.toml` in `apps/rust/`
|
|
||||||
- Dev via `docker compose` instead of `process-compose`
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
# Production GitHub Actions redesign
|
|
||||||
|
|
||||||
## Goal
|
|
||||||
|
|
||||||
Rework `.github/workflows` into a production baseline that balances reliability, security, speed, and clear failures while keeping automatic deploys from `main`.
|
|
||||||
|
|
||||||
## Current problems
|
|
||||||
|
|
||||||
- Deploy runner checks out submodules recursively even though deploy work happens on the VPS. Fresh submodule SHAs can fail with `not our ref` before deploy starts.
|
|
||||||
- Repository dispatch can update a parent submodule pointer before the submodule SHA is fetchable by GitHub Actions.
|
|
||||||
- Build and deploy permissions are broader than needed at workflow level.
|
|
||||||
- Failure logs do not clearly separate build, manifest update, submodule readiness, and deployment phases.
|
|
||||||
|
|
||||||
## Chosen approach
|
|
||||||
|
|
||||||
Use a split pipeline:
|
|
||||||
|
|
||||||
1. `docker-build-push.yml` remains the build and manifest update pipeline.
|
|
||||||
2. `deploy-docker.yml` remains deploy-only.
|
|
||||||
3. Repository dispatch waits for the requested submodule SHA to be fetchable before building and updating the parent pointer.
|
|
||||||
4. Deploy checkout no longer uses recursive submodules; the VPS updates submodules after resetting to `origin/main`.
|
|
||||||
|
|
||||||
## Build workflow design
|
|
||||||
|
|
||||||
Triggers:
|
|
||||||
|
|
||||||
- `push` to `main` for app/package/docker/workflow changes.
|
|
||||||
- `repository_dispatch` with `service` and `sha` payload.
|
|
||||||
- `workflow_dispatch` for manual full builds.
|
|
||||||
|
|
||||||
Jobs:
|
|
||||||
|
|
||||||
- `changes`: detects the service matrix and validates dispatch payloads.
|
|
||||||
- `build`: builds and pushes only selected service images using Docker Buildx registry cache.
|
|
||||||
- `update-manifest`: updates compose image tags and, for dispatch events, updates the matching submodule pointer.
|
|
||||||
|
|
||||||
Repository dispatch handling:
|
|
||||||
|
|
||||||
- For dispatch events, wait until `git ls-remote` or equivalent fetch confirms the payload SHA exists in the service submodule remote.
|
|
||||||
- Retry for a bounded timeout and fail with an explicit message if the SHA never becomes visible.
|
|
||||||
- Only after readiness is confirmed, checkout the SHA in the submodule and commit the parent pointer update.
|
|
||||||
|
|
||||||
## Deploy workflow design
|
|
||||||
|
|
||||||
Triggers:
|
|
||||||
|
|
||||||
- `workflow_run` from successful `Build and Push Docker Images` on `main`.
|
|
||||||
- `push` to `main` for `infra/compose/**` and deploy workflow changes.
|
|
||||||
- `workflow_dispatch`.
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
|
|
||||||
- Keep automatic deploy from `main`.
|
|
||||||
- Keep a single deploy concurrency group.
|
|
||||||
- Use non-recursive checkout on the runner.
|
|
||||||
- On the VPS, fetch/reset `origin/main`, update submodules, compute changed compose stacks, pull images, and run Docker Compose.
|
|
||||||
|
|
||||||
## Permissions and action trust
|
|
||||||
|
|
||||||
Defaults:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
```
|
|
||||||
|
|
||||||
Job-specific permissions:
|
|
||||||
|
|
||||||
- Build job: `contents: read`, `packages: write`.
|
|
||||||
- Manifest update job: `contents: write`.
|
|
||||||
- Deploy job: `contents: read`.
|
|
||||||
|
|
||||||
Action pinning:
|
|
||||||
|
|
||||||
- GitHub-owned and Docker official actions may use major versions such as `actions/checkout@v4` and `docker/build-push-action@v6`.
|
|
||||||
- Any future third-party action should be pinned to a full commit SHA.
|
|
||||||
|
|
||||||
## Reliability details
|
|
||||||
|
|
||||||
- Keep `set -euo pipefail` in shell steps.
|
|
||||||
- Add bounded retry around submodule SHA readiness.
|
|
||||||
- Add clear logs for selected services, image tags, compose files changed, and dispatch payload values.
|
|
||||||
- Avoid recursive submodule checkout in deploy to eliminate fresh-SHA checkout race.
|
|
||||||
- Manifest commits continue using `[skip ci]` to prevent build loops.
|
|
||||||
|
|
||||||
## Speed details
|
|
||||||
|
|
||||||
- Keep selective matrix builds.
|
|
||||||
- Keep Docker Buildx registry cache.
|
|
||||||
- Keep deploy checkout shallow and submodule-free.
|
|
||||||
- Avoid rebuilding from compose-only manifest commits.
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
Before marking implementation complete:
|
|
||||||
|
|
||||||
- Validate workflow YAML syntax.
|
|
||||||
- Run `gh workflow list` or equivalent sanity checks.
|
|
||||||
- Verify build workflow still detects React updates.
|
|
||||||
- Verify deploy workflow no longer fails during runner checkout for fresh submodule SHAs.
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
# Monorepo Restructure: ultimate-asepharyana.tech → asepharyana-hub
|
|
||||||
|
|
||||||
**Date:** 2026-07-09
|
|
||||||
**Status:** Approved
|
|
||||||
**Owner:** @asepharyana
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
Proyek ini sebelumnya bernama `ultimate-asepharyana.tech` — sebuah monorepo yang berisi beberapa service aplikasi, infrastruktur, dokumentasi, dan utility scripts. Karena GitHub org `MythEclipse` kena banned, semua remote repos tidak bisa diakses. Perlu dilakukan restruktur dan rename project secara menyeluruh.
|
|
||||||
|
|
||||||
## Goals
|
|
||||||
|
|
||||||
1. Rename project dari `ultimate-asepharyana.tech` ke `asepharyana-hub`
|
|
||||||
2. Hapus service docker-manager dan teleuploader dari proyek
|
|
||||||
3. Hapus semua pointer `.git` submodule (clean slate)
|
|
||||||
4. Update `.gitmodules` dengan remote baru ke `github.com/asepharyana/*`
|
|
||||||
5. Update semua referensi: workflows, konfigurasi, dokumentasi, image names
|
|
||||||
6. Siapkan struktur lokal — inisialisasi git dan push dilakukan terpisah
|
|
||||||
|
|
||||||
## Service yang tetap dipertahankan
|
|
||||||
|
|
||||||
| Service | Path | Git remote baru |
|
|
||||||
|---------|------|----------------|
|
|
||||||
| Scraper | `apps/scraper` | `asepharyana/asepharyana-hub-scraper` |
|
|
||||||
|
|
||||||
## Service yang dihapus
|
|
||||||
|
|
||||||
| Service | Path |
|
|
||||||
|---------|------|
|
|
||||||
| Docker Manager | `apps/docker-manager/` |
|
|
||||||
| TeleUploader | `apps/teleuploader/` |
|
|
||||||
| Elysia API | `apps/elysia/` |
|
|
||||||
| React Frontend | `apps/react/` |
|
|
||||||
| Rust Auth | `apps/rust-auth/` |
|
|
||||||
|
|
||||||
## File yang akan dihapus (gelombang 1)
|
|
||||||
|
|
||||||
- `apps/docker-manager/` (seluruh direktori)
|
|
||||||
- `apps/teleuploader/` (seluruh direktori)
|
|
||||||
- `infra/compose/docker-manager.yml`
|
|
||||||
- `infra/compose/teleuploader.yml`
|
|
||||||
- `infra/docker/docker-manager.Dockerfile`
|
|
||||||
- `infra/docker/teleuploader.Dockerfile`
|
|
||||||
|
|
||||||
## File pointer `.git` submodule yang dihapus
|
|
||||||
|
|
||||||
- `apps/elysia/.git`
|
|
||||||
- `apps/react/.git`
|
|
||||||
- `apps/scraper/.git`
|
|
||||||
- `apps/rust-auth/.git`
|
|
||||||
|
|
||||||
## Rename mapping
|
|
||||||
|
|
||||||
| Lokasi | Dari | Ke |
|
|
||||||
|--------|------|----|
|
|
||||||
| `package.json` `name` | `ultimate-asepharyana.tech` | `asepharyana-hub` |
|
|
||||||
| `README.md` | judul & path references | `asepharyana-hub` |
|
|
||||||
| `ARCHITECTURE.md` | directory tree, paths | `asepharyana-hub` |
|
|
||||||
| `CONTRIBUTING.md` | repo names & paths | `asepharyana-hub-*` |
|
|
||||||
| `infra/README.md` | deskripsi | `asepharyana-hub` |
|
|
||||||
| `.env.example` | `TRAEFIK_CONFIG_PATH` | `asepharyana-hub` |
|
|
||||||
| Perlengkapan infra Traefik | path config references | `asepharyana-hub` |
|
|
||||||
| `scripts/cleanup-ghcr.sh` | `MythEclipse` | `asepharyana` |
|
|
||||||
| `docs/add-new-app.md` | path references | `asepharyana-hub` |
|
|
||||||
| `docs/adr/*.md` | path references | `asepharyana-hub` |
|
|
||||||
|
|
||||||
## Image name migration (GHCR)
|
|
||||||
|
|
||||||
| Lama | Baru |
|
|
||||||
|------|------|
|
|
||||||
| `ghcr.io/mytheclipse/ultimate-asepharyana.tech/scraper-api:*` | `ghcr.io/asepharyana/asepharyana-hub/scraper-api:*` |
|
|
||||||
| `ghcr.io/mytheclipse/ultimate-asepharyana.tech/elysia-api:*` | — (dihapus) |
|
|
||||||
| `ghcr.io/mytheclipse/ultimate-asepharyana.tech/react-web:*` | — (dihapus) |
|
|
||||||
| `ghcr.io/mytheclipse/ultimate-asepharyana.tech/rust-auth:*` | — (dihapus) |
|
|
||||||
| `ghcr.io/mytheclipse/ultimate-asepharyana.tech/docker-manager:*` | — (dihapus) |
|
|
||||||
| `ghcr.io/mytheclipse/ultimate-asepharyana.tech/teleuploader:*` | — (dihapus) |
|
|
||||||
|
|
||||||
## Workflow changes
|
|
||||||
|
|
||||||
### `docker-build-push.yml`
|
|
||||||
- `IMAGE_NAME_PREFIX`: `mytheclipse/ultimate-asepharyana.tech` → `asepharyana/asepharyana-hub`
|
|
||||||
- Hapus service entries: `docker-manager`, `teleuploader`, `elysia-api`, `react-web`, `rust-auth`
|
|
||||||
- Update repo URLs di `wait-submodule-ref` dari `MythEclipse/*` ke `asepharyana/*`
|
|
||||||
- Update `update-manifest` phase — hapus service docker-manager, teleuploader, elysia-api, react-web, rust-auth
|
|
||||||
|
|
||||||
### `deploy-docker.yml`
|
|
||||||
- Update `git remote add origin`
|
|
||||||
- Hapus docker-manager, teleuploader, elysia-api, react-web, rust-auth dari compose list dan checkout
|
|
||||||
|
|
||||||
### `update-submodule.yml`
|
|
||||||
- Hapus service docker-manager, teleuploader, elysia-api, react-web, rust-auth
|
|
||||||
|
|
||||||
## `.gitmodules`
|
|
||||||
|
|
||||||
Hanya berisi 1 app scraper:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[submodule "apps/scraper"]
|
|
||||||
path = apps/scraper
|
|
||||||
url = https://github.com/asepharyana/asepharyana-hub-scraper.git
|
|
||||||
```
|
|
||||||
|
|
||||||
## Execution plan
|
|
||||||
|
|
||||||
1. Hapus docker-manager & teleuploader direktori + file infra
|
|
||||||
2. Hapus `.git` pointer di submodule
|
|
||||||
3. Update `.gitmodules`
|
|
||||||
4. Update `package.json`
|
|
||||||
5. Update `README.md`, `ARCHITECTURE.md`, `CONTRIBUTING.md`
|
|
||||||
6. Update `infra/compose/*.yml` image tags
|
|
||||||
7. Update `.env.example`, `infra/README.md`, docs traefik
|
|
||||||
8. Update `docker-build-push.yml`
|
|
||||||
9. Update `deploy-docker.yml`
|
|
||||||
10. Update `update-submodule.yml`
|
|
||||||
11. Update `scripts/cleanup-ghcr.sh`
|
|
||||||
12. Update `docs/add-new-app.md`
|
|
||||||
13. Update `docs/adr/*.md` path references (historical)
|
|
||||||
|
|
||||||
## Post-execution state
|
|
||||||
|
|
||||||
- Root direktori `asepharyana-hub/` dengan source code apps utuh (tanpa git)
|
|
||||||
- 1 app submodule (scraper) terdaftar di `.gitmodules` dengan remote baru
|
|
||||||
- Infra/docs/scripts tetap menyatu di root
|
|
||||||
- 0 references ke `MythEclipse/ultimate-asepharyana.tech` di file konfigurasi
|
|
||||||
- Siap untuk `git init && git add && git commit` kapan saja
|
|
||||||
+1
-3
@@ -60,9 +60,7 @@ Traefik reads dynamic config from `infra/traefik/dynamic/`:
|
|||||||
|
|
||||||
- `apps.yaml` — routers and upstream services
|
- `apps.yaml` — routers and upstream services
|
||||||
- `middlewares.yaml` — shared middleware chains
|
- `middlewares.yaml` — shared middleware chains
|
||||||
- `ssl.yaml` — TLS certificates
|
- `ssl.yaml` — TLS certificates for `asepharyana.my.id` and `asepharyana.web.id`
|
||||||
|
|
||||||
The primary certificate intentionally pairs `asephstech.pem` with `asephscloud.key` to preserve the current production layout.
|
|
||||||
|
|
||||||
## Validation
|
## Validation
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
scraper-api:
|
scraper-api:
|
||||||
container_name: scraper-api
|
container_name: scraper-api
|
||||||
image: ghcr.io/asepharyana/asepharyana-hub/scraper-api:sha-b199f8c
|
image: ghcr.io/asepharyana/asepharyana-hub/scraper-api:latest
|
||||||
restart: always
|
restart: always
|
||||||
networks:
|
networks:
|
||||||
app-shared-net:
|
app-shared-net:
|
||||||
@@ -9,12 +9,16 @@ services:
|
|||||||
- scraper-api
|
- scraper-api
|
||||||
env_file:
|
env_file:
|
||||||
- ../../.env
|
- ../../.env
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD', 'curl', '-f', 'http://localhost:4091/health']
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 15s
|
||||||
environment:
|
environment:
|
||||||
- REDIS_URL=redis://redis:6379
|
- REDIS_URL=redis://redis:6379
|
||||||
- JWT_SECRET=${JWT_SECRET:?JWT_SECRET is required}
|
- JWT_SECRET=${JWT_SECRET:?JWT_SECRET is required}
|
||||||
- DATABASE_URL=${DATABASE_URL}
|
- DATABASE_URL=${DATABASE_URL}
|
||||||
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otlp-metrics-backend:4317
|
|
||||||
- OTEL_SERVICE_NAME=scraper-api
|
|
||||||
networks:
|
networks:
|
||||||
app-shared-net:
|
app-shared-net:
|
||||||
name: app-shared-net
|
name: app-shared-net
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ services:
|
|||||||
- redis
|
- redis
|
||||||
ports:
|
ports:
|
||||||
- '${SHARED_REDIS_EXPOSE:-127.0.0.1:6379:6379}'
|
- '${SHARED_REDIS_EXPOSE:-127.0.0.1:6379:6379}'
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD', 'redis-cli', 'ping']
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 3
|
||||||
|
start_period: 5s
|
||||||
volumes:
|
volumes:
|
||||||
- 'redis_data:/data'
|
- 'redis_data:/data'
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,13 @@ services:
|
|||||||
- '--experimental.plugins.real-ip.version=v1.0.3'
|
- '--experimental.plugins.real-ip.version=v1.0.3'
|
||||||
- '--experimental.plugins.blockpath.moduleName=github.com/traefik/plugin-blockpath'
|
- '--experimental.plugins.blockpath.moduleName=github.com/traefik/plugin-blockpath'
|
||||||
- '--experimental.plugins.blockpath.version=v0.2.1'
|
- '--experimental.plugins.blockpath.version=v0.2.1'
|
||||||
|
- '--ping=true'
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD', 'wget', '--spider', 'http://localhost:8080/ping']
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 15s
|
||||||
environment:
|
environment:
|
||||||
- DOCKER_API_VERSION=1.41
|
- DOCKER_API_VERSION=1.41
|
||||||
- GOMEMLIMIT=4096MiB
|
- GOMEMLIMIT=4096MiB
|
||||||
@@ -56,10 +63,10 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
- ${TRAEFIK_CONFIG_PATH:-/root/asepharyana-hub/infra/traefik/dynamic}:/etc/traefik/dynamic:ro
|
- ${TRAEFIK_CONFIG_PATH:-/root/asepharyana-hub/infra/traefik/dynamic}:/etc/traefik/dynamic:ro
|
||||||
- ${TRAEFIK_CERT_ASEPHARYANA_MY_ID_PEM:-/root/asepharyana.my.id.pem}:/etc/traefik/certs/asepharyana.my.id.pem:ro
|
- ${TRAEFIK_CERT_MY_ID_PEM:-/root/asepharyana.my.id.pem}:/etc/traefik/certs/asepharyana.my.id.pem:ro
|
||||||
- ${TRAEFIK_CERT_ASEPHARYANA_MY_ID_KEY:-/root/asepharyana.my.id.key}:/etc/traefik/certs/asepharyana.my.id.key:ro
|
- ${TRAEFIK_CERT_MY_ID_KEY:-/root/asepharyana.my.id.key}:/etc/traefik/certs/asepharyana.my.id.key:ro
|
||||||
- ${TRAEFIK_CERT_ASEPHARYANA_WEB_ID_PEM:-/root/asepharyana.web.id.pem}:/etc/traefik/certs/asepharyana.web.id.pem:ro
|
- ${TRAEFIK_CERT_WEB_ID_PEM:-/root/asepharyana.web.id.pem}:/etc/traefik/certs/asepharyana.web.id.pem:ro
|
||||||
- ${TRAEFIK_CERT_ASEPHARYANA_WEB_ID_KEY:-/root/asepharyana.web.id.key}:/etc/traefik/certs/asepharyana.web.id.key:ro
|
- ${TRAEFIK_CERT_WEB_ID_KEY:-/root/asepharyana.web.id.key}:/etc/traefik/certs/asepharyana.web.id.key:ro
|
||||||
labels:
|
labels:
|
||||||
- 'traefik.enable=true'
|
- 'traefik.enable=true'
|
||||||
- 'traefik.http.routers.traefik.rule=Host(`traefik.asepharyana.my.id`) || Host(`traefik.asepharyana.web.id`)'
|
- 'traefik.http.routers.traefik.rule=Host(`traefik.asepharyana.my.id`) || Host(`traefik.asepharyana.web.id`)'
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
-- Create database if not exists
|
|
||||||
CREATE DATABASE IF NOT EXISTS `tracer_study`;
|
|
||||||
|
|
||||||
-- Create dedicated user for tracer_study
|
|
||||||
CREATE USER IF NOT EXISTS 'tracerstudy'@'%' IDENTIFIED BY 'tracerstudy_secret';
|
|
||||||
GRANT ALL PRIVILEGES ON `tracer_study`.* TO 'tracerstudy'@'%';
|
|
||||||
|
|
||||||
|
|
||||||
-- Flush privileges
|
|
||||||
FLUSH PRIVILEGES;
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,49 +1,36 @@
|
|||||||
# Use cargo-chef for dependency caching
|
# ── Build stage: cargo-chef for dependency caching ──
|
||||||
FROM lukemathwalker/cargo-chef:latest-rust-1.89.0 AS chef
|
FROM lukemathwalker/cargo-chef:latest-rust-1.89.0 AS chef
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install Node.js if needed for build scripts
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
nodejs \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
FROM chef AS planner
|
FROM chef AS planner
|
||||||
COPY apps/scraper .
|
COPY apps/scraper .
|
||||||
RUN cargo chef prepare --recipe-path recipe.json
|
RUN cargo chef prepare --recipe-path recipe.json
|
||||||
|
|
||||||
FROM chef AS builder
|
FROM chef AS builder
|
||||||
COPY --from=planner /app/recipe.json recipe.json
|
COPY --from=planner /app/recipe.json recipe.json
|
||||||
# Utilize buildkit cache mounts for cargo
|
|
||||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||||
--mount=type=cache,target=/app/target \
|
--mount=type=cache,target=/app/target \
|
||||||
cargo chef cook --release --recipe-path recipe.json
|
cargo chef cook --release --recipe-path recipe.json
|
||||||
|
|
||||||
# Build application
|
|
||||||
COPY apps/scraper .
|
COPY apps/scraper .
|
||||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||||
--mount=type=cache,target=/app/target \
|
--mount=type=cache,target=/app/target \
|
||||||
cargo build --release && \
|
cargo build --release && \
|
||||||
cp target/release/scraper /app/scraper
|
cp target/release/scraper /app/scraper
|
||||||
|
|
||||||
# Final runtime image
|
# ── Runtime image ──
|
||||||
FROM debian:bookworm-slim AS runtime
|
FROM debian:bookworm-slim AS runtime
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
libssl3 \
|
libssl3 \
|
||||||
chromium \
|
|
||||||
fonts-liberation \
|
|
||||||
fonts-noto-color-emoji \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Add non-root user
|
|
||||||
RUN groupadd -g 1001 appgroup && \
|
RUN groupadd -g 1001 appgroup && \
|
||||||
useradd -u 1001 -g appgroup -s /bin/sh appuser
|
useradd -u 1001 -g appgroup -s /bin/sh appuser
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=builder /app/scraper /app/scraper
|
COPY --from=builder /app/scraper /app/scraper
|
||||||
|
|
||||||
# Run as non-root
|
|
||||||
USER appuser
|
USER appuser
|
||||||
|
|
||||||
EXPOSE 4091
|
EXPOSE 4091
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ All certificate paths support environment variable substitution with safe fallba
|
|||||||
|
|
||||||
### Configuration Variables
|
### Configuration Variables
|
||||||
|
|
||||||
| Variable | Description | Default Path | Purpose |
|
| Variable | Description | Default Path | Purpose |
|
||||||
| ------------------------------------- | --------------------------------------------------------------- | --------------------------------------------- | ------------------------------------------------------- |
|
| --------------------------- | --------------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------- |
|
||||||
| `TRAEFIK_CONFIG_PATH` | Directory containing dynamic Traefik configuration files (YAML) | `/root/asepharyana-hub/infra/traefik/dynamic` | Location of middleware, router, and service definitions |
|
| `TRAEFIK_CONFIG_PATH` | Directory containing dynamic Traefik configuration files (YAML) | `/root/asepharyana-hub/infra/traefik/dynamic` | Location of middleware, router, and service definitions |
|
||||||
| `TRAEFIK_CERT_ASEPHARYANA_MY_ID_PEM` | Certificate file for asepharyana.my.id | `/root/asepharyana.my.id.pem` | SSL/TLS certificate for asepharyana.my.id domain |
|
| `TRAEFIK_CERT_MY_ID_PEM` | Certificate file for asepharyana.my.id | `/root/asepharyana.my.id.pem` | SSL/TLS certificate for asepharyana.my.id domain |
|
||||||
| `TRAEFIK_CERT_ASEPHARYANA_MY_ID_KEY` | Key file for asepharyana.my.id | `/root/asepharyana.my.id.key` | SSL/TLS private key for asepharyana.my.id domain |
|
| `TRAEFIK_CERT_MY_ID_KEY` | Key file for asepharyana.my.id | `/root/asepharyana.my.id.key` | SSL/TLS private key for asepharyana.my.id domain |
|
||||||
| `TRAEFIK_CERT_ASEPHARYANA_WEB_ID_PEM` | Certificate file for asepharyana.web.id | `/root/asepharyana.web.id.pem` | SSL/TLS certificate for asepharyana.web.id domain |
|
| `TRAEFIK_CERT_WEB_ID_PEM` | Certificate file for asepharyana.web.id | `/root/asepharyana.web.id.pem` | SSL/TLS certificate for asepharyana.web.id domain |
|
||||||
| `TRAEFIK_CERT_ASEPHARYANA_WEB_ID_KEY` | Key file for asepharyana.web.id | `/root/asepharyana.web.id.key` | SSL/TLS private key for asepharyana.web.id domain |
|
| `TRAEFIK_CERT_WEB_ID_KEY` | Key file for asepharyana.web.id | `/root/asepharyana.web.id.key` | SSL/TLS private key for asepharyana.web.id domain |
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -42,10 +42,10 @@ Create or update your `.env` file in the deployment directory:
|
|||||||
|
|
||||||
```env
|
```env
|
||||||
TRAEFIK_CONFIG_PATH=/root/asepharyana-hub/infra/traefik/dynamic
|
TRAEFIK_CONFIG_PATH=/root/asepharyana-hub/infra/traefik/dynamic
|
||||||
TRAEFIK_CERT_ASEPHARYANA_MY_ID_PEM=/root/asepharyana.my.id.pem
|
TRAEFIK_CERT_MY_ID_PEM=/root/asepharyana.my.id.pem
|
||||||
TRAEFIK_CERT_ASEPHARYANA_MY_ID_KEY=/root/asepharyana.my.id.key
|
TRAEFIK_CERT_MY_ID_KEY=/root/asepharyana.my.id.key
|
||||||
TRAEFIK_CERT_ASEPHARYANA_WEB_ID_PEM=/root/asepharyana.web.id.pem
|
TRAEFIK_CERT_WEB_ID_PEM=/root/asepharyana.web.id.pem
|
||||||
TRAEFIK_CERT_ASEPHARYANA_WEB_ID_KEY=/root/asepharyana.web.id.key
|
TRAEFIK_CERT_WEB_ID_KEY=/root/asepharyana.web.id.key
|
||||||
```
|
```
|
||||||
|
|
||||||
Then deploy:
|
Then deploy:
|
||||||
|
|||||||
@@ -65,8 +65,6 @@ http:
|
|||||||
common-chain:
|
common-chain:
|
||||||
chain:
|
chain:
|
||||||
middlewares:
|
middlewares:
|
||||||
# - real-ip
|
|
||||||
# - block-sensitive-paths
|
|
||||||
- secure-headers
|
- secure-headers
|
||||||
- compress
|
- compress
|
||||||
- retry
|
- retry
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
tls:
|
tls:
|
||||||
certificates:
|
certificates:
|
||||||
# Legacy production layout: asephstech.pem is paired with asephscloud.key.
|
|
||||||
- certFile: /etc/traefik/certs/asephstech.pem
|
|
||||||
keyFile: /etc/traefik/certs/asephscloud.key
|
|
||||||
- certFile: /etc/traefik/certs/asepharyana.my.id.pem
|
- certFile: /etc/traefik/certs/asepharyana.my.id.pem
|
||||||
keyFile: /etc/traefik/certs/asepharyana.my.id.key
|
keyFile: /etc/traefik/certs/asepharyana.my.id.key
|
||||||
- certFile: /etc/traefik/certs/asepharyana.web.id.pem
|
- certFile: /etc/traefik/certs/asepharyana.web.id.pem
|
||||||
@@ -10,5 +7,5 @@ tls:
|
|||||||
stores:
|
stores:
|
||||||
default:
|
default:
|
||||||
defaultCertificate:
|
defaultCertificate:
|
||||||
certFile: /etc/traefik/certs/asephstech.pem
|
certFile: /etc/traefik/certs/asepharyana.my.id.pem
|
||||||
keyFile: /etc/traefik/certs/asephscloud.key
|
keyFile: /etc/traefik/certs/asepharyana.my.id.key
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
api:
|
api:
|
||||||
dashboard: true
|
dashboard: true
|
||||||
insecure: true
|
insecure: false
|
||||||
|
|
||||||
log:
|
log:
|
||||||
level: INFO
|
level: INFO
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
||||||
"extends": ["config:recommended"],
|
|
||||||
"git-submodules": {
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,7 @@ if [ ! -f "$ROOT_ENV" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for parent in apps packages; do
|
for parent in apps; do
|
||||||
for dir in ./$parent/*/; do
|
for dir in ./$parent/*/; do
|
||||||
# Remove trailing slash
|
# Remove trailing slash
|
||||||
dir="${dir%/}"
|
dir="${dir%/}"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ set -e
|
|||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
ORG="asepharyana"
|
ORG="asepharyana"
|
||||||
PACKAGE_NAMES=("rust-api" "elysia-api" "nextjs-web")
|
PACKAGE_NAMES=("asepharyana-hub/scraper-api")
|
||||||
|
|
||||||
echo "🚀 Starting GHCR cleanup for $ORG..."
|
echo "🚀 Starting GHCR cleanup for $ORG..."
|
||||||
|
|
||||||
|
|||||||
+10
-58
@@ -1,65 +1,17 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Define an array of directories to aggressively prune from the search tree
|
echo "=== Updating root dependencies ==="
|
||||||
# This prevents exhaustive traversal into massive localized dependency stores, build caches,
|
bun update
|
||||||
# and anomalously created cache directories (e.g., literal '~').
|
|
||||||
PRUNE_DIRS=(
|
|
||||||
"node_modules"
|
|
||||||
"dist"
|
|
||||||
".next"
|
|
||||||
".bun"
|
|
||||||
"~"
|
|
||||||
"build"
|
|
||||||
".git"
|
|
||||||
"target"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Construct the prune expression dynamically to feed the find command
|
echo ""
|
||||||
PRUNE_ARGS=()
|
echo "=== Updating app submodule dependencies ==="
|
||||||
for dir in "${PRUNE_DIRS[@]}"; do
|
for app in apps/*/; do
|
||||||
if [ ${#PRUNE_ARGS[@]} -gt 0 ]; then
|
if [ -f "${app}package.json" ]; then
|
||||||
PRUNE_ARGS+=("-o")
|
echo "→ $app"
|
||||||
|
(cd "$app" && bun update 2>/dev/null && echo " ✓ $app updated") || echo " - skipping $app (no bun setup)"
|
||||||
fi
|
fi
|
||||||
PRUNE_ARGS+=("-name" "$dir")
|
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Scanning for top-level and workspace package.json manifests..."
|
echo ""
|
||||||
|
echo "✅ All dependencies updated"
|
||||||
# Perform an optimized find:
|
|
||||||
# 1. -prune halts traversal immediately upon matching a PRUNE_DIR, achieving extreme I/O efficiency.
|
|
||||||
# 2. -print0 strictly streams zero-byte delimited paths, immunizing the loop against spaces/newlines.
|
|
||||||
find . \( "${PRUNE_ARGS[@]}" \) -prune -o -name "package.json" -type f -print0 | while IFS= read -r -d '' filename; do
|
|
||||||
dir=$(dirname "$filename")
|
|
||||||
|
|
||||||
echo "--------------------------------------------------------------------------------"
|
|
||||||
echo "Initiating strict dependency update sequence in: $dir"
|
|
||||||
|
|
||||||
# Spawn a tightly scoped subshell. This isolates environment state and traps internal pathing failures.
|
|
||||||
(
|
|
||||||
# Strict directory entry checks.
|
|
||||||
cd "$dir" || {
|
|
||||||
echo "CRITICAL: Directory transition failed for $dir. Process aborted." >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Heuristic check: Ensure the manifest actually declares dependencies before thrashing the disk with ncu.
|
|
||||||
if ! grep -Eq '"(dependencies|devDependencies|peerDependencies)"[[:space:]]*:' package.json; then
|
|
||||||
echo "Notice: No valid dependency blocks detected in $dir/package.json. Bypassing node traversal."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Delegate command resolution to bunx. This obliterates the 'command not found' failure mode
|
|
||||||
# by dynamically sourcing the npm-check-updates binary, completely ignoring global namespace pollution.
|
|
||||||
echo "Executing constraint-free updates via bunx..."
|
|
||||||
bunx --bun npm-check-updates -u
|
|
||||||
|
|
||||||
echo "Commencing rigorous package installation phase..."
|
|
||||||
bun install
|
|
||||||
) || {
|
|
||||||
echo "WARNING: Subshell failure encountered in $dir. Subsystem continues." >&2
|
|
||||||
}
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "--------------------------------------------------------------------------------"
|
|
||||||
echo "SYSTEM STATE: All traversable dependencies aggressively updated."
|
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
PROMETHEUS_VERSION="2.53.0"
|
|
||||||
TAILSCALE_IP="100.108.1.124"
|
|
||||||
PROMETHEUS_DIR="/etc/prometheus"
|
|
||||||
PROMETHEUS_DATA_DIR="/var/lib/prometheus"
|
|
||||||
|
|
||||||
echo "Creating prometheus user and group..."
|
|
||||||
if ! getent group prometheus >/dev/null; then
|
|
||||||
groupadd --system prometheus
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! getent passwd prometheus >/dev/null; then
|
|
||||||
useradd --system -g prometheus --no-create-home --shell /usr/sbin/nologin prometheus
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Creating directories..."
|
|
||||||
mkdir -p "$PROMETHEUS_DIR" "$PROMETHEUS_DATA_DIR"
|
|
||||||
chown prometheus:prometheus "$PROMETHEUS_DIR" "$PROMETHEUS_DATA_DIR"
|
|
||||||
|
|
||||||
echo "Downloading Prometheus v${PROMETHEUS_VERSION}..."
|
|
||||||
cd /tmp
|
|
||||||
wget -qO prometheus.tar.gz "https://github.com/prometheus/prometheus/releases/download/v${PROMETHEUS_VERSION}/prometheus-${PROMETHEUS_VERSION}.linux-amd64.tar.gz"
|
|
||||||
|
|
||||||
echo "Extracting Prometheus..."
|
|
||||||
tar -xf prometheus.tar.gz
|
|
||||||
cd "prometheus-${PROMETHEUS_VERSION}.linux-amd64"
|
|
||||||
|
|
||||||
echo "Installing binaries..."
|
|
||||||
install -m 0755 -o prometheus -g prometheus prometheus promtool /usr/local/bin/
|
|
||||||
|
|
||||||
echo "Installing consoles and libraries..."
|
|
||||||
cp -a consoles console_libraries "$PROMETHEUS_DIR/"
|
|
||||||
chown -R prometheus:prometheus "$PROMETHEUS_DIR/consoles" "$PROMETHEUS_DIR/console_libraries"
|
|
||||||
|
|
||||||
echo "Creating configuration file..."
|
|
||||||
cat <<EOF > "${PROMETHEUS_DIR}/prometheus.yml"
|
|
||||||
global:
|
|
||||||
scrape_interval: 15s
|
|
||||||
evaluation_interval: 15s
|
|
||||||
|
|
||||||
scrape_configs:
|
|
||||||
- job_name: "prometheus"
|
|
||||||
static_configs:
|
|
||||||
- targets: ["${TAILSCALE_IP}:9090"]
|
|
||||||
EOF
|
|
||||||
chown prometheus:prometheus "${PROMETHEUS_DIR}/prometheus.yml"
|
|
||||||
chmod 0644 "${PROMETHEUS_DIR}/prometheus.yml"
|
|
||||||
|
|
||||||
echo "Creating systemd unit file..."
|
|
||||||
cat <<EOF > /etc/systemd/system/prometheus.service
|
|
||||||
[Unit]
|
|
||||||
Description=Prometheus Time Series Collection and Processing Server
|
|
||||||
Wants=network-online.target
|
|
||||||
After=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
User=prometheus
|
|
||||||
Group=prometheus
|
|
||||||
Type=simple
|
|
||||||
ExecStart=/usr/local/bin/prometheus \\
|
|
||||||
--config.file=${PROMETHEUS_DIR}/prometheus.yml \\
|
|
||||||
--storage.tsdb.path=${PROMETHEUS_DATA_DIR} \\
|
|
||||||
--web.console.templates=${PROMETHEUS_DIR}/consoles \\
|
|
||||||
--web.console.libraries=${PROMETHEUS_DIR}/console_libraries \\
|
|
||||||
--web.listen-address=${TAILSCALE_IP}:9090 \\
|
|
||||||
--web.external-url=http://${TAILSCALE_IP}:9090/
|
|
||||||
ExecReload=/bin/kill -HUP \$MAINPID
|
|
||||||
Restart=on-failure
|
|
||||||
RestartSec=5s
|
|
||||||
LimitNOFILE=65536
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOF
|
|
||||||
chmod 0644 /etc/systemd/system/prometheus.service
|
|
||||||
|
|
||||||
echo "Reloading systemd, enabling and starting prometheus..."
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl enable prometheus
|
|
||||||
systemctl restart prometheus
|
|
||||||
|
|
||||||
echo "Checking Prometheus status..."
|
|
||||||
systemctl status prometheus --no-pager || true
|
|
||||||
|
|
||||||
echo "Prometheus setup completed successfully!"
|
|
||||||
Reference in New Issue
Block a user