mirror of
https://github.com/EnumeratedDev/bpm-utils.git
synced 2026-09-26 15:36:13 +00:00
Compare commits
50
Commits
7aff954df4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66e1aacc5f
|
||
|
|
8034c7d7ea
|
||
|
|
d781c30146
|
||
|
|
8eb771111c
|
||
|
|
3f84dd4bc3
|
||
|
|
e1daa498e8
|
||
|
|
68ffb7972f
|
||
|
|
9114bb4148
|
||
|
|
5dbdfdd4c1
|
||
|
|
eb13b2653d
|
||
|
|
581c864f1d
|
||
|
|
7e7b8487ea
|
||
|
|
a7f751c094
|
||
|
|
54e192f220
|
||
|
|
c508ce6a01
|
||
|
|
2b81dad6cf
|
||
|
|
82777ebac1
|
||
|
|
b20766bddb
|
||
|
|
0ff9eb9cf2
|
||
|
|
4371b7f0e3
|
||
|
|
65849fdf86
|
||
|
|
a64ad39ca7
|
||
|
|
bf9874af6b
|
||
|
|
07a9e8c9d2
|
||
|
|
598e6710f4
|
||
|
|
902fcbf296
|
||
|
|
03bf97b8d8
|
||
|
|
5240ae4a8c
|
||
|
|
9d03b5b423
|
||
|
|
f17bc777d9
|
||
|
|
154f546b7f
|
||
|
|
c4a0e9f860
|
||
|
|
b9d7dfd9d0
|
||
|
|
4217626dde
|
||
|
|
d7da13034c | ||
|
|
cdb044d751
|
||
|
|
60b0f4489e
|
||
|
|
65c32d628f
|
||
|
|
4ff4a74fa3
|
||
|
|
f6ef60aa40
|
||
|
|
9625b5d7af
|
||
|
|
d881ac31c3
|
||
|
|
96a39a6b40
|
||
|
|
7a63ad6388
|
||
|
|
10f918034a
|
||
|
|
59c72d1a40
|
||
|
|
61c01ae88f | ||
|
|
0fdef74c73 | ||
|
|
d5abad3371 | ||
|
|
334eb15c94 |
@@ -8,7 +8,7 @@ BPM Utils provides a number of different helper commands for creating and mainta
|
|||||||
## Provided utilities
|
## Provided utilities
|
||||||
- bpm-setup (Sets up directories for BPM source package creation)
|
- bpm-setup (Sets up directories for BPM source package creation)
|
||||||
- bpm-repo (Allows for easy management of multiple-package repositories)
|
- bpm-repo (Allows for easy management of multiple-package repositories)
|
||||||
- bpm-package (Turns a BPM package directory into a .bpm archive)
|
- bpm-package (Turns a BPM source package directory into a .bpm archive)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
#### Using a package manager
|
#### Using a package manager
|
||||||
@@ -33,7 +33,7 @@ Creating a package for BPM with these utilities is simple
|
|||||||
bpm-setup -D my_package
|
bpm-setup -D my_package
|
||||||
```
|
```
|
||||||
2) This will create a directory named `my_package` containing all files required for bpm package creation
|
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
|
3) You may wish to edit the info.yml metedata file inside the newly created directory to include dependencies or add/change other information. Here's an example of what a metedata file could look like
|
||||||
```yaml
|
```yaml
|
||||||
name: my_package
|
name: my_package
|
||||||
description: My package's description
|
description: My package's description
|
||||||
@@ -42,17 +42,29 @@ revision: 2 (Optional)
|
|||||||
url: https://www.my-website.com/ (Optional)
|
url: https://www.my-website.com/ (Optional)
|
||||||
license: MyLicense (Optional)
|
license: MyLicense (Optional)
|
||||||
architecture: x86_64
|
architecture: x86_64
|
||||||
depends: ["dependency1","dependency2"] (Optional)
|
|
||||||
optional_depends: ["optional_depend1","optional_depend2"] (Optional)
|
|
||||||
make_depends: ["make_depend1","make_depend2"] (Optional)
|
|
||||||
keep: ["etc/my_config.conf"] (Optional)
|
|
||||||
type: source
|
type: source
|
||||||
|
depends:
|
||||||
|
- dependency1
|
||||||
|
- dependency2
|
||||||
|
optional_depends:
|
||||||
|
- optional_depend1
|
||||||
|
- optional_depend2
|
||||||
|
make_depends:
|
||||||
|
- make_depend1
|
||||||
|
- make_depend2
|
||||||
|
keep:
|
||||||
|
- etc/my_config.conf
|
||||||
|
downloads:
|
||||||
|
- url: https://wwww.my-url.com/file.tar.gz
|
||||||
|
extract_strip_components: 1
|
||||||
|
extract_to_bpm_source: true
|
||||||
|
checksum: 9d19c8884cb22a594ba06a4caa6a3088e15ddfd4f3ede8c3b9e8f5cbb5a4a7a8
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
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 recipe.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
|
5) You now need to edit your recipe.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 source package archive. You may run the `bpm-package` command with no arguments to get an explanation of what each flag does
|
6) When you are done editing your recipe.sh script run the following command to create a BPM source package archive. You may run the `bpm-package` command with no arguments to get an explanation of what each flag does
|
||||||
```
|
```
|
||||||
bpm-package
|
bpm-package
|
||||||
```
|
```
|
||||||
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
|
7) The `bpm-package` command will output a source bpm archive (and binary if passed the '-c' flag) 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
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
# Exclude bpm archives
|
# Ignore BPM archives and signatures
|
||||||
*.bpm
|
*.bpm
|
||||||
|
*.bpm.sig
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# This is the recipe.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
|
||||||
|
|
||||||
|
# The prepare function is executed in the root of the temp directory
|
||||||
|
# This function is used for putting downloaded files to the correct location or applying patches
|
||||||
|
prepare() {
|
||||||
|
cd "$BPM_SOURCE"
|
||||||
|
# Fetch cargo dependencies
|
||||||
|
cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# The build function is executed in the source directory
|
||||||
|
# This function is used to compile the source code
|
||||||
|
build() {
|
||||||
|
cargo build --release --frozen --workspace
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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() {
|
||||||
|
cargo test --frozen
|
||||||
|
}
|
||||||
|
|
||||||
|
# The package function is executed in the source directory
|
||||||
|
# This function is used to move the compiled files into the output directory
|
||||||
|
package() {
|
||||||
|
find target/release -maxdepth 1 -type f -executable -exec install -Dm755 -t "$BPM_OUTPUT"/usr/bin {} +
|
||||||
|
|
||||||
|
# Install package license
|
||||||
|
install -Dm644 "$BPM_SOURCE"/LICENSE "$BPM_OUTPUT"/usr/share/licenses/$NAME/LICENSE
|
||||||
|
}
|
||||||
+7
-18
@@ -1,41 +1,30 @@
|
|||||||
# This is the source.sh script. It is executed by BPM in a temporary directory when compiling a source package
|
# This is the recipe.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 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
|
# 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
|
# 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
|
# This function is used for putting downloaded files to the correct location or applying patches
|
||||||
prepare() {
|
prepare() {
|
||||||
wget "$DOWNLOAD"
|
echo "You may remove this function if you do not intend to use it"
|
||||||
tar -xvf "$FILENAME" --strip-components=1 -C "$BPM_SOURCE"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# The build function is executed in the source directory
|
# The build function is executed in the source directory
|
||||||
# This function is used to compile the source code
|
# This function is used to compile the source code
|
||||||
build() {
|
build() {
|
||||||
mkdir build
|
cmake -B build -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
cd build
|
cmake --build build
|
||||||
|
|
||||||
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
|
|
||||||
make
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# The check function is executed in the source directory
|
# The check function is executed in the source directory
|
||||||
# This function is used to run tests to verify the package has been compiled correctly
|
# This function is used to run tests to verify the package has been compiled correctly
|
||||||
check() {
|
check() {
|
||||||
cd build
|
ctest --test-dir build
|
||||||
|
|
||||||
make test
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# The package function is executed in the source directory
|
# The package function is executed in the source directory
|
||||||
# This function is used to move the compiled files into the output directory
|
# This function is used to move the compiled files into the output directory
|
||||||
package() {
|
package() {
|
||||||
cd build
|
DESTDIR="$BPM_OUTPUT" cmake --install build
|
||||||
|
|
||||||
make DESTDIR="$BPM_OUTPUT" install
|
|
||||||
|
|
||||||
# Install package license
|
# Install package license
|
||||||
install -Dm644 "$BPM_SOURCE"/LICENSE "$BPM_OUTPUT"/usr/share/licenses/$NAME/LICENSE
|
install -Dm644 "$BPM_SOURCE"/LICENSE "$BPM_OUTPUT"/usr/share/licenses/$NAME/LICENSE
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
# This is the source.sh script. It is executed by BPM in a temporary directory when compiling a source package
|
# This is the recipe.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 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
|
# 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
|
# 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
|
# This function is used for putting downloaded files to the correct location or applying patches
|
||||||
prepare() {
|
prepare() {
|
||||||
wget "$DOWNLOAD"
|
echo "You may remove this function if you do not intend to use it"
|
||||||
tar -xvf "$FILENAME" --strip-components=1 -C "$BPM_SOURCE"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# The build function is executed in the source directory
|
# The build function is executed in the source directory
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
# This is the source.sh script. It is executed by BPM in a temporary directory when compiling a source package
|
# This is the recipe.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 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
|
# 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
|
# 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
|
# This function is used for putting downloaded files to the correct location or applying patches
|
||||||
prepare() {
|
prepare() {
|
||||||
wget "$DOWNLOAD"
|
echo "You may remove this function if you do not intend to use it"
|
||||||
tar -xvf "$FILENAME" --strip-components=1 -C "$BPM_SOURCE"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# The build function is executed in the source directory
|
# The build function is executed in the source directory
|
||||||
|
|||||||
+7
-18
@@ -1,41 +1,30 @@
|
|||||||
# This is the source.sh script. It is executed by BPM in a temporary directory when compiling a source package
|
# This is the recipe.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 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
|
# 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
|
# 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
|
# This function is used for putting downloaded files to the correct location or applying patches
|
||||||
prepare() {
|
prepare() {
|
||||||
wget "$DOWNLOAD"
|
echo "You may remove this function if you do not intend to use it"
|
||||||
tar -xvf "$FILENAME" --strip-components=1 -C "$BPM_SOURCE"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# The build function is executed in the source directory
|
# The build function is executed in the source directory
|
||||||
# This function is used to compile the source code
|
# This function is used to compile the source code
|
||||||
build() {
|
build() {
|
||||||
mkdir build
|
meson setup build --prefix=/usr
|
||||||
cd build
|
meson compile -C build
|
||||||
|
|
||||||
meson setup --prefix=/usr ..
|
|
||||||
meson compile
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# The check function is executed in the source directory
|
# The check function is executed in the source directory
|
||||||
# This function is used to run tests to verify the package has been compiled correctly
|
# This function is used to run tests to verify the package has been compiled correctly
|
||||||
check() {
|
check() {
|
||||||
cd build
|
meson test -C build --print-errorlogs
|
||||||
|
|
||||||
meson test
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# The package function is executed in the source directory
|
# The package function is executed in the source directory
|
||||||
# This function is used to move the compiled files into the output directory
|
# This function is used to move the compiled files into the output directory
|
||||||
package() {
|
package() {
|
||||||
cd build
|
meson install -C build --destdir="$BPM_OUTPUT"
|
||||||
|
|
||||||
meson install --destdir="$BPM_OUTPUT"
|
|
||||||
|
|
||||||
# Install package license
|
# Install package license
|
||||||
install -Dm644 "$BPM_SOURCE"/LICENSE "$BPM_OUTPUT"/usr/share/licenses/$NAME/LICENSE
|
install -Dm644 "$BPM_SOURCE"/LICENSE "$BPM_OUTPUT"/usr/share/licenses/$NAME/LICENSE
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
# This is the source.sh script. It is executed by BPM in a temporary directory when compiling a source package
|
# This is the recipe.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 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
|
# 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
|
# 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
|
# This function is used for putting downloaded files to the correct location or applying patches
|
||||||
prepare() {
|
prepare() {
|
||||||
wget "$DOWNLOAD"
|
echo "You may remove this function if you do not intend to use it"
|
||||||
tar -xvf "$FILENAME" --strip-components=1 -C "$BPM_SOURCE"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# The build function is executed in the source directory
|
# The build function is executed in the source directory
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# This is the recipe.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
|
||||||
|
|
||||||
|
# The prepare function is executed in the root of the temp directory
|
||||||
|
# This function is used for putting downloaded files to the correct location or applying patches
|
||||||
|
prepare() {
|
||||||
|
cd "$BPM_SOURCE"
|
||||||
|
cargo fetch --locked --target "$(rustc --print host-tuple)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# The build function is executed in the source directory
|
||||||
|
# This function is used to compile the source code
|
||||||
|
build() {
|
||||||
|
cargo build --release --locked --frozen
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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() {
|
||||||
|
cargo test --release --frozen
|
||||||
|
}
|
||||||
|
|
||||||
|
# The package function is executed in the source directory
|
||||||
|
# This function is used to move the compiled files into the output directory
|
||||||
|
package() {
|
||||||
|
# Cargo built packages are not usually packaged manually and not with an 'install' subcommand
|
||||||
|
|
||||||
|
# Install package license
|
||||||
|
install -Dm644 "$BPM_SOURCE"/LICENSE "$BPM_OUTPUT"/usr/share/licenses/$NAME/LICENSE
|
||||||
|
}
|
||||||
@@ -8,4 +8,9 @@ require (
|
|||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/drone/envsubst v1.0.3 // indirect
|
||||||
|
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f // indirect
|
||||||
|
)
|
||||||
|
|
||||||
replace bpm-utils-shared => ../bpm-utils-shared
|
replace bpm-utils-shared => ../bpm-utils-shared
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
|
||||||
|
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
|
||||||
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g=
|
||||||
|
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
|
||||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
|||||||
+241
-62
@@ -2,8 +2,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
bpmutilsshared "bpm-utils-shared"
|
bpmutilsshared "bpm-utils-shared"
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"maps"
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
@@ -11,6 +13,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
flag "github.com/spf13/pflag"
|
flag "github.com/spf13/pflag"
|
||||||
@@ -18,16 +21,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var compile = flag.BoolP("compile", "c", false, "Compile BPM source package")
|
var compile = flag.BoolP("compile", "c", false, "Compile BPM source package")
|
||||||
var skipCheck = flag.BoolP("skip-checks", "s", false, "Skip 'check' function while compiling")
|
var verbose = flag.BoolP("verbose", "v", false, "Show additional information about the compilation process")
|
||||||
|
var skipChecks = flag.Bool("skip-checks", false, "Skip 'check' function while compiling")
|
||||||
|
var keepCompilationFiles = flag.BoolP("keep", "k", false, "Keep compilation files after successful package compilation")
|
||||||
var installDepends = flag.BoolP("depends", "d", false, "Install package dependencies for compilation")
|
var installDepends = flag.BoolP("depends", "d", false, "Install package dependencies for compilation")
|
||||||
|
var force = flag.BoolP("force", "f", false, "Bypass warnings during package compilation")
|
||||||
var installPackage = flag.BoolP("install", "i", false, "Install compiled BPM package after compilation finishes")
|
var installPackage = flag.BoolP("install", "i", false, "Install compiled BPM package after compilation finishes")
|
||||||
var moveToBinaryDir = flag.BoolP("move", "m", false, "Move output packages to the current repository's binary directory")
|
var compilationJobs = flag.IntP("jobs", "j", 0, "Set the amount of concurrent processes to use for source package compilation")
|
||||||
|
var updateInfo = flag.BoolP("update-info", "u", false, "Update the info.yml file")
|
||||||
|
var signPackage = flag.BoolP("sign", "s", false, "Sign package using GPG")
|
||||||
var yesAll = flag.BoolP("yes", "y", false, "Accept all confirmation prompts")
|
var yesAll = flag.BoolP("yes", "y", false, "Accept all confirmation prompts")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Setup flags and help
|
// Setup flags and help
|
||||||
bpmutilsshared.SetupHelp("bpm-package <options>", "Generates source BPM package from current directory")
|
setupFlagsAndHelp("bpm-package <options>", "Generates source BPM package from current directory")
|
||||||
bpmutilsshared.SetupFlags()
|
|
||||||
|
|
||||||
// Run checks
|
// Run checks
|
||||||
runChecks()
|
runChecks()
|
||||||
@@ -38,25 +45,35 @@ func main() {
|
|||||||
if *compile {
|
if *compile {
|
||||||
compilePackage(outputFile)
|
compilePackage(outputFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if repo := bpmutilsshared.GetRepository(); repo != "" {
|
||||||
|
bpmutilsshared.UpdateDatabases(repo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runChecks() {
|
func runChecks() {
|
||||||
// Check if pkg.info file exists
|
// Check if info.yml file exists
|
||||||
if stat, err := os.Stat("pkg.info"); err != nil || !stat.Mode().IsRegular() {
|
if stat, err := os.Stat("info.yml"); err != nil || !stat.Mode().IsRegular() {
|
||||||
log.Fatalf("Error: pkg.info does not exist or is not a regular file")
|
log.Fatalf("Error: info.yml does not exist or is not a regular file")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if source.sh file exists
|
// Check if recipe.sh file exists
|
||||||
if stat, err := os.Stat("source.sh"); err != nil || !stat.Mode().IsRegular() {
|
if stat, err := os.Stat("recipe.sh"); err != nil || !stat.Mode().IsRegular() {
|
||||||
log.Fatalf("Error: pkg.info does not exist or is not a regular file")
|
log.Fatalf("Error: info.yml does not exist or is not a regular file")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createArchive() string {
|
func createArchive() string {
|
||||||
|
// Read BPM utils config
|
||||||
|
config, err := bpmutilsshared.ReadBPMUtilsConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: failed to read config: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
filesToInclude := make([]string, 0)
|
filesToInclude := make([]string, 0)
|
||||||
|
|
||||||
// Include base files
|
// Include base files
|
||||||
filesToInclude = append(filesToInclude, "pkg.info", "source.sh")
|
filesToInclude = append(filesToInclude, "info.yml", "recipe.sh")
|
||||||
|
|
||||||
// Check if non-empty source-files directory exists and include it
|
// Check if non-empty source-files directory exists and include it
|
||||||
if stat, err := os.Stat("source-files"); err == nil && stat.IsDir() {
|
if stat, err := os.Stat("source-files"); err == nil && stat.IsDir() {
|
||||||
@@ -70,49 +87,89 @@ func createArchive() string {
|
|||||||
// Check for package scripts and include them
|
// 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"} {
|
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() {
|
if stat, err := os.Stat(script); err == nil && stat.Mode().IsRegular() {
|
||||||
fmt.Printf("Package script '%s' found", script)
|
fmt.Printf("Package script '%s' found\n", script)
|
||||||
filesToInclude = append(filesToInclude, script)
|
filesToInclude = append(filesToInclude, script)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read pkg.info file into basic struct
|
// Read info.yml file
|
||||||
pkgInfo := struct {
|
pkgInfo, err := bpmutilsshared.ReadPacakgeInfoFromFile("info.yml")
|
||||||
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 {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not read pkg.info file")
|
log.Fatalf("Error: could not read package info: %s", err)
|
||||||
}
|
|
||||||
err = yaml.Unmarshal(data, &pkgInfo)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not unmarshal pkg.info file")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove old BPM archives in current directory
|
// Update info.yml file
|
||||||
oldArchives, err := filepath.Glob("*.bpm")
|
if *updateInfo {
|
||||||
if err != nil {
|
// Update download checksums
|
||||||
log.Printf("Warning: could not search for old BPM archives: %s", err)
|
for i, download := range pkgInfo.Downloads {
|
||||||
}
|
if download.Checksum == "skip" {
|
||||||
for _, bpmArchive := range oldArchives {
|
continue
|
||||||
err = os.Remove(bpmArchive)
|
}
|
||||||
|
|
||||||
|
download.Checksum, err = download.CalculateChecksum(pkgInfo)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not calculate checksum for download entry %d: %s", i+1, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgInfo.Downloads[i] = download
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add default maintainer
|
||||||
|
if config.AddDefaultMaintainer {
|
||||||
|
if !slices.Contains(pkgInfo.Maintainers, config.DefaultMaintainer) {
|
||||||
|
pkgInfo.Maintainers = append(pkgInfo.Maintainers, config.DefaultMaintainer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save yaml back to file
|
||||||
|
var data bytes.Buffer
|
||||||
|
encoder := yaml.NewEncoder(&data)
|
||||||
|
encoder.SetIndent(2)
|
||||||
|
encoder.Encode(pkgInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Warning: could not remove old BPM archive (%s): %s", bpmArchive, err)
|
log.Fatalf("Could not marshal package info: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat info.yml
|
||||||
|
stat, err := os.Stat("info.yml")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not stat info.yml: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write package info back to file
|
||||||
|
err = os.WriteFile("info.yml", data.Bytes(), stat.Mode().Perm())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not write package info to info.yml: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove old package from source dir
|
||||||
|
if repo := bpmutilsshared.GetRepository(); repo != "" {
|
||||||
|
removedFiles, err := RemoveSourcePackage(repo, pkgInfo.Name)
|
||||||
|
for _, file := range removedFiles {
|
||||||
|
fmt.Println("Removed " + file)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Warning: could not remove old source packages: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create filename
|
// Create filename
|
||||||
filename := fmt.Sprintf("%s-%s-%d-%s-src.bpm", pkgInfo.Name, pkgInfo.Version, pkgInfo.Revision, pkgInfo.Arch)
|
filename := fmt.Sprintf("%s-%s-%d-%s-src.bpm", pkgInfo.Name, pkgInfo.Version, pkgInfo.Revision, pkgInfo.Arch)
|
||||||
|
if repo := bpmutilsshared.GetRepository(); repo != "" {
|
||||||
|
// Create source directory
|
||||||
|
os.MkdirAll(path.Join(repo, "source", pkgInfo.Arch), 0755)
|
||||||
|
|
||||||
|
filename = path.Join(repo, "source", pkgInfo.Arch, filename)
|
||||||
|
}
|
||||||
|
|
||||||
// Create archive using tar
|
// Create archive using tar
|
||||||
args := make([]string, 0)
|
cmd := exec.Command("tar", "cf", filename,
|
||||||
args = append(args, "-c", "--owner=0", "--group=0", "--no-same-owner", "-f", filename)
|
"--sort=name",
|
||||||
args = append(args, filesToInclude...)
|
"--pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime",
|
||||||
cmd := exec.Command("tar", args...)
|
"--mtime=UTC 1970-01-01",
|
||||||
|
"--owner=0", "--group=0", "--numeric-owner")
|
||||||
|
cmd.Args = append(cmd.Args, filesToInclude...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
@@ -120,6 +177,19 @@ func createArchive() string {
|
|||||||
log.Fatalf("Error: failed to create BPM source archive: %s", err)
|
log.Fatalf("Error: failed to create BPM source archive: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sign package
|
||||||
|
if *signPackage {
|
||||||
|
cmd := exec.Command("gpg", "--yes", "--detach-sign", filename)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not sign package: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get absolute path to filename
|
// Get absolute path to filename
|
||||||
absFilepath, err := filepath.Abs(filename)
|
absFilepath, err := filepath.Abs(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -134,16 +204,19 @@ func compilePackage(archive string) {
|
|||||||
// Setup compile command
|
// Setup compile command
|
||||||
args := make([]string, 0)
|
args := make([]string, 0)
|
||||||
args = append(args, "compile")
|
args = append(args, "compile")
|
||||||
if *skipCheck {
|
addArg := func(condition bool, arg string) {
|
||||||
args = append(args, "-s")
|
if condition {
|
||||||
|
args = append(args, arg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if *installDepends {
|
addArg(*verbose, "-v")
|
||||||
args = append(args, "-d")
|
addArg(*skipChecks, "--skip-checks")
|
||||||
}
|
addArg(*keepCompilationFiles, "-k")
|
||||||
if *yesAll {
|
addArg(*installDepends, "-d")
|
||||||
args = append(args, "-y")
|
addArg(*compilationJobs > 0, "-j"+strconv.Itoa(*compilationJobs))
|
||||||
}
|
addArg(*yesAll, "-y")
|
||||||
args = append(args, "--fd=3")
|
addArg(*force, "-f")
|
||||||
|
args = append(args, "--output-fd=3")
|
||||||
args = append(args, archive)
|
args = append(args, archive)
|
||||||
cmd := exec.Command("bpm", args...)
|
cmd := exec.Command("bpm", args...)
|
||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
@@ -182,35 +255,49 @@ func compilePackage(archive string) {
|
|||||||
outputPkgs := make(map[string]string)
|
outputPkgs := make(map[string]string)
|
||||||
for _, line := range strings.Split(strings.TrimSpace(string(cmdOutput)), "\n") {
|
for _, line := range strings.Split(strings.TrimSpace(string(cmdOutput)), "\n") {
|
||||||
// Read generated package info
|
// Read generated package info
|
||||||
pkgInfo, err := bpmutilsshared.ReadPacakgeInfo(line)
|
pkgInfo, err := bpmutilsshared.ReadPacakgeInfoFromTarball(line)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not read package info: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
if repo := bpmutilsshared.GetRepository(); repo != "" && *moveToBinaryDir {
|
if repo := bpmutilsshared.GetRepository(); repo != "" {
|
||||||
// Remove old package from binary dir
|
// Remove old package from binary dir
|
||||||
if database, err := bpmutilsshared.ReadDatabase(path.Join(repo, "binary/database.bpmdb")); err == nil {
|
|
||||||
if entry, ok := database.Entries[pkgInfo.Name]; ok {
|
removedFiles, err := RemoveBinaryPackage(repo, pkgInfo.Name)
|
||||||
pkgFilepath := path.Join(repo, "binary", entry.Filepath)
|
for _, file := range removedFiles {
|
||||||
err := os.Remove(pkgFilepath)
|
fmt.Println("Removed " + file)
|
||||||
if err != nil {
|
}
|
||||||
log.Printf("Warning: could not remove old binary package (%s): %s", pkgFilepath, err)
|
if err != nil {
|
||||||
}
|
log.Printf("Warning: could not remove old binary packages: %s", err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move package to binary dir
|
// Move package to binary dir
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not read package info: %s", err)
|
|
||||||
}
|
|
||||||
newPath := path.Join(repo, "binary", pkgInfo.Arch, path.Base(line))
|
newPath := path.Join(repo, "binary", pkgInfo.Arch, path.Base(line))
|
||||||
os.MkdirAll(path.Dir(newPath), 0755)
|
os.MkdirAll(path.Dir(newPath), 0755)
|
||||||
os.Rename(line, newPath)
|
os.Rename(line, newPath)
|
||||||
outputPkgs[pkgInfo.Name] = newPath
|
outputPkgs[pkgInfo.Name] = newPath
|
||||||
|
|
||||||
bpmutilsshared.UpdateDatabases(repo)
|
|
||||||
} else {
|
} else {
|
||||||
outputPkgs[pkgInfo.Name] = line
|
outputPkgs[pkgInfo.Name] = line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sign package
|
||||||
|
if *signPackage {
|
||||||
|
for k, v := range outputPkgs {
|
||||||
|
cmd := exec.Command("gpg", "--yes", "--detach-sign", v)
|
||||||
|
cmd.Dir = path.Dir(v)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not sign package (%s) at: %s", k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Print out generated packages
|
// Print out generated packages
|
||||||
for k, v := range outputPkgs {
|
for k, v := range outputPkgs {
|
||||||
fmt.Printf("Package (%s) was successfully compiled! Binary package generated at: %s\n", k, v)
|
fmt.Printf("Package (%s) was successfully compiled! Binary package generated at: %s\n", k, v)
|
||||||
@@ -244,3 +331,95 @@ func compilePackage(archive string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupFlagsAndHelp(usage, desc string) {
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Println("Usage: " + usage)
|
||||||
|
fmt.Println("Description: " + desc)
|
||||||
|
fmt.Println("Options:")
|
||||||
|
flag.PrintDefaults()
|
||||||
|
}
|
||||||
|
flag.Parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveSourcePackage(repo, pkgName string) (removedFiles []string, err error) {
|
||||||
|
if _, err := os.Stat(path.Join(repo, "source")); err != nil {
|
||||||
|
return removedFiles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = filepath.Walk(path.Join(repo, "source"), func(packagePath string, info fs.FileInfo, err error) error {
|
||||||
|
if !strings.HasSuffix(packagePath, ".bpm") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get package info
|
||||||
|
pkgInfo, err := bpmutilsshared.ReadPacakgeInfoFromTarball(packagePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgInfo.Name != pkgName {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove package and its signature
|
||||||
|
err = os.Remove(packagePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if relPath, err := filepath.Rel(repo, packagePath); err != nil {
|
||||||
|
removedFiles = append(removedFiles, packagePath)
|
||||||
|
} else {
|
||||||
|
removedFiles = append(removedFiles, relPath)
|
||||||
|
}
|
||||||
|
err = os.Remove(packagePath + ".sig")
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return removedFiles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveBinaryPackage(repo, pkgName string) (removedFiles []string, err error) {
|
||||||
|
if _, err := os.Stat(path.Join(repo, "binary")); err != nil {
|
||||||
|
return removedFiles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = filepath.Walk(path.Join(repo, "binary"), func(packagePath string, info fs.FileInfo, err error) error {
|
||||||
|
if !strings.HasSuffix(packagePath, ".bpm") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get package info
|
||||||
|
pkgInfo, err := bpmutilsshared.ReadPacakgeInfoFromTarball(packagePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgInfo.Name != pkgName {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove package and its signature
|
||||||
|
err = os.Remove(packagePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if relPath, err := filepath.Rel(repo, packagePath); err != nil {
|
||||||
|
removedFiles = append(removedFiles, packagePath)
|
||||||
|
} else {
|
||||||
|
removedFiles = append(removedFiles, relPath)
|
||||||
|
}
|
||||||
|
err = os.Remove(packagePath + ".sig")
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return removedFiles, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,4 +8,9 @@ require (
|
|||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/drone/envsubst v1.0.3 // indirect
|
||||||
|
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f // indirect
|
||||||
|
)
|
||||||
|
|
||||||
replace bpm-utils-shared => ../bpm-utils-shared
|
replace bpm-utils-shared => ../bpm-utils-shared
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
|
||||||
|
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
|
||||||
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g=
|
||||||
|
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
|
||||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
|||||||
+504
-71
@@ -3,6 +3,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
bpmutilsshared "bpm-utils-shared"
|
bpmutilsshared "bpm-utils-shared"
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
@@ -13,6 +15,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -20,20 +23,23 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createRepo = flag.BoolP("create", "c", false, "Create a new BPM repository")
|
var currentFlagSet *flag.FlagSet
|
||||||
var updateDatabases = flag.BoolP("update-databases", "u", false, "Update update source and binary databases in current repository")
|
|
||||||
var checkVersions = flag.BoolP("check-versions", "v", false, "Get the latest version of each package")
|
|
||||||
var listPackages = flag.BoolP("list", "l", false, "List packages")
|
|
||||||
|
|
||||||
var verbose = flag.Bool("verbose", false, "Show additional information about current operation")
|
|
||||||
var force = flag.BoolP("force", "f", false, "Force current operation to bypass certain conditions")
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Setup flags and help
|
if len(os.Args) < 2 {
|
||||||
bpmutilsshared.SetupHelp("bpm-repo <options>", "Manage BPM repositories and databases")
|
log.Println("Error: no subcommand")
|
||||||
bpmutilsshared.SetupFlags()
|
listSubcommands()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
subcommand := os.Args[1]
|
||||||
|
|
||||||
|
switch subcommand {
|
||||||
|
case "create-repo", "c":
|
||||||
|
// Setup flags and help
|
||||||
|
flagset := flag.NewFlagSet("create-repo", flag.ExitOnError)
|
||||||
|
setupFlagsAndHelp(flagset, fmt.Sprintf("bpm-repo %s <options>", subcommand), "Create a new BPM repository", os.Args[:1])
|
||||||
|
|
||||||
if *createRepo {
|
|
||||||
// Get current database
|
// Get current database
|
||||||
repo := bpmutilsshared.GetRepository()
|
repo := bpmutilsshared.GetRepository()
|
||||||
if repo != "" {
|
if repo != "" {
|
||||||
@@ -53,7 +59,11 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createRepository(strings.TrimSpace(name), strings.TrimSpace(desc))
|
createRepository(strings.TrimSpace(name), strings.TrimSpace(desc))
|
||||||
} else if *updateDatabases {
|
case "update-db", "u":
|
||||||
|
// Setup flags and help
|
||||||
|
flagset := flag.NewFlagSet("update-db", flag.ExitOnError)
|
||||||
|
setupFlagsAndHelp(flagset, fmt.Sprintf("bpm-repo %s <options>", subcommand), "Update update source and binary databases in current repository", os.Args[:1])
|
||||||
|
|
||||||
// Get current database
|
// Get current database
|
||||||
repo := bpmutilsshared.GetRepository()
|
repo := bpmutilsshared.GetRepository()
|
||||||
if repo == "" {
|
if repo == "" {
|
||||||
@@ -61,15 +71,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bpmutilsshared.UpdateDatabases(repo)
|
bpmutilsshared.UpdateDatabases(repo)
|
||||||
} else if *checkVersions {
|
case "list", "l":
|
||||||
// Get current database
|
flagset := flag.NewFlagSet("list", flag.ExitOnError)
|
||||||
repo := bpmutilsshared.GetRepository()
|
setupFlagsAndHelp(flagset, fmt.Sprintf("bpm-repo %s <options>", subcommand), "List packages", os.Args[:1])
|
||||||
if repo == "" {
|
|
||||||
log.Fatal("Error: this command may only be run inside a BPM repository")
|
|
||||||
}
|
|
||||||
|
|
||||||
checkVersionsFunc(repo)
|
|
||||||
} else if *listPackages {
|
|
||||||
// Get current database
|
// Get current database
|
||||||
repo := bpmutilsshared.GetRepository()
|
repo := bpmutilsshared.GetRepository()
|
||||||
if repo == "" {
|
if repo == "" {
|
||||||
@@ -77,8 +82,56 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
listPackagesFunc(repo)
|
listPackagesFunc(repo)
|
||||||
} else {
|
case "check-versions", "v":
|
||||||
bpmutilsshared.ShowHelp()
|
// Setup flags and help
|
||||||
|
flagset := flag.NewFlagSet("check-versions", flag.ExitOnError)
|
||||||
|
flagset.BoolP("verbose", "v", false, "Show additional information about the current operation")
|
||||||
|
flagset.BoolP("force", "f", false, "Force current operation to bypass certain conditions")
|
||||||
|
flagset.BoolP("apply", "a", false, "Apply new versions to packages")
|
||||||
|
setupFlagsAndHelp(flagset, fmt.Sprintf("bpm-repo %s <options>", subcommand), "Manage BPM repositories and databases", os.Args[2:])
|
||||||
|
currentFlagSet = flagset
|
||||||
|
|
||||||
|
// Get current database
|
||||||
|
repo := bpmutilsshared.GetRepository()
|
||||||
|
if repo == "" {
|
||||||
|
log.Fatal("Error: this command may only be run inside a BPM repository")
|
||||||
|
}
|
||||||
|
|
||||||
|
checkVersionsFunc(repo)
|
||||||
|
case "hold", "h":
|
||||||
|
// Setup flags and help
|
||||||
|
flagset := flag.NewFlagSet("hold", flag.ExitOnError)
|
||||||
|
flagset.Bool("get", false, "Show current value")
|
||||||
|
setupFlagsAndHelp(flagset, fmt.Sprintf("bpm-repo %s <options>", subcommand), "Manage BPM repositories and databases", os.Args[2:])
|
||||||
|
currentFlagSet = flagset
|
||||||
|
|
||||||
|
// Get current database
|
||||||
|
repo := bpmutilsshared.GetRepository()
|
||||||
|
if repo == "" {
|
||||||
|
log.Fatal("Error: this command may only be run inside a BPM repository")
|
||||||
|
}
|
||||||
|
|
||||||
|
holdPackage(repo)
|
||||||
|
case "compile-all", "a":
|
||||||
|
// Setup flags and help
|
||||||
|
flagset := flag.NewFlagSet("check-versions", flag.ExitOnError)
|
||||||
|
flagset.BoolP("verbose", "v", false, "Show additional information about the current operation")
|
||||||
|
flagset.BoolP("modified", "m", true, "Skip non-modified source packages")
|
||||||
|
flagset.BoolP("show-order", "o", false, "Show the order in which all packages will be compiled and exit")
|
||||||
|
setupFlagsAndHelp(flagset, fmt.Sprintf("bpm-repo %s <options>", subcommand), "Manage BPM repositories and databases", os.Args[2:])
|
||||||
|
currentFlagSet = flagset
|
||||||
|
|
||||||
|
// Get current database
|
||||||
|
repo := bpmutilsshared.GetRepository()
|
||||||
|
if repo == "" {
|
||||||
|
log.Fatal("Error: this command may only be run inside a BPM repository")
|
||||||
|
}
|
||||||
|
|
||||||
|
compileAllPackagesFunc(repo)
|
||||||
|
default:
|
||||||
|
log.Println("Error: unknown subcommand")
|
||||||
|
listSubcommands()
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,10 +147,20 @@ func createRepository(name, description string) {
|
|||||||
log.Fatalf("Error: could not write to file: %s", err)
|
log.Fatalf("Error: could not write to file: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = os.Mkdir(path.Join(name, "recipes"), 0755)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not create directory: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println("Repository created successfully!")
|
fmt.Println("Repository created successfully!")
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkVersionsFunc(repo string) {
|
func checkVersionsFunc(repo string) {
|
||||||
|
// Get flags
|
||||||
|
verbose, _ := currentFlagSet.GetBool("verbose")
|
||||||
|
force, _ := currentFlagSet.GetBool("force")
|
||||||
|
apply, _ := currentFlagSet.GetBool("apply")
|
||||||
|
|
||||||
// Read environment files
|
// Read environment files
|
||||||
err := readEnvFile(repo)
|
err := readEnvFile(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -108,6 +171,7 @@ func checkVersionsFunc(repo string) {
|
|||||||
type CachedVersionEntry struct {
|
type CachedVersionEntry struct {
|
||||||
LatestVersion string `yaml:"latest_version"`
|
LatestVersion string `yaml:"latest_version"`
|
||||||
Timestamp int64 `yaml:"timestamp"`
|
Timestamp int64 `yaml:"timestamp"`
|
||||||
|
OnHold bool `yaml:"on_hold,omitempty"`
|
||||||
}
|
}
|
||||||
cachedVersions := make(map[string]CachedVersionEntry)
|
cachedVersions := make(map[string]CachedVersionEntry)
|
||||||
data, err := os.ReadFile(path.Join(repo, ".version-cache"))
|
data, err := os.ReadFile(path.Join(repo, ".version-cache"))
|
||||||
@@ -119,17 +183,17 @@ func checkVersionsFunc(repo string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
directories := make([]string, 0)
|
directories := make([]string, 0)
|
||||||
if flag.NArg() > 0 {
|
if currentFlagSet.NArg() > 0 {
|
||||||
for _, dir := range flag.Args() {
|
for _, dir := range currentFlagSet.Args() {
|
||||||
if _, err := os.Stat(path.Join(repo, "source", dir, "pkg.info")); err != nil {
|
if _, err := os.Stat(path.Join(repo, "recipes", dir, "info.yml")); err != nil {
|
||||||
log.Fatalf("Error: could not find pkg.info file in directory (%s): %s", dir, err)
|
log.Fatalf("Error: could not find info.yml file in directory (%s): %s", dir, err)
|
||||||
}
|
}
|
||||||
directories = append(directories, path.Join(repo, "source", dir))
|
directories = append(directories, path.Join(repo, "recipes", dir))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Loop through each directory with a 'pkg.info' file
|
// Loop through each directory with a 'info.yml' file
|
||||||
err := filepath.Walk(path.Join(repo, "source"), func(path string, info fs.FileInfo, err error) error {
|
err := filepath.Walk(path.Join(repo, "recipes"), func(path string, info fs.FileInfo, err error) error {
|
||||||
if filepath.Base(path) == "pkg.info" {
|
if filepath.Base(path) == "info.yml" {
|
||||||
directories = append(directories, filepath.Dir(path))
|
directories = append(directories, filepath.Dir(path))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -140,6 +204,7 @@ func checkVersionsFunc(repo string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pkgsWithoutScript := make([]string, 0)
|
pkgsWithoutScript := make([]string, 0)
|
||||||
|
pkgsIgnored := make([]string, 0)
|
||||||
pkgsWithError := make(map[string]error)
|
pkgsWithError := make(map[string]error)
|
||||||
pkgsWithUpdates := make(map[string]struct {
|
pkgsWithUpdates := make(map[string]struct {
|
||||||
OldVersion string
|
OldVersion string
|
||||||
@@ -147,14 +212,18 @@ func checkVersionsFunc(repo string) {
|
|||||||
}, 0)
|
}, 0)
|
||||||
pkgsUpToDate := 0
|
pkgsUpToDate := 0
|
||||||
for _, dir := range directories {
|
for _, dir := range directories {
|
||||||
pkgInfo, err := bpmutilsshared.ReadPacakgeInfoFromFile(path.Join(dir, "pkg.info"))
|
pkgInfo, err := bpmutilsshared.ReadPacakgeInfoFromFile(path.Join(dir, "info.yml"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Could not read package info: %s", err)
|
log.Fatalf("Could not read package info: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
fmt.Printf("Checking version for package (%s)...\n", pkgInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
// Check cached latest version
|
// Check cached latest version
|
||||||
latestVersion := ""
|
latestVersion := ""
|
||||||
if cachedVersion, ok := cachedVersions[pkgInfo.Name]; ok && !*force && time.Since(time.UnixMilli(cachedVersion.Timestamp)).Milliseconds() < 604800000 {
|
if cachedVersion, ok := cachedVersions[pkgInfo.Name]; ok && !force && time.Since(time.UnixMilli(cachedVersion.Timestamp)).Milliseconds() < 604800000 {
|
||||||
latestVersion = cachedVersion.LatestVersion
|
latestVersion = cachedVersion.LatestVersion
|
||||||
} else {
|
} else {
|
||||||
// Check whether check-version.sh script exists
|
// Check whether check-version.sh script exists
|
||||||
@@ -164,7 +233,9 @@ func checkVersionsFunc(repo string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute check-version.sh script
|
// Execute check-version.sh script
|
||||||
cmd := exec.Command("bash", "-e", path.Join(dir, "check-version.sh"))
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
cmd := exec.CommandContext(ctx, "bash", "-e", path.Join(dir, "check-version.sh"))
|
||||||
cmd.Environ()
|
cmd.Environ()
|
||||||
output, err := cmd.Output()
|
output, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -173,16 +244,33 @@ func checkVersionsFunc(repo string) {
|
|||||||
}
|
}
|
||||||
latestVersion = strings.TrimSpace(string(output))
|
latestVersion = strings.TrimSpace(string(output))
|
||||||
|
|
||||||
|
// Check if package should be ignored
|
||||||
|
if latestVersion == "ignore" {
|
||||||
|
pkgsIgnored = append(pkgsIgnored, pkgInfo.Name)
|
||||||
|
|
||||||
|
// Remove cached status
|
||||||
|
delete(cachedVersions, pkgInfo.Name)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure latest version is valid
|
// Ensure latest version is valid
|
||||||
if latestVersion == "" || latestVersion == "null" {
|
if latestVersion == "" || latestVersion == "null" {
|
||||||
pkgsWithError[pkgInfo.Name] = fmt.Errorf("invalid version number \"%s\"", latestVersion)
|
pkgsWithError[pkgInfo.Name] = fmt.Errorf("invalid version number \"%s\"", latestVersion)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get current on_hold value
|
||||||
|
onHold := false
|
||||||
|
if v, ok := cachedVersions[pkgInfo.Name]; ok {
|
||||||
|
onHold = v.OnHold
|
||||||
|
}
|
||||||
|
|
||||||
// Cache latest version
|
// Cache latest version
|
||||||
cachedVersions[pkgInfo.Name] = CachedVersionEntry{
|
cachedVersions[pkgInfo.Name] = CachedVersionEntry{
|
||||||
LatestVersion: latestVersion,
|
LatestVersion: latestVersion,
|
||||||
Timestamp: time.Now().UnixMilli(),
|
Timestamp: time.Now().UnixMilli(),
|
||||||
|
OnHold: onHold,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,6 +283,31 @@ func checkVersionsFunc(repo string) {
|
|||||||
OldVersion: pkgInfo.Version,
|
OldVersion: pkgInfo.Version,
|
||||||
NewVersion: latestVersion,
|
NewVersion: latestVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply new version
|
||||||
|
pkgInfo.Version = latestVersion
|
||||||
|
pkgInfo.Revision = 1
|
||||||
|
if apply && !cachedVersions[pkgInfo.Name].OnHold {
|
||||||
|
var data bytes.Buffer
|
||||||
|
encoder := yaml.NewEncoder(&data)
|
||||||
|
encoder.SetIndent(2)
|
||||||
|
encoder.Encode(pkgInfo)
|
||||||
|
|
||||||
|
// Write package information
|
||||||
|
err := os.WriteFile(path.Join(dir, "info.yml"), data.Bytes(), 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Warning: could not write new version for package (%s) to file: %s", pkgInfo.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate source package
|
||||||
|
cmd := exec.Command("bpm-package")
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Dir = dir
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
log.Printf("Warning: could not generate source pacakge (%s): %s", pkgInfo.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,21 +327,34 @@ func checkVersionsFunc(repo string) {
|
|||||||
keys := slices.Collect(maps.Keys(pkgsWithUpdates))
|
keys := slices.Collect(maps.Keys(pkgsWithUpdates))
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
for _, pkg := range keys {
|
for _, pkg := range keys {
|
||||||
fmt.Printf("Update available for package (%s): %s -> %s\n", pkg, pkgsWithUpdates[pkg].OldVersion, pkgsWithUpdates[pkg].NewVersion)
|
if bpmutilsshared.CompareVersions(pkgsWithUpdates[pkg].NewVersion, pkgsWithUpdates[pkg].OldVersion) >= 0 {
|
||||||
|
fmt.Printf("Update available for package (%s): %s -> %s", pkg, pkgsWithUpdates[pkg].OldVersion, pkgsWithUpdates[pkg].NewVersion)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Downgrade(?) available for package (%s): %s -> %s", pkg, pkgsWithUpdates[pkg].OldVersion, pkgsWithUpdates[pkg].NewVersion)
|
||||||
|
}
|
||||||
|
if cachedVersions[pkg].OnHold {
|
||||||
|
fmt.Print(" (On hold)")
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
if *verbose {
|
if verbose {
|
||||||
|
// Print ignored packages
|
||||||
|
for _, pkg := range pkgsIgnored {
|
||||||
|
log.Printf("Warning: package (%s) was ignored\n", pkg)
|
||||||
|
}
|
||||||
|
|
||||||
// Print packages without check-version.sh script
|
// Print packages without check-version.sh script
|
||||||
for _, pkg := range pkgsWithoutScript {
|
for _, pkg := range pkgsWithoutScript {
|
||||||
log.Printf("Warning: package (%s) has no check-version.sh script\n", pkg)
|
log.Printf("Warning: package (%s) has no check-version.sh script\n", pkg)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Print errors
|
// Print errors
|
||||||
keys = slices.Collect(maps.Keys(pkgsWithError))
|
keys = slices.Collect(maps.Keys(pkgsWithError))
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
for _, pkg := range keys {
|
for _, pkg := range keys {
|
||||||
log.Printf("Error: check-version.sh script for package (%s) failed: %s", pkg, pkgsWithError[pkg])
|
log.Printf("Error: check-version.sh script for package (%s) failed: %s", pkg, pkgsWithError[pkg])
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print summary
|
// Print summary
|
||||||
@@ -236,48 +362,332 @@ func checkVersionsFunc(repo string) {
|
|||||||
fmt.Println("Available updates:", len(pkgsWithUpdates))
|
fmt.Println("Available updates:", len(pkgsWithUpdates))
|
||||||
fmt.Println("Up to date:", pkgsUpToDate)
|
fmt.Println("Up to date:", pkgsUpToDate)
|
||||||
fmt.Println("Missing script:", len(pkgsWithoutScript))
|
fmt.Println("Missing script:", len(pkgsWithoutScript))
|
||||||
|
fmt.Println("Ignored: ", len(pkgsIgnored))
|
||||||
fmt.Println("Errors:", len(pkgsWithError))
|
fmt.Println("Errors:", len(pkgsWithError))
|
||||||
}
|
}
|
||||||
|
|
||||||
func listPackagesFunc(repo string) {
|
func holdPackage(repo string) {
|
||||||
// Read databases
|
// Get flags
|
||||||
sourceDatabase, err := bpmutilsshared.ReadDatabase(path.Join(repo, "source/database.bpmdb"))
|
get, _ := currentFlagSet.GetBool("get")
|
||||||
if err != nil {
|
|
||||||
|
// Get package name
|
||||||
|
pkgName := ""
|
||||||
|
if len(currentFlagSet.Args()) < 1 {
|
||||||
|
log.Fatalf("Error: no package name set")
|
||||||
|
} else {
|
||||||
|
pkgName = currentFlagSet.Arg(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure package exists
|
||||||
|
if _, err := os.Stat(path.Join(repo, "recipes", pkgName, "info.yml")); err != nil {
|
||||||
|
log.Fatalf("Error: could not find info.yml file in directory (%s): %s", pkgName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read version cache
|
||||||
|
type CachedVersionEntry struct {
|
||||||
|
LatestVersion string `yaml:"latest_version"`
|
||||||
|
Timestamp int64 `yaml:"timestamp"`
|
||||||
|
OnHold bool `yaml:"on_hold,omitempty"`
|
||||||
|
}
|
||||||
|
cachedVersions := make(map[string]*CachedVersionEntry)
|
||||||
|
data, err := os.ReadFile(path.Join(repo, ".version-cache"))
|
||||||
|
if err == nil {
|
||||||
|
err := yaml.Unmarshal(data, &cachedVersions)
|
||||||
|
if err != nil {
|
||||||
|
cachedVersions = make(map[string]*CachedVersionEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current on_hold value
|
||||||
|
if get {
|
||||||
|
if cachedVersion, ok := cachedVersions[pkgName]; ok && cachedVersion.OnHold {
|
||||||
|
fmt.Printf("Package (%s) has been put on hold.\n", pkgName)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Package (%s) has not been put on hold.\n", pkgName)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
binaryDatabase, err := bpmutilsshared.ReadDatabase(path.Join(repo, "binary/database.bpmdb"))
|
// Get boolean value
|
||||||
if err != nil {
|
var value bool
|
||||||
binaryDatabase = &bpmutilsshared.BPMDatabase{}
|
if len(currentFlagSet.Args()) < 2 {
|
||||||
|
log.Fatalf("Error: no boolean value set")
|
||||||
|
} else {
|
||||||
|
value, err = strconv.ParseBool(currentFlagSet.Arg(1))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: value (%s) is not a boolean", currentFlagSet.Arg(1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort database entries
|
// Set on_hold value
|
||||||
entriesSorted := slices.Collect(maps.Values(sourceDatabase.Entries))
|
cachedVersions[pkgName].OnHold = value
|
||||||
sort.Slice(entriesSorted, func(i, j int) bool {
|
|
||||||
return entriesSorted[i].PackageInfo.Name < entriesSorted[j].PackageInfo.Name
|
|
||||||
})
|
|
||||||
|
|
||||||
// Loop through each entry
|
// Save cached versions to file
|
||||||
for _, entry := range entriesSorted {
|
data, err = yaml.Marshal(cachedVersions)
|
||||||
if len(entry.PackageInfo.SplitPackages) > 0 {
|
if err == nil {
|
||||||
for _, splitPkg := range entry.PackageInfo.SplitPackages {
|
err := os.WriteFile(path.Join(repo, ".version-cache"), data, 0644)
|
||||||
// Handle split packages
|
if err != nil {
|
||||||
additionalText := ""
|
log.Printf("Warning: could not write cached versions to file: %s", err)
|
||||||
if binaryEntry, ok := binaryDatabase.Entries[splitPkg.Name]; !ok {
|
}
|
||||||
additionalText += "(Binary package missing) "
|
}
|
||||||
} else if fmt.Sprintf("%s-%d", binaryEntry.PackageInfo.Version, binaryEntry.PackageInfo.Revision) != fmt.Sprintf("%s-%d", entry.PackageInfo.Version, entry.PackageInfo.Revision) {
|
|
||||||
additionalText += "(Binary package version mismatch) "
|
if value {
|
||||||
|
fmt.Printf("Package (%s) was put on hold.\n", pkgName)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Package (%s) is no longer put on hold.\n", pkgName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listPackagesFunc(repo string) {
|
||||||
|
// Read package recipes
|
||||||
|
pkgs := bpmutilsshared.ReadRepositoryRecipes(repo)
|
||||||
|
|
||||||
|
// Read databases
|
||||||
|
sourceDatabase, _ := bpmutilsshared.ReadDatabase(path.Join(repo, "source/database.bpmdb"))
|
||||||
|
binaryDatabase, _ := bpmutilsshared.ReadDatabase(path.Join(repo, "binary/database.bpmdb"))
|
||||||
|
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
fmt.Printf("%s (%s):\n", pkg.Name, pkg.Version)
|
||||||
|
|
||||||
|
// Show source package version
|
||||||
|
if sourceDatabase != nil {
|
||||||
|
if entry, ok := sourceDatabase.Entries[pkg.Name]; ok {
|
||||||
|
if entry.PackageInfo.Version == pkg.Version {
|
||||||
|
fmt.Printf(" Source package: %s\n", entry.PackageInfo.Version)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" Source package: %s ≠ %s (Version mismatch)\n", entry.PackageInfo.Version, pkg.Version)
|
||||||
}
|
}
|
||||||
fmt.Printf("%s %s-%d %s\n", splitPkg.Name, entry.PackageInfo.Version, entry.PackageInfo.Revision, additionalText)
|
} else {
|
||||||
|
fmt.Println(" Source package not found!")
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
additionalText := ""
|
|
||||||
if binaryEntry, ok := binaryDatabase.Entries[entry.PackageInfo.Name]; !ok {
|
// Show binary package version
|
||||||
additionalText += "(Binary package missing) "
|
if binaryDatabase != nil {
|
||||||
} else if fmt.Sprintf("%s-%d", binaryEntry.PackageInfo.Version, binaryEntry.PackageInfo.Revision) != fmt.Sprintf("%s-%d", entry.PackageInfo.Version, entry.PackageInfo.Revision) {
|
if entry, ok := binaryDatabase.Entries[pkg.Name]; ok {
|
||||||
additionalText += "(Binary package version mismatch) "
|
if entry.PackageInfo.Version == pkg.Version {
|
||||||
|
fmt.Printf(" Binary package: %s\n", entry.PackageInfo.Version)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" Binary package: %s ≠ %s (Version mismatch)\n", entry.PackageInfo.Version, pkg.Version)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println(" Binary package not found!")
|
||||||
}
|
}
|
||||||
fmt.Printf("%s %s-%d %s\n", entry.PackageInfo.Name, entry.PackageInfo.Version, entry.PackageInfo.Revision, additionalText)
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func compileAllPackagesFunc(repo string) {
|
||||||
|
// Get flags
|
||||||
|
verbose, _ := currentFlagSet.GetBool("verbose")
|
||||||
|
modifiedOnly, _ := currentFlagSet.GetBool("modified")
|
||||||
|
showOrder, _ := currentFlagSet.GetBool("show-order")
|
||||||
|
|
||||||
|
// Read BPM utils config
|
||||||
|
config, err := bpmutilsshared.ReadBPMUtilsConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: failed to read config: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read package recipes
|
||||||
|
pkgs := bpmutilsshared.ReadRepositoryRecipes(repo)
|
||||||
|
pkgsMap := make(map[string]bpmutilsshared.PackageInfo)
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
pkgsMap[pkg.Name] = pkg
|
||||||
|
|
||||||
|
// Add split packages
|
||||||
|
for _, splitPkg := range pkg.SplitPackages {
|
||||||
|
if _, ok := pkgsMap[splitPkg.Name]; !ok {
|
||||||
|
pkgsMap[splitPkg.Name] = pkg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read databases
|
||||||
|
sourceDatabase, err := bpmutilsshared.ReadDatabase(path.Join(repo, "source/database.bpmdb"))
|
||||||
|
binaryDatabase, _ := bpmutilsshared.ReadDatabase(path.Join(repo, "binary/database.bpmdb"))
|
||||||
|
|
||||||
|
// Toposort packages using Depth-first search algorithm
|
||||||
|
sorted := make([]bpmutilsshared.PackageInfo, 0)
|
||||||
|
marked := make(map[string]int) // 0 = Unmarked, 1 = Temporary mark, 2 = Permanent mark
|
||||||
|
var visit func(pkgInfo bpmutilsshared.PackageInfo) error
|
||||||
|
visit = func(pkgInfo bpmutilsshared.PackageInfo) error {
|
||||||
|
if mark, _ := marked[pkgInfo.Name]; mark == 2 {
|
||||||
|
return nil
|
||||||
|
} else if mark == 1 {
|
||||||
|
return fmt.Errorf("circular dependency found")
|
||||||
|
}
|
||||||
|
|
||||||
|
marked[pkgInfo.Name] = 1
|
||||||
|
|
||||||
|
// Get all dependencies
|
||||||
|
depends := slices.Clone(pkgInfo.Depends)
|
||||||
|
depends = append(depends, pkgInfo.MakeDepends...)
|
||||||
|
|
||||||
|
for _, depend := range depends {
|
||||||
|
dependName, _, _ := bpmutilsshared.SplitPkgNameAndVersion(depend)
|
||||||
|
|
||||||
|
// Find package in repository
|
||||||
|
dependInfo, ok := pkgsMap[dependName]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
// Search for virtual package
|
||||||
|
for _, pkg := range pkgsMap {
|
||||||
|
if slices.Contains(pkg.Provides, dependName) {
|
||||||
|
dependInfo = pkg
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
// Try to see if package exists in BPM's other repositories
|
||||||
|
err := exec.Command("bpm", "query", "-d", depend).Run()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not find package (%s) in any database", depend)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := visit(dependInfo)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "circular") {
|
||||||
|
if verbose {
|
||||||
|
fmt.Printf("Circular dependency found! (%s -> %s)\n", pkgInfo.Name, dependInfo.Name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Fatalf("Error: could not resolve dependencies: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
marked[pkgInfo.Name] = 2
|
||||||
|
sorted = append(sorted, pkgInfo)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
if mark, _ := marked[pkg.Name]; mark != 2 {
|
||||||
|
visit(pkg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile all packages in order
|
||||||
|
for _, pkgInfo := range sorted {
|
||||||
|
skip := false
|
||||||
|
|
||||||
|
if modifiedOnly {
|
||||||
|
skip = true
|
||||||
|
|
||||||
|
// Check if required version is valid
|
||||||
|
for _, depend := range pkgInfo.Depends {
|
||||||
|
if !strings.ContainsRune(depend, '=') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dependName, _, _ := bpmutilsshared.SplitPkgNameAndVersion(depend)
|
||||||
|
|
||||||
|
if !bpmutilsshared.EvaluateDependency(depend, pkgsMap[dependName].Version) {
|
||||||
|
skip = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, depend := range pkgInfo.MakeDepends {
|
||||||
|
if !strings.ContainsRune(depend, '=') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dependName, _, _ := bpmutilsshared.SplitPkgNameAndVersion(depend)
|
||||||
|
|
||||||
|
if !bpmutilsshared.EvaluateDependency(depend, pkgsMap[dependName].Version) {
|
||||||
|
skip = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if source database entry is not synced
|
||||||
|
if sourceDatabase != nil {
|
||||||
|
if len(pkgInfo.SplitPackages) != 0 {
|
||||||
|
sourcePkgInfo, ok := sourceDatabase.Entries[pkgInfo.Name]
|
||||||
|
if ok {
|
||||||
|
hasSplitPkg := func(name string) bool {
|
||||||
|
return slices.ContainsFunc(sourcePkgInfo.PackageInfo.SplitPackages, func(splitPkg *bpmutilsshared.PackageInfo) bool {
|
||||||
|
return name == splitPkg.Name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, splitPkg := range pkgInfo.SplitPackages {
|
||||||
|
if !hasSplitPkg(splitPkg.Name) || sourcePkgInfo.PackageInfo.GetFullVersion() != pkgInfo.GetFullVersion() {
|
||||||
|
skip = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
skip = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if sourcePkgInfo, ok := sourceDatabase.Entries[pkgInfo.Name]; !ok || sourcePkgInfo.PackageInfo.GetFullVersion() != pkgInfo.GetFullVersion() {
|
||||||
|
skip = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if binary database entry is not synced
|
||||||
|
if binaryDatabase != nil {
|
||||||
|
if len(pkgInfo.SplitPackages) != 0 {
|
||||||
|
for _, splitPkg := range pkgInfo.SplitPackages {
|
||||||
|
if binaryPkgInfo, ok := binaryDatabase.Entries[splitPkg.Name]; !ok || binaryPkgInfo.PackageInfo.GetFullVersion() != pkgInfo.GetFullVersion() {
|
||||||
|
skip = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if binaryPkgInfo, ok := binaryDatabase.Entries[pkgInfo.Name]; !ok || binaryPkgInfo.PackageInfo.GetFullVersion() != pkgInfo.GetFullVersion() {
|
||||||
|
skip = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if showOrder {
|
||||||
|
fmt.Println(pkgInfo.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure installed packages are up-to-date
|
||||||
|
cmd := exec.Command(config.PrivilegeEscalatorCmd, "sh", "-c", "bpm u -y")
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
log.Fatalf("Error: could not update packages): %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile source package
|
||||||
|
cmd = exec.Command("bpm-package", "-cdusy")
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Dir = path.Join(repo, "recipes", pkgInfo.Name)
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
log.Fatalf("Error: could not compile package (%s): %s", pkgInfo.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure installed packages are up-to-date
|
||||||
|
if !showOrder {
|
||||||
|
cmd := exec.Command(config.PrivilegeEscalatorCmd, "sh", "-c", "bpm u -y")
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
log.Fatalf("Error: could not update packages): %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,3 +716,26 @@ func readEnvFile(repo string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func listSubcommands() {
|
||||||
|
fmt.Println("Usage: bpm-repo <subcommand> <options>")
|
||||||
|
fmt.Println("Description: Manage BPM repositories and databases")
|
||||||
|
fmt.Println("Subcommands:")
|
||||||
|
fmt.Println(" c, create-repo Create a new BPM repository")
|
||||||
|
fmt.Println(" u, update-db Update update source and binary databases in current repositor")
|
||||||
|
fmt.Println(" v, check-versions Manage BPM repositories and databases")
|
||||||
|
fmt.Println(" h, hold Prevent package from being automatically updated")
|
||||||
|
fmt.Println(" l, list List packages")
|
||||||
|
fmt.Println(" a, compile-all Compile all packages in the current repository")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupFlagsAndHelp(flagset *flag.FlagSet, usage, desc string, args []string) {
|
||||||
|
flagset.Usage = func() {
|
||||||
|
fmt.Println("Usage: " + usage)
|
||||||
|
fmt.Println("Description: " + desc)
|
||||||
|
fmt.Println("Options:")
|
||||||
|
flagset.PrintDefaults()
|
||||||
|
}
|
||||||
|
flagset.Parse(args)
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,8 +5,12 @@ go 1.23
|
|||||||
require (
|
require (
|
||||||
bpm-utils-shared v1.0.0
|
bpm-utils-shared v1.0.0
|
||||||
github.com/spf13/pflag v1.0.10
|
github.com/spf13/pflag v1.0.10
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require gopkg.in/yaml.v3 v3.0.1 // indirect
|
require (
|
||||||
|
github.com/drone/envsubst v1.0.3 // indirect
|
||||||
|
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f // indirect
|
||||||
|
)
|
||||||
|
|
||||||
replace bpm-utils-shared => ../bpm-utils-shared
|
replace bpm-utils-shared => ../bpm-utils-shared
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
|
||||||
|
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
|
||||||
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g=
|
||||||
|
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
|
||||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
|||||||
+86
-41
@@ -3,19 +3,19 @@ package main
|
|||||||
import (
|
import (
|
||||||
bpmutilsshared "bpm-utils-shared"
|
bpmutilsshared "bpm-utils-shared"
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
flag "github.com/spf13/pflag"
|
flag "github.com/spf13/pflag"
|
||||||
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var directory = flag.StringP("directory", "D", "", "Path to package directory")
|
|
||||||
var name = flag.StringP("name", "n", "", "Set the package name")
|
var name = flag.StringP("name", "n", "", "Set the package name")
|
||||||
var description = flag.StringP("description", "d", "Default Package Description", "Set the description")
|
var description = flag.StringP("description", "d", "Default Package Description", "Set the description")
|
||||||
var version = flag.StringP("version", "v", "1.0", "Set the package version")
|
var version = flag.StringP("version", "v", "1.0", "Set the package version")
|
||||||
@@ -24,21 +24,35 @@ var license = flag.StringP("license", "l", "", "Set the package licenses")
|
|||||||
var template = flag.StringP("template", "t", "gnu-configure", "Set the package template")
|
var template = flag.StringP("template", "t", "gnu-configure", "Set the package template")
|
||||||
var git = flag.BoolP("git", "g", true, "Create git repository")
|
var git = flag.BoolP("git", "g", true, "Create git repository")
|
||||||
|
|
||||||
|
var directory = ""
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Setup flags and help
|
// Setup flags and help
|
||||||
bpmutilsshared.SetupHelp("bpm-setup <options>", "Sets up files and directories for BPM source package creation")
|
setupFlagsAndHelp("bpm-setup <options>", "Sets up files and directories for BPM source package creation")
|
||||||
bpmutilsshared.SetupFlags()
|
|
||||||
|
// Trim package name
|
||||||
|
*name = strings.TrimSpace(*name)
|
||||||
|
|
||||||
// Show command help if no directory name is given
|
// Show command help if no directory name is given
|
||||||
if *directory == "" {
|
if *name == "" {
|
||||||
log.Println("Directory flag is required")
|
log.Println("Error: name flag is required")
|
||||||
bpmutilsshared.ShowHelp()
|
flag.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set package name to directory name if empty
|
// Get current repository
|
||||||
if *name == "" {
|
repo := bpmutilsshared.GetRepository()
|
||||||
*name = filepath.Base(*directory)
|
|
||||||
|
// Set directory
|
||||||
|
if repo != "" {
|
||||||
|
directory = path.Join(repo, "recipes", *name)
|
||||||
|
} else {
|
||||||
|
workDir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not get working directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
directory = path.Join(workDir, *name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// run checks
|
// run checks
|
||||||
@@ -61,25 +75,29 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runChecks() {
|
func runChecks() {
|
||||||
if strings.TrimSpace(*directory) == "" {
|
// Check if package name is valid
|
||||||
log.Fatalf("No directory was specified!")
|
if *name == "" {
|
||||||
|
log.Fatalf("Error: no package name was specified!")
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.TrimSpace(*name) == "" {
|
// Check if parent directory is valid
|
||||||
log.Fatalf("No package name was specified!")
|
if _, err := os.Stat(path.Dir(directory)); err != nil {
|
||||||
|
log.Fatalf("Error: %s is not a valid directory: %s", path.Dir(directory), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if package directory exists
|
||||||
|
if _, err := os.Stat(directory); err == nil {
|
||||||
|
log.Fatalf("Error: %s already exists", directory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if template file is valid
|
||||||
if stat, err := os.Stat(path.Join("/etc/bpm-utils/templates/", *template)); err != nil || stat.IsDir() {
|
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)
|
log.Fatalf("Error: '%s' is not a valid template file!", *template)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func showSummary() {
|
func showSummary() {
|
||||||
absPath, err := filepath.Abs(strings.TrimSpace(*directory))
|
fmt.Printf("Setting up package directory at %s with the following information:\n", 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 name: %s\n", strings.TrimSpace(*name))
|
||||||
fmt.Printf("Package description: %s\n", *description)
|
fmt.Printf("Package description: %s\n", *description)
|
||||||
fmt.Printf("Package version: %s\n", *version)
|
fmt.Printf("Package version: %s\n", *version)
|
||||||
@@ -107,37 +125,54 @@ func replaceVariables(templateContents string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createDirectory() {
|
func createDirectory() {
|
||||||
// Trim spaces
|
// Read BPM utils config
|
||||||
*directory = strings.TrimSpace(*directory)
|
config, err := bpmutilsshared.ReadBPMUtilsConfig()
|
||||||
*name = strings.TrimSpace(*name)
|
if err != nil {
|
||||||
|
log.Fatalf("Error: failed to read config: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create directory
|
// Create directory
|
||||||
err := os.Mkdir(*directory, 0755)
|
err = os.Mkdir(directory, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not create directory: %s", err)
|
log.Fatalf("Error: could not create directory: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create pkg.info contents string
|
// Create package info struct
|
||||||
pkgInfo := "name: " + *name + "\n"
|
pkgInfo := bpmutilsshared.PackageInfo{
|
||||||
pkgInfo += "description: " + *description + "\n"
|
Name: *name,
|
||||||
pkgInfo += "version: " + *version + "\n"
|
Description: *description,
|
||||||
if url != nil && *url != "" {
|
Version: *version,
|
||||||
pkgInfo += "url: " + *url + "\n"
|
Revision: 1,
|
||||||
|
Url: *url,
|
||||||
|
License: *license,
|
||||||
|
Arch: "any",
|
||||||
|
Type: "source",
|
||||||
|
Downloads: []bpmutilsshared.PackageDownload{
|
||||||
|
{
|
||||||
|
Url: "https://wwww.my-url.com/file.tar.gz",
|
||||||
|
ExtractTo: "${BPM_SOURCE}",
|
||||||
|
ExtractStripComponents: 1,
|
||||||
|
Checksum: "replaceme",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if license != nil && *license != "" {
|
if config.DefaultMaintainer != "" {
|
||||||
pkgInfo += "license: " + *license + "\n"
|
pkgInfo.Maintainers = append(pkgInfo.Maintainers, config.DefaultMaintainer)
|
||||||
}
|
}
|
||||||
pkgInfo += "architecture: any\n"
|
|
||||||
pkgInfo += "type: source\n"
|
|
||||||
|
|
||||||
// Write string to file
|
var buffer bytes.Buffer
|
||||||
err = os.WriteFile(path.Join(*directory, "pkg.info"), []byte(pkgInfo), 0644)
|
encoder := yaml.NewEncoder(&buffer)
|
||||||
|
encoder.SetIndent(2)
|
||||||
|
encoder.Encode(&pkgInfo)
|
||||||
|
|
||||||
|
// Write package info to file
|
||||||
|
err = os.WriteFile(path.Join(directory, "info.yml"), buffer.Bytes(), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Could not write to pkg.info: %s", err)
|
log.Fatalf("Could not write to info.yml: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create source-files directory
|
// Create source-files directory
|
||||||
err = os.Mkdir(path.Join(*directory, "source-files"), 0755)
|
err = os.Mkdir(path.Join(directory, "source-files"), 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not create directory: %s", err)
|
log.Fatalf("Error: could not create directory: %s", err)
|
||||||
}
|
}
|
||||||
@@ -152,7 +187,7 @@ func createDirectory() {
|
|||||||
// Replace variables in template file
|
// Replace variables in template file
|
||||||
input = []byte(replaceVariables(string(input)))
|
input = []byte(replaceVariables(string(input)))
|
||||||
|
|
||||||
err = os.WriteFile(path.Join(*directory, "source.sh"), input, 0644)
|
err = os.WriteFile(path.Join(directory, "recipe.sh"), input, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not write to source file: %s", err)
|
log.Fatalf("Error: could not write to source file: %s", err)
|
||||||
return
|
return
|
||||||
@@ -160,7 +195,7 @@ func createDirectory() {
|
|||||||
|
|
||||||
// Create git repository
|
// Create git repository
|
||||||
if git != nil && *git {
|
if git != nil && *git {
|
||||||
err = exec.Command("git", "init", *directory).Run()
|
err = exec.Command("git", "init", directory).Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not initialize git repository: %s", err)
|
log.Fatalf("Error: could not initialize git repository: %s", err)
|
||||||
}
|
}
|
||||||
@@ -171,7 +206,7 @@ func createDirectory() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer defaultGitignoreFile.Close()
|
defer defaultGitignoreFile.Close()
|
||||||
newGitignoreFile, err := os.OpenFile(path.Join(*directory, ".gitignore"), os.O_CREATE|os.O_WRONLY, 0644)
|
newGitignoreFile, err := os.OpenFile(path.Join(directory, ".gitignore"), os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not create .gitignore file: %s", err)
|
log.Fatalf("Error: could not create .gitignore file: %s", err)
|
||||||
}
|
}
|
||||||
@@ -182,3 +217,13 @@ func createDirectory() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupFlagsAndHelp(usage, desc string) {
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Println("Usage: " + usage)
|
||||||
|
fmt.Println("Description: " + desc)
|
||||||
|
fmt.Println("Options:")
|
||||||
|
flag.PrintDefaults()
|
||||||
|
}
|
||||||
|
flag.Parse()
|
||||||
|
}
|
||||||
|
|||||||
@@ -58,29 +58,42 @@ func GenerateDatabase(path string) error {
|
|||||||
// Initialize database entry
|
// Initialize database entry
|
||||||
entry := BPMDatabaseEntry{}
|
entry := BPMDatabaseEntry{}
|
||||||
entry.DownloadSize = info.Size()
|
entry.DownloadSize = info.Size()
|
||||||
|
entry.InstalledSize = 0
|
||||||
entry.Filepath, err = filepath.Rel(path, packagePath)
|
entry.Filepath, err = filepath.Rel(path, packagePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get package installed size
|
// Get package info
|
||||||
cmd := exec.Command("tar", "-t", "-v", "-f", packagePath, "files.tar.gz")
|
entry.PackageInfo, err = ReadPacakgeInfoFromTarball(packagePath)
|
||||||
output, err := cmd.Output()
|
if err != nil {
|
||||||
if err == nil {
|
|
||||||
entry.InstalledSize, err = strconv.ParseInt(strings.Fields(string(output))[2], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if err.(*exec.ExitError).ExitCode() == 2 {
|
|
||||||
entry.InstalledSize = 0
|
|
||||||
} else {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get package info
|
// Get package installed size
|
||||||
entry.PackageInfo, err = ReadPacakgeInfo(packagePath)
|
if entry.PackageInfo.Type == "binary" {
|
||||||
if err != nil {
|
cmd := exec.Command("tar", "xf", packagePath, "files.txt", "-O")
|
||||||
return err
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range strings.Split(string(output), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
stringEntry := strings.Split(line, " ")
|
||||||
|
if len(stringEntry) < 5 {
|
||||||
|
return fmt.Errorf("files.txt is not formatted correctly")
|
||||||
|
}
|
||||||
|
size, err := strconv.ParseInt(stringEntry[len(stringEntry)-1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.InstalledSize += size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add entry to database
|
// Add entry to database
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ module bpm-utils-shared
|
|||||||
|
|
||||||
go 1.23
|
go 1.23
|
||||||
|
|
||||||
require (
|
require gopkg.in/yaml.v3 v3.0.1
|
||||||
github.com/spf13/pflag v1.0.10
|
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
require github.com/drone/envsubst v1.0.3
|
||||||
)
|
|
||||||
|
require github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f // indirect
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
|
||||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
|
||||||
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g=
|
||||||
|
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
package bpm_utils_shared
|
package bpm_utils_shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
flag "github.com/spf13/pflag"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var usageMsg string
|
|
||||||
var description string
|
|
||||||
|
|
||||||
type BPMUtilsConfig struct {
|
type BPMUtilsConfig struct {
|
||||||
PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"`
|
PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"`
|
||||||
|
DefaultMaintainer string `yaml:"default_maintainer,omitempty"`
|
||||||
|
AddDefaultMaintainer bool `yaml:"add_default_maintainer,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadBPMUtilsConfig() (*BPMUtilsConfig, error) {
|
func ReadBPMUtilsConfig() (*BPMUtilsConfig, error) {
|
||||||
@@ -29,20 +26,3 @@ func ReadBPMUtilsConfig() (*BPMUtilsConfig, error) {
|
|||||||
|
|
||||||
return config, nil
|
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()
|
|
||||||
}
|
|
||||||
|
|||||||
+228
-61
@@ -1,60 +1,114 @@
|
|||||||
package bpm_utils_shared
|
package bpm_utils_shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/drone/envsubst"
|
||||||
|
version "github.com/knqyf263/go-rpm-version"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PackageInfo struct {
|
type PackageInfo struct {
|
||||||
Name string `yaml:"name,omitempty"`
|
Name string `yaml:"name"`
|
||||||
Description string `yaml:"description,omitempty"`
|
Description string `yaml:"description,omitempty"`
|
||||||
Version string `yaml:"version,omitempty"`
|
Version string `yaml:"version,omitempty"`
|
||||||
Revision int `yaml:"revision,omitempty"`
|
Revision int `yaml:"revision,omitempty"`
|
||||||
Url string `yaml:"url,omitempty"`
|
Url string `yaml:"url,omitempty"`
|
||||||
License string `yaml:"license,omitempty"`
|
License string `yaml:"license,omitempty"`
|
||||||
Arch string `yaml:"architecture,omitempty"`
|
Maintainers []string `yaml:"maintainers,omitempty"`
|
||||||
Type string `yaml:"type,omitempty"`
|
Arch string `yaml:"architecture,omitempty"`
|
||||||
Keep []string `yaml:"keep,omitempty"`
|
OutputArch string `yaml:"output_architecture,omitempty"`
|
||||||
Depends []string `yaml:"depends,omitempty"`
|
Type string `yaml:"type,omitempty"`
|
||||||
MakeDepends []string `yaml:"make_depends,omitempty"`
|
Keep []string `yaml:"keep,omitempty"`
|
||||||
OptionalDepends []string `yaml:"optional_depends,omitempty"`
|
Depends []string `yaml:"depends,omitempty"`
|
||||||
Conflicts []string `yaml:"conflicts,omitempty"`
|
RuntimeDepends []string `yaml:"runtime_depends,omitempty"`
|
||||||
Replaces []string `yaml:"replaces,omitempty"`
|
OptionalDepends []string `yaml:"optional_depends,omitempty"`
|
||||||
Provides []string `yaml:"provides,omitempty"`
|
MakeDepends []string `yaml:"make_depends,omitempty"`
|
||||||
SplitPackages []*PackageInfo `yaml:"split_packages,omitempty"`
|
CheckDepends []string `yaml:"check_depends,omitempty"`
|
||||||
|
Conflicts []string `yaml:"conflicts,omitempty"`
|
||||||
|
Replaces []string `yaml:"replaces,omitempty"`
|
||||||
|
Provides []string `yaml:"provides,omitempty"`
|
||||||
|
Options []string `yaml:"options,omitempty"`
|
||||||
|
Flags []PackageFlag `yaml:"flags,omitempty"`
|
||||||
|
Downloads []PackageDownload `yaml:"downloads,omitempty"`
|
||||||
|
SplitPackages []*PackageInfo `yaml:"split_packages,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadPacakgeInfo(path string) (*PackageInfo, error) {
|
type PackageFlag struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
DefaultValue string `yaml:"default_value"`
|
||||||
|
BuiltValue string `yaml:"built_value"`
|
||||||
|
AcceptedValues []PackageAcceptedValue `yaml:"accepted_values,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PackageAcceptedValue struct {
|
||||||
|
Value string `yaml:"value"`
|
||||||
|
Depends []string `yaml:"depends,omitempty"`
|
||||||
|
RuntimeDepends []string `yaml:"runtime_depends,omitempty"`
|
||||||
|
OptionalDepends []string `yaml:"optional_depends,omitempty"`
|
||||||
|
MakeDepends []string `yaml:"make_depends,omitempty"`
|
||||||
|
CheckDepends []string `yaml:"check_depends,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PackageDownload struct {
|
||||||
|
Url string `yaml:"url"`
|
||||||
|
Type string `yaml:"type,omitempty"`
|
||||||
|
Filepath string `yaml:"filepath,omitempty,omitempty"`
|
||||||
|
|
||||||
|
// Archive options
|
||||||
|
NoExtract bool `yaml:"no_extract,omitempty"`
|
||||||
|
ExtractTo string `yaml:"extract_to,omitempty"`
|
||||||
|
ExtractStripComponents int `yaml:"extract_strip_components,omitempty"`
|
||||||
|
|
||||||
|
// Git options
|
||||||
|
CloneTo string `yaml:"clone_to,omitempty"`
|
||||||
|
GitBranch string `yaml:"git_branch,omitempty"`
|
||||||
|
|
||||||
|
Checksum string `yaml:"checksum,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadPackageInfo(data []byte) (*PackageInfo, error) {
|
||||||
|
pkgInfo := &PackageInfo{
|
||||||
|
Revision: 1,
|
||||||
|
Keep: make([]string, 0),
|
||||||
|
Depends: make([]string, 0),
|
||||||
|
MakeDepends: make([]string, 0),
|
||||||
|
RuntimeDepends: make([]string, 0),
|
||||||
|
OptionalDepends: make([]string, 0),
|
||||||
|
Conflicts: make([]string, 0),
|
||||||
|
Replaces: make([]string, 0),
|
||||||
|
Provides: make([]string, 0),
|
||||||
|
Options: make([]string, 0),
|
||||||
|
Downloads: make([]PackageDownload, 0),
|
||||||
|
SplitPackages: make([]*PackageInfo, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal yaml
|
||||||
|
err := yaml.Unmarshal(data, pkgInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkgInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkgInfo *PackageInfo) GetFullVersion() string {
|
||||||
|
return pkgInfo.Version + "-" + strconv.Itoa(pkgInfo.Revision)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadPacakgeInfoFromTarball(path string) (*PackageInfo, error) {
|
||||||
// Extract package info using tar
|
// Extract package info using tar
|
||||||
cmd := exec.Command("tar", "-x", "-f", path, "pkg.info", "-O")
|
cmd := exec.Command("tar", "-x", "-f", path, "info.yml", "-O")
|
||||||
output, err := cmd.Output()
|
output, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pkgInfo := &PackageInfo{
|
pkgInfo, err := ReadPackageInfo(output)
|
||||||
Name: "",
|
|
||||||
Description: "",
|
|
||||||
Version: "",
|
|
||||||
Revision: 1,
|
|
||||||
Url: "",
|
|
||||||
License: "",
|
|
||||||
Arch: "",
|
|
||||||
Type: "",
|
|
||||||
Keep: make([]string, 0),
|
|
||||||
Depends: make([]string, 0),
|
|
||||||
MakeDepends: make([]string, 0),
|
|
||||||
OptionalDepends: make([]string, 0),
|
|
||||||
Conflicts: make([]string, 0),
|
|
||||||
Replaces: make([]string, 0),
|
|
||||||
Provides: make([]string, 0),
|
|
||||||
SplitPackages: make([]*PackageInfo, 0),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal yaml
|
|
||||||
err = yaml.Unmarshal(output, pkgInfo)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -63,36 +117,149 @@ func ReadPacakgeInfo(path string) (*PackageInfo, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ReadPacakgeInfoFromFile(path string) (*PackageInfo, error) {
|
func ReadPacakgeInfoFromFile(path string) (*PackageInfo, error) {
|
||||||
// Extract package info using tar
|
// Read data from file
|
||||||
output, err := os.ReadFile(path)
|
output, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pkgInfo := &PackageInfo{
|
pkgInfo, err := ReadPackageInfo(output)
|
||||||
Name: "",
|
|
||||||
Description: "",
|
|
||||||
Version: "",
|
|
||||||
Revision: 1,
|
|
||||||
Url: "",
|
|
||||||
License: "",
|
|
||||||
Arch: "",
|
|
||||||
Type: "",
|
|
||||||
Keep: make([]string, 0),
|
|
||||||
Depends: make([]string, 0),
|
|
||||||
MakeDepends: make([]string, 0),
|
|
||||||
OptionalDepends: make([]string, 0),
|
|
||||||
Conflicts: make([]string, 0),
|
|
||||||
Replaces: make([]string, 0),
|
|
||||||
Provides: make([]string, 0),
|
|
||||||
SplitPackages: make([]*PackageInfo, 0),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal yaml
|
|
||||||
err = yaml.Unmarshal(output, pkgInfo)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return pkgInfo, nil
|
return pkgInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pkgDownload *PackageDownload) CalculateChecksum(pkgInfo *PackageInfo) (string, error) {
|
||||||
|
switch pkgDownload.Type {
|
||||||
|
case "", "file":
|
||||||
|
fmt.Println("Downloading and calculating checksum for file...")
|
||||||
|
|
||||||
|
// Replace variables in download url
|
||||||
|
downloadUrl := pkgDownload.Url
|
||||||
|
downloadUrl, err := envsubst.Eval(downloadUrl, func(s string) string {
|
||||||
|
switch s {
|
||||||
|
case "BPM_PKG_VERSION":
|
||||||
|
return pkgInfo.Version
|
||||||
|
case "BPM_PKG_NAME":
|
||||||
|
return pkgInfo.Name
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("sh", "-c", fmt.Sprintf("curl -s -L %s | sha256sum | awk '{print $1}'", downloadUrl))
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
checksum, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(string(checksum)), err
|
||||||
|
case "git":
|
||||||
|
fmt.Println("Calculating checksum for git branch...")
|
||||||
|
|
||||||
|
// Replace variables in git branch
|
||||||
|
gitBranch := pkgDownload.GitBranch
|
||||||
|
gitBranch, err := envsubst.Eval(gitBranch, func(s string) string {
|
||||||
|
switch s {
|
||||||
|
case "BPM_PKG_VERSION":
|
||||||
|
return pkgInfo.Version
|
||||||
|
case "BPM_PKG_NAME":
|
||||||
|
return pkgInfo.Name
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgDownload.GitBranch == "" {
|
||||||
|
return "", fmt.Errorf("'git_branch' field cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("sh", "-c", fmt.Sprintf("git ls-remote -bt %s | grep -E 'refs/.*/%s(\\^\\{\\})?$' | tail -n1 | awk '{print $1}'", pkgDownload.Url, gitBranch))
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
checksum, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(string(checksum)), err
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unknown download type (%s)", pkgDownload.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompareVersions(version1, version2 string) int {
|
||||||
|
v1 := version.NewVersion(version1)
|
||||||
|
v2 := version.NewVersion(version2)
|
||||||
|
|
||||||
|
return v1.Compare(v2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SplitPkgNameAndVersion(pkg string) (string, string, string) {
|
||||||
|
if strings.Contains(pkg, ">=") {
|
||||||
|
pkgSplit := strings.SplitN(pkg, ">=", 2)
|
||||||
|
pkgName := pkgSplit[0]
|
||||||
|
pkgVersion := pkgSplit[1]
|
||||||
|
|
||||||
|
return pkgName, ">=", pkgVersion
|
||||||
|
} else if strings.Contains(pkg, ">") {
|
||||||
|
pkgSplit := strings.SplitN(pkg, ">", 2)
|
||||||
|
pkgName := pkgSplit[0]
|
||||||
|
pkgVersion := pkgSplit[1]
|
||||||
|
|
||||||
|
return pkgName, ">", pkgVersion
|
||||||
|
} else if strings.Contains(pkg, "<=") {
|
||||||
|
pkgSplit := strings.SplitN(pkg, "<=", 2)
|
||||||
|
pkgName := pkgSplit[0]
|
||||||
|
pkgVersion := pkgSplit[1]
|
||||||
|
|
||||||
|
return pkgName, "<=", pkgVersion
|
||||||
|
} else if strings.Contains(pkg, "<") {
|
||||||
|
pkgSplit := strings.SplitN(pkg, "<", 2)
|
||||||
|
pkgName := pkgSplit[0]
|
||||||
|
pkgVersion := pkgSplit[1]
|
||||||
|
|
||||||
|
return pkgName, "<", pkgVersion
|
||||||
|
} else if strings.Contains(pkg, "=") {
|
||||||
|
pkgSplit := strings.SplitN(pkg, "=", 2)
|
||||||
|
pkgName := pkgSplit[0]
|
||||||
|
pkgVersion := pkgSplit[1]
|
||||||
|
|
||||||
|
return pkgName, "=", pkgVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkg, "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func EvaluateDependency(pkg, matchVersion string) bool {
|
||||||
|
_, comparisonSymbol, pkgVersion := SplitPkgNameAndVersion(pkg)
|
||||||
|
|
||||||
|
switch comparisonSymbol {
|
||||||
|
case ">=":
|
||||||
|
return CompareVersions(matchVersion, pkgVersion) >= 0
|
||||||
|
case ">":
|
||||||
|
return CompareVersions(matchVersion, pkgVersion) > 0
|
||||||
|
case "<=":
|
||||||
|
return CompareVersions(matchVersion, pkgVersion) <= 0
|
||||||
|
case "<":
|
||||||
|
return CompareVersions(matchVersion, pkgVersion) < 0
|
||||||
|
case "=":
|
||||||
|
if cutPkgVersion, ok := strings.CutSuffix(pkgVersion, "*"); ok {
|
||||||
|
return strings.HasPrefix(matchVersion, cutPkgVersion)
|
||||||
|
} else {
|
||||||
|
return CompareVersions(matchVersion, pkgVersion) == 0
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package bpm_utils_shared
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetRepository() string {
|
func GetRepository() string {
|
||||||
@@ -23,3 +24,31 @@ func GetRepository() string {
|
|||||||
}
|
}
|
||||||
return dir
|
return dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReadRepositoryRecipes(repository string) []PackageInfo {
|
||||||
|
// Read package recipes
|
||||||
|
recipeDirs, err := os.ReadDir(path.Join(repository, "recipes"))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgs := make([]PackageInfo, 0)
|
||||||
|
for _, dir := range recipeDirs {
|
||||||
|
pkgInfoPath := path.Join(repository, "recipes", dir.Name(), "info.yml")
|
||||||
|
if _, err := os.Stat(pkgInfoPath); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgInfo, err := ReadPacakgeInfoFromFile(pkgInfoPath)
|
||||||
|
if err == nil {
|
||||||
|
pkgs = append(pkgs, *pkgInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort package recipes
|
||||||
|
sort.Slice(pkgs, func(i, j int) bool {
|
||||||
|
return pkgs[i].Name < pkgs[j].Name
|
||||||
|
})
|
||||||
|
|
||||||
|
return pkgs
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user