trgclub — Multi-module repo scaffold — Implementation Plan
TLDR: Criar o repo
trgclubno padrão WeHive multi-module com backend (Rails) e frontend (Next.js), infraestrutura K8s + Terraform e CI/CD para staging e production em endpointsbeta.*.
Branch:
feat/multi-module-scaffold
Architecture: O repo agrupa trgclub-api e trgclub-web como módulos independentes em modules/backend e modules/frontend, seguindo o padrão idêntico ao nectar-charges. Cada módulo tem seu próprio .infra/ com K8s (base + overlays) e Terraform. O CI/CD usa reusable workflows de ibft-corp/pipelines.
Tech stack: Ruby 3.4.4 (backend), Node 20.18.0 (frontend), Kustomize, Terraform, GitHub Actions.
Global constraints
- Portas TRG Club (código 05):
API_PORT=4005,WEB_PORT=3005,DATABASE_PORT=5005 - ECR registry:
616795656532.dkr.ecr.us-east-1.amazonaws.com - Namespace K8s:
trgclub--stagingetrgclub--production - Domínio:
trgclub.com(beta endpoints, não definitivos) - Ward path:
trgclub.infra - Terraform state bucket:
wehive-terraform-states
Task 1: Estrutura raiz do repo
Files:
- Create: Makefile
- Create: compose.yml
- Create: .env.example
- Create: .gitignore
- Create: .tool-versions
- Create: .github/PULL_REQUEST_TEMPLATE.md
- Create: .project/docker/compose/services.yml
- Create: .project/docs/modules.md
- Create: tasks/registry/create.sh
- [ ] Step 1: Criar Makefile raiz
```makefile # Makefile include $(COMMONS_DIR)/make/main.makefile
frontend.%: @$(MAKE) -C modules/frontend $*
backend.%: @$(MAKE) -C modules/backend $*
run.test: @$(MAKE) -j2 backend.run.test frontend.run.test
modules.image.push: @$(MAKE) backend.image.dev.push frontend.image.dev.push
registry.create: @sh tasks/registry/create.sh ```
- [ ] Step 2: Criar compose.yml raiz
```yaml # compose.yml name: trgclub
include: - path: - ${COMMONS_DIR}/docker/compose/shared/database.yml - .project/docker/compose/services.yml ```
- [ ] Step 3: Criar .project/docker/compose/services.yml
```yaml # .project/docker/compose/services.yml include: - path: - ${COMMONS_DIR}/docker/compose/stacks/ruby.yml - ${COMMONS_DIR}/docker/compose/stacks/node.yml
x-backend-base: &backend-base extends: file: ${COMMONS_DIR}/docker/compose/stacks/ruby.yml service: x-stack-base build: context: ${PROJECT_DIR}/modules/backend volumes: - ${PROJECT_DIR}/modules/backend:/source
x-frontend-base: &frontend-base extends: file: ${COMMONS_DIR}/docker/compose/stacks/node.yml service: x-stack-base build: context: ${PROJECT_DIR}/modules/frontend volumes: - ${PROJECT_DIR}/modules/frontend:/source
services: backend-api: «: *backend-base environment: PORT: ${API_PORT} ports: - “${API_PORT}:${API_PORT}” depends_on: database: condition: service_healthy
backend-worker: «: *backend-base environment: RUNTIME_MODE: worker depends_on: database: condition: service_healthy
frontend-web: «: *frontend-base ports: - “${WEB_PORT}:${WEB_PORT}” ```
- [ ] Step 4: Criar .env.example
```bash # .env.example # ───────────────────────────────────────────── # Dev mode # ───────────────────────────────────────────── DEV_MODE=container DEV_DEPENDENCIES=start DEV_ALWAYS_BUILD_CONTAINERS=false
─────────────────────────────────────────────
# Stack versions # ───────────────────────────────────────────── NODE_VERSION=20 RUBY_VERSION=3.4.4
─────────────────────────────────────────────
# Frontend (Next.js dev server) # ───────────────────────────────────────────── WEB_PORT=3005
NEXT_PUBLIC_API_URL=http://localhost:4005
─────────────────────────────────────────────
# Backend (Rails API) # ───────────────────────────────────────────── APP_NAME=trgclub-api CONTAINER_REGISTRY=616795656532.dkr.ecr.us-east-1.amazonaws.com/wehive API_PORT=4005
CORS_ALLOWED_ORIGINS=http://localhost:3005
JWT_SECRET=changeme JWT_ACCESS_TTL=604800
DATABASE_URL_STAGING= DATABASE_URL_PRODUCTION=
─────────────────────────────────────────────
# Banco de dados (PostgreSQL) # ───────────────────────────────────────────── DATABASE_HOST=database DATABASE_PORT=5005 DATABASE_NAME=trgclub_api_development DATABASE_USERNAME=postgres DATABASE_PASSWORD=postgres DATABASE_POOL=5
─────────────────────────────────────────────
# Datadog (staging e production apenas) # ───────────────────────────────────────────── DD_API_KEY= DD_SITE=us5.datadoghq.com DD_ENV=staging DD_SERVICE=trgclub-api DD_VERSION= ```
- [ ] Step 5: Criar .gitignore
gitignore
node_modules/
dist/
dist-ssr/
*.local
.env
.commons
.DS_Store
.claude/settings.local.json
.codex/settings.local.json
.ward/.key
modules/**/.claude/
modules/**/.codex/
modules/**/.project/
.infra/build-args
modules/**/.infra/terraform/.terraform/
modules/**/.infra/terraform/.terraform.lock.hcl
modules/**/.infra/terraform/tfplan
modules/**/.infra/terraform/*.tfstate
modules/**/.infra/terraform/*.tfstate.backup
.claude/*
!.claude/
!.claude/skills
!.claude/commands
.codex/*
!.codex/
!.codex/skills
!.codex/commands
- [ ] Step 6: Criar .tool-versions
nodejs 20.18.0
ruby 3.4.4
- [ ] Step 7: Criar .github/PULL_REQUEST_TEMPLATE.md
```markdown ## TLDR
CONTEXT
CHANGES
- - -
HOW TO TEST
1. 2. 3.
CHECKLIST
- [ ] Código testado em staging
- [ ] Testes automáticos criados/atualizados para as mudanças
- [ ] PRs pequenos e atômicos (um contexto: feature/bug/regra de negócio)
- [ ] Commits atômicos com mensagens claras (max 60 chars)
-
[ ] Código estável e não quebra funcionalidades existentes ```
- [ ] Step 8: Criar tasks/registry/create.sh
```bash #!/bin/sh set -e
aws ecr create-repository –repository-name trgclub-backend –region us-east-1 –profile wehive-ecr aws ecr create-repository –repository-name trgclub-frontend –region us-east-1 –profile wehive-ecr ```
bash
chmod +x tasks/registry/create.sh
- [ ] Step 9: Criar .project/docs/modules.md
Copiar o conteúdo de nectar-charges/.project/docs/modules.md substituindo referências ao projeto.
- [ ] Step 10: Commit
bash
git add Makefile compose.yml .env.example .gitignore .tool-versions \
.github/PULL_REQUEST_TEMPLATE.md \
.project/docker/compose/services.yml \
.project/docs/modules.md \
tasks/registry/create.sh
git commit -m "feat: root repo structure"
Task 2: Módulo backend — scaffold e conteúdo
Files:
- Create: modules/backend/Makefile
- Create: modules/backend/.module/make/main.mk
- Create: modules/backend/.infra/build-args
- Move: conteúdo de trgclub-api/ → modules/backend/
- [ ] Step 1: Criar estrutura do módulo backend
bash
mkdir -p modules/backend/.module/make
mkdir -p modules/backend/.infra
- [ ] Step 2: Criar modules/backend/Makefile
makefile
include $(COMMONS_DIR)/make/main.makefile
include .module/make/main.mk
- [ ] Step 3: Criar modules/backend/.module/make/main.mk
makefile
APP_SERVICE = backend-api
MODULE_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))/../..)
export APP_SERVICE
export MODULE_DIR
- [ ] Step 4: Criar modules/backend/.infra/build-args
DOCKERFILE=${COMMONS_DIR}/docker/Dockerfile
STACK=ruby
STACK_VERSION=3.4.4
DISTRO=alpine
DEPS_PATH=/usr/local/bundle
LOCK_FILES=Gemfile Gemfile.lock .ruby-version
- [ ] Step 5: Copiar conteúdo do trgclub-api
bash
# Copiar todo o conteúdo de trgclub-api para modules/backend
# excluindo .git, node_modules e arquivos temporários
rsync -av --exclude='.git' --exclude='node_modules' --exclude='tmp/cache' \
--exclude='log/*.log' --exclude='coverage' \
../trgclub-api/ modules/backend/
- [ ] Step 6: Commit
bash
git add modules/backend/
git commit -m "feat: backend module scaffold with trgclub-api"
Task 3: Módulo frontend — scaffold e conteúdo
Files:
- Create: modules/frontend/Makefile
- Create: modules/frontend/.module/make/main.mk
- Create: modules/frontend/.infra/build-args
- Move: conteúdo de trgclub-web/ → modules/frontend/
- [ ] Step 1: Criar estrutura do módulo frontend
bash
mkdir -p modules/frontend/.module/make
mkdir -p modules/frontend/.infra
- [ ] Step 2: Criar modules/frontend/Makefile
makefile
include $(COMMONS_DIR)/make/main.makefile
include .module/make/main.mk
- [ ] Step 3: Criar modules/frontend/.module/make/main.mk
makefile
APP_SERVICE = frontend-web
MODULE_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))/../..)
export APP_SERVICE
export MODULE_DIR
- [ ] Step 4: Criar modules/frontend/.infra/build-args
DOCKERFILE=${COMMONS_DIR}/docker/Dockerfile
STACK=node
STACK_VERSION=20.18.0
DISTRO=alpine
LOCK_FILES=package-lock.json
- [ ] Step 5: Copiar conteúdo do trgclub-web
bash
rsync -av --exclude='.git' --exclude='node_modules' --exclude='.next' \
--exclude='coverage' --exclude='cypress/videos' --exclude='cypress/screenshots' \
../trgclub-web/ modules/frontend/
- [ ] Step 6: Commit
bash
git add modules/frontend/
git commit -m "feat: frontend module scaffold with trgclub-web"
Task 4: K8s backend — base
Files:
- Create: modules/backend/.infra/k8s/base/kustomization.yaml
- Create: modules/backend/.infra/k8s/base/specs/api-deployment.yaml
- Create: modules/backend/.infra/k8s/base/specs/api-service.yaml
- Create: modules/backend/.infra/k8s/base/specs/ingress.yaml
- Create: modules/backend/.infra/k8s/base/specs/migrate-job.yaml
- Create: modules/backend/.infra/k8s/base/specs/worker-deployment.yaml
- [ ] Step 1: Criar diretórios
bash
mkdir -p modules/backend/.infra/k8s/base/specs
- [ ] Step 2: Criar kustomization.yaml
```yaml # modules/backend/.infra/k8s/base/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization
resources: - specs/api-deployment.yaml - specs/api-service.yaml - specs/ingress.yaml ```
- [ ] Step 3: Criar api-deployment.yaml
yaml
# modules/backend/.infra/k8s/base/specs/api-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-api
labels:
app: trgclub
module: backend
component: api
spec:
replicas: 1
selector:
matchLabels:
app: trgclub
module: backend
component: api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: trgclub
module: backend
component: api
spec:
terminationGracePeriodSeconds: 30
containers:
- name: api
image: 616795656532.dkr.ecr.us-east-1.amazonaws.com/trgclub-backend:latest
ports:
- containerPort: 4005
env:
- name: RAILS_ENV
value: "production"
- name: RAILS_ALLOWED_HOSTS
value: "placeholder.trgclub.com"
envFrom:
- secretRef:
name: trgclub-secrets
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
readinessProbe:
httpGet:
path: /health
port: 4005
initialDelaySeconds: 10
periodSeconds: 10
- [ ] Step 4: Criar api-service.yaml
yaml
# modules/backend/.infra/k8s/base/specs/api-service.yaml
apiVersion: v1
kind: Service
metadata:
name: backend-api
labels:
app: trgclub
module: backend
component: api
spec:
selector:
app: trgclub
module: backend
component: api
ports:
- port: 80
targetPort: 4005
- [ ] Step 5: Criar ingress.yaml
yaml
# modules/backend/.infra/k8s/base/specs/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: backend-api
labels:
app: trgclub
module: backend
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-read-timeout: "90"
nginx.ingress.kubernetes.io/proxy-send-timeout: "90"
spec:
tls:
- hosts:
- placeholder.trgclub.com
secretName: trgclub-api-tls
rules:
- host: placeholder.trgclub.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: backend-api
port:
number: 80
- [ ] Step 6: Criar migrate-job.yaml
yaml
# modules/backend/.infra/k8s/base/specs/migrate-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: migrate
spec:
ttlSecondsAfterFinished: 120
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: MIGRATE_IMAGE
command: ["entrypoint"]
args: ["run/migrate"]
env:
- name: RAILS_ENV
value: "MIGRATE_ENV"
envFrom:
- secretRef:
name: trgclub-secrets
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 500m
memory: 512Mi
- [ ] Step 7: Criar worker-deployment.yaml
yaml
# modules/backend/.infra/k8s/base/specs/worker-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-worker
labels:
app: trgclub
module: backend
component: worker
spec:
replicas: 1
selector:
matchLabels:
app: trgclub
module: backend
component: worker
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: trgclub
module: backend
component: worker
spec:
terminationGracePeriodSeconds: 60
containers:
- name: worker
image: 616795656532.dkr.ecr.us-east-1.amazonaws.com/trgclub-backend:latest
env:
- name: RAILS_ENV
value: "production"
- name: RUNTIME_MODE
value: worker
envFrom:
- secretRef:
name: trgclub-secrets
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
- [ ] Step 8: Verificar kustomize
bash
kubectl kustomize modules/backend/.infra/k8s/base
Expected: manifests renderizados sem erro.
- [ ] Step 9: Commit
bash
git add modules/backend/.infra/k8s/base/
git commit -m "feat: backend k8s base manifests"
Task 5: K8s backend — overlays staging e production
Files:
- Create: modules/backend/.infra/k8s/overlays/staging/kustomization.yaml
- Create: modules/backend/.infra/k8s/overlays/staging/namespace.yaml
- Create: modules/backend/.infra/k8s/overlays/staging/patches/deployment.yaml
- Create: modules/backend/.infra/k8s/overlays/staging/patches/ingress.yaml
- Create: modules/backend/.infra/k8s/overlays/production/kustomization.yaml
- Create: modules/backend/.infra/k8s/overlays/production/namespace.yaml
- Create: modules/backend/.infra/k8s/overlays/production/patches/deployment.yaml
- Create: modules/backend/.infra/k8s/overlays/production/patches/ingress.yaml
- [ ] Step 1: Criar diretórios
bash
mkdir -p modules/backend/.infra/k8s/overlays/staging/patches
mkdir -p modules/backend/.infra/k8s/overlays/production/patches
- [ ] Step 2: Staging — kustomization.yaml
```yaml # modules/backend/.infra/k8s/overlays/staging/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization
namespace: trgclub–staging
resources: - ../../base - namespace.yaml
images: - name: 616795656532.dkr.ecr.us-east-1.amazonaws.com/trgclub-backend newTag: staging
patches: - path: patches/ingress.yaml - path: patches/deployment.yaml ```
- [ ] Step 3: Staging — namespace.yaml
yaml
# modules/backend/.infra/k8s/overlays/staging/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: trgclub--staging
- [ ] Step 4: Staging — patches/deployment.yaml
yaml
# modules/backend/.infra/k8s/overlays/staging/patches/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-api
spec:
template:
spec:
containers:
- name: api
env:
- name: RAILS_ENV
value: "staging"
- name: RAILS_ALLOWED_HOSTS
value: "beta.staging.api.trgclub.com"
- [ ] Step 5: Staging — patches/ingress.yaml
yaml
# modules/backend/.infra/k8s/overlays/staging/patches/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: backend-api
spec:
tls:
- hosts:
- beta.staging.api.trgclub.com
secretName: trgclub-api-tls
rules:
- host: beta.staging.api.trgclub.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: backend-api
port:
number: 80
- [ ] Step 6: Production — kustomization.yaml
```yaml # modules/backend/.infra/k8s/overlays/production/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization
namespace: trgclub–production
resources: - ../../base - namespace.yaml
images: - name: 616795656532.dkr.ecr.us-east-1.amazonaws.com/trgclub-backend newTag: production
patches: - path: patches/ingress.yaml - path: patches/deployment.yaml ```
- [ ] Step 7: Production — namespace.yaml
yaml
# modules/backend/.infra/k8s/overlays/production/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: trgclub--production
- [ ] Step 8: Production — patches/deployment.yaml
yaml
# modules/backend/.infra/k8s/overlays/production/patches/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-api
spec:
template:
spec:
containers:
- name: api
env:
- name: RAILS_ENV
value: "production"
- name: RAILS_ALLOWED_HOSTS
value: "beta.api.trgclub.com"
- [ ] Step 9: Production — patches/ingress.yaml
yaml
# modules/backend/.infra/k8s/overlays/production/patches/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: backend-api
spec:
tls:
- hosts:
- beta.api.trgclub.com
secretName: trgclub-api-tls
rules:
- host: beta.api.trgclub.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: backend-api
port:
number: 80
- [ ] Step 10: Verificar overlays
bash
kubectl kustomize modules/backend/.infra/k8s/overlays/staging
kubectl kustomize modules/backend/.infra/k8s/overlays/production
Expected: manifests renderizados com namespaces e hosts corretos.
- [ ] Step 11: Commit
bash
git add modules/backend/.infra/k8s/overlays/
git commit -m "feat: backend k8s overlays staging and production"
Task 6: K8s frontend — base e overlays
Files:
- Create: modules/frontend/.infra/k8s/base/kustomization.yaml
- Create: modules/frontend/.infra/k8s/base/specs/rbac.yaml
- Create: modules/frontend/.infra/k8s/base/specs/web-deployment.yaml
- Create: modules/frontend/.infra/k8s/base/specs/web-service.yaml
- Create: modules/frontend/.infra/k8s/base/specs/ingress.yaml
- Create: overlays staging e production (mesma estrutura do backend)
- [ ] Step 1: Criar diretórios
bash
mkdir -p modules/frontend/.infra/k8s/base/specs
mkdir -p modules/frontend/.infra/k8s/overlays/staging/patches
mkdir -p modules/frontend/.infra/k8s/overlays/production/patches
- [ ] Step 2: Criar base/kustomization.yaml
```yaml # modules/frontend/.infra/k8s/base/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization
resources: - specs/rbac.yaml - specs/web-deployment.yaml - specs/web-service.yaml - specs/ingress.yaml ```
- [ ] Step 3: Criar base/specs/rbac.yaml
yaml
# modules/frontend/.infra/k8s/base/specs/rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: frontend-web
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: frontend-web
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: frontend-web
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: frontend-web
subjects:
- kind: ServiceAccount
name: frontend-web
- [ ] Step 4: Criar base/specs/web-deployment.yaml
yaml
# modules/frontend/.infra/k8s/base/specs/web-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-web
labels:
app: trgclub
module: frontend
component: web
spec:
replicas: 1
selector:
matchLabels:
app: trgclub
module: frontend
component: web
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: trgclub
module: frontend
component: web
spec:
serviceAccountName: frontend-web
terminationGracePeriodSeconds: 30
initContainers:
- name: wait-backend
image: bitnami/kubectl:latest
command:
- kubectl
- rollout
- status
- deployment/backend-api
- --timeout=5m
containers:
- name: web
image: 616795656532.dkr.ecr.us-east-1.amazonaws.com/trgclub-frontend:latest
env:
- name: RUNTIME_MODE
value: "web"
- name: WEB_PORT
value: "3005"
- name: VITE_ALLOWED_HOSTS
value: "placeholder.trgclub.com"
ports:
- containerPort: 3005
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 256Mi
- [ ] Step 5: Criar base/specs/web-service.yaml
yaml
# modules/frontend/.infra/k8s/base/specs/web-service.yaml
apiVersion: v1
kind: Service
metadata:
name: frontend-web
labels:
app: trgclub
module: frontend
component: web
spec:
selector:
app: trgclub
module: frontend
component: web
ports:
- port: 80
targetPort: 3005
- [ ] Step 6: Criar base/specs/ingress.yaml
yaml
# modules/frontend/.infra/k8s/base/specs/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: frontend-web
labels:
app: trgclub
module: frontend
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- placeholder.trgclub.com
secretName: trgclub-web-tls
rules:
- host: placeholder.trgclub.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-web
port:
number: 80
- [ ] Step 7: Staging — overlays
```yaml # modules/frontend/.infra/k8s/overlays/staging/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization
namespace: trgclub–staging
resources: - ../../base - namespace.yaml
images: - name: 616795656532.dkr.ecr.us-east-1.amazonaws.com/trgclub-frontend newTag: staging
patches: - path: patches/ingress.yaml - path: patches/deployment.yaml ```
yaml
# modules/frontend/.infra/k8s/overlays/staging/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: trgclub--staging
yaml
# modules/frontend/.infra/k8s/overlays/staging/patches/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-web
spec:
template:
spec:
containers:
- name: web
env:
- name: VITE_ALLOWED_HOSTS
value: "beta.staging.trgclub.com"
yaml
# modules/frontend/.infra/k8s/overlays/staging/patches/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: frontend-web
spec:
tls:
- hosts:
- beta.staging.trgclub.com
secretName: trgclub-web-tls
rules:
- host: beta.staging.trgclub.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-web
port:
number: 80
- [ ] Step 8: Production — overlays
```yaml # modules/frontend/.infra/k8s/overlays/production/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization
namespace: trgclub–production
resources: - ../../base - namespace.yaml
images: - name: 616795656532.dkr.ecr.us-east-1.amazonaws.com/trgclub-frontend newTag: production
patches: - path: patches/ingress.yaml - path: patches/deployment.yaml ```
yaml
# modules/frontend/.infra/k8s/overlays/production/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: trgclub--production
yaml
# modules/frontend/.infra/k8s/overlays/production/patches/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-web
spec:
template:
spec:
containers:
- name: web
env:
- name: VITE_ALLOWED_HOSTS
value: "beta.trgclub.com"
yaml
# modules/frontend/.infra/k8s/overlays/production/patches/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: frontend-web
spec:
tls:
- hosts:
- beta.trgclub.com
secretName: trgclub-web-tls
rules:
- host: beta.trgclub.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-web
port:
number: 80
- [ ] Step 9: Verificar overlays
bash
kubectl kustomize modules/frontend/.infra/k8s/overlays/staging
kubectl kustomize modules/frontend/.infra/k8s/overlays/production
Expected: manifests renderizados sem erro.
- [ ] Step 10: Commit
bash
git add modules/frontend/.infra/k8s/
git commit -m "feat: frontend k8s base and overlays"
Task 7: Terraform backend
Files:
- Create: modules/backend/.infra/terraform/backend.tf
- Create: modules/backend/.infra/terraform/data.tf
- Create: modules/backend/.infra/terraform/database.tf
- Create: modules/backend/.infra/terraform/dns.tf
- Create: modules/backend/.infra/terraform/main.tf
- Create: modules/backend/.infra/terraform/namespace.tf
- Create: modules/backend/.infra/terraform/secrets.tf
- Create: modules/backend/.infra/terraform/variables.tf
- Create: modules/backend/.infra/terraform/versions.tf
- Create: modules/backend/.infra/terraform/.tool-versions
- [ ] Step 1: Criar diretório
bash
mkdir -p modules/backend/.infra/terraform
- [ ] Step 2: Criar .tool-versions
terraform 1.10.3
- [ ] Step 3: Criar backend.tf
hcl
# modules/backend/.infra/terraform/backend.tf
terraform {
backend "s3" {
bucket = "wehive-terraform-states"
key = "stacks/trgclub/backend/terraform.tfstate"
region = "us-east-1"
encrypt = true
}
}
- [ ] Step 4: Criar versions.tf
```hcl # modules/backend/.infra/terraform/versions.tf terraform { required_version = “>= 1.5.0”
required_providers { aws = { source = “hashicorp/aws” version = “~> 5.0” } kubernetes = { source = “hashicorp/kubernetes” version = “~> 2.23” } cloudflare = { source = “cloudflare/cloudflare” version = “~> 4.0” } random = { source = “hashicorp/random” version = “~> 3.0” } } } ```
- [ ] Step 5: Criar data.tf
```hcl # modules/backend/.infra/terraform/data.tf provider “aws” { region = var.aws_region access_key = var.aws_terraform_access_key_id secret_key = var.aws_terraform_secret_access_key
assume_role { role_arn = “arn:aws:iam::${var.aws_shared_account_id}:role/TerraformRole” } }
data “aws_eks_cluster” “shared” { name = “shared-kubernetes” }
data “aws_eks_cluster_auth” “shared” { name = “shared-kubernetes” }
data “aws_subnets” “shared” { filter { name = “vpc-id” values = [var.aws_shared_vpc_id] } }
provider “kubernetes” { host = data.aws_eks_cluster.shared.endpoint cluster_ca_certificate = base64decode(data.aws_eks_cluster.shared.certificate_authority[0].data) token = data.aws_eks_cluster_auth.shared.token }
provider “cloudflare” { api_token = var.cloudflare_api_token } ```
- [ ] Step 6: Criar main.tf
```hcl # modules/backend/.infra/terraform/main.tf locals { environment = terraform.workspace
namespace = “trgclub–${local.environment}”
domain = “trgclub.com”
# staging: beta.staging.api.trgclub.com # production: beta.api.trgclub.com app_subdomain = local.environment == “production” ? “beta.api” : “beta.staging.api” app_fqdn = “${local.app_subdomain}.${local.domain}”
common_labels = { app = “trgclub” module = “backend” environment = local.environment managed-by = “terraform” } } ```
- [ ] Step 7: Criar namespace.tf
```hcl # modules/backend/.infra/terraform/namespace.tf import { to = kubernetes_namespace.trgclub id = local.namespace }
resource “kubernetes_namespace” “trgclub” { metadata { name = local.namespace labels = local.common_labels } } ```
- [ ] Step 8: Criar database.tf
```hcl # modules/backend/.infra/terraform/database.tf resource “random_password” “db” { length = 32 special = false }
resource “aws_security_group” “aurora” { name = “trgclub-aurora-sg-${local.environment}” description = “Aurora access from shared VPC” vpc_id = var.aws_shared_vpc_id
ingress { from_port = 5432 to_port = 5432 protocol = “tcp” cidr_blocks = [var.aws_shared_vpc_cidr] }
egress { from_port = 0 to_port = 0 protocol = “-1” cidr_blocks = [“0.0.0.0/0”] }
tags = { Name = “trgclub-aurora-sg-${local.environment}” Environment = local.environment ManagedBy = “terraform” } }
resource “aws_db_subnet_group” “aurora” { name = “trgclub-aurora-subnets-${local.environment}” subnet_ids = data.aws_subnets.shared.ids
tags = { Name = “trgclub-aurora-subnets-${local.environment}” Environment = local.environment ManagedBy = “terraform” } }
resource “aws_rds_cluster” “trgclub” { cluster_identifier = “trgclub-cluster-${local.environment}” engine = “aurora-postgresql” engine_mode = “provisioned” engine_version = “17.4” database_name = “trgclub” master_username = “trgclub” master_password = random_password.db.result db_subnet_group_name = aws_db_subnet_group.aurora.name vpc_security_group_ids = [aws_security_group.aurora.id] skip_final_snapshot = true deletion_protection = false
serverlessv2_scaling_configuration { min_capacity = 0 max_capacity = 2 seconds_until_auto_pause = 300 }
tags = { Name = “trgclub-cluster-${local.environment}” Environment = local.environment ManagedBy = “terraform” } }
resource “aws_rds_cluster_instance” “writer” { identifier = “trgclub-cluster-writer-${local.environment}-1” cluster_identifier = aws_rds_cluster.trgclub.id instance_class = “db.serverless” engine = aws_rds_cluster.trgclub.engine engine_version = aws_rds_cluster.trgclub.engine_version db_subnet_group_name = aws_db_subnet_group.aurora.name publicly_accessible = false
tags = { Name = “trgclub-cluster-writer-${local.environment}-1” Environment = local.environment ManagedBy = “terraform” } } ```
- [ ] Step 9: Criar dns.tf
```hcl # modules/backend/.infra/terraform/dns.tf resource “cloudflare_record” “trgclub_api” { zone_id = var.cloudflare_zone_id name = local.app_subdomain type = “CNAME” content = var.ingress_hostname ttl = 1 proxied = false
comment = “TRG Club API - ${local.environment} - Managed by Terraform” } ```
- [ ] Step 10: Criar secrets.tf
```hcl # modules/backend/.infra/terraform/secrets.tf resource “kubernetes_secret” “trgclub” { metadata { name = “trgclub-secrets” namespace = kubernetes_namespace.trgclub.metadata[0].name labels = local.common_labels }
data = { RAILS_MASTER_KEY = var.rails_master_key
DATABASE_URL_READONLY = "postgresql://${aws_rds_cluster.trgclub.master_username}:${aws_rds_cluster.trgclub.master_password}@${aws_rds_cluster.trgclub.reader_endpoint}:${aws_rds_cluster.trgclub.port}/${aws_rds_cluster.trgclub.database_name}?sslmode=require"
DATABASE_URL_FULLACCESS = "postgresql://${aws_rds_cluster.trgclub.master_username}:${aws_rds_cluster.trgclub.master_password}@${aws_rds_cluster.trgclub.endpoint}:${aws_rds_cluster.trgclub.port}/${aws_rds_cluster.trgclub.database_name}?sslmode=require" }
type = “Opaque” } ```
- [ ] Step 11: Criar variables.tf
```hcl # modules/backend/.infra/terraform/variables.tf variable “cloudflare_api_token” { type = string sensitive = true }
variable “cloudflare_zone_id” { type = string }
variable “rails_master_key” { type = string sensitive = true }
variable “aws_terraform_access_key_id” { type = string sensitive = true }
variable “aws_terraform_secret_access_key” { type = string sensitive = true }
variable “aws_shared_account_id” { type = string }
variable “aws_shared_vpc_id” { type = string description = “VPC ID of the shared AWS account” }
variable “aws_shared_vpc_cidr” { type = string description = “CIDR block of the shared VPC” }
variable “aws_region” { type = string default = “us-east-1” }
variable “ingress_hostname” { type = string description = “ALB hostname for the EKS ingress” } ```
- [ ] Step 12: Commit
bash
git add modules/backend/.infra/terraform/
git commit -m "feat: backend terraform"
Task 8: Terraform frontend
Files:
- Create: modules/frontend/.infra/terraform/backend.tf
- Create: modules/frontend/.infra/terraform/data.tf
- Create: modules/frontend/.infra/terraform/dns.tf
- Create: modules/frontend/.infra/terraform/main.tf
- Create: modules/frontend/.infra/terraform/variables.tf
- Create: modules/frontend/.infra/terraform/versions.tf
- Create: modules/frontend/.infra/terraform/.tool-versions
- [ ] Step 1: Criar diretório
bash
mkdir -p modules/frontend/.infra/terraform
- [ ] Step 2: Criar .tool-versions
terraform 1.10.3
- [ ] Step 3: Criar backend.tf
hcl
# modules/frontend/.infra/terraform/backend.tf
terraform {
backend "s3" {
bucket = "wehive-terraform-states"
key = "stacks/trgclub/frontend/terraform.tfstate"
region = "us-east-1"
encrypt = true
}
}
- [ ] Step 4: Criar versions.tf
```hcl # modules/frontend/.infra/terraform/versions.tf terraform { required_version = “>= 1.5.0”
required_providers { aws = { source = “hashicorp/aws” version = “~> 5.0” } kubernetes = { source = “hashicorp/kubernetes” version = “~> 2.23” } cloudflare = { source = “cloudflare/cloudflare” version = “~> 4.0” } } } ```
- [ ] Step 5: Criar data.tf
```hcl # modules/frontend/.infra/terraform/data.tf provider “aws” { region = var.aws_region access_key = var.aws_terraform_access_key_id secret_key = var.aws_terraform_secret_access_key
assume_role { role_arn = “arn:aws:iam::${var.aws_shared_account_id}:role/TerraformRole” } }
data “aws_eks_cluster” “shared” { name = “shared-kubernetes” }
data “aws_eks_cluster_auth” “shared” { name = “shared-kubernetes” }
provider “kubernetes” { host = data.aws_eks_cluster.shared.endpoint cluster_ca_certificate = base64decode(data.aws_eks_cluster.shared.certificate_authority[0].data) token = data.aws_eks_cluster_auth.shared.token }
provider “cloudflare” { api_token = var.cloudflare_api_token } ```
- [ ] Step 6: Criar main.tf
```hcl # modules/frontend/.infra/terraform/main.tf locals { environment = terraform.workspace
namespace = “trgclub–${local.environment}”
domain = “trgclub.com”
# staging: beta.staging.trgclub.com # production: beta.trgclub.com app_subdomain = local.environment == “production” ? “beta” : “beta.staging” app_fqdn = “${local.app_subdomain}.${local.domain}”
common_labels = { app = “trgclub” module = “frontend” environment = local.environment managed-by = “terraform” } } ```
- [ ] Step 7: Criar dns.tf
```hcl # modules/frontend/.infra/terraform/dns.tf resource “cloudflare_record” “trgclub_web” { zone_id = var.cloudflare_zone_id name = local.app_subdomain type = “CNAME” content = var.ingress_hostname ttl = 1 proxied = false
comment = “TRG Club Web - ${local.environment} - Managed by Terraform” } ```
- [ ] Step 8: Criar variables.tf
```hcl # modules/frontend/.infra/terraform/variables.tf variable “cloudflare_api_token” { type = string sensitive = true }
variable “cloudflare_zone_id” { type = string }
variable “aws_terraform_access_key_id” { type = string sensitive = true }
variable “aws_terraform_secret_access_key” { type = string sensitive = true }
variable “aws_shared_account_id” { type = string }
variable “aws_region” { type = string default = “us-east-1” }
variable “ingress_hostname” { type = string description = “ALB hostname for the EKS ingress” } ```
- [ ] Step 9: Commit
bash
git add modules/frontend/.infra/terraform/
git commit -m "feat: frontend terraform"
Task 9: GitHub Actions workflows
Files:
- Create: .github/workflows/review-pr.yml
- Create: .github/workflows/check-main.yml
- Create: .github/workflows/deploy-staging.yml
- Create: .github/workflows/deploy-production.yml
- [ ] Step 1: Criar review-pr.yml
```yaml # .github/workflows/review-pr.yml name: review-pr
on: pull_request:
env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs: changes: name: detect changes runs-on: ubuntu-latest outputs: frontend: ${{ steps.filter.outputs.frontend }} backend: ${{ steps.filter.outputs.backend }} frontend_infra: ${{ steps.filter.outputs.frontend_infra }} backend_infra: ${{ steps.filter.outputs.backend_infra }} steps: - uses: actions/checkout@v5 - uses: dorny/paths-filter@v4 id: filter with: filters: | frontend: - ‘modules/frontend/’ backend: - ‘modules/backend/’ frontend_infra: - ‘modules/frontend/.infra/terraform/’ backend_infra: - ‘modules/backend/.infra/terraform/’
check-frontend: needs: changes if: needs.changes.outputs.frontend == ‘true’ uses: ibft-corp/pipelines/.github/workflows/check.yml@main with: app_name: trgclub module: frontend version_tag: ${{ github.sha }} secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
check-backend: needs: changes if: needs.changes.outputs.backend == ‘true’ uses: ibft-corp/pipelines/.github/workflows/check.yml@main with: app_name: trgclub module: backend version_tag: ${{ github.sha }} services: database secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
infra-frontend: needs: changes if: needs.changes.outputs.frontend_infra == ‘true’ uses: ibft-corp/pipelines/.github/workflows/infra-plan.yml@main with: app_name: trgclub module: frontend environment: staging ward_path: trgclub.infra secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} WARD_KEY: ${{ secrets.WARD_KEY }}
infra-backend: needs: changes if: needs.changes.outputs.backend_infra == ‘true’ uses: ibft-corp/pipelines/.github/workflows/infra-plan.yml@main with: app_name: trgclub module: backend environment: staging ward_path: trgclub.infra secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} WARD_KEY: ${{ secrets.WARD_KEY }}
finish: needs: [check-frontend, check-backend] if: always() uses: ibft-corp/pipelines/.github/workflows/check-finish.yml@v4 with: results: ${{ needs.check-frontend.result }},${{ needs.check-backend.result }} ```
- [ ] Step 2: Criar check-main.yml
```yaml # .github/workflows/check-main.yml name: check-main
on: push: branches: [main] paths: - ‘modules/frontend/’ - ‘modules/backend/’
env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs: changes: name: detect changes runs-on: ubuntu-latest outputs: frontend: ${{ steps.filter.outputs.frontend }} backend: ${{ steps.filter.outputs.backend }} frontend_infra: ${{ steps.filter.outputs.frontend_infra }} backend_infra: ${{ steps.filter.outputs.backend_infra }} steps: - uses: actions/checkout@v5 - uses: dorny/paths-filter@v4 id: filter with: filters: | frontend: - ‘modules/frontend/’ backend: - ‘modules/backend/’ frontend_infra: - ‘modules/frontend/.infra/terraform/’ backend_infra: - ‘modules/backend/.infra/terraform/’
check-frontend: needs: changes if: needs.changes.outputs.frontend == ‘true’ uses: ibft-corp/pipelines/.github/workflows/check.yml@main with: app_name: trgclub module: frontend version_tag: ${{ github.sha }} secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
check-backend: needs: changes if: needs.changes.outputs.backend == ‘true’ uses: ibft-corp/pipelines/.github/workflows/check.yml@main with: app_name: trgclub module: backend version_tag: ${{ github.sha }} services: database secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
infra-frontend: needs: changes if: needs.changes.outputs.frontend_infra == ‘true’ uses: ibft-corp/pipelines/.github/workflows/infra-plan.yml@main with: app_name: trgclub module: frontend environment: staging ward_path: trgclub.infra secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} WARD_KEY: ${{ secrets.WARD_KEY }}
infra-backend: needs: changes if: needs.changes.outputs.backend_infra == ‘true’ uses: ibft-corp/pipelines/.github/workflows/infra-plan.yml@main with: app_name: trgclub module: backend environment: staging ward_path: trgclub.infra secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} WARD_KEY: ${{ secrets.WARD_KEY }} ```
- [ ] Step 3: Criar deploy-staging.yml
```yaml # .github/workflows/deploy-staging.yml name: deploy-staging
on: push: tags: - ‘v*’ workflow_dispatch:
env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs: require-tag: name: require-tag uses: ibft-corp/pipelines/.github/workflows/require-tag.yml@main
changes: needs: require-tag name: detect changes runs-on: ubuntu-latest outputs: frontend: ${{ steps.filter.outputs.frontend }} backend: ${{ steps.filter.outputs.backend }} frontend_infra: ${{ steps.has_infra.outputs.frontend }} backend_infra: ${{ steps.has_infra.outputs.backend }} steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - name: find previous tag id: prev run: | PREV=$(git tag –sort=-version:refname | grep ‘^v’ | sed -n ‘2p’) echo “tag=${PREV:-HEAD~1}” » $GITHUB_OUTPUT - uses: dorny/paths-filter@v4 id: filter with: base: ${{ steps.prev.outputs.tag }} filters: | frontend: - ‘modules/frontend/’ backend: - ‘modules/backend/’ - name: check terraform exists id: has_infra run: | [ -d modules/frontend/.infra/terraform ] && echo “frontend=true” » $GITHUB_OUTPUT || echo “frontend=false” » $GITHUB_OUTPUT [ -d modules/backend/.infra/terraform ] && echo “backend=true” » $GITHUB_OUTPUT || echo “backend=false” » $GITHUB_OUTPUT
backend-infra: name: backend:infra needs: changes if: needs.changes.outputs.backend == ‘true’ && needs.changes.outputs.backend_infra == ‘true’ uses: ibft-corp/pipelines/.github/workflows/infra-apply.yml@main with: app_name: trgclub module: backend environment: staging ward_path: trgclub.infra secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} WARD_KEY: ${{ secrets.WARD_KEY }}
backend-package: name: backend:package needs: changes if: needs.changes.outputs.backend == ‘true’ uses: ibft-corp/pipelines/.github/workflows/package.yml@main with: app_name: trgclub module: backend version_tag: ${{ github.ref_name }} secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
backend-migrate: name: backend:migrate needs: [backend-infra, backend-package] if: always() && needs.backend-package.result == ‘success’ && needs.backend-infra.result != ‘failure’ uses: ibft-corp/pipelines/.github/workflows/migrate.yml@main with: app_name: trgclub module: backend version_tag: ${{ github.ref_name }} environment: staging namespace: trgclub–staging secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
frontend-infra: name: frontend:infra needs: changes if: needs.changes.outputs.frontend == ‘true’ && needs.changes.outputs.frontend_infra == ‘true’ uses: ibft-corp/pipelines/.github/workflows/infra-apply.yml@main with: app_name: trgclub module: frontend environment: staging ward_path: trgclub.infra secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} WARD_KEY: ${{ secrets.WARD_KEY }}
frontend-package: name: frontend:package needs: changes if: needs.changes.outputs.frontend == ‘true’ uses: ibft-corp/pipelines/.github/workflows/package.yml@main with: app_name: trgclub module: frontend version_tag: ${{ github.ref_name }} secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
backend-delivery: name: backend:delivery needs: [backend-migrate, frontend-package] if: always() && needs.backend-migrate.result == ‘success’ && needs.frontend-package.result != ‘failure’ uses: ibft-corp/pipelines/.github/workflows/delivery.yml@main with: app_name: trgclub module: backend version_tag: ${{ github.ref_name }} environment: staging environment_url: https://beta.staging.api.trgclub.com namespace: trgclub–staging secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
frontend-delivery: name: frontend:delivery needs: [backend-migrate, backend-package, frontend-infra, frontend-package] if: always() && needs.frontend-package.result == ‘success’ && needs.frontend-infra.result != ‘failure’ && needs.backend-migrate.result != ‘failure’ && needs.backend-package.result != ‘failure’ uses: ibft-corp/pipelines/.github/workflows/delivery.yml@main with: app_name: trgclub module: frontend version_tag: ${{ github.ref_name }} environment: staging environment_url: https://beta.staging.trgclub.com namespace: trgclub–staging secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
land: name: land needs: [backend-delivery, frontend-delivery] if: always() && (needs.backend-delivery.result == ‘success’ || needs.frontend-delivery.result == ‘success’) uses: ibft-corp/pipelines/.github/workflows/land.yml@main with: app_name: trgclub version_tag: ${{ github.ref_name }} environment: staging namespace: trgclub–staging secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} DD_API_KEY: ${{ secrets.DD_API_KEY }} ```
- [ ] Step 4: Criar deploy-production.yml
```yaml # .github/workflows/deploy-production.yml name: deploy-production
on: push: tags: - ‘v*’ workflow_dispatch:
env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs: require-tag: name: require-tag uses: ibft-corp/pipelines/.github/workflows/require-tag.yml@main
changes: name: detect changes needs: require-tag runs-on: ubuntu-latest outputs: frontend: ${{ steps.filter.outputs.frontend }} backend: ${{ steps.filter.outputs.backend }} frontend_infra: ${{ steps.has_infra.outputs.frontend }} backend_infra: ${{ steps.has_infra.outputs.backend }} steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - name: find previous tag id: prev run: | PREV=$(git tag –sort=-version:refname | grep ‘^v’ | sed -n ‘2p’) echo “tag=${PREV:-HEAD~1}” » $GITHUB_OUTPUT - uses: dorny/paths-filter@v4 id: filter with: base: ${{ steps.prev.outputs.tag }} filters: | frontend: - ‘modules/frontend/’ backend: - ‘modules/backend/’ - name: check terraform exists id: has_infra run: | [ -d modules/frontend/.infra/terraform ] && echo “frontend=true” » $GITHUB_OUTPUT || echo “frontend=false” » $GITHUB_OUTPUT [ -d modules/backend/.infra/terraform ] && echo “backend=true” » $GITHUB_OUTPUT || echo “backend=false” » $GITHUB_OUTPUT
backend-infra: name: backend:infra needs: changes if: needs.changes.outputs.backend == ‘true’ && needs.changes.outputs.backend_infra == ‘true’ uses: ibft-corp/pipelines/.github/workflows/infra-apply.yml@main with: app_name: trgclub module: backend environment: production ward_path: trgclub.infra secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} WARD_KEY: ${{ secrets.WARD_KEY }}
backend-package: name: backend:package needs: changes if: needs.changes.outputs.backend == ‘true’ uses: ibft-corp/pipelines/.github/workflows/package.yml@main with: app_name: trgclub module: backend version_tag: ${{ github.ref_name }} secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
backend-migrate: name: backend:migrate needs: [backend-infra, backend-package] if: always() && needs.backend-package.result == ‘success’ && needs.backend-infra.result != ‘failure’ uses: ibft-corp/pipelines/.github/workflows/migrate.yml@main with: app_name: trgclub module: backend version_tag: ${{ github.ref_name }} environment: production namespace: trgclub–production secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
frontend-infra: name: frontend:infra needs: changes if: needs.changes.outputs.frontend == ‘true’ && needs.changes.outputs.frontend_infra == ‘true’ uses: ibft-corp/pipelines/.github/workflows/infra-apply.yml@main with: app_name: trgclub module: frontend environment: production ward_path: trgclub.infra secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} WARD_KEY: ${{ secrets.WARD_KEY }}
frontend-package: name: frontend:package needs: changes if: needs.changes.outputs.frontend == ‘true’ uses: ibft-corp/pipelines/.github/workflows/package.yml@main with: app_name: trgclub module: frontend version_tag: ${{ github.ref_name }} secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
backend-delivery: name: backend:delivery needs: [backend-migrate, frontend-package] if: always() && needs.backend-migrate.result == ‘success’ && needs.frontend-package.result != ‘failure’ uses: ibft-corp/pipelines/.github/workflows/delivery.yml@main with: app_name: trgclub module: backend version_tag: ${{ github.ref_name }} environment: production environment_url: https://beta.api.trgclub.com namespace: trgclub–production secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
frontend-delivery: name: frontend:delivery needs: [backend-migrate, backend-package, frontend-infra, frontend-package] if: always() && needs.frontend-package.result == ‘success’ && needs.frontend-infra.result != ‘failure’ && needs.backend-migrate.result != ‘failure’ && needs.backend-package.result != ‘failure’ uses: ibft-corp/pipelines/.github/workflows/delivery.yml@main with: app_name: trgclub module: frontend version_tag: ${{ github.ref_name }} environment: production environment_url: https://beta.trgclub.com namespace: trgclub–production secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
land: name: land needs: [backend-delivery, frontend-delivery] if: always() && (needs.backend-delivery.result == ‘success’ || needs.frontend-delivery.result == ‘success’) uses: ibft-corp/pipelines/.github/workflows/land.yml@main with: app_name: trgclub version_tag: ${{ github.ref_name }} environment: production namespace: trgclub–production secrets: GH_COMMONS_READ_TOKEN: ${{ secrets.GH_COMMONS_READ_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} DD_API_KEY: ${{ secrets.DD_API_KEY }} ```
- [ ] Step 5: Commit
bash
git add .github/workflows/
git commit -m "feat: github actions workflows"
Task 10: Symlink .commons e verificação final
Files:
- Create: .commons symlink
- [ ] Step 1: Criar symlink .commons
bash
ln -s /Users/oporpino/workspace/wehive.tech/commons .commons
- [ ] Step 2: Verificar estrutura completa
bash
# Verificar que os arquivos críticos existem
ls modules/backend/.infra/k8s/overlays/staging/
ls modules/backend/.infra/terraform/
ls modules/frontend/.infra/k8s/overlays/production/
ls modules/frontend/.infra/terraform/
ls .github/workflows/
- [ ] Step 3: Verificar kustomize (todos os overlays)
bash
kubectl kustomize modules/backend/.infra/k8s/overlays/staging
kubectl kustomize modules/backend/.infra/k8s/overlays/production
kubectl kustomize modules/frontend/.infra/k8s/overlays/staging
kubectl kustomize modules/frontend/.infra/k8s/overlays/production
Expected: todos renderizam sem erro.
- [ ] Step 4: Commit symlink (se rastreado)
bash
git add .commons 2>/dev/null || true
git commit -m "feat: commons symlink" 2>/dev/null || echo "nothing to commit"