1420502005e68b92da7cc83ef4d74aa63ee5216c

Author
Ayman Bagabas <ayman.bagabas@gmail.com>
Committer
GitHub <noreply@github.com>
Date

Message

fix(backend): wrap i/o operations in transactions (#278)

* fix(backend): wrap i/o operations in transactions

when i/o errors, transaction rolls back

* fix(lint): inefficient assignment

Diff

This diff is truncated to protect this page.

  1diff --git a/server/backend/sqlite/hooks.go b/server/backend/sqlite/hooks.go
  2index b3691b187e973f43ca0aeb90ba24b274da7d586a..ff39046f42207e5deda43d3c6845391bd734ce1b 100644
  3--- a/server/backend/sqlite/hooks.go
  4+++ b/server/backend/sqlite/hooks.go
  5@@ -75,9 +75,12 @@ func updateServerInfo(d *SqliteBackend, repo string) error {
  6 
  7 func populateLastModified(d *SqliteBackend, repo string) error {
  8 	var rr *Repo
  9-	if rr, err := d.Repository(repo); err != nil {
 10+	_rr, err := d.Repository(repo)
 11+	if err != nil {
 12 		return err
 13-	} else if r, ok := rr.(*Repo); ok {
 14+	}
 15+
 16+	if r, ok := _rr.(*Repo); ok {
 17 		rr = r
 18 	} else {
 19 		return ErrRepoNotExist
 20diff --git a/server/backend/sqlite/sqlite.go b/server/backend/sqlite/sqlite.go
 21index e4c9111325d8c0c317325a4ffa39510c932290f3..91527e3fcc0a82b8b35e8f1cba9eab55477cd8bb 100644
 22--- a/server/backend/sqlite/sqlite.go
 23+++ b/server/backend/sqlite/sqlite.go
 24@@ -144,28 +144,25 @@ func (d *SqliteBackend) CreateRepository(name string, opts backend.RepositoryOpt
 25 	repo := name + ".git"
 26 	rp := filepath.Join(d.reposPath(), repo)
 27 
 28-	cleanup := func() error {
 29-		return os.RemoveAll(rp)
 30-	}
 31+	if err := wrapTx(d.db, d.ctx, func(tx *sqlx.Tx) error {
 32+		if _, err := tx.Exec(`INSERT INTO repo (name, project_name, description, private, mirror, hidden, updated_at)
 33+			VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP);`,
 34+			name, opts.ProjectName, opts.Description, opts.Private, opts.Mirror, opts.Hidden); err != nil {
 35+			return err
 36+		}
 37 
 38-	rr, err := git.Init(rp, true)
 39-	if err != nil {
 40-		d.logger.Debug("failed to create repository", "err", err)
 41-		cleanup() // nolint: errcheck
 42-		return nil, err
 43-	}
 44+		rr, err := git.Init(rp, true)
 45+		if err != nil {
 46+			d.logger.Debug("failed to create repository", "err", err)
 47+			return err
 48+		}
 49 
 50-	if err := rr.UpdateServerInfo(); err != nil {
 51-		d.logger.Debug("failed to update server info", "err", err)
 52-		cleanup() // nolint: errcheck
 53-		return nil, err
 54-	}
 55+		if err := rr.UpdateServerInfo(); err != nil {
 56+			d.logger.Debug("failed to update server info", "err", err)
 57+			return err
 58+		}
 59 
 60-	if err := wrapTx(d.db, d.ctx, func(tx *sqlx.Tx) error {
 61-		_, err := tx.Exec(`INSERT INTO repo (name, project_name, description, private, mirror, hidden, updated_at)
 62-			VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP);`,
 63-			name, opts.ProjectName, opts.Description, opts.Private, opts.Mirror, opts.Hidden)
 64-		return err
 65+		return nil
 66 	}); err != nil {
 67 		d.logger.Debug("failed to create repository in database", "err", err)
 68 		return nil, wrapDbErr(err)
 69@@ -216,6 +213,7 @@ func (d *SqliteBackend) ImportRepository(name string, remote string, opts backen
 70 
 71 	if err := git.Clone(remote, rp, copts); err != nil {
 72 		d.logger.Error("failed to clone repository", "err", err, "mirror", opts.Mirror, "remote", remote, "path", rp)
 73+		// Cleanup the mess!
 74 		if rerr := os.RemoveAll(rp); rerr != nil {
 75 			err = errors.Join(err, rerr)
 76 		}
 77@@ -237,11 +235,11 @@ func (d *SqliteBackend) DeleteRepository(name string) error {
 78 		// Delete repo from cache
 79 		defer d.cache.Delete(name)
 80 
 81-		if err := os.RemoveAll(rp); err != nil {
 82+		if _, err := tx.Exec("DELETE FROM repo WHERE name = ?;", name); err != nil {
 83 			return err
 84 		}
 85-		_, err := tx.Exec("DELETE FROM repo WHERE name = ?;", name)
 86-		return err
 87+
 88+		return os.RemoveAll(rp)
 89 	})
 90 }
 91 
 92@@ -263,32 +261,37 @@ func (d *SqliteBackend) RenameRepository(oldName string, newName string) error {
 93 	op := filepath.Join(d.reposPath(), oldRepo)
 94 	np := filepath.Join(d.reposPath(), newRepo)
 95 	if _, err := os.Stat(op); err != nil {
 96-		return fmt.Errorf("repository %s does not exist", oldName)
 97+		return ErrRepoNotExist
 98 	}
 99 
100 	if _, err := os.Stat(np); err == nil {
101-		return fmt.Errorf("repository %s already exists", newName)
102+		return ErrRepoExist
103 	}
104 
105 	if err := wrapTx(d.db, d.ctx, func(tx *sqlx.Tx) error {
106+		// Delete cache
107+		defer d.cache.Delete(oldName)
108+
109 		_, err := tx.Exec("UPDATE repo SET name = ?, updated_at = CURRENT_TIMESTAMP WHERE name = ?;", newName, oldName)
110-		return err
111+		if err != nil {
112+			return err
113+		}
114+
115+		// Make sure the new repository parent directory exists.
116+		if err := os.MkdirAll(filepath.Dir(np), os.ModePerm); err != nil {
117+			return err
118+		}
119+
120+		if err := os.Rename(op, np); err != nil {
121+			return err
122+		}
123+