diff --git a/.travis.yml b/.travis.yml index c621f5a3..13a07f42 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,6 +51,8 @@ script: # GitHub Wiki deploy echo "This page was automatically deployed by doctr on $(date)" > deploy-test.md; python -m doctr deploy --sync --key-path deploy_key.enc --no-require-master --deploy-repo drdoctr/doctr.wiki . --built-docs deploy-test.md; + # Build on tags + python -m doctr deploy --sync --key-path deploy_key.enc "tag-$TRAVIS_TAG" --build-tags --branch-whitelist; fi - if [[ "${TESTS}" == "true" ]]; then pyflakes doctr; diff --git a/docs/recipes.rst b/docs/recipes.rst index a9459026..756a7c06 100644 --- a/docs/recipes.rst +++ b/docs/recipes.rst @@ -41,6 +41,35 @@ For security purposes, it is not possible to deploy from branches on forks requests from forks). If you want to deploy the docs for a branch from a pull request, you will need to push it up to the main repository. +Deploy docs from git tags +========================= + +Travis CI runs separate builds for git tags that are pushed to your repo. By +default, doctr does not deploy on these builds, but it can be enabled with the +``--build-tags`` flag to ``doctr deploy``. This is useful if you want to use +doctr to deploy versioned docs for releases, for example. + +On Travis CI, the tag is set to the environment variable ``$TRAVIS_TAG``, +which is empty otherwise. The following will deploy the docs to ``dev`` for +normal ``master`` builds, and ``version-`` for tag builds: + +.. code:: yaml + + - if [[ -z "$TRAVIS_TAG" ]]; then + DEPLOY_DIR=dev; + else + DEPLOY_DIR="version-$TRAVIS_TAG"; + fi + - doctr deploy --build-tags --built-docs build/ $DEPLOY_DIR + +If you want to deploy only on a tag, use ``--branch-whitelist`` with no +arguments to tell doctr to not deploy from any branch. For instance, to deploy +only tags to ``latest``: + +.. code:: yaml + + - doctr deploy latest --built-docs build/ --build-tags --branch-whitelist + Deploy to a separate repo ========================= diff --git a/doctr/__main__.py b/doctr/__main__.py index 524dacc8..befc05a2 100644 --- a/doctr/__main__.py +++ b/doctr/__main__.py @@ -145,6 +145,10 @@ def get_parser(config=None): help=argparse.SUPPRESS) deploy_parser_add_argument('--deploy-repo', default=None, help="""Repo to deploy the docs to. By default, it deploys to the repo Doctr is run from.""") + deploy_parser_add_argument('--branch-whitelist', default=None, nargs='*', + help="""Branches to deploy from. Pass no arguments to not build on any branch + (typically used in conjunction with --build-tags). Note that you can + deploy from every branch with --no-require-master.""", type=set, metavar="BRANCH") deploy_parser_add_argument('--no-require-master', dest='require_master', action='store_false', default=True, help="""Allow docs to be pushed from a branch other than master""") deploy_parser_add_argument('--command', default=None, @@ -163,6 +167,11 @@ def get_parser(config=None): deploy_parser_add_argument('--no-push', dest='push', action='store_false', default=True, help="Run all the steps except the last push step. " "Useful for debugging") + deploy_parser_add_argument('--build-tags', action='store_true', + default=False, help="""Deploy on tag builds. On a tag build, + $TRAVIS_TAG is set to the name of the tag. The default is to not + deploy on tag builds. Note that this will still build on a branch, + unless --branch-whitelist (with no arguments) is passed.""") deploy_parser_add_argument('--gh-pages-docs', default=None, help="""!!DEPRECATED!! Directory to deploy the html documentation to on gh-pages. The default is %(default)r. The deploy directory should be passed as @@ -273,13 +282,19 @@ def deploy(args, parser): current_commit = subprocess.check_output(['git', 'rev-parse', 'HEAD']).decode('utf-8').strip() try: - branch_whitelist = {'master'} if args.require_master else set(get_travis_branch()) + branch_whitelist = set() if args.require_master else set(get_travis_branch()) branch_whitelist.update(set(config.get('branches',set({})))) + if args.branch_whitelist is not None: + branch_whitelist.update(args.branch_whitelist) + if not args.branch_whitelist: + branch_whitelist = {'master'} canpush = setup_GitHub_push(deploy_repo, deploy_branch=deploy_branch, auth_type='token' if args.token else 'deploy_key', full_key_path=keypath, - branch_whitelist=branch_whitelist, env_name=env_name) + branch_whitelist=branch_whitelist, + build_tags=args.build_tags, + env_name=env_name) if args.sync: built_docs = args.built_docs or find_sphinx_build_dir() diff --git a/doctr/tests/test_travis.py b/doctr/tests/test_travis.py index 6a4c3bbe..136ddeaf 100644 --- a/doctr/tests/test_travis.py +++ b/doctr/tests/test_travis.py @@ -144,16 +144,51 @@ def test_sync_from_log(src, dst): os.chdir(old_curdir) -@pytest.mark.parametrize("travis_branch, travis_pr, whitelist, canpush", - [('doctr', 'true', 'master', False), - ('doctr', 'false', 'master', False), - ('master', 'true', 'master', False), - ('master', 'false', 'master', True), - ('doctr', 'True', 'doctr', False), - ('doctr', 'false', 'doctr', True), - ('doctr', 'false', 'set()', False), - ]) -def test_determine_push_rights(travis_branch, travis_pr, whitelist, canpush, monkeypatch): - branch_whitelist = {whitelist} +@pytest.mark.parametrize("""branch_whitelist, TRAVIS_BRANCH, + TRAVIS_PULL_REQUEST, TRAVIS_TAG, build_tags, + canpush""", + [ + + ('master', 'doctr', 'true', "", False, False), + ('master', 'doctr', 'false', "", False, False), + ('master', 'master', 'true', "", False, False), + ('master', 'master', 'false', "", False, True), + ('doctr', 'doctr', 'True', "", False, False), + ('doctr', 'doctr', 'false', "", False, True), + ('set()', 'doctr', 'false', "", False, False), + + ('master', 'doctr', 'true', "tagname", False, False), + ('master', 'doctr', 'false', "tagname", False, False), + ('master', 'master', 'true', "tagname", False, False), + ('master', 'master', 'false', "tagname", False, False), + ('doctr', 'doctr', 'True', "tagname", False, False), + ('doctr', 'doctr', 'false', "tagname", False, False), + ('set()', 'doctr', 'false', "tagname", False, False), + + ('master', 'doctr', 'true', "", True, False), + ('master', 'doctr', 'false', "", True, False), + ('master', 'master', 'true', "", True, False), + ('master', 'master', 'false', "", True, True), + ('doctr', 'doctr', 'True', "", True, False), + ('doctr', 'doctr', 'false', "", True, True), + ('set()', 'doctr', 'false', "", True, False), + + ('master', 'doctr', 'true', "tagname", True, True), + ('master', 'doctr', 'false', "tagname", True, True), + ('master', 'master', 'true', "tagname", True, True), + ('master', 'master', 'false', "tagname", True, True), + ('doctr', 'doctr', 'True', "tagname", True, True), + ('doctr', 'doctr', 'false', "tagname", True, True), + ('set()', 'doctr', 'false', "tagname", True, True), - assert determine_push_rights(branch_whitelist, travis_branch, travis_pr) == canpush + ]) +def test_determine_push_rights(branch_whitelist, TRAVIS_BRANCH, + TRAVIS_PULL_REQUEST, TRAVIS_TAG, build_tags, canpush, monkeypatch): + branch_whitelist = {branch_whitelist} + + assert determine_push_rights( + branch_whitelist=branch_whitelist, + TRAVIS_BRANCH=TRAVIS_BRANCH, + TRAVIS_PULL_REQUEST=TRAVIS_PULL_REQUEST, + TRAVIS_TAG=TRAVIS_TAG, + build_tags=build_tags) == canpush diff --git a/doctr/travis.py b/doctr/travis.py index 557c4a9d..9682022c 100644 --- a/doctr/travis.py +++ b/doctr/travis.py @@ -183,8 +183,10 @@ def get_travis_branch(): else: return os.environ.get("TRAVIS_BRANCH", "") -def setup_GitHub_push(deploy_repo, auth_type='deploy_key', full_key_path='github_deploy_key.enc', - require_master=None, branch_whitelist=None, deploy_branch='gh-pages', env_name='DOCTR_DEPLOY_ENCRYPTION_KEY'): +def setup_GitHub_push(deploy_repo, *, auth_type='deploy_key', + full_key_path='github_deploy_key.enc', require_master=None, + branch_whitelist=None, deploy_branch='gh-pages', + env_name='DOCTR_DEPLOY_ENCRYPTION_KEY', build_tags=False): """ Setup the remote to push to GitHub (to be run on Travis). @@ -196,6 +198,8 @@ def setup_GitHub_push(deploy_repo, auth_type='deploy_key', full_key_path='github For ``auth_type='deploy_key'``, this sets up the remote with ssh access. """ + # Set to the name of the tag for tag builds + TRAVIS_TAG = os.environ.get("TRAVIS_TAG", "") if not branch_whitelist: branch_whitelist={'master'} @@ -213,8 +217,12 @@ def setup_GitHub_push(deploy_repo, auth_type='deploy_key', full_key_path='github TRAVIS_BRANCH = os.environ.get("TRAVIS_BRANCH", "") TRAVIS_PULL_REQUEST = os.environ.get("TRAVIS_PULL_REQUEST", "") - canpush = determine_push_rights(branch_whitelist, TRAVIS_BRANCH, - TRAVIS_PULL_REQUEST) + canpush = determine_push_rights( + branch_whitelist=branch_whitelist, + TRAVIS_BRANCH=TRAVIS_BRANCH, + TRAVIS_PULL_REQUEST=TRAVIS_PULL_REQUEST, + TRAVIS_TAG=TRAVIS_TAG, + build_tags=build_tags) print("Setting git attributes") set_git_user_email() @@ -441,6 +449,9 @@ def commit_docs(*, added, removed): TRAVIS_COMMIT = os.environ.get("TRAVIS_COMMIT", "") TRAVIS_REPO_SLUG = os.environ.get("TRAVIS_REPO_SLUG", "") TRAVIS_JOB_ID = os.environ.get("TRAVIS_JOB_ID", "") + TRAVIS_TAG = os.environ.get("TRAVIS_TAG", "") + branch = "tag" if TRAVIS_TAG else "branch" + DOCTR_COMMAND = ' '.join(map(shlex.quote, sys.argv)) for f in added: @@ -452,7 +463,7 @@ def commit_docs(*, added, removed): Update docs after building Travis build {TRAVIS_BUILD_NUMBER} of {TRAVIS_REPO_SLUG} -The docs were built from the branch '{TRAVIS_BRANCH}' against the commit +The docs were built from the {branch} '{TRAVIS_BRANCH}' against the commit {TRAVIS_COMMIT}. The Travis build that generated this commit is at @@ -462,6 +473,7 @@ def commit_docs(*, added, removed): {DOCTR_COMMAND} """.format( + branch=branch, TRAVIS_BUILD_NUMBER=TRAVIS_BUILD_NUMBER, TRAVIS_BRANCH=TRAVIS_BRANCH, TRAVIS_COMMIT=TRAVIS_COMMIT, @@ -504,12 +516,18 @@ def push_docs(deploy_branch='gh-pages', retries=3): return sys.exit("Giving up...") -def determine_push_rights(branch_whitelist, TRAVIS_BRANCH, TRAVIS_PULL_REQUEST): +def determine_push_rights(*, branch_whitelist, TRAVIS_BRANCH, + TRAVIS_PULL_REQUEST, TRAVIS_TAG, build_tags): """Check if Travis is running on ``master`` (or a whitelisted branch) to determine if we can/should push the docs to the deploy repo """ canpush = True + if TRAVIS_TAG: + if not build_tags: + print("The docs are not pushed on tag builds. To push on future tag builds, use --build-tags") + return build_tags + if not any([re.compile(x).match(TRAVIS_BRANCH) for x in branch_whitelist]): print("The docs are only pushed to gh-pages from master. To allow pushing from " "a non-master branch, use the --no-require-master flag", file=sys.stderr)