From c8075ffe2b7de776a9dbf51b1ff6257d4fbe8c2a Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Thu, 20 Mar 2025 23:08:26 -0400 Subject: [PATCH 01/19] Add CI that builds CloudFlare Pages previews on forks CloudFlare Pages integration doesn't run on forks. But, because the forks are public, we can do custom CI that manually `git clone`s those forks, and, after checking them out, runs the CloudFlare Pages build for us, commenting back in the originating PR with a link. --- .../workflows/forks-build-on-cloudflare.yml | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 .github/workflows/forks-build-on-cloudflare.yml diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml new file mode 100644 index 0000000000..b65efd8710 --- /dev/null +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -0,0 +1,124 @@ +name: PR Deploy to Cloudflare Pages + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + deploy-pr: + name: Deploy PR to Cloudflare Pages + runs-on: ubuntu-latest + + # Only run on PRs from forks + # GitHub context: https://docs.github.com/en/actions/learn-github-actions/contexts#github-context + if: github.event.pull_request.head.repo.full_name != github.repository + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required for Cloudflare Pages to have complete Git history + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build project + run: npm run build + + - name: Get current timestamp + id: timestamp + run: echo "timestamp=$(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_OUTPUT + + - name: Deploy to Cloudflare Pages + id: cloudflare-deployment + uses: cloudflare/pages-action@v1 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }} + directory: ./dist # Change this to your build output directory (e.g., build, dist, out, etc.) + gitHubToken: ${{ secrets.GITHUB_TOKEN }} + branch: ${{ github.head_ref || github.ref_name }} + # Enable preview deployments for PRs + wranglerVersion: '3.0.0' + + - name: Check for existing comment + id: find-comment + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = context.issue.number; + const commentIdentifier = '🚀 PR Preview deployed successfully!'; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes(commentIdentifier) + ); + + if (botComment) { + return { + comment_id: botComment.id, + exists: true + }; + } + + return { + exists: false, + comment_id: null + }; + + - name: Create or update comment + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const deploymentUrl = '${{ steps.cloudflare-deployment.outputs.url }}'; + const buildTimestamp = '${{ steps.timestamp.outputs.timestamp }}'; + const prNumber = context.issue.number; + const commentExists = ${{ steps.find-comment.outputs.result.exists }}; + const commentId = ${{ steps.find-comment.outputs.result.comment_id }}; + + const shortSHA = context.payload.pull_request.head.sha.substring(0, 7); + + const commentBody = `🚀 PR Preview deployed successfully!\n\n` + + `📝 Preview URL: ${deploymentUrl}\n\n` + + `✅ Last built: ${buildTimestamp}\n` + + `📌 Commit: ${shortSHA}\n\n` + + `This preview was automatically generated from fork PR \`${{ github.event.pull_request.head.label }}\`.`; + + if (commentExists) { + github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: commentId, + body: commentBody + }); + + console.log(`Updated existing comment ID ${commentId} with new deployment info`); + } else { + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: commentBody + }); + + console.log(`Posted new comment with deployment URL to PR #${prNumber}`); + } + + if (!deploymentUrl) { + core.setFailed('Failed to get deployment URL from Cloudflare Pages action'); + } From 50dc04e25aab558f725bbc9a7cc4cd848f8a1863 Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Thu, 20 Mar 2025 23:13:24 -0400 Subject: [PATCH 02/19] Linkback to logs, error handling --- .../workflows/forks-build-on-cloudflare.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml index b65efd8710..361b2e99c3 100644 --- a/.github/workflows/forks-build-on-cloudflare.yml +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -38,6 +38,7 @@ jobs: - name: Deploy to Cloudflare Pages id: cloudflare-deployment uses: cloudflare/pages-action@v1 + continue-on-error: true # Continue workflow even if deployment fails with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} @@ -48,6 +49,17 @@ jobs: # Enable preview deployments for PRs wranglerVersion: '3.0.0' + - name: Check Deployment Status + id: check-status + run: | + if [ "${{ steps.cloudflare-deployment.outcome }}" == "success" ]; then + echo "status=success" >> $GITHUB_OUTPUT + echo "message=Deployment completed successfully" >> $GITHUB_OUTPUT + else + echo "status=failure" >> $GITHUB_OUTPUT + echo "message=Deployment to Cloudflare Pages failed" >> $GITHUB_OUTPUT + fi + - name: Check for existing comment id: find-comment uses: actions/github-script@v7 @@ -93,10 +105,14 @@ jobs: const shortSHA = context.payload.pull_request.head.sha.substring(0, 7); + const runId = context.runId; + const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; + const commentBody = `🚀 PR Preview deployed successfully!\n\n` + `📝 Preview URL: ${deploymentUrl}\n\n` + `✅ Last built: ${buildTimestamp}\n` + - `📌 Commit: ${shortSHA}\n\n` + + `📌 Commit: ${shortSHA}\n` + + `🔍 [View build details](${runUrl})\n\n` + `This preview was automatically generated from fork PR \`${{ github.event.pull_request.head.label }}\`.`; if (commentExists) { From e96330802836fb028c9a2ec6ff7ac87eb07ecc4c Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Thu, 20 Mar 2025 23:33:13 -0400 Subject: [PATCH 03/19] Update forks-build-on-cloudflare.yml --- .../workflows/forks-build-on-cloudflare.yml | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml index 361b2e99c3..af9075ae0b 100644 --- a/.github/workflows/forks-build-on-cloudflare.yml +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -23,13 +23,45 @@ jobs: uses: actions/setup-node@v4 with: node-version: '20' - cache: 'npm' + # Remove the cache option since we don't have a lock file + # cache: 'npm' + + - name: Check for package.json + id: check-package + run: | + if [ -f "package.json" ]; then + echo "exists=true" >> $GITHUB_OUTPUT + else + echo "exists=false" >> $GITHUB_OUTPUT + fi - name: Install dependencies - run: npm ci + if: steps.check-package.outputs.exists == 'true' + run: | + if [ -f "package-lock.json" ] || [ -f "npm-shrinkwrap.json" ]; then + npm ci + elif [ -f "yarn.lock" ]; then + yarn install --frozen-lockfile + else + npm install + fi - name: Build project - run: npm run build + if: steps.check-package.outputs.exists == 'true' + run: npm run build || yarn build || echo "No build script found, continuing workflow" + + # Fallback for projects without Node.js build process + - name: Check for common static directories + id: check-static + if: steps.check-package.outputs.exists == 'false' + run: | + for dir in "public" "static" "dist" "build" "out" "_site" "site"; do + if [ -d "$dir" ]; then + echo "static_dir=$dir" >> $GITHUB_OUTPUT + echo "Found static directory: $dir" + break + fi + done - name: Get current timestamp id: timestamp @@ -43,7 +75,7 @@ jobs: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }} - directory: ./dist # Change this to your build output directory (e.g., build, dist, out, etc.) + directory: ${{ steps.check-static.outputs.static_dir || (steps.check-package.outputs.exists == 'true' && './dist') || '.' }} # Use detected static dir, build output, or repo root gitHubToken: ${{ secrets.GITHUB_TOKEN }} branch: ${{ github.head_ref || github.ref_name }} # Enable preview deployments for PRs From c769f16684743de55ec84aff261c2ba2eb62e2fb Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Thu, 20 Mar 2025 23:34:09 -0400 Subject: [PATCH 04/19] Update quickstart.md --- content/guides/quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/quickstart.md b/content/guides/quickstart.md index e974e6f0c7..e023ed598c 100644 --- a/content/guides/quickstart.md +++ b/content/guides/quickstart.md @@ -125,4 +125,4 @@ Explore more features of the W&B ecosystem: 3. Create [W&B Artifacts]({{< relref "/guides/core/artifacts/" >}}) to track datasets, models, dependencies, and results throughout your machine learning pipeline. 4. Automate hyperparameter searches and explore models with [W&B Sweeps]({{< relref "/guides/models/sweeps/" >}}). 5. Analyze datasets, visualize model predictions, and share insights on a [central dashboard]({{< relref "/guides/models/tables/" >}}). -6. Access W&B AI Academy to learn about LLMs, MLOps, and W&B Models through hands-on [courses](https://wandb.me/courses). \ No newline at end of file +6. Visit W&B AI Academy to learn about LLMs, MLOps, and W&B Models through hands-on [courses](https://wandb.me/courses). From 9f565025c1cf884582c7628f6436d1caecd71e22 Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Thu, 20 Mar 2025 23:38:16 -0400 Subject: [PATCH 05/19] Update forks-build-on-cloudflare.yml From 38e94e9408a1a4551fd8bf5ff4639f549be7d153 Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Thu, 20 Mar 2025 23:58:38 -0400 Subject: [PATCH 06/19] Update forks-build-on-cloudflare.yml --- .github/workflows/forks-build-on-cloudflare.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml index af9075ae0b..7a884715d3 100644 --- a/.github/workflows/forks-build-on-cloudflare.yml +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -1,7 +1,7 @@ name: PR Deploy to Cloudflare Pages on: - pull_request: + pull_request_target: # Use pull_request_target instead of pull_request for fork security types: [opened, synchronize, reopened] jobs: @@ -14,9 +14,11 @@ jobs: if: github.event.pull_request.head.repo.full_name != github.repository steps: + # Note: When using pull_request_target, this step is important for security - name: Checkout repository uses: actions/checkout@v4 with: + ref: ${{ github.event.pull_request.head.sha }} # Checkout the PR's head fetch-depth: 0 # Required for Cloudflare Pages to have complete Git history - name: Setup Node.js From 4af060965032403ca2ab722720d7047672f3f0eb Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Fri, 21 Mar 2025 00:06:25 -0400 Subject: [PATCH 07/19] Update forks-build-on-cloudflare.yml --- .../workflows/forks-build-on-cloudflare.yml | 116 +++++++++++++----- 1 file changed, 87 insertions(+), 29 deletions(-) diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml index 7a884715d3..0f7560c60e 100644 --- a/.github/workflows/forks-build-on-cloudflare.yml +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -28,42 +28,100 @@ jobs: # Remove the cache option since we don't have a lock file # cache: 'npm' - - name: Check for package.json - id: check-package + # Check if this is a Hugo site + - name: Check for Hugo + id: check-hugo run: | - if [ -f "package.json" ]; then - echo "exists=true" >> $GITHUB_OUTPUT + if [ -f "config.toml" ] || [ -f "config.yaml" ] || [ -f "config.json" ] || [ -d "themes" ]; then + echo "is_hugo=true" >> $GITHUB_OUTPUT + echo "✅ Detected Hugo site" else - echo "exists=false" >> $GITHUB_OUTPUT + echo "is_hugo=false" >> $GITHUB_OUTPUT + echo "⚠️ Not a Hugo site" fi - - name: Install dependencies - if: steps.check-package.outputs.exists == 'true' + # Install Hugo if needed + - name: Setup Hugo + if: steps.check-hugo.outputs.is_hugo == 'true' + uses: peaceiris/actions-hugo@v2 + with: + hugo-version: 'latest' + extended: true + + # Build Hugo site + - name: Build with Hugo + if: steps.check-hugo.outputs.is_hugo == 'true' + run: hugo --minify + + # Check build output + - name: Verify Hugo output + if: steps.check-hugo.outputs.is_hugo == 'true' run: | - if [ -f "package-lock.json" ] || [ -f "npm-shrinkwrap.json" ]; then - npm ci - elif [ -f "yarn.lock" ]; then - yarn install --frozen-lockfile + if [ -d "public" ]; then + echo "✅ Hugo generated 'public' directory" + echo "Contents of public directory:" + ls -la public else - npm install + echo "❌ Hugo did not generate 'public' directory" fi - - name: Build project - if: steps.check-package.outputs.exists == 'true' - run: npm run build || yarn build || echo "No build script found, continuing workflow" - # Fallback for projects without Node.js build process - - name: Check for common static directories - id: check-static - if: steps.check-package.outputs.exists == 'false' + # Debug directory structure + - name: Debug directory structure run: | - for dir in "public" "static" "dist" "build" "out" "_site" "site"; do - if [ -d "$dir" ]; then - echo "static_dir=$dir" >> $GITHUB_OUTPUT - echo "Found static directory: $dir" - break - fi - done + echo "Current directory: $(pwd)" + echo "Directory contents:" + ls -la + echo "--------------------" + + if [ -d "public" ]; then + echo "✅ Found 'public' directory (Hugo output)" + echo "deploy_dir=public" >> $GITHUB_OUTPUT + elif [ -d "dist" ]; then + echo "✅ Found 'dist' directory" + echo "deploy_dir=dist" >> $GITHUB_OUTPUT + elif [ -d "build" ]; then + echo "✅ Found 'build' directory" + echo "deploy_dir=build" >> $GITHUB_OUTPUT + elif [ -d "_site" ]; then + echo "✅ Found '_site' directory" + echo "deploy_dir=_site" >> $GITHUB_OUTPUT + else + # Create a directory with a simple index.html if none exists + mkdir -p _preview + echo "

