Compare commits

..

14 Commits

26 changed files with 687 additions and 603 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# IDE Directories
.idea
# Build directory
build

29
Makefile Normal file
View File

@ -0,0 +1,29 @@
# Installation paths
PREFIX ?= /usr/local
BINDIR ?= $(PREFIX)/bin
SYSCONFDIR := $(PREFIX)/etc
# Compilers and tools
GO ?= $(shell which go)
build:
mkdir -p build
cd src/bpm-convert; $(GO) build -ldflags "-w" -o ../../build/bpm-convert git.enumerated.dev/bubble-package-manager/bpm-utils/src/bpm-convert
cd src/bpm-package; $(GO) build -ldflags "-w" -o ../../build/bpm-package git.enumerated.dev/bubble-package-manager/bpm-utils/src/bpm-package
cd src/bpm-repo; $(GO) build -ldflags "-w" -o ../../build/bpm-repo git.enumerated.dev/bubble-package-manager/bpm-utils/src/bpm-repo
cd src/bpm-setup; $(GO) build -ldflags "-w" -o ../../build/bpm-setup git.enumerated.dev/bubble-package-manager/bpm-utils/src/bpm-setup
install: build/ config/
# Create directories
install -dm755 $(DESTDIR)$(BINDIR)
install -dm755 $(DESTDIR)$(SYSCONFDIR)
# Install binaries
install -Dm755 build/bpm-* -t $(DESTDIR)$(BINDIR)/
# Install config files
install -dm755 $(DESTDIR)$(SYSCONFDIR)/bpm-utils/
cp -r config/* -t $(DESTDIR)$(SYSCONFDIR)/bpm-utils/
clean:
rm -r build/
.PHONY: build

View File

@ -1,56 +1,58 @@
# BPM Utils
### _Package creation and editing utilities for BPM_ ###
### _Creation and maintenance utilities for BPM packages and repositories_ ###
## Information
BPM Utils provides a number of different helper commands for creating and maintaining BPM packages or repositories
BPM Utils is a package providing a number of different helper scripts for setting up and archiving BPM packages
## Provided Scripts
- bpm-setup (Creates a directory with the required files for a BPM package)
## Provided utilities
- bpm-setup (Sets up directories for BPM source package creation)
- bpm-repo (Allows for easy management of multiple-package repositories)
- bpm-package (Turns a BPM package directory into a .bpm archive)
- bpm-convert (Converts source packages to binary ones)
- create-repository-data (Generates a repository package data list for unpac)
## Installation
Currently all BPM Utilities are simple bash scripts. This means you are able to simply clone this repository and place these scripts wherever you would like
#### Using a package manager
- Tide Linux: Tide linux provides a `bpm-utils` package which can be installed using `bpm install bpm-utils`
#### Building from source
- Download `go` from your package manager or from the go website
- Download `make` from your package manager
- Run the following command to compile the project
```
make SYSCONFDIR=/etc
```
- Run the following command to install bpm-utils to your system. You may also append a DESTDIR variable at the end of this line if you wish to install the files to a different location
```
make install PREFIX=/usr SYSCONFDIR=/etc
```
## Package Creation using BPM Utils
Creating a package for BPM with these utilities is simple
2) Run the following command (You can run the comamnd with no arguments to see available options)
1) Run the following command (You can run the command with no arguments to see all available options)
```
bpm-setup -D my_package -t <binary/source>
```
3) This will create a directory named 'my_bpm_package' under the current directory with all the required files for the chosen package type
4) You are able to edit the pkg.info descriptor file inside the newly created directory to your liking. Here's an example of what a descriptor file could look like
bpm-setup -D my_package
```
2) This will create a directory named `my_package` containing all files required for bpm package creation
3) You may wish to edit the pkg.info descriptor file inside the newly created directory to include dependencies or add/change other information. Here's an example of what a descriptor file could look like
```yaml
name: my_package
description: My package's description
version: 1.0
revision: 2 (Optional)
architecture: x86_64
url: https://www.my-website.com/ (Optional)
license: MyLicense (Optional)
type: <binary/source>
architecture: x86_64
depends: ["dependency1","dependency2"] (Optional)
optional_depends: ["optional_depend1","optional_depend2"]
optional_depends: ["optional_depend1","optional_depend2"] (Optional)
make_depends: ["make_depend1","make_depend2"] (Optional)
keep: ["etc/my_config.conf"] (Optional)
type: source
```
### Binary Packages
3) If you are making a binary package, copy all your binaries along with the directories they reside in (i.e files/usr/bin/my_binary)
6) Run the following to create a package archive
4) If you would like to bundle patches or other files with your package place them in the 'source-files' directory. They will be extracted to the same location as the source.sh file during compilation
5) You now need to edit your source.sh file which contains the compilation instructions for your package, the default source template comments should explain the basic process of compiling your program and how to edit it
6) When you are done editing your source.sh script run the following command to create a BPM package archive. You may run the `bpm-package` command with no arguments to get an explanation of what each flag does
```
bpm-package <filename.bpm>
bpm-package -d
```
7) It's done! You now hopefully have a working BPM package!
### Source Packages
3) If you would like to bundle patches or other files with your source package place them in the 'source-files' directory. They will be extracted to the same location as the source.sh file during compilation
4) You need to edit your 'source.sh' file, the default source.sh template should explain the basic process of compiling your program
5) Your goal is to download your program's source code with either git, wget, curl, etc. and put the binaries under a folder called 'output' in the root of the temp directory. There is a simple example script with helpful comments in the htop-src test package
6) When you are done making your source.sh script run the following to create a package archive. You may also append the -c flag to compile the package and create a binary package as well
```
bpm-package <filename.bpm>
```
7) That's it! Your source package should now be compiling correctly!
7) The `bpm-package` command will output a binary bpm archive which can be installed by BPM using `bpm install <file.bpm>`. If you are operating inside a BPM repository created using `bpm-repo` the file will automatically be moved to the binary subdirectory of your package repository

View File

@ -1,217 +0,0 @@
#!/bin/bash
if [ -f .compilation-options ]; then
source ./.compilation-options
fi
echo "$ARCH"
while getopts "ksfa:" o; do
case "${o}" in
a) ARCH="$OPTARG";;
k) KEEP=true;;
s) SKIPCHECK=true;;
f) FORCE=true;;
*) exit 1;;
esac
done
PACKAGE="${@:$OPTIND:1}"
DIR="$PWD"
if [ -z "$ARCH" ]; then
ARCH=$(uname -m)
fi
if ! [ -f "$PACKAGE" ]; then
echo "$PACKAGE is not a path to a file"
exit 1
fi
if ! file "$PACKAGE" | grep -q 'POSIX tar archive'; then
echo "$PACKAGE is not a BPM package"
exit 1
fi
if ! tar -tf "$PACKAGE" | grep -q 'source.sh'; then
echo "$PACKAGE is not a BPM source package"
exit 1
fi
echo "$Converting $PACKAGE..."
declare -A PKGINFO
# Declare and run Read Package Information function
function ReadPkgInfo() {
local BACK="$PWD"
cd "$DIR"
if [ $# -eq 0 ]; then
FILE=pkg.info
PKGINFO_FILE=$(tar -xf "$PACKAGE" pkg.info -O)
else
FILE=pkg.info."$1"
fi
if ! tar -tf "$PACKAGE" "$FILE"; then
echo "Could not find $FILE in $PACKAGE"
exit 1
fi
PKGINFO_FILE=$(tar -xf "$PACKAGE" "$FILE" -O)
while read line; do
PKGINFO[$(echo -n "$line" | cut -d":" -f1 | xargs)]=$(echo -n "$line" | cut -d":" -f2 | xargs)
done < <(tar -xf "$PACKAGE" "$FILE" -O)
cd "$BACK"
}
ReadPkgInfo
if [ -z "$FORCE" ] && command -v bpm &> /dev/null && [ -n "$PKGINFO[depends]" ]; then
MISSING=()
for depend in $(echo "${PKGINFO[depends]}" | tr -d '[]' | tr ',' '\n' ); do
if ! bpm info "$depend" &> /dev/null; then
MISSING+=("$depend")
fi
done
if [ "${#MISSING[@]}" -ne 0 ]; then
echo "The following dependencies could not be resolved: ${MISSING[@]}"
EXIT=true
fi
elif ! command -v bpm &> /dev/null; then
echo "BPM not in PATH. Skipping dependency resolution"
elif [ -n "$FORCE" ]; then
echo "Force compilation enabled. Skipping dependency resolution"
fi
if [ -z "$FORCE" ] && command -v bpm &> /dev/null && [ -n "$PKGINFO[make_depends]" ]; then
MISSING=()
for depend in $(echo "${PKGINFO[make_depends]}" | tr -d '[]' | tr ',' '\n' ); do
if ! bpm info "$depend" &> /dev/null; then
MISSING+=("$depend")
fi
done
if [ "${#MISSING[@]}" -ne 0 ]; then
echo "The following make dependencies could not be resolved: ${MISSING[@]}"
EXIT=true
fi
elif ! command -v bpm &> /dev/null; then
echo "BPM not in PATH. Skipping make dependency resolution"
elif [ -n "$FORCE" ]; then
echo "Force compilation enabled. Skipping make dependency resolution"
fi
if [ -n "$EXIT" ]; then
exit 1
fi
# Creating temporary compilation directory structure
TEMPDIR="/var/tmp/bpm_source_${PKGINFO[name]}"
if [ -d "$TEMPDIR" ] && [ -z "$KEEP" ]; then
rm -rf "$TEMPDIR"
fi
mkdir -p "$TEMPDIR"
mkdir -p "$TEMPDIR"/source
[ -d "$TEMPDIR"/output ] && rm -rf "$TEMPDIR"/output
mkdir -p "$TEMPDIR"/output
tar -xf "$PACKAGE" -C "$TEMPDIR" source.sh
if tar -xf "$PACKAGE" -C "$TEMPDIR" source-files &> /dev/null; then
mv "$TEMPDIR"/source-files/* "$TEMPDIR"/
rm -d "$TEMPDIR"/source-files
fi
PACKAGE_SCRIPTS=()
while read line; do
PACKAGE_SCRIPTS+=("$line")
done < <(tar -tf "$PACKAGE" | grep -v -E 'source.sh|pkg.info|source-files')
tar -xf "$PACKAGE" -C "$TEMPDIR" ${PACKAGE_SCRIPTS[@]} &> /dev/null
cd "$TEMPDIR"
# Declare and run Set Variables function
function SetVariables() {
export BPM_PKG_NAME="${PKGINFO[name]}"
export BPM_PKG_DESC="${PKGINFO[description]}"
export BPM_PKG_VERSION="${PKGINFO[version]}"
export BPM_PKG_REVISION="${PKGINFO[revision]}"
[ -z "$BPM_PKG_REVISION" ] && export BPM_PKG_REVISION=1
export BPM_PKG_URL="${PKGINFO[url]}"
export BPM_PKG_ARCH="${PKGINFO[architecture]}"
IFS=',' read -r -a BPM_PKG_DEPENDS <<< "${PKGINFO[depends]}"
IFS=',' read -r -a BPM_PKG_MAKE_DEPENDS <<< "${PKGINFO[make_depends]}"
export BPM_PKG_DEPENDS
export BPM_PKG_MAKE_DEPENDS
export BPM_WORKDIR="$TEMPDIR"
export BPM_SOURCE="$TEMPDIR"/source
export BPM_OUTPUT="$TEMPDIR"/output
}
SetVariables
# Declare Run Package Function function
function RunPkgFunction() {
if [ $# -eq 0 ]; then
echo "No function name given"
exit 1
fi
func="$1"
if [[ $(type -t "$func") == function ]]; then
echo "Running ${func}() function..."
bash -e -c "$func"
if [ $? -ne 0 ]; then
echo "Failed to run ${func}() function in source.sh"
exit 1
fi
fi
}
# Read source.sh file and source functions
set -a
source source.sh
set +a
cd "$BPM_WORKDIR"
RunPkgFunction prepare
cd "$BPM_SOURCE"
RunPkgFunction build
cd "$BPM_SOURCE"
if [ -z "$SKIPCHECK" ]; then
RunPkgFunction check
fi
# Packaging all packages
for func in $(typeset -F | awk '{print $3}' | grep '^package'); do
cd "$BPM_SOURCE"
if [[ "$func" == "package" ]]; then
pkgname="$BPM_PKG_NAME"
ReadPkgInfo
else
pkgname="${func##package-}"
ReadPkgInfo "$pkgname"
fi
SetVariables
echo "Running ${func}() function..."
touch "$TEMPDIR"/fakeroot_file_"$pkgname"
fakeroot -s "$TEMPDIR"/fakeroot_file_"$pkgname" bash -e -c "$func"
if [ $? -ne 0 ]; then
echo "Failed to run ${func}() function in source.sh"
exit 1
fi
cd "$BPM_WORKDIR"
touch pkg.info
if [[ "$pkgname" == "$BPM_PKG_NAME" ]]; then
echo "${PKGINFO_FILE}" > pkg.info
else
echo "${PKGINFO_FILE}" > pkg.info
fi
echo "${PKGINFO_FILE}" > pkg.info
sed -i "s/architecture:.*/architecture: ${ARCH}/g" pkg.info
sed -i 's/type:.*/type: binary/g' pkg.info
fakeroot -i "$TEMPDIR"/fakeroot_file_"$pkgname" find "$TEMPDIR"/output -mindepth 1 -printf "%P %#m %U %G %s\n" > "$TEMPDIR"/pkg.files
find output -printf "%P\n" | fakeroot -i "$TEMPDIR"/fakeroot_file_"$pkgname" tar -czf files.tar.gz --no-recursion -C output -T -
tar -cf "$DIR"/"$pkgname"-"$BPM_PKG_VERSION"-"$BPM_PKG_REVISION"-"$ARCH".bpm --owner=0 --group=0 -C "$TEMPDIR" pkg.info pkg.files ${PACKAGE_SCRIPTS[@]} files.tar.gz
echo "Packaged ${pkgname} successfully!"
rm "$TEMPDIR"/fakeroot_file_"$pkgname"
rm -r output/
mkdir output
rm pkg.info
done
if [ -z "$KEEP" ]; then
rm -rf "$TEMPDIR"
fi

