This commit is contained in:
Wyvest 2026-02-23 22:45:33 -05:00
parent 87cb86943c
commit bd85c8cdef
11 changed files with 260 additions and 15 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@onelauncher/action-publish-artifacts",
"version": "1.0.0-alpha.6",
"version": "1.0.0",
"private": true,
"description": "Next-generation open source Minecraft launcher",
"license": "GPL-3.0-only",

14
Cargo.lock generated
View file

@ -3722,7 +3722,7 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "oneclient_gui"
version = "1.0.0-alpha.6"
version = "1.0.0"
dependencies = [
"anyhow",
"chrono",
@ -3758,7 +3758,7 @@ dependencies = [
[[package]]
name = "onelauncher_core"
version = "1.0.0-alpha.6"
version = "1.0.0"
dependencies = [
"anyhow",
"async-compression",
@ -3822,7 +3822,7 @@ dependencies = [
[[package]]
name = "onelauncher_entity"
version = "1.0.0-alpha.6"
version = "1.0.0"
dependencies = [
"chrono",
"interpulse",
@ -3838,7 +3838,7 @@ dependencies = [
[[package]]
name = "onelauncher_gamemode"
version = "1.0.0-alpha.6"
version = "1.0.0"
dependencies = [
"cc",
"libc",
@ -3846,7 +3846,7 @@ dependencies = [
[[package]]
name = "onelauncher_gui"
version = "1.0.0-alpha.6"
version = "1.0.0"
dependencies = [
"anyhow",
"chrono",
@ -3880,7 +3880,7 @@ dependencies = [
[[package]]
name = "onelauncher_macro"
version = "1.0.0-alpha.6"
version = "1.0.0"
dependencies = [
"proc-macro2",
"quote",
@ -3889,7 +3889,7 @@ dependencies = [
[[package]]
name = "onelauncher_migration"
version = "1.0.0-alpha.6"
version = "1.0.0"
dependencies = [
"async-trait",
"chrono",

View file

@ -22,7 +22,7 @@ homepage = "https://polyfrost.org/projects/onelauncher"
license = "GPL-3.0-only"
readme = "README.md"
repository = "https://github.com/Polyfrost/OneLauncher"
version = "1.0.0-alpha.6"
version = "1.0.0"
[workspace.dependencies]
# workspace

View file

@ -27,6 +27,14 @@ If you encounter any issues, ensure that you are using the following versions (o
- `node` version: **22.15**
- `pnpm` version: **10.13**
### Versioning
OneClient and OneLauncher share a single version across workspace manifests.
- Set an explicit version: `pnpm version:bump 1.0.0-alpha.7`
- Increment using semver: `pnpm version:bump prerelease`
- Preview changes only: `pnpm version:bump prerelease --dry-run`
### Project Structure
- **`packages/`** - Shared libraries and utilities.
- [**`core/`**](./packages/core/) - Launcher Core. This is the library that contains the entire launcher logic.

View file

@ -1,7 +1,7 @@
{
"name": "@oneclient/desktop",
"type": "module",
"version": "1.0.0-alpha.6",
"version": "1.0.0",
"private": true,
"description": "Next-generation open source Minecraft launcher",
"license": "GPL-3.0-only",

View file

@ -1,7 +1,7 @@
{
"name": "@onelauncher/desktop",
"type": "module",
"version": "1.0.0-alpha.6",
"version": "1.0.0",
"private": true,
"description": "Next-generation open source Minecraft launcher",
"license": "GPL-3.0-only",

View file

@ -1,7 +1,7 @@
{
"name": "@onelauncher/root",
"type": "module",
"version": "1.0.0-alpha.6",
"version": "1.0.0",
"private": true,
"engineStrict": true,
"description": "Next-generation open source Minecraft launcher and tools for interacting with Polyfrost",
@ -22,6 +22,7 @@
},
"scripts": {
"prep": "pnpm scripts prep",
"version:bump": "pnpm scripts version",
"typecheck": "pnpm -r typecheck",
"bootstrap": "cargo clean && ./packages/scripts/setup.sh && pnpm install && pnpm prep",
"core": "pnpm --filter @onelauncher/core -- ",

View file

@ -1,6 +1,6 @@
{
"name": "@onelauncher/core",
"version": "1.0.0-alpha.6",
"version": "1.0.0",
"description": "Next-generation open source Minecraft launcher core",
"license": "GPL-3.0-only",
"repository": {

View file

@ -1,6 +1,6 @@
{
"name": "@onelauncher/gamemode",
"version": "1.0.0-alpha.6",
"version": "1.0.0",
"description": "Next-generation open source Minecraft launcher feral gamemode bindings",
"license": "GPL-3.0-only",
"repository": {

View file

@ -1,7 +1,7 @@
{
"name": "@onelauncher/scripts",
"type": "module",
"version": "1.0.0-alpha.6",
"version": "1.0.0",
"private": true,
"description": "Scripts for developing OneLauncher",
"license": "GPL-3.0-only",
@ -16,6 +16,7 @@
"types": "./src/index.ts",
"scripts": {
"tauri": "pnpm tsx tauri.ts",
"version": "pnpm tsx version.ts",
"prep": "pnpm tsx prepare.ts",
"setup-win": "./setup.ps1",
"setup-nix": "./setup.sh",

235
packages/scripts/version.ts Normal file
View file

@ -0,0 +1,235 @@
import type { ReleaseType } from 'semver';
import { readFile, writeFile } from 'node:fs/promises';
import process from 'node:process';
import { consola } from 'consola';
import { join } from 'pathe';
import semver from 'semver';
import { parse as parseTOML } from 'smol-toml';
import { checkEnvironment } from './utils';
const env = checkEnvironment(import.meta);
const RELEASE_TYPES: Set<ReleaseType> = new Set([
'major',
'minor',
'patch',
'premajor',
'preminor',
'prepatch',
'prerelease',
]);
const VERSIONED_PACKAGE_JSON_PATHS = [
'package.json',
'packages/core/package.json',
'packages/gamemode/package.json',
'packages/scripts/package.json',
'apps/oneclient/desktop/package.json',
'apps/onelauncher/desktop/package.json',
'.github/actions/publish-artifacts/package.json',
] as const;
interface PackageJsonLike {
version?: unknown;
[key: string]: unknown;
}
interface CargoWorkspaceManifest {
workspace?: {
members?: unknown;
};
package?: {
name?: unknown;
version?: unknown;
};
}
function usage() {
consola.info(`
usage:
pnpm version:bump <version>
pnpm version:bump <major|minor|patch|premajor|preminor|prepatch|prerelease> [preid]
pnpm version:bump <...> --dry-run
`);
}
function inferPreid(version: string): string {
const prerelease = semver.prerelease(version);
const preid = prerelease?.find(part => typeof part === 'string');
return typeof preid === 'string' ? preid : 'alpha';
}
function hasWorkspaceVersion(value: unknown): boolean {
return typeof value === 'object'
&& value !== null
&& 'workspace' in value
&& (value as { workspace?: unknown }).workspace === true;
}
function resolveNextVersion(currentVersion: string, positionalArgs: Array<string>): string | null {
const [firstArg, secondArg] = positionalArgs;
if (!firstArg)
return null;
if (RELEASE_TYPES.has(firstArg as ReleaseType)) {
const releaseType = firstArg as ReleaseType;
const usePreid = releaseType.startsWith('pre') || releaseType === 'prerelease';
if (!usePreid && secondArg)
return null;
return semver.inc(currentVersion, releaseType, usePreid ? (secondArg ?? inferPreid(currentVersion)) : undefined);
}
if (secondArg)
return null;
return semver.valid(firstArg);
}
async function readRootVersion(): Promise<string> {
const raw = await readFile(join(env.__root, 'package.json'), 'utf8');
const parsed = JSON.parse(raw) as PackageJsonLike;
if (typeof parsed.version !== 'string')
throw new Error('root package.json does not contain a valid version string');
return parsed.version;
}
async function updatePackageJson(path: string, version: string, dryRun: boolean): Promise<boolean> {
const fullPath = join(env.__root, path);
const raw = await readFile(fullPath, 'utf8');
const parsed = JSON.parse(raw) as PackageJsonLike;
if (typeof parsed.version !== 'string')
throw new Error(`${path} does not contain a valid version string`);
if (parsed.version === version)
return false;
parsed.version = version;
if (!dryRun)
await writeFile(fullPath, `${JSON.stringify(parsed, null, '\t')}\n`, 'utf8');
return true;
}
async function getWorkspacePackageNames(): Promise<Set<string>> {
const workspaceManifestPath = join(env.__root, 'Cargo.toml');
const workspaceRaw = await readFile(workspaceManifestPath, 'utf8');
const workspaceManifest = parseTOML(workspaceRaw) as CargoWorkspaceManifest;
const members = workspaceManifest.workspace?.members;
if (!Array.isArray(members))
throw new Error('failed to parse workspace members from Cargo.toml');
const names: Set<string> = new Set();
await Promise.all(members.map(async (member) => {
if (typeof member !== 'string')
return;
const memberManifestPath = join(env.__root, member, 'Cargo.toml');
const memberRaw = await readFile(memberManifestPath, 'utf8');
const memberManifest = parseTOML(memberRaw) as CargoWorkspaceManifest;
const crateName = memberManifest.package?.name;
const crateVersion = memberManifest.package?.version;
if (typeof crateName === 'string' && hasWorkspaceVersion(crateVersion))
names.add(crateName);
}));
return names;
}
async function updateCargoWorkspaceVersion(version: string, dryRun: boolean): Promise<boolean> {
const cargoPath = join(env.__root, 'Cargo.toml');
const cargoRaw = await readFile(cargoPath, 'utf8');
const workspaceVersionPattern = /(\[workspace\.package\][\s\S]*?\nversion\s*=\s*")([^"]+)(")/;
if (!workspaceVersionPattern.test(cargoRaw))
throw new Error('failed to find [workspace.package].version in Cargo.toml');
const nextCargo = cargoRaw.replace(workspaceVersionPattern, `$1${version}$3`);
if (nextCargo === cargoRaw)
return false;
if (!dryRun)
await writeFile(cargoPath, nextCargo, 'utf8');
return true;
}
async function updateCargoLock(version: string, crateNames: Set<string>, dryRun: boolean): Promise<boolean> {
const cargoLockPath = join(env.__root, 'Cargo.lock');
const cargoLockRaw = await readFile(cargoLockPath, 'utf8');
const packagePattern = /(\[\[package\]\]\r?\nname = "([^"]+)"\r?\nversion = ")([^"]+)(")/g;
const nextCargoLock = cargoLockRaw.replace(packagePattern, (match, prefix, packageName, currentVersion, suffix) => {
if (!crateNames.has(packageName))
return match;
if (currentVersion === version)
return match;
return `${prefix}${version}${suffix}`;
});
if (nextCargoLock === cargoLockRaw)
return false;
if (!dryRun)
await writeFile(cargoLockPath, nextCargoLock, 'utf8');
return true;
}
async function main() {
const rawArgs = process.argv.slice(2);
const dryRun = rawArgs.includes('--dry-run');
const positionalArgs = rawArgs.filter(arg => arg !== '--dry-run');
if (positionalArgs.length === 0 || positionalArgs.length > 2) {
usage();
throw new Error('invalid arguments');
}
const currentVersion = await readRootVersion();
const nextVersion = resolveNextVersion(currentVersion, positionalArgs);
if (!nextVersion) {
usage();
throw new Error('unable to resolve target version');
}
if (currentVersion === nextVersion) {
consola.warn(`version already set to ${nextVersion}`);
return;
}
consola.start(`${dryRun ? 'previewing' : 'bumping'} version: ${currentVersion} -> ${nextVersion}`);
const changedPaths: Array<string> = [];
for (const packagePath of VERSIONED_PACKAGE_JSON_PATHS)
if (await updatePackageJson(packagePath, nextVersion, dryRun))
changedPaths.push(packagePath);
if (await updateCargoWorkspaceVersion(nextVersion, dryRun))
changedPaths.push('Cargo.toml');
const crateNames = await getWorkspacePackageNames();
if (await updateCargoLock(nextVersion, crateNames, dryRun))
changedPaths.push('Cargo.lock');
if (changedPaths.length === 0) {
consola.warn('no files required changes');
return;
}
const action = dryRun ? 'would update' : 'updated';
consola.success(`${action} ${changedPaths.length} files:`);
changedPaths.forEach((path) => {
consola.info(`- ${path}`);
});
}
void main().catch((error: unknown) => {
consola.error(error instanceof Error ? error.message : String(error));
env.__exit(1);
});