#6 Add a pull-request command.
Merged 8 years ago by ralph. Opened 8 years ago by ralph.

file modified
+3 -1
@@ -1,7 +1,9 @@ 

  pag

  ===

  

- Command line tool for interacting with https://pagure.io

+ ``pag`` helps you win at `pagure.io <https://pagure.io>`_.

+ 

+ Intended to mimic the `hub <https://github.com/github/hub>`_ cli tool for `github.com <https://github.com>`_.

  

  Usage

  -----

file modified
+1
@@ -14,6 +14,7 @@ 

  from .commands import clone

  from .commands import fork

  from .commands import remote

+ from .commands import pullrequest

  

  

  if __name__ == '__main__':

file modified
+29 -1
@@ -2,6 +2,7 @@ 

  

  import fedora.client

  

+ from pag.utils import repo_url

  

  class PagureException(Exception):

      pass
@@ -74,8 +75,35 @@ 

          if not bool(response):

              del data['csrf_token']

              raise PagureException('Bad status code from pagure when '

-                                   'creating project: %r.  Sent %r' % (

+                                   'forking project: %r.  Sent %r' % (

                                        response, data))

          return repo_url(name)

  

+     def submit_pull_request(self, name, base, head, title, comment):

+         url = 'https://pagure.io/{name}/diff/{base}..{head}'

+         url = url.format(name=name, base=base, head=head)

+ 

+         response = self._session.get(url)

+         if not bool(response):

+             raise PagureException("Couldn't get form to get "

+                                   "csrf token %r" % response)

+ 

+         soup = bs4.BeautifulSoup(response.text, "html.parser")

+         data = dict(

+             csrf_token=soup.find(id='csrf_token').attrs['value'],

+             branch_to=base,

+             title=title,

+             initial_comment=comment,

+         )

+         response = self._session.post(url, data=data)

+ 

+         if not bool(response):

+             del data['csrf_token']

+             raise PagureException('Bad status code from pagure when '

+                                   'creating pull request: %r.  Sent %r' % (

+                                       response, data))

+ 

+         return response.url

+ 

+ 

  client = Pagure()

file modified
+1 -1
@@ -10,4 +10,4 @@ 

  @click.argument('name')

  def clone(name):

      url = repo_url(name, ssh=True, git=True)

-     run('git clone %s %s' % (url, name.split('/')[-1]))

+     run(['git', 'clone', url, name.split('/')[-1]])

the name.split like this may actually break with one feature of pagure under review: pseudo-namespace where we allow up to 1 '/' in project names.

But that's eventually something for later when it'll be in used.

file modified
+3 -3
@@ -29,9 +29,9 @@ 

      local_repo = in_git_repo()

      if local_repo is None or local_repo != name:

          url = repo_url(name, ssh=True, git=True)

-         run('git clone %s %s' % (url, name.split('/')[-1]))

+         run(['git', 'clone', url, name.split('/')[-1]])

      else:

          url = repo_url(name, ssh=True, git=True)

          name = name.split('/')[0]

-         run('git remote add %s %s' % (name, url))

-         run('git remote add %s %s' % ('origin', url))

+         run(['git', 'remote', 'add', name, url])

+         run(['git', 'remote', 'add', 'origin', url])

file modified
+1 -1
@@ -30,4 +30,4 @@ 

      name = username + '/' + name

      url = repo_url(name, ssh=True, git=True)

      name = name.split('/')[0]

-     run('git remote add %s %s' % (name, url))

+     run(['git', 'remote', 'add', name, url])

@@ -0,0 +1,68 @@ 

+ import getpass

+ import sys

+ 

+ import click

+ 

+ from pag.app import app

+ from pag.utils import (

+     configured,

+     assert_local_repo,

+     in_git_repo,

+     get_default_upstream_branch,

+     get_current_local_branch,

+     run,

+ )

+ from pag.client import client

+ 

+ HEADER = "Pull request title goes here."

+ MARKER = "# All lines below this marker are ignored."

+ 

+ 

+ @app.command('pull-request')

+ @assert_local_repo

+ @click.option('-b', '--base')

+ @click.option('-h', '--head')

+ @configured

+ def pullrequest(conf, base, head):

+ 

+     name = in_git_repo()

+ 

+     if base is None:

+         try:

+             base = get_default_upstream_branch(name)

+         except Exception:

+             click.echo("Failed to find default upstream branch for %r" % name)

+             click.echo("Please specify a base branch explicitly.")

+             sys.exit(1)

+ 

+     if head is None:

+         head = get_current_local_branch()

+ 

+     cmd = ['git', 'log', '{base}..{head}'.format(base=base, head=head)]

+     _, log = run(cmd, echo=False)

+ 

+     def modify(line):

+         if not line:

+             return line

+         if line[0].isspace():

+             return line.strip()

+         return '# ' + line

+ 

+     log = '\n'.join([modify(line) for line in log.split('\n')])

+     edited = click.edit(

+         "\n\n".join([HEADER, MARKER, log]),

+         env=dict(VIMINIT='set filetype="gitcommit"'),

+     )

+     title, comment = edited.split('\n', 1)

+     if not title:

+         click.echo("Aborting due to empty pull request message.")

+         sys.exit(1)

+     comment = comment.split(MARKER)[0]

+     comment = comment.strip()

+ 

+     username = conf['username']

+     if not client.is_logged_in:

+         password = getpass.getpass("FAS password for %r" % username)

+         client.login(username=username, password=password)

+     url = client.submit_pull_request(name, base, head, title, comment)

+     click.echo(url)

file modified
+1 -1
@@ -28,4 +28,4 @@ 

      repo = in_git_repo()

      url = repo_url(name + '/' + repo, ssh=True, git=True)

      name = name.split('/')[0]

-     return run('git remote add %s %s' % (name, url))

+     return run(['git', 'remote', 'add', name, url])

file modified
+34 -3
@@ -1,16 +1,26 @@ 

  import functools

  import os

+ import subprocess as sp

  import sys

  

  import click

+ import requests

  import yaml

  

  

  CONF_FILE = os.path.expanduser('~/.config/pag')

  

- def run(cmd):

-     click.echo('  $ ' + cmd)

-     return os.system(cmd)

+ 

+ def run(cmd, echo=True, graceful=True):

+     click.echo('  $ ' + " ".join(cmd))

+     proc = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.STDOUT)

