Skip to content

Commit ffcb006

Browse files
committed
Add command for modifying library data
Although much of the Library Manager content is based on the contents of the individual library repositories, some is defined during the library registration, and so a change to this after the time of the registration requires direct action by a maintainer. This procedure will be facilitated by adding the capability to the engine tool's command line interface in the form of the new `modify` subcommand. The initial implementation of this command provides for the modification of the library repository URL: ``` libraries-repository-engine modify --repo-url=NEW_URL LIBRARY_NAME ``` will change the current repository URL of the library name specified via the positional argument (LIBRARY_NAME) to the new repository URL specified via the `--repo-url` flag (NEW_URL).
1 parent 31bc32a commit ffcb006

File tree

5 files changed

+603
-0
lines changed

5 files changed

+603
-0
lines changed

internal/cli/modify.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// This file is part of libraries-repository-engine.
2+
//
3+
// Copyright 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
// You can be released from the requirements of the above licenses by purchasing
19+
// a commercial license. Buying such a license is mandatory if you want to
20+
// modify or otherwise use the software for commercial activities involving the
21+
// Arduino software without disclosing the source code of your own applications.
22+
// To purchase a commercial license, send an email to license@arduino.cc.
23+
24+
package cli
25+
26+
import (
27+
"github.com/arduino/libraries-repository-engine/internal/command/modify"
28+
"github.com/spf13/cobra"
29+
)
30+
31+
// modifyCmd defines the `modify` CLI subcommand.
32+
var modifyCmd = &cobra.Command{
33+
Short: "Modify library data",
34+
Long: "Modify a library's registration data",
35+
DisableFlagsInUseLine: true,
36+
Use: `modify FLAG... LIBRARY_NAME
37+
38+
Modify the registration data of library name LIBRARY_NAME according to the FLAGs.`,
39+
Args: cobra.ExactArgs(1),
40+
Run: modify.Run,
41+
}
42+
43+
func init() {
44+
modifyCmd.Flags().String("repo-url", "", "New library repository URL")
45+
46+
rootCmd.AddCommand(modifyCmd)
47+
}

