diff --git a/.github/workflows/cloudflare-conditional-deploy.yml b/.github/workflows/cloudflare-conditional-deploy.yml index 9d8751a2b..854cdbf77 100644 --- a/.github/workflows/cloudflare-conditional-deploy.yml +++ b/.github/workflows/cloudflare-conditional-deploy.yml @@ -1,4 +1,4 @@ -name: Cloudflare conditional deploy +name: Cloudflare conditional deploy (wrangler + deployments box, no comments) on: # Previews on PRs targeting gh-pages (Codex PRs) @@ -6,27 +6,32 @@ on: types: [opened, synchronize, reopened] branches: - gh-pages - # Production branch pushes (no prod steps yet; safe to keep for later) + # Keep pushes to gh-pages enabled (you can add prod steps later) push: branches: - gh-pages jobs: - detect-and-deploy: + deploy: name: Detect changes and deploy only touched apps runs-on: ubuntu-latest - # Create PR comments + GitHub Deployments + # Needed for GitHub Deployments box permissions: contents: read deployments: write pull-requests: write - # Auto-cancel older runs for the same PR/branch + # Auto-cancel older runs for same PR/branch concurrency: - group: cf-pages-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: cf-wrangler-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true + env: + # Convenient refs + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + steps: - name: Checkout uses: actions/checkout@v4 @@ -57,7 +62,6 @@ jobs: echo "Changed files:" echo "$DIFF" - # default outputs (false) { echo "library=false" echo "hbnb=false" @@ -68,189 +72,387 @@ jobs: echo "bpvsbuckler=false" } >> "$GITHUB_OUTPUT" - # detect changes in each app directory - if echo "$DIFF" | grep -qE '^static/library/'; then echo "library=true" >> "$GITHUB_OUTPUT"; fi - if echo "$DIFF" | grep -qE '^static/hbnb/'; then echo "hbnb=true" >> "$GITHUB_OUTPUT"; fi - if echo "$DIFF" | grep -qE '^static/forces/'; then echo "forces=true" >> "$GITHUB_OUTPUT"; fi - if echo "$DIFF" | grep -qE '^static/gui/'; then echo "gui=true" >> "$GITHUB_OUTPUT"; fi - if echo "$DIFF" | grep -qE '^static/datro/'; then echo "datro=true" >> "$GITHUB_OUTPUT"; fi + if echo "$DIFF" | grep -qE '^static/library/'; then echo "library=true" >> "$GITHUB_OUTPUT"; fi + if echo "$DIFF" | grep -qE '^static/hbnb/'; then echo "hbnb=true" >> "$GITHUB_OUTPUT"; fi + if echo "$DIFF" | grep -qE '^static/forces/'; then echo "forces=true" >> "$GITHUB_OUTPUT"; fi + if echo "$DIFF" | grep -qE '^static/gui/'; then echo "gui=true" >> "$GITHUB_OUTPUT"; fi + if echo "$DIFF" | grep -qE '^static/datro/'; then echo "datro=true" >> "$GITHUB_OUTPUT"; fi if echo "$DIFF" | grep -qE '^static/projections/'; then echo "projections=true" >> "$GITHUB_OUTPUT"; fi if echo "$DIFF" | grep -qE '^static/bpvsbuckler/'; then echo "bpvsbuckler=true" >> "$GITHUB_OUTPUT"; fi - - name: Debug outputs - run: | - echo "library: ${{ steps.changes.outputs.library }}" - echo "hbnb: ${{ steps.changes.outputs.hbnb }}" - echo "forces: ${{ steps.changes.outputs.forces }}" - echo "gui: ${{ steps.changes.outputs.gui }}" - echo "datro: ${{ steps.changes.outputs.datro }}" - echo "projections: ${{ steps.changes.outputs.projections }}" - echo "bpvsbuckler: ${{ steps.changes.outputs.bpvsbuckler }}" - - - name: Setup Node (for builds) + - name: Setup Node uses: actions/setup-node@v4 with: - node-version: '22' # or '20' - cache: 'npm' + node-version: '22' + + - name: Install Wrangler + run: npm i -g wrangler@^4.47.0 + ###################################################################### - # PREVIEW DEPLOYS FOR PRs — posts a comment with the preview URL(s) - # Uses andykenward/github-actions-cloudflare-pages to create GH Deployments - # Repo Secrets required: - # CLOUDFLARE_API_TOKEN (Account:Read + Pages:Builds:Edit) - # CLOUDFLARE_ACCOUNT_ID - # - # IMPORTANT: `cloudflare-project-name` must match CF Pages project names. - # IMPORTANT: `github-environment` must match your GitHub Environments: - # library(preview), hbnb(preview), ceo(preview), gui(preview), - # datro(preview), projections(preview), bpvsbuckler(preview) + # Helper: function to deploy and emit PREVIEW_URL for a given app ###################################################################### - - - name: Build library + - name: Deploy library (preview) + id: deploy_library if: github.event_name == 'pull_request' && steps.changes.outputs.library == 'true' + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CF_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + PROJECT_NAME: library # Cloudflare Pages project name + DIRECTORY: static/library # Adjust to build output if needed + BRANCH_NAME: ${{ env.BRANCH_NAME }} + COMMIT_SHA: ${{ env.COMMIT_SHA }} + shell: bash run: | - if [ -f static/library/package.json ]; then - cd static/library - npm ci - npm run build --if-present + set -euo pipefail + if [ -f "$DIRECTORY/package.json" ]; then + (cd "$DIRECTORY" && npm ci || npm install && npm run build --if-present) fi + OUT=$(wrangler pages deploy "$DIRECTORY" --project-name="$PROJECT_NAME" --branch="$BRANCH_NAME" --commit-dirty=true --commit-hash="$COMMIT_SHA" 2>&1 | tee /tmp/wrangler_library.log || true) + echo "$OUT" + URL=$(echo "$OUT" | grep -Eo 'https://[a-zA-Z0-9.-]+\.pages\.dev[[:alnum:]/._-]*' | tail -n1 || true) + if [ -z "${URL:-}" ]; then + echo "No preview URL detected in wrangler output"; exit 1 + fi + echo "url=$URL" >> "$GITHUB_OUTPUT" - - name: Deploy library (Preview) and comment URL + - name: Deployment box: library(preview) if: github.event_name == 'pull_request' && steps.changes.outputs.library == 'true' - uses: andykenward/github-actions-cloudflare-pages@v3.1.0 - id: deploy_library + uses: actions/github-script@v7 with: - cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} - cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - cloudflare-project-name: library - directory: static/library - github-token: ${{ secrets.GITHUB_TOKEN }} - github-environment: library(preview) + script: | + const ref = context.payload.pull_request?.head.sha || context.sha; + const env = "library(preview)"; + const url = "${{ steps.deploy_library.outputs.url }}"; + const { data: dep } = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref, + environment: env, + transient_environment: true, + auto_merge: false, + required_contexts: [], + description: "Cloudflare Pages preview" + }); + await github.rest.repos.createDeploymentStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: dep.id, + state: "success", + environment: env, + environment_url: url, + log_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + auto_inactive: false + }); - - name: Build hbnb + # ---------- hbnb ---------- + - name: Deploy hbnb (preview) + id: deploy_hbnb if: github.event_name == 'pull_request' && steps.changes.outputs.hbnb == 'true' + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CF_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + PROJECT_NAME: hbnb + DIRECTORY: static/hbnb + BRANCH_NAME: ${{ env.BRANCH_NAME }} + COMMIT_SHA: ${{ env.COMMIT_SHA }} + shell: bash run: | - if [ -f static/hbnb/package.json ]; then - cd static/hbnb - npm ci - npm run build --if-present + set -euo pipefail + if [ -f "$DIRECTORY/package.json" ]; then + (cd "$DIRECTORY" && npm ci || npm install && npm run build --if-present) fi + OUT=$(wrangler pages deploy "$DIRECTORY" --project-name="$PROJECT_NAME" --branch="$BRANCH_NAME" --commit-dirty=true --commit-hash="$COMMIT_SHA" 2>&1 | tee /tmp/wrangler_hbnb.log || true) + echo "$OUT" + URL=$(echo "$OUT" | grep -Eo 'https://[a-zA-Z0-9.-]+\.pages\.dev[[:alnum:]/._-]*' | tail -n1 || true) + if [ -z "${URL:-}" ]; then echo "No preview URL found"; exit 1; fi + echo "url=$URL" >> "$GITHUB_OUTPUT" - - name: Deploy hbnb (Preview) and comment URL + - name: Deployment box: hbnb(preview) if: github.event_name == 'pull_request' && steps.changes.outputs.hbnb == 'true' - uses: andykenward/github-actions-cloudflare-pages@v3.1.0 - id: deploy_hbnb + uses: actions/github-script@v7 with: - cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} - cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - cloudflare-project-name: hbnb - directory: static/hbnb - github-token: ${{ secrets.GITHUB_TOKEN }} - github-environment: hbnb(preview) + script: | + const ref = context.payload.pull_request?.head.sha || context.sha; + const env = "hbnb(preview)"; + const url = "${{ steps.deploy_hbnb.outputs.url }}"; + const { data: dep } = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref, + environment: env, + transient_environment: true, + auto_merge: false, + required_contexts: [], + description: "Cloudflare Pages preview" + }); + await github.rest.repos.createDeploymentStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: dep.id, + state: "success", + environment: env, + environment_url: url, + log_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + auto_inactive: false + }); - - name: Build forces (ceo) + # ---------- forces (ceo) ---------- + - name: Deploy forces/ceo (preview) + id: deploy_forces if: github.event_name == 'pull_request' && steps.changes.outputs.forces == 'true' + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CF_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + PROJECT_NAME: ceo + DIRECTORY: static/forces + BRANCH_NAME: ${{ env.BRANCH_NAME }} + COMMIT_SHA: ${{ env.COMMIT_SHA }} + shell: bash run: | - if [ -f static/forces/package.json ]; then - cd static/forces - npm ci - npm run build --if-present + set -euo pipefail + if [ -f "$DIRECTORY/package.json" ]; then + (cd "$DIRECTORY" && npm ci || npm install && npm run build --if-present) fi + OUT=$(wrangler pages deploy "$DIRECTORY" --project-name="$PROJECT_NAME" --branch="$BRANCH_NAME" --commit-dirty=true --commit-hash="$COMMIT_SHA" 2>&1 | tee /tmp/wrangler_forces.log || true) + echo "$OUT" + URL=$(echo "$OUT" | grep -Eo 'https://[a-zA-Z0-9.-]+\.pages\.dev[[:alnum:]/._-]*' | tail -n1 || true) + if [ -z "${URL:-}" ]; then echo "No preview URL found"; exit 1; fi + echo "url=$URL" >> "$GITHUB_OUTPUT" - - name: Deploy forces/ceo (Preview) and comment URL + - name: Deployment box: ceo(preview) if: github.event_name == 'pull_request' && steps.changes.outputs.forces == 'true' - uses: andykenward/github-actions-cloudflare-pages@v3.1.0 - id: deploy_forces + uses: actions/github-script@v7 with: - cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} - cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - cloudflare-project-name: ceo - directory: static/forces - github-token: ${{ secrets.GITHUB_TOKEN }} - github-environment: ceo(preview) + script: | + const ref = context.payload.pull_request?.head.sha || context.sha; + const env = "ceo(preview)"; + const url = "${{ steps.deploy_forces.outputs.url }}"; + const { data: dep } = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref, + environment: env, + transient_environment: true, + auto_merge: false, + required_contexts: [], + description: "Cloudflare Pages preview" + }); + await github.rest.repos.createDeploymentStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: dep.id, + state: "success", + environment: env, + environment_url: url, + log_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + auto_inactive: false + }); - - name: Build gui + # ---------- gui ---------- + - name: Deploy gui (preview) + id: deploy_gui if: github.event_name == 'pull_request' && steps.changes.outputs.gui == 'true' + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CF_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + PROJECT_NAME: gui + DIRECTORY: static/gui + BRANCH_NAME: ${{ env.BRANCH_NAME }} + COMMIT_SHA: ${{ env.COMMIT_SHA }} + shell: bash run: | - if [ -f static/gui/package.json ]; then - cd static/gui - npm ci - npm run build --if-present + set -euo pipefail + if [ -f "$DIRECTORY/package.json" ]; then + (cd "$DIRECTORY" && npm ci || npm install && npm run build --if-present) fi + OUT=$(wrangler pages deploy "$DIRECTORY" --project-name="$PROJECT_NAME" --branch="$BRANCH_NAME" --commit-dirty=true --commit-hash="$COMMIT_SHA" 2>&1 | tee /tmp/wrangler_gui.log || true) + echo "$OUT" + URL=$(echo "$OUT" | grep -Eo 'https://[a-zA-Z0-9.-]+\.pages\.dev[[:alnum:]/._-]*' | tail -n1 || true) + if [ -z "${URL:-}" ]; then echo "No preview URL found"; exit 1; fi + echo "url=$URL" >> "$GITHUB_OUTPUT" - - name: Deploy gui (Preview) and comment URL + - name: Deployment box: gui(preview) if: github.event_name == 'pull_request' && steps.changes.outputs.gui == 'true' - uses: andykenward/github-actions-cloudflare-pages@v3.1.0 - id: deploy_gui + uses: actions/github-script@v7 with: - cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} - cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - cloudflare-project-name: gui - directory: static/gui - github-token: ${{ secrets.GITHUB_TOKEN }} - github-environment: gui(preview) + script: | + const ref = context.payload.pull_request?.head.sha || context.sha; + const env = "gui(preview)"; + const url = "${{ steps.deploy_gui.outputs.url }}"; + const { data: dep } = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref, + environment: env, + transient_environment: true, + auto_merge: false, + required_contexts: [], + description: "Cloudflare Pages preview" + }); + await github.rest.repos.createDeploymentStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: dep.id, + state: "success", + environment: env, + environment_url: url, + log_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + auto_inactive: false + }); - - name: Build datro-homepage + # ---------- datro-homepage ---------- + - name: Deploy datro-homepage (preview) + id: deploy_datro if: github.event_name == 'pull_request' && steps.changes.outputs.datro == 'true' + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CF_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + PROJECT_NAME: datro-homepage + DIRECTORY: static/datro + BRANCH_NAME: ${{ env.BRANCH_NAME }} + COMMIT_SHA: ${{ env.COMMIT_SHA }} + shell: bash run: | - if [ -f static/datro/package.json ]; then - cd static/datro - npm ci - npm run build --if-present + set -euo pipefail + if [ -f "$DIRECTORY/package.json" ]; then + (cd "$DIRECTORY" && npm ci || npm install && npm run build --if-present) fi + OUT=$(wrangler pages deploy "$DIRECTORY" --project-name="$PROJECT_NAME" --branch="$BRANCH_NAME" --commit-dirty=true --commit-hash="$COMMIT_SHA" 2>&1 | tee /tmp/wrangler_datro.log || true) + echo "$OUT" + URL=$(echo "$OUT" | grep -Eo 'https://[a-zA-Z0-9.-]+\.pages\.dev[[:alnum:]/._-]*' | tail -n1 || true) + if [ -z "${URL:-}" ]; then echo "No preview URL found"; exit 1; fi + echo "url=$URL" >> "$GITHUB_OUTPUT" - - name: Deploy datro-homepage (Preview) and comment URL + - name: Deployment box: datro(preview) if: github.event_name == 'pull_request' && steps.changes.outputs.datro == 'true' - uses: andykenward/github-actions-cloudflare-pages@v3.1.0 - id: deploy_datro + uses: actions/github-script@v7 with: - cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} - cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - cloudflare-project-name: datro-homepage - directory: static/datro - github-token: ${{ secrets.GITHUB_TOKEN }} - github-environment: datro(preview) + script: | + const ref = context.payload.pull_request?.head.sha || context.sha; + const env = "datro(preview)"; + const url = "${{ steps.deploy_datro.outputs.url }}"; + const { data: dep } = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref, + environment: env, + transient_environment: true, + auto_merge: false, + required_contexts: [], + description: "Cloudflare Pages preview" + }); + await github.rest.repos.createDeploymentStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: dep.id, + state: "success", + environment: env, + environment_url: url, + log_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + auto_inactive: false + }); - - name: Build projections + # ---------- projections ---------- + - name: Deploy projections (preview) + id: deploy_projections if: github.event_name == 'pull_request' && steps.changes.outputs.projections == 'true' + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CF_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + PROJECT_NAME: projections + DIRECTORY: static/projections + BRANCH_NAME: ${{ env.BRANCH_NAME }} + COMMIT_SHA: ${{ env.COMMIT_SHA }} + shell: bash run: | - if [ -f static/projections/package.json ]; then - cd static/projections - npm ci - npm run build --if-present + set -euo pipefail + if [ -f "$DIRECTORY/package.json" ]; then + (cd "$DIRECTORY" && npm ci || npm install && npm run build --if-present) fi + OUT=$(wrangler pages deploy "$DIRECTORY" --project-name="$PROJECT_NAME" --branch="$BRANCH_NAME" --commit-dirty=true --commit-hash="$COMMIT_SHA" 2>&1 | tee /tmp/wrangler_projections.log || true) + echo "$OUT" + URL=$(echo "$OUT" | grep -Eo 'https://[a-zA-Z0-9.-]+\.pages\.dev[[:alnum:]/._-]*' | tail -n1 || true) + if [ -z "${URL:-}" ]; then echo "No preview URL found"; exit 1; fi + echo "url=$URL" >> "$GITHUB_OUTPUT" - - name: Deploy projections (Preview) and comment URL + - name: Deployment box: projections(preview) if: github.event_name == 'pull_request' && steps.changes.outputs.projections == 'true' - uses: andykenward/github-actions-cloudflare-pages@v3.1.0 - id: deploy_projections + uses: actions/github-script@v7 with: - cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} - cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - cloudflare-project-name: projections - directory: static/projections - github-token: ${{ secrets.GITHUB_TOKEN }} - github-environment: projections(preview) + script: | + const ref = context.payload.pull_request?.head.sha || context.sha; + const env = "projections(preview)"; + const url = "${{ steps.deploy_projections.outputs.url }}"; + const { data: dep } = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref, + environment: env, + transient_environment: true, + auto_merge: false, + required_contexts: [], + description: "Cloudflare Pages preview" + }); + await github.rest.repos.createDeploymentStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: dep.id, + state: "success", + environment: env, + environment_url: url, + log_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + auto_inactive: false + }); - - name: Build bpvsbuckler + # ---------- bpvsbuckler ---------- + - name: Deploy bpvsbuckler (preview) + id: deploy_bpvsbuckler if: github.event_name == 'pull_request' && steps.changes.outputs.bpvsbuckler == 'true' + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CF_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + PROJECT_NAME: bpvsbuckler + DIRECTORY: static/bpvsbuckler + BRANCH_NAME: ${{ env.BRANCH_NAME }} + COMMIT_SHA: ${{ env.COMMIT_SHA }} + shell: bash run: | - if [ -f static/bpvsbuckler/package.json ]; then - cd static/bpvsbuckler - npm ci - npm run build --if-present + set -euo pipefail + if [ -f "$DIRECTORY/package.json" ]; then + (cd "$DIRECTORY" && npm ci || npm install && npm run build --if-present) fi + OUT=$(wrangler pages deploy "$DIRECTORY" --project-name="$PROJECT_NAME" --branch="$BRANCH_NAME" --commit-dirty=true --commit-hash="$COMMIT_SHA" 2>&1 | tee /tmp/wrangler_bpvs.log || true) + echo "$OUT" + URL=$(echo "$OUT" | grep -Eo 'https://[a-zA-Z0-9.-]+\.pages\.dev[[:alnum:]/._-]*' | tail -n1 || true) + if [ -z "${URL:-}" ]; then echo "No preview URL found"; exit 1; fi + echo "url=$URL" >> "$GITHUB_OUTPUT" - - name: Deploy bpvsbuckler (Preview) and comment URL + - name: Deployment box: bpvsbuckler(preview) if: github.event_name == 'pull_request' && steps.changes.outputs.bpvsbuckler == 'true' - uses: andykenward/github-actions-cloudflare-pages@v3.1.0 - id: deploy_bpvsbuckler + uses: actions/github-script@v7 with: - cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} - cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - cloudflare-project-name: bpvsbuckler - directory: static/bpvsbuckler - github-token: ${{ secrets.GITHUB_TOKEN }} - github-environment: bpvsbuckler(preview) + script: | + const ref = context.payload.pull_request?.head.sha || context.sha; + const env = "bpvsbuckler(preview)"; + const url = "${{ steps.deploy_bpvsbuckler.outputs.url }}"; + const { data: dep } = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref, + environment: env, + transient_environment: true, + auto_merge: false, + required_contexts: [], + description: "Cloudflare Pages preview" + }); + await github.rest.repos.createDeploymentStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: dep.id, + state: "success", + environment: env, + environment_url: url, + log_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + auto_inactive: false + }); - - name: Done - run: echo "Conditional deploy workflow finished." diff --git a/.github/workflows/cloudflare-conditional-deploy.yml.old b/.github/workflows/cloudflare-conditional-deploy.yml.old new file mode 100644 index 000000000..8bff74bb5 --- /dev/null +++ b/.github/workflows/cloudflare-conditional-deploy.yml.old @@ -0,0 +1,256 @@ +name: Cloudflare conditional deploy + +on: + # Previews on PRs targeting gh-pages (Codex PRs) + pull_request: + types: [opened, synchronize, reopened] + branches: + - gh-pages + # Production branch pushes (no prod steps yet; safe to keep for later) + push: + branches: + - gh-pages + +jobs: + detect-and-deploy: + name: Detect changes and deploy only touched apps + runs-on: ubuntu-latest + + # Create PR comments + GitHub Deployments + permissions: + contents: read + deployments: write + pull-requests: write + + # Auto-cancel older runs for the same PR/branch + concurrency: + group: cf-pages-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Determine changed files + id: changes + shell: bash + run: | + set -euo pipefail + + echo "Determining changed files..." + if [ "${{ github.event_name }}" = "pull_request" ]; then + BASE_SHA="${{ github.event.pull_request.base.sha }}" + git fetch origin "${{ github.event.pull_request.base.ref }}" --depth=1 || true + DIFF=$(git diff --name-only "$BASE_SHA"...HEAD) || true + else + BEFORE="${{ github.event.before }}" + AFTER="${{ github.sha }}" + if [[ "$BEFORE" =~ ^0+$ ]]; then + DIFF=$(git ls-files) || true + else + DIFF=$(git diff --name-only "$BEFORE" "$AFTER") || true + fi + fi + + echo "Changed files:" + echo "$DIFF" + + # default outputs (false) + { + echo "library=false" + echo "hbnb=false" + echo "forces=false" + echo "gui=false" + echo "datro=false" + echo "projections=false" + echo "bpvsbuckler=false" + } >> "$GITHUB_OUTPUT" + + # detect changes in each app directory + if echo "$DIFF" | grep -qE '^static/library/'; then echo "library=true" >> "$GITHUB_OUTPUT"; fi + if echo "$DIFF" | grep -qE '^static/hbnb/'; then echo "hbnb=true" >> "$GITHUB_OUTPUT"; fi + if echo "$DIFF" | grep -qE '^static/forces/'; then echo "forces=true" >> "$GITHUB_OUTPUT"; fi + if echo "$DIFF" | grep -qE '^static/gui/'; then echo "gui=true" >> "$GITHUB_OUTPUT"; fi + if echo "$DIFF" | grep -qE '^static/datro/'; then echo "datro=true" >> "$GITHUB_OUTPUT"; fi + if echo "$DIFF" | grep -qE '^static/projections/'; then echo "projections=true" >> "$GITHUB_OUTPUT"; fi + if echo "$DIFF" | grep -qE '^static/bpvsbuckler/'; then echo "bpvsbuckler=true" >> "$GITHUB_OUTPUT"; fi + + - name: Debug outputs + run: | + echo "library: ${{ steps.changes.outputs.library }}" + echo "hbnb: ${{ steps.changes.outputs.hbnb }}" + echo "forces: ${{ steps.changes.outputs.forces }}" + echo "gui: ${{ steps.changes.outputs.gui }}" + echo "datro: ${{ steps.changes.outputs.datro }}" + echo "projections: ${{ steps.changes.outputs.projections }}" + echo "bpvsbuckler: ${{ steps.changes.outputs.bpvsbuckler }}" + + - name: Setup Node (for builds) + uses: actions/setup-node@v4 + with: + node-version: '22' # or '20' + + ###################################################################### + # PREVIEW DEPLOYS FOR PRs — posts a comment with the preview URL(s) + # Uses andykenward/github-actions-cloudflare-pages to create GH Deployments + # Repo Secrets required: + # CLOUDFLARE_API_TOKEN (Account:Read + Pages:Builds:Edit) + # CLOUDFLARE_ACCOUNT_ID + # + # IMPORTANT: `cloudflare-project-name` must match CF Pages project names. + # IMPORTANT: `github-environment` must match your GitHub Environments: + # library(preview), hbnb(preview), ceo(preview), gui(preview), + # datro(preview), projections(preview), bpvsbuckler(preview) + ###################################################################### + + - name: Build library + if: github.event_name == 'pull_request' && steps.changes.outputs.library == 'true' + run: | + if [ -f static/library/package.json ]; then + cd static/library + npm ci + npm run build --if-present + fi + + - name: Deploy library (Preview) and comment URL + if: github.event_name == 'pull_request' && steps.changes.outputs.library == 'true' + uses: andykenward/github-actions-cloudflare-pages@v3.1.0 + id: deploy_library + with: + cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + cloudflare-project-name: library + directory: static/library + github-token: ${{ secrets.GITHUB_TOKEN }} + github-environment: library(preview) + + - name: Build hbnb + if: github.event_name == 'pull_request' && steps.changes.outputs.hbnb == 'true' + run: | + if [ -f static/hbnb/package.json ]; then + cd static/hbnb + npm ci + npm run build --if-present + fi + + - name: Deploy hbnb (Preview) and comment URL + if: github.event_name == 'pull_request' && steps.changes.outputs.hbnb == 'true' + uses: andykenward/github-actions-cloudflare-pages@v3.1.0 + id: deploy_hbnb + with: + cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + cloudflare-project-name: hbnb + directory: static/hbnb + github-token: ${{ secrets.GITHUB_TOKEN }} + github-environment: hbnb(preview) + + - name: Build forces (ceo) + if: github.event_name == 'pull_request' && steps.changes.outputs.forces == 'true' + run: | + if [ -f static/forces/package.json ]; then + cd static/forces + npm ci + npm run build --if-present + fi + + - name: Deploy forces/ceo (Preview) and comment URL + if: github.event_name == 'pull_request' && steps.changes.outputs.forces == 'true' + uses: andykenward/github-actions-cloudflare-pages@v3.1.0 + id: deploy_forces + with: + cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + cloudflare-project-name: ceo + directory: static/forces + github-token: ${{ secrets.GITHUB_TOKEN }} + github-environment: ceo(preview) + + - name: Build gui + if: github.event_name == 'pull_request' && steps.changes.outputs.gui == 'true' + run: | + if [ -f static/gui/package.json ]; then + cd static/gui + npm ci + npm run build --if-present + fi + + - name: Deploy gui (Preview) and comment URL + if: github.event_name == 'pull_request' && steps.changes.outputs.gui == 'true' + uses: andykenward/github-actions-cloudflare-pages@v3.1.0 + id: deploy_gui + with: + cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + cloudflare-project-name: gui + directory: static/gui + github-token: ${{ secrets.GITHUB_TOKEN }} + github-environment: gui(preview) + + - name: Build datro-homepage + if: github.event_name == 'pull_request' && steps.changes.outputs.datro == 'true' + run: | + if [ -f static/datro/package.json ]; then + cd static/datro + npm ci + npm run build --if-present + fi + + - name: Deploy datro-homepage (Preview) and comment URL + if: github.event_name == 'pull_request' && steps.changes.outputs.datro == 'true' + uses: andykenward/github-actions-cloudflare-pages@v3.1.0 + id: deploy_datro + with: + cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + cloudflare-project-name: datro-homepage + directory: static/datro + github-token: ${{ secrets.GITHUB_TOKEN }} + github-environment: datro(preview) + + - name: Build projections + if: github.event_name == 'pull_request' && steps.changes.outputs.projections == 'true' + run: | + if [ -f static/projections/package.json ]; then + cd static/projections + npm ci + npm run build --if-present + fi + + - name: Deploy projections (Preview) and comment URL + if: github.event_name == 'pull_request' && steps.changes.outputs.projections == 'true' + uses: andykenward/github-actions-cloudflare-pages@v3.1.0 + id: deploy_projections + with: + cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + cloudflare-project-name: projections + directory: static/projections + github-token: ${{ secrets.GITHUB_TOKEN }} + github-environment: projections(preview) + + - name: Build bpvsbuckler + if: github.event_name == 'pull_request' && steps.changes.outputs.bpvsbuckler == 'true' + run: | + if [ -f static/bpvsbuckler/package.json ]; then + cd static/bpvsbuckler + npm ci + npm run build --if-present + fi + + - name: Deploy bpvsbuckler (Preview) and comment URL + if: github.event_name == 'pull_request' && steps.changes.outputs.bpvsbuckler == 'true' + uses: andykenward/github-actions-cloudflare-pages@v3.1.0 + id: deploy_bpvsbuckler + with: + cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + cloudflare-project-name: bpvsbuckler + directory: static/bpvsbuckler + github-token: ${{ secrets.GITHUB_TOKEN }} + github-environment: bpvsbuckler(preview) + + - name: Done + run: echo "Conditional deploy workflow finished." diff --git a/CHANGELOG.md b/CHANGELOG.md index 48793b2fa..f683f8441 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ## [0.0.1-rtw.17] - Q4/2025 -Nov-12 - Added `cloudflare-conditional-deploy.yml` to new top level directory .github/workflow `so preview links can be generated on pull requests' +Nov-12 - There's two preview links being generated in each preview request - I need 1 only - This should fix it +Nov-12 - Setup a Github Workflow so I get cloudflare build previews in my pull requests in github Nov-09 - DATRO was previously soley the tech. Now it's expanding as a security group, tech being one of multiple arms of the security group Oct-28 - Added a timeline with a nice menu /static/timeline - hoping to build this feature out diff --git a/index.html b/index.html index 2df539129..afe1e7973 100755 --- a/index.html +++ b/index.html @@ -64,11 +64,184 @@ } .v-h { position: absolute !important; - height: 1px; width: 1px; + height: 1px; width: 1px; overflow: hidden; clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ clip: rect(1px, 1px, 1px, 1px); } +#mainWrapper + .release-timeline-bar { + margin-top: 82px; +} +.release-timeline-bar { + background: rgba(20, 20, 50, 0.88); + color: #fff; + padding: 16px; + position: relative; + z-index: 9; + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35); +} +.release-timeline-inner { + display: flex; + align-items: center; + gap: 18px; + flex-wrap: nowrap; +} +.release-tier-controls { + display: flex; + align-items: center; + gap: 10px; + flex-shrink: 0; +} +.release-tier-button { + display: inline-flex; + align-items: center; + justify-content: center; + background: transparent; + border: 1px solid rgba(255, 255, 255, 0.4); + border-radius: 4px; + color: #fff; + padding: 6px 14px; + font-size: 0.85rem; + cursor: pointer; + transition: background 0.2s ease, color 0.2s ease; +} +.release-tier-button:hover, +.release-tier-button:focus { + background: rgba(255, 255, 255, 0.18); + color: #fff; + outline: none; +} +.release-tier-select { + background: rgba(14, 14, 35, 0.85); + border: 1px solid rgba(255, 255, 255, 0.35); + border-radius: 4px; + color: #fff; + padding: 6px 32px 6px 12px; + font-size: 0.85rem; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + min-width: 120px; + cursor: pointer; +} +.release-tier-select:focus { + outline: none; + box-shadow: 0 0 0 2px rgba(102, 204, 255, 0.45); +} +.release-slider-scroll { + overflow-x: auto; + scrollbar-width: thin; + padding: 8px 0 12px; + flex: 1 1 auto; + min-width: 0; +} +.release-slider-scroll::-webkit-scrollbar { + height: 6px; +} +.release-slider-scroll::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.1); +} +.release-slider-scroll::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.35); + border-radius: 10px; +} +.release-slider-track { + min-width: max(680px, calc(var(--stage-count, 1) * 140px)); + padding: 0 12px; +} +.release-stage-slider { + width: 100%; + appearance: none; + background: linear-gradient(90deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.15)); + height: 6px; + border-radius: 999px; + outline: none; + position: relative; +} +.release-stage-slider::-webkit-slider-thumb { + -webkit-appearance: none; + height: 18px; + width: 18px; + border-radius: 50%; + border: 2px solid #ffffff; + background: #66CCFF; + cursor: pointer; + margin-top: -6px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.25); + transition: transform 0.2s ease; +} +.release-stage-slider::-moz-range-thumb { + height: 18px; + width: 18px; + border-radius: 50%; + border: 2px solid #ffffff; + background: #66CCFF; + cursor: pointer; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.25); + transition: transform 0.2s ease; +} +.release-stage-slider::-webkit-slider-thumb:hover, +.release-stage-slider::-moz-range-thumb:hover { + transform: scale(1.05); +} +.release-slider-labels { + display: flex; + justify-content: space-between; + gap: 0; + margin-top: 12px; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.03em; + color: rgba(255, 255, 255, 0.75); +} +.release-slider-label { + flex: 1 1 0; + text-align: center; + min-width: 120px; + cursor: pointer; + position: relative; + white-space: nowrap; + background: none; + border: none; + color: inherit; + padding: 18px 4px 0; + font: inherit; +} +.release-slider-label::before { + content: ''; + position: absolute; + top: -14px; + left: 50%; + transform: translateX(-50%); + width: 2px; + height: 10px; + background: rgba(255, 255, 255, 0.5); +} +.release-slider-label:focus { + outline: none; + color: #ffffff; +} +.release-slider-label.is-active { + color: #66CCFF; + font-weight: 600; +} +.release-slider-label.is-active::before { + background: #66CCFF; + height: 14px; +} +.release-popup-content { + display: none; + max-width: 520px; +} +.release-popup-content h2 { + font-size: 1.4rem; + margin-bottom: 0.75rem; + color: #333366; +} +.release-popup-content p { + margin-bottom: 0.5rem; + line-height: 1.5; +} #bg-video { position: fixed; top: 0; @@ -133,11 +306,11 @@ Your browser does not support the video tag. -
- +
+
+
+
+ + + +
+
+
+ + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
@@ -439,19 +655,279 @@
- - + + - - + + + diff --git a/static/datro/documents.html b/static/datro/documents.html index 37f339f80..ba52987a6 100755 --- a/static/datro/documents.html +++ b/static/datro/documents.html @@ -182,18 +182,14 @@ DATRO Logo diff --git a/static/datro/index.html b/static/datro/index.html index d7b9d3ca5..803854886 100644 --- a/static/datro/index.html +++ b/static/datro/index.html @@ -1,6 +1,7 @@ + @@ -60,9 +61,182 @@ .add-to-cart-btn { width:120px; max-height:30px; min-height:25px; min-width:80px; padding-top:3px; font-weight:lighter;} } - .btn, a.btn {display:inline-block; background:#333366!important; color:#fff; font-size:1rem; min-height:2rem; text-align:center; border-radius:4px; padding:1px; border:1px solid white!important; min-width:90px; max-width:120px; width:30vw; margin:1px; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";} + .btn, a.btn {display:inline-block; background:#333366!important; color:#fff; font-size:1rem; min-height:2rem; text-align:center; border-radius:4px; padding:1px; border:1px solid white!important; min-width:90px; max-width:120px; width:30vw; margin:1px; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";} .glyphinbutton {} + #mainWrapper + .release-timeline-bar { + margin-top: 82px; + } + .release-timeline-bar { + background: rgba(20, 20, 50, 0.88); + color: #fff; + padding: 16px; + position: relative; + z-index: 9; + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35); + } + .release-timeline-inner { + display: flex; + align-items: center; + gap: 18px; + flex-wrap: nowrap; + } + .release-tier-controls { + display: flex; + align-items: center; + gap: 10px; + flex-shrink: 0; + } + .release-tier-button { + display: inline-flex; + align-items: center; + justify-content: center; + background: transparent; + border: 1px solid rgba(255, 255, 255, 0.4); + border-radius: 4px; + color: #fff; + padding: 6px 14px; + font-size: 0.85rem; + cursor: pointer; + transition: background 0.2s ease, color 0.2s ease; + } + .release-tier-button:hover, + .release-tier-button:focus { + background: rgba(255, 255, 255, 0.18); + color: #fff; + outline: none; + } + .release-tier-select { + background: rgba(14, 14, 35, 0.85); + border: 1px solid rgba(255, 255, 255, 0.35); + border-radius: 4px; + color: #fff; + padding: 6px 32px 6px 12px; + font-size: 0.85rem; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + min-width: 120px; + cursor: pointer; + } + .release-tier-select:focus { + outline: none; + box-shadow: 0 0 0 2px rgba(102, 204, 255, 0.45); + } + .release-slider-scroll { + overflow-x: auto; + scrollbar-width: thin; + padding: 8px 0 12px; + flex: 1 1 auto; + min-width: 0; + } + .release-slider-scroll::-webkit-scrollbar { + height: 6px; + } + .release-slider-scroll::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.1); + } + .release-slider-scroll::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.35); + border-radius: 10px; + } + .release-slider-track { + min-width: max(680px, calc(var(--stage-count, 1) * 140px)); + padding: 0 12px; + } + .release-stage-slider { + width: 100%; + appearance: none; + background: linear-gradient(90deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.15)); + height: 6px; + border-radius: 999px; + outline: none; + position: relative; + } + .release-stage-slider::-webkit-slider-thumb { + -webkit-appearance: none; + height: 18px; + width: 18px; + border-radius: 50%; + border: 2px solid #ffffff; + background: #66CCFF; + cursor: pointer; + margin-top: -6px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.25); + transition: transform 0.2s ease; + } + .release-stage-slider::-moz-range-thumb { + height: 18px; + width: 18px; + border-radius: 50%; + border: 2px solid #ffffff; + background: #66CCFF; + cursor: pointer; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.25); + transition: transform 0.2s ease; + } + .release-stage-slider::-webkit-slider-thumb:hover, + .release-stage-slider::-moz-range-thumb:hover { + transform: scale(1.05); + } + .release-slider-labels { + display: flex; + justify-content: space-between; + gap: 0; + margin-top: 12px; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.03em; + color: rgba(255, 255, 255, 0.75); + } + .release-slider-label { + flex: 1 1 0; + text-align: center; + min-width: 120px; + cursor: pointer; + position: relative; + white-space: nowrap; + background: none; + border: none; + color: inherit; + padding: 18px 4px 0; + font: inherit; + } + .release-slider-label::before { + content: ''; + position: absolute; + top: -14px; + left: 50%; + transform: translateX(-50%); + width: 2px; + height: 10px; + background: rgba(255, 255, 255, 0.5); + } + .release-slider-label:focus { + outline: none; + color: #ffffff; + } + .release-slider-label.is-active { + color: #66CCFF; + font-weight: 600; + } + .release-slider-label.is-active::before { + background: #66CCFF; + height: 14px; + } + .release-popup-content { + display: none; + max-width: 520px; + } + .release-popup-content h2 { + font-size: 1.4rem; + margin-bottom: 0.75rem; + color: #333366; + } + .release-popup-content p { + margin-bottom: 0.5rem; + line-height: 1.5; + } @media (max-width: 600px) and (min-width: 345px) and (orientation: portrait) { .video-iframe {min-height:45vw; min-width: 78vw; max-height: 48vw!important; max-width: 80vw!Important;} } @@ -117,8 +291,51 @@

Site Navigation

- -
+ +
+
+
+ + + +
+
+
+ + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+