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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ bun run build # bundles src/ -> dist/index.cjs (committed; CI verifies it is
`dist/index.cjs` is committed because a composite action runs straight from the
checked-out repo. Rebuild it whenever `src/` changes.

Other tools can drive Blockbench the same way by importing from
`animated-java-ci/lib` (see [`src/lib.ts`](src/lib.ts)). Bun runs it straight
from source, so it needs no build.

## License

MIT
24 changes: 12 additions & 12 deletions dist/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -11546,7 +11546,7 @@ function oneLine(message) {

// src/blockbench.ts
var delay = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
var ENVIRONMENT = "animated-java-ci";
var DEFAULT_ENVIRONMENT = "animated-java-ci";
function getFreePort() {
return new Promise((resolve2, reject) => {
const server = (0, import_node_net.createServer)();
Expand All @@ -11567,12 +11567,12 @@ function numericSemver(a, b2) {
}
return 0;
}
async function ensurePortable(eb, blockbenchVersion) {
if (await eb.environmentExists(ENVIRONMENT) !== "env") {
async function ensurePortable(eb, environment, blockbenchVersion) {
if (await eb.environmentExists(environment) !== "env") {
info(`Provisioning Blockbench (${blockbenchVersion}) via envbench...`);
let lastPercent = -1;
await eb.createEnvironment(
ENVIRONMENT,
environment,
{ blockbenchVersion, force: true },
{
onDownloadStart: (version2) => info(`Downloading Blockbench ${version2}`),
Expand All @@ -11586,7 +11586,7 @@ async function ensurePortable(eb, blockbenchVersion) {
}
);
}
const env = await eb.getEnvironment(ENVIRONMENT);
const env = await eb.getEnvironment(environment);
let version = "";
try {
version = await eb.resolveVersion(env.blockbench_version);
Expand All @@ -11604,21 +11604,21 @@ async function ensurePortable(eb, blockbenchVersion) {
}
return portable;
}
async function launchBlockbench(blockbenchVersion) {
async function launchBlockbench(blockbenchVersion, environment = DEFAULT_ENVIRONMENT) {
const eb = new se();
await eb.ensureStorageFolder();
const userDataDir = (0, import_node_path2.join)(eb.storageDir, ENVIRONMENT);
const userDataDir = (0, import_node_path2.join)(eb.storageDir, environment);
await killByUserData(userDataDir);
for (const lock of ["SingletonLock", "SingletonSocket", "SingletonCookie"]) {
try {
(0, import_node_fs3.rmSync)((0, import_node_path2.join)(userDataDir, lock), { force: true });
} catch {
}
}
await ensurePortable(eb, blockbenchVersion);
await ensurePortable(eb, environment, blockbenchVersion);
grantFsPermission(userDataDir, "animated_java");
const debugPort = await getFreePort();
const child = await eb.launch(ENVIRONMENT, {
const child = await eb.launch(environment, {
extraArgs: [
`--remote-debugging-port=${debugPort}`,
"--remote-allow-origins=*",
Expand Down Expand Up @@ -12017,7 +12017,7 @@ async function rendererLoadBlueprint(blueprintPath, fileName, content) {
}
};
}
async function rendererExport() {
async function rendererExport(options) {
const g2 = globalThis;
const aj = g2.window && g2.window.AnimatedJava || g2.AnimatedJava;
if (!aj || typeof aj.exportProject !== "function") {
Expand All @@ -12043,7 +12043,7 @@ async function rendererExport() {
let ok = false;
let thrown = null;
try {
ok = await aj.exportProject();
ok = await aj.exportProject(options || void 0);
} catch (e) {
thrown = e && (e.stack || e.message) || String(e);
} finally {
Expand Down Expand Up @@ -12134,7 +12134,7 @@ async function exportBlueprint(bridge, blueprintPath, config) {
try {
result = await bridge.evaluate(
rendererExport,
[],
[{ forceSave: false }],
config.exportTimeout * 1e3
);
} catch (e) {
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"url": "https://github.com/Animated-Java/animated-java-ci"
},
"type": "module",
"exports": {
"./lib": "./src/lib.ts"
},
"scripts": {
"build": "esbuild src/main.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/index.cjs --banner:js=\"/* Generated by \\`bun run build\\` - edit src/ instead. */\"",
"typecheck": "tsc --noEmit",
Expand Down
29 changes: 19 additions & 10 deletions src/blockbench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import * as log from './log'

const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))

/** Fixed envbench environment name - one isolated Blockbench install for the action. */
const ENVIRONMENT = 'animated-java-ci'
/** Default envbench environment name - one isolated Blockbench install for the action. */
const DEFAULT_ENVIRONMENT = 'animated-java-ci'

export interface RunningBlockbench {
debugPort: number
Expand Down Expand Up @@ -46,12 +46,16 @@ function numericSemver(a: string, b: string): number {
* Ensure the isolated Blockbench environment exists (downloading Blockbench on
* first use) and return the path to its portable executable.
*/
async function ensurePortable(eb: Envbench, blockbenchVersion: string): Promise<string> {
if ((await eb.environmentExists(ENVIRONMENT)) !== 'env') {
async function ensurePortable(
eb: Envbench,
environment: string,
blockbenchVersion: string
): Promise<string> {
if ((await eb.environmentExists(environment)) !== 'env') {
log.info(`Provisioning Blockbench (${blockbenchVersion}) via envbench...`)
let lastPercent = -1
await eb.createEnvironment(
ENVIRONMENT,
environment,
{ blockbenchVersion: blockbenchVersion as NamedBlockbenchVersion, force: true },
{
onDownloadStart: version => log.info(`Downloading Blockbench ${version}`),
Expand All @@ -66,7 +70,7 @@ async function ensurePortable(eb: Envbench, blockbenchVersion: string): Promise<
)
}

const env = await eb.getEnvironment(ENVIRONMENT)
const env = await eb.getEnvironment(environment)
let version: ResolvedBlockbenchVersion | '' = ''
try {
version = await eb.resolveVersion(env.blockbench_version)
Expand All @@ -91,11 +95,16 @@ async function ensurePortable(eb: Envbench, blockbenchVersion: string): Promise<
* Launch Blockbench with the Chrome DevTools Protocol enabled and wait until its
* endpoint answers. The caller is expected to already be running under
* `xvfb-run` (the action wrapper does this) so no window appears.
*
* `environment` names the isolated envbench install, so separate tools don't share one.
*/
export async function launchBlockbench(blockbenchVersion: string): Promise<RunningBlockbench> {
export async function launchBlockbench(
blockbenchVersion: string,
environment = DEFAULT_ENVIRONMENT
): Promise<RunningBlockbench> {
const eb = new Envbench()
await eb.ensureStorageFolder()
const userDataDir = join(eb.storageDir, ENVIRONMENT)
const userDataDir = join(eb.storageDir, environment)

// Clear a stale Chromium singleton lock a previously hard-killed run may have left.
await killByUserData(userDataDir)
Expand All @@ -107,11 +116,11 @@ export async function launchBlockbench(blockbenchVersion: string): Promise<Runni
}
}

await ensurePortable(eb, blockbenchVersion)
await ensurePortable(eb, environment, blockbenchVersion)
grantFsPermission(userDataDir, 'animated_java')
const debugPort = await getFreePort()

const child = await eb.launch(ENVIRONMENT, {
const child = await eb.launch(environment, {
extraArgs: [
`--remote-debugging-port=${debugPort}`,
'--remote-allow-origins=*',
Expand Down
3 changes: 2 additions & 1 deletion src/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,10 @@ export async function exportBlueprint(

let result: ExportResult
try {
// Don't let the exporter save the blueprint back over the user's file.
result = await bridge.evaluate<ExportResult>(
rendererExport,
[],
[{ forceSave: false }],
config.exportTimeout * 1000
)
} catch (e) {
Expand Down
16 changes: 16 additions & 0 deletions src/lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Library entry for other tools that drive Blockbench headlessly
* (e.g. animated-java-gametest). The action itself starts at `main.ts`.
*/
export { launchBlockbench, type RunningBlockbench } from './blockbench'
export { RendererBridge } from './cdp'
export {
type ExportOptions,
type ExportResult,
type LoadResult,
type PluginLoadResult,
type RendererBlueprintSettings,
rendererExport,
rendererLoadBlueprint,
rendererLoadPlugin,
} from './renderer'
9 changes: 7 additions & 2 deletions src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ export interface ExportResult {
messages: string[]
}

export interface ExportOptions {
/** Save the blueprint after exporting. Animated Java defaults this to `true`. */
forceSave?: boolean
}

export interface PluginLoadResult {
ok: boolean
error?: string | null
Expand Down Expand Up @@ -150,7 +155,7 @@ export async function rendererLoadBlueprint(
}
}

export async function rendererExport(): Promise<ExportResult> {
export async function rendererExport(options: ExportOptions | null): Promise<ExportResult> {
const g = globalThis as any
const aj = (g.window && g.window.AnimatedJava) || g.AnimatedJava
if (!aj || typeof aj.exportProject !== 'function') {
Expand Down Expand Up @@ -184,7 +189,7 @@ export async function rendererExport(): Promise<ExportResult> {
let ok = false
let thrown: string | null = null
try {
ok = await aj.exportProject()
ok = await aj.exportProject(options || undefined)
} catch (e: any) {
thrown = (e && (e.stack || e.message)) || String(e)
} finally {
Expand Down
13 changes: 13 additions & 0 deletions test/lib.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { describe, expect, it } from 'bun:test'
import * as lib from '../src/lib'

// animated-java-gametest imports these; a rename here breaks it silently otherwise.
describe('lib', () => {
it('exports the Blockbench driver', () => {
expect(typeof lib.launchBlockbench).toBe('function')
expect(typeof lib.RendererBridge.attach).toBe('function')
expect(typeof lib.rendererLoadPlugin).toBe('function')
expect(typeof lib.rendererLoadBlueprint).toBe('function')
expect(typeof lib.rendererExport).toBe('function')
})
})
Loading