Skip to content

Commit 080d7b3

Browse files
mmckykp992
andauthored
ENH: Migrate to use mystmd and quantecon-theme. (#31)
* keep 1 lecture for testing * rename * demo working * use qe theme * fix * fix ci * use html flag * add lectures * final fixes * update figure syntax * ENH: Upgrade Netlify CI configuration with better preview deployment handling * FIX: Add deployments and statuses permissions to CI workflow * Switch to Netlify CLI deployment approach (matching lecture-python.myst) * FIX: Install Netlify CLI without sudo to avoid permission issues * FIX: Enable workflow for PRs targeting main branch * ENH: Align workflow with lecture-python.myst configuration - Add PR event types (opened, synchronize, reopened) - Remove branch restrictions on pull_request to work for all PRs - Add workflow_dispatch input for preview_page - Add --context flags to Netlify deploy (pr-preview, dev) - Add manual preview page support in PR comments --------- Co-authored-by: kp992 <[email protected]>
1 parent f13d13f commit 080d7b3

File tree

120 files changed

+1005
-750
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

120 files changed

+1005
-750
lines changed

.github/workflows/ci_pr.yml

Lines changed: 126 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ name: Build and Netlify Deploy
22

33
on:
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

1416
concurrency:
1517
group: ${{ github.workflow }}-${{ github.ref }}
@@ -25,7 +27,7 @@ jobs:
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

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,4 @@ _output
118118
# build
119119
**/*/_build
120120
**/*/.teachbooks
121+
_build/

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,14 @@ python update_lectures.py
4141

4242
This allows us the keep all the lectures up-to-date in a single place and keep this repository a mirror of the
4343
main repository.
44+
45+
### Lectures unsupported
46+
47+
- business_cycle
48+
- inequality
49+
- prob-dist
50+
- heavy-tails
51+
- commod-price
52+
- lp-intro
53+
- short_path
54+
- input-output

0 commit comments

Comments
 (0)