Skip to content

Commit d6805a3

Browse files
authored
Merge pull request #298 from Lissy93/feat/advisory-panel
Advisory panel
2 parents b913398 + 316b815 commit d6805a3

208 files changed

Lines changed: 1791 additions & 881 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

β€Ž.github/workflows/deploy-aws.ymlβ€Ž

Lines changed: 0 additions & 127 deletions
This file was deleted.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Publishes GH release for new versions, with compiled app attached
2+
# - Triggered when a git tag matching X.Y.0 (major or minor) is pushed
3+
# - Or if manually dispatched with any valid and existing tag specified
4+
# - Check out the source at the requested tag
5+
# - Run a clean production build (yarn build β†’ dist/)
6+
# - Bundle dist/ + server.js + package.json + yarn.lock into tar.gz and zip
7+
# - Create a DRAFT GH release with auto-gen changelog and bundled artifacts attached
8+
9+
name: πŸš€ Release
10+
11+
on:
12+
push:
13+
tags:
14+
- '*.*.0'
15+
workflow_dispatch:
16+
inputs:
17+
tag:
18+
description: 'Existing git tag to release (e.g. 2.1.0)'
19+
required: true
20+
type: string
21+
22+
permissions:
23+
contents: write
24+
25+
concurrency:
26+
group: release-${{ inputs.tag || github.ref_name }}
27+
cancel-in-progress: false
28+
29+
jobs:
30+
release:
31+
name: πŸš€ Build & Publish Release
32+
runs-on: ubuntu-latest
33+
steps:
34+
- name: Resolve tag 🏷️
35+
id: tag
36+
env:
37+
INPUT_TAG: ${{ inputs.tag }}
38+
EVENT_NAME: ${{ github.event_name }}
39+
REF: ${{ github.ref }}
40+
run: |
41+
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
42+
TAG="$INPUT_TAG"
43+
else
44+
TAG="${REF#refs/tags/}"
45+
fi
46+
if ! echo "$TAG" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
47+
echo "::error::Invalid tag '$TAG'. Expected semver (e.g. 2.1.0)."
48+
exit 1
49+
fi
50+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
51+
echo "Releasing tag: $TAG"
52+
53+
- name: Checkout source at tag πŸ›ŽοΈ
54+
uses: actions/checkout@v6
55+
with:
56+
ref: refs/tags/${{ steps.tag.outputs.tag }}
57+
fetch-depth: 0
58+
59+
- name: Setup Node.js πŸ”§
60+
uses: actions/setup-node@v6
61+
with:
62+
node-version: '22'
63+
cache: 'yarn'
64+
65+
- name: Install dependencies πŸ“¦
66+
run: yarn install --frozen-lockfile
67+
68+
- name: Build for production πŸ—οΈ
69+
run: yarn build
70+
71+
- name: Verify build output βœ…
72+
run: |
73+
if [ ! -d "dist/client" ]; then
74+
echo "::error::Build failed: dist/client directory not created"
75+
exit 1
76+
fi
77+
if [ ! -f "dist/server/entry.mjs" ]; then
78+
echo "::error::Build failed: SSR entry not found"
79+
exit 1
80+
fi
81+
echo "βœ… Build successful"
82+
83+
- name: Package release artifacts πŸ—œοΈ
84+
id: package
85+
env:
86+
TAG: ${{ steps.tag.outputs.tag }}
87+
run: |
88+
STAGING="web-check-${TAG}"
89+
rm -rf "$STAGING"
90+
mkdir -p "$STAGING"
91+
cp -r dist api public "$STAGING/"
92+
cp server.js package.json yarn.lock "$STAGING/"
93+
[ -f LICENSE ] && cp LICENSE "$STAGING/" || true
94+
[ -f README.md ] && cp README.md "$STAGING/" || true
95+
96+
TARBALL="${STAGING}.tar.gz"
97+
ZIPFILE="${STAGING}.zip"
98+
tar -czf "$TARBALL" "$STAGING"
99+
zip -qr "$ZIPFILE" "$STAGING"
100+
101+
ls -lh "$TARBALL" "$ZIPFILE"
102+
echo "tarball=$TARBALL" >> "$GITHUB_OUTPUT"
103+
echo "zipfile=$ZIPFILE" >> "$GITHUB_OUTPUT"
104+
105+
- name: Create draft release πŸš€
106+
id: release
107+
uses: softprops/action-gh-release@v3
108+
with:
109+
tag_name: ${{ steps.tag.outputs.tag }}
110+
name: Web-Check v${{ steps.tag.outputs.tag }}
111+
draft: true
112+
prerelease: false
113+
generate_release_notes: true
114+
fail_on_unmatched_files: true
115+
files: |
116+
${{ steps.package.outputs.tarball }}
117+
${{ steps.package.outputs.zipfile }}
118+
token: ${{ secrets.BOT_TOKEN || secrets.GITHUB_TOKEN }}
119+
120+
- name: Job summary πŸ“‹
121+
if: always()
122+
env:
123+
TAG: ${{ steps.tag.outputs.tag }}
124+
REPO_URL: ${{ github.server_url }}/${{ github.repository }}
125+
RELEASE_URL: ${{ steps.release.outputs.url }}
126+
RELEASE_OUTCOME: ${{ steps.release.outcome }}
127+
run: |
128+
{
129+
echo "## πŸš€ Release"
130+
echo ""
131+
echo "| Step | Result |"
132+
echo "|------|--------|"
133+
if [ -n "$TAG" ]; then
134+
echo "| Tag | [\`${TAG}\`](${REPO_URL}/releases/tag/${TAG}) |"
135+
else
136+
echo "| Tag | ❌ Could not resolve |"
137+
fi
138+
if [ "$RELEASE_OUTCOME" = "success" ]; then
139+
if [ -n "$RELEASE_URL" ]; then
140+
echo "| Draft release | βœ… [View draft](${RELEASE_URL}) |"
141+
else
142+
echo "| Draft release | βœ… [View releases](${REPO_URL}/releases) |"
143+
fi
144+
echo "| Artifacts | \`web-check-${TAG}.tar.gz\`, \`web-check-${TAG}.zip\` |"
145+
else
146+
echo "| Draft release | ❌ Failed (outcome: ${RELEASE_OUTCOME:-unknown}) |"
147+
fi
148+
} >> "$GITHUB_STEP_SUMMARY"

