[skolo] Provide our own Python to power Ansible on Macs.

macOS 13 quit shipping Python (shipping only an XCode shim), so we have
no choice. However, I found a way to embed the Python bootstrapping in
the main playbook, so we can bring our own Python without having a
separate setup step. Just run mac.yml on a brand new unboxed machine,
and away you go.

Also discovered changed_when, which gets rid of spurious yellow status
messages.
Change-Id: If50374b73599b679f79634d28b213171f62382c1
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/564918
Commit-Queue: Erik Rose <erikrose@google.com>
Reviewed-by: Joe Gregorio <jcgregorio@google.com>
diff --git a/skolo/ansible/hosts.yml b/skolo/ansible/hosts.yml
index 658225b..172233d 100644
--- a/skolo/ansible/hosts.yml
+++ b/skolo/ansible/hosts.yml
@@ -296,72 +296,14 @@
       vars:
         install_test_machine_monitor__start_swarming: true
         skolo_group: staff
-
-    # Use the Python which actually ships with the Mac. This is Python 2 most of
-    # the time, which is still supported module-side as of Ansible 2.12.
-    # /usr/bin/python3 is present but often an XCode shim, which launches
-    # xcode-select, which requires GUI confirmation to proceed. The alternative
-    # is to parse the CLI output `softwareupdate -l`, whose format varies
-    # between OS releases. (See
-    # https://apple.stackexchange.com/questions/107307/how-can-i-install-the
-    # -command-line-tools-completely-from-the-command-line.) Or to re-introduce
-    # a separate pre-Ansible setup script, which I'd like to avoid.
-    #
-    # Which of {python, python3} are truly present and which are shims vary from
-    # macOS version to macOS version. Ansible 2.10 worked fine, but 2.12 broke
-    # interpreter discovery, even with interpreter_python=auto_legacy. So we use
-    # a manual lookup table. See b/233103849 for more detail.
-    #
-    # Here are the macOS versions where /usr/bin/python is the real, non-shim Python:
-    macs_with_usr_bin_python:
-      children:
-        macos_10_12:
-          hosts:
-            skia-e-mac-[101:102]:
-        macos_10_13:
-          hosts:
-            build[19:20]-m3:
-            build[337:341]-a9:
-            skia-e-mac-[101:104]:
-            skia-e-mac-[233:234]:
-            skia-e-mac-236:
-            skia-e-mac-[333:335]:
-            v-mac-[155:156]-g576:
-        macos_10_14:
-          hosts:
-            skia-e-mac-[105:106]:
-            v-mac-[157:158]-g576:
-        macos_10_15:
-          hosts:
-            build336-a9:
-            mac-[140:145]-h525:
-            mac-[653:665]-g580:
-            skia-e-mac-[107:108]:
-            skia-e-mac-[230:232]:
-            skia-e-mac-[330:332]:
-        macos_11:
-          hosts:
-            skia-e-mac-235:
-            skia-e-mac-[237:238]:
-            skia-e-mac-[336:338]:
-        # Apple started shipping python3 (and omitting python 2) in 12.3 exactly.
-        macos_12_1:
-          hosts:
-            skia-e-mac-[109:113]:
-            skia-e-mac-[116:117]:
-            skia-e-mac-[239:240]:
-            skia-e-mac-[339:340]:
-      vars:
-        ansible_python_interpreter: /usr/bin/python
-
-    # Here are the macOS versions where /usr/bin/python3 is the real, non-shim Python:
-    macs_with_usr_bin_python3:
-      children:
-        macos_12_3:
-          hosts:
-            skia-e-mac-[114:115]:
-      vars:
-        ansible_python_interpreter: /usr/bin/python3
+        # Always use the Python we provide ourselves. Python shipped with the
+        # Mac until macOS 13, but the version varied, and some copies were
+        # actually decoys--not real Python interpreters but shims which launched
+        # xcode-select, which itself required GUI confirmation to proceed. In
+        # addition, Ansible 2.12 broke interpreter discovery (2.10 worked fine),
+        # even with interpreter_python=auto_legacy. So we keep it consistent and
+        # use the one we stick on the box. See b/233103849 for more detail.
+        ansible_python_interpreter: /usr/local/bin/python3
 
     all_win:
       children:
diff --git a/skolo/ansible/switchboard/mac.yml b/skolo/ansible/switchboard/mac.yml
index a119145..b398493 100644
--- a/skolo/ansible/switchboard/mac.yml
+++ b/skolo/ansible/switchboard/mac.yml
@@ -1,3 +1,7 @@
+- name: Ensure Python is around for Ansible to use.
+  # It stopped being included in macOS 13.
+  import_playbook: prepare-mac-for-ansible.yml
+
 - hosts: all_mac
   user: chrome-bot
   gather_facts: true