View File

@ -1,99 +0,0 @@
#!/bin/bash
if [ -f .compilation-options ]; then
source ./.compilation-options
fi
while getopts "cskfa:" flag; do
case "$flag" in
c) CONVERT=true;;
k) KEEP=true;;
s) SKIPCHECK=true;;
f) FORCE=true;;
a) ARCH="${OPTARG}";;
*) exit 1;;
esac
done
if [ -f pkg.info ]; then
echo "pkg.info file found"
else
echo "pkg.info file not found in $PWD"
exit 1
fi
output="${@:$OPTIND:1}"
if [ -z "$output" ]; then
pkgname=$(grep "^name: " pkg.info)
output=$(echo "$pkgname" | cut -d' ' -f2)"-src.bpm"
fi
if [ -z "$output" ] || [[ ! "$output" =~ ^[a-z.A-Z0-9_-]{1,}$ ]]; then
echo "Invalid output name! The name must only contain letters, numbers, hyphens or underscores!"
exit 1
fi
type="binary"
toCompress=()
echo "Creating package with the name $output..."
if [ -d files ]; then
echo "files/ directory found"
toCompress+=("files/")
else
if [ -f source.sh ]; then
type="source"
echo "source.sh file found"
toCompress+=("source.sh")
if [ -f pre_update.sh ]; then
echo "pre_update.sh file found"
toCompress+=("pre_update.sh")
fi
if [ -f post_update.sh ]; then
echo "post_update.sh file found"
toCompress+=("post_update.sh")
fi
if [ -f pre_install.sh ]; then
echo "pre_install.sh file found"
toCompress+=("pre_install.sh")
fi
if [ -f post_install.sh ]; then
echo "post_install.sh file found"
toCompress+=("post_install.sh")
fi
if [ -f pre_remove.sh ]; then
echo "pre_remove.sh file found"
toCompress+=("pre_remove.sh")
fi
if [ -f post_remove.sh ]; then
echo "post_remove.sh file found"
toCompress+=("post_remove.sh")
fi
if [ -d source-files ]; then
if [ ! -z "$(ls -A source-files)" ]; then
echo "source-files/ directory found"
toCompress+=("source-files/")
fi
fi
else
echo "files/ directory or source.sh file not found in $PWD"
exit 1
fi
fi
for pkginfo in ./pkg.inf*; do
pkginfo=$(basename "$pkginfo")
echo "${pkginfo} file found"
toCompress+=("$pkginfo")
done
echo "Creating $type package as $output"
tar -c --owner=0 --group=0 --no-same-owner -f "$output" "${toCompress[@]}"
if [ ! -z "$CONVERT" ] && "$CONVERT" && [[ "$type" == "source" ]]; then
args=()
[ -n "$KEEP" ] && args+=("-k")
[ -n "$SKIPCHECK" ] && args+=("-s")
[ -n "$FORCE" ] && args+=("-f")
bpm-convert "${args[@]}" -a "$ARCH" "$output"
fi

