From c194b963d559de7b1de86ad0f4ca2df6645175de Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Fri, 30 May 2025 13:33:41 +1000
Subject: [PATCH 01/11] ci: update appcast on builds

---
 .github/workflows/release.yml             | 39 +++++++++++++++++++++++
 flake.nix                                 |  1 +
 scripts/update-appcast/Sources/main.swift |  6 ++--
 3 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index adbc130d..98db4e0c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -46,6 +46,14 @@ jobs:
       - name: Setup Nix
         uses: ./.github/actions/nix-devshell
 
+      - name: Authenticate to Google Cloud
+        id: gcloud_auth
+        uses: google-github-actions/auth@71f986410dfbc7added4569d411d040a91dc6935 # v2.1.8
+        with:
+          workload_identity_provider: ${{ secrets.GCP_WORKLOAD_ID_PROVIDER }}
+          service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
+          token_format: "access_token"
+
       - name: Build
         env:
           APPLE_DEVELOPER_ID_PKCS12_B64: ${{ secrets.APPLE_DEVELOPER_ID_PKCS12_B64 }}
@@ -68,6 +76,11 @@ jobs:
           path: ${{ github.workspace }}/outputs/out
           retention-days: 7
 
+      - name: Get version string
+        id: version_string
+        run: |
+          echo "versionstring=$(./scripts/version.sh)" >> "$GITHUB_OUTPUT"
+
       # Upload to release in non-dry-run mode
       - name: Upload Release Assets
         if: ${{ !inputs.dryrun }}
@@ -76,6 +89,32 @@ jobs:
           GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || 'preview' }}
 
+      - name: Download Appcast
+        # if: ${{ !inputs.dryrun }}
+        run: |
+         gsutil cp "gs://releases.coder.com/coder-desktop/mac/appcast.xml" ./oldappcast.xml
+
+      - name: Update Appcast
+        # if: ${{ !inputs.dryrun }}
+        run: |
+          pushd scripts/update-appcast
+          swift run update-appcast \
+            -i ../../oldappcast.xml \
+            -s "$out"/Coder-Desktop.pkg.sig \
+            -v "$VERSION_STRING" \
+            -o ../../appcast.xml
+            -d "$VERSION_DESCRIPTION"
+          popd
+        env:
+          VERSION_STRING: ${{ steps.version_string.outputs.versionstring }}
+          VERSION_DESCRIPTION: ${{ github.event_name == 'release' && github.event.release.body || '' }}
+
+
+      - name: Upload Appcast
+        # if: ${{ !inputs.dryrun }}
+        run: |
+          gsutil -h "Cache-Control:no-cache,max-age=0" cp ./appcast.xml "gs://releases.coder.com/coder-desktop/mac/appcast.xml"
+
   update-cask:
     name: Update homebrew-coder cask
     runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest'}}
diff --git a/flake.nix b/flake.nix
index ab3ab0a1..1d67c4c2 100644
--- a/flake.nix
+++ b/flake.nix
@@ -49,6 +49,7 @@
                 gh
                 git
                 gnumake
+                google-cloud-sdk
                 protobuf_28
                 grpc-swift.packages.${system}.protoc-gen-grpc-swift
                 grpc-swift.packages.${system}.protoc-gen-swift
diff --git a/scripts/update-appcast/Sources/main.swift b/scripts/update-appcast/Sources/main.swift
index 27cd7109..d546003f 100644
--- a/scripts/update-appcast/Sources/main.swift
+++ b/scripts/update-appcast/Sources/main.swift
@@ -68,7 +68,7 @@ struct UpdateAppcast: AsyncParsableCommand {
         }
 
         let xmlData = try Data(contentsOf: URL(fileURLWithPath: input))
