Skip to content

Commit 7e28a68

Browse files
Attempt to speed up CI build
1 parent 26f2d7b commit 7e28a68

File tree

3 files changed

+5964
-34
lines changed

3 files changed

+5964
-34
lines changed

.github/workflows/ci.yml

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -86,21 +86,42 @@ jobs:
8686
id: pages
8787
uses: actions/configure-pages@v5
8888

89-
- name: Clean install dependencies
89+
- name: Setup Yarn
90+
run: corepack enable
91+
92+
- name: Cache dependencies
93+
uses: actions/cache@v4
94+
with:
95+
path: |
96+
~/.yarn/cache
97+
node_modules
98+
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
99+
restore-keys: |
100+
${{ runner.os }}-yarn-
101+
102+
- name: Cache Playwright browsers
103+
uses: actions/cache@v4
104+
with:
105+
path: |
106+
~/.cache/ms-playwright
107+
key: ${{ runner.os }}-playwright-${{ hashFiles('**/package.json') }}
108+
restore-keys: |
109+
${{ runner.os }}-playwright-
110+
111+
- name: Install dependencies
90112
run: |
91-
rm -rf node_modules package-lock.json
92-
npm install
113+
yarn install --frozen-lockfile
93114
npx playwright install-deps chromium
94115
working-directory: ${{ env.BUILD_PATH }}
95116

96117
- name: Build with Astro
97118
run: |
98-
npm run generate-json
99-
npx astro check
100-
npx astro build \
119+
yarn generate-json
120+
yarn astro check
121+
yarn astro build \
101122
--site "${{ steps.pages.outputs.origin }}" \
102123
--base "${{ steps.pages.outputs.base_path }}"
103-
npm run generate-og-images
124+
yarn generate-og-images
104125
working-directory: ${{ env.BUILD_PATH }}
105126

106127
- name: Upload artifact
@@ -266,40 +287,43 @@ jobs:
266287
id: pages
267288
uses: actions/configure-pages@v5
268289

269-
- name: Clean install dependencies
270-
run: |
271-
rm -rf node_modules package-lock.json
272-
npm install
273-
working-directory: ${{ env.BUILD_PATH }}
290+
- name: Setup Yarn
291+
run: corepack enable
274292

275-
- name: Install Playwright system dependencies
293+
- name: Cache dependencies
294+
uses: actions/cache@v4
295+
with:
296+
path: |
297+
~/.yarn/cache
298+
node_modules
299+
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
300+
restore-keys: |
301+
${{ runner.os }}-yarn-
302+
303+
- name: Cache Playwright browsers
304+
uses: actions/cache@v4
305+
with:
306+
path: |
307+
~/.cache/ms-playwright
308+
key: ${{ runner.os }}-playwright-${{ hashFiles('**/package.json') }}
309+
restore-keys: |
310+
${{ runner.os }}-playwright-
311+
312+
- name: Install dependencies
276313
run: |
277-
sudo apt-get update
278-
sudo apt-get install -y \
279-
fonts-liberation \
280-
libappindicator3-1 \
281-
libasound2 \
282-
libatk-bridge2.0-0 \
283-
libdrm2 \
284-
libgtk-3-0 \
285-
libnspr4 \
286-
libnss3 \
287-
libxcomposite1 \
288-
libxdamage1 \
289-
libxrandr2 \
290-
xdg-utils \
291-
libxss1 \
292-
libgconf-2-4
293-
npx playwright install-deps chromium
314+
yarn install --frozen-lockfile
315+
316+
- name: Install Playwright
317+
run: npx playwright install-deps chromium
294318

295319
- name: Build with Astro
296320
run: |
297-
npm run generate-json
298-
npx astro check
299-
npx astro build \
321+
yarn generate-json
322+
yarn astro check
323+
yarn astro build \
300324
--site "${{ steps.pages.outputs.origin }}" \
301325
--base "${{ steps.pages.outputs.base_path }}"
302-
npm run generate-og-images
326+
yarn generate-og-images
303327
working-directory: ${{ env.BUILD_PATH }}
304328