+     output, _ = proc.communicate()

+     output = output.decode('utf-8')

+     if echo:

+         click.echo(output)

+     if not graceful and proc.returncode != 0:

+         sys.exit(1)

+     return proc.returncode, output

  

  

  def die(msg, code=1):
@@ -34,6 +44,27 @@ 

      return inner

  

  

+ def get_default_upstream_branch(name):

if we assume the local default branch is the same as upstream's I wonder if we couldn't save a call to the API, but I can't find the right command right now, will need to look further

+     url = 'https://pagure.io/api/0/projects'

+     response = requests.get(url, params=dict(pattern=name, fork=False))

+     if not bool(response):

+         raise IOError("Failed to talk to %r %r", (url, response))

+     data = response.json()

+     projects = data['projects']

+     if not projects:

+         raise ValueError("No such project %r" % name)

+     if len(projects) > 1:

+         raise ValueError("More than one project called %r found "

+                          "(%i of them, in fact)." % (name, len(projects)))

+     project = projects[0]

+     return project['default_branch']

+ 

+ 

+ def get_current_local_branch():

+     code, stdout = run(['git', 'branch', '--contains'])

+     return stdout.split(maxsplit=1)[1].strip()

+ 

+ 

  def repo_url(name, ssh=False, git=False, domain='pagure.io'):

      if ssh:

          prefix = 'ssh://git@'

no initial comment

(incidentally, also fixes #1).

This pull request was submitted with the command written in this patch. :)

Pull-Request has been updated

8 years ago

the name.split like this may actually break with one feature of pagure under review: pseudo-namespace where we allow up to 1 '/' in project names.

But that's eventually something for later when it'll be in used.

if we assume the local default branch is the same as upstream's I wonder if we couldn't save a call to the API, but I can't find the right command right now, will need to look further

2 comments but nothing blocking :thumbsup:

Pull-Request has been updated

8 years ago

Pull-Request has been merged by ralph

8 years ago