internal/command/modify/modify.go

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
// This file is part of libraries-repository-engine.
2+
//
3+
// Copyright 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
// You can be released from the requirements of the above licenses by purchasing
19+
// a commercial license. Buying such a license is mandatory if you want to
20+
// modify or otherwise use the software for commercial activities involving the
21+
// Arduino software without disclosing the source code of your own applications.
22+
// To purchase a commercial license, send an email to license@arduino.cc.
23+
24+
// Package modify implements the `modify` CLI subcommand used by the maintainer for modifications to the library registration data.
25+
package modify
26+
27+
import (
28+
"fmt"
29+
"os"
30+
31+
"github.com/arduino/go-paths-helper"
32+
"github.com/arduino/libraries-repository-engine/internal/backup"
33+
"github.com/arduino/libraries-repository-engine/internal/configuration"
34+
"github.com/arduino/libraries-repository-engine/internal/feedback"
35+
"github.com/arduino/libraries-repository-engine/internal/libraries"
36+
"github.com/arduino/libraries-repository-engine/internal/libraries/archive"
37+
"github.com/arduino/libraries-repository-engine/internal/libraries/db"
38+
"github.com/arduino/libraries-repository-engine/internal/libraries/metadata"
39+
40+
"github.com/spf13/cobra"
41+
"github.com/spf13/pflag"
42+
)
43+
44+
var config *configuration.Config
45+
var libraryName string
46+
var libraryData *db.Library
47+
var releasesData []*db.Release
48+
49+
// Run executes the command.
50+
func Run(command *cobra.Command, cliArguments []string) {
51+
var err error
52+
config = configuration.ReadConf(command.Flags())
53+
54+
libraryName = cliArguments[0]
55+
56+
librariesDBPath := paths.New(config.LibrariesDB)
57+
exist, err := librariesDBPath.ExistCheck()
58+
if err != nil {
59+
feedback.Errorf("While checking existence of database file: %s", err)
60+
os.Exit(1)
61+
}
62+
if !exist {
63+
feedback.Errorf("Database file not found at %s. Check the LibrariesDB configuration value.", librariesDBPath)
64+
os.Exit(1)
65+
}
66+
67+
if err := backup.Backup(librariesDBPath); err != nil {
68+
feedback.Errorf("While backing up database: %s", err)
69+
os.Exit(1)
70+
}
71+
72+
// Load all the library's data from the DB.
73+
librariesDb := db.Init(librariesDBPath.String())
74+
if !librariesDb.HasLibrary(libraryName) {
75+
feedback.Errorf("Library of name %s not found", libraryName)
76+
os.Exit(1)
77+
}
78+
libraryData, err = librariesDb.FindLibrary(libraryName)
79+
if err != nil {
80+
panic(err)
81+
}
82+
releasesData = librariesDb.FindReleasesOfLibrary(libraryData)
83+
84+
restore, err := modifications(command.Flags())
85+
if err != nil {
86+
feedback.Error(err)
87+
if restore {
88+
if err := backup.Restore(); err != nil {
89+
feedback.Errorf("While restoring the content from backup: %s", err)
90+
}
91+
fmt.Println("Original files were restored.")
92+
} else {
93+
if err := backup.Clean(); err != nil {
94+
feedback.Errorf("While cleaning up the backup content: %s", err)
95+
}
96+
}
97+
os.Exit(1)
98+
}
99+
100+
if err := librariesDb.Commit(); err != nil {
101+
feedback.Errorf("While saving changes to database: %s", err)
102+
if err := backup.Restore(); err != nil {
103+
feedback.Errorf("While restoring the content from backup: %s", err)
104+
}
105+
fmt.Println("Original files were restored.")
106+
os.Exit(1)
107+
}
108+
109+
if err := backup.Clean(); err != nil {
110+
feedback.Errorf("While cleaning up the backup files: %s", err)
111+
os.Exit(1)
112+
}
113+
114+
fmt.Println("Success!")
115+
}
116+
117+
func modifications(flags *pflag.FlagSet) (bool, error) {
118+
didModify := false // Require at least one modification operation was specified by user.
119+
120+
newRepositoryURL, err := flags.GetString("repo-url")
121+
if err != nil {
122+
return false, err
123+
}
124+
125+
if newRepositoryURL != "" {
126+
if err := modifyRepositoryURL(newRepositoryURL); err != nil {
127+
return true, err
128+
}
129+
130+
didModify = true
131+
}
132+
133+
if !didModify {
134+
return false, fmt.Errorf("No modification flags provided so nothing happened. See 'libraries-repository-engine modify --help'")
135+
}
136+
137+
return false, nil
138+
}
139+
140+
func modifyRepositoryURL(newRepositoryURL string) error {
141+
if !libraries.RepoURLValid(newRepositoryURL) {
142+
return fmt.Errorf("Library URL %s does not have a valid format", newRepositoryURL)
143+
}
144+
145+
if libraryData.Repository == newRepositoryURL {
146+
return fmt.Errorf("Library %s already has URL %s", libraryName, newRepositoryURL)
147+
}
148+
149+
oldRepositoryURL := libraryData.Repository
150+
151+
fmt.Printf("Changing URL of library %s from %s to %s\n", libraryName, oldRepositoryURL, newRepositoryURL)
152+
153+
// Move the library Git clone to the new path.
154+
gitClonePath := func(url string) (*paths.Path, error) {
155+
libraryRegistration := libraries.Repo{URL: url}
156+
gitCloneSubfolder, err := libraryRegistration.AsFolder()
157+
if err != nil {
158+
return nil, err
159+
}
160+
161+
return paths.New(config.GitClonesFolder, gitCloneSubfolder), nil
162+
}
163+
oldGitClonePath, err := gitClonePath(oldRepositoryURL)
164+
if err != nil {
165+
return err
166+
}
167+
newGitClonePath, err := gitClonePath(newRepositoryURL)
168+
if err != nil {
169+
return err
170+
}
171+
if err := newGitClonePath.Parent().MkdirAll(); err != nil {
172+
return fmt.Errorf("While creating new library Git clone path: %w", err)
173+
}
174+
if err := backup.Backup(oldGitClonePath); err != nil {
175+
return fmt.Errorf("While backing up library's Git clone: %w", err)
176+
}
177+
if err := oldGitClonePath.Rename(newGitClonePath); err != nil {
178+
return fmt.Errorf("While moving library's Git clone: %w", err)
179+
}
180+
181+
// Update the library repository URL in the database.
182+
libraryData.Repository = newRepositoryURL
183+
184+
// Update library releases.
185+
oldRepositoryObject := libraries.Repository{URL: oldRepositoryURL}
186+
newRepositoryObject := libraries.Repository{URL: newRepositoryURL}
187+
libraryMetadata := metadata.LibraryMetadata{Name: libraryData.Name}
188+
for _, releaseData := range releasesData {
189+
libraryMetadata.Version = releaseData.Version.String()
190+
oldArchiveObject, err := archive.New(&oldRepositoryObject, &libraryMetadata, config)
191+
if err != nil {
192+
return err
193+
}
194+
newArchiveObject, err := archive.New(&newRepositoryObject, &libraryMetadata, config)
195+
if err != nil {
196+
return err
197+
}
198+
199+
// Move the release archive to the correct path for the new URL (some path components are based on the library repo URL).
200+
oldArchiveObjectPath := paths.New(oldArchiveObject.Path)
201+
newArchiveObjectPath := paths.New(newArchiveObject.Path)
202+
if err := newArchiveObjectPath.Parent().MkdirAll(); err != nil {
203+
return fmt.Errorf("While creating new library release archives path: %w", err)
204+
}
205+
if err := backup.Backup(oldArchiveObjectPath); err != nil {
206+
return fmt.Errorf("While backing up library release archive: %w", err)
207+
}
208+
if err := oldArchiveObjectPath.Rename(newArchiveObjectPath); err != nil {
209+
return fmt.Errorf("While moving library release archive: %w", err)
210+
}
211+
212+
// Update the release download URL in the database.
213+
releaseData.URL = newArchiveObject.URL
214+
}
215+
216+
return nil
217+
}

0 commit comments

Comments
 (0)