diff --git a/.dockerignore b/.dockerignore index 247b8e4..998ec0c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,13 @@ node_modules -dist/ +dist +dist.tar.gz .git -.gitignore +.gitea +.gitattributes +.claude +.wolf +.superset +docs *.md +.DS_Store +*.log diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..5188465 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,118 @@ +name: CI + +# Documentação e configuração de ferramenta não mudam nada que a suíte teste. +# Na main não roda por push: o release.yml é quem a chama, e só quando a imagem +# precisa ser reconstruída; rodar nos dois repetiria a mesma suíte no mesmo commit. +# Sem gatilho de pull_request: o push da branch já valida o mesmo commit, e os +# dois juntos rodariam tudo duas vezes (o repositório não recebe PR de fork). +# +# A imagem é construída aqui uma única vez, em arm64 (a arquitetura do servidor), +# testada e publicada como inputs-. Ao fazer o merge, o release encontra +# essa tag e só aplica os nomes novos, sem rodar nem construir nada de novo. +on: + push: + branches-ignore: [main] + paths-ignore: ['**.md', 'LICENSE', 'CITATION.cff', 'paper.md', 'references.bib', 'docs/**', '.claude/**', '.wolf/**', '.superset/**', 'scripts/release-notes.sh'] + schedule: + - cron: '0 9 * * 1' # toda segunda: reavalia com a base de vulnerabilidades nova + workflow_call: + +permissions: + contents: read + +concurrency: + group: ci-${{ gitea.ref }} + cancel-in-progress: true + +jobs: + verificar: + name: Tipos e testes + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - run: npm ci + + # tsc -b é o mesmo que o build faz antes do Vite; aqui falha antes de gastar a imagem. + - run: npx tsc -b + - run: npm test + + # Trivy roda pela imagem oficial: o trivy-action clona o repositório do Trivy + # do mesmo servidor do workflow, que aqui é o Gitea, e não o GitHub. + # O workspace do job é um volume que o daemon não enxerga, então os arquivos + # entram no container com `docker cp` em vez de `-v`. + seguranca: + name: Trivy (dependências e segredos) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Repositório + run: | + cid=$(docker create --network host -v trivy-cache:/root/.cache/ -w /src aquasec/trivy:0.70.0 \ + fs --scanners vuln,secret,misconfig --severity HIGH,CRITICAL --exit-code 1 .) + docker cp . "$cid:/src" + docker start -a "$cid" + status=$(docker inspect -f '{{.State.ExitCode}}' "$cid") + docker rm "$cid" >/dev/null + exit "$status" + + # Só começa depois das verificações baratas: um teste quebrado não gasta um + # build de imagem. Roda no runner arm64, o mesmo tipo do servidor, então o + # que é escaneado e testado é a imagem que vai ao ar. + imagem: + name: Imagem Docker (linux/arm64) + needs: [verificar, seguranca] + runs-on: ubuntu-24.04-arm + env: + REGISTRY: git.lucasrguerra.dev.br + steps: + - uses: actions/checkout@v4 + + # Conteúdo já publicado (re-push, tag num commit já validado): nada a fazer. + - id: plano + run: scripts/plano-imagem.sh "$REGISTRY" "${{ gitea.repository }}" >> "$GITHUB_OUTPUT" + + - if: ${{ steps.plano.outputs.reuse != 'true' }} + uses: docker/login-action@v4 + with: + registry: ${{ env.REGISTRY }} + username: ${{ gitea.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + + # `docker build` simples, no daemon persistente do runner: as camadas e o + # cache mount do npm sobrevivem entre execuções. Um buildx criaria um + # BuildKit novo, e vazio, a cada job. + - name: Build + if: ${{ steps.plano.outputs.reuse != 'true' }} + run: docker build -t pss:ci . + + - name: Trivy na imagem + if: ${{ steps.plano.outputs.reuse != 'true' }} + run: | + docker run --rm --network host -v trivy-cache:/root/.cache/ -e DOCKER_HOST=tcp://127.0.0.1:2375 aquasec/trivy:0.70.0 \ + image --severity HIGH,CRITICAL --exit-code 1 pss:ci + + # A SPA sobe, cai no index nas rotas internas e entrega os assets com + # cache imutável (scripts/smoke-imagem.sh). + - name: A imagem sobe e responde + if: ${{ steps.plano.outputs.reuse != 'true' }} + run: | + docker run -d --name pss-ci -p 127.0.0.1:3998:80 pss:ci + status=0 + scripts/smoke-imagem.sh http://127.0.0.1:3998 || status=1 + docker rm -f pss-ci >/dev/null + exit "$status" + + # Passou em tudo: publica a mesma imagem que foi testada, sem reconstruir. + - name: Publica inputs- + if: ${{ steps.plano.outputs.reuse != 'true' }} + run: | + tag="${{ steps.plano.outputs.image }}:inputs-${{ steps.plano.outputs.inputs }}" + docker tag pss:ci "$tag" + docker push "$tag" diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..9778fed --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,126 @@ +name: Release + +# Publica a imagem no registro de containers do Gitea depois de um merge em main, +# e de novo com tag semver quando uma tag v* é enviada. O servidor só faz pull. +# Precisa do secret de usuário REGISTRY_TOKEN (token do Gitea com write:package). +on: + push: + branches: [main] + tags: ['v*'] + workflow_dispatch: + +concurrency: + group: release + cancel-in-progress: false + +env: + REGISTRY: git.lucasrguerra.dev.br + +jobs: + # A imagem só depende dos arquivos que entram no build, não da documentação nem + # da configuração de ferramentas. O push da branch já a construiu, testou e + # publicou como inputs-; quando essa tag existe (o caso normal de um + # merge, uma tag num commit validado, um merge só de docs), não há o que + # construir: as tags novas apontam para o manifesto existente. + plano: + name: Plano (reaproveitar a imagem?) + runs-on: ubuntu-latest + outputs: + inputs: ${{ steps.plano.outputs.inputs }} + reuse: ${{ steps.plano.outputs.reuse }} + image: ${{ steps.plano.outputs.image }} + steps: + - uses: actions/checkout@v4 + - id: plano + run: scripts/plano-imagem.sh "$REGISTRY" "${{ gitea.repository }}" >> "$GITHUB_OUTPUT" + + # Nada é publicado sem a suíte completa passar antes. Pulada quando a imagem é + # reaproveitada: o mesmo conteúdo já passou por ela. + ci: + needs: plano + if: ${{ needs.plano.outputs.reuse != 'true' }} + uses: ./.gitea/workflows/ci.yml + + publicar: + name: Publica a imagem (linux/arm64) + needs: [plano, ci] + # ci é pulado, e não bem-sucedido, quando a imagem é reaproveitada. + if: ${{ !cancelled() && needs.plano.result == 'success' && (needs.ci.result == 'success' || needs.plano.outputs.reuse == 'true') }} + # O único runner é arm64 e o servidor de destino (Oracle) também, então a + # imagem é construída nativamente, sem emulação. + runs-on: ubuntu-24.04-arm + steps: + - uses: actions/checkout@v4 + + # Se o ci chamado acima acabou de publicar a imagem (push direto na main), + # o plano lá de cima está desatualizado: confere de novo antes de construir. + - id: replano + run: scripts/plano-imagem.sh "$REGISTRY" "${{ gitea.repository }}" >> "$GITHUB_OUTPUT" + + - uses: docker/setup-buildx-action@v4 + + - name: Autentica no registro + uses: docker/login-action@v4 + with: + registry: ${{ env.REGISTRY }} + username: ${{ gitea.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Metadados + id: meta + uses: docker/metadata-action@v6 + with: + images: ${{ needs.plano.outputs.image }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest + type=sha,format=long + + - name: Build e push + if: ${{ steps.replano.outputs.reuse != 'true' }} + uses: docker/build-push-action@v7 + with: + context: . + platforms: linux/arm64 + push: true + tags: | + ${{ steps.meta.outputs.tags }} + ${{ needs.plano.outputs.image }}:inputs-${{ needs.plano.outputs.inputs }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=registry,ref=${{ needs.plano.outputs.image }}:buildcache + cache-to: type=registry,ref=${{ needs.plano.outputs.image }}:buildcache,mode=max + + # Mesmo manifesto, nomes novos: nenhuma camada é lida nem escrita. + - name: Aplica as tags à imagem existente + if: ${{ steps.replano.outputs.reuse == 'true' }} + run: | + tags=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< '${{ steps.meta.outputs.json }}') + docker buildx imagetools create $tags "${{ needs.plano.outputs.image }}:inputs-${{ needs.plano.outputs.inputs }}" + + # Cria a entrada em "Releases" quando a execução é de uma tag v*. Roda depois + # de publicar a imagem, com as notas da seção do CHANGELOG.md. + release: + name: Cria a release no Gitea + needs: [plano, publicar] + # Explícito, como o publicar: o ci acima é pulado quando a imagem é + # reaproveitada, e o success() implícito pularia este job também. + if: ${{ !cancelled() && needs.publicar.result == 'success' && startsWith(gitea.ref, 'refs/tags/v') }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Release + env: + TAG: ${{ gitea.ref_name }} + TOKEN: ${{ gitea.token }} + API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }} + IMAGE: ${{ needs.plano.outputs.image }} + run: | + scripts/release-notes.sh "$TAG" "$IMAGE" > notes.md + pre=false; case "$TAG" in *-*) pre=true ;; esac + jq -n --arg tag "$TAG" --arg name "PSS ${TAG#v}" --rawfile body notes.md --argjson pre "$pre" \ + '{tag_name:$tag, name:$name, body:$body, prerelease:$pre}' > release.json + curl -fsS -X POST "$API/releases" -H "Authorization: token $TOKEN" \ + -H 'Content-Type: application/json' -d @release.json diff --git a/.gitignore b/.gitignore index 293fc57..ce57878 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,10 @@ -!**/glob-import/dir/node_modules -.DS_Store -.idea -.pnpm-store -*.cpuprofile -*.local -*.log -/.vscode/ -/docs/.vitepress/cache -/docs/.vitepress/.temp -/packages/vite/LICENSE +node_modules dist dist-ssr -explorations -node_modules -playground-temp -temp -TODOs.md -.eslintcache \ No newline at end of file +dist.tar.gz +*.local +*.log +.DS_Store +.idea +/.vscode/ +.eslintcache diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..046237c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,78 @@ +# Changelog + +Everything notable in PSS is recorded here. The format follows +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project follows +[Semantic Versioning](https://semver.org/). + +## [Unreleased] + +## [1.3.3] - 2026-11-01 + +### Changed + +- The code, the container image and the releases now live on Gitea + (`git.lucasrguerra.dev.br`). The deployment pulls the image from its registry. +- The `Dockerfile` is multi-stage: it builds Vite from source inside the image instead + of unpacking a `dist.tar.gz` committed to the repository (removed, along with the + `build:release` script). +- New CI on Gitea runners (`.gitea/workflows`): types, 282 tests, Trivy on the repository + and on the image, and an image smoke test. The image is built once per content hash and + reused by the release, so a merge or a tag on an already validated commit rebuilds nothing. +- Repository links in `package.json`, `CITATION.cff`, the READMEs and `CONTRIBUTING.md` + point to Gitea. `.gitattributes` forces LF line endings. + +### Fixed + +- `docker-compose.yml` healthcheck uses `CMD-SHELL` and the correct local URL, and the + port mapping honours `PORT`. + +## [1.3.2] - 2026-03-25 + +### Added + +- Regression tests for Round Robin scheduling with threads. + +## [1.3.1] - 2026-03-25 + +### Added + +- `ThreadMetricsChart` and per-thread metrics. + +### Changed + +- Improvements to the Gantt chart and the metrics panel. + +## [1.3.0] - 2026-03-24 + +### Added + +- Multilevel Feedback Queue (MLFQ) scheduling algorithm. +- Thread scheduler. + +## [1.2.0] - 2026-03-23 + +### Added + +- English documentation, a `CONTRIBUTING.md`, the JOSS paper and a Zenodo DOI in + `CITATION.cff`. +- Multilevel Queue (MLQ) scheduling algorithm and its documentation. + +### Changed + +- License switched to MIT. +- Scheduling algorithms refactored into a class-based `BaseAlgorithm` structure, with + the engine and tests updated and new algorithm and metrics documentation. + +## [1.0.0] - 2026-03-23 + +### Added + +- First release of the browser-based CPU scheduling simulator: core scheduling engine, + algorithms (including Priority Round Robin), Gantt chart with context-switch handling, + UI components and Docker setup. + +[Unreleased]: https://git.lucasrguerra.dev.br/lucasrguerra/pss/compare/v1.3.3...HEAD +[1.3.3]: https://git.lucasrguerra.dev.br/lucasrguerra/pss/compare/v1.3.2...v1.3.3 +[1.3.2]: https://git.lucasrguerra.dev.br/lucasrguerra/pss/compare/v1.2.0...v1.3.2 +[1.2.0]: https://git.lucasrguerra.dev.br/lucasrguerra/pss/compare/v1.0.0...v1.2.0 +[1.0.0]: https://git.lucasrguerra.dev.br/lucasrguerra/pss/releases/tag/v1.0.0 diff --git a/CITATION.cff b/CITATION.cff index 026c587..4f5efac 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -6,7 +6,7 @@ version: 1.2.0 date-released: "2026-03-23" license: MIT url: "https://pss.lucasrguerra.dev.br" -repository-code: "https://github.com/lucasrguerra/pss" +repository-code: "https://git.lucasrguerra.dev.br/lucasrguerra/pss" doi: "10.5281/zenodo.19197235" identifiers: - type: doi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4962a7a..d18441b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ This document provides guidelines and instructions for contributing to this proj This section guides you through submitting a bug report for PSS. Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports. -If you find a bug, please create an issue on GitHub and include: +If you find a bug, please create an issue on Gitea and include: - A clear and descriptive title. - Exact steps to reproduce the problem. - What you expected to happen, and what actually happened. @@ -37,10 +37,10 @@ To contribute code, you'll need to set up the project locally. #### Installation Steps -1. **Fork the repository** on GitHub. +1. **Fork the repository** on Gitea. 2. **Clone your fork** locally: ```bash - git clone https://github.com/YOUR_USERNAME/pss.git + git clone https://git.lucasrguerra.dev.br/YOUR_USERNAME/pss.git cd pss ``` 3. **Install dependencies**: @@ -78,7 +78,7 @@ If you add a new feature, please include appropriate tests covering the new func ```bash git commit -m "feat: add awesome new feature" ``` -4. Push your branch to your fork on GitHub: +4. Push your branch to your fork on Gitea: ```bash git push origin feature/my-awesome-feature ``` diff --git a/Dockerfile b/Dockerfile index d38a19b..fd00d45 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,20 @@ -# ── Production image ── +# syntax=docker/dockerfile:1 + +# ── Build: compila o Vite a partir do código, sem artefato pré-construído ── +FROM node:22-alpine AS build +WORKDIR /app +COPY package.json package-lock.json ./ +RUN --mount=type=cache,target=/root/.npm npm ci +COPY . . +RUN npm run build + +# ── Imagem final: só o nginx e os arquivos estáticos ── FROM nginx:alpine - COPY nginx.conf /etc/nginx/conf.d/default.conf - -# Unpack pre-built Vite output -COPY dist.tar.gz /usr/share/nginx/html/ -RUN tar -xzf /usr/share/nginx/html/dist.tar.gz --strip-components=1 -C /usr/share/nginx/html \ - && rm /usr/share/nginx/html/dist.tar.gz +COPY --from=build /app/dist /usr/share/nginx/html EXPOSE 80 +HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \ + CMD wget -q --spider http://127.0.0.1:80/ || exit 1 CMD ["nginx", "-g", "daemon off;"] diff --git a/README.en.md b/README.en.md index e3cfdaf..79c60f8 100644 --- a/README.en.md +++ b/README.en.md @@ -35,7 +35,7 @@ A live demo is available at: **https://pss.lucasrguerra.dev.br/** ```bash # Clone the repository -git clone https://github.com/lucasrguerra/pss.git +git clone https://git.lucasrguerra.dev.br/lucasrguerra/pss.git cd pss # Install dependencies @@ -57,9 +57,36 @@ npm run preview ### With Docker ```bash -docker compose up +docker compose up --build ``` +The `Dockerfile` builds Vite inside the image (Node 22) and serves the static files +with nginx. There is no pre-built artifact in the repository. + +### Published image + +Every version is published to the Gitea container registry (linux/arm64): + +```bash +docker run -p 8080:80 git.lucasrguerra.dev.br/lucasrguerra/pss:latest +``` + +Tags: `latest`, `X.Y.Z`, `X.Y` and `sha-`. The public instance runs at +[pss.lucasrguerra.dev.br](https://pss.lucasrguerra.dev.br). + +### Continuous integration and releases + +- `.gitea/workflows/ci.yml`: types (`tsc -b`), tests, Trivy (dependencies and secrets), + image build, Trivy on the image and a smoke test (the SPA responds, falls back to the + index on internal routes and serves assets with immutable caching). Runs on branches + and every Monday. +- `.gitea/workflows/release.yml`: on `main` and `vX.Y.Z` tags. If the registry already + has the image for the same content (`inputs-`), nothing is rebuilt: only the new + tags are applied. On a tag, it creates the Gitea release with the [CHANGELOG](CHANGELOG.md) notes. +- To release: update the CHANGELOG, the version in `package.json` and the label in + `src/components/Layout/Sidebar.tsx`, commit, and push the tag + (`git tag vX.Y.Z && git push gitea vX.Y.Z`). + --- ## 🎮 How to use @@ -229,4 +256,4 @@ Contributions are welcome! Please read our [CONTRIBUTING.md](./CONTRIBUTING.md) ## 📜 License -Distributed under the [MIT License](./LICENSE). Developed by [Lucas Rayan Guerra](https://github.com/lucasrguerra). +Distributed under the [MIT License](./LICENSE). Developed by [Lucas Rayan Guerra](https://git.lucasrguerra.dev.br/lucasrguerra). diff --git a/README.md b/README.md index 87f4307..e4cca29 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Ideal para: ```bash # Clone o repositório -git clone https://github.com/lucasrguerra/pss.git +git clone https://git.lucasrguerra.dev.br/lucasrguerra/pss.git cd pss # Instale as dependências @@ -55,9 +55,36 @@ npm run preview ### Com Docker ```bash -docker compose up +docker compose up --build ``` +O `Dockerfile` compila o Vite dentro da própria imagem (Node 22) e entrega os +arquivos estáticos pelo nginx. Não há artefato pré-construído no repositório. + +### Imagem publicada + +Cada versão é publicada no registro de contêineres do Gitea (linux/arm64): + +```bash +docker run -p 8080:80 git.lucasrguerra.dev.br/lucasrguerra/pss:latest +``` + +Tags: `latest`, `X.Y.Z`, `X.Y` e `sha-`. A instância pública roda em +[pss.lucasrguerra.dev.br](https://pss.lucasrguerra.dev.br). + +### Integração contínua e releases + +- `.gitea/workflows/ci.yml`: tipos (`tsc -b`), testes, Trivy (dependências e segredos), + build da imagem, Trivy na imagem e um teste de fumaça (a SPA responde, cai no index + nas rotas internas e entrega os assets com cache imutável). Roda nas branches e toda + segunda-feira. +- `.gitea/workflows/release.yml`: na `main` e nas tags `vX.Y.Z`. Se o registro já tem a + imagem do mesmo conteúdo (`inputs-`), nada é refeito: só as tags novas são + aplicadas. Numa tag, cria a release no Gitea com as notas do [CHANGELOG](CHANGELOG.md). +- Para lançar: atualize o CHANGELOG, a versão em `package.json` e o rótulo em + `src/components/Layout/Sidebar.tsx`, faça o commit e envie a tag + (`git tag vX.Y.Z && git push gitea vX.Y.Z`). + --- ## 🎮 Como usar @@ -227,4 +254,4 @@ Contribuições são bem-vindas! Por favor, consulte nosso guia em [CONTRIBUTING ## 📜 Licença -Distribuído sob a [Licença MIT](./LICENSE). Desenvolvido por [Lucas Rayan Guerra](https://github.com/lucasrguerra). +Distribuído sob a [Licença MIT](./LICENSE). Desenvolvido por [Lucas Rayan Guerra](https://git.lucasrguerra.dev.br/lucasrguerra). diff --git a/dist.tar.gz b/dist.tar.gz deleted file mode 100644 index 6c3629d..0000000 Binary files a/dist.tar.gz and /dev/null differ diff --git a/package-lock.json b/package-lock.json index f60dcf9..c59b318 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pss", - "version": "1.2.0", + "version": "1.3.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pss", - "version": "1.2.0", + "version": "1.3.3", "license": "MIT", "dependencies": { "clsx": "^2.1.1", diff --git a/package.json b/package.json index 838e264..680ad0a 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,11 @@ { "name": "pss", - "version": "1.3.2", + "version": "1.3.3", "description": "Process Scheduler Simulator — an interactive educational tool that runs entirely in the browser", "main": "index.js", "scripts": { "dev": "vite", "build": "tsc -b && vite build", - "build:release": "tsc -b && vite build && tar --dereference -czf dist.tar.gz dist", "preview": "vite preview", "test": "vitest run", "test:watch": "vitest", @@ -39,14 +38,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/lucasrguerra/pss.git" + "url": "git+https://git.lucasrguerra.dev.br/lucasrguerra/pss.git" }, "keywords": [], "author": "Lucas Rayan Guerra", "license": "MIT", "type": "commonjs", "bugs": { - "url": "https://github.com/lucasrguerra/pss/issues" + "url": "https://git.lucasrguerra.dev.br/lucasrguerra/pss/issues" }, - "homepage": "https://github.com/lucasrguerra/pss#readme" + "homepage": "https://git.lucasrguerra.dev.br/lucasrguerra/pss#readme" } diff --git a/scripts/plano-imagem.sh b/scripts/plano-imagem.sh new file mode 100644 index 0000000..929124e --- /dev/null +++ b/scripts/plano-imagem.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Uso: scripts/plano-imagem.sh +# Imprime inputs=, image= e reuse= (para $GITHUB_OUTPUT). O hash cobre só os +# arquivos que entram no build da imagem, não a documentação nem a configuração +# de ferramentas. reuse=true quando o registro já tem inputs-. +set -euo pipefail +registry=$1; ns=${2,,} + +inputs=$(git ls-tree -r HEAD \ + | awk -F'\t' '$2 !~ /^(\.gitea|\.github|\.claude|\.wolf|\.superset|docs)\// && $2 !~ /^(README.*|CONTRIBUTING\.md|CHANGELOG\.md|CITATION\.cff|LICENSE|paper\.md|references\.bib|\.gitignore|\.gitattributes|scripts\/release-notes\.sh)$/' \ + | sha256sum | cut -c1-16) +token=$(curl -fsS "https://$registry/v2/token?service=container_registry&scope=repository:$ns:pull" | jq -r .token) +code=$(curl -s -o /dev/null -w '%{http_code}' -I \ + -H "Authorization: Bearer $token" \ + -H 'Accept: application/vnd.oci.image.index.v1+json,application/vnd.docker.distribution.manifest.list.v2+json,application/vnd.oci.image.manifest.v1+json' \ + "https://$registry/v2/$ns/manifests/inputs-$inputs") +echo "inputs-$inputs -> HTTP $code" >&2 +reuse=false; [ "$code" = 200 ] && reuse=true +echo "inputs=$inputs" +echo "image=$registry/$ns" +echo "reuse=$reuse" diff --git a/scripts/release-notes.sh b/scripts/release-notes.sh new file mode 100644 index 0000000..7664192 --- /dev/null +++ b/scripts/release-notes.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Imprime as notas de uma release: a imagem publicada (quando houver) e a seção +# do CHANGELOG.md daquela versão (ou o log de commits desde a tag anterior, se +# não houver seção). +# +# scripts/release-notes.sh v1.2.3 [git.exemplo.com/dono/projeto] +set -euo pipefail + +tag=${1:?uso: release-notes.sh [imagem]} +image=${2:-} +version=${tag#v} + +if [ -n "$image" ]; then + echo "## Imagem" + echo + echo '```' + echo "$image:$version" + echo '```' + echo +fi + +section="" +if [ -f CHANGELOG.md ]; then + section=$(awk -v head="## [$version]" ' + index($0, head) == 1 { found = 1; next } + found && /^## \[/ { exit } + found && /^\[[^]]+\]: / { next } + found { print } + ' CHANGELOG.md) +fi + +if [ -n "$(printf '%s' "$section" | tr -d '[:space:]')" ]; then + printf '%s\n' "$section" +else + previous=$(git describe --tags --abbrev=0 "$tag^" 2>/dev/null || true) + echo "## Mudanças${previous:+ desde $previous}" + echo + git log --no-merges --format='- %s (%h)' "${previous:+$previous..}$tag" +fi diff --git a/scripts/smoke-imagem.sh b/scripts/smoke-imagem.sh new file mode 100644 index 0000000..d0ab816 --- /dev/null +++ b/scripts/smoke-imagem.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Teste de fumaça da imagem: a SPA serve o index, cai no index em rotas internas +# e entrega os assets com cache longo. +# +# scripts/smoke-imagem.sh http://127.0.0.1:3998 +# +# Cada falha é impressa e o script sai com 1 no fim, para mostrar tudo de uma vez. +set -uo pipefail + +base=${1:?uso: smoke-imagem.sh } +falhas=0 +falhou() { echo "FALHOU $1"; falhas=$((falhas + 1)); } + +# O nginx leva um instante para subir. +for _ in $(seq 1 30); do curl -fsS -o /dev/null "$base/" 2>/dev/null && break; sleep 1; done + +index=$(curl -fsS "$base/") || { echo "FALHOU /: sem resposta"; exit 1; } +grep -q 'id="root"' <<< "$index" || falhou "/: o index não tem o #root" + +# Rota interna da SPA devolve o mesmo index (try_files), não 404. +[ "$(curl -s -o /dev/null -w '%{http_code}' "$base/qualquer/rota")" = 200 ] || falhou "/qualquer/rota: esperado 200" + +# Todo asset que o index referencia existe e tem cache imutável. +assets=$(grep -oE '(src|href)="/assets/[^"]+"' <<< "$index" | cut -d'"' -f2) +[ -n "$assets" ] || falhou "o index não referencia nenhum /assets/" +for a in $assets; do + head=$(curl -sI "$base$a") + grep -q '^HTTP/[0-9.]* 200' <<< "$head" || falhou "$a: esperado 200" + grep -qi 'cache-control:.*immutable' <<< "$head" || falhou "$a: sem cache imutável" +done + +[ "$falhas" = 0 ] && echo "ok: $(wc -w <<< "$assets") assets e rotas conferidos" +[ "$falhas" = 0 ] diff --git a/src/components/Layout/Sidebar.tsx b/src/components/Layout/Sidebar.tsx index ab98a10..89e81b5 100644 --- a/src/components/Layout/Sidebar.tsx +++ b/src/components/Layout/Sidebar.tsx @@ -12,7 +12,7 @@ const Sidebar = () => {
- PSS v1.3.2 + PSS v1.3.3
);