# YAP — Yet Another Packager yap-logo

[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/M0Rf30/yap) [![View examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](examples) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg?style=flat-square)](https://www.gnu.org/licenses/gpl-3.0) [![GitHub release](https://img.shields.io/github/release/M0Rf30/yap.svg?style=flat-square)](https://github.com/M0Rf30/yap/releases/latest)
YAP builds native packages for multiple GNU/Linux distributions from a single PKGBUILD specification. Write your package once; get `.deb`, `.rpm`, `.apk`, and `.pkg.tar.zst` out. All builds run in isolated OCI containers (Docker or Podman). ## Features - **Multi-format output**: DEB (Debian/Ubuntu), RPM (Fedora/RHEL/Rocky/openSUSE), APK (Alpine), TAR.ZST (Arch) - **Container isolation**: reproducible builds, no host contamination, Docker and Podman supported - **PKGBUILD-based**: familiar Arch Linux syntax extended with distribution and architecture overrides - **Cross-compilation**: build for a different architecture than your host - **Dependency-aware builds**: sequential by default; opt-in parallel topo-sort via `--parallel` - **Package signing**: APK RSA + DEB/RPM/Pacman GPG (no `gpg` binary required) - **SBOM generation**: CycloneDX 1.5 and SPDX 2.3 sidecars - **Publishing**: `yap publish` uploads DEB/RPM (and APK/Pacman as raw files) to an [OpenArtifact](https://github.com/openartifact/openartifact) repository - **Per-format compression**: `zstd`/`gzip`/`xz` for DEB and RPM - **Changelog support**: `changelog` PKGBUILD field renders to native format per distro - **Pacman scriptlets**: full 6-hook lifecycle (pre/post install/upgrade/remove) - **Structured logging**: slog-based, tree rendering for long lines, zero external UI deps ## Installation One-liner (Linux/macOS, amd64/arm64): ```bash curl -fsSL https://raw.githubusercontent.com/M0Rf30/yap/main/scripts/install.sh | sh ``` Pin a version or install only one tool: ```bash curl -fsSL https://raw.githubusercontent.com/M0Rf30/yap/main/scripts/install.sh \ | sh -s -- --version v2.1.3 --tool yap ``` Manual download: ```bash wget https://github.com/M0Rf30/yap/releases/latest/download/yap_Linux_x86_64.tar.gz tar -xzf yap_Linux_x86_64.tar.gz sudo mv yap /usr/local/bin/ yap version ``` ### Build from source ```bash git clone https://github.com/M0Rf30/yap.git cd yap make build sudo mv yap /usr/local/bin/ ``` Requires Docker or Podman: ```bash # Docker sudo systemctl enable --now docker && sudo usermod -aG docker $USER # Podman sudo systemctl enable --now podman ``` ## Quick start ### 1. Project structure Create `yap.json`: ```json { "name": "My Package", "description": "A sample package built with YAP", "buildDir": "/tmp/yap-build", "output": "artifacts", "projects": [ { "name": "my-package" } ] } ``` ### 2. PKGBUILD Create `my-package/PKGBUILD`: ```bash pkgname=my-package pkgver=1.0.0 pkgrel=1 pkgdesc="My awesome application" arch=('x86_64') license=('GPL-3.0') url="https://github.com/user/my-package" makedepends=('gcc' 'make') source=("https://github.com/user/my-package/archive/v${pkgver}.tar.gz") sha256sums=('SKIP') build() { cd "${srcdir}/${pkgname}-${pkgver}" make } package() { cd "${srcdir}/${pkgname}-${pkgver}" install -Dm755 my-package "${pkgdir}/usr/bin/my-package" install -Dm644 README.md "${pkgdir}/usr/share/doc/${pkgname}/README.md" } ``` ### 3. Build ```bash # Auto-detect host distro from /etc/os-release yap build . # Specific distribution yap build ubuntu-jammy . yap build fedora-38 /path/to/project yap build --cleanbuild --no-makedeps ubuntu-jammy . ``` ### 4. Output ``` artifacts/ ├── my-package_1.0.0-1_amd64.deb ├── my-package-1.0.0-1.x86_64.rpm ├── my-package-1.0.0-r1.apk └── my-package-1.0.0-1-x86_64.pkg.tar.zst ``` ## Project configuration (`yap.json`) ```json { "name": "My Multi-Package Project", "description": "Project description", "buildDir": "/tmp/yap-builds", "output": "dist", "projects": [ { "name": "package-one", "install": true }, { "name": "package-two" } ] } ``` | Field | Required | Default | Description | |-------|----------|---------|-------------| | `name` | yes | — | Project collection name | | `description` | yes | — | Project collection description | | `buildDir` | yes | — | Build/working directory | | `output` | yes | — | Output directory for built packages | | `projects` | yes | — | Ordered array of packages to build | | `projects[].name` | yes | — | Sub-directory containing the package's PKGBUILD | | `projects[].install` | no | `false` | Install immediately after build so later packages can depend on it | | `compressionDeb` / `compressionRpm` | no | `zstd` | Per-format compression: `zstd`/`gzip`/`xz` | | `signing` | no | — | Signing config (see [Package signing](#package-signing)) | | `repos` | no | — | Extra package repositories to configure before resolving deps | | `skipDeps` | no | — | Package names to omit from makedepends | | `targetArch` | no | host | Cross-compilation target architecture | | `parallel` | no | `false` | Build independent packages in parallel (topo-sort) | | `sbom` | no | `false` | Generate SBOM sidecars | | `sbomFormat` | no | `both` | `cyclonedx`/`spdx`/`both` | | `publish` | no | — | Defaults for `yap publish` (see [Publishing](#publishing-to-openartifact)); never holds the token | Build ordering between packages comes from the `install` flag and each PKGBUILD's `depends`/`makedepends` — there is no `yap.json`-level `depends` field. See [`yap.schema.json`](./yap.schema.json) for the complete spec. ### Editor validation (JSON Schema) A JSON Schema for `yap.json` ships at the repo root: [`yap.schema.json`](./yap.schema.json). It documents every supported field (compression, signing, repos, SBOM, cross-compilation, etc.). Reference it from your `yap.json` for autocomplete and inline validation in editors that support JSON Schema: ```json { "$schema": "https://www.schemastore.org/yap.json", "name": "My Multi-Package Project", "...": "..." } ``` The schema is published on [SchemaStore](https://www.schemastore.org/json/), so editors (VS Code, JetBrains, Neovim, …) validate any `yap.json` by filename automatically — no `$schema` line required. ## PKGBUILD extensions ### Distribution-specific variables Use `__` (double underscore) to override any variable per distribution. Priority (highest wins): | Priority | Syntax | Example | |----------|--------|---------| | 4+ | arch + distro | `depends_x86_64__ubuntu_noble` | | 4 | arch only | `depends_x86_64` | | 3 | distro + codename | `depends__ubuntu_noble` | | 2 | distro | `depends__ubuntu` | | 1 | package manager | `depends__apt` | | 0 | base (fallback) | `depends` | ```bash pkgdesc="My application" pkgdesc__debian="My application for Debian/Ubuntu" pkgdesc__ubuntu_noble="My application optimized for Ubuntu 24.04" makedepends=('gcc' 'make') makedepends__apt=('build-essential' 'cmake') makedepends__yum=('gcc-c++' 'cmake3') makedepends__ubuntu_noble=('build-essential' 'cmake' 'pkg-config' 'libtool') ``` ### Architecture-specific variables ```bash depends=('glibc' 'gcc') depends_x86_64=('glibc' 'gcc' 'lib32-glibc') depends_aarch64=('glibc' 'gcc' 'aarch64-linux-gnu-gcc') source=('https://example.com/generic-source.tar.gz') source_x86_64=('https://example.com/x86_64-optimized.tar.gz') source_aarch64=('https://example.com/aarch64-source.tar.gz') sha256sums=('generic_hash') sha256sums_x86_64=('x86_64_specific_hash') sha256sums_aarch64=('aarch64_specific_hash') ``` Supported architectures: `x86_64`, `i686`, `aarch64`, `armv7h`, `armv6h`, `armv5`, `ppc64`, `ppc64le`, `s390x`, `mips`, `mipsle`, `riscv64`, `pentium4`, `any`. ### Checksum types Supported (strongest to fastest): `b2sums`, `sha512sums`, `sha384sums`, `sha256sums`, `sha224sums`, `cksums`. ```bash # BLAKE2b (recommended) b2sums=('2f240f2a3d2f8d8f...') # CRC32 (format: checksum filesize) cksums=('1234567890 2048576') ``` ### Changelog ```bash changelog=CHANGELOG.md ``` - **DEB**: `usr/share/doc//changelog.Debian.gz` (Lintian-compliant) - **RPM**: native `%changelog` entries embedded in package metadata - **Pacman**: `.CHANGELOG` in the archive - **APK**: ignored (no Alpine convention) ### Pacman scriptlets ```bash pre_install() { echo "Before install"; } post_install() { systemctl daemon-reload; } pre_upgrade() { systemctl stop myservice; } post_upgrade() { systemctl start myservice; } pre_remove() { systemctl disable myservice; } post_remove() { systemctl daemon-reload; } ``` Emitted to `.install` only when at least one hook is defined. `pre/post_upgrade` fall back to `pre/post_install` when absent. ### Package manager-specific fields ```bash # DEB section=utils priority=optional # RPM group="Applications/System" requires_pre=('shadow-utils') # APK maintainer="John Doe " ``` ## Supported distributions | Distribution ID | Format | Package Manager | |-----------------|--------|-----------------| | `almalinux` | `.rpm` | yum | | `alpine` | `.apk` | apk | | `amzn` | `.rpm` | yum | | `arch` | `.pkg.tar.zst` | pacman | | `centos` | `.rpm` | yum | | `debian` | `.deb` | apt | | `fedora` | `.rpm` | yum | | `linuxmint` | `.deb` | apt | | `opensuse-leap` | `.rpm` | zypper | | `opensuse-tumbleweed` | `.rpm` | zypper | | `ol` | `.rpm` | yum | | `pop` | `.deb` | apt | | `rhel` | `.rpm` | yum | | `rocky` | `.rpm` | yum | | `ubuntu` | `.deb` | apt | ## Model Context Protocol (MCP) YAP ships a companion `yap-mcp` binary that exposes the same build pipeline over the [Model Context Protocol](https://modelcontextprotocol.io), so any MCP-compatible LLM client (Claude Desktop, opencode, Cursor, Zed, Continue, Goose, …) can drive yap directly. Quickstart (release binary): ```sh curl -fsSL https://raw.githubusercontent.com/M0Rf30/yap/main/scripts/install.sh | sh # then add { "mcpServers": { "yap": { "command": "yap-mcp" } } } to your client config ``` Or pull the standalone OCI image (also listed on the [MCP registry](https://registry.modelcontextprotocol.io) as `io.github.M0Rf30/yap`): ```sh docker pull ghcr.io/m0rf30/yap-mcp:latest # client config: # { "mcpServers": { "yap": { "command": "docker", # "args": ["run","-i","--rm","ghcr.io/m0rf30/yap-mcp:latest"] } } } ``` ### Claude Code plugin (server + skill in one) ```text /plugin marketplace add M0Rf30/claude-plugins /plugin install yap@M0Rf30 ``` ### Smithery ```sh npx @smithery/cli install io.github.M0Rf30/yap --client claude ``` The full tool surface, per-client config snippets, security model, and troubleshooting are in **[`MCP.md`](MCP.md)**. The shipped skill card at [`skills/yap/SKILL.md`](skills/yap/SKILL.md) follows the Anthropic Agent Skills layout and gives agents a workflow cheatsheet for free. Distribution channels and release steps are documented in **[`PUBLISHING.md`](PUBLISHING.md)**. ## CLI reference ### Commands ```bash yap build [distro[-release]] # Build packages (distro auto-detected if omitted) yap zap [distro] # Clean build environment yap prepare [distro[-release]] # Prepare host build environment yap pull # Pull pre-built container images yap install # Install a built artifact yap publish [artifact-or-dir]... # Upload built packages to an OpenArtifact repository yap graph [path] # Show dependency graph yap list-distros # List supported distributions yap status # Show host status and runtime detection yap version # Show version information yap completion # Generate shell completion (bash/zsh/fish/powershell) ``` ### Build flags ```bash # Build behavior --cleanbuild, -c # Clean srcdir before build --no-build, -o # Download sources only --zap, -z # Deep clean staging directory --skip-hash-check, -H # Skip source checksum verification --no-container # Build natively on the host (skip container dispatch) # Dependencies --no-makedeps, -d # Skip makedeps installation --skip-sync, -s # Skip package manager sync --skip-deps pkg1,pkg2 # Omit specific packages from makedeps --parallel, -P # Enable parallel topo-sort (opt-in) # Version --pkgver 1.2.3, -w # Override package version --pkgrel 2, -r # Override release number # Range / filter --from package1 # Start from specific package --to package5 # Stop at specific package --only pkg1,pkg2 # Build only listed packages --skip pkg1,pkg2 # Skip listed packages # Source access --ssh-password pass, -p # Password for SSH source access --source-retries # Retries after first attempt for source/gensum/toolchain downloads (default 3; env: YAP_SOURCE_MAX_RETRIES) # Cross-compilation --target-arch arm64, -t # Cross-compile for target architecture --skip-toolchain-validation, -T # Repositories / trust --repo "" # Add an extra package repository (repeatable) --allow-unverified-repos, -U # Allow apt sources without a verifiable Signed-By key # Signing --sign, -K # Enable artifact signing --sign-key /path/to/key # Private key path --sign-passphrase pass # Passphrase (prefer env: YAP_SIGN_PASSPHRASE) --sign-key-name mykey # APK key name # SBOM --sbom, -S # Generate SBOM sidecars --sbom-format cyclonedx # CycloneDX 1.5 only --sbom-format spdx # SPDX 2.3 only --sbom-format both # Both (default) # Compression --compression-deb gzip # DEB: zstd|gzip|xz (default: zstd) --compression-rpm xz # RPM: zstd|gzip|xz (default: zstd) # Debug / output --debug-dir /path, -D # Emit split debug info --verbose # Verbose logging --no-color # Disable colored output ``` ### Shell completion ```bash yap completion bash > /etc/bash_completion.d/yap yap completion zsh > /usr/share/zsh/site-functions/_yap yap completion fish > ~/.config/fish/completions/yap.fish yap completion powershell > yap.ps1 ``` ## Package signing | Format | Algorithm | Output | |--------|-----------|--------| | APK | RSA PKCS#1 v1.5 SHA1 | `.SIGN.RSA..rsa.pub` embedded stream | | DEB | OpenPGP | `.deb.asc` (ASCII-armored detached) | | RPM | OpenPGP | `.rpm.asc` + optional in-RPM via rpmpack | | Pacman | OpenPGP | `.pkg.tar.zst.sig` (binary detached) | Signing uses `github.com/ProtonMail/go-crypto/openpgp` — no `gpg` binary required. ### Key resolution (highest to lowest) 1. `--sign-key ` CLI flag 2. Format env: `YAP_APK_KEY`, `YAP_DEB_KEY`, `YAP_RPM_KEY`, `YAP_PACMAN_KEY` 3. Global env: `YAP_SIGN_KEY` 4. `yap.json` field `signing.keyPath` 5. `~/.config/yap/keys/.{rsa,gpg}` then `~/.config/yap/keys/default.{rsa,gpg}` Passphrase resolution mirrors key resolution with `_PASSPHRASE` suffix. ### yap.json signing config ```json { "signing": { "enabled": true, "keyPath": "~/.config/yap/keys/release.gpg", "keyName": "release" } } ``` ### Verifying signed packages ```bash # APK (after distributing the public key to /etc/apk/keys/) apk add my-package.apk # DEB gpg --verify my-package_1.0.0_amd64.deb.asc my-package_1.0.0_amd64.deb # RPM rpm -K my-package-1.0.0-1.x86_64.rpm # Pacman pacman-key --verify my-package-1.0.0-1-x86_64.pkg.tar.zst.sig ``` ## Publishing to OpenArtifact `yap publish` uploads built packages to a hosted [OpenArtifact](https://github.com/openartifact/openartifact) repository, which serves the apt/dnf indexes (`Release`, `Packages`, `repomd.xml`) itself. | Format | Route used | Notes | |--------|-----------|-------| | DEB | `POST {url}/api/v1/repositories/{deb-repo}/debs` | multipart `file`, `distribution`, `component` | | RPM | `POST {url}/api/v1/repositories/{rpm-repo}/packages` | multipart `file` | | APK | `PUT {url}/repos/raw/{apk-repo}/{apk-path}/{file}` | hosted *raw* repository; OpenArtifact has no apk index yet | | Pacman | `PUT {url}/repos/raw/{pacman-repo}/{pacman-path}/{file}` | raw repository; the `.sig`/`.asc` sidecars next to a package are uploaded too | ```bash export YAP_OA_URL=https://oa.example.com export YAP_OA_TOKEN_FILE=~/.config/yap/oa-token # or YAP_OA_TOKEN export YAP_OA_DEB_REPO=debs YAP_OA_RPM_REPO=rpms yap build ubuntu-jammy . yap publish --distribution jammy --component main ./dist # or a single file yap publish --dry-run ./dist # list only, no network ``` Settings resolve in this order (later wins): the `publish` object of `yap.json`, the `YAP_OA_*` environment, command-line flags. ```json { "output": "dist", "publish": { "url": "https://oa.example.com", "debRepo": "debs", "rpmRepo": "rpms", "distribution": "jammy", "component": "main" } } ``` | Setting | yap.json | Environment | Flag | |---------|----------|-------------|------| | Server URL | `url` | `YAP_OA_URL` | `--url` | | API token | — | `YAP_OA_TOKEN`, `YAP_OA_TOKEN_FILE` | `--token-file` | | DEB repo | `debRepo` | `YAP_OA_DEB_REPO` | `--deb-repo` | | RPM repo | `rpmRepo` | `YAP_OA_RPM_REPO` | `--rpm-repo` | | APK raw repo / path | `apkRepo` / `apkPath` | `YAP_OA_APK_REPO` / `YAP_OA_APK_PATH` | `--apk-repo` / `--apk-path` | | Pacman raw repo / path | `pacmanRepo` / `pacmanPath` | `YAP_OA_PACMAN_REPO` / `YAP_OA_PACMAN_PATH` | `--pacman-repo` / `--pacman-path` | | apt distribution / component | `distribution` / `component` | `YAP_OA_DISTRIBUTION` / `YAP_OA_COMPONENT` | `--distribution` / `--component` | Other flags: `--config ` (default `./yap.json`), `--retries ` (attempts per request, default 3), `--timeout ` (per request, default 30m), `--insecure-http`, `--dry-run`. Behaviour: - **Token**: an OpenArtifact API token with write permission on the target repositories (a `deploy` token, `oa_dep_…`, is enough). It is sent as `Authorization: Bearer …`, is never accepted from `yap.json` or a flag value (so it stays out of the process list), and is redacted from logs and errors. The token is only sent over `https`, or `http` to a loopback host unless you pass `--insecure-http`. - **Arguments**: package files and/or directories. Without arguments the `output` directory of `yap.json` is published. Packages found while scanning a directory whose format has no repository configured are skipped with a warning; an explicitly named file in that situation is an error. - **Verification**: the SHA-256 returned by the server (or `X-Checksum-Sha256` for raw files) must equal the local file's, otherwise the publish fails. - **Retries**: connection errors and HTTP 408/429/5xx are retried with exponential backoff and jitter; other 4xx answers (bad token, wrong repository, invalid package) fail immediately. - **Idempotent**: DEB/RPM packages are immutable on the server, which answers `409`. yap then looks the SHA-256 up in the repository listing: identical bytes count as success ("unchanged"), different bytes under the same name and version fail. Raw files are skipped when `X-Checksum-Sha256` already matches and replaced otherwise (unless the repository enforces immutable releases, which fails the publish). - **Signatures**: OpenArtifact signs repository *indexes*; yap's per-package `.asc` files are not uploaded for DEB/RPM. For APK/Pacman raw repositories the `.sig`/`.asc` sidecars are uploaded next to the package. Exit status is non-zero if any package failed; the remaining packages are still attempted. ## SBOM generation ```bash yap build --sbom . # Both formats (default) yap build --sbom --sbom-format cyclonedx . yap build --sbom --sbom-format spdx . ``` Output alongside each artifact: ``` artifacts/ └── my-package_1.0.0-1_amd64.deb my-package_1.0.0-1_amd64.deb.cdx.json ← CycloneDX 1.5 my-package_1.0.0-1_amd64.deb.spdx.json ← SPDX 2.3 ``` Captured: name, version, license, runtime/build deps, source URLs and checksums, file hashes, DESCRIBES/DEPENDS_ON relationships. ## Advanced usage ### Cross-compilation ```bash yap build --target-arch=aarch64 ubuntu-jammy . yap build --target-arch=armv7 fedora-38 . yap build --target-arch=i686 alpine . yap build --target-arch=ppc64le arch . ``` YAP installs the required cross-compilation toolchains and configures the build environment automatically. ### Multi-package projects ```json { "name": "My Suite", "projects": [ { "name": "core-library", "install": true }, { "name": "main-application", "install": true }, { "name": "plugins", "install": false } ] } ``` Packages with `"install": true` are installed immediately after building so subsequent packages can use them as build-time dependencies. ```bash # Sequential (default) — explicit ordering via "install" field yap build . # Parallel — topo-sort + worker pool yap build --parallel . ``` ### Build environment preparation ```bash yap prepare # Auto-detect host distro yap prepare ubuntu-jammy yap prepare fedora-38 yap prepare --golang arch yap prepare --skip-sync rocky-9 ``` ### CI/CD integration #### GitHub Actions ```yaml name: Build Packages on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install YAP run: | wget https://github.com/M0Rf30/yap/releases/latest/download/yap_Linux_x86_64.tar.gz tar -xzf yap_Linux_x86_64.tar.gz sudo mv yap /usr/local/bin/ - name: Build Packages run: yap build - name: Upload Artifacts uses: actions/upload-artifact@v4 with: name: packages path: artifacts/ ``` #### GitLab CI ```yaml build-packages: stage: build image: ubuntu:22.04 before_script: - apt-get update && apt-get install -y wget docker.io - wget https://github.com/M0Rf30/yap/releases/latest/download/yap_Linux_x86_64.tar.gz - tar -xzf yap_Linux_x86_64.tar.gz && mv yap /usr/local/bin/ script: - yap build artifacts: paths: - artifacts/ expire_in: 1 week ``` ## Examples The [examples](examples) directory contains complete, ready-to-build projects: | Example | Description | |---------|-------------| | [circular-deps](examples/circular-deps) | Circular dependency detection — YAP fails with a clear error | | [dependency-orchestration](examples/dependency-orchestration) | 5-package project with automatic dep resolution and build ordering | | [yap](examples/yap) | YAP packaging itself — Go application with install scripts | | [split-package](examples/split-package) | One build producing multiple installable packages with distro overrides | | [multi-architecture](examples/multi-architecture) | Architecture-specific sources, deps, and checksums | ## PKGBUILD examples ### Simple C application ```bash pkgname=hello-world pkgver=1.0.0 pkgrel=1 pkgdesc="A simple Hello World program" arch=('x86_64') license=('MIT') makedepends=('gcc') source=("hello.c") sha256sums=('SKIP') build() { gcc -o hello hello.c; } package() { install -Dm755 hello "${pkgdir}/usr/bin/hello"; } ``` ### Python application ```bash pkgname=python-myapp pkgver=2.1.0 pkgrel=1 pkgdesc="My Python application" arch=('any') license=('Apache-2.0') depends=('python3') makedepends__apt=('python3-dev' 'python3-setuptools') makedepends__yum=('python3-devel' 'python3-setuptools') source=("https://pypi.io/packages/source/m/myapp/myapp-${pkgver}.tar.gz") sha256sums=('...') build() { cd "${srcdir}/myapp-${pkgver}" python3 setup.py build } package() { cd "${srcdir}/myapp-${pkgver}" python3 setup.py install --root="${pkgdir}" --optimize=1 } ``` ### Web service with systemd ```bash pkgname=web-service pkgver=1.5.0 pkgrel=1 pkgdesc="My web service" arch=('x86_64') license=('GPL-3.0') depends=('systemd') backup=('etc/web-service/config.yml') source=("web-service-${pkgver}.tar.gz" "web-service.service") sha256sums=('...' 'SKIP') build() { cd "${srcdir}/web-service-${pkgver}" make build } package() { cd "${srcdir}/web-service-${pkgver}" install -Dm755 web-service "${pkgdir}/usr/bin/web-service" install -Dm644 ../web-service.service \ "${pkgdir}/usr/lib/systemd/system/web-service.service" install -Dm644 config.yml "${pkgdir}/etc/web-service/config.yml" install -dm755 "${pkgdir}/var/lib/web-service" install -dm755 "${pkgdir}/var/log/web-service" } ``` ## Development ### Make targets ```bash make all # clean, deps, fmt, lint, test, doc, build make build # build the yap binary make build-all # build for linux/darwin/windows, amd64/arm64 make clean # clean build artifacts make deps # download and tidy Go modules make fmt # gofmt make lint # golangci-lint make lint-md # markdownlint make test # run all tests (-p 1 -v, sequential required) make test-coverage # tests with coverage report (coverage.html) make release # create release packages make doc # view all package documentation make doc-serve # start pkgsite on localhost:8080 make doc-generate # generate static docs in docs/api/ make doc-package PKG=./pkg/builders/apk make docker-build DISTRO=ubuntu make docker-build-all make docker-list-distros make i18n-check # verify localization file integrity make i18n-stats # localization statistics ``` ### Testing Tests must run sequentially (`-p 1`): ```bash make test go test ./pkg/source -v go test ./pkg/builders/deb -v go test ./pkg/graph -v go test -race ./pkg/builders/... go test -timeout 30s ./pkg/download/... ``` ### Internationalization Supported languages: English (`en`), Italian (`it`). Language is auto-detected from `LANG`/`LC_ALL`/`LC_MESSAGES`/`LANGUAGE`. Override with `--language` / `-l`: ```bash yap --language=it build . ``` To add a language: copy `pkg/i18n/locales/en.yaml` to `pkg/i18n/locales/{code}.yaml`, translate, add the code to `SupportedLanguages` in `pkg/i18n/i18n.go`, submit a PR. ## Troubleshooting ### Container runtime ```bash systemctl status docker # or podman docker run --rm hello-world # test access sudo usermod -aG docker $USER # fix permissions (re-login required) ``` ### Build failures ```bash yap build --verbose yap zap ubuntu-jammy /path/to/project yap status ``` ### Permission issues ```bash sudo chown -R $USER:$USER artifacts/ setsebool -P container_manage_cgroup true # SELinux (Red Hat family) ``` ### Performance ```bash yap build --skip-sync # skip package manager sync yap build --cleanbuild # clean source before build ``` ## Contributing 1. Fork the repository 2. Create a feature branch: `git checkout -b feature/my-feature` 3. Make changes and add tests 4. Run `make fmt lint test` 5. Commit and open a pull request See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for community guidelines. ## License GNU General Public License v3.0. See [LICENSE.md](LICENSE.md). ## Credits - [Zachary Huff](https://github.com/zachhuff386) for contributions to Pacur, the project that inspired YAP - The Arch Linux community for the PKGBUILD format - All contributors Built with [Go](https://golang.org/), [Cobra](https://github.com/spf13/cobra), [Docker](https://www.docker.com/) / [Podman](https://podman.io/). --- [Report issues](https://github.com/M0Rf30/yap/issues) · [Discussions](https://github.com/M0Rf30/yap/discussions) · [Wiki](https://github.com/M0Rf30/yap/wiki)