Compare commits

...
9 Commits
Author SHA1 Message Date
Lucas RayanandClaude Sonnet 5.5 0ff9aaaf88 Merge ci/gitea-registry: publish to the Gitea registry, workflows on Gitea Actions
CI / TypeScript (auth) (push) Successful in 2m53s
CI / Trivy (dependências, segredos e Dockerfiles) (push) Successful in 1m8s
Release / Namespace das imagens (push) Successful in 1s
CI / Lint de idioma (R9) (push) Successful in 4s
CI / TypeScript (storage) (push) Successful in 14s
CI / Go (protocols) (push) Successful in 3m16s
CI / Imagem (auth) (push) Successful in 1m0s
CI / Build (webmail) (push) Successful in 2m15s
CI / Imagem (migrations) (push) Successful in 26s
CI / Imagem (rspamd) (push) Successful in 27s
CI / Imagem (protocols) (push) Successful in 1m32s
CI / Imagem (storage) (push) Successful in 1m19s
CI / Imagem (webmail) (push) Successful in 3m31s
CI / Stack completa (push) Successful in 3m42s
Release / ci (push) Successful in 11m19s
Release / Build migrations (linux/amd64) (push) Successful in 2m12s
Release / Build auth (linux/amd64) (push) Successful in 4m25s
Release / Build rspamd (linux/amd64) (push) Successful in 42s
Release / Build storage (linux/amd64) (push) Successful in 2m47s
Release / Build protocols (linux/amd64) (push) Successful in 12m49s
Release / Build auth (linux/arm64) (push) Successful in 43s
Release / Build migrations (linux/arm64) (push) Successful in 28s
Release / Build protocols (linux/arm64) (push) Successful in 1m29s
Release / Build rspamd (linux/arm64) (push) Successful in 33s
Release / Build storage (linux/arm64) (push) Successful in 42s
Release / Build webmail (linux/arm64) (push) Successful in 2m43s
Release / Build webmail (linux/amd64) (push) Successful in 21m34s
Release / Publica auth (push) Successful in 22s
Release / Publica migrations (push) Successful in 22s
Release / Publica rspamd (push) Successful in 21s
Release / Publica protocols (push) Successful in 21s
Release / Publica webmail (push) Successful in 21s
Release / Publica storage (push) Successful in 21s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 09:59:03 -03:00
Lucas RayanandClaude Sonnet 5.5 7baaab2fd7 test(auth): give real Argon2id hashing a 30s test budget
CI / TypeScript (auth) (push) Successful in 2m51s
CI / TypeScript (storage) (push) Successful in 14s
CI / Go (protocols) (push) Successful in 3m12s
CI / Trivy (dependências, segredos e Dockerfiles) (push) Successful in 1m9s
CI / Lint de idioma (R9) (push) Successful in 4s
CI / Build (webmail) (push) Successful in 2m9s
CI / Imagem (auth) (push) Successful in 58s
CI / Imagem (migrations) (push) Successful in 25s
CI / Imagem (rspamd) (push) Successful in 28s
CI / Imagem (protocols) (push) Successful in 1m32s
CI / Imagem (storage) (push) Successful in 1m18s
CI / Imagem (webmail) (push) Successful in 3m30s
CI / Stack completa (push) Successful in 3m44s
The default 5s does not fit a chain of 11 hashes on a shared CI runner. The
production cost parameters are untouched.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 09:47:16 -03:00
Lucas RayanandClaude Sonnet 5.5 a28a9bc9d4 ci: run Go tests as a non-root user
CI / Lint de idioma (R9) (push) Successful in 22s
CI / TypeScript (auth) (push) Failing after 3m33s
CI / Go (protocols) (push) Successful in 5m6s
CI / Trivy (dependências, segredos e Dockerfiles) (push) Successful in 1m9s
CI / Build (webmail) (push) Successful in 2m6s
CI / Stack completa (push) Skipped
CI / Imagem (rspamd) (push) Successful in 29s
CI / Imagem (storage) (push) Successful in 1m19s
CI / TypeScript (storage) (push) Successful in 17s
CI / Imagem (migrations) (push) Successful in 2m24s
CI / Imagem (auth) (push) Successful in 2m58s
CI / Imagem (protocols) (push) Successful in 1m47s
CI / Imagem (webmail) (push) Successful in 2m35s
initdb, used by the embedded Postgres in the e2e suites, refuses to run as root.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 09:33:43 -03:00
Lucas RayanandClaude Sonnet 5.5 c30d618b3f ci: copy files into containers instead of bind-mounting, patch two advisories
CI / Lint de idioma (R9) (push) Successful in 9s
CI / Go (protocols) (push) Failing after 2m22s
CI / TypeScript (storage) (push) Successful in 12s
CI / TypeScript (auth) (push) Failing after 3m42s
CI / Imagem (migrations) (push) Successful in 1m45s
CI / Build (webmail) (push) Successful in 1m53s
CI / Stack completa (push) Skipped
CI / Trivy (dependências, segredos e Dockerfiles) (push) Successful in 1m3s
CI / Imagem (auth) (push) Successful in 2m53s
CI / Imagem (rspamd) (push) Successful in 27s
CI / Imagem (protocols) (push) Successful in 2m10s
CI / Imagem (storage) (push) Canceled after 2m9s
CI / Imagem (webmail) (push) Canceled after 1m6s
The job workspace is a volume the DinD daemon cannot see, so migrations and the
Trivy filesystem scan now docker cp their inputs. Trivy's image gate flagged
next 15.5.27 (unauthenticated RCE in image optimisation, fixed in 15.5.28) and
go-jose 4.1.4 (DoS, fixed in 4.1.6); both are patch bumps.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 09:22:42 -03:00
Lucas RayanandClaude Sonnet 5.5 248e857b85 ci: rerun on the runner with a shared /workspace
CI / Lint de idioma (R9) (push) Successful in 10s
CI / Go (protocols) (push) Failing after 29s
CI / TypeScript (auth) (push) Failing after 37s
CI / TypeScript (storage) (push) Successful in 25s
CI / Trivy (dependências, segredos e Dockerfiles) (push) Successful in 1m6s
CI / Build (webmail) (push) Successful in 1m50s
CI / Stack completa (push) Skipped
CI / Imagem (migrations) (push) Successful in 1m39s
CI / Imagem (auth) (push) Successful in 2m58s
CI / Imagem (rspamd) (push) Successful in 29s
CI / Imagem (protocols) (push) Failing after 1m50s
CI / Imagem (storage) (push) Successful in 1m20s
CI / Imagem (webmail) (push) Failing after 2m40s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 09:11:32 -03:00
Lucas RayanandClaude Sonnet 5.5 33fa05b019 ci: run Trivy from its official image and drop the gha cache
CI / Imagem (protocols) (push) Failing after 1m29s
CI / Lint de idioma (R9) (push) Successful in 3s
CI / Go (protocols) (push) Failing after 30s
CI / TypeScript (auth) (push) Failing after 36s
CI / TypeScript (storage) (push) Successful in 16s
CI / Trivy (dependências, segredos e Dockerfiles) (push) Successful in 1m14s
CI / Build (webmail) (push) Successful in 1m57s
CI / Stack completa (push) Skipped
CI / Imagem (auth) (push) Successful in 57s
CI / Imagem (migrations) (push) Successful in 39s
CI / Imagem (rspamd) (push) Successful in 30s
CI / Imagem (storage) (push) Successful in 1m20s
CI / Imagem (webmail) (push) Canceled after 2m58s
aquasecurity/trivy-action clones the Trivy repository from the workflow's own
server, which on Gitea is not GitHub. The gha buildx cache has no Gitea
counterpart.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 09:01:45 -03:00
Lucas RayanandClaude Sonnet 5.5 91c5df433e ci: build linux/amd64 under QEMU on the arm64 runner
CI / Lint de idioma (R9) (push) Successful in 10s
CI / Go (protocols) (push) Failing after 31s
CI / TypeScript (auth) (push) Failing after 37s
CI / TypeScript (storage) (push) Successful in 23s
CI / Build (webmail) (push) Successful in 1m47s
CI / Stack completa (push) Skipped
CI / Trivy (dependências, segredos e Dockerfiles) (push) Failing after 3m8s
CI / Imagem (auth) (push) Canceled after 3m32s
CI / Imagem (migrations) (push) Canceled after 2m4s
CI / Imagem (protocols) (push) Canceled after 48s
CI / Imagem (rspamd) (push) Canceled after 48s
CI / Imagem (storage) (push) Canceled after 0s
CI / Imagem (webmail) (push) Canceled after 0s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 08:56:34 -03:00
Lucas RayanandClaude Sonnet 5.5 59363734ff ci: publish images to the Gitea registry, use the gitea context
CI / Lint de idioma (R9) (push) Successful in 4s
CI / Go (protocols) (push) Failing after 5s
CI / TypeScript (auth) (push) Failing after 6s
CI / TypeScript (storage) (push) Successful in 12s
CI / Build (webmail) (push) Successful in 1m38s
CI / Stack completa (push) Skipped
CI / Trivy (dependências, segredos e Dockerfiles) (push) Failing after 1m32s
CI / Imagem (auth) (push) Failing after 11s
CI / Imagem (migrations) (push) Failing after 11s
CI / Imagem (protocols) (push) Failing after 11s
CI / Imagem (rspamd) (push) Failing after 11s
CI / Imagem (storage) (push) Failing after 11s
CI / Imagem (webmail) (push) Failing after 11s
Release now pushes to git.lucasrguerra.dev.br instead of ghcr.io, tags each
architecture separately (no artifact upload, no gha cache) and joins them with
imagetools. GITHUB_OUTPUT and GITHUB_STEP_SUMMARY stay: they are the runner's
own interface and the GITEA_* equivalents are not set.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 08:48:44 -03:00
Lucas RayanandClaude Sonnet 5.5 9a722bc5ac ci: publish images to the Gitea registry and move workflows to .gitea
CI / Lint de idioma (R9) (push) Successful in 3s
CI / Go (protocols) (push) Failing after 20s
CI / TypeScript (auth) (push) Canceled after 4s
CI / TypeScript (storage) (push) Canceled after 0s
CI / Build (webmail) (push) Canceled after 0s
CI / Trivy (dependências, segredos e Dockerfiles) (push) Canceled after 0s
CI / Imagem (auth) (push) Canceled after 0s
CI / Imagem (migrations) (push) Canceled after 0s
CI / Imagem (protocols) (push) Canceled after 0s
CI / Imagem (rspamd) (push) Canceled after 0s
CI / Imagem (storage) (push) Canceled after 0s
CI / Imagem (webmail) (push) Canceled after 0s
CI / Stack completa (push) Canceled after 0s
Release now pushes to git.lucasrguerra.dev.br instead of ghcr.io, tags each
architecture separately (no artifact upload, no gha cache) and joins them with
imagetools. Workflows live in .gitea/workflows and use the gitea context.
GITHUB_OUTPUT and GITHUB_STEP_SUMMARY stay: they are the runner's own interface.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 08:48:14 -03:00
11 changed files with 150 additions and 135 deletions

