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

|

|
||||||
|
|
||||||
*Tart* is a virtualization toolset to build, run and manage virtual machines on Apple Silicon.
|
*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:
|
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
|
tart run monterey-base
|
||||||
```
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## CI Integration
|
## CI Integration
|
||||||
|
|
||||||
[Cirrus CLI](https://github.com/cirruslabs/cirrus-cli) is an open-sourced CI-agnostic tool that can run workloads
|
[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:
|
macos_instance:
|
||||||
# can be a remote or a local virtual machine
|
# can be a remote or a local virtual machine
|
||||||
image: ghcr.io/cirruslabs/macos-monterey-base:latest
|
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:
|
Run it locally or in CI with the following command:
|
||||||
@@ -38,6 +44,8 @@ brew install cirruslabs/cli/cirrus
|
|||||||
cirrus run
|
cirrus run
|
||||||
```
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
[Cirrus CI](https://cirrus-ci.org/) already leverages Tart to power its macOS cloud infrastructure. The `.cirrus.yml`
|
[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.
|
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
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:46046004d63f9b4b7de3d5d3f9ca14367a9e4b0a124b4b0b65225f67a0d0fb45
|
oid sha256:3a43f541b1ab0b57ae2060d371cba5dbb1f5c80b89c76434b7154d8144f66e61
|
||||||
size 1266887
|
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 {
|
struct TokenResponse: Decodable {
|
||||||
|
let creationTime = Date()
|
||||||
|
|
||||||
var token: String
|
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 {
|
class Registry {
|
||||||
var baseURL: URL
|
var baseURL: URL
|
||||||
var namespace: String
|
var namespace: String
|
||||||
|
|
||||||
var token: String? = nil
|
var currentAuthToken: TokenResponse? = nil
|
||||||
|
|
||||||
init(host: String, namespace: String) throws {
|
init(host: String, namespace: String) throws {
|
||||||
var baseURLComponents = URLComponents()
|
var baseURLComponents = URLComponents()
|
||||||
@@ -147,6 +170,11 @@ class Registry {
|
|||||||
}
|
}
|
||||||
request.httpBody = body
|
request.httpBody = body
|
||||||
|
|
||||||
|
// Invalidate token if it has expired
|
||||||
|
if currentAuthToken?.isValid == false {
|
||||||
|
currentAuthToken = nil
|
||||||
|
}
|
||||||
|
|
||||||
var (data, response) = try await authAwareRequest(request: request)
|
var (data, response) = try await authAwareRequest(request: request)
|
||||||
|
|
||||||
if response.statusCode == 401 {
|
if response.statusCode == 401 {
|
||||||
@@ -199,20 +227,20 @@ class Registry {
|
|||||||
headers["Authorization"] = "Basic \(encodedCredentials!)"
|
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 {
|
if response.statusCode != 200 {
|
||||||
throw RegistryError.AuthFailed(why: "received unexpected HTTP status code \(response.statusCode) "
|
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) {
|
private func authAwareRequest(request: URLRequest) async throws -> (Data, HTTPURLResponse) {
|
||||||
var request = request
|
var request = request
|
||||||
|
|
||||||
if let token = self.token {
|
if let token = currentAuthToken {
|
||||||
request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
request.addValue("Bearer \(token.token)", forHTTPHeaderField: "Authorization")
|
||||||
}
|
}
|
||||||
|
|
||||||
let (responseData, response) = try await URLSession.shared.data(for: request)
|
let (responseData, response) = try await URLSession.shared.data(for: request)
|
||||||
|
|||||||
Reference in New Issue
Block a user