treewide: build via nix

This commit is contained in:
Ophestra 2025-06-27 20:31:45 +09:00
parent f16ef88f9f
commit 42c21ad906
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
16 changed files with 213 additions and 11022 deletions

27
flake.lock generated Normal file
View File

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1750838302,
"narHash": "sha256-aVkL3/yu50oQzi2YuKo0ceiCypVZpZXYd2P2p1FMJM4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7284e2decc982b81a296ab35aa46e804baaa1cfe",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

85
flake.nix Normal file
View File

@ -0,0 +1,85 @@
{
description = "hakurei.app website, based on grapheneos.org";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
};
outputs =
{
self,
nixpkgs,
}:
let
supportedSystems = [
"aarch64-linux"
"x86_64-linux"
];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
in
{
checks = forAllSystems (
system:
let
pkgs = nixpkgsFor.${system};
inherit (pkgs)
runCommandLocal
nixfmt-rfc-style
deadnix
statix
;
in
{
formatting = runCommandLocal "check-formatting" { nativeBuildInputs = [ nixfmt-rfc-style ]; } ''
cd ${./.}
echo "running nixfmt..."
nixfmt --check .
touch $out
'';
lint =
runCommandLocal "check-lint"
{
nativeBuildInputs = [
deadnix
statix
];
}
''
cd ${./.}
echo "running deadnix..."
deadnix --fail
echo "running statix..."
statix check .
touch $out
'';
}
);
packages = forAllSystems (
system:
let
inherit (self.packages.${system}) hakurei-static hakurei-static-web-server;
pkgs = nixpkgsFor.${system};
in
{
default = hakurei-static-web-server;
hakurei-static = pkgs.callPackage ./package.nix { };
hakurei-static-web-server = pkgs.writeShellScriptBin "hakurei-static-web-server" ''
exec ${pkgs.static-web-server}/bin/static-web-server \
-g info \
-p 49151 \
-d ${hakurei-static}
'';
}
);
};
}

View File

@ -1,55 +0,0 @@
#!/usr/bin/env python3
from datetime import datetime
import lxml.html
from lxml import etree
document = lxml.html.parse("static-tmp/releases.html").getroot()
releases = document.body.cssselect("#changelog article")
updated = None
entries = []
for release in releases[:20]:
title = release.attrib["id"]
try:
time = datetime.strptime(title, "%Y%m%d%H").isoformat() + "Z"
except ValueError:
time = datetime.strptime(title, "%Y.%m.%d.%H").isoformat() + "Z"
if updated is None:
updated = time
content = [etree.tostring(e).decode() for e in release.getchildren()[1:]]
entries.append(f"""
<entry>
<id>https://grapheneos.org/releases#{title}</id>
<link href="https://grapheneos.org/releases#{title}"/>
<title>{title}</title>
<updated>{time}</updated>
<published>{time}</published>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">
{"".join(content)}
</div>
</content>
</entry>""")
feed = f"""<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>https://grapheneos.org/releases#changelog</id>
<link href="https://grapheneos.org/releases#changelog"/>
<link rel="self" href="https://grapheneos.org/releases.atom"/>
<link rel="license" href="https://grapheneos.org/LICENSE.txt"/>
<icon>https://grapheneos.org/favicon.ico</icon>
<title>GrapheneOS changelog</title>
<updated>{updated}</updated>
<author>
<name>GrapheneOS</name>
<email>contact@grapheneos.org</email>
<uri>https://grapheneos.org/</uri>
</author>{"".join(entries)}
</feed>
"""
with open("static-tmp/releases.atom", "w") as f:
f.write(feed)

3
generate-sitemap → generate-sitemap.py Executable file → Normal file
View File

@ -1,5 +1,3 @@
#!/usr/bin/env python3
from datetime import datetime, timezone from datetime import datetime, timezone
from os.path import getmtime from os.path import getmtime
from pathlib import Path from pathlib import Path
@ -31,7 +29,6 @@ pages = [
["/install/", 0.5], ["/install/", 0.5],
["/install/cli", 0.5], ["/install/cli", 0.5],
["/install/web", 0.5], ["/install/web", 0.5],
["/releases", 0.5],
["/source", 0.5], ["/source", 0.5],
["/usage", 1.0] ["/usage", 1.0]
] ]

View File

@ -1,100 +0,0 @@
types {
text/html html htm shtml;
text/css css;
text/javascript js mjs;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/atom+xml atom;
application/rss+xml rss;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/avif avif;
image/png png;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/webp webp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
font/woff woff;
font/woff2 woff2;
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/manifest+json webmanifest;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation
pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document
docx;
application/vnd.wap.wmlc wmlc;
application/wasm wasm;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}

