test #83
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "Build and Deploy" | |
| on: | |
| workflow_dispatch: | |
| push: | |
| branches: | |
| - main | |
| env: | |
| PROJECT_NAME: ${{ vars.PROJECT_NAME }} | |
| GCP_PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} | |
| REGION: ${{ vars.REGION }} | |
| ZONE: ${{ vars.ZONE }} | |
| IMAGE_TAG: ${{ github.sha }} | |
| NODE_VERSION: ${{ vars.NODE_VERSION }} | |
| IMAGE_REPOSITORY: ifsp-extensao-api-module-prod | |
| jobs: | |
| build-deploy: | |
| name: "Build and Deploy" | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: GCP Auth | |
| uses: google-github-actions/auth@v2 | |
| with: | |
| credentials_json: ${{ secrets.GCP_CREDENTIALS }} | |
| - name: Setup gcloud | |
| uses: google-github-actions/setup-gcloud@v2 | |
| - name: Setup Terraform | |
| uses: hashicorp/setup-terraform@v2 | |
| with: | |
| terraform_wrapper: false | |
| - name: Terraform Init | |
| working-directory: ./tf | |
| run: terraform init | |
| env: | |
| GOOGLE_CREDENTIALS: ${{ secrets.GCP_CREDENTIALS }} | |
| - name: Terraform Apply | |
| working-directory: ./tf | |
| run: | | |
| terraform apply -auto-approve | |
| echo "VM_HOST=$(terraform output -raw vm_public_ip)" >> $GITHUB_ENV | |
| echo "REGISTRY_URL=$(terraform output -raw artifact_registry_url)" >> $GITHUB_ENV | |
| env: | |
| GOOGLE_CREDENTIALS: ${{ secrets.GCP_CREDENTIALS }} | |
| TF_VAR_ssh_public_key: ${{ secrets.VM_SSH_PUBLIC_KEY }} | |
| TF_VAR_project_name: ${{ env.PROJECT_NAME }} | |
| TF_VAR_project_id: ${{ env.GCP_PROJECT_ID }} | |
| TF_VAR_env: "prod" | |
| - name: Load .env from GitHub Secrets | |
| run: echo "${{ secrets.APP_ENV_FILE }}" > .env | |
| - name: Configure Docker for Artifact Registry | |
| run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev --quiet | |
| - name: Build, tag, and push image | |
| run: | | |
| docker build -t $REGISTRY_URL/$IMAGE_REPOSITORY:$IMAGE_TAG \ | |
| -f Dockerfile \ | |
| --build-arg NODE_VERSION="${{ env.NODE_VERSION }}" \ | |
| --no-cache=true . | |
| docker push $REGISTRY_URL/$IMAGE_REPOSITORY:$IMAGE_TAG | |
| - name: Copy .env to VM | |
| uses: appleboy/scp-action@master | |
| with: | |
| host: ${{ env.VM_HOST }} | |
| username: gcpuser | |
| key: ${{ secrets.VM_SSH_KEY }} | |
| source: ".env" | |
| target: "/home/gcpuser/app" | |
| - name: Prepare OTel directory on VM | |
| uses: appleboy/ssh-action@master | |
| with: | |
| host: ${{ env.VM_HOST }} | |
| username: gcpuser | |
| key: ${{ secrets.VM_SSH_KEY }} | |
| script: | | |
| sudo mkdir -p /opt/otel | |
| sudo chown gcpuser:gcpuser /opt/otel | |
| sudo chmod 755 /opt/otel | |
| - name: Copy OTel config to VM | |
| uses: appleboy/scp-action@master | |
| with: | |
| host: ${{ env.VM_HOST }} | |
| username: gcpuser | |
| key: ${{ secrets.VM_SSH_KEY }} | |
| source: "devops/otel/otel-compose.yaml,devops/otel/collector-config.yaml" | |
| target: "/opt/otel" | |
| strip_components: 2 | |
| - name: Deploy via SSH | |
| uses: appleboy/ssh-action@master | |
| env: | |
| REGISTRY_URL: ${{ env.REGISTRY_URL }} | |
| IMAGE_REPOSITORY: ifsp-extensao-api-module-prod | |
| IMAGE_TAG: ${{ github.sha }} | |
| CONTAINER_NAME: api-ifsp | |
| REGION: ${{ env.REGION }} | |
| GRAFANA_CLOUD_OTLP_ENDPOINT: ${{ secrets.GRAFANA_CLOUD_OTLP_ENDPOINT }} | |
| GRAFANA_CLOUD_AUTH_HEADER: ${{ secrets.GRAFANA_CLOUD_AUTH_HEADER }} | |
| with: | |
| host: ${{ env.VM_HOST }} | |
| username: gcpuser | |
| key: ${{ secrets.VM_SSH_KEY }} | |
| envs: REGISTRY_URL,IMAGE_REPOSITORY,IMAGE_TAG,CONTAINER_NAME,REGION,GRAFANA_CLOUD_OTLP_ENDPOINT,GRAFANA_CLOUD_AUTH_HEADER | |
| command_timeout: 15m | |
| script: | | |
| set -e | |
| echo "Waiting for Docker daemon..." | |
| for i in $(seq 1 30); do | |
| sudo docker info >/dev/null 2>&1 && break | |
| sleep 3 | |
| if [ "$i" -eq 30 ]; then | |
| echo "Docker never came up on this VM after 90s" | |
| exit 1 | |
| fi | |
| done | |
| TOKEN=$(curl -sf "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" \ | |
| -H "Metadata-Flavor: Google" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])") | |
| echo $TOKEN | sudo docker login -u oauth2accesstoken --password-stdin https://$REGION-docker.pkg.dev | |
| sudo docker network inspect observability >/dev/null 2>&1 || sudo docker network create observability | |
| cat > /opt/otel/.env <<EOF | |
| GRAFANA_CLOUD_OTLP_ENDPOINT=$GRAFANA_CLOUD_OTLP_ENDPOINT | |
| GRAFANA_CLOUD_AUTH_HEADER=$GRAFANA_CLOUD_AUTH_HEADER | |
| EOF | |
| cd /opt/otel | |
| sudo docker compose --env-file .env -f otel-compose.yaml up -d | |
| cd - | |
| sudo docker pull $REGISTRY_URL/$IMAGE_REPOSITORY:$IMAGE_TAG | |
| sudo docker stop $CONTAINER_NAME 2>/dev/null || true | |
| sudo docker rm $CONTAINER_NAME 2>/dev/null || true | |
| sudo docker run -d \ | |
| --name $CONTAINER_NAME \ | |
| --restart unless-stopped \ | |
| --network observability \ | |
| --env-file /home/gcpuser/app/.env \ | |
| -p 80:8000 \ | |
| $REGISTRY_URL/$IMAGE_REPOSITORY:$IMAGE_TAG | |
| sudo docker system prune -f |