mirror of
https://github.com/cirruslabs/tart.git
synced 2026-10-02 04:01:12 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f5a6790d8 | ||
|
|
cc697b4f6e | ||
|
|
f6acbe8fe5 | ||
|
|
341fd168b3 | ||
|
|
e8a6efa60a |
+1
-2
@@ -1,7 +1,6 @@
|
||||
persistent_worker:
|
||||
labels:
|
||||
os: darwin
|
||||
arch: arm64
|
||||
name: Mac-Mini-M1
|
||||
|
||||
task:
|
||||
name: Test
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.gif filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
@@ -12,9 +12,6 @@ builds:
|
||||
before:
|
||||
hooks:
|
||||
- swift build -c release --product tart
|
||||
|
||||
after:
|
||||
hooks:
|
||||
- codesign --sign - --entitlements Resources/tart.entitlements --force .build/arm64-apple-macosx/release/tart
|
||||
|
||||
archives:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
*Tart* is a virtualization toolset to build, run and manage virtual machines on Apple Silicon.
|
||||
Built by CI engineers for your automation needs. Here are some highlights of Tart:
|
||||
@@ -16,6 +16,8 @@ tart clone ghcr.io/cirruslabs/macos-monterey-base:latest monterey-base
|
||||
tart run monterey-base
|
||||
```
|
||||
|
||||

|
||||
|
||||
## CI Integration
|
||||
|
||||
[Cirrus CLI](https://github.com/cirruslabs/cirrus-cli) is an open-sourced CI-agnostic tool that can run workloads
|
||||
@@ -28,7 +30,11 @@ task:
|
||||
macos_instance:
|
||||
# can be a remote or a local virtual machine
|
||||
image: ghcr.io/cirruslabs/macos-monterey-base:latest
|
||||
script: echo "Hello from within a Tart VM!"
|
||||
hello_script:
|
||||
- echo "Hello from within a Tart VM!"
|
||||
- echo "Here is my CPU info:"
|
||||
- sysctl -n machdep.cpu.brand_string
|
||||
- sleep 15
|
||||
```
|
||||
|
||||
Run it locally or in CI with the following command:
|
||||
@@ -38,6 +44,8 @@ brew install cirruslabs/cli/cirrus
|
||||
cirrus run
|
||||
```
|
||||
|
||||

|
||||
|
||||
[Cirrus CI](https://cirrus-ci.org/) already leverages Tart to power its macOS cloud infrastructure. The `.cirrus.yml`
|
||||
config from above will just work in Cirrus CI and your tasks will be executed inside Tart VMs in our cloud.
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8a3a324193c4bd7797102765ab16f44adf58e49ca615bac3963cefd0d3a10594
|
||||
size 339678
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:46046004d63f9b4b7de3d5d3f9ca14367a9e4b0a124b4b0b65225f67a0d0fb45
|
||||
size 1266887
|
||||
oid sha256:3a43f541b1ab0b57ae2060d371cba5dbb1f5c80b89c76434b7154d8144f66e61
|
||||
size 205325
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:769dcd5411f44071cb46f63459118fef728c866a581cf94a28811f407ca9827d
|
||||
size 606936
|
||||
@@ -8,14 +8,37 @@ enum RegistryError: Error {
|
||||
}
|
||||
|
||||
struct TokenResponse: Decodable {
|
||||
let creationTime = Date()
|
||||
|
||||
var token: String
|
||||
var expires_in: Int?
|
||||
|
||||
var tokenExpiresAt: Date {
|
||||
get {
|
||||
// Tokens can expire and expire_in field is used to determine when:
|
||||
//
|
||||
// >The duration in seconds since the token was issued that it will remain valid.
|
||||
// >When omitted, this defaults to 60 seconds. For compatibility with older clients,
|
||||
// >a token should never be returned with less than 60 seconds to live.
|
||||
//
|
||||
// [1]: https://docs.docker.com/registry/spec/auth/token/#requesting-a-token
|
||||
|
||||
creationTime + TimeInterval(expires_in ?? 60)
|
||||
}
|
||||
}
|
||||
|
||||
var isValid: Bool {
|
||||
get {
|
||||
Date() < tokenExpiresAt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Registry {
|
||||
var baseURL: URL
|
||||
var namespace: String
|
||||
|
||||
var token: String? = nil
|
||||
var currentAuthToken: TokenResponse? = nil
|
||||
|
||||
init(host: String, namespace: String) throws {
|
||||
var baseURLComponents = URLComponents()
|
||||
@@ -147,6 +170,11 @@ class Registry {
|
||||
}
|
||||
request.httpBody = body
|
||||
|
||||
// Invalidate token if it has expired
|
||||
if currentAuthToken?.isValid == false {
|
||||
currentAuthToken = nil
|
||||
}
|
||||
|
||||
var (data, response) = try await authAwareRequest(request: request)
|
||||
|
||||
if response.statusCode == 401 {
|
||||
@@ -199,20 +227,20 @@ class Registry {
|
||||
headers["Authorization"] = "Basic \(encodedCredentials!)"
|
||||
}
|
||||
|
||||
let (responseData, response) = try await rawRequest("GET", authenticateURL, headers: headers)
|
||||
let (tokenResponseRaw, response) = try await rawRequest("GET", authenticateURL, headers: headers)
|
||||
if response.statusCode != 200 {
|
||||
throw RegistryError.AuthFailed(why: "received unexpected HTTP status code \(response.statusCode) "
|
||||
+ "while retrieving an authentication token", details: String(decoding: responseData, as: UTF8.self))
|
||||
+ "while retrieving an authentication token", details: String(decoding: tokenResponseRaw, as: UTF8.self))
|
||||
}
|
||||
|
||||
token = try JSONDecoder().decode(TokenResponse.self, from: responseData).token
|
||||
currentAuthToken = try JSONDecoder().decode(TokenResponse.self, from: tokenResponseRaw)
|
||||
}
|
||||
|
||||
private func authAwareRequest(request: URLRequest) async throws -> (Data, HTTPURLResponse) {
|
||||
var request = request
|
||||
|
||||
if let token = self.token {
|
||||
request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
||||
if let token = currentAuthToken {
|
||||
request.addValue("Bearer \(token.token)", forHTTPHeaderField: "Authorization")
|
||||
}
|
||||
|
||||
let (responseData, response) = try await URLSession.shared.data(for: request)
|
||||
|
||||
Reference in New Issue
Block a user