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/
playwright-report/
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/downloads/*.jar

View File

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

View File

@@ -70,7 +70,7 @@ export default async function Home() {
</div>
<div className="map-art-frame">
<Image
src="/generated/paper-proof/map.webp"
src="/images/example-map.webp"
alt="Top-down map of the real Paper renderer test world"
width={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}`}>
<div className="card-image">
<Image
src={map.image || "/generated/paper-proof/map.webp"}
src={map.image || "/images/example-map.webp"}
alt={`${map.name} terrain`}
fill
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 { mkdir, copyFile } from "node:fs/promises";
const windows = process.platform === "win32";
const command = windows ? "gradlew.bat" : "./gradlew";
const command = windows ? "gradlew.bat" : "sh";
const targets = process.argv.slice(2);
if (!targets.length) targets.push(":plugin:build", ":fabric:build");
await new Promise((resolve, reject) => {
const child = spawn(command, targets, {
const child = spawn(command, windows ? targets : ["./gradlew", ...targets], {
cwd: "minecraft",
stdio: "inherit",
shell: windows,
@@ -16,9 +16,15 @@ await new Promise((resolve, reject) => {
code === 0 ? resolve() : reject(new Error(`Gradle exited ${code}`)),
);
});
await mkdir("public/downloads", { recursive: true });
for (const name of ["plugin", "fabric"]) {
const directory = `public/tools/${name === "fabric" ? "mod" : "plugin"}`;
const file = `minegeo-${name}-1.21.11.jar`,
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 {createHash,randomBytes} from 'node:crypto';
import {mkdir,readFile,writeFile} from 'node:fs/promises';
import {existsSync} from 'node:fs';
process.loadEnvFile('.env.local');
const client=createClient(process.env.NEXT_PUBLIC_SUPABASE_URL!,process.env.SUPABASE_SECRET_KEY!,{auth:{persistSession:false}});
function check<T>(r:{data:T,error:{message:string}|null}):NonNullable<T>{if(r.error)throw new Error(r.error.message);return r.data!;}
await mkdir('.data',{recursive:true});
if(!existsSync('.data/test-account.json')){
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');
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}));
import { createClient } from "@supabase/supabase-js";
import { createHash, randomBytes } from "node:crypto";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { existsSync } from "node:fs";
process.loadEnvFile(".env.local");
const client = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SECRET_KEY!,
{ auth: { persistSession: false } },
);
function check<T>(r: {
data: T;
error: { message: string } | null;
}): 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'));
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.');
await mkdir(".data", { recursive: true });
if (!existsSync(".data/test-account.json")) {
const password = randomBytes(32).toString("hex"),
token = "mg_" + randomBytes(32).toString("hex"),
email =
"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");
await mkdir(".data", { 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) => {
const p = spawn(
process.execPath,