Repository navigation
fix: ask the node about lingering pooled transactions instead of presuming them dropped #1342
Workflow file for this run
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: Deploy Preview Environment | |
| # A preview is a bundle in the shared bucket, its own runtime-config.json, and one key in the | |
| # routing store. There is nothing per-PR in the infrastructure any more, so a teardown is three | |
| # deletes and can never strand a load balancer rule or a DNS record. | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, labeled] | |
| branches: | |
| - main | |
| concurrency: | |
| group: preview-pr-${{ github.event.pull_request.number }} | |
| cancel-in-progress: false | |
| env: | |
| AWS_REGION: eu-west-1 | |
| TERRAFORM_VERSION: 1.16.0 | |
| CDN_DIR: terraform/cdn | |
| CDN_BACKEND_CONFIG: ../environments/dev/backend.config | |
| jobs: | |
| # The `create-preview` label is the approval; one-way, with teardown still on PR close. | |
| gate: | |
| name: Preview deploy gate | |
| runs-on: ubuntu-latest | |
| permissions: {} | |
| # A `labeled` event fires for any label, hence the last clause. | |
| if: >- | |
| github.event.pull_request.head.repo.full_name == github.repository && | |
| contains(github.event.pull_request.labels.*.name, 'create-preview') && | |
| (github.event.action != 'labeled' || github.event.label.name == 'create-preview') | |
| steps: | |
| - name: Confirm gate | |
| run: echo "Preview deploy gate passed" | |
| announce: | |
| name: Post Initial PR Comment | |
| runs-on: ubuntu-latest | |
| needs: [gate] | |
| permissions: | |
| pull-requests: write | |
| steps: | |
| - uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5 | |
| with: | |
| header: preview | |
| message: | | |
| ### Preview — deploying | |
| Building `${{ github.event.pull_request.head.sha }}`. [Follow along →](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | |
| deploy: | |
| name: Deploy Preview Environment | |
| runs-on: ubuntu-latest | |
| needs: [gate] | |
| permissions: | |
| id-token: write | |
| contents: read | |
| actions: read | |
| # Shared, never per-PR: nothing in a workflow can delete an environment. See workflows/README.md. | |
| environment: | |
| name: preview | |
| url: https://pr-${{ github.event.pull_request.number }}.block-explorer-preview.vechain.org | |
| outputs: | |
| url: ${{ steps.meta.outputs.url }} | |
| steps: | |
| # The tree the artifact was named from, and not one the PR can edit. | |
| - name: Checkout the default branch | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ github.event.repository.default_branch }} | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Resolve the preview's identity | |
| id: meta | |
| env: | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| run: | | |
| set -euo pipefail | |
| git rev-parse --verify "${HEAD_SHA}^{commit}" >/dev/null 2>&1 || git fetch --quiet origin "$HEAD_SHA" | |
| APP_SHA=$(bash scripts/app-content-sha.sh "$HEAD_SHA") | |
| SHORT_SHA="${HEAD_SHA:0:7}" | |
| { | |
| echo "app_sha=${APP_SHA}" | |
| echo "config=pr-${PR_NUMBER}" | |
| echo "host=pr-${PR_NUMBER}.block-explorer-preview.vechain.org" | |
| echo "url=https://pr-${PR_NUMBER}.block-explorer-preview.vechain.org" | |
| echo "app_version=v.0.0.0-pr.${PR_NUMBER}.${SHORT_SHA}" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "PR #${PR_NUMBER} at ${HEAD_SHA} carries app content ${APP_SHA}." | |
| # Long window because a `labeled` event can arrive while the unit tests and the bundle build | |
| # are still running on that commit. | |
| - name: Wait for the bundle | |
| id: artifact | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| APP_SHA: ${{ steps.meta.outputs.app_sha }} | |
| run: | | |
| set -euo pipefail | |
| for attempt in $(seq 1 40); do | |
| url=$(gh api "repos/${REPO}/actions/artifacts?name=${APP_SHA}&per_page=100" \ | |
| --jq '[.artifacts[] | select(.expired == false)] | first | .archive_download_url // empty') | |
| if [ -n "$url" ]; then | |
| echo "url=${url}" >> "$GITHUB_OUTPUT" | |
| echo "${APP_SHA} is published." | |
| exit 0 | |
| fi | |
| echo "Attempt ${attempt}/40: not published yet, retrying in 30s..." | |
| sleep 30 | |
| done | |
| echo "::error::No bundle named ${APP_SHA} appeared. The unit tests for this commit have to pass before a preview can deploy." | |
| exit 1 | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3 | |
| with: | |
| aws-region: ${{ env.AWS_REGION }} | |
| role-to-assume: ${{ secrets.AWS_ACC_ROLE }} | |
| - name: Setup Terraform | |
| uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1 | |
| with: | |
| terraform_version: ${{ env.TERRAFORM_VERSION }} | |
| terraform_wrapper: false | |
| # Read-only: previews own no infrastructure, they borrow dev's distribution. | |
| - name: Read the CDN outputs | |
| id: cdn | |
| working-directory: ${{ env.CDN_DIR }} | |
| run: | | |
| set -euo pipefail | |
| terraform init -input=false -backend-config="${CDN_BACKEND_CONFIG}" | |
| terraform workspace select dev | |
| { | |
| echo "bucket=$(terraform output -raw bucket_name)" | |
| echo "distribution=$(terraform output -raw distribution_id)" | |
| echo "kvs_arn=$(terraform output -raw key_value_store_arn)" | |
| } >> "$GITHUB_OUTPUT" | |
| # Shared with dev and with every other preview on the same content, so this is usually a skip. | |
| - name: Copy the bundle into the bucket | |
| env: | |
| BUCKET: ${{ steps.cdn.outputs.bucket }} | |
| APP_SHA: ${{ steps.meta.outputs.app_sha }} | |
| ARTIFACT_URL: ${{ steps.artifact.outputs.url }} | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| if aws s3api head-object --bucket "$BUCKET" --key "${APP_SHA}/en.html" >/dev/null 2>&1; then | |
| echo "${APP_SHA} is already in ${BUCKET}." | |
| exit 0 | |
| fi | |
| curl -sSfL -H "Authorization: Bearer ${GH_TOKEN}" -o artifact.zip "$ARTIFACT_URL" | |
| unzip -q artifact.zip | |
| mkdir -p bundle && tar -xzf bundle.tgz -C bundle | |
| aws s3 sync bundle/_next "s3://${BUCKET}/${APP_SHA}/_next" --only-show-errors \ | |
| --cache-control 'public, max-age=31536000, immutable' | |
| aws s3 sync bundle "s3://${BUCKET}/${APP_SHA}" --exclude '_next/*' --only-show-errors \ | |
| --cache-control 'public, max-age=0, must-revalidate, s-maxage=31536000' | |
| - name: Activate the preview | |
| env: | |
| BUCKET: ${{ steps.cdn.outputs.bucket }} | |
| KVS_ARN: ${{ steps.cdn.outputs.kvs_arn }} | |
| DISTRIBUTION: ${{ steps.cdn.outputs.distribution }} | |
| APP_SHA: ${{ steps.meta.outputs.app_sha }} | |
| APP_VERSION: ${{ steps.meta.outputs.app_version }} | |
| CONFIG: ${{ steps.meta.outputs.config }} | |
| HOST: ${{ steps.meta.outputs.host }} | |
| run: | | |
| set -euo pipefail | |
| jq -n --arg version "$APP_VERSION" --arg bundle "$APP_SHA" \ | |
| '{appVersion: $version, allowDevMode: true, soloContracts: {}, bundle: $bundle}' \ | |
| > runtime-config.json | |
| aws s3 cp runtime-config.json "s3://${BUCKET}/${CONFIG}/runtime-config.json" \ | |
| --content-type application/json --cache-control 'no-cache' --only-show-errors | |
| etag=$(aws cloudfront-keyvaluestore describe-key-value-store --kvs-arn "$KVS_ARN" \ | |
| --no-cli-pager --query ETag --output text) | |
| aws cloudfront-keyvaluestore put-key --kvs-arn "$KVS_ARN" --if-match "$etag" \ | |
| --key "$HOST" --value "$(jq -nc --arg b "$APP_SHA" --arg c "$CONFIG" '{bundle: $b, config: $c}')" \ | |
| --no-cli-pager >/dev/null | |
| id=$(aws cloudfront create-invalidation --distribution-id "$DISTRIBUTION" \ | |
| --paths "/runtime-config.json" "/${CONFIG}/runtime-config.json" \ | |
| --no-cli-pager --query 'Invalidation.Id' --output text) | |
| aws cloudfront wait invalidation-completed --distribution-id "$DISTRIBUTION" --id "$id" | |
| - name: Confirm the preview is serving | |
| env: | |
| URL: ${{ steps.meta.outputs.url }} | |
| APP_VERSION: ${{ steps.meta.outputs.app_version }} | |
| run: | | |
| set -euo pipefail | |
| for attempt in $(seq 1 10); do | |
| served=$(curl -sf "${URL}/runtime-config.json" | jq -r '.appVersion // empty' || true) | |
| if [ "$served" = "$APP_VERSION" ]; then | |
| echo "${URL} serves ${served}." | |
| exit 0 | |
| fi | |
| echo "Attempt ${attempt}/10: saw '${served}', retrying in 15s..." | |
| sleep 15 | |
| done | |
| echo "::error::${URL} never served ${APP_VERSION}." | |
| exit 1 | |
| comment: | |
| name: Update PR Comment | |
| runs-on: ubuntu-latest | |
| needs: [gate, deploy] | |
| # `always()` disables the implicit skip, so the gate has to be re-checked here. | |
| if: always() && needs.gate.result == 'success' | |
| permissions: | |
| pull-requests: write | |
| steps: | |
| - name: Report success | |
| if: needs.deploy.result == 'success' | |
| uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5 | |
| with: | |
| header: preview | |
| message: | | |
| ### Preview — ready | |
| ${{ needs.deploy.outputs.url }} | |
| <sub>Commit `${{ github.event.pull_request.head.sha }}`. Torn down when this PR closes.</sub> | |
| - name: Report failure | |
| if: needs.deploy.result != 'success' | |
| uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5 | |
| with: | |
| header: preview | |
| message: | | |
| ### Preview — failed | |
| Commit `${{ github.event.pull_request.head.sha }}` did not deploy. [View logs →](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) |