feat: 2d hypergraph visual polish #6363
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: Apps Deployment | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| skip_build: | |
| required: false | |
| description: "Skip Docker Build" | |
| type: boolean | |
| default: false | |
| push: | |
| pull_request: | |
| types: | |
| - opened | |
| - reopened | |
| - closed | |
| - synchronize | |
| permissions: write-all | |
| jobs: | |
| pr-check: | |
| runs-on: ubicloud-standard-2 | |
| outputs: | |
| skip: ${{ github.event_name == 'push' && steps.findPr.outputs.number != '' }} | |
| pr_exists: ${{ steps.findPr.outputs.number != '' }} | |
| steps: | |
| - uses: jwalton/gh-find-current-pr@master | |
| id: findPr | |
| - name: Debug PR info | |
| run: | | |
| echo "PR exists: ${{ steps.findPr.outputs.number != '' }}" | |
| echo "PR number: ${{ steps.findPr.outputs.number }}" | |
| echo "Is push based: ${{ github.event_name == 'push' }}" | |
| find-deployments: | |
| needs: pr-check | |
| if: | | |
| github.event.pull_request.state != 'closed' && | |
| (github.ref_name == 'main' || github.ref_name == 'dev' | |
| || needs.pr-check.outputs.skip == 'false') | |
| runs-on: ubicloud-standard-2 | |
| outputs: | |
| deploy_matrix: ${{ steps.parse.outputs.deploy_matrix }} | |
| pr_preview_matrix: ${{ steps.parse.outputs.pr_preview_matrix }} | |
| all_apps: ${{ steps.parse.outputs.all_apps }} | |
| preview_domains: ${{ steps.parse.outputs.preview_domains }} | |
| runners: ${{ steps.parse.outputs.runners }} | |
| package_paths: ${{ steps.parse.outputs.package_paths }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@v4 | |
| name: Install pnpm | |
| with: | |
| run_install: false | |
| - name: Install turbo | |
| run: pnpm add turbo@^2.3.3 --global | |
| - name: Get apps list | |
| id: apps | |
| run: | | |
| # List both apps and services directories | |
| apps_list=$(ls -d apps/* 2>/dev/null | sed 's/apps\///' || echo "") | |
| services_list=$(ls -d services/* 2>/dev/null | sed 's/services\///' || echo "") | |
| # Combine both lists | |
| all_apps=$(echo -e "${apps_list}\n${services_list}" | grep -v '^$' | jq -R -s -c 'split("\n")[:-1]') | |
| echo "all_apps=$all_apps" >> $GITHUB_OUTPUT | |
| echo "All apps in workspace:" | |
| echo "$all_apps" | jq '.' | |
| - name: Get build json (PR based) | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| pnpm exec turbo run build --filter="...[origin/${{ github.base_ref }}]" --dry-run=json 2>/dev/null > build.json | |
| - name: Get build json (Push based) | |
| if: github.event_name == 'push' | |
| run: | | |
| if git rev-parse --quiet --verify ${{ github.event.before }}^{commit}; then | |
| BASE_COMMIT="$(git merge-base ${{ github.event.before }} HEAD)" | |
| pnpm exec turbo run build --filter="...[${BASE_COMMIT}]" --dry-run=json 2>/dev/null > build.json | |
| else | |
| pnpm exec turbo run build --dry-run=json 2>/dev/null > build.json | |
| fi | |
| - name: Get changed packages from turbo | |
| id: turbo | |
| run: | | |
| changed_packages=$(cat build.json | jq -c '[.tasks[].package]') | |
| echo "changed_packages=$changed_packages" >> $GITHUB_OUTPUT | |
| echo "Changed packages from turbo:" | |
| echo "$changed_packages" | jq '.' | |
| # Extract package directories mapping (package -> directory parent folder) | |
| # e.g., {"torus-cache": "services", "torus-wallet": "apps"} | |
| package_paths=$(cat build.json | jq -c ' | |
| [.tasks[] | {key: .package, value: (.directory | split("/")[0])}] | |
| | unique_by(.key) | |
| | from_entries | |
| ') | |
| echo "package_paths=$package_paths" >> $GITHUB_OUTPUT | |
| echo "Package paths:" | |
| echo "$package_paths" | jq '.' | |
| # Get paths for ALL packages | |
| all_package_paths=$(pnpm exec turbo run build --dry-run=json 2>/dev/null | jq -c ' | |
| [.tasks[] | {key: .package, value: (.directory | split("/")[0])}] | |
| | unique_by(.key) | |
| | from_entries | |
| ') | |
| echo "all_package_paths=$all_package_paths" >> $GITHUB_OUTPUT | |
| echo "All package paths:" | |
| echo "$all_package_paths" | jq '.' | |
| - name: Filter changed apps | |
| id: changed | |
| run: | | |
| set -euo pipefail | |
| all_apps='${{ steps.apps.outputs.all_apps }}' | |
| changed_packages='${{ steps.turbo.outputs.changed_packages }}' | |
| changed_apps=$(echo "$all_apps" | jq -c --argjson changed "$changed_packages" \ | |
| '[.[] | select(. as $pkg | $changed | any(. == $pkg))]') | |
| echo "changed_apps=$changed_apps" >> $GITHUB_OUTPUT | |
| echo 'Changed apps:' | |
| echo "$changed_apps" | jq '.' | |
| - name: Parse deployment config | |
| id: parse | |
| run: | | |
| set -euo pipefail | |
| config=$(cat .github/ci-config.json) | |
| changed_apps='${{ steps.changed.outputs.changed_apps }}' | |
| declare -A apps_to_deploy | |
| all_apps=$(echo "$config" | jq -c '[.apps | keys[]]') | |
| # For each app in config, check if it was changed and mark for deploy. | |
| # The deploys list is used to mark what dependency apps should be deployed | |
| # as well. | |
| for app in $(echo "$changed_apps" | jq -r '.[]'); do | |
| apps_to_deploy["$app"]=1 | |
| dependent_apps=$(echo "$config" | jq -r ".apps[\"$app\"].deploys[]? // empty") | |
| for dep_app in $dependent_apps; do | |
| apps_to_deploy["$dep_app"]=1 | |
| done | |
| done | |
| deploy_apps=() | |
| pr_apps=() | |
| # For all apps marked for deploy, we aggregate | |
| # those that must deploy PR previews and filter prod-only apps on non-main branches. | |
| for app in "${!apps_to_deploy[@]}"; do | |
| # Check if app is prod-only and we're NOT on main branch | |
| prod_only=$(echo "$config" | jq -r ".apps[\"$app\"].prod_only // false") | |
| if [ "$prod_only" == "true" ] && [ "${{ github.ref_name }}" != "main" ]; then | |
| echo "Skipping $app (prod-only, current branch: ${{ github.ref_name }})" | |
| continue | |
| fi | |
| deploy_apps+=("\"$app\"") | |
| pr_preview=$(echo "$config" | jq -r ".apps[\"$app\"].pr_preview // false") | |
| if [ "$pr_preview" == "true" ]; then | |
| pr_apps+=("\"$app\"") | |
| fi | |
| done | |
| # Github matrix shenanigans | |
| if [ ${#deploy_apps[@]} -eq 0 ]; then | |
| deploy_json="[]" | |
| else | |
| deploy_json=$(IFS=,; echo "[${deploy_apps[*]}]") | |
| fi | |
| if [ ${#pr_apps[@]} -eq 0 ]; then | |
| pr_json="[]" | |
| else | |
| pr_json=$(IFS=,; echo "[${pr_apps[*]}]") | |
| fi | |
| preview_domains=$(echo "$config" | jq -c '[.apps | to_entries[] | select(.value.preview_domain != null) | {key: .key, value: .value.preview_domain}] | from_entries') | |
| # Build runners mapping (app -> runner, default to ubicloud-standard-16) | |
| runners=$(echo "$config" | jq -c '[.apps | to_entries[] | {key: .key, value: (.value.runner // "ubicloud-standard-16")}] | from_entries') | |
| # Use complete package paths from turbo | |
| package_paths='${{ steps.turbo.outputs.all_package_paths }}' | |
| echo "deploy_matrix=$deploy_json" >> $GITHUB_OUTPUT | |
| echo "pr_preview_matrix=$pr_json" >> $GITHUB_OUTPUT | |
| echo "all_apps=$all_apps" >> $GITHUB_OUTPUT | |
| echo "preview_domains=$preview_domains" >> $GITHUB_OUTPUT | |
| echo "runners=$runners" >> $GITHUB_OUTPUT | |
| echo "package_paths=$package_paths" >> $GITHUB_OUTPUT | |
| echo "Apps to deploy: $deploy_json" | |
| echo "Apps with PR preview: $pr_json" | |
| echo "Runners: $runners" | |
| echo "Package paths: $package_paths" | |
| ################################### | |
| ###### REGULAR DEPLOYMENTS ####### | |
| ################################### | |
| deploy: | |
| needs: find-deployments | |
| if: | | |
| inputs.skip_build || | |
| contains(github.event.head_commit.message, 'skip build') || | |
| (needs.find-deployments.outputs.deploy_matrix != '[]' && | |
| (github.ref_name == 'main' || github.ref_name == 'dev' || github.ref_name == 'cicd')) | |
| strategy: | |
| matrix: | |
| app: ${{ fromJson(needs.find-deployments.outputs.deploy_matrix) }} | |
| fail-fast: false | |
| uses: ./.github/workflows/app-deployment-template.yml | |
| secrets: inherit | |
| with: | |
| app_name: ${{ matrix.app }} | |
| app_path: ${{ fromJson(needs.find-deployments.outputs.package_paths)[matrix.app] }} | |
| skip_build: ${{ inputs.skip_build || false }} | |
| runner: ${{ fromJson(needs.find-deployments.outputs.runners)[matrix.app] }} | |
| ################################### | |
| ######## DRY RUN BUILDS ########## | |
| ################################### | |
| dry-run: | |
| needs: [find-deployments, pr-check] | |
| if: | | |
| github.ref_name != 'main' && | |
| github.ref_name != 'dev' && | |
| github.ref_name != 'cicd' && | |
| needs.find-deployments.outputs.deploy_matrix != '[]' && | |
| needs.pr-check.outputs.pr_exists == 'false' | |
| strategy: | |
| matrix: | |
| app: ${{ fromJson(needs.find-deployments.outputs.deploy_matrix) }} | |
| fail-fast: false | |
| uses: ./.github/workflows/dry-run-build-template.yml | |
| secrets: inherit | |
| with: | |
| app_name: ${{ matrix.app }} | |
| runner: ${{ fromJson(needs.find-deployments.outputs.runners)[matrix.app] }} | |
| app_path: ${{ fromJson(needs.find-deployments.outputs.package_paths)[matrix.app] }} | |
| ################################### | |
| ########### PR PREVIEW ############ | |
| ################################### | |
| pr-preview-deploy: | |
| needs: find-deployments | |
| if: | | |
| github.event.pull_request.state == 'open' && !github.event.repository.fork | |
| && !github.event.pull_request.draft | |
| && github.head_ref != 'main' && github.head_ref != 'dev' | |
| && needs.find-deployments.outputs.pr_preview_matrix != '[]' | |
| strategy: | |
| matrix: | |
| app: ${{ fromJson(needs.find-deployments.outputs.pr_preview_matrix) }} | |
| fail-fast: false | |
| uses: ./.github/workflows/pr-preview-template.yml | |
| secrets: inherit | |
| with: | |
| app_name: ${{ matrix.app }} | |
| runner: ${{ fromJson(needs.find-deployments.outputs.runners)[matrix.app] }} | |
| app_path: ${{ fromJson(needs.find-deployments.outputs.package_paths)[matrix.app] }} | |
| pr-preview-cleanup: | |
| needs: find-deployments | |
| if: | | |
| github.event.pull_request.state == 'closed' | |
| && github.head_ref != 'main' && github.head_ref != 'dev' | |
| strategy: | |
| matrix: | |
| app: ${{ fromJson(needs.find-deployments.outputs.all_apps) }} | |
| fail-fast: false | |
| uses: ./.github/workflows/pr-preview-template.yml | |
| secrets: inherit | |
| with: | |
| app_name: ${{ matrix.app }} | |
| runner: ${{ fromJson(needs.find-deployments.outputs.runners)[matrix.app] }} | |
| app_path: ${{ fromJson(needs.find-deployments.outputs.package_paths)[matrix.app] }} | |
| cleanup: true | |
| deployment-summary: | |
| if: always() | |
| needs: | |
| [find-deployments, deploy, dry-run, pr-preview-deploy, pr-preview-cleanup] | |
| runs-on: ubicloud-standard-2 | |
| steps: | |
| - name: Generate deployment summary | |
| run: | | |
| set -euo pipefail | |
| echo "## 🚀 Deployment Overview" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Environment detection | |
| if [ "${{ github.ref_name }}" == "main" ]; then | |
| ENV_NAME="Production" | |
| ENV_ICON="🏭" | |
| elif [ "${{ github.ref_name }}" == "dev" ]; then | |
| ENV_NAME="Development" | |
| ENV_ICON="🧪" | |
| else | |
| ENV_NAME="Preview/Dry-run" | |
| ENV_ICON="👀" | |
| fi | |
| echo "**Environment:** $ENV_ICON $ENV_NAME (\`${{ github.ref_name }}\`)" >> $GITHUB_STEP_SUMMARY | |
| echo "**Trigger:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| all_apps='${{ needs.find-deployments.outputs.all_apps }}' | |
| deploy_apps='${{ needs.find-deployments.outputs.deploy_matrix }}' | |
| # Production/Dev Deployments | |
| if [ "${{ github.ref_name }}" == "main" ] || [ "${{ github.ref_name }}" == "dev" ] || [ "${{ github.ref_name }}" == "cicd" ]; then | |
| echo "### 🏗️ Application Deployments" >> $GITHUB_STEP_SUMMARY | |
| echo "| Application | Changed | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------------|---------|--------|" >> $GITHUB_STEP_SUMMARY | |
| deploy_result='${{ needs.deploy.result }}' | |
| if [ "$deploy_apps" != "[]" ]; then | |
| for app in $(echo "$deploy_apps" | jq -r '.[]'); do | |
| case "$deploy_result" in | |
| "success") deploy_icon="✅"; deploy_text="Deployed";; | |
| "failure") deploy_icon="❌"; deploy_text="Failed";; | |
| "skipped") deploy_icon="⏭️"; deploy_text="Skipped";; | |
| *) deploy_icon="❓"; deploy_text="Unknown";; | |
| esac | |
| echo "| ${app} | ✅ Yes | ${deploy_icon} ${deploy_text} |" >> $GITHUB_STEP_SUMMARY | |
| done | |
| fi | |
| fi | |
| # PR Previews | |
| if [ "${{ github.event_name }}" == "pull_request" ]; then | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 🔍 PR Preview Deployments" >> $GITHUB_STEP_SUMMARY | |
| echo "| Application | Status | Preview URL |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------------|--------|-------------|" >> $GITHUB_STEP_SUMMARY | |
| preview_domains='${{ needs.find-deployments.outputs.preview_domains }}' | |
| pr_apps='${{ needs.find-deployments.outputs.pr_preview_matrix }}' | |
| deploy_result='${{ needs.pr-preview-deploy.result }}' | |
| cleanup_result='${{ needs.pr-preview-cleanup.result }}' | |
| if [ "$pr_apps" != "[]" ]; then | |
| for app in $(echo "$pr_apps" | jq -r '.[]'); do | |
| if [ "$deploy_result" == "success" ]; then | |
| preview_domain=$(echo "$preview_domains" | jq -r ".[\"$app\"] // empty") | |
| if [ -n "$preview_domain" ]; then | |
| preview_url="https://pr-${{ github.event.number }}.${preview_domain}" | |
| else | |
| preview_url="https://pr-${{ github.event.number }}.${app}.torus.network" | |
| fi | |
| echo "| ${app} | ✅ Deployed | [Preview](${preview_url}) |" >> $GITHUB_STEP_SUMMARY | |
| elif [ "$cleanup_result" == "success" ]; then | |
| echo "| ${app} | 🗑️ Deleted | - |" >> $GITHUB_STEP_SUMMARY | |
| elif [ "$deploy_result" == "skipped" ] && [ "$cleanup_result" == "skipped" ]; then | |
| echo "| ${app} | ⏭️ No Changes | - |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| ${app} | ❌ Failed | - |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| done | |
| fi | |
| fi | |
| # Dry Run Builds | |
| if [ "${{ github.ref_name }}" != "main" ] && [ "${{ github.ref_name }}" != "dev" ] && [ "${{ github.ref_name }}" != "cicd" ] && [ "${{ github.event_name }}" != "pull_request" ]; then | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 🔨 Dry Run Builds" >> $GITHUB_STEP_SUMMARY | |
| echo "| Application | Build Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------------|--------------|" >> $GITHUB_STEP_SUMMARY | |
| dry_run_result='${{ needs.dry-run.result }}' | |
| if [ "$deploy_apps" != "[]" ]; then | |
| for app in $(echo "$deploy_apps" | jq -r '.[]'); do | |
| case "$dry_run_result" in | |
| "success") echo "| ${app} | ✅ Build Successful |" >> $GITHUB_STEP_SUMMARY;; | |
| "failure") echo "| ${app} | ❌ Build Failed |" >> $GITHUB_STEP_SUMMARY;; | |
| "skipped") echo "| ${app} | ⏭️ Skipped (No Changes) |" >> $GITHUB_STEP_SUMMARY;; | |
| *) echo "| ${app} | ❓ Unknown |" >> $GITHUB_STEP_SUMMARY;; | |
| esac | |
| done | |
| fi | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "**Workflow Run:** [#${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY |