Repository: drdoctr/doctr

Issue:
Detect if the repo is a fork
If I'm understanding the behavior I'm seeing correctly, encrypted environment variables aren't available on forks at all, even if they are encrypted with the key for the fork. 


Discussion:

Patch:
diff --git a/.travis.yml b/.travis.yml
index 5e0cee53..5badcd81 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -41,7 +41,10 @@ script:
       echo `date` >> test
       python -m doctr deploy --key-path deploy_key.enc --no-require-master docs;
       # Test syncing a tracked file with a change
-      python -m doctr deploy --sync --key-path deploy_key.enc stash-test --built-docs test;
+      python -m doctr deploy --sync --key-path deploy_key.enc stash-test/ --built-docs test;
+      # Test syncing a single file
+      echo `date` >> test-file
+      python -m doctr deploy --sync --key-path deploy_key.enc . --built-docs test-file;
       # Test deploy branch creation. Delete the branch gh-pages-testing on drdoctr/doctr whenever you want to test this.
       python -m doctr deploy --sync --key-path deploy_key.enc --no-require-master --deploy-branch gh-pages-testing docs;
       # Test pushing to .github.io
@@ -64,7 +67,7 @@ script:
     fi
   - if [[ "${TESTS}" == "true" ]]; then
       pyflakes doctr;
-      py.test doctr;
+      py.test doctr -v -rs;
     fi
 
 doctr:
diff --git a/doctr/travis.py b/doctr/travis.py
index 8df96da2..5f34d682 100644
--- a/doctr/travis.py
+++ b/doctr/travis.py
@@ -13,6 +13,8 @@ import pathlib
 import tempfile
 import time
 
+import requests
+
 from cryptography.fernet import Fernet
 
 from .common import red, blue
@@ -214,10 +216,21 @@ def setup_GitHub_push(deploy_repo, *, auth_type='deploy_key',
     TRAVIS_BRANCH = os.environ.get("TRAVIS_BRANCH", "")
     TRAVIS_PULL_REQUEST = os.environ.get("TRAVIS_PULL_REQUEST", "")
 
+    # Check if the repo is a fork
+    TRAVIS_REPO_SLUG = os.environ["TRAVIS_REPO_SLUG"]
+    REPO_URL = 'https://api.github.com/repos/{slug}'
+    r = requests.get(REPO_URL.format(slug=TRAVIS_REPO_SLUG))
+    fork = r.json().get('fork', False)
+    # Rate limits prevent this check from working every time. By default, we
+    # assume it isn't a fork so that things just work on non-fork builds.
+    if r.status_code == 403:
+        print(red("Warning: GitHub's API rate limits prevented doctr from detecting if this build is a fork. If it is, doctr will fail with an error like 'DOCTR_DEPLOY_ENCRYPTION_KEY environment variable is not set'. This error can be safely ignored. If this is not a fork build, you can ignore this warning."), file=sys.stderr)
+
     canpush = determine_push_rights(
         branch_whitelist=branch_whitelist,
         TRAVIS_BRANCH=TRAVIS_BRANCH,
         TRAVIS_PULL_REQUEST=TRAVIS_PULL_REQUEST,
+        fork=fork,
         TRAVIS_TAG=TRAVIS_TAG,
         build_tags=build_tags)
 
@@ -434,14 +447,17 @@ def sync_from_log(src, dst, log_file, exclude=()):
         files = glob.iglob(join(src, '**'), recursive=True)
     else:
         files = [src]
-        src = os.path.dirname(src) + os.sep
+        src = os.path.dirname(src) + os.sep if os.sep in src else ''
+
+        os.makedirs(dst, exist_ok=True)
 
     # sorted makes this easier to test
     for f in sorted(files):
         if any(is_subdir(f, os.path.join(src, i)) for i in exclude):
             continue
         new_f = join(dst, f[len(src):])
-        if isdir(f):
+
+        if isdir(f) or f.endswith(os.sep):
             os.makedirs(new_f, exist_ok=True)
         else:
             shutil.copy2(f, new_f)
@@ -549,7 +565,7 @@ def last_commit_by_doctr():
     return False
 
 def determine_push_rights(*, branch_whitelist, TRAVIS_BRANCH,
-    TRAVIS_PULL_REQUEST, TRAVIS_TAG, build_tags):
+    TRAVIS_PULL_REQUEST, TRAVIS_TAG, build_tags, fork):
     """Check if Travis is running on ``master`` (or a whitelisted branch) to
     determine if we can/should push the docs to the deploy repo
     """