View File

@ -1 +0,0 @@
/usr/lib/nginx/modules

View File

@ -1,484 +0,0 @@
load_module modules/ngx_http_brotli_static_module.so;
error_log syslog:server=unix:/dev/log,nohostname;
# leave stderr open but minimize duplicate logging to it
error_log stderr emerg;
worker_processes auto;
worker_rlimit_nofile 32768;
worker_shutdown_timeout 1h;
events {
worker_connections 8192;
}
http {
root /var/empty;
include mime.types;
default_type application/octet-stream;
charset utf-8;
charset_types text/css text/javascript text/plain text/xml application/atom+xml;
sendfile on;
sendfile_max_chunk 256k;
tcp_nopush on;
keepalive_requests 256;
keepalive_timeout 0;
server_tokens off;
msie_padding off;
client_max_body_size 1k;
client_body_buffer_size 1k;
client_header_buffer_size 1k;
large_client_header_buffers 2 1k;
http2_chunk_size 4k;
reset_timedout_connection on;
client_body_timeout 15s;
client_header_timeout 15s;
send_timeout 30s;
max_ranges 1;
resolver [::1];
resolver_timeout 15s;
http2_max_concurrent_streams 16;
limit_conn_status 429;
limit_conn_zone $binary_remote_addr zone=http-limit:10m;
limit_conn http-limit 128;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
ssl_conf_command Options PrioritizeChaCha;
ssl_certificate /etc/letsencrypt/live/grapheneos.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/grapheneos.org/privkey.pem;
# maintained by rotate-session-ticket-keys in noswap tmpfs
ssl_session_ticket_key /etc/session-ticket-keys/4.key;
ssl_session_ticket_key /etc/session-ticket-keys/3.key;
ssl_session_ticket_key /etc/session-ticket-keys/2.key;
ssl_session_ticket_key /etc/session-ticket-keys/1.key;
ssl_session_timeout 1d;
ssl_buffer_size 4k;
log_format main '$connection-$connection_requests $remote_addr $remote_user $ssl_session_reused $ssl_protocol $server_protocol '
'$host $request_method "$request_uri" $status $request_length $body_bytes_sent/$bytes_sent '
'$request_time $upstream_connect_time/$upstream_header_time/$upstream_response_time '
'$upstream_cache_status "$http_referer" "$http_user_agent"';
access_log syslog:server=unix:/dev/log,nohostname main;
log_subrequest on;
log_not_found off;
gzip_proxied any;
gzip_vary on;
if_modified_since before;
aio threads;
aio_write on;
map $uri $preload_resources_uri {
/articles/grapheneos-servers.html ", <[[path|/js/redirect.js]]>; rel=modulepreload; integrity=[[integrity|/js/redirect.js]]";
/build.html ", <[[path|/js/redirect.js]]>; rel=modulepreload; integrity=[[integrity|/js/redirect.js]]";
/faq.html ", <[[path|/js/redirect.js]]>; rel=modulepreload; integrity=[[integrity|/js/redirect.js]]";
/features.html ", <[[path|/js/redirect.js]]>; rel=modulepreload; integrity=[[integrity|/js/redirect.js]]";
/index.html ", <[[path|/pixel-7-pro.svg]]>; rel=preload; as=image; fetchpriority=high, <[[path|/js/redirect.js]]>; rel=modulepreload; integrity=[[integrity|/js/redirect.js]]";
/install/cli.html ", <[[path|/js/redirect.js]]>; rel=modulepreload; integrity=[[integrity|/js/redirect.js]]";
/install/index.html ", <[[path|/js/redirect.js]]>; rel=modulepreload; integrity=[[integrity|/js/redirect.js]]";
/install/web.html ", <[[path|/js/redirect.js]]>; rel=modulepreload; integrity=[[integrity|/js/redirect.js]]";
/releases.html ", <[[path|/js/redirect.js]]>; rel=modulepreload; integrity=[[integrity|/js/redirect.js]]";
/usage.html ", <[[path|/js/redirect.js]]>; rel=modulepreload; integrity=[[integrity|/js/redirect.js]]";
}
server {
listen 80 default_server backlog=4096 rcvbuf=2048 sndbuf=2048;
listen [::]:80 default_server backlog=4096 rcvbuf=2048 sndbuf=2048;
# https://trac.nginx.org/nginx/ticket/2012
location / {
return 404;
}
}
server {
listen 80;
listen [::]:80;
server_name grapheneos.org www.grapheneos.org grapheneos.app www.grapheneos.app grapheneos.ca www.grapheneos.ca grapheneos.com www.grapheneos.com grapheneos.dev www.grapheneos.dev grapheneos.foundation www.grapheneos.foundation grapheneos.info www.grapheneos.info grapheneos.net www.grapheneos.net grapheneos.ovh www.grapheneos.ovh grapheneos.page www.grapheneos.page vanadium.app www.vanadium.app;
location /.well-known/acme-challenge/ {
return 301 http://0.grapheneos.org$request_uri;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 80;
listen [::]:80;
server_name 0.grapheneos.org;
location /.well-known/acme-challenge/ {
root /srv/certbot;
}
location / {
return 301 https://grapheneos.org$request_uri;
}
}
server {
listen 443 default_server ssl backlog=4096;
listen [::]:443 default_server ssl backlog=4096;
http2 on;
ssl_reject_handshake on;
# https://trac.nginx.org/nginx/ticket/2012
location / {
return 404;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name www.grapheneos.org grapheneos.app www.grapheneos.app grapheneos.ca www.grapheneos.ca grapheneos.com www.grapheneos.com grapheneos.dev www.grapheneos.dev grapheneos.foundation www.grapheneos.foundation grapheneos.info www.grapheneos.info grapheneos.net www.grapheneos.net grapheneos.ovh www.grapheneos.ovh grapheneos.page www.grapheneos.page;
keepalive_timeout 3m;
include snippets/security-headers.conf;
add_header Cross-Origin-Resource-Policy "same-origin" always;
# https://trac.nginx.org/nginx/ticket/2012
location / {
return 301 https://grapheneos.org$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name www.vanadium.app;
keepalive_timeout 3m;
include snippets/security-headers.conf;
add_header Cross-Origin-Resource-Policy "same-origin" always;
# https://trac.nginx.org/nginx/ticket/2012
location / {
return 301 https://vanadium.app$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name vanadium.app;
keepalive_timeout 3m;
include snippets/security-headers.conf;
add_header Cross-Origin-Resource-Policy "same-origin" always;
location = / {
include snippets/security-headers.conf;
add_header Cache-Control "public, max-age=2592000";
return 301 https://grapheneos.org/features#vanadium;
}
location / {
return 404;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name grapheneos.org;
include root_grapheneos.org.conf;
error_page 403 =404 /404;
error_page 404 /404;
keepalive_timeout 3m;
open_file_cache max=2048 inactive=1d;
open_file_cache_valid 1d;
include snippets/security-headers.conf;
add_header Cross-Origin-Resource-Policy "same-origin" always;
gzip_static on;
brotli_static on;
if ($request_uri ~ ^[^?]*//) {
rewrite ^(.*)$ $1 permanent;
}
location = /, {
return 301 /;
}
location = /security.txt {
return 301 /.well-known/security.txt;
}
location = /bitcoin-address.png {
return 301 /donate-bitcoin.png;
}
location = /bitcoin-donation.png {
return 301 /donate-bitcoin.png;
}
location = /monero-donation.png {
return 301 /donate-monero.png;
}
location = /pdfviewer_privacy_policy {
return 301 /pdfviewer-privacy-policy;
}
# mangled backlinks to /install
location = /installMinimal {
return 301 /install/;
}
# mangled backlink to /faq
location = /fa {
return 301 /faq;
}
location = /FAQ {
return 301 /faq;
}
# mangled backlinks to /usage#updates
location = /updates {
return 301 /usage#updates;
}
location = /web-install {
return 301 /install/web;
}
location = /install-web {
return 301 /install/web;
}
location = /cli/install {
return 301 /install/cli;
}
location = /web/install {
return 301 /install/web;
}
location = /generate_204 {
return 301 /faq#default-connections;
}
# redirect away from the old SVG favicon location
location = /mask-icon.svg {
return 301 /favicon.svg;
}
location = "/legal/Micay_ Copperhead_ Statement of Defendant and Counterclaim.pdf" {
return 301 /history/copperheados;
}
location = /discord {
include snippets/security-headers.conf;
add_header Cache-Control "public, max-age=86400";
return 301 https://discord.com/invite/grapheneos;
}
location = /code-of-conduct {
include snippets/security-headers.conf;
add_header Cache-Control "public, max-age=86400";
return 301 https://discuss.grapheneos.org/d/11-grapheneos-code-of-conduct;
}
location = /UBL {
return 301 /faq#bootloader-locking-setup;
}
location = /ubl {
return 301 /faq#bootloader-locking-setup;
}
location = /404 {
internal;
include snippets/security-headers.conf;
add_header Cross-Origin-Resource-Policy "same-origin" always;
include snippets/preload.conf;
try_files $uri.html =404;
}
location = /allowed_signers {}
location = /allowed_signers.sig {}
location = /allowed_signers.asc {}
location = /manifest.webmanifest {
include snippets/security-headers.conf;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Cache-Control "public, max-age=604800";
}
location = /favicon.ico {
if ($http_accept ~ "image/svg\+xml") {
rewrite ^ /favicon.svg last;
}
include snippets/security-headers.conf;
# avoid breaking image hotlinking such as https://github.com/TryGhost/Ghost/issues/12880
add_header Cross-Origin-Resource-Policy "cross-origin" always;
add_header Cache-Control "public, max-age=604800";
}
location = /favicon.svg {
include snippets/security-headers.conf;
# avoid breaking image hotlinking such as https://github.com/TryGhost/Ghost/issues/12880
add_header Cross-Origin-Resource-Policy "cross-origin" always;
add_header Cache-Control "public, max-age=604800";
}
location = /bimi.svg {
include snippets/security-headers.conf;
# allow https://bimigroup.org/bimi-generator/ to hotlink the image
add_header Cross-Origin-Resource-Policy "cross-origin" always;
add_header Cache-Control "public, max-age=604800";
}
location = /.well-known/matrix/client {
include snippets/security-headers.conf;
add_header Cross-Origin-Resource-Policy "cross-origin" always;
add_header Access-Control-Allow-Origin "*";
add_header Cache-Control "public, max-age=172800";
default_type application/json;
}
location = /.well-known/matrix/server {
include snippets/security-headers.conf;
add_header Cache-Control "public, max-age=172800";
default_type application/json;
}
location = /.well-known/traffic-advice {
default_type application/trafficadvice+json;
}
location = /install/web {
if ($request_uri ~ \?) {
rewrite ^(.*)$ $1? permanent;
}
include snippets/security-headers-base.conf;
add_header Content-Security-Policy "default-src 'none'; child-src 'self'; connect-src 'self' https://releases.grapheneos.org/; font-src 'self'; img-src 'self'; manifest-src 'self'; script-src 'self'; style-src 'self'; webrtc 'block'; form-action 'none'; frame-ancestors 'none'; base-uri 'none'" always;
add_header Permissions-Policy "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), bluetooth=(), camera=(), clipboard-read=(), clipboard-write=(), display-capture=(), document-domain=(), encrypted-media=(), fullscreen=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), interest-cohort=(), keyboard-map=(), local-fonts=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(self), serial=(), speaker-selection=(), sync-xhr=(), xr-spatial-tracking=()" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Cache-Control "public, no-cache";
include snippets/preload.conf;
try_files $uri.html =404;
}
location ^~ /fonts/ {
include snippets/security-headers.conf;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Cache-Control "public, max-age=31536000, immutable";
gzip_static off;
brotli_static off;
}
location ~ "/$" {
if ($request_uri ~ \?) {
rewrite ^(.*)$ $1? permanent;
}
include snippets/security-headers.conf;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Cache-Control "public, no-cache";
include snippets/preload.conf;
try_files ${uri}index.html @noslash;
}
# redirect /path/ to /path if /path.html exists
location @noslash {
rewrite ^(.*)/$ $1;
if (-f $request_filename.html) {
rewrite ^(.*) $1 permanent;
}
return 404;
}
location ~ "\.(?:css|js|map|mjs)$" {
include snippets/security-headers-base.conf;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Cache-Control "public, max-age=31536000, immutable";
}
location ~ "\.svg$" {
include snippets/security-headers.conf;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Cache-Control "public, max-age=31536000, immutable";
}
location ~ "\.png$" {
include snippets/security-headers.conf;
# avoid breaking image hotlinking such as https://github.com/TryGhost/Ghost/issues/12880
add_header Cross-Origin-Resource-Policy "cross-origin" always;
add_header Cache-Control "public, max-age=31536000";
gzip_static off;
brotli_static off;
}
location ~ "\.atom$" {
include snippets/security-headers.conf;
# Thunderbird uses wrong origin for feeds: https://bugzilla.mozilla.org/show_bug.cgi?id=1698755
add_header Cross-Origin-Resource-Policy "cross-origin" always;
add_header Cache-Control "public, max-age=1800";
}
location ~ "\.(?:json|txt|xml)$" {
include snippets/security-headers.conf;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Cache-Control "public, max-age=1800";
}
location ~ "/index|\.(?:br|gz|html)$" {
internal;
}
location / {
if ($request_uri ~ \?) {
rewrite ^(.*)$ $1? permanent;
}
include snippets/security-headers.conf;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Cache-Control "public, no-cache";
include snippets/preload.conf;
try_files $uri.html $uri/ =404;
}
}
server {
listen unix:/run/nginx/status.sock;
access_log off;
location = / {
stub_status;
}
location / {
return 404;
}
}
}

View File

@ -1,2 +0,0 @@
# placeholder for gixy
root /srv/grapheneos.org_a;

View File

@ -1 +0,0 @@
add_header Link "<[[path|/main.css]]>; rel=preload; as=style; integrity=[[integrity|/main.css]], </fonts/roboto-v30-regular-latin.woff2>; rel=preload; as=font; crossorigin, </fonts/roboto-v30-bold-latin.woff2>; rel=preload; as=font; crossorigin, <[[path|/mask-icon.svg]]>; rel=preload; as=image; fetchpriority=high$preload_resources_uri" always;

View File

@ -1,12 +0,0 @@
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Origin-Agent-Cluster "?1" always;
# obsolete and replaced with Content-Security-Policy frame-ancestors 'none'
add_header X-Frame-Options "DENY" always;
# obsolete, unsafe and replaced with strong Content-Security-Policy
add_header X-XSS-Protection "0" always;

View File

@ -1,5 +0,0 @@
include snippets/security-headers-base.conf;
add_header Content-Security-Policy "default-src 'none'; connect-src 'self' https://releases.grapheneos.org/; font-src 'self'; img-src 'self'; manifest-src 'self'; script-src 'self'; style-src 'self'; webrtc 'block'; form-action 'none'; frame-ancestors 'none'; base-uri 'none'; require-trusted-types-for 'script'; trusted-types 'none'" always;
add_header Permissions-Policy "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), bluetooth=(), camera=(), clipboard-read=(), clipboard-write=(), display-capture=(), document-domain=(), encrypted-media=(), fullscreen=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), interest-cohort=(), keyboard-map=(), local-fonts=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), speaker-selection=(), sync-xhr=(), usb=(), xr-spatial-tracking=()" always;

View File

@ -1,4 +1,6 @@
{ {
"name": "grapheneos-nodejs",
"version": "0.0.0",
"dependencies": { "dependencies": {
"@stylistic/eslint-plugin": "^5.0.0", "@stylistic/eslint-plugin": "^5.0.0",
"csso-cli": "^4.0.2", "csso-cli": "^4.0.2",

97
package.nix Normal file
View File

@ -0,0 +1,97 @@
{
stdenvNoCC,
runCommandNoCC,
util-linux,
moreutils,
parallel,
openssl,
libxml2,
zopfli,
brotli,
rsync,
yajl,
stylelint,
jre,
python3,
buildNpmPackage,
}:
stdenvNoCC.mkDerivation rec {
pname = "hakurei.app";
version = "0.0.2";
src = ./.;
nodejsEnv = buildNpmPackage {
pname = "grapheneos-nodejs";
inherit version;
src = builtins.path {
name = "${pname}-nodejs";
path = ./.;
filter =
path: _:
builtins.elem (/. + path) [
./package.json
./package-lock.json
];
};
dontNpmBuild = true;
npmFlags = [ "--ignore-scripts" ];
npmDepsHash = "sha256-N3ouirw0B1JtH171/vkA8xtsaQzWnk8+XI2OLaMLeCw=";
};
nativeBuildInputs = [
util-linux
(runCommandNoCC "sponge" { } "mkdir -p $out/bin && ln -s ${moreutils}/bin/sponge $out/bin")
parallel
openssl
libxml2
zopfli
brotli
rsync
yajl
stylelint
];
buildInputs = [
jre
(python3.withPackages (
packages: with packages; [
lxml
cssselect
jinja2
]
))
];
configurePhase = ''
runHook preConfigure
ln -s ${nodejsEnv}/lib/node_modules/${nodejsEnv.pname}/node_modules
runHook postConfigure
'';
buildPhase = ''
runHook preBuild
sh -x process-static
rsync -rpcv --chmod=D755,F644 --delete --fsync --preallocate static-tmp/ static-production
python3 generate-sitemap.py
runHook postBuild
'';
installPhase = ''
runHook preInstall
xmllint --noblanks static-tmp/sitemap.xml --output static-tmp/sitemap.xml
brotli -f static-tmp/sitemap.xml
zopfli static-tmp/sitemap.xml
rsync -pcv --chmod=D755,F644 --fsync --preallocate static-tmp/sitemap.xml{,.gz,.br} $out
rsync -rptcv --chmod=D755,F644 --delete --fsync --preallocate static-production/ $out
runHook postInstall
'';
}

View File

@ -3,10 +3,6 @@
set -o errexit -o nounset -o pipefail set -o errexit -o nounset -o pipefail
shopt -s dotglob extglob globstar shopt -s dotglob extglob globstar
if [[ ${GITHUB_ACTIONS:-false} != true ]]; then
source venv/bin/activate
fi
if [[ $# -eq 1 ]]; then if [[ $# -eq 1 ]]; then
fd=$1 fd=$1
else else
@ -21,17 +17,10 @@ fi
export PATH="$PWD/node_modules/.bin:$PATH" export PATH="$PWD/node_modules/.bin:$PATH"
# can use file:// to avoid network requests
[[ -f releases-base ]] && RELEASES_BASE=$(cat releases-base)
RELEASES_BASE=${RELEASES_BASE:-https://releases.grapheneos.org}
rm -rf nginx-tmp
cp -a nginx nginx-tmp
rm -rf static-tmp rm -rf static-tmp
cp -a static static-tmp cp -a static static-tmp
./process-templates static-tmp python3 process-templates.py static-tmp
for file in static-tmp/**/*.@(json|webmanifest); do for file in static-tmp/**/*.@(json|webmanifest); do
json_verify < "$file" >/dev/null json_verify < "$file" >/dev/null
@ -60,21 +49,7 @@ for file in static-tmp/**/*.css static-tmp/js/*.js static-tmp/**/!(bimi|favicon)
replace+=";s@\[\[integrity|/${file#*/}\]\]@${sri_hash}@g" replace+=";s@\[\[integrity|/${file#*/}\]\]@${sri_hash}@g"
replace+=";s@\[\[path|/${file#*/}\]\]@/${dest#*/}@g" replace+=";s@\[\[path|/${file#*/}\]\]@/${dest#*/}@g"
done done
sed -i "$replace" static-tmp/**/*.html nginx-tmp/nginx.conf nginx-tmp/snippets/preload.conf sed -i "$replace" static-tmp/**/*.html
replace=
devices=(tegu comet komodo caiman tokay akita husky shiba felix tangorpro lynx cheetah panther bluejay raven oriole barbet redfin bramble sunfish coral flame)
channels=(stable beta alpha)
for device in ${devices[@]}; do
for channel in ${channels[@]}; do
metadata=$(curl -s $RELEASES_BASE/$device-$channel)
build_number=$(echo -n $metadata | cut -d ' ' -f 1)
replace+=";s@\[\[$device-$channel-BUILD_NUMBER\]\]@$build_number@g"
done
done
sed -i "$replace" static-tmp/releases.html
gixy nginx-tmp/nginx.conf
xmllint --noout static-tmp/**/*.@(html|svg|xml) xmllint --noout static-tmp/**/*.@(html|svg|xml)
java -jar node_modules/vnu-jar/build/dist/vnu.jar --errors-only static-tmp/**/*.html java -jar node_modules/vnu-jar/build/dist/vnu.jar --errors-only static-tmp/**/*.html
@ -86,8 +61,6 @@ find static-tmp -name '*.html' -exec html-minifier-terser --collapse-whitespace
--remove-redundant-attributes --remove-script-type-attributes \ --remove-redundant-attributes --remove-script-type-attributes \
--remove-style-link-type-attributes --sort-attributes --sort-class-name {} -o {} \; --remove-style-link-type-attributes --sort-attributes --sort-class-name {} -o {} \;
./generate-feed
for file in static-tmp/**/*.@(atom|xml); do for file in static-tmp/**/*.@(atom|xml); do
xmllint --noblanks "$file" --output "$file" xmllint --noblanks "$file" --output "$file"
done done

2
process-templates → process-templates.py Executable file → Normal file
View File

@ -1,5 +1,3 @@
#!/usr/bin/env python3
from jinja2 import FileSystemLoader, Environment from jinja2 import FileSystemLoader, Environment
from pathlib import Path from pathlib import Path
import os import os

File diff suppressed because it is too large Load Diff