Skip to content

Commit

Permalink
Revert "Merge pull request #2774 from ActiveState/mitchell/dx-1961"
Browse files Browse the repository at this point in the history
This reverts commit 7b0308d, reversing
changes made to 30a336c.
  • Loading branch information
mitchell-as committed Oct 3, 2023
1 parent c02d94a commit e5cb1e0
Show file tree
Hide file tree
Showing 9 changed files with 286 additions and 127 deletions.
7 changes: 6 additions & 1 deletion internal/runbits/buildscript/buildscript.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ func Sync(proj *project.Project, commitID *strfmt.UUID, out output.Outputer, aut
return false, nil // nothing to do
}
logging.Debug("Merging changes")
expr, err = script.ToBuildExpression()
if err != nil {
return false, errs.Wrap(err, "Unable to translate local build script to build expression")
}

out.Notice(locale.Tl("buildscript_update", "Updating project to reflect build script changes..."))

localCommitID, err := localcommit.Get(proj.Dir())
Expand All @@ -69,7 +74,7 @@ func Sync(proj *project.Project, commitID *strfmt.UUID, out output.Outputer, aut
Owner: proj.Owner(),
Project: proj.Name(),
ParentCommit: localCommitID.String(),
Expression: script.Expr,
Expression: expr,
})
if err != nil {
return false, errs.Wrap(err, "Could not update project to reflect build script changes.")
Expand Down
11 changes: 2 additions & 9 deletions internal/runbits/buildscript/buildscript_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package buildscript

import (
"encoding/json"
"testing"

"github.com/ActiveState/cli/internal/rtutils/ptr"
"github.com/ActiveState/cli/pkg/platform/runtime/buildexpression"
"github.com/ActiveState/cli/pkg/platform/runtime/buildscript"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -31,16 +29,11 @@ in:
runtime`))
require.NoError(t, err)

// Make a copy of the original expression.
bytes, err := json.Marshal(script.Expr)
require.NoError(t, err)
expr, err := buildexpression.New(bytes)
expr, err := script.ToBuildExpression()
require.NoError(t, err)

// Modify the build script.
(*script.Expr.Let.Assignments[0].Value.Ap.Arguments[0].Assignment.Value.List)[0].Str = ptr.To(`77777`)
(*script.Let.Assignments[0].Value.FuncCall.Arguments[0].Assignment.Value.List)[0].Str = ptr.To(`"77777"`)

// Generate the difference between the modified script and the original expression.
result, err := generateDiff(script, expr)
require.NoError(t, err)
assert.Equal(t, `let:
Expand Down
5 changes: 4 additions & 1 deletion internal/runners/pull/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,10 @@ func (p *Pull) mergeBuildScript(strategies *mono_models.MergeStrategies, remoteC
}

// Get the local and remote build expressions to merge.
exprA := script.Expr
exprA, err := script.ToBuildExpression()
if err != nil {
return errs.Wrap(err, "Unable to transform local buildscript into buildexpression")
}
bp := model.NewBuildPlannerModel(p.auth)
exprB, err := bp.GetBuildExpression(p.project.Owner(), p.project.Name(), remoteCommit.String())
if err != nil {
Expand Down
4 changes: 0 additions & 4 deletions pkg/platform/runtime/buildexpression/buildexpression.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,6 @@ func newValue(path []string, valueInterface interface{}) (*Value, error) {
case float64:
value.Float = ptr.To(v)

case nil:
// An empty value is interpreted as JSON null.
value.Null = &Null{}

default:
logging.Debug("Unknown type: %T at path %s", v, strings.Join(path, "."))
// An empty value is interpreted as JSON null.
Expand Down
228 changes: 228 additions & 0 deletions pkg/platform/runtime/buildscript/buildexpression.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
package buildscript

import (
"encoding/json"
"sort"
"strings"

"github.com/ActiveState/cli/internal/errs"
"github.com/ActiveState/cli/internal/multilog"
"github.com/ActiveState/cli/internal/rtutils/ptr"
"github.com/ActiveState/cli/pkg/platform/runtime/buildexpression"
)

const SolveFunction = "solve"
const SolveLegacyFunction = "solve_legacy"
const MergeFunction = "merge"

func NewScriptFromBuildExpression(expr *buildexpression.BuildExpression) (*Script, error) {
data, err := json.Marshal(expr)
if err != nil {
return nil, errs.Wrap(err, "Unable to marshal buildexpression to JSON")
}

m := make(map[string]interface{})
err = json.Unmarshal(data, &m)
if err != nil { // this really should not happen
return nil, errs.Wrap(err, "Could not unmarshal buildexpression")
}

letValue, ok := m["let"]
if !ok {
return nil, errs.New("Build expression has no 'let' key")
}
letMap, ok := letValue.(map[string]interface{})
if !ok {
return nil, errs.New("'let' key is not a JSON object")
}
inValue, ok := letMap["in"]
if !ok {
return nil, errs.New("Build expression's 'let' object has no 'in' key")
}
delete(letMap, "in") // prevent duplication of "in" field when writing the build script

let, err := newLet(letMap)
if err != nil {
return nil, errs.Wrap(err, "Could not parse 'let' key")
}

in, err := newIn(inValue)
if err != nil {
return nil, errs.Wrap(err, "Could not parse 'in' key's value: %v", inValue)
}

return &Script{let, in}, nil
}

func newLet(m map[string]interface{}) (*Let, error) {
assignments, err := newAssignments(m)
if err != nil {
return nil, errs.Wrap(err, "Could not parse 'let' key")
}
return &Let{Assignments: *assignments}, nil
}

func isFunction(name string) bool {
return name == SolveFunction || name == SolveLegacyFunction || name == MergeFunction
}

func newValue(valueInterface interface{}, preferIdent bool) (*Value, error) {
value := &Value{}

switch v := valueInterface.(type) {
case map[string]interface{}:
// Examine keys first to see if this is a function call.
for key := range v {
if isFunction(key) {
f, err := newFuncCall(v)
if err != nil {
return nil, errs.Wrap(err, "Could not parse '%s' function's value: %v", key, v)
}
value.FuncCall = f
}
}

if value.FuncCall == nil {
// It's not a function call, but an object.
object, err := newAssignments(v)
if err != nil {
return nil, errs.Wrap(err, "Could not parse object: %v", v)
}
value.Object = object
}

case []interface{}:
values := []*Value{}
for _, item := range v {
value, err := newValue(item, false)
if err != nil {
return nil, errs.Wrap(err, "Could not parse list: %v", v)
}
values = append(values, value)
}
value.List = &values

case string:
if preferIdent {
value.Ident = &v
} else {
b, err := json.Marshal(v)
if err != nil {
return nil, errs.Wrap(err, "Could not marshal string '%s'", v)
}
value.Str = ptr.To(string(b))
}

case float64:
value.Number = ptr.To(v)

default:
// An empty value is interpreted as JSON null.
value.Null = &Null{}
}

return value, nil
}

func newFuncCall(m map[string]interface{}) (*FuncCall, error) {
// Look in the given object for the function's name and argument object or list.
var name string
var argsInterface interface{}
for key, value := range m {
if isFunction(key) {
name = key
argsInterface = value
break
}
}

args := []*Value{}

switch v := argsInterface.(type) {
case map[string]interface{}:
for key, valueInterface := range v {
value, err := newValue(valueInterface, name == MergeFunction)
if err != nil {
return nil, errs.Wrap(err, "Could not parse '%s' function's argument '%s': %v", name, key, valueInterface)
}
args = append(args, &Value{Assignment: &Assignment{Key: key, Value: value}})
}
sort.SliceStable(args, func(i, j int) bool { return args[i].Assignment.Key < args[j].Assignment.Key })

case []interface{}:
for _, item := range v {
value, err := newValue(item, false)
if err != nil {
return nil, errs.Wrap(err, "Could not parse '%s' function's argument list item: %v", name, item)
}
args = append(args, value)
}

default:
return nil, errs.New("Function '%s' expected to be object or list", name)
}

return &FuncCall{Name: name, Arguments: args}, nil
}

func newAssignments(m map[string]interface{}) (*[]*Assignment, error) {
assignments := []*Assignment{}
for key, valueInterface := range m {
value, err := newValue(valueInterface, false)
if err != nil {
return nil, errs.Wrap(err, "Could not parse '%s' key's value: %v", key, valueInterface)
}
assignments = append(assignments, &Assignment{Key: key, Value: value})
}
sort.SliceStable(assignments, func(i, j int) bool { return assignments[i].Key < assignments[j].Key })
return &assignments, nil
}

func newIn(inValue interface{}) (*In, error) {
in := &In{}

switch v := inValue.(type) {
case map[string]interface{}:
f, err := newFuncCall(v)
if err != nil {
return nil, errs.Wrap(err, "'in' object is not a function call")
}
in.FuncCall = f

case string:
in.Name = ptr.To(strings.TrimPrefix(v, "$"))

default:
return nil, errs.New("'in' value expected to be a function call or string")
}

return in, nil
}

func (s *Script) EqualsBuildExpressionBytes(exprBytes []byte) bool {
expr, err := buildexpression.New(exprBytes)
if err != nil {
multilog.Error("Unable to create buildexpression from incoming JSON: %v", err)
return false
}
return s.EqualsBuildExpression(expr)
}

func (s *Script) EqualsBuildExpression(expr *buildexpression.BuildExpression) bool {
myJson, err := json.Marshal(s)
if err != nil {
multilog.Error("Unable to marshal this buildscript to JSON: %v", err)
return false
}
otherScript, err := NewScriptFromBuildExpression(expr)
if err != nil {
multilog.Error("Unable to transform buildexpression to buildscript: %v", err)
return false
}
otherJson, err := json.Marshal(otherScript)
if err != nil {
multilog.Error("Unable to marshal other buildscript to JSON: %v", err)
return false
}
return string(myJson) == string(otherJson)
}
Loading

0 comments on commit e5cb1e0

Please sign in to comment.