#4935 Add API endpoint to set the default git branch and expose it in an existing endpoint
Merged 2 years ago by pingou. Opened 2 years ago by pingou.

file modified
+104 -5
@@ -660,13 +660,17 @@ 

  

          {

            "total_branches": 2,

-           "branches": ["master", "dev"]

+           "branches": ["main", "dev"]

+           "default": "main"

          }

  

          {

            "total_branches": 2,

+           "default": {

+             "main": "16ae2a4df107658b52750063ae203f978cf02ff7",

+           }

            "branches": {

-             "master": "16ae2a4df107658b52750063ae203f978cf02ff7",

+             "main": "16ae2a4df107658b52750063ae203f978cf02ff7",

              "dev": "8351c460167a41defc393f5b6c1d51fe1b3b82b8"

            }

          }
@@ -680,10 +684,105 @@ 

      repo = _get_repo(repo, username, namespace)

  

      branches = pagure.lib.git.get_git_branches(repo, with_commits=with_commits)

+     default_name = default_commit = None

+     try:

+         default_name, default_commit = pagure.lib.git.get_default_git_branches(

+             repo

+         )

+     except pygit2.GitError:

+         pass

  

-     return flask.jsonify(

-         {"total_branches": len(branches), "branches": branches}

-     )

+     output = {

+         "total_branches": len(branches),

+         "branches": branches,

+         "default": {},

+     }

+     if with_commits:

+         if default_name:

+             output["default"] = {default_name: default_commit}

+     else:

+         output["default"] = default_name

+ 

+     return flask.jsonify(output)

+ 

+ 

+ @API.route("/<repo>/git/branches", methods=["POST"])

+ @API.route("/<namespace>/<repo>/git/branches", methods=["POST"])

+ @API.route("/fork/<username>/<repo>/git/branches", methods=["POST"])

+ @API.route(

+     "/fork/<username>/<namespace>/<repo>/git/branches", methods=["POST"]

+ )

+ @api_login_required(acls=["modify_project"])

+ @api_method

+ def api_set_git_default_branch(repo, username=None, namespace=None):

+     """

+     Set the default git branch

+     --------------------------

+     Set the default git branch of the git repository

+ 

+     ::

+ 

+         POST /api/0/<repo>/git/branches

+         POST /api/0/<namespace>/<repo>/git/branches

+ 

+     ::

+ 

+         POST /api/0/fork/<username>/<repo>/git/branches

+         POST /api/0/fork/<username>/<namespace>/<repo>/git/branches

+ 

+     Parameters

+     ^^^^^^^^^^

+ 

+     +-----------------+----------+---------------+--------------------------+

+     | Key             | Type     | Optionality   | Description              |

+     +=================+==========+===============+==========================+

+     | ``branch_name`` | string   | Mandatory     | | Name of the git branch |

+     |                 |          |               |   to be made the default |

+     |                 |          |               |   branch of the git repo |

+     +-----------------+----------+---------------+--------------------------+

+     | ``with_commits``| boolean  | Optional      | | Include the commit hash|

+     |                 |          |               |   corresponding to the   |

+     |                 |          |               |   HEAD of each branch    |

+     +-----------------+----------+---------------+--------------------------+

+ 

+     Sample response

+     ^^^^^^^^^^^^^^^

+ 

+     ::

+ 

+         {

+           "total_branches": 2,

+           "branches": ["main", "dev"]

+           "default": "main"

+         }

+ 

+         {

+           "total_branches": 2,

+           "default": {

+             "main": "16ae2a4df107658b52750063ae203f978cf02ff7",

+           }

+           "branches": {

+             "main": "16ae2a4df107658b52750063ae203f978cf02ff7",

+             "dev": "8351c460167a41defc393f5b6c1d51fe1b3b82b8"

+           }

+         }

+ 

+     """

+ 

+     branch_name = flask.request.values.get("branch_name")

+ 

+     repo = _get_repo(repo, username, namespace)

+     _check_token(repo, project_token=False)

+ 

+     try:

+         pagure.lib.git.git_set_ref_head(project=repo, branch=branch_name)

+     except Exception as err:

+         _log.exception(err)

+         raise pagure.exceptions.APIError(

+             400, error_code=APIERROR.EGITERROR, error=str(err)

+         )

+ 

