| @@ -11,6 +11,7 @@ import ( |
| 11 | 11 | "strconv" |
| 12 | 12 | "strings" |
| 13 | 13 | |
| 14 | "gitbay.org/gitbay/internal/backuplock" |
| 14 | 15 | "gitbay.org/gitbay/internal/gitutil" |
| 15 | 16 | "gitbay.org/gitbay/internal/policy" |
| 16 | 17 | "gitbay.org/gitbay/internal/protocol" |
| @@ -513,6 +514,11 @@ func runRepoTransfer(c *Ctx, args []string) int { |
| 513 | 514 | if _, err := os.Stat(newDir); err == nil { |
| 514 | 515 | return c.fail(protocol.ExitFailure, "repository directory already exists at %s/%s", newOwner, repo.Name) |
| 515 | 516 | } |
| 517 | release, lockCode := holdOffBackup(c) |
| 518 | if lockCode >= 0 { |
| 519 | return lockCode |
| 520 | } |
| 521 | defer release() |
| 516 | 522 | // The directory moves before the record changes: a move that fails |
| 517 | 523 | // leaves nothing to undo, whereas the record's change into an org |
| 518 | 524 | // folds labels and milestones into the org's rows, which a revert |
| @@ -557,6 +563,11 @@ func runRepoRename(c *Ctx, args []string) int { |
| 557 | 563 | if _, err := os.Stat(newDir); err == nil { |
| 558 | 564 | return c.fail(protocol.ExitFailure, "repository directory already exists at %s/%s", repo.OwnerName, newName) |
| 559 | 565 | } |
| 566 | release, lockCode := holdOffBackup(c) |
| 567 | if lockCode >= 0 { |
| 568 | return lockCode |
| 569 | } |
| 570 | defer release() |
| 560 | 571 | if err := c.Store.RenameRepo(repo.ID, newName); err != nil { |
| 561 | 572 | return c.failErr(err) |
| 562 | 573 | } |
| @@ -609,6 +620,11 @@ func runRepoDelete(c *Ctx, args []string) int { |
| 609 | 620 | // webhooks; an instance that needs to hear about it wants the audit log |
| 610 | 621 | // (#112). |
| 611 | 622 | func deleteRepo(c *Ctx, repo store.Repo) int { |
| 623 | release, lockCode := holdOffBackup(c) |
| 624 | if lockCode >= 0 { |
| 625 | return lockCode |
| 626 | } |
| 627 | defer release() |
| 612 | 628 | // Open MRs sourced from this repo keep working (targets own the |
| 613 | 629 | // objects) but must show that the source is gone. |
| 614 | 630 | if err := c.Store.MarkSourceGoneForRepo(repo.ID); err != nil { |
| @@ -625,6 +641,18 @@ func deleteRepo(c *Ctx, repo store.Repo) int { |
| 625 | 641 | }) |
| 626 | 642 | } |
| 627 | 643 | |
| 644 | // holdOffBackup keeps a full backup from starting while a repository |
| 645 | // directory moves or goes, and refuses while one runs: the backup's |
| 646 | // database snapshot names every repository its walk then archives |
| 647 | // (#259). The caller defers the returned release. |
| 648 | func holdOffBackup(c *Ctx) (func(), int) { |
| 649 | release, err := backuplock.TryShared(c.Cfg.Server.Root) |
| 650 | if err != nil { |
| 651 | return nil, c.fail(protocol.ExitFailure, "%v", err) |
| 652 | } |
| 653 | return release, -1 |
| 654 | } |
| 655 | |
| 628 | 656 | func runAccessGrant(c *Ctx, args []string) int { |
| 629 | 657 | if len(args) != 3 || !slices.Contains([]string{"read", "write", "admin"}, args[2]) { |
| 630 | 658 | return c.usage() |