From a994819625b87258992654586ce8f9a89940b78c Mon Sep 17 00:00:00 2001 From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com> Date: Sun, 8 Nov 2026 22:28:10 -0800 Subject: [PATCH] fix(ci): restore hosted macOS DNS after fleet enrollment --- .github/actions/setup-bazel/action.yml | 15 +++++++++ .github/actions/setup-bazel/macos_dns.py | 42 ++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 .github/actions/setup-bazel/macos_dns.py diff --git a/.github/actions/setup-bazel/action.yml b/.github/actions/setup-bazel/action.yml index 26e0225862..c6fae999fe 100644 --- a/.github/actions/setup-bazel/action.yml +++ b/.github/actions/setup-bazel/action.yml @@ -173,6 +173,12 @@ runs: sudo chmod 0440 /etc/sudoers.d/tailscale-ci-network sudo visudo -cf /etc/sudoers.d/tailscale-ci-network + - name: Read the hosted Mac's Ethernet resolver settings + id: macos-dns + if: runner.os == 'macOS' && inputs.tailscale-client-id != '' + shell: bash + run: python3 "$GITHUB_ACTION_PATH/macos_dns.py" read >> "$GITHUB_OUTPUT" + - name: Connect to the private Bazel fleet id: tailscale if: inputs.tailscale-client-id != '' @@ -187,6 +193,15 @@ runs: args: --accept-dns=false ping: ubuntu-main + # The pinned Tailscale action explicitly reconfigures Ethernet when MagicDNS + # is enabled on macOS, even when tailscale up receives --accept-dns=false. + - name: Restore the hosted Mac's Ethernet resolver settings + if: always() && steps.macos-dns.outcome == 'success' + shell: bash + env: + ETHERNET_DNS: ${{ steps.macos-dns.outputs.settings }} + run: python3 "$GITHUB_ACTION_PATH/macos_dns.py" restore + - name: Select private executor or hosted execution id: fleet shell: bash diff --git a/.github/actions/setup-bazel/macos_dns.py b/.github/actions/setup-bazel/macos_dns.py new file mode 100644 index 0000000000..0e84723aa4 --- /dev/null +++ b/.github/actions/setup-bazel/macos_dns.py @@ -0,0 +1,42 @@ +"""Preserve the Ethernet settings changed by the Tailscale macOS action.""" + +import json +import os +import subprocess +import sys + + +# networksetup returns one line per entry, or a sentence when DHCP supplies it. +SETTINGS = {"dnsservers": "DNS Servers", "searchdomains": "Search Domains"} + + +def read_settings(service): + settings = {} + for option, label in SETTINGS.items(): + value = subprocess.check_output( + ["networksetup", "-get" + option, service], + text=True, + env={**os.environ, "LC_ALL": "C"}, + ).strip() + if not value: + raise ValueError(f"networksetup returned no result for {option}") + automatic = f"There aren't any {label} set on {service}." + settings[option] = [] if value == automatic else value.splitlines() + return settings + + +def restore_settings(service, settings): + for option in SETTINGS: + subprocess.run( + ["networksetup", "-set" + option, service, *(settings[option] or ["Empty"])], + check=True, + ) + + +if __name__ == "__main__": + if sys.argv[1:] == ["read"]: + print("settings=" + json.dumps(read_settings("Ethernet"))) + elif sys.argv[1:] == ["restore"]: + restore_settings("Ethernet", json.loads(os.environ["ETHERNET_DNS"])) + else: + sys.exit("expected read or restore")