Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for generating lockfiles to rpmtree #80

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 130 additions & 37 deletions cmd/rpmtree.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package main

import (
"errors"
"os"

"github.com/bazelbuild/buildtools/build"
"github.com/rmohr/bazeldnf/cmd/template"
"github.com/rmohr/bazeldnf/pkg/api"
"github.com/rmohr/bazeldnf/pkg/api/bazeldnf"
"github.com/rmohr/bazeldnf/pkg/bazel"
"github.com/rmohr/bazeldnf/pkg/reducer"
"github.com/rmohr/bazeldnf/pkg/repo"
Expand All @@ -22,21 +25,121 @@ type rpmtreeOpts struct {
workspace string
toMacro string
buildfile string
configname string
lockfile string
name string
public bool
forceIgnoreRegex []string
}

var rpmtreeopts = rpmtreeOpts{}

type Handler interface {
AddRPMs(pkgs []*api.Package, arch string) error
PruneRPMs(buildfile *build.File)
Write() error
}

type MacroHandler struct {
bzl, defName string
bzlfile *build.File
}

func NewMacroHandler(toMacro string) (Handler, error) {
bzl, defName, err := bazel.ParseMacro(rpmtreeopts.toMacro)

if err != nil {
return nil, err
}

bzlfile, err := bazel.LoadBzl(bzl)
if err != nil {
return nil, err
}

return &MacroHandler{
bzl: bzl,
bzlfile: bzlfile,
defName: defName,
}, nil
}

func (h *MacroHandler) AddRPMs(pkgs []*api.Package, arch string) error {
return bazel.AddBzlfileRPMs(h.bzlfile, h.defName, pkgs, arch)
}

func (h *MacroHandler) PruneRPMs(buildfile *build.File) {
bazel.PruneBzlfileRPMs(buildfile, h.bzlfile, h.defName)
}

func (h *MacroHandler) Write() error {
return bazel.WriteBzl(false, h.bzlfile, h.bzl)
}

type WorkspaceHandler struct {
workspace string
workspacefile *build.File
}

func NewWorkspaceHandler(workspace string) (Handler, error) {
workspacefile, err := bazel.LoadWorkspace(workspace)
if err != nil {
return nil, err
}

return &WorkspaceHandler{
workspace: workspace,
workspacefile: workspacefile,
}, nil
}

func (h *WorkspaceHandler) AddRPMs(pkgs []*api.Package, arch string) error {
return bazel.AddWorkspaceRPMs(h.workspacefile, pkgs, arch)
}

func (h *WorkspaceHandler) PruneRPMs(buildfile *build.File) {
bazel.PruneWorkspaceRPMs(buildfile, h.workspacefile)
}

func (h *WorkspaceHandler) Write() error {
return bazel.WriteWorkspace(false, h.workspacefile, h.workspace)
}

type LockFileHandler struct {
filename string
config *bazeldnf.Config
}

func NewLockFileHandler(configname, filename string) (Handler, error) {
return &LockFileHandler{
filename: filename,
config: &bazeldnf.Config{
Name: configname,
RPMs: []bazeldnf.RPM{},
},
}, nil
}

func (h *LockFileHandler) AddRPMs(pkgs []*api.Package, arch string) error {
return bazel.AddConfigRPMs(h.config, pkgs, arch)
}

func (h *LockFileHandler) PruneRPMs(buildfile *build.File) {}

func (h *LockFileHandler) Write() error {
return bazel.WriteLockFile(h.config, h.filename)
}

func NewRpmTreeCmd() *cobra.Command {

rpmtreeCmd := &cobra.Command{
Use: "rpmtree",
Short: "Writes a rpmtree rule and its rpmdependencies to bazel files",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, required []string) error {
writeToMacro := rpmtreeopts.toMacro != ""
if rpmtreeopts.toMacro != "" && rpmtreeopts.lockfile != "" {
return errors.New("Must provide at most one of --lockfile --to-macro")
}

repos, err := repo.LoadRepoFiles(rpmtreeopts.repofiles)
if err != nil {
Expand Down Expand Up @@ -68,54 +171,42 @@ func NewRpmTreeCmd() *cobra.Command {
if err != nil {
return err
}
workspace, err := bazel.LoadWorkspace(rpmtreeopts.workspace)

var handler Handler
if rpmtreeopts.toMacro != "" {
handler, err = NewMacroHandler(rpmtreeopts.toMacro)
} else if rpmtreeopts.lockfile != "" {
handler, err = NewLockFileHandler(
rpmtreeopts.configname,
rpmtreeopts.lockfile,
)
} else {
handler, err = NewWorkspaceHandler(rpmtreeopts.workspace)
}

if err != nil {
return err
}
var bzlfile *build.File
var bzl, defName string
if writeToMacro {
bzl, defName, err = bazel.ParseMacro(rpmtreeopts.toMacro)
if err != nil {
return err
}
bzlfile, err = bazel.LoadBzl(bzl)
if err != nil {
return err
}
}

build, err := bazel.LoadBuild(rpmtreeopts.buildfile)
if err != nil {
return err
}
if writeToMacro {
err = bazel.AddBzlfileRPMs(bzlfile, defName, install, rpmtreeopts.arch)
if err != nil {
return err
}
} else {
err = bazel.AddWorkspaceRPMs(workspace, install, rpmtreeopts.arch)
if err != nil {
return err
}
}
bazel.AddTree(rpmtreeopts.name, build, install, rpmtreeopts.arch, rpmtreeopts.public)
if writeToMacro {
bazel.PruneBzlfileRPMs(build, bzlfile, defName)
} else {
bazel.PruneWorkspaceRPMs(build, workspace)

err = handler.AddRPMs(install, rpmtreeopts.arch)
if err != nil {
return err
}

bazel.AddTree(rpmtreeopts.name, rpmtreeopts.configname, build, install, rpmtreeopts.arch, rpmtreeopts.public)

handler.PruneRPMs(build)
logrus.Info("Writing bazel files.")
err = bazel.WriteWorkspace(false, workspace, rpmtreeopts.workspace)
err = handler.Write()
if err != nil {
return err
}
if writeToMacro {
err = bazel.WriteBzl(false, bzlfile, bzl)
if err != nil {
return err
}
}

err = bazel.WriteBuild(false, build, rpmtreeopts.buildfile)
if err != nil {
return err
Expand All @@ -136,6 +227,8 @@ func NewRpmTreeCmd() *cobra.Command {
rpmtreeCmd.Flags().StringVarP(&rpmtreeopts.workspace, "workspace", "w", "WORKSPACE", "Bazel workspace file")
rpmtreeCmd.Flags().StringVarP(&rpmtreeopts.toMacro, "to-macro", "", "", "Tells bazeldnf to write the RPMs to a macro in the given bzl file instead of the WORKSPACE file. The expected format is: macroFile%defName")
rpmtreeCmd.Flags().StringVarP(&rpmtreeopts.buildfile, "buildfile", "b", "rpm/BUILD.bazel", "Build file for RPMs")
rpmtreeCmd.Flags().StringVar(&rpmtreeopts.configname, "configname", "rpms", "config name to use in lockfile")
rpmtreeCmd.Flags().StringVar(&rpmtreeopts.lockfile, "lockfile", "", "lockfile for RPMs")
rpmtreeCmd.Flags().StringVar(&rpmtreeopts.name, "name", "", "rpmtree rule name")
rpmtreeCmd.Flags().StringArrayVar(&rpmtreeopts.forceIgnoreRegex, "force-ignore-with-dependencies", []string{}, "Packages matching these regex patterns will not be installed. Allows force-removing unwanted dependencies. Be careful, this can lead to hidden missing dependencies.")
rpmtreeCmd.MarkFlagRequired("name")
Expand Down
5 changes: 4 additions & 1 deletion pkg/api/bazeldnf/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ load("@rules_go//go:def.bzl", "go_library")

go_library(
name = "bazeldnf",
srcs = ["repo.go"],
srcs = [
"config.go",
"repo.go",
],
importpath = "github.com/rmohr/bazeldnf/pkg/api/bazeldnf",
visibility = ["//visibility:public"],
)
12 changes: 12 additions & 0 deletions pkg/api/bazeldnf/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package bazeldnf

type RPM struct {
Name string `json:"name"`
SHA256 string `json:"sha256"`
URLs []string `json:"urls"`
}

type Config struct {
Name string `json:"name"`
RPMs []RPM `json:"rpms"`
}
1 change: 1 addition & 0 deletions pkg/bazel/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//pkg/api",
"//pkg/api/bazeldnf",
"@com_github_bazelbuild_buildtools//build:go_default_library",
"@com_github_bazelbuild_buildtools//edit:go_default_library",
],
Expand Down
63 changes: 54 additions & 9 deletions pkg/bazel/bazel.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
package bazel

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"sort"
"strings"

"github.com/bazelbuild/buildtools/build"
"github.com/bazelbuild/buildtools/edit"
"github.com/rmohr/bazeldnf/pkg/api"
"github.com/rmohr/bazeldnf/pkg/api/bazeldnf"
)

type Artifact struct {
rule *build.Rule
}

func LoadWorkspace(path string) (*build.File, error) {
workspaceData, err := ioutil.ReadFile(path)
workspaceData, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to parse WORSPACE orig: %v", err)
}
Expand All @@ -30,7 +32,7 @@ func LoadWorkspace(path string) (*build.File, error) {
}

func LoadBuild(path string) (*build.File, error) {
buildfileData, err := ioutil.ReadFile(path)
buildfileData, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to parse BUILD.bazel orig: %v", err)
}
Expand All @@ -42,7 +44,7 @@ func LoadBuild(path string) (*build.File, error) {
}

func LoadBzl(path string) (*build.File, error) {
bzlData, err := ioutil.ReadFile(path)
bzlData, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to parse bzl orig: %v", err)
}
Expand All @@ -58,23 +60,31 @@ func WriteBuild(dryRun bool, buildfile *build.File, path string) error {
fmt.Println(build.FormatString(buildfile))
return nil
}
return ioutil.WriteFile(path, build.Format(buildfile), 0666)
return os.WriteFile(path, build.Format(buildfile), 0666)
}

func WriteWorkspace(dryRun bool, workspace *build.File, path string) error {
if dryRun {
fmt.Println(build.FormatString(workspace))
return nil
}
return ioutil.WriteFile(path, build.Format(workspace), 0666)
return os.WriteFile(path, build.Format(workspace), 0666)
}

func WriteBzl(dryRun bool, bzl *build.File, path string) error {
if dryRun {
fmt.Println(build.FormatString(bzl))
return nil
}
return ioutil.WriteFile(path, build.Format(bzl), 0666)
return os.WriteFile(path, build.Format(bzl), 0666)
}

func WriteLockFile(config *bazeldnf.Config, path string) error {
configJson, err := json.Marshal(config)
if err != nil {
return err
}
return os.WriteFile(path, configJson, 0666)
}

// ParseMacro parses a macro expression of the form macroFile%defName and returns the bzl file and the def name.
Expand Down Expand Up @@ -258,7 +268,16 @@ func AddTar2Files(name string, rpmtree string, buildfile *build.File, files []st
}
}

func AddTree(name string, buildfile *build.File, pkgs []*api.Package, arch string, public bool) {
func AddTree(name, configname string, buildfile *build.File, pkgs []*api.Package, arch string, public bool) {
transform := func(n string) string {
return "@"+n+"//rpm"
}
if configname != "" {
transform = func(n string) string {
return "@" + configname + "//" + n
}
}

rpmtrees := map[string]*rpmTree{}

for _, rule := range buildfile.Rules("rpmtree") {
Expand All @@ -269,7 +288,7 @@ func AddTree(name string, buildfile *build.File, pkgs []*api.Package, arch strin
rpms := []string{}
for _, pkg := range pkgs {
pkgName := sanitize(pkg.String() + "." + arch)
rpms = append(rpms, "@"+pkgName+"//rpm")
rpms = append(rpms, transform(pkgName))
}
sort.SliceStable(rpms, func(i, j int) bool {
return rpms[i] < rpms[j]
Expand Down Expand Up @@ -486,6 +505,32 @@ func (r *tar2Files) SetFiles(dirs []string, fileMap map[string][]string) {
r.Rule.SetAttr("files", filesMapExpr)
}

func AddConfigRPMs(config *bazeldnf.Config, pkgs []*api.Package, arch string) error {
for _, pkg := range pkgs {
URLs := []string{}

for _, mirror := range pkg.Repository.Mirrors {
u, err := url.Parse(mirror)
if err != nil {
return err
}
u = u.JoinPath(pkg.Location.Href)
URLs = append(URLs, u.String())
}

config.RPMs = append(
config.RPMs,
bazeldnf.RPM{
Name: sanitize(pkg.String() + "." + arch),
SHA256: pkg.Checksum.Text,
URLs: URLs,
},
)
}

return nil
}

func sanitize(name string) string {
name = strings.ReplaceAll(name, ":", "__")
name = strings.ReplaceAll(name, "+", "__plus__")
Expand Down
Loading