Migrate CI from Cirrus CI to GitHub Actions (#447)

* Migrate CI to GitHub Actions

* Publish Orchard through the OpenAI Homebrew tap

* Scope release secrets to GoReleaser steps

* Separate release and snapshot trust paths

* Use GitHub-hosted macOS 26 runner

* Run macOS unit tests on hosted runner

* Update Homebrew install command

* Build Docker image with GoReleaser

* Use GitHub-hosted Linux runner

* Use smaller Vetu image for Linux tests

* Run Linux tests on hosted ARM runner

* Use host architecture for Linux integration tests

* Preinstall Cloud Hypervisor for Linux tests

* Select Linux test images by architecture

* Satisfy platform image lint rules

* Format platform image errors

* Enable KVM access for Linux tests

* Honor VM wait time in WebSocket requests

* Satisfy WebSocket client lint rules

* Allow hosted Linux integration tests more time
This commit is contained in:
Fedor Kororkov
2026-07-16 17:57:06 -04:00
committed by GitHub
parent b898769396
commit 461af8de47
18 changed files with 451 additions and 243 deletions
+32 -2
View File
@@ -13,6 +13,7 @@ import (
"net"
"net/http"
"net/url"
"strconv"
"time"
"github.com/cirruslabs/orchard/internal/config"
@@ -25,6 +26,11 @@ import (
"google.golang.org/grpc/metadata"
)
const (
defaultHTTPTimeout = 30 * time.Second
waitParameterName = "wait"
)
type APIError struct {
StatusCode int
}
@@ -107,7 +113,7 @@ func New(opts ...Option) (*Client, error) {
// the requests may hang indefinitely. See [1] for more details.
//
// [1]: https://github.com/cirruslabs/orchard/issues/152#issuecomment-1927091747
Timeout: 30 * time.Second,
Timeout: defaultHTTPTimeout,
Transport: transport,
}
@@ -315,7 +321,7 @@ func (client *Client) wsRequestRaw(
endpointURL.RawQuery = values.Encode()
dialOptions := &websocket.DialOptions{
HTTPClient: client.httpClient,
HTTPClient: client.httpClientForWebSocket(params),
HTTPHeader: make(http.Header),
}
@@ -397,3 +403,27 @@ func (client *Client) RPC() *RPCService {
client: client,
}
}
func (client *Client) httpClientForWebSocket(params map[string]string) *http.Client {
waitRaw, ok := params[waitParameterName]
if !ok {
return client.httpClient
}
waitSeconds, err := strconv.ParseUint(waitRaw, 10, 16)
if err != nil {
return client.httpClient
}
waitTimeout := time.Duration(waitSeconds)*time.Second + defaultHTTPTimeout
if client.httpClient.Timeout == 0 || client.httpClient.Timeout >= waitTimeout {
return client.httpClient
}
return &http.Client{
CheckRedirect: client.httpClient.CheckRedirect,
Jar: client.httpClient.Jar,
Timeout: waitTimeout,
Transport: client.httpClient.Transport,
}
}
+25 -7
View File
@@ -4,21 +4,39 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/coder/websocket"
"github.com/stretchr/testify/require"
)
func TestHTTPClientForWebSocketHonorsWait(t *testing.T) {
devClient, err := New(WithAddress("http://localhost"))
require.NoError(t, err)
httpClient := devClient.httpClientForWebSocket(map[string]string{waitParameterName: "120"})
require.Equal(t, 150*time.Second, httpClient.Timeout)
require.Same(t, devClient.httpClient.Transport, httpClient.Transport)
}
func TestExecSessionBuildsReconnectableQuery(t *testing.T) {
var query map[string][]string
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
query = request.URL.Query()
server := httptest.NewServer(
http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
query = request.URL.Query()
conn, err := websocket.Accept(writer, request, nil)
require.NoError(t, err)
defer conn.CloseNow()
}))
conn, err := websocket.Accept(writer, request, nil)
if err != nil {
t.Errorf("failed to accept WebSocket connection: %v", err)
return
}
defer conn.CloseNow()
}),
)
defer server.Close()
devClient, err := New(WithAddress(server.URL))
@@ -45,6 +63,6 @@ func TestExecSessionBuildsReconnectableQuery(t *testing.T) {
require.Equal(t, []string{"80"}, query["cols"])
require.Equal(t, []string{"hello"}, query["env[GREETING]"])
require.Equal(t, []string{"/tmp"}, query["workdir"])
require.Equal(t, []string{"7"}, query["wait"])
require.Equal(t, []string{"7"}, query[waitParameterName])
require.Equal(t, []string{"resume-me"}, query["session"])
}