Desafio 01: Projetar fluxo de trabalho
Habilidades do exame cobertas
- Projetar e implementar uma estrutura para o fluxo de trabalho, incluindo GitHub Flow
Foco da plataforma
GitHub-first
Cenário
A Contoso Ltd tem cinco equipes de desenvolvimento trabalhando em um único monorepo. Cada equipe inventou sua própria estratégia de branching: algumas usam branches de funcionalidade de longa duração, outras fazem commit diretamente na main, e uma equipe mantém quatro branches de release paralelas. O resultado é um caos previsível. Merges regularmente levam de dois a três dias para resolver conflitos, a main é quebrada pelo menos uma vez por sprint, e ninguém sabe qual branch representa o estado de produção. O VP de Engenharia determinou um workflow unificado baseado em GitHub Flow, com proteções que impedem pushes diretos e merges com falhas.
Pré-requisitos
- Uma organização GitHub com pelo menos um repositório (crie
contoso-flow-demopara este laboratório) - GitHub CLI instalado e autenticado (
gh auth login) - Acesso de administrador ao repositório para configuração de proteção de branch
- Familiaridade básica com conceitos de branching no Git
Tarefa 1: Entender e comparar estratégias de branching
Antes de implementar qualquer coisa, você precisa entender as três estratégias dominantes e quando cada uma se aplica.
GitHub Flow
- Um único branch de longa duração:
main - Desenvolvedores criam branches de funcionalidade de curta duração a partir da
main - Pull requests são abertos cedo para discussão
- CI executa a cada push para o branch do PR
- Após revisão e CI verde, o PR é feito merge na
main mainé sempre implantável; deploys acontecem a partir damain
GitFlow
- Branches de longa duração:
main(produção) edevelop(integração) - Branches de funcionalidade derivam de
develop - Branches de release derivam de
developao preparar um lançamento - Branches de hotfix derivam de
main - Melhor para: software com ciclos formais de release (aplicativos móveis, software empacotado)
Trunk-based development
- Um único branch:
main(o tronco) - Desenvolvedores fazem commit diretamente na
mainou usam branches extremamente curtos (menos de um dia) - Depende fortemente de feature flags para trabalho incompleto
- Requer testes automatizados abrangentes
- Melhor para: equipes de alta performance com infraestrutura madura de CI/CD e feature flags
Quando escolher GitHub Flow (caso da Contoso)
GitHub Flow é a escolha certa para a Contoso porque:
- Eles fazem deploy de serviços web continuamente (sem janelas formais de release)
- As equipes precisam de um modelo simples e consistente que todos possam seguir
- Eles querem validação de CI antes que qualquer código chegue à main
- Eles não precisam da complexidade de branches de release
Documente sua comparação em um registro de decisão:
mkdir -p docs/decisions
cat > docs/decisions/001-branching-strategy.md << 'EOF'
# ADR 001: Adopt GitHub Flow as unified branching strategy
## Status
Accepted
## Context
Five teams use inconsistent branching strategies causing merge conflicts,
broken main branch, and unclear production state.
## Decision
Adopt GitHub Flow: short-lived feature branches, PR-based merges to main,
deploy from main.
## Consequences
- All teams follow one workflow
- Main is always deployable
- No long-lived branches (feature flags for incomplete work)
- Requires branch protection and CI gates
EOF
Tarefa 2: Criar o repositório e implementar GitHub Flow
Crie o repositório de demonstração e configure a estrutura inicial:
# Create a new repository
gh repo create contoso-flow-demo --public --clone --description "Contoso GitHub Flow demo"
cd contoso-flow-demo
# Initialize with a README and basic structure
cat > README.md << 'EOF'
# Contoso Flow Demo
This repository demonstrates GitHub Flow with branch protection,
required CI checks, and automated merge policies.
## Workflow
1. Create a feature branch from main
2. Make changes and push
3. Open a pull request
4. Wait for CI checks and code review
5. Merge to main (squash merge preferred)
6. Deploy from main
EOF
git add -A
git commit -m "feat: initial repository structure"
git push origin main
Demonstre o workflow criando um branch de funcionalidade:
# Create a feature branch
git checkout -b feature/add-user-service
# Make changes
mkdir -p src
cat > src/user-service.js << 'EOF'
class UserService {
constructor(database) {
this.db = database;
}
async getUser(id) {
return this.db.query('SELECT * FROM users WHERE id = $1', [id]);
}
}
module.exports = UserService;
EOF
git add -A
git commit -m "feat: add user service with basic query support"
git push origin feature/add-user-service
# Open a pull request
gh pr create \
--title "feat: add user service" \
--body "Adds UserService class with database query support.
## Changes
- New UserService class in src/user-service.js
- Basic getUser method with parameterized query
## Testing
- Unit tests pending CI pipeline setup" \
--base main
Tarefa 3: Configurar regras de proteção de branch
Configure a proteção de branch para aplicar as proteções do GitHub Flow:
# Configure branch protection for main using GitHub CLI
gh api repos/{owner}/{repo}/branches/main/protection \
--method PUT \
--field required_status_checks='{"strict":true,"contexts":["ci/build","ci/test"]}' \
--field enforce_admins=true \
--field required_pull_request_reviews='{"required_approving_review_count":1,"dismiss_stale_reviews":true,"require_code_owner_reviews":true}' \
--field restrictions=null \
--field allow_force_pushes=false \
--field allow_deletions=false
# Verify the protection is applied
gh api repos/{owner}/{repo}/branches/main/protection --jq '{
required_reviews: .required_pull_request_reviews.required_approving_review_count,
dismiss_stale: .required_pull_request_reviews.dismiss_stale_reviews,
strict_status: .required_status_checks.strict,
checks: .required_status_checks.contexts,
enforce_admins: .enforce_admins.enabled
}'
Usando a abordagem mais recente de branch ruleset (recomendada para organizações):
# Create a ruleset for the main branch
gh api repos/{owner}/{repo}/rulesets \
--method POST \
--input - << 'EOF'
{
"name": "Main branch protection",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
"include": ["refs/heads/main"],
"exclude": []
}
},
"rules": [
{
"type": "pull_request",
"parameters": {
"required_approving_review_count": 1,
"dismiss_stale_reviews_on_push": true,
"require_code_owner_reviews": true,
"require_last_push_approval": false
}
},
{
"type": "required_status_checks",
"parameters": {
"strict_required_status_checks_policy": true,
"required_status_checks": [
{"context": "ci/build"},
{"context": "ci/test"}
]
}
},
{
"type": "deletion"
},
{
"type": "non_fast_forward"
}
]
}
EOF
Tarefa 4: Configurar templates de pull request
Crie um template de PR que garanta consistência:
mkdir -p .github
cat > .github/pull_request_template.md << 'EOF'
## Summary
<!-- Describe what this PR does in 1-2 sentences -->
## Type of change
- [ ] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would break existing functionality)
- [ ] Documentation update
## Related issues
<!-- Link related issues: Fixes #123, Relates to #456 -->
## How has this been tested?
<!-- Describe the tests you ran -->
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Manual testing performed
## Checklist
- [ ] My code follows the project style guidelines
- [ ] I have performed a self-review
- [ ] I have added tests that prove my fix/feature works
- [ ] New and existing unit tests pass locally
- [ ] Any dependent changes have been merged
EOF
git add .github/pull_request_template.md
git commit -m "chore: add pull request template"
git push origin main
Tarefa 5: Configurar auto-merge quando as verificações passarem
Habilite o auto-merge para que PRs sejam mesclados automaticamente quando todas as verificações obrigatórias passarem e as revisões forem aprovadas:
# Enable auto-merge on the repository
gh api repos/{owner}/{repo} \
--method PATCH \
--field allow_auto_merge=true \
--field delete_branch_on_merge=true \
--field allow_squash_merge=true \
--field allow_merge_commit=false \
--field allow_rebase_merge=true \
--field squash_merge_commit_title="PR_TITLE" \
--field squash_merge_commit_message="PR_BODY"
Agora os desenvolvedores podem habilitar o auto-merge em seus PRs:
# After opening a PR, enable auto-merge
gh pr merge --auto --squash
# The PR will merge automatically when:
# 1. All required status checks pass
# 2. Required reviews are approved
# 3. Branch is up to date with main (if strict mode enabled)
Tarefa 6: Implementar um workflow de CI que aplique a estratégia
Crie um workflow do GitHub Actions que valide o modelo de branching:
mkdir -p .github/workflows
cat > .github/workflows/ci.yml << 'EOF'
name: CI Pipeline
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run build
run: npm run build
test:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
validate-branch-name:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Check branch naming convention
run: |
BRANCH_NAME="${{ github.head_ref }}"
PATTERN="^(feature|bugfix|hotfix|chore|docs)/[a-z0-9._-]+$"
if [[ ! "$BRANCH_NAME" =~ $PATTERN ]]; then
echo "::error::Branch name '$BRANCH_NAME' does not match pattern: $PATTERN"
echo "Valid prefixes: feature/, bugfix/, hotfix/, chore/, docs/"
exit 1
fi
echo "Branch name '$BRANCH_NAME' is valid"
enforce-no-long-lived-branches:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check branch age
run: |
BRANCH_CREATED=$(git log --format=%ci --diff-filter=A --follow HEAD | tail -1)
DAYS_OLD=$(( ($(date +%s) - $(date -d "$BRANCH_CREATED" +%s)) / 86400 ))
if [ "$DAYS_OLD" -gt 7 ]; then
echo "::warning::This branch is $DAYS_OLD days old. GitHub Flow recommends short-lived branches (< 7 days)."
fi
EOF
git add .github/workflows/ci.yml
git commit -m "ci: add pipeline with branch name validation"
git push origin main
Exercícios de quebra e conserto
Cenário 1: Alguém faz push diretamente na main
Um desenvolvedor ignora o workflow e faz push diretamente na main:
# Simulate: try to push directly to main
git checkout main
echo "quick fix" >> README.md
git add -A
git commit -m "quick fix"
git push origin main
Comportamento esperado: O push é rejeitado porque a proteção de branch exige PRs.
Se a proteção não estiver funcionando, verifique:
Mostrar solução
gh api repos/{owner}/{repo}/branches/main/protection \
--jq '.enforce_admins.enabled'
# Should be true - admins are also subject to protection
gh api repos/{owner}/{repo}/branches/main/protection \
--jq '.required_pull_request_reviews.required_approving_review_count'
# Should be >= 1
Cenário 2: Um PR é mesclado com verificações falhando
Os nomes de contexto das verificações de status devem corresponder exatamente ao que seu CI reporta.
Mostrar solução
# List recent check runs to see their exact names
gh api repos/{owner}/{repo}/commits/main/check-runs \
--jq '.check_runs[].name'
# Update branch protection with correct check names if mismatched
gh api repos/{owner}/{repo}/branches/main/protection/required_status_checks \
--method PATCH \
--field strict=true \
--field contexts='["build","test","validate-branch-name"]'
Cenário 3: Aprovações obsoletas de PR persistem após novos commits
Após um revisor aprovar, o desenvolvedor faz push de novos commits. A aprovação antiga deve ser descartada.
Mostrar solução
# Verify dismiss_stale_reviews is enabled
gh api repos/{owner}/{repo}/branches/main/protection/required_pull_request_reviews \
--jq '.dismiss_stale_reviews'
# Must be true
Verificação de conhecimento
1. No GitHub Flow, qual é o papel do branch 'main'?
2. Qual configuração de proteção de branch garante que um PR não pode ser mesclado se novos commits foram enviados para a main após o branch do PR ter sido criado?
3. Quando você deve escolher GitFlow em vez de GitHub Flow?
4. O que habilitar auto-merge com squash em um repositório realiza?
Limpeza
# Delete the demo repository
gh repo delete contoso-flow-demo --yes
# Remove the local clone
cd ..
rm -rf contoso-flow-demo