No files matched your search

+2 -2
View File
@@ -1,10 +1,10 @@
# --- Container images --- # --- Container images ---
# Published to GHCR by .github/workflows/release.yml, multi-architecture # Published to the Gitea registry by .gitea/workflows/release.yml, multi-architecture
# (linux/amd64 and linux/arm64). The deploy host pulls these instead of # (linux/amd64 and linux/arm64). The deploy host pulls these instead of
# compiling: `docker compose pull && docker compose up -d`. Running # compiling: `docker compose pull && docker compose up -d`. Running
# `docker compose build` locally still overrides them with a local build. # `docker compose build` locally still overrides them with a local build.
# Pin IMAGE_TAG to a released version (e.g. 1.2.3) or a sha- tag to roll back. # Pin IMAGE_TAG to a released version (e.g. 1.2.3) or a sha- tag to roll back.
IMAGE_NAMESPACE=ghcr.io/lucasrguerra/lambdamail IMAGE_NAMESPACE=git.lucasrguerra.dev.br/lucasrguerra/lambdamail
IMAGE_TAG=latest IMAGE_TAG=latest
# --- Postgres --- # --- Postgres ---
+2 -2
View File
@@ -10,9 +10,9 @@
# and must be URL-safe - see the note above them. # and must be URL-safe - see the note above them.
# --- Container images ------------------------------------------------------- # --- Container images -------------------------------------------------------
# Published by .github/workflows/release.yml for linux/amd64 and linux/arm64. # Published by .gitea/workflows/release.yml for linux/amd64 and linux/arm64.
# Pin IMAGE_TAG to a released version or a sha- tag to roll back. # Pin IMAGE_TAG to a released version or a sha- tag to roll back.
IMAGE_NAMESPACE=ghcr.io/lucasrguerra/lambdamail IMAGE_NAMESPACE=git.lucasrguerra.dev.br/lucasrguerra/lambdamail
IMAGE_TAG=latest IMAGE_TAG=latest
# --- Identity --------------------------------------------------------------- # --- Identity ---------------------------------------------------------------
@@ -15,7 +15,7 @@ on:
workflow_call: workflow_call:
concurrency: concurrency:
group: ci-${{ github.ref }} group: ci-${{ gitea.ref }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
@@ -57,16 +57,30 @@ jobs:
cache-dependency-path: services/protocols/go.sum cache-dependency-path: services/protocols/go.sum
- name: Run migrations - name: Run migrations
run: | run: |
docker run --rm --network host \ # The job's workspace is a volume of its own, invisible to the Docker
-v "$(pwd)/../../migrations:/migrations" \ # daemon the job talks to, so `-v "$PWD:..."` would mount an empty
migrate/migrate:v4.17.1 \ # directory. Files are copied into the container instead.
cid=$(docker create --network host migrate/migrate:v4.17.1 \
-path=/migrations \ -path=/migrations \
-database "postgres://lambdamail_test:lambdamail_test@localhost:5432/lambdamail_test?sslmode=disable" \ -database "postgres://lambdamail_test:lambdamail_test@localhost:5432/lambdamail_test?sslmode=disable" \
up up)
docker cp "$(pwd)/../../migrations/." "$cid:/migrations"
docker start -a "$cid"
status=$(docker inspect -f '{{.State.ExitCode}}' "$cid")
docker rm "$cid" >/dev/null
exit "$status"
- run: go vet ./... - run: go vet ./...
- run: go test ./... -v # The end-to-end suites start an embedded Postgres, and initdb refuses to
# run as root, which is what a job container is. Run the tests as a
# throwaway unprivileged user (GitHub's hosted runners already are one).
- name: Testes
env: env:
TEST_DATABASE_URL: postgres://lambdamail_test:lambdamail_test@localhost:5432/lambdamail_test?sslmode=disable TEST_DATABASE_URL: postgres://lambdamail_test:lambdamail_test@localhost:5432/lambdamail_test?sslmode=disable
run: |
id tester >/dev/null 2>&1 || useradd -m -u 1001 tester
chown -R tester:tester "$GITHUB_WORKSPACE"
runuser -u tester -- env "HOME=/home/tester" "PATH=$PATH" \
"TEST_DATABASE_URL=$TEST_DATABASE_URL" go test ./... -v
ts-auth: ts-auth:
name: TypeScript (auth) name: TypeScript (auth)
@@ -97,12 +111,18 @@ jobs:
# the database, so a mock would prove nothing. # the database, so a mock would prove nothing.
- name: Run migrations - name: Run migrations
run: | run: |
docker run --rm --network host \ # The job's workspace is a volume of its own, invisible to the Docker
-v "$(pwd)/../../migrations:/migrations" \ # daemon the job talks to, so `-v "$PWD:..."` would mount an empty
migrate/migrate:v4.17.1 \ # directory. Files are copied into the container instead.
cid=$(docker create --network host migrate/migrate:v4.17.1 \
-path=/migrations \ -path=/migrations \
-database "postgres://lambdamail_test:lambdamail_test@localhost:5432/lambdamail_test?sslmode=disable" \ -database "postgres://lambdamail_test:lambdamail_test@localhost:5432/lambdamail_test?sslmode=disable" \
up up)
docker cp "$(pwd)/../../migrations/." "$cid:/migrations"
docker start -a "$cid"
status=$(docker inspect -f '{{.State.ExitCode}}' "$cid")
docker rm "$cid" >/dev/null
exit "$status"
- run: npm install - run: npm install
- run: npm run lint - run: npm run lint
- run: npm test - run: npm test
@@ -148,14 +168,20 @@ jobs:
# A mail server holds credentials for every mailbox it serves, so the # A mail server holds credentials for every mailbox it serves, so the
# secret scanner matters as much as the vulnerability one here: a DKIM # secret scanner matters as much as the vulnerability one here: a DKIM
# key or a Cloudflare token committed by accident is a full compromise. # key or a Cloudflare token committed by accident is a full compromise.
#
# Trivy runs from its official image rather than aquasecurity/trivy-action:
# that action clones the Trivy repository from the workflow's own server,
# which on Gitea is not GitHub.
- name: Repositório - name: Repositório
uses: aquasecurity/trivy-action@v0.36.0 run: |
with: cid=$(docker create --network host -w /src aquasec/trivy:0.70.0 \
scan-type: fs fs --scanners vuln,secret,misconfig --severity HIGH,CRITICAL --exit-code 1 \
scanners: vuln,secret,misconfig --skip-dirs services/auth/node_modules,services/storage/node_modules,services/webmail/node_modules,services/webmail/.next .)
severity: HIGH,CRITICAL docker cp . "$cid:/src"
exit-code: '1' docker start -a "$cid"
skip-dirs: services/auth/node_modules,services/storage/node_modules,services/webmail/node_modules,services/webmail/.next status=$(docker inspect -f '{{.State.ExitCode}}' "$cid")
docker rm "$cid" >/dev/null
exit "$status"
# MEDIUM is reported but does not fail the build. # MEDIUM is reported but does not fail the build.
# #
@@ -168,13 +194,15 @@ jobs:
# Reporting rather than gating, because failing on every MEDIUM makes the # Reporting rather than gating, because failing on every MEDIUM makes the
# scanner something people learn to skip. # scanner something people learn to skip.
- name: Repositório (MEDIUM, informativo) - name: Repositório (MEDIUM, informativo)
uses: aquasecurity/trivy-action@v0.36.0 run: |
with: cid=$(docker create --network host -w /src aquasec/trivy:0.70.0 \
scan-type: fs fs --scanners vuln,secret --severity MEDIUM --exit-code 0 \
scanners: vuln,secret --skip-dirs services/auth/node_modules,services/storage/node_modules,services/webmail/node_modules,services/webmail/.next .)
severity: MEDIUM docker cp . "$cid:/src"
exit-code: '0' docker start -a "$cid"
skip-dirs: services/auth/node_modules,services/storage/node_modules,services/webmail/node_modules,services/webmail/.next status=$(docker inspect -f '{{.State.ExitCode}}' "$cid")
docker rm "$cid" >/dev/null
exit "$status"
image: image:
name: Imagem (${{ matrix.service }}) name: Imagem (${{ matrix.service }})
@@ -198,8 +226,6 @@ jobs:
push: false push: false
load: true load: true
tags: lambdamail-${{ matrix.service }}:ci tags: lambdamail-${{ matrix.service }}:ci
cache-from: type=gha,scope=${{ matrix.service }}
cache-to: type=gha,mode=max,scope=${{ matrix.service }}
# Scanned only for the images whose contents we author. migrations and # Scanned only for the images whose contents we author. migrations and
# rspamd add .sql and .conf files to an upstream image and nothing else, # rspamd add .sql and .conf files to an upstream image and nothing else,
@@ -209,11 +235,9 @@ jobs:
# that their base images have to be watched. # that their base images have to be watched.
- name: Trivy na imagem - name: Trivy na imagem
if: matrix.service != 'migrations' && matrix.service != 'rspamd' if: matrix.service != 'migrations' && matrix.service != 'rspamd'
uses: aquasecurity/trivy-action@v0.36.0 run: |
with: docker run --rm --network host -e DOCKER_HOST=tcp://127.0.0.1:2375 aquasec/trivy:0.70.0 \
image-ref: lambdamail-${{ matrix.service }}:ci image --severity HIGH,CRITICAL --exit-code 1 lambdamail-${{ matrix.service }}:ci
severity: HIGH,CRITICAL
exit-code: '1'
compose-up: compose-up:
name: Stack completa name: Stack completa
@@ -1,8 +1,9 @@
name: Release name: Release
# Publishes the four service images to GHCR after a merge into main, and again # Publishes the service images to the Gitea container registry after a merge
# under semver tags when a v* tag is pushed. The deploy host only pulls, so # into main, and again under semver tags when a v* tag is pushed. The deploy
# nothing has to be built on the server. # host only pulls, so nothing has to be built on the server. Needs the user
# secret REGISTRY_TOKEN (a Gitea token with the write:package scope).
on: on:
push: push:
branches: [main] branches: [main]
@@ -14,15 +15,15 @@ concurrency:
cancel-in-progress: false cancel-in-progress: false
env: env:
REGISTRY: ghcr.io REGISTRY: git.lucasrguerra.dev.br
jobs: jobs:
# Nothing is published unless the full suite passes first. # Nothing is published unless the full suite passes first.
ci: ci:
uses: ./.github/workflows/ci.yml uses: ./.gitea/workflows/ci.yml
# GHCR rejects an uppercase path, and this repository is "lucasrguerra/LambdaMail". # Registry paths must be lowercase, and this repository is "lucasrguerra/LambdaMail".
# Lowercasing github.repository here keeps the workflow fork-safe instead of # Lowercasing gitea.repository here keeps the workflow fork-safe instead of
# hardcoding the owner. # hardcoding the owner.
prepare: prepare:
name: Namespace das imagens name: Namespace das imagens
@@ -31,7 +32,10 @@ jobs:
namespace: ${{ steps.namespace.outputs.value }} namespace: ${{ steps.namespace.outputs.value }}
steps: steps:
- id: namespace - id: namespace
run: echo "value=${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT" # GITHUB_OUTPUT is the runner's own interface: Gitea sets it under that name.
run: |
repo='${{ gitea.repository }}'
echo "value=${repo,,}" >> "$GITHUB_OUTPUT"
# Each architecture is built on a runner of its own kind. QEMU emulation # Each architecture is built on a runner of its own kind. QEMU emulation
# would also work, but it makes the Go and Next builds roughly an order of # would also work, but it makes the Go and Next builds roughly an order of
@@ -54,7 +58,7 @@ jobs:
platform: [linux/amd64, linux/arm64] platform: [linux/amd64, linux/arm64]
include: include:
- platform: linux/amd64 - platform: linux/amd64
runner: ubuntu-latest runner: linux-amd64
arch: amd64 arch: amd64
- platform: linux/arm64 - platform: linux/arm64
runner: ubuntu-24.04-arm runner: ubuntu-24.04-arm
@@ -62,14 +66,19 @@ jobs:
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
# The only runner is arm64; linux/amd64 is built under QEMU emulation
# (label linux-amd64 points at the same runner), so it is slower.
- uses: docker/setup-qemu-action@v3
if: matrix.arch == 'amd64'
- uses: docker/setup-buildx-action@v4 - uses: docker/setup-buildx-action@v4
- name: Autentica no GHCR - name: Autentica no registro
uses: docker/login-action@v4 uses: docker/login-action@v4
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ gitea.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.REGISTRY_TOKEN }}
- name: Metadados - name: Metadados
id: meta id: meta
@@ -77,9 +86,10 @@ jobs:
with: with:
images: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }} images: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}
# Pushed by digest only: the tags are attached later, once both # Each architecture is pushed under its own tag (no digest artifacts: they
# architectures exist and can be joined into a single manifest list. # are not portable to Gitea). The final tags are attached later, once both
- name: Build e push por digest # exist and can be joined into a single manifest list.
- name: Build e push por arquitetura
id: build id: build
uses: docker/build-push-action@v7 uses: docker/build-push-action@v7
with: with:
@@ -87,26 +97,10 @@ jobs:
file: services/${{ matrix.service }}/Dockerfile file: services/${{ matrix.service }}/Dockerfile
platforms: ${{ matrix.platform }} platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }},push-by-digest=true,name-canonical=true,push=true push: true
cache-from: type=gha,scope=${{ matrix.service }}-${{ matrix.arch }} tags: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}:build-${{ gitea.sha }}-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.service }}-${{ matrix.arch }} cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}:buildcache-${{ matrix.arch }}
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}:buildcache-${{ matrix.arch }},mode=max
- name: Exporta o digest
run: |
mkdir -p /tmp/digests
# The output already carries the `sha256:` prefix; the file name must
# not, since the merge step prepends it again.
digest='${{ steps.build.outputs.digest }}'
touch "/tmp/digests/${digest#sha256:}"
- name: Guarda o digest
uses: actions/upload-artifact@v7
with:
# Slashes are not allowed in artifact names, hence the arch alias.
name: digest-${{ matrix.service }}-${{ matrix.arch }}
path: /tmp/digests/*
retention-days: 1
if-no-files-found: error
publish: publish:
name: Publica ${{ matrix.service }} name: Publica ${{ matrix.service }}
@@ -124,19 +118,12 @@ jobs:
- uses: docker/setup-buildx-action@v4 - uses: docker/setup-buildx-action@v4
- name: Autentica no GHCR - name: Autentica no registro
uses: docker/login-action@v4 uses: docker/login-action@v4
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ gitea.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.REGISTRY_TOKEN }}
- name: Recupera os digests
uses: actions/download-artifact@v7
with:
pattern: digest-${{ matrix.service }}-*
merge-multiple: true
path: /tmp/digests
# `latest` is what compose pulls. The semver tags only materialise on a # `latest` is what compose pulls. The semver tags only materialise on a
# v* tag push and make it possible to pin or roll back to an exact # v* tag push and make it possible to pin or roll back to an exact
@@ -156,11 +143,11 @@ jobs:
# Joins both architectures under one manifest list, so `docker pull` # Joins both architectures under one manifest list, so `docker pull`
# resolves to the right one automatically. # resolves to the right one automatically.
- name: Cria o manifesto multi-arquitetura - name: Cria o manifesto multi-arquitetura
working-directory: /tmp/digests
run: | run: |
image='${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}'
docker buildx imagetools create \ docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< '${{ steps.meta.outputs.json }}') \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< '${{ steps.meta.outputs.json }}') \
$(printf '${{ env.REGISTRY }}/${{ needs.prepare.outputs.namespace }}/${{ matrix.service }}@sha256:%s ' *) "$image:build-${{ gitea.sha }}-amd64" "$image:build-${{ gitea.sha }}-arm64"
- name: Confere o resultado - name: Confere o resultado
run: | run: |
+6 -6
View File
@@ -60,7 +60,7 @@ services:
# probe - a health check on a container that is meant to stop would report # probe - a health check on a container that is meant to stop would report
# unhealthy exactly when it has done its job. # unhealthy exactly when it has done its job.
migrate: migrate:
image: ${IMAGE_NAMESPACE:-ghcr.io/lucasrguerra/lambdamail}/migrations:${IMAGE_TAG:-latest} image: ${IMAGE_NAMESPACE:-git.lucasrguerra.dev.br/lucasrguerra/lambdamail}/migrations:${IMAGE_TAG:-latest}
# "latest" is a moving tag and Docker considers a tag it already holds # "latest" is a moving tag and Docker considers a tag it already holds
# locally to be satisfied, so recreating this container silently reused # locally to be satisfied, so recreating this container silently reused
# the cached image and the newly published one was never fetched: the # the cached image and the newly published one was never fetched: the
@@ -80,7 +80,7 @@ services:
# ------------------------------------------------------------ RSPAMD # ------------------------------------------------------------ RSPAMD
rspamd: rspamd:
image: ${IMAGE_NAMESPACE:-ghcr.io/lucasrguerra/lambdamail}/rspamd:${IMAGE_TAG:-latest} image: ${IMAGE_NAMESPACE:-git.lucasrguerra.dev.br/lucasrguerra/lambdamail}/rspamd:${IMAGE_TAG:-latest}
# "latest" is a moving tag and Docker considers a tag it already holds # "latest" is a moving tag and Docker considers a tag it already holds
# locally to be satisfied, so recreating this container silently reused # locally to be satisfied, so recreating this container silently reused
# the cached image and the newly published one was never fetched: the # the cached image and the newly published one was never fetched: the
@@ -140,7 +140,7 @@ services:
# -------------------------------------------------------------- AUTH # -------------------------------------------------------------- AUTH
auth: auth:
image: ${IMAGE_NAMESPACE:-ghcr.io/lucasrguerra/lambdamail}/auth:${IMAGE_TAG:-latest} image: ${IMAGE_NAMESPACE:-git.lucasrguerra.dev.br/lucasrguerra/lambdamail}/auth:${IMAGE_TAG:-latest}
# "latest" is a moving tag and Docker considers a tag it already holds # "latest" is a moving tag and Docker considers a tag it already holds
# locally to be satisfied, so recreating this container silently reused # locally to be satisfied, so recreating this container silently reused
# the cached image and the newly published one was never fetched: the # the cached image and the newly published one was never fetched: the
@@ -178,7 +178,7 @@ services:
# ----------------------------------------------------------- STORAGE # ----------------------------------------------------------- STORAGE
storage: storage:
image: ${IMAGE_NAMESPACE:-ghcr.io/lucasrguerra/lambdamail}/storage:${IMAGE_TAG:-latest} image: ${IMAGE_NAMESPACE:-git.lucasrguerra.dev.br/lucasrguerra/lambdamail}/storage:${IMAGE_TAG:-latest}
# "latest" is a moving tag and Docker considers a tag it already holds # "latest" is a moving tag and Docker considers a tag it already holds
# locally to be satisfied, so recreating this container silently reused # locally to be satisfied, so recreating this container silently reused
# the cached image and the newly published one was never fetched: the # the cached image and the newly published one was never fetched: the
@@ -214,7 +214,7 @@ services:
# --------------------------------------------------------- PROTOCOLS # --------------------------------------------------------- PROTOCOLS
protocols: protocols:
image: ${IMAGE_NAMESPACE:-ghcr.io/lucasrguerra/lambdamail}/protocols:${IMAGE_TAG:-latest} image: ${IMAGE_NAMESPACE:-git.lucasrguerra.dev.br/lucasrguerra/lambdamail}/protocols:${IMAGE_TAG:-latest}
# "latest" is a moving tag and Docker considers a tag it already holds # "latest" is a moving tag and Docker considers a tag it already holds
# locally to be satisfied, so recreating this container silently reused # locally to be satisfied, so recreating this container silently reused
# the cached image and the newly published one was never fetched: the # the cached image and the newly published one was never fetched: the
@@ -297,7 +297,7 @@ services:
# -------------------------------------------------------------- WEB # -------------------------------------------------------------- WEB
web: web:
image: ${IMAGE_NAMESPACE:-ghcr.io/lucasrguerra/lambdamail}/webmail:${IMAGE_TAG:-latest} image: ${IMAGE_NAMESPACE:-git.lucasrguerra.dev.br/lucasrguerra/lambdamail}/webmail:${IMAGE_TAG:-latest}
# "latest" is a moving tag and Docker considers a tag it already holds # "latest" is a moving tag and Docker considers a tag it already holds
# locally to be satisfied, so recreating this container silently reused # locally to be satisfied, so recreating this container silently reused
# the cached image and the newly published one was never fetched: the # the cached image and the newly published one was never fetched: the
+2 -2
View File
@@ -176,7 +176,7 @@ have propagated and the IP has some sending history.
## Upgrading ## Upgrading
Images are published to GHCR for `linux/amd64` and `linux/arm64` on every merge Images are published to the Gitea container registry for `linux/amd64` and `linux/arm64` on every merge
to `main`. to `main`.
```bash ```bash
@@ -198,7 +198,7 @@ every release, which is what forces the pull.
IMAGE_TAG=sha-<full commit sha> IMAGE_TAG=sha-<full commit sha>
The tags are published by `release.yml`; `docker buildx imagetools inspect The tags are published by `release.yml`; `docker buildx imagetools inspect
ghcr.io/<owner>/lambdamail/webmail:latest` shows what `latest` currently points git.lucasrguerra.dev.br/<owner>/lambdamail/webmail:latest` shows what `latest` currently points
at. at.
**Pinning has a second edge: bump it on every release.** A pinned tag is an **Pinning has a second edge: bump it on every release.** A pinned tag is an
+4
View File
@@ -2,6 +2,10 @@ import { defineConfig } from "vitest/config";
export default defineConfig({ export default defineConfig({
test: { test: {
// Real Argon2id hashes cost 1-3s each on a shared CI runner, and several
// tests chain a dozen of them. The production cost parameters stay as they
// are; the default 5s budget is what does not fit.
testTimeout: 30_000,
// session.ts refuses to load without a real signing secret, and ESM hoists // session.ts refuses to load without a real signing secret, and ESM hoists
// imports - so the value has to exist before any test file is evaluated, // imports - so the value has to exist before any test file is evaluated,
// which an assignment inside a test file cannot guarantee. // which an assignment inside a test file cannot guarantee.
+1 -1
View File
@@ -21,7 +21,7 @@ require (
require ( require (
github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-jose/go-jose/v4 v4.1.6 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect
+2 -2
View File
@@ -23,8 +23,8 @@ github.com/fergusstrange/embedded-postgres v1.30.0 h1:ewv1e6bBlqOIYtgGgRcEnNDpfG
github.com/fergusstrange/embedded-postgres v1.30.0/go.mod h1:w0YvnCgf19o6tskInrOOACtnqfVlOvluz3hlNLY7tRk= github.com/fergusstrange/embedded-postgres v1.30.0/go.mod h1:w0YvnCgf19o6tskInrOOACtnqfVlOvluz3hlNLY7tRk=
github.com/go-acme/lego/v4 v4.35.2 h1:uVQg+KC/yj9R2g7Q9W5wDqhvQvxV5SMu5eqFVoN5xZU= github.com/go-acme/lego/v4 v4.35.2 h1:uVQg+KC/yj9R2g7Q9W5wDqhvQvxV5SMu5eqFVoN5xZU=
github.com/go-acme/lego/v4 v4.35.2/go.mod h1:pX2jN5n8OphMGY1IaMjYm5DAEzguBaKRt8AvJAgJXpc= github.com/go-acme/lego/v4 v4.35.2/go.mod h1:pX2jN5n8OphMGY1IaMjYm5DAEzguBaKRt8AvJAgJXpc=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.6 h1:sNRkftxFJxwsILiQukqp5z83QBSmntVwmgRY7AqBDO4=
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-jose/go-jose/v4 v4.1.6/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+41 -41
View File
@@ -14,7 +14,7 @@
"framer-motion": "^11.18.2", "framer-motion": "^11.18.2",
"imagetracerjs": "1.2.6", "imagetracerjs": "1.2.6",
"lucide-react": "^0.474.0", "lucide-react": "^0.474.0",
"next": "15.5.27", "next": "15.5.28",
"qrcode-generator": "^2.0.4", "qrcode-generator": "^2.0.4",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
@@ -1334,15 +1334,15 @@
} }
}, },
"node_modules/@next/env": { "node_modules/@next/env": {
"version": "15.5.27", "version": "15.5.28",
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.27.tgz", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.28.tgz",
"integrity": "sha512-WC5hvVqiuFOcRyz6gKPeJkiVvSMf3scNeAV49CdWtVqxmUlzh8UKR6YHt9yiNkh8LngtzqSlVz07/Ma3ZAzchA==", "integrity": "sha512-xxW5EBI8fMPuVTPBW7LL980IRbuDXlbkUAwp9DDhtb4Lnich3nJ13uYpvrX4VE74hWPf6AvHHAMmGCidcHjbfg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@next/swc-darwin-arm64": { "node_modules/@next/swc-darwin-arm64": {
"version": "15.5.27", "version": "15.5.28",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.27.tgz", "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.28.tgz",
"integrity": "sha512-fBA9QurmK4+uj4wNhmRTuO5zMVRaBHCvp6H93zTG/5i6Yi6TewbvbesXB8YQNj8T99Cwrow87hHiFV6wL/hyMA==", "integrity": "sha512-dPoQYXcfSIqx5o+TOXVXO7kONK5qbtNRDeiTfII0bT3puGIT6VF5e0QwmxNZfEZ5Zety5f3m9P/p4xqwfiLlPg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1356,9 +1356,9 @@
} }
}, },
"node_modules/@next/swc-darwin-x64": { "node_modules/@next/swc-darwin-x64": {
"version": "15.5.27", "version": "15.5.28",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.27.tgz", "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.28.tgz",
"integrity": "sha512-FGZwJwry8qZNHxD/nfwrCwCHHrqx6epqItkkxbYcBQEQ2uw5S43bY83fQB5zbAsZE7TELMjoYkbhsQ4oKM7wwg==", "integrity": "sha512-RgCJ7buBivTH+6sddqaVm+/v8kwMHlFkobvdt3DfETutws5HrxnToMIWSN9X3MZ3aAi7BbKrGZZWUDQLEycXqw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1372,9 +1372,9 @@
} }
}, },
"node_modules/@next/swc-linux-arm64-gnu": { "node_modules/@next/swc-linux-arm64-gnu": {
"version": "15.5.27", "version": "15.5.28",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.27.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.28.tgz",
"integrity": "sha512-GQulAaJYNF1RYuatlsw9/uzbKz4if2V7Ic9nVO+P0ljYPm4yBRTFIl6g5YkKTw40lMLbpy9edLoH44g3jSHMYA==", "integrity": "sha512-OHXQe13olE59aKe++zHavGcJPh5SLHXi6Anret3BBeq1+qhSuOJDNAli/EDDfxs6UTqA4Qddo4uyYry2slyjiQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1391,9 +1391,9 @@
} }
}, },
"node_modules/@next/swc-linux-arm64-musl": { "node_modules/@next/swc-linux-arm64-musl": {
"version": "15.5.27", "version": "15.5.28",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.27.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.28.tgz",
"integrity": "sha512-URFFsUmu8Pi0/1qdnUI7tx7keZw1Mt5zHfrIX1RfDG9Nih8DYxK54WZsDJUX2ZTlii1jgotL7qcbwKnSAES6xQ==", "integrity": "sha512-nTB7bB6vfn4ezCJNYP43ukt2o8L3+WxKF/sTcTxepdXsJh3SM9RhlU7wrZYbH7L4HO5M87dqDTwm8587vAkCiw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1410,9 +1410,9 @@
} }
}, },
"node_modules/@next/swc-linux-x64-gnu": { "node_modules/@next/swc-linux-x64-gnu": {
"version": "15.5.27", "version": "15.5.28",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.27.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.28.tgz",
"integrity": "sha512-aJp74z7uvU5jQhyH7lyk+4qSRcTdKbKT9NQE3jiO9kWYkoHZjKi0zzHDbCoZ+v2sJDEiJ/z58R0UlWMGWNF3HA==", "integrity": "sha512-WXCfVI32rW3xCusoUX1RpZKtyChWU2Zwe27JUDmM3KrXwcDNc9vV4zEq0GKFAURnuQ/Ouo/XqPvB0yh445XZCg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1429,9 +1429,9 @@
} }
}, },
"node_modules/@next/swc-linux-x64-musl": { "node_modules/@next/swc-linux-x64-musl": {
"version": "15.5.27", "version": "15.5.28",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.27.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.28.tgz",
"integrity": "sha512-ILl2bQ8MbpvyMpY8AXljb3BXs0MJSC8HXZ6QV5AWpVS7ZELqNPtrHvhUk+SNarvjvICXX8bCS287TCi+gUFVYg==", "integrity": "sha512-Miq+NDb/HWsXuxseYxMM6fuEOQXgTziarkKIRpVKCn8W69cEfhQaQr+90pO+u4q2CbJR2wzXmUuCFyZaJs/Nng==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1448,9 +1448,9 @@
} }
}, },
"node_modules/@next/swc-win32-arm64-msvc": { "node_modules/@next/swc-win32-arm64-msvc": {
"version": "15.5.27", "version": "15.5.28",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.27.tgz", "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.28.tgz",
"integrity": "sha512-1MB9Rh5y8TOPN2x7naNTJNkCZQav9c70AI7EBYXxsEewwOffC4DjjwBwKf20NBvcA+u+5jDv7nVmHxunlP2d7Q==", "integrity": "sha512-5LR79lemgDlgxvG7MQJ+zHCkVcrUoQuO7CiIvq3I58vNW4goSOE2b+19Z6FujRWMouvni4gvudYFuFuNxolGWA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1464,9 +1464,9 @@
} }
}, },
"node_modules/@next/swc-win32-x64-msvc": { "node_modules/@next/swc-win32-x64-msvc": {
"version": "15.5.27", "version": "15.5.28",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.27.tgz", "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.28.tgz",
"integrity": "sha512-XtYgpGOswgnyBWXzOEG6q9RhvnSw0CEZ0MbWqCsl5PISgt9EzvJvNW5mU5n1PsphX/lnamzxjgQXH2GUJL8nqg==", "integrity": "sha512-GNipvXUYROSTf4PXEyH95FrWImIB5zdaK6uEOg0RP1/X2drnvoMwiBkJ0VWdzX7w3iiBm3y5g2TxkKDGVTOQcg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -3016,12 +3016,12 @@
} }
}, },
"node_modules/next": { "node_modules/next": {
"version": "15.5.27", "version": "15.5.28",
"resolved": "https://registry.npmjs.org/next/-/next-15.5.27.tgz", "resolved": "https://registry.npmjs.org/next/-/next-15.5.28.tgz",
"integrity": "sha512-F82CrlPZ8GRxBH9RIIoR3qU1t5RT3dv1K8moe8SjlDEquiFipvEI13R1Om90TaUOEwShkWCMlg8+gvTNzfMhvg==", "integrity": "sha512-JjhLBBY1PNnuwPtcZ7t8yxVeJVYvD2Jyp633J+gRCNbzSt7N0E+xqCf6uQiff3+3qmUkwcF/ksfSsK1xsZ76Tw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@next/env": "15.5.27", "@next/env": "15.5.28",
"@swc/helpers": "0.5.15", "@swc/helpers": "0.5.15",
"caniuse-lite": "^1.0.30001579", "caniuse-lite": "^1.0.30001579",
"postcss": "8.4.31", "postcss": "8.4.31",
@@ -3034,15 +3034,15 @@
"node": "^18.18.0 || ^19.8.0 || >= 20.0.0" "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@next/swc-darwin-arm64": "15.5.27", "@next/swc-darwin-arm64": "15.5.28",
"@next/swc-darwin-x64": "15.5.27", "@next/swc-darwin-x64": "15.5.28",
"@next/swc-linux-arm64-gnu": "15.5.27", "@next/swc-linux-arm64-gnu": "15.5.28",
"@next/swc-linux-arm64-musl": "15.5.27", "@next/swc-linux-arm64-musl": "15.5.28",
"@next/swc-linux-x64-gnu": "15.5.27", "@next/swc-linux-x64-gnu": "15.5.28",
"@next/swc-linux-x64-musl": "15.5.27", "@next/swc-linux-x64-musl": "15.5.28",
"@next/swc-win32-arm64-msvc": "15.5.27", "@next/swc-win32-arm64-msvc": "15.5.28",
"@next/swc-win32-x64-msvc": "15.5.27", "@next/swc-win32-x64-msvc": "15.5.28",
"sharp": "^0.34.3 || ^0.35.4" "sharp": "^0.34.3 || ^0.35.5"
}, },
"peerDependencies": { "peerDependencies": {
"@opentelemetry/api": "^1.1.0", "@opentelemetry/api": "^1.1.0",
+1 -1
View File
@@ -16,7 +16,7 @@
"framer-motion": "^11.18.2", "framer-motion": "^11.18.2",
"imagetracerjs": "1.2.6", "imagetracerjs": "1.2.6",
"lucide-react": "^0.474.0", "lucide-react": "^0.474.0",
"next": "15.5.27", "next": "15.5.28",
"qrcode-generator": "^2.0.4", "qrcode-generator": "^2.0.4",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",