diff --git a/.travis.yml b/.travis.yml index 5bce766d4..dd405f523 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,23 @@ sudo: required services: - docker - +addons: + apt: + sources: + - deadsnakes + packages: + - python3.5 language: python python: 2.7 env: + - TOX_ENV=py35 - TOX_ENV=py34 - TOX_ENV=py27 - TOX_ENV=pep8 - TOX_ENV=docs - - TOX_ENV=noop_py - - TOX_ENV=noop_cli + - TOX_ENV=py_func + - TOX_ENV=cli_func install: - pip install tox script: - tox -e $TOX_ENV -after_success: - if [ "$TOX_ENV" = "noop_py" ]; then ./tools/py_functional_tests.sh; elif [ "$TOX_ENV" = "noop_cli" ]; then ./tools/functional_tests.sh; fi diff --git a/AUTHORS b/AUTHORS index cf419ddff..f3290a7a8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -36,3 +36,8 @@ Peter Mosmans Stefan K. Dunkler Missionrulz Rafael Eyng +Armin Weihbold +derek-austin +Jonathon Reinhart +Michal Galet +Will Starms diff --git a/ChangeLog b/ChangeLog index b6d8efa7e..94d6b18d1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +Version 0.16 + + * Add the ability to fork to a specific namespace + * JIRA service - add api_url to optional attributes + * Fix bug: Missing coma concatenates array values + * docs: branch protection notes + * Create a project in a group + * Add only_allow_merge_if_build_succeeds option to project objects + * Add support for --all in CLI + * Fix examples for file modification + * Use the plural merge_requests URL everywhere + * Rework travis and tox setup + * Workaround gitlab setup failure in tests + * Add ProjectBuild.erase() + * Implement ProjectBuild.play() + Version 0.15.1 * docs: improve the pagination section diff --git a/docs/cli.rst b/docs/cli.rst index 7721f54d5..8b79d78fb 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -136,12 +136,18 @@ Example: Examples ======== -List all the projects: +List the projects (paginated): .. code-block:: console $ gitlab project list +List all the projects: + +.. code-block:: console + + $ gitlab project list --all + Limit to 5 items per request, display the 1st page only .. code-block:: console diff --git a/docs/gl_objects/branches.rst b/docs/gl_objects/branches.rst index 1b61af34c..50b97a799 100644 --- a/docs/gl_objects/branches.rst +++ b/docs/gl_objects/branches.rst @@ -41,3 +41,10 @@ Protect/unprotect a repository branch: .. literalinclude:: branches.py :start-after: # protect :end-before: # end protect + +.. note:: + + By default, developers will not be able to push or merge into + protected branches. This can be changed by passing ``developers_can_push`` + or ``developers_can_merge`` like so: + ``branch.protect(developers_can_push=False, developers_can_merge=True)`` diff --git a/docs/gl_objects/builds.py b/docs/gl_objects/builds.py index b0d7ea2c7..9f5ef12c1 100644 --- a/docs/gl_objects/builds.py +++ b/docs/gl_objects/builds.py @@ -103,10 +103,10 @@ def __call__(self, chunk): build.retry() # end retry -# delete -gl.project_builds.delete(build_id, project_id=1) -# or -project.builds.delete(build_id) -# or -build.delete() -# end delete +# erase +build.erase() +# end erase + +# play +build.play() +# end play diff --git a/docs/gl_objects/builds.rst b/docs/gl_objects/builds.rst index 1c4c525ad..78412b48a 100644 --- a/docs/gl_objects/builds.rst +++ b/docs/gl_objects/builds.rst @@ -150,8 +150,14 @@ Cancel/retry a build: :start-after: # retry :end-before: # end retry -Erase a build: +Play (trigger) a build: .. literalinclude:: builds.py - :start-after: # delete - :end-before: # end delete + :start-after: # play + :end-before: # end play + +Erase a build (artifacts and trace): + +.. literalinclude:: builds.py + :start-after: # erase + :end-before: # end erase diff --git a/docs/gl_objects/projects.py b/docs/gl_objects/projects.py index 269574720..7623c15cc 100644 --- a/docs/gl_objects/projects.py +++ b/docs/gl_objects/projects.py @@ -51,6 +51,9 @@ fork = gl.project_forks.create({}, project_id=1) # or fork = project.forks.create({}) + +# fork to a specific namespace +fork = gl.project_forks.create({'namespace': 'myteam'}, project_id=1) # end fork # forkrelation @@ -223,11 +226,11 @@ # files update f.content = 'new content' -f.save(branch='master', commit_message='Update testfile') +f.save(branch_name='master', commit_message='Update testfile') # or for binary data f.content = base64.b64encode(open('image.png').read()) -f.save(branch='master', commit_message='Update testfile', encoding='base64') +f.save(branch_name='master', commit_message='Update testfile', encoding='base64') # end files update # files delete diff --git a/docs/gl_objects/projects.rst b/docs/gl_objects/projects.rst index 37f04cf6e..afc0d3a88 100644 --- a/docs/gl_objects/projects.rst +++ b/docs/gl_objects/projects.rst @@ -46,6 +46,16 @@ Create a project for a user (admin only): :start-after: # user create :end-before: # end user create +Create a project in a group: + +You need to get the id of the group, then use the namespace_id attribute to create the group: + +.. code:: python + + group_id = gl.groups.search('my-group')[0].id + project = gl.projects.create({'name': 'myrepo', 'namespace_id': group_id}) + + Update a project: .. literalinclude:: projects.py diff --git a/gitlab/__init__.py b/gitlab/__init__.py index fa1927d80..2699328b8 100644 --- a/gitlab/__init__.py +++ b/gitlab/__init__.py @@ -33,7 +33,7 @@ from gitlab.objects import * # noqa __title__ = 'python-gitlab' -__version__ = '0.15.1' +__version__ = '0.16' __author__ = 'Gauvain Pocentek' __email__ = 'gauvain@pocentek.net' __license__ = 'LGPL3' diff --git a/gitlab/cli.py b/gitlab/cli.py index 9f7f4143d..1826a7b72 100644 --- a/gitlab/cli.py +++ b/gitlab/cli.py @@ -379,6 +379,8 @@ def _populate_sub_parser_by_class(cls, sub_parser): for x in cls.requiredListAttrs] sub_parser_action.add_argument("--page", required=False) sub_parser_action.add_argument("--per-page", required=False) + sub_parser_action.add_argument("--all", required=False, + action='store_true') if action_name in ["get", "delete"]: if cls not in [gitlab.CurrentUser]: diff --git a/gitlab/exceptions.py b/gitlab/exceptions.py index 7b0f7f006..733551fab 100644 --- a/gitlab/exceptions.py +++ b/gitlab/exceptions.py @@ -95,6 +95,14 @@ class GitlabBuildRetryError(GitlabRetryError): pass +class GitlabBuildPlayError(GitlabRetryError): + pass + + +class GitlabBuildEraseError(GitlabRetryError): + pass + + class GitlabPipelineRetryError(GitlabRetryError): pass diff --git a/gitlab/objects.py b/gitlab/objects.py index 5a23dbf83..e61483a93 100644 --- a/gitlab/objects.py +++ b/gitlab/objects.py @@ -689,7 +689,7 @@ class ApplicationSettings(GitlabObject): 'domain_blacklist', 'domain_blacklist_enabled', 'domain_whitelist', - 'enabled_git_access_protocol' + 'enabled_git_access_protocol', 'gravatar_enabled', 'home_page_url', 'max_attachment_size', @@ -961,6 +961,18 @@ def retry(self, **kwargs): r = self.gitlab._raw_post(url) raise_error_from_response(r, GitlabBuildRetryError, 201) + def play(self, **kwargs): + """Trigger a build explicitly.""" + url = '/projects/%s/builds/%s/play' % (self.project_id, self.id) + r = self.gitlab._raw_post(url) + raise_error_from_response(r, GitlabBuildPlayError, 201) + + def erase(self, **kwargs): + """Erase the build (remove build artifacts and trace).""" + url = '/projects/%s/builds/%s/erase' % (self.project_id, self.id) + r = self.gitlab._raw_post(url) + raise_error_from_response(r, GitlabBuildEraseError, 201) + def keep_artifacts(self, **kwargs): """Prevent artifacts from being delete when expiration is set. @@ -1169,6 +1181,7 @@ class ProjectFork(GitlabObject): canList = False canGet = False requiredUrlAttrs = ['project_id'] + optionalCreateAttrs = ['namespace'] class ProjectForkManager(BaseManager): @@ -1378,8 +1391,7 @@ class ProjectMergeRequestNoteManager(BaseManager): class ProjectMergeRequest(GitlabObject): - _url = '/projects/%(project_id)s/merge_request' - _urlPlural = '/projects/%(project_id)s/merge_requests' + _url = '/projects/%(project_id)s/merge_requests' _constructorTypes = {'author': 'User', 'assignee': 'User'} requiredUrlAttrs = ['project_id'] requiredCreateAttrs = ['source_branch', 'target_branch', 'title'] @@ -1787,7 +1799,7 @@ class ProjectService(GitlabObject): 'irker': (('recipients', ), ('default_irc_uri', 'server_port', 'server_host', 'colorize_messages')), 'jira': (('new_issue_url', 'project_url', 'issues_url'), - ('description', 'username', 'password')), + ('api_url', 'description', 'username', 'password')), 'pivotaltracker': (('token', ), tuple()), 'pushover': (('api_key', 'user_key', 'priority'), ('device', 'sound')), 'redmine': (('new_issue_url', 'project_url', 'issues_url'), @@ -1882,14 +1894,16 @@ class Project(GitlabObject): 'snippets_enabled', 'container_registry_enabled', 'public', 'visibility_level', 'namespace_id', 'description', 'path', 'import_url', - 'builds_enabled', 'public_builds'] + 'builds_enabled', 'public_builds', + 'only_allow_merge_if_build_succeeds'] optionalUpdateAttrs = ['name', 'default_branch', 'issues_enabled', 'wall_enabled', 'merge_requests_enabled', 'wiki_enabled', 'snippets_enabled', 'container_registry_enabled', 'public', 'visibility_level', 'namespace_id', 'description', 'path', 'import_url', 'builds_enabled', - 'public_builds'] + 'public_builds', + 'only_allow_merge_if_build_succeeds'] shortPrintAttr = 'path' managers = [ ('accessrequests', ProjectAccessRequestManager, @@ -2304,7 +2318,7 @@ class UserProject(GitlabObject): 'merge_requests_enabled', 'wiki_enabled', 'snippets_enabled', 'public', 'visibility_level', 'description', 'builds_enabled', 'public_builds', - 'import_url'] + 'import_url', 'only_allow_merge_if_build_succeeds'] class ProjectManager(BaseManager): diff --git a/tools/build_test_env.sh b/tools/build_test_env.sh index 7881c1826..3f6191a2a 100755 --- a/tools/build_test_env.sh +++ b/tools/build_test_env.sh @@ -102,20 +102,25 @@ while :; do I=$((I+5)) [ "$I" -lt 120 ] || fatal "timed out" done -sleep 5 # Get the token log "Getting GitLab token..." -TOKEN_JSON=$( - try curl -s http://localhost:8080/api/v3/session \ - -X POST \ - --data "login=$LOGIN&password=$PASSWORD" -) || exit 1 -TOKEN=$( - pecho "${TOKEN_JSON}" | - try python -c \ - 'import sys, json; print(json.load(sys.stdin)["private_token"])' -) || exit 1 +I=0 +while :; do + sleep 1 + TOKEN_JSON=$( + try curl -s http://localhost:8080/api/v3/session \ + -X POST \ + --data "login=$LOGIN&password=$PASSWORD" + ) >/dev/null 2>&1 || true + TOKEN=$( + pecho "${TOKEN_JSON}" | + try python -c \ + 'import sys, json; print(json.load(sys.stdin)["private_token"])' + ) >/dev/null 2>&1 && break + I=$((I+1)) + [ "$I" -lt 20 ] || fatal "timed out" +done cat > $CONFIG << EOF [global] diff --git a/tox.ini b/tox.ini index 89b4f9b35..ef3e68a9c 100644 --- a/tox.ini +++ b/tox.ini @@ -32,12 +32,8 @@ commands = python setup.py build_sphinx commands = python setup.py testr --slowest --coverage --testr-args="{posargs}" -[testenv:noop_cli] -usedevelop = True -install_command = true {opts} {packages} -commands = true +[testenv:cli_func] +commands = {toxinidir}/tools/functional_tests.sh -[testenv:noop_py] -usedevelop = True -install_command = true {opts} {packages} -commands = true +[testenv:py_func] +commands = {toxinidir}/tools/py_functional_tests.sh