Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/arduino-app-cli/system/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"github.com/arduino/arduino-app-cli/cmd/arduino-app-cli/internal/servicelocator"
"github.com/arduino/arduino-app-cli/cmd/feedback"
"github.com/arduino/arduino-app-cli/internal/eventstream"
"github.com/arduino/arduino-app-cli/internal/helpers"
"github.com/arduino/arduino-app-cli/internal/orchestrator"
"github.com/arduino/arduino-app-cli/internal/orchestrator/config"
Expand Down Expand Up @@ -115,7 +116,7 @@ func newUpdateCmd() *cobra.Command {
for event := range events {
feedback.Printf("[%s] %s", event.Type.String(), event.Data)

if event.Type == update.DoneEvent {
if event.Type == eventstream.DoneEvent {
break
}
}
Expand Down
8 changes: 4 additions & 4 deletions cmd/gendoc/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ import (

"github.com/arduino/arduino-app-cli/internal/api/handlers"
"github.com/arduino/arduino-app-cli/internal/api/models"
"github.com/arduino/arduino-app-cli/internal/eventstream"
"github.com/arduino/arduino-app-cli/internal/orchestrator"
"github.com/arduino/arduino-app-cli/internal/orchestrator/app"
"github.com/arduino/arduino-app-cli/internal/orchestrator/bricks"
"github.com/arduino/arduino-app-cli/internal/update"
)

type Tag string
Expand Down Expand Up @@ -81,10 +81,10 @@ func NewOpenApiGenerator(version string) *Generator {
openapi3.SchemaOrRef{
Schema: &openapi3.Schema{
UniqueItems: f.Ptr(true),
Enum: f.Map(update.PackageType("").AllowedStatuses(), func(v update.PackageType) interface{} { return v }),
Enum: f.Map(eventstream.PackageType("").AllowedStatuses(), func(v eventstream.PackageType) interface{} { return v }),
Type: f.Ptr(openapi3.SchemaTypeString),
Description: f.Ptr("Package type"),
ReflectType: reflect.TypeOf(update.PackageType("")),
ReflectType: reflect.TypeOf(eventstream.PackageType("")),
},
},
)
Expand Down Expand Up @@ -216,7 +216,7 @@ func NewOpenApiGenerator(version string) *Generator {
})
}