diff --git a/skolo/ansible/switchboard/prepare-mac-for-ansible.yml b/skolo/ansible/switchboard/prepare-mac-for-ansible.yml
new file mode 100644
index 0000000..764ba53
--- /dev/null
+++ b/skolo/ansible/switchboard/prepare-mac-for-ansible.yml
@@ -0,0 +1,48 @@
+# Install Python 3 so we can do more than issue 'raw' commands.
+#
+# You don't need to run this by hand; mac.yml will run it as necessary.
+- hosts: all_mac
+  gather_facts: false
+
+  pre_tasks:
+    - name: See if our version of Python is already installed.
+      raw: /usr/local/bin/python3 --version
+      register: local_python_version
+      failed_when: false  # Tolerate the binary not being there.
+      changed_when: false
+
+    - name: Install Python 3 and prerequisites.
+      when: local_python_version.stdout_lines[0] != "Python 3.9.6"
+      block:
+        - name: Check for Rosetta.
+          raw: test -e /Library/Apple/usr/libexec/oah/libRosettaRuntime
+          register: rosetta_present
+          failed_when: false
+          changed_when: false
+
+        - name: Check architecture.
+          raw: arch
+          register: cpu_arch
+          changed_when: false
+
+        - name: Install Rosetta on ARM machines if it's absent.
+          # The Python installer package isn't marked as being ARM-native, so it
+          # refuses to run unless Rosetta 2 is around. This is true up to and
+          # including the 3.10.1 universal2 installer.
+          become: true
+          when: rosetta_present.rc != 0 and cpu_arch.stdout_lines[0] == "arm64"
+          raw: softwareupdate --install-rosetta --agree-to-license
+
+        - name: Install Python 3.
+          vars:
+            # Contains universal binaries. Happily runs on macOS 10.9+, despite
+            # name.
+            python_pkg: python-3.9.6-macos11.pkg
+          block:
+            - name: Download Python installer.
+              # Works even if the file is already there:
+              raw: curl -O 'https://www.python.org/ftp/python/3.9.6/{{ python_pkg }}' --output-dir ~{{ skolo_account }}/Downloads
+
+            - name: Run installer.
+              become: true
+              raw: /usr/sbin/installer -verboseR -package ~{{ skolo_account }}/Downloads/{{ python_pkg }} -target /
diff --git a/skolo/ansible/switchboard/roles/swarming_needs/tasks/mac.yml b/skolo/ansible/switchboard/roles/swarming_needs/tasks/mac.yml
index d44ba52..f9e141e 100644
--- a/skolo/ansible/switchboard/roles/swarming_needs/tasks/mac.yml
+++ b/skolo/ansible/switchboard/roles/swarming_needs/tasks/mac.yml
@@ -18,34 +18,7 @@
     owner: '{{ skolo_account }}'
     mode: 0755
 
-- name: Install Rosetta on ARM machines if it's absent.
-  # The Python installer package isn't marked as being ARM-native, so it refuses
-  # to run unless Rosetta 2 is around. This is true as of the 3.10.1 universal2
-  # installer.
-  command:
-    cmd: softwareupdate --install-rosetta --agree-to-license
-    creates: /Library/Apple/usr/libexec/oah/libRosettaRuntime
-  when: ansible_facts['machine'] == 'arm64'
-
-- name: Install Python 3.
-  vars:
-    # Contains universal binaries. Happily runs on macOS 10.9+, despite name.
-    python_pkg: python-3.9.6-macos11.pkg
-  block:
-    - name: Download Python installer.
-      # Do this with curl instead of the get_url module because 10.12's Python
-      # has too old a version of TLS to work.
-      command:
-        cmd: curl -O 'https://www.python.org/ftp/python/3.9.6/{{ python_pkg }}'
-        chdir: ~{{ skolo_account }}/Downloads
-        creates: ~{{ skolo_account }}/Downloads/{{ python_pkg }}
-        warn: false
-
-    - name: Run installer.
-      become: true
-      command:
-        cmd: /usr/sbin/installer -verboseR -package ~{{ skolo_account }}/Downloads/{{ python_pkg }} -target /
-        creates: /usr/local/bin/python3
+# Python 3.9.6 is taken to be already present, since it's installed by prepare-mac-for-ansible.yml.
 
 - name: Install pyobjc.
   block: