Skip to content

Enhances tracking of installed platforms #998

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 20, 2020
Merged
Show file tree
Hide file tree
Changes from 7 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
6 changes: 6 additions & 0 deletions arduino/cores/cores.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,18 @@ type Platform struct {
Package *Package `json:"-"`
}

// PlatformReleaseHelp represents the help URL for this Platform release
type PlatformReleaseHelp struct {
Online string `json:"-"`
}

// PlatformRelease represents a release of a plaform package.
type PlatformRelease struct {
Resource *resources.DownloadResource
Version *semver.Version
BoardsManifest []*BoardManifest
Dependencies ToolDependencies // The Dependency entries to load tools.
Help PlatformReleaseHelp `json:"-"`
Platform *Platform `json:"-"`
Properties *properties.Map `json:"-"`
Boards map[string]*Board `json:"-"`
Expand Down
81 changes: 80 additions & 1 deletion arduino/cores/packageindex/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,15 @@ type indexToolReleaseFlavour struct {
// indexBoard represents a single Board as written in package_index.json file.
type indexBoard struct {
Name string `json:"name"`
ID []indexBoardID `json:"id"`
ID []indexBoardID `json:"id,omitempty"`
}

// indexBoardID represents the ID of a single board. i.e. uno, yun, diecimila, micro and the likes
type indexBoardID struct {
USB string `json:"usb"`
}

// indexHelp represents the help URL
type indexHelp struct {
Online string `json:"online,omitempty"`
}
Expand All @@ -105,6 +107,82 @@ func (index Index) MergeIntoPackages(outPackages cores.Packages) {
}
}

// IndexFromPlatformRelease creates an Index that contains a single indexPackage
// which in turn contains a single indexPlatformRelease converted from the one
// passed as argument
func IndexFromPlatformRelease(pr *cores.PlatformRelease) Index {
boards := []indexBoard{}
for _, manifest := range pr.BoardsManifest {
board := indexBoard{
Name: manifest.Name,
}
for _, id := range manifest.ID {
if id.USB != "" {
board.ID = []indexBoardID{{USB: id.USB}}
}
}
boards = append(boards, board)
}

tools := []indexToolDependency{}
for _, t := range pr.Dependencies {
tools = append(tools, indexToolDependency{
Packager: t.ToolPackager,
Name: t.ToolName,
Version: t.ToolVersion,
})
}

packageTools := []*indexToolRelease{}
for name, tool := range pr.Platform.Package.Tools {
for _, toolRelease := range tool.Releases {
flavours := []indexToolReleaseFlavour{}
for _, flavour := range toolRelease.Flavors {
flavours = append(flavours, indexToolReleaseFlavour{
OS: flavour.OS,
URL: flavour.Resource.URL,
ArchiveFileName: flavour.Resource.ArchiveFileName,
Size: json.Number(fmt.Sprintf("%d", flavour.Resource.Size)),
Checksum: flavour.Resource.Checksum,
})
}
packageTools = append(packageTools, &indexToolRelease{
Name: name,
Version: toolRelease.Version,
Systems: flavours,
})
}
}

return Index{
IsTrusted: pr.IsTrusted,
Packages: []*indexPackage{
{
Name: pr.Platform.Package.Name,
Maintainer: pr.Platform.Package.Maintainer,
WebsiteURL: pr.Platform.Package.WebsiteURL,
URL: pr.Platform.Package.URL,
Email: pr.Platform.Package.Email,
Platforms: []*indexPlatformRelease{{
Name: pr.Platform.Name,
Architecture: pr.Platform.Architecture,
Version: pr.Version,
Category: pr.Platform.Category,
URL: pr.Resource.URL,
ArchiveFileName: pr.Resource.ArchiveFileName,
Checksum: pr.Resource.Checksum,
Size: json.Number(fmt.Sprintf("%d", pr.Resource.Size)),
Boards: boards,
Help: indexHelp{Online: pr.Help.Online},
ToolDependencies: tools,
}},
Tools: packageTools,
Help: indexHelp{Online: pr.Platform.Package.Help.Online},
},
},
}
}

func (inPackage indexPackage) extractPackageIn(outPackages cores.Packages, trusted bool) {
outPackage := outPackages.GetOrCreatePackage(inPackage.Name)
outPackage.Maintainer = inPackage.Maintainer
Expand Down Expand Up @@ -144,6 +222,7 @@ func (inPlatformRelease indexPlatformRelease) extractPlatformIn(outPackage *core
URL: inPlatformRelease.URL,
CachePath: "packages",
}
outPlatformRelease.Help = cores.PlatformReleaseHelp{Online: inPlatformRelease.Help.Online}
outPlatformRelease.BoardsManifest = inPlatformRelease.extractBoardsManifest()
if deps, err := inPlatformRelease.extractDeps(); err == nil {
outPlatformRelease.Dependencies = deps
Expand Down
16 changes: 16 additions & 0 deletions arduino/cores/packagemanager/install_uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
package packagemanager

import (
"encoding/json"
"fmt"
"runtime"

"github.com/arduino/arduino-cli/arduino/cores"
"github.com/arduino/arduino-cli/arduino/cores/packageindex"
"github.com/arduino/arduino-cli/executils"
"github.com/pkg/errors"
)
Expand All @@ -39,6 +41,20 @@ func (pm *PackageManager) InstallPlatform(platformRelease *cores.PlatformRelease
} else {
return err
}
if err := pm.cacheInstalledJSON(platformRelease); err != nil {
return errors.Errorf("creating installed.json in %s: %s", platformRelease.InstallDir, err)
}
return nil
}

func (pm *PackageManager) cacheInstalledJSON(platformRelease *cores.PlatformRelease) error {
index := packageindex.IndexFromPlatformRelease(platformRelease)
platformJSON, err := json.MarshalIndent(index, "", " ")
if err != nil {
return err
}
installedJSON := platformRelease.InstallDir.Join("installed.json")
installedJSON.WriteFile(platformJSON)
return nil
}

Expand Down
102 changes: 102 additions & 0 deletions arduino/cores/packagemanager/install_uninstall_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package packagemanager_test

import (
"encoding/json"
"testing"

"github.com/arduino/arduino-cli/arduino/cores/packageindex"
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
"github.com/arduino/arduino-cli/cli/output"
"github.com/arduino/arduino-cli/commands"
"github.com/arduino/go-paths-helper"
"github.com/stretchr/testify/require"
semver "go.bug.st/relaxed-semver"
)

func TestInstallPlatform(t *testing.T) {
dataDir := paths.New("testdata", "data_dir_1")
packageDir := paths.TempDir().Join("test", "packages")
downloadDir := paths.TempDir().Join("test", "staging")
tmpDir := paths.TempDir().Join("test", "tmp")
packageDir.MkdirAll()
downloadDir.MkdirAll()
tmpDir.MkdirAll()
defer paths.TempDir().Join("test").RemoveAll()

pm := packagemanager.NewPackageManager(dataDir, packageDir, downloadDir, tmpDir)
pm.LoadPackageIndexFromFile(dataDir.Join("package_index.json"))

platformRelease, tools, err := pm.FindPlatformReleaseDependencies(&packagemanager.PlatformReference{
Package: "arduino",
PlatformArchitecture: "avr",
PlatformVersion: semver.MustParse("1.6.23"),
})
require.NotNil(t, platformRelease)
require.NotNil(t, tools)
require.Nil(t, err)

downloaderConfig, err := commands.GetDownloaderConfig()
require.NotNil(t, downloaderConfig)
require.Nil(t, err)
downloader, err := pm.DownloadPlatformRelease(platformRelease, downloaderConfig)
require.NotNil(t, downloader)
require.Nil(t, err)
err = commands.Download(downloader, platformRelease.String(), output.NewNullDownloadProgressCB())
require.Nil(t, err)

err = pm.InstallPlatform(platformRelease)
require.Nil(t, err)

destDir := packageDir.Join("arduino", "hardware", "avr", "1.6.23")
require.True(t, destDir.IsDir())

installedJSON := destDir.Join("installed.json")
require.True(t, installedJSON.Exist())

bt, err := installedJSON.ReadFile()
require.Nil(t, err)

index := &packageindex.Index{}
err = json.Unmarshal(bt, index)
require.Nil(t, err)

expectedJSON := paths.New("testdata", "installed.json")
expectedBt, err := expectedJSON.ReadFile()
require.Nil(t, err)

expectedIndex := &packageindex.Index{}
err = json.Unmarshal(expectedBt, expectedIndex)
require.Nil(t, err)

require.Equal(t, expectedIndex.IsTrusted, index.IsTrusted)
require.Equal(t, len(expectedIndex.Packages), len(index.Packages))

for i := range expectedIndex.Packages {
expectedPackage := expectedIndex.Packages[i]
indexPackage := index.Packages[i]
require.Equal(t, expectedPackage.Name, indexPackage.Name)
require.Equal(t, expectedPackage.Maintainer, indexPackage.Maintainer)
require.Equal(t, expectedPackage.WebsiteURL, indexPackage.WebsiteURL)
require.Equal(t, expectedPackage.Email, indexPackage.Email)
require.Equal(t, expectedPackage.Help.Online, indexPackage.Help.Online)
require.Equal(t, len(expectedPackage.Tools), len(indexPackage.Tools))
require.ElementsMatch(t, expectedPackage.Tools, indexPackage.Tools)

require.Equal(t, len(expectedPackage.Platforms), len(indexPackage.Platforms))
for n := range expectedPackage.Platforms {
expectedPlatform := expectedPackage.Platforms[n]
indexPlatform := indexPackage.Platforms[n]
require.Equal(t, expectedPlatform.Name, indexPlatform.Name)
require.Equal(t, expectedPlatform.Architecture, indexPlatform.Architecture)
require.Equal(t, expectedPlatform.Version.String(), indexPlatform.Version.String())
require.Equal(t, expectedPlatform.Category, indexPlatform.Category)
require.Equal(t, expectedPlatform.Help.Online, indexPlatform.Help.Online)
require.Equal(t, expectedPlatform.URL, indexPlatform.URL)
require.Equal(t, expectedPlatform.ArchiveFileName, indexPlatform.ArchiveFileName)
require.Equal(t, expectedPlatform.Checksum, indexPlatform.Checksum)
require.Equal(t, expectedPlatform.Size, indexPlatform.Size)
require.ElementsMatch(t, expectedPlatform.Boards, indexPlatform.Boards)
require.ElementsMatch(t, expectedPlatform.ToolDependencies, indexPlatform.ToolDependencies)
}
}
}
Copy link
Member

@cmaglie cmaglie Oct 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now looks more like an integration test... I guess we can split all of this in two parts:

  1. A test that verifies if the written installed.json contains the correct information: this one is a unit-test that should be placed in packageindex. In the test you can create a fake cores.Package and try to convert it to json.
  2. An integration test that:
  • installs a platform and checks that the installed.json exists
  • remove the index relative to the previously installed platform and checks that querying the platform will still produce the correct output (in particular name and tool dependencies)

IMHO 1. can be omitted if 2. is done propertly.

10 changes: 10 additions & 0 deletions arduino/cores/packagemanager/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,20 @@ func (pm *PackageManager) loadPlatformRelease(platform *cores.PlatformRelease, p
platform.InstallDir = path

// Some useful paths
installedJSONPath := path.Join("installed.json")
platformTxtPath := path.Join("platform.txt")
platformTxtLocalPath := path.Join("platform.local.txt")
programmersTxtPath := path.Join("programmers.txt")

// If the installed.json file is found load it, this is done to handle the
// case in which the platform's index and its url have been deleted locally,
// if we don't load it some information about the platform is lost
if installedJSONPath.Exist() {
if _, err := pm.LoadPackageIndexFromFile(installedJSONPath); err != nil {
return fmt.Errorf("loading %s: %s", installedJSONPath, err)
}
}

// Create platform properties
platform.Properties = platform.Properties.Clone() // TODO: why CLONE?
if p, err := properties.SafeLoad(platformTxtPath.String()); err == nil {
Expand Down
Loading