No description
  • Rust 79.8%
  • Nix 20.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
jingus 0e0109fb20
All checks were successful
example-pack / client (push) Successful in 4m58s
example-pack / server (push) Successful in 27s
ci / check (push) Successful in 5m35s
fix(build): create the mrpack output directory before writing the archive
2026-10-20 21:19:21 -05:00
.forgejo/workflows feat: .mcpb state dir, mcpb.toml, pack icon, leaner init, template devshell 2026-10-20 20:21:24 -05:00
actions/build feat: .mcpb state dir, mcpb.toml, pack icon, leaner init, template devshell 2026-10-20 20:21:24 -05:00
example feat: .mcpb state dir, mcpb.toml, pack icon, leaner init, template devshell 2026-10-20 20:21:24 -05:00
example-mrpack feat: .mcpb state dir, mcpb.toml, pack icon, leaner init, template devshell 2026-10-20 20:21:24 -05:00
lib feat: .mcpb state dir, mcpb.toml, pack icon, leaner init, template devshell 2026-10-20 20:21:24 -05:00
nix-modules fix(build): create the mrpack output directory before writing the archive 2026-10-20 21:19:21 -05:00
src fix(build): create the mrpack output directory before writing the archive 2026-10-20 21:19:21 -05:00
templates/pack feat: .mcpb state dir, mcpb.toml, pack icon, leaner init, template devshell 2026-10-20 20:21:24 -05:00
.envrc feat: mcpb — cargo-style minecraft modpack builder (CLI, nix lib, CI action) 2026-10-20 18:03:32 -05:00
.gitignore feat: .mcpb state dir, mcpb.toml, pack icon, leaner init, template devshell 2026-10-20 20:21:24 -05:00
Cargo.lock feat: mcpb — cargo-style minecraft modpack builder (CLI, nix lib, CI action) 2026-10-20 18:03:32 -05:00
Cargo.toml feat: mcpb — cargo-style minecraft modpack builder (CLI, nix lib, CI action) 2026-10-20 18:03:32 -05:00
flake.lock feat: mcpb — cargo-style minecraft modpack builder (CLI, nix lib, CI action) 2026-10-20 18:03:32 -05:00
flake.nix feat: mcpb — cargo-style minecraft modpack builder (CLI, nix lib, CI action) 2026-10-20 18:03:32 -05:00
README.md feat: .mcpb state dir, mcpb.toml, pack icon, leaner init, template devshell 2026-10-20 20:21:24 -05:00

mcpb

A Cargo-inspired Minecraft modpack builder: an mcpb.toml manifest, an mcpb.lock pin file, features, side-aware targets, and builds for client and server — as a CLI, as a nix lib, and as a CI action.

mcpb init my-pack --loader fabric
cd my-pack
mcpb add mod modrinth:sodium
mcpb build client
mcpb build server
mcpb build client --feature performance

Install

nix profile install git+https://git.jingus.dev/jingus/mcpb#mcpb
# or ad hoc:
nix run git+https://git.jingus.dev/jingus/mcpb#mcpb -- --help

Other flakes can pull the binary:

inputs.mcpb.url = "git+https://git.jingus.dev/jingus/mcpb";
# packages.${system}.mcpb

CLI

command what it does
mcpb init [PATH] scaffold mcpb.toml and a default icon.png
mcpb add mod modrinth:sodium resolve + pin a mod from Modrinth
mcpb add mod url:https://…/x.jar pin a mod from a direct URL (hashed on add)
mcpb add mod files/x.jar reference a local jar (any path inside the pack)
mcpb add resource-pack modrinth:… / shader-pack / asset same sources, other kinds
mcpb add … --feature perf [--side client] optional entry, enabled by a feature
mcpb add asset files/icon.png --path server-icon.png place a file anywhere in the pack
mcpb remove mod sodium remove from manifest + lock (prunes feature lists)
mcpb update [NAME…] re-resolve pins to latest
mcpb list show features and pinned entries
mcpb build client / server build (see below)

Build flags: --features a,b (or repeated --feature), --all-features, --no-default-features, --format folder|zip|mrpack, --output DIR (output root, defaults to <pack>/.mcpb/out), --pack DIR (global; defaults to the nearest ancestor with mcpb.toml).

Manifest (mcpb.toml)

[pack]
name = "my-pack"
version = "0.1.0"
minecraft = "1.21.1"
loader = "fabric"            # fabric | quilt | neoforge | forge | paper; omit for none
# loader-version = "0.16.9"  # pin a loader version
icon = "icon.png"            # copied into every build

[features]
default = ["sodium"]
performance = ["lithium"]

[dependencies]
sodium = { modrinth = "sodium", side = "client" }
lithium = { modrinth = "lithium", optional = true }

[resource-packs]
fresh = { modrinth = "fresh-animations" }

[shader-packs]
complementary = { modrinth = "complementary-reimagined" }

[assets]
icon = { file = "files/server-icon.png", path = "server-icon.png" }
  • Entry sources: modrinth = "<slug|id>", url = "…", or file = "…" (relative to the pack root — any tracked path works; files/ is a convention, not a requirement, and is not created for you).
  • optional = true entries install only when an enabled feature references them (mcpb add --feature does this for you).
  • side = "client"|"server"|"both" overrides automatic side detection. Without it, Modrinth's client_side/server_side decide: client builds skip entries unsupported on the client; server builds include an entry only when the server side is required (an optional server side, like Sodium's, is skipped — force it with side = "both").
  • icon points at any file in the pack; it is copied to the root of every build (and into the mrpack overrides). mcpb init writes a default pixel-art crate icon you can replace.
  • Paper packs install plugins into plugins/ instead of mods/.

