Skip to content

Commit 00db5f2

Browse files
committed
Add command for removing libraries or releases
Once a library submission is accepted, the registration and releases is stored in the library database. This means that a library release or library will remain in Library Manager even after the removal of a tag or repository by the library maintainer, or the removal of the library's registration from the registry. A library release or library removal 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 `remove` subcommand. It can be used to remove specific releases: ``` libraries-repository-engine remove LIBRARY_NAME@VERSION... ``` or libraries entirely: ``` libraries-repository-engine remove LIBRARY_NAME... ```
1 parent 455ea5c commit 00db5f2

File tree

5 files changed

+554
-0
lines changed

5 files changed

+554
-0
lines changed

internal/cli/remove.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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/remove"
28+
"github.com/spf13/cobra"
29+
)
30+
31+
// removeCmd defines the `remove` CLI subcommand.
32+
var removeCmd = &cobra.Command{
33+
Short: "Remove libraries or releases",
34+
Long: "Remove libraries or library releases from Library Manager",
35+
DisableFlagsInUseLine: true,
36+
Use: `remove [FLAG]... LIBRARY_NAME[@RELEASE]...
37+
38+
Remove library name LIBRARY_NAME Library Manager content entirely.
39+
-or-
40+
Remove release RELEASE of library name LIBRARY_NAME from the Library Manager content.`,
41+
Run: remove.Run,
42+
}
43+
44+
func init() {
45+
rootCmd.AddCommand(removeCmd)
46+
}

