Skip to content

Commit f278d5f

Browse files
committed
refactor: Replace custom Netlify deployment with quantecon/actions/preview-netlify@v0.6.0
- Removes ~260 lines of custom Netlify deployment code - Uses shared action that provides same functionality: - Automatic changed lecture file detection - Smart PR comments with direct links - Duplicate comment prevention - Built-in security handling (skips forks/Dependabot) - Improves maintainability across QuantEcon repos - Enables Dependabot to auto-update the action version
1 parent 935df28 commit f278d5f

1 file changed

Lines changed: 4 additions & 261 deletions

File tree

.github/workflows/ci.yml

Lines changed: 4 additions & 261 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ on:
1111
jobs:
1212
preview:
1313
runs-on: "runs-on=${{ github.run_id }}/family=g4dn.2xlarge/image=quantecon_ubuntu2404/disk=large"
14-
env:
15-
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
16-
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
1714
steps:
1815
- uses: actions/checkout@v6
1916
with:
@@ -86,263 +83,9 @@ jobs:
8683
with:
8784
name: execution-reports-html
8885
path: _build/html/reports
89-
- name: Install Node.js and Netlify CLI
90-
shell: bash -l {0}
91-
run: |
92-
# Install Node.js via system package manager since conda-forge doesn't have npm
93-
sudo apt-get update
94-
sudo apt-get install -y nodejs npm
95-
sudo npm install -g netlify-cli
96-
- name: Detect Changed Lecture Files
97-
id: detect-changes
98-
shell: bash -l {0}
99-
run: |
100-
if [ "${{ github.event_name }}" = "pull_request" ]; then
101-
echo "Detecting changed lecture files..."
102-
echo "Base SHA: ${{ github.event.pull_request.base.sha }}"
103-
echo "Head SHA: ${{ github.event.pull_request.head.sha }}"
104-
105-
# Ensure we have both base and head commits available
106-
git fetch origin ${{ github.event.pull_request.base.sha }}:refs/remotes/origin/pr-base || true
107-
git fetch origin ${{ github.event.pull_request.head.sha }}:refs/remotes/origin/pr-head || true
108-
109-
# Get changed files using git diff with status to see the type of change
110-
echo "Getting diff between commits..."
111-
all_changed=$(git diff --name-status ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} 2>/dev/null || echo "")
112-
113-
if [ -z "$all_changed" ]; then
114-
echo "No changes detected or error in git diff"
115-
echo "changed_files=" >> $GITHUB_OUTPUT
116-
exit 0
117-
fi
118-
119-
echo "All changed files with status:"
120-
echo "$all_changed"
121-
122-
# Filter for lecture files that are Added or Modified (not Deleted)
123-
# Format: M lectures/file.md or A lectures/file.md
124-
changed_lecture_files=""
125-
while IFS=$'\t' read -r status file; do
126-
# Skip if empty line
127-
[ -z "$status" ] && continue
128-
129-
echo "Processing: status='$status' file='$file'"
130-
131-
# Only include Added (A) or Modified (M) files, skip Deleted (D)
132-
if [[ "$status" =~ ^[AM] ]] && [[ "$file" =~ ^lectures/.*\.md$ ]] && [[ ! "$file" =~ ^lectures/_ ]] && [[ "$file" != "lectures/intro.md" ]]; then
133-
# Double-check that the file exists and has real content changes
134-
if [ -f "$file" ]; then
135-
# Use git show to check if there are actual content changes (not just metadata)
136-
content_diff=$(git diff ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} -- "$file" | grep -E '^[+-]' | grep -v '^[+-]{3}' | wc -l)
137-
if [ "$content_diff" -gt 0 ]; then
138-
echo "✓ Confirmed content changes in: $file"
139-
if [ -z "$changed_lecture_files" ]; then
140-
changed_lecture_files="$file"
141-
else
142-
changed_lecture_files="$changed_lecture_files"$'\n'"$file"
143-
fi
144-
else
145-
echo "⚠ No content changes found in: $file (possibly metadata only)"
146-
fi
147-
else
148-
echo "⚠ File not found in working directory: $file"
149-
fi
150-
else
151-
echo "⚠ Skipping: $file (status: $status, doesn't match lecture file pattern or is excluded)"
152-
fi
153-
done <<< "$all_changed"
154-
155-
if [ ! -z "$changed_lecture_files" ]; then
156-
echo ""
157-
echo "Final validated changed lecture files:"
158-
echo "$changed_lecture_files"
159-
echo "changed_files<<EOF" >> $GITHUB_OUTPUT
160-
echo "$changed_lecture_files" >> $GITHUB_OUTPUT
161-
echo "EOF" >> $GITHUB_OUTPUT
162-
else
163-
echo "No lecture files with actual content changes found"
164-
echo "changed_files=" >> $GITHUB_OUTPUT
165-
fi
166-
else
167-
echo "Not a PR, skipping change detection"
168-
echo "changed_files=" >> $GITHUB_OUTPUT
169-
fi
17086
- name: Preview Deploy to Netlify
171-
id: netlify-deploy
172-
if: >
173-
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) &&
174-
env.NETLIFY_AUTH_TOKEN != '' &&
175-
env.NETLIFY_SITE_ID != ''
176-
shell: bash -l {0}
177-
run: |
178-
if [ "${{ github.event_name }}" = "pull_request" ]; then
179-
# Deploy to Netlify and capture the response
180-
deploy_message="Preview Deploy from GitHub Actions PR #${{ github.event.pull_request.number }} (commit: ${{ github.event.pull_request.head.sha }})"
181-
182-
netlify_output=$(netlify deploy \
183-
--dir _build/html/ \
184-
--site ${{ secrets.NETLIFY_SITE_ID }} \
185-
--auth ${{ secrets.NETLIFY_AUTH_TOKEN }} \
186-
--context pr-preview \
187-
--alias pr-${{ github.event.pull_request.number }} \
188-
--message "${deploy_message}" \
189-
--json)
190-
191-
echo "Netlify deployment output:"
192-
echo "$netlify_output"
193-
194-
# Extract the actual deploy URL from the JSON response
195-
deploy_url=$(echo "$netlify_output" | jq -r '.deploy_url')
196-
197-
echo "deploy_url=$deploy_url" >> $GITHUB_OUTPUT
198-
echo "✅ Deployment completed!"
199-
echo "🌐 Actual Deploy URL: $deploy_url"
200-
201-
# Generate preview URLs for changed files using the actual deploy URL
202-
if [ ! -z "${{ steps.detect-changes.outputs.changed_files }}" ]; then
203-
echo ""
204-
echo "📚 Direct links to changed lecture pages:"
205-
while read -r file; do
206-
if [ ! -z "$file" ]; then
207-
basename=$(basename "$file" .md)
208-
html_file="${basename}.html"
209-
echo "- ${basename}: ${deploy_url}/${html_file}"
210-
fi
211-
done <<< "${{ steps.detect-changes.outputs.changed_files }}"
212-
fi
213-
214-
# Display manual preview page if specified
215-
if [ ! -z "${{ github.event.inputs.preview_page }}" ]; then
216-
echo ""
217-
echo "🎯 Manual preview page: ${deploy_url}/${{ github.event.inputs.preview_page }}"
218-
fi
219-
else
220-
# Handle manual deployment
221-
deploy_message="Manual Deploy from GitHub Actions (commit: ${{ github.sha }})"
222-
223-
netlify_output=$(netlify deploy \
224-
--dir _build/html/ \
225-
--site ${{ secrets.NETLIFY_SITE_ID }} \
226-
--auth ${{ secrets.NETLIFY_AUTH_TOKEN }} \
227-
--alias manual-${{ github.run_id }} \
228-
--context dev \
229-
--message "${deploy_message}" \
230-
--json)
231-
232-
echo "Netlify deployment output:"
233-
echo "$netlify_output"
234-
235-
# Extract the actual deploy URL from the JSON response
236-
deploy_url=$(echo "$netlify_output" | jq -r '.deploy_url')
237-
238-
echo "deploy_url=$deploy_url" >> $GITHUB_OUTPUT
239-
echo "✅ Manual deployment completed!"
240-
echo "🌐 Actual Deploy URL: $deploy_url"
241-
242-
if [ ! -z "${{ github.event.inputs.preview_page }}" ]; then
243-
echo "🎯 Preview page: ${deploy_url}/${{ github.event.inputs.preview_page }}"
244-
fi
245-
fi
246-
- name: Skip Netlify Deploy (no secrets or untrusted actor)
247-
if: >
248-
!((github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) &&
249-
env.NETLIFY_AUTH_TOKEN != '' &&
250-
env.NETLIFY_SITE_ID != '')
251-
run: |
252-
echo "Skipping Netlify preview deploy: secrets unavailable or untrusted PR (from fork)"
253-
- name: Post PR Comment with Preview Links
254-
if: github.event_name == 'pull_request' && steps.netlify-deploy.outputs.deploy_url != ''
255-
uses: actions/github-script@v7
87+
uses: quantecon/actions/preview-netlify@v0.6.0
25688
with:
257-
script: |
258-
const changedFiles = `${{ steps.detect-changes.outputs.changed_files }}`;
259-
const manualPage = `${{ github.event.inputs.preview_page }}`;
260-
const deployUrl = `${{ steps.netlify-deploy.outputs.deploy_url }}`;
261-
const prNumber = ${{ github.event.pull_request.number }};
262-
const commitSha = `${{ github.event.pull_request.head.sha }}`;
263-
const shortSha = commitSha.substring(0, 7);
264-
265-
console.log(`Checking for existing comments for commit: ${commitSha}`);
266-
console.log(`Deploy URL: ${deployUrl}`);
267-
console.log(`Changed files: ${changedFiles}`);
268-
269-
// Get all comments on this PR to check for duplicates
270-
const comments = await github.rest.issues.listComments({
271-
issue_number: prNumber,
272-
owner: context.repo.owner,
273-
repo: context.repo.repo,
274-
});
275-
276-
console.log(`Found ${comments.data.length} comments on PR`);
277-
278-
// Look for existing comment with this exact commit SHA and deploy URL
279-
const duplicateComment = comments.data.find(comment => {
280-
const hasMarker = comment.body.includes('**📖 Netlify Preview Ready!**');
281-
const hasCommitSha = comment.body.includes(`([${shortSha}]`);
282-
const hasDeployUrl = comment.body.includes(deployUrl);
283-
284-
console.log(`Comment ${comment.id}: hasMarker=${hasMarker}, hasCommitSha=${hasCommitSha}, hasDeployUrl=${hasDeployUrl}`);
285-
286-
return hasMarker && hasCommitSha && hasDeployUrl;
287-
});
288-
289-
if (duplicateComment) {
290-
console.log(`Duplicate comment found (${duplicateComment.id}) for commit ${shortSha} and deploy URL ${deployUrl}, skipping...`);
291-
return;
292-
}
293-
294-
console.log(`No duplicate found, creating new comment for commit ${shortSha}`);
295-
296-
const commitUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${commitSha}`;
297-
298-
let comment = `**📖 Netlify Preview Ready!**\n\n`;
299-
comment += `**Preview URL:** ${deployUrl} ([${shortSha}](${commitUrl}))\n\n`;
300-
301-
// Add manual preview page if specified
302-
if (manualPage) {
303-
comment += `🎯 **Manual Preview:** [${manualPage}](${deployUrl}/${manualPage})\n\n`;
304-
}
305-
306-
// Add direct links to changed lecture pages
307-
if (changedFiles && changedFiles.trim()) {
308-
console.log('Processing changed files for preview links...');
309-
const files = changedFiles.split('\n').filter(f => f.trim() && f.includes('lectures/') && f.endsWith('.md'));
310-
console.log('Filtered lecture files:', files);
311-
312-
if (files.length > 0) {
313-
comment += `📚 **Changed Lecture Pages:** `;
314-
315-
const pageLinks = [];
316-
for (const file of files) {
317-
const cleanFile = file.trim();
318-
if (cleanFile && cleanFile.startsWith('lectures/') && cleanFile.endsWith('.md')) {
319-
const fileName = cleanFile.replace('lectures/', '').replace('.md', '');
320-
console.log(`Creating preview link: ${cleanFile} -> ${fileName}.html`);
321-
const pageUrl = `${deployUrl}/${fileName}.html`;
322-
pageLinks.push(`[${fileName}](${pageUrl})`);
323-
}
324-
}
325-
326-
if (pageLinks.length > 0) {
327-
comment += pageLinks.join(', ') + '\n\n';
328-
} else {
329-
console.log('No valid page links created');
330-
}
331-
} else {
332-
console.log('No lecture files in changed files list');
333-
}
334-
} else {
335-
console.log('No changed files detected');
336-
}
337-
338-
console.log('Final comment:', comment);
339-
340-
// Post the comment
341-
await github.rest.issues.createComment({
342-
issue_number: prNumber,
343-
owner: context.repo.owner,
344-
repo: context.repo.repo,
345-
body: comment
346-
});
347-
348-
console.log('Comment posted successfully');
89+
netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
90+
netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }}
91+
build-dir: _build/html

0 commit comments

Comments
 (0)