305329
- name: Upload artifact
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
const { chromium } = require('playwright');
2+
const fs = require('fs');
3+
const path = require('path');
4+
5+
async function generateOGImages() {
6+
console.log('🎨 Starting OG image generation...');
7+
8+
let browser;
9+
try {
10+
browser = await chromium.launch({
11+
headless: true,
12+
args: [
13+
'--no-sandbox',
14+
'--disable-setuid-sandbox',
15+
'--disable-web-security',
16+
'--disable-features=VizDisplayCompositor',
17+
'--disable-background-timer-throttling',
18+
'--disable-backgrounding-occluded-windows',
19+
'--disable-renderer-backgrounding'
20+
]
21+
});
22+
} catch (error) {
23+
console.error('❌ Failed to launch browser:', error.message);
24+
console.log('💡 Please run "npx playwright install chromium" to install the browser');
25+
return;
26+
}
27+
28+
try {
29+
const distPath = path.join(__dirname, '..', 'dist');
30+
const ogApiPath = path.join(distPath, 'api', 'og');
31+
32+
if (!fs.existsSync(ogApiPath)) {
33+
console.log('❌ No OG API directory found in dist. Make sure to run astro build first.');
34+
return;
35+
}
36+
37+
const htmlFiles = findHtmlFiles(ogApiPath);
38+
console.log(`📁 Found ${htmlFiles.length} OG image files to convert`);
39+
40+
// Process in parallel with limited concurrency for better performance
41+
const BATCH_SIZE = 5;
42+
let converted = 0;
43+
let failed = 0;
44+
45+
for (let i = 0; i < htmlFiles.length; i += BATCH_SIZE) {
46+
const batch = htmlFiles.slice(i, i + BATCH_SIZE);
47+
const results = await Promise.allSettled(
48+
batch.map(htmlFile => processFile(browser, htmlFile, distPath))
49+
);
50+
51+
results.forEach((result, index) => {
52+
if (result.status === 'fulfilled') {
53+
converted++;
54+
} else {
55+
console.error(`❌ Failed to convert ${path.relative(distPath, batch[index])}: ${result.reason}`);
56+
failed++;
57+
}
58+
});
59+
60+
console.log(`✅ Processed batch ${Math.floor(i/BATCH_SIZE) + 1}/${Math.ceil(htmlFiles.length/BATCH_SIZE)}`);
61+
}
62+
63+
console.log(`✅ Successfully converted ${converted} images to PNG format`);
64+
if (failed > 0) {
65+
console.log(`⚠️ Failed to convert ${failed} images`);
66+
}
67+
68+
} catch (error) {
69+
console.error('❌ Error during OG image generation:', error);
70+
} finally {
71+
await browser.close();
72+
}
73+
}
74+
75+
async function processFile(browser, htmlFile, distPath) {
76+
const page = await browser.newPage();
77+
78+
try {
79+
await page.setViewportSize({ width: 1200, height: 630 });
80+
81+
// Read and optimize HTML content
82+
let htmlContent = fs.readFileSync(htmlFile, 'utf8');
83+
htmlContent = htmlContent.replace(
84+
/@import url\('https:\/\/fonts\.googleapis\.com\/[^']+'\);/g,
85+
''
86+
);
87+
htmlContent = htmlContent.replace(
88+
/font-family: 'Inter'[^;]+;/g,
89+
"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;"
90+
);
91+
92+
await page.setContent(htmlContent, {
93+
waitUntil: 'domcontentloaded',
94+
timeout: 5000
95+
});
96+
97+
// Minimal wait for rendering
98+
await page.waitForTimeout(250);
99+
100+
const screenshot = await page.screenshot({
101+
type: 'png',
102+
fullPage: false,
103+
clip: { x: 0, y: 0, width: 1200, height: 630 }
104+
});
105+
106+
fs.writeFileSync(htmlFile, screenshot);
107+
console.log(`🖼️ Converted: ${path.relative(distPath, htmlFile)}`);
108+
109+
} finally {
110+
await page.close();
111+
}
112+
}
113+
114+
function findHtmlFiles(dir) {
115+
const files = [];
116+
117+
function scan(currentDir) {
118+
const items = fs.readdirSync(currentDir);
119+
120+
for (const item of items) {
121+
const fullPath = path.join(currentDir, item);
122+
const stat = fs.statSync(fullPath);
123+
124+
if (stat.isDirectory()) {
125+
scan(fullPath);
126+
} else if (item.endsWith('.png')) {
127+
try {
128+
const content = fs.readFileSync(fullPath, 'utf8');
129+
if (content.trim().startsWith('<!DOCTYPE html>')) {
130+
files.push(fullPath);
131+
}
132+
} catch (error) {
133+
// Skip files that can't be read as text
134+
}
135+
}
136+
}
137+
}
138+
139+
scan(dir);
140+
return files;
141+
}
142+
143+
generateOGImages().catch(console.error);

0 commit comments

Comments
 (0)