Overrides: config files, worlds, anything not an entry

Builds assemble entries into mods/ (or plugins/), resourcepacks/, shaderpacks/, and placed assets — then copy plain files over the result. Those files live in up to three optional directories at the pack root. None exist by default; create them when needed:

directory copied into typical use
overrides/ every build shared config/, options.txt, worlds
client-overrides/ client builds only keybindings, client-only config
server-overrides/ server builds only server.properties, whitelist

Contents map to the build root: overrides/config/foo.toml becomes config/foo.toml in the output. Target-specific directories are applied after overrides/, so they can replace shared files. In --format mrpack, the common + client overrides are packaged as the archive's overrides/ directory; server overrides are not included.

Lock (mcpb.lock)

mcpb add/mcpb update/mcpb build keep mcpb.lock pinned: Modrinth project + version ids, file URLs, sha1/sha512, sizes, environment sides, and the server launcher. Builds verify hashes on download. Commit it — it is the reproducibility boundary for the nix lib.

Targets, formats, and .mcpb/

Targets are installation mechanisms, not architectures. Everything mcpb generates lives under .mcpb/ (gitignored):

.mcpb/
  deps/                                    # content-addressed download cache,
                                           # reused across builds
  out/
    client/folder/                         # ready-to-use tree
    client/zip/my-pack-client-0.1.0.zip    # zip of the same tree (folder kept)
    client/mrpack/my-pack-0.1.0.mrpack     # Modrinth index format (client only)
    server/folder/                         # launcher/installer + run.sh/run.bat
    server/zip/my-pack-server-0.1.0.zip
  • folder (default): the build tree, including the pack icon.
  • zip: additionally writes the archive next to the kept folder.
  • mrpack (client only): modrinth.index.json with URLs + hashes + env, the pinned loader version, and overrides; local-file entries are folded into overrides/.

Server builds also fetch and pin the loader: the fabric/quilt server launcher jar, the neoforge/forge installer (with run.sh/run.bat that install on first start), or the paper server.jar. MCPB_JAVA_ARGS overrides the default -Xms512M -Xmx4G heap flags at runtime.

Nix lib

The flake exposes lib.buildModpack, lib.buildClient, and lib.buildServer — pure, hermetic builds from mcpb.toml + mcpb.lock (modrinth/url entries via fetchurl, so remote entries require the lock; loader pins come from the lock's [loader]):

# in a pack repo
{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    mcpb.url = "git+https://git.jingus.dev/jingus/mcpb";
  };
  outputs = { self, nixpkgs, mcpb }:
    let pkgs = nixpkgs.legacyPackages.x86_64-linux; in
    {
      packages.x86_64-linux = rec {
        client = mcpb.lib.buildClient { inherit pkgs; pack = self; };
        server = mcpb.lib.buildServer { inherit pkgs; pack = self; };
        default = client;
      };
    };
}

Options: pack, target (client/server), format (folder/zip/mrpack; mrpack requires a pinned loader-version), features, allFeatures, noDefaultFeatures, name. Feature, side, overrides, and icon semantics mirror the CLI. There is a starter template with a devshell that provides the CLI:

nix flake init -t git+https://git.jingus.dev/jingus/mcpb#pack

This repo also carries example/ and example-mrpack/ — offline packs exercising the lib in nix flake check.

CI

Composite action (actions/build/action.yml):

- uses: https://git.jingus.dev/jingus/mcpb/actions/build@main
  with:
    target: client          # or server
    format: zip             # folder | zip | mrpack
    features: performance   # optional, comma separated
    pack-dir: .             # directory containing mcpb.toml

It runs nix run <mcpb flake>#mcpb -- build …; override the flake ref with the mcpb: input. Reusable workflow (.forgejo/workflows/build-pack.yml):

jobs:
  build:
    uses: https://git.jingus.dev/jingus/mcpb/.forgejo/workflows/build-pack.yml@main
    with:
      target: client
      format: zip

It builds and uploads .mcpb/out/ as an artifact. This repo dogfoods the action in .forgejo/workflows/example-pack.yml.

Development

nix develop
cargo run -- --help
cargo test
nix build .#mcpb
nix flake check
nix run .#validate

Unit tests cover feature resolution, side filtering, manifest editing (toml_edit roundtrips), lock roundtrips, Modrinth payload parsing, loader versioning/script generation, hashing, and zip writing — all offline. nix flake check additionally builds the example packs with the nix lib and smoke-tests the CLI in the sandbox.

Layout

flake.nix                    # inputs only; modules under nix-modules/
lib/default.nix              # nix lib: buildModpack / buildClient / buildServer
nix-modules/
  packages/mcpb.nix          # crane build + clippy/fmt/test checks
  checks.nix                 # CLI smoke, example packs, template check
  apps.nix                   # `nix run .#mcpb`, `nix run .#validate`
  lib.nix, templates.nix, parts.nix, shell.nix
src/
  main.rs, manifest.rs, lock.rs, modrinth.rs, loader.rs, build.rs,
  util.rs, cmd/
src/assets/icon.png          # default pack icon written by `mcpb init`
templates/pack/              # `nix flake init -t` starter (devshell + builds)
example/, example-mrpack/    # offline example packs (checked in CI)
actions/build/action.yml     # composite CI action
.forgejo/workflows/          # ci.yml, build-pack.yml, example-pack.yml