PR Preview

This is a preview of PR #${{ github.event.pull_request.number }} from ${{ github.event.pull_request.head.label }}

" > _preview/index.html + echo "deploy_dir=_preview" >> $GITHUB_OUTPUT + echo "Created fallback _preview directory with placeholder content" + fi + + # Create a directory if none exists + - name: Ensure deploy directory exists + id: ensure-dir + run: | + # Check if any common directories exist + if [ -d "dist" ]; then + echo "deploy_dir=dist" >> $GITHUB_OUTPUT + elif [ -d "build" ]; then + echo "deploy_dir=build" >> $GITHUB_OUTPUT + elif [ -d "public" ]; then + echo "deploy_dir=public" >> $GITHUB_OUTPUT + elif [ -d "_site" ]; then + echo "deploy_dir=_site" >> $GITHUB_OUTPUT + elif [ -d "out" ]; then + echo "deploy_dir=out" >> $GITHUB_OUTPUT + elif [ -d "static" ]; then + echo "deploy_dir=static" >> $GITHUB_OUTPUT + elif [ -d "site" ]; then + echo "deploy_dir=site" >> $GITHUB_OUTPUT + else + # Create a directory with a simple index.html if none exists + mkdir -p _preview + echo "

PR Preview

This is a preview of PR #${{ github.event.pull_request.number }} from ${{ github.event.pull_request.head.label }}

