From ea06f0dfbc0a675e774748793713759f7a7328ef Mon Sep 17 00:00:00 2001 From: 0x1eef <0x1eef@protonmail.com> Date: Sat, 11 May 2024 16:03:00 -0300 Subject: [PATCH] First commit A modified copy of portzap repurposed for management of the hardenedbsd source tree. --- .github/workflows/shellcheck.yml | 16 +++++++ .projectile | 1 + LICENSE | 15 +++++++ Makefile | 24 ++++++++++ bin/setup-srczap | 36 +++++++++++++++ bin/srczap | 64 ++++++++++++++++++++++++++ libexec/srczap/git-changed-files | 25 +++++++++++ libexec/srczap/git-removed-files | 22 +++++++++ libexec/srczap/git-rev | 20 +++++++++ libexec/srczap/issrczap-member | 11 +++++ libexec/srczap/srczap-clone | 41 +++++++++++++++++ libexec/srczap/srczap-erase | 43 ++++++++++++++++++ libexec/srczap/srczap-install | 77 ++++++++++++++++++++++++++++++++ libexec/srczap/srczap-pull | 59 ++++++++++++++++++++++++ man/man8/srczap.8 | 75 +++++++++++++++++++++++++++++++ share/srczap/VERSION | 1 + share/srczap/doas.conf | 4 ++ 17 files changed, 534 insertions(+) create mode 100644 .github/workflows/shellcheck.yml create mode 100644 .projectile create mode 100644 LICENSE create mode 100644 Makefile create mode 100755 bin/setup-srczap create mode 100755 bin/srczap create mode 100644 libexec/srczap/git-changed-files create mode 100644 libexec/srczap/git-removed-files create mode 100644 libexec/srczap/git-rev create mode 100644 libexec/srczap/issrczap-member create mode 100755 libexec/srczap/srczap-clone create mode 100644 libexec/srczap/srczap-erase create mode 100755 libexec/srczap/srczap-install create mode 100755 libexec/srczap/srczap-pull create mode 100644 man/man8/srczap.8 create mode 100644 share/srczap/VERSION create mode 100644 share/srczap/doas.conf diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml new file mode 100644 index 0000000..34866f9 --- /dev/null +++ b/.github/workflows/shellcheck.yml @@ -0,0 +1,16 @@ +name: srczap +permissions: {} + +jobs: + shellcheck: + name: shellcheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ludeeus/action-shellcheck@master + - run: make shellcheck +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] diff --git a/.projectile b/.projectile new file mode 100644 index 0000000..4139a2e --- /dev/null +++ b/.projectile @@ -0,0 +1 @@ +-/.git diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..548a1d0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +Copyright (C) 2023 by 0x1eef <0x1eef@protonmail.com> + +Permission to use, copy, modify, and/or distribute this +software for any purpose with or without fee is hereby +granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS +ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8712562 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +PREFIX = /usr/local +BINDIR = $(PREFIX)/bin +MANDIR = $(PREFIX)/man/man8 +LIBEXECDIR = $(PREFIX)/libexec/srczap +SHAREDIR = $(PREFIX)/share/srczap + +install: + install -d $(BINDIR) $(LIBEXECDIR) $(SHAREDIR) $(MANDIR) + install -m 0755 bin/srczap $(BINDIR) + install -m 0755 bin/setup-srczap $(BINDIR) + install -m 0755 libexec/srczap/* $(LIBEXECDIR) + install -m 0644 share/srczap/* $(SHAREDIR) + install -m 0644 man/man8/srczap.8 $(MANDIR) + +deinstall: + rm $(BINDIR)/srczap + rm $(BINDIR)/setup-srczap + rm $(MANDIR)/srczap.8 + rm -rf $(LIBEXECDIR) + rm -rf $(SHAREDIR) + +shellcheck: + shellcheck bin/srczap + shellcheck libexec/srczap/* diff --git a/bin/setup-srczap b/bin/setup-srczap new file mode 100755 index 0000000..7201024 --- /dev/null +++ b/bin/setup-srczap @@ -0,0 +1,36 @@ +#!/bin/sh -e + +## +# variables +localbase="${LOCALBASE:-/usr/local}" +conf=$(cat "${localbase}"/share/srczap/doas.conf) +doas="${localbase}"/etc/doas.conf + +## +# main +if [ "$(id -u)" != "0" ]; then + echo "[-] This command must be run by root" + exit 1 +fi + +if id -u _srczap > /dev/null 2>&1; then + echo "[-] The _srczap user exists" + echo "[-] Add user(s) to the _srczap group:" + echo "root# pw groupmod -n _srczap -m user1,user2" +else + pw useradd -n _srczap \ + -c "srczap user" \ + -m \ + -s /sbin/nologin + chmod u=rwX,g=rX,o= /home/_srczap/ + echo "[-] The _srczap user, group and home directory have been created." + echo "[-] Add user(s) to the _srczap group:" + echo "root# pw groupmod -n _srczap -m user1,user2" +fi + +if grep -F "^${conf}$" "${doas}" > /dev/null 2>&1; then + echo "[-] No changes made to ${doas}" +else + echo "$conf" >> "$doas" + echo "[-] ${doas} has been changed. Please review the changes" +fi diff --git a/bin/srczap b/bin/srczap new file mode 100755 index 0000000..0358488 --- /dev/null +++ b/bin/srczap @@ -0,0 +1,64 @@ +#!/bin/sh -e + +## +# variables +localbase="${LOCALBASE:-/usr/local}" +gitdir="/home/_srczap/src" +giturl="${SRCZAP_GITURL:-https://git.hardenedbsd.org/hardenedbsd/HardenedBSD/}" +branch="${SRCZAP_BRANCH:-hardened/14-stable/master}" +installdir="${SRCZAP_INSTALLDIR:-/usr/src}" +revision="${installdir}"/.srczap +libexec="${localbase}"/libexec/srczap + +## +# functions +require_dependency() { + deps=$1 + for dep in $deps; do + if ! which -s "$dep"; then + echo "[-] This command requires ${dep}, but ${dep} wasn't found" + exit 1 + fi + done +} + +## +# main +i=1 +while [ "${i}" -le "$#" ]; do + eval "_srczap_option=\$${i}" + # shellcheck disable=SC2154 + if [ "${_srczap_option}" = "-v" ]; then + cat "${localbase}"/share/srczap/VERSION + exit 0 + fi + # shellcheck disable=SC2003 + i=$(expr "${i}" + 1); +done + +case $1 in + "clone") + require_dependency "git doas" + "${libexec}"/srczap-clone "${giturl}" "${gitdir}" "${branch}" + ;; + "pull") + require_dependency "git doas" + "${libexec}"/srczap-pull "${gitdir}" "${branch}" + ;; + "erase") + "${libexec}"/srczap-erase "${gitdir}" "${installdir}" + ;; + "install") + require_dependency "git doas" + "${libexec}"/srczap-install "${gitdir}" "${installdir}" "${revision}" + ;; + *) + printf "Usage: srczap COMMAND [OPTIONS]\n" + printf "\n" + printf "Commands:\n" + printf " clone Clone the HardenedBSD source code\n" + printf " pull Pull source code updates\n" + printf " erase Erase /usr/src/ and /home/_srczap/src/\n" + printf " install Install the source tree into /usr/src/\n" + ;; +esac diff --git a/libexec/srczap/git-changed-files b/libexec/srczap/git-changed-files new file mode 100644 index 0000000..4dc3197 --- /dev/null +++ b/libexec/srczap/git-changed-files @@ -0,0 +1,25 @@ +#!/bin/sh -e + +## +# variables +localbase=${LOCALBASE:-/usr/local} +git="${localbase}"/bin/git +gitdir=$1 +commit=$2 +mode=u=rwX,g=rX,o= + +## +# functions +gitexec() +{ + doas -n -u _srczap \ + /bin/sh -c "umask ${mode}; ${git} ${1}" +} + +## +# main +cd "${gitdir}" +add=$(gitexec "diff --name-only --diff-filter=A ${commit} HEAD") +mod=$(gitexec "diff --name-only --diff-filter=M ${commit} HEAD") +echo "${add}" +echo "${mod}" diff --git a/libexec/srczap/git-removed-files b/libexec/srczap/git-removed-files new file mode 100644 index 0000000..aa0ff05 --- /dev/null +++ b/libexec/srczap/git-removed-files @@ -0,0 +1,22 @@ +#!/bin/sh -e + +## +# variables +localbase=${LOCALBASE:-/usr/local} +git="${localbase}"/bin/git +gitdir=$1 +commit=$2 +mode=u=rwX,g=rX,o= + +## +# functions +gitexec() +{ + doas -n -u _srczap \ + /bin/sh -c "umask ${mode}; ${git} ${1}" +} + +## +# main +cd "${gitdir}" +gitexec "diff --name-only --diff-filter=D ${commit} HEAD" diff --git a/libexec/srczap/git-rev b/libexec/srczap/git-rev new file mode 100644 index 0000000..9f1437d --- /dev/null +++ b/libexec/srczap/git-rev @@ -0,0 +1,20 @@ +#!/bin/sh -e + +## +# variables +git=/usr/local/bin/git +gitdir=$1 +mode=u=rwX,g=rX,o= + +## +# functions +gitexec() +{ + doas -n -u _srczap \ + /bin/sh -c "umask ${mode}; ${git} ${1}" +} + +## +# main +cd "${gitdir}" +gitexec "rev-parse HEAD" diff --git a/libexec/srczap/issrczap-member b/libexec/srczap/issrczap-member new file mode 100644 index 0000000..535f6a5 --- /dev/null +++ b/libexec/srczap/issrczap-member @@ -0,0 +1,11 @@ +#!/bin/sh -e + +group="_srczap" +if id -Gn | \ + tr ' ' '\n' | \ + grep -e "^${group}$" \ + > /dev/null 2>&1; then + exit 0 +else + exit 1 +fi diff --git a/libexec/srczap/srczap-clone b/libexec/srczap/srczap-clone new file mode 100755 index 0000000..e88920e --- /dev/null +++ b/libexec/srczap/srczap-clone @@ -0,0 +1,41 @@ +#!/bin/sh -e + +## +# variables +localbase=${LOCALBASE:-/usr/local} +libexec=$(dirname "$0") +git="${localbase}"/bin/git +giturl=$1 +gitdir=$2 +branch=$3 +mode=u=rwX,g=rX,o= + +## +# functions +gitexec() +{ + doas -n -u _srczap \ + /bin/sh -c "umask ${mode}; ${git} ${1}" +} + +## +# main +if ! "${libexec}"/issrczap-member; then + echo "[-] This command must be run by a member of the '_srczap' group" + exit 1 +fi + +if [ -e "${gitdir}/.git" ]; then + echo "[-] ${gitdir} exists." + echo "[-] Try 'srczap pull'" + exit 1 +fi + +set -x +gitexec "clone ${giturl} ${gitdir}" +cd "${gitdir}" +gitexec "config core.filemode off" +set +x +echo "[-] git checkout ${branch}" +gitexec "checkout -t origin/${branch} > /dev/null 2>&1" +echo "[-] Done" diff --git a/libexec/srczap/srczap-erase b/libexec/srczap/srczap-erase new file mode 100644 index 0000000..54349b6 --- /dev/null +++ b/libexec/srczap/srczap-erase @@ -0,0 +1,43 @@ +#!/bin/sh -e + +## +# variables +gitdir=$1 +installdir=$2 + +## +# main +if [ "$(id -u)" != "0" ]; then + echo "[-] This command must be run by root" + exit 1 +fi + +printf "[-] Are you sure ? \n" +printf "[-] These directories will be erased:\n" +printf " [*] %s \n" "${gitdir}" +printf " [*] %s \n" "${installdir}" +printf "[y|n] " +while true; do + read -r r + if [ "${r}" = "y" ]; then + break + elif [ "${r}" = "n" ]; then + printf "[-] Nothing to do\n" + exit + else + printf "[-] '%s' is not a valid option.\n" "${r}" + printf "[y|n] " + fi +done +for dir in "${gitdir}" "${installdir}"; do + printf "%s " "${dir}" + find "${dir}" \ + -maxdepth 1 \ + \! -name "." \ + \! -name ".." \ + \! -name "src" \ + -exec printf . \; \ + -exec rm -rf "{}" \; + echo +done +printf "[-] Done\n" diff --git a/libexec/srczap/srczap-install b/libexec/srczap/srczap-install new file mode 100755 index 0000000..9ed06ae --- /dev/null +++ b/libexec/srczap/srczap-install @@ -0,0 +1,77 @@ +#!/bin/sh -e + +## +# variables +gitdir=$1 +installdir=$2 +revfile=$3 +libexec=$(dirname "$0") +mode="u=rwX,g=rX,o=" + +## +# functions +perform_update() +{ + rev=$(cat "${revfile}") + add=$("${libexec}"/git-changed-files "${gitdir}" "${rev}") + del=$("${libexec}"/git-removed-files "${gitdir}" "${rev}") + for file in ${del}; do + target="${installdir}/${file}" + parent=$(dirname "${target}") + echo "rm ${target}" + rm -f "${target}" + find "${parent}" -type d -maxdepth 0 -empty -delete + done + for file in ${add}; do + target="${installdir}/${file}" + parent=$(dirname "${target}") + parents="" + while [ ! -e "${parent}" ]; do + parents="${parent} ${parents}" + parent=$(dirname "${parent}") + done + for dir in ${parents}; do + run_install "-d" "${dir}" + done + run_install "${file}" "${target}" + done +} + +perform_install() +{ + find -s . \ + -maxdepth 1 \ + ! -name "." \ + ! -name ".git" \ + ! -name ".gitignore" \ + ! -name ".hooks" \ + ! -name ".arcconfig" \ + -exec cp -Rpv {} "${installdir}" \; + set -x + chown -R root "${installdir}" +} + +run_install() +{ + install -o root -g _srczap -m "${mode}" -v "$@" +} + +## +# main +if [ "$(id -u)" != "0" ]; then + echo "[-] This command must be run by root" + exit 1 +fi + +set -x +umask ${mode} +cd "${gitdir}" +set +x +run_install "-d" "${installdir}" +chmod ${mode} "${installdir}" +if [ -e "${revfile}" ]; then + perform_update +else + perform_install +fi +"${libexec}"/git-rev "${gitdir}" > "${revfile}" diff --git a/libexec/srczap/srczap-pull b/libexec/srczap/srczap-pull new file mode 100755 index 0000000..84047ef --- /dev/null +++ b/libexec/srczap/srczap-pull @@ -0,0 +1,59 @@ +#!/bin/sh -e + +## +# variables +libexec=$(dirname "$0") +localbase=${LOCALBASE:-/usr/local} +git="${localbase}"/bin/git +gitdir=$1 +branch=$2 +mode="u=rwX,g=rX,o=" + +## +# functions +gitexec() +{ + doas -n -u _srczap \ + /bin/sh -c "umask ${mode}; ${git} ${1}" +} + +change_branch() +{ + set +e + remote=$1 + branch=$2 + echo "[-] Attempt to change branch: ${branch}" + gitexec "fetch ${remote} > /dev/null 2>&1" + if ! gitexec "checkout ${branch} > /dev/null 2>&1" || + gitexec "checkout -t ${remote}/${branch} > /dev/null 2>&1"; then + r="${?}" + echo "[-] 'git checkout' exited with an error" + exit "${r}" + else + echo "[-] Done" + fi + set -e +} + +## +# main +if ! "${libexec}"/issrczap-member; then + echo "[-] This command must be run by a member of the '_srczap' group" + exit 1 +fi + +if [ ! -e "${gitdir}/.git" ]; then + set +x + echo "[-] ${gitdir} is not a valid git repository." + echo "[-] Try 'srczap clone'" + exit 1 +fi + +cd "${gitdir}" +remote=$(gitexec "remote" | head -n1) +cbranch=$(gitexec "branch --show-current") +if [ "${cbranch}" != "${branch}" ]; then + change_branch "${remote}" "${branch}" +fi +set -x +gitexec "pull --rebase ${remote} ${branch}" diff --git a/man/man8/srczap.8 b/man/man8/srczap.8 new file mode 100644 index 0000000..5e09347 --- /dev/null +++ b/man/man8/srczap.8 @@ -0,0 +1,75 @@ +.Dd May 2023 +.Dt SRCZAP 8 +.Os +.Sh NAME +.Nm srczap +.Nd manages a copy of the HardenedBSD source code +.Sh SYNOPSIS +.br +srczap clone +.br +srczap pull +.br +srczap install +.br +srczap erase +.Sh DESCRIPTION +.Nm srczap +manages a copy of the HardenedBSD source code. +The copy of the source tree is maintained by members of +the '_srczap' group, and a copy of the source tree +can be installed to /usr/src/ by root. +.Sh EXAMPLES +.sp +.sp +.Nm srczap clone +.br +Clone the hardenedbsd ports tree into /home/_srczap/src/. +.br +This command is delegated to the '_srczap' user. +.Pp +.Nm srczap pull +.br +Pull updates into /home/_srczap/src/. +.br +This command is delegated to the '_srczap' user. +.Pp +.Nm srczap install +.br +Install /home/_srczap/src/ into /usr/src/. +.br +This command requires root privileges. +.Pp +.Nm srczap erase +.br +Start over: erase /usr/src/ and /home/_srczap/src/. +.br +This command requires root privileges. +.br +.Sh ENVIRONMENT +.sp +.sp +.Nm SRCZAP_GITURL +.br +The URL to a git repository. +.br +Default: https://git.hardenedbsd.org/hardenedbsd/HardenedBSD/ +.sp +.Nm SRCZAP_BRANCH +.br +The git branch to clone and pull updates from. +.br +Default: hardened/14-stable/master +.br +.sp +.Nm SRCZAP_INSTALLDIR +.br +The directory where the ports collection will be installed. +.br +Default: /usr/src/ +.sp +.Sh AUTHORS +The +.Nm srczap +utility and this manual page were written by +0x1eef <0x1eef@protonmail.com>. diff --git a/share/srczap/VERSION b/share/srczap/VERSION new file mode 100644 index 0000000..d31a6e6 --- /dev/null +++ b/share/srczap/VERSION @@ -0,0 +1 @@ +v0.1.0-dev diff --git a/share/srczap/doas.conf b/share/srczap/doas.conf new file mode 100644 index 0000000..c51359a --- /dev/null +++ b/share/srczap/doas.conf @@ -0,0 +1,4 @@ +## +# srczap +permit nopass root as _srczap cmd /bin/sh +permit nopass :_srczap as _srczap cmd /bin/sh