Support GCE Windows.

1. Allow updating cookie files.
2. Error out with warning when --nofork is missing. os.fork() is not supported on Windows.
3. Error out with warning when env var HOMEPATH is not set.

Change-Id: I155774afed8910d5cd38780a47e321fac6651953
diff --git a/README.md b/README.md
index 92af485..9e04de6 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,10 @@
   ...
 ```
 
-## Installation
+To add a scope to an existing GCE instance see this
+[gcloud beta feature](https://cloud.google.com/sdk/gcloud/reference/beta/compute/instances/set-scopes).
+
+## Installation on Linux
 
 Install the daemon within the VM image and start it running:
 
@@ -29,3 +32,63 @@
 
 The daemon launches itself into the background and continues
 to keep the OAuth2 access token fresh.
+
+## Installation on Windows
+
+1. Install [Python](https://www.python.org/downloads/windows/) and
+   [Git](https://git-scm.com/download) for Windows.
+1. Run `git-cookie-authdaemon` in the same environment under the same user
+   git commands will be run, for example in either `Command Prompt`
+   or `Cygwin bash shell` under user `builder`.
+```
+python git-cookie-authdaemon --nofork
+```
+
+### Launch at Windows boot
+
+It may be desired in automation to launch `git-cookie-authdaemon` at
+Windows boot. It can be done as a scheduled task. The following is an
+example on a Jenkins node. The setup is:
+
+1. The VM is created from GCE Windows Server 2012R2 image.
+1. Gygwin with SSHD is installed.
+1. The Jenkins master connects to the VM through SSH as `builder` account.
+
+How to create a scheduled task.
+
+1. Launch `Task Scheduler` from an Administrator account.
+1. Click `Create Task` in the right pane.
+1. In `General` tab:
+   1. Change user to the one running Jenkins node if it is different. You may
+      want to run Jenkins node as a non-privileged user, `builder` in this
+      example.
+   1. Select `Run whether user is logged on or not`
+1. In `Trigger` tab. Add a trigger
+   1. Set `Begin the task` as `At startup`.
+   1. Uncheck `Stop task if it runs longer than`.
+   1. Check `Enabled`.
+1. In `Actions` tab.  Add `Start a program`.
+   1. Set `Program/script` as `C:\cygwin64\bin\bash.ext`,
+   1. Set `Add arguments` as
+      `--login -c /home/builder/git-cookie-authdaemon_wrapper.sh` (see note
+      below)
+1. Click `Ok` to save it.
+1. Optional: click `Enable All Tasks History` in `Task Scheduler`'s right pane.
+1. Add `builder` account to `Administrative Tools -> Local Security Policy ->
+   Local Policies -> User Rights Assignment -> Log On As Batch Job`
+
+Note: /home/builder/git-cookie-authdaemon_wrapper.sh` below does
+
+1. Set HOMEPATH if it is not.
+2. Capture git-cookie-autodaemon.log stdout and stderr for debugging.
+
+```
+#!/bin/bash
+exe=gcompute-tools/git-cookie-authdaemon
+log=/cygdrive/c/build/git-cookie-autodaemon.log
+
+# HOMEPATH is not set in task scheduled at machine boot.
+export HOMEPATH=${HOMEPATH:-'\Users\builder'}
+
+/cygdrive/c/Python27/python $exe --nofork >> $log 2>&1 # option --debug is also available.
+```
diff --git a/git-cookie-authdaemon b/git-cookie-authdaemon
index e18b279..536e011 100755
--- a/git-cookie-authdaemon
+++ b/git-cookie-authdaemon
@@ -18,7 +18,8 @@
 Tokens are written to ~/.git-credential-cache/cookie
 Git config variable http.cookiefile is updated.
 
-Runs only on Google Compute Engine.
+Runs only on Google Compute Engine (GCE). On GCE Windows '--nofork' option
+is needed. '--debug' option is available.
 """
 
 import atexit
@@ -26,6 +27,7 @@
 import cookielib
 import json
 import os
+import platform
 import subprocess
 import sys
 import time
@@ -42,6 +44,7 @@
   'https://www.googleapis.com/auth/source.read_only',
 ]
 COOKIE_JAR = None
+IS_WINDOWS = platform.system() == 'Windows'
 
 def read_meta(part):
   r = urllib2.Request(META_URL + part)
@@ -62,8 +65,26 @@
 def configure_git():
   global COOKIE_JAR
 
-  dir = os.path.join(os.environ['HOME'], '.git-credential-cache')
+  if IS_WINDOWS:
+    # Git for Windows reads %HOMEPATH%/.gitconfig in Command Prompt,
+    # but $HOME/.gitconfig in Cygwin. The two paths can be different.
+    # Cygwin sets env var $HOMEPATH accordingly to %HOMEPATH%.
+    # Set cookie file as %HOMEPATH%/.git-credential-cache/cookie,
+    # so it can be used in both cases.
+    if 'HOMEPATH' in os.environ:
+      homepath = os.environ['HOMEPATH']
+    else:
+      # When launched as a scheduled task at machine startup
+      # HOMEPATH may not be set.
+      sys.stderr.write('HOMEPATH is not set.\n')
+      sys.exit(1)
+  else:
+    homepath = os.environ['HOME']
+  dir = os.path.join(homepath, '.git-credential-cache')
+
   COOKIE_JAR = os.path.join(dir, 'cookie')
+  if '--debug' in sys.argv:
+    print 'Cookie file: %s' % COOKIE_JAR
 
   if os.path.exists(dir):
     os.chmod(dir, 0700)
@@ -88,7 +109,7 @@
   now = int(time.time())
   token = acquire_token(scope, retry)
   access_token = token['access_token']
-  expires = now + int(token['expires_in'])
+  expires = now + int(token['expires_in'])  # Epoch in sec
 
   tmp_jar = COOKIE_JAR + '.lock'
   cj = cookielib.MozillaCookieJar(tmp_jar)
@@ -113,6 +134,16 @@
       rest = None))
 
   cj.save()
+  if '--debug' in sys.argv:
+    print 'Updating %s.' % COOKIE_JAR
+    print 'Expires: %d, %s, in %d seconds'% (
+      expires, time.ctime(expires), expires - now)
+    sys.stdout.flush()
+  if IS_WINDOWS:
+    # os.rename() below on Windows will raise OSError when dst exists.
+    # See https://docs.python.org/2/library/os.html#os.rename
+    if os.path.isfile(COOKIE_JAR):
+      os.remove(COOKIE_JAR)
   os.rename(tmp_jar, COOKIE_JAR)
   return expires
 
@@ -136,9 +167,15 @@
 def main():
   scope = select_scope()
   configure_git()
+
   expires = update_cookie(scope, retry=False)
 
   if '--nofork' not in sys.argv:
+    if IS_WINDOWS:
+      # os.fork() is not supported on Windows.
+      sys.stderr.write('Add \'--nofork\' on Windows\n')
+      sys.exit(1)
+
     if os.fork() > 0:
       sys.exit(0)