β€Žapi/carbon.jsβ€Ž

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ const GRID_INTENSITY = 442;
1616
const RENEWABLE_INTENSITY = 50;
1717
const LITRES_PER_GRAM = 0.5562;
1818

19+
// Reference median grams CO2 per visit, drawn from websitecarbon's published average.
20+
// Used to estimate a percentile rank since we lack their measured-sites dataset
21+
const REFERENCE_MEDIAN_GRAMS = 0.8;
22+
23+
// Approximate percentile via log2 distance from the reference median.
24+
// 1 doubling above median drops 25 points; clamp to [1, 99]
25+
const estimateCleanerThan = (grams) => {
26+
if (!grams || grams <= 0) return 0;
27+
const pct = 50 - 25 * Math.log2(grams / REFERENCE_MEDIAN_GRAMS);
28+
return Math.max(1, Math.min(99, Math.round(pct)));
29+
};
30+
1931
// Stream the response, cap at MAX_BYTES so huge pages can't blow memory or time
2032
const fetchByteCount = async (url) => {
2133
const r = await fetch(url, {
@@ -66,11 +78,13 @@ const carbonHandler = async (url) => {
6678
}
6779
if (!bytes) return { skipped: 'Site returned no content, cannot calculate carbon' };
6880
log.debug(`measured ${bytes} bytes for ${url}`);
81+
const statistics = computeCarbon(bytes);
6982
return {
7083
url,
7184
bytes,
7285
green: false,
73-
statistics: computeCarbon(bytes),
86+
statistics,
87+
cleanerThan: estimateCleanerThan(statistics.co2.grid.grams),
7488
scanUrl: url,
7589
};
7690
};

β€Žastro.config.mjsβ€Ž

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { defineConfig } from 'astro/config';
2+
import { loadEnv } from 'vite';
23

34
// Integrations
45
import svelte from '@astrojs/svelte';
56
import react from '@astrojs/react';
6-
import partytown from '@astrojs/partytown';
77
import sitemap from '@astrojs/sitemap';
88

99
// Adapters
@@ -12,12 +12,12 @@ import netlifyAdapter from '@astrojs/netlify';
1212
import nodeAdapter from '@astrojs/node';
1313
import cloudflareAdapter from '@astrojs/cloudflare';
1414

15-
// Helper function to unwrap both Vite and Node environment variables
16-
const unwrapEnvVar = (varName, fallbackValue) => {
17-
const classicEnvVar = process?.env && process.env[varName];
18-
const viteEnvVar = import.meta.env[varName];
19-
return classicEnvVar || viteEnvVar || fallbackValue;
20-
};
15+
// Pre-load .env so values are available in this config, before Vite
16+
const fileEnv = loadEnv(process.env.NODE_ENV || 'development', process.cwd(), '');
17+
18+
// Read an env var, preferring shell over .env, with a final fallback
19+
const unwrapEnvVar = (varName, fallbackValue) =>
20+
process.env[varName] ?? fileEnv[varName] ?? fallbackValue;
2121

2222
// Determine the deploy target (vercel, netlify, cloudflare, node)
2323
const deployTarget = unwrapEnvVar('PLATFORM', 'node').toLowerCase();
@@ -35,7 +35,7 @@ const base = unwrapEnvVar('BASE_URL', '/');
3535
const isBossServer = unwrapEnvVar('BOSS_SERVER', false);
3636

3737
// Initialize Astro integrations
38-
const integrations = [svelte(), react(), partytown(), sitemap()];
38+
const integrations = [svelte(), react(), sitemap()];
3939

4040
// Set the appropriate adapter, based on the deploy target
4141
function getAdapter(target) {

β€Žpackage.jsonβ€Ž

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
"puppeteer-core": "^24.42.0",
4747
"react": "^19.2.5",
4848
"react-dom": "^19.2.5",
49-
"react-masonry-css": "^1.0.16",
5049
"react-router-dom": "^7.14.2",
5150
"react-simple-maps": "^3.0.0",
5251
"react-toastify": "^11.1.0",
@@ -73,7 +72,6 @@
7372
"@astrojs/cloudflare": "^13.3.1",
7473
"@astrojs/netlify": "^7.0.8",
7574
"@astrojs/node": "^10.0.6",
76-
"@astrojs/partytown": "^2.1.7",
7775
"@astrojs/sitemap": "^3.7.2",
7876
"@astrojs/svelte": "^8.1.0",
7977
"@astrojs/ts-plugin": "^1.10.7",
-347 KB
Binary file not shown.
-166 KB
Binary file not shown.
-37.9 KB
Binary file not shown.
-44.5 KB
Binary file not shown.
-40 KB
Binary file not shown.

0 commit comments

Comments
Β (0)