+     return api_git_branches(repo.name, username=username, namespace=namespace)

  

  

  @API.route("/<repo>/tree")

file modified
+13
@@ -2608,6 +2608,19 @@ 

      return branches

  

  

+ def get_default_git_branches(project):

+     """ Return a tuple of the default branchname and its head commit hash

+     :arg project: The Project instance to get the branches for

+     """

+     repo_path = pagure.utils.get_repo_path(project)

+     repo_obj = PagureRepo(repo_path)

+     branchname = repo_obj.head.shorthand

+     branch = repo_obj.lookup_branch(branchname)

+     commit = branch.peel(pygit2.Commit)

+ 

+     return branchname, commit.oid.hex

+ 

+ 

  def new_git_branch(

      username, project, branch, from_branch=None, from_commit=None

  ):

@@ -117,93 +117,6 @@ 

  

          shutil.rmtree(newpath)

  

-     def test_api_git_branches(self):

-         """ Test the api_git_branches method of the flask api. """

-         # Create a git repo to add branches to

-         tests.create_projects(self.session)

-         repo_path = os.path.join(self.path, "repos", "test.git")

-         tests.add_content_git_repo(repo_path)

-         new_repo_path = tempfile.mkdtemp(prefix="pagure-api-git-branches-test")

-         clone_repo = pygit2.clone_repository(repo_path, new_repo_path)

- 

-         # Create two other branches based on master

-         for branch in ["pats-win-49", "pats-win-51"]:

-             clone_repo.create_branch(branch, clone_repo.head.peel())

-             refname = "refs/heads/{0}:refs/heads/{0}".format(branch)

-             PagureRepo.push(clone_repo.remotes[0], refname)

- 

-         # Check that the branches show up on the API

-         output = self.app.get("/api/0/test/git/branches")

-         # Delete the cloned git repo after the API call

-         shutil.rmtree(new_repo_path)

- 

-         # Verify the API data

-         self.assertEqual(output.status_code, 200)

-         data = json.loads(output.get_data(as_text=True))

-         self.assertDictEqual(

-             data,

-             {

-                 "branches": ["master", "pats-win-49", "pats-win-51"],

-                 "total_branches": 3,

-             },

-         )

- 

-     def test_api_git_branches_with_commits(self):

-         """ Test the api_git_branches method of the flask api with with_commits=True. """

-         # Create a git repo to add branches to

-         tests.create_projects(self.session)

-         repo_path = os.path.join(self.path, "repos", "test.git")

-         tests.add_content_git_repo(repo_path)

-         new_repo_path = tempfile.mkdtemp(prefix="pagure-api-git-branches-test")

-         clone_repo = pygit2.clone_repository(repo_path, new_repo_path)

- 

-         # Create two other branches based on master

-         for branch in ["pats-win-49", "pats-win-51"]:

-             clone_repo.create_branch(branch, clone_repo.head.peel())

-             refname = "refs/heads/{0}:refs/heads/{0}".format(branch)

-             PagureRepo.push(clone_repo.remotes[0], refname)

- 

-         # Check that the branches show up on the API

-         output = self.app.get("/api/0/test/git/branches?with_commits=true")

-         # Delete the cloned git repo after the API call

-         shutil.rmtree(new_repo_path)

- 

-         # Get the commit hex

-         repo_obj = pygit2.Repository(

-             os.path.join(self.path, "repos", "test.git")

-         )

-         commit = repo_obj[repo_obj.head.target]

- 

-         # Verify the API data

-         self.assertEqual(output.status_code, 200)

-         data = json.loads(output.get_data(as_text=True))

-         self.assertDictEqual(

-             data,

-             {

-                 "branches": {

-                     "master": commit.hex,

-                     "pats-win-49": commit.hex,

-                     "pats-win-51": commit.hex,

-                 },

-                 "total_branches": 3,

-             },

-         )

- 

-     def test_api_git_branches_empty_repo(self):

-         """ Test the api_git_branches method of the flask api when the repo is

-         empty.

-         """

-         # Create a git repo without any branches

-         tests.create_projects(self.session)

-         repo_base_path = os.path.join(self.path, "repos")

-         tests.create_projects_git(repo_base_path)

- 

-         # Check that no branches show up on the API

-         output = self.app.get("/api/0/test/git/branches")

-         self.assertEqual(output.status_code, 200)

-         data = json.loads(output.get_data(as_text=True))

-         self.assertDictEqual(data, {"branches": [], "total_branches": 0})

- 

      def test_api_git_branches_no_repo(self):

          """ Test the api_git_branches method of the flask api when there is no

          repo on a project.
@@ -4573,5 +4486,210 @@ 

          self.assertEqual(pagure.api.APIERROR.ENOCOMMIT.value, data["error"])

  

  

+ class PagureFlaskApiProjectGitBranchestests(tests.Modeltests):

+     """ Tests for the flask API of pagure for git branches

+     """

+ 

+     maxDiff = None

+ 

+     def setUp(self):

+         """ Set up the environnment, ran before every tests. """

+         super(PagureFlaskApiProjectGitBranchestests, self).setUp()

+ 

+         tests.create_projects(self.session)

+         repo_path = os.path.join(self.path, "repos")

+         self.git_path = os.path.join(repo_path, "test.git")

+         tests.create_projects_git(repo_path, bare=True)

+         tests.add_content_git_repo(self.git_path)

+ 

+         tests.create_tokens(self.session, project_id=None)

+         # Set a default ACL to avoid get all rights set on

+         tests.create_tokens_acl(self.session, "foo_token", "modify_project")

+         tests.create_tokens_acl(self.session, "aaabbbcccddd", "create_branch")

+ 

+         # Add a couple of branches to the test project

+         repo_obj = pygit2.Repository(self.git_path)

+         self.commit = repo_obj.revparse_single("HEAD")

+ 

+         new_repo_path = os.path.join(self.path, "lcl_forks")

+         clone_repo = pygit2.clone_repository(self.git_path, new_repo_path)

+ 

+         # Create two other branches based on master

+         for branch in ["pats-win-49", "pats-win-51"]:

+             clone_repo.create_branch(branch, clone_repo.head.peel())

+             refname = "refs/heads/{0}:refs/heads/{0}".format(branch)

+             PagureRepo.push(clone_repo.remotes[0], refname)

+ 

+     def test_api_git_branches(self):

+         """ Test the api_git_branches method of the flask api. """

+         # Check that the branches show up on the API

+         output = self.app.get("/api/0/test/git/branches")

+ 

+         self.assertEqual(output.status_code, 200)

+         data = json.loads(output.get_data(as_text=True))

+         self.assertDictEqual(

+             data,

+             {

+                 "branches": ["master", "pats-win-49", "pats-win-51"],

+                 "default": "master",

+                 "total_branches": 3,

+             },

+         )

+ 

+     def test_api_git_branches_with_commits(self):

+         """ Test the api_git_branches method of the flask api with with_commits=True. """

+         # Check that the branches show up on the API

+         output = self.app.get("/api/0/test/git/branches?with_commits=true")

+ 

+         self.assertEqual(output.status_code, 200)

+         data = json.loads(output.get_data(as_text=True))

+         self.assertDictEqual(

+             data,

+             {

+                 "branches": {

+                     "master": self.commit.hex,

+                     "pats-win-49": self.commit.hex,

+                     "pats-win-51": self.commit.hex,

+                 },

+                 "default": {"master": self.commit.hex,},

+                 "total_branches": 3,

+             },

+         )

+ 

+     def test_api_git_branches_empty_repo(self):

+         """ Test the api_git_branches method of the flask api when the repo is

+         empty.

+         """

+         # Check that no branches show up on the API

+         output = self.app.get("/api/0/test2/git/branches")

+         self.assertEqual(output.status_code, 200)

+         data = json.loads(output.get_data(as_text=True))

+         self.assertDictEqual(

+             data, {"branches": [], "default": None, "total_branches": 0}

+         )

+ 

+     def test_api_set_git_default_branch(self):

+         """ Test the api_git_branches method of the flask api. """

+         headers = {"Authorization": "token foo_token"}

+         data = {"branch_name": "pats-win-49"}

+         output = self.app.post(

+             "/api/0/test/git/branches", data=data, headers=headers

+         )

+         self.assertEqual(output.status_code, 200)

+         data = json.loads(output.get_data(as_text=True))

+         self.assertDictEqual(

+             data,

+             {

+                 "branches": ["master", "pats-win-49", "pats-win-51"],

+                 "default": "pats-win-49",

+                 "total_branches": 3,

+             },

+         )

+ 

+     def test_api_set_git_default_branch_with_commits_form(self):

+         """ Test the api_git_branches method of the flask api with with_commits=True. """

+         headers = {"Authorization": "token foo_token"}

+         data = {"branch_name": "pats-win-49", "with_commits": True}

+         output = self.app.post(

+             "/api/0/test/git/branches", data=data, headers=headers

+         )

+         self.assertEqual(output.status_code, 200)

+         data = json.loads(output.get_data(as_text=True))

+         self.assertDictEqual(

+             data,

+             {

+                 "branches": {

+                     "master": self.commit.hex,

+                     "pats-win-49": self.commit.hex,

+                     "pats-win-51": self.commit.hex,

+                 },

+                 "default": {"pats-win-49": self.commit.hex,},

+                 "total_branches": 3,

+             },

+         )

+ 

+     def test_api_set_git_default_branch_with_commits_url(self):

+         """ Test the api_git_branches method of the flask api with with_commits=True. """

+         headers = {"Authorization": "token foo_token"}

+         data = {"branch_name": "pats-win-49"}

+         output = self.app.post(

+             "/api/0/test/git/branches?with_commits=1",

+             data=data,

+             headers=headers,

+         )

+         self.assertEqual(output.status_code, 200)

+         data = json.loads(output.get_data(as_text=True))

+         self.assertDictEqual(

+             data,

+             {

+                 "branches": {

+                     "master": self.commit.hex,

+                     "pats-win-49": self.commit.hex,

+                     "pats-win-51": self.commit.hex,

+                 },

+                 "default": {"pats-win-49": self.commit.hex,},

+                 "total_branches": 3,

+             },

+         )

+ 

+     def test_api_set_git_default_branch_invalid_branch(self):

+         """ Test the api_git_branches method of the flask api with with_commits=True. """

+         headers = {"Authorization": "token foo_token"}

+         data = {"branch_name": "main"}

+         output = self.app.post(

+             "/api/0/test/git/branches?with_commits=1",

+             data=data,

+             headers=headers,

+         )

+         self.assertEqual(output.status_code, 400)

+         data = json.loads(output.get_data(as_text=True))

+         self.assertDictEqual(

+             data,

+             {

+                 "error": "An error occurred during a git operation",

+                 "error_code": "EGITERROR",

+             },

+         )

+ 

+     def test_api_set_git_default_branch_invalid_token(self):

+         """ Test the api_git_branches method of the flask api with with_commits=True. """

+         headers = {"Authorization": "token aaabbbcccddd"}

+         data = {"branch_name": "main"}

+         output = self.app.post(

+             "/api/0/test/git/branches", data=data, headers=headers,

+         )

+         self.assertEqual(output.status_code, 401)

+         data = json.loads(output.get_data(as_text=True))

+         self.assertDictEqual(

+             data,

+             {

+                 "error": "Invalid or expired token. Please visit "

+                 "http://localhost.localdomain/settings#nav-api-tab to get or renew "

+                 "your API token.",

+                 "error_code": "EINVALIDTOK",

+                 "errors": "Missing ACLs: modify_project",

+             },

+         )

+ 

+     def test_api_set_git_default_branch_empty_repo(self):

+         """ Test the api_git_branches method of the flask api when the repo is

+         empty.

+         """

+         headers = {"Authorization": "token foo_token"}

+         data = {"branch_name": "main"}

+         output = self.app.post(

+             "/api/0/test2/git/branches", data=data, headers=headers

+         )

+         self.assertEqual(output.status_code, 400)

+         data = json.loads(output.get_data(as_text=True))

+         self.assertDictEqual(

+             data,

+             {

+                 "error": "An error occurred during a git operation",

+                 "error_code": "EGITERROR",

+             },

+         )

+ 

+ 

  if __name__ == "__main__":

      unittest.main(verbosity=2)

This commit adds a "default" field to api/0/<namespace>/<name>/git/branches
which returns the name of the default branch and potentially its HEAD
commit hash.

It also adds a new API endpoint allowing to change this default branch
to any other existing branch.

Signed-off-by: Pierre-Yves Chibon pingou@pingoured.fr

rebased onto 5a53da04fdefb388ebc73c1c408c6737c884c58b

2 years ago

rebased onto e7286c6

2 years ago

Thanks for the review! :)

Pull-Request has been merged by pingou

2 years ago