Skip to content

Commit 8f027ee

Browse files
committed
utils: add reflection-driven dependency container
Add a 'DependencyContainer' type, which contains methods to register and lazily retieve singleton dependencies. This is implemented in anticipation of a substantial increase in the complexity of the dependency hierarchy. Its primary benefit is avoiding the need to initialize an entire hierarchy of dependent structs "manually", hopefully making for easier to read (and modify) code. Note, though, that this design carries some risk: - errors due to unregistered components will only manifest at runtime. - we might see potentially slower startup due to the use of reflection; that said, in manual testing, I didn't notice a slowdown. - we can end up in a recursive loop of dependency resolution, ultimately leading to a stack overflow. To mitigate that risk, add a test 'TestBuildGitBundleServerContainer', which uses a combination of reflection and (somewhat fragile, but documented in the test) abstract syntax tree parsing to verify that the container is fully-registered. Signed-off-by: Victoria Dye <[email protected]>
1 parent 9c50d33 commit 8f027ee

File tree

11 files changed

+285
-46
lines changed

11 files changed

+285
-46
lines changed

cmd/git-bundle-server/delete.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@ import (
44
"context"
55
"os"
66

7+
"github.com/github/git-bundle-server/cmd/utils"
78
"github.com/github/git-bundle-server/internal/argparse"
89
"github.com/github/git-bundle-server/internal/core"
910
"github.com/github/git-bundle-server/internal/log"
1011
)
1112

1213
type deleteCmd struct {
13-
logger log.TraceLogger
14+
logger log.TraceLogger
15+
container *utils.DependencyContainer
1416
}
1517

