Skip to content

Commit 985d7fc

Browse files
committed
Toolprovider: entrypoint in workflow run
1 parent d72169f commit 985d7fc

File tree

7 files changed

+192
-58
lines changed

7 files changed

+192
-58
lines changed

cli/run.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/bitrise-io/bitrise/v2/log"
2020
"github.com/bitrise-io/bitrise/v2/models"
2121
"github.com/bitrise-io/bitrise/v2/plugins"
22+
"github.com/bitrise-io/bitrise/v2/toolprovider"
2223
"github.com/bitrise-io/bitrise/v2/tools"
2324
envmanModels "github.com/bitrise-io/envman/v2/models"
2425
"github.com/bitrise-io/go-utils/colorstring"
@@ -269,7 +270,11 @@ func (r WorkflowRunner) runWorkflows(tracker analytics.Tracker) (models.BuildRun
269270
environments := append(r.config.Secrets, r.config.Config.App.Environments...)
270271

271272
// Toolprovider entrypoint
272-
// r.config.Config.Tools
273+
err := toolprovider.Run(r.config.Config)
274+
if err != nil {
275+
// TODO: better error logging
276+
return models.BuildRunResultsModel{}, fmt.Errorf("set up tools: %w", err)
277+
}
273278
// append to environments
274279

275280
if err := os.Setenv("BITRISE_TRIGGERED_WORKFLOW_ID", r.config.Workflow); err != nil {

models/models.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -183,18 +183,6 @@ type AppModel struct {
183183
Environments []envmanModels.EnvironmentItemModel `json:"envs,omitempty" yaml:"envs,omitempty"`
184184
}
185185

186-
type ToolID string
187-
188-
// ToolsModel is a mapping of tool IDs to their versions (see package toolprovider about the version syntax)
189-
type ToolsModel map[ToolID]string
190-
191-
type ToolConfigModel struct {
192-
Provider string `json:"provider,omitempty" yaml:"provider,omitempty"`
193-
194-
// Extra tool-plugins on top of Bitrise-vetted integrations. This is very provider-specific, but the map value is a URL to the plugin source.
195-
ExtraPlugins map[ToolID]string `json:"extra_plugins,omitempty" yaml:"extra_plugins,omitempty"`
196-
}
197-
198186
type BitriseDataModel struct {
199187
FormatVersion string `json:"format_version" yaml:"format_version"`
200188
DefaultStepLibSource string `json:"default_step_lib_source,omitempty" yaml:"default_step_lib_source,omitempty"`

models/models_methods.go

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@ import (
55
"errors"
66
"fmt"
77
"regexp"
8-
"slices"
98
"sort"
109
"strconv"
1110
"strings"
1211

13-
"github.com/bitrise-io/bitrise/v2/toolprovider"
1412
envmanModels "github.com/bitrise-io/envman/v2/models"
1513
"github.com/bitrise-io/go-utils/pointers"
1614
"github.com/bitrise-io/go-utils/sliceutil"
@@ -1082,39 +1080,6 @@ func validateID(id, modelType string) (string, error) {
10821080
return "", nil
10831081
}
10841082

1085-
func validateTools(config *BitriseDataModel) error {
1086-
if config.Tools == nil {
1087-
return nil
1088-
}
1089-
1090-
for toolID, versionString := range config.Tools {
1091-
_, _, err := toolprovider.ParseVersionString(versionString)
1092-
if err != nil {
1093-
return fmt.Errorf("%s: invalid version syntax %s: %w", toolID, versionString, err)
1094-
}
1095-
}
1096-
1097-
return nil
1098-
}
1099-
1100-
func validateToolConfig(toolConfig *ToolConfigModel) error {
1101-
if toolConfig == nil {
1102-
return nil
1103-
}
1104-
1105-
if toolConfig.Provider != "" && !slices.Contains(toolprovider.SupportedProviders, toolConfig.Provider) {
1106-
return fmt.Errorf("invalid provider: %s, should be one of: %v", toolConfig.Provider, toolprovider.SupportedProviders)
1107-
}
1108-
1109-
for id, url := range toolConfig.ExtraPlugins {
1110-
if url == "" {
1111-
return fmt.Errorf("URL of extra plugin %s is empty", id)
1112-
}
1113-
}
1114-
1115-
return nil
1116-
}
1117-
11181083
// ----------------------------
11191084
// --- FillMissingDefaults
11201085

models/toolprovider.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package models
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"slices"
7+
"strings"
8+
)
9+
10+
var ToolProviders = []string{"asdf", "mise"}
11+
12+
type ToolID string
13+
14+
// ToolsModel is a mapping of tool IDs to their versions (see package toolprovider about the version syntax)
15+
type ToolsModel map[ToolID]string
16+
17+
type ToolConfigModel struct {
18+
Provider string `json:"provider,omitempty" yaml:"provider,omitempty"`
19+
20+
// Extra tool-plugins on top of Bitrise-vetted integrations. This is very provider-specific, but the map value is a URL to the plugin source.
21+
ExtraPlugins map[ToolID]string `json:"extra_plugins,omitempty" yaml:"extra_plugins,omitempty"`
22+
}
23+
24+
const ToolSyntaxPatternLatest = `(.*):latest$`
25+
const ToolSyntaxPatternInstalled = `(.*):installed$`
26+
27+
func isProviderSupported(providerName string) bool {
28+
return providerName != "" && !slices.Contains(ToolProviders, providerName)
29+
}
30+
31+
func validateToolConfig(toolConfig *ToolConfigModel) error {
32+
if toolConfig == nil {
33+
return nil
34+
}
35+
36+
if !isProviderSupported(toolConfig.Provider) {
37+
return fmt.Errorf("invalid provider: %s, should be one of: %v", toolConfig.Provider, ToolProviders)
38+
}
39+
40+
for id, url := range toolConfig.ExtraPlugins {
41+
if url == "" {
42+
return fmt.Errorf("URL of extra plugin %s is empty", id)
43+
}
44+
}
45+
46+
return nil
47+
}
48+
49+
func validateTools(config *BitriseDataModel) error {
50+
if config.Tools == nil {
51+
return nil
52+
}
53+
54+
for toolID, versionString := range config.Tools {
55+
err := validateVersionString(versionString)
56+
if err != nil {
57+
return fmt.Errorf("%s: invalid version syntax %s: %w", toolID, versionString, err)
58+
}
59+
}
60+
61+
return nil
62+
}
63+
64+
// validateVersionString takes a string like `3.12:latest` or `3.12.0` and validates it against the expected syntax.
65+
func validateVersionString(versionString string) error {
66+
versionString = strings.TrimSpace(versionString)
67+
68+
latestSyntaxPattern, err := regexp.Compile(ToolSyntaxPatternLatest)
69+
if err != nil {
70+
return fmt.Errorf("compile regex pattern: %v", err)
71+
}
72+
preinstalledSyntaxPattern, err := regexp.Compile(ToolSyntaxPatternInstalled)
73+
if err != nil {
74+
return fmt.Errorf("compile regex pattern: %v", err)
75+
}
76+
77+
if latestSyntaxPattern.MatchString(versionString) {
78+
matches := latestSyntaxPattern.FindStringSubmatch(versionString)
79+
if len(matches) <= 1 {
80+
return fmt.Errorf("%s does not match version:latest syntax", versionString)
81+
}
82+
} else if preinstalledSyntaxPattern.MatchString(versionString) {
83+
matches := preinstalledSyntaxPattern.FindStringSubmatch(versionString)
84+
if len(matches) <= 1 {
85+
return fmt.Errorf("%s does not match version:installed syntax", versionString)
86+
}
87+
}
88+
89+
return nil
90+
}

toolprovider/config.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package toolprovider
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/bitrise-io/bitrise/v2/models"
7+
"github.com/bitrise-io/bitrise/v2/toolprovider/provider"
8+
)
9+
10+
func getToolRequests(config models.BitriseDataModel) ([]provider.ToolRequest, error) {
11+
tools := config.Tools
12+
13+
var toolRequests []provider.ToolRequest
14+
for toolID, toolVersion := range tools {
15+
v, strategy, err := ParseVersionString(toolVersion)
16+
if err != nil {
17+
return nil, fmt.Errorf("parse %s version: %w", toolID, err)
18+
}
19+
toolRequests = append(toolRequests, provider.ToolRequest{
20+
ToolName: provider.ToolID(toolID),
21+
UnparsedVersion: v,
22+
ResolutionStrategy: strategy,
23+
// TODO: plugin identifier
24+
})
25+
}
26+
27+
return toolRequests, nil
28+
}
29+
30+
func defaultToolConfig() models.ToolConfigModel {
31+
return models.ToolConfigModel{
32+
Provider: "asdf",
33+
}
34+
}

toolprovider/run.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package toolprovider
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/bitrise-io/bitrise/v2/models"
7+
"github.com/bitrise-io/bitrise/v2/toolprovider/asdf"
8+
"github.com/bitrise-io/bitrise/v2/toolprovider/asdf/execenv"
9+
"github.com/bitrise-io/bitrise/v2/toolprovider/provider"
10+
)
11+
12+
func Run(config models.BitriseDataModel) error {
13+
toolRequests, err := getToolRequests(config)
14+
if err != nil {
15+
return fmt.Errorf("tools: %w", err)
16+
}
17+
providerID := defaultToolConfig().Provider
18+
if config.ToolConfig != nil {
19+
if config.ToolConfig.Provider != "" {
20+
providerID = config.ToolConfig.Provider
21+
}
22+
}
23+
24+
var toolProvider provider.ToolProvider
25+
switch providerID {
26+
case "asdf":
27+
toolProvider = asdf.AsdfToolProvider{
28+
ExecEnv: execenv.ExecEnv{
29+
// At this time, the asdf tool provider relies on the system-wide asdf install and config provided by the stack.
30+
EnvVars: map[string]string{},
31+
ShellInit: "",
32+
ClearInheritedEnvs: false,
33+
},
34+
}
35+
default:
36+
return fmt.Errorf("unsupported tool provider: %s", providerID)
37+
}
38+
39+
err = toolProvider.Bootstrap()
40+
if err != nil {
41+
return fmt.Errorf("bootstrap %s: %w", providerID, err)
42+
}
43+
44+
if len(toolRequests) == 0 {
45+
fmt.Println("No tools to set up.")
46+
return nil
47+
}
48+
49+
// TOOD: continue
50+
51+
return nil
52+
}

toolprovider/version.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,44 @@ import (
44
"fmt"
55
"regexp"
66
"strings"
7-
)
87

9-
const latestSyntaxPattern = `(.*):latest$`
10-
const installedSyntaxPattern = `(.*):installed$`
8+
"github.com/bitrise-io/bitrise/v2/models"
9+
"github.com/bitrise-io/bitrise/v2/toolprovider/provider"
10+
)
1111

