diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..50bfb1e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,145 @@ +### Projeto +test + +### IDE's template + +# JetBrains +.idea +.iml + +# Visual Studio Code +.code + + +### Node template +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# 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 + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..761b9f2 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,40 @@ +name: Build +on: + push: + branches: + - dev + tags: + - 'v*' +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup QEMU + uses: docker/setup-qemu-action@v3 + + - name: Setup docker buildx + uses: docker/setup-buildx-action@v3 + + - name: Login no GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get current date + id: date + run: echo "date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT + + - name: Build e push multi-arch + uses: docker/build-push-action@v6 + with: + push: true + platforms: linux/amd64,linux/arm64 + tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }} + build-args: | + BUILD_DATE=${{ steps.date.outputs.date }} + BUILD_VERSION=${{ github.ref_name }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..37c56ee --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,6 @@ +repos: + - repo: . + rev: v1.0.0 + hooks: + - id: cod3rocket + exclude: test/ diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml new file mode 100644 index 0000000..06b4c7c --- /dev/null +++ b/.pre-commit-hooks.yaml @@ -0,0 +1,9 @@ +- id: cod3rocket + name: Cod3Rocket + entry: ghcr.io/cod3rocker/pre-commit-hooks:v1.0.0 + language: docker_image + +- id: cod3rocket-dev + name: Cod3Rocket + entry: ghcr.io/cod3rocker/pre-commit-hooks:dev + language: docker_image diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..921401e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,46 @@ +FROM docker.io/library/almalinux:10 AS base + +ARG BUILD_DATE +ARG BUILD_VERSION + +LABEL maintainer="contato@cod3rocket.com" +LABEL org.opencontainers.image.created=$BUILD_DATE +LABEL org.opencontainers.image.authors="Cod3Rocket" +LABEL org.opencontainers.image.url="https://github.com/cod3rocket/pre-commit-hooks" +LABEL org.opencontainers.image.documentation="https://github.com/cod3rocket/pre-commit-hooks" +LABEL org.opencontainers.image.source="https://github.com/cod3rocket/pre-commit-hooks" +LABEL org.opencontainers.image.version=$BUILD_VERSION +LABEL org.opencontainers.image.vendor="Cod3Rocket" +LABEL org.opencontainers.image.licenses="MIT" +LABEL org.opencontainers.image.title="pre-commit-hooks" +LABEL org.opencontainers.image.description="Hooks de pre commit da cod3Rocket" +LABEL org.opencontainers.image.base.name="docker.io/library/almalinux:10" + +ENV USER=cod3rocket +ENV HOME=/home/$USER +ENV PATH="$PATH:$HOME/.local/bin:$HOME/.local/share/mise/shims" + +RUN dnf install -y curl ca-certificates tar freetype dejavu-sans-fonts fontconfig \ + && dnf clean all \ + && rm -rf /var/cache/yum \ + && useradd -m -s /bin/bash $USER + +USER $USER +WORKDIR $HOME + +RUN curl https://mise.run | sh \ + && mise settings set experimental true \ + && echo "eval \"\$(~/.local/bin/mise activate bash)\"" >> ~/.bashrc \ + && eval "$(~/.local/bin/mise activate bash)" + +COPY mise.toml /etc/mise/config.toml + +RUN mise trust && mise install --yes && mise reshim + +COPY --chown=$USER:$USER package.json bun.lock entrypoint.ts /opt/cod3rocket/pre-commit-hooks/ + +WORKDIR /opt/cod3rocket/pre-commit-hooks + +RUN bun i + +ENTRYPOINT [ "bun", "run", "entrypoint.ts" ] diff --git a/README.md b/README.md index c7d547a..d247635 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ # Pre Commit Hooks Hooks de pre commit da cod3Rocket + +## Ferramentas + +- [bun](https://bun/com) v1.3.5 +- [gitleaks](https://gitleaks.io) v8.30.0 +- [java (temurin)](https://adoptium.net) v21.0.9+10.0.LTS +- [ktlint](https://pinterest.github.io/ktlint) v1.8.0 +- [opentofu](https://opentofu.org) v1.11.2 +- [task](https://taskfile.dev) v3.46.4 diff --git a/Taskfile.yaml b/Taskfile.yaml new file mode 100644 index 0000000..2583530 --- /dev/null +++ b/Taskfile.yaml @@ -0,0 +1,5 @@ +version: "3" + +tasks: + build:dev: + cmd: docker buildx build -t ghcr.io/cod3rocket/pre-commit-hooks:dev . diff --git a/entrypoint.ts b/entrypoint.ts index f67b2c6..118bdac 100644 --- a/entrypoint.ts +++ b/entrypoint.ts @@ -1 +1,114 @@ -console.log("Hello via Bun!"); \ No newline at end of file +import {$} from "bun"; +import * as path from "node:path"; + +// Utilitários de cor para o terminal +const col = { + red: (txt: string) => `\x1b[31m${txt}\x1b[0m`, + green: (txt: string) => `\x1b[32m${txt}\x1b[0m`, + yellow: (txt: string) => `\x1b[33m${txt}\x1b[0m`, + blue: (txt: string) => `\x1b[34m${txt}\x1b[0m`, + dim: (txt: string) => `\x1b[2m${txt}\x1b[0m`, +}; + +enum HookName { + KtLint = "KtLint", + OpenTofu = "OpenTofu", +} + +interface Hook { + include: RegExp; + + run(filePaths: string[]): Promise; +} + +const hooks: Record = { + [HookName.KtLint]: { + include: /\.kts?$/, + async run(filePaths: string[]): Promise { + if (filePaths.length === 0) return 0; + + console.log(col.blue(`ℹ️ Rodando KtLint em ${filePaths.length} arquivos...`)); + // Adicionado --relative para output mais limpo, se suportado, ou mantenha paths absolutos + const {exitCode} = await $`ktlint -F ${filePaths}`.nothrow(); + return exitCode; + }, + }, + [HookName.OpenTofu]: { + // Melhorado regex para pegar extensões corretamente e fixado o fim da string ($) + include: /\.(tf|tofu|tfvars|tftest\.hcl)$/, + async run(filePaths: string[]): Promise { + if (filePaths.length === 0) return 0; + + console.log(col.blue(`ℹ️ Rodando OpenTofu fmt em ${filePaths.length} arquivos...`)); + // Removido -recursive, pois estamos passando arquivos específicos + const {exitCode} = await $`tofu fmt ${filePaths}`.nothrow(); + return exitCode; + }, + }, +}; + +async function runGitLeaks(): Promise { + console.log(col.dim("🔒 Verificando segredos com Gitleaks...")); + + // 2. Uso do comando 'git' e captura de output (.quiet()) + // O .quiet() impede que o stdout vazie no terminal a menos que a gente mande + const {exitCode, stdout, stderr} = await $`gitleaks git --pre-commit --redact --staged --verbose --no-banner`.quiet().nothrow(); + + if (exitCode !== 0) { + console.error(col.red("\n❌ Gitleaks detectou segredos no código!")); + // Só mostramos o log se houver erro + console.log(stdout.toString()); + console.log(stderr.toString()); + return false; + } + + return true; +} + +async function main(args: string[]) { + // slice é mais seguro que splice para não mutar o argv original, embora splice funcione + const sources = args.slice(2); + + if (sources.length === 0) { + // Se não houver arquivos na staged area passados pelo lint-staged ou similar + console.log(col.dim("⏭️ Nenhum arquivo para verificar.")); + process.exit(0); + } + + // 1. Segurança Primeiro + const isSecure = await runGitLeaks(); + if (!isSecure) { + // Segurança é prioridade: se falhar, aborta tudo imediatamente. + process.exit(1); + } + + const filesPaths = sources.map((source) => path.resolve(source)); + let hasFailure = false; + + // 2. Execução dos Hooks + for (const [name, hook] of Object.entries(hooks)) { + const parsedFiles = filesPaths.filter((filePath) => hook.include.test(filePath)); + + // Pula se não houver arquivos para este hook + if (parsedFiles.length === 0) continue; + + const exitCode = await hook.run(parsedFiles); + + if (exitCode !== 0) { + console.error(col.red(`❌ Falha na execução do hook: ${name}`)); + hasFailure = true; + } else { + console.log(col.green(`✅ ${name} executado com sucesso.`)); + } + } + + // 3. Saída Final + if (hasFailure) { + console.error(col.red("\n⛔ O commit foi abortado devido a erros nos hooks.")); + process.exit(1); + } + + console.log(col.green("\n✨ Todos as verificações passaram!")); +} + +await main(process.argv); diff --git a/mise.toml b/mise.toml index f6db502..1b4ef54 100644 --- a/mise.toml +++ b/mise.toml @@ -1,2 +1,7 @@ [tools] bun = "1.3.5" +gitleaks = "8.30.0" +java = "temurin-21.0.9+10.0.LTS" +ktlint = "1.8.0" +opentofu = "1.11.2" +task = "3.46.4" diff --git a/package.json b/package.json index 4fe9c0b..4a68766 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "pre-commit-hooks", + "version": "1.0.0", "module": "entrypoint.ts", "type": "module", "private": true,