@@ -570,6 +586,10 @@ def determine_push_rights(*, branch_whitelist, TRAVIS_BRANCH,
         print("The website and docs are not pushed to gh-pages on pull requests", file=sys.stderr)
         canpush = False
 
+    if fork:
+        print("The website and docs are not pushed to gh-pages on fork builds.", file=sys.stderr)
+        canpush = False
+
     if last_commit_by_doctr():
         print(red("The last commit on this branch was pushed by doctr. Not pushing to "
         "avoid an infinite build-loop."), file=sys.stderr)


Test patch:
diff --git a/doctr/tests/test_local.py b/doctr/tests/test_local.py
index 78064556..70ea9c94 100644
--- a/doctr/tests/test_local.py
+++ b/doctr/tests/test_local.py
@@ -68,10 +68,11 @@ def test_GIT_URL():
 
     assert not GIT_URL.fullmatch('https://gitlab.com/drdoctr/doctr.git')
 
+@pytest.mark.skipif(os.environ.get('TRAVIS_REPO_SLUG', '') != 'drdoctr/doctr', reason="Not run on Travis fork builds")
+@pytest.mark.skipif(not on_travis(), reason="Not on Travis")
 def test_guess_github_repo():
     """
     Only works if run in this repo, and if cloned from origin. For safety,
-    only run on Travis
+    only run on Travis and not run on fork builds.
     """
-    if on_travis():
-        assert guess_github_repo() == 'drdoctr/doctr'
+    assert guess_github_repo() == 'drdoctr/doctr'
diff --git a/doctr/tests/test_travis.py b/doctr/tests/test_travis.py
index 09b9e5bf..81286cec 100644
--- a/doctr/tests/test_travis.py
+++ b/doctr/tests/test_travis.py
@@ -221,46 +221,91 @@ def test_sync_from_log(src, dst):
             os.chdir(old_curdir)
 
 
+@pytest.mark.parametrize("dst", ['dst', 'dst/'])
+def test_sync_from_log_file_to_dir(dst):
+    with tempfile.TemporaryDirectory() as dir:
+        try:
+            old_curdir = os.path.abspath(os.curdir)
+            os.chdir(dir)
+
+            src = 'file'
+
+            with open(src, 'w') as f:
+                f.write('test1')
+
+            # Test that the sync happens
+            added, removed = sync_from_log(src, dst, 'logfile')
+
+            assert added == [
+                os.path.join('dst', 'file'),
+                'logfile',
+                ]
+
+            assert removed == []
+
+            assert os.path.isdir(dst)
+            # Make sure dst is a file
+            with open(os.path.join('dst', 'file')) as f:
+                assert f.read() == 'test1'
+
+
+            with open('logfile') as f:
+                assert f.read() == '\n'.join([
+                    os.path.join('dst', 'file')
+                    ])
+
+        finally:
+            os.chdir(old_curdir)
+
+
 @pytest.mark.parametrize("""branch_whitelist, TRAVIS_BRANCH,
-                         TRAVIS_PULL_REQUEST, TRAVIS_TAG, build_tags,
+                         TRAVIS_PULL_REQUEST, TRAVIS_TAG, fork, 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),
+                             ('master', 'doctr', 'true', "", False, False, False),
+                             ('master', 'doctr', 'false', "", False, False, False),
+                             ('master', 'master', 'true', "", False, False, False),
+                             ('master', 'master', 'false', "", False, False, True),
+                             ('doctr', 'doctr', 'True', "", False, False, False),
+                             ('doctr', 'doctr', 'false', "", False, False, True),
+                             ('set()', 'doctr', 'false', "", False, False, False),
+
+                             ('master', 'doctr', 'true', "tagname", False, False, False),
+                             ('master', 'doctr', 'false', "tagname", False, False, False),
+                             ('master', 'master', 'true', "tagname", False, False, False),
+                             ('master', 'master', 'false', "tagname", False, False, False),
+                             ('doctr', 'doctr', 'True', "tagname", False, False, False),
+                             ('doctr', 'doctr', 'false', "tagname", False, False, False),
+                             ('set()', 'doctr', 'false', "tagname", False, False, False),
+
+                             ('master', 'doctr', 'true', "", False, True, False),
+                             ('master', 'doctr', 'false', "", False, True, False),
+                             ('master', 'master', 'true', "", False, True, False),
+                             ('master', 'master', 'false', "", False, True, True),
+                             ('doctr', 'doctr', 'True', "", False, True, False),
+                             ('doctr', 'doctr', 'false', "", False, True, True),
+                             ('set()', 'doctr', 'false', "", False, True, False),
+
+                             ('master', 'doctr', 'true', "tagname", False, True, True),
+                             ('master', 'doctr', 'false', "tagname", False, True, True),
+                             ('master', 'master', 'true', "tagname", False, True, True),
+                             ('master', 'master', 'false', "tagname", False, True, True),
+                             ('doctr', 'doctr', 'True', "tagname", False, True, True),
+                             ('doctr', 'doctr', 'false', "tagname", False, True, True),
+                             ('set()', 'doctr', 'false', "tagname", False, True, True),
+
+                             ('master', 'doctr', 'true', "", True, False, False),
+                             ('master', 'doctr', 'false', "", True, False, False),
+                             ('master', 'master', 'true', "", True, False, False),
+                             ('master', 'master', 'false', "", True, False, False),
+                             ('doctr', 'doctr', 'True', "", True, False, False),
+                             ('doctr', 'doctr', 'false', "", True, False, False),
+                             ('set()', 'doctr', 'false', "", True, False, False),
 
                          ])
 def test_determine_push_rights(branch_whitelist, TRAVIS_BRANCH,
-    TRAVIS_PULL_REQUEST, TRAVIS_TAG, build_tags, canpush, monkeypatch):
+    TRAVIS_PULL_REQUEST, TRAVIS_TAG, build_tags, fork, canpush, monkeypatch):
     branch_whitelist = {branch_whitelist}
 
     assert determine_push_rights(
@@ -268,6 +313,7 @@ def test_determine_push_rights(branch_whitelist, TRAVIS_BRANCH,
         TRAVIS_BRANCH=TRAVIS_BRANCH,
         TRAVIS_PULL_REQUEST=TRAVIS_PULL_REQUEST,
         TRAVIS_TAG=TRAVIS_TAG,
+        fork=fork,
         build_tags=build_tags) == canpush
 
 @pytest.mark.parametrize("src", ["src", "."])