1212
// ParseVersionString takes a string like `3.12:latest` and parses it into a plain version string (3.12) and a ResolutionStrategy (latest released).
13-
func ParseVersionString(versionString string) (string, ResolutionStrategy, error) {
13+
func ParseVersionString(versionString string) (string, provider.ResolutionStrategy, error) {
1414
versionString = strings.TrimSpace(versionString)
1515

16-
latestSyntaxPattern, err := regexp.Compile(latestSyntaxPattern)
16+
latestSyntaxPattern, err := regexp.Compile(models.ToolSyntaxPatternLatest)
1717
if err != nil {
1818
return "", 0, fmt.Errorf("compile regex pattern: %v", err)
1919
}
20-
preinstalledSyntaxPattern, err := regexp.Compile(installedSyntaxPattern)
20+
preinstalledSyntaxPattern, err := regexp.Compile(models.ToolSyntaxPatternInstalled)
2121
if err != nil {
2222
return "", 0, fmt.Errorf("compile regex pattern: %v", err)
2323
}
2424

25-
var resolutionStrategy ResolutionStrategy
25+
var resolutionStrategy provider.ResolutionStrategy
2626
var plainVersion string
2727
if latestSyntaxPattern.MatchString(versionString) {
28-
resolutionStrategy = ResolutionStrategyLatestReleased
28+
resolutionStrategy = provider.ResolutionStrategyLatestReleased
2929
matches := latestSyntaxPattern.FindStringSubmatch(versionString)
3030
if len(matches) > 1 {
3131
plainVersion = matches[1]
3232
} else {
3333
return "", 0, fmt.Errorf("%s does not match :latest syntax", versionString)
3434
}
3535
} else if preinstalledSyntaxPattern.MatchString(versionString) {
36-
resolutionStrategy = ResolutionStrategyLatestInstalled
36+
resolutionStrategy = provider.ResolutionStrategyLatestInstalled
3737
matches := preinstalledSyntaxPattern.FindStringSubmatch(versionString)
3838
if len(matches) > 1 {
3939
plainVersion = matches[1]
4040
} else {
4141
return "", 0, fmt.Errorf("%s does not match :installed syntax", versionString)
4242
}
4343
} else {
44-
resolutionStrategy = ResolutionStrategyStrict
44+
resolutionStrategy = provider.ResolutionStrategyStrict
4545
plainVersion = versionString
4646
}
4747

0 commit comments

Comments
 (0)