SOLID Infrastructure & Docker Standards
To keep the Elo Orgânico infrastructure, DevOps tooling, and repository automations highly modular, extensible, and robust, we apply the SOLID design principles directly to directory layouts, shell scripts, Docker orchestration, and CI/CD pipelines.
1. Architectural Application of SOLID
Single Responsibility Principle (SRP)
Each directory and configuration file must serve exactly one operational purpose.
- Separation of Concerns: We separate runtime-specific service environments from host-executed automation scripts, configuration schema templates, and orchestration files.
- Reusable CI Modules: Workflows under
.github/workflows/must not duplicate environment setup logic. Shared behaviors (such as node execution, PNPM installations, and cache configurations) are extracted into independent GitHub Composite Actions in.github/actions/.
Open/Closed Principle (OCP)
The infrastructure must be open for extension but closed for modification.
- Pluggable Services: Containerized services are located under a unified
services/namespace. Adding a new downstream tool or container requires creating an independent folder (e.g.,services/slack-notifier/) with its own environment config, leaving existing workspace containers and automation scripts untouched.
Liskov Substitution Principle (LSP)
All containerized services must conform to a standardized interface contract.
- Service Layout Contract: Each tool subdirectory must implement a uniform interface consisting of a
Dockerfileat the root, anentrypoint.shimplementing standard logging and healthcheck parameters, and isolated volume hooks. - Unified API: Scripts running across containers must execute under matching process signals and environment bindings.
Interface Segregation Principle (ISP)
Each service must only receive the inputs and system privileges it needs to perform its task.
- Strict Env Segregation: Do not pass a monolithic
.envfile containing all system secrets to every container. Instead, define separated environment files (e.g.,.env.gh-cliand.env.git-agent) so that each container operates within its minimal security surface.
Dependency Inversion Principle (DIP)
High-level scripts must depend on abstract environment interfaces, not on concrete host path representations.
- Path Abstraction: Shell scripts running inside containers must never use hardcoded relative paths to find configs on the host machine. Instead, they depend on environment variable boundaries (e.g.,
$SECRETS_DIRand$VARIABLES_FILE). - Dependency Injection: The orchestration layer (
docker-compose.yamlor container runner) maps concrete host-level volumes to the abstract file locations expected inside the container.
2. Standard Directory Structure
Below is the required directory structure for repository tooling workspaces (such as tools/github/):
tools/github/
├── config/ # Configuration schemas and schemas templates
│ ├── security/
│ │ ├── .env.actions.secrets.example
│ │ └── variables.json
│ └── schemas/
│ └── secrets.schema.json
├── infrastructure/ # Orchestration Layer
│ ├── docker/
│ │ ├── compose.yaml # Central Multi-Service Composer
│ │ ├── .env.infra
│ │ └── .env.infra.example
│ └── logs/ # Isolated system logs
├── scripts/ # Host-Executed TS Automation
│ ├── generate-changelog.ts
│ └── generate-roadmap.ts
└── services/ # Pluggable Service Modules
├── gh-cli/ # GitHub CLI Sync Service
│ ├── Dockerfile
│ ├── entrypoint.sh # Unified entrypoint contract
│ ├── .env.gh-cli.example
│ └── src/
│ └── sync_repo_secrets_variables.sh
└── git-agent/ # Git Commit / Signing Automation
├── Dockerfile
├── entrypoint.sh
├── .env.git-agent.example
└── src/
3. Implementation Blueprints
Reusable CI Component: .github/actions/setup-pnpm-env/action.yml (SRP)
Instead of duplicating dependencies setups across GitHub workflows, extract them into a composite action:
name: 'Setup PNPM Environment'
description: 'Installs Node, PNPM, and configures dependency caching'
inputs:
node-version:
description: 'Node.js version to use'
required: false
default: '22'
runs:
using: 'composite'
steps:
- name: Checkout Repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install pnpm
uses: pnpm/action-setup@v6
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: ${{ inputs.node-version }}
cache: 'pnpm'
- name: Install Dependencies
shell: bash
run: pnpm install
Decoupled Script Contract: sync_repo_secrets_variables.sh (DIP)
This script depends strictly on environment-injected parameters, completely abstracting physical disk paths on the runner machine:
#!/usr/bin/env bash
set -euo pipefail
log_info() {
echo "[GH-CLI INFO] $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_error() {
echo "[GH-CLI ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $1" >&2
}
# Verify dependencies are injected via the environment
if [[ -z "${SECRETS_DIR:-}" ]]; then
log_error "Required environment variable SECRETS_DIR is undefined."
exit 1
fi
if [[ -z "${VARIABLES_FILE:-}" ]]; then
log_error "Required environment variable VARIABLES_FILE is undefined."
exit 1
fi
log_info "Reading variables from: $VARIABLES_FILE"
log_info "Scanning secrets directory: $SECRETS_DIR"
if [[ ! -f "$VARIABLES_FILE" ]]; then
log_error "Variables file not found at: $VARIABLES_FILE"
exit 1
fi
log_info "Synchronization completed successfully."
Decoupled Orchestrator: compose.yaml (DIP & ISP)
The orchestrator maps concrete files to the abstract targets required by the container boundary:
services:
gh-cli:
build:
context: ../../services/gh-cli
dockerfile: Dockerfile
container_name: elo-tools-gh-cli
env_file:
- .env.infra
environment:
- SECRETS_DIR=/etc/github-tool/secrets
- VARIABLES_FILE=/etc/github-tool/variables/variables.json
- GITHUB_TOKEN=${GITHUB_TOKEN}
volumes:
- ../../config/security/secrets:/etc/github-tool/secrets:ro
- ../../config/security/variables:/etc/github-tool/variables:ro
- ../../infrastructure/logs:/var/log/gh-cli:rw
restart: 'no'
Under this layout, adding a new tooling container (e.g., sentry-uploader or sonar-scanner) is as simple as dropping a directory under services/, establishing its .env requirements, and registering it in /infrastructure/docker/compose.yaml. The existing core actions, workflows, and configuration directories remain locked and completely secure.