16-
func NewDeleteCommand(logger log.TraceLogger) argparse.Subcommand {
18+
func NewDeleteCommand(logger log.TraceLogger, container *utils.DependencyContainer) argparse.Subcommand {
1719
return &deleteCmd{
18-
logger: logger,
20+
logger: logger,
21+
container: container,
1922
}
2023
}
2124

cmd/git-bundle-server/init.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66

7+
"github.com/github/git-bundle-server/cmd/utils"
78
"github.com/github/git-bundle-server/internal/argparse"
89
"github.com/github/git-bundle-server/internal/bundles"
910
"github.com/github/git-bundle-server/internal/core"
@@ -12,12 +13,14 @@ import (
1213
)
1314

1415
type initCmd struct {
15-
logger log.TraceLogger
16+
logger log.TraceLogger
17+
container *utils.DependencyContainer
1618
}
1719

18-
func NewInitCommand(logger log.TraceLogger) argparse.Subcommand {
20+
func NewInitCommand(logger log.TraceLogger, container *utils.DependencyContainer) argparse.Subcommand {
1921
return &initCmd{
20-
logger: logger,
22+
logger: logger,
23+
container: container,
2124
}
2225
}
2326

cmd/git-bundle-server/main.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,22 @@ import (
44
"context"
55
"os"
66

7+
"github.com/github/git-bundle-server/cmd/utils"
78
"github.com/github/git-bundle-server/internal/argparse"
89
"github.com/github/git-bundle-server/internal/log"
910
)
1011

1112
func all(logger log.TraceLogger) []argparse.Subcommand {
13+
container := utils.BuildGitBundleServerContainer(logger)
14+
1215
return []argparse.Subcommand{
13-
NewDeleteCommand(logger),
14-
NewInitCommand(logger),
15-
NewStartCommand(logger),
16-
NewStopCommand(logger),
17-
NewUpdateCommand(logger),
18-
NewUpdateAllCommand(logger),
19-
NewWebServerCommand(logger),
16+
NewDeleteCommand(logger, container),
17+
NewInitCommand(logger, container),
18+
NewStartCommand(logger, container),
19+
NewStopCommand(logger, container),
20+
NewUpdateCommand(logger, container),
21+
NewUpdateAllCommand(logger, container),
22+
NewWebServerCommand(logger, container),
2023
}
2124
}
2225

cmd/git-bundle-server/start.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@ import (
44
"context"
55
"os"
66

7+
"github.com/github/git-bundle-server/cmd/utils"
78
"github.com/github/git-bundle-server/internal/argparse"
89
"github.com/github/git-bundle-server/internal/core"
910
"github.com/github/git-bundle-server/internal/log"
1011
)
1112

1213
type startCmd struct {
13-
logger log.TraceLogger
14+
logger log.TraceLogger
15+
container *utils.DependencyContainer
1416
}
1517

16-
func NewStartCommand(logger log.TraceLogger) argparse.Subcommand {
18+
func NewStartCommand(logger log.TraceLogger, container *utils.DependencyContainer) argparse.Subcommand {
1719
return &startCmd{
18-
logger: logger,
20+
logger: logger,
21+
container: container,
1922
}
2023
}
2124

cmd/git-bundle-server/stop.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@ package main
33
import (
44
"context"
55

6+
"github.com/github/git-bundle-server/cmd/utils"
67
"github.com/github/git-bundle-server/internal/argparse"
78
"github.com/github/git-bundle-server/internal/core"
89
"github.com/github/git-bundle-server/internal/log"
910
)
1011

1112
type stopCmd struct {
12-
logger log.TraceLogger
13+
logger log.TraceLogger
14+
container *utils.DependencyContainer
1315
}
1416

15-
func NewStopCommand(logger log.TraceLogger) argparse.Subcommand {
17+
func NewStopCommand(logger log.TraceLogger, container *utils.DependencyContainer) argparse.Subcommand {
1618
return &stopCmd{
17-
logger: logger,
19+
logger: logger,
20+
container: container,
1821
}
1922
}
2023

cmd/git-bundle-server/update-all.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,22 @@ import (
55
"os"
66
"os/exec"
77

8+
"github.com/github/git-bundle-server/cmd/utils"
89
"github.com/github/git-bundle-server/internal/argparse"
910
"github.com/github/git-bundle-server/internal/common"
1011
"github.com/github/git-bundle-server/internal/core"
1112
"github.com/github/git-bundle-server/internal/log"
1213
)
1314

1415
type updateAllCmd struct {
15-
logger log.TraceLogger
16+
logger log.TraceLogger
17+
container *utils.DependencyContainer
1618
}
1719

18-
func NewUpdateAllCommand(logger log.TraceLogger) argparse.Subcommand {
20+
func NewUpdateAllCommand(logger log.TraceLogger, container *utils.DependencyContainer) argparse.Subcommand {
1921
return &updateAllCmd{
20-
logger: logger,
22+
logger: logger,
23+
container: container,
2124
}
2225
}
2326

@@ -31,11 +34,11 @@ For every configured route, run 'git-bundle-server update <options> <route>'.`
3134
}
3235

3336
func (u *updateAllCmd) Run(ctx context.Context, args []string) error {
34-
user, err := common.NewUserProvider().CurrentUser()
37+
user, err := utils.GetDependency[common.UserProvider](ctx, u.container).CurrentUser()
3538
if err != nil {
3639
return u.logger.Error(ctx, err)
3740
}
38-
fs := common.NewFileSystem()
41+
fs := utils.GetDependency[common.FileSystem](ctx, u.container)
3942

4043
parser := argparse.NewArgParser(u.logger, "git-bundle-server update-all")
4144
parser.Parse(ctx, args)

cmd/git-bundle-server/update.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,22 @@ import (
44
"context"
55
"fmt"
66

7+
"github.com/github/git-bundle-server/cmd/utils"
78
"github.com/github/git-bundle-server/internal/argparse"
89
"github.com/github/git-bundle-server/internal/bundles"
910
"github.com/github/git-bundle-server/internal/core"
1011
"github.com/github/git-bundle-server/internal/log"
1112
)
1213

1314
type updateCmd struct {
14-
logger log.TraceLogger
15+
logger log.TraceLogger
16+
container *utils.DependencyContainer
1517
}
1618

17-
func NewUpdateCommand(logger log.TraceLogger) argparse.Subcommand {
19+
func NewUpdateCommand(logger log.TraceLogger, container *utils.DependencyContainer) argparse.Subcommand {
1820
return &updateCmd{
19-
logger: logger,
21+
logger: logger,
22+
container: container,
2023
}
2124
}
2225

cmd/git-bundle-server/web-server.go

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,14 @@ import (
1717
)
1818

1919
type webServerCmd struct {
20-
logger log.TraceLogger
21-
user common.UserProvider
22-
cmdExec common.CommandExecutor
23-
fileSystem common.FileSystem
20+
logger log.TraceLogger
21+
container *utils.DependencyContainer
2422
}
2523

26-
func NewWebServerCommand(logger log.TraceLogger) argparse.Subcommand {
27-
// Create subcommand-specific dependencies
24+
func NewWebServerCommand(logger log.TraceLogger, container *utils.DependencyContainer) argparse.Subcommand {
2825
return &webServerCmd{
29-
logger: logger,
30-
user: common.NewUserProvider(),
31-
cmdExec: common.NewCommandExecutor(),
32-
fileSystem: common.NewFileSystem(),
26+
logger: logger,
27+
container: container,
3328
}
3429
}
3530

@@ -64,7 +59,8 @@ func (w *webServerCmd) getDaemonConfig(ctx context.Context) (*daemon.DaemonConfi
6459
}
6560

6661
programPath = filepath.Join(exeDir, "git-bundle-web-server")
67-
programExists, err := w.fileSystem.FileExists(programPath)
62+
fileSystem := utils.GetDependency[common.FileSystem](ctx, w.container)
63+
programExists, err := fileSystem.FileExists(programPath)
6864
if err != nil {
6965
return nil, w.logger.Errorf(ctx, "could not determine whether path to 'git-bundle-web-server' exists: %w", err)
7066
} else if !programExists {
@@ -97,10 +93,7 @@ func (w *webServerCmd) startServer(ctx context.Context, args []string) error {
9793
parser.Parse(ctx, args)
9894
validate(ctx)
9995

100-
d, err := daemon.NewDaemonProvider(w.logger, w.user, w.cmdExec, w.fileSystem)
101-
if err != nil {
102-
return w.logger.Error(ctx, err)
103-
}
96+
d := utils.GetDependency[daemon.DaemonProvider](ctx, w.container)
10497

10598
config, err := w.getDaemonConfig(ctx)
10699
if err != nil {
@@ -153,10 +146,7 @@ func (w *webServerCmd) stopServer(ctx context.Context, args []string) error {
153146
remove := parser.Bool("remove", false, "Remove the web server daemon configuration from the system after stopping")
154147
parser.Parse(ctx, args)
155148

156-
d, err := daemon.NewDaemonProvider(w.logger, w.user, w.cmdExec, w.fileSystem)
157-
if err != nil {
158-
return w.logger.Error(ctx, err)
159-
}
149+
d := utils.GetDependency[daemon.DaemonProvider](ctx, w.container)
160150

161151
config, err := w.getDaemonConfig(ctx)
162152
if err != nil {

cmd/utils/container-helpers.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package utils
2+
3+
import (
4+
"context"
5+
6+
"github.com/github/git-bundle-server/internal/common"
7+
"github.com/github/git-bundle-server/internal/daemon"
8+
"github.com/github/git-bundle-server/internal/log"
9+
)
10+
11+
func BuildGitBundleServerContainer(logger log.TraceLogger) *DependencyContainer {
12+
container := NewDependencyContainer()
13+
registerDependency(container, func(ctx context.Context) common.UserProvider {
14+
return common.NewUserProvider()
15+
})
16+
registerDependency(container, func(ctx context.Context) common.CommandExecutor {
17+
return common.NewCommandExecutor()
18+
})
19+
registerDependency(container, func(ctx context.Context) common.FileSystem {
20+
return common.NewFileSystem()
21+
})
22+
registerDependency(container, func(ctx context.Context) daemon.DaemonProvider {
23+
t, err := daemon.NewDaemonProvider(
24+
logger,
25+
GetDependency[common.UserProvider](ctx, container),
26+
GetDependency[common.CommandExecutor](ctx, container),
27+
GetDependency[common.FileSystem](ctx, container),
28+
)
29+
if err != nil {
30+
logger.Fatal(ctx, err)
31+
}
32+
return t
33+
})
34+
35+
return container
36+
}

0 commit comments

Comments
 (0)