diff --git a/.github/workflows/azure-pr-deploy.yml b/.github/workflows/azure-pr-deploy.yml new file mode 100644 index 0000000..aa44c04 --- /dev/null +++ b/.github/workflows/azure-pr-deploy.yml @@ -0,0 +1,177 @@ +name: Azure Container Apps PR Deployment + +on: + pull_request: + types: [opened, reopened, synchronize, closed] + +permissions: + id-token: write + contents: read + pull-requests: write + +env: + AZURE_CONTAINER_REGISTRY: dotinc.azurecr.io + AZURE_RESOURCE_GROUP: rg-brease-pr-${{ github.event.number }} + AZURE_LOCATION: westeurope + API_APP_NAME: brease-api-pr-${{ github.event.number }} + +jobs: + deploy: + if: github.event.action != 'closed' + runs-on: ubuntu-latest + concurrency: + group: pr-${{ github.event.number }} + cancel-in-progress: true + outputs: + api-url: ${{ steps.get-api-url.outputs.url }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Debug OIDC token + run: | + echo "GitHub context:" + echo "Event: ${{ github.event_name }}" + echo "Ref: ${{ github.ref }}" + echo "SHA: ${{ github.sha }}" + + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZ_SP_CLIENT_ID }} + tenant-id: ${{ secrets.AZ_SP_TENANT_ID }} + subscription-id: ${{ secrets.AZ_SUBSCRIPTION_ID }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Azure Container Registry + run: az acr login --name dotinc + + - name: Go Build Cache for Docker + uses: actions/cache@v4 + with: + path: go-build-cache + key: ${{ runner.os }}-go-build-cache-${{ hashFiles('**/go.sum') }} + + - name: Inject go-build-cache + uses: reproducible-containers/buildkit-cache-dance@4b2444fec0c0fb9dbf175a96c094720a692ef810 # v2.1.4 + with: + cache-source: go-build-cache + + - name: Build and push API image + uses: docker/build-push-action@v6 + with: + context: ./apps/api + push: true + file: ./apps/api/Dockerfile + tags: ${{ env.AZURE_CONTAINER_REGISTRY }}/brease-api:pr-${{ github.event.number }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Create resource group + run: | + az group create \ + --name ${{ env.AZURE_RESOURCE_GROUP }} \ + --location ${{ env.AZURE_LOCATION }} + + - name: Deploy API service + run: | + az containerapp up \ + --name ${{ env.API_APP_NAME }} \ + --resource-group ${{ env.AZURE_RESOURCE_GROUP }} \ + --location ${{ env.AZURE_LOCATION }} \ + --image ${{ env.AZURE_CONTAINER_REGISTRY }}/brease-api:pr-${{ github.event.number }} \ + --registry-server ${{ env.AZURE_CONTAINER_REGISTRY }} \ + --ingress external \ + --target-port 4400 \ + --env-vars \ + PORT=4400 \ + NODE_ENV=staging \ + INFISICAL_PROJECT_ID=642ed4939db25595ac7eb9cd \ + INFISICAL_ENVIRONMENT=staging \ + INFISICAL_CLIENT_ID=${{ secrets.INFISICAL_CLIENT_ID }} \ + INFISICAL_CLIENT_SECRET=${{ secrets.INFISICAL_CLIENT_SECRET }} \ + + - name: Get API URL + id: get-api-url + run: | + URL=$(az containerapp show \ + --name ${{ env.API_APP_NAME }} \ + --resource-group ${{ env.AZURE_RESOURCE_GROUP }} \ + --query properties.configuration.ingress.fqdn \ + --output tsv) + echo "url=https://$URL" >> $GITHUB_OUTPUT + + - name: Comment on PR + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: pr-deployment + message: | + ## ๐Ÿš€ Brease PR Environment Deployed Successfully! + + **API Service**: ${{ steps.get-api-url.outputs.url }} + **OpenAPI Docs**: ${{ steps.get-api-url.outputs.url }}/ + **Stats**: ${{ steps.get-api-url.outputs.url }}/stats + + **Resources Created:** + - Resource Group: `${{ env.AZURE_RESOURCE_GROUP }}` + - API Service: `${{ env.API_APP_NAME }}` (with managed environment) + + **API Testing:** + ```bash + # Test the API endpoint + curl ${{ steps.get-api-url.outputs.url }}/stats + + # View OpenAPI documentation + open ${{ steps.get-api-url.outputs.url }}/ + ``` + + > ๐Ÿ’ก This environment will be automatically cleaned up when the PR is merged or closed. + + cleanup: + if: github.event.action == 'closed' + runs-on: ubuntu-latest + steps: + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZ_SP_CLIENT_ID }} + tenant-id: ${{ secrets.AZ_SP_TENANT_ID }} + subscription-id: ${{ secrets.AZ_SUBSCRIPTION_ID }} + + - name: Delete resource group + run: | + if az group exists --name ${{ env.AZURE_RESOURCE_GROUP }}; then + echo "Deleting resource group: ${{ env.AZURE_RESOURCE_GROUP }}" + az group delete \ + --name ${{ env.AZURE_RESOURCE_GROUP }} \ + --yes \ + --no-wait + else + echo "Resource group ${{ env.AZURE_RESOURCE_GROUP }} does not exist" + fi + + - name: Clean up container images + run: | + # Delete PR-specific images from ACR + az acr repository delete \ + --name dotinc \ + --repository brease-api \ + --tag pr-${{ github.event.number }} \ + --yes || true + + - name: Comment on PR + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: pr-deployment + message: | + ## ๐Ÿงน Brease PR Environment Cleaned Up + + All Azure resources for this PR have been deleted: + - Resource Group: `${{ env.AZURE_RESOURCE_GROUP }}` + - Container image: `brease-api:pr-${{ github.event.number }}` + + > โœ… Cleanup completed successfully. \ No newline at end of file diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index 1302d7d..ba6e4e4 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -3,18 +3,26 @@ FROM golang:${GO_VERSION}-bookworm as builder # Update package lists and install ca-certificates WORKDIR /usr/src/app +RUN go env -w GOMODCACHE=/root/.cache/go-build COPY go.mod go.sum ./ -RUN go mod download && go mod verify +RUN --mount=type=cache,target=/root/.cache/go-build go mod download && go mod verify COPY . . -RUN go build -v -o /brease . - +RUN --mount=type=cache,target=/root/.cache/go-build go build -v -o /brease . FROM debian:bookworm -# Update package lists and install ca-certificates +# Update package lists and install ca-certificates and Infisical CLI RUN apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ apt-get update --fix-missing && apt-get install -y \ ca-certificates \ + curl \ + bash \ + && curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash \ + && apt-get update && apt-get install -y infisical \ && rm -rf /var/lib/apt/lists/* + COPY --from=builder /brease /usr/local/bin/ -CMD ["brease"] +COPY start.sh /usr/local/bin/start.sh +RUN chmod +x /usr/local/bin/start.sh + +CMD ["/usr/local/bin/start.sh"] diff --git a/apps/api/start.sh b/apps/api/start.sh new file mode 100644 index 0000000..145fed4 --- /dev/null +++ b/apps/api/start.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e +echo "Fetching secrets from Infisical..." +export INFISICAL_TOKEN=$(infisical login --method=universal-auth --client-id=$INFISICAL_CLIENT_ID --client-secret=$INFISICAL_CLIENT_SECRET --plain --silent) +infisical run --projectId=$INFISICAL_PROJECT_ID --env=staging brease \ No newline at end of file