Merge pull request 'Release/versao1' (#18) from Release/versao1 into main
All checks were successful
BCards Deployment Pipeline / Run Tests (push) Successful in 3s
BCards Deployment Pipeline / PR Validation (push) Has been skipped
BCards Deployment Pipeline / Build and Push Image (push) Successful in 8m2s
BCards Deployment Pipeline / Deploy to Release Swarm (ARM) (push) Has been skipped
BCards Deployment Pipeline / Deploy to Production (ARM - OCI) (push) Successful in 1m32s
BCards Deployment Pipeline / Cleanup Old Resources (push) Has been skipped
BCards Deployment Pipeline / Deployment Summary (push) Successful in 0s

Reviewed-on: http://git.carneiro.ddnsfree.com/ricardo/BCards/pulls/18
This commit is contained in:
ricardo 2025-09-22 18:37:52 +00:00
commit 5834afc648
5 changed files with 201 additions and 183 deletions

View File

@ -101,7 +101,7 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
platforms: linux/amd64,linux/arm64
platforms: linux/arm64
- name: Determine build settings
id: settings
@ -109,22 +109,23 @@ jobs:
BRANCH_NAME="${{ github.ref_name }}"
if [ "$BRANCH_NAME" = "main" ]; then
# Main = Produção (ARM64) - usando Dockerfile da raiz como QRRapido
# Main = Produção (ARM64) - usando Dockerfile simples
echo "tag=latest" >> $GITHUB_OUTPUT
echo "platform=linux/arm64" >> $GITHUB_OUTPUT
echo "environment=Production" >> $GITHUB_OUTPUT
echo "dockerfile=Dockerfile" >> $GITHUB_OUTPUT
echo "deploy_target=production" >> $GITHUB_OUTPUT
elif [[ "$BRANCH_NAME" == Release/* ]]; then
# Release = Staging (x86)
# Release = Swarm tests (Orange Pi arm64) - usando Dockerfile simples também
VERSION_RAW=${BRANCH_NAME#Release/}
VERSION=$(echo "$VERSION_RAW" | sed 's/^[Vv]//')
# Only remove V/v if it's at the start and followed by a number (like v1.0.0)
VERSION=$(echo "$VERSION_RAW" | sed 's/^[Vv]\([0-9]\)/\1/')
[ -z "$VERSION" ] && VERSION="0.0.1"
echo "tag=$VERSION" >> $GITHUB_OUTPUT
echo "platform=linux/amd64" >> $GITHUB_OUTPUT
echo "platform=linux/arm64" >> $GITHUB_OUTPUT
echo "environment=Testing" >> $GITHUB_OUTPUT
echo "dockerfile=Dockerfile.release" >> $GITHUB_OUTPUT
echo "dockerfile=Dockerfile" >> $GITHUB_OUTPUT
echo "deploy_target=testing" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
fi
@ -544,12 +545,16 @@ jobs:
if: startsWith(github.ref_name, 'Release/')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Extract version
id: version
run: |
BRANCH_NAME="${{ github.ref_name }}"
VERSION_RAW=${BRANCH_NAME#Release/}
VERSION=$(echo "$VERSION_RAW" | sed 's/^[Vv]//')
# Only remove V/v if it's at the start and followed by a number (like v1.0.0)
VERSION=$(echo "$VERSION_RAW" | sed 's/^[Vv]\([0-9]\)/\1/')
[ -z "$VERSION" ] && VERSION="0.0.1"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "📦 Deploying version: $VERSION"
@ -557,23 +562,42 @@ jobs:
- name: Prepare release stack manifest
run: |
mkdir -p artifacts
export BCARDS_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}
envsubst '$BCARDS_IMAGE' < deploy/docker-stack.release.yml > artifacts/docker-stack.release.yml
BCARDS_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}
# Replace ${BCARDS_IMAGE} with actual image name using sed
sed "s|\${BCARDS_IMAGE}|${BCARDS_IMAGE}|g" deploy/docker-stack.release.yml > artifacts/docker-stack.release.yml
echo "🔧 Generated manifest with image: ${BCARDS_IMAGE}"
echo "📄 Manifest content:"
head -10 artifacts/docker-stack.release.yml
- name: Deploy to release swarm
run: |
echo "🚀 Deploying release stack to Orange Pi swarm..."
docker stack deploy -c artifacts/docker-stack.release.yml bcards-release
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H 141.148.162.114 >> ~/.ssh/known_hosts
ssh-keyscan -H 129.146.116.218 >> ~/.ssh/known_hosts
- name: Await release service readiness
run: |
echo "⏳ Aguardando serviço bcards-release estabilizar..."
ATTEMPTS=30
while [ $ATTEMPTS -gt 0 ]; do
REPLICAS=$(docker service ls --filter name=bcards-release_bcards-release --format '{{.Replicas}}')
if [ "$REPLICAS" = "1/1" ]; then
echo "✅ Serviço com $REPLICAS réplica"
break
fi
echo "Atual: ${REPLICAS:-N/A}; aguardando..."
sleep 5
ATTEMPTS=$((ATTEMPTS-1))
done
scp -o StrictHostKeyChecking=no artifacts/docker-stack.release.yml ubuntu@141.148.162.114:/home/ubuntu/docker-stack.release.yml
if [ "$REPLICAS" != "1/1" ]; then
echo "❌ Serviço não atingiu 1/1 réplica"
docker service ps bcards-release_bcards-release
exit 1
fi
ssh -o StrictHostKeyChecking=no ubuntu@141.148.162.114 \
'/home/ubuntu/scripts/swarm_deploy.sh bcards-release bcards-release /home/ubuntu/docker-stack.release.yml http://localhost:28080/health 2'
docker service ps bcards-release_bcards-release
cleanup:
name: Cleanup Old Resources
@ -586,40 +610,26 @@ jobs:
run: |
echo "🧹 Limpando recursos antigos..."
# Configura SSH (igual ao QRRapido)
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
# Adiciona hosts conhecidos
ssh-keyscan -H 141.148.162.114 >> ~/.ssh/known_hosts
ssh-keyscan -H 129.146.116.218 >> ~/.ssh/known_hosts
# Testa a chave SSH
ssh-add ~/.ssh/id_rsa 2>/dev/null || echo "SSH key loaded"
# Lista de servidores baseada na branch
if [ "${{ github.ref_name }}" = "main" ]; then
SERVERS=("141.148.162.114" "129.146.116.218")
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H 141.148.162.114 >> ~/.ssh/known_hosts
ssh-keyscan -H 129.146.116.218 >> ~/.ssh/known_hosts
ssh-add ~/.ssh/id_rsa 2>/dev/null || echo "SSH key loaded"
for server in 141.148.162.114 129.146.116.218; do
echo "🧹 Limpando servidor $server..."
ssh -o StrictHostKeyChecking=no ubuntu@$server << 'EOF'
docker container prune -f
docker image prune -f
docker network prune -f
EOF
done
else
SERVERS=("141.148.162.114" "129.146.116.218")
echo " Release branch: limpeza remota ignorada (Swarm gerencia recursos)."
fi
# Limpeza em cada servidor
for server in "${SERVERS[@]}"; do
echo "🧹 Limpando servidor $server..."
ssh -o StrictHostKeyChecking=no ubuntu@$server << 'EOF'
# Remove containers parados
docker container prune -f
# Remove imagens não utilizadas
docker image prune -f
# Remove redes não utilizadas
docker network prune -f
EOF
done
echo "✅ Limpeza concluída!"
deployment-summary:

View File

@ -1,120 +1,120 @@
# Dockerfile.release - Multi-architecture build for Release environment
# Supports: linux/amd64, linux/arm64
ARG BUILDPLATFORM=linux/amd64
ARG TARGETPLATFORM
ARG VERSION=0.0.1
ARG COMMIT=unknown
# Base runtime image with multi-arch support
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 8080
EXPOSE 8443
# Install dependencies based on target platform
RUN apt-get update && \
apt-get install -y --no-install-recommends \
libgdiplus \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# Create application directories
RUN mkdir -p /app/uploads /app/logs \
&& chmod 755 /app/uploads /app/logs
# Build stage - restore and publish
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG TARGETPLATFORM
ARG VERSION
ARG COMMIT
WORKDIR /src
# Copy project file and restore dependencies
COPY ["src/BCards.Web/BCards.Web.csproj", "src/BCards.Web/"]
# Map platform to .NET runtime identifier and restore
RUN case "$TARGETPLATFORM" in \
"linux/amd64") RID="linux-x64" ;; \
"linux/arm64") RID="linux-arm64" ;; \
*) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \
esac && \
echo "🔧 Restoring for RID: $RID" && \
dotnet restore "src/BCards.Web/BCards.Web.csproj" --runtime $RID
# Copy source code
COPY . .
WORKDIR "/src/src/BCards.Web"
# Publish diretamente (build + publish em um comando)
RUN case "$TARGETPLATFORM" in \
"linux/amd64") RID="linux-x64" ;; \
"linux/arm64") RID="linux-arm64" ;; \
*) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \
esac && \
echo "📦 Publishing for RID: $RID" && \
dotnet publish "BCards.Web.csproj" \
-c Release \
-o /app/publish \
--no-restore \
--runtime $RID \
--self-contained false \
-p:PublishReadyToRun=false \
-p:PublishSingleFile=false \
-p:UseAppHost=false \
-p:Version=$VERSION \
-p:InformationalVersion=$COMMIT
# Final stage - runtime optimized for Release environment
FROM base AS final
ARG VERSION=0.0.1
ARG COMMIT=unknown
ARG TARGETPLATFORM
# Metadata labels
LABEL maintainer="BCards Team"
LABEL version=$VERSION
LABEL commit=$COMMIT
LABEL platform=$TARGETPLATFORM
LABEL environment="release"
WORKDIR /app
# Copy published application
COPY --from=build /app/publish .
# Create non-root user for security
RUN groupadd -r bcards && useradd -r -g bcards bcards \
&& chown -R bcards:bcards /app
# Environment variables for Release
ENV ASPNETCORE_ENVIRONMENT=Release
ENV ASPNETCORE_URLS=http://+:8080
ENV DOTNET_RUNNING_IN_CONTAINER=true
ENV DOTNET_EnableDiagnostics=0
ENV DOTNET_USE_POLLING_FILE_WATCHER=true
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
# Platform-specific optimizations
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
echo "🔧 Applying ARM64 optimizations..." && \
echo 'export DOTNET_TieredPGO=1' >> /etc/environment && \
echo 'export DOTNET_TC_QuickJitForLoops=1' >> /etc/environment && \
echo 'export DOTNET_ReadyToRun=0' >> /etc/environment; \
else \
echo "🔧 Applying AMD64 optimizations..." && \
echo 'export DOTNET_TieredPGO=1' >> /etc/environment && \
echo 'export DOTNET_ReadyToRun=1' >> /etc/environment; \
fi
# Health check endpoint
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# Switch to non-root user
USER bcards
# Entry point with optimized runtime settings
ENTRYPOINT ["dotnet", "BCards.Web.dll"]
# Dockerfile.release - Multi-architecture build for Release environment
# Supports: linux/amd64, linux/arm64
ARG BUILDPLATFORM=linux/amd64
ARG TARGETPLATFORM
ARG VERSION=0.0.1
ARG COMMIT=unknown
# Base runtime image with multi-arch support
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 8080
EXPOSE 8443
# Install dependencies based on target platform
RUN apt-get update && \
apt-get install -y --no-install-recommends \
libgdiplus \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# Create application directories
RUN mkdir -p /app/uploads /app/logs \
&& chmod 755 /app/uploads /app/logs
# Build stage - restore and publish
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG TARGETPLATFORM
ARG VERSION
ARG COMMIT
WORKDIR /src
# Copy project file and restore dependencies
COPY ["src/BCards.Web/BCards.Web.csproj", "src/BCards.Web/"]
# Map platform to .NET runtime identifier and restore
RUN case "$TARGETPLATFORM" in \
"linux/amd64") RID="linux-x64" ;; \
"linux/arm64") RID="linux-arm64" ;; \
*) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \
esac && \
echo "🔧 Restoring for RID: $RID" && \
dotnet restore "src/BCards.Web/BCards.Web.csproj" --runtime $RID
# Copy source code
COPY . .
WORKDIR "/src/src/BCards.Web"
# Publish diretamente (build + publish em um comando)
RUN case "$TARGETPLATFORM" in \
"linux/amd64") RID="linux-x64" ;; \
"linux/arm64") RID="linux-arm64" ;; \
*) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \
esac && \
echo "📦 Publishing for RID: $RID" && \
dotnet publish "BCards.Web.csproj" \
-c Release \
-o /app/publish \
--no-restore \
--runtime $RID \
--self-contained false \
-p:PublishReadyToRun=false \
-p:PublishSingleFile=false \
-p:UseAppHost=false \
-p:Version=$VERSION \
-p:InformationalVersion=$COMMIT
# Final stage - runtime optimized for Release environment
FROM base AS final
ARG VERSION=0.0.1
ARG COMMIT=unknown
ARG TARGETPLATFORM
# Metadata labels
LABEL maintainer="BCards Team"
LABEL version=$VERSION
LABEL commit=$COMMIT
LABEL platform=$TARGETPLATFORM
LABEL environment="release"
WORKDIR /app
# Copy published application
COPY --from=build /app/publish .
# Create non-root user for security
RUN groupadd -r bcards && useradd -r -g bcards bcards \
&& chown -R bcards:bcards /app
# Environment variables for Release
ENV ASPNETCORE_ENVIRONMENT=Release
ENV ASPNETCORE_URLS=http://+:8080
ENV DOTNET_RUNNING_IN_CONTAINER=true
ENV DOTNET_EnableDiagnostics=0
ENV DOTNET_USE_POLLING_FILE_WATCHER=true
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
# Platform-specific optimizations
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
echo "🔧 Applying ARM64 optimizations..." && \
echo 'export DOTNET_TieredPGO=1' >> /etc/environment && \
echo 'export DOTNET_TC_QuickJitForLoops=1' >> /etc/environment && \
echo 'export DOTNET_ReadyToRun=0' >> /etc/environment; \
else \
echo "🔧 Applying AMD64 optimizations..." && \
echo 'export DOTNET_TieredPGO=1' >> /etc/environment && \
echo 'export DOTNET_ReadyToRun=1' >> /etc/environment; \
fi
# Health check endpoint
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# Switch to non-root user
USER bcards
# Entry point with optimized runtime settings
ENTRYPOINT ["dotnet", "BCards.Web.dll"]

View File

@ -6,9 +6,7 @@ services:
networks:
- bcards-net
deploy:
replicas: 2
placement:
max_replicas_per_node: 1
replicas: 1
update_config:
parallelism: 1
delay: 10s
@ -22,13 +20,20 @@ services:
ASPNETCORE_ENVIRONMENT: Release
ASPNETCORE_URLS: http://+:8080
ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true"
# MongoDB local (Core i5)
MongoDb__ConnectionString: mongodb://192.168.0.100:27017/BCardsDB
MongoDb__DatabaseName: BCardsDB
DataProtection__Mongo__ConnectionString: mongodb://192.168.0.100:27017/BCardsDB
DataProtection__Mongo__DatabaseName: BCardsDB
DataProtection__Mongo__CollectionName: DataProtectionKeys
Serilog__OpenSearchUrl: http://141.148.162.114:19201
Serilog__OpenSearchFallback: http://129.146.116.218:19202
# OpenSearch local (Core i5)
Serilog__OpenSearchUrl: http://192.168.0.100:9200
Serilog__OpenSearchFallback: http://192.168.0.100:9200
# Stripe test keys (same as development)
Stripe__PublishableKey: pk_test_51RjUmIBMIadsOxJVP4bWc54pHEOSf5km1hpOkOBSoGVoKxI46N4KSWtevpXCSq68OjFazBuXmPJGBwZ1KDN5MNJy003lj1YmAS
Stripe__SecretKey: sk_test_51RjUmIBMIadsOxJVeqsMFxnZ8ePR7d8IbnaF4sAwBVJv9rrfODPEQ2C9fF3beoABpITdfzEk0ZDzGTTQfvKv63xI00PeZoABGO
Stripe__WebhookSecret: whsec_8d189c137ff170ab5e62498003512b9d073e2db50c50ed7d8712b7ef11a37543
Stripe__Environment: test
Logging__LogLevel__Default: Debug
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]

View File

@ -193,11 +193,11 @@ deploy_new_version() {
cp "$PROJECT_ROOT/$DOCKER_COMPOSE_FILE" "$DEPLOY_DIR/"
# Create/update environment file
cat > "$DEPLOY_DIR/.env" << EOF
IMAGE_TAG=$image_tag
REGISTRY=registry.redecarneir.us
MONGODB_CONNECTION_STRING=mongodb://192.168.0.100:27017/BCardsDB
ASPNETCORE_ENVIRONMENT=Release
cat > "$DEPLOY_DIR/.env" << EOF
IMAGE_TAG=$image_tag
REGISTRY=registry.redecarneir.us
MONGODB_CONNECTION_STRING=mongodb://admin:c4rn31r0@129.146.116.218:27017,141.148.162.114:27017/BCardsDB?replicaSet=rs0&authSource=admin
ASPNETCORE_ENVIRONMENT=Release
CERT_PASSWORD=
EOF
@ -366,4 +366,4 @@ main() {
}
# Run main function with all arguments
main "$@"
main "$@"

View File

@ -53,10 +53,13 @@ namespace BCards.Web.Middleware
// Só adicionar se não foi definido explicitamente pelo controller
if (!context.Response.Headers.ContainsKey("Cache-Control"))
{
context.Response.Headers["Cache-Control"] = "no-cache, must-revalidate";
context.Response.Headers["Vary"] = "Cookie";
_logger.LogDebug("AuthCache: Applied no-cache for authenticated user on {Path}", path);
// Headers mais fortes para garantir que CDNs como Cloudflare não façam cache
context.Response.Headers["Cache-Control"] = "no-store, no-cache, must-revalidate, proxy-revalidate";
context.Response.Headers["Pragma"] = "no-cache";
context.Response.Headers["Expires"] = "0";
context.Response.Headers["Vary"] = "Cookie, Authorization";
_logger.LogDebug("AuthCache: Applied strong no-cache headers for authenticated user on {Path}", path);
}
}
else