refactor: rename hub-guide → code-guide, remove project-specific content
- Rename all references from hub-guide to code-guide - Remove project-specific hub-guide skill (100% Asepharyana Hub specific) - Genericize examples in monorepo, docker, ci-cd, engineering-principles skills - Use generic service names (frontend, api, worker instead of hub, scraper) - Use generic registry paths instead of ghcr.io/asepharyana/asepharyana-hub - Remove project-specific deployment and infrastructure references
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
# hub-guide — Comprehensive Programming Best-Practice Plugin
|
# code-guide — Comprehensive Programming Best-Practice Plugin
|
||||||
|
|
||||||
A Claude Code plugin serving as a complete engineering guide for **all programming situations** — monorepo, standalone, any language, any framework.
|
A Claude Code plugin serving as a complete engineering guide for **all programming situations** — monorepo, standalone, any language, any framework.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### 🧠 24 Best-Practice Skills
|
### 24 Best-Practice Skills
|
||||||
|
|
||||||
| Category | Skills |
|
| Category | Skills |
|
||||||
|----------|--------|
|
|----------|--------|
|
||||||
@@ -13,27 +13,21 @@ A Claude Code plugin serving as a complete engineering guide for **all programmi
|
|||||||
| **Frameworks** | react-frontend, elysiajs, hono-backend, drizzle-database, nextjs |
|
| **Frameworks** | react-frontend, elysiajs, hono-backend, drizzle-database, nextjs |
|
||||||
| **Infrastructure** | docker, ci-cd, monitoring |
|
| **Infrastructure** | docker, ci-cd, monitoring |
|
||||||
| **Monorepo** | monorepo (patterns + submodules + workspace tooling) |
|
| **Monorepo** | monorepo (patterns + submodules + workspace tooling) |
|
||||||
| **Hub-specific** | hub-guide (Asepharyana Hub monorepo infra & workflow) |
|
|
||||||
|
|
||||||
Skills activate automatically when Claude detects relevant context (language, framework, topic).
|
Skills activate automatically when Claude detects relevant context (language, framework, topic).
|
||||||
|
|
||||||
### ⚡ Hooks for Auto-Detection
|
### Hooks for Auto-Activation
|
||||||
|
|
||||||
| Hook | Trigger | What It Does |
|
| Hook | Trigger | What It Does |
|
||||||
|------|---------|--------------|
|
|------|---------|--------------|
|
||||||
| **SessionStart** | Session begins | Detects project type from files (package.json, Cargo.toml, go.mod, etc.) and activates relevant skills |
|
| **Stop** | Before stopping | Verifies mandatory skills are being applied (engineering-principles, clean-code, clean-architecture, testing, error-handling, security, git-workflow, api-design) |
|
||||||
| **PreToolUse** | Before Write/Edit | Detects file extension and injects language-specific best-practice rules |
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Quick Install
|
### Quick Install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone
|
# From anywhere with access to the plugin directory
|
||||||
git clone https://github.com/asepharyana/asepharyana-hub-hub-guide.git
|
|
||||||
cd asepharyana-hub-hub-guide
|
|
||||||
|
|
||||||
# Install as one unit (recommended)
|
|
||||||
./install.sh
|
./install.sh
|
||||||
|
|
||||||
# Or symlink (edits in this repo are live)
|
# Or symlink (edits in this repo are live)
|
||||||
@@ -49,37 +43,19 @@ Example triggers:
|
|||||||
- *"Write a test for this"* → `testing` activates
|
- *"Write a test for this"* → `testing` activates
|
||||||
- *"Design an API endpoint"* → `api-design` activates
|
- *"Design an API endpoint"* → `api-design` activates
|
||||||
- Working with `.ts` files → `typescript` activates
|
- Working with `.ts` files → `typescript` activates
|
||||||
- Project with `Cargo.toml` → `rust` activates (SessionStart hook)
|
- Project with `Cargo.toml` → `rust` activates
|
||||||
|
|
||||||
## Auto-Detection (Hooks)
|
|
||||||
|
|
||||||
When you start a session in a project, the SessionStart hook scans for:
|
|
||||||
|
|
||||||
| File | Skills Activated |
|
|
||||||
|------|-----------------|
|
|
||||||
| `tsconfig.json` | typescript, react-frontend, elysiajs, hono-backend, drizzle-database |
|
|
||||||
| `Cargo.toml` | rust |
|
|
||||||
| `go.mod` | go |
|
|
||||||
| `pyproject.toml` | python |
|
|
||||||
| `pnpm-workspace.yaml` | monorepo |
|
|
||||||
| `Dockerfile` | docker |
|
|
||||||
| `.github/workflows/` | ci-cd |
|
|
||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
hub-guide/
|
code-guide/
|
||||||
├── .claude-plugin/
|
├── .claude-plugin/
|
||||||
│ └── plugin.json # Plugin manifest
|
│ └── plugin.json # Plugin manifest
|
||||||
├── hooks/
|
├── hooks/
|
||||||
│ ├── hooks.json # Hook configuration
|
│ └── hooks.json # Hook configuration (Stop prompt)
|
||||||
│ └── scripts/
|
|
||||||
│ ├── detect-project.sh # SessionStart auto-detection
|
|
||||||
│ └── detect-file-type.sh # PreToolWrite language detection
|
|
||||||
├── skills/
|
├── skills/
|
||||||
│ ├── clean-code/ # +23 more skill directories
|
│ ├── clean-code/ # 23 more skill directories
|
||||||
│ ├── ...
|
│ └── ...
|
||||||
│ └── hub-guide/ # Existing hub monorepo guide
|
|
||||||
└── README.md
|
└── README.md
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -87,11 +63,7 @@ hub-guide/
|
|||||||
|
|
||||||
Skills are in `skills/<name>/SKILL.md` format (modern Claude Code plugin convention). Each skill includes:
|
Skills are in `skills/<name>/SKILL.md` format (modern Claude Code plugin convention). Each skill includes:
|
||||||
|
|
||||||
- **Frontmatter** — `name` and `description` with specific trigger phrases
|
- **Frontmatter** — `name` and `description` with trigger context
|
||||||
- **Lean body** — key rules, examples, and anti-patterns (~1500-2000 words)
|
- **Lean body** — key rules, examples, and anti-patterns
|
||||||
|
|
||||||
Hooks use bash scripts. Edits to hooks/scripts/ take effect immediately when installed as a symlink.
|
The Stop hook injects reminders when mandatory skills aren't being applied.
|
||||||
|
|
||||||
## Related
|
|
||||||
|
|
||||||
Based on patterns from [kana-best-practice-engineering](https://github.com/asepharyana/kana-best-practice-engineering).
|
|
||||||
|
|||||||
+19
-68
@@ -1,82 +1,33 @@
|
|||||||
# install.ps1 — hub-guide plugin installer for Claude Code (Windows)
|
# install.ps1 — code-guide plugin installer for Claude Code (Windows)
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# .\install.ps1 # Install to %USERPROFILE%\.claude\plugins\
|
# .\install.ps1 # Install to %USERPROFILE%\.claude\skills\
|
||||||
# .\install.ps1 -Project # Install to .claude\plugins\ (project-scoped)
|
# .\install.ps1 -Link # Symlink (PowerShell Admin/Dev mode)
|
||||||
# .\install.ps1 -Link # Symlink (PowerShell Admin/Dev mode)
|
|
||||||
# .\install.ps1 -NoHooks # Skills only
|
|
||||||
# .\install.ps1 clean-code testing # Install specific skills
|
|
||||||
|
|
||||||
param(
|
param(
|
||||||
[switch]$Project,
|
[switch]$Link
|
||||||
[switch]$Link,
|
|
||||||
[switch]$NoHooks,
|
|
||||||
[Parameter(Position=0, ValueFromRemainingArguments=$true)]
|
|
||||||
[string[]]$SkillFilter
|
|
||||||
)
|
)
|
||||||
|
|
||||||
$PluginName = "hub-guide"
|
$PluginName = "code-guide"
|
||||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$TargetDir = Join-Path $env:USERPROFILE ".claude\skills"
|
||||||
# Determine target
|
|
||||||
if ($Project) {
|
|
||||||
$TargetDir = Join-Path (Get-Location) ".claude\plugins"
|
|
||||||
} else {
|
|
||||||
$TargetDir = Join-Path $env:USERPROFILE ".claude\plugins"
|
|
||||||
}
|
|
||||||
|
|
||||||
$PluginDir = Join-Path $TargetDir $PluginName
|
$PluginDir = Join-Path $TargetDir $PluginName
|
||||||
|
|
||||||
Write-Host "📐 hub-guide installer" -ForegroundColor Cyan
|
Write-Host "code-guide installer" -ForegroundColor Cyan
|
||||||
|
|
||||||
# Ensure target exists
|
New-Item -ItemType Directory -Force -Path $TargetDir | Out-Null
|
||||||
New-Item -ItemType Directory -Force -Path $PluginDir | Out-Null
|
|
||||||
|
|
||||||
# Install manifest
|
|
||||||
Copy-Item -Path (Join-Path $ScriptDir ".claude-plugin") -Destination $PluginDir -Recurse -Force
|
|
||||||
|
|
||||||
# Install hooks
|
|
||||||
if (-not $NoHooks) {
|
|
||||||
Write-Host " hooks/ → $PluginDir\hooks\"
|
|
||||||
if ($Link) {
|
|
||||||
New-Item -ItemType SymbolicLink -Path "$PluginDir\hooks" -Target (Join-Path $ScriptDir "hooks") -Force | Out-Null
|
|
||||||
} else {
|
|
||||||
Copy-Item -Path (Join-Path $ScriptDir "hooks") -Destination $PluginDir -Recurse -Force
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Install skills
|
|
||||||
if ($SkillFilter.Count -gt 0) {
|
|
||||||
$SkillDir = Join-Path $PluginDir "skills"
|
|
||||||
New-Item -ItemType Directory -Force -Path $SkillDir | Out-Null
|
|
||||||
foreach ($skill in $SkillFilter) {
|
|
||||||
$skillName = Split-Path $skill -Leaf
|
|
||||||
$src = Join-Path $ScriptDir "skills" $skillName
|
|
||||||
if (Test-Path $src) {
|
|
||||||
Write-Host " skills/$skillName/ → $SkillDir"
|
|
||||||
if ($Link) {
|
|
||||||
New-Item -ItemType SymbolicLink -Path (Join-Path $SkillDir $skillName) -Target $src -Force | Out-Null
|
|
||||||
} else {
|
|
||||||
Copy-Item -Path $src -Destination $SkillDir -Recurse -Force
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Write-Host " ⚠️ Skill '$skillName' not found at $src" -ForegroundColor Yellow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Write-Host " skills/ (all) → $PluginDir\skills\"
|
|
||||||
if ($Link) {
|
|
||||||
New-Item -ItemType SymbolicLink -Path (Join-Path $PluginDir "skills") -Target (Join-Path $ScriptDir "skills") -Force | Out-Null
|
|
||||||
} else {
|
|
||||||
Copy-Item -Path (Join-Path $ScriptDir "skills") -Destination $PluginDir -Recurse -Force
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "✅ hub-guide installed to $PluginDir" -ForegroundColor Green
|
|
||||||
if ($Link) {
|
if ($Link) {
|
||||||
Write-Host " (symlink — edits in this repo are live)"
|
Write-Host " symlink mode"
|
||||||
|
Remove-Item -Path $PluginDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
New-Item -ItemType SymbolicLink -Path $PluginDir -Target $ScriptDir -Force | Out-Null
|
||||||
|
Write-Host " (symlink — edits in this repo are live)"
|
||||||
|
} else {
|
||||||
|
Write-Host " copy mode"
|
||||||
|
Remove-Item -Path $PluginDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
Copy-Item -Path $ScriptDir -Destination $PluginDir -Recurse -Force
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host " Restart Claude Code or run /reload to activate."
|
Write-Host "Done. Restart Claude Code or run /reload."
|
||||||
Write-Host " Skills auto-trigger when you work — no commands needed."
|
Write-Host "Skills auto-trigger. Hooks auto-load from hooks/hooks.json."
|
||||||
|
|||||||
+4
-4
@@ -1,9 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# install.sh — hub-guide installer for Claude Code
|
# install.sh — code-guide installer for Claude Code
|
||||||
# Installs the entire hub-guide directory as one unit into ~/.claude/skills/.
|
# Installs the entire code-guide directory as one unit into ~/.claude/skills/.
|
||||||
# Skills auto-discover, hooks auto-load from hooks/hooks.json.
|
# Skills auto-discover, hooks auto-load from hooks/hooks.json.
|
||||||
# Usage:
|
# Usage:
|
||||||
# ./install.sh # Copy hub-guide to ~/.claude/skills/
|
# ./install.sh # Copy code-guide to ~/.claude/skills/
|
||||||
# ./install.sh --link # Symlink (edits live)
|
# ./install.sh --link # Symlink (edits live)
|
||||||
# Requires: Claude Code
|
# Requires: Claude Code
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ echo " target: ${SKILLS_DIR}/code-guide/"
|
|||||||
echo " mode: $([ "$LINK_MODE" = true ] && echo 'symlink' || echo 'copy')"
|
echo " mode: $([ "$LINK_MODE" = true ] && echo 'symlink' || echo 'copy')"
|
||||||
|
|
||||||
mkdir -p "$SKILLS_DIR"
|
mkdir -p "$SKILLS_DIR"
|
||||||
DST="${SKILLS_DIR}/hub-guide"
|
DST="${SKILLS_DIR}/code-guide"
|
||||||
|
|
||||||
if [ "$LINK_MODE" = true ]; then
|
if [ "$LINK_MODE" = true ]; then
|
||||||
rm -rf "$DST"
|
rm -rf "$DST"
|
||||||
|
|||||||
+5
-5
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# setup-hooks.sh — Configure hub-guide hooks in Claude Code
|
# setup-hooks.sh — Configure code-guide hooks in Claude Code
|
||||||
#
|
#
|
||||||
# Adds hub-guide hooks to ~/.claude/settings.local.json
|
# Adds code-guide hooks to ~/.claude/settings.local.json
|
||||||
# This allows Claude to auto-detect your project and suggest relevant skills.
|
# This allows Claude to auto-detect your project and suggest relevant skills.
|
||||||
#
|
#
|
||||||
# Usage: ./setup-hooks.sh
|
# Usage: ./setup-hooks.sh
|
||||||
@@ -11,7 +11,7 @@ set -euo pipefail
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
SETTINGS_FILE="${HOME}/.claude/settings.local.json"
|
SETTINGS_FILE="${HOME}/.claude/settings.local.json"
|
||||||
|
|
||||||
echo "📐 hub-guide hooks setup"
|
echo "code-guide hooks setup"
|
||||||
|
|
||||||
python3 << PYEOF
|
python3 << PYEOF
|
||||||
import json, os
|
import json, os
|
||||||
@@ -51,8 +51,8 @@ os.makedirs(os.path.dirname(settings_file), exist_ok=True)
|
|||||||
with open(settings_file, 'w') as f:
|
with open(settings_file, 'w') as f:
|
||||||
json.dump(cfg, f, indent=2)
|
json.dump(cfg, f, indent=2)
|
||||||
|
|
||||||
print(f"✅ Hooks configured in {settings_file}")
|
print(f"Hooks configured in {settings_file}")
|
||||||
PYEOF
|
PYEOF
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo " Restart Claude Code or run /reload to activate hooks."
|
echo "Restart Claude Code or run /reload to activate hooks."
|
||||||
|
|||||||
@@ -55,16 +55,16 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
service: [scraper, hub]
|
service: [frontend, api]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- run: |
|
- run: |
|
||||||
docker build \
|
docker build \
|
||||||
-f infra/docker/${{ matrix.service }}.Dockerfile \
|
-f infra/docker/${{ matrix.service }}.Dockerfile \
|
||||||
-t ghcr.io/.../${{ matrix.service }}:sha-${{ github.sha }} \
|
-t ghcr.io/myorg/myproject/${{ matrix.service }}:sha-${{ github.sha }} \
|
||||||
-t ghcr.io/.../${{ matrix.service }}:latest \
|
-t ghcr.io/myorg/myproject/${{ matrix.service }}:latest \
|
||||||
.
|
.
|
||||||
- run: docker push --all-tags ghcr.io/.../${{ matrix.service }}
|
- run: docker push --all-tags ghcr.io/myorg/myproject/${{ matrix.service }}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Deploy (after build)**
|
**Deploy (after build)**
|
||||||
@@ -101,7 +101,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
service: [scraper, hub, api]
|
service: [frontend, api, worker]
|
||||||
fail-fast: false # Let others complete even if one fails
|
fail-fast: false # Let others complete even if one fails
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -149,10 +149,10 @@ secrets:
|
|||||||
6. **Security scan** (<5 min) — CodeQL, dependency audit, secret scan
|
6. **Security scan** (<5 min) — CodeQL, dependency audit, secret scan
|
||||||
7. **Deploy** (<2 min) — SSH, pull, restart
|
7. **Deploy** (<2 min) — SSH, pull, restart
|
||||||
|
|
||||||
## Deployment (this repo's pattern)
|
## Deployment Pattern
|
||||||
|
|
||||||
1. SSH to VPS (`orangevps`)
|
1. SSH to deployment target
|
||||||
2. Pull latest images from GHCR
|
2. Pull latest images from container registry
|
||||||
3. Restart specific container (not all)
|
3. Restart specific container (not all)
|
||||||
4. Health check after restart
|
4. Health check after restart
|
||||||
5. Rollback if health check fails
|
5. Rollback if health check fails
|
||||||
|
|||||||
+10
-12
@@ -76,12 +76,11 @@ networks:
|
|||||||
external: true
|
external: true
|
||||||
```
|
```
|
||||||
|
|
||||||
### Typical Services Layout (this repo)
|
### Typical Docker Compose Order
|
||||||
- **Redis:** `shared.yml` — always first
|
- **Data layer:** `db.yml`, `redis.yml` — stateful services first
|
||||||
- **NATS:** `nats.yml` — JetStream-enabled
|
- **Messaging:** `nats.yml`, `rabbitmq.yml` — message brokers
|
||||||
- **Dapr:** `dapr.yml` — placement service
|
- **Infrastructure:** `traefik.yml`, `nginx.yml` — reverse proxy
|
||||||
- **Traefik:** `traefik.yml` — reverse proxy
|
- **Application:** `app.yml` — service containers
|
||||||
- **App + Sidecar:** `app.yml` — service + daprd sidecar
|
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
@@ -99,17 +98,16 @@ networks:
|
|||||||
sha-<short-sha> # Immutable — for deterministic rollbacks
|
sha-<short-sha> # Immutable — for deterministic rollbacks
|
||||||
latest # Mutable — convenience
|
latest # Mutable — convenience
|
||||||
|
|
||||||
# Example (from this repo's CI)
|
# Example
|
||||||
ghcr.io/asepharyana/asepharyana-hub/<service>:sha-a1b2c3d
|
ghcr.io/myorg/myproject/<service>:sha-a1b2c3d
|
||||||
ghcr.io/asepharyana/asepharyana-hub/<service>:latest
|
ghcr.io/myorg/myproject/<service>:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
## Networking
|
## Networking
|
||||||
|
|
||||||
- **All containers** join `app-shared-net` (external Docker bridge).
|
- **All containers** join the same Docker network (external bridge).
|
||||||
- **DNS resolution** via Docker DNS (container name = hostname).
|
- **DNS resolution** via Docker DNS (container name = hostname).
|
||||||
- **Cross-VPS** via Tailscale (`100.64.0.0/10`).
|
- **Expose only needed ports** — reverse proxy handles external traffic on port 443.
|
||||||
- **Expose only needed ports** — Traefik handles external traffic on port 443.
|
|
||||||
|
|
||||||
## Debugging
|
## Debugging
|
||||||
|
|
||||||
|
|||||||
@@ -312,4 +312,4 @@ Never guess, speculate, or assume. **Every claim, suggestion, or piece of code y
|
|||||||
✅ "I searched for .env and didn't find one. There's a .env.example — maybe that's the template. Could you check?"
|
✅ "I searched for .env and didn't find one. There's a .env.example — maybe that's the template. Could you check?"
|
||||||
|
|
||||||
❌ "Dockerfiles are usually in the root"
|
❌ "Dockerfiles are usually in the root"
|
||||||
✅ "I found the Dockerfile at: infra/docker/hub.Dockerfile"
|
✅ "I found the Dockerfile at: infra/docker/app.Dockerfile"
|
||||||
|
|||||||
@@ -1,107 +0,0 @@
|
|||||||
---
|
|
||||||
name: hub-guide
|
|
||||||
description: Guide for the Asepharyana Hub monorepo — submodule workflow, infrastructure stack (Traefik, Dapr, NATS, Redis), CI/CD pipelines, adding new services, and debugging tips. Use when working in the asepharyana-hub monorepo, managing submodules, dealing with Docker/infra setup. Detects from code context and project files — not dependent on specific language keywords."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Hub Guide — Asepharyana Hub Monorepo
|
|
||||||
|
|
||||||
## Submodule Workflow
|
|
||||||
|
|
||||||
- Code changes go in the submodule repo, not here. The hub monorepo only tracks submodule pointers.
|
|
||||||
- After pushing changes to a submodule repo, update the pointer here:
|
|
||||||
```bash
|
|
||||||
cd apps/<name> && git checkout main && git pull
|
|
||||||
cd ../.. && git add apps/<name> && git commit -m "chore(deps): update <name> submodule"
|
|
||||||
```
|
|
||||||
- CI/CD auto-updates submodule pointers via `repository_dispatch`. Manual updates are fine for dev.
|
|
||||||
|
|
||||||
### Typical Submodule State
|
|
||||||
|
|
||||||
| State | Meaning |
|
|
||||||
|-------|---------|
|
|
||||||
| `(HEAD)` | Detached HEAD — submodule is at the committed pointer |
|
|
||||||
| `(main)` | On the default branch — you've done `cd apps/name && git checkout main` |
|
|
||||||
| Dirty | Uncommitted changes inside submodule |
|
|
||||||
|
|
||||||
To reset a submodule to its committed pointer:
|
|
||||||
```bash
|
|
||||||
git submodule update --init --recursive apps/<name>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development Quickstart
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make init-submodules # After fresh clone — fetches all submodules
|
|
||||||
make dev # Start Redis for local dev
|
|
||||||
docker compose -f infra/compose/shared.yml up -d # Full infra stack
|
|
||||||
```
|
|
||||||
|
|
||||||
## Local vs Production
|
|
||||||
|
|
||||||
| Aspect | Local | Production (VPS) |
|
|
||||||
|--------|-------|------------------|
|
|
||||||
| DB | None (or local) | PostgreSQL on `imrnes` via Tailscale |
|
|
||||||
| Redis | `make dev` | Container on `orangevps` |
|
|
||||||
| Traefik | Not running | TLS-terminated on `orangevps` |
|
|
||||||
| DNS | `localhost` | `*.asepharyana.my.id`, `*.asepharya.web.id` |
|
|
||||||
|
|
||||||
## Debugging Tips
|
|
||||||
|
|
||||||
### Docker compose validation
|
|
||||||
```bash
|
|
||||||
for f in infra/compose/*.yml; do docker compose -f "$f" config >/dev/null && echo "OK $f"; done
|
|
||||||
```
|
|
||||||
|
|
||||||
### YAML syntax check
|
|
||||||
```bash
|
|
||||||
python -c "import pathlib, yaml; [yaml.safe_load(open(p)) for p in pathlib.Path('infra').rglob('*.yml')]"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check submodule pointers
|
|
||||||
```bash
|
|
||||||
git submodule status
|
|
||||||
# Leading `-` = not initialized, `+` = different from committed hash, ` ` = matches
|
|
||||||
```
|
|
||||||
|
|
||||||
### Traefik route not working?
|
|
||||||
1. Check `infra/traefik/dynamic/apps.yaml` — router rule + service definition present?
|
|
||||||
2. Container labels in compose file include Traefik config?
|
|
||||||
3. Container on `app-shared-net`?
|
|
||||||
|
|
||||||
## Adding a New Service — Checklist
|
|
||||||
|
|
||||||
1. [ ] Create separate repo for app code
|
|
||||||
2. [ ] `git submodule add <url> apps/<name>`
|
|
||||||
3. [ ] Create Dockerfile in `infra/docker/`
|
|
||||||
4. [ ] Create compose file in `infra/compose/` (app + Dapr sidecar)
|
|
||||||
5. [ ] Add Traefik router in `infra/traefik/dynamic/apps.yaml`
|
|
||||||
6. [ ] Add build job in `.github/workflows/docker-build-push.yml`
|
|
||||||
7. [ ] Verify: `docker compose -f infra/compose/<name>.yml config`
|
|
||||||
|
|
||||||
See `docs/add-new-app.md` for full guide.
|
|
||||||
|
|
||||||
## Monitoring
|
|
||||||
|
|
||||||
- **Dashboard**: `/dashboard` on the hub site (auto-refresh 15s)
|
|
||||||
- **Dashboard API**: `/api/dashboard` — JSON with containers, traces, metrics
|
|
||||||
- **Prometheus**: Auto-discovers containers with `prometheus.io/scrape=true` label via Docker SD
|
|
||||||
- **Jaeger**: Traces via OTLP — check for cross-service latency
|
|
||||||
|
|
||||||
## Infrastructure Files Map
|
|
||||||
|
|
||||||
| Path | Purpose |
|
|
||||||
|------|---------|
|
|
||||||
| `infra/compose/*.yml` | One Docker Compose file per service |
|
|
||||||
| `infra/dapr/components/` | Dapr pub/sub, state store component configs |
|
|
||||||
| `infra/docker/*.Dockerfile` | Build files per service |
|
|
||||||
| `infra/traefik/dynamic/apps.yaml` | Traefik route definitions |
|
|
||||||
| `infra/traefik/traefik.yml` | Traefik static config (entrypoints, providers) |
|
|
||||||
| `.github/workflows/` | CI/CD pipelines |
|
|
||||||
| `docs/` | ADRs, deployment guide, new-app guide |
|
|
||||||
|
|
||||||
## Git Hook Scripts
|
|
||||||
|
|
||||||
Located in `scripts/`:
|
|
||||||
- `scripts/cleanup.sh` — prune old Docker images, clean temp files
|
|
||||||
- `scripts/update-deps.sh` — bump dependencies across submodules
|
|
||||||
- `scripts/setup-hooks.sh` — install local git hooks
|
|
||||||
+10
-10
@@ -19,8 +19,8 @@ description: Monorepo best practices — tooling, workspace configuration, share
|
|||||||
|
|
||||||
```
|
```
|
||||||
├── apps/
|
├── apps/
|
||||||
│ ├── hub/ # Next.js app (submodule)
|
│ ├── app1/ # Application (submodule)
|
||||||
│ └── scraper/ # Rust API (submodule)
|
│ └── app2/ # Another application (submodule)
|
||||||
├── packages/ # Shared libraries (when not submodules)
|
├── packages/ # Shared libraries (when not submodules)
|
||||||
├── infra/ # Shared infra config
|
├── infra/ # Shared infra config
|
||||||
├── pnpm-workspace.yaml
|
├── pnpm-workspace.yaml
|
||||||
@@ -61,13 +61,13 @@ pnpm -r run build
|
|||||||
- **Explicit `dependencies`** — never rely on hoisting.
|
- **Explicit `dependencies`** — never rely on hoisting.
|
||||||
- **Lock file** (`pnpm-lock.yaml`) committed — immutable installs.
|
- **Lock file** (`pnpm-lock.yaml`) committed — immutable installs.
|
||||||
|
|
||||||
## Git Submodules (this repo's pattern)
|
## Git Submodules
|
||||||
|
|
||||||
```
|
```
|
||||||
asepharyana-hub/
|
my-monorepo/
|
||||||
├── apps/
|
├── apps/
|
||||||
│ ├── hub/ → asepharyana/asepharyana-hub-hub
|
│ ├── app1/ → org/app1-repo
|
||||||
│ └── scraper/ → asepharyana/asepharyana-hub-scraper
|
│ └── app2/ → org/app2-repo
|
||||||
```
|
```
|
||||||
|
|
||||||
### Submodule Workflow
|
### Submodule Workflow
|
||||||
@@ -79,8 +79,8 @@ git submodule update --init --recursive
|
|||||||
git submodule foreach git pull origin main
|
git submodule foreach git pull origin main
|
||||||
|
|
||||||
# Update one submodule
|
# Update one submodule
|
||||||
cd apps/hub && git checkout main && git pull
|
cd apps/app1 && git checkout main && git pull
|
||||||
cd ../.. && git add apps/hub && git commit -m "chore(deps): update hub submodule"
|
cd ../.. && git add apps/app1 && git commit -m "chore(deps): update app1 submodule"
|
||||||
git push
|
git push
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -107,8 +107,8 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'apps/hub/**'
|
- 'apps/app1/**'
|
||||||
- 'infra/docker/hub.Dockerfile'
|
- 'infra/docker/app1.Dockerfile'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Affected Commands (Nx/Turborepo/Moon)
|
### Affected Commands (Nx/Turborepo/Moon)
|
||||||
|
|||||||
Reference in New Issue
Block a user