4 Commits
Author SHA1 Message Date
EnumDev d881ac31c3 Update README.md 2025-09-27 21:32:07 +03:00
EnumDev 96a39a6b40 Update default pkg.info content 2025-09-27 21:19:13 +03:00
EnumDev 7a63ad6388 Update templates 2025-09-27 21:18:39 +03:00
EnumDev 10f918034a Add 'update-checksums' flag 2025-09-27 13:39:54 +03:00
11 changed files with 203 additions and 82 deletions
+19 -7
View File
@@ -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 pkg.info 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,11 +42,23 @@ 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 source.sh file during compilation
@@ -55,4 +67,4 @@ type: source
``` ```
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
+2 -6
View File
@@ -2,14 +2,10 @@
# 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
+2 -6
View File
@@ -2,14 +2,10 @@
# 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
+2 -6
View File
@@ -2,14 +2,10 @@
# 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
+2 -6
View File
@@ -2,14 +2,10 @@
# 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
+2 -6
View File
@@ -2,14 +2,10 @@
# 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
+40 -13
View File
@@ -2,6 +2,7 @@ package main
import ( import (
bpmutilsshared "bpm-utils-shared" bpmutilsshared "bpm-utils-shared"
"bytes"
"fmt" "fmt"
"io" "io"
"log" "log"
@@ -22,6 +23,7 @@ var skipCheck = flag.BoolP("skip-checks", "s", false, "Skip 'check' function whi
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 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 moveToBinaryDir = flag.BoolP("move", "m", false, "Move output packages to the current repository's binary directory")
var updateChecksums = flag.BoolP("update-checksums", "u", false, "Update the checksums for all download entries")
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() {
@@ -74,22 +76,47 @@ func createArchive() string {
} }
} }
// Read pkg.info file into basic struct // Read pkg.info file
pkgInfo := struct { pkgInfo, err := bpmutilsshared.ReadPacakgeInfoFromFile("pkg.info")
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)
// Update checksums
if *updateChecksums {
for i, download := range pkgInfo.Downloads {
if download.Checksum == "skip" {
continue
}
download.Checksum, err = download.CalculateChecksum(pkgInfo)
if err != nil { if err != nil {
log.Fatalf("Error: could not unmarshal pkg.info file") log.Fatalf("Could not calculate checksum for download entry %d: %s", i+1, err)
}
pkgInfo.Downloads[i] = download
}
// Save yaml back to file
var data bytes.Buffer
encoder := yaml.NewEncoder(&data)
encoder.SetIndent(2)
encoder.Encode(pkgInfo)
if err != nil {
log.Fatalf("Could not marshal package info: %s", err)
}
// Stat pkg.info
stat, err := os.Stat("pkg.info")
if err != nil {
log.Fatalf("Could not stat pkg.info: %s", err)
}
// Write package info back to file
err = os.WriteFile("pkg.info", data.Bytes(), stat.Mode().Perm())
if err != nil {
log.Fatalf("Could not write package info to pkg.info: %s", err)
}
} }
// Remove old BPM archives in current directory // Remove old BPM archives in current directory
+5 -1
View File
@@ -2,6 +2,10 @@ module git.enumerated.dev/bubble-package-manager/bpm-utils/src/bpm-setup
go 1.23 go 1.23
require github.com/spf13/pflag v1.0.10 require (
bpm-utils-shared v1.0.0
github.com/spf13/pflag v1.0.10
gopkg.in/yaml.v3 v3.0.1
)
replace bpm-utils-shared => ../bpm-utils-shared replace bpm-utils-shared => ../bpm-utils-shared
+4
View File
@@ -1,2 +1,6 @@
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+27 -13
View File
@@ -1,7 +1,9 @@
package main package main
import ( import (
bpmutilsshared "bpm-utils-shared"
"bufio" "bufio"
"bytes"
"fmt" "fmt"
"io" "io"
"log" "log"
@@ -12,6 +14,7 @@ import (
"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 directory = flag.StringP("directory", "D", "", "Path to package directory")
@@ -115,21 +118,32 @@ func createDirectory() {
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" Url: *url,
License: *license,
Arch: "any",
Type: "source",
Downloads: []bpmutilsshared.PackageDownload{
{
Url: "https://wwww.my-url.com/file.tar.gz",
ExtractStripComponents: 1,
ExtractToBPMSource: true,
Checksum: "replaceme",
},
},
} }
if license != nil && *license != "" {
pkgInfo += "license: " + *license + "\n"
}
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, "pkg.info"), 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 pkg.info: %s", err)
} }
+85 -5
View File
@@ -1,31 +1,46 @@
package bpm_utils_shared package bpm_utils_shared
import ( import (
"fmt"
"os" "os"
"os/exec" "os/exec"
"strings"
"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"`
Version string `yaml:"version,omitempty"` Version string `yaml:"version"`
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"` Arch string `yaml:"architecture,omitempty"`
OutputArch string `yaml:"output_architecture,omitempty"`
Type string `yaml:"type,omitempty"` Type string `yaml:"type,omitempty"`
Keep []string `yaml:"keep,omitempty"` Keep []string `yaml:"keep,omitempty"`
Depends []string `yaml:"depends,omitempty"` Depends []string `yaml:"depends,omitempty"`
MakeDepends []string `yaml:"make_depends,omitempty"`
OptionalDepends []string `yaml:"optional_depends,omitempty"` OptionalDepends []string `yaml:"optional_depends,omitempty"`
MakeDepends []string `yaml:"make_depends,omitempty"`
Conflicts []string `yaml:"conflicts,omitempty"` Conflicts []string `yaml:"conflicts,omitempty"`
Replaces []string `yaml:"replaces,omitempty"` Replaces []string `yaml:"replaces,omitempty"`
Provides []string `yaml:"provides,omitempty"` Provides []string `yaml:"provides,omitempty"`
Downloads []PackageDownload `yaml:"downloads,omitempty"`
SplitPackages []*PackageInfo `yaml:"split_packages,omitempty"` SplitPackages []*PackageInfo `yaml:"split_packages,omitempty"`
} }
type PackageDownload struct {
Url string `yaml:"url"`
Type string `yaml:"type,omitempty"`
NoExtract bool `yaml:"no_extract,omitempty"`
ExtractToBPMSource bool `yaml:"extract_to_bpm_source,omitempty"`
ExtractStripComponents int `yaml:"extract_strip_components,omitempty"`
GitBranch string `yaml:"git_branch,omitempty"`
Filepath string `yaml:"filepath,omitempty,omitempty"`
Checksum string `yaml:"checksum,omitempty"`
}
func ReadPacakgeInfo(path string) (*PackageInfo, error) { func ReadPacakgeInfo(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, "pkg.info", "-O")
@@ -42,6 +57,7 @@ func ReadPacakgeInfo(path string) (*PackageInfo, error) {
Url: "", Url: "",
License: "", License: "",
Arch: "", Arch: "",
OutputArch: "",
Type: "", Type: "",
Keep: make([]string, 0), Keep: make([]string, 0),
Depends: make([]string, 0), Depends: make([]string, 0),
@@ -50,6 +66,7 @@ func ReadPacakgeInfo(path string) (*PackageInfo, error) {
Conflicts: make([]string, 0), Conflicts: make([]string, 0),
Replaces: make([]string, 0), Replaces: make([]string, 0),
Provides: make([]string, 0), Provides: make([]string, 0),
Downloads: make([]PackageDownload, 0),
SplitPackages: make([]*PackageInfo, 0), SplitPackages: make([]*PackageInfo, 0),
} }
@@ -77,14 +94,16 @@ func ReadPacakgeInfoFromFile(path string) (*PackageInfo, error) {
Url: "", Url: "",
License: "", License: "",
Arch: "", Arch: "",
OutputArch: "",
Type: "", Type: "",
Keep: make([]string, 0), Keep: make([]string, 0),
Depends: make([]string, 0), Depends: make([]string, 0),
MakeDepends: make([]string, 0),
OptionalDepends: make([]string, 0), OptionalDepends: make([]string, 0),
MakeDepends: make([]string, 0),
Conflicts: make([]string, 0), Conflicts: make([]string, 0),
Replaces: make([]string, 0), Replaces: make([]string, 0),
Provides: make([]string, 0), Provides: make([]string, 0),
Downloads: make([]PackageDownload, 0),
SplitPackages: make([]*PackageInfo, 0), SplitPackages: make([]*PackageInfo, 0),
} }
@@ -96,3 +115,64 @@ func ReadPacakgeInfoFromFile(path string) (*PackageInfo, error) {
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 = os.Expand(downloadUrl, func(s string) string {
switch s {
case "BPM_PKG_VERSION":
return pkgInfo.Version
case "BPM_PKG_NAME":
return pkgInfo.Name
default:
return ""
}
})
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 = os.Expand(gitBranch, func(s string) string {
switch s {
case "BPM_PKG_VERSION":
return pkgInfo.Version
case "BPM_PKG_NAME":
return pkgInfo.Name
default:
return ""
}
})
if pkgDownload.GitBranch == "" {
return "", fmt.Errorf("'git_branch' field cannot be empty")
}
cmd := exec.Command("sh", "-c", fmt.Sprintf("git ls-remote --refs %s | grep 'refs/.*/%s$' | 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)
}
}