Merge pull request #18 from tmaier/claude/add-pdf-generation-workflow-01LQWhnfdm3BgseAt6QjyLJR

Add GitHub workflow to generate PDF from HTML
This commit is contained in:
Tobias L. Maier 2025-11-23 17:58:05 +01:00 committed by GitHub
commit d706d181ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 200 additions and 0 deletions

16
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,16 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

38
.github/workflows/generate-pdf.yml vendored Normal file
View file

@ -0,0 +1,38 @@
name: Generate PDF Labels
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
workflow_dispatch:
jobs:
generate-pdf:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Install Playwright Chromium
run: npx playwright install chromium
- name: Generate PDF
run: npm run generate-pdf
- name: Upload PDF artifact
uses: actions/upload-artifact@v4
with:
name: asn-labels-pdf
path: asn-labels.pdf
retention-days: 30

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules/
asn-labels.pdf

62
package-lock.json generated Normal file
View file

@ -0,0 +1,62 @@
{
"name": "asn-qr-code-label-generator",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "asn-qr-code-label-generator",
"version": "1.0.0",
"devDependencies": {
"playwright": "^1.49.0"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/playwright": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz",
"integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.56.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz",
"integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
}
}
}

12
package.json Normal file
View file

@ -0,0 +1,12 @@
{
"name": "asn-qr-code-label-generator",
"version": "1.0.0",
"description": "QR code label generator for Paperless-ngx Archive Serial Numbers",
"private": true,
"scripts": {
"generate-pdf": "node scripts/generate-pdf.js"
},
"devDependencies": {
"playwright": "^1.56.0"
}
}

70
scripts/generate-pdf.js Normal file
View file

@ -0,0 +1,70 @@
const { chromium } = require('playwright');
const http = require('http');
const fs = require('fs');
const path = require('path');
async function generatePDF() {
// Start a simple HTTP server to serve the HTML file
const htmlPath = path.join(__dirname, '..', 'index.html');
const htmlContent = fs.readFileSync(htmlPath, 'utf8');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(htmlContent);
});
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
const port = server.address().port;
console.log(`Server started on port ${port}`);
const browser = await chromium.launch({
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage'
]
});
const context = await browser.newContext();
const page = await context.newPage();
try {
// Load the page from local server and wait for network to settle
await page.goto(`http://127.0.0.1:${port}`, { waitUntil: 'networkidle' });
// Wait for AlpineJS to initialize and generate labels
console.log('Waiting for labels to be generated...');
await page.waitForFunction(() => {
const labels = document.querySelectorAll('ol li');
return labels.length === 189; // 7 columns x 27 rows
}, { timeout: 60000 });
console.log('Labels generated, waiting for QR code images to load...');
// Wait for all QR code images to load
await page.waitForFunction(() => {
const images = document.querySelectorAll('ol li img');
if (images.length === 0) return false;
return Array.from(images).every(img => img.complete && img.naturalHeight > 0);
}, { timeout: 120000 });
console.log('All images loaded, generating PDF...');
// Generate PDF with A4 format and print media
await page.pdf({
path: 'asn-labels.pdf',
format: 'A4',
printBackground: true,
margin: { top: 0, right: 0, bottom: 0, left: 0 }
});
console.log('PDF generated successfully: asn-labels.pdf');
} finally {
await browser.close();
server.close();
}
}
generatePDF().catch(err => {
console.error('Error generating PDF:', err);
process.exit(1);
});