refactor: replace global 'buildr' variable with 'repo' and 'vcs'

This commit is contained in:
Peter 2023-06-09 17:33:46 +02:00
parent 99f863f1d6
commit ab282b8dc0
No known key found for this signature in database
29 changed files with 329 additions and 273 deletions

View file

@ -19,7 +19,7 @@ build "go_build" "linux" {
ldflags = [
"-w -s",
"-X 'code.icb4dc0.de/buildr/buildr/cmd.CurrentVersion=${buildr.repo.git.tag == "" ? buildr.repo.git.branch : buildr.repo.git.tag}'"
"-X 'code.icb4dc0.de/buildr/buildr/cmd.CurrentVersion=${vcs.tag == "" ? vcs.branch : vcs.tag}'"
]
environment = {

View file

@ -29,7 +29,7 @@ package "container_image" "buildr_image" {
}
input_mapping = {
"${buildr.repo.root}" = "."
"${repo.root}" = "."
"${builds.linux["amd64"].out_dir}" = "out/"
}

View file

@ -1,9 +1,15 @@
task "script" "go_fmt" {
inline = [
"go fmt ${join(" ", toset([for s in vcs.staged_files : "code.icb4dc0.de/buildr/buildr/${dirname(s)}" if ext(s) == ".go"]))}"
]
}
task "script" "buf_generate" {
inline = [
"buf generate --debug"
]
out_dir = buildr.repo.root
out_dir = repo.root
input_mapping = {
"api" = "api",
@ -48,7 +54,7 @@ task "script" "go_test" {
}
input_mapping = {
"${buildr.repo.root}" = "."
"${repo.root}" = "."
}
container {
@ -73,11 +79,12 @@ task "script" "golangci_lint" {
]
depends_on = [
tasks.go_fmt.id,
tasks.go_generate.id,
tools.golangci_lint.id
]
input_mapping = {
"${buildr.repo.root}" = "."
"${repo.root}" = "."
}
}

View file

@ -5,7 +5,7 @@ import (
"flag"
"io"
"code.icb4dc0.de/buildr/buildr/modules/buildr"
"code.icb4dc0.de/buildr/buildr/internal/execution"
"code.icb4dc0.de/buildr/buildr/internal/rpc"
"code.icb4dc0.de/buildr/buildr/modules"
@ -43,8 +43,8 @@ func (c *VaultInitConfig) Flags() *flag.FlagSet {
return fs
}
type BuildrAccessor interface {
BuildR() buildr.Buildr
type ExecutionSpecAccessor interface {
ExecutionSpec() execution.Spec
}
type AppConfigAccessor interface {

View file

@ -33,8 +33,8 @@ import (
"code.icb4dc0.de/buildr/buildr/internal/vault"
"code.icb4dc0.de/buildr/buildr/modules"
"code.icb4dc0.de/buildr/buildr/modules/build"
"code.icb4dc0.de/buildr/buildr/modules/buildr"
"code.icb4dc0.de/buildr/buildr/modules/packaging"
"code.icb4dc0.de/buildr/buildr/modules/repo"
"code.icb4dc0.de/buildr/buildr/modules/task"
"code.icb4dc0.de/buildr/buildr/modules/tool"
@ -155,15 +155,17 @@ var (
type App struct {
*services.Collection
loggingCfg logging.Config
appCfg AppConfig
rootCmd *cobra.Command
recorder *profiling.Recorder
initializers map[InitLevel]AppInitializer
buildrInstance *buildr.Buildr
repo *modules.Repository
pluginMgr *modules.PluginManager
parsingState struct {
loggingCfg logging.Config
appCfg AppConfig
rootCmd *cobra.Command
recorder *profiling.Recorder
initializers map[InitLevel]AppInitializer
execSpec *execution.Spec
vcs any
repoDetails repo.Repo
repo *modules.Repository
pluginMgr *modules.PluginManager
parsingState struct {
parsingRemainder hcl2.Body
currentEvalCtx *hcl2.EvalContext
}
@ -238,8 +240,8 @@ func (a *App) AppConfig() AppConfig {
return a.appCfg
}
func (a *App) BuildR() buildr.Buildr {
return *a.buildrInstance
func (a *App) ExecutionSpec() execution.Spec {
return *a.execSpec
}
func (a *App) BootstrapModule(cat modules.Category, typeName, moduleName string) error {
@ -278,7 +280,7 @@ func (a *App) RunModule(ctx context.Context, cat modules.Category, name string)
return err
}
return plan.Execute(ctx, *a.buildrInstance)
return plan.Execute(ctx, *a.execSpec)
}
func (a *App) ArgProviderFor(cat modules.Category) ModuleArgsProvider {
@ -326,7 +328,11 @@ func (a *App) basicParseConfig(ctx context.Context) (err error) {
_ = a.Collection.With(services.WithDiagnosticsWriter(parser.DiagsWriter()))
if a.buildrInstance, err = completeSpec.BuildrCfg.InitBuildr(a.appCfg.BuildRDirectory, a.appCfg.RepoRoot); err != nil {
if a.vcs, a.repoDetails, err = a.appCfg.ParseVCSInfo(); err != nil {
return err
}
if a.execSpec, err = completeSpec.BuildrCfg.PrepareExecutionSpec(a.appCfg.BuildRDirectory, a.appCfg.RepoRoot); err != nil {
return err
}
@ -335,12 +341,12 @@ func (a *App) basicParseConfig(ctx context.Context) (err error) {
return err
}
a.pluginMgr = modules.NewPluginManager(a.StateStore(), a.buildrInstance.Config.CacheDirectory)
a.pluginMgr = modules.NewPluginManager(a.StateStore(), a.execSpec.CacheDirectory)
if err = a.pluginMgr.Add(ctx, pluginURLs...); err != nil {
return err
}
if a.repo, err = completeSpec.ModulesSpec.Repository(evalCtx, a.DiagsWriter(), *a.buildrInstance, a.TypeRegistry()); err != nil {
if a.repo, err = completeSpec.ModulesSpec.Repository(evalCtx, a.DiagsWriter(), a.TypeRegistry(), a.execSpec.OutDirectory); err != nil {
return err
}
@ -393,7 +399,11 @@ func (a *App) initBuildRConfig(ctx context.Context) (err error) {
_ = a.Collection.With(services.WithDiagnosticsWriter(parser.DiagsWriter()))
a.buildrInstance, err = buildrCfg.InitBuildr(a.appCfg.BuildRDirectory, a.appCfg.RepoRoot)
if a.vcs, a.repoDetails, err = a.appCfg.ParseVCSInfo(); err != nil {
return err
}
a.execSpec, err = buildrCfg.PrepareExecutionSpec(a.appCfg.BuildRDirectory, a.appCfg.RepoRoot)
if err != nil {
return err
}
@ -403,16 +413,12 @@ func (a *App) initBuildRConfig(ctx context.Context) (err error) {
return err
}
a.pluginMgr = modules.NewPluginManager(a.StateStore(), a.buildrInstance.Config.CacheDirectory)
a.pluginMgr = modules.NewPluginManager(a.StateStore(), a.execSpec.CacheDirectory)
if err = a.pluginMgr.Add(ctx, pluginURLs...); err != nil {
return err
}
if err = a.Collection.With(services.WithGitHubTokenClient(ctx, a.buildrInstance.GitHub.APIToken)); err != nil {
return err
}
if err = copyCurrentBinaryToBinariesDir(a.buildrInstance.Config.BinariesDirectory); err != nil {
if err = copyCurrentBinaryToBinariesDir(a.execSpec.BinariesDirectory); err != nil {
return err
}
@ -434,7 +440,15 @@ func (a *App) initParseConfigs(ctx context.Context) (err error) {
return errs.ErrAlreadyLogged
}
if a.repo, err = rawSpec.Repository(evalCtx, a.DiagsWriter(), *a.buildrInstance, a.TypeRegistry()); err != nil {
a.repo, err = rawSpec.Repository(
evalCtx,
a.DiagsWriter(),
a.TypeRegistry(),
a.execSpec.OutDirectory,
hcl.WithGlobalVariable("vcs", a.vcs),
hcl.WithGlobalVariable("repo", a.repoDetails),
)
if err != nil {
return err
}

View file

@ -9,16 +9,16 @@ import (
var _ EnvCommander = (*EnvApp)(nil)
func NewEnvApp(initializer LevelInitializer, accessor BuildrAccessor) *EnvApp {
func NewEnvApp(initializer LevelInitializer, accessor ExecutionSpecAccessor) *EnvApp {
return &EnvApp{
initializer: initializer,
buildrAccessor: accessor,
initializer: initializer,
executionSpecAccessor: accessor,
}
}
type EnvApp struct {
initializer LevelInitializer
buildrAccessor BuildrAccessor
initializer LevelInitializer
executionSpecAccessor ExecutionSpecAccessor
}
func (e EnvApp) PrintPath(writer io.Writer) (err error) {
@ -26,14 +26,14 @@ func (e EnvApp) PrintPath(writer io.Writer) (err error) {
return err
}
buildr := e.buildrAccessor.BuildR()
execSpec := e.executionSpecAccessor.ExecutionSpec()
currentPath, ok := os.LookupEnv("PATH")
if !ok {
_, err = fmt.Fprintf(writer, "PATH=%s", buildr.Config.BinariesDirectory)
_, err = fmt.Fprintf(writer, "PATH=%s", execSpec.BinariesDirectory)
return err
}
joinedPath := strings.Join([]string{buildr.Config.BinariesDirectory, currentPath}, string(os.PathListSeparator))
joinedPath := strings.Join([]string{execSpec.BinariesDirectory, currentPath}, string(os.PathListSeparator))
_, err = fmt.Fprintf(writer, "PATH=%s", joinedPath)
return err
}

View file

@ -4,12 +4,16 @@ import (
"context"
"errors"
"flag"
"fmt"
"io/fs"
"os"
"path/filepath"
"strconv"
"time"
"code.icb4dc0.de/buildr/buildr/internal/semver"
"code.icb4dc0.de/buildr/buildr/modules/repo"
"code.icb4dc0.de/buildr/buildr/modules/state"
"code.icb4dc0.de/buildr/buildr/internal/config"
@ -42,6 +46,30 @@ type AppConfig struct {
}
}
func (c *AppConfig) ParseVCSInfo() (vcsDetails any, repoDetails repo.Repo, err error) {
repoDetails.Root = c.RepoRoot
switch c.VCSType {
case vcs.TypeGit:
gitInfo, err := vcs.ParseGitInfo(c.RepoRoot)
if err != nil {
return nil, repoDetails, err
}
if tag := gitInfo.Tag; tag != "" && semver.IsValid(tag) {
if repoDetails.Version, err = semver.ParseVersion(tag); err != nil {
return nil, repoDetails, err
} else {
return gitInfo, repoDetails, nil
}
} else {
return gitInfo, repoDetails, nil
}
default:
return nil, repoDetails, fmt.Errorf("unsupported VCS type: %s", c.VCSType)
}
}
func (c *AppConfig) RepoFS() fs.FS {
return os.DirFS(c.RepoRoot)
}

View file

@ -4,16 +4,23 @@ import (
"context"
"code.icb4dc0.de/buildr/buildr/modules"
"code.icb4dc0.de/buildr/buildr/modules/buildr"
)
type Spec struct {
RepoRoot string
BinariesDirectory string
CacheDirectory string
OutDirectory string
LogsDirectory string
LogToStdErr bool
}
type TaskProvider interface {
CanProvide(m modules.ModuleWithMeta) bool
Create(m modules.ModuleWithMeta) (Task, error)
}
type Task interface {
Execute(ctx context.Context, b buildr.Buildr) error
Execute(ctx context.Context, spec Spec) error
AddDependentTask(other Task)
}

View file

@ -3,8 +3,6 @@ package execution
import (
"context"
"code.icb4dc0.de/buildr/buildr/modules/buildr"
"golang.org/x/sync/errgroup"
)
@ -16,7 +14,7 @@ func (d *TaskDependencies) AddDependentTask(other Task) {
*d = deps
}
func (d *TaskDependencies) Execute(ctx context.Context, b buildr.Buildr) error {
func (d *TaskDependencies) Execute(ctx context.Context, spec Spec) error {
if len(*d) < 1 {
return nil
}
@ -27,7 +25,7 @@ func (d *TaskDependencies) Execute(ctx context.Context, b buildr.Buildr) error {
for i := range deps {
dep := deps[i]
grp.Go(func() error {
return dep.Execute(grpCtx, b)
return dep.Execute(grpCtx, spec)
})
}

View file

@ -25,7 +25,6 @@ import (
"code.icb4dc0.de/buildr/buildr/internal/ioutils"
"code.icb4dc0.de/buildr/buildr/internal/logging"
"code.icb4dc0.de/buildr/buildr/modules"
"code.icb4dc0.de/buildr/buildr/modules/buildr"
)
type containerTask struct {
@ -37,16 +36,16 @@ type containerTask struct {
moduleWithMeta modules.ModuleWithMeta
}
func (c *containerTask) Execute(ctx context.Context, b buildr.Buildr) (err error) {
func (c *containerTask) Execute(ctx context.Context, spec execution.Spec) (err error) {
c.once.Do(func() {
err = c.doExecute(ctx, b)
err = c.doExecute(ctx, spec)
})
return err
}
func (c *containerTask) doExecute(ctx context.Context, b buildr.Buildr) (err error) {
if err = c.TaskDependencies.Execute(ctx, b); err != nil {
func (c *containerTask) doExecute(ctx context.Context, spec execution.Spec) (err error) {
if err = c.TaskDependencies.Execute(ctx, spec); err != nil {
return err
}
@ -81,8 +80,8 @@ func (c *containerTask) doExecute(ctx context.Context, b buildr.Buildr) (err err
}
outputSink, err := logging.NewTaskOutputSink(
b.Config.Logging.LogsDirectory,
b.Config.Logging.LogToStdErr,
spec.LogsDirectory,
spec.LogToStdErr,
modules.LogFileNameFormatter(c.moduleWithMeta),
)
if err != nil {
@ -96,25 +95,25 @@ func (c *containerTask) doExecute(ctx context.Context, b buildr.Buildr) (err err
inputMappings := c.moduleWithMeta.InputMappings()
if len(inputMappings) == 0 {
inputMappings[b.Repo.Root] = "."
inputMappings[spec.RepoRoot] = "."
}
containerSpec := c.moduleWithMeta.ContainerSpec()
spec := containers.BuildRContainerSpec{
buildrContainerSpec := containers.BuildRContainerSpec{
ID: c.moduleWithMeta.ID(),
ModuleName: c.moduleWithMeta.Name(),
Image: containerSpec.Image,
User: containerSpec.User,
Privileged: containerSpec.Privileged,
RepoRoot: b.Repo.Root,
RepoRoot: spec.RepoRoot,
Content: inputMappings,
BinariesDir: b.Config.BinariesDirectory,
BinariesDir: spec.BinariesDirectory,
ExtraBinaries: extraBinaries,
Mounts: containerSpec.Mounts(),
}
logger.Debug("Preparing container")
con, grpcConn, err := c.orchestrator.BuildRContainer(ctx, spec)
con, grpcConn, err := c.orchestrator.BuildRContainer(ctx, buildrContainerSpec)
if err != nil {
return fmt.Errorf("failed to create container for task %s/%s: %w", c.moduleWithMeta.Type(), c.moduleWithMeta.Name(), err)
}
@ -195,7 +194,7 @@ func (c *containerTask) doExecute(ctx context.Context, b buildr.Buildr) (err err
if msg.TaskResult.ModifiedFilesArchivePath != "" {
outDir := c.moduleWithMeta.OutDir()
if c.moduleWithMeta.Category() == modules.CategoryTool {
outDir = b.Config.BinariesDirectory
outDir = spec.BinariesDirectory
}
if err := c.handleModifiedFiles(ctx, con, msg.TaskResult.ModifiedFilesArchivePath, outDir); err != nil {
return fmt.Errorf("failed to fetch modified files from execution container: %w", err)

View file

@ -13,8 +13,6 @@ import (
"code.icb4dc0.de/buildr/buildr/internal/execution"
"code.icb4dc0.de/buildr/buildr/internal/storage"
"code.icb4dc0.de/buildr/buildr/modules"
"code.icb4dc0.de/buildr/buildr/modules/buildr"
"golang.org/x/exp/slog"
"golang.org/x/sync/errgroup"
)
@ -28,16 +26,16 @@ type localTask struct {
stateStore state.Store
}
func (t *localTask) Execute(ctx context.Context, b buildr.Buildr) (err error) {
func (t *localTask) Execute(ctx context.Context, spec execution.Spec) (err error) {
t.once.Do(func() {
err = t.doExecute(ctx, b)
err = t.doExecute(ctx, spec)
})
return err
}
func (t *localTask) doExecute(ctx context.Context, b buildr.Buildr) (err error) {
if err = t.TaskDependencies.Execute(ctx, b); err != nil {
func (t *localTask) doExecute(ctx context.Context, spec execution.Spec) (err error) {
if err = t.TaskDependencies.Execute(ctx, spec); err != nil {
return err
}
@ -52,17 +50,15 @@ func (t *localTask) doExecute(ctx context.Context, b buildr.Buildr) (err error)
}
if mappings := t.module.InputMappings(); len(mappings) > 0 || t.module.Category() == modules.CategoryTool {
return t.executeIsolated(ctx, b)
return t.executeIsolated(ctx, spec)
}
execCtx, err := t.executionContextFor(
ctx,
t.module,
b.Repo.Root,
spec,
spec.RepoRoot,
t.module.OutDir(),
b.Config.BinariesDirectory,
b.Config.Logging.LogsDirectory,
b.Config.Logging.LogToStdErr,
)
if err != nil {
return fmt.Errorf("failed to prepare execution context: %w", err)
@ -80,7 +76,7 @@ func (t *localTask) doExecute(ctx context.Context, b buildr.Buildr) (err error)
return t.module.Execute(execCtx)
}
func (t *localTask) executeIsolated(ctx context.Context, b buildr.Buildr) error {
func (t *localTask) executeIsolated(ctx context.Context, spec execution.Spec) error {
workDir, err := os.MkdirTemp(os.TempDir(), "buildr-work-*")
if err != nil {
return err
@ -92,7 +88,7 @@ func (t *localTask) executeIsolated(ctx context.Context, b buildr.Buildr) error
outDir := t.module.OutDir()
if t.module.Category() == modules.CategoryTool {
outDir = b.Config.BinariesDirectory
outDir = spec.BinariesDirectory
}
mappings := t.module.InputMappings()
@ -106,7 +102,7 @@ func (t *localTask) executeIsolated(ctx context.Context, b buildr.Buildr) error
}
if !filepath.IsAbs(outDir) {
outDir = filepath.Join(b.Repo.Root, outDir)
outDir = filepath.Join(spec.RepoRoot, outDir)
}
ufs := storage.NewUnionFS(outDir)
@ -136,16 +132,13 @@ func (t *localTask) executeIsolated(ctx context.Context, b buildr.Buildr) error
defer func() {
err = errors.Join(err, srv.Unmount())
}()
b.Repo.Root = workDir
execCtx, err := t.executionContextFor(
grpcCtx,
t.module,
spec,
workDir,
outDir,
b.Config.BinariesDirectory,
b.Config.Logging.LogsDirectory,
b.Config.Logging.LogToStdErr,
)
if err != nil {
@ -161,8 +154,8 @@ func (t *localTask) executeIsolated(ctx context.Context, b buildr.Buildr) error
func (t *localTask) executionContextFor(
ctx context.Context,
m modules.ModuleWithMeta,
workingDir, outDir, binDir, logsDir string,
logToStdErr bool,
spec execution.Spec,
workingDir, outDir string,
) (DefaultExecutionContext, error) {
return NewDefaultExecutionContext(
ctx,
@ -170,9 +163,9 @@ func (t *localTask) executionContextFor(
m,
workingDir,
outDir,
binDir,
logsDir,
logToStdErr,
spec.BinariesDirectory,
spec.LogsDirectory,
spec.LogToStdErr,
WithLoggerFactory(func() *slog.Logger {
return slog.Default().With(
slog.String("module_name", m.Name()),

View file

@ -5,8 +5,6 @@ import (
"fmt"
"code.icb4dc0.de/buildr/buildr/modules"
"code.icb4dc0.de/buildr/buildr/modules/buildr"
)
func NewPlanFor(
@ -34,6 +32,6 @@ type Plan struct {
entryPoint Task
}
func (p *Plan) Execute(ctx context.Context, b buildr.Buildr) error {
return p.entryPoint.Execute(ctx, b)
func (p *Plan) Execute(ctx context.Context, spec Spec) error {
return p.entryPoint.Execute(ctx, spec)
}

View file

@ -5,10 +5,7 @@ import (
"os"
"path/filepath"
"code.icb4dc0.de/buildr/buildr/internal/semver"
"code.icb4dc0.de/buildr/buildr/modules/vcs"
"code.icb4dc0.de/buildr/buildr/modules/buildr"
"code.icb4dc0.de/buildr/buildr/internal/execution"
)
const defaultDirectoryPermissions = 0o755
@ -45,83 +42,65 @@ func (c BuildrConfig) PluginURLs() (pluginUrls []*url.URL, err error) {
return pluginUrls, nil
}
func (c BuildrConfig) InitBuildr(buildRDir, repoRoot string) (b *buildr.Buildr, err error) {
b = &buildr.Buildr{
Repo: buildr.Repo{
Root: repoRoot,
},
}
if c.GitHub != nil {
b.GitHub.APIToken = c.GitHub.APIToken
}
if b.Repo.Git, err = vcs.ParseGitInfo(repoRoot); err != nil {
return nil, err
}
if b.Repo.Git != nil {
if tag := b.Repo.Git.Tag; tag != "" && semver.IsValid(tag) {
if b.Repo.Version, err = semver.ParseVersion(tag); err != nil {
return nil, err
}
}
func (c BuildrConfig) PrepareExecutionSpec(buildRDir, repoRoot string) (s *execution.Spec, err error) {
s = &execution.Spec{
RepoRoot: repoRoot,
}
if cacheDir, err := os.UserCacheDir(); err != nil {
return nil, err
} else {
b.Config.CacheDirectory = filepath.Join(cacheDir, "buildr")
s.CacheDirectory = filepath.Join(cacheDir, "buildr")
}
if err = c.createCleanDir(b.Config.CacheDirectory, false); err != nil {
if err = c.createCleanDir(s.CacheDirectory, false); err != nil {
return nil, err
}
if filepath.IsAbs(c.BinariesDirectory) {
b.Config.BinariesDirectory = c.BinariesDirectory
s.BinariesDirectory = c.BinariesDirectory
} else if c.BinariesDirectory == "" {
b.Config.BinariesDirectory = filepath.Join(buildRDir, "bin")
s.BinariesDirectory = filepath.Join(buildRDir, "bin")
} else {
b.Config.BinariesDirectory = filepath.Join(b.Repo.Root, c.BinariesDirectory)
s.BinariesDirectory = filepath.Join(repoRoot, c.BinariesDirectory)
}
if err = c.createCleanDir(b.Config.BinariesDirectory, false); err != nil {
if err = c.createCleanDir(s.BinariesDirectory, false); err != nil {
return nil, err
}
if filepath.IsAbs(c.OutDirectory) {
b.Config.OutDirectory = c.OutDirectory
s.OutDirectory = c.OutDirectory
} else if c.OutDirectory == "" {
b.Config.OutDirectory = filepath.Join(buildRDir, "out")
s.OutDirectory = filepath.Join(buildRDir, "out")
} else {
b.Config.OutDirectory = filepath.Join(b.Repo.Root, c.OutDirectory)
s.OutDirectory = filepath.Join(repoRoot, c.OutDirectory)
}
if err = c.createCleanDir(b.Config.OutDirectory, true); err != nil {
if err = c.createCleanDir(s.OutDirectory, true); err != nil {
return nil, err
}
b.Config.Logging.LogToStdErr = c.LogToStderr
s.LogToStdErr = c.LogToStderr
if c.LogToStderr {
b.Config.Logging.LogToStdErr = c.LogToStderr
return b, nil
s.LogToStdErr = c.LogToStderr
return s, nil
}
if filepath.IsAbs(c.LogsDirectory) {
b.Config.BinariesDirectory = c.LogsDirectory
s.BinariesDirectory = c.LogsDirectory
} else if c.LogsDirectory == "" {
b.Config.Logging.LogsDirectory = filepath.Join(buildRDir, "logs")
s.LogsDirectory = filepath.Join(buildRDir, "logs")
} else {
b.Config.Logging.LogsDirectory = filepath.Join(b.Repo.Root, c.LogsDirectory)
s.LogsDirectory = filepath.Join(repoRoot, c.LogsDirectory)
}
if err = c.createCleanDir(b.Config.Logging.LogsDirectory, true); err != nil {
if err = c.createCleanDir(s.LogsDirectory, true); err != nil {
return nil, err
}
return b, nil
return s, nil
}
func (c BuildrConfig) createCleanDir(dir string, clean bool) error {

View file

@ -1,6 +1,7 @@
package hcl
import (
"code.icb4dc0.de/buildr/buildr/modules/helpers/paths"
"context"
"os"
"strings"
@ -28,8 +29,9 @@ func MockContext() *hcl.EvalContext {
env.RegisterInContext(evalctx)
string_helpers.RegisterInContext(evalctx)
hclhelpers.RegisterInContext(evalctx)
paths.RegisterInContext(evalctx)
vaultHelpers.RegisterInContext(evalctx, vaultHelpers.MockGetter("<mocked>"))
github.RegisterInContext(context.Background(), evalctx, nil, nil)
github.RegisterInContext(context.Background(), evalctx, nil)
return evalctx
}
@ -45,6 +47,7 @@ func BasicContext(vaultGetter vaultHelpers.VaultGetter) *hcl.EvalContext {
env.RegisterInContext(evalctx)
string_helpers.RegisterInContext(evalctx)
hclhelpers.RegisterInContext(evalctx)
paths.RegisterInContext(evalctx)
vaultHelpers.RegisterInContext(evalctx, vaultGetter)
return evalctx
@ -65,7 +68,7 @@ func FullContext(ctx context.Context, parent *hcl.EvalContext, svc *services.Col
evalctx.Variables = make(map[string]cty.Value)
}
github.RegisterInContext(ctx, evalctx, svc.GitHubClient(), svc.Cache())
github.RegisterInContext(ctx, evalctx, svc.Cache())
return evalctx
}

View file

@ -9,14 +9,13 @@ import (
"github.com/zclconf/go-cty/cty"
"code.icb4dc0.de/buildr/buildr/modules"
"code.icb4dc0.de/buildr/buildr/modules/buildr"
)
func (s ModulesSpec) prepareBlockForParsing(
block GenericBlock,
modType modules.Category,
evalCtx *hcl.EvalContext,
buildrInstance buildr.Buildr,
outDir string,
) (blockPreparationResult, error) {
syntaxBlock, ok := block.BlockBody.(*hclsyntax.Body)
if !ok {
@ -30,7 +29,7 @@ func (s ModulesSpec) prepareBlockForParsing(
forEachAttr, ok := syntaxBlock.Attributes["for_each"]
if !ok {
initBlock(syntaxBlock, modType, block.BlockName, buildrInstance.Config.OutDirectory)
initBlock(syntaxBlock, modType, block.BlockName, outDir)
bodyAsValue, _ := s.parseSyntaxBodyToObject(syntaxBlock, evalCtx, true)
return blockPreparationResult{
Specs: []blockParsingSpec{parsingSpecOf(modType, block, nil)},
@ -98,7 +97,7 @@ func (s ModulesSpec) prepareBlockForParsing(
gb := GenericBlock{
ModuleName: block.ModuleName,
BlockName: blockName,
BlockBody: initBlock(blockCopy, modType, blockName, buildrInstance.Config.OutDirectory),
BlockBody: initBlock(blockCopy, modType, blockName, outDir),
}
result.Specs = append(result.Specs, parsingSpecOf(modType, gb, vals))

View file

@ -3,11 +3,29 @@ package hcl
import (
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/gocty"
"code.icb4dc0.de/buildr/buildr/modules"
"code.icb4dc0.de/buildr/buildr/modules/buildr"
)
type RepositoryOption func(evalCtx *hcl.EvalContext) error
func WithGlobalVariable(key string, value any) RepositoryOption {
return func(evalCtx *hcl.EvalContext) error {
ctyType, err := gocty.ImpliedType(value)
if err != nil {
return err
}
mapped, err := gocty.ToCtyValue(value, ctyType)
if err != nil {
return err
}
evalCtx.Variables[key] = mapped
return nil
}
}
type ModulesSpec struct {
Locals []LocalsBlock `hcl:"locals,block"`
Tools GenericBlocks `hcl:"tool,block"`
@ -21,14 +39,17 @@ type ModulesSpec struct {
func (s ModulesSpec) Repository(
evalCtx *hcl.EvalContext,
diagsWriter hcl.DiagnosticWriter,
b buildr.Buildr,
registry *modules.TypeRegistry,
outDir string,
opts ...RepositoryOption,
) (repo *modules.Repository, err error) {
repo = new(modules.Repository)
s.diagsWriter = diagsWriter
if evalCtx.Variables["buildr"], err = mapToCtyVal(b, mappingCfg{}); err != nil {
return nil, err
for _, opt := range opts {
if err := opt(evalCtx); err != nil {
return nil, err
}
}
if err = s.parseLocals(s.Locals, evalCtx); err != nil {
@ -37,25 +58,25 @@ func (s ModulesSpec) Repository(
parsingSpecs := make([]blockParsingSpec, 0, len(s.Tools)+len(s.Tasks)+len(s.Builds))
if specs, err := s.buildParsingInventory(modules.CategoryTool, s.Tools, evalCtx, b); err != nil {
if specs, err := s.buildParsingInventory(modules.CategoryTool, s.Tools, evalCtx, outDir); err != nil {
return nil, err
} else {
parsingSpecs = append(parsingSpecs, specs...)
}
if specs, err := s.buildParsingInventory(modules.CategoryTask, s.Tasks, evalCtx, b); err != nil {
if specs, err := s.buildParsingInventory(modules.CategoryTask, s.Tasks, evalCtx, outDir); err != nil {
return nil, err
} else {
parsingSpecs = append(parsingSpecs, specs...)
}
if specs, err := s.buildParsingInventory(modules.CategoryBuild, s.Builds, evalCtx, b); err != nil {
if specs, err := s.buildParsingInventory(modules.CategoryBuild, s.Builds, evalCtx, outDir); err != nil {
return nil, err
} else {
parsingSpecs = append(parsingSpecs, specs...)
}
if specs, err := s.buildParsingInventory(modules.CategoryPackage, s.Packages, evalCtx, b); err != nil {
if specs, err := s.buildParsingInventory(modules.CategoryPackage, s.Packages, evalCtx, outDir); err != nil {
return nil, err
} else {
parsingSpecs = append(parsingSpecs, specs...)
@ -74,7 +95,7 @@ func (s ModulesSpec) buildParsingInventory(
modType modules.Category,
blocks []GenericBlock,
evalCtx *hcl.EvalContext,
buildrInstance buildr.Buildr,
outDir string,
) ([]blockParsingSpec, error) {
if len(blocks) == 0 {
return nil, nil
@ -86,7 +107,7 @@ func (s ModulesSpec) buildParsingInventory(
blockGroupParsingSpecs := make([]blockParsingSpec, 0)
for i := range blocks {
result, err := s.prepareBlockForParsing(blocks[i], modType, evalCtx, buildrInstance)
result, err := s.prepareBlockForParsing(blocks[i], modType, evalCtx, outDir)
if err != nil {
return nil, err
}

View file

@ -8,8 +8,6 @@ import (
"os"
"path/filepath"
"strings"
"code.icb4dc0.de/buildr/buildr/modules/buildr"
)
const FileName = ".buildrignore"
@ -19,29 +17,6 @@ var (
_ fs.ReadDirFS = (*Ignorer)(nil)
)
// NewRootIgnorer returns a new Ignorer with the default ignore patterns included
func NewRootIgnorer(b *buildr.Buildr, additionalPatterns ...string) (*Ignorer, error) {
for i := range additionalPatterns {
rel, err := filepath.Rel(b.Repo.Root, additionalPatterns[i])
if err != nil {
return nil, err
}
additionalPatterns[i] = rel
}
ig, err := NewIgnorer(b.Repo.Root, additionalPatterns...)
if err != nil {
return nil, err
}
if err := defaultIgnorePatterns(b, ig); err != nil {
return nil, err
}
return ig, nil
}
func MustNewIgnorer(dir string, additionalPatterns ...string) *Ignorer {
ig, err := NewIgnorer(dir, additionalPatterns...)
if err != nil {
@ -237,29 +212,3 @@ func readGitIgnore(content io.Reader) (patterns []string, err error) {
}
return patterns, scanner.Err()
}
func defaultIgnorePatterns(b *buildr.Buildr, ig *Ignorer) error {
appendRelBuildRDir := func(buildRDir string) error {
if buildRDir == "" {
return nil
}
relDir, err := filepath.Rel(b.Repo.Root, buildRDir)
if err != nil {
return fmt.Errorf("failed to make dir %s relative to Buildr repo root %s: %w", buildRDir, b.Repo.Root, err)
}
if !strings.HasSuffix(relDir, "/") {
relDir += "/"
}
return ig.AddPatterns(relDir)
}
for _, d := range []string{b.Config.BinariesDirectory, b.Config.OutDirectory, b.Config.Logging.LogsDirectory} {
if err := appendRelBuildRDir(d); err != nil {
return err
}
}
return nil
}

View file

@ -49,9 +49,9 @@ func ParseVersion(version string) (v Version, err error) {
}
type Version struct {
Major int
Minor int
Patch int
Prerelease string
BuildMetadata string
Major int `cty:"major"`
Minor int `cty:"minor"`
Patch int `cty:"patch"`
Prerelease string `cty:"prerelease"`
BuildMetadata string `cty:"build_metadata"`
}

View file

@ -2,18 +2,15 @@ package services
import (
"context"
"net/http"
"github.com/hashicorp/hcl/v2"
"code.icb4dc0.de/buildr/buildr/modules/state"
"github.com/docker/docker/client"
gh "github.com/google/go-github/v50/github"
"code.icb4dc0.de/buildr/buildr/internal/ignore"
"code.icb4dc0.de/buildr/buildr/internal/vault"
"code.icb4dc0.de/buildr/buildr/modules"
"github.com/docker/docker/client"
)
type CollectionOption interface {
@ -87,20 +84,8 @@ func WithDockerClientFromEnv(ctx context.Context) CollectionOption {
})
}
func WithGitHubTokenClient(ctx context.Context, token string) CollectionOption {
return collectionOptionFunc(func(svc *Collection) error {
if token != "" {
svc.ghClient = gh.NewTokenClient(ctx, token)
}
return nil
})
}
func NewCollection(opts ...CollectionOption) (*Collection, error) {
svc := &Collection{
ghClient: gh.NewClient(http.DefaultClient),
}
svc := new(Collection)
if err := svc.With(opts...); err != nil {
return nil, err
@ -111,7 +96,6 @@ func NewCollection(opts ...CollectionOption) (*Collection, error) {
type Collection struct {
registry *modules.TypeRegistry
ghClient *gh.Client
vault *vault.Vault
dockerClient *client.Client
ignorer *ignore.Ignorer
@ -138,10 +122,6 @@ func (c *Collection) Cache() state.Cache {
return c.cache
}
func (c *Collection) GitHubClient() *gh.Client {
return c.ghClient
}
func (c *Collection) TypeRegistry() *modules.TypeRegistry {
return c.registry
}

View file

@ -1,22 +0,0 @@
package buildr
import (
"code.icb4dc0.de/buildr/buildr/internal/semver"
"code.icb4dc0.de/buildr/buildr/modules/vcs"
)
type Repo struct {
Root string `hcl:"root"`
Git *vcs.Git `hcl:"git"`
Version semver.Version `hcl:"version"`
}
type GitHub struct {
APIToken string `hcl:"api_token"`
}
type Buildr struct {
Config Config `hcl:"config"`
Repo Repo `hcl:"repo"`
GitHub GitHub `hcl:"github"`
}

View file

@ -1,13 +0,0 @@
package buildr
type Config struct {
OutDirectory string `hcl:"out_dir"`
BinariesDirectory string `hcl:"bin_dir"`
CacheDirectory string `hcl:"cache_dir"`
Logging Logging `hcl:"logging"`
}
type Logging struct {
LogToStdErr bool `hcl:"log_to_stdout"`
LogsDirectory string `hcl:"logs_dir"`
}

View file

@ -2,18 +2,20 @@ package github
import (
"context"
"net/http"
"time"
gh "github.com/google/go-github/v50/github"
"code.icb4dc0.de/buildr/buildr/modules/state"
"github.com/google/go-github/v50/github"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
)
const DefaultAPITimeout = 30 * time.Second
func GetLatestReleaseTag(ctx context.Context, cli *github.Client, cache state.Cache) function.Function {
func GetLatestReleaseTag(ctx context.Context, cache state.Cache) function.Function {
return function.New(&function.Spec{
Description: "Get tag of latest release of Github project",
Params: []function.Parameter{
@ -28,6 +30,11 @@ func GetLatestReleaseTag(ctx context.Context, cli *github.Client, cache state.Ca
Type: cty.String,
},
},
VarParam: &function.Parameter{
Name: "token",
Description: "GitHub API token",
Type: cty.String,
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
owner := args[0].AsString()
@ -42,6 +49,14 @@ func GetLatestReleaseTag(ctx context.Context, cli *github.Client, cache state.Ca
}
}
var cli *gh.Client
if len(args) >= 3 {
cli = gh.NewTokenClient(ctx, args[2].AsString())
} else {
cli = gh.NewClient(http.DefaultClient)
}
if cli == nil {
return cty.StringVal("<mocked>"), nil
}

View file

@ -5,10 +5,9 @@ import (
"code.icb4dc0.de/buildr/buildr/modules/state"
"github.com/google/go-github/v50/github"
"github.com/hashicorp/hcl/v2"
)
func RegisterInContext(ctx context.Context, evalCtx *hcl.EvalContext, cli *github.Client, cache state.Cache) {
evalCtx.Functions["gh_latest_release"] = GetLatestReleaseTag(ctx, cli, cache)
func RegisterInContext(ctx context.Context, evalCtx *hcl.EvalContext, cache state.Cache) {
evalCtx.Functions["gh_latest_release"] = GetLatestReleaseTag(ctx, cache)
}

View file

@ -0,0 +1,23 @@
package paths
import (
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
"path/filepath"
)
func Dirname() function.Function {
return function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "dirname",
Description: "Strip the filename from the path",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
return cty.StringVal(filepath.Dir(args[0].AsString())), nil
},
})
}

View file

@ -0,0 +1,22 @@
package paths
import (
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
"path/filepath"
)
func FileExtension() function.Function {
return function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "path",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
return cty.StringVal(filepath.Ext(args[0].AsString())), nil
},
})
}

View file

@ -0,0 +1,8 @@
package paths
import "github.com/hashicorp/hcl/v2"
func RegisterInContext(evalCtx *hcl.EvalContext) {
evalCtx.Functions["dirname"] = Dirname()
evalCtx.Functions["ext"] = FileExtension()
}

10
modules/repo/repo.go Normal file
View file

@ -0,0 +1,10 @@
package repo
import (
"code.icb4dc0.de/buildr/buildr/internal/semver"
)
type Repo struct {
Root string `cty:"root"`
Version semver.Version `cty:"version"`
}

View file

@ -1,19 +1,24 @@
package vcs
type GitCommitAuthor struct {
Name string `hcl:"name"`
Email string `hcl:"email"`
Name string `cty:"name"`
Email string `cty:"email"`
}
type GitCommit struct {
Hash string `hcl:"commit_hash"`
Author GitCommitAuthor `hcl:"author"`
Message string `hcl:"message"`
Hash string `cty:"commit_hash"`
Author GitCommitAuthor `cty:"author"`
Message string `cty:"message"`
}
type Git struct {
Commit GitCommit `hcl:"commit"`
Branch string `hcl:"branch"`
Tag string `hcl:"tag"`
Reference string `hcl:"reference"`
Dirty bool `cty:"dirty"`
Commit GitCommit `cty:"commit"`
Branch string `cty:"branch"`
Tag string `cty:"tag"`
Reference string `cty:"reference"`
StagedFiles []string `cty:"staged_files"`
UnstagedFiles []string `cty:"unstaged_files"`
ModifiedFiles []string `cty:"modified_files"`
UntrackedFiles []string `cty:"untracked_files"`
}

View file

@ -3,6 +3,8 @@ package vcs
import (
"fmt"
"golang.org/x/exp/maps"
"github.com/go-git/go-git/v5"
)
@ -14,6 +16,38 @@ func ParseGitInfo(root string) (*Git, error) {
g := new(Git)
workTree, err := repo.Worktree()
if err != nil {
return nil, err
}
stat, err := workTree.Status()
if err != nil {
return nil, err
}
g.Dirty = !stat.IsClean()
uniqueModifiedFiles := make(map[string]bool)
for p, s := range stat {
switch s.Staging {
case git.Added, git.Modified, git.Copied, git.Renamed:
g.StagedFiles = append(g.StagedFiles, p)
uniqueModifiedFiles[p] = true
}
switch s.Worktree {
case git.Added, git.Modified, git.Copied, git.Renamed:
g.UnstagedFiles = append(g.UnstagedFiles, p)
uniqueModifiedFiles[p] = true
case git.Untracked:
g.UntrackedFiles = append(g.UntrackedFiles, p)
}
}
g.ModifiedFiles = maps.Keys(uniqueModifiedFiles)
if head, err := repo.Head(); err != nil {
return nil, fmt.Errorf("failed to read HEAD information: %w", err)
} else {