Move public assets to stable paths

This commit is contained in:
2026-10-04 16:13:47 +05:00
parent 53d7b263c4
commit 5289c17adf
12 changed files with 118 additions and 31 deletions

4
.gitattributes vendored Normal file
View File

@@ -0,0 +1,4 @@
# Preserve downloadable files exactly and keep the Unix wrapper runnable on macOS.
*.jar binary
*.webp binary
minecraft/gradlew text eol=lf

2
.gitignore vendored
View File

@@ -10,5 +10,7 @@ artifacts/
**/build/ **/build/
playwright-report/ playwright-report/
test-results/ test-results/
# Local renderer outputs and legacy build copies. The site's example image in
# public/images/ and distributable JARs in public/tools/ are committed to Git.
public/generated/ public/generated/
public/downloads/*.jar public/downloads/*.jar

View File

@@ -4,8 +4,8 @@ import { existsSync } from "node:fs";
import Link from "next/link"; import Link from "next/link";
export const dynamic = "force-dynamic"; export const dynamic = "force-dynamic";
export default function Downloads() { export default function Downloads() {
const fabric = existsSync("public/downloads/minegeo-fabric-1.21.11.jar"), const fabric = existsSync("public/tools/mod/minegeo-fabric-1.21.11.jar"),
plugin = existsSync("public/downloads/minegeo-plugin-1.21.11.jar"); plugin = existsSync("public/tools/plugin/minegeo-plugin-1.21.11.jar");
return ( return (
<> <>
<Header /> <Header />
@@ -37,7 +37,7 @@ export default function Downloads() {
{fabric ? ( {fabric ? (
<a <a
className="button primary" className="button primary"
href="/downloads/minegeo-fabric-1.21.11.jar" href="/tools/mod/minegeo-fabric-1.21.11.jar"
download download
> >
<Download size={16} /> Download Fabric mod <Download size={16} /> Download Fabric mod
@@ -68,7 +68,7 @@ export default function Downloads() {
{plugin ? ( {plugin ? (
<a <a
className="button primary" className="button primary"
href="/downloads/minegeo-plugin-1.21.11.jar" href="/tools/plugin/minegeo-plugin-1.21.11.jar"
download download
> >
<Download size={16} /> Download server plugin <Download size={16} /> Download server plugin

View File

@@ -70,7 +70,7 @@ export default async function Home() {
</div> </div>
<div className="map-art-frame"> <div className="map-art-frame">
<Image <Image
src="/generated/paper-proof/map.webp" src="/images/example-map.webp"
alt="Top-down map of the real Paper renderer test world" alt="Top-down map of the real Paper renderer test world"
width={1024} width={1024}
height={1024} height={1024}

View File

@@ -16,7 +16,7 @@ export function MapCard({ map, index = 0 }: { map: CardMap; index?: number }) {
<Link className="map-card" href={`/servers/${map.id}`}> <Link className="map-card" href={`/servers/${map.id}`}>
<div className="card-image"> <div className="card-image">
<Image <Image
src={map.image || "/generated/paper-proof/map.webp"} src={map.image || "/images/example-map.webp"}
alt={`${map.name} terrain`} alt={`${map.name} terrain`}
fill fill
unoptimized unoptimized

22
docs/shared-assets.md Normal file
View File

@@ -0,0 +1,22 @@
# Files shared between development machines
Commit these website assets together with the source code:
- `public/images/example-map.webp` — homepage illustration and map-card fallback.
- `public/tools/mod/minegeo-fabric-1.21.11.jar` — Fabric mod download.
- `public/tools/plugin/minegeo-plugin-1.21.11.jar` — server plugin download.
Next.js serves these directly at `/images/example-map.webp`,
`/tools/mod/minegeo-fabric-1.21.11.jar`, and
`/tools/plugin/minegeo-plugin-1.21.11.jar`. They are deliberately tracked, so a
fresh checkout on macOS has the homepage image and both download buttons without
building Minecraft tools or running the renderer proof.
After changing the Java source, run `npm run java:build` with Java 21 installed.
The build copies the new JARs into `public/tools/`; commit those updated files too.
Gradle caches, intermediate build output, and generated test panoramas remain
ignored. The JARs contain Java bytecode and work across Windows, macOS, and Linux.
On the Mac, pull the commit, run `npm install`, and restart the development
server. For a production server, run `npm run build` and then `npm start`.
Keep that machine's Supabase credentials in its own `.env.local`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -2,11 +2,11 @@ import { spawn } from "node:child_process";
import { existsSync } from "node:fs"; import { existsSync } from "node:fs";
import { mkdir, copyFile } from "node:fs/promises"; import { mkdir, copyFile } from "node:fs/promises";
const windows = process.platform === "win32"; const windows = process.platform === "win32";
const command = windows ? "gradlew.bat" : "./gradlew"; const command = windows ? "gradlew.bat" : "sh";
const targets = process.argv.slice(2); const targets = process.argv.slice(2);
if (!targets.length) targets.push(":plugin:build", ":fabric:build"); if (!targets.length) targets.push(":plugin:build", ":fabric:build");
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const child = spawn(command, targets, { const child = spawn(command, windows ? targets : ["./gradlew", ...targets], {
cwd: "minecraft", cwd: "minecraft",
stdio: "inherit", stdio: "inherit",
shell: windows, shell: windows,
@@ -16,9 +16,15 @@ await new Promise((resolve, reject) => {
code === 0 ? resolve() : reject(new Error(`Gradle exited ${code}`)), code === 0 ? resolve() : reject(new Error(`Gradle exited ${code}`)),
); );
}); });
await mkdir("public/downloads", { recursive: true });
for (const name of ["plugin", "fabric"]) { for (const name of ["plugin", "fabric"]) {
const directory = `public/tools/${name === "fabric" ? "mod" : "plugin"}`;
const file = `minegeo-${name}-1.21.11.jar`, const file = `minegeo-${name}-1.21.11.jar`,
path = `minecraft/${name}/build/libs/${file}`; path = `minecraft/${name}/build/libs/${file}`;
if (existsSync(path)) await copyFile(path, `public/downloads/${file}`); if (existsSync(path)) {
await mkdir(directory, { recursive: true });
await copyFile(path, `${directory}/${file}`);
console.log(
`Updated ${directory}/${file}. Include this JAR in your next commit.`,
);
}
} }

View File

@@ -1,21 +1,73 @@
import {createClient} from '@supabase/supabase-js'; import { createClient } from "@supabase/supabase-js";
import {createHash,randomBytes} from 'node:crypto'; import { createHash, randomBytes } from "node:crypto";
import {mkdir,readFile,writeFile} from 'node:fs/promises'; import { mkdir, readFile, writeFile } from "node:fs/promises";
import {existsSync} from 'node:fs'; import { existsSync } from "node:fs";
process.loadEnvFile('.env.local'); process.loadEnvFile(".env.local");
const client=createClient(process.env.NEXT_PUBLIC_SUPABASE_URL!,process.env.SUPABASE_SECRET_KEY!,{auth:{persistSession:false}}); const client = createClient(
function check<T>(r:{data:T,error:{message:string}|null}):NonNullable<T>{if(r.error)throw new Error(r.error.message);return r.data!;} process.env.NEXT_PUBLIC_SUPABASE_URL!,
await mkdir('.data',{recursive:true}); process.env.SUPABASE_SECRET_KEY!,
if(!existsSync('.data/test-account.json')){ { auth: { persistSession: false } },
const password=randomBytes(32).toString('hex'),token='mg_'+randomBytes(32).toString('hex'),email='minegeo-fixture-'+randomBytes(4).toString('hex')+'@example.com'; );
const {user}=check(await client.auth.admin.createUser({email,password,email_confirm:true}));if(!user)throw new Error('No fixture account returned'); function check<T>(r: {
check(await client.from('profiles').insert({id:user.id,display_name:'MineGeo test world',role:'moderator'})); data: T;
check(await client.from('contributor_tokens').insert({owner_id:user.id,token_hash:createHash('sha256').update(token).digest('hex'),label:'Local integration fixture'})); error: { message: string } | null;
await writeFile('.data/test-account.json',JSON.stringify({id:user.id,email,password,token})); }): NonNullable<T> {
if (r.error) throw new Error(r.error.message);
return r.data!;
} }
const manifest=JSON.parse(await readFile('.cache/paper-proof/plugins/MineGeo/export/manifest.json','utf8')); await mkdir(".data", { recursive: true });
manifest.name='The First Expedition';manifest.serverName='MineGeo Test World';manifest.description='A real Paper 1.21.11 renderer test world: terrain, water and a builder’s outpost. Exported with no connected capture player. This small map is for trying MineGeo.'; if (!existsSync(".data/test-account.json")) {
await mkdir('.data/proof-upload',{recursive:true}); const password = randomBytes(32).toString("hex"),
await writeFile('.data/proof-upload/manifest.json',JSON.stringify(manifest)); token = "mg_" + randomBytes(32).toString("hex"),
await writeFile('.data/proof-upload/terrain.json.gz',await readFile('.cache/paper-proof/plugins/MineGeo/export/terrain.json.gz')); email =
console.log('Fixture contributor and real Paper upload are ready. Credentials remain in ignored local files.'); "minegeo-fixture-" + randomBytes(4).toString("hex") + "@example.com";
const created = await client.auth.admin.createUser({
email,
password,
email_confirm: true,
});
if (created.error) throw new Error(created.error.message);
const user = created.data.user;
if (!user) throw new Error("No fixture account returned");
check(
await client
.from("profiles")
.insert({
id: user.id,
display_name: "MineGeo test world",
role: "moderator",
}),
);
check(
await client
.from("contributor_tokens")
.insert({
owner_id: user.id,
token_hash: createHash("sha256").update(token).digest("hex"),
label: "Local integration fixture",
}),
);
await writeFile(
".data/test-account.json",
JSON.stringify({ id: user.id, email, password, token }),
);
}
const manifest = JSON.parse(
await readFile(
".cache/paper-proof/plugins/MineGeo/export/manifest.json",
"utf8",
),
);
manifest.name = "The First Expedition";
manifest.serverName = "MineGeo Test World";
manifest.description =
"A real Paper 1.21.11 renderer test world: terrain, water and a builder’s outpost. Exported with no connected capture player. This small map is for trying MineGeo.";
await mkdir(".data/proof-upload", { recursive: true });
await writeFile(".data/proof-upload/manifest.json", JSON.stringify(manifest));
await writeFile(
".data/proof-upload/terrain.json.gz",
await readFile(".cache/paper-proof/plugins/MineGeo/export/terrain.json.gz"),
);
console.log(
"Fixture contributor and real Paper upload are ready. Credentials remain in ignored local files.",
);

View File

@@ -5,7 +5,8 @@ import { prepareAssets } from "./assets.mjs";
if (!existsSync(".env.local")) await copyFile(".env.example", ".env.local"); if (!existsSync(".env.local")) await copyFile(".env.example", ".env.local");
await mkdir(".data", { recursive: true }); await mkdir(".data", { recursive: true });
await mkdir("public/generated", { recursive: true }); await mkdir("public/generated", { recursive: true });
await mkdir("public/downloads", { recursive: true }); await mkdir("public/tools/mod", { recursive: true });
await mkdir("public/tools/plugin", { recursive: true });
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const p = spawn( const p = spawn(
process.execPath, process.execPath,