Package Manager
***************

Carbs Linux uses its own package managing toolchain named cpt. It is a fork of the kiss (https://github.com/kisslinux/kiss) package manager. Unlike kiss, however, its main goal is being easily extendable. Instead of being a single file package manager, it revolves around the shell library cpt-lib, and many tools that wrap around it.

Usage
=====

cpt is formed of many tools combined in a single environment, similar to git. When you run cpt without any arguments, it will show all available tools and their explanations. Here is an example call with extra scripts on my system:

     -> Carbs Packaging Tool
     -> add               Commit the current directory as a new package
     -> alternatives      List and swap to alternatives
     -> build             Build a package
     -> bump              Commit the current directory as a version bump
     -> cargo-urlgen      Create static cargo sources for Rust packages
     -> cargolock-urlgen  Convert the given Cargo.lock file to sources
     -> cat               Concatanate package files in the installed package database
     -> changelog         Print the git log of the specific package
     -> chbuild           Create/destroy temporary chroots
     -> checkmissing      Verify package manifests
     -> checksum          Generate checksums
     -> chroot            Enter a chroot
     -> commit            Commit a package without the prefix of 'package:'
     -> depends           Display a package's dependencies
     -> download          Download sources for the given package
     -> exec              Execute a command inside the alternatives system
     -> export            Turn an installed package into a CPT tarball
     -> fetch             Fetch repositories
     -> fork              Fork a package to the current directory
     -> getchoice         Prints the full path to a file in the alternatives system.
     -> install           Install a package
     -> link              Link a forked package's files to the other repository
     -> list              List installed packages
     -> maintainer        Find the maintainer of a package
     -> manifest          Display all files owned by a package
     -> manifest-tree     Display all files owned by a package with a tree view
     -> new               Create a boilerplate CPT package
     -> orphans           List orphaned packages
     -> owns              Check which package owns a file
     -> rel               Bump the release number of a package
     -> remove            Remove a package
     -> repodepends       Display a package's dependencies in the repository
     -> reporevdepends    Display packages on the repository which depend on package
     -> reset             Remove all packages except for the base
     -> revdepends        Display packages which depend on package
     -> search            Search for a package
     -> size              Show the size on disk for a package
     -> source            Extract sources of a given package to the current directory
     -> update            Check for updates

cpt-alternatives
----------------

You can list and swap to alternatives using cpt-alternatives, or cpt a for short. When run without alternatives, it will list alternatives. It can read from standard input if - is given as an argument.

Examples
........

List alternatives

     $ cpt-alternatives
     ncurses /usr/bin/clear
     ncurses /usr/bin/reset

Swap to clear from ncurses.

     $ cpt-alternatives ncurses /usr/bin/clear
     -> Swapping '/usr/bin/clear' from 'busybox' to 'ncurses'

Swap in bulk (all of sbase).

     $ cpt a | grep ^sbase | cpt a -

cpt-build
---------

cpt-build will build given packages and their dependencies. If multiple packages are specified, it will ask to install the packages as well.

Options
.......

cpt-build will accept the following options:

  * -y, --no-prompt -- Do not prompt for confirmation

Environment Variables
=====================

Since there is no configuration file for cpt, the package manager is configured through environment variables. These can be set per operation, or be set to your shell configuration or ~/.profile. Here are the environment variables that alter the behaviour of cpt:

CPT_PATH
     Set the locations of your repositories. This is set similar to the PATH variable.

XDG_CACHE_HOME
     Unless this is set, the ~/.cache directory will be used instead.

CPT_CACHE
     The cache directory for cpt. Default: $XDG_CACHE_HOME/cpt

CPT_CHOICE
     If this is set to 0, a package installation will be aborted on conflicts. Default: 1

CPT_COMPRESS
     Program used to compress package tarballs. The values should be the default suffixes for the program. Available values are:

       * gz

       * zst

       * bz2

       * xz

     Default: gz

CPT_DEBUG
     If this is set to 1, temporary build directories will not be removed after the given operation. Default: unset

CPT_FETCH
     If this is set to 0, cpt-update will not fetch the repositories. This behaviour can also be achieved by adding -n or --no-fetch
     as an argument. Default: 0

CPT_FORCE
     If this is set to 1, some of the cpt tools will continue regardless of errors or skip certain checks. Here are some examples:

       * cpt-install will install a package without verifying its manifest.

       * cpt-install will install a package even when there are missing dependencies.

       * cpt-remove will remove packages even when there are other packages that depend on the current package.

     This behaviour can also be achieved by adding -f or --force
     as an argument to those utilities.

     Default: 0

CPT_HOOK
     Location for the hook file see Hooks. Default: unset

CPT_KEEPLOG
     Normally, logs are deleted if the package is built successfully. If set to 1, logs will be kept even when the packages are built as intended. Default: 0

CPT_PID
     If this variable is set, the temporary files will be created with this variable as the suffix, instead of the PID of the cpt process. The advantage is that you can know exactly where the build directory is located, while the disadvantage is that there will be issues with multiple operations at the same time. So the best way to use this variable is during one-time cpt calls.

          CPT_PID=mesa cpt b mesa

     By running the above, you will know that the created build directories will end with the *-mesa suffix.

CPT_PROMPT
     If set to 0, the package manager will not prompt you for anything and will continue with the default action. This behaviour can also be achieved by adding -y or --no-prompt
     as an argument to some utilities. Default: 1

CPT_ROOT
     If this variable is set, cpt will assume this as the system root, and will install/remove/update/list packages assuming this is the system root. This behaviour can also be achieved by adding --root as an argument to some utilities.

CPT_TEST
     If set to 1, cpt-build will run tests where a package has the test build file. This behaviour can also be achieved by adding -t or --test
     as an argument to cpt-build. Default: 0

CPT_TMPDIR
     The directory to create the build files. This can be changed (for example to /tmp) for building on RAM, saving SSD space, etc. Default: $CPT_CACHE

Hooks
=====

Hooks can be used in order to change the runtime behaviour of the package manager. There are a variety of package hooks, mostly self explanatory:

  * pre-build

  * post-build

  * build-fail

  * pre-test

  * test-fail

  * pre-install

  * post-install

  * pre-remove

  * post-remove

  * pre-fetch

  * post-fetch

  * post-package

In order to use hooks, you will need to set the CPT_HOOK variable pointing to your hook file. Your hook file MUST be a POSIX shell script as its contents are sourced by the package manager.

The hook is given 3 variables when it is executed. Those are:

$TYPE
     The type of the hook, (pre-build, post-build, etc.)

$PKG
     The package that cpt is currently working on. Can be null.

$DEST
     The destination of the operation. Can be null.

Editing the build file during pre-build
---------------------------------------

You can edit the build file during pre-build. The file is copied from the repository to the build directory named as .build.cpt. You can use sed or any other tool to edit the build file. After the build is complete, a diff file will be placed to the package database named as build.diff. Here is an example build file manipulation during the pre-build hook.

     cat <<EOF> .build.cpt
     #!/bin/sh -e

     for patch in bash50-0??; do
         patch -p0 < "\$patch"
     done

     export LDFLAGS=-static

     ./configure \
         --prefix=/usr \
         --without-bash-malloc \
         --disable-nls

     export MAKEFLAGS="TERMCAP_LIB=/usr/lib/libncursesw.a $MAKEFLAGS"

     make
     make DESTDIR="\$1" install

     ln -s bash "\$1/usr/bin/sh"
     EOF

Packaging System
================

A package is formed of several files, these are:

  * build

  * sources

  * checksums

  * version

  * depends

  * post-install

  * message

  * test

Any other file can be added to the package directory at the discretion of the package maintainer. Everything in the package directory will also be added to the package database that is located on '/var/db/cpt/installed'. These can be patches, configuration files, etc.

build
-----

Typically build files are shell scripts that run commands to prepare the source code to be installed on the target system. Even though we will be assuming that the build file is a POSIX shell script (for portability's sake), build files can be any executable program from binary programs to perl scripts.

The contents of a build script do not need to follow a certain rule for the package manager, except for the fact that the user needs the permission to execute the file.

An important advice is to append an '-e' to the shebang (#!/bin/sh -e) so that the build script exits on compilation error.

Build is run with three arguments ($#)

 1. Location of the package directory (DESTDIR)

 2. Package version

 3. System architecture

sources
-------

sources file is a list of files and sources that will be put to the build directory during the build process. Those can be remote sources (such as tarballs), git repositories, and files that reside on the package directory.

The syntax is pretty simple for the soures file; src dest. The dest parameter is optional. It is the directory that the source will be placed in. Here is the sources file for the gst-plugins package:

     https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-1.16.2.tar.xz good
     https://gstreamer.freedesktop.org/src/gst-plugins-bad/gst-plugins-bad-1.16.2.tar.xz   bad
     https://gstreamer.freedesktop.org/src/gst-plugins-ugly/gst-plugins-ugly-1.16.2.tar.xz ugly
     https://gstreamer.freedesktop.org/src/gst-libav/gst-libav-1.16.2.tar.xz               libav

This file is read from the package manager as space seperated. Files that begin with a '#' comment are ignored. The first value points to the location of the source.

If it starts with a protcol url, (such as ftp:// http:// https://) it will be downloaded with curl.

If the source is a git repository, it shall be prefixed with a git+ git(1) will be used to do a shallow clone of the repository. If the commit is suffixed by a history pointer, git will checkout the relevant revision. So,

git+git://example.com/pub/repo#v1.2.3
     will checkout the tag named 'v1.2.3'

git+git://example.com/pub/repo#development
     will checkout the branch named 'development'

git+git://example.com/pub/repo#1a314s87
     will checkout the commit named '1a314s87'

Other files are assumed to be residing in the package directory. They should be added with their paths relative to the package directory.

checksums
---------

checksums file is generated by the cpt c pkg command. It is generated according to the order of the sources file. That's why you shouldn't be editing it manually. The checksums file is created with the digests of the files using the sha256 algorithm.

version
-------

The version file includes the version of the software and the release number of of the package on a space seperated format. The contents of the file should look like below.

     1.3.2 1

The version should always match to the number of the upstream release. For drastic changes that require a rebuild Those can be,

  * update of libraries that forces the package to be relinked

  * change in the build scripts that affect the output of the package

When a version bump occurs, the release should be reset to 1.

depends
-------

This is a list of dependencies that must be installed before a package build. You can append ``make'' after a dependency to mark a package is only required during the build process of a package. Packages marked as a make dependency can be removed after the build. There are also ``test'' dependencies. These dependencies are only installed if either the CPT_TEST is set to 1, or the build is run with the -t or --test options. So, a package package could have the following depends file:

     linux-headers make
     python        test
     zlib

post-install
------------

post-install files have the same requirements as the build script. They will be run after the package is installed as root (or as the user if the user has write permissions on CPT_ROOT).

message
-------

This plaintext file will be outputted with cat after every package is installed.

test
----

Test files are mainly for the repository maintainer to test the packages, and will only run if the user has the CPT_TEST variable set, or the build is run with the -t or --test options. This script is run on the build directory. It is run right after the build script is finished.

Rsync Repositories
==================

Rsync repositories are simple to serve and simple to use. In the repository directory, there needs to be a '.rsync' file that points to the remote of the repository. This is used in order to fetch changes from the upstream. '.rsync' file looks like this for the core repository:

     rsync://carbslinux.org/repo/core

Rsync repositories have some few distinctions when it comes to fetching them. They can be either synced individually or as a ``root''. There are 2 important files, those are .rsync and .rsync_root. Here is the Carbs Linux rsync repository structure.

                /
        -----------------
       |                |
     .rsync           core/
               ----------------
               |              |
             .rsync      .rsync_root

Unlike git repositories, they don't have a defined ``root'' directory. This is both an advantage and a disadvantage. This way, we can sync individual repositories, but that also means we need extra files to define root directories and repository locations. Here is the content for each of these files:

     /.rsync:           rsync://carbslinux.org/repo
     /core/.rsync:      rsync://carbslinux.org/repo/core
     /core/.rsync_root: ..

The .rsync_root file on the core repository points to the upper directory. If a .rsync file exists on the upper directory, this means that is the whole repository and will sync the entire repository instead of each individual repository.

If the upper directory doesn't have this .rsync file, this means that this is an individual repository, and the package manager will fetch accordingly.

Setting up an rsync repository for distribution
-----------------------------------------------

Carbs Linux repositories automatically sync from the git repostitories and serve it through the rsync daemon. Here is a sample shell script that I use in order to sync repositories. Feel free to customize for your own use.

    #!/bin/sh
    HOSTNAME="rsync://carbslinux.org/repo"
    GITDIR="/pub/git/repo"
    SHAREDIR="/pub/share/repo"
    git -C "$GITDIR" pull

    rsync -avcC --delete --include=core --exclude=.rsync,.rsync_root "$GITDIR/." "$SHAREDIR"

    printf '%s\n' "$HOSTNAME" > "$GITDIR/.rsync"
    for dir in "$GITDIR/"*; do
        [ -d "$dir" ] || continue
        [ -f "$dir/.rsync" ] ||
        printf '%s/%s\n' "$HOSTNAME" "${dir##*/}" > "$dir/.rsync"
        printf '..\n' > "$dir/.rsync_root"
    done

You can then create an rsync user for serving the repositories.

     $ adduser -SD rsync

Create /etc/rsyncd.conf and a service configuration as well.

    uid = rsync
    gid = rsync
    address = example.com
    max connections = 10
    use chroot = yes

    [repo]
        path = /pub/share/repo
        comment = My repository

Create a service file at /etc/sv/rsync/run (runit):

     #!/bin/sh
     exec rsync --daemon --no-detach