# Tips, Hints and useful information

## General maintainability of your packages

- Try hard to make all files work on all platforms you support.
  - Maintaining separate scripts, config, service files, etc for each platform
    quickly becomes difficult
- Put as much conditional logic in the pre/post install scripts as possible
  instead of trying to build it into the nfpm.yaml
- _if_ you need to know the packaging system I have found it useful to add a
  `/etc/path-to-cfg/package.env` that contains `_INSTALLED_FROM=apk|deb|rpm`
  which can be sourced into the pre/post install/remove scripts
- _if/when_ you need to ask questions during the installation process, create an
  `install.sh` || `setup.sh` script that asks those questions and stores the
  answers as env vars in `/etc/path-to-cfg/package.env` for use by the pre/post
  install/remove scripts
  - If you only need to support deb packages you can use the debconf
    template/config feature, but since rpm does not support this I would try to
    unify the way you ask questions.

## Pre/post install scripts

Here are some useful links for how these scripts work in each packager:

- [APK Docs](https://wiki.alpinelinux.org/wiki/Creating_an_Alpine_package#install)
- [RPM Docs](https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/)
- [DEB Docs](https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html)

### Examples

<details>
<summary>Example Multi platform post-install script</summary>

```bash
#!/bin/sh

# Step 1, decide if we should use SystemD or init/upstart
use_systemctl="True"
systemd_version=0
if ! command -V systemctl >/dev/null 2>&1; then
  use_systemctl="False"
else
    systemd_version=$(systemctl --version | head -1 | sed 's/systemd //g')
fi

cleanup() {
    # This is where you remove files that were not needed on this platform / system
    if [ "${use_systemctl}" = "False" ]; then
    	rm -f /path/to/<SERVICE NAME>.service
    else
        rm -f /etc/chkconfig/<SERVICE NAME>
        rm -f /etc/init.d/<SERVICE NAME>
    fi
}

cleanInstall() {
    printf "\033[32m Post Install of an clean install\033[0m\n"
    # Step 3 (clean install), enable the service in the proper way for this platform
    if [ "${use_systemctl}" = "False" ]; then
        if command -V chkconfig >/dev/null 2>&1; then
          chkconfig --add <SERVICE NAME>
        fi

        service <SERVICE NAME> restart ||:
    else
    	# rhel/centos7 cannot use ExecStartPre=+ to specify the pre start should be run as root
    	# even if you want your service to run as non root.
        if [ "${systemd_version}" -lt 231 ]; then
	        printf "\033[31m systemd version %s is less then 231, fixing the service file \033[0m\n" "${systemd_version}"
	        sed -i "s/=+/=/g" /path/to/<SERVICE NAME>.service
	    fi
        printf "\033[32m Reload the service unit from disk\033[0m\n"
        systemctl daemon-reload ||:
        printf "\033[32m Unmask the service\033[0m\n"
        systemctl unmask <SERVICE NAME> ||:
        printf "\033[32m Set the preset flag for the service unit\033[0m\n"
        systemctl preset <SERVICE NAME> ||:
        printf "\033[32m Set the enabled flag for the service unit\033[0m\n"
        systemctl enable <SERVICE NAME> ||:
        systemctl restart <SERVICE NAME> ||:
    fi
}

upgrade() {
    printf "\033[32m Post Install of an upgrade\033[0m\n"
    # Step 3(upgrade), do what you need
    ...
}

# Step 2, check if this is a clean install or an upgrade
action="$1"
if  [ "$1" = "configure" ] && [ -z "$2" ]; then
  # Alpine linux does not pass args, and deb passes $1=configure
  action="install"
elif [ "$1" = "configure" ] && [ -n "$2" ]; then
    # deb passes $1=configure $2=<current version>
    action="upgrade"
fi

case "$action" in
  "1" | "install")
    cleanInstall
    ;;
  "2" | "upgrade")
    printf "\033[32m Post Install of an upgrade\033[0m\n"
    upgrade
    ;;
  *)
    # $1 == version being installed
    printf "\033[32m Alpine\033[0m"
    cleanInstall
    ;;
esac

# Step 4, clean up unused files, yes you get a warning when you remove the package, but that is ok.
cleanup

```

</details>

<details>
<summary>Example Multi platform (RPM & Deb) post-remove script</summary>

```bash
#!/bin/sh

remove() {
    printf "\033[32m Post Remove of a normal remove\033[0m\n"
    echo "Remove" > /tmp/postremove-proof
}

purge() {
    printf "\033[32m Post Remove purge, deb only\033[0m\n"
    echo "Purge" > /tmp/postremove-proof
}

upgrade() {
    printf "\033[32m Post Remove of an upgrade\033[0m\n"
    echo "Upgrade" > /tmp/postremove-proof
}

echo "$@"

action="$1"

case "$action" in
  "0" | "remove")
    remove
    ;;
  "1" | "upgrade")
    upgrade
    ;;
  "purge")
    purge
    ;;
  *)
    printf "\033[32m Alpine\033[0m"
    remove
    ;;
esac
```

</details>

### Execution order

On upgrade, the scripts are being executed in the following order:

1. `pretrans` of new package (only applies to RPM)
2. `preinstall` of new package
3. `postinstall` of new package
4. `preremove` of old package
5. `postremove` of old package
6. `posttrans` of new package (only applies to RPM)

## SystemD and upstart/init

### upstart / init

- try to just say no to supporting this, but if you must make sure you have a
  single script that works on all platforms you need to support.
  - as the `post-install` script above does.

### SystemD

- The docs you find for SystemD are generally for the latest and greatest
  version, and it can be hard to find docs for older versions.
  - In the above `post-install` script you see I am doing a SystemD version
    check to correct the `ExecStartPre=+...` and `ExecStop=+...` lines
- You should always use
  [automatic directory creation and environment variables](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#id-1.14.4.3.6.2)
  - With the note that only `RuntimeDirectory` is used in SystemD < 211
- `/bin/bash -c "$(which ...) ...` is a great way to make your single service
  file work on all platforms since RHEL and Debian-based systems have standard
  executables in differing locations and complain about `executable path is not absolute`
  - e.g. `/bin/bash -c '$(which mkdir) -p /var/log/your-service'`

## Debian packages

### The `.lintian-overrides` file

It is recommended to run [lintian](https://wiki.debian.org/Lintian) against your
deb packages to see if there are any problems.

You can also add a `lintian-overrides` file:

```yaml
# nfpm.yaml
contents:
- src: .lintian-overrides
  dst: ./usr/share/lintian/overrides/nfpm
  packager: deb
  file_info:
	mode: 0644
```

You can read more in [lintian's documentation](https://wiki.debian.org/Lintian).

### The `copyright` file

If you need a `copyright` file, you can add it using the `contents` directive:

```yaml
# nfpm.yaml
contents:
- src: ./path/to/copyright
  dst: /usr/share/doc/<product_name>/copyright
  packager: deb
  file_info:
	mode: 0644
```
