@@ -2,14 +2,16 @@ name: Build and Netlify Deploy
22
33on :
44 pull_request :
5+ types : [opened, synchronize, reopened]
6+ push :
57 branches :
6- - main
7- paths :
8- - book/**
9- - requirements.txt
10- - .github/workflows/deploy.yml
11- - .github/workflows/ci_pr.yml
8+ - theme_updates
129 workflow_dispatch :
10+ inputs :
11+ preview_page :
12+ description : ' Specific page to preview (e.g., intro.html)'
13+ required : false
14+ type : string
1315
1416concurrency :
1517 group : ${{ github.workflow }}-${{ github.ref }}
2527 permissions :
2628 contents : read
2729 pull-requests : write
28-
30+
2931 steps :
3032 - name : Checkout current branch
3133 uses : actions/checkout@v4
@@ -41,169 +43,148 @@ jobs:
4143 echo "PR number: ${{ github.event.number }}"
4244 echo "Commit SHA: ${{ github.sha }}"
4345
44- - name : Record extra cache info
45- run : |
46- # Record filenames listing -- this updates the cache on filename change
47- find book/ -type f >> EXTRA_CACHE_VARS.txt
48- # For PR builds, we don't preprocess or archive
49- echo false >> EXTRA_CACHE_VARS.txt # preprocess
50- echo false >> EXTRA_CACHE_VARS.txt # archive
51- cat EXTRA_CACHE_VARS.txt
52-
53- - name : Cache page build
54- id : cache-html
55- uses : actions/cache@v4
56- with :
57- path : " book/_build/html"
58- key : html-build-${{ github.head_ref }}-${{ hashFiles('book/**', 'requirements.txt', 'EXTRA_CACHE_VARS.txt') }}
5946
60- - if : ${{ steps.cache-html.outputs.cache-hit != 'true' }}
61- name : Set up Python 3.11
62- uses : actions/setup-python@v5
63- id : setup-python
47+ - uses : actions/setup-node@v4
6448 with :
65- python -version : 3.11
49+ node -version : 18.x
6650
67- - if : ${{ steps.cache-html.outputs.cache-hit != 'true' }}
68- name : Record current date for cache
51+ - name : Install MyST Markdown CLI
6952 run : |
70- echo "WEEK=$(date +%V)" >> $GITHUB_ENV
71-
72- - if : ${{ steps.cache-html.outputs.cache-hit != 'true' }}
73- name : Cache virtualenv
74- uses : actions/cache@v4
75- with :
76- key : venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-week${{ env.WEEK }}-${{ github.repository }}
77- path : .venv
53+ npm install -g mystmd thebe-core thebe thebe-lite
7854
79- - if : ${{ steps.cache-html.outputs.cache-hit != 'true' }}
80- name : Install dependencies
55+ - name : Build HTML
56+ working-directory : ./lectures
8157 run : |
82- python -m venv .venv
83- source .venv/bin/activate
84- python -m pip install -r requirements.txt
85- echo "$VIRTUAL_ENV/bin" >> $GITHUB_PATH
86- echo "VIRTUAL_ENV=$VIRTUAL_ENV" >> $GITHUB_ENV
58+ myst build --html
8759
88- - if : ${{ steps.cache-html.outputs.cache-hit != 'true' }}
89- name : Build the book with TeachBooks
90- run : |
91- set -o pipefail
92- # Build without preprocessing for PR preview
93- teachbooks build book/ > >(tee stdout.log) 2> >(tee stderr.log >&2)
94- set +o pipefail
95-
96- - name : If build failed, attempt to restore from cache
97- if : failure()
98- id : attempt-restore
99- uses : actions/cache/restore@v4
60+ - name : Upload build output
61+ uses : actions/upload-pages-artifact@v3
10062 with :
101- key : none
102- path : " book/_build/html"
103- restore-keys : html-build-${{ github.head_ref }}
63+ path : ' ./lectures/_build/html'
10464
105- - name : Check build output
65+ - name : Install Netlify CLI
10666 run : |
107- echo "Build completed. Contents of book/_build/html/:"
108- ls -la book/_build/html/ || echo "No build output found"
109- echo "Checking for index.html:"
110- ls -la book/_build/html/index.html || echo "No index.html found"
67+ npm install -g netlify-cli
68+ mkdir -p ~/.config/netlify
11169
112- - name : Create build status summary
113- if : always()
70+ - name : Deploy Preview to Netlify
71+ id : netlify-deploy
11472 run : |
115- if [ ${{ job.status }} == 'success' ]; then
116- echo "✅ **Build Status**: Success" >> build_summary.md
117- echo "Book built successfully from branch \`${{ github.head_ref }}\`" >> build_summary.md
118- else
119- echo "**Build Status**: Failed" >> build_summary.md
120- if [ -n "${{ steps.attempt-restore.outputs.cache-matched-key }}" ]; then
121- echo "Using cached version from previous successful build" >> build_summary.md
122- else
123- echo " No cached version available" >> build_summary.md
73+ if [ "${{ github.event_name }}" = "pull_request" ]; then
74+ # Deploy to Netlify and capture the response
75+ deploy_message="Preview Deploy from GitHub Actions PR #${{ github.event.pull_request.number }} (commit: ${{ github.event.pull_request.head.sha }})"
76+
77+ netlify_output=$(netlify deploy \
78+ --dir lectures/_build/html/ \
79+ --site ${{ secrets.NETLIFY_SITE_ID }} \
80+ --auth ${{ secrets.NETLIFY_AUTH_TOKEN }} \
81+ --context pr-preview \
82+ --alias pr-${{ github.event.pull_request.number }} \
83+ --message "${deploy_message}" \
84+ --json)
85+
86+ echo "Netlify deployment output:"
87+ echo "$netlify_output"
88+
89+ # Extract the actual deploy URL from the JSON response
90+ deploy_url=$(echo "$netlify_output" | jq -r '.deploy_url')
91+
92+ echo "deploy_url=$deploy_url" >> $GITHUB_OUTPUT
93+ echo "✅ Deployment completed!"
94+ echo "🌐 Deploy URL: $deploy_url"
95+
96+ # Display manual preview page if specified
97+ if [ ! -z "${{ github.event.inputs.preview_page }}" ]; then
98+ echo ""
99+ echo "🎯 Manual preview page: ${deploy_url}/${{ github.event.inputs.preview_page }}"
124100 fi
101+ else
102+ # Handle push to branch
103+ deploy_message="Deploy from GitHub Actions (commit: ${{ github.sha }})"
104+
105+ netlify_output=$(netlify deploy \
106+ --dir lectures/_build/html/ \
107+ --site ${{ secrets.NETLIFY_SITE_ID }} \
108+ --auth ${{ secrets.NETLIFY_AUTH_TOKEN }} \
109+ --context dev \
110+ --alias ${{ github.ref_name }} \
111+ --message "${deploy_message}" \
112+ --json)
113+
114+ echo "Netlify deployment output:"
115+ echo "$netlify_output"
116+
117+ # Extract the actual deploy URL from the JSON response
118+ deploy_url=$(echo "$netlify_output" | jq -r '.deploy_url')
119+
120+ echo "deploy_url=$deploy_url" >> $GITHUB_OUTPUT
121+ echo "✅ Deployment completed!"
122+ echo "🌐 Deploy URL: $deploy_url"
125123 fi
126-
127- if [ -s stderr.log ]; then
128- echo "" >> build_summary.md
129- echo " **Build Warnings/Errors**:" >> build_summary.md
130- echo '```' >> build_summary.md
131- cat stderr.log >> build_summary.md
132- echo '```' >> build_summary.md
133- fi
134-
135- - name : Deploy to Netlify
136- if : always() && hashFiles('book/_build/html/**') != ''
137- uses : nwtgck/actions-netlify@v3
138- with :
139- publish-dir : ' book/_build/html/'
140- production-branch : main
141- github-token : ${{ secrets.GITHUB_TOKEN }}
142- deploy-message : " Preview Deploy from ${{ github.head_ref }} (PR #${{ github.event.number }})"
143- alias : pr-${{ github.event.number }}
144- enable-pull-request-comment : true
145- enable-commit-comment : false
146- overwrites-pull-request-comment : true
147124 env :
148125 NETLIFY_AUTH_TOKEN : ${{ secrets.NETLIFY_AUTH_TOKEN }}
149126 NETLIFY_SITE_ID : ${{ secrets.NETLIFY_SITE_ID }}
150127
151- - name : Comment build summary on PR
152- if : always()
128+ - name : Post PR Comment with Preview Link
129+ if : github.event_name == 'pull_request'
153130 uses : actions/github-script@v7
154131 with :
155132 script : |
156- const fs = require('fs');
157- let summary = '';
158- try {
159- summary = fs.readFileSync('build_summary.md', 'utf8');
160- } catch (error) {
161- summary = '📋 Build summary not available';
133+ const deployUrl = `${{ steps.netlify-deploy.outputs.deploy_url }}`;
134+ const manualPage = `${{ github.event.inputs.preview_page }}`;
135+ const prNumber = ${{ github.event.pull_request.number }};
136+ const commitSha = `${{ github.event.pull_request.head.sha }}`;
137+ const shortSha = commitSha.substring(0, 7);
138+
139+ console.log(`Deploy URL: ${deployUrl}`);
140+ console.log(`Manual preview page: ${manualPage}`);
141+ console.log(`PR Number: ${prNumber}`);
142+ console.log(`Commit SHA: ${shortSha}`);
143+
144+ // Get all comments on this PR to check for duplicates
145+ const comments = await github.rest.issues.listComments({
146+ issue_number: prNumber,
147+ owner: context.repo.owner,
148+ repo: context.repo.repo,
149+ });
150+
151+ console.log(`Found ${comments.data.length} comments on PR`);
152+
153+ // Look for existing comment with this exact commit SHA and deploy URL
154+ const duplicateComment = comments.data.find(comment => {
155+ const hasMarker = comment.body.includes('**📖 Netlify Preview Ready!**');
156+ const hasCommitSha = comment.body.includes(`([${shortSha}]`);
157+ const hasDeployUrl = comment.body.includes(deployUrl);
158+
159+ return hasMarker && hasCommitSha && hasDeployUrl;
160+ });
161+
162+ if (duplicateComment) {
163+ console.log(`Duplicate comment found (${duplicateComment.id}), skipping...`);
164+ return;
162165 }
163166
164- const body = `## 🚀 TeachBooks Build & Deploy Summary
165-
166- ${summary}
167-
168- ---
169- <details>
170- <summary>📖 TeachBooks Configuration</summary>
167+ console.log(`No duplicate found, creating new comment for commit ${shortSha}`);
171168
172- - **Branch**: \`${{ github.head_ref }}\`
173- - **Build Command**: \`teachbooks build book/\`
174- - **Preprocessing**: Disabled (PR preview)
175- - **Archive Banner**: Disabled (PR preview)
176- - **Cache**: ${{ steps.cache-html.outputs.cache-hit == 'true' && 'Hit ✅' || 'Miss ❌' }}
169+ const commitUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${commitSha}`;
177170
178- </details>`;
179-
180- // Get existing comments
181- const { data: comments } = await github.rest.issues.listComments({
171+ let comment = `**📖 Netlify Preview Ready!**\n\n`;
172+ comment += `**Preview URL:** ${deployUrl} ([${shortSha}](${commitUrl}))\n\n`;
173+
174+ // Add manual preview page if specified
175+ if (manualPage) {
176+ comment += `🎯 **Manual Preview:** [${manualPage}](${deployUrl}/${manualPage})\n\n`;
177+ }
178+
179+ comment += `✨ Browse the preview at the URL above.\n`;
180+
181+ // Post the comment
182+ await github.rest.issues.createComment({
183+ issue_number: prNumber,
182184 owner: context.repo.owner,
183185 repo: context.repo.repo,
184- issue_number: context.issue.number,
186+ body: comment
185187 });
186-
187- // Find existing bot comment
188- const botComment = comments.find(comment =>
189- comment.user.type === 'Bot' &&
190- comment.body.includes('🚀 TeachBooks Build & Deploy Summary')
191- );
192-
193- if (botComment) {
194- // Update existing comment
195- await github.rest.issues.updateComment({
196- owner: context.repo.owner,
197- repo: context.repo.repo,
198- comment_id: botComment.id,
199- body: body
200- });
201- } else {
202- // Create new comment
203- await github.rest.issues.createComment({
204- owner: context.repo.owner,
205- repo: context.repo.repo,
206- issue_number: context.issue.number,
207- body: body
208- });
209- }
188+
189+ console.log('Comment posted successfully');
190+ timeout-minutes : 10
0 commit comments