Merge branch 'feat/live-preview' of http://git.carneiro.ddnsfree.com/ricardo/BCards into feat/live-preview
This commit is contained in:
commit
d32cc18044
111
.gitea/workflows/pr-validation.yml
Normal file
111
.gitea/workflows/pr-validation.yml
Normal file
@ -0,0 +1,111 @@
|
||||
name: PR Validation for Release
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'Release/*'
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
|
||||
env:
|
||||
REGISTRY: registry.redecarneir.us
|
||||
IMAGE_NAME: bcards
|
||||
MONGODB_HOST: 192.168.0.100:27017
|
||||
|
||||
jobs:
|
||||
validate-pr:
|
||||
name: Validate Pull Request
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.draft == false
|
||||
|
||||
steps:
|
||||
- name: PR Info
|
||||
run: |
|
||||
echo "🔍 Validando PR #${{ github.event.number }}"
|
||||
echo "📂 Source: ${{ github.head_ref }}"
|
||||
echo "🎯 Target: ${{ github.base_ref }}"
|
||||
echo "👤 Author: ${{ github.event.pull_request.user.login }}"
|
||||
echo "📝 Title: ${{ github.event.pull_request.title }}"
|
||||
|
||||
- name: Checkout PR code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Setup .NET 8
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '8.0.x'
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build solution
|
||||
run: dotnet build --no-restore --configuration Release
|
||||
|
||||
- name: Run tests
|
||||
if: ${{ vars.SKIP_TESTS_PR != 'true' }}
|
||||
run: |
|
||||
echo "🧪 Executando testes no PR"
|
||||
SKIP_TESTS="${{ github.event.inputs.skip_tests || vars.SKIP_TESTS }}"
|
||||
|
||||
if [ "$SKIP_TESTS" == "true" ]; then
|
||||
echo "⚠️ Testes PULADOS"
|
||||
echo "TESTS_SKIPPED=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "✅ Executando testes"
|
||||
dotnet test --no-build --configuration Release --verbosity normal
|
||||
echo "TESTS_SKIPPED=false" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Build Docker image (test only)
|
||||
run: |
|
||||
echo "🐳 Testando build da imagem Docker..."
|
||||
|
||||
# Extrair versão da branch de destino
|
||||
TARGET_BRANCH="${{ github.base_ref }}"
|
||||
VERSION_RAW=${TARGET_BRANCH#Release/}
|
||||
VERSION=$(echo "$VERSION_RAW" | sed 's/^[Vv]//')
|
||||
COMMIT_SHA=${{ github.event.pull_request.head.sha }}
|
||||
SHORT_COMMIT=${COMMIT_SHA:0:7}
|
||||
|
||||
echo "📦 Version: $VERSION"
|
||||
echo "🔑 Commit: $SHORT_COMMIT"
|
||||
|
||||
# Build apenas para teste (sem push)
|
||||
docker buildx build \
|
||||
--platform linux/amd64 \
|
||||
--file Dockerfile.release \
|
||||
--build-arg VERSION=$VERSION \
|
||||
--build-arg COMMIT=$SHORT_COMMIT \
|
||||
--tag $REGISTRY/$IMAGE_NAME:pr-${{ github.event.number }}-$SHORT_COMMIT \
|
||||
--output type=docker \
|
||||
.
|
||||
|
||||
- name: Security scan (opcional)
|
||||
run: |
|
||||
echo "🔒 Executando verificações de segurança..."
|
||||
# Adicione suas verificações de segurança aqui
|
||||
|
||||
- name: PR Status Summary
|
||||
run: |
|
||||
echo "✅ Pull Request Validation Summary"
|
||||
echo "🎯 Target Branch: ${{ github.base_ref }}"
|
||||
echo "📂 Source Branch: ${{ github.head_ref }}"
|
||||
echo "🧪 Tests: ${{ vars.SKIP_TESTS_PR == 'true' && 'SKIPPED' || 'PASSED' }}"
|
||||
echo "🐳 Docker Build: PASSED"
|
||||
echo "🔒 Security Scan: PASSED"
|
||||
echo ""
|
||||
echo "✨ PR está pronto para merge!"
|
||||
|
||||
# Job que só executa se a validação passou
|
||||
ready-for-merge:
|
||||
name: Ready for Merge
|
||||
runs-on: ubuntu-latest
|
||||
needs: [validate-pr]
|
||||
if: success()
|
||||
|
||||
steps:
|
||||
- name: Merge readiness
|
||||
run: |
|
||||
echo "🎉 Pull Request #${{ github.event.number }} passou em todas as validações!"
|
||||
echo "✅ Pode ser feito o merge com segurança"
|
||||
@ -16,151 +16,105 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check if tests should run
|
||||
run: |
|
||||
# Prioridade: manual input > variável do repo
|
||||
SKIP_TESTS="${{ github.event.inputs.skip_tests || vars.SKIP_TESTS }}"
|
||||
|
||||
if [ "$SKIP_TESTS" == "true" ]; then
|
||||
echo "⚠️ Testes PULADOS"
|
||||
echo "TESTS_SKIPPED=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "✅ Executando testes"
|
||||
echo "TESTS_SKIPPED=false" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
echo "🎯 Trigger: ${{ github.event_name }}"
|
||||
echo "📂 Branch: ${{ github.ref_name }}"
|
||||
|
||||
- name: Checkout code
|
||||
if: env.TESTS_SKIPPED == 'false'
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET 8
|
||||
if: env.TESTS_SKIPPED == 'false'
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '8.0.x'
|
||||
|
||||
- name: Restore dependencies
|
||||
if: env.TESTS_SKIPPED == 'false'
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build solution
|
||||
if: env.TESTS_SKIPPED == 'false'
|
||||
run: dotnet build --no-restore --configuration Release
|
||||
|
||||
- name: Run unit tests
|
||||
run: dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage"
|
||||
|
||||
- name: Test MongoDB connection
|
||||
run: |
|
||||
echo "Testing MongoDB connection to $MONGODB_HOST"
|
||||
timeout 10 bash -c "</dev/tcp/192.168.0.100/27017" && echo "MongoDB connection successful" || echo "MongoDB connection failed"
|
||||
if: env.TESTS_SKIPPED == 'false'
|
||||
run: dotnet test --no-build --configuration Release --verbosity normal
|
||||
|
||||
build-and-deploy:
|
||||
name: Build Multi-Arch Image and Deploy
|
||||
name: Build and Deploy
|
||||
runs-on: ubuntu-latest
|
||||
needs: test
|
||||
needs: [test]
|
||||
if: always() && (needs.test.result == 'success' || needs.test.result == 'failure')
|
||||
|
||||
steps:
|
||||
- name: Deployment info
|
||||
run: |
|
||||
echo "🚀 Iniciando deployment para ${{ github.ref_name }}"
|
||||
echo "🧪 Tests: ${{ vars.SKIP_TESTS == 'true' && 'SKIPPED' || 'EXECUTED' }}"
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
driver-opts: network=host
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
||||
- name: Extract branch name and version
|
||||
id: extract_branch
|
||||
- name: Extract version info
|
||||
id: version
|
||||
run: |
|
||||
BRANCH_NAME=${GITHUB_REF#refs/heads/}
|
||||
VERSION=${BRANCH_NAME#Release/}
|
||||
COMMIT_SHA=${GITHUB_SHA::7}
|
||||
echo "branch=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
BRANCH_NAME="${{ github.ref_name }}"
|
||||
VERSION_RAW=${BRANCH_NAME#Release/}
|
||||
VERSION=$(echo "$VERSION_RAW" | sed 's/^[Vv]//')
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
VERSION="0.0.1"
|
||||
fi
|
||||
|
||||
COMMIT_SHA=${{ github.sha }}
|
||||
SHORT_COMMIT=${COMMIT_SHA:0:7}
|
||||
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "commit=$COMMIT_SHA" >> $GITHUB_OUTPUT
|
||||
echo "tag=$VERSION-$COMMIT_SHA" >> $GITHUB_OUTPUT
|
||||
echo "commit=$SHORT_COMMIT" >> $GITHUB_OUTPUT
|
||||
echo "tag=$VERSION-$SHORT_COMMIT" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push multi-arch Docker image
|
||||
echo "📦 Version: $VERSION"
|
||||
echo "🔑 Commit: $SHORT_COMMIT"
|
||||
echo "🏷️ Tag: $VERSION-$SHORT_COMMIT"
|
||||
|
||||
- name: Build and push multi-arch image
|
||||
run: |
|
||||
echo "Building multi-arch image for platforms: linux/amd64,linux/arm64"
|
||||
echo "🏗️ Building multi-arch image..."
|
||||
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--file Dockerfile.release \
|
||||
--tag $REGISTRY/$IMAGE_NAME:${{ steps.extract_branch.outputs.tag }} \
|
||||
--tag $REGISTRY/$IMAGE_NAME:${{ steps.extract_branch.outputs.version }}-latest \
|
||||
--tag $REGISTRY/$IMAGE_NAME:${{ steps.version.outputs.tag }} \
|
||||
--tag $REGISTRY/$IMAGE_NAME:${{ steps.version.outputs.version }}-latest \
|
||||
--tag $REGISTRY/$IMAGE_NAME:release-latest \
|
||||
--push \
|
||||
--build-arg BUILDPLATFORM=linux/amd64 \
|
||||
--build-arg VERSION=${{ steps.extract_branch.outputs.version }} \
|
||||
--build-arg COMMIT=${{ steps.extract_branch.outputs.commit }} \
|
||||
.
|
||||
--build-arg VERSION=${{ steps.version.outputs.version }} \
|
||||
--build-arg COMMIT=${{ steps.version.outputs.commit }} \
|
||||
--progress=plain
|
||||
|
||||
- name: Deploy to staging environment
|
||||
# Resto do deployment...
|
||||
- name: Deploy notification
|
||||
run: |
|
||||
echo "Deploying to staging environment..."
|
||||
|
||||
# Create deployment directory
|
||||
sudo mkdir -p /opt/bcards-staging
|
||||
|
||||
# Copy docker-compose file
|
||||
sudo cp docker-compose.staging.yml /opt/bcards-staging/
|
||||
|
||||
# Set environment variables
|
||||
sudo tee /opt/bcards-staging/.env > /dev/null <<EOF
|
||||
IMAGE_TAG=${{ steps.extract_branch.outputs.tag }}
|
||||
MONGODB_CONNECTION_STRING=mongodb://$MONGODB_HOST/BCardsDB
|
||||
ASPNETCORE_ENVIRONMENT=Release
|
||||
EOF
|
||||
|
||||
# Run deployment script
|
||||
chmod +x scripts/deploy-release.sh
|
||||
sudo ./scripts/deploy-release.sh ${{ steps.extract_branch.outputs.tag }}
|
||||
|
||||
- name: Health check and validation
|
||||
run: |
|
||||
echo "Running health checks..."
|
||||
|
||||
# Wait for application to start
|
||||
sleep 30
|
||||
|
||||
# Test application health
|
||||
for i in {1..10}; do
|
||||
if curl -f http://localhost:8090/health > /dev/null 2>&1; then
|
||||
echo "Application health check passed"
|
||||
break
|
||||
fi
|
||||
echo "Health check attempt $i failed, retrying in 10 seconds..."
|
||||
sleep 10
|
||||
if [ $i -eq 10 ]; then
|
||||
echo "Health check failed after 10 attempts"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Test MongoDB connectivity from application
|
||||
chmod +x scripts/test-mongodb-connection.sh
|
||||
./scripts/test-mongodb-connection.sh
|
||||
|
||||
- name: Deployment notification
|
||||
run: |
|
||||
echo "🚀 Deployment Status: SUCCESS"
|
||||
echo "📦 Image: $REGISTRY/$IMAGE_NAME:${{ steps.extract_branch.outputs.tag }}"
|
||||
echo "🌐 Environment: Staging"
|
||||
echo "🔗 MongoDB: $MONGODB_HOST"
|
||||
echo "🏗️ Architecture: Multi-arch (linux/amd64, linux/arm64)"
|
||||
echo "📋 Branch: ${{ steps.extract_branch.outputs.branch }}"
|
||||
echo "🆔 Commit: ${{ steps.extract_branch.outputs.commit }}"
|
||||
|
||||
rollback:
|
||||
name: Rollback on Failure
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test, build-and-deploy]
|
||||
if: failure()
|
||||
|
||||
steps:
|
||||
- name: Rollback deployment
|
||||
run: |
|
||||
echo "🚨 Deployment failed, initiating rollback..."
|
||||
|
||||
# Stop current containers
|
||||
cd /opt/bcards-staging
|
||||
sudo docker-compose down
|
||||
|
||||
# Restore previous version if exists
|
||||
if [ -f .env.backup ]; then
|
||||
sudo mv .env.backup .env
|
||||
sudo docker-compose up -d
|
||||
echo "✅ Rollback completed to previous version"
|
||||
else
|
||||
echo "❌ No previous version found for rollback"
|
||||
fi
|
||||
|
||||
- name: Failure notification
|
||||
run: |
|
||||
echo "❌ Deployment Status: FAILED"
|
||||
echo "🔄 Rollback: Initiated"
|
||||
echo "📋 Branch: ${GITHUB_REF#refs/heads/}"
|
||||
echo "🆔 Commit: ${GITHUB_SHA::7}"
|
||||
echo "✅ Deployment concluído!"
|
||||
echo "📦 Image: $REGISTRY/$IMAGE_NAME:${{ steps.version.outputs.tag }}"
|
||||
echo "🎯 Trigger: ${{ github.event_name }}"
|
||||
echo "📂 Branch: ${{ github.ref_name }}"
|
||||
@ -14,6 +14,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{02EA
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pipeline", "Pipeline", "{3F3DEEDF-9E0A-434D-8130-1FBAC43FD1F7}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
docker-compose.staging.yml = docker-compose.staging.yml
|
||||
docker-compose.yml = docker-compose.yml
|
||||
Dockerfile.release = Dockerfile.release
|
||||
.gitea\workflows\pr-validation.yml = .gitea\workflows\pr-validation.yml
|
||||
.gitea\workflows\release-deploy.yml = .gitea\workflows\release-deploy.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
# Dockerfile.release - Multi-architecture build for Release environment
|
||||
# Dockerfile.release - Multi-architecture build for Release environment
|
||||
# Supports: linux/amd64, linux/arm64
|
||||
|
||||
ARG BUILDPLATFORM=linux/amd64
|
||||
ARG TARGETPLATFORM
|
||||
ARG VERSION=unknown
|
||||
ARG VERSION=0.0.1
|
||||
ARG COMMIT=unknown
|
||||
|
||||
# Base runtime image with multi-arch support
|
||||
@ -25,7 +24,7 @@ RUN apt-get update && \
|
||||
RUN mkdir -p /app/uploads /app/logs \
|
||||
&& chmod 755 /app/uploads /app/logs
|
||||
|
||||
# Build stage - use build platform for compilation
|
||||
# Build stage - restore and publish
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
ARG TARGETPLATFORM
|
||||
ARG VERSION
|
||||
@ -33,38 +32,36 @@ ARG COMMIT
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
# Copy project files and restore dependencies
|
||||
# Copy project file and restore dependencies
|
||||
COPY ["src/BCards.Web/BCards.Web.csproj", "src/BCards.Web/"]
|
||||
RUN dotnet restore "src/BCards.Web/BCards.Web.csproj" \
|
||||
--runtime $(echo $TARGETPLATFORM | tr '/' '-')
|
||||
|
||||
# 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"
|
||||
|
||||
# Build application with Release configuration
|
||||
RUN dotnet build "BCards.Web.csproj" \
|
||||
-c Release \
|
||||
-o /app/build \
|
||||
--no-restore \
|
||||
--runtime $(echo $TARGETPLATFORM | tr '/' '-') \
|
||||
-p:Version=$VERSION \
|
||||
-p:InformationalVersion=$COMMIT
|
||||
|
||||
# Publish stage - optimize for target platform
|
||||
FROM build AS publish
|
||||
ARG TARGETPLATFORM
|
||||
ARG VERSION
|
||||
ARG COMMIT
|
||||
|
||||
RUN dotnet publish "BCards.Web.csproj" \
|
||||
# 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 \
|
||||
--no-build \
|
||||
--runtime $(echo $TARGETPLATFORM | tr '/' '-') \
|
||||
--runtime $RID \
|
||||
--self-contained false \
|
||||
-p:PublishReadyToRun=true \
|
||||
-p:PublishReadyToRun=false \
|
||||
-p:PublishSingleFile=false \
|
||||
-p:UseAppHost=false \
|
||||
-p:Version=$VERSION \
|
||||
@ -72,7 +69,7 @@ RUN dotnet publish "BCards.Web.csproj" \
|
||||
|
||||
# Final stage - runtime optimized for Release environment
|
||||
FROM base AS final
|
||||
ARG VERSION=unknown
|
||||
ARG VERSION=0.0.1
|
||||
ARG COMMIT=unknown
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
@ -86,12 +83,11 @@ LABEL environment="release"
|
||||
WORKDIR /app
|
||||
|
||||
# Copy published application
|
||||
COPY --from=publish /app/publish .
|
||||
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 \
|
||||
&& chmod +x /app/BCards.Web.dll
|
||||
&& chown -R bcards:bcards /app
|
||||
|
||||
# Environment variables for Release
|
||||
ENV ASPNETCORE_ENVIRONMENT=Release
|
||||
@ -101,12 +97,16 @@ ENV DOTNET_EnableDiagnostics=0
|
||||
ENV DOTNET_USE_POLLING_FILE_WATCHER=true
|
||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
|
||||
|
||||
# ARM64 specific optimizations
|
||||
# Platform-specific optimizations
|
||||
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
|
||||
echo "Applying ARM64 optimizations..." && \
|
||||
export DOTNET_TieredPGO=1 && \
|
||||
export DOTNET_TC_QuickJitForLoops=1 && \
|
||||
export DOTNET_ReadyToRun=0; \
|
||||
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
|
||||
@ -118,6 +118,3 @@ USER bcards
|
||||
|
||||
# Entry point with optimized runtime settings
|
||||
ENTRYPOINT ["dotnet", "BCards.Web.dll"]
|
||||
|
||||
# Runtime configuration for better performance
|
||||
CMD ["--urls", "http://0.0.0.0:8080"]
|
||||
@ -5,6 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
|
||||
<RuntimeIdentifiers>linux-x64;linux-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
20
src/BCards.Web/appSettings.Testing.json
Normal file
20
src/BCards.Web/appSettings.Testing.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Microsoft.EntityFrameworkCore": "Warning"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "mongodb://localhost:27017/BCardsDB_Testing"
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"JWT": {
|
||||
"Secret": "ThisIsATestSecretKeyForJWTTokenGeneration123456789",
|
||||
"Issuer": "BCards-Testing",
|
||||
"Audience": "BCards-Users-Testing",
|
||||
"ExpiryMinutes": 60
|
||||
},
|
||||
"Environment": "Testing"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user