diff --git a/.github/workflows/02-tests-ci.yml b/.github/workflows/02-tests-ci.yml index 3a9801b..bee57ae 100644 --- a/.github/workflows/02-tests-ci.yml +++ b/.github/workflows/02-tests-ci.yml @@ -1,13 +1,15 @@ name: "Nível 2: Testes Automatizados" on: pull_request: - branches: [ main ] + branches: + - main + - desafio-nivel-3 env: NODE_VERSION: '18' CHALLENGE_LEVEL: 2 CHALLENGE_NAME: "testes-automatizados" - COVERAGE_MIN: 80 # Cobertura mínima exigida em porcentagem + COVERAGE_MIN: 99 # Cobertura mínima exigida em porcentagem jobs: tests-and-coverage: @@ -20,10 +22,15 @@ jobs: - name: "Checkout do código" uses: actions/checkout@v4 - # INSIRA AQUI A LÓGICA PARA RODAR OS TESTES E VERIFICAR A COBERTURA - ### - ### - ### + - name: "Setup Node" + uses: actions/setup-node@v5 + with: + node-version: ${{env.NODE_VERSION}} + + - name: "Instalar dependências" + run: npm ci + - name: "Executar testes" + run: npm run tests - name: "Extrair porcentagem de cobertura" # Esse step será validado pelo desafio, não altere o nome. No final, ele deve gerar o output "coverage" com a porcentagem de cobertura. id: coverage @@ -31,7 +38,12 @@ jobs: COVERAGE=$(npx nyc report --reporter=text-summary | grep -oP 'Statements\s*:\s*\K[0-9\.]+(?=%)' | head -1) echo "Coverage: $COVERAGE%" echo "coverage=$COVERAGE" >> $GITHUB_OUTPUT - + + - name: "Validar se os testes passaram" + if: ${{steps.coverage.outputs.coverage < env.COVERAGE_MIN}} + run: | + echo "Cobertura mínima não atendida: Esperado - ${{ env.COVERAGE_MIN }}%. Atingido: ${{ steps.coverage.outputs.coverage }}%" + exit 1 generate-certificate: # DAQUI PARA BAIXO, NÃO ALTERAR name: "Desafio Nível 2 - Certificado" runs-on: ubuntu-latest diff --git a/.github/workflows/03-build-containers.yml b/.github/workflows/03-build-containers.yml index cbddc24..095bebc 100644 --- a/.github/workflows/03-build-containers.yml +++ b/.github/workflows/03-build-containers.yml @@ -17,11 +17,69 @@ env: jobs: build-scan-and-push: name: "Build, Lint, Trivy Scan e Push no GHCR" - if: #???? + if: github.event.pull_request.merged == true runs-on: ubuntu-latest steps: - # AQUI VAI O CÓDIGO DO DESAFIO :) + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Lint Dockerfile with Hadolint + uses: hadolint/hadolint-action@v2 + with: + dockerfile: Dockerfile + format: tty + output-file: lint-report.txt + no-fail: true + + - name: Check for DL3006 or DL3008 violations + run: | + if grep -qE "DL3006|DL3008" lint-report.txt; then + echo "Linting failed due to DL3006 or DL3008." && exit 1; + fi + + - name: Upload lint report + uses: actions/upload-artifact@v3 + with: + name: lint-report + path: lint-report.txt + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Docker image + id: build-image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: false + tags: ghcr.io/${{ github.repository_owner }}/${{ vars.IMAGE_NAME }}:${{ github.sha }} + + - name: Scan Docker image for vulnerabilities with Trivy + id: scan-image + uses: aquasecurity/trivy-action@0.28.0 + with: + image-ref: ghcr.io/${{ github.repository_owner }}/${{ vars.IMAGE_NAME }}:${{ github.sha }} + format: table + output: trivy-report.txt + + - name: Check for critical vulnerabilities + run: | + if grep -q "CRITICAL" trivy-report.txt; then + echo "Critical vulnerabilities found." && exit 1; + fi + shell: bash + + - name: Upload Trivy report + uses: actions/upload-artifact@v4 + with: + name: trivy-report + path: trivy-report.txt generate-certificate: # DAQUI PARA BAIXO, NÃO ALTERAR name: "Desafio Nível 3 - Certificado" diff --git a/LINUXtips-github-actions-main/.env.example b/LINUXtips-github-actions-main/.env.example new file mode 100644 index 0000000..e1e9e55 --- /dev/null +++ b/LINUXtips-github-actions-main/.env.example @@ -0,0 +1,21 @@ +# Descomplicando Github Actions - Configuração de Ambiente + +# Porta do servidor (padrão: 3000) +PORT=3000 + +# Ambiente de execução +NODE_ENV=development + +# URL base da aplicação (para produção) +# BASE_URL=https://seu-dominio.com + +# Configurações de monitoramento (opcional) +# MONITORING_ENABLED=true +# WEBHOOK_URL=https://hooks.slack.com/services/... + +# GitHub Token para APIs (opcional - para funcionalidades avançadas) +# GITHUB_TOKEN=ghp_... + +# Configurações de deploy (opcional) +# DEPLOY_ENVIRONMENT=staging + diff --git a/LINUXtips-github-actions-main/.eslintrc.js b/LINUXtips-github-actions-main/.eslintrc.js new file mode 100644 index 0000000..07ae10b --- /dev/null +++ b/LINUXtips-github-actions-main/.eslintrc.js @@ -0,0 +1,41 @@ +module.exports = { + env: { + browser: true, + commonjs: true, + es2021: true, + node: true, + jest: true + }, + extends: [ + 'eslint:recommended' + ], + parserOptions: { + ecmaVersion: 12 + }, + rules: { + // Regras básicas de qualidade + 'indent': ['error', 2], + 'linebreak-style': ['error', 'unix'], + 'quotes': ['error', 'single'], + 'semi': ['error', 'always'], + + // Regras de segurança + 'no-eval': 'error', + 'no-implied-eval': 'error', + 'no-new-func': 'error', + 'no-script-url': 'error', + + // Regras de boas práticas + 'no-unused-vars': 'warn', + 'no-console': 'warn', + 'prefer-const': 'error', + 'no-var': 'error' + }, + ignorePatterns: [ + 'node_modules/', + 'coverage/', + 'dist/', + 'build/' + ] +}; + diff --git a/LINUXtips-github-actions-main/.github/workflows/01-basic-ci.yml b/LINUXtips-github-actions-main/.github/workflows/01-basic-ci.yml new file mode 100644 index 0000000..429e3d1 --- /dev/null +++ b/LINUXtips-github-actions-main/.github/workflows/01-basic-ci.yml @@ -0,0 +1,230 @@ +name: "Nível 1: Basic CI" + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + workflow_dispatch: + +env: + NODE_VERSION: '18' + CHALLENGE_LEVEL: 1 + CHALLENGE_NAME: "first-steps" + +jobs: + basic-setup: + name: "Setup & Basic Checks" + runs-on: ubuntu-latest + + outputs: + node-version: ${{ steps.setup.outputs.node-version }} + challenge-status: ${{ steps.challenge.outputs.status }} + + steps: + - name: "Checkout do código" + uses: actions/checkout@v4 + + - name: "Setup Node.js" + id: setup + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: "Informações do ambiente" + run: | + echo "Runner: ${{ runner.os }}" + echo "Node.js: $(node --version)" + echo "NPM: $(npm --version)" + echo "Challenge: ${{ env.CHALLENGE_NAME }}" + echo "Data: $(date)" + + - name: "Instalar dependências" + run: | + echo "Instalando dependências..." + npm ci + echo "Dependências instaladas com sucesso!" + + - name: "Verificar estrutura do projeto" + run: | + echo "Verificando estrutura do projeto..." + ls -la + echo "" + echo "Arquivos principais encontrados:" + [ -f "package.json" ] && echo "package.json" + [ -f "server.js" ] && echo "server.js" + [ -f "server.test.js" ] && echo "server.test.js" + [ -d "public" ] && echo "diretório public/" + [ -d ".github/workflows" ] && echo "workflows configurados" + + - name: "Challenge Status" + id: challenge + run: | + echo "Parabéns! Você executou seu primeiro GitHub Action!" + echo "Status: INICIADO" + echo "Próximo passo: Execute os testes automatizados" + echo "status=started" >> $GITHUB_OUTPUT + + build: + name: "Build da Aplicação" + runs-on: ubuntu-latest + needs: basic-setup + + steps: + - name: "Checkout do código" + uses: actions/checkout@v4 + + - name: "Setup Node.js" + uses: actions/setup-node@v4 + with: + node-version: ${{ needs.basic-setup.outputs.node-version }} + cache: 'npm' + + - name: "Instalar dependências" + run: npm ci + + - name: "Build da aplicação" + run: | + echo "Iniciando build..." + npm run build + echo "Build concluído com sucesso!" + + - name: "Estatísticas do build" + run: | + echo "Estatísticas do build:" + echo "Tamanho do projeto: $(du -sh . | cut -f1)" + echo "Arquivos: $(find . -type f | wc -l)" + + health-check: + name: "Health Check" + runs-on: ubuntu-latest + needs: [basic-setup, build] + + steps: + - name: "Checkout do código" + uses: actions/checkout@v4 + + - name: "Setup Node.js" + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: "Instalar dependências" + run: npm ci + + - name: "Iniciar servidor em background" + run: | + echo "Iniciando servidor..." + npm start & + SERVER_PID=$! + echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV + sleep 10 + + - name: "Verificar health endpoint" + run: | + echo "Verificando health check..." + curl -f http://localhost:3000/health || exit 1 + echo "Health check passou!" + + - name: "Parar servidor" + if: always() + run: | + if [ ! -z "$SERVER_PID" ]; then + kill $SERVER_PID || true + fi + + challenge-complete: + name: "Desafio Nível 1 - Concluído!" + runs-on: ubuntu-latest + needs: [basic-setup, build, health-check] + if: success() + + steps: + - name: "Parabéns!" + run: | + echo "PARABÉNS! Você concluiu o Nível 1!" + echo "" + echo "O que você aprendeu:" + echo " • Como criar um workflow básico" + echo " • Usar actions do marketplace (checkout, setup-node)" + echo " • Definir jobs e steps" + echo " • Usar variáveis de ambiente" + echo " • Fazer build e health check" + echo "" + echo "Badge desbloqueado: First Steps 🚀" + echo "Próximo desafio: Nível 2 - Testing Master" + echo "" + echo "Veja seu progresso em: https://github.com/${{ github.repository }}/actions" + + - name: "Atualizar estatísticas" + run: | + echo "Atualizando estatísticas do desafio..." + echo "Challenge: ${{ env.CHALLENGE_NAME }}" + echo "Status: CONCLUÍDO" + echo "Data: $(date)" + echo "Usuário: ${{ github.actor }}" + + - name: "Gerar certificado" + run: | + mkdir -p certificates + cat > certificates/level-1-certificate.md << EOF + # Certificado de Conclusão - Nível 1 + + **Descomplicando Github Actions - GitHub Actions Edition** + + --- + + Este certificado atesta que **${{ github.actor }}** concluiu com sucesso: + + ## Nível 1: Primeiro Contato + + **Competências desenvolvidas:** + - Configuração de workflow básico + - Uso de actions do marketplace + - Definição de jobs e steps + - Variáveis de ambiente + - Build e health check automatizados + + **Data de conclusão:** $(date) + **Repositório:** ${{ github.repository }} + **Workflow:** ${{ github.run_id }} + + --- + + **Badge conquistado:** First Steps + + **Próximo desafio:** Nível 2 - Testing Master + + --- + + *Certificado gerado automaticamente pelo GitHub Actions* + *LINUXtips* + EOF + + - name: "Upload do certificado" + uses: actions/upload-artifact@v4 + with: + name: level-1-certificate + path: certificates/ + retention-days: 30 + + - name: "Notificar aplicação sobre conclusão" + run: | + echo "Notificando aplicação sobre a conclusão do workflow..." + + # Tentar notificar a aplicação local (se estiver rodando) + curl -X POST "http://localhost:3000/api/workflow-complete" \ + -H "Content-Type: application/json" \ + -d '{ + "username": "${{ github.actor }}", + "repository": "${{ github.event.repository.name }}", + "workflowName": "${{ github.workflow }}", + "runId": "${{ github.run_id }}", + "certificateGenerated": true + }' || echo "Aplicação local não encontrada (normal em ambiente de produção)" + + echo "Workflow concluído com sucesso!" + echo "Badge será desbloqueado automaticamente se a aplicação estiver rodando" + diff --git a/LINUXtips-github-actions-main/.github/workflows/02-tests-ci.yml b/LINUXtips-github-actions-main/.github/workflows/02-tests-ci.yml new file mode 100644 index 0000000..a5fad7b --- /dev/null +++ b/LINUXtips-github-actions-main/.github/workflows/02-tests-ci.yml @@ -0,0 +1,93 @@ +name: "Nível 2: Testes Automatizados" +on: + pull_request: + branches: + - main + - desafio-nivel-3 + +env: + NODE_VERSION: '18' + CHALLENGE_LEVEL: 2 + CHALLENGE_NAME: "testes-automatizados" + COVERAGE_MIN: 80 # Cobertura mínima exigida em porcentagem + +jobs: + tests-and-coverage: + name: "Testes e Cobertura" + runs-on: ubuntu-latest + outputs: + coverage: ${{ steps.coverage.outputs.coverage }} + + steps: + - name: "Checkout do código" + uses: actions/checkout@v4 + + - name: "Setup Node.js" + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: "Install dependencies" + run: npm ci + + - name: "Build" + run: npm run build + + - name: "Run tests with coverage" + run: npm run tests + + - name: "Extrair porcentagem de cobertura" # Esse step será validado pelo desafio, não altere o nome. No final, ele deve gerar o output "coverage" com a porcentagem de cobertura. + id: coverage + run: | + COVERAGE=$(npx nyc report --reporter=text-summary | grep -oP 'Statements\s*:\s*\K[0-9\.]+(?=%)' | head -1) + echo "Coverage: $COVERAGE%" + echo "coverage=$COVERAGE" >> $GITHUB_OUTPUT + if (( $(echo "$COVERAGE < $COVERAGE_MIN" | bc -l) )); then + echo "Cobertura ($COVERAGE) está abaixo do mínimo configurado de: $COVERAGE_MIN%." + exit 1 + fi + + generate-certificate: # DAQUI PARA BAIXO, NÃO ALTERAR + name: "Desafio Nível 2 - Certificado" + runs-on: ubuntu-latest + needs: tests-and-coverage + if: success() && needs.tests-and-coverage.outputs.coverage != '' + + steps: + - name: "Gerar certificado" + run: | + mkdir -p certificates + cat > certificates/level-2-certificate.md << EOF + # Certificado de Conclusão - Nível 2 + + **Descomplicando Github Actions - GitHub Actions Edition** + --- + + Este certificado atesta que **${{ github.actor }}** concluiu com sucesso: + ## Nível 2: Testes Automatizados + + **Competências desenvolvidas:** + - Automação de testes + - Análise de cobertura + - Lógica de validação com coverage mínimo + - Upload de artefatos + + **Cobertura:** >= ${{ env.COVERAGE_MIN }}% + **Data de conclusão:** $(date) + **Repositório:** ${{ github.repository }} + **Workflow:** ${{ github.run_id }} + + --- + **Badge conquistado:** Testes Automatizados + + --- + *Certificado gerado automaticamente pelo GitHub Actions* + *LINUXtips* + EOF + + - name: "Upload do certificado" + uses: actions/upload-artifact@v4 + with: + name: level-2-certificate + path: certificates/ + retention-days: 30 diff --git a/LINUXtips-github-actions-main/.github/workflows/03-build-containers.yml b/LINUXtips-github-actions-main/.github/workflows/03-build-containers.yml new file mode 100644 index 0000000..095bebc --- /dev/null +++ b/LINUXtips-github-actions-main/.github/workflows/03-build-containers.yml @@ -0,0 +1,128 @@ +name: "Nível 3: Containers e Segurança" + +on: + pull_request: + types: [closed] + branches: [ desafio-nivel-3 ] + +permissions: + contents: read + packages: write + +env: + CHALLENGE_LEVEL: 3 + CHALLENGE_NAME: "containers-e-seguranca" + REGISTRY: ghcr.io + +jobs: + build-scan-and-push: + name: "Build, Lint, Trivy Scan e Push no GHCR" + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Lint Dockerfile with Hadolint + uses: hadolint/hadolint-action@v2 + with: + dockerfile: Dockerfile + format: tty + output-file: lint-report.txt + no-fail: true + + - name: Check for DL3006 or DL3008 violations + run: | + if grep -qE "DL3006|DL3008" lint-report.txt; then + echo "Linting failed due to DL3006 or DL3008." && exit 1; + fi + + - name: Upload lint report + uses: actions/upload-artifact@v3 + with: + name: lint-report + path: lint-report.txt + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Docker image + id: build-image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: false + tags: ghcr.io/${{ github.repository_owner }}/${{ vars.IMAGE_NAME }}:${{ github.sha }} + + - name: Scan Docker image for vulnerabilities with Trivy + id: scan-image + uses: aquasecurity/trivy-action@0.28.0 + with: + image-ref: ghcr.io/${{ github.repository_owner }}/${{ vars.IMAGE_NAME }}:${{ github.sha }} + format: table + output: trivy-report.txt + + - name: Check for critical vulnerabilities + run: | + if grep -q "CRITICAL" trivy-report.txt; then + echo "Critical vulnerabilities found." && exit 1; + fi + shell: bash + + - name: Upload Trivy report + uses: actions/upload-artifact@v4 + with: + name: trivy-report + path: trivy-report.txt + + generate-certificate: # DAQUI PARA BAIXO, NÃO ALTERAR + name: "Desafio Nível 3 - Certificado" + needs: build-scan-and-push + if: success() + runs-on: ubuntu-latest + steps: + - name: "Gerar certificado" + run: | + mkdir -p certificates + cat > certificates/level-3-certificate.md << EOF + # Certificado de Conclusão - Nível 3 + + **Descomplicando Github Actions - GitHub Actions Edition** + --- + + Este certificado atesta que **${{ github.actor }}** concluiu com sucesso: + ## Nível 3: Containers e Segurança + + **Competências desenvolvidas:** + - Build de imagem Docker + - Lint de Dockerfile com Hadolint + - Scan de vulnerabilidades com Trivy (CRITICAL = 0) + - Relatório de vulnerabilidades como artefato + - Smoke test de execução do container + - Publicação no GitHub Container Registry (GHCR) condicionada ao scan + - Boas práticas de supply chain + + **Data de conclusão:** $(date) + **Repositório:** ${{ github.repository }} + **Workflow:** ${{ github.run_id }} + + --- + **Badge conquistado:** Containers e Segurança + + --- + *Certificado gerado automaticamente pelo GitHub Actions* + *LINUXtips* + EOF + + - name: "Upload do certificado" + uses: actions/upload-artifact@v4 + with: + name: level-3-certificate + path: certificates/ + retention-days: 30 diff --git a/LINUXtips-github-actions-main/.gitignore b/LINUXtips-github-actions-main/.gitignore new file mode 100644 index 0000000..e887b96 --- /dev/null +++ b/LINUXtips-github-actions-main/.gitignore @@ -0,0 +1,116 @@ +# Dependencies +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage/ +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage +.grunt + +# Bower dependency directory +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons +build/Release + +# Dependency directories +jspm_packages/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# parcel-bundler cache +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ + +# Storybook build outputs +.out +.storybook-out + +# Temporary folders +tmp/ +temp/ + +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# IDE files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Local development +.local + diff --git a/README.md b/README.md index 7bb74fc..0edc2b7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +** Repo do Dvargas ** + # Descomplicando GitHub Actions Se inscreva na aula ao vivo e gratuita: https://quiz.linuxtips.io/github-actions @@ -22,6 +24,7 @@ Neste projeto você aprenderá: Clique no botão **"Fork"** no canto superior direito desta página para criar uma cópia deste projeto na sua própria conta do GitHub. 2. **Clone o seu Fork:** + ```bash git clone https://github.com/SEU-USUARIO-GITHUB/LINUXtips-github-actions.git cd LINUXtips-github-actions @@ -32,14 +35,17 @@ Neste projeto você aprenderá: 4. **Instale as Dependências:** Você precisa do Node.js (versão 16 ou superior): + ```bash npm install ``` 5. **Teste a Aplicação Localmente:** + ```bash npm start ``` + Acesse [http://localhost:3000](http://localhost:3000) no seu navegador. 6. **Execute seu Primeiro Workflow:** @@ -86,5 +92,6 @@ Ao completar cada workflow com sucesso, você ganha os badges: --- **Feito pela comunidade LINUXtips.** +#VAIIIIII - +teste nivel3