Skip to content

Commit a8e130b

Browse files
authored
Merge pull request #74 from per1234/modify-command
Add command for modifying library data
2 parents 217e559 + ffcb006 commit a8e130b

File tree

7 files changed

+627
-0
lines changed

7 files changed

+627
-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 [email protected].
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 [email protected].
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+
}

internal/libraries/repolist.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ func (repoMatcherIfDotGit) Match(url string) bool {
7474
return strings.Index(url, "https://") == 0 && strings.LastIndex(url, ".git") == len(url)-len(".git")
7575
}
7676

77+
// RepoURLValid returns whether the given URL has a valid format.
78+
func RepoURLValid(url string) bool {
79+
return repoMatcherIfDotGit{}.Match(url)
80+
}
81+
7782
// GitURLsError is the type for the unknown or unsupported repositories data.
7883
type GitURLsError struct {
7984
Repos []*Repo

internal/libraries/repolist_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,30 @@
2424
package libraries
2525

2626
import (
27+
"fmt"
2728
"testing"
2829

30+
"github.com/stretchr/testify/assert"
2931
"github.com/stretchr/testify/require"
3032
)
3133

34+
func TestRepoURLValid(t *testing.T) {
35+
testTables := []struct {
36+
url string
37+
assertion assert.BoolAssertionFunc
38+
}{
39+
{"example.com", assert.False},
40+
{"example.com/foo.git", assert.False},
41+
{"http://example.com/foo.git", assert.False},
42+
{"https://example.com/foo", assert.False},
43+
{"https://example/com/foo.git", assert.True},
44+
}
45+
46+
for _, testTable := range testTables {
47+
testTable.assertion(t, RepoURLValid(testTable.url), fmt.Sprintf("URL: %s", testTable.url))
48+
}
49+
}
50+
3251
func TestRepoFolderPathDetermination(t *testing.T) {
3352
repo := &Repo{URL: "https://github.com/arduino-libraries/Servo.git"}
3453
f, err := repo.AsFolder()

0 commit comments

Comments
 (0)