" > _preview/index.html + echo "deploy_dir=_preview" >> $GITHUB_OUTPUT + echo "Created fallback _preview directory with placeholder content" + fi + + echo "Will deploy from directory: $(cat $GITHUB_OUTPUT | cut -d= -f2)" - name: Get current timestamp id: timestamp @@ -77,11 +135,11 @@ jobs: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }} - directory: ${{ steps.check-static.outputs.static_dir || (steps.check-package.outputs.exists == 'true' && './dist') || '.' }} # Use detected static dir, build output, or repo root + directory: ${{ steps.ensure-dir.outputs.deploy_dir }} gitHubToken: ${{ secrets.GITHUB_TOKEN }} branch: ${{ github.head_ref || github.ref_name }} - # Enable preview deployments for PRs - wranglerVersion: '3.0.0' + # Use latest version instead of specific version to avoid deprecation warnings + wranglerVersion: 'latest' - name: Check Deployment Status id: check-status From 6c2074da721c36dd9fa846d65307ecda2f19fd2f Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Fri, 21 Mar 2025 00:09:49 -0400 Subject: [PATCH 08/19] Update forks-build-on-cloudflare.yml --- .github/workflows/forks-build-on-cloudflare.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml index 0f7560c60e..9f5eb9f87b 100644 --- a/.github/workflows/forks-build-on-cloudflare.yml +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -129,17 +129,19 @@ jobs: - name: Deploy to Cloudflare Pages id: cloudflare-deployment - uses: cloudflare/pages-action@v1 + uses: cloudflare/pages-action@v1.5.0 continue-on-error: true # Continue workflow even if deployment fails with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }} - directory: ${{ steps.ensure-dir.outputs.deploy_dir }} + directory: ${{ steps.deploy-dir.outputs.dir }} gitHubToken: ${{ secrets.GITHUB_TOKEN }} branch: ${{ github.head_ref || github.ref_name }} - # Use latest version instead of specific version to avoid deprecation warnings + # Specify a newer wrangler version wranglerVersion: 'latest' + # Use new deploy command + command: deploy - name: Check Deployment Status id: check-status From 74974b79e59f71c3c4d1e7e0f6b0b6ca4394307f Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Fri, 21 Mar 2025 00:13:22 -0400 Subject: [PATCH 09/19] Update forks-build-on-cloudflare.yml --- .github/workflows/forks-build-on-cloudflare.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml index 9f5eb9f87b..9c30011f3b 100644 --- a/.github/workflows/forks-build-on-cloudflare.yml +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -145,8 +145,9 @@ jobs: - name: Check Deployment Status id: check-status + if: always() # Run this step even if previous steps failed run: | - if [ "${{ steps.cloudflare-deployment.outcome }}" == "success" ]; then + if [ -n "${{ steps.cloudflare-deployment.outputs.url }}" ]; then echo "status=success" >> $GITHUB_OUTPUT echo "message=Deployment completed successfully" >> $GITHUB_OUTPUT else From a512bad723b087f77a4db24d81629b4819b4f712 Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Fri, 21 Mar 2025 00:17:00 -0400 Subject: [PATCH 10/19] Update forks-build-on-cloudflare.yml --- .github/workflows/forks-build-on-cloudflare.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml index 9c30011f3b..5b58fe0e91 100644 --- a/.github/workflows/forks-build-on-cloudflare.yml +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -32,7 +32,7 @@ jobs: - name: Check for Hugo id: check-hugo run: | - if [ -f "config.toml" ] || [ -f "config.yaml" ] || [ -f "config.json" ] || [ -d "themes" ]; then + if [ -f "config.toml" ] || [ -f "config.yaml" ] || [ -f "config.json" ] || [ -f "hugo.yaml" ] || [ -f "hugo.toml" ] || [ -f "hugo.json" ] || [ -d "themes" ]; then echo "is_hugo=true" >> $GITHUB_OUTPUT echo "✅ Detected Hugo site" else From 138e950ba740fedb38cb27268bb18ec327f6916c Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Fri, 21 Mar 2025 00:19:49 -0400 Subject: [PATCH 11/19] Update forks-build-on-cloudflare.yml --- .github/workflows/forks-build-on-cloudflare.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml index 5b58fe0e91..80d66160aa 100644 --- a/.github/workflows/forks-build-on-cloudflare.yml +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -40,6 +40,20 @@ jobs: echo "⚠️ Not a Hugo site" fi + # Setup Node.js for PostCSS + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + # Install PostCSS and other dependencies for Hugo + - name: Install PostCSS and dependencies + if: steps.check-hugo.outputs.is_hugo == 'true' + run: | + npm install -g postcss postcss-cli autoprefixer + echo "Installed PostCSS and dependencies:" + postcss --version + # Install Hugo if needed - name: Setup Hugo if: steps.check-hugo.outputs.is_hugo == 'true' From 5705feb6203ec888b3a1a1762d147fde6680b719 Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Fri, 21 Mar 2025 00:24:08 -0400 Subject: [PATCH 12/19] Update forks-build-on-cloudflare.yml --- .github/workflows/forks-build-on-cloudflare.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml index 80d66160aa..03645ca6f4 100644 --- a/.github/workflows/forks-build-on-cloudflare.yml +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -50,9 +50,24 @@ jobs: - name: Install PostCSS and dependencies if: steps.check-hugo.outputs.is_hugo == 'true' run: | - npm install -g postcss postcss-cli autoprefixer + # Create package.json if it doesn't exist + if [ ! -f "package.json" ]; then + echo '{"name":"hugo-build","version":"1.0.0","private":true}' > package.json + fi + + # Install both globally and locally to ensure modules are found + npm install -g postcss postcss-cli + npm install --save-dev autoprefixer postcss postcss-cli + + # Create postcss.config.js if it doesn't exist + if [ ! -f "postcss.config.js" ]; then + echo 'module.exports = {plugins: {autoprefixer: {}}}' > postcss.config.js + fi + echo "Installed PostCSS and dependencies:" postcss --version + echo "Node modules directory:" + ls -la node_modules # Install Hugo if needed - name: Setup Hugo From 597131e431def3100f49c01656c8aca56838f5cb Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Fri, 21 Mar 2025 00:26:30 -0400 Subject: [PATCH 13/19] Update forks-build-on-cloudflare.yml --- .github/workflows/forks-build-on-cloudflare.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml index 03645ca6f4..3c80883275 100644 --- a/.github/workflows/forks-build-on-cloudflare.yml +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -164,13 +164,11 @@ jobs: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }} - directory: ${{ steps.deploy-dir.outputs.dir }} + directory: ${{ steps.deploy-dir.outputs.dir || 'public' }} gitHubToken: ${{ secrets.GITHUB_TOKEN }} branch: ${{ github.head_ref || github.ref_name }} # Specify a newer wrangler version wranglerVersion: 'latest' - # Use new deploy command - command: deploy - name: Check Deployment Status id: check-status From 7df7aea3cbdc013735e0081c392bd5fa58a3c892 Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Fri, 21 Mar 2025 00:32:07 -0400 Subject: [PATCH 14/19] Update forks-build-on-cloudflare.yml From a16db3b4927f25b6dd71260dcb69f7052cc1121b Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Fri, 21 Mar 2025 00:39:37 -0400 Subject: [PATCH 15/19] Update forks-build-on-cloudflare.yml From 3d86f5d3d75a17ee553385d8bcbde045c145c1e8 Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Fri, 21 Mar 2025 00:45:59 -0400 Subject: [PATCH 16/19] Update forks-build-on-cloudflare.yml --- .../workflows/forks-build-on-cloudflare.yml | 218 ++++++++---------- 1 file changed, 92 insertions(+), 126 deletions(-) diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml index 3c80883275..84b4159d77 100644 --- a/.github/workflows/forks-build-on-cloudflare.yml +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -14,20 +14,19 @@ jobs: if: github.event.pull_request.head.repo.full_name != github.repository steps: - # Note: When using pull_request_target, this step is important for security + # Note: When using pull_request_target, this step is important for security - name: Checkout repository uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} # Checkout the PR's head fetch-depth: 0 # Required for Cloudflare Pages to have complete Git history + # Setup Node.js for PostCSS - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - # Remove the cache option since we don't have a lock file - # cache: 'npm' - + # Check if this is a Hugo site - name: Check for Hugo id: check-hugo @@ -40,12 +39,6 @@ jobs: echo "⚠️ Not a Hugo site" fi - # Setup Node.js for PostCSS - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - # Install PostCSS and other dependencies for Hugo - name: Install PostCSS and dependencies if: steps.check-hugo.outputs.is_hugo == 'true' @@ -94,7 +87,6 @@ jobs: echo "❌ Hugo did not generate 'public' directory" fi - # Fallback for projects without Node.js build process # Debug directory structure - name: Debug directory structure run: | @@ -123,140 +115,114 @@ jobs: echo "Created fallback _preview directory with placeholder content" fi - # Create a directory if none exists - - name: Ensure deploy directory exists - id: ensure-dir + # Set directory for deployment + - name: Set deployment directory + id: deploy-dir run: | - # Check if any common directories exist - if [ -d "dist" ]; then - echo "deploy_dir=dist" >> $GITHUB_OUTPUT + if [ -d "public" ]; then + echo "dir=public" >> $GITHUB_OUTPUT + elif [ -d "dist" ]; then + echo "dir=dist" >> $GITHUB_OUTPUT elif [ -d "build" ]; then - echo "deploy_dir=build" >> $GITHUB_OUTPUT - elif [ -d "public" ]; then - echo "deploy_dir=public" >> $GITHUB_OUTPUT + echo "dir=build" >> $GITHUB_OUTPUT elif [ -d "_site" ]; then - echo "deploy_dir=_site" >> $GITHUB_OUTPUT - elif [ -d "out" ]; then - echo "deploy_dir=out" >> $GITHUB_OUTPUT - elif [ -d "static" ]; then - echo "deploy_dir=static" >> $GITHUB_OUTPUT - elif [ -d "site" ]; then - echo "deploy_dir=site" >> $GITHUB_OUTPUT + echo "dir=_site" >> $GITHUB_OUTPUT else # Create a directory with a simple index.html if none exists mkdir -p _preview echo "

