Repository: bazelbuild/bazel
Issue: Make %USERPROFILE%\_netrc the default location for the netrc file on Windows
Labels: type: feature request
P2
area-Windows
team-OSS

### Description of the problem / feature request:

Bazel has a default "netrc" file location under Linux and Mac OS, but not under Windows. This is identified as "TODO" in [http.bzl](https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/repo/http.bzl#L55)

### Feature requests: what underlying problem are you trying to solve with this feature?

Not having a default location for the netrc file on Windows makes it hard to use, especially as http_archive requires an absolute path ( #10689 ). The only supported way is to use a hard-coded path such as "C:/netrc".

### Proposed Solution

Use "%USERPROFILE%/_netrc" as a default location. While GNU does not define any location for Windows, this location is de facto the one being used. This location is used by:
 - [curl/libcurl](https://ec.haxx.se/usingcurl/usingcurl-netrc)
 - By extension any software using libcurl, including [git credential manager](https://confluence.atlassian.com/bitbucketserver/permanently-authenticating-with-git-repositories-776639846.html)
 - [Go langage](https://github.com/search?q=org%3Agolang+%22_netrc%22&type=Code)
 - [Heroku Ruby Library](https://github.com/heroku/netrc/blob/master/lib/netrc.rb#L31)

### What operating system are you running Bazel on?

Windows 10

### What's the output of `bazel info release`?

release 2.1.0


Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/tools/build_defs/repo/http.bzl b/tools/build_defs/repo/http.bzl
index 587dff0c192529..04efba860e305f 100644
--- a/tools/build_defs/repo/http.bzl
+++ b/tools/build_defs/repo/http.bzl
@@ -45,14 +45,17 @@ def _get_auth(ctx, urls):
         netrc = read_netrc(ctx, ctx.attr.netrc)
         return use_netrc(netrc, urls, ctx.attr.auth_patterns)
 
-    if "HOME" in ctx.os.environ:
-        if not ctx.os.name.startswith("windows"):
-            netrcfile = "%s/.netrc" % (ctx.os.environ["HOME"],)
-            if ctx.execute(["test", "-f", netrcfile]).return_code == 0:
-                netrc = read_netrc(ctx, netrcfile)
-                return use_netrc(netrc, urls, ctx.attr.auth_patterns)
-
-    # TODO: Search at a similarly canonical place for Windows as well
+    if "HOME" in ctx.os.environ and not ctx.os.name.startswith("windows"):
+        netrcfile = "%s/.netrc" % (ctx.os.environ["HOME"])
+        if ctx.execute(["test", "-f", netrcfile]).return_code == 0:
+            netrc = read_netrc(ctx, netrcfile)
+            return use_netrc(netrc, urls, ctx.attr.auth_patterns)
+
+    if "USERPROFILE" in ctx.os.environ and ctx.os.name.startswith("windows"):
+        netrcfile = "%s/.netrc" % (ctx.os.environ["USERPROFILE"])
+        if ctx.path(netrcfile).exists:
+            netrc = read_netrc(ctx, netrcfile)
+            return use_netrc(netrc, urls, ctx.attr.auth_patterns)
 
     return {}
 


Test patch:
diff --git a/src/test/shell/bazel/BUILD b/src/test/shell/bazel/BUILD
index d47e4d4af52810..12448cd1b7d387 100644
--- a/src/test/shell/bazel/BUILD
+++ b/src/test/shell/bazel/BUILD
@@ -729,7 +729,6 @@ sh_test(
     ],
     shard_count = 6,
     tags = [
-        "no_windows",
         "requires-network",
     ],
 )
diff --git a/src/test/shell/bazel/skylark_repository_test.sh b/src/test/shell/bazel/skylark_repository_test.sh
index 3bf128d03436aa..aeea7d660477c8 100755
--- a/src/test/shell/bazel/skylark_repository_test.sh
+++ b/src/test/shell/bazel/skylark_repository_test.sh
@@ -473,7 +473,8 @@ def _impl(repository_ctx):
   result = repository_ctx.execute(
     [str(repository_ctx.which("bash")), "-c", "echo PWD=\$PWD TOTO=\$TOTO"],
     1000000,
-    { "TOTO": "titi" })
+    { "TOTO": "titi" },
+    working_directory = "$repo2")
   if result.return_code != 0:
     fail("Incorrect return code from bash: %s != 0\n%s" % (result.return_code, result.stderr))
   print(result.stdout)
@@ -481,6 +482,9 @@ repo = repository_rule(implementation=_impl, local=True)
 EOF
 
   bazel build @foo//:bar >& $TEST_log || fail "Failed to build"
+  if "$is_windows"; then
+    repo2="$(cygpath $repo2)"
+  fi
   expect_log "PWD=$repo2 TOTO=titi"
 }
 
@@ -974,6 +978,11 @@ EOF
 }
 
 function test_skylark_repository_executable_flag() {
+  if "$is_windows"; then
+    # There is no executable flag on Windows.
+    echo "Skipping test_skylark_repository_executable_flag on Windows"
+    return
+  fi
   setup_skylark_repository
 
   # Our custom repository rule
@@ -1039,6 +1048,12 @@ EOF
   diff "${output_base}/external/foo/download_executable_file.sh" \
     "${download_executable_file}" >/dev/null \
     || fail "download_executable_file.sh is not downloaded successfully"
+
+  # No executable flag for file on Windows
+  if "$is_windows"; then
+    return
+  fi
+
   # Test executable
   test ! -x "${output_base}/external/foo/download_with_sha256.txt" \
     || fail "download_with_sha256.txt is executable"
@@ -1069,6 +1084,12 @@ function test_skylark_repository_context_downloads_return_struct() {
   # Start HTTP server with Python
   startup_server "${server_dir}"
 
+  # On Windows, a file url should be file:///C:/foo/bar,
+  # we need to add one more slash at the beginning.
+  if "$is_windows"; then
+    server_dir="/${server_dir}"
+  fi
+
   setup_skylark_repository
   # Our custom repository rule
   cat >test.bzl <<EOF
@@ -1711,10 +1732,16 @@ netrcrepo = repository_rule(
   attrs = {"path": attr.string()},
 )
 EOF
+
+  netrc_dir="$(pwd)"
+  if "$is_windows"; then
+    netrc_dir="$(cygpath -m ${netrc_dir})"
+  fi
+
   cat >> $(create_workspace_with_default_repos WORKSPACE) <<EOF
 load("//:def.bzl", "netrcrepo")
 
-netrcrepo(name = "netrc", path="$(pwd)/.netrc")
+netrcrepo(name = "netrc", path="${netrc_dir}/.netrc")
 EOF
   # ...and that from the parse result, we can read off the
   # credentials for example.com.
@@ -1802,12 +1829,18 @@ authrepo = repository_rule(
   },
 )
 EOF
+
+  netrc_dir="$(pwd)"
+  if "$is_windows"; then
+    netrc_dir="$(cygpath -m ${netrc_dir})"
+  fi
+
   cat >> $(create_workspace_with_default_repos WORKSPACE) <<EOF
 load("//:def.bzl", "authrepo")
 
 authrepo(
   name = "auth",
-  path="$(pwd)/.netrc",
+  path="${netrc_dir}/.netrc",
   urls = [
     "http://example.org/public/null.tar",
     "https://foo.example.org/file1.tar",
@@ -1922,12 +1955,16 @@ function test_http_archive_netrc() {
   tar cvf x.tar x
   sha256=$(sha256sum x.tar | head -c 64)
   serve_file_auth x.tar
+  netrc_dir="$(pwd)"
+  if "$is_windows"; then
+    netrc_dir="$(cygpath -m ${netrc_dir})"
+  fi
   cat > WORKSPACE <<EOF
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 http_archive(
   name="ext",
   url = "http://127.0.0.1:$nc_port/x.tar",
-  netrc = "$(pwd)/.netrc",
+  netrc = "${netrc_dir}/.netrc",
   sha256="$sha256",
 )
 EOF
@@ -1955,12 +1992,16 @@ function test_http_archive_auth_patterns() {
   tar cvf x.tar x
   sha256=$(sha256sum x.tar | head -c 64)
   serve_file_auth x.tar
+  netrc_dir="$(pwd)"
+  if "$is_windows"; then
+    netrc_dir="$(cygpath -m ${netrc_dir})"
+  fi
   cat > WORKSPACE <<EOF
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 http_archive(
   name="ext",
   url = "http://127.0.0.1:$nc_port/x.tar",
-  netrc = "$(pwd)/.netrc",
+  netrc = "${netrc_dir}/.netrc",
   sha256="$sha256",
   auth_patterns = {
     "127.0.0.1": "Bearer <password>"
@@ -1992,6 +2033,9 @@ function test_implicit_netrc() {
   serve_file_auth x.tar
 
   export HOME=`pwd`
+  if "$is_windows"; then
+    export USERPROFILE="$(cygpath -m ${HOME})"
+  fi
   cat > .netrc <<'EOF'
 machine 127.0.0.1
 login foo
diff --git a/src/test/shell/bazel/testing_server.py b/src/test/shell/bazel/testing_server.py
index b84f100f2edf57..fd5a0768e42707 100644
--- a/src/test/shell/bazel/testing_server.py
+++ b/src/test/shell/bazel/testing_server.py
@@ -26,11 +26,13 @@
 import os.path
 try:
   from socketserver import TCPServer
-  from socketserver import UnixStreamServer
+  if not os.name == 'nt':
+    from socketserver import UnixStreamServer
 except ImportError:
   # Python 2.x compatibility hack.
   from SocketServer import TCPServer
-  from SocketServer import UnixStreamServer
+  if not os.name == 'nt':
+    from SocketServer import UnixStreamServer
 import random
 import socket
 import sys
diff --git a/src/test/shell/testenv.sh b/src/test/shell/testenv.sh
index d297c86ea342a5..1a92fd733e1a04 100755
--- a/src/test/shell/testenv.sh
+++ b/src/test/shell/testenv.sh
@@ -106,7 +106,7 @@ linux_sandbox="${BAZEL_RUNFILES}/src/main/tools/linux-sandbox"
 
 # Test data
 testdata_path=${BAZEL_RUNFILES}/src/test/shell/bazel/testdata
-python_server="${BAZEL_RUNFILES}/src/test/shell/bazel/testing_server.py"
+python_server="$(rlocation io_bazel/src/test/shell/bazel/testing_server.py)"
 
 # Third-party
 protoc_compiler="${BAZEL_RUNFILES}/src/test/shell/integration/protoc"