Skip to content

Commit aae5b59

Browse files
authored
Merge pull request #3681 from ActiveState/mitchell/cp-920
Added `--ts=dynamic` parameter to `state install` in order to use dynamic imports.
2 parents 3c3adfd + 0a98d16 commit aae5b59

11 files changed

Lines changed: 219 additions & 60 deletions

File tree

internal/captain/values.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,10 @@ func (p *PackagesValueNoVersion) Type() string {
258258
}
259259

260260
type TimeValue struct {
261-
raw string
262-
Time *time.Time
263-
now bool
261+
raw string
262+
Time *time.Time
263+
now bool
264+
dynamic bool
264265
}
265266

266267
var _ FlagMarshaler = &TimeValue{}
@@ -271,14 +272,15 @@ func (u *TimeValue) String() string {
271272

272273
func (u *TimeValue) Set(v string) error {
273274
u.raw = v
274-
if v != "now" {
275+
if v != "now" && v != "dynamic" {
275276
tsv, err := time.Parse(time.RFC3339, v)
276277
if err != nil {
277278
return locale.WrapInputError(err, "timeflag_format", "Invalid timestamp: Should be RFC3339 formatted.")
278279
}
279280
u.Time = &tsv
280281
}
281282
u.now = v == "now"
283+
u.dynamic = v == "dynamic"
282284
return nil
283285
}
284286

@@ -290,6 +292,10 @@ func (u *TimeValue) Now() bool {
290292
return u.now
291293
}
292294

295+
func (u *TimeValue) Dynamic() bool {
296+
return u.dynamic
297+
}
298+
293299
type IntValue struct {
294300
raw string
295301
Int *int

internal/runbits/reqop_runbit/update.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ func UpdateAndReload(prime primeable, script *buildscript.BuildScript, oldCommit
6565
}()
6666
pg = output.StartSpinner(out, locale.T("progress_solve_preruntime"), constants.TerminalAnimationInterval)
6767

68+
if script.Dynamic() {
69+
// Evaluate with dynamic imports first. Then commit.
70+
err := bp.Evaluate(pj.Owner(), pj.Name(), script)
71+
if err != nil {
72+
return errs.Wrap(err, "Unable to dynamically evaluate build expression")
73+
}
74+
script.SetDynamic(false) // StageCommit needs to be called with "solve" node and atTime="now"
75+
}
76+
6877
commitParams := buildplanner.StageCommitParams{
6978
Owner: pj.Owner(),
7079
Project: pj.Name(),

internal/runners/install/install.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func (i *Install) Run(params Params) (rerr error) {
141141
}
142142

143143
// Resolve requirements
144-
reqs, err = i.resolveRequirements(params.Packages, ts, languages)
144+
reqs, err = i.resolveRequirements(params.Packages, ts, languages, params.Timestamp.Dynamic())
145145
if err != nil {
146146
return errs.Wrap(err, "Unable to resolve requirements")
147147
}
@@ -153,7 +153,7 @@ func (i *Install) Run(params Params) (rerr error) {
153153

154154
// Prepare updated buildscript
155155
script := oldCommit.BuildScript()
156-
if err := prepareBuildScript(script, reqs, ts); err != nil {
156+
if err := prepareBuildScript(script, reqs, ts, params.Timestamp.Dynamic()); err != nil {
157157
return errs.Wrap(err, "Could not prepare build script")
158158
}
159159

@@ -181,7 +181,7 @@ type errNoMatches struct {
181181
}
182182

183183
// resolveRequirements will attempt to resolve the ingredient and namespace for each requested package
184-
func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Time, languages []model.Language) (requirements, error) {
184+
func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Time, languages []model.Language, dynamic bool) (requirements, error) {
185185
failed := []*requirement{}
186186
reqs := []*requirement{}
187187
for _, pkg := range packages {
@@ -191,6 +191,13 @@ func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Ti
191191
req.Resolved.Namespace = pkg.Namespace
192192
}
193193

194+
// When using dynamic imports, the packages may not yet exist in the inventory, so searching
195+
// for them is fruitless. Just pass them along.
196+
if dynamic {
197+
reqs = append(reqs, req)
198+
continue
199+
}
200+
194201
// Find ingredients that match the pkg query
195202
ingredients, err := model.SearchIngredientsStrict(pkg.Namespace, pkg.Name, false, false, &ts, i.prime.Auth())
196203
if err != nil {
@@ -274,7 +281,9 @@ func resolveVersion(req *requirement) error {
274281
}
275282

276283
// Verify that the version provided can be resolved
277-
if versionRe.MatchString(version) {
284+
// Note: if the requirement does not have an ingredient, it is being dynamically imported, so
285+
// we cannot resolve its versions yet.
286+
if versionRe.MatchString(version) && req.Resolved.ingredient != nil {
278287
match := false
279288
for _, knownVersion := range req.Resolved.ingredient.Versions {
280289
if knownVersion.Version == version {
@@ -340,8 +349,14 @@ func (i *Install) renderUserFacing(reqs requirements) {
340349
i.prime.Output().Notice("")
341350
}
342351

343-
func prepareBuildScript(script *buildscript.BuildScript, requirements requirements, ts time.Time) error {
352+
func prepareBuildScript(script *buildscript.BuildScript, requirements requirements, ts time.Time, dynamic bool) error {
344353
script.SetAtTime(ts, true)
354+
355+
err := script.SetDynamic(dynamic)
356+
if err != nil {
357+
return errs.Wrap(err, "Unable to update solve function")
358+
}
359+
345360
for _, req := range requirements {
346361
requirement := types.Requirement{
347362
Namespace: req.Resolved.Namespace,

pkg/buildscript/buildscript.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type BuildScript struct {
2222

2323
project string
2424
atTime *time.Time
25+
dynamic bool
2526
}
2627

2728
func init() {
@@ -77,6 +78,24 @@ func (b *BuildScript) SetAtTime(t time.Time, override bool) {
7778
_ = b.atTime
7879
}
7980

81+
func (b *BuildScript) Dynamic() bool {
82+
return b.dynamic
83+
}
84+
85+
func (b *BuildScript) SetDynamic(dynamic bool) error {
86+
b.dynamic = dynamic
87+
solveNode, err := b.getSolveNode()
88+
if err != nil {
89+
return errs.Wrap(err, "Unable to find solve node")
90+
}
91+
if dynamic {
92+
solveNode.FuncCall.Name = solveDynamicFuncName
93+
} else {
94+
solveNode.FuncCall.Name = solveFuncName
95+
}
96+
return nil
97+
}
98+
8099
func (b *BuildScript) Equals(other *BuildScript) (bool, error) {
81100
b2, err := b.Clone()
82101
if err != nil {

pkg/buildscript/queries.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import (
1212
)
1313

1414
const (
15-
solveFuncName = "solve"
16-
solveLegacyFuncName = "solve_legacy"
17-
srcKey = "src"
18-
mergeKey = "merge"
19-
requirementsKey = "requirements"
20-
platformsKey = "platforms"
15+
solveFuncName = "solve"
16+
solveLegacyFuncName = "solve_legacy"
17+
solveDynamicFuncName = "dynamic_solve"
18+
srcKey = "src"
19+
mergeKey = "merge"
20+
requirementsKey = "requirements"
21+
platformsKey = "platforms"
2122
)
2223

2324
var errNodeNotFound = errs.New("Could not find node")
@@ -177,7 +178,7 @@ func getVersionRequirements(v *value) []types.VersionRequirement {
177178
}
178179

179180
func isSolveFuncName(name string) bool {
180-
return name == solveFuncName || name == solveLegacyFuncName
181+
return name == solveFuncName || name == solveLegacyFuncName || name == solveDynamicFuncName
181182
}
182183

183184
func (b *BuildScript) getTargetSolveNode(targets ...string) (*value, error) {

pkg/buildscript/unmarshal.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,5 +83,5 @@ func Unmarshal(data []byte) (*BuildScript, error) {
8383
atTime = &atTimeVal
8484
}
8585

86-
return &BuildScript{raw, project, atTime}, nil
86+
return &BuildScript{raw: raw, project: project, atTime: atTime}, nil
8787
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package request
2+
3+
func Build(owner, project, commitId, target string) *build {
4+
return &build{map[string]interface{}{
5+
"organization": owner,
6+
"project": project,
7+
"commitId": commitId,
8+
"target": target,
9+
}}
10+
}
11+
12+
type build struct {
13+
vars map[string]interface{}
14+
}
15+
16+
func (b *build) Query() string {
17+
return `
18+
mutation ($organization: String!, $project: String!, $commitId: String!, $target: String) {
19+
buildCommitTarget(
20+
input: {organization: $organization, project: $project, commitId: $commitId, target: $target}
21+
) {
22+
... on Build {
23+
__typename
24+
status
25+
}
26+
... on Error {
27+
__typename
28+
message
29+
}
30+
... on ErrorWithSubErrors {
31+
__typename
32+
subErrors {
33+
__typename
34+
... on GenericSolveError {
35+
message
36+
isTransient
37+
validationErrors {
38+
error
39+
jsonPath
40+
}
41+
}
42+
... on RemediableSolveError {
43+
message
44+
isTransient
45+
errorType
46+
validationErrors {
47+
error
48+
jsonPath
49+
}
50+
}
51+
}
52+
}
53+
}
54+
}
55+
`
56+
}
57+
58+
func (b *build) Vars() (map[string]interface{}, error) {
59+
return b.vars, nil
60+
}

pkg/platform/api/buildplanner/request/evaluate.go

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,30 @@
11
package request
22

3-
func Evaluate(owner, project, commitId, target string) *evaluate {
4-
return &evaluate{map[string]interface{}{
5-
"organization": owner,
3+
import (
4+
"time"
5+
6+
"github.com/ActiveState/cli/internal/rtutils/ptr"
7+
)
8+
9+
func Evaluate(organization, project string, expr []byte, atTime *time.Time, dynamic bool, target string) *evaluate {
10+
eval := &evaluate{map[string]interface{}{
11+
"organization": organization,
612
"project": project,
7-
"commitId": commitId,
13+
"expr": string(expr),
814
"target": target,
915
}}
16+
17+
var timestamp *string
18+
if atTime != nil {
19+
timestamp = ptr.To(atTime.Format(time.RFC3339))
20+
}
21+
if !dynamic {
22+
eval.vars["atTime"] = timestamp
23+
} else {
24+
eval.vars["atTime"] = "dynamic"
25+
}
26+
27+
return eval
1028
}
1129

1230
type evaluate struct {
@@ -15,42 +33,44 @@ type evaluate struct {
1533

1634
func (b *evaluate) Query() string {
1735
return `
18-
mutation ($organization: String!, $project: String!, $commitId: String!, $target: String) {
19-
buildCommitTarget(
20-
input: {organization: $organization, project: $project, commitId: $commitId, target: $target}
21-
) {
22-
... on Build {
23-
__typename
24-
status
25-
}
26-
... on Error {
27-
__typename
28-
message
29-
}
30-
... on ErrorWithSubErrors {
31-
__typename
32-
subErrors {
33-
__typename
34-
... on GenericSolveError {
35-
message
36-
isTransient
37-
validationErrors {
38-
error
39-
jsonPath
40-
}
41-
}
42-
... on RemediableSolveError {
43-
message
44-
isTransient
45-
errorType
46-
validationErrors {
47-
error
48-
jsonPath
49-
}
50-
}
51-
}
52-
}
53-
}
36+
query ($organization: String!, $project: String!, $expr: BuildExpr!, $atTime: AtTime, $target: String) {
37+
project(organization: $organization, project: $project) {
38+
... on Project {
39+
evaluate(expr: $expr, atTime: $atTime, target: $target) {
40+
... on Build {
41+
__typename
42+
status
43+
}
44+
... on Error {
45+
__typename
46+
message
47+
}
48+
... on ErrorWithSubErrors {
49+
__typename
50+
subErrors {
51+
__typename
52+
... on GenericSolveError {
53+
message
54+
isTransient
55+
validationErrors {
56+
error
57+
jsonPath
58+
}
59+
}
60+
... on RemediableSolveError {
61+
message
62+
isTransient
63+
errorType
64+
validationErrors {
65+
error
66+
jsonPath
67+
}
68+
}
69+
}
70+
}
71+
}
72+
}
73+
}
5474
}
5575
`
5676
}

pkg/platform/model/buildplanner/build.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,17 +273,17 @@ func (e ErrFailedArtifacts) Error() string {
273273
func (bp *BuildPlanner) BuildTarget(owner, project, commitID, target string) error {
274274
logging.Debug("BuildTarget, owner: %s, project: %s, commitID: %s, target: %s", owner, project, commitID, target)
275275
resp := &response.BuildResponse{}
276-
err := bp.client.Run(request.Evaluate(owner, project, commitID, target), resp)
276+
err := bp.client.Run(request.Build(owner, project, commitID, target), resp)
277277
if err != nil {
278-
return processBuildPlannerError(err, "Failed to evaluate target")
278+
return processBuildPlannerError(err, "Failed to build target")
279279
}
280280

281281
if resp == nil {
282282
return errs.New("Build is nil")
283283
}
284284

285285
if response.IsErrorResponse(resp.Type) {
286-
return response.ProcessBuildError(resp, "Could not process error response from evaluate target")
286+
return response.ProcessBuildError(resp, "Could not process error response from build target")
287287
}
288288

289289
return nil

0 commit comments

Comments
 (0)