PR Preview

This is a preview of PR #${{ github.event.pull_request.number }} from ${{ github.event.pull_request.head.label }}

" > _preview/index.html - echo "deploy_dir=_preview" >> $GITHUB_OUTPUT + echo "dir=_preview" >> $GITHUB_OUTPUT echo "Created fallback _preview directory with placeholder content" fi - echo "Will deploy from directory: $(cat $GITHUB_OUTPUT | cut -d= -f2)" + echo "Will deploy from directory: $(cat $GITHUB_OUTPUT | grep dir | cut -d= -f2)" + # Get timestamp for build - name: Get current timestamp id: timestamp run: echo "timestamp=$(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_OUTPUT + # Deploy to Cloudflare Pages using wrangler directly - name: Deploy to Cloudflare Pages id: cloudflare-deployment - uses: cloudflare/pages-action@v1.5.0 continue-on-error: true # Continue workflow even if deployment fails - with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }} - directory: ${{ steps.deploy-dir.outputs.dir || 'public' }} - gitHubToken: ${{ secrets.GITHUB_TOKEN }} - branch: ${{ github.head_ref || github.ref_name }} - # Specify a newer wrangler version - wranglerVersion: 'latest' + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: | + # Install Wrangler + npm install -g wrangler + + # Deploy to Cloudflare Pages using the newer 'deploy' command + OUTPUT=$(wrangler pages deploy "${{ steps.deploy-dir.outputs.dir || 'public' }}" \ + --project-name="${{ secrets.CLOUDFLARE_PROJECT_NAME }}" \ + --branch="${{ github.head_ref || github.ref_name }}" \ + --commit-hash="${{ github.event.pull_request.head.sha }}" \ + --commit-message="${{ github.event.pull_request.title }}" \ + --commit-dirty=true) + + # Extract the deployment URL from the output + DEPLOYMENT_URL=$(echo "$OUTPUT" | grep -o 'https://[^ ]*' | head -1) + + # Print the output and URL for debugging + echo "$OUTPUT" + echo "Extracted URL: $DEPLOYMENT_URL" + + # Set the URL as an output + echo "url=$DEPLOYMENT_URL" >> $GITHUB_OUTPUT - - name: Check Deployment Status - id: check-status - if: always() # Run this step even if previous steps failed + # Create or update PR comment with deployment info using GitHub CLI + - name: Create or update comment with gh CLI + if: always() + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + DEPLOYMENT_URL: ${{ steps.cloudflare-deployment.outputs.url }} + TIMESTAMP: ${{ steps.timestamp.outputs.timestamp }} + SHORT_SHA: ${{ github.event.pull_request.head.sha }} + RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + PR_LABEL: ${{ github.event.pull_request.head.label }} run: | - if [ -n "${{ steps.cloudflare-deployment.outputs.url }}" ]; then - echo "status=success" >> $GITHUB_OUTPUT - echo "message=Deployment completed successfully" >> $GITHUB_OUTPUT + # Determine comment content based on deployment success + if [ -n "$DEPLOYMENT_URL" ]; then + TITLE="🚀 PR Preview deployed successfully!" + URL_LINE="📝 Preview URL: $DEPLOYMENT_URL" else - echo "status=failure" >> $GITHUB_OUTPUT - echo "message=Deployment to Cloudflare Pages failed" >> $GITHUB_OUTPUT + TITLE="❌ PR Preview deployment failed!" + URL_LINE="⚠️ The build for Cloudflare Pages failed to complete." fi - - - name: Check for existing comment - id: find-comment - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const prNumber = context.issue.number; - const commentIdentifier = '🚀 PR Preview deployed successfully!'; - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber - }); - - const botComment = comments.find(comment => - comment.user.type === 'Bot' && - comment.body.includes(commentIdentifier) - ); - - if (botComment) { - return { - comment_id: botComment.id, - exists: true - }; - } - - return { - exists: false, - comment_id: null - }; - - - name: Create or update comment - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const deploymentUrl = '${{ steps.cloudflare-deployment.outputs.url }}'; - const buildTimestamp = '${{ steps.timestamp.outputs.timestamp }}'; - const prNumber = context.issue.number; - const commentExists = ${{ steps.find-comment.outputs.result.exists }}; - const commentId = ${{ steps.find-comment.outputs.result.comment_id }}; - - const shortSHA = context.payload.pull_request.head.sha.substring(0, 7); - - const runId = context.runId; - const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; - - const commentBody = `🚀 PR Preview deployed successfully!\n\n` + - `📝 Preview URL: ${deploymentUrl}\n\n` + - `✅ Last built: ${buildTimestamp}\n` + - `📌 Commit: ${shortSHA}\n` + - `🔍 [View build details](${runUrl})\n\n` + - `This preview was automatically generated from fork PR \`${{ github.event.pull_request.head.label }}\`.`; - - if (commentExists) { - github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: commentId, - body: commentBody - }); - - console.log(`Updated existing comment ID ${commentId} with new deployment info`); - } else { - github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body: commentBody - }); - - console.log(`Posted new comment with deployment URL to PR #${prNumber}`); - } - - if (!deploymentUrl) { - core.setFailed('Failed to get deployment URL from Cloudflare Pages action'); - } + + # Create a temporary file for the comment body + COMMENT_FILE=$(mktemp) + cat > "$COMMENT_FILE" << EOF + $TITLE + + $URL_LINE + + ✅ Last built: $TIMESTAMP + 📌 Commit: ${SHORT_SHA:0:7} + 🔍 [View build details]($RUN_URL) + + This preview was automatically generated from fork PR \`$PR_LABEL\`. + EOF + + # List comments to find any existing deployment comment + COMMENTS_FILE=$(mktemp) + gh api repos/$REPO/issues/$PR_NUMBER/comments > "$COMMENTS_FILE" + + # Search for an existing bot comment that mentions PR Preview + COMMENT_ID=$(jq -r '.[] | select(.user.login=="github-actions[bot]" and .body | contains("PR Preview")) | .id' "$COMMENTS_FILE") + + if [ -n "$COMMENT_ID" ]; then + # Update existing comment + echo "Updating existing comment ID: $COMMENT_ID" + gh api --method PATCH repos/$REPO/issues/comments/$COMMENT_ID -f body="$(cat $COMMENT_FILE)" + else + # Create new comment + echo "Creating new comment" + gh api repos/$REPO/issues/$PR_NUMBER/comments -f body="$(cat $COMMENT_FILE)" + fi + + # Clean up temp files + rm -f "$COMMENT_FILE" "$COMMENTS_FILE" From e16de07773506f10af4154c51ba45086e010255b Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Fri, 21 Mar 2025 00:53:27 -0400 Subject: [PATCH 17/19] Update forks-build-on-cloudflare.yml --- .github/workflows/forks-build-on-cloudflare.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/forks-build-on-cloudflare.yml b/.github/workflows/forks-build-on-cloudflare.yml index 84b4159d77..63bafa1093 100644 --- a/.github/workflows/forks-build-on-cloudflare.yml +++ b/.github/workflows/forks-build-on-cloudflare.yml @@ -212,7 +212,7 @@ jobs: gh api repos/$REPO/issues/$PR_NUMBER/comments > "$COMMENTS_FILE" # Search for an existing bot comment that mentions PR Preview - COMMENT_ID=$(jq -r '.[] | select(.user.login=="github-actions[bot]" and .body | contains("PR Preview")) | .id' "$COMMENTS_FILE") + COMMENT_ID=$(jq -r '.[] | select(.user.login=="github-actions[bot]" and (.body | contains("PR Preview"))) | .id' "$COMMENTS_FILE") if [ -n "$COMMENT_ID" ]; then # Update existing comment From e0a009dfea1ff1574cc2cddf2f5c7ad56d2a293a Mon Sep 17 00:00:00 2001 From: johndmulhausen Date: Fri, 21 Mar 2025 14:01:53 -0400 Subject: [PATCH 18/19] revert quickstart.md to main --- content/guides/quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/quickstart.md b/content/guides/quickstart.md index e023ed598c..e974e6f0c7 100644 --- a/content/guides/quickstart.md +++ b/content/guides/quickstart.md @@ -125,4 +125,4 @@ Explore more features of the W&B ecosystem: 3. Create [W&B Artifacts]({{< relref "/guides/core/artifacts/" >}}) to track datasets, models, dependencies, and results throughout your machine learning pipeline. 4. Automate hyperparameter searches and explore models with [W&B Sweeps]({{< relref "/guides/models/sweeps/" >}}). 5. Analyze datasets, visualize model predictions, and share insights on a [central dashboard]({{< relref "/guides/models/tables/" >}}). -6. Visit W&B AI Academy to learn about LLMs, MLOps, and W&B Models through hands-on [courses](https://wandb.me/courses). +6. Access W&B AI Academy to learn about LLMs, MLOps, and W&B Models through hands-on [courses](https://wandb.me/courses). \ No newline at end of file From 26b08ca2fbeb35a307c210347ecdf5a4645d889f Mon Sep 17 00:00:00 2001 From: John Mulhausen Date: Fri, 21 Mar 2025 14:55:37 -0400 Subject: [PATCH 19/19] Minor improvements to Quickstart (#1206) Mission 1 of this PR: To improve some things that are currently a bit awkward in the quickstart. Mission 2: Testing new functionality that deploys CloudFlare preview builds on PRs created from forks. (See the GitHub Actions comment/CloudFlare links below) Mission 2 is why this PR targets the `forks-build-on-cloudflare` branch (so you can see the CI in action), and is also why the correct merge order is: this PR first, then #1205 after to finally publish into main. --- content/guides/quickstart.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/content/guides/quickstart.md b/content/guides/quickstart.md index e974e6f0c7..ab4908cf4f 100644 --- a/content/guides/quickstart.md +++ b/content/guides/quickstart.md @@ -16,8 +16,6 @@ To authenticate your machine with W&B, generate an API key from your user profil ## Install the `wandb` library and log in -Install the `wandb` library and log in by following these steps: - {{< tabpane text=true >}} {{% tab header="Command Line" value="cli" %}} @@ -61,7 +59,7 @@ wandb.login() ## Start a run and track hyperparameters -In your Python script or notebook, initialize a W&B run object with [`wandb.init()`]({{< relref "/ref/python/run.md" >}}). Use a dictionary for the `config` parameter to specify hyperparameter names and values: +In your Python script or notebook, initialize a W&B run object with [`wandb.init()`]({{< relref "/ref/python/run.md" >}}). Use a dictionary for the `config` parameter to specify hyperparameter names and values. ```python run = wandb.init( @@ -73,7 +71,7 @@ run = wandb.init( ) ``` -A [run]({{< relref "/guides/models/track/runs/" >}}) serves as the core element of W&B, used to [track metrics]({{< relref "/guides/models/track/" >}}), [create logs]({{< relref "/guides/core/artifacts/" >}}), and more. +A [run]({{< relref "/guides/models/track/runs/" >}}) serves as the core element of W&B, used to [track metrics]({{< relref "/guides/models/track/" >}}), [create logs]({{< relref "/guides/models/track/log/" >}}), and more. ## Assemble the components @@ -110,19 +108,17 @@ for epoch in range(2, epochs): # run.log_code() ``` -Visit the W&B App at [wandb.ai/home](https://wandb.ai/home) to view recorded metrics such as accuracy and loss during each training step. +Visit [wandb.ai/home](https://wandb.ai/home) to view recorded metrics such as accuracy and loss and how they changed during each training step. The following image shows the loss and accuracy tracked from each run. Each run object appears in the **Runs** column with generated names. {{< img src="/images/quickstart/quickstart_image.png" alt="Shows loss and accuracy tracked from each run." >}} -The preceding image shows the loss and accuracy tracked from each run. Each run object appears in the **Runs** column with generated names. - ## Next steps Explore more features of the W&B ecosystem: -1. Review [W&B Integrations]({{< relref "guides/integrations/" >}}) to combine W&B with ML frameworks like PyTorch, ML libraries like Hugging Face, or services like SageMaker. +1. Read the [W&B Integration tutorials]({{< relref "guides/integrations/" >}}) that combine W&B with frameworks like PyTorch, libraries like Hugging Face, and services like SageMaker. 2. Organize runs, automate visualizations, summarize findings, and share updates with collaborators using [W&B Reports]({{< relref "/guides/core/reports/" >}}). 3. Create [W&B Artifacts]({{< relref "/guides/core/artifacts/" >}}) to track datasets, models, dependencies, and results throughout your machine learning pipeline. -4. Automate hyperparameter searches and explore models with [W&B Sweeps]({{< relref "/guides/models/sweeps/" >}}). -5. Analyze datasets, visualize model predictions, and share insights on a [central dashboard]({{< relref "/guides/models/tables/" >}}). -6. Access W&B AI Academy to learn about LLMs, MLOps, and W&B Models through hands-on [courses](https://wandb.me/courses). \ No newline at end of file +4. Automate hyperparameter searches and optimize models with [W&B Sweeps]({{< relref "/guides/models/sweeps/" >}}). +5. Analyze runs, visualize model predictions, and share insights on a [central dashboard]({{< relref "/guides/models/tables/" >}}). +6. Visit [W&B AI Academy](https://wandb.ai/site/courses/) to learn about LLMs, MLOps, and W&B Models through hands-on courses.