Init Commit
This commit is contained in:
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [zetxek] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: zetxek
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
What browsers did you test?
|
||||
Do you have a public URL to reproduce, or a repository showing the error?
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Setup issue
|
||||
about: Trouble getting your own version of the theme running
|
||||
title: "[setup]"
|
||||
labels: setup
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Link to your source code
|
||||
|
||||
https://github.com/...
|
||||
|
||||
_this helps understand even better the context, and in some occasions will make it possible to get a PR to get the issues fixed_
|
||||
|
||||
_if your code is not publicly available it will be less prioritized to give support - as it will not be as beneficial for the community_
|
||||
|
||||
## What do you want to achieve?
|
||||
|
||||
_describe what you are trying to do_
|
||||
|
||||
## What have you tried so far?
|
||||
|
||||
_describe what steps you have taken: have you created your site, are you customizing the theme, which version are you using..._
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[IDEA]"
|
||||
labels: enhancement
|
||||
assignees: zetxek
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
# Adritian Hugo Theme - AI Coding Instructions
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a **Hugo theme** for personal websites/portfolios, not a standalone site (although a example site is provided, to demonstrate the usage of the theme itself). The theme provides layouts, styling, and functionality that users import via Hugo modules or as a git submodule.
|
||||
|
||||
**Technology Stack:**
|
||||
- **Static Site Generator:** Hugo (v0.136+ extended version required)
|
||||
- **CSS Framework:** Bootstrap 5 (v5.3.8)
|
||||
- **CSS Preprocessor:** SCSS/Sass
|
||||
- **JavaScript Libraries:** Fuse.js (search), DOMPurify (XSS protection)
|
||||
- **Testing:** Playwright (E2E tests)
|
||||
- **Package Manager:** npm
|
||||
- **Templating:** Hugo Templates (Go templates)
|
||||
- **Build Tools:** Hugo's built-in asset pipeline, PostCSS, Autoprefixer
|
||||
|
||||
## Project Architecture
|
||||
|
||||
This is a **Hugo theme** for personal websites/portfolios, not a standalone site. The theme provides layouts, styling, and functionality that users import via Hugo modules or as a git submodule.
|
||||
|
||||
The theme provides a example site (`exampleSite/`) that demonstrates all features and serves as a starting point for users. It is also used for the integration tests.
|
||||
|
||||
**Key Structure:**
|
||||
- `layouts/` - Hugo templates (partials, shortcodes, content types)
|
||||
- `assets/scss/` - Bootstrap 5-based styling with theme customizations
|
||||
- `exampleSite/` - Demo content that gets distributed to users via `adritian-theme-helper` and serves as a github template repository
|
||||
- `archetypes/` - Content templates for different content types
|
||||
- `i18n/` - Multilingual translations (en/es/fr)
|
||||
|
||||
## Content Architecture & Custom Types
|
||||
|
||||
The theme implements **5 custom content types** with specific frontmatter:
|
||||
|
||||
1. **Experience** (`content/experience/`) - Job history with `jobTitle`, `company`, `location`, `duration`
|
||||
2. **Skills** (`content/skills/`) - Technical skills with progress bars and categories
|
||||
3. **Blog** (`content/blog/`) - Posts with sidebar/default layouts
|
||||
4. **Client-work** (`content/client-work/`) - Portfolio pieces
|
||||
5. **Testimonial** (`content/testimonial/`) - User testimonials
|
||||
|
||||
**Critical:** Each content type has a corresponding archetype in `archetypes/` and layout in `layouts/`. Always maintain this trio when adding new types.
|
||||
|
||||
## Development Workflows
|
||||
|
||||
### Local Development
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install # Installs Bootstrap + theme-helper
|
||||
# Theme development (testing with exampleSite)
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
npm test # Full test suite
|
||||
npm run test:e2e # Playwright E2E tests
|
||||
npm run test:e2e:no-menus # Tests without navigation (for menu-less configs)
|
||||
```
|
||||
|
||||
**Test Coverage:** E2E tests validate theme switching, language switching, content rendering, search functionality, and HTML validation across all major features.
|
||||
|
||||
### Performance Optimization
|
||||
```bash
|
||||
npm run build:performance # Optimized build with critical CSS
|
||||
node scripts/optimize-performance.js # Performance analysis
|
||||
```
|
||||
|
||||
## Styling System
|
||||
|
||||
**SCSS Architecture:** Modular approach extending Bootstrap 5
|
||||
- `assets/scss/adritian.scss` - Main entry point importing all partials
|
||||
- `assets/scss/_variables.scss` - Theme color/sizing variables
|
||||
- Individual component files: `_navbar.scss`, `_blog.scss`, `_experience.scss`, etc.
|
||||
|
||||
**Theme Colors:** Uses CSS custom properties with `$base-color` as primary. Dark/light themes switch via CSS variables, not separate stylesheets.
|
||||
|
||||
## Multilingual (i18n) Implementation
|
||||
|
||||
**Translation Strategy:**
|
||||
- Static strings: `i18n/*.yaml` files (navigation, UI elements)
|
||||
- Content: Hugo's file-based translation (`content/en/`, `content/es/`, etc.)
|
||||
- **Post-v1.7.0:** Prefers shortcode-based content over i18n strings for flexibility
|
||||
|
||||
## Shortcode System
|
||||
|
||||
**Primary Content Method:** Pages built with shortcodes rather than traditional Hugo sections.
|
||||
|
||||
Key shortcodes in `layouts/shortcodes/`:
|
||||
- `experience-section` - Displays limited experience list (controlled by `homepageExperienceCount`)
|
||||
- `experience-list` - Print-friendly experience list
|
||||
- `contact-section` - Formspree integration
|
||||
- `showcase-section` - Portfolio display
|
||||
- `about-section`, `text-section` - Content blocks
|
||||
|
||||
**Pattern:** Most homepage sections use shortcodes for maximum customization flexibility.
|
||||
|
||||
## Module Distribution Model
|
||||
|
||||
**Critical Understanding:** This theme is distributed via:
|
||||
1. **Hugo Module** (recommended) - `hugo mod get github.com/zetxek/adritian-free-hugo-theme`
|
||||
2. **Theme Helper Package** - `@zetxek/adritian-theme-helper` downloads demo content
|
||||
3. **Git Submodule** (legacy) - Direct theme inclusion
|
||||
|
||||
**Bootstrap Process:**
|
||||
```bash
|
||||
hugo mod get -u github.com/zetxek/adritian-free-hugo-theme
|
||||
hugo mod npm pack
|
||||
npm install # Installs Bootstrap + theme-helper
|
||||
./node_modules/@zetxek/adritian-theme-helper/dist/scripts/download-content.js
|
||||
```
|
||||
|
||||
## Configuration Patterns
|
||||
|
||||
**Hugo Config (`hugo.toml`):**
|
||||
- Performance optimizations: `[minify]`, `[imaging]`, `[markup]` sections
|
||||
- Custom output formats for content types (footer content excluded from HTML generation)
|
||||
- Module imports section for theme loading
|
||||
|
||||
**Package.json Scripts:**
|
||||
- `serve` - Development with exampleSite
|
||||
- `build` - Production build with minification
|
||||
- Separate test commands for different scenarios
|
||||
|
||||
## Key Integration Points
|
||||
|
||||
**Search:** Powered by Fuse.js with JSON index generation
|
||||
**Contact Forms:** Formspree integration via shortcode
|
||||
**Analytics:** Vercel Analytics support built-in
|
||||
**Performance:** Critical CSS generation, image optimization, minification
|
||||
|
||||
## Common Development Pitfalls
|
||||
|
||||
1. **Missing Dependencies:** Theme requires Bootstrap via npm - users often miss `npm install` step
|
||||
2. **Content Structure:** Custom content types require specific frontmatter fields - validate against archetypes
|
||||
3. **Translation Scope:** Post-v1.7.0 prefers shortcode content over i18n strings
|
||||
4. **Module vs Submodule:** Most issues stem from incorrect installation method
|
||||
|
||||
## Testing Philosophy
|
||||
|
||||
E2E tests validate real user workflows rather than individual components. Tests include theme switching, language switching, navigation, search, and content rendering across different configurations (with/without menus).
|
||||
|
||||
When modifying layouts or adding features, ensure corresponding E2E tests exist in `tests/e2e/`.
|
||||
|
||||
## Coding Standards and Conventions
|
||||
|
||||
### HTML/Hugo Templates
|
||||
- Use semantic HTML5 elements for proper document structure
|
||||
- Follow Hugo's template naming conventions (lowercase with hyphens)
|
||||
- Partial templates should be in `layouts/partials/`
|
||||
- Shortcodes should be in `layouts/shortcodes/`
|
||||
- Use Hugo's built-in functions when available (e.g., `absURL`, `relURL`)
|
||||
|
||||
### SCSS/CSS
|
||||
- Follow Bootstrap 5 conventions and utility classes where possible
|
||||
- Use BEM naming methodology for custom components when needed
|
||||
- Prefix custom variables with theme-specific identifiers
|
||||
- Keep files modular - one component per file
|
||||
- Use CSS custom properties for themeable values
|
||||
- File naming: lowercase with underscores (e.g., `_navbar.scss`, `_blog.scss`)
|
||||
|
||||
### JavaScript
|
||||
- Use vanilla JavaScript (no jQuery)
|
||||
- Prefer modern ES6+ syntax
|
||||
- Keep JavaScript minimal - theme is primarily CSS-driven
|
||||
- Use DOMPurify for any user-generated content sanitization
|
||||
- Ensure all JavaScript is progressively enhanced (works without JS when possible)
|
||||
|
||||
### Content and Configuration
|
||||
- YAML frontmatter for content files
|
||||
- TOML for Hugo configuration files
|
||||
- Follow existing frontmatter patterns for each content type
|
||||
- Use descriptive, human-readable keys
|
||||
|
||||
### File Naming Conventions
|
||||
- Templates: lowercase with hyphens (e.g., `single.html`, `list.html`)
|
||||
- SCSS files: lowercase with underscores, prefixed with `_` for partials
|
||||
- Content files: lowercase with hyphens (e.g., `my-blog-post.md`)
|
||||
- Archetypes: match content type name (e.g., `blog.md`, `experience.md`)
|
||||
|
||||
## Web Accessibility Requirements
|
||||
|
||||
**This theme prioritizes accessibility (WCAG 2.1 AA compliance):**
|
||||
|
||||
- All interactive elements must be keyboard accessible
|
||||
- Proper ARIA labels and roles for non-semantic elements
|
||||
- Color contrast ratios must meet WCAG AA standards (4.5:1 for normal text, 3:1 for large text)
|
||||
- Images must have descriptive `alt` attributes
|
||||
- Forms must have associated labels
|
||||
- Focus indicators must be visible and clear
|
||||
- Dark/light theme switching must maintain accessibility standards in both modes
|
||||
- Skip navigation links for keyboard users
|
||||
- Semantic HTML structure (proper heading hierarchy, landmarks, etc.)
|
||||
- Test changes with screen readers when modifying navigation or interactive elements
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
- Never commit secrets, API keys, or sensitive data
|
||||
- Use DOMPurify for sanitizing any user-generated or external content
|
||||
- Validate and sanitize all user inputs in contact forms
|
||||
- Follow Hugo's security best practices for template rendering
|
||||
- Keep dependencies updated to patch security vulnerabilities
|
||||
- Use Hugo's built-in `safeHTML`, `safeCSS`, `safeJS` functions appropriately
|
||||
|
||||
## Files and Directories to Ignore
|
||||
|
||||
Copilot should **ignore** or be cautious with:
|
||||
- `node_modules/` - Third-party dependencies
|
||||
- `public/` - Hugo build output (generated files)
|
||||
- `resources/` - Hugo's asset cache
|
||||
- `.hugo_build.lock` - Hugo build lock file
|
||||
- `package-lock.json` - npm lock file (only update via npm commands)
|
||||
- `.git/` - Version control directory
|
||||
- `exampleSite/public/` - Example site build output
|
||||
- `exampleSite/resources/` - Example site cache
|
||||
- `.DS_Store` - macOS system files
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# 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" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "friday"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/.github/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "friday"
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ci": {
|
||||
"collect": {
|
||||
"staticDistDir": "./built-site"
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
name: Update contributors
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
pull-requests: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
contrib-readme-job:
|
||||
runs-on: ubuntu-latest
|
||||
name: Readme contributors
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Contribute List
|
||||
uses: akhilmhdh/contributors-readme-action@v2.3.10
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
name: Crease release
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
deployments: write
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
- name: Install Go dependencies
|
||||
run: go mod download
|
||||
|
||||
- name: Install NPM dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create ZIP archive
|
||||
run: zip -r "adritian-free-hugo-theme-${{ steps.get_version.outputs.VERSION }}.zip" . -x "*.git*"
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
generate_release_notes: true
|
||||
files: "adritian-free-hugo-theme-${{ steps.get_version.outputs.VERSION }}.zip"
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
# Workflow to verify that the exampleSite runs ok
|
||||
name: Test example site
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
on:
|
||||
# Runs on pull requests targeting the default branch
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
env:
|
||||
CI: true
|
||||
|
||||
jobs:
|
||||
# Prepare dependencies job
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.147.2' }}
|
||||
steps:
|
||||
- name: Install Hugo CLI
|
||||
run: |
|
||||
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
|
||||
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Cache Node Modules
|
||||
id: cache-node-modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
key: modules-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Cache Playwright Browsers
|
||||
id: cache-playwright
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright
|
||||
key: playwright-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install Playwright browsers
|
||||
if: steps.cache-playwright.outputs.cache-hit != 'true'
|
||||
run: npx playwright install --with-deps
|
||||
|
||||
# Test with no menus
|
||||
test-no-menus:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.147.2' }}
|
||||
steps:
|
||||
- name: Install Hugo CLI
|
||||
run: |
|
||||
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
|
||||
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Restore Node Modules
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
key: modules-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Restore Playwright Browsers
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright
|
||||
key: playwright-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Build site (no menus)
|
||||
run: |
|
||||
cd exampleSite
|
||||
hugo server --themesDir ../.. --buildDrafts --buildFuture --bind 0.0.0.0 --config hugo.toml,hugo.disablemenu.toml &
|
||||
sleep 10
|
||||
|
||||
- name: Run Playwright tests (no menus)
|
||||
run: npm run test:e2e:no-menus
|
||||
|
||||
- name: Get the failure count
|
||||
id: tests
|
||||
run: |
|
||||
export count=$(cat ./playwright-report/result.txt | grep Failed | cut -d ':' -f 2 | tr -d ' ')
|
||||
echo "fail_count=$count" >> "$GITHUB_OUTPUT"
|
||||
echo $count
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: playwright-report-no-menus
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
outputs:
|
||||
fail_count: ${{ steps.tests.outputs.fail_count }}
|
||||
|
||||
# Test with menus
|
||||
test-with-menus:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.147.2' }}
|
||||
steps:
|
||||
- name: Install Hugo CLI
|
||||
run: |
|
||||
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
|
||||
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Restore Node Modules
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
key: modules-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Restore Playwright Browsers
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright
|
||||
key: playwright-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Build and serve demo site (with menus)
|
||||
run: |
|
||||
cd exampleSite
|
||||
hugo server --themesDir ../.. --buildDrafts --buildFuture --bind 0.0.0.0 &
|
||||
sleep 10
|
||||
|
||||
- name: Run Playwright tests (with menus)
|
||||
run: npm run test:e2e
|
||||
|
||||
- name: Get the failure count
|
||||
id: tests
|
||||
run: |
|
||||
export count=$(cat ./playwright-report/result.txt | grep Failed | cut -d ':' -f 2 | tr -d ' ')
|
||||
echo "fail_count=$count" >> "$GITHUB_OUTPUT"
|
||||
echo $count
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: playwright-report-with-menus
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
outputs:
|
||||
fail_count: ${{ steps.tests.outputs.fail_count }}
|
||||
|
||||
# Combine reports and publish
|
||||
publish-reports:
|
||||
if: github.ref == 'refs/heads/main'
|
||||
continue-on-error: true
|
||||
needs: [test-no-menus, test-with-menus]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Download no-menus report
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: playwright-report-no-menus
|
||||
path: playwright-report-no-menus
|
||||
|
||||
- name: Download with-menus report
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: playwright-report-with-menus
|
||||
path: playwright-report-with-menus
|
||||
|
||||
- name: Combine reports
|
||||
run: |
|
||||
mkdir -p combined-report
|
||||
cp -r playwright-report-no-menus/* combined-report/
|
||||
mkdir -p combined-report/with-menus
|
||||
cp -r playwright-report-with-menus/* combined-report/with-menus/
|
||||
|
||||
- name: Upload combined artifact for pages
|
||||
if: always()
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: "./combined-report"
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
- name: Check for test failures
|
||||
if: ${{ needs.test-no-menus.outputs.fail_count != '0' || needs.test-with-menus.outputs.fail_count != '0' }}
|
||||
run: exit 1
|
||||
@@ -0,0 +1,443 @@
|
||||
name: Module Init, HTML and Lighthouse Check
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
test-module-init:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v3
|
||||
with:
|
||||
hugo-version: ${{ vars.HUGO_VERSION || 'latest' }}
|
||||
extended: true
|
||||
|
||||
- name: Create test site
|
||||
run: |
|
||||
hugo new site test-site
|
||||
cd test-site
|
||||
|
||||
- name: Initialize Hugo module
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
hugo mod init github.com/zetxek/test-site
|
||||
hugo mod get -u github.com/zetxek/adritian-free-hugo-theme
|
||||
|
||||
- name: Configure module ref
|
||||
if: github.ref != 'refs/heads/main'
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
# Use the branch that initiated the PR
|
||||
branch_name=${GITHUB_HEAD_REF}
|
||||
echo "Using branch: ${branch_name}"
|
||||
hugo mod get -u github.com/zetxek/adritian-free-hugo-theme@${branch_name}
|
||||
|
||||
- name: Configure Hugo module in hugo.toml
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
echo '[module]
|
||||
[[module.imports]]
|
||||
path = "github.com/zetxek/adritian-free-hugo-theme"' > hugo.toml
|
||||
|
||||
- name: Validate Hugo module configuration
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
hugo mod verify
|
||||
hugo mod graph
|
||||
|
||||
- name: Upload test site
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-site
|
||||
path: test-site
|
||||
retention-days: 1
|
||||
|
||||
build-site:
|
||||
needs: test-module-init
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v3
|
||||
with:
|
||||
hugo-version: 'latest'
|
||||
extended: true
|
||||
|
||||
- name: Download test site
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: test-site
|
||||
path: test-site
|
||||
|
||||
- name: Pack dependencies
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
hugo mod npm pack
|
||||
|
||||
- name: (debug) Cat package.json
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
cat package.json
|
||||
|
||||
- name: Npm install (bootstrap)
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
npm install
|
||||
|
||||
- name: (debug) ls node_modules
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
ls node_modules
|
||||
|
||||
- name: Run helper script (copy config and content)
|
||||
working-directory: ./test-site
|
||||
run: |
|
||||
./node_modules/@zetxek/adritian-theme-helper/dist/scripts/download-content.js
|
||||
|
||||
- name: Run build
|
||||
working-directory: ./test-site
|
||||
run: hugo
|
||||
|
||||
- name: Upload built site
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: built-site
|
||||
path: test-site/public
|
||||
retention-days: 1
|
||||
|
||||
validate-html:
|
||||
needs: build-site
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download built site
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: built-site
|
||||
path: ./public
|
||||
|
||||
- name: Print index.html
|
||||
working-directory: ./public
|
||||
run: |
|
||||
cat index.html
|
||||
|
||||
# Validate HTML syntax with https://github.com/marketplace/actions/html5-validator
|
||||
- name: Validate HTML syntax
|
||||
uses: Cyb3r-Jak3/html5validator-action@v7.2.0
|
||||
with:
|
||||
root: ./public
|
||||
skip_git_check: true
|
||||
|
||||
lighthouse-check:
|
||||
needs: build-site
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download built site
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: built-site
|
||||
path: ./public
|
||||
|
||||
- name: Create output directory
|
||||
run: mkdir -p ${{ github.workspace }}/tmp/artifacts
|
||||
|
||||
- name: Start local server
|
||||
run: |
|
||||
npx http-server ./public -p 8080 &
|
||||
SERVER_PID=$!
|
||||
echo "Started http-server with PID: $SERVER_PID"
|
||||
|
||||
# Wait for server to be ready (max 30 seconds)
|
||||
for i in {1..30}; do
|
||||
if curl -s http://localhost:8080 > /dev/null 2>&1; then
|
||||
echo "✅ Server is ready and responding"
|
||||
break
|
||||
fi
|
||||
echo "Waiting for server to start... ($i/30)"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Verify server is actually responding
|
||||
if ! curl -s http://localhost:8080 > /dev/null 2>&1; then
|
||||
echo "❌ Server failed to start properly"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Server is ready, proceeding with Lighthouse check"
|
||||
|
||||
- name: Lighthouse Check
|
||||
id: lighthouse
|
||||
uses: foo-software/lighthouse-check-action@v12.0.1
|
||||
with:
|
||||
device: 'all'
|
||||
gitHubAccessToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
prCommentEnabled: false
|
||||
urls: 'http://localhost:8080'
|
||||
outputDirectory: ${{ github.workspace }}/tmp/artifacts
|
||||
|
||||
- name: Extract and save Lighthouse scores
|
||||
id: extract-scores
|
||||
run: |
|
||||
# Extract scores from Lighthouse reports
|
||||
mkdir -p .github
|
||||
|
||||
results_file="${{ github.workspace }}/tmp/artifacts/results.json"
|
||||
|
||||
if [ ! -f "$results_file" ]; then
|
||||
echo "❌ Error: results.json not found"
|
||||
echo '{"timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","commit":"${{ github.sha }}","scores":{}}' > current-scores.json
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "✅ Found results.json, extracting scores..."
|
||||
|
||||
echo "{" > current-scores.json
|
||||
echo ' "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",' >> current-scores.json
|
||||
echo ' "commit": "${{ github.sha }}",' >> current-scores.json
|
||||
echo ' "scores": {' >> current-scores.json
|
||||
|
||||
# The results.json from lighthouse-check-action contains an array of results
|
||||
# Scores are already 0-100, not 0-1
|
||||
num_results=$(jq 'length' "$results_file")
|
||||
|
||||
first_device=true
|
||||
|
||||
# Loop through results and organize by emulatedFormFactor
|
||||
for i in $(seq 0 $((num_results - 1))); do
|
||||
form_factor=$(jq -r ".[$i].emulatedFormFactor // \"unknown\"" "$results_file")
|
||||
|
||||
perf=$(jq -r ".[$i].scores.performance // 0" "$results_file")
|
||||
a11y=$(jq -r ".[$i].scores.accessibility // 0" "$results_file")
|
||||
bp=$(jq -r ".[$i].scores.bestPractices // 0" "$results_file")
|
||||
seo=$(jq -r ".[$i].scores.seo // 0" "$results_file")
|
||||
|
||||
echo " 📱 $form_factor: Performance=$perf, Accessibility=$a11y, Best Practices=$bp, SEO=$seo"
|
||||
|
||||
if [ "$form_factor" = "mobile" ] || [ "$form_factor" = "desktop" ]; then
|
||||
if [ "$first_device" = false ]; then
|
||||
echo "," >> current-scores.json
|
||||
fi
|
||||
first_device=false
|
||||
|
||||
echo " \"$form_factor\": {" >> current-scores.json
|
||||
echo " \"performance\": $perf," >> current-scores.json
|
||||
echo " \"accessibility\": $a11y," >> current-scores.json
|
||||
echo " \"best-practices\": $bp," >> current-scores.json
|
||||
echo " \"seo\": $seo" >> current-scores.json
|
||||
echo -n " }" >> current-scores.json
|
||||
fi
|
||||
done
|
||||
|
||||
echo "" >> current-scores.json
|
||||
echo " }" >> current-scores.json
|
||||
echo "}" >> current-scores.json
|
||||
|
||||
echo "✅ Scores extracted successfully"
|
||||
|
||||
- name: Load baseline scores from lighthouse-data branch
|
||||
id: load-baseline
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
# Try to fetch baseline scores from lighthouse-data branch
|
||||
if git show origin/lighthouse-data:.github/lighthouse-scores.json > baseline-scores.json 2>/dev/null; then
|
||||
echo "baseline_exists=true" >> $GITHUB_OUTPUT
|
||||
baseline_commit=$(jq -r '.commit // ""' baseline-scores.json)
|
||||
echo "✅ Baseline scores loaded from lighthouse-data branch (commit: ${baseline_commit:0:7})"
|
||||
else
|
||||
echo "baseline_exists=false" >> $GITHUB_OUTPUT
|
||||
echo "ℹ️ No baseline found - this will be the first baseline after merge"
|
||||
# Create empty baseline
|
||||
echo '{"commit":"","scores":{}}' > baseline-scores.json
|
||||
fi
|
||||
|
||||
- name: Format Lighthouse results with comparison
|
||||
id: format-results
|
||||
run: |
|
||||
# Function to get score emoji
|
||||
get_emoji() {
|
||||
score=$1
|
||||
# Handle empty or invalid scores
|
||||
if [ -z "$score" ] || [ "$score" = "0" ] || [ "$score" = "null" ]; then
|
||||
echo "⚪"
|
||||
return
|
||||
fi
|
||||
if (( $(echo "$score >= 90" | bc -l) )); then
|
||||
echo "🟢"
|
||||
elif (( $(echo "$score >= 50" | bc -l) )); then
|
||||
echo "🟠"
|
||||
else
|
||||
echo "🔴"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to get delta indicator
|
||||
get_delta() {
|
||||
current=$1
|
||||
baseline=$2
|
||||
if [ "$baseline" = "null" ] || [ -z "$baseline" ] || [ "$baseline" = "0" ]; then
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
delta=$((current - baseline))
|
||||
if [ $delta -gt 0 ]; then
|
||||
echo "(↑ +$delta)"
|
||||
elif [ $delta -lt 0 ]; then
|
||||
echo "(↓ $delta)"
|
||||
else
|
||||
echo "(→)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Build URLs
|
||||
commit_sha="${{ github.sha }}"
|
||||
commit_url="https://github.com/${{ github.repository }}/commit/$commit_sha"
|
||||
artifacts_url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
|
||||
echo "## 🔦 Lighthouse Performance Report" > comment.md
|
||||
echo "" >> comment.md
|
||||
echo "**Test Results for commit:** [\`${commit_sha:0:7}\`]($commit_url)" >> comment.md
|
||||
echo "" >> comment.md
|
||||
|
||||
# Check if we have baseline scores
|
||||
has_baseline=false
|
||||
if [ -f "baseline-scores.json" ]; then
|
||||
baseline_commit=$(jq -r '.commit // ""' baseline-scores.json)
|
||||
if [ -n "$baseline_commit" ] && [ "$baseline_commit" != "null" ]; then
|
||||
has_baseline=true
|
||||
baseline_commit_url="https://github.com/${{ github.repository }}/commit/$baseline_commit"
|
||||
echo "_Comparing against baseline from lighthouse-data branch (commit: [\`${baseline_commit:0:7}\`]($baseline_commit_url))_" >> comment.md
|
||||
echo "" >> comment.md
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if current scores exist
|
||||
if [ ! -f "current-scores.json" ]; then
|
||||
echo "❌ **Error:** No Lighthouse scores were generated." >> comment.md
|
||||
echo "" >> comment.md
|
||||
echo "Check the workflow logs for more details." >> comment.md
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Parse and format the results
|
||||
found_scores=false
|
||||
for device in "mobile" "desktop"; do
|
||||
current_perf=$(jq -r ".scores.$device.performance // 0" current-scores.json)
|
||||
current_a11y=$(jq -r ".scores.$device.accessibility // 0" current-scores.json)
|
||||
current_bp=$(jq -r ".scores.$device[\"best-practices\"] // 0" current-scores.json)
|
||||
current_seo=$(jq -r ".scores.$device.seo // 0" current-scores.json)
|
||||
|
||||
# Check if we have any real scores (not just 0)
|
||||
if [ "$current_perf" != "0" ] || [ "$current_a11y" != "0" ] || [ "$current_bp" != "0" ] || [ "$current_seo" != "0" ]; then
|
||||
found_scores=true
|
||||
echo "### ${device^} Results" >> comment.md
|
||||
echo "" >> comment.md
|
||||
|
||||
if [ "$has_baseline" = true ]; then
|
||||
echo "| Category | Score | Change |" >> comment.md
|
||||
echo "|----------|-------|--------|" >> comment.md
|
||||
|
||||
baseline_perf=$(jq -r ".scores.$device.performance // 0" baseline-scores.json)
|
||||
baseline_a11y=$(jq -r ".scores.$device.accessibility // 0" baseline-scores.json)
|
||||
baseline_bp=$(jq -r ".scores.$device[\"best-practices\"] // 0" baseline-scores.json)
|
||||
baseline_seo=$(jq -r ".scores.$device.seo // 0" baseline-scores.json)
|
||||
|
||||
echo "| Performance | $(get_emoji $current_perf) $current_perf | $(get_delta $current_perf $baseline_perf) |" >> comment.md
|
||||
echo "| Accessibility | $(get_emoji $current_a11y) $current_a11y | $(get_delta $current_a11y $baseline_a11y) |" >> comment.md
|
||||
echo "| Best Practices | $(get_emoji $current_bp) $current_bp | $(get_delta $current_bp $baseline_bp) |" >> comment.md
|
||||
echo "| SEO | $(get_emoji $current_seo) $current_seo | $(get_delta $current_seo $baseline_seo) |" >> comment.md
|
||||
else
|
||||
echo "| Category | Score |" >> comment.md
|
||||
echo "|----------|-------|" >> comment.md
|
||||
|
||||
echo "| Performance | $(get_emoji $current_perf) $current_perf |" >> comment.md
|
||||
echo "| Accessibility | $(get_emoji $current_a11y) $current_a11y |" >> comment.md
|
||||
echo "| Best Practices | $(get_emoji $current_bp) $current_bp |" >> comment.md
|
||||
echo "| SEO | $(get_emoji $current_seo) $current_seo |" >> comment.md
|
||||
fi
|
||||
echo "" >> comment.md
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$found_scores" = false ]; then
|
||||
echo "⚠️ **Warning:** No Lighthouse scores were found in the reports." >> comment.md
|
||||
echo "" >> comment.md
|
||||
echo "This may be because:" >> comment.md
|
||||
echo "- The Lighthouse action did not generate reports" >> comment.md
|
||||
echo "- The report files have an unexpected format" >> comment.md
|
||||
echo "" >> comment.md
|
||||
fi
|
||||
|
||||
echo "📊 **[View full detailed reports in workflow artifacts]($artifacts_url)**" >> comment.md
|
||||
echo "" >> comment.md
|
||||
echo "_Report generated at $(date)_" >> comment.md
|
||||
|
||||
- name: Update PR comment
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
header: lighthouse-report
|
||||
path: comment.md
|
||||
|
||||
- name: Upload Lighthouse report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lighthouse-report
|
||||
path: ${{ github.workspace }}/tmp/artifacts
|
||||
|
||||
- name: Save baseline scores to lighthouse-data branch
|
||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||
run: |
|
||||
# Copy scores to temp location before branch switching
|
||||
# (git clean will remove it from the working directory)
|
||||
TEMP_SCORES="/tmp/lighthouse-scores-${{ github.sha }}.json"
|
||||
cp "${{ github.workspace }}/current-scores.json" "$TEMP_SCORES"
|
||||
|
||||
# Configure git
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Try to fetch and checkout the lighthouse-data branch
|
||||
if git fetch origin lighthouse-data 2>/dev/null && git checkout lighthouse-data 2>/dev/null; then
|
||||
echo "✅ Checked out existing lighthouse-data branch"
|
||||
else
|
||||
echo "Creating new lighthouse-data orphan branch"
|
||||
git checkout --orphan lighthouse-data
|
||||
git rm -rf . 2>/dev/null || true
|
||||
git clean -fd 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Copy the lighthouse scores file from temp location
|
||||
mkdir -p .github
|
||||
cp "$TEMP_SCORES" .github/lighthouse-scores.json
|
||||
|
||||
# Commit and push if there are changes
|
||||
git add .github/lighthouse-scores.json
|
||||
if git diff --staged --quiet 2>/dev/null; then
|
||||
echo "No changes to lighthouse scores"
|
||||
else
|
||||
git commit -m "chore: update lighthouse baseline scores [skip ci]"
|
||||
git push origin lighthouse-data
|
||||
echo "✅ Lighthouse baseline scores updated in lighthouse-data branch"
|
||||
fi
|
||||
|
||||
# Clean up temp file
|
||||
rm -f "$TEMP_SCORES"
|
||||
@@ -0,0 +1,43 @@
|
||||
## Updates demo site (https://github.com/zetxek/adritian-demo)
|
||||
## triggers the update-hugo-module workflow in adritian-demo
|
||||
## and the update-git-submodule workflow in the git-submodule-demo site
|
||||
name: Update demo repos
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
actions: write
|
||||
|
||||
on:
|
||||
push:
|
||||
## run on push to main or a new semantic tag (ie: v1.2.3)
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
- name: Trigger Update Hugo Module workflow
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: token ${{ secrets.PRIVATE_TOKEN_GITHUB }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/zetxek/adritian-demo/actions/workflows/update-hugo-module.yml/dispatches \
|
||||
-d '{"ref":"main"}'
|
||||
|
||||
- name: Trigger Update Git Submodule workflow
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: token ${{ secrets.PRIVATE_TOKEN_GITHUB }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://github.com/zetxek/adritian-git-submodule-demo/actions/workflows/reset-submodule.yml/dispatches \
|
||||
-d '{"ref":"main"}'
|
||||
@@ -0,0 +1,147 @@
|
||||
## Updates demo site (https://github.com/zetxek/adritian-demo)
|
||||
## taken from https://stackoverflow.com/a/68213855/570087
|
||||
|
||||
name: PR demo site (on external PR)
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
actions: write
|
||||
|
||||
## This will open a PR which will open a vercel preview URL in the demo site
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [labeled]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update-demo:
|
||||
env:
|
||||
SOURCE_BRANCH_NAME: ${{ github.head_ref || github.ref_name }} # PR branch name
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
runs-on: ubuntu-latest
|
||||
if: contains(github.event.pull_request.labels.*.name, 'safe to test')
|
||||
steps:
|
||||
- name: Checkout source repository (theme)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: theme-source
|
||||
ref: ${{ github.head_ref }}
|
||||
token: ${{ secrets.PRIVATE_TOKEN_GITHUB }}
|
||||
|
||||
- name: Checkout demo repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: zetxek/adritian-demo
|
||||
path: demo-repo
|
||||
token: ${{ secrets.PRIVATE_TOKEN_GITHUB }}
|
||||
|
||||
- name: Dump github context
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
|
||||
- name: Send pull-request
|
||||
run: |
|
||||
LATEST_TAG=$(cd theme-source && git describe --tags --always)
|
||||
LATEST_COMMIT=$(cd theme-source && git rev-parse HEAD)
|
||||
SOURCE_REPOSITORY="zetxek/adritian-free-hugo-theme"
|
||||
REPOSITORY="zetxek/adritian-demo"
|
||||
REPO_NAME=${{ github.event.pull_request.head.repo.full_name }}
|
||||
FOLDER="bin/$REPOSITORY"
|
||||
PR_URL="https://github.com/$SOURCE_REPOSITORY/pull/$PR_NUMBER"
|
||||
BRANCH_NAME="theme-update/update-theme-to-$LATEST_TAG"
|
||||
BASE_BRANCH="main"
|
||||
ASSIGNEE="zetxek"
|
||||
|
||||
echo "Latest tag: $LATEST_TAG"
|
||||
echo "Latest commit: $LATEST_COMMIT"
|
||||
echo "PR URL: $PR_URL"
|
||||
|
||||
git config --global --add --bool push.autoSetupRemote true
|
||||
|
||||
# Clone the remote repository and change working directory to the
|
||||
# folder it was cloned to.
|
||||
git clone \
|
||||
--depth=1 \
|
||||
--branch=main \
|
||||
https://some-user:${{ secrets.PRIVATE_TOKEN_GITHUB }}@github.com/$REPOSITORY \
|
||||
$FOLDER
|
||||
|
||||
cd $FOLDER
|
||||
|
||||
# Setup the committers identity.
|
||||
git config user.email "actions@github.com"
|
||||
git config user.name "GitHub Actions - update theme module version"
|
||||
|
||||
# Create a new feature branch for the changes.
|
||||
echo "Working branch: $BRANCH_NAME"
|
||||
git checkout -b $BRANCH_NAME
|
||||
|
||||
# Commit the changes and push the feature branch to origin
|
||||
git config --global --add --bool push.autoSetupRemote true
|
||||
echo "Committing all changes."
|
||||
git add --all
|
||||
|
||||
COMMIT_MSG_THEME='chore: update theme module version to `'"$LATEST_TAG"'`'
|
||||
echo 'Updating theme module: '"$COMMIT_MSG_THEME"
|
||||
git commit -am "$COMMIT_MSG_THEME" && git push --force || echo "No changes to theme"
|
||||
|
||||
# Copy content from the checked-out theme source
|
||||
cp ../../theme-source/exampleSite/hugo.toml hugo.toml
|
||||
# Update URL
|
||||
sed -i -e "s/\"https\:\/\/www\.adrianmoreno\.info\"/\"https\:\/\/adritian-demo\.vercel\.app\/\"/g" hugo.toml
|
||||
|
||||
COMMIT_MSG_CONFIG='chore: update config/content to `'"$LATEST_TAG"'` from https://github.com/zetxek/adritian-free-hugo-theme'
|
||||
echo "Committing content/config: $COMMIT_MSG_CONFIG"
|
||||
git commit -am "$COMMIT_MSG_CONFIG" && git push --force || echo "No changes to config"
|
||||
|
||||
echo "Pushing branch: $BRANCH_NAME"
|
||||
git push origin $BRANCH_NAME --force
|
||||
|
||||
# Store the PAT in a file that can be accessed by the
|
||||
# GitHub CLI.
|
||||
echo "${{ secrets.PRIVATE_TOKEN_GITHUB }}" > token.txt
|
||||
|
||||
# Authorize GitHub CLI for the current repository and
|
||||
# create a pull-requests containing the updates.
|
||||
echo "Logging in to GitHub CLI."
|
||||
gh auth login --with-token < token.txt
|
||||
|
||||
# Check if the PR already exists - if there's no "number" returned, we default to empty string
|
||||
PR_EXISTS=$(gh pr list --state open --base $BASE_BRANCH --head $BRANCH_NAME --json number | jq '.[0].number // empty')
|
||||
echo "PR_EXISTS: $PR_EXISTS"
|
||||
# Check if there's a PR number. If the PR exists, update it (empty = it doesn't exist)
|
||||
if [ -n "$PR_EXISTS" ]; then
|
||||
echo "PR Exists. Updating pull-request..."
|
||||
gh pr view
|
||||
|
||||
echo "✅ Pull-request created - done! "
|
||||
|
||||
# Else, we create it
|
||||
else
|
||||
echo "✨PR Does not exist yet. Creating pull-request..."
|
||||
PR_TITLE='preview: update theme to `'$SOURCE_BRANCH_NAME'`'
|
||||
echo 'PR title: '$PR_TITLE
|
||||
PR_BODY="⚠️ The source PR is not merged yet - this is a preview PR.
|
||||
🤖 This automated PR updates the theme version to a PR in the source repo: $PR_URL.
|
||||
🔗 Triggered by https://github.com/zetxek/adritian-free-hugo-theme/actions/workflows/update-demo-pr.yml"
|
||||
echo "PR body: "$PR_BODY
|
||||
|
||||
gh pr create \
|
||||
--title "$PR_TITLE" \
|
||||
--body "$PR_BODY" \
|
||||
--head $BRANCH_NAME \
|
||||
--base $BASE_BRANCH \
|
||||
--assignee $ASSIGNEE \
|
||||
--label preview
|
||||
|
||||
echo "✅ Pull-request created - done! "
|
||||
fi
|
||||
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
## Updates demo site (https://github.com/zetxek/adritian-demo)
|
||||
## taken from https://stackoverflow.com/a/68213855/570087
|
||||
|
||||
name: Preview demo site
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
actions: write
|
||||
## This will open a PR which will open a vercel preview URL in the demo site
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
label-dependabot:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
steps:
|
||||
- name: Add safe-to-test label to Dependabot PRs
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
try {
|
||||
// First check if the label exists
|
||||
await github.rest.issues.getLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: 'safe-to-test'
|
||||
});
|
||||
|
||||
// If it exists, add it to the PR
|
||||
await github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['safe-to-test']
|
||||
});
|
||||
console.log('Added safe-to-test label to PR');
|
||||
} catch (error) {
|
||||
// If getting the label failed, the PR workflow can't add it
|
||||
// Let's just modify our workflow to handle this differently
|
||||
console.log('Could not add safe-to-test label: ' + error.message);
|
||||
console.log('Will handle Dependabot PRs directly in check-conditions job');
|
||||
}
|
||||
|
||||
check-dependabot:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should-run: ${{ steps.check-conditions.outputs.should-run }}
|
||||
steps:
|
||||
- id: check-conditions
|
||||
run: |
|
||||
# Check if PR has safe-to-test label
|
||||
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'safe-to-test') }}" == "true" ]]; then
|
||||
echo "should-run=true" >> $GITHUB_OUTPUT
|
||||
echo "PR has safe-to-test label, allowing workflow to run"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Always allow Dependabot PRs to run
|
||||
if [[ "${{ github.actor }}" == "dependabot[bot]" ]]; then
|
||||
echo "should-run=true" >> $GITHUB_OUTPUT
|
||||
echo "PR is from Dependabot, automatically allowing workflow to run"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# For other contributors
|
||||
echo "should-run=true" >> $GITHUB_OUTPUT
|
||||
echo "PR is from a trusted contributor, allowing workflow to run"
|
||||
|
||||
update-demo:
|
||||
needs: check-dependabot
|
||||
if: needs.check-dependabot.outputs.should-run == 'true'
|
||||
env:
|
||||
SOURCE_BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
HUGO_VERSION: ${{ vars.HUGO_VERSION || '0.147.2' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: zetxek/adritian-free-hugo-theme
|
||||
ref: ${{ github.head_ref || github.ref_name }}
|
||||
token: ${{ secrets.PRIVATE_TOKEN_GITHUB }}
|
||||
path: source-repo
|
||||
|
||||
- name: Checkout demo repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: zetxek/adritian-demo
|
||||
token: ${{ secrets.PRIVATE_TOKEN_GITHUB }}
|
||||
path: demo-repo
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v2
|
||||
with:
|
||||
hugo-version: ${{ env.HUGO_VERSION }}
|
||||
extended: true
|
||||
|
||||
- name: Send pull-request
|
||||
shell: bash
|
||||
run: |
|
||||
SOURCE_REPOSITORY="zetxek/adritian-free-hugo-theme"
|
||||
LATEST_COMMIT=${{ github.event.pull_request.head.sha || github.sha }}
|
||||
SHORT_COMMIT=${LATEST_COMMIT:0:8}
|
||||
|
||||
# Handle both PR events and manual workflow dispatch
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
PR_URL="https://github.com/$SOURCE_REPOSITORY/pull/$PR_NUMBER"
|
||||
BRANCH_NAME="theme-update/pr-$PR_NUMBER-$SHORT_COMMIT"
|
||||
PR_TITLE="preview: theme PR #$PR_NUMBER ($SHORT_COMMIT)"
|
||||
else
|
||||
# For manual workflow dispatch, use branch name instead
|
||||
BRANCH_REF="${{ github.head_ref || github.ref_name }}"
|
||||
PR_URL="https://github.com/$SOURCE_REPOSITORY/tree/$BRANCH_REF"
|
||||
BRANCH_NAME="theme-update/branch-$BRANCH_REF-$SHORT_COMMIT"
|
||||
PR_TITLE="preview: theme branch $BRANCH_REF ($SHORT_COMMIT)"
|
||||
fi
|
||||
|
||||
BASE_BRANCH="main"
|
||||
ASSIGNEE="zetxek"
|
||||
|
||||
echo "Latest commit: $LATEST_COMMIT"
|
||||
echo "Short commit: $SHORT_COMMIT"
|
||||
echo "Branch name: $BRANCH_NAME"
|
||||
echo "PR URL: $PR_URL"
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
echo "PR Number: $PR_NUMBER"
|
||||
else
|
||||
echo "Manual workflow dispatch - no PR number"
|
||||
fi
|
||||
|
||||
git config --global --add --bool push.autoSetupRemote true
|
||||
|
||||
# Verify source commit exists
|
||||
cd source-repo
|
||||
if ! git cat-file -e $LATEST_COMMIT; then
|
||||
echo "Error: Commit $LATEST_COMMIT does not exist in source repository"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Verified commit $LATEST_COMMIT exists in source repository"
|
||||
|
||||
# Switch to demo repository
|
||||
cd ../demo-repo
|
||||
|
||||
# Setup git identity
|
||||
git config user.email "actions@github.com"
|
||||
git config user.name "GitHub Actions - update theme module"
|
||||
|
||||
# Create feature branch
|
||||
git checkout -b $BRANCH_NAME
|
||||
|
||||
# Initialize Hugo module if needed
|
||||
if [ ! -f "go.mod" ]; then
|
||||
hugo mod init github.com/zetxek/adritian-demo
|
||||
fi
|
||||
|
||||
# Clean up any existing invalid module references
|
||||
if [ -f "go.mod" ]; then
|
||||
echo "Cleaning up existing go.mod..."
|
||||
# Remove any existing theme references that might have invalid revisions
|
||||
grep -v 'github.com/zetxek/adritian-free-hugo-theme' go.mod > go.mod.tmp || touch go.mod.tmp
|
||||
mv go.mod.tmp go.mod
|
||||
# Clean up any existing go.sum entries
|
||||
if [ -f "go.sum" ]; then
|
||||
grep -v 'github.com/zetxek/adritian-free-hugo-theme' go.sum > go.sum.tmp || touch go.sum.tmp
|
||||
mv go.sum.tmp go.sum
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get the theme from the PR branch - use branch name instead of commit for better reliability
|
||||
BRANCH_NAME_THEME="${{ github.head_ref || github.ref_name }}"
|
||||
echo "Using theme branch: $BRANCH_NAME_THEME"
|
||||
|
||||
# Try to get the specific commit first, fallback to branch if commit is not available
|
||||
echo "Attempting to get theme with commit: $LATEST_COMMIT"
|
||||
if hugo mod get github.com/zetxek/adritian-free-hugo-theme@$LATEST_COMMIT; then
|
||||
echo "✅ Successfully got theme with commit hash"
|
||||
else
|
||||
echo "Failed to get specific commit, trying branch instead..."
|
||||
echo "Attempting to get theme with branch: $BRANCH_NAME_THEME"
|
||||
if hugo mod get github.com/zetxek/adritian-free-hugo-theme@$BRANCH_NAME_THEME; then
|
||||
echo "✅ Successfully got theme with branch name"
|
||||
else
|
||||
echo "❌ Failed to get theme with both commit and branch, trying main branch as last resort..."
|
||||
hugo mod get github.com/zetxek/adritian-free-hugo-theme@main
|
||||
fi
|
||||
fi
|
||||
|
||||
hugo mod tidy
|
||||
|
||||
# Commit changes
|
||||
git add go.mod go.sum
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
COMMIT_MSG="preview: theme PR #$PR_NUMBER ($SHORT_COMMIT)"
|
||||
else
|
||||
BRANCH_REF="${{ github.head_ref || github.ref_name }}"
|
||||
COMMIT_MSG="preview: theme branch $BRANCH_REF ($SHORT_COMMIT)"
|
||||
fi
|
||||
git commit -m "$COMMIT_MSG" && git push --force || echo "No changes to commit"
|
||||
|
||||
# Push branch
|
||||
git push origin $BRANCH_NAME --force
|
||||
|
||||
# Setup GitHub CLI
|
||||
echo "${{ secrets.PRIVATE_TOKEN_GITHUB }}" > token.txt
|
||||
gh auth login --with-token < token.txt
|
||||
|
||||
# Check if PR exists
|
||||
PR_EXISTS=$(gh pr list --state open --base $BASE_BRANCH --head $BRANCH_NAME --json number | jq '.[0].number // empty')
|
||||
echo "PR_EXISTS: $PR_EXISTS"
|
||||
|
||||
if [ -n "$PR_EXISTS" ]; then
|
||||
echo "PR Exists. Updating pull-request..."
|
||||
gh pr view
|
||||
echo "✅ Pull-request updated!"
|
||||
# TODO: update the PR body with the latest commit
|
||||
# Add a comment to the PR with the update
|
||||
gh pr comment $PR_EXISTS --body "Updated PR to last commit in source repo: \`$LATEST_COMMIT\`"
|
||||
else
|
||||
echo "✨ Creating new pull-request..."
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
PR_BODY="⚠️ The source PR is not merged yet - this is a preview PR created to test the theme update with a vercel preview URL.
|
||||
- Last commit: \`$LATEST_COMMIT\`
|
||||
- Source PR: $PR_URL
|
||||
- 🔗 Triggered by https://github.com/zetxek/adritian-free-hugo-theme/actions/workflows/update-demo-pr.yml"
|
||||
else
|
||||
BRANCH_REF="${{ github.head_ref || github.ref_name }}"
|
||||
PR_BODY="⚠️ This is a preview PR created to test the theme update from branch \`$BRANCH_REF\` with a vercel preview URL.
|
||||
- Last commit: \`$LATEST_COMMIT\`
|
||||
- Source branch: $PR_URL
|
||||
- 🔗 Triggered by https://github.com/zetxek/adritian-free-hugo-theme/actions/workflows/update-demo-pr.yml"
|
||||
fi
|
||||
|
||||
PR_RESULT=$(gh pr create \
|
||||
--title "$PR_TITLE" \
|
||||
--body "$PR_BODY" \
|
||||
--head $BRANCH_NAME \
|
||||
--base $BASE_BRANCH \
|
||||
--assignee $ASSIGNEE \
|
||||
--label preview)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error creating PR: $PR_RESULT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "✅ Pull-request created!"
|
||||
fi
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
name: Update Example Site
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pages: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
statuses: write
|
||||
actions: write
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Every Sunday at midnight
|
||||
|
||||
jobs:
|
||||
update-example-site:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DATE: ${{ format('YYYY-MM-DD', github.event.repository.pushed_at) }}
|
||||
|
||||
steps:
|
||||
- name: Set date
|
||||
run: echo "DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
|
||||
|
||||
- name: Set branch name
|
||||
run: echo "BRANCH_NAME=update/exampleSite-${DATE}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Update example site content
|
||||
run: |
|
||||
cd exampleSite
|
||||
../node_modules/@zetxek/adritian-theme-helper/dist/scripts/download-content.js
|
||||
|
||||
- name: Create new branch
|
||||
run: |
|
||||
git checkout -b ${{ env.BRANCH_NAME }}
|
||||
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config --global user.name 'GitHub Actions'
|
||||
git config --global user.email 'actions@github.com'
|
||||
git add .
|
||||
git commit -m "Update example site content"
|
||||
|
||||
- name: Push changes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
git push origin ${{ env.BRANCH_NAME }}
|
||||
|
||||
- name: Create Pull Request
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
PR_TITLE="ExampleSite content update (${DATE})"
|
||||
PR_BODY="This automated PR updates the example site with the latest content from [adritian-demo](https://github.com/zetxek/adritian-demo).
|
||||
|
||||
**Overview of changes:**
|
||||
|
||||
<code>
|
||||
$(git diff --stat HEAD~1)
|
||||
</code>"
|
||||
gh pr create --title "$PR_TITLE" --body "$PR_BODY" --base main --head "${{ env.BRANCH_NAME }}"
|
||||
Reference in New Issue
Block a user