feat: support mirrors
This commit is contained in:
@ -11,7 +11,7 @@
|
|||||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||||
<text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
|
<text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
|
||||||
<text x="33.5" y="14">coverage</text>
|
<text x="33.5" y="14">coverage</text>
|
||||||
<text x="86" y="15" fill="#010101" fill-opacity=".3">19.3%</text>
|
<text x="86" y="15" fill="#010101" fill-opacity=".3">18.8%</text>
|
||||||
<text x="86" y="14">19.3%</text>
|
<text x="86" y="14">18.8%</text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 926 B |
53
e2e-tests/issue_78_mirrors_test.go
Normal file
53
e2e-tests/issue_78_mirrors_test.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// ALR - Any Linux Repository
|
||||||
|
// Copyright (C) 2025 The ALR Authors
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//go:build e2e
|
||||||
|
|
||||||
|
package e2etests_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/efficientgo/e2e"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestE2EIssue78Mirrors(t *testing.T) {
|
||||||
|
dockerMultipleRun(
|
||||||
|
t,
|
||||||
|
"issue-78-mirrors",
|
||||||
|
COMMON_SYSTEMS,
|
||||||
|
func(t *testing.T, r e2e.Runnable) {
|
||||||
|
defaultPrepare(t, r)
|
||||||
|
execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "add", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
|
||||||
|
execShouldNoError(t, r, "sudo", "alr", "repo", "set-url", REPO_NAME_FOR_E2E_TESTS, "https://example.com")
|
||||||
|
execShouldNoError(t, r, "sudo", "alr", "ref")
|
||||||
|
execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "clear", REPO_NAME_FOR_E2E_TESTS)
|
||||||
|
execShouldError(t, r, "sudo", "alr", "ref")
|
||||||
|
|
||||||
|
execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "add", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
|
||||||
|
execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "rm", "--partial", REPO_NAME_FOR_E2E_TESTS, "gitea.plemya-x.ru/Maks1mS")
|
||||||
|
execShouldError(t, r, "sudo", "alr", "ref")
|
||||||
|
|
||||||
|
execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "add", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
|
||||||
|
execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "rm", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
|
||||||
|
execShouldError(t, r, "sudo", "alr", "ref")
|
||||||
|
|
||||||
|
execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "add", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
|
||||||
|
execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "rm", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
|
||||||
|
execShouldError(t, r, "sudo", "alr", "ref")
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
@ -31,7 +31,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-git/go-billy/v5"
|
"github.com/go-git/go-billy/v5"
|
||||||
"github.com/go-git/go-billy/v5/osfs"
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
gitConfig "github.com/go-git/go-git/v5/config"
|
gitConfig "github.com/go-git/go-git/v5/config"
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
@ -69,159 +68,212 @@ func (rs *Repos) Pull(ctx context.Context, repos []types.Repo) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, repo := range repos {
|
for _, repo := range repos {
|
||||||
repoURL, err := url.Parse(repo.URL)
|
urls := []string{repo.URL}
|
||||||
|
urls = append(urls, repo.Mirrors...)
|
||||||
|
|
||||||
|
var lastErr error
|
||||||
|
|
||||||
|
for i, repoURL := range urls {
|
||||||
|
if i > 0 {
|
||||||
|
slog.Info(gotext.Get("Trying mirror"), "repo", repo.Name, "mirror", repoURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := rs.pullRepoFromURL(ctx, repoURL, repo)
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
slog.Warn(gotext.Get("Failed to pull from URL"), "repo", repo.Name, "url", repoURL, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("failed to pull repository %s from any URL: %w", repo.Name, lastErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readGitRepo(repoDir, repoUrl string) (*git.Repository, bool, error) {
|
||||||
|
gitDir := filepath.Join(repoDir, ".git")
|
||||||
|
if fi, err := os.Stat(gitDir); err == nil && fi.IsDir() {
|
||||||
|
r, err := git.PlainOpen(repoDir)
|
||||||
|
if err == nil {
|
||||||
|
err = updateRemoteURL(r, repoUrl)
|
||||||
|
if err == nil {
|
||||||
|
_, err := r.Head()
|
||||||
|
if err == nil {
|
||||||
|
return r, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.Is(err, plumbing.ErrReferenceNotFound) {
|
||||||
|
return r, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Debug("error getting HEAD, reinitializing...", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Debug("error while reading repo, reinitializing...", "err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.RemoveAll(repoDir); err != nil {
|
||||||
|
return nil, false, fmt.Errorf("failed to remove repo directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(repoDir, 0o755); err != nil {
|
||||||
|
return nil, false, fmt.Errorf("failed to create repo directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := git.PlainInit(repoDir, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("failed to initialize git repo: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = r.CreateRemote(&gitConfig.RemoteConfig{
|
||||||
|
Name: git.DefaultRemoteName,
|
||||||
|
URLs: []string{repoUrl},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *Repos) pullRepoFromURL(ctx context.Context, rawRepoUrl string, repo types.Repo) error {
|
||||||
|
repoURL, err := url.Parse(rawRepoUrl)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid URL %s: %w", rawRepoUrl, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info(gotext.Get("Pulling repository"), "name", repo.Name)
|
||||||
|
repoDir := filepath.Join(rs.cfg.GetPaths().RepoDir, repo.Name)
|
||||||
|
|
||||||
|
var repoFS billy.Filesystem
|
||||||
|
|
||||||
|
r, freshGit, err := readGitRepo(repoDir, repoURL.String())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open repo")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.FetchContext(ctx, &git.FetchOptions{
|
||||||
|
Progress: os.Stderr,
|
||||||
|
Force: true,
|
||||||
|
})
|
||||||
|
if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var old *plumbing.Reference
|
||||||
|
|
||||||
|
w, err := r.Worktree()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
revHash, err := resolveHash(r, repo.Ref)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error resolving hash: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !freshGit {
|
||||||
|
old, err = r.Head()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Info(gotext.Get("Pulling repository"), "name", repo.Name)
|
if old.Hash() == *revHash {
|
||||||
repoDir := filepath.Join(rs.cfg.GetPaths().RepoDir, repo.Name)
|
slog.Info(gotext.Get("Repository up to date"), "name", repo.Name)
|
||||||
|
|
||||||
var repoFS billy.Filesystem
|
|
||||||
gitDir := filepath.Join(repoDir, ".git")
|
|
||||||
// Only pull repos that contain valid git repos
|
|
||||||
if fi, err := os.Stat(gitDir); err == nil && fi.IsDir() {
|
|
||||||
r, err := git.PlainOpen(repoDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.FetchContext(ctx, &git.FetchOptions{
|
|
||||||
Progress: os.Stderr,
|
|
||||||
Force: true,
|
|
||||||
})
|
|
||||||
if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := r.Worktree()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
old, err := r.Head()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
revHash, err := resolveHash(r, repo.Ref)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error resolving hash: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if old.Hash() == *revHash {
|
|
||||||
slog.Info(gotext.Get("Repository up to date"), "name", repo.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = w.Checkout(&git.CheckoutOptions{
|
|
||||||
Hash: plumbing.NewHash(revHash.String()),
|
|
||||||
Force: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
repoFS = w.Filesystem
|
|
||||||
|
|
||||||
new, err := r.Head()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the DB was not present at startup, that means it's
|
|
||||||
// empty. In this case, we need to update the DB fully
|
|
||||||
// rather than just incrementally.
|
|
||||||
if rs.db.IsEmpty() {
|
|
||||||
err = rs.processRepoFull(ctx, repo, repoDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = rs.processRepoChanges(ctx, repo, r, w, old, new)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = os.RemoveAll(repoDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.MkdirAll(repoDir, 0o755)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := git.PlainInit(repoDir, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = r.CreateRemote(&gitConfig.RemoteConfig{
|
|
||||||
Name: git.DefaultRemoteName,
|
|
||||||
URLs: []string{repoURL.String()},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.FetchContext(ctx, &git.FetchOptions{
|
|
||||||
Progress: os.Stderr,
|
|
||||||
Force: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := r.Worktree()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
revHash, err := resolveHash(r, repo.Ref)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error resolving hash: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = w.Checkout(&git.CheckoutOptions{
|
|
||||||
Hash: plumbing.NewHash(revHash.String()),
|
|
||||||
Force: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = rs.processRepoFull(ctx, repo, repoDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
repoFS = osfs.New(repoDir)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fl, err := repoFS.Open("alr-repo.toml")
|
err = w.Checkout(&git.CheckoutOptions{
|
||||||
if err != nil {
|
Hash: plumbing.NewHash(revHash.String()),
|
||||||
slog.Warn(gotext.Get("Git repository does not appear to be a valid ALR repo"), "repo", repo.Name)
|
Force: true,
|
||||||
continue
|
})
|
||||||
}
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
repoFS = w.Filesystem
|
||||||
|
|
||||||
var repoCfg types.RepoConfig
|
new, err := r.Head()
|
||||||
err = toml.NewDecoder(fl).Decode(&repoCfg)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the DB was not present at startup, that means it's
|
||||||
|
// empty. In this case, we need to update the DB fully
|
||||||
|
// rather than just incrementally.
|
||||||
|
if rs.db.IsEmpty() || freshGit {
|
||||||
|
err = rs.processRepoFull(ctx, repo, repoDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fl.Close()
|
} else {
|
||||||
|
err = rs.processRepoChanges(ctx, repo, r, w, old, new)
|
||||||
// If the version doesn't have a "v" prefix, it's not a standard version.
|
if err != nil {
|
||||||
// It may be "unknown" or a git version, but either way, there's no way
|
return err
|
||||||
// to compare it to the repo version, so only compare versions with the "v".
|
|
||||||
if strings.HasPrefix(config.Version, "v") {
|
|
||||||
if vercmp.Compare(config.Version, repoCfg.Repo.MinVersion) == -1 {
|
|
||||||
slog.Warn(gotext.Get("ALR repo's minimum ALR version is greater than the current version. Try updating ALR if something doesn't work."), "repo", repo.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fl, err := repoFS.Open("alr-repo.toml")
|
||||||
|
if err != nil {
|
||||||
|
slog.Warn(gotext.Get("Git repository does not appear to be a valid ALR repo"), "repo", repo.Name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var repoCfg types.RepoConfig
|
||||||
|
err = toml.NewDecoder(fl).Decode(&repoCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fl.Close()
|
||||||
|
|
||||||
|
// If the version doesn't have a "v" prefix, it's not a standard version.
|
||||||
|
// It may be "unknown" or a git version, but either way, there's no way
|
||||||
|
// to compare it to the repo version, so only compare versions with the "v".
|
||||||
|
if strings.HasPrefix(config.Version, "v") {
|
||||||
|
if vercmp.Compare(config.Version, repoCfg.Repo.MinVersion) == -1 {
|
||||||
|
slog.Warn(gotext.Get("ALR repo's minimum ALR version is greater than the current version. Try updating ALR if something doesn't work."), "repo", repo.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateRemoteURL(r *git.Repository, newURL string) error {
|
||||||
|
cfg, err := r.Config()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
remote, ok := cfg.Remotes[git.DefaultRemoteName]
|
||||||
|
if !ok || len(remote.URLs) == 0 {
|
||||||
|
return fmt.Errorf("no remote '%s' found", git.DefaultRemoteName)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentURL := remote.URLs[0]
|
||||||
|
if currentURL == newURL {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Debug("Updating remote URL", "old", currentURL, "new", newURL)
|
||||||
|
|
||||||
|
err = r.DeleteRemote(git.DefaultRemoteName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete old remote: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = r.CreateRemote(&gitConfig.RemoteConfig{
|
||||||
|
Name: git.DefaultRemoteName,
|
||||||
|
URLs: []string{newURL},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create new remote: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
|
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
|
||||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
|
|
||||||
database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
|
database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
|
||||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/repos"
|
"gitea.plemya-x.ru/Plemya-x/ALR/internal/repos"
|
||||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
|
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
|
||||||
@ -35,7 +34,7 @@ import (
|
|||||||
type TestEnv struct {
|
type TestEnv struct {
|
||||||
Ctx context.Context
|
Ctx context.Context
|
||||||
Cfg *TestALRConfig
|
Cfg *TestALRConfig
|
||||||
Db *db.Database
|
Db *database.Database
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestALRConfig struct {
|
type TestALRConfig struct {
|
||||||
|
@ -383,19 +383,27 @@ msgstr ""
|
|||||||
msgid "ERROR"
|
msgid "ERROR"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: internal/repos/pull.go:77
|
#: internal/repos/pull.go:78
|
||||||
|
msgid "Trying mirror"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: internal/repos/pull.go:84
|
||||||
|
msgid "Failed to pull from URL"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: internal/repos/pull.go:151
|
||||||
msgid "Pulling repository"
|
msgid "Pulling repository"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: internal/repos/pull.go:113
|
#: internal/repos/pull.go:188
|
||||||
msgid "Repository up to date"
|
msgid "Repository up to date"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: internal/repos/pull.go:204
|
#: internal/repos/pull.go:223
|
||||||
msgid "Git repository does not appear to be a valid ALR repo"
|
msgid "Git repository does not appear to be a valid ALR repo"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: internal/repos/pull.go:220
|
#: internal/repos/pull.go:239
|
||||||
msgid ""
|
msgid ""
|
||||||
"ALR repo's minimum ALR version is greater than the current version. Try "
|
"ALR repo's minimum ALR version is greater than the current version. Try "
|
||||||
"updating ALR if something doesn't work."
|
"updating ALR if something doesn't work."
|
||||||
@ -461,63 +469,104 @@ msgstr ""
|
|||||||
msgid "Pull all repositories that have changed"
|
msgid "Pull all repositories that have changed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:39
|
#: repo.go:41
|
||||||
msgid "Manage repos"
|
msgid "Manage repos"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:51 repo.go:269
|
#: repo.go:55 repo.go:625
|
||||||
msgid "Remove an existing repository"
|
msgid "Remove an existing repository"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:53
|
#: repo.go:57 repo.go:521
|
||||||
msgid "<name>"
|
msgid "<name>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:83
|
#: repo.go:102 repo.go:465 repo.go:568
|
||||||
msgid "Repo \"%s\" does not exist"
|
msgid "Repo \"%s\" does not exist"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:90
|
#: repo.go:109
|
||||||
msgid "Error removing repo directory"
|
msgid "Error removing repo directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:94 repo.go:161 repo.go:219
|
#: repo.go:113 repo.go:180 repo.go:253 repo.go:316 repo.go:389 repo.go:504
|
||||||
|
#: repo.go:576
|
||||||
msgid "Error saving config"
|
msgid "Error saving config"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:113
|
#: repo.go:132
|
||||||
msgid "Error removing packages from database"
|
msgid "Error removing packages from database"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:124 repo.go:239
|
#: repo.go:143 repo.go:595
|
||||||
msgid "Add a new repository"
|
msgid "Add a new repository"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:125
|
#: repo.go:144 repo.go:270 repo.go:345 repo.go:402
|
||||||
msgid "<name> <url>"
|
msgid "<name> <url>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:150
|
#: repo.go:169
|
||||||
msgid "Repo \"%s\" already exists"
|
msgid "Repo \"%s\" already exists"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:187
|
#: repo.go:206
|
||||||
msgid "Set the reference of the repository"
|
msgid "Set the reference of the repository"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:188
|
#: repo.go:207
|
||||||
msgid "<name> <ref>"
|
msgid "<name> <ref>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:246
|
#: repo.go:269
|
||||||
|
msgid "Set the main url of the repository"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: repo.go:332
|
||||||
|
msgid "Manage mirrors of repos"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: repo.go:344
|
||||||
|
msgid "Add a mirror URL to repository"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: repo.go:401
|
||||||
|
msgid "Remove mirror from the repository"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: repo.go:420
|
||||||
|
msgid "Ignore if mirror does not exist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: repo.go:425
|
||||||
|
msgid "Match partial URL (e.g., github.com instead of full URL)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: repo.go:490
|
||||||
|
msgid "No mirrors containing \"%s\" found in repo \"%s\""
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: repo.go:492
|
||||||
|
msgid "URL \"%s\" does not exist in repo \"%s\""
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: repo.go:508 repo.go:580
|
||||||
|
msgid "Removed %d mirrors from repo \"%s\"\n"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: repo.go:520
|
||||||
|
msgid "Remove all mirrors from the repository"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: repo.go:602
|
||||||
msgid "Name of the new repo"
|
msgid "Name of the new repo"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:252
|
#: repo.go:608
|
||||||
msgid "URL of the new repo"
|
msgid "URL of the new repo"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: repo.go:276
|
#: repo.go:632
|
||||||
msgid "Name of the repo to be deleted"
|
msgid "Name of the repo to be deleted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -5,15 +5,15 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: unnamed project\n"
|
"Project-Id-Version: unnamed project\n"
|
||||||
"PO-Revision-Date: 2025-06-15 16:05+0300\n"
|
"PO-Revision-Date: 2025-06-19 18:54+0300\n"
|
||||||
"Last-Translator: Maxim Slipenko <maks1ms@alt-gnome.ru>\n"
|
"Last-Translator: Maxim Slipenko <maks1ms@alt-gnome.ru>\n"
|
||||||
"Language-Team: Russian\n"
|
"Language-Team: Russian\n"
|
||||||
"Language: ru\n"
|
"Language: ru\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
"X-Generator: Gtranslator 48.0\n"
|
"X-Generator: Gtranslator 48.0\n"
|
||||||
|
|
||||||
#: build.go:42
|
#: build.go:42
|
||||||
@ -356,8 +356,8 @@ msgid ""
|
|||||||
"This command is deprecated and would be removed in the future, use \"%s\" "
|
"This command is deprecated and would be removed in the future, use \"%s\" "
|
||||||
"instead!"
|
"instead!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Эта команда устарела и будет удалена в будущем, используйте вместо нее \"%s"
|
"Эта команда устарела и будет удалена в будущем, используйте вместо нее "
|
||||||
"\"!"
|
"\"%s\"!"
|
||||||
|
|
||||||
#: internal/db/db.go:76
|
#: internal/db/db.go:76
|
||||||
msgid "Database version mismatch; resetting"
|
msgid "Database version mismatch; resetting"
|
||||||
@ -397,19 +397,27 @@ msgstr "%s %s загружается — %s/с\n"
|
|||||||
msgid "ERROR"
|
msgid "ERROR"
|
||||||
msgstr "ОШИБКА"
|
msgstr "ОШИБКА"
|
||||||
|
|
||||||
#: internal/repos/pull.go:77
|
#: internal/repos/pull.go:78
|
||||||
|
msgid "Trying mirror"
|
||||||
|
msgstr "Пробую зеркало"
|
||||||
|
|
||||||
|
#: internal/repos/pull.go:84
|
||||||
|
msgid "Failed to pull from URL"
|
||||||
|
msgstr "Не удалось извлечь из URL"
|
||||||
|
|
||||||
|
#: internal/repos/pull.go:151
|
||||||
msgid "Pulling repository"
|
msgid "Pulling repository"
|
||||||
msgstr "Скачивание репозитория"
|
msgstr "Скачивание репозитория"
|
||||||
|
|
||||||
#: internal/repos/pull.go:113
|
#: internal/repos/pull.go:188
|
||||||
msgid "Repository up to date"
|
msgid "Repository up to date"
|
||||||
msgstr "Репозиторий уже обновлён"
|
msgstr "Репозиторий уже обновлён"
|
||||||
|
|
||||||
#: internal/repos/pull.go:204
|
#: internal/repos/pull.go:223
|
||||||
msgid "Git repository does not appear to be a valid ALR repo"
|
msgid "Git repository does not appear to be a valid ALR repo"
|
||||||
msgstr "Репозиторий Git не поддерживается репозиторием ALR"
|
msgstr "Репозиторий Git не поддерживается репозиторием ALR"
|
||||||
|
|
||||||
#: internal/repos/pull.go:220
|
#: internal/repos/pull.go:239
|
||||||
msgid ""
|
msgid ""
|
||||||
"ALR repo's minimum ALR version is greater than the current version. Try "
|
"ALR repo's minimum ALR version is greater than the current version. Try "
|
||||||
"updating ALR if something doesn't work."
|
"updating ALR if something doesn't work."
|
||||||
@ -477,63 +485,104 @@ msgstr "Ошибка при запуске приложения"
|
|||||||
msgid "Pull all repositories that have changed"
|
msgid "Pull all repositories that have changed"
|
||||||
msgstr "Скачать все изменённые репозитории"
|
msgstr "Скачать все изменённые репозитории"
|
||||||
|
|
||||||
#: repo.go:39
|
#: repo.go:41
|
||||||
msgid "Manage repos"
|
msgid "Manage repos"
|
||||||
msgstr "Управление репозиториями"
|
msgstr "Управление репозиториями"
|
||||||
|
|
||||||
#: repo.go:51 repo.go:269
|
#: repo.go:55 repo.go:625
|
||||||
msgid "Remove an existing repository"
|
msgid "Remove an existing repository"
|
||||||
msgstr "Удалить существующий репозиторий"
|
msgstr "Удалить существующий репозиторий"
|
||||||
|
|
||||||
#: repo.go:53
|
#: repo.go:57 repo.go:521
|
||||||
msgid "<name>"
|
msgid "<name>"
|
||||||
msgstr "<имя>"
|
msgstr "<имя>"
|
||||||
|
|
||||||
#: repo.go:83
|
#: repo.go:102 repo.go:465 repo.go:568
|
||||||
msgid "Repo \"%s\" does not exist"
|
msgid "Repo \"%s\" does not exist"
|
||||||
msgstr "Репозитория \"%s\" не существует"
|
msgstr "Репозитория \"%s\" не существует"
|
||||||
|
|
||||||
#: repo.go:90
|
#: repo.go:109
|
||||||
msgid "Error removing repo directory"
|
msgid "Error removing repo directory"
|
||||||
msgstr "Ошибка при удалении каталога репозитория"
|
msgstr "Ошибка при удалении каталога репозитория"
|
||||||
|
|
||||||
#: repo.go:94 repo.go:161 repo.go:219
|
#: repo.go:113 repo.go:180 repo.go:253 repo.go:316 repo.go:389 repo.go:504
|
||||||
|
#: repo.go:576
|
||||||
msgid "Error saving config"
|
msgid "Error saving config"
|
||||||
msgstr "Ошибка при сохранении конфигурации"
|
msgstr "Ошибка при сохранении конфигурации"
|
||||||
|
|
||||||
#: repo.go:113
|
#: repo.go:132
|
||||||
msgid "Error removing packages from database"
|
msgid "Error removing packages from database"
|
||||||
msgstr "Ошибка при удалении пакетов из базы данных"
|
msgstr "Ошибка при удалении пакетов из базы данных"
|
||||||
|
|
||||||
#: repo.go:124 repo.go:239
|
#: repo.go:143 repo.go:595
|
||||||
msgid "Add a new repository"
|
msgid "Add a new repository"
|
||||||
msgstr "Добавить новый репозиторий"
|
msgstr "Добавить новый репозиторий"
|
||||||
|
|
||||||
#: repo.go:125
|
#: repo.go:144 repo.go:270 repo.go:345 repo.go:402
|
||||||
msgid "<name> <url>"
|
msgid "<name> <url>"
|
||||||
msgstr "<имя> <url>"
|
msgstr "<имя> <url>"
|
||||||
|
|
||||||
#: repo.go:150
|
#: repo.go:169
|
||||||
msgid "Repo \"%s\" already exists"
|
msgid "Repo \"%s\" already exists"
|
||||||
msgstr "Репозиторий \"%s\" уже существует"
|
msgstr "Репозиторий \"%s\" уже существует"
|
||||||
|
|
||||||
#: repo.go:187
|
#: repo.go:206
|
||||||
msgid "Set the reference of the repository"
|
msgid "Set the reference of the repository"
|
||||||
msgstr "Установить ссылку на версию репозитория"
|
msgstr "Установить ссылку на версию репозитория"
|
||||||
|
|
||||||
#: repo.go:188
|
#: repo.go:207
|
||||||
msgid "<name> <ref>"
|
msgid "<name> <ref>"
|
||||||
msgstr "<имя> <ссылка_на_версию>"
|
msgstr "<имя> <ссылка_на_версию>"
|
||||||
|
|
||||||
#: repo.go:246
|
#: repo.go:269
|
||||||
|
msgid "Set the main url of the repository"
|
||||||
|
msgstr "Установить главный URL репозитория"
|
||||||
|
|
||||||
|
#: repo.go:332
|
||||||
|
msgid "Manage mirrors of repos"
|
||||||
|
msgstr "Управление зеркалами репозитория"
|
||||||
|
|
||||||
|
#: repo.go:344
|
||||||
|
msgid "Add a mirror URL to repository"
|
||||||
|
msgstr "Добавить зеркало репозитория"
|
||||||
|
|
||||||
|
#: repo.go:401
|
||||||
|
msgid "Remove mirror from the repository"
|
||||||
|
msgstr "Удалить зеркало из репозитория"
|
||||||
|
|
||||||
|
#: repo.go:420
|
||||||
|
msgid "Ignore if mirror does not exist"
|
||||||
|
msgstr "Игнорировать, если зеркала не существует"
|
||||||
|
|
||||||
|
#: repo.go:425
|
||||||
|
msgid "Match partial URL (e.g., github.com instead of full URL)"
|
||||||
|
msgstr "Соответствует частичному URL (например, github.com вместо полного URL)"
|
||||||
|
|
||||||
|
#: repo.go:490
|
||||||
|
msgid "No mirrors containing \"%s\" found in repo \"%s\""
|
||||||
|
msgstr "В репозитории \"%s\" не найдено зеркал, содержащих \"%s\""
|
||||||
|
|
||||||
|
#: repo.go:492
|
||||||
|
msgid "URL \"%s\" does not exist in repo \"%s\""
|
||||||
|
msgstr "URL \"%s\" не существует в репозитории \"%s\""
|
||||||
|
|
||||||
|
#: repo.go:508 repo.go:580
|
||||||
|
msgid "Removed %d mirrors from repo \"%s\"\n"
|
||||||
|
msgstr "Удалены зеркала %d из репозитория \"%s\"\n"
|
||||||
|
|
||||||
|
#: repo.go:520
|
||||||
|
msgid "Remove all mirrors from the repository"
|
||||||
|
msgstr "Удалить все зеркала из репозитория"
|
||||||
|
|
||||||
|
#: repo.go:602
|
||||||
msgid "Name of the new repo"
|
msgid "Name of the new repo"
|
||||||
msgstr "Название нового репозитория"
|
msgstr "Название нового репозитория"
|
||||||
|
|
||||||
#: repo.go:252
|
#: repo.go:608
|
||||||
msgid "URL of the new repo"
|
msgid "URL of the new repo"
|
||||||
msgstr "URL-адрес нового репозитория"
|
msgstr "URL-адрес нового репозитория"
|
||||||
|
|
||||||
#: repo.go:276
|
#: repo.go:632
|
||||||
msgid "Name of the repo to be deleted"
|
msgid "Name of the repo to be deleted"
|
||||||
msgstr "Название репозитория удалён"
|
msgstr "Название репозитория удалён"
|
||||||
|
|
||||||
|
@ -32,7 +32,8 @@ type Config struct {
|
|||||||
|
|
||||||
// Repo represents a ALR repo within a configuration file
|
// Repo represents a ALR repo within a configuration file
|
||||||
type Repo struct {
|
type Repo struct {
|
||||||
Name string `toml:"name"`
|
Name string `toml:"name"`
|
||||||
URL string `toml:"url"`
|
URL string `toml:"url"`
|
||||||
Ref string `toml:"ref"`
|
Ref string `toml:"ref"`
|
||||||
|
Mirrors []string `toml:"mirrors"`
|
||||||
}
|
}
|
||||||
|
356
repo.go
356
repo.go
@ -20,8 +20,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/leonelquinteros/gotext"
|
"github.com/leonelquinteros/gotext"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
@ -41,6 +43,8 @@ func RepoCmd() *cli.Command {
|
|||||||
RemoveRepoCmd(),
|
RemoveRepoCmd(),
|
||||||
AddRepoCmd(),
|
AddRepoCmd(),
|
||||||
SetRepoRefCmd(),
|
SetRepoRefCmd(),
|
||||||
|
RepoMirrorCmd(),
|
||||||
|
SetUrlCmd(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,6 +55,21 @@ func RemoveRepoCmd() *cli.Command {
|
|||||||
Usage: gotext.Get("Remove an existing repository"),
|
Usage: gotext.Get("Remove an existing repository"),
|
||||||
Aliases: []string{"rm"},
|
Aliases: []string{"rm"},
|
||||||
ArgsUsage: gotext.Get("<name>"),
|
ArgsUsage: gotext.Get("<name>"),
|
||||||
|
BashComplete: func(c *cli.Context) {
|
||||||
|
if c.NArg() == 0 {
|
||||||
|
// Get repo names from config
|
||||||
|
ctx := c.Context
|
||||||
|
deps, err := appbuilder.New(ctx).WithConfig().Build()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer deps.Defer()
|
||||||
|
|
||||||
|
for _, repo := range deps.Cfg.Repos() {
|
||||||
|
fmt.Println(repo.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
Action: utils.RootNeededAction(func(c *cli.Context) error {
|
Action: utils.RootNeededAction(func(c *cli.Context) error {
|
||||||
if c.Args().Len() < 1 {
|
if c.Args().Len() < 1 {
|
||||||
return cliutils.FormatCliExit("missing args", nil)
|
return cliutils.FormatCliExit("missing args", nil)
|
||||||
@ -186,6 +205,21 @@ func SetRepoRefCmd() *cli.Command {
|
|||||||
Name: "set-ref",
|
Name: "set-ref",
|
||||||
Usage: gotext.Get("Set the reference of the repository"),
|
Usage: gotext.Get("Set the reference of the repository"),
|
||||||
ArgsUsage: gotext.Get("<name> <ref>"),
|
ArgsUsage: gotext.Get("<name> <ref>"),
|
||||||
|
BashComplete: func(c *cli.Context) {
|
||||||
|
if c.NArg() == 0 {
|
||||||
|
// Get repo names from config
|
||||||
|
ctx := c.Context
|
||||||
|
deps, err := appbuilder.New(ctx).WithConfig().Build()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer deps.Defer()
|
||||||
|
|
||||||
|
for _, repo := range deps.Cfg.Repos() {
|
||||||
|
fmt.Println(repo.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
Action: utils.RootNeededAction(func(c *cli.Context) error {
|
Action: utils.RootNeededAction(func(c *cli.Context) error {
|
||||||
if c.Args().Len() < 2 {
|
if c.Args().Len() < 2 {
|
||||||
return cliutils.FormatCliExit("missing args", nil)
|
return cliutils.FormatCliExit("missing args", nil)
|
||||||
@ -229,6 +263,328 @@ func SetRepoRefCmd() *cli.Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetUrlCmd() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "set-url",
|
||||||
|
Usage: gotext.Get("Set the main url of the repository"),
|
||||||
|
ArgsUsage: gotext.Get("<name> <url>"),
|
||||||
|
BashComplete: func(c *cli.Context) {
|
||||||
|
if c.NArg() == 0 {
|
||||||
|
// Get repo names from config
|
||||||
|
ctx := c.Context
|
||||||
|
deps, err := appbuilder.New(ctx).WithConfig().Build()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer deps.Defer()
|
||||||
|
|
||||||
|
for _, repo := range deps.Cfg.Repos() {
|
||||||
|
fmt.Println(repo.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action: utils.RootNeededAction(func(c *cli.Context) error {
|
||||||
|
if c.Args().Len() < 2 {
|
||||||
|
return cliutils.FormatCliExit("missing args", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := c.Args().Get(0)
|
||||||
|
repoUrl := c.Args().Get(1)
|
||||||
|
|
||||||
|
deps, err := appbuilder.
|
||||||
|
New(c.Context).
|
||||||
|
WithConfig().
|
||||||
|
WithDB().
|
||||||
|
WithReposNoPull().
|
||||||
|
Build()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer deps.Defer()
|
||||||
|
|
||||||
|
repos := deps.Cfg.Repos()
|
||||||
|
newRepos := []types.Repo{}
|
||||||
|
for _, repo := range repos {
|
||||||
|
if repo.Name == name {
|
||||||
|
repo.URL = repoUrl
|
||||||
|
}
|
||||||
|
newRepos = append(newRepos, repo)
|
||||||
|
}
|
||||||
|
deps.Cfg.SetRepos(newRepos)
|
||||||
|
err = deps.Cfg.SaveUserConfig()
|
||||||
|
if err != nil {
|
||||||
|
return cliutils.FormatCliExit(gotext.Get("Error saving config"), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = deps.Repos.Pull(c.Context, newRepos)
|
||||||
|
if err != nil {
|
||||||
|
return cliutils.FormatCliExit(gotext.Get("Error pulling repositories"), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RepoMirrorCmd() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "mirror",
|
||||||
|
Usage: gotext.Get("Manage mirrors of repos"),
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
AddMirror(),
|
||||||
|
RemoveMirror(),
|
||||||
|
ClearMirrors(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddMirror() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "add",
|
||||||
|
Usage: gotext.Get("Add a mirror URL to repository"),
|
||||||
|
ArgsUsage: gotext.Get("<name> <url>"),
|
||||||
|
BashComplete: func(c *cli.Context) {
|
||||||
|
if c.NArg() == 0 {
|
||||||
|
ctx := c.Context
|
||||||
|
deps, err := appbuilder.New(ctx).WithConfig().Build()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer deps.Defer()
|
||||||
|
|
||||||
|
for _, repo := range deps.Cfg.Repos() {
|
||||||
|
fmt.Println(repo.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action: utils.RootNeededAction(func(c *cli.Context) error {
|
||||||
|
if c.Args().Len() < 2 {
|
||||||
|
return cliutils.FormatCliExit("missing args", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := c.Args().Get(0)
|
||||||
|
url := c.Args().Get(1)
|
||||||
|
|
||||||
|
deps, err := appbuilder.
|
||||||
|
New(c.Context).
|
||||||
|
WithConfig().
|
||||||
|
WithDB().
|
||||||
|
WithReposNoPull().
|
||||||
|
Build()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer deps.Defer()
|
||||||
|
|
||||||
|
repos := deps.Cfg.Repos()
|
||||||
|
for i, repo := range repos {
|
||||||
|
if repo.Name == name {
|
||||||
|
repos[i].Mirrors = append(repos[i].Mirrors, url)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deps.Cfg.SetRepos(repos)
|
||||||
|
err = deps.Cfg.SaveUserConfig()
|
||||||
|
if err != nil {
|
||||||
|
return cliutils.FormatCliExit(gotext.Get("Error saving config"), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveMirror() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "remove",
|
||||||
|
Aliases: []string{"rm"},
|
||||||
|
Usage: gotext.Get("Remove mirror from the repository"),
|
||||||
|
ArgsUsage: gotext.Get("<name> <url>"),
|
||||||
|
BashComplete: func(c *cli.Context) {
|
||||||
|
ctx := c.Context
|
||||||
|
deps, err := appbuilder.New(ctx).WithConfig().Build()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer deps.Defer()
|
||||||
|
|
||||||
|
if c.NArg() == 0 {
|
||||||
|
for _, repo := range deps.Cfg.Repos() {
|
||||||
|
fmt.Println(repo.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "ignore-missing",
|
||||||
|
Usage: gotext.Get("Ignore if mirror does not exist"),
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "partial",
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
Usage: gotext.Get("Match partial URL (e.g., github.com instead of full URL)"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: utils.RootNeededAction(func(c *cli.Context) error {
|
||||||
|
if c.Args().Len() < 2 {
|
||||||
|
return cliutils.FormatCliExit("missing args", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := c.Args().Get(0)
|
||||||
|
urlToRemove := c.Args().Get(1)
|
||||||
|
ignoreMissing := c.Bool("ignore-missing")
|
||||||
|
partialMatch := c.Bool("partial")
|
||||||
|
|
||||||
|
deps, err := appbuilder.
|
||||||
|
New(c.Context).
|
||||||
|
WithConfig().
|
||||||
|
WithDB().
|
||||||
|
WithReposNoPull().
|
||||||
|
Build()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer deps.Defer()
|
||||||
|
|
||||||
|
reposSlice := deps.Cfg.Repos()
|
||||||
|
repoIndex := -1
|
||||||
|
urlIndicesToRemove := []int{}
|
||||||
|
|
||||||
|
// Находим репозиторий
|
||||||
|
for i, repo := range reposSlice {
|
||||||
|
if repo.Name == name {
|
||||||
|
repoIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if repoIndex == -1 {
|
||||||
|
if ignoreMissing {
|
||||||
|
return nil // Тихо завершаем, если репозиторий не найден
|
||||||
|
}
|
||||||
|
return cliutils.FormatCliExit(gotext.Get("Repo \"%s\" does not exist", name), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ищем зеркала для удаления
|
||||||
|
repo := reposSlice[repoIndex]
|
||||||
|
for j, mirror := range repo.Mirrors {
|
||||||
|
var match bool
|
||||||
|
if partialMatch {
|
||||||
|
// Частичное совпадение - проверяем, содержит ли зеркало указанную строку
|
||||||
|
match = strings.Contains(mirror, urlToRemove)
|
||||||
|
} else {
|
||||||
|
// Точное совпадение
|
||||||
|
match = mirror == urlToRemove
|
||||||
|
}
|
||||||
|
|
||||||
|
if match {
|
||||||
|
urlIndicesToRemove = append(urlIndicesToRemove, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(urlIndicesToRemove) == 0 {
|
||||||
|
if ignoreMissing {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if partialMatch {
|
||||||
|
return cliutils.FormatCliExit(gotext.Get("No mirrors containing \"%s\" found in repo \"%s\"", urlToRemove, name), nil)
|
||||||
|
} else {
|
||||||
|
return cliutils.FormatCliExit(gotext.Get("URL \"%s\" does not exist in repo \"%s\"", urlToRemove, name), nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := len(urlIndicesToRemove) - 1; i >= 0; i-- {
|
||||||
|
urlIndex := urlIndicesToRemove[i]
|
||||||
|
reposSlice[repoIndex].Mirrors = slices.Delete(reposSlice[repoIndex].Mirrors, urlIndex, urlIndex+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
deps.Cfg.SetRepos(reposSlice)
|
||||||
|
err = deps.Cfg.SaveUserConfig()
|
||||||
|
if err != nil {
|
||||||
|
return cliutils.FormatCliExit(gotext.Get("Error saving config"), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(urlIndicesToRemove) > 1 {
|
||||||
|
fmt.Println(gotext.Get("Removed %d mirrors from repo \"%s\"\n", len(urlIndicesToRemove), name))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearMirrors() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "clear",
|
||||||
|
Aliases: []string{"rm-all"},
|
||||||
|
Usage: gotext.Get("Remove all mirrors from the repository"),
|
||||||
|
ArgsUsage: gotext.Get("<name>"),
|
||||||
|
BashComplete: func(c *cli.Context) {
|
||||||
|
if c.NArg() == 0 {
|
||||||
|
// Get repo names from config
|
||||||
|
ctx := c.Context
|
||||||
|
deps, err := appbuilder.New(ctx).WithConfig().Build()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer deps.Defer()
|
||||||
|
|
||||||
|
for _, repo := range deps.Cfg.Repos() {
|
||||||
|
fmt.Println(repo.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action: utils.RootNeededAction(func(c *cli.Context) error {
|
||||||
|
if c.Args().Len() < 1 {
|
||||||
|
return cliutils.FormatCliExit("missing args", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := c.Args().Get(0)
|
||||||
|
|
||||||
|
deps, err := appbuilder.
|
||||||
|
New(c.Context).
|
||||||
|
WithConfig().
|
||||||
|
WithDB().
|
||||||
|
WithReposNoPull().
|
||||||
|
Build()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer deps.Defer()
|
||||||
|
|
||||||
|
reposSlice := deps.Cfg.Repos()
|
||||||
|
repoIndex := -1
|
||||||
|
urlIndicesToRemove := []int{}
|
||||||
|
|
||||||
|
// Находим репозиторий
|
||||||
|
for i, repo := range reposSlice {
|
||||||
|
if repo.Name == name {
|
||||||
|
repoIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if repoIndex == -1 {
|
||||||
|
return cliutils.FormatCliExit(gotext.Get("Repo \"%s\" does not exist", name), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
reposSlice[repoIndex].Mirrors = []string{}
|
||||||
|
|
||||||
|
deps.Cfg.SetRepos(reposSlice)
|
||||||
|
err = deps.Cfg.SaveUserConfig()
|
||||||
|
if err != nil {
|
||||||
|
return cliutils.FormatCliExit(gotext.Get("Error saving config"), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(urlIndicesToRemove) > 1 {
|
||||||
|
fmt.Println(gotext.Get("Removed %d mirrors from repo \"%s\"\n", len(urlIndicesToRemove), name))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: remove
|
// TODO: remove
|
||||||
//
|
//
|
||||||
// Deprecated: use "alr repo add"
|
// Deprecated: use "alr repo add"
|
||||||
|
Reference in New Issue
Block a user