if params.Value.Type() == reflect.TypeOf(update.PackageType("")) {
if params.Value.Type() == reflect.TypeOf(eventstream.PackageType("")) {
params.Schema.WithRef("#/components/schemas/PackageType")
return true, nil
}
Expand Down
12 changes: 9 additions & 3 deletions internal/api/handlers/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"log/slog"

"github.com/arduino/arduino-app-cli/internal/api/models"
"github.com/arduino/arduino-app-cli/internal/eventstream"
"github.com/arduino/arduino-app-cli/internal/render"
"github.com/arduino/arduino-app-cli/internal/update"
)
Expand Down Expand Up @@ -127,12 +128,17 @@ func HandleUpdateEvents(updater *update.Manager) http.HandlerFunc {
slog.Info("APT event channel closed, stopping SSE stream")
return
}
if event.Type == update.ErrorEvent {
switch event.Type {
case eventstream.ErrorEvent:
sseStream.SendError(render.SSEErrorData{
Code: render.InternalServiceErr,
Message: event.Data,
Message: event.Data})
case eventstream.ProgressEvent:
sseStream.Send(render.SSEEvent{
Type: event.Type.String(),
Data: event.Progress,
})
} else {
default:
sseStream.Send(render.SSEEvent{
Type: event.Type.String(),
Data: event.Data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.

package update
package eventstream

// EventType defines the type of upgrade event.
type EventType int
Expand All @@ -24,13 +24,15 @@ const (
RestartEvent
DoneEvent
ErrorEvent
ProgressEvent
)

// Event represents a single event in the upgrade process.
type Event struct {
Type EventType
Data string
Err error // Optional error field for error events
Type EventType
Data string
Err error // Optional error field for error events
Progress float32
}

func (t EventType) String() string {
Expand All @@ -45,6 +47,8 @@ func (t EventType) String() string {
return "done"
case ErrorEvent:
return "error"
case ProgressEvent:
return "progress"
default:
panic("unreachable")
}
Expand Down
47 changes: 26 additions & 21 deletions internal/update/apt/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/arduino/go-paths-helper"
"go.bug.st/f"

"github.com/arduino/arduino-app-cli/internal/eventstream"
"github.com/arduino/arduino-app-cli/internal/orchestrator"
"github.com/arduino/arduino-app-cli/internal/update"
)
Expand Down Expand Up @@ -74,11 +75,11 @@ func (s *Service) ListUpgradablePackages(ctx context.Context, matcher func(updat
// UpgradePackages upgrades the specified packages using the `apt-get upgrade` command.
// It publishes events to subscribers during the upgrade process.
// It returns an error if the upgrade is already in progress or if the upgrade command fails.
func (s *Service) UpgradePackages(ctx context.Context, names []string) (<-chan update.Event, error) {
func (s *Service) UpgradePackages(ctx context.Context, names []string) (<-chan eventstream.Event, error) {
if !s.lock.TryLock() {
return nil, update.ErrOperationAlreadyInProgress
}
eventsCh := make(chan update.Event, 100)
eventsCh := make(chan eventstream.Event, 100)

go func() {
defer s.lock.Unlock()
Expand All @@ -87,72 +88,76 @@ func (s *Service) UpgradePackages(ctx context.Context, names []string) (<-chan u
ctx, cancel := context.WithTimeout(ctx, 10*time.Minute)
defer cancel()

eventsCh <- update.Event{Type: update.StartEvent, Data: "Upgrade is starting"}
eventsCh <- eventstream.Event{Type: eventstream.StartEvent, Data: "Upgrade is starting"}
stream := runUpgradeCommand(ctx, names)
for line, err := range stream {
if err != nil {
eventsCh <- update.Event{
Type: update.ErrorEvent,
eventsCh <- eventstream.Event{
Type: eventstream.ErrorEvent,
Err: err,
Data: "Error running upgrade command",
}
slog.Error("error processing upgrade command output", "error", err)
return
}
eventsCh <- update.Event{Type: update.UpgradeLineEvent, Data: line}
eventsCh <- eventstream.Event{Type: eventstream.UpgradeLineEvent, Data: line}
}
eventsCh <- update.Event{Type: update.StartEvent, Data: "apt cleaning cache is starting"}
eventsCh <- eventstream.Event{Type: eventstream.StartEvent, Data: "apt cleaning cache is starting"}
eventsCh <- eventstream.Event{Type: eventstream.ProgressEvent, Progress: 80.0}
eventsCh <- eventstream.Event{Type: eventstream.StartEvent, Data: "apt cleaning cache is starting"}
for line, err := range runAptCleanCommand(ctx) {
if err != nil {
eventsCh <- update.Event{
Type: update.ErrorEvent,
eventsCh <- eventstream.Event{
Type: eventstream.ErrorEvent,
Err: err,
Data: "Error running apt clean command",
}
slog.Error("error processing apt clean command output", "error", err)
return
}
eventsCh <- update.Event{Type: update.UpgradeLineEvent, Data: line}
eventsCh <- eventstream.Event{Type: eventstream.UpgradeLineEvent, Data: line}
}
eventsCh <- eventstream.Event{Type: eventstream.ProgressEvent, Progress: 85.0}
// TEMPORARY PATCH: stopping and destroying docker containers and images since IDE does not implement it yet.
// TODO: Remove this workaround once IDE implements it.
// Tracking issue: https://github.com/arduino/arduino-app-cli/issues/623
eventsCh <- update.Event{Type: update.UpgradeLineEvent, Data: "Stop and destroy docker containers and images ..."}
eventsCh <- eventstream.Event{Type: eventstream.UpgradeLineEvent, Data: "Stop and destroy docker containers and images ..."}
streamCleanup := cleanupDockerContainers(ctx)
for line, err := range streamCleanup {
if err != nil {
// TODO: maybe we should retun an error or a better feedback to the user?
// currently, we just log the error and continue considenring not blocking
slog.Error("Error stopping and destroying docker containers", "error", err)
}
eventsCh <- update.Event{Type: update.UpgradeLineEvent, Data: line}
eventsCh <- eventstream.Event{Type: eventstream.UpgradeLineEvent, Data: line}
}

eventsCh <- eventstream.Event{Type: eventstream.ProgressEvent, Progress: 90.0}
// TEMPORARY PATCH: Install the latest docker images and show the logs to the users.
// TODO: Remove this workaround once docker image versions are no longer hardcoded in arduino-app-cli.
// Tracking issue: https://github.com/arduino/arduino-app-cli/issues/600
// Currently, we need to launch `arduino-app-cli system init` to pull the latest docker images because
// the version of the docker images are hardcoded in the (new downloaded) version of the arduino-app-cli.
eventsCh <- update.Event{Type: update.UpgradeLineEvent, Data: "Pulling the latest docker images ..."}
eventsCh <- eventstream.Event{Type: eventstream.UpgradeLineEvent, Data: "Pulling the latest docker images ..."}
streamDocker := pullDockerImages(ctx)
for line, err := range streamDocker {
if err != nil {
eventsCh <- update.Event{
Type: update.ErrorEvent,
eventsCh <- eventstream.Event{
Type: eventstream.ErrorEvent,
Err: err,
Data: "Error upgrading docker images",
}
slog.Error("error upgrading docker images", "error", err)
return
}
eventsCh <- update.Event{Type: update.UpgradeLineEvent, Data: line}
eventsCh <- eventstream.Event{Type: eventstream.UpgradeLineEvent, Data: line}
}
eventsCh <- update.Event{Type: update.RestartEvent, Data: "Upgrade completed. Restarting ..."}
eventsCh <- eventstream.Event{Type: eventstream.ProgressEvent, Progress: 100.0}
eventsCh <- eventstream.Event{Type: eventstream.RestartEvent, Data: "Upgrade completed. Restarting ..."}

err := restartServices(ctx)
if err != nil {
eventsCh <- update.Event{
Type: update.ErrorEvent,
eventsCh <- eventstream.Event{
Type: eventstream.ErrorEvent,
Err: err,
Data: "Error restart services after upgrade",
}
Expand Down Expand Up @@ -361,7 +366,7 @@ func parseListUpgradableOutput(r io.Reader) []update.UpgradablePackage {
name := strings.Split(matches[1], "/")[0]

pkg := update.UpgradablePackage{
Type: update.Debian,
Type: eventstream.Debian,
Name: name,
ToVersion: matches[2],
Architecture: matches[3],
Expand Down
13 changes: 7 additions & 6 deletions internal/update/apt/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/stretchr/testify/require"

"github.com/arduino/arduino-app-cli/internal/eventstream"
"github.com/arduino/arduino-app-cli/internal/update"
)

Expand All @@ -46,7 +47,7 @@ func TestParseListUpgradableOutput(t *testing.T) {
input: "nano/bionic-updates 2.9.3-2 amd64",
expected: []update.UpgradablePackage{
{
Type: update.Debian,
Type: eventstream.Debian,
Name: "nano",
ToVersion: "2.9.3-2",
FromVersion: "",
Expand All @@ -59,7 +60,7 @@ func TestParseListUpgradableOutput(t *testing.T) {
input: "apt/focal-updates 2.0.11 amd64 [upgradable from: 2.0.10]",
expected: []update.UpgradablePackage{
{
Type: update.Debian,
Type: eventstream.Debian,
Name: "apt",
ToVersion: "2.0.11",
FromVersion: "2.0.10",
Expand All @@ -77,28 +78,28 @@ containerd.io/focal 1.7.27-1 amd64 [upgradable from: 1.7.25-1]
`,
expected: []update.UpgradablePackage{
{
Type: update.Debian,
Type: eventstream.Debian,
Name: "distro-info-data",
ToVersion: "0.43ubuntu1.18",
FromVersion: "0.43ubuntu1.16",
Architecture: "all",
},
{
Type: update.Debian,
Type: eventstream.Debian,
Name: "apt",
ToVersion: "2.0.11",
FromVersion: "2.0.10",
Architecture: "amd64",
},
{
Type: update.Debian,
Type: eventstream.Debian,
Name: "code",
ToVersion: "1.100.3-1748872405",
FromVersion: "1.100.2-1747260578",
Architecture: "amd64",
},
{
Type: update.Debian,
Type: eventstream.Debian,
Name: "containerd.io",
ToVersion: "1.7.27-1",
FromVersion: "1.7.25-1",
Expand Down
Loading