Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

> See [Project Usage](https://github.com/Lissy93/weather-front?tab=readme-ov-file#usage) for setup and contribution instructions.
> See [Project Usage](https://github.com/lissy93/framework-benchmarks?tab=readme-ov-file#usage) for setup and contribution instructions.
118 changes: 75 additions & 43 deletions .github/README.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions .github/docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,6 @@ If tests fail or you need help:
1. Check this README
2. Review test output and screenshots
3. Run tests in debug mode
4. Check [GitHub Issues](https://github.com/Lissy93/weather-front/issues)
4. Check [GitHub Issues](https://github.com/lissy93/framework-benchmarks/issues)

**Happy Testing! 🎉**
**Happy Testing! 🎉**
3 changes: 1 addition & 2 deletions .github/docs/troubleshooting.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@


# Troubleshooting

### "Unable to connect to Chrome"
- Ensure Chrome/Chromium is installed
Expand Down
23 changes: 22 additions & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ on:
required: false
default: '60'
type: string
skip_commit:
description: 'Skip committing results to main branch (results branch will still be updated)'
required: false
default: false
type: boolean
schedule:
- cron: '30 4 * * *' # Daily at 04:30 UTC

env:
NODE_VERSION: '20'
Expand Down Expand Up @@ -158,11 +165,19 @@ jobs:
run: |
echo "PYTHONPATH=$PWD:$PWD/scripts" >> $GITHUB_ENV

# Set skip_commit based on event type or manual input
if [ "${{ github.event_name }}" = "schedule" ]; then
echo "SKIP_COMMIT=true" >> $GITHUB_ENV
echo "⏰ Scheduled run - will skip main branch commit"
else
echo "SKIP_COMMIT=${{ github.event.inputs.skip_commit || 'false' }}" >> $GITHUB_ENV
echo "👤 Manual run - skip commit: ${{ github.event.inputs.skip_commit || 'false' }}"
fi

echo "📊 Benchmark Configuration:"
echo " Types: ${{ github.event.inputs.benchmark_types || 'all' }}"
echo " Frameworks: ${{ github.event.inputs.frameworks || 'all' }}"
echo " Executions: ${{ github.event.inputs.executions || '1' }}"
echo " Detailed: ${{ github.event.inputs.detailed_output }}"

- name: 🧪 Run Benchmarks
run: |
Expand Down Expand Up @@ -251,13 +266,19 @@ jobs:
echo "- **Artifacts**: Debug logs may be available below (if any)" >> $GITHUB_STEP_SUMMARY
fi

- name: 📋 Save Workflow Context
run: |
echo '{"skip_commit": "${{ env.SKIP_COMMIT }}"}' > workflow-context.json
cat workflow-context.json

- name: 📤 Upload Benchmark Results
uses: actions/upload-artifact@v4
if: always()
with:
name: benchmark-results-${{ github.run_number }}
path: |
benchmark-results/
workflow-context.json
!benchmark-results/**/.gitkeep
retention-days: 30
compression-level: 6
Expand Down
91 changes: 72 additions & 19 deletions .github/workflows/transform-results.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ jobs:
exit 1
fi

# Read workflow context to determine if we should skip main branch commit
if [ -f "benchmark-results/workflow-context.json" ]; then
SKIP_COMMIT=$(python3 -c "import json; print(json.load(open('benchmark-results/workflow-context.json')).get('skip_commit', 'false'))" 2>/dev/null || echo "false")
echo "SKIP_MAIN_COMMIT=$SKIP_COMMIT" >> $GITHUB_ENV
echo "📋 Workflow context: skip_commit=$SKIP_COMMIT"
rm benchmark-results/workflow-context.json # Clean up
else
echo "SKIP_MAIN_COMMIT=false" >> $GITHUB_ENV
echo "📋 No workflow context found, defaulting to commit to main branch"
fi

echo "📊 Final benchmark-results structure:"
find benchmark-results -type f -name "*.json" | head -10

Expand Down Expand Up @@ -102,7 +113,12 @@ jobs:
run: |
echo "📊 Generating Chart.js configurations..."
python scripts/transform/build_charts.py
echo "✅ Chart configurations generated"
echo "✅ Chart configurations and README charts generated"

- name: 📊 Generate Framework Stats
run: |
echo "📊 Generating framework stats..."
python scripts/transform/fetch_framework_stats.py || echo "⚠️ Framework stats generation failed"

- name: 📊 Generate Results Summary
run: |
Expand All @@ -111,33 +127,70 @@ jobs:
echo "- **Status**: ✅ Success" >> $GITHUB_STEP_SUMMARY
echo "- **Timestamp**: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY
echo "- **Source**: Workflow run #${{ github.event.workflow_run.run_number }}" >> $GITHUB_STEP_SUMMARY

- name: 📝 Commit Results
echo "- **Main Branch Commit**: $([ "$SKIP_MAIN_COMMIT" = "true" ] && echo "⏭️ Skipped" || echo "✅ Updated")" >> $GITHUB_STEP_SUMMARY

- name: 📝 Commit to Results Branch
run: |
set -e
TIMESTAMP=$(date -u '+%Y%m%d_%H%M%S')
CURRENT_DIR=$(pwd)

git config user.name "liss-bot"
git config user.email "alicia-gh-bot@mail.as93.net"

# Only commit if there are changes
git add results/ website/static/chart*.json website/static/charts/ || true
if git diff --cached --quiet; then
echo "ℹ️ No changes to commit."
echo "skip_commit=true" >> $GITHUB_ENV

# Create temporary directory for results branch
mkdir -p /tmp/results-branch
cd /tmp/results-branch

# Initialize or fetch results branch
git init
git remote add origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
git fetch origin results:results 2>/dev/null && git checkout results || {
git checkout --orphan results
git rm -rf . 2>/dev/null || true
}

# Set up directory structure
mkdir -p raw summary charts stats

# Copy files from main workspace
[ -d "$CURRENT_DIR/benchmark-results" ] && cp -r "$CURRENT_DIR/benchmark-results"/* raw/ 2>/dev/null || true
[ -f "$CURRENT_DIR/results/summary.json" ] && cp "$CURRENT_DIR/results/summary.json" summary/summary-${TIMESTAMP}.json
[ -f "$CURRENT_DIR/results/summary.tsv" ] && cp "$CURRENT_DIR/results/summary.tsv" summary/summary-${TIMESTAMP}.tsv
[ -f "$CURRENT_DIR/results/summary.json" ] && cp "$CURRENT_DIR/results/summary.json" summary/summary.json
[ -f "$CURRENT_DIR/results/summary.tsv" ] && cp "$CURRENT_DIR/results/summary.tsv" summary/summary.tsv
[ -d "$CURRENT_DIR/website/static/charts" ] && cp -r "$CURRENT_DIR/website/static/charts"/* charts/ 2>/dev/null || true
[ -f "$CURRENT_DIR/results/framework-stats.json" ] && cp "$CURRENT_DIR/results/framework-stats.json" stats/stats-${TIMESTAMP}.json
[ -f "$CURRENT_DIR/results/framework-stats.json" ] && cp "$CURRENT_DIR/results/framework-stats.json" stats/framework-stats.json

# Commit and push if there are changes
git add . || true
if ! git diff --cached --quiet; then
git commit -m "chore: update results from run #${{ github.event.workflow_run.run_number }} [${TIMESTAMP}]"
git push origin results
echo "✅ Results committed to results branch"
else
commit_msg="chore(results): update benchmark summaries from run #${{ github.event.workflow_run.run_number }}"
git commit -m "$commit_msg"
echo "✅ Results committed"
echo "ℹ️ No changes to commit to results branch"
fi

# Return to main workspace and conditionally update main branch
cd "$CURRENT_DIR"
if [ "$SKIP_MAIN_COMMIT" = "true" ]; then
echo "⏭️ Skipping main branch commit (skip_commit=true)"
else
git add results/ website/static/chart*.json website/static/charts/ .github/README.md || true
if ! git diff --cached --quiet; then
git commit -m "chore(results): update benchmark summaries from run #${{ github.event.workflow_run.run_number }}"
git push
echo "✅ Main branch updated"
else
echo "ℹ️ No changes to commit to main branch"
fi
fi

- name: 🚀 Push Changes
if: env.skip_commit != 'true'
run: |
git push
echo "✅ Changes pushed to repository"

- name: 🧹 Cleanup
if: always()
run: |
echo "🧹 Cleaning up temporary files..."
rm -rf ./downloaded-artifacts benchmark-results
rm -rf ./downloaded-artifacts benchmark-results /tmp/results-branch
echo "✅ Cleanup complete"
32 changes: 24 additions & 8 deletions apps/alpine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<h1 align="center">🏔️ Weather Front - Alpine.js</h1>

<p align="center">
<img width="64" src="https://raw.githubusercontent.com/Lissy93/weather-front/refs/heads/main/assets/favicon.png" /><br>
<img width="64" src="https://raw.githubusercontent.com/lissy93/framework-benchmarks/refs/heads/main/assets/favicon.png" /><br>
<i>A tiny weather app</i>
<br>
<b><a href="/">🚀 Demo</a> ● <a href="https://frontend-framework-benchmarks.as93.net">📊 Results</a></b>
Expand Down Expand Up @@ -40,17 +40,17 @@ This is a simple weather app, built in [Alpine.js](https://alpinejs.dev/) (as we

| Task | Status |
|---|---|
| **Test** - Executes all e2e and unit tests | [![Test Status](https://raw.githubusercontent.com/Lissy93/weather-front/refs/heads/badges/test-alpine.svg)](https://github.com/Lissy93/weather-front/actions/workflows/test.yml) |
| **Lint** - Verifies code style and quality | [![Lint Status](https://raw.githubusercontent.com/Lissy93/weather-front/refs/heads/badges/lint-alpine.svg)](https://github.com/Lissy93/weather-front/actions/workflows/lint.yml) |
| **Build** - Builds and deploys the app | [![Build Status](https://raw.githubusercontent.com/Lissy93/weather-front/refs/heads/badges/build-alpine.svg)](https://github.com/Lissy93/weather-front/actions/workflows/build.yml) |
| **Test** - Executes all e2e and unit tests | [![Test Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/refs/heads/badges/test-alpine.svg)](https://github.com/lissy93/framework-benchmarks/actions/workflows/test.yml) |
| **Lint** - Verifies code style and quality | [![Lint Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/refs/heads/badges/lint-alpine.svg)](https://github.com/lissy93/framework-benchmarks/actions/workflows/lint.yml) |
| **Build** - Builds and deploys the app | [![Build Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/refs/heads/badges/build-alpine.svg)](https://github.com/lissy93/framework-benchmarks/actions/workflows/build.yml) |

<!-- end_status -->

<!-- start_usage -->

## Usage

First, follow the [repo setup instructions](https://github.com/Lissy93/weather-front?tab=readme-ov-file#usage). Then `cd apps/alpine` and use the following commands:
First, follow the [repo setup instructions](https://github.com/lissy93/framework-benchmarks?tab=readme-ov-file#usage). Then `cd apps/alpine` and use the following commands:

```bash
npm run dev # Start dev server (python3 -m http.server 3000 || python -m http.server 3000)
Expand All @@ -67,13 +67,29 @@ For troubleshooting, use `npm run verify` from the root of the project.
## Alpine Implementation

<!-- start_framework_specific -->
### Notable files
- `index.html` - Main HTML with Alpine directives sprinkled in
- `js/weather-app.js` - Alpine data and methods for weather logic
- `js/weather-service.js` - API calls integrated with Alpine reactivity
- `js/weather-utils.js` - Utility functions for data formatting
<!-- end_framework_specific -->

## About Alpine
<!-- start_framework_description -->
<!-- end_framework_description -->

## My Thoughts on Alpine

<!-- start_my_thoughts -->
Alpine.js is like jQuery had a baby with Vue and decided to live directly in your HTML. It's refreshingly simple - you sprinkle a few `x-` attributes into your markup and suddenly you have reactive behavior. No build tools, no bundlers, no complexity. Just add a script tag and start building.

The approach feels intuitive once you get it. `x-data` sets up your reactive state, `x-show` handles conditional rendering, and `x-for` loops through arrays. Our weather app's forecast list is just `<div x-for="day in forecast">` - no components, no imports, no ceremony.

What's clever is how Alpine stays out of your way. The HTML is still readable, the JavaScript is minimal, and everything degrades gracefully if Alpine doesn't load. It's progressive enhancement done right - the page works without JavaScript, but becomes interactive when it loads.

The syntax reads naturally: `x-on:click="searchWeather()"`, `x-text="temperature"`, `x-bind:class="{'active': isExpanded}"`. It's declarative like Vue templates but lives right in the HTML. The reactive updates happen automatically when you modify the data.

For simple interactive websites, Alpine hits the sweet spot. You get modern reactivity without the complexity of a full framework. But for anything complex, you'll miss proper component organization and tooling. Alpine works great for [my whois lookup API](https://github.com/Lissy93/who-dat), because I just needed sprinkles of interactivity to update results, not a full SPA experience.
<!-- end_my_thoughts -->

<!-- start_real_world_app -->

Expand All @@ -93,7 +109,7 @@ Since the weather app is very simple, it may be helpful to see a more practical

## License

Weather-Front is licensed under [MIT](https://github.com/Lissy93/weather-front/blob/main/LICENSE) © Alicia Sykes 2025.<br>
View [Attributions](https://github.com/Lissy93/weather-front?tab=readme-ov-file#attributions) for credits, thanks and contributors.
Weather-Front is licensed under [MIT](https://github.com/lissy93/framework-benchmarks/blob/main/LICENSE) © Alicia Sykes 2025.<br>
View [Attributions](https://github.com/lissy93/framework-benchmarks?tab=readme-ov-file#attributions) for credits, thanks and contributors.

<!-- end_license -->
33 changes: 24 additions & 9 deletions apps/angular/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<h1 align="center">🅰️ Weather Front - Angular</h1>

<p align="center">
<img width="64" src="https://raw.githubusercontent.com/Lissy93/weather-front/refs/heads/main/assets/favicon.png" /><br>
<img width="64" src="https://raw.githubusercontent.com/lissy93/framework-benchmarks/refs/heads/main/assets/favicon.png" /><br>
<i>A tiny weather app</i>
<br>
<b><a href="/">🚀 Demo</a> ● <a href="https://frontend-framework-benchmarks.as93.net">📊 Results</a></b>
Expand Down Expand Up @@ -40,17 +40,17 @@ This is a simple weather app, built in [Angular](https://angular.io/) (as well a

| Task | Status |
|---|---|
| **Test** - Executes all e2e and unit tests | [![Test Status](https://raw.githubusercontent.com/Lissy93/weather-front/refs/heads/badges/test-angular.svg)](https://github.com/Lissy93/weather-front/actions/workflows/test.yml) |
| **Lint** - Verifies code style and quality | [![Lint Status](https://raw.githubusercontent.com/Lissy93/weather-front/refs/heads/badges/lint-angular.svg)](https://github.com/Lissy93/weather-front/actions/workflows/lint.yml) |
| **Build** - Builds and deploys the app | [![Build Status](https://raw.githubusercontent.com/Lissy93/weather-front/refs/heads/badges/build-angular.svg)](https://github.com/Lissy93/weather-front/actions/workflows/build.yml) |
| **Test** - Executes all e2e and unit tests | [![Test Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/refs/heads/badges/test-angular.svg)](https://github.com/lissy93/framework-benchmarks/actions/workflows/test.yml) |
| **Lint** - Verifies code style and quality | [![Lint Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/refs/heads/badges/lint-angular.svg)](https://github.com/lissy93/framework-benchmarks/actions/workflows/lint.yml) |
| **Build** - Builds and deploys the app | [![Build Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/refs/heads/badges/build-angular.svg)](https://github.com/lissy93/framework-benchmarks/actions/workflows/build.yml) |

<!-- end_status -->

<!-- start_usage -->

## Usage

First, follow the [repo setup instructions](https://github.com/Lissy93/weather-front?tab=readme-ov-file#usage). Then `cd apps/angular` and use the following commands:
First, follow the [repo setup instructions](https://github.com/lissy93/framework-benchmarks?tab=readme-ov-file#usage). Then `cd apps/angular` and use the following commands:

```bash
npm run dev # Start dev server (ng serve --port 3000)
Expand All @@ -64,17 +64,32 @@ For troubleshooting, use `npm run verify` from the root of the project.

<!-- end_usage -->

## Angular Implementation
## About Angular
<!-- start_framework_description -->
<!-- end_framework_description -->

## Angular Implementation
<!-- start_framework_specific -->
### Notable files
- `src/app/app.component.ts` - Root component with weather state management
- `src/app/services/weather.service.ts` - Injectable service using Angular's HttpClient
- `src/app/services/weather-state.service.ts` - Centralized state with RxJS observables
- `src/app/components/` - Standalone components for weather display
- `src/app/types/weather.types.ts` - TypeScript interfaces for type safety
<!-- end_framework_specific -->

## My Thoughts on Angular
<!-- start_my_thoughts -->
Angular isn't the cool kid anymore, but it's incredibly solid and ships with absolutely everything you need. TypeScript from day one, dependency injection, forms, HTTP client, routing, testing utilities - it's all there, officially maintained and deeply integrated. No need to cobble together a stack from random npm packages.

For our weather app, Angular did kinda feel like using a sledgehammer to crack a nut. Using the newer standalone components (no more `NgModule` boilerplate!) made things cleaner, but I was still writing a lot more code than I needed in Svelte or Vue. That said, everything does just works, and the TypeScript integration is phenomenal.

The dependency injection system was quite nice for having `WeatherService` automatically injected into components. And RxJS observables handle all the async weather data very nicley, though they do add a learning curve if you're not familiar with reactive programming.

Angular's template syntax with `*ngIf`, `*ngFor`, and `(click)` feels natural once you get used to it. Change detection just works without thinking about it (unlike React where you're constantly memoizing things).

For a simple weather app, we really didn't need any of Angular's big or flagship features (like guards, resolvers, or lazy loading). But I recently build [Domain Locker](https://github.com/lissy93/domain-locker) using Angular, and it was a great fit for the complexity of that project, As the structure, type safety, and tooling made it easy to manage a large codebase with multiple features.
<!-- end_my_thoughts -->


<!-- start_real_world_app -->

Expand All @@ -94,7 +109,7 @@ Since the weather app is very simple, it may be helpful to see a more practical

## License

Weather-Front is licensed under [MIT](https://github.com/Lissy93/weather-front/blob/main/LICENSE) © Alicia Sykes 2025.<br>
View [Attributions](https://github.com/Lissy93/weather-front?tab=readme-ov-file#attributions) for credits, thanks and contributors.
Weather-Front is licensed under [MIT](https://github.com/lissy93/framework-benchmarks/blob/main/LICENSE) © Alicia Sykes 2025.<br>
View [Attributions](https://github.com/lissy93/framework-benchmarks?tab=readme-ov-file#attributions) for credits, thanks and contributors.

<!-- end_license -->
Loading
Loading