Skip to content

Commit

Permalink
Merge pull request #3467 from ActiveState/DX-2623
Browse files Browse the repository at this point in the history
Added `state upgrade`
  • Loading branch information
Naatan authored Aug 29, 2024
2 parents 038b440 + b86f7a0 commit 85b4b5c
Show file tree
Hide file tree
Showing 32 changed files with 822 additions and 138 deletions.
1 change: 1 addition & 0 deletions cmd/state/internal/cmdtree/cmdtree.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ func New(prime *primer.Values, args ...string) *CmdTree {
newEvalCommand(prime),
newManifestCommmand(prime),
artifactsCmd,
newUpgradeCommand(prime),
)

return &CmdTree{
Expand Down
43 changes: 43 additions & 0 deletions cmd/state/internal/cmdtree/upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cmdtree

import (
"github.com/ActiveState/cli/internal/captain"
"github.com/ActiveState/cli/internal/locale"
"github.com/ActiveState/cli/internal/primer"
"github.com/ActiveState/cli/internal/runners/upgrade"
)

func newUpgradeCommand(prime *primer.Values) *captain.Command {
runner := upgrade.New(prime)

params := upgrade.NewParams()

cmd := captain.NewCommand(
"upgrade",
locale.Tl("upgrade_cmd_title", "Upgrading Project"),
locale.Tl("upgrade_cmd_description", "Upgrade dependencies of a project"),
prime,
[]*captain.Flag{
{
Name: "ts",
Description: locale.T("flag_state_upgrade_ts_description"),
Value: &params.Timestamp,
},
{
Name: "expand",
Description: locale.T("flag_state_upgrade_expand_description"),
Value: &params.Expand,
},
},
[]*captain.Argument{},
func(_ *captain.Command, _ []string) error {
return runner.Run(params)
},
)

cmd.SetGroup(PackagesGroup)
cmd.SetSupportsStructuredOutput()
cmd.SetUnstable(true)

return cmd
}
8 changes: 7 additions & 1 deletion internal/colorize/colorize.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func init() {
defer profile.Measure("colorize:init", time.Now())
var err error
colorRx, err = regexp.Compile(
`\[(HEADING|NOTICE|SUCCESS|ERROR|WARNING|DISABLED|ACTIONABLE|CYAN|GREEN|RED|ORANGE|YELLOW|MAGENTA|/RESET)!?\]`,
`\[(HEADING|BOLD|NOTICE|SUCCESS|ERROR|WARNING|DISABLED|ACTIONABLE|CYAN|GREEN|RED|ORANGE|YELLOW|MAGENTA|/RESET)!?\]`,
)
if err != nil {
panic(fmt.Sprintf("Could not compile regex: %v", err))
Expand All @@ -27,6 +27,7 @@ func init() {

type ColorTheme interface {
Heading(writer io.Writer)
Bold(writer io.Writer)
Notice(writer io.Writer)
Success(writer io.Writer)
Error(writer io.Writer)
Expand All @@ -51,6 +52,11 @@ func (dct defaultColorTheme) Heading(writer io.Writer) {
c.SetStyle(colorstyle.Bold, false)
}

func (dct defaultColorTheme) Bold(writer io.Writer) {
c := colorstyle.New(writer)
c.SetStyle(colorstyle.Bold, false)
}

// Notice switches to bright foreground
func (dct defaultColorTheme) Notice(writer io.Writer) {
colorstyle.New(writer).SetStyle(colorstyle.Default, true)
Expand Down
9 changes: 9 additions & 0 deletions internal/locale/locale.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ func T(translationID string, args ...interface{}) string {
return translateFunction(translationID, args...)
}

// Ts aliases to T, but accepts a list of translationIDs to be translated
func Ts(translationIDs ...string) []string {
result := []string{}
for _, id := range translationIDs {
result = append(result, T(id))
}
return result
}

// Tr is like T but it accepts string params that will be used as numbered params, eg. V0, V1, V2 etc
func Tr(translationID string, values ...string) string {
return T(translationID, parseInput(values...))
Expand Down
32 changes: 32 additions & 0 deletions internal/locale/locales/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1549,3 +1549,35 @@ warn_additional_requirements:
[WARNING]WARNING:[/RESET] This project has additional build criteria that cannot be managed through the State Tool. Please edit your [ACTIONABLE]buildscript.as[/RESET] file manually to modify these:
migrate_project_error:
other: Could not migrate your project files. Please address any errors and try again. For full detail view your log file with '[ACTIONABLE]state export log -i 0[/RESET]'.
upgrade_solving:
other: • Searching for available upgrades for your requirements
upgrade_confirm:
other: Would you like to install these upgrades?
upgrade_no_changes:
other: No upgrades were found for your project. Note that requirements with fixed version numbers will never be upgraded.
upgrade_aborted:
other: Upgrade aborted
upgrade_success:
other: Upgrade completed
upgrade_field_same:
other: "[CYAN]{{.V0}}[/RESET]"
upgrade_field_change:
other: "[CYAN]{{.V0}}[/RESET] > [BOLD][ACTIONABLE]{{.V1}}[/RESET]"
name:
other: Name
version:
other: Version
license:
other: License
vulnerabilities:
other: Vulnerabilities (CVEs)
dependency_row:
other: " [DISABLED]{{.V0}}[/RESET] [CYAN]{{.V1}}[/RESET] [DISABLED]transitive dependencies touched[/RESET]"
dependency_detail_row:
other: " [DISABLED]{{.V0}} {{.V1}}[/RESET] {{.V2}}"
namespace_row:
other: " [DISABLED]{{.V0}} namespace:[/RESET] [CYAN]{{.V1}}[/RESET]"
flag_state_upgrade_expand_description:
other: Show individual transitive dependency changes rather than a summary
flag_state_upgrade_ts_description:
other: Manually specify the timestamp to 'upgrade' to. Can be either 'now' or RFC3339 formatted timestamp.
26 changes: 21 additions & 5 deletions internal/output/presets.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,35 @@ func (h Emphasize) MarshalStructured(f Format) interface{} {
return Suppress
}

type preparedOutput struct {
plain interface{}
type plainOutput struct {
plain interface{}
}

type structuredOutput struct {
structured interface{}
}

func (o *preparedOutput) MarshalOutput(_ Format) interface{} {
type preparedOutput struct {
*plainOutput
*structuredOutput
}

func (o *plainOutput) MarshalOutput(_ Format) interface{} {
return o.plain
}

func (o *preparedOutput) MarshalStructured(_ Format) interface{} {
func (o *structuredOutput) MarshalStructured(_ Format) interface{} {
return o.structured
}

func Prepare(plain interface{}, structured interface{}) *preparedOutput {
return &preparedOutput{plain, structured}
return &preparedOutput{&plainOutput{plain}, &structuredOutput{structured}}
}

func Structured(structured interface{}) *structuredOutput {
return &structuredOutput{structured}
}

const TreeMid = "├─"
const TreeLink = "│"
const TreeEnd = "└─"
65 changes: 65 additions & 0 deletions internal/runbits/commits_runbit/time.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package commits_runbit

import (
"time"

"github.com/ActiveState/cli/internal/captain"
"github.com/ActiveState/cli/internal/errs"
"github.com/ActiveState/cli/pkg/localcommit"
"github.com/ActiveState/cli/pkg/platform/authentication"
"github.com/ActiveState/cli/pkg/platform/model"
"github.com/ActiveState/cli/pkg/project"
)

// ExpandTime returns a timestamp based on the given "--ts" value.
// If the timestamp was already defined, we just return it.
// If "now" was given, returns "now" according to the platform.
// Otherwise, returns the specified timestamp or nil (which falls back on the default Platform
// timestamp for a given operation)
func ExpandTime(ts *captain.TimeValue, auth *authentication.Auth) (time.Time, error) {
if ts.Time != nil {
return *ts.Time, nil
}

if ts.Now() {
latest, err := model.FetchLatestRevisionTimeStamp(auth)
if err != nil {
return time.Time{}, errs.Wrap(err, "Unable to determine latest revision time")
}
return latest, nil
}

latest, err := model.FetchLatestTimeStamp(auth)
if err != nil {
return time.Time{}, errs.Wrap(err, "Unable to fetch latest Platform timestamp")
}

return latest, nil
}

// ExpandTimeForProject is the same as ExpandTime except that it ensures the returned time is either the same or
// later than that of the most recent commit.
func ExpandTimeForProject(ts *captain.TimeValue, auth *authentication.Auth, proj *project.Project) (time.Time, error) {
timestamp, err := ExpandTime(ts, auth)
if err != nil {
return time.Time{}, errs.Wrap(err, "Unable to expand time")
}

if proj != nil {
commitID, err := localcommit.Get(proj.Dir())
if err != nil {
return time.Time{}, errs.Wrap(err, "Unable to get commit ID")
}

atTime, err := model.FetchTimeStampForCommit(commitID, auth)
if err != nil {
return time.Time{}, errs.Wrap(err, "Unable to get commit time")
}

if atTime.After(timestamp) {
return *atTime, nil
}
}

return timestamp, nil
}
10 changes: 5 additions & 5 deletions internal/runbits/cves/cves.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil
}

var ingredients []*request.Ingredient
for _, artifact := range changeset.Added {
for _, ing := range artifact.Ingredients {
for _, change := range changeset.Filter(buildplan.ArtifactAdded) {
for _, ing := range change.Artifact.Ingredients {
ingredients = append(ingredients, &request.Ingredient{
Namespace: ing.Namespace,
Name: ing.Name,
Expand All @@ -57,12 +57,12 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil
}
}

for _, change := range changeset.Updated {
for _, change := range changeset.Filter(buildplan.ArtifactUpdated) {
if !change.VersionsChanged() {
continue // For CVE reporting we only care about ingredient changes
}

for _, ing := range change.To.Ingredients {
for _, ing := range change.Artifact.Ingredients {
ingredients = append(ingredients, &request.Ingredient{
Namespace: ing.Namespace,
Name: ing.Name,
Expand Down Expand Up @@ -118,7 +118,7 @@ func (c *CveReport) shouldSkipReporting(changeset buildplan.ArtifactChangeset) b
return true
}

return len(changeset.Added) == 0 && len(changeset.Updated) == 0
return len(changeset.Filter(buildplan.ArtifactAdded, buildplan.ArtifactUpdated)) == 0
}

func (c *CveReport) shouldPromptForSecurity(vulnerabilities model.VulnerableIngredientsByLevels) bool {
Expand Down
18 changes: 10 additions & 8 deletions internal/runbits/dependencies/changesummary.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ func OutputChangeSummary(out output.Outputer, newBuildPlan *buildplan.BuildPlan,
dependencies := buildplan.Ingredients{}
directDependencies := buildplan.Ingredients{}
changeset := newBuildPlan.DiffArtifacts(oldBuildPlan, false)
for _, a := range changeset.Added {
for _, change := range changeset.Filter(buildplan.ArtifactAdded) {
a := change.Artifact
if _, exists := requested[a.ArtifactID]; !exists {
continue
}
Expand All @@ -43,16 +44,17 @@ func OutputChangeSummary(out output.Outputer, newBuildPlan *buildplan.BuildPlan,
}

// Check for any direct dependencies added by a requested package update.
for _, u := range changeset.Updated {
if _, exists := requested[u.To.ArtifactID]; !exists {
for _, change := range changeset.Filter(buildplan.ArtifactUpdated) {
if _, exists := requested[change.Artifact.ArtifactID]; !exists {
continue
}
for _, dep := range u.To.RuntimeDependencies(false) {
for _, a := range changeset.Added {
for _, dep := range change.Artifact.RuntimeDependencies(false, nil) {
for _, subchange := range changeset.Filter(buildplan.ArtifactAdded) {
a := subchange.Artifact
if a.ArtifactID != dep.ArtifactID {
continue
}
v := fmt.Sprintf("%s@%s", u.To.Name(), u.To.Version()) // updated/requested package, not added package
v := fmt.Sprintf("%s@%s", change.Artifact.Name(), change.Artifact.Version()) // updated/requested package, not added package
addedString = append(addedLocale, v)
addedLocale = append(addedLocale, fmt.Sprintf("[ACTIONABLE]%s[/RESET]", v))

Expand Down Expand Up @@ -104,9 +106,9 @@ func OutputChangeSummary(out output.Outputer, newBuildPlan *buildplan.BuildPlan,
// depending on whether or not it has subdependencies, and whether or not showUpdatedPackages is
// `true`.
for i, ingredient := range directDependencies {
prefix := "├─"
prefix := output.TreeMid
if i == len(directDependencies)-1 {
prefix = "└─"
prefix = output.TreeEnd
}

// Retrieve runtime dependencies, and then filter out any dependencies that are common between all added ingredients.
Expand Down
4 changes: 2 additions & 2 deletions internal/runbits/dependencies/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ func OutputSummary(out output.Outputer, directDependencies buildplan.Artifacts)
out.Notice(locale.Tl("setting_up_dependencies", " Setting up the following dependencies:"))

for i, ingredient := range ingredients {
prefix := " ├─"
prefix := " " + output.TreeMid
if i == len(ingredients)-1 {
prefix = " └─"
prefix = " " + output.TreeEnd
}

subdependencies := ""
Expand Down
8 changes: 4 additions & 4 deletions internal/runners/artifacts/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ func (b *Artifacts) outputPlain(out *StructuredOutput, fullID bool) error {
switch {
case len(artifact.Errors) > 0:
b.out.Print(fmt.Sprintf(" • %s ([ERROR]%s[/RESET])", artifact.Name, locale.T("artifact_status_failed")))
b.out.Print(fmt.Sprintf(" ├─ %s: [ERROR]%s[/RESET]", locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": ")))
b.out.Print(fmt.Sprintf(" └─ %s: [ACTIONABLE]%s[/RESET]", locale.T("artifact_status_failed_log"), artifact.LogURL))
b.out.Print(fmt.Sprintf(" %s %s: [ERROR]%s[/RESET]", output.TreeMid, locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": ")))
b.out.Print(fmt.Sprintf(" %s %s: [ACTIONABLE]%s[/RESET]", output.TreeEnd, locale.T("artifact_status_failed_log"), artifact.LogURL))
continue
case artifact.status == types.ArtifactSkipped:
b.out.Print(fmt.Sprintf(" • %s ([NOTICE]%s[/RESET])", artifact.Name, locale.T("artifact_status_skipped")))
Expand All @@ -227,8 +227,8 @@ func (b *Artifacts) outputPlain(out *StructuredOutput, fullID bool) error {
switch {
case len(artifact.Errors) > 0:
b.out.Print(fmt.Sprintf(" • %s ([ERROR]%s[/RESET])", artifact.Name, locale.T("artifact_status_failed")))
b.out.Print(fmt.Sprintf(" ├─ %s: [ERROR]%s[/RESET]", locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": ")))
b.out.Print(fmt.Sprintf(" └─ %s: [ACTIONABLE]%s[/RESET]", locale.T("artifact_status_failed_log"), artifact.LogURL))
b.out.Print(fmt.Sprintf(" %s %s: [ERROR]%s[/RESET]", output.TreeMid, locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": ")))
b.out.Print(fmt.Sprintf(" %s %s: [ACTIONABLE]%s[/RESET]", output.TreeEnd, locale.T("artifact_status_failed_log"), artifact.LogURL))
continue
case artifact.status == types.ArtifactSkipped:
b.out.Print(fmt.Sprintf(" • %s ([NOTICE]%s[/RESET])", artifact.Name, locale.T("artifact_status_skipped")))
Expand Down
7 changes: 4 additions & 3 deletions internal/runners/branch/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"sort"
"strings"

"github.com/ActiveState/cli/internal/output"
"github.com/ActiveState/cli/pkg/platform/api/mono/mono_models"
"github.com/ActiveState/cli/pkg/platform/model"
)
Expand All @@ -17,9 +18,9 @@ type branchNode struct {
type tree map[branchNode]tree

const (
prefixLink string = "│"
prefixMid string = "├─"
prefixEnd string = "└─"
prefixLink string = output.TreeLink
prefixMid string = output.TreeMid
prefixEnd string = output.TreeEnd

branchFormatting string = "[NOTICE]%s[/RESET]"
localBranchFormatting string = "[ACTIONABLE]%s[/RESET] [DISABLED](Current)[/RESET]"
Expand Down
4 changes: 2 additions & 2 deletions internal/runners/cve/cve.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,9 @@ func (rd *cveOutput) MarshalOutput(format output.Format) interface{} {
})

for i, d := range ap.Details {
bar := "├─"
bar := output.TreeMid
if i == len(ap.Details)-1 {
bar = "└─"
bar = output.TreeEnd
}
severity := d.Severity
if severity == "CRITICAL" {
Expand Down
2 changes: 1 addition & 1 deletion internal/runners/manifest/requirements.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (o requirements) Print(out output.Outputer) {
}

if req.Namespace != "" {
requirementOutput.Namespace = locale.Tl("manifest_namespace", " └─ [DISABLED]namespace:[/RESET] [CYAN]{{.V0}}[/RESET]", req.Namespace)
requirementOutput.Namespace = locale.Tr("namespace_row", output.TreeEnd, req.Namespace)
}

requirementsOutput = append(requirementsOutput, requirementOutput)
Expand Down
Loading

0 comments on commit 85b4b5c

Please sign in to comment.