From eaf0388c25f983a500b3d2d7dbbfc3ee309d602e Mon Sep 17 00:00:00 2001 From: Vibhav Bobade Date: Mon, 16 Jan 2023 21:33:31 +0530 Subject: [PATCH] create preview environments for every PR using Uffizzi (#1106) * Integrate Uffizzi * remove lsp build Co-authored-by: Ruben Fiszel --- .github/uffizzi/caddy/Caddyfile | 5 ++ .github/uffizzi/docker-compose.uffizzi.yml | 49 ++++++++++++ .github/workflows/uffizzi-build.yml | 88 ++++++++++++++++++++++ .github/workflows/uffizzi-preview.yml | 83 ++++++++++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 .github/uffizzi/caddy/Caddyfile create mode 100644 .github/uffizzi/docker-compose.uffizzi.yml create mode 100644 .github/workflows/uffizzi-build.yml create mode 100644 .github/workflows/uffizzi-preview.yml diff --git a/.github/uffizzi/caddy/Caddyfile b/.github/uffizzi/caddy/Caddyfile new file mode 100644 index 0000000000..b44d41e881 --- /dev/null +++ b/.github/uffizzi/caddy/Caddyfile @@ -0,0 +1,5 @@ +localhost { + bind 0.0.0.0 + reverse_proxy /ws/* http://0.0.0.0:3001 + reverse_proxy /* http://0.0.0.0:8000 +} diff --git a/.github/uffizzi/docker-compose.uffizzi.yml b/.github/uffizzi/docker-compose.uffizzi.yml new file mode 100644 index 0000000000..9014348433 --- /dev/null +++ b/.github/uffizzi/docker-compose.uffizzi.yml @@ -0,0 +1,49 @@ +version: '3.7' + +x-uffizzi: + ingress: + service: windmill + port: 8000 + +services: + db: + image: postgres:14 + environment: + POSTGRES_PASSWORD: changeme + POSTGRES_DB: windmill + + windmill: + image: '${WINDMILL_IMAGE}' + privileged: false + restart: unless-stopped + ports: + - 8000:8000 + environment: + - DATABASE_URL=postgres://postgres:changeme@localhost/windmill?sslmode=disable + - BASE_URL=http://localhost + - BASE_INTERNAL_URL=http://localhost:8000 + - RUST_LOG=info + - NUM_WORKERS=3 + - KEEP_JOB_DIR=false + - DENO_PATH=/usr/bin/deno + - PYTHON_PATH=/usr/local/bin/python3 + - METRICS_ADDR=false + volumes: + - worker_dependency_cache:/tmp/windmill/cache + + lsp: + image: '${LSP_IMAGE}' + restart: unless-stopped + ports: + - 3001:3001 + + # caddy: + # image: caddy:2.5.2-alpine + # restart: unless-stopped + # volumes: + # - ./.github/uffizzi/caddy:/etc/caddy + # environment: + # - BASE_URL=localhost + +volumes: + worker_dependency_cache: diff --git a/.github/workflows/uffizzi-build.yml b/.github/workflows/uffizzi-build.yml new file mode 100644 index 0000000000..47330e8c46 --- /dev/null +++ b/.github/workflows/uffizzi-build.yml @@ -0,0 +1,88 @@ +name: Build PR Image +on: + pull_request: + types: [opened,synchronize,reopened,closed] + +jobs: + build-windmill: + name: Build and Push `windmill` + runs-on: ubuntu-latest + if: ${{ (github.event_name != 'pull_request' || github.event.action != 'closed')}} + outputs: + tags: ${{ steps.meta.outputs.tags }} + steps: + - name: Checkout git repo + uses: actions/checkout@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Generate UUID image name + id: uuid + run: echo "UUID_TAG_APP=$(uuidgen)" >> $GITHUB_ENV + - name: Docker metadata + id: meta + uses: docker/metadata-action@v3 + with: + images: registry.uffizzi.com/${{ env.UUID_TAG_APP }} + tags: type=raw,value=60d + - name: Build and Push Image to registry.uffizzi.com ephemeral registry + uses: docker/build-push-action@v2 + with: + push: true + context: ./ + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + + render-compose-file: + name: Render Docker Compose File + # Pass output of this workflow to another triggered by `workflow_run` event. + runs-on: ubuntu-latest + needs: + - build-windmill + outputs: + compose-file-cache-key: ${{ steps.hash.outputs.hash }} + steps: + - name: Checkout git repo + uses: actions/checkout@v3 + - name: Render Compose File + run: | + WINDMILL_IMAGE=${{ needs.build-windmill.outputs.tags }} + export WINDMILL_IMAGE + LSP_IMAGE=ghcr.io/windmill-labs/windmill-lsp + export LSP_IMAGE + envsubst '${WINDMILL_IMAGE} ${LSP_IMAGE}' < ./.github/uffizzi/docker-compose.uffizzi.yml > docker-compose.rendered.yml + cat docker-compose.rendered.yml + - name: Upload Rendered Compose File as Artifact + uses: actions/upload-artifact@v3 + with: + name: preview-spec + path: docker-compose.rendered.yml + retention-days: 2 + - name: Serialize PR Event to File + run: | + cat << EOF > event.json + ${{ toJSON(github.event) }} + EOF + - name: Upload PR Event as Artifact + uses: actions/upload-artifact@v3 + with: + name: preview-spec + path: event.json + retention-days: 2 + + delete-preview: + name: Call for Preview Deletion + runs-on: ubuntu-latest + if: ${{ github.event.action == 'closed' }} + steps: + # If this PR is closing, we will not render a compose file nor pass it to the next workflow. + - name: Serialize PR Event to File + run: echo '${{ toJSON(github.event) }}' > event.json + - name: Upload PR Event as Artifact + uses: actions/upload-artifact@v3 + with: + name: preview-spec + path: event.json + retention-days: 2 diff --git a/.github/workflows/uffizzi-preview.yml b/.github/workflows/uffizzi-preview.yml new file mode 100644 index 0000000000..034a795572 --- /dev/null +++ b/.github/workflows/uffizzi-preview.yml @@ -0,0 +1,83 @@ +name: Deploy Uffizzi Preview + +on: + workflow_run: + workflows: + - "Build PR Image" + types: + - completed + +jobs: + cache-compose-file: + name: Cache Compose File + runs-on: ubuntu-latest + outputs: + compose-file-cache-key: ${{ env.COMPOSE_FILE_HASH }} + pr-number: ${{ env.PR_NUMBER }} + steps: + - name: 'Download artifacts' + # Fetch output (zip archive) from the workflow run that triggered this workflow. + uses: actions/github-script@v6 + with: + script: | + let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { + return artifact.name == "preview-spec" + })[0]; + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + let fs = require('fs'); + fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview-spec.zip`, Buffer.from(download.data)); + - name: 'Unzip artifact' + run: unzip preview-spec.zip + - name: Read Event into ENV + run: | + echo 'EVENT_JSON<> $GITHUB_ENV + cat event.json >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + - name: Hash Rendered Compose File + id: hash + # If the previous workflow was triggered by a PR close event, we will not have a compose file artifact. + if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }} + run: echo "COMPOSE_FILE_HASH=$(md5sum docker-compose.rendered.yml | awk '{ print $1 }')" >> $GITHUB_ENV + - name: Cache Rendered Compose File + if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }} + uses: actions/cache@v3 + with: + path: docker-compose.rendered.yml + key: ${{ env.COMPOSE_FILE_HASH }} + + - name: Read PR Number From Event Object + id: pr + run: echo "PR_NUMBER=${{ fromJSON(env.EVENT_JSON).number }}" >> $GITHUB_ENV + + - name: DEBUG - Print Job Outputs + if: ${{ runner.debug }} + run: | + echo "PR number: ${{ env.PR_NUMBER }}" + echo "Compose file hash: ${{ env.COMPOSE_FILE_HASH }}" + cat event.json + deploy-uffizzi-preview: + name: Use Remote Workflow to Preview on Uffizzi + needs: + - cache-compose-file + uses: UffizziCloud/preview-action/.github/workflows/reusable.yaml@v2.6.1 + with: + # If this workflow was triggered by a PR close event, cache-key will be an empty string + # and this reusable workflow will delete the preview deployment. + compose-file-cache-key: ${{ needs.cache-compose-file.outputs.compose-file-cache-key }} + compose-file-cache-path: docker-compose.rendered.yml + server: https://app.uffizzi.com/ + pr-number: ${{ needs.cache-compose-file.outputs.pr-number }} + permissions: + contents: read + pull-requests: write + id-token: write