From 049feeca76188ed36c3feda0b6b79cd82817a94c Mon Sep 17 00:00:00 2001 From: "gcp-cherry-pick-bot[bot]" <98988430+gcp-cherry-pick-bot[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 10:14:31 -0500 Subject: [PATCH 1/8] fix: handle paths with spaces in Match exec clause of SSH config (cherry-pick #18266) (#18778) Co-authored-by: Spike Curtis fixes #18199 --- cli/configssh.go | 28 +++++++++------ cli/configssh_internal_test.go | 63 ++++++++++++++++++++++++++++++++-- cli/configssh_other.go | 31 +++++++++++++++++ cli/configssh_windows.go | 53 ++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 14 deletions(-) diff --git a/cli/configssh.go b/cli/configssh.go index e3e168d2b198c..5aec0d5035e15 100644 --- a/cli/configssh.go +++ b/cli/configssh.go @@ -112,14 +112,19 @@ func (o sshConfigOptions) equal(other sshConfigOptions) bool { } func (o sshConfigOptions) writeToBuffer(buf *bytes.Buffer) error { - escapedCoderBinary, err := sshConfigExecEscape(o.coderBinaryPath, o.forceUnixSeparators) + escapedCoderBinaryProxy, err := sshConfigProxyCommandEscape(o.coderBinaryPath, o.forceUnixSeparators) if err != nil { - return xerrors.Errorf("escape coder binary for ssh failed: %w", err) + return xerrors.Errorf("escape coder binary for ProxyCommand failed: %w", err) } - escapedGlobalConfig, err := sshConfigExecEscape(o.globalConfigPath, o.forceUnixSeparators) + escapedCoderBinaryMatchExec, err := sshConfigMatchExecEscape(o.coderBinaryPath) if err != nil { - return xerrors.Errorf("escape global config for ssh failed: %w", err) + return xerrors.Errorf("escape coder binary for Match exec failed: %w", err) + } + + escapedGlobalConfig, err := sshConfigProxyCommandEscape(o.globalConfigPath, o.forceUnixSeparators) + if err != nil { + return xerrors.Errorf("escape global config for ProxyCommand failed: %w", err) } rootFlags := fmt.Sprintf("--global-config %s", escapedGlobalConfig) @@ -155,7 +160,7 @@ func (o sshConfigOptions) writeToBuffer(buf *bytes.Buffer) error { _, _ = buf.WriteString("\t") _, _ = fmt.Fprintf(buf, "ProxyCommand %s %s ssh --stdio%s --ssh-host-prefix %s %%h", - escapedCoderBinary, rootFlags, flags, o.userHostPrefix, + escapedCoderBinaryProxy, rootFlags, flags, o.userHostPrefix, ) _, _ = buf.WriteString("\n") } @@ -174,11 +179,11 @@ func (o sshConfigOptions) writeToBuffer(buf *bytes.Buffer) error { // the ^^ options should always apply, but we only want to use the proxy command if Coder Connect is not running. if !o.skipProxyCommand { _, _ = fmt.Fprintf(buf, "\nMatch host *.%s !exec \"%s connect exists %%h\"\n", - o.hostnameSuffix, escapedCoderBinary) + o.hostnameSuffix, escapedCoderBinaryMatchExec) _, _ = buf.WriteString("\t") _, _ = fmt.Fprintf(buf, "ProxyCommand %s %s ssh --stdio%s --hostname-suffix %s %%h", - escapedCoderBinary, rootFlags, flags, o.hostnameSuffix, + escapedCoderBinaryProxy, rootFlags, flags, o.hostnameSuffix, ) _, _ = buf.WriteString("\n") } @@ -759,7 +764,8 @@ func sshConfigSplitOnCoderSection(data []byte) (before, section []byte, after [] return data, nil, nil, nil } -// sshConfigExecEscape quotes the string if it contains spaces, as per +// sshConfigProxyCommandEscape prepares the path for use in ProxyCommand. +// It quotes the string if it contains spaces, as per // `man 5 ssh_config`. However, OpenSSH uses exec in the users shell to // run the command, and as such the formatting/escape requirements // cannot simply be covered by `fmt.Sprintf("%q", path)`. @@ -804,7 +810,7 @@ func sshConfigSplitOnCoderSection(data []byte) (before, section []byte, after [] // This is a control flag, and that is ok. It is a control flag // based on the OS of the user. Making this a different file is excessive. // nolint:revive -func sshConfigExecEscape(path string, forceUnixPath bool) (string, error) { +func sshConfigProxyCommandEscape(path string, forceUnixPath bool) (string, error) { if forceUnixPath { // This is a workaround for #7639, where the filepath separator is // incorrectly the Windows separator (\) instead of the unix separator (/). @@ -814,9 +820,9 @@ func sshConfigExecEscape(path string, forceUnixPath bool) (string, error) { // This is unlikely to ever happen, but newlines are allowed on // certain filesystems, but cannot be used inside ssh config. if strings.ContainsAny(path, "\n") { - return "", xerrors.Errorf("invalid path: %s", path) + return "", xerrors.Errorf("invalid path: %q", path) } - // In the unlikely even that a path contains quotes, they must be + // In the unlikely event that a path contains quotes, they must be // escaped so that they are not interpreted as shell quotes. if strings.Contains(path, "\"") { path = strings.ReplaceAll(path, "\"", "\\\"") diff --git a/cli/configssh_internal_test.go b/cli/configssh_internal_test.go index d3eee395de0a3..acf534e7ae157 100644 --- a/cli/configssh_internal_test.go +++ b/cli/configssh_internal_test.go @@ -139,7 +139,7 @@ func Test_sshConfigSplitOnCoderSection(t *testing.T) { // This test tries to mimic the behavior of OpenSSH // when executing e.g. a ProxyCommand. // nolint:tparallel -func Test_sshConfigExecEscape(t *testing.T) { +func Test_sshConfigProxyCommandEscape(t *testing.T) { t.Parallel() tests := []struct { @@ -171,7 +171,7 @@ func Test_sshConfigExecEscape(t *testing.T) { err = os.WriteFile(bin, contents, 0o755) //nolint:gosec require.NoError(t, err) - escaped, err := sshConfigExecEscape(bin, false) + escaped, err := sshConfigProxyCommandEscape(bin, false) if tt.wantErr { require.Error(t, err) return @@ -186,6 +186,63 @@ func Test_sshConfigExecEscape(t *testing.T) { } } +// This test tries to mimic the behavior of OpenSSH +// when executing e.g. a match exec command. +// nolint:tparallel +func Test_sshConfigMatchExecEscape(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + path string + wantErrOther bool + wantErrWindows bool + }{ + {"no spaces", "simple", false, false}, + {"spaces", "path with spaces", false, false}, + {"quotes", "path with \"quotes\"", true, true}, + {"backslashes", "path with\\backslashes", false, false}, + {"tabs", "path with \ttabs", false, true}, + {"newline fails", "path with \nnewline", true, true}, + } + // nolint:paralleltest // Fixes a flake + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + cmd := "/bin/sh" + arg := "-c" + contents := []byte("#!/bin/sh\necho yay\n") + if runtime.GOOS == "windows" { + cmd = "cmd.exe" + arg = "/c" + contents = []byte("@echo yay\n") + } + + dir := filepath.Join(t.TempDir(), tt.path) + bin := filepath.Join(dir, "coder.bat") // Windows will treat it as batch, Linux doesn't care + escaped, err := sshConfigMatchExecEscape(bin) + if (runtime.GOOS == "windows" && tt.wantErrWindows) || (runtime.GOOS != "windows" && tt.wantErrOther) { + require.Error(t, err) + return + } + require.NoError(t, err) + + err = os.MkdirAll(dir, 0o755) + require.NoError(t, err) + + err = os.WriteFile(bin, contents, 0o755) //nolint:gosec + require.NoError(t, err) + + // OpenSSH processes %% escape sequences into % + escaped = strings.ReplaceAll(escaped, "%%", "%") + b, err := exec.Command(cmd, arg, escaped).CombinedOutput() //nolint:gosec + require.NoError(t, err) + got := strings.TrimSpace(string(b)) + require.Equal(t, "yay", got) + }) + } +} + func Test_sshConfigExecEscapeSeparatorForce(t *testing.T) { t.Parallel() @@ -236,7 +293,7 @@ func Test_sshConfigExecEscapeSeparatorForce(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - found, err := sshConfigExecEscape(tt.path, tt.forceUnix) + found, err := sshConfigProxyCommandEscape(tt.path, tt.forceUnix) if tt.wantErr { require.Error(t, err) return diff --git a/cli/configssh_other.go b/cli/configssh_other.go index fde7cc0e47e63..07417487e8c8f 100644 --- a/cli/configssh_other.go +++ b/cli/configssh_other.go @@ -2,4 +2,35 @@ package cli +import ( + "strings" + + "golang.org/x/xerrors" +) + var hideForceUnixSlashes = true + +// sshConfigMatchExecEscape prepares the path for use in `Match exec` statement. +// +// OpenSSH parses the Match line with a very simple tokenizer that accepts "-enclosed strings for the exec command, and +// has no supported escape sequences for ". This means we cannot include " within the command to execute. +func sshConfigMatchExecEscape(path string) (string, error) { + // This is unlikely to ever happen, but newlines are allowed on + // certain filesystems, but cannot be used inside ssh config. + if strings.ContainsAny(path, "\n") { + return "", xerrors.Errorf("invalid path: %s", path) + } + // Quotes are allowed in path names on unix-like file systems, but OpenSSH's parsing of `Match exec` doesn't allow + // them. + if strings.Contains(path, `"`) { + return "", xerrors.Errorf("path must not contain quotes: %q", path) + } + + // OpenSSH passes the match exec string directly to the user's shell. sh, bash and zsh accept spaces, tabs and + // backslashes simply escaped by a `\`. It's hard to predict exactly what more exotic shells might do, but this + // should work for macOS and most Linux distros in their default configuration. + path = strings.ReplaceAll(path, `\`, `\\`) // must be first, since later replacements add backslashes. + path = strings.ReplaceAll(path, " ", "\\ ") + path = strings.ReplaceAll(path, "\t", "\\\t") + return path, nil +} diff --git a/cli/configssh_windows.go b/cli/configssh_windows.go index 642a388fc873c..5df0d6b50c00e 100644 --- a/cli/configssh_windows.go +++ b/cli/configssh_windows.go @@ -2,5 +2,58 @@ package cli +import ( + "fmt" + "strings" + + "golang.org/x/xerrors" +) + // Must be a var for unit tests to conform behavior var hideForceUnixSlashes = false + +// sshConfigMatchExecEscape prepares the path for use in `Match exec` statement. +// +// OpenSSH parses the Match line with a very simple tokenizer that accepts "-enclosed strings for the exec command, and +// has no supported escape sequences for ". This means we cannot include " within the command to execute. +// +// To make matters worse, on Windows, OpenSSH passes the string directly to cmd.exe for execution, and as far as I can +// tell, the only supported way to call a path that has spaces in it is to surround it with ". +// +// So, we can't actually include " directly, but here is a horrible workaround: +// +// "for /f %%a in ('powershell.exe -Command [char]34') do @cmd.exe /c %%aC:\Program Files\Coder\bin\coder.exe%%a connect exists %h" +// +// The key insight here is to store the character " in a variable (%a in this case, but the % itself needs to be +// escaped, so it becomes %%a), and then use that variable to construct the double-quoted path: +// +// %%aC:\Program Files\Coder\bin\coder.exe%%a. +// +// How do we generate a single " character without actually using that character? I couldn't find any command in cmd.exe +// to do it, but powershell.exe can convert ASCII to characters like this: `[char]34` (where 34 is the code point for "). +// +// Other notes: +// - @ in `@cmd.exe` suppresses echoing it, so you don't get this command printed +// - we need another invocation of cmd.exe (e.g. `do @cmd.exe /c %%aC:\Program Files\Coder\bin\coder.exe%%a`). Without +// it the double-quote gets interpreted as part of the path, and you get: '"C:\Program' is not recognized. +// Constructing the string and then passing it to another instance of cmd.exe does this trick here. +// - OpenSSH passes the `Match exec` command to cmd.exe regardless of whether the user has a unix-like shell like +// git bash, so we don't have a `forceUnixPath` option like for the ProxyCommand which does respect the user's +// configured shell on Windows. +func sshConfigMatchExecEscape(path string) (string, error) { + // This is unlikely to ever happen, but newlines are allowed on + // certain filesystems, but cannot be used inside ssh config. + if strings.ContainsAny(path, "\n") { + return "", xerrors.Errorf("invalid path: %s", path) + } + // Windows does not allow double-quotes or tabs in paths. If we get one it is an error. + if strings.ContainsAny(path, "\"\t") { + return "", xerrors.Errorf("path must not contain quotes or tabs: %q", path) + } + + if strings.ContainsAny(path, " ") { + // c.f. function comment for how this works. + path = fmt.Sprintf("for /f %%%%a in ('powershell.exe -Command [char]34') do @cmd.exe /c %%%%a%s%%%%a", path) //nolint:gocritic // We don't want %q here. + } + return path, nil +} From a7f0dba4c3d0c523f686c1d985537fa86787a58d Mon Sep 17 00:00:00 2001 From: "gcp-cherry-pick-bot[bot]" <98988430+gcp-cherry-pick-bot[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 10:43:57 +0500 Subject: [PATCH 2/8] fix(cli): handle nil unwrap errors when formatting (cherry-pick #18099) (#18821) Co-authored-by: Ethan <39577870+ethanndickson@users.noreply.github.com> --- cli/root.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cli/root.go b/cli/root.go index 8fec1a945b0b3..22a1c0f3ac329 100644 --- a/cli/root.go +++ b/cli/root.go @@ -1060,11 +1060,12 @@ func cliHumanFormatError(from string, err error, opts *formatOpts) (string, bool return formatRunCommandError(cmdErr, opts), true } - uw, ok := err.(interface{ Unwrap() error }) - if ok { - msg, special := cliHumanFormatError(from+traceError(err), uw.Unwrap(), opts) - if special { - return msg, special + if uw, ok := err.(interface{ Unwrap() error }); ok { + if unwrapped := uw.Unwrap(); unwrapped != nil { + msg, special := cliHumanFormatError(from+traceError(err), unwrapped, opts) + if special { + return msg, special + } } } // If we got here, that means that the wrapped error chain does not have From 63155d2d0a5eccaa30618d6cd51dee3a1b1011cd Mon Sep 17 00:00:00 2001 From: "gcp-cherry-pick-bot[bot]" <98988430+gcp-cherry-pick-bot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 10:45:47 +0500 Subject: [PATCH 3/8] chore: add rdp icon (cherry-pick #18736) (#18738) Co-authored-by: Atif Ali --- site/src/theme/icons.json | 1 + site/static/icon/rdp.svg | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 site/static/icon/rdp.svg diff --git a/site/src/theme/icons.json b/site/src/theme/icons.json index b60dce9fbcab3..92961f775ff33 100644 --- a/site/src/theme/icons.json +++ b/site/src/theme/icons.json @@ -87,6 +87,7 @@ "pycharm.svg", "python.svg", "pytorch.svg", + "rdp.svg", "rider.svg", "rockylinux.svg", "rstudio.svg", diff --git a/site/static/icon/rdp.svg b/site/static/icon/rdp.svg new file mode 100644 index 0000000000000..a67223263ac1d --- /dev/null +++ b/site/static/icon/rdp.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 1c8ba5141068a609d44bd7521570d90457996c65 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Tue, 15 Jul 2025 18:23:02 +1000 Subject: [PATCH 4/8] cherry: feat: sign coder binaries with the release key using GPG (#18774) (#18867) (cherry picked from commit dc0919da332f27364969b9d5be63d8045ca0d993) Co-authored-by: Jakub Domeracki --- .github/workflows/ci.yaml | 2 ++ .github/workflows/release.yaml | 2 ++ Makefile | 4 +++ scripts/build_go.sh | 13 ++++++++ scripts/release/publish.sh | 21 ++---------- scripts/sign_with_gpg.sh | 59 ++++++++++++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 19 deletions(-) create mode 100755 scripts/sign_with_gpg.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 419291689473a..cc06557a8409a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1259,6 +1259,8 @@ jobs: # do (see above). CODER_SIGN_WINDOWS: "1" CODER_WINDOWS_RESOURCES: "1" + CODER_SIGN_GPG: "1" + CODER_GPG_RELEASE_KEY_BASE64: ${{ secrets.GPG_RELEASE_KEY_BASE64 }} EV_KEY: ${{ secrets.EV_KEY }} EV_KEYSTORE: ${{ secrets.EV_KEYSTORE }} EV_TSA_URL: ${{ secrets.EV_TSA_URL }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 881cc4c437db6..6396f1efeeec5 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -323,6 +323,8 @@ jobs: env: CODER_SIGN_WINDOWS: "1" CODER_SIGN_DARWIN: "1" + CODER_SIGN_GPG: "1" + CODER_GPG_RELEASE_KEY_BASE64: ${{ secrets.GPG_RELEASE_KEY_BASE64 }} CODER_WINDOWS_RESOURCES: "1" AC_CERTIFICATE_FILE: /tmp/apple_cert.p12 AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt diff --git a/Makefile b/Makefile index 0b8cefbab0663..070e8c738d8d2 100644 --- a/Makefile +++ b/Makefile @@ -250,6 +250,10 @@ $(CODER_ALL_BINARIES): go.mod go.sum \ fi cp "$@" "./site/out/bin/coder-$$os-$$arch$$dot_ext" + + if [[ "$${CODER_SIGN_GPG:-0}" == "1" ]]; then + cp "$@.asc" "./site/out/bin/coder-$$os-$$arch$$dot_ext.asc" + fi fi # This task builds Coder Desktop dylibs diff --git a/scripts/build_go.sh b/scripts/build_go.sh index 97d9431beb544..b3b074b183f91 100755 --- a/scripts/build_go.sh +++ b/scripts/build_go.sh @@ -20,6 +20,9 @@ # binary will be signed using ./sign_darwin.sh. Read that file for more details # on the requirements. # +# If the --sign-gpg parameter is specified, the output binary will be signed using ./sign_with_gpg.sh. +# Read that file for more details on the requirements. +# # If the --agpl parameter is specified, builds only the AGPL-licensed code (no # Coder enterprise features). # @@ -41,6 +44,7 @@ slim="${CODER_SLIM_BUILD:-0}" agpl="${CODER_BUILD_AGPL:-0}" sign_darwin="${CODER_SIGN_DARWIN:-0}" sign_windows="${CODER_SIGN_WINDOWS:-0}" +sign_gpg="${CODER_SIGN_GPG:-0}" boringcrypto=${CODER_BUILD_BORINGCRYPTO:-0} dylib=0 windows_resources="${CODER_WINDOWS_RESOURCES:-0}" @@ -85,6 +89,10 @@ while true; do sign_windows=1 shift ;; + --sign-gpg) + sign_gpg=1 + shift + ;; --boringcrypto) boringcrypto=1 shift @@ -319,4 +327,9 @@ if [[ "$sign_windows" == 1 ]] && [[ "$os" == "windows" ]]; then execrelative ./sign_windows.sh "$output_path" 1>&2 fi +# Platform agnostic signing +if [[ "$sign_gpg" == 1 ]]; then + execrelative ./sign_with_gpg.sh "$output_path" 1>&2 +fi + echo "$output_path" diff --git a/scripts/release/publish.sh b/scripts/release/publish.sh index df28d46ad2710..5ffd40aeb65cb 100755 --- a/scripts/release/publish.sh +++ b/scripts/release/publish.sh @@ -129,26 +129,9 @@ if [[ "$dry_run" == 0 ]] && [[ "${CODER_GPG_RELEASE_KEY_BASE64:-}" != "" ]]; the log "--- Signing checksums file" log - # Import the GPG key. - old_gnupg_home="${GNUPGHOME:-}" - gnupg_home_temp="$(mktemp -d)" - export GNUPGHOME="$gnupg_home_temp" - echo "$CODER_GPG_RELEASE_KEY_BASE64" | base64 -d | gpg --import 1>&2 - - # Sign the checksums file. This generates a file in the same directory and - # with the same name as the checksums file but ending in ".asc". - # - # We pipe `true` into `gpg` so that it never tries to be interactive (i.e. - # ask for a passphrase). The key we import above is not password protected. - true | gpg --detach-sign --armor "${temp_dir}/${checksum_file}" 1>&2 - - rm -rf "$gnupg_home_temp" - unset GNUPGHOME - if [[ "$old_gnupg_home" != "" ]]; then - export GNUPGHOME="$old_gnupg_home" - fi - + execrelative ../sign_with_gpg.sh "${temp_dir}/${checksum_file}" signed_checksum_path="${temp_dir}/${checksum_file}.asc" + if [[ ! -e "$signed_checksum_path" ]]; then log "Signed checksum file not found: ${signed_checksum_path}" log diff --git a/scripts/sign_with_gpg.sh b/scripts/sign_with_gpg.sh new file mode 100755 index 0000000000000..fb75df5ca1bb9 --- /dev/null +++ b/scripts/sign_with_gpg.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +# This script signs a given binary using GPG. +# It expects the binary to be signed as the first argument. +# +# Usage: ./sign_with_gpg.sh path/to/binary +# +# On success, the input file will be signed using the GPG key and the signature output file will moved to /site/out/bin/ (happens in the Makefile) +# +# Depends on the GPG utility. Requires the following environment variables to be set: +# - $CODER_GPG_RELEASE_KEY_BASE64: The base64 encoded private key to use. + +set -euo pipefail +# shellcheck source=scripts/lib.sh +source "$(dirname "${BASH_SOURCE[0]}")/lib.sh" + +requiredenvs CODER_GPG_RELEASE_KEY_BASE64 + +FILE_TO_SIGN="$1" + +if [[ -z "$FILE_TO_SIGN" ]]; then + error "Usage: $0 " +fi + +if [[ ! -f "$FILE_TO_SIGN" ]]; then + error "File not found: $FILE_TO_SIGN" +fi + +# Import the GPG key. +old_gnupg_home="${GNUPGHOME:-}" +gnupg_home_temp="$(mktemp -d)" +export GNUPGHOME="$gnupg_home_temp" + +# Ensure GPG uses the temporary directory +echo "$CODER_GPG_RELEASE_KEY_BASE64" | base64 -d | gpg --homedir "$gnupg_home_temp" --import 1>&2 + +# Sign the binary. This generates a file in the same directory and +# with the same name as the binary but ending in ".asc". +# +# We pipe `true` into `gpg` so that it never tries to be interactive (i.e. +# ask for a passphrase). The key we import above is not password protected. +true | gpg --homedir "$gnupg_home_temp" --detach-sign --armor "$FILE_TO_SIGN" 1>&2 + +# Verify the signature and capture the exit status +gpg --homedir "$gnupg_home_temp" --verify "${FILE_TO_SIGN}.asc" "$FILE_TO_SIGN" 1>&2 +verification_result=$? + +# Clean up the temporary GPG home +rm -rf "$gnupg_home_temp" +unset GNUPGHOME +if [[ "$old_gnupg_home" != "" ]]; then + export GNUPGHOME="$old_gnupg_home" +fi + +if [[ $verification_result -eq 0 ]]; then + echo "${FILE_TO_SIGN}.asc" +else + error "Signature verification failed!" +fi From 54d0575fde47925754b6d7fd24bf3b4ec36833f6 Mon Sep 17 00:00:00 2001 From: Jakub Domeracki Date: Wed, 16 Jul 2025 14:15:26 +0200 Subject: [PATCH 5/8] chore: publish CLI binaries and detached signatures to releases.coder.com (#18900) Cherry pick (https://github.com/coder/coder/commit/e4d3453e2b55edfc5a9650083f4bffc765423b1c) Starting with version `2.24.X `, Coder CLI binaries & corresponding detached signatures will get published to the GCS bucket releases.coder.com. --- .github/workflows/release.yaml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 6396f1efeeec5..2cddf0d0a5bde 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -634,6 +634,29 @@ jobs: - name: ls build run: ls -lh build + - name: Publish Coder CLI binaries and detached signatures to GCS + if: ${{ !inputs.dry_run && github.ref == 'refs/heads/main' && github.repository_owner == 'coder'}} + run: | + set -euxo pipefail + + version="$(./scripts/version.sh)" + + binaries=( + "coder-darwin-amd64" + "coder-darwin-arm64" + "coder-linux-amd64" + "coder-linux-arm64" + "coder-linux-armv7" + "coder-windows-amd64.exe" + "coder-windows-arm64.exe" + ) + + for binary in "${binaries[@]}"; do + detached_signature="${binary}.asc" + gcloud storage cp "./site/out/bin/${binary}" "gs://releases.coder.com/coder-cli/${version}/${binary}" + gcloud storage cp "./site/out/bin/${detached_signature}" "gs://releases.coder.com/coder-cli/${version}/${detached_signature}" + done + - name: Publish release run: | set -euo pipefail From 580081c76f34b627ff733f188970163d186b1742 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Thu, 7 Aug 2025 15:13:36 +0400 Subject: [PATCH 6/8] fix: upgrade to 1.24.6 to fix race in lib/pq queries (#19214) (#19220) THIS IS A SECURITY FIX - cherry picks #19214 upgrade to go 1.24.6 to avoid https://github.com/golang/go/issues/74831 (CVE-2025-47907) Also points to a new version of our lib/pq fork that worked around the Go issue, which should restore better performance. --------- Co-authored-by: Cian Johnston Co-authored-by: Ethan <39577870+ethanndickson@users.noreply.github.com> --- .github/actions/setup-go/action.yaml | 2 +- .github/workflows/ci.yaml | 7 ++++++- .github/workflows/release.yaml | 4 ++-- dogfood/coder/Dockerfile | 2 +- go.mod | 4 ++-- go.sum | 4 ++-- 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/actions/setup-go/action.yaml b/.github/actions/setup-go/action.yaml index 6656ba5d06490..097a1b6cfd119 100644 --- a/.github/actions/setup-go/action.yaml +++ b/.github/actions/setup-go/action.yaml @@ -4,7 +4,7 @@ description: | inputs: version: description: "The Go version to use." - default: "1.24.2" + default: "1.24.6" use-preinstalled-go: description: "Whether to use preinstalled Go." default: "false" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index cc06557a8409a..330bf9e7d9c11 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -428,6 +428,11 @@ jobs: - name: Disable Spotlight Indexing if: runner.os == 'macOS' run: | + enabled=$(sudo mdutil -a -s | grep "Indexing enabled" | wc -l) + if [ $enabled -eq 0 ]; then + echo "Spotlight indexing is already disabled" + exit 0 + fi sudo mdutil -a -i off sudo mdutil -X / sudo launchctl bootout system /System/Library/LaunchDaemons/com.apple.metadata.mds.plist @@ -1082,7 +1087,7 @@ jobs: - name: Switch XCode Version uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0 with: - xcode-version: "16.0.0" + xcode-version: "16.1.0" - name: Setup Go uses: ./.github/actions/setup-go diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 2cddf0d0a5bde..6b5532b5fd6e9 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -60,7 +60,7 @@ jobs: - name: Switch XCode Version uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0 with: - xcode-version: "16.0.0" + xcode-version: "16.1.0" - name: Setup Go uses: ./.github/actions/setup-go @@ -655,7 +655,7 @@ jobs: detached_signature="${binary}.asc" gcloud storage cp "./site/out/bin/${binary}" "gs://releases.coder.com/coder-cli/${version}/${binary}" gcloud storage cp "./site/out/bin/${detached_signature}" "gs://releases.coder.com/coder-cli/${version}/${detached_signature}" - done + done - name: Publish release run: | diff --git a/dogfood/coder/Dockerfile b/dogfood/coder/Dockerfile index cc9122c74c5cf..83852226e8ef3 100644 --- a/dogfood/coder/Dockerfile +++ b/dogfood/coder/Dockerfile @@ -9,7 +9,7 @@ RUN cargo install jj-cli typos-cli watchexec-cli FROM ubuntu:jammy@sha256:0e5e4a57c2499249aafc3b40fcd541e9a456aab7296681a3994d631587203f97 AS go # Install Go manually, so that we can control the version -ARG GO_VERSION=1.24.2 +ARG GO_VERSION=1.24.6 # Boring Go is needed to build FIPS-compliant binaries. RUN apt-get update && \ diff --git a/go.mod b/go.mod index 1bc98d5f01b26..143aef98edfa8 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/coder/coder/v2 -go 1.24.2 +go 1.24.6 // Required until a v3 of chroma is created to lazily initialize all XML files. // None of our dependencies seem to use the registries anyways, so this @@ -58,7 +58,7 @@ replace github.com/imulab/go-scim/pkg/v2 => github.com/coder/go-scim/pkg/v2 v2.0 // Adds support for a new Listener from a driver.Connector // This lets us use rotating authentication tokens for passwords in connection strings // which we use in the awsiamrds package. -replace github.com/lib/pq => github.com/coder/pq v1.10.5-0.20240813183442-0c420cb5a048 +replace github.com/lib/pq => github.com/coder/pq v1.10.5-0.20250807075151-6ad9b0a25151 // Removes an init() function that causes terminal sequences to be printed to the web terminal when // used in conjunction with agent-exec. See https://github.com/coder/coder/pull/15817 diff --git a/go.sum b/go.sum index ff82f4db0ec17..e1c51c9c5f9df 100644 --- a/go.sum +++ b/go.sum @@ -907,8 +907,8 @@ github.com/coder/go-scim/pkg/v2 v2.0.0-20230221055123-1d63c1222136 h1:0RgB61LcNs github.com/coder/go-scim/pkg/v2 v2.0.0-20230221055123-1d63c1222136/go.mod h1:VkD1P761nykiq75dz+4iFqIQIZka189tx1BQLOp0Skc= github.com/coder/guts v1.5.0 h1:a94apf7xMf5jDdg1bIHzncbRiTn3+BvBZgrFSDbUnyI= github.com/coder/guts v1.5.0/go.mod h1:0Sbv5Kp83u1Nl7MIQiV2zmacJ3o02I341bkWkjWXSUQ= -github.com/coder/pq v1.10.5-0.20240813183442-0c420cb5a048 h1:3jzYUlGH7ZELIH4XggXhnTnP05FCYiAFeQpoN+gNR5I= -github.com/coder/pq v1.10.5-0.20240813183442-0c420cb5a048/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/coder/pq v1.10.5-0.20250807075151-6ad9b0a25151 h1:YAxwg3lraGNRwoQ18H7R7n+wsCqNve7Brdvj0F1rDnU= +github.com/coder/pq v1.10.5-0.20250807075151-6ad9b0a25151/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc= github.com/coder/preview v0.0.2-0.20250527172548-ab173d35040c h1:lPIImqcf46QcK3hYlr20xt2SG66IAAK/kfZdEhM6OJc= From 9eaecf1425e5ea54cbdeb1c029dc2a5f42421303 Mon Sep 17 00:00:00 2001 From: Jakub Domeracki Date: Thu, 7 Aug 2025 17:17:03 +0200 Subject: [PATCH 7/8] chore: fix CLI binary publishing for releases.coder.com (#19229) --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 6b5532b5fd6e9..c1af70dc0a7c6 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -635,7 +635,7 @@ jobs: run: ls -lh build - name: Publish Coder CLI binaries and detached signatures to GCS - if: ${{ !inputs.dry_run && github.ref == 'refs/heads/main' && github.repository_owner == 'coder'}} + if: ${{ !inputs.dry_run }} run: | set -euxo pipefail From de62750d7fc128deb7fc8416d3751ca4e55215a8 Mon Sep 17 00:00:00 2001 From: Jakub Domeracki Date: Thu, 7 Aug 2025 18:06:03 +0200 Subject: [PATCH 8/8] chore: revert CLI binary publishing for releases.coder.com (#19234) --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c1af70dc0a7c6..6b5532b5fd6e9 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -635,7 +635,7 @@ jobs: run: ls -lh build - name: Publish Coder CLI binaries and detached signatures to GCS - if: ${{ !inputs.dry_run }} + if: ${{ !inputs.dry_run && github.ref == 'refs/heads/main' && github.repository_owner == 'coder'}} run: | set -euxo pipefail pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy