#2675 Include the branches' head in the commit list
Merged 6 years ago by pingou. Opened 6 years ago by pingou.

@@ -10,6 +10,8 @@ 

  

  """

  

+ import collections

+ import logging

  import os

  

  import flask
@@ -28,6 +30,9 @@ 

  import pagure.ui.fork  # noqa: E402

  

  

+ _log = logging.getLogger(__name__)

+ 

+ 

  MERGE_OPTIONS = {

      'NO_CHANGE': {

          'short_code': 'No changes',
@@ -58,6 +63,8 @@ 

          ip_allowed = pagure.APP.config.get(

              'IP_ALLOWED_INTERNAL', ['127.0.0.1', 'localhost', '::1'])

          if flask.request.remote_addr not in ip_allowed:

+             _log.debug('IP: %s is not in the list of allowed IPs: %s' % (

+                 flask.request.remote_addr, ip_allowed))

              flask.abort(403)

          else:

              return function(*args, **kwargs)
@@ -493,3 +500,75 @@ 

              'branches': branches,

          }

      )

+ 

+ 

+ @PV.route('/branches/heads/', methods=['POST'])

+ @localonly

+ def get_branches_head():

+     """ Return the heads of each branch in the repo, using the following

+     structure:

+     {

+         code: 'OK',

+         branches: {

+             name : commit,

+             ...

+         },

+         heads: {

+             commit : [branch, ...],

+             ...

+         }

+     }