176
bpm-setup
View File

@ -1,176 +0,0 @@
#!/bin/bash
usage () {
echo "------BPM-Setup options------"
echo "bpm-setup -D <directory> | Path to package directory"
echo "bpm-setup -y | Skips confirmation prompt"
echo "bpm-setup -n <name> | Set the package name (Defaults to \"package-name\")"
echo "bpm-setup -d <description> | Set the package description (Defaults to \"Default package description\")"
echo "bpm-setup -v <version> | Set the package version (Defaults to \"1.0\")"
echo "bpm-setup -u <url> | Set the package URL (Optional)"
echo "bpm-setup -l <licenses> | Set the package licenses (Optional)"
echo "bpm-setup -t <binary/source> | Set the package type to binary or source (Defaults to binary)"
echo "bpm-setup -s <source template file> | Use a default template file (Defaults to /etc/bpm-utils/source.default)"
echo "bpm-setup -g <true/false> | Create git repository (Defaults to true)"
}
if [ $# -eq 0 ]; then
usage
exit
fi
DESCRIPTION="Default package description"
VERSION="1.0"
#URL="https://my.project.url/ (Optional)"
#LICENSE="Your project's license (Optional)"
TYPE="binary"
SOURCE_FILE="/etc/bpm-utils/source.default"
GIT=true
while getopts "D:n:d:v:u:l:t:s:g:y" o; do
case "${o}" in
D)
DIRECTORY="${OPTARG}"
;;
y)
CONFIRM=yes
;;
n)
NAME="${OPTARG}"
;;
d)
DESCRIPTION="${OPTARG}"
;;
v)
VERSION="${OPTARG}"
;;
u)
URL="${OPTARG}"
;;
l)
LICENSE="${OPTARG}"
;;
t)
TYPE="${OPTARG}"
;;
s)
SOURCE_FILE="$(realpath ${OPTARG})"
;;
g)
if [[ "${OPTARG}" == true ]]; then
GIT=TRUE
elif [[ "${OPTARG}" == false ]]; then
GIT=false
else
echo "${OPTARG} is not a true or false value!"
exit 1
fi
;;
*)
usage
exit 1
;;
esac
done
if [ -z "${DIRECTORY}" ]; then
echo "Required directory argument missing. Try 'bpm-setup -D <directory> [other options...]"
exit 1
fi
if [ -z "${NAME}" ]; then
NAME="${DIRECTORY}"
fi
if [[ ! "${DIRECTORY}" == "/"* ]]; then
DIRECTORY="${PWD}/${DIRECTORY}"
fi
if [ -e "${DIRECTORY}" ]; then
echo "This path already exists"
exit 1
fi
if [[ "$TYPE" != "binary" ]] && [[ "$TYPE" != "source" ]]; then
echo "Invalid package type! Package type must be either 'binary' or 'source'"
exit 1
fi
if [ -z "${CONFIRM}" ]; then
echo "Setting up package working directory at ${DIRECTORY} with the following information:"
echo "Package name: $NAME"
echo "Package description: $DESCRIPTION"
echo "Package version: $VERSION"
if [ -z "${URL}" ]; then
echo "Package URL: Not set"
else
echo "Package URL: $URL"
fi
if [ -z "${LICENSE}" ]; then
echo "Package license: Not set"
else
echo "Package license: $LICENSE"
fi
echo "Package type: $TYPE"
if [[ "$TYPE" == "source" ]]; then
echo "Create Git repository: $GIT"
fi
read -p "Create package directory? [y/N]: " CREATE
case $CREATE in
[Yy]* )
;;
*)
echo "Exiting bpm-setup..."
exit
;;
esac
fi
if ! mkdir -pv $DIRECTORY; then
echo "Could not create $DIRECTORY!"
exit 1
fi
cd $DIRECTORY
touch pkg.info
echo "name: ${NAME}" >> pkg.info
echo "description: ${DESCRIPTION}" >> pkg.info
echo "version: ${VERSION}" >> pkg.info
if [ ! -z "${URL}" ]; then echo "url: ${URL}" >> pkg.info; fi
if [ ! -z "${LICENSE}" ]; then echo "license: ${LICENSE}" >> pkg.info; fi
if [[ "$TYPE" == "binary" ]]; then
echo "architecture: $(uname -m)" >> pkg.info
echo "type: binary" >> pkg.info
mkdir -pv files
echo "Package directory created successfully!"
echo "Make sure to edit the pkg.info file with the appropriate information for your package"
echo "Add your binaries under the 'files' directory. For example a binary called 'my_binary' should go under files/usr/bin/my_binary"
echo "You can turn your package into a .bpm file use the 'bpm-create <name>' command"
else
echo "architecture: any" >> pkg.info
echo "type: source" >> pkg.info
mkdir -pv source-files
if [ -f "${SOURCE_FILE}" ]; then
touch source.temp
cat "${SOURCE_FILE}" > source.temp
export NAME DESCRIPTION VERSION URL LICENSE TYPE
envsubst '$NAME:$DESCRIPTION:$VERSION:$URL:$LICENSE:$TYPE' < source.temp > source.sh
rm source.temp
else
echo "Source file at ${SOURCE_FILE} does not exist! Creating empty source.sh instead..."
touch source.sh
fi
if $GIT ; then
git init
echo -e "*.bpm\n.gitignore" > .gitignore
fi
echo "Package directory created successfully!"
echo "Make sure to edit the pkg.info file with the appropriate information for your package"
echo "Add your compilation code in the source.sh file. Follow the instructions on the template file on how to properly create your compilation script"
echo "You can add additional files that will be used during compilation to the 'source-files' directory"
echo "You can turn your package into a .bpm file use the 'bpm-package <filename>' command"
fi