-        let doc = try XMLDocument(data: xmlData, options: .nodePrettyPrint)
+        let doc = try XMLDocument(data: xmlData, options: [.nodePrettyPrint, .nodePreserveAll])
 
         guard let channelElem = try doc.nodes(forXPath: "/rss/channel").first as? XMLElement else {
             throw RuntimeError("<channel> element not found in appcast.")
@@ -98,7 +98,7 @@ struct UpdateAppcast: AsyncParsableCommand {
             item.addChild(XMLElement(name: "title", stringValue: "Preview"))
         }
 
-        if let description {
+        if let description, !description.isEmpty {
             let description = description.replacingOccurrences(of: #"\r\n"#, with: "\n")
             let descriptionDoc: Document
             do {
@@ -143,7 +143,7 @@ struct UpdateAppcast: AsyncParsableCommand {
 
         channelElem.insertChild(item, at: insertionIndex)
 
-        let outputStr = doc.xmlString(options: [.nodePrettyPrint]) + "\n"
+        let outputStr = doc.xmlString(options: [.nodePrettyPrint, .nodePreserveAll]) + "\n"
         try outputStr.write(to: URL(fileURLWithPath: output), atomically: true, encoding: .utf8)
     }
 

From 02b3b7dc36b7751564697f5c041ffed0d144f11a Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Fri, 30 May 2025 13:38:08 +1000
Subject: [PATCH 02/11] fixup

---
 .github/workflows/release.yml | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 98db4e0c..dc4829d9 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -76,11 +76,6 @@ jobs:
           path: ${{ github.workspace }}/outputs/out
           retention-days: 7
 
-      - name: Get version string
-        id: version_string
-        run: |
-          echo "versionstring=$(./scripts/version.sh)" >> "$GITHUB_OUTPUT"
-
       # Upload to release in non-dry-run mode
       - name: Upload Release Assets
         if: ${{ !inputs.dryrun }}
@@ -101,12 +96,11 @@ jobs:
           swift run update-appcast \
             -i ../../oldappcast.xml \
             -s "$out"/Coder-Desktop.pkg.sig \
-            -v "$VERSION_STRING" \
+            -v "$(../version.sh)" \
             -o ../../appcast.xml
             -d "$VERSION_DESCRIPTION"
           popd
         env:
-          VERSION_STRING: ${{ steps.version_string.outputs.versionstring }}
           VERSION_DESCRIPTION: ${{ github.event_name == 'release' && github.event.release.body || '' }}
 
 

From 820f63b21b3864a8427d0abeb9752368eb1ec1c8 Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Fri, 30 May 2025 13:51:42 +1000
Subject: [PATCH 03/11] gcp auth perms

---
 .github/workflows/release.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index dc4829d9..ab2b168c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -30,6 +30,8 @@ jobs:
     permissions:
       # To upload assets to the release
       contents: write
+      # for GCP auth
+      id-token: write
     steps:
       - name: Checkout
         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

From 0a69ec6fe344cdc1c91ab6689681cc02b4ebdc5a Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Fri, 30 May 2025 15:52:19 +1000
Subject: [PATCH 04/11] fix nix

---
 flake.nix | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/flake.nix b/flake.nix
index 1d67c4c2..eebc3911 100644
--- a/flake.nix
+++ b/flake.nix
@@ -60,6 +60,14 @@
                 xcpretty
                 zizmor
               ];
+              shellHook = ''
+                # Copied from https://github.com/ghostty-org/ghostty/blob/c4088f0c73af1c153c743fc006637cc76c1ee127/nix/devShell.nix#L189-L199
+                # We want to rely on the system Xcode tools in CI!
+                unset SDKROOT
+                unset DEVELOPER_DIR
+                # We need to remove the nix "xcrun" from the PATH.
+                export PATH=$(echo "$PATH" | awk -v RS=: -v ORS=: '$0 !~ /xcrun/ || $0 == "/usr/bin" {print}' | sed 's/:$//')
+              '';
             };
 
             default = pkgs.mkShellNoCC {

From 2c791770110d083217a08b677bdc8e88c37630b1 Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Fri, 30 May 2025 16:13:57 +1000
Subject: [PATCH 05/11] macos 14...

---
 scripts/update-appcast/Package.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/update-appcast/Package.swift b/scripts/update-appcast/Package.swift
index 6f12df29..aa6a53e0 100644
--- a/scripts/update-appcast/Package.swift
+++ b/scripts/update-appcast/Package.swift
@@ -6,7 +6,7 @@ import PackageDescription
 let package = Package(
     name: "update-appcast",
     platforms: [
-        .macOS(.v15),
+        .macOS(.v14),
     ],
     dependencies: [
         .package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"),

From d6c671870470ace825500e87750132152aadb3b5 Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Fri, 30 May 2025 17:03:23 +1000
Subject: [PATCH 06/11] retry

---
 .github/workflows/release.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ab2b168c..f1171ecb 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -99,7 +99,7 @@ jobs:
             -i ../../oldappcast.xml \
             -s "$out"/Coder-Desktop.pkg.sig \
             -v "$(../version.sh)" \
-            -o ../../appcast.xml
+            -o ../../appcast.xml \
             -d "$VERSION_DESCRIPTION"
           popd
         env:

From 9ce6ae72c909381b6b9601b6a8ef1e09f40df2d8 Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Fri, 30 May 2025 17:32:11 +1000
Subject: [PATCH 07/11] trial

---
 .github/workflows/release.yml | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f1171ecb..2cb908a6 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -56,6 +56,16 @@ jobs:
           service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
           token_format: "access_token"
 
+      - name: Download Appcast
+        # if: ${{ !inputs.dryrun }}
+        run: |
+         gsutil cp "gs://releases.coder.com/coder-desktop/mac/appcast.xml" ./appcast.xml
+
+      - name: Upload Appcast
+        # if: ${{ !inputs.dryrun }}
+        run: |
+          gsutil -h "Cache-Control:no-cache,max-age=0" cp ./appcast.xml "gs://releases.coder.com/coder-desktop/mac/appcast.xml"
+
       - name: Build
         env:
           APPLE_DEVELOPER_ID_PKCS12_B64: ${{ secrets.APPLE_DEVELOPER_ID_PKCS12_B64 }}

From f235bc68f72f8d5c66d2fc9d9f230caa0b134c41 Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Fri, 30 May 2025 18:26:00 +1000
Subject: [PATCH 08/11] trial

---
 .github/workflows/release.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 2cb908a6..32804b28 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -64,6 +64,8 @@ jobs:
       - name: Upload Appcast
         # if: ${{ !inputs.dryrun }}
         run: |
+          gcloud auth login
+          gcloud auth application-default login
           gsutil -h "Cache-Control:no-cache,max-age=0" cp ./appcast.xml "gs://releases.coder.com/coder-desktop/mac/appcast.xml"
 
       - name: Build

From 6cd6de71baad8b40e52a1ab4536e494bcb2b0b1b Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Fri, 30 May 2025 18:36:31 +1000
Subject: [PATCH 09/11] trial

---
 .github/workflows/release.yml | 5 +++--
 flake.nix                     | 1 -
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 32804b28..4422cd75 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -56,6 +56,9 @@ jobs:
           service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
           token_format: "access_token"
 
+      - name: Setup GCloud SDK
+        uses: google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a # v2.1.4
+
       - name: Download Appcast
         # if: ${{ !inputs.dryrun }}
         run: |
@@ -64,8 +67,6 @@ jobs:
       - name: Upload Appcast
         # if: ${{ !inputs.dryrun }}
         run: |
-          gcloud auth login
-          gcloud auth application-default login
           gsutil -h "Cache-Control:no-cache,max-age=0" cp ./appcast.xml "gs://releases.coder.com/coder-desktop/mac/appcast.xml"
 
       - name: Build
diff --git a/flake.nix b/flake.nix
index eebc3911..10af339f 100644
--- a/flake.nix
+++ b/flake.nix
@@ -49,7 +49,6 @@
                 gh
                 git
                 gnumake
-                google-cloud-sdk
                 protobuf_28
                 grpc-swift.packages.${system}.protoc-gen-grpc-swift
                 grpc-swift.packages.${system}.protoc-gen-swift

From c0c28ad30b1b43a29812d13483d631ed560c5f85 Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Fri, 30 May 2025 18:58:59 +1000
Subject: [PATCH 10/11] finish

---
 .github/workflows/release.yml | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4422cd75..8a415c3a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -59,16 +59,6 @@ jobs:
       - name: Setup GCloud SDK
         uses: google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a # v2.1.4
 
-      - name: Download Appcast
-        # if: ${{ !inputs.dryrun }}
-        run: |
-         gsutil cp "gs://releases.coder.com/coder-desktop/mac/appcast.xml" ./appcast.xml
-
-      - name: Upload Appcast
-        # if: ${{ !inputs.dryrun }}
-        run: |
-          gsutil -h "Cache-Control:no-cache,max-age=0" cp ./appcast.xml "gs://releases.coder.com/coder-desktop/mac/appcast.xml"
-
       - name: Build
         env:
           APPLE_DEVELOPER_ID_PKCS12_B64: ${{ secrets.APPLE_DEVELOPER_ID_PKCS12_B64 }}
@@ -100,12 +90,12 @@ jobs:
           RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || 'preview' }}
 
       - name: Download Appcast
-        # if: ${{ !inputs.dryrun }}
+        if: ${{ !inputs.dryrun }}
         run: |
          gsutil cp "gs://releases.coder.com/coder-desktop/mac/appcast.xml" ./oldappcast.xml
 
       - name: Update Appcast
-        # if: ${{ !inputs.dryrun }}
+        if: ${{ !inputs.dryrun }}
         run: |
           pushd scripts/update-appcast
           swift run update-appcast \
@@ -120,7 +110,7 @@ jobs:
 
 
       - name: Upload Appcast
-        # if: ${{ !inputs.dryrun }}
+        if: ${{ !inputs.dryrun }}
         run: |
           gsutil -h "Cache-Control:no-cache,max-age=0" cp ./appcast.xml "gs://releases.coder.com/coder-desktop/mac/appcast.xml"
 

From b9822b81bba2f3befc623ac00eecb63042ce3a8b Mon Sep 17 00:00:00 2001
From: Ethan Dickson <ethan@coder.com>
Date: Mon, 2 Jun 2025 15:23:33 +1000
Subject: [PATCH 11/11] merge 3 steps

---
 .github/workflows/release.yml | 13 ++-----------
 1 file changed, 2 insertions(+), 11 deletions(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 8a415c3a..484d89e6 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -89,14 +89,10 @@ jobs:
           GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || 'preview' }}
 
-      - name: Download Appcast
-        if: ${{ !inputs.dryrun }}
-        run: |
-         gsutil cp "gs://releases.coder.com/coder-desktop/mac/appcast.xml" ./oldappcast.xml
-
       - name: Update Appcast
         if: ${{ !inputs.dryrun }}
         run: |
+          gsutil cp "gs://releases.coder.com/coder-desktop/mac/appcast.xml" ./oldappcast.xml
           pushd scripts/update-appcast
           swift run update-appcast \
             -i ../../oldappcast.xml \
@@ -105,15 +101,10 @@ jobs:
             -o ../../appcast.xml \
             -d "$VERSION_DESCRIPTION"
           popd
+          gsutil -h "Cache-Control:no-cache,max-age=0" cp ./appcast.xml "gs://releases.coder.com/coder-desktop/mac/appcast.xml"
         env:
           VERSION_DESCRIPTION: ${{ github.event_name == 'release' && github.event.release.body || '' }}
 
-
-      - name: Upload Appcast
-        if: ${{ !inputs.dryrun }}
-        run: |
-          gsutil -h "Cache-Control:no-cache,max-age=0" cp ./appcast.xml "gs://releases.coder.com/coder-desktop/mac/appcast.xml"
-
   update-cask:
     name: Update homebrew-coder cask
     runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest'}}