internal/command/remove/remove.go

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
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 remove implements the `remove` CLI subcommand used by the maintainer for removals of libraries or releases.
25+
package remove
26+
27+
import (
28+
"fmt"
29+
"os"
30+
"strings"
31+
32+
"github.com/arduino/go-paths-helper"
33+
"github.com/arduino/libraries-repository-engine/internal/backup"
34+
"github.com/arduino/libraries-repository-engine/internal/configuration"
35+
"github.com/arduino/libraries-repository-engine/internal/feedback"
36+
"github.com/arduino/libraries-repository-engine/internal/libraries"
37+
"github.com/arduino/libraries-repository-engine/internal/libraries/archive"
38+
"github.com/arduino/libraries-repository-engine/internal/libraries/db"
39+
"github.com/arduino/libraries-repository-engine/internal/libraries/metadata"
40+
41+
"github.com/spf13/cobra"
42+
)
43+
44+
var config *configuration.Config
45+
var librariesDb *db.DB
46+
var libraryData *db.Library
47+
48+
// Run executes the command.
49+
func Run(command *cobra.Command, cliArguments []string) {
50+
config = configuration.ReadConf(command.Flags())
51+
52+
if len(cliArguments) == 0 {
53+
feedback.Error("LIBRARY_NAME argument is required")
54+
os.Exit(1)
55+
}
56+
57+
librariesDBPath := paths.New(config.LibrariesDB)
58+
exist, err := librariesDBPath.ExistCheck()
59+
if err != nil {
60+
feedback.Errorf("While checking existence of database file: %s", err)
61+
os.Exit(1)
62+
}
63+
if !exist {
64+
feedback.Errorf("Database file not found at %s. Check the LibrariesDB configuration value.", librariesDBPath)
65+
os.Exit(1)
66+
}
67+
68+
if err := backup.Backup(librariesDBPath); err != nil {
69+
feedback.Errorf("While backing up database: %s", err)
70+
os.Exit(1)
71+
}
72+
73+
librariesDb = db.Init(config.LibrariesDB)
74+
75+
restore, err := removals(cliArguments)
76+
if err != nil {
77+
feedback.Error(err)
78+
if restore {
79+
if err := backup.Restore(); err != nil {
80+
feedback.Errorf("While restoring the content from backup: %s", err)
81+
}
82+
fmt.Println("Original files were restored.")
83+
} else {
84+
if err := backup.Clean(); err != nil {
85+
feedback.Errorf("While cleaning up the backup content: %s", err)
86+
}
87+
}
88+
os.Exit(1)
89+
}
90+
91+
if err := librariesDb.Commit(); err != nil {
92+
feedback.Errorf("While saving changes to database: %s", err)
93+
if err := backup.Restore(); err != nil {
94+
feedback.Errorf("While restoring the content from backup: %s", err)
95+
}
96+
fmt.Println("Original files were restored.")
97+
os.Exit(1)
98+
}
99+
100+
if err := backup.Clean(); err != nil {
101+
feedback.Errorf("While cleaning up the backup files: %s", err)
102+
os.Exit(1)
103+
}
104+
105+
fmt.Println("Success!")
106+
}
107+
108+
func removals(libraryReferences []string) (bool, error) {
109+
for _, libraryReference := range libraryReferences {
110+
referenceComponents := strings.SplitN(libraryReference, "@", 2)
111+
libraryName := referenceComponents[0]
112+
var libraryVersion string
113+
if len(referenceComponents) > 1 {
114+
if referenceComponents[1] == "" {
115+
return false, fmt.Errorf("Missing version for library name %s. For full removal, omit the '@'", libraryName)
116+
}
117+
libraryVersion = referenceComponents[1]
118+
}
119+
120+
if !librariesDb.HasLibrary(libraryName) {
121+
return false, fmt.Errorf("Library name %s not found", libraryName)
122+
}
123+
124+
var err error
125+
libraryData, err = librariesDb.FindLibrary(libraryName)
126+
if err != nil {
127+
return true, err
128+
}
129+
130+
if libraryVersion == "" {
131+
// Remove the library entirely.
132+
if err := removeLibrary(libraryName); err != nil {
133+
return true, err
134+
}
135+
} else {
136+
// Remove only a specific release of the library.
137+
if err := removeRelease(libraryName, libraryVersion); err != nil {
138+
return true, err
139+
}
140+
}
141+
}
142+
143+
return false, nil
144+
}
145+
146+
func removeLibrary(libraryName string) error {
147+
fmt.Printf("Removing %s\n", libraryName)
148+
149+
// Remove the library's release archive files.
150+
releasesData := librariesDb.FindReleasesOfLibrary(libraryData)
151+
for _, releaseData := range releasesData {
152+
if err := removeReleaseArchive(releaseData.Version.String()); err != nil {
153+
return err
154+
}
155+
}
156+
157+
// Remove the library and its releases from database.
158+
if err := librariesDb.RemoveLibrary(libraryName); err != nil {
159+
return err
160+
}
161+
162+
// Remove the library Git clone folder.
163+
libraryRegistration := libraries.Repo{URL: libraryData.Repository}
164+
gitCloneSubfolder, err := libraryRegistration.AsFolder()
165+
if err != nil {
166+
return err
167+
}
168+
gitClonePath := paths.New(config.GitClonesFolder, gitCloneSubfolder)
169+
if err := backup.Backup(gitClonePath); err != nil {
170+
return fmt.Errorf("While backing up library's Git clone: %w", err)
171+
}
172+
if err := gitClonePath.RemoveAll(); err != nil {
173+
return fmt.Errorf("While removing library Git clone: %s", err)
174+
}
175+
176+
return nil
177+
}
178+
179+
func removeRelease(libraryName string, version string) error {
180+
fmt.Printf("Removing %s@%s\n", libraryName, version)
181+
182+
if !librariesDb.HasReleaseByNameVersion(libraryName, version) {
183+
return fmt.Errorf("Library release %s@%s not found", libraryName, version)
184+
}
185+
186+
// Remove the release archive file.
187+
if err := removeReleaseArchive(version); err != nil {
188+
return err
189+
}
190+
191+
// Remove the release from the database.
192+
if err := librariesDb.RemoveReleaseByNameVersion(libraryName, version); err != nil {
193+
return err
194+
}
195+
196+
return nil
197+
}
198+
199+
func removeReleaseArchive(version string) error {
200+
repositoryObject := libraries.Repository{URL: libraryData.Repository}
201+
libraryMetadata := metadata.LibraryMetadata{
202+
Name: libraryData.Name,
203+
Version: version,
204+
}
205+
archiveObject, err := archive.New(&repositoryObject, &libraryMetadata, config)
206+
if err != nil {
207+
panic(err)
208+
}
209+
210+
archivePath := paths.New(archiveObject.Path)
211+
if err := backup.Backup(archivePath); err != nil {
212+
return fmt.Errorf("While backing up library release archive: %w", err)
213+
}
214+
if err := archivePath.RemoveAll(); err != nil {
215+
return fmt.Errorf("While removing library release archive: %s", err)
216+
}
217+
218+
return nil
219+
}

0 commit comments

Comments
 (0)