+     """

+     form = pagure.forms.ConfirmationForm()

+     if not form.validate_on_submit():

+         response = flask.jsonify({

+             'code': 'ERROR',

+             'message': 'Invalid input submitted',

+         })

+         response.status_code = 400

+         return response

+ 

+     repo = pagure.get_authorized_project(

+         pagure.SESSION,

+         flask.request.form.get('repo', '').strip() or None,

+         namespace=flask.request.form.get('namespace', '').strip() or None,

+         user=flask.request.form.get('repouser', '').strip() or None)

+ 

+     if not repo:

+         response = flask.jsonify({

+             'code': 'ERROR',

+             'message': 'No repo found with the information provided',

+         })

+         response.status_code = 404

+         return response

+ 

+     repopath = os.path.join(pagure.APP.config['GIT_FOLDER'], repo.path)

+ 

+     if not os.path.exists(repopath):

+         response = flask.jsonify({

+             'code': 'ERROR',

+             'message': 'No git repo found with the information provided',

+         })

+         response.status_code = 404

+         return response

+ 

+     repo_obj = pygit2.Repository(repopath)

+ 

+     branches = {}

+     if not repo_obj.is_empty and repo_obj.listall_branches() > 1:

+         for branchname in repo_obj.listall_branches():

+             branch = repo_obj.lookup_branch(branchname)

+             branches[branchname] = branch.get_object().hex

+ 

+     # invert the dict

+     heads = collections.defaultdict(list)

+     for branch, commit in branches.items():

+         heads[commit].append(branch)

+ 

+     return flask.jsonify(

+         {

+             'code': 'OK',

+             'branches': branches,

+             'heads': heads,

+         }

+     )

file modified
+1 -1
@@ -197,7 +197,7 @@ 

          data: {

            repo: "{{ repo.name }}",

            repouser: "{{ repo.user.user if repo.is_fork else '' }}",

-           namespace: "{{ repo.namespace }}",

+           namespace: "{{ repo.namespace if repo.namespace else '' }}",

I can split this in a different commit as well if we want

            commit_id: "{{ commitid }}",

            csrf_token: "{{ form.csrf_token.current_token }}",

          },

file modified
+34 -2
@@ -156,8 +156,10 @@ 

                      namespace=repo.namespace,

                      author=commit.author.email),

                  cssclass="notblue")|safe}}

-             <div class="pull-xs-right">

-               <span class="label label-default">{{ commit.hex|short }}</span>

+             <div class="pull-xs-right" >

+               <span class="label label-default commithash" id="c_{{ commit.hex }}">

+                 {{ commit.hex|short }}

+               </span>

              </div>

            </div>

          {% endfor %}
@@ -176,6 +178,36 @@ 

      $('#diff_commits_link').click(function(){

        $('#diff_commits').toggle();

      });

+     $.ajax({

+         url: '{{ url_for("internal_ns.get_branches_head") }}' ,

+         type: 'POST',

+         data: {

+           repo: "{{ repo.name }}",

+           repouser: "{{ repo.user.user if repo.is_fork else '' }}",

+           namespace: "{{ repo.namespace if repo.namespace else '' }}",

+           csrf_token: "{{ form.csrf_token.current_token }}",

+         },

+         dataType: 'json',

+         success: function(res) {

+           for (var _c in res.heads) {

+             for (var i=0; i < res.heads[_c].length; i++){

+               var _url = '{{ url_for('.view_commits',

+                 repo=repo.name,

+                 branchname='---',

+                 username=username,

+                 namespace=repo.namespace) }}';

+               var _b = res.heads[_c][i];

+               var html = '<a href="' + _url.replace('---', _b) + '">'

+                 +'<span class="label label-info"'

+                 +'title="Head of branch(es): ' + _b + '">'

+                 + '<span class="oi" data-glyph="fork"></span> '

+                 + _b + '</span></a> ';

+               var el = $('#c_' + _c);

+               el.parent().before(html);

+             }

+           }

+         },

+     });

    });

  </script>

  {% endblock %}

@@ -41,6 +41,7 @@ 

              pagure.APP.config['IP_ALLOWED_INTERNAL'] + [None]))

          pagure.SESSION = self.session

          pagure.internal.SESSION = self.session

+         pagure.ui.app.SESSION = self.session

          pagure.ui.repo.SESSION = self.session

          pagure.ui.filters.SESSION = self.session

  
@@ -1181,6 +1182,177 @@ 

              }

          )

  

+     def test_get_branches_head(self):

+         ''' Test the get_branches_head from the internal API. '''

+         tests.create_projects(self.session)

+         tests.create_projects_git(os.path.join(self.path, 'repos'))

+ 

+         user = tests.FakeUser()

+         user.username = 'pingou'

+         with tests.user_set(pagure.APP, user):

+             csrf_token = self.get_csrf()

+ 

+         # No CSRF token

+         data = {

+             'repo': 'fakerepo',

+         }

+         output = self.app.post('/pv/branches/heads/', data=data)

+         self.assertEqual(output.status_code, 400)

+         js_data = json.loads(output.data.decode('utf-8'))

+         self.assertDictEqual(

+             js_data,

+             {u'code': u'ERROR', u'message': u'Invalid input submitted'}

+         )

+ 

+         # Invalid repo

+         data = {

+             'repo': 'fakerepo',

+             'commit_id': 'foo',

+             'csrf_token': csrf_token,

+         }

+         output = self.app.post('/pv/branches/heads/', data=data)

+         self.assertEqual(output.status_code, 404)

+         js_data = json.loads(output.data.decode('utf-8'))

+         self.assertDictEqual(

+             js_data,

+             {

+                 u'code': u'ERROR',

+                 u'message': u'No repo found with the information provided'

+             }

+         )

+ 

+         # Rigth repo, no commit

+         data = {

+             'repo': 'test',

+             'csrf_token': csrf_token,

+         }

+ 

+         output = self.app.post('/pv/branches/heads/', data=data)

+         self.assertEqual(output.status_code, 200)

+         js_data = json.loads(output.data.decode('utf-8'))

+         self.assertDictEqual(

+             js_data,

+             {u"branches": {}, u"code": u"OK", u"heads": {}}

+         )

+ 

+         # Request is fine, but git repo doesn't exist

+         item = pagure.lib.model.Project(

+             user_id=1,  # pingou

+             name='test20',

+             description='test project #20',

+             hook_token='aaabbbhhh',

+         )

+         self.session.add(item)

+         self.session.commit()

+ 

+         data = {

+             'repo': 'test20',

+             'csrf_token': csrf_token,

+         }

+         output = self.app.post('/pv/branches/heads/', data=data)

+         self.assertEqual(output.status_code, 404)

+         js_data = json.loads(output.data.decode('utf-8'))

+         self.assertDictEqual(

+             js_data,

+             {

+                 u'code': u'ERROR',

+                 u'message': u'No git repo found with the information provided'

+             }

+         )

+ 

+         # Create a git repo to play with

+         gitrepo = os.path.join(self.path, 'repos', 'test.git')

+         self.assertTrue(os.path.exists(gitrepo))

+         repo = pygit2.Repository(gitrepo)

+ 

+         # Create a file in that git repo

+         with open(os.path.join(gitrepo, 'sources'), 'w') as stream:

+             stream.write('foo\n bar')

+         repo.index.add('sources')

+         repo.index.write()

+ 

+         # Commits the files added

+         tree = repo.index.write_tree()

+         author = pygit2.Signature(

+             'Alice Author', 'alice@authors.tld')

+         committer = pygit2.Signature(

+             'Cecil Committer', 'cecil@committers.tld')

+         repo.create_commit(

+             'refs/heads/master',  # the name of the reference to update

+             author,

+             committer,

+             'Add sources file for testing',

+             # binary string representing the tree object ID

+             tree,

+             # list of binary strings representing parents of the new commit

+             []

+         )

+ 

+         first_commit = repo.revparse_single('HEAD')

+ 

+         # Edit the sources file again

+         with open(os.path.join(gitrepo, 'sources'), 'w') as stream:

+             stream.write('foo\n bar\nbaz\n boose')

+         repo.index.add('sources')

+         repo.index.write()

+ 

+         # Commits the files added

+         tree = repo.index.write_tree()

+         author = pygit2.Signature(

+             'Alice Author', 'alice@authors.tld')

+         committer = pygit2.Signature(

+             'Cecil Committer', 'cecil@committers.tld')

+         repo.create_commit(

+             'refs/heads/feature',  # the name of the reference to update

+             author,

+             committer,

+             'Add baz and boose to the sources\n\n There are more objects to '

+             'consider',

+             # binary string representing the tree object ID

+             tree,

+             # list of binary strings representing parents of the new commit

+             [first_commit.oid.hex]

+         )

+ 

+         # Create another file in the master branch

+         with open(os.path.join(gitrepo, '.gitignore'), 'w') as stream:

+             stream.write('*~')

+         repo.index.add('.gitignore')

+         repo.index.write()

+ 

+         # Commits the files added

+         tree = repo.index.write_tree()

+         author = pygit2.Signature(

+             'Alice Author', 'alice@authors.tld')

+         committer = pygit2.Signature(

+             'Cecil Committer', 'cecil@committers.tld')

+         commit_hash = repo.create_commit(

+             'refs/heads/feature_branch',  # the name of the reference to update

+             author,

+             committer,

+             'Add .gitignore file for testing',

+             # binary string representing the tree object ID

+             tree,

+             # list of binary strings representing parents of the new commit

+             [first_commit.oid.hex]

+         )

+ 

+         # All good

+         data = {

+             'repo': 'test',

+             'csrf_token': csrf_token,

+         }

+         output = self.app.post('/pv/branches/heads/', data=data)

+         self.assertEqual(output.status_code, 200)

+         js_data = json.loads(output.data.decode('utf-8'))

+         # We can't test the content since the commit hash will change all

+         # the time, so let's just check the structure

+         self.assertEqual(

+             sorted(js_data.keys()), ['branches', 'code', 'heads'])

+         self.assertEqual(js_data['code'], 'OK')

+         self.assertEqual(len(js_data['heads']), 3)

+         self.assertEqual(len(js_data['branches']), 3)

+ 

  

  if __name__ == '__main__':

      unittest.main(verbosity=2)

This gives quickly a visual overview of where branches are at

Fixes https://pagure.io/pagure/issue/2490

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

rebased onto 17e9e23

6 years ago

Not sure if first commit is related to htis PR ;)

CSRF protection for the internal endpoints :)

Not directly but it's a change that I wanted to make to the internal endpoints so I did it :)

It has its own commit at least :)

I can split this in a different commit as well if we want

1 new commit added

  • Drop an entire block of unused code
6 years ago

1 new commit added

  • Make the branches different tags and link them
6 years ago

4 new commits added

  • Make the branches different tags and link them
  • Drop an entire block of unused code
  • Log which IP address was denied accessing the internal endpoint
  • Include the branches' head in the commit list
6 years ago

4 new commits added

  • Make the branches different tags and link them
  • Drop an entire block of unused code
  • Log which IP address was denied accessing the internal endpoint
  • Include the branches' head in the commit list
6 years ago

Pull-Request has been merged by pingou

6 years ago