1
config/bpm-utils.conf Normal file
View File

@ -0,0 +1 @@
privilege_escalator_cmd: "sudo"

42
config/templates/cmake Normal file
View File

@ -0,0 +1,42 @@
# This is the source.sh script. It is executed by BPM in a temporary directory when compiling a source package
# BPM Expects the source code to be extracted into the automatically created 'source' directory which can be accessed using $BPM_SOURCE
# BPM Expects the output files to be present in the automatically created 'output' directory which can be accessed using $BPM_OUTPUT
DOWNLOAD="https://wwww.my-url.com/file.tar.gz"
FILENAME="${DOWNLOAD##*/}"
# The prepare function is executed in the root of the temp directory
# This function is used for downloading files and putting them into the correct location
prepare() {
wget "$DOWNLOAD"
tar -xvf "$FILENAME" --strip-components=1 -C "$BPM_SOURCE"
}
# The build function is executed in the source directory
# This function is used to compile the source code
build() {
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
}
# The check function is executed in the source directory
# This function is used to run tests to verify the package has been compiled correctly
check() {
cd build
make test
}
# The package function is executed in the source directory
# This function is used to move the compiled files into the output directory
package() {
cd build
make DESTDIR="$BPM_OUTPUT" install
# Install package license
install -Dm644 "$BPM_SOURCE"/LICENSE "$BPM_OUTPUT"/usr/share/licenses/$NAME/LICENSE
}

