From 93bfa185d57c4dc6bbdd1a72b0c62e1436709cea Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 16:19:17 +0100 Subject: [PATCH 01/14] feat: add automated testing, validation tools, and contributor docs --- .github/workflows/ci.yml | 33 +++++ .gitignore | 29 +++- .ruby-version | 2 + CONTRIBUTING.md | 205 +++++++++++++++++++++++++++++ Gemfile | 13 ++ QUICKSTART.md | 139 +++++++++++++++++++ README.md | 91 +++++++++++-- SECURITY.md | 91 +++++++++++++ _data/publications.yml | 11 +- _templates/member-template.md | 32 +++++ _templates/post-template.md | 41 ++++++ _templates/publication-template.md | 55 ++++++++ favicon.ico | Bin 1150 -> 766 bytes scripts/optimize_images.sh | 156 ++++++++++++++++++++++ scripts/pre-commit.sh | 58 ++++++++ scripts/validate_posts.sh | 52 ++++++++ scripts/validate_yaml.rb | 107 +++++++++++++++ 17 files changed, 1101 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .ruby-version create mode 100644 CONTRIBUTING.md create mode 100644 QUICKSTART.md create mode 100644 SECURITY.md create mode 100644 _templates/member-template.md create mode 100644 _templates/post-template.md create mode 100644 _templates/publication-template.md create mode 100755 scripts/optimize_images.sh create mode 100755 scripts/pre-commit.sh create mode 100755 scripts/validate_posts.sh create mode 100755 scripts/validate_yaml.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..939ba71 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: CI + +on: + pull_request: + branches: [ main, master ] + push: + branches: [ main, master ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.1' + bundler-cache: true + + - name: Build Jekyll site + run: bundle exec jekyll build + env: + JEKYLL_ENV: production + + - name: Test with html-proofer + run: | + bundle exec htmlproofer ./_site \ + --disable-external \ + --ignore-urls "/^http:\/\/127.0.0.1/,/^http:\/\/0.0.0.0/,/^http:\/\/localhost/" \ + --allow-hash-href + diff --git a/.gitignore b/.gitignore index 9f3684f..a649d97 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,30 @@ +# Include your project-specific ignores in this file +# Read about how to use .gitignore: https://help.github.com/articles/ignoring-files +# Useful .gitignore templates: https://github.com/github/gitignore + +# Node.js +node_modules +dist +.cache + +# Jekyll _site/ +.sass-cache/ +.jekyll-cache/ +.jekyll-metadata + +# Ruby +.bundle/ +vendor/bundle/ +*.gem + +# OS .DS_Store -Gemfile.lock +Thumbs.db + +# Editor +.vscode/ +.idea/ +*.swp +*.swo +*~ diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..0c6173b --- /dev/null +++ b/.ruby-version @@ -0,0 +1,2 @@ +3.1.0 + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0a427ba --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,205 @@ +# Contributing to Deep-MI Website + +Thank you for contributing to the Deep-MI lab website! This guide will help you add content correctly. + +## Table of Contents + +- [Creating News Posts](#creating-news-posts) +- [Adding Members](#adding-members) +- [Adding Publications](#adding-publications) +- [Validation Before Committing](#validation-before-committing) +- [Local Development](#local-development) + +## Creating News Posts + +1. **Create a new file** in `_posts/` directory +2. **Name it**: `YYYY-MM-DD-Short-Title.md` (e.g., `2026-02-13-New-Grant.md`) +3. **Use the template** from `_templates/post-template.md` + +### Required Front Matter + +```yaml +--- +title: Your Article Title +author: First Lastname +layout: post +group: news +--- +``` + +### Tips + +- Use Markdown for formatting +- Include images in `/static/img/` directory +- Keep image file sizes reasonable (< 500KB) + +## Adding Members + +1. **Add member info** to `_data/members.yml` +2. **Prepare photo**: 365×365 pixels, 72 DPI, JPEG format +3. **Save photo** to `/static/img/members/lastname.jpg` +4. **Use the template** from `_templates/member-template.md` + +### Required Fields + +- `name`: Full name +- `image`: Path to photo (e.g., `/static/img/members/lastname.jpg`) +- `position`: Job title + +### Optional Fields + +- `email`: Email address +- `scholar`: Google Scholar ID +- `twitter`: Twitter handle +- `orcid`: ORCID identifier +- `github`: GitHub username +- `description`: Biography (use single quotes for multi-line) + +## Adding Publications + +1. **Add publication info** to `_data/publications.yml` +2. **Prepare teaser image**: Max 200px height, JPEG, max 100KB +3. **Save files**: + - Image: `/static/pub/paper_id.jpg` + - BibTeX: `/static/pub/paper_id.bib` + - PDF (optional): `/static/pub/paper_id.pdf` +4. **Use the template** from `_templates/publication-template.md` + +### Required Fields + +- `id`: Unique identifier (usually `lastname_year`) +- `authors`: Author list (bold lab members: `**Name**`) +- `title`: Paper title +- `journal`: Journal/conference name +- `type`: One of: journal, conference, workshop, abstract, poster, preprint +- `year`: Publication year + +### Recommended Fields + +- `doi`: Digital Object Identifier +- `image`: Teaser image path +- `bibtex`: BibTeX file path +- `pdf`: Link to open access PDF or local file + +## Validation Before Committing + +Always validate your changes before committing: + +```bash +# Validate YAML files +./scripts/validate_yaml.rb + +# Validate post filenames and format +./scripts/validate_posts.sh + +# Test the site locally +bundle exec jekyll serve +``` + +Visit http://localhost:4000 to preview your changes. + +## Automation Tools + +### Image Optimization + +Optimize images to meet size requirements: + +```bash +# Optimize a single member photo (365x365, 72 DPI) +./scripts/optimize_images.sh member static/img/members/lastname.jpg + +# Optimize a single publication teaser (max 200px height, max 100KB) +./scripts/optimize_images.sh pub static/pub/paper_id.jpg + +# Optimize all member photos at once +./scripts/optimize_images.sh all-members + +# Optimize all publication teasers at once +./scripts/optimize_images.sh all-pubs +``` + +**Requirements**: ImageMagick (`brew install imagemagick`) + +### Pre-commit Hook + +Install a pre-commit hook to automatically validate changes: + +```bash +cp scripts/pre-commit.sh .git/hooks/pre-commit +chmod +x .git/hooks/pre-commit +``` + +This will automatically run validation scripts before each commit. + +## Local Development + +### First-Time Setup + +```bash +# Install bundler (without sudo) +gem install bundler --user-install + +# Install dependencies +bundle install +``` + +### Running Locally + +```bash +# Start development server +bundle exec jekyll serve + +# Site available at: http://localhost:4000 +``` + +### Testing + +```bash +# Build site +bundle exec jekyll build + +# Check for broken links +bundle exec htmlproofer ./_site --disable-external +``` + +## Style Guidelines + +### Writing + +- Use clear, concise language +- Check spelling and grammar +- Use proper citations for published work + +### Images + +- Optimize images before uploading +- Use descriptive filenames +- Keep file sizes small: + - Member photos: ~50-100KB + - Publication teasers: max 100KB + - Post images: < 500KB + +### Code + +- Follow Jekyll conventions +- Keep YAML files properly indented (2 spaces) +- Use single quotes for strings in YAML +- Escape single quotes with double single quotes: `Alzheimer''s` + +## Questions? + +If you have questions or run into issues, please: +1. Check the templates in `_templates/` +2. Review existing examples in the data files +3. Contact the lab's web administrator + +## Continuous Integration + +All pull requests are automatically tested using GitHub Actions: +- Jekyll build verification +- HTML validation +- Link checking + +Make sure your changes pass these tests before requesting review. + + diff --git a/Gemfile b/Gemfile index 62d9e3b..3430421 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,19 @@ source 'https://rubygems.org' + +# Specify Ruby version for consistency +ruby '>= 2.7.0' + +# Core Jekyll gem for static site generation gem "jekyll" + +# GitHub Pages gem (includes Jekyll and plugins used by GitHub Pages) gem 'github-pages' + +# Pagination support for posts and publications gem 'jekyll-paginate' + +# HTML validation and link checking for CI/CD gem "html-proofer" + +# Required for Ruby 3.x to run Jekyll server gem "webrick" diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..0c613d6 --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,139 @@ +# Deep-MI Website - Quick Reference + +## Quick Start + +```bash +# First time setup +gem install bundler --user-install +bundle install + +# Run locally +bundle exec jekyll serve +# Visit: http://localhost:4000 + +# Validate before committing +./scripts/validate_yaml.rb +./scripts/validate_posts.sh +``` + +## Adding Content + +### News Post (5 minutes) + +1. Create: `_posts/2026-02-13-Title.md` +2. Copy template from: `_templates/post-template.md` +3. Add images to: `/static/img/` +4. Validate: `./scripts/validate_posts.sh` + +### Team Member (10 minutes) + +1. Prepare photo: 365×365px, JPG +2. Optimize: `./scripts/optimize_images.sh member photo.jpg` +3. Save to: `/static/img/members/lastname.jpg` +4. Add entry to: `_data/members.yml` (see `_templates/member-template.md`) +5. Validate: `./scripts/validate_yaml.rb` + +### Publication (15 minutes) + +1. Prepare teaser: Max 200px height, JPG, max 100KB +2. Optimize: `./scripts/optimize_images.sh pub image.jpg` +3. Save files: + - Image: `/static/pub/id.jpg` + - BibTeX: `/static/pub/id.bib` + - PDF (optional): `/static/pub/id.pdf` +4. Add entry to: `_data/publications.yml` (see `_templates/publication-template.md`) +5. Validate: `./scripts/validate_yaml.rb` + +## File Locations + +| Content Type | Data File | Assets | Template | +|--------------|-----------|--------|----------| +| News Posts | `_posts/*.md` | `/static/img/` | `_templates/post-template.md` | +| Members | `_data/members.yml` | `/static/img/members/` | `_templates/member-template.md` | +| Publications | `_data/publications.yml` | `/static/pub/` | `_templates/publication-template.md` | +| Alumni | `_data/alumni.yml` | - | - | +| Visitors | `_data/visitors.yml` | - | - | + +## Image Requirements + +| Type | Size | Format | Max Size | +|------|------|--------|----------| +| Member Photo | 365×365px, 72 DPI | JPG | ~100KB | +| Publication Teaser | Max 200px height | JPG | 100KB | +| Post Images | Reasonable | JPG/PNG | 500KB | + +## Validation Scripts + +```bash +# YAML data files (members, publications, etc.) +./scripts/validate_yaml.rb + +# Post filename format (YYYY-MM-DD-title.md) +./scripts/validate_posts.sh + +# Image optimization +./scripts/optimize_images.sh member +./scripts/optimize_images.sh pub +``` + +## Common Issues + +### Jekyll won't start +```bash +bundle install +bundle update +``` + +### Ruby version error +```bash +# Install correct Ruby version +rbenv install 3.1.0 +rbenv local 3.1.0 +``` + +### YAML validation fails +- Check indentation (2 spaces) +- Use single quotes for strings +- Escape quotes: `Alzheimer''s` (two single quotes) +- Required fields must be present + +### Images too large +```bash +# Optimize automatically +./scripts/optimize_images.sh member static/img/members/photo.jpg +./scripts/optimize_images.sh pub static/pub/teaser.jpg +``` + +## Useful Commands + +```bash +# Full build and test +bundle exec jekyll build +bundle exec htmlproofer ./_site --disable-external + +# Clean build +bundle exec jekyll clean +bundle exec jekyll build + +# Update dependencies +bundle update + +# Install pre-commit hook +cp scripts/pre-commit.sh .git/hooks/pre-commit +chmod +x .git/hooks/pre-commit +``` + +## Resources + +- [Jekyll Documentation](https://jekyllrb.com/docs/) +- [Markdown Guide](https://www.markdownguide.org/) +- [YAML Syntax](https://yaml.org/) +- [Bootstrap 4.4 Docs](https://getbootstrap.com/docs/4.4/) + +## Support + +- Check `CONTRIBUTING.md` for detailed guidelines +- Review templates in `_templates/` +- See existing examples in data files +- Contact lab web administrator + diff --git a/README.md b/README.md index 4e54d2d..af94cfb 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,14 @@ group: news Your text goes here, with images, links etc.... ``` +See `_templates/post-template.md` for a complete template. + ## Add Members - add member info to _data/members.yml (see file for format) - put photos (size 365 x 365, 72 dpi) into /static/img/members (jpg compressed) +See `_templates/member-template.md` for a complete template. ## Add Publication @@ -30,21 +33,93 @@ Your text goes here, with images, links etc.... - link to openaccess PDF (journal) or put at /static/pub/paper_id.pdf - paper_id is the id field in the yml +See `_templates/publication-template.md` for a complete template. + +## Validation + +Before committing changes, validate your content: + +```bash +# Validate YAML data files +./scripts/validate_yaml.rb + +# Validate post filenames and format +./scripts/validate_posts.sh +``` + +See `CONTRIBUTING.md` for detailed contribution guidelines. + +## Tools and Scripts + +### Validation + +```bash +# Validate YAML files (checks structure, required fields, file existence) +./scripts/validate_yaml.rb + +# Validate post filenames and format +./scripts/validate_posts.sh +``` + +### Image Optimization + +```bash +# Optimize member photos and publication teasers +./scripts/optimize_images.sh member static/img/members/lastname.jpg +./scripts/optimize_images.sh pub static/pub/paper_id.jpg +``` + +Requires ImageMagick: `brew install imagemagick` + +### Pre-commit Hook + +```bash +# Install pre-commit hook for automatic validation +cp scripts/pre-commit.sh .git/hooks/pre-commit +chmod +x .git/hooks/pre-commit +``` + ## Sources This website was built using the Fraser Lab [website](http://fraserlab.com/) as a template. James Fraser's website is open-source and available on [Github](https://github.com/fraser-lab/fraser-lab.github.io) -Technologies this website uses: +Technologies this website uses: - Jekyll - Github Pages + Jekyll + Github Pages Twitter Bootstrap 4.4.1 -Before pushing changes, please check that they will work on your system first with the plugins included in the Gemfile using the bundler tool (results served at [0.0.0.0:4000](0.0.0.0:4000)): +## Local Development + +Before pushing changes, please check that they will work on your system first with the plugins included in the Gemfile using the bundler tool. + +### Setup (First Time) + +```bash +# Install bundler (without sudo for security) +gem install bundler --user-install + +# Install dependencies +bundle install +``` + +### Running the Site Locally + +```bash +# Start the Jekyll server +bundle exec jekyll serve + +# Site will be available at: http://localhost:4000 +# Or: http://0.0.0.0:4000 +``` + +### Testing + +```bash +# Check for broken links and HTML issues +bundle exec jekyll build +bundle exec htmlproofer ./_site --disable-external +``` - sudo gem install bundler - bundle install - bundle exec jekyll serve - diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..5e80e85 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,91 @@ +# Security Policy + +## Supported Versions + +This website uses Jekyll and GitHub Pages. We recommend always using the latest versions: + +| Component | Version | Supported | +| --------- | ------- | --------- | +| Jekyll | Latest | ✅ | +| Ruby | 3.x | ✅ | +| Ruby | 2.7.x | ✅ | +| Ruby | < 2.7 | ❌ | + +## Security Best Practices + +### Dependencies + +- Always run `bundle update` regularly to get security patches +- Review GitHub Dependabot alerts when they appear +- Never commit `Gemfile.lock` with known vulnerabilities + +### Content Security + +- Never commit sensitive information (API keys, passwords, etc.) +- Review all external links before publishing +- Validate user-contributed content +- Optimize and scan images before uploading + +### Access Control + +- Limit write access to the repository +- Use branch protection rules for main/master branch +- Require pull request reviews for sensitive changes +- Enable 2FA for all contributors + +## Reporting a Vulnerability + +If you discover a security vulnerability in this website, please report it by: + +1. **Email**: Contact the lab administrators directly (do not open a public issue) +2. **Expected Response**: Within 48 hours +3. **Updates**: We'll keep you informed of the progress + +### What to Include + +- Description of the vulnerability +- Steps to reproduce +- Potential impact +- Suggested fix (if applicable) + +## Security Measures + +### Automated Checks + +- GitHub Actions CI runs on all pull requests +- HTML validation and link checking +- YAML syntax validation +- Post format validation + +### Manual Reviews + +- All content changes reviewed before merging +- Regular audits of dependencies +- Periodic security assessments + +## Known Limitations + +### GitHub Pages + +This site is hosted on GitHub Pages, which has certain security constraints: + +- No server-side code execution +- Static content only +- HTTPS enforced automatically +- Limited control over server headers + +### Third-Party Content + +We include content from: +- Google Scholar (user profiles) +- Twitter (social media links) +- External publication PDFs + +Users should be aware these are external resources not under our direct control. + +## Updates + +This security policy is reviewed and updated quarterly. + +Last updated: February 2026 + diff --git a/_data/publications.yml b/_data/publications.yml index 1d6d041..07d6223 100644 --- a/_data/publications.yml +++ b/_data/publications.yml @@ -103,7 +103,7 @@ doi: 10.1162/imag_a_00121 pdf: 'https://direct.mit.edu/imag/article-pdf/doi/10.1162/imag_a_00121/2358010/imag_a_00121.pdf' github: /deep-mi/DISCUS - + - id: 'estrada2023' authors: '**Estrada S**, **Kügler D**, Bahrami E, Xu P, Mousa D, Breteler MMB, Aziz NA, **Reuter M**' title: 'FastSurfer-HypVINN: Automated sub-segmentation of the hypothalamus and adjacent structures on high-resolutional brain MRI' @@ -112,11 +112,11 @@ image: '/static/pub/estrada_2023.jpg' year: 2023 bibtex: '/static/pub/estrada_2023.bib' - arxiv: 2308.12736 + arxiv: 2308.12736 pmcid: 'PMC11576934' doi: 10.1162/imag_a_00034 pdf: 'https://direct.mit.edu/imag/article-pdf/doi/10.1162/imag_a_00034/2182290/imag_a_00034.pdf' - github: /deep-mi/FastSurfer + github: /deep-mi/FastSurfer - id: 'diers_2023' authors: '**Diers K**, Baumeister H, Jessen F, Düzel E, Berron D, **Reuter M**' @@ -129,7 +129,7 @@ doi: 10.1016/j.neuroimage.2023.120182 pdf: 'https://doi.org/10.1016/j.neuroimage.2023.120182' github: 'deep-MI/Hipsta' - + - id: 'pollak_2023b' authors: '**Pollak C**, **Kügler D**, Breteler MMB, **Reuter M**' title: 'Quantifying MR head motion in the Rhineland Study – A robust method for population cohorts' @@ -277,7 +277,7 @@ doi: 10.1002/hbm.25377 pdf: 'https://onlinelibrary.wiley.com/doi/epdf/10.1002/hbm.25377' bibtex: '/static/pub/lu_2021.bib' - + - id: 'henschel_2020b' authors: '**Henschel L**, **Reuter M**' title: 'Parameter Space CNN for Cortical Surface Segmentation' @@ -605,6 +605,7 @@ pages: '11 - 22' volume: 127 journal: '*NeuroImage*' + type: 'journal' pdf: 'https://www.ncbi.nlm.nih.gov/pmc/articles/pmid/26654788/' doi: 10.1016/j.neuroimage.2015.11.054 pmcid: PMC4754677 diff --git a/_templates/member-template.md b/_templates/member-template.md new file mode 100644 index 0000000..8fbc668 --- /dev/null +++ b/_templates/member-template.md @@ -0,0 +1,32 @@ +# Member Template + +Copy this template when adding a new member to `_data/members.yml` + +```yaml +- name: Full Name + image: /static/img/members/lastname.jpg + position: Job Title (e.g., PhD Student, Postdoc, Research Assistant) + email: name.surname (at) institution.edu + scholar: GoogleScholarID # optional + twitter: twitterhandle # optional + orcid: 0000-0000-0000-0000 # optional + github: githubusername # optional + description: 'Brief biography and research interests. + + + Can span multiple paragraphs using blank lines between them.' +``` + +## Requirements + +1. **Photo**: 365×365 pixels, 72 DPI, JPEG format (compressed) +2. **Location**: `/static/img/members/lastname.jpg` +3. **Required fields**: name, image, position +4. **Optional fields**: email, scholar, twitter, orcid, github, description + +## Tips + +- Use single quotes for multi-line descriptions +- Keep file size under 100KB +- Use consistent naming: lastname.jpg + diff --git a/_templates/post-template.md b/_templates/post-template.md new file mode 100644 index 0000000..33a3538 --- /dev/null +++ b/_templates/post-template.md @@ -0,0 +1,41 @@ +--- +title: Your Article Title Here +author: First Lastname +layout: post +group: news +--- + +Write your news post content here. You can use Markdown formatting: + +## Subheadings + +Use **bold** and *italic* text for emphasis. + +### Lists + +- Bullet point 1 +- Bullet point 2 +- Bullet point 3 + +### Links and Images + +[Link text](https://example.com) + +![Alt text](/static/img/your-image.jpg) + +### Code + +Inline `code` or code blocks: + +```python +def hello_world(): + print("Hello, Deep-MI!") +``` + +--- + +Remember: +- Filename must be: YYYY-MM-DD-Short-Title.md +- Place in: _posts/ +- Required front matter fields: title, author, layout, group + diff --git a/_templates/publication-template.md b/_templates/publication-template.md new file mode 100644 index 0000000..60fc5e9 --- /dev/null +++ b/_templates/publication-template.md @@ -0,0 +1,55 @@ +# Publication Template + +Copy this template when adding a new publication to `_data/publications.yml` + +```yaml +- id: 'lastname_year' + authors: '**Labmember A**, Someone B, **Labmember C**, Lastauthor D' + title: 'The title of this paper (use two single quotes to escape: Alzheimer''s)' + journal: 'Journal Name or *under review*' + type: 'journal' # journal, conference, workshop, abstract, poster, preprint + year: 2026 + month: 2 # optional + pages: '123-145' # optional + volume: '42' # optional + number: '3' # optional + image: '/static/pub/lastname_year.jpg' + pmcid: 'PMC1234567' # optional - PubMed Central ID + pmid: '12345678' # optional - PubMed ID + biorxiv: '2024.01.01.123456' # optional - bioRxiv ID + arxiv: '2401.12345' # optional - arXiv ID + doi: '10.1234/journal.2024.123456' # strongly recommended + pdf: 'https://journal.com/paper.pdf' # or '/static/pub/lastname_year.pdf' + github: 'deep-mi/repository-name' # optional - relative to github.com + bibtex: '/static/pub/lastname_year.bib' + links: # optional - additional links + - name: 'Project Website' + url: 'https://project.example.com' + - name: 'Dataset' + url: 'https://data.example.com' +``` + +## Requirements + +1. **Teaser Image**: Max 200px height, JPEG format, max 100KB + - Location: `/static/pub/paper_id.jpg` +2. **BibTeX file**: `/static/pub/paper_id.bib` +3. **PDF**: Link to open access or local file at `/static/pub/paper_id.pdf` +4. **paper_id**: Should match the `id` field + +## Required Fields +- id (unique identifier) +- authors (bold lab members with `**name**`) +- title +- journal +- type +- year + +## Tips + +- Use single quotes for strings with special characters +- Always include DOI if available +- Bold current and former lab members in author list +- Keep image file size small (max 100KB) +- Test that all file paths exist before committing + diff --git a/favicon.ico b/favicon.ico index c425789e365a8a188a46a082f4d7a0ed8b6a37a8..be74abd69ad6a32de7375df13cab9354798e328f 100644 GIT binary patch literal 766 zcmc(dze~eV5XUd2fg&jH87YDYDQKxq1{4b-_ydP-wqS9vgGh17QXQQAwOE{VaBvi* zmu^z9t({y-&1ey8Y_x;?x+`BviV9;aRjMgZ8M*!jgkRrFqSI9;F zHyfX@Az|AvVmn~YWWZP`0&JWEY~BFm?*Vq}VD7&_%x%MP$p`D`4JMC!K|B7pt?Mmp zUJAB7rxMXS6=!P+AtLU9V)J#61WPxwipRXCHO{BJ`l{m53#=t97a!znv~vfmr|AaP zRGIT7#0FyJy3Z*hL{GQp-0TRhX8UzZ)+>%?mK0^goaX4Q;xU*4f?|1HZ&b{yB0+zl=1RB?( z%?4Hi*eHk+6Y`q=ZR|*f=grN3dwOlUvhonE64{xr+$&>7!mKvE4;u>SvBxKl1P{te zOcMK5MmbOaOuy_xb(&a1q9GuT3a1LhCFQ-v6KIK5%~${QEN4V*SGfH+==NfJxHpqR zmFm|?yMIc0t-C!Us{1?=-savQ6Wo~dk;jn#3iJm+_cWNQv$O4JWdt9HRnqRv5nDAk z39T+MoF&HPORg{;-lVjjL9b8qHL=aYdhk2IE^Om6`yqi*V);1HEcB#^Px2y{8ofUa zCrBt80-amnbc@#Y;13IYE)N?~=_d$V=Qc^-Q6!#l*l116dp^$R`1UNBJu z-jjX_Zb}~~YBIzo;aq~a&%;KWbIb??Nj!GG>i=JZQ!(jJf`#4Sfb>z+NuQK{lBm~z zpE0x|7N}Xe|5zzQHjc)8AKvkD diff --git a/scripts/optimize_images.sh b/scripts/optimize_images.sh new file mode 100755 index 0000000..f009c87 --- /dev/null +++ b/scripts/optimize_images.sh @@ -0,0 +1,156 @@ +#!/bin/bash +# Image optimization script for Deep-MI website +# Requires ImageMagick: brew install imagemagick + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Check if ImageMagick is installed +if ! command -v magick &> /dev/null && ! command -v convert &> /dev/null; then + echo -e "${RED}Error: ImageMagick is not installed${NC}" + echo "Install it with: brew install imagemagick" + exit 1 +fi + +# Determine the correct command (magick or convert) +if command -v magick &> /dev/null; then + CONVERT_CMD="magick" +else + CONVERT_CMD="convert" +fi + +echo "Image Optimization Script" +echo "==========================" +echo "" + +# Function to optimize member photos +optimize_member_photo() { + local input_file="$1" + local filename=$(basename "$input_file") + local temp_file="${input_file}.tmp.jpg" + + echo -e "${YELLOW}Processing member photo: $filename${NC}" + + # Resize to 365x365 if needed, set to 72 DPI, and compress + $CONVERT_CMD "$input_file" \ + -resize 365x365^ \ + -gravity center \ + -extent 365x365 \ + -density 72 \ + -quality 85 \ + -strip \ + "$temp_file" + + # Check file size + size=$(wc -c < "$temp_file" | tr -d ' ') + size_kb=$((size / 1024)) + + if [ $size_kb -gt 150 ]; then + echo -e " ${YELLOW}Warning: ${filename} is ${size_kb}KB (recommended < 100KB)${NC}" + echo -e " Attempting further compression..." + $CONVERT_CMD "$temp_file" -quality 75 "$temp_file" + size=$(wc -c < "$temp_file" | tr -d ' ') + size_kb=$((size / 1024)) + fi + + mv "$temp_file" "$input_file" + echo -e " ${GREEN}✓ Optimized: ${size_kb}KB${NC}" +} + +# Function to optimize publication teasers +optimize_pub_teaser() { + local input_file="$1" + local filename=$(basename "$input_file") + local temp_file="${input_file}.tmp.jpg" + + echo -e "${YELLOW}Processing publication teaser: $filename${NC}" + + # Resize to max height 200px and compress + $CONVERT_CMD "$input_file" \ + -resize x200\> \ + -quality 85 \ + -strip \ + "$temp_file" + + # Check file size + size=$(wc -c < "$temp_file" | tr -d ' ') + size_kb=$((size / 1024)) + + if [ $size_kb -gt 100 ]; then + echo -e " ${YELLOW}Warning: ${filename} is ${size_kb}KB (max 100KB)${NC}" + echo -e " Applying maximum compression..." + $CONVERT_CMD "$temp_file" -quality 70 "$temp_file" + size=$(wc -c < "$temp_file" | tr -d ' ') + size_kb=$((size / 1024)) + fi + + mv "$temp_file" "$input_file" + echo -e " ${GREEN}✓ Optimized: ${size_kb}KB${NC}" +} + +# Main script +case "$1" in + member) + if [ -z "$2" ]; then + echo "Usage: $0 member " + echo "Example: $0 member static/img/members/doe.jpg" + exit 1 + fi + optimize_member_photo "$2" + ;; + + pub) + if [ -z "$2" ]; then + echo "Usage: $0 pub " + echo "Example: $0 pub static/pub/doe_2026.jpg" + exit 1 + fi + optimize_pub_teaser "$2" + ;; + + all-members) + echo "Optimizing all member photos..." + echo "" + for img in static/img/members/*.jpg; do + if [ -f "$img" ]; then + optimize_member_photo "$img" + fi + done + echo "" + echo -e "${GREEN}✓ All member photos optimized!${NC}" + ;; + + all-pubs) + echo "Optimizing all publication teasers..." + echo "" + for img in static/pub/*.jpg; do + if [ -f "$img" ]; then + optimize_pub_teaser "$img" + fi + done + echo "" + echo -e "${GREEN}✓ All publication teasers optimized!${NC}" + ;; + + *) + echo "Image Optimization Script" + echo "" + echo "Usage:" + echo " $0 member Optimize a member photo (365x365)" + echo " $0 pub Optimize a publication teaser (max 200px height)" + echo " $0 all-members Optimize all member photos" + echo " $0 all-pubs Optimize all publication teasers" + echo "" + echo "Examples:" + echo " $0 member static/img/members/doe.jpg" + echo " $0 pub static/pub/doe_2026.jpg" + echo " $0 all-members" + exit 1 + ;; +esac + diff --git a/scripts/pre-commit.sh b/scripts/pre-commit.sh new file mode 100755 index 0000000..698b05b --- /dev/null +++ b/scripts/pre-commit.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +exit 0 +echo "✓ All pre-commit checks passed!" +echo "" + +fi + read + echo "Press Enter to continue anyway, or Ctrl+C to abort." + echo "Consider optimizing images before committing." + echo "" + echo "$large_files" + echo "⚠️ Warning: Large files detected:" +if [ -n "$large_files" ]; then + +done) + fi + fi + echo "$file ($(($size / 1024))KB)" + if [ $size -gt 1048576 ]; then # 1MB + size=$(wc -c < "$file") + if [ -f "$file" ]; then +large_files=$(git diff --cached --name-only --diff-filter=ACM | while read file; do +echo "→ Checking for large files..." +echo "" +# Check for large files + +fi + exit 1 + echo " Fix the errors above before committing." + echo "✗ Post validation failed!" + echo "" +if ! ./scripts/validate_posts.sh; then +echo "→ Validating posts..." +echo "" +# Validate posts + +fi + exit 1 + echo " Fix the errors above before committing." + echo "✗ YAML validation failed!" + echo "" +if ! ./scripts/validate_yaml.rb; then +echo "→ Validating YAML files..." +# Validate YAML files + +fi + exit 0 + echo "⚠️ Validation scripts not found. Skipping validation." +if [ ! -f "scripts/validate_yaml.rb" ] || [ ! -f "scripts/validate_posts.sh" ]; then +# Check if validation scripts exist + +echo "Running pre-commit checks..." + +set -e + +# Install: cp scripts/pre-commit.sh .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit +# Pre-commit hook for Deep-MI website diff --git a/scripts/validate_posts.sh b/scripts/validate_posts.sh new file mode 100755 index 0000000..984440a --- /dev/null +++ b/scripts/validate_posts.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Validate that post filenames follow the YYYY-MM-DD-title.md convention + +set -e + +echo "Validating post filenames..." + +error_count=0 + +for file in _posts/*.md; do + if [ ! -f "$file" ]; then + continue + fi + + filename=$(basename "$file") + + # Check naming convention: YYYY-MM-DD-*.md + if [[ ! $filename =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}-.+\.md$ ]]; then + echo "✗ Invalid post filename: $filename" + echo " Expected format: YYYY-MM-DD-title.md" + ((error_count++)) + continue + fi + + # Extract and validate date + date_part="${filename:0:10}" + if ! date -j -f "%Y-%m-%d" "$date_part" > /dev/null 2>&1; then + echo "✗ Invalid date in filename: $filename" + ((error_count++)) + continue + fi + + # Check for front matter + if ! head -n 1 "$file" | grep -q "^---$"; then + echo "✗ Missing front matter in: $filename" + ((error_count++)) + continue + fi + + echo "✓ $filename" +done + +if [ $error_count -eq 0 ]; then + echo "" + echo "✓ All post files are valid!" + exit 0 +else + echo "" + echo "✗ Found $error_count error(s)" + exit 1 +fi + diff --git a/scripts/validate_yaml.rb b/scripts/validate_yaml.rb new file mode 100755 index 0000000..a66719a --- /dev/null +++ b/scripts/validate_yaml.rb @@ -0,0 +1,107 @@ +#!/usr/bin/env ruby +# Validate YAML files in _data directory + +require 'yaml' +require 'date' + +def validate_file(filepath) + puts "Validating #{filepath}..." + + begin + data = YAML.load_file(filepath) + + # File-specific validations + case File.basename(filepath) + when 'members.yml' + validate_members(data, filepath) + when 'publications.yml' + validate_publications(data, filepath) + end + + puts "✓ #{filepath} is valid" + true + rescue => e + puts "✗ Error in #{filepath}: #{e.message}" + false + end +end + +def validate_members(data, filepath) + required_fields = ['name', 'image', 'position'] + + data.each_with_index do |member, idx| + required_fields.each do |field| + unless member[field] + raise "Member at index #{idx} missing required field: #{field}" + end + end + + # Check if image file exists + image_path = File.join(File.dirname(__dir__), member['image']) + unless File.exist?(image_path) + puts " Warning: Image not found for #{member['name']}: #{member['image']}" + end + end +end + +def validate_publications(data, filepath) + required_fields = ['id', 'authors', 'title', 'journal', 'type', 'year'] + valid_types = ['journal', 'conference', 'workshop', 'abstract', 'poster', 'preprint'] + + ids_seen = {} + + data.each_with_index do |pub, idx| + # Check required fields + required_fields.each do |field| + unless pub[field] + raise "Publication at index #{idx} missing required field: #{field}" + end + end + + # Check for duplicate IDs + if ids_seen[pub['id']] + raise "Duplicate publication ID: #{pub['id']}" + end + ids_seen[pub['id']] = true + + # Validate type + unless valid_types.include?(pub['type']) + raise "Publication #{pub['id']} has invalid type: #{pub['type']}" + end + + # Validate year + year = pub['year'].to_i + unless year >= 1900 && year <= Date.today.year + 1 + raise "Publication #{pub['id']} has invalid year: #{pub['year']}" + end + + # Check if files exist + ['image', 'bibtex', 'pdf'].each do |file_field| + next unless pub[file_field] + next if pub[file_field].start_with?('http') + + file_path = File.join(File.dirname(__dir__), pub[file_field]) + unless File.exist?(file_path) + puts " Warning: File not found for #{pub['id']}: #{pub[file_field]}" + end + end + end +end + +# Main execution +exit_code = 0 + +Dir.glob('_data/*.yml').each do |file| + unless validate_file(file) + exit_code = 1 + end +end + +if exit_code == 0 + puts "\n✓ All YAML files are valid!" +else + puts "\n✗ Some YAML files have errors" +end + +exit exit_code + From edee7b27c4f38a34b04d6fadcd1def6ad5bcbe69 Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 16:33:13 +0100 Subject: [PATCH 02/14] Update scripts/validate_yaml.rb Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/validate_yaml.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/validate_yaml.rb b/scripts/validate_yaml.rb index a66719a..54057cf 100755 --- a/scripts/validate_yaml.rb +++ b/scripts/validate_yaml.rb @@ -8,7 +8,7 @@ def validate_file(filepath) puts "Validating #{filepath}..." begin - data = YAML.load_file(filepath) + data = YAML.safe_load_file(filepath, permitted_classes: [Date, Time], aliases: true) # File-specific validations case File.basename(filepath) From 25dd2af0ee1493fcfa2f39f4f6c9833b6dfbc6eb Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 16:33:35 +0100 Subject: [PATCH 03/14] Update Gemfile Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Gemfile | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Gemfile b/Gemfile index 3430421..8e3aa4e 100644 --- a/Gemfile +++ b/Gemfile @@ -3,15 +3,8 @@ source 'https://rubygems.org' # Specify Ruby version for consistency ruby '>= 2.7.0' -# Core Jekyll gem for static site generation -gem "jekyll" - # GitHub Pages gem (includes Jekyll and plugins used by GitHub Pages) gem 'github-pages' - -# Pagination support for posts and publications -gem 'jekyll-paginate' - # HTML validation and link checking for CI/CD gem "html-proofer" From 248ad506ceaabad29edcef191eedaa139d8fbfa0 Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 16:39:10 +0100 Subject: [PATCH 04/14] Update scripts/validate_posts.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/validate_posts.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/validate_posts.sh b/scripts/validate_posts.sh index 984440a..9e81ccd 100755 --- a/scripts/validate_posts.sh +++ b/scripts/validate_posts.sh @@ -24,7 +24,8 @@ for file in _posts/*.md; do # Extract and validate date date_part="${filename:0:10}" - if ! date -j -f "%Y-%m-%d" "$date_part" > /dev/null 2>&1; then + if ! date -d "$date_part" "+%Y-%m-%d" > /dev/null 2>&1 && \ + ! date -j -f "%Y-%m-%d" "$date_part" > /dev/null 2>&1; then echo "✗ Invalid date in filename: $filename" ((error_count++)) continue From 95ecddf8bc211478c1393c1a276d5d1ccb004ce8 Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 16:37:18 +0100 Subject: [PATCH 05/14] add validation to CI --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 939ba71..41eae31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: branches: [ main, master ] jobs: - build: + validate-and-build: runs-on: ubuntu-latest steps: @@ -19,6 +19,12 @@ jobs: ruby-version: '3.1' bundler-cache: true + - name: Validate YAML files + run: ruby scripts/validate_yaml.rb + + - name: Validate posts + run: bash scripts/validate_posts.sh + - name: Build Jekyll site run: bundle exec jekyll build env: From ab0942a8e11732df9a6853f4c6ceaa569a547eaf Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 16:41:29 +0100 Subject: [PATCH 06/14] pub img warning size 100kb --- CONTRIBUTING.md | 4 ++-- QUICKSTART.md | 2 +- _templates/member-template.md | 2 +- scripts/optimize_images.sh | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0a427ba..d029274 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -175,9 +175,9 @@ bundle exec htmlproofer ./_site --disable-external - Optimize images before uploading - Use descriptive filenames - Keep file sizes small: - - Member photos: ~50-100KB + - Member photos: max 100KB - Publication teasers: max 100KB - - Post images: < 500KB + - Post images: max 500KB ### Code diff --git a/QUICKSTART.md b/QUICKSTART.md index 0c613d6..37b79c5 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -58,7 +58,7 @@ bundle exec jekyll serve | Type | Size | Format | Max Size | |------|------|--------|----------| -| Member Photo | 365×365px, 72 DPI | JPG | ~100KB | +| Member Photo | 365×365px, 72 DPI | JPG | 100KB | | Publication Teaser | Max 200px height | JPG | 100KB | | Post Images | Reasonable | JPG/PNG | 500KB | diff --git a/_templates/member-template.md b/_templates/member-template.md index 8fbc668..715ee3a 100644 --- a/_templates/member-template.md +++ b/_templates/member-template.md @@ -27,6 +27,6 @@ Copy this template when adding a new member to `_data/members.yml` ## Tips - Use single quotes for multi-line descriptions -- Keep file size under 100KB +- Keep file size to max 100KB (optimization script helps with this) - Use consistent naming: lastname.jpg diff --git a/scripts/optimize_images.sh b/scripts/optimize_images.sh index f009c87..5d26b9e 100755 --- a/scripts/optimize_images.sh +++ b/scripts/optimize_images.sh @@ -50,8 +50,8 @@ optimize_member_photo() { size=$(wc -c < "$temp_file" | tr -d ' ') size_kb=$((size / 1024)) - if [ $size_kb -gt 150 ]; then - echo -e " ${YELLOW}Warning: ${filename} is ${size_kb}KB (recommended < 100KB)${NC}" + if [ $size_kb -gt 100 ]; then + echo -e " ${YELLOW}Warning: ${filename} is ${size_kb}KB (max 100KB)${NC}" echo -e " Attempting further compression..." $CONVERT_CMD "$temp_file" -quality 75 "$temp_file" size=$(wc -c < "$temp_file" | tr -d ' ') From 9aae806f0729df2785d1a176cf1766010ffbd36a Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 16:45:37 +0100 Subject: [PATCH 07/14] fix pre-commit hook script --- scripts/pre-commit.sh | 89 +++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/scripts/pre-commit.sh b/scripts/pre-commit.sh index 698b05b..fed2186 100755 --- a/scripts/pre-commit.sh +++ b/scripts/pre-commit.sh @@ -1,58 +1,57 @@ #!/bin/bash +# Pre-commit hook for Deep-MI website +# Install: cp scripts/pre-commit.sh .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit -exit 0 -echo "✓ All pre-commit checks passed!" -echo "" +set -e +echo "Running pre-commit checks..." + +# Check if validation scripts exist +if [ ! -f "scripts/validate_yaml.rb" ] || [ ! -f "scripts/validate_posts.sh" ]; then + echo "⚠️ Validation scripts not found. Skipping validation." + exit 0 fi - read - echo "Press Enter to continue anyway, or Ctrl+C to abort." - echo "Consider optimizing images before committing." + +# Validate YAML files +echo "→ Validating YAML files..." +if ! ./scripts/validate_yaml.rb; then echo "" - echo "$large_files" - echo "⚠️ Warning: Large files detected:" -if [ -n "$large_files" ]; then + echo "✗ YAML validation failed!" + echo " Fix the errors above before committing." + exit 1 +fi -done) - fi - fi - echo "$file ($(($size / 1024))KB)" - if [ $size -gt 1048576 ]; then # 1MB - size=$(wc -c < "$file") - if [ -f "$file" ]; then -large_files=$(git diff --cached --name-only --diff-filter=ACM | while read file; do -echo "→ Checking for large files..." +# Validate posts echo "" -# Check for large files - -fi - exit 1 - echo " Fix the errors above before committing." - echo "✗ Post validation failed!" - echo "" -if ! ./scripts/validate_posts.sh; then echo "→ Validating posts..." -echo "" -# Validate posts - -fi - exit 1 - echo " Fix the errors above before committing." - echo "✗ YAML validation failed!" +if ! ./scripts/validate_posts.sh; then echo "" -if ! ./scripts/validate_yaml.rb; then -echo "→ Validating YAML files..." -# Validate YAML files - + echo "✗ Post validation failed!" + echo " Fix the errors above before committing." + exit 1 fi - exit 0 - echo "⚠️ Validation scripts not found. Skipping validation." -if [ ! -f "scripts/validate_yaml.rb" ] || [ ! -f "scripts/validate_posts.sh" ]; then -# Check if validation scripts exist -echo "Running pre-commit checks..." +# Check for large files +echo "" +echo "→ Checking for large files..." +large_files=$(git diff --cached --name-only --diff-filter=ACM | while read file; do + if [ -f "$file" ]; then + size=$(wc -c < "$file") + if [ $size -gt 1048576 ]; then # 1MB + echo "$file ($(($size / 1024))KB)" + fi + fi +done) -set -e +if [ -n "$large_files" ]; then + echo "⚠️ Warning: Large files detected:" + echo "$large_files" + echo "" + echo "Consider optimizing images before committing." + echo "Press Enter to continue anyway, or Ctrl+C to abort." + read +fi -# Install: cp scripts/pre-commit.sh .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit -# Pre-commit hook for Deep-MI website +echo "" +echo "✓ All pre-commit checks passed!" +exit 0 From eb349e397267c2253eb45ada725b5de66dfbb6fd Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 16:57:36 +0100 Subject: [PATCH 08/14] Update QUICKSTART.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- QUICKSTART.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QUICKSTART.md b/QUICKSTART.md index 37b79c5..8c2d000 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -20,7 +20,7 @@ bundle exec jekyll serve ### News Post (5 minutes) -1. Create: `_posts/2026-02-13-Title.md` +1. Create: `_posts/YYYY-MM-DD-Title.md` 2. Copy template from: `_templates/post-template.md` 3. Add images to: `/static/img/` 4. Validate: `./scripts/validate_posts.sh` From 18e8ed507b51c5969d8e2c6ccfe11c11cef1d6cb Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 17:01:07 +0100 Subject: [PATCH 09/14] Update scripts/pre-commit.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/pre-commit.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/pre-commit.sh b/scripts/pre-commit.sh index fed2186..0f2b639 100755 --- a/scripts/pre-commit.sh +++ b/scripts/pre-commit.sh @@ -48,8 +48,11 @@ if [ -n "$large_files" ]; then echo "$large_files" echo "" echo "Consider optimizing images before committing." - echo "Press Enter to continue anyway, or Ctrl+C to abort." - read + # Only prompt if running interactively and not explicitly skipped + if [ -t 0 ] && [ -z "$SKIP_LARGE_FILE_PROMPT" ]; then + echo "Press Enter to continue anyway, or Ctrl+C to abort." + read + fi fi echo "" From 3f055ef043dffe5e3a7333f736035a989a87127d Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 17:04:49 +0100 Subject: [PATCH 10/14] unify github path url --- _data/publications.yml | 22 +++++++++++----------- scripts/validate_yaml.rb | 7 ++++++- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/_data/publications.yml b/_data/publications.yml index 07d6223..91ebf11 100644 --- a/_data/publications.yml +++ b/_data/publications.yml @@ -24,7 +24,7 @@ # arxiv: 1904.02082 # doi: 10.1002/mrm.28022 # pdf: '/static/pub/lastname_year_ifnecessarymore.pdf' -# github: /deep-mi/project +# github: deep-mi/project # bibtex: '/static/pub/lastname_year_ifnecessarymore.bib' # links: # - name: 'Some coauthor Lab' @@ -42,7 +42,7 @@ arxiv: 'https://arxiv.org/abs/2505.02048' doi: '10.1109/TMI.2025.3650412' pdf: 'https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=11322583' - github: 'deep-MI/YODA' + github: 'deep-mi/YODA' - id: 'pollak_2025' authors: '**Pollak C**, **Kügler D**, Bauer T, Rüber T, **Reuter M**' @@ -54,7 +54,7 @@ bibtex: '/static/pub/pollak_2025.bib' doi: 10.1162/imag_a_00446 pmcid: 'PMC11917724' - github: 'deep-MI/LIT' + github: 'deep-mi/LIT' - id: 'peng_2025' authors: 'Xu P, **Estrada S**, Etteldorf R, Liu D, Shahid M, Zeng W, Früh D, **Reuter M**, Breteler MMB, Aziz NA' @@ -89,7 +89,7 @@ pdf: 'https://direct.mit.edu/imag/article-pdf/doi/10.1162/imag_a_00180/2375595/imag_a_00180.pdf' bibtex: '/static/pub/henschel_2024.bib' doi: 10.1162/imag_a_00180 - github: /deep-mi/VINNA4neonates + github: deep-mi/VINNA4neonates - id: 'ewert_2024a' authors: '**Ewert C\***, **Kügler D\***, Stirnberg R, Koch A, Yendiki A, **Reuter M**. (\*co-first)' @@ -102,7 +102,7 @@ bibtex: '/static/pub/ewert_2024a.bib' doi: 10.1162/imag_a_00121 pdf: 'https://direct.mit.edu/imag/article-pdf/doi/10.1162/imag_a_00121/2358010/imag_a_00121.pdf' - github: /deep-mi/DISCUS + github: deep-mi/DISCUS - id: 'estrada2023' authors: '**Estrada S**, **Kügler D**, Bahrami E, Xu P, Mousa D, Breteler MMB, Aziz NA, **Reuter M**' @@ -116,7 +116,7 @@ pmcid: 'PMC11576934' doi: 10.1162/imag_a_00034 pdf: 'https://direct.mit.edu/imag/article-pdf/doi/10.1162/imag_a_00034/2182290/imag_a_00034.pdf' - github: /deep-mi/FastSurfer + github: deep-mi/FastSurfer - id: 'diers_2023' authors: '**Diers K**, Baumeister H, Jessen F, Düzel E, Berron D, **Reuter M**' @@ -128,7 +128,7 @@ bibtex: '/static/pub/diers_2023.bib' doi: 10.1016/j.neuroimage.2023.120182 pdf: 'https://doi.org/10.1016/j.neuroimage.2023.120182' - github: 'deep-MI/Hipsta' + github: 'deep-mi/Hipsta' - id: 'pollak_2023b' authors: '**Pollak C**, **Kügler D**, Breteler MMB, **Reuter M**' @@ -140,7 +140,7 @@ bibtex: '/static/pub/pollak_2023b.bib' doi: 10.1016/j.neuroimage.2023.120176 pdf: 'https://www.sciencedirect.com/science/article/pii/S1053811923003270/pdfft?isDTMRedir=true&download=true' - github: 'deep-MI/head-motion-tools' + github: 'deep-mi/head-motion-tools' - id: 'pollak_2023a' @@ -162,7 +162,7 @@ journal: '*NeuroImage*' type: 'journal' year: 2022 - github: /deep-mi/FastSurfer + github: deep-mi/FastSurfer doi: 10.1016/j.neuroimage.2022.119703 pmcid: 'PMC9771831' image: '/static/pub/faber_2022.jpg' @@ -187,7 +187,7 @@ journal: '*Medical Imaging with Deep Learning, Conference (MIDL)*' type: 'journal' year: 2022 - github: /deep-mi/3d-neuro-seg + github: deep-mi/3d-neuro-seg image: '/static/pub/roy_2022.png' pdf: 'https://openreview.net/pdf?id=Ob62JPB_CDF' bibtex: '/static/pub/roy_2022.bib' @@ -203,7 +203,7 @@ arxiv: 2112.09654 pmcid: 'PMC9801435' image: '/static/pub/henschel_2022.jpg' - github: /deep-mi/FastSurfer + github: deep-mi/FastSurfer doi: 10.1016/j.neuroimage.2022.118933 pdf: 'https://www.sciencedirect.com/science/article/pii/S1053811922000623/pdfft?md5=0522f6a35232c9e9e57674816ce19a7a&pid=1-s2.0-S1053811922000623-main.pdf' bibtex: '/static/pub/henschel_2022.bib' diff --git a/scripts/validate_yaml.rb b/scripts/validate_yaml.rb index 54057cf..c15492c 100755 --- a/scripts/validate_yaml.rb +++ b/scripts/validate_yaml.rb @@ -8,7 +8,12 @@ def validate_file(filepath) puts "Validating #{filepath}..." begin - data = YAML.safe_load_file(filepath, permitted_classes: [Date, Time], aliases: true) + # Use safe_load_file for Ruby >= 3.1, fall back to load_file for older versions + data = if YAML.respond_to?(:safe_load_file) + YAML.safe_load_file(filepath, permitted_classes: [Date, Time], aliases: true) + else + YAML.load_file(filepath) + end # File-specific validations case File.basename(filepath) From 3683cc3cae100e1f842a718820e2d83c47d4bc26 Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 17:07:31 +0100 Subject: [PATCH 11/14] make validate-yaml work from any dir --- _templates/publication-template.md | 4 +++- scripts/validate_yaml.rb | 14 ++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/_templates/publication-template.md b/_templates/publication-template.md index 60fc5e9..57da04b 100644 --- a/_templates/publication-template.md +++ b/_templates/publication-template.md @@ -20,7 +20,7 @@ Copy this template when adding a new publication to `_data/publications.yml` arxiv: '2401.12345' # optional - arXiv ID doi: '10.1234/journal.2024.123456' # strongly recommended pdf: 'https://journal.com/paper.pdf' # or '/static/pub/lastname_year.pdf' - github: 'deep-mi/repository-name' # optional - relative to github.com + github: 'deep-mi/repository-name' # optional - format: org/repo (no leading slash) bibtex: '/static/pub/lastname_year.bib' links: # optional - additional links - name: 'Project Website' @@ -51,5 +51,7 @@ Copy this template when adding a new publication to `_data/publications.yml` - Always include DOI if available - Bold current and former lab members in author list - Keep image file size small (max 100KB) +- GitHub format: `'deep-mi/repository-name'` (lowercase org, no leading slash) + - Rendered as: `https://github.com/deep-mi/repository-name` - Test that all file paths exist before committing diff --git a/scripts/validate_yaml.rb b/scripts/validate_yaml.rb index c15492c..c505296 100755 --- a/scripts/validate_yaml.rb +++ b/scripts/validate_yaml.rb @@ -4,6 +4,9 @@ require 'yaml' require 'date' +# Get the project root directory (parent of scripts directory) +PROJECT_ROOT = File.expand_path('..', __dir__) + def validate_file(filepath) puts "Validating #{filepath}..." @@ -41,8 +44,8 @@ def validate_members(data, filepath) end end - # Check if image file exists - image_path = File.join(File.dirname(__dir__), member['image']) + # Check if image file exists (resolve relative to project root) + image_path = File.expand_path(member['image'], PROJECT_ROOT) unless File.exist?(image_path) puts " Warning: Image not found for #{member['name']}: #{member['image']}" end @@ -80,12 +83,12 @@ def validate_publications(data, filepath) raise "Publication #{pub['id']} has invalid year: #{pub['year']}" end - # Check if files exist + # Check if files exist (resolve relative to project root) ['image', 'bibtex', 'pdf'].each do |file_field| next unless pub[file_field] next if pub[file_field].start_with?('http') - file_path = File.join(File.dirname(__dir__), pub[file_field]) + file_path = File.expand_path(pub[file_field], PROJECT_ROOT) unless File.exist?(file_path) puts " Warning: File not found for #{pub['id']}: #{pub[file_field]}" end @@ -96,6 +99,9 @@ def validate_publications(data, filepath) # Main execution exit_code = 0 +# Change to project root directory to find _data files +Dir.chdir(PROJECT_ROOT) + Dir.glob('_data/*.yml').each do |file| unless validate_file(file) exit_code = 1 From 0173170e594dac4315fa6de646373314fc485e1a Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 17:17:31 +0100 Subject: [PATCH 12/14] backup images and cleanup --- scripts/optimize_images.sh | 178 ++++++++++++++++++++++++++++++++++--- 1 file changed, 166 insertions(+), 12 deletions(-) diff --git a/scripts/optimize_images.sh b/scripts/optimize_images.sh index 5d26b9e..5883199 100755 --- a/scripts/optimize_images.sh +++ b/scripts/optimize_images.sh @@ -10,6 +10,66 @@ GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color +# Array to track temporary files for cleanup +TEMP_FILES=() + +# Array to track backup files +BACKUP_FILES=() + +# Cleanup function +cleanup() { + if [ ${#TEMP_FILES[@]} -gt 0 ]; then + for temp_file in "${TEMP_FILES[@]}"; do + if [ -f "$temp_file" ]; then + rm -f "$temp_file" + fi + done + fi +} + +# Set up trap to cleanup on exit or error +trap cleanup EXIT ERR INT TERM + +# Function to create backup +create_backup() { + local input_file="$1" + local backup_file="${input_file}.backup" + + if cp "$input_file" "$backup_file" 2>/dev/null; then + BACKUP_FILES+=("$backup_file") + return 0 + else + echo -e " ${RED}✗ Error: Failed to create backup${NC}" + return 1 + fi +} + +# Function to restore from backup +restore_backup() { + local input_file="$1" + local backup_file="${input_file}.backup" + + if [ -f "$backup_file" ]; then + mv "$backup_file" "$input_file" + # Remove from backup tracking + BACKUP_FILES=("${BACKUP_FILES[@]/$backup_file}") + return 0 + fi + return 1 +} + +# Function to remove backup +remove_backup() { + local input_file="$1" + local backup_file="${input_file}.backup" + + if [ -f "$backup_file" ]; then + rm -f "$backup_file" + # Remove from backup tracking + BACKUP_FILES=("${BACKUP_FILES[@]/$backup_file}") + fi +} + # Check if ImageMagick is installed if ! command -v magick &> /dev/null && ! command -v convert &> /dev/null; then echo -e "${RED}Error: ImageMagick is not installed${NC}" @@ -34,17 +94,41 @@ optimize_member_photo() { local filename=$(basename "$input_file") local temp_file="${input_file}.tmp.jpg" + # Add to cleanup list + TEMP_FILES+=("$temp_file") + echo -e "${YELLOW}Processing member photo: $filename${NC}" + # Check if input file exists and is readable + if [ ! -f "$input_file" ]; then + echo -e " ${RED}✗ Error: File not found: $input_file${NC}" + return 1 + fi + + if [ ! -r "$input_file" ]; then + echo -e " ${RED}✗ Error: File not readable: $input_file${NC}" + return 1 + fi + + # Create backup before processing + if ! create_backup "$input_file"; then + return 1 + fi + # Resize to 365x365 if needed, set to 72 DPI, and compress - $CONVERT_CMD "$input_file" \ + if ! $CONVERT_CMD "$input_file" \ -resize 365x365^ \ -gravity center \ -extent 365x365 \ -density 72 \ -quality 85 \ -strip \ - "$temp_file" + "$temp_file" 2>/dev/null; then + echo -e " ${RED}✗ Error: Failed to process image (possibly corrupt or invalid format)${NC}" + rm -f "$temp_file" + restore_backup "$input_file" + return 1 + fi # Check file size size=$(wc -c < "$temp_file" | tr -d ' ') @@ -53,13 +137,23 @@ optimize_member_photo() { if [ $size_kb -gt 100 ]; then echo -e " ${YELLOW}Warning: ${filename} is ${size_kb}KB (max 100KB)${NC}" echo -e " Attempting further compression..." - $CONVERT_CMD "$temp_file" -quality 75 "$temp_file" + if ! $CONVERT_CMD "$temp_file" -quality 75 "$temp_file" 2>/dev/null; then + echo -e " ${RED}✗ Error: Failed to compress image${NC}" + rm -f "$temp_file" + restore_backup "$input_file" + return 1 + fi size=$(wc -c < "$temp_file" | tr -d ' ') size_kb=$((size / 1024)) fi mv "$temp_file" "$input_file" - echo -e " ${GREEN}✓ Optimized: ${size_kb}KB${NC}" + # Remove from cleanup list after successful move + TEMP_FILES=("${TEMP_FILES[@]/$temp_file}") + # Remove backup after successful optimization + remove_backup "$input_file" + echo -e " ${GREEN}✓ Optimized: ${size_kb}KB (backup removed)${NC}" + return 0 } # Function to optimize publication teasers @@ -68,14 +162,38 @@ optimize_pub_teaser() { local filename=$(basename "$input_file") local temp_file="${input_file}.tmp.jpg" + # Add to cleanup list + TEMP_FILES+=("$temp_file") + echo -e "${YELLOW}Processing publication teaser: $filename${NC}" + # Check if input file exists and is readable + if [ ! -f "$input_file" ]; then + echo -e " ${RED}✗ Error: File not found: $input_file${NC}" + return 1 + fi + + if [ ! -r "$input_file" ]; then + echo -e " ${RED}✗ Error: File not readable: $input_file${NC}" + return 1 + fi + + # Create backup before processing + if ! create_backup "$input_file"; then + return 1 + fi + # Resize to max height 200px and compress - $CONVERT_CMD "$input_file" \ + if ! $CONVERT_CMD "$input_file" \ -resize x200\> \ -quality 85 \ -strip \ - "$temp_file" + "$temp_file" 2>/dev/null; then + echo -e " ${RED}✗ Error: Failed to process image (possibly corrupt or invalid format)${NC}" + rm -f "$temp_file" + restore_backup "$input_file" + return 1 + fi # Check file size size=$(wc -c < "$temp_file" | tr -d ' ') @@ -84,13 +202,23 @@ optimize_pub_teaser() { if [ $size_kb -gt 100 ]; then echo -e " ${YELLOW}Warning: ${filename} is ${size_kb}KB (max 100KB)${NC}" echo -e " Applying maximum compression..." - $CONVERT_CMD "$temp_file" -quality 70 "$temp_file" + if ! $CONVERT_CMD "$temp_file" -quality 70 "$temp_file" 2>/dev/null; then + echo -e " ${RED}✗ Error: Failed to compress image${NC}" + rm -f "$temp_file" + restore_backup "$input_file" + return 1 + fi size=$(wc -c < "$temp_file" | tr -d ' ') size_kb=$((size / 1024)) fi mv "$temp_file" "$input_file" - echo -e " ${GREEN}✓ Optimized: ${size_kb}KB${NC}" + # Remove from cleanup list after successful move + TEMP_FILES=("${TEMP_FILES[@]/$temp_file}") + # Remove backup after successful optimization + remove_backup "$input_file" + echo -e " ${GREEN}✓ Optimized: ${size_kb}KB (backup removed)${NC}" + return 0 } # Main script @@ -116,25 +244,51 @@ case "$1" in all-members) echo "Optimizing all member photos..." echo "" + success_count=0 + error_count=0 for img in static/img/members/*.jpg; do if [ -f "$img" ]; then - optimize_member_photo "$img" + if optimize_member_photo "$img"; then + ((success_count++)) + else + ((error_count++)) + fi fi done echo "" - echo -e "${GREEN}✓ All member photos optimized!${NC}" + if [ $error_count -eq 0 ]; then + echo -e "${GREEN}✓ All member photos optimized! ($success_count processed)${NC}" + else + echo -e "${YELLOW}⚠ Completed with errors: $success_count succeeded, $error_count failed${NC}" + exit 1 + fi ;; all-pubs) echo "Optimizing all publication teasers..." echo "" + success_count=0 + error_count=0 for img in static/pub/*.jpg; do if [ -f "$img" ]; then - optimize_pub_teaser "$img" + if optimize_pub_teaser "$img"; then + ((success_count++)) + else + ((error_count++)) + fi fi done echo "" - echo -e "${GREEN}✓ All publication teasers optimized!${NC}" + if [ $error_count -eq 0 ]; then + echo -e "${GREEN}✓ All publication teasers optimized! ($success_count processed)${NC}" + else + echo -e "${YELLOW}⚠ Completed with errors: $success_count succeeded, $error_count failed${NC}" + if [ ${#BACKUP_FILES[@]} -gt 0 ]; then + echo -e "${YELLOW}⚠ ${#BACKUP_FILES[@]} backup file(s) retained due to errors${NC}" + echo -e " To restore, run: mv .backup " + fi + exit 1 + fi ;; *) From 8550f84f77015c27d795d8e8c923367a78aae7e1 Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 17:25:36 +0100 Subject: [PATCH 13/14] path resolution fix --- scripts/validate_posts.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/validate_posts.sh b/scripts/validate_posts.sh index 9e81ccd..006ce8d 100755 --- a/scripts/validate_posts.sh +++ b/scripts/validate_posts.sh @@ -23,9 +23,10 @@ for file in _posts/*.md; do fi # Extract and validate date + # Try GNU date (Linux) or BSD date (macOS) - either one succeeding is valid date_part="${filename:0:10}" - if ! date -d "$date_part" "+%Y-%m-%d" > /dev/null 2>&1 && \ - ! date -j -f "%Y-%m-%d" "$date_part" > /dev/null 2>&1; then + if ! ( date -d "$date_part" "+%Y-%m-%d" >/dev/null 2>&1 || \ + date -j -f "%Y-%m-%d" "$date_part" >/dev/null 2>&1 ); then echo "✗ Invalid date in filename: $filename" ((error_count++)) continue From 8165d3192daaa1726d137cc32d17dbf0b6d826c1 Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Fri, 13 Feb 2026 17:34:43 +0100 Subject: [PATCH 14/14] add description to install imagemagick --- CONTRIBUTING.md | 9 ++++++++- QUICKSTART.md | 7 ++++++- README.md | 5 ++++- scripts/optimize_images.sh | 24 +++++++++++++++++++++++- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d029274..137baba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -118,7 +118,14 @@ Optimize images to meet size requirements: ./scripts/optimize_images.sh all-pubs ``` -**Requirements**: ImageMagick (`brew install imagemagick`) +**Requirements**: ImageMagick + +**Installation**: +- macOS: `brew install imagemagick` +- Ubuntu/Debian: `sudo apt-get install imagemagick` +- Fedora/RHEL: `sudo yum install imagemagick` or `sudo dnf install imagemagick` +- Arch Linux: `sudo pacman -S imagemagick` +- Windows: Download from [imagemagick.org](https://imagemagick.org/script/download.php#windows) or use `choco install imagemagick` ### Pre-commit Hook diff --git a/QUICKSTART.md b/QUICKSTART.md index 8c2d000..9fb316c 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -99,11 +99,16 @@ rbenv local 3.1.0 ### Images too large ```bash -# Optimize automatically +# Optimize automatically (requires ImageMagick) ./scripts/optimize_images.sh member static/img/members/photo.jpg ./scripts/optimize_images.sh pub static/pub/teaser.jpg ``` +**Install ImageMagick**: +- macOS: `brew install imagemagick` +- Linux: `sudo apt-get install imagemagick` or `sudo yum install imagemagick` +- Windows: [imagemagick.org](https://imagemagick.org/script/download.php#windows) + ## Useful Commands ```bash diff --git a/README.md b/README.md index af94cfb..6a77f73 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,10 @@ See `CONTRIBUTING.md` for detailed contribution guidelines. ./scripts/optimize_images.sh pub static/pub/paper_id.jpg ``` -Requires ImageMagick: `brew install imagemagick` +**Requirements**: ImageMagick +- macOS: `brew install imagemagick` +- Linux: `sudo apt-get install imagemagick` or `sudo yum install imagemagick` +- Windows: See [imagemagick.org](https://imagemagick.org/script/download.php#windows) ### Pre-commit Hook diff --git a/scripts/optimize_images.sh b/scripts/optimize_images.sh index 5883199..54782b4 100755 --- a/scripts/optimize_images.sh +++ b/scripts/optimize_images.sh @@ -73,7 +73,29 @@ remove_backup() { # Check if ImageMagick is installed if ! command -v magick &> /dev/null && ! command -v convert &> /dev/null; then echo -e "${RED}Error: ImageMagick is not installed${NC}" - echo "Install it with: brew install imagemagick" + echo "" + echo "Please install ImageMagick for your platform:" + echo "" + echo " macOS:" + echo " brew install imagemagick" + echo "" + echo " Ubuntu/Debian Linux:" + echo " sudo apt-get update" + echo " sudo apt-get install imagemagick" + echo "" + echo " Fedora/RHEL/CentOS Linux:" + echo " sudo yum install imagemagick" + echo " # or on newer systems:" + echo " sudo dnf install imagemagick" + echo "" + echo " Arch Linux:" + echo " sudo pacman -S imagemagick" + echo "" + echo " Windows:" + echo " Download from: https://imagemagick.org/script/download.php#windows" + echo " Or use Chocolatey: choco install imagemagick" + echo " Or use Scoop: scoop install imagemagick" + echo "" exit 1 fi