- Fork the repository
- Register the repo's git hooks:
git config core.hooksPath .githooks(see Git Hooks) - Create a feature branch from
develop - Make your changes
- Run the lint gate:
./tests/shellcheck.sh - Run the test suite:
sh tests/run.sh - Test a full build:
./mediaforge.sh build --enable-nonfree - Submit a pull request targeting
develop
.githooks/pre-push runs tests/shellcheck.sh and refuses the push if it fails.
Git does not install hooks from a checkout, so cloning does not enable it. Each clone opts in once:
git config core.hooksPath .githookscore.hooksPath replaces the hook directory for every hook type, not just
pre-push. If you keep personal hooks in .git/hooks/ for this repo, move them
into .githooks/ (or expect them to stop firing — silently, with no error).
Things worth knowing about what the hook checks:
- It lints the working tree, not the commits being pushed. Pushing from a clean tree makes those the same thing; pushing with unrelated dirty edits does not.
- A deletion-only push (
git push origin --delete <branch>) ships no content and is let through, so a lint failure that predates the deletion cannot block the branch cleanup this workflow expects. - It sets
REQUIRE_SHELLCHECK=1, so the gate refuses to report a pass whenshellcheckis not installed. An ad-hoc./tests/shellcheck.shstill degrades to thesh -nhalf and says so; a gate that blocks a push does not get to. - The gate covers every executable file under
.githooks/— checked first, so a hook you just broke is reported in a fraction of a second — plusmediaforge.shitself and every.shfile underlib/,recipes/, andtests/. A syntax error in the hook would otherwise block every push with a failure nothing lints. - It refuses a linter that only resolves:
SHELLCHECK=may name the binary, but the binary must identify itself as ShellCheck. A no-op shim would otherwise report every file clean, in silence. - It fails if
mediaforge.shortests/shellcheck.shhas lost its executable bit. Those are the two commands documented here as./path, and nothing else in the tree depends on their mode — so if you see that error after a fresh clone on a filesystem that drops modes (a zip export, a Windows checkout, a copy over CIFS), it is this rule firing, not a broken checkout:chmod +xthem.
Do not push past a failing hook with --no-verify; fix the findings.
Reviewing changes to .githooks/. This is the one directory whose contents
run automatically, on a contributor's machine, outside CI. The project already
executes in-repo shell freely — every recipe is sourced during a build — so this
is not a new class of trust, but a diff that touches .githooks/ deserves the
same attention as one that touches a recipe's pkg_install.
All code must be POSIX sh — no Bashisms.
[ "$var" = value ]not[[ ]]command -vnotwhichprintfnotecho(where output matters)- No arrays, no
local, no+=, no=~, no process substitution - Prefix local-scope variables with
_(e.g.,_pkg,_ver) sed ... > tmp && mv tmp orignotsed -i- Prefer
awkoversedfor field-based edits - Use
patch -p1for complex multi-line source fixes (store inpatches/)
- Create
recipes/<category>/<name>.sh:
PKG_NAME="mylib"
PKG_VERSION="${PKG_VERSION_MYLIB:-1.0.0}"
PKG_URL="https://example.com/mylib-${PKG_VERSION}.tar.gz"
PKG_FFMPEG_OPT="--enable-mylib"
# Override phases as needed
pkg_configure() {
run ./configure --prefix="$PREFIX" --disable-shared --enable-static
}- Add the recipe path to
recipes/_order.conf(respecting dependency order) - Add a version pin to each profile in
profiles/ffmpeg-*.conf:PKG_VERSION_MYLIB="1.0.0" - Test:
./mediaforge.sh build
| Variable | Required | Description |
|---|---|---|
PKG_NAME |
Yes | Package identifier |
PKG_VERSION |
Yes | Version string (use ${PKG_VERSION_NAME:-default} for profile support) |
PKG_URL |
Yes* | Download URL (*not required if PKG_SKIP_EXTRACT=true) |
PKG_FFMPEG_OPT |
No | FFmpeg configure flag to accumulate |
PKG_GPL |
No | Set true to require --enable-gpl |
PKG_NONFREE |
No | Set true to require --enable-nonfree |
PKG_CMAKE |
No | Set true to use cmake instead of autoconf |
PKG_CMAKE_FLAGS |
No | Extra cmake flags |
PKG_CONFIGURE_FLAGS |
No | Extra configure flags |
PKG_REQUIRES_CMD |
No | Space-separated list of required commands |
PKG_REQUIRES_MESON |
No | Set true to require meson + ninja |
PKG_LINUX_ONLY |
No | Set true to skip on non-Linux |
PKG_SKIP_ON_ARCH |
No | Architecture to skip (e.g., arm64) |
PKG_FILENAME |
No | Override tarball filename |
PKG_DIRNAME |
No | Override extracted directory name |
PKG_SKIP_EXTRACT |
No | Set true for header-only packages |
PKG_GITHUB_REPO |
No | owner/repo for update checking |
Override any subset — unoverridden phases use defaults:
| Phase | Default | Purpose |
|---|---|---|
pkg_prepare() |
no-op | Patches, env setup |
pkg_configure() |
./configure or cmake |
Configure build |
pkg_build() |
make -j $MJOBS |
Compile |
pkg_install() |
make install |
Install to $PREFIX |
pkg_post_install() |
no-op | pkgconfig fixups, extra flags |
For complex multi-line source fixes, use patch files instead of inline sed/awk:
- Extract the clean source tarball
- Copy the target file
- Make your fix
- Generate the patch:
diff -u original fixed > patches/<name>.patch - Apply in
pkg_prepare():patch -p1 < "$SCRIPT_DIR/patches/<name>.patch"
Use conventional commits:
feat:new recipe or featurefix:bug fixrefactor:code change that doesn't add features or fix bugsdocs:documentation onlychore:maintenance (deps, CI, tooling)
main— stable releasesdevelop— integration branchfeature/*— new features (branch fromdevelop)bugfix/*— bug fixes (branch fromdevelop)hotfix/*— production fixes (branch frommain)