View File

@ -29,4 +29,7 @@ check() {
# This function is used to move the compiled files into the output directory
package() {
make DESTDIR="$BPM_OUTPUT" install
# Install package license
install -Dm644 "$BPM_SOURCE"/LICENSE "$BPM_OUTPUT"/usr/share/licenses/$NAME/LICENSE
}

34
config/templates/gnu-make Normal file
View File

@ -0,0 +1,34 @@
# This is the source.sh script. It is executed by BPM in a temporary directory when compiling a source package
# BPM Expects the source code to be extracted into the automatically created 'source' directory which can be accessed using $BPM_SOURCE
# BPM Expects the output files to be present in the automatically created 'output' directory which can be accessed using $BPM_OUTPUT
DOWNLOAD="https://wwww.my-url.com/file.tar.gz"
FILENAME="${DOWNLOAD##*/}"
# The prepare function is executed in the root of the temp directory
# This function is used for downloading files and putting them into the correct location
prepare() {
wget "$DOWNLOAD"
tar -xvf "$FILENAME" --strip-components=1 -C "$BPM_SOURCE"
}
# The build function is executed in the source directory
# This function is used to compile the source code
build() {
make PREFIX=/usr
}
# The check function is executed in the source directory
# This function is used to run tests to verify the package has been compiled correctly
check() {
make check
}
# The package function is executed in the source directory
# This function is used to move the compiled files into the output directory
package() {
make DESTDIR="$BPM_OUTPUT" PREFIX=/usr install
# Install package license
install -Dm644 "$BPM_SOURCE"/LICENSE "$BPM_OUTPUT"/usr/share/licenses/$NAME/LICENSE
}

42
config/templates/meson Normal file
View File

@ -0,0 +1,42 @@
# This is the source.sh script. It is executed by BPM in a temporary directory when compiling a source package
# BPM Expects the source code to be extracted into the automatically created 'source' directory which can be accessed using $BPM_SOURCE
# BPM Expects the output files to be present in the automatically created 'output' directory which can be accessed using $BPM_OUTPUT
DOWNLOAD="https://wwww.my-url.com/file.tar.gz"
FILENAME="${DOWNLOAD##*/}"
# The prepare function is executed in the root of the temp directory
# This function is used for downloading files and putting them into the correct location
prepare() {
wget "$DOWNLOAD"
tar -xvf "$FILENAME" --strip-components=1 -C "$BPM_SOURCE"
}
# The build function is executed in the source directory
# This function is used to compile the source code
build() {
mkdir build
cd build
meson setup --prefix=/usr ..
meson compile
}
# The check function is executed in the source directory
# This function is used to run tests to verify the package has been compiled correctly
check() {
cd build
meson test
}
# The package function is executed in the source directory
# This function is used to move the compiled files into the output directory
package() {
cd build
meson install --destdir="$BPM_OUTPUT"
# Install package license
install -Dm644 "$BPM_SOURCE"/LICENSE "$BPM_OUTPUT"/usr/share/licenses/$NAME/LICENSE
}

View File

@ -0,0 +1,34 @@
# This is the source.sh script. It is executed by BPM in a temporary directory when compiling a source package
# BPM Expects the source code to be extracted into the automatically created 'source' directory which can be accessed using $BPM_SOURCE
# BPM Expects the output files to be present in the automatically created 'output' directory which can be accessed using $BPM_OUTPUT
DOWNLOAD="https://wwww.my-url.com/file.tar.gz"
FILENAME="${DOWNLOAD##*/}"
# The prepare function is executed in the root of the temp directory
# This function is used for downloading files and putting them into the correct location
prepare() {
wget "$DOWNLOAD"
tar -xvf "$FILENAME" --strip-components=1 -C "$BPM_SOURCE"
}
# The build function is executed in the source directory
# This function is used to compile the source code
build() {
python3 -m build --wheel --no-isolation .
}
# The check function is executed in the source directory
# This function is used to run tests to verify the package has been compiled correctly
check() {
# Python module testing command varies from module to module
}
# The package function is executed in the source directory
# This function is used to move the compiled files into the output directory
package() {
python3 -m installer --destdir="$BPM_OUTPUT" dist/*.whl
# Install package license
install -Dm644 "$BPM_SOURCE"/LICENSE "$BPM_OUTPUT"/usr/share/licenses/$NAME/LICENSE
}

View File

@ -1,80 +0,0 @@
#!/bin/bash
containsElement () {
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
SCRIPT_PATH=$(realpath "$0")
SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
DEEP=false
PKGS=()
while getopts "d" flag; do
case "$flag" in
d) DEEP=true
shopt -s globstar;;
*) >&2 echo "Unknown flag ${flag}"
exit 2;;
esac
done
if [ -z "${@:$OPTIND:1}" ]; then
echo "You have not specified a repository"
exit 1
fi
if ! [ -d "${@:$OPTIND:1}" ]; then
>&2 echo "The given path is not a directory"
exit 1
fi
if realpath "${@:$OPTIND:1}" &> /dev/null; then
REPO=$(realpath "${@:$OPTIND:1}")
fi
shopt -s nullglob
create_entry() {
if [ $# -eq 0 ]; then
return 0
fi
if ! [ -f "$1" ]; then
return 0
fi
info="info:"$'\n'
info+=$(tar -xf "$pkg" pkg.info -O | sed 's/^/ /g')
package=$(echo "$info" | grep 'name: ' | xargs | awk '{print $2}')
if ! echo "$info" | grep -q '^ type: binary'; then
>&2 echo "The following package is not a binary package: ${package}"
exit 1
fi
if containsElement "$package" "${PKGS[@]}"; then
>&2 echo "The following package was found in more than 1 package archives: ${package}"
exit 1
fi
PKGS+=("$package")
file="$(realpath -s --relative-to="$REPO" "$pkg")"
info+=$'\n'"download: ${file}"
info+=$'\n'"download_size: "$(cat "$pkg" | wc -c)
info+=$'\n'"installed_size: "$(tar -xf "$pkg" files.tar.gz -O | zcat | wc -c)
info+=$'\n---'
echo "$info"
}
if ! $DEEP; then
for arch in "$REPO"/*/; do
for pkg in "$arch"/*.bpm; do
create_entry "$pkg"
done
done
else
for arch in "$REPO"/*/; do
for pkg in "$arch"/**/*.bpm; do
create_entry "$pkg"
done
done
fi

3
src/bpm-convert/go.mod Normal file
View File

@ -0,0 +1,3 @@
module git.enumerated.dev/bubble-package-manager/bpm-utils/src/bpm-convert
go 1.23

7
src/bpm-convert/main.go Normal file
View File

@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("bpm-convert")
}

8
src/bpm-package/go.mod Normal file
View File

@ -0,0 +1,8 @@
module git.enumerated.dev/bubble-package-manager/bpm-utils/src/bpm-package
go 1.23
require gopkg.in/yaml.v3 v3.0.1 // indirect
require bpm-utils-shared v1.0.0
replace bpm-utils-shared => ../bpm-utils-shared

3
src/bpm-package/go.sum Normal file
View File

@ -0,0 +1,3 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

202
src/bpm-package/main.go Normal file
View File

@ -0,0 +1,202 @@
package main
import (
bpmutilsshared "bpm-utils-shared"
"bufio"
"flag"
"fmt"
"gopkg.in/yaml.v3"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
)
var compile = flag.Bool("c", false, "Compile BPM source package")
var skipCheck = flag.Bool("s", false, "Skip 'check' function while compiling")
var installDepends = flag.Bool("d", false, "Install package dependencies for compilation")
var installPackage = flag.Bool("i", false, "Install compiled BPM package after compilation finishes")
var yesAll = flag.Bool("y", false, "Accept all confirmation prompts")
func main() {
// Setup flags and help
bpmutilsshared.SetupHelp("bpm-package <options>", "Generates source BPM package from current directory")
bpmutilsshared.SetupFlags()
// Run checks
runChecks()
// Create BPM archive
outputFile := createArchive()
if *compile {
compilePackage(outputFile)
}
}
func runChecks() {
// Check if pkg.info file exists
if stat, err := os.Stat("pkg.info"); err != nil || !stat.Mode().IsRegular() {
log.Fatalf("Error: pkg.info does not exist or is not a regular file")
}
// Check if source.sh file exists
if stat, err := os.Stat("source.sh"); err != nil || !stat.Mode().IsRegular() {
log.Fatalf("Error: pkg.info does not exist or is not a regular file")
}
}
func createArchive() string {
filesToInclude := make([]string, 0)
// Include base files
filesToInclude = append(filesToInclude, "pkg.info", "source.sh")
// Check if non-empty source-files directory exists and include it
if stat, err := os.Stat("source-files"); err == nil && stat.IsDir() {
dir, err := os.ReadDir("source-files")
if err == nil && len(dir) != 0 {
fmt.Println("Non-empty 'source-files' directory found")
filesToInclude = append(filesToInclude, "source-files")
}
}
// Check for package scripts and include them
for _, script := range []string{"pre_install.sh", "post_install.sh", "pre_update.sh", "post_update.sh", "pre_remove.sh", "post_remove.sh"} {
if stat, err := os.Stat(script); err == nil && stat.Mode().IsRegular() {
fmt.Printf("Package script '%s' found", script)
filesToInclude = append(filesToInclude, script)
}
}
// Read pkg.info file into basic struct
pkgInfo := struct {
Name string `yaml:"name"`
Version string `yaml:"version"`
Revision int `yaml:"revision"`
Arch string `yaml:"architecture"`
}{
Revision: 1,
}
data, err := os.ReadFile("pkg.info")
if err != nil {
log.Fatalf("Error: could not read pkg.info file")
}
err = yaml.Unmarshal(data, &pkgInfo)
if err != nil {
log.Fatalf("Error: could not unmarshal pkg.info file")
}
// Create filename
filename := fmt.Sprintf("%s-%s-%d-%s-src.bpm", pkgInfo.Name, pkgInfo.Version, pkgInfo.Revision, pkgInfo.Arch)
// Create archive using tar
args := make([]string, 0)
args = append(args, "-c", "--owner=0", "--group=0", "--no-same-owner", "-f", filename)
args = append(args, filesToInclude...)
cmd := exec.Command("tar", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
log.Fatalf("Error: failed to create BPM source archive: %s", err)
}
// Get absolute path to filename
absFilepath, err := filepath.Abs(filename)
if err != nil {
log.Fatalf("Error: failed to get absolute path of BPM source archive: %s", err)
}
fmt.Printf("BPM source archive created at: %s\n", absFilepath)
return absFilepath
}
func compilePackage(archive string) {
// Setup compile command
args := make([]string, 0)
args = append(args, "compile")
if *skipCheck {
args = append(args, "-s")
}
if *installDepends {
args = append(args, "-d")
}
if *yesAll {
args = append(args, "-y")
}
args = append(args, archive)
cmd := exec.Command("bpm", args...)
cmd.Stdin = os.Stdin
cmd.Stderr = os.Stderr
pipe, err := cmd.StdoutPipe()
if err != nil {
log.Fatalf("Error: could not setup stdout pipe: %s", err)
}
// Run command
err = cmd.Start()
if err != nil {
log.Fatalf("Error: failed to compile BPM source package: %s", err)
}
// Print command output and store it in variable
pipeOutput := ""
buf := bufio.NewReader(pipe)
for {
b, err := buf.ReadByte()
if err == io.EOF {
break
} else if err != nil {
log.Fatalf("Error: failed to read byte from command: %s", err)
}
pipeOutput += string(b)
fmt.Print(string(b))
}
fmt.Println()
// Put output file into slice
outputFiles := make([]string, 0)
for _, line := range strings.Split(pipeOutput, "\n") {
if strings.Contains(line, "Binary package generated at: ") {
path := strings.TrimSpace(strings.SplitN(line, ":", 2)[1])
outputFiles = append(outputFiles, path)
}
}
// Wait for process to complete
err = cmd.Wait()
if err != nil {
log.Fatalf("Error: failed to compile BPM source package: %s", err)
}
// Install compiled packages
if *installPackage && len(outputFiles) != 0 {
// Read BPM utils config
config, err := bpmutilsshared.ReadBPMUtilsConfig()
if err != nil {
log.Fatalf("Error: failed to read config: %s", err)
}
// Setup install command
args = make([]string, 0)
args = append(args, "bpm", "install", "--reinstall")
if *yesAll {
args = append(args, "-y")
}
args = append(args, outputFiles...)
cmd = exec.Command(config.PrivilegeEscalatorCmd, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
// Run command
err = cmd.Run()
if err != nil {
log.Fatalf("Error: failed to install compiled BPM packages: %s", err)
}
}
}

3
src/bpm-repo/go.mod Normal file
View File

@ -0,0 +1,3 @@
module git.enumerated.dev/bubble-package-manager/bpm-utils/src/bpm-repo
go 1.23

7
src/bpm-repo/main.go Normal file
View File

@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("bpm-repo")
}

8
src/bpm-setup/go.mod Normal file
View File

@ -0,0 +1,8 @@
module git.enumerated.dev/bubble-package-manager/bpm-utils/src/bpm-setup
go 1.23
require bpm-utils-shared v1.0.0
require gopkg.in/yaml.v3 v3.0.1 // indirect
replace bpm-utils-shared => ../bpm-utils-shared

3
src/bpm-setup/go.sum Normal file
View File

@ -0,0 +1,3 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

165
src/bpm-setup/main.go Normal file
View File

@ -0,0 +1,165 @@
package main
import (
bpmutilsshared "bpm-utils-shared"
"bufio"
"flag"
"fmt"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
)
var directory = flag.String("D", "", "Path to package directory (required)")
var name = flag.String("n", "", "Set the package name")
var description = flag.String("d", "Default Package Description", "Set the description")
var version = flag.String("v", "1.0", "Set the package version")
var url = flag.String("u", "", "Set the package URL")
var license = flag.String("l", "", "Set the package licenses")
var template = flag.String("t", "gnu-configure", "Set the package template")
var git = flag.Bool("g", true, "Create git repository")
func main() {
// Setup flags and help
bpmutilsshared.SetupHelp("bpm-setup <options>", "Sets up files and directories for BPM source package creation")
bpmutilsshared.SetupFlags()
// Show command help if no directory name is given
if *directory == "" {
bpmutilsshared.ShowHelp()
os.Exit(1)
}
// Set package name to directory name if empty
if *name == "" {
*name = filepath.Base(*directory)
}
// run checks
runChecks()
// Show summary
showSummary()
// Confirmation prompt
fmt.Printf("Create package directory? [y\\N]: ")
reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n')
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
fmt.Println("Cancelling package directory creation...")
os.Exit(1)
}
// Create package directory
createDirectory()
}
func runChecks() {
if strings.TrimSpace(*directory) == "" {
log.Fatalf("No directory was specified!")
}
if strings.TrimSpace(*name) == "" {
log.Fatalf("No package name was specified!")
}
if stat, err := os.Stat(path.Join("/etc/bpm-utils/templates/", *template)); err != nil || stat.IsDir() {
log.Fatalf("%s is not a valid template file!", *template)
}
}
func showSummary() {
absPath, err := filepath.Abs(strings.TrimSpace(*directory))
if err != nil {
log.Fatalf("Failed to determine absolute path: %s", err)
}
fmt.Printf("Setting up package directory at %s with the following information:\n", absPath)
fmt.Printf("Package name: %s\n", strings.TrimSpace(*name))
fmt.Printf("Package description: %s\n", *description)
fmt.Printf("Package version: %s\n", *version)
if url != nil && *url != "" {
fmt.Printf("Package URL: %s\n", *url)
} else {
fmt.Printf("Package URL: Not Set\n")
}
if license != nil && *license != "" {
fmt.Printf("Package license: %s\n", *license)
} else {
fmt.Printf("Package license: Not Set\n")
}
fmt.Printf("Template file: %s\n", *template)
fmt.Printf("Create git repository: %t\n", *git)
}
func replaceVariables(templateContents string) string {
templateContents = strings.ReplaceAll(templateContents, "$NAME", *name)
templateContents = strings.ReplaceAll(templateContents, "$DESCRIPTION", *description)
templateContents = strings.ReplaceAll(templateContents, "$VERSION", *version)
templateContents = strings.ReplaceAll(templateContents, "$URL", *url)
return templateContents
}
func createDirectory() {
// Trim spaces
*directory = strings.TrimSpace(*directory)
*name = strings.TrimSpace(*name)
// Create directory
err := os.Mkdir(*directory, 0755)
if err != nil {
log.Fatalf("Error: could not create directory: %s", err)
}
// Create pkg.info contents string
pkgInfo := "name: " + *name + "\n"
pkgInfo += "description: " + *description + "\n"
pkgInfo += "version: " + *version + "\n"
if url != nil && *url != "" {
pkgInfo += "url: " + *url + "\n"
}
if license != nil && *license != "" {
pkgInfo += "license: " + *license + "\n"
}
pkgInfo += "architecture: any\n"
pkgInfo += "type: source\n"
// Write string to file
err = os.WriteFile(path.Join(*directory, "pkg.info"), []byte(pkgInfo), 0644)
if err != nil {
log.Fatalf("Could not write to pkg.info: %s", err)
}
// Create source-files directory
err = os.Mkdir(path.Join(*directory, "source-files"), 0755)
if err != nil {
log.Fatalf("Error: could not create directory: %s", err)
}
// Copy source template file
input, err := os.ReadFile(path.Join("/etc/bpm-utils/templates", *template))
if err != nil {
log.Fatalf("Error: could not read template file: %s", err)
return
}
// Replace variables in template file
input = []byte(replaceVariables(string(input)))
err = os.WriteFile(path.Join(*directory, "source.sh"), input, 0644)
if err != nil {
log.Fatalf("Error: could not write to source file: %s", err)
return
}
// Create git repository
if git != nil && *git {
err = exec.Command("git", "init", *directory).Run()
if err != nil {
log.Fatalf("Error: could not initialize git repository: %s", err)
}
}
}

View File

@ -0,0 +1,5 @@
module bpm-utils-shared
go 1.23
require gopkg.in/yaml.v3 v3.0.1 // indirect

View File

@ -0,0 +1,3 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -0,0 +1,47 @@
package bpm_utils_shared
import (
"flag"
"fmt"
"gopkg.in/yaml.v3"
"os"
)
var usageMsg string
var description string
type BPMUtilsConfig struct {
PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"`
}
func ReadBPMUtilsConfig() (*BPMUtilsConfig, error) {
data, err := os.ReadFile("/etc/bpm-utils/bpm-utils.conf")
if err != nil {
return nil, err
}
config := &BPMUtilsConfig{}
err = yaml.Unmarshal(data, config)
if err != nil {
return nil, err
}
return config, nil
}
func SetupFlags() {
flag.Usage = ShowHelp
flag.Parse()
}
func SetupHelp(usage, desc string) {
usageMsg = usage
description = desc
}
func ShowHelp() {
fmt.Println("Usage: " + usageMsg)
fmt.Println("Description: " + description)
fmt.Println("Options:")
flag.PrintDefaults()
}