@limrun/api 0.41.1 → 0.42.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/client.d.mts +6 -2
- package/client.d.mts.map +1 -1
- package/client.d.ts +6 -2
- package/client.d.ts.map +1 -1
- package/client.js +3 -0
- package/client.js.map +1 -1
- package/client.mjs +3 -0
- package/client.mjs.map +1 -1
- package/exec-client.d.mts +34 -1
- package/exec-client.d.mts.map +1 -1
- package/exec-client.d.ts +34 -1
- package/exec-client.d.ts.map +1 -1
- package/exec-client.js +18 -4
- package/exec-client.js.map +1 -1
- package/exec-client.mjs +18 -4
- package/exec-client.mjs.map +1 -1
- package/folder-sync-ignore.d.mts +8 -0
- package/folder-sync-ignore.d.mts.map +1 -1
- package/folder-sync-ignore.d.ts +8 -0
- package/folder-sync-ignore.d.ts.map +1 -1
- package/folder-sync-ignore.js +12 -4
- package/folder-sync-ignore.js.map +1 -1
- package/folder-sync-ignore.mjs +12 -4
- package/folder-sync-ignore.mjs.map +1 -1
- package/index.d.mts +3 -2
- package/index.d.mts.map +1 -1
- package/index.d.ts +3 -2
- package/index.d.ts.map +1 -1
- package/index.js.map +1 -1
- package/index.mjs.map +1 -1
- package/package.json +1 -1
- package/resources/daemon-client-shared.d.mts +37 -0
- package/resources/daemon-client-shared.d.mts.map +1 -0
- package/resources/daemon-client-shared.d.ts +37 -0
- package/resources/daemon-client-shared.d.ts.map +1 -0
- package/resources/daemon-client-shared.js +51 -0
- package/resources/daemon-client-shared.js.map +1 -0
- package/resources/daemon-client-shared.mjs +45 -0
- package/resources/daemon-client-shared.mjs.map +1 -0
- package/resources/gradle-instances-helpers.d.mts +54 -0
- package/resources/gradle-instances-helpers.d.mts.map +1 -0
- package/resources/gradle-instances-helpers.d.ts +54 -0
- package/resources/gradle-instances-helpers.d.ts.map +1 -0
- package/resources/gradle-instances-helpers.js +98 -0
- package/resources/gradle-instances-helpers.js.map +1 -0
- package/resources/gradle-instances-helpers.mjs +94 -0
- package/resources/gradle-instances-helpers.mjs.map +1 -0
- package/resources/gradle-instances.d.mts +149 -0
- package/resources/gradle-instances.d.mts.map +1 -0
- package/resources/gradle-instances.d.ts +149 -0
- package/resources/gradle-instances.d.ts.map +1 -0
- package/resources/gradle-instances.js +41 -0
- package/resources/gradle-instances.js.map +1 -0
- package/resources/gradle-instances.mjs +37 -0
- package/resources/gradle-instances.mjs.map +1 -0
- package/resources/index.d.mts +3 -1
- package/resources/index.d.mts.map +1 -1
- package/resources/index.d.ts +3 -1
- package/resources/index.d.ts.map +1 -1
- package/resources/index.js +3 -1
- package/resources/index.js.map +1 -1
- package/resources/index.mjs +1 -0
- package/resources/index.mjs.map +1 -1
- package/resources/xcode-instances-helpers.d.mts +43 -11
- package/resources/xcode-instances-helpers.d.mts.map +1 -1
- package/resources/xcode-instances-helpers.d.ts +43 -11
- package/resources/xcode-instances-helpers.d.ts.map +1 -1
- package/resources/xcode-instances-helpers.js +18 -41
- package/resources/xcode-instances-helpers.js.map +1 -1
- package/resources/xcode-instances-helpers.mjs +16 -38
- package/resources/xcode-instances-helpers.mjs.map +1 -1
- package/src/client.ts +31 -0
- package/src/exec-client.ts +55 -5
- package/src/folder-sync-ignore.ts +22 -6
- package/src/index.ts +12 -0
- package/src/resources/daemon-client-shared.ts +71 -0
- package/src/resources/gradle-instances-helpers.ts +155 -0
- package/src/resources/gradle-instances.ts +206 -0
- package/src/resources/index.ts +15 -0
- package/src/resources/xcode-instances-helpers.ts +81 -56
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Helpers and platform-neutral types shared by the per-instance build-daemon
|
|
2
|
+
// clients (xcode, gradle).
|
|
3
|
+
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import crypto from 'crypto';
|
|
7
|
+
|
|
8
|
+
export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Derives the client-side folder-sync cache location for a local project.
|
|
12
|
+
* The key format is a compatibility contract: changing it orphans every
|
|
13
|
+
* user's existing basis cache, so both daemon clients must share this one
|
|
14
|
+
* derivation.
|
|
15
|
+
*/
|
|
16
|
+
export function deriveBasisCache(
|
|
17
|
+
localCodePath: string,
|
|
18
|
+
override?: string,
|
|
19
|
+
): { cacheKey: string; basisCacheDir: string } {
|
|
20
|
+
const resolvedPath = path.resolve(localCodePath);
|
|
21
|
+
const folderName = path.basename(resolvedPath);
|
|
22
|
+
const hash = crypto.createHash('sha1').update(resolvedPath).digest('hex').slice(0, 8);
|
|
23
|
+
const cacheKey = `limsync-cache-${folderName}-${hash}`;
|
|
24
|
+
return { cacheKey, basisCacheDir: override ?? path.join(os.tmpdir(), cacheKey) };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type SyncResult = {
|
|
28
|
+
/**
|
|
29
|
+
* Number of bytes transmitted to the server for this sync (full uploads plus
|
|
30
|
+
* delta patches, before transport compression). In watch mode, this reflects
|
|
31
|
+
* the initial sync only.
|
|
32
|
+
*/
|
|
33
|
+
bytesSent?: number;
|
|
34
|
+
/** Present only when watch=true; call to stop watching */
|
|
35
|
+
stopWatching?: () => Promise<void>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type AssetUploadUrls = { signedUploadUrl: string; signedDownloadUrl: string };
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Mints presigned upload/download URLs for a named asset via assets.getOrCreate,
|
|
42
|
+
* wrapping failures with the asset name (and the original error as cause).
|
|
43
|
+
*/
|
|
44
|
+
export function mintAssetUploadUrls(
|
|
45
|
+
assets: { getOrCreate: (body: { name: string; ttl?: string }) => Promise<AssetUploadUrls> },
|
|
46
|
+
name: string,
|
|
47
|
+
ttl?: string,
|
|
48
|
+
): Promise<AssetUploadUrls> {
|
|
49
|
+
return assets.getOrCreate({ name, ...(ttl && { ttl }) }).catch((err) => {
|
|
50
|
+
const message = `Failed to create upload URL for asset '${name}': ${
|
|
51
|
+
err instanceof Error ? err.message : err
|
|
52
|
+
}`;
|
|
53
|
+
// @ts-ignore - not all envs have native support for cause yet
|
|
54
|
+
throw new Error(message, { cause: err });
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const logLevels: LogLevel[] = ['none', 'error', 'warn', 'info', 'debug'];
|
|
59
|
+
|
|
60
|
+
export function createDaemonLogger(prefix: string, logLevel: LogLevel) {
|
|
61
|
+
const threshold = logLevels.indexOf(logLevel);
|
|
62
|
+
const shouldLog = (level: LogLevel) => threshold >= logLevels.indexOf(level);
|
|
63
|
+
return (level: 'debug' | 'info' | 'warn' | 'error', msg: string) => {
|
|
64
|
+
if (!shouldLog(level)) return;
|
|
65
|
+
if (level === 'error' || level === 'warn') {
|
|
66
|
+
console[level](prefix, msg);
|
|
67
|
+
} else {
|
|
68
|
+
console.log(prefix, msg);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { GradleInstances as GeneratedGradleInstances, type GradleInstance } from './gradle-instances';
|
|
2
|
+
import {
|
|
3
|
+
exec,
|
|
4
|
+
type ExecChildProcess,
|
|
5
|
+
type GradleBuildExecRequest,
|
|
6
|
+
type GradleReactNativeConfig,
|
|
7
|
+
} from '../exec-client';
|
|
8
|
+
import { syncFolder as syncFolderImpl, type FolderSyncOptions } from '../folder-sync';
|
|
9
|
+
import { createIgnoreFn } from '../folder-sync-ignore';
|
|
10
|
+
import {
|
|
11
|
+
createDaemonLogger,
|
|
12
|
+
deriveBasisCache,
|
|
13
|
+
mintAssetUploadUrls,
|
|
14
|
+
type LogLevel,
|
|
15
|
+
type SyncResult,
|
|
16
|
+
} from './daemon-client-shared';
|
|
17
|
+
|
|
18
|
+
export type GradleCreateClientParams = { logLevel?: LogLevel } & (
|
|
19
|
+
| { instance: GradleInstance }
|
|
20
|
+
| { apiUrl: string; token: string }
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export type GradleSyncOptions = {
|
|
24
|
+
/**
|
|
25
|
+
* Directory for the client-side folder-sync cache.
|
|
26
|
+
* Defaults to a temporary directory under the OS temp directory.
|
|
27
|
+
*/
|
|
28
|
+
basisCacheDir?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Optional predicate for ignoring files and directories during sync.
|
|
31
|
+
* Called with the relative path from the sync root (using forward slashes).
|
|
32
|
+
* For directories, the path ends with '/'.
|
|
33
|
+
* Return true to ignore, false to keep.
|
|
34
|
+
*/
|
|
35
|
+
ignore?: (relativePath: string) => boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Optional predicate for force-including paths that would otherwise be
|
|
38
|
+
* dropped. Same calling convention as `ignore`; return true to force-sync.
|
|
39
|
+
*/
|
|
40
|
+
include?: (relativePath: string) => boolean;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type GradleBuildOptions = {
|
|
44
|
+
/** Gradle tasks to run. Omit for the server default (assembleDebug). */
|
|
45
|
+
tasks?: string[];
|
|
46
|
+
/** Relative path to the Gradle root when auto-discovery is ambiguous. */
|
|
47
|
+
projectPath?: string;
|
|
48
|
+
/** Upload the built APK as a named org asset, or to a presigned URL. */
|
|
49
|
+
upload?: { assetName: string; signedUploadUrl?: never } | { signedUploadUrl: string; assetName?: never };
|
|
50
|
+
/** React Native / Expo tuning. */
|
|
51
|
+
reactNative?: GradleReactNativeConfig;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type GradleClient = {
|
|
55
|
+
sync(localCodePath: string, opts?: GradleSyncOptions): Promise<SyncResult>;
|
|
56
|
+
gradlebuild(options?: GradleBuildOptions): ExecChildProcess;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Machine-local or regenerable files that must never reach the build
|
|
60
|
+
// sandbox: local.properties points at the developer's own SDK and would
|
|
61
|
+
// shadow the image's. Only ROOT-level cache/output dirs are defaulted;
|
|
62
|
+
// nested module build/ dirs are the project .gitignore's job (dropping any
|
|
63
|
+
// 'build' segment would silently exclude legitimate packages named build).
|
|
64
|
+
function gradleDefaultIgnore(relativePath: string): boolean {
|
|
65
|
+
const trimmed = relativePath.endsWith('/') ? relativePath.slice(0, -1) : relativePath;
|
|
66
|
+
const segments = trimmed.split('/');
|
|
67
|
+
if (segments[segments.length - 1] === 'local.properties') {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
return segments.length === 1 && (trimmed === '.gradle' || trimmed === '.kotlin' || trimmed === 'build');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class GradleInstances extends GeneratedGradleInstances {
|
|
74
|
+
async createClient(params: GradleCreateClientParams): Promise<GradleClient> {
|
|
75
|
+
let apiUrl: string;
|
|
76
|
+
let token: string;
|
|
77
|
+
if ('instance' in params) {
|
|
78
|
+
if (!params.instance.status.apiUrl) {
|
|
79
|
+
throw new Error('Instance not ready: apiUrl is not available');
|
|
80
|
+
}
|
|
81
|
+
apiUrl = params.instance.status.apiUrl;
|
|
82
|
+
token = params.instance.status.token;
|
|
83
|
+
} else {
|
|
84
|
+
apiUrl = params.apiUrl;
|
|
85
|
+
token = params.token;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const log = createDaemonLogger('[GradleInstance]', params.logLevel ?? 'info');
|
|
89
|
+
const client = this._client;
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
async sync(localCodePath: string, opts?: GradleSyncOptions): Promise<SyncResult> {
|
|
93
|
+
const { cacheKey, basisCacheDir } = deriveBasisCache(localCodePath, opts?.basisCacheDir);
|
|
94
|
+
const userIgnore = opts?.ignore;
|
|
95
|
+
const codeSyncOpts: FolderSyncOptions = {
|
|
96
|
+
apiUrl,
|
|
97
|
+
token,
|
|
98
|
+
udid: cacheKey,
|
|
99
|
+
install: false,
|
|
100
|
+
ignoreFn: await createIgnoreFn(localCodePath, {
|
|
101
|
+
basisCacheDir,
|
|
102
|
+
log,
|
|
103
|
+
xcodeDefaults: false,
|
|
104
|
+
// Honor nested module .gitignore files (app/.gitignore's /build,
|
|
105
|
+
// etc.); gradleDefaultIgnore only covers root-level build/.gradle/
|
|
106
|
+
// .kotlin, so without this a locally-built project would sync its
|
|
107
|
+
// nested build/ artifact trees.
|
|
108
|
+
nestedGitignore: true,
|
|
109
|
+
additional: userIgnore ? (p) => gradleDefaultIgnore(p) || userIgnore(p) : gradleDefaultIgnore,
|
|
110
|
+
...(opts?.include ? { include: opts.include } : {}),
|
|
111
|
+
}),
|
|
112
|
+
basisCacheDir,
|
|
113
|
+
// Gradle builds are one-shot; no dev-loop watch like xcode.
|
|
114
|
+
watch: false,
|
|
115
|
+
launchMode: 'ForegroundIfRunning',
|
|
116
|
+
log,
|
|
117
|
+
syncSymlinks: true,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const result = await syncFolderImpl(localCodePath, codeSyncOpts);
|
|
121
|
+
const out: SyncResult = {};
|
|
122
|
+
if (result.bytesSent !== undefined) {
|
|
123
|
+
out.bytesSent = result.bytesSent;
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
gradlebuild(options?: GradleBuildOptions): ExecChildProcess {
|
|
129
|
+
const request: GradleBuildExecRequest = {
|
|
130
|
+
command: 'gradlebuild',
|
|
131
|
+
...(options?.tasks?.length && { tasks: options.tasks }),
|
|
132
|
+
...(options?.projectPath && { projectPath: options.projectPath }),
|
|
133
|
+
...(options?.reactNative && { reactNative: options.reactNative }),
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
if (options?.upload && 'assetName' in options.upload) {
|
|
137
|
+
const requestPromise = mintAssetUploadUrls(client.assets, options.upload.assetName).then(
|
|
138
|
+
(asset) => {
|
|
139
|
+
request.signedUploadUrl = asset.signedUploadUrl;
|
|
140
|
+
request.additionalMetadata = { signedDownloadUrl: asset.signedDownloadUrl };
|
|
141
|
+
return request;
|
|
142
|
+
},
|
|
143
|
+
);
|
|
144
|
+
return exec(requestPromise, { apiUrl, token, log });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (options?.upload && 'signedUploadUrl' in options.upload) {
|
|
148
|
+
request.signedUploadUrl = options.upload.signedUploadUrl;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return exec(request, { apiUrl, token, log });
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// Hand-written following the Stainless resource pattern; a future generation
|
|
2
|
+
// from the OpenAPI spec reconciles with this file.
|
|
3
|
+
|
|
4
|
+
import { APIResource } from '../core/resource';
|
|
5
|
+
import { APIPromise } from '../core/api-promise';
|
|
6
|
+
import { Items, type ItemsParams, PagePromise } from '../core/pagination';
|
|
7
|
+
import { buildHeaders } from '../internal/headers';
|
|
8
|
+
import { RequestOptions } from '../internal/request-options';
|
|
9
|
+
import { path } from '../internal/utils/path';
|
|
10
|
+
|
|
11
|
+
export class GradleInstances extends APIResource {
|
|
12
|
+
/**
|
|
13
|
+
* Create a gradle instance
|
|
14
|
+
*/
|
|
15
|
+
create(params: GradleInstanceCreateParams, options?: RequestOptions): APIPromise<GradleInstance> {
|
|
16
|
+
const { reuseIfExists, wait, ...body } = params;
|
|
17
|
+
return this._client.post('/v1/gradle_instances', { query: { reuseIfExists, wait }, body, ...options });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* List gradle instances
|
|
22
|
+
*/
|
|
23
|
+
list(
|
|
24
|
+
query: GradleInstanceListParams | null | undefined = {},
|
|
25
|
+
options?: RequestOptions,
|
|
26
|
+
): PagePromise<GradleInstancesItems, GradleInstance> {
|
|
27
|
+
return this._client.getAPIList('/v1/gradle_instances', Items<GradleInstance>, { query, ...options });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Delete gradle instance with given name
|
|
32
|
+
*/
|
|
33
|
+
delete(id: string, options?: RequestOptions): APIPromise<void> {
|
|
34
|
+
return this._client.delete(path`/v1/gradle_instances/${id}`, {
|
|
35
|
+
...options,
|
|
36
|
+
headers: buildHeaders([{ Accept: '*/*' }, options?.headers]),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get gradle instance with given ID
|
|
42
|
+
*/
|
|
43
|
+
get(id: string, options?: RequestOptions): APIPromise<GradleInstance> {
|
|
44
|
+
return this._client.get(path`/v1/gradle_instances/${id}`, options);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type GradleInstancesItems = Items<GradleInstance>;
|
|
49
|
+
|
|
50
|
+
export interface GradleInstance {
|
|
51
|
+
metadata: GradleInstance.Metadata;
|
|
52
|
+
|
|
53
|
+
spec: GradleInstance.Spec;
|
|
54
|
+
|
|
55
|
+
status: GradleInstance.Status;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export namespace GradleInstance {
|
|
59
|
+
export interface Metadata {
|
|
60
|
+
id: string;
|
|
61
|
+
|
|
62
|
+
createdAt: string;
|
|
63
|
+
|
|
64
|
+
organizationId: string;
|
|
65
|
+
|
|
66
|
+
displayName?: string;
|
|
67
|
+
|
|
68
|
+
labels?: { [key: string]: string };
|
|
69
|
+
|
|
70
|
+
terminatedAt?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface Spec {
|
|
74
|
+
inactivityTimeout: string;
|
|
75
|
+
|
|
76
|
+
region: string;
|
|
77
|
+
|
|
78
|
+
hardTimeout?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface Status {
|
|
82
|
+
token: string;
|
|
83
|
+
|
|
84
|
+
state: 'unknown' | 'creating' | 'assigned' | 'ready' | 'terminated';
|
|
85
|
+
|
|
86
|
+
apiUrl?: string;
|
|
87
|
+
|
|
88
|
+
errorMessage?: string;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Machine-readable reason the instance was terminated. Always present once state
|
|
92
|
+
* is "terminated", never present before that. New values may be added over time,
|
|
93
|
+
* so treat any unrecognized value as "Unknown". Known values:
|
|
94
|
+
*
|
|
95
|
+
* - "UserRequested": terminated by a delete request to the API.
|
|
96
|
+
* - "InactivityTimeout": the timeout given in spec.inactivityTimeout elapsed.
|
|
97
|
+
* - "HardTimeout": the timeout given in spec.hardTimeout elapsed.
|
|
98
|
+
* - "Unknown": terminated for a cause the platform did not attribute, including
|
|
99
|
+
* instances that failed to get ready during creation. See errorMessage for
|
|
100
|
+
* details when available.
|
|
101
|
+
*/
|
|
102
|
+
terminationReason?: string;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface GradleInstanceCreateParams {
|
|
107
|
+
/**
|
|
108
|
+
* Query param: If there is another instance with given labels and region, return
|
|
109
|
+
* that one instead of creating a new instance.
|
|
110
|
+
*/
|
|
111
|
+
reuseIfExists?: boolean;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Query param: Return after the instance is ready to connect.
|
|
115
|
+
*/
|
|
116
|
+
wait?: boolean;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Body param
|
|
120
|
+
*/
|
|
121
|
+
metadata?: GradleInstanceCreateParams.Metadata;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Body param
|
|
125
|
+
*/
|
|
126
|
+
spec?: GradleInstanceCreateParams.Spec;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export namespace GradleInstanceCreateParams {
|
|
130
|
+
export interface Metadata {
|
|
131
|
+
displayName?: string;
|
|
132
|
+
|
|
133
|
+
labels?: { [key: string]: string };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface Spec {
|
|
137
|
+
clues?: Array<Spec.Clue>;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* After how many minutes should the instance be terminated. Example values 1m,
|
|
141
|
+
* 10m, 3h. Default is "0" which means no hard timeout.
|
|
142
|
+
*/
|
|
143
|
+
hardTimeout?: string;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* After how many minutes of inactivity should the instance be terminated. Example
|
|
147
|
+
* values 1m, 10m, 3h. Default is 5m.
|
|
148
|
+
*/
|
|
149
|
+
inactivityTimeout?: string;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Where the instance will be created. If not given, the region is decided based on
|
|
153
|
+
* scheduling clues (client IP) and availability.
|
|
154
|
+
*
|
|
155
|
+
* A region is a preference, not a hard pin: the request always overflows to every
|
|
156
|
+
* other available region, ordered by proximity, when the preferred ones are full.
|
|
157
|
+
*
|
|
158
|
+
* Accepted values:
|
|
159
|
+
*
|
|
160
|
+
* - A specific region name (e.g. "us-west1"). It is tried first, then the
|
|
161
|
+
* remaining regions in order of proximity to it. Scheduling clues (client IP)
|
|
162
|
+
* are ignored when a region is given.
|
|
163
|
+
* - A region group name (e.g. "us", "eu"). Its member regions are tried first in
|
|
164
|
+
* their listed order, then the remaining regions by proximity to the first
|
|
165
|
+
* member.
|
|
166
|
+
* - A pipe-separated, ordered list of regions (e.g. "us-east1|us-west1"). Those
|
|
167
|
+
* are tried first in the given order, then the remaining regions by proximity to
|
|
168
|
+
* the first.
|
|
169
|
+
*/
|
|
170
|
+
region?: string;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export namespace Spec {
|
|
174
|
+
export interface Clue {
|
|
175
|
+
kind: 'ClientIP';
|
|
176
|
+
|
|
177
|
+
clientIp?: string;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface GradleInstanceListParams extends ItemsParams {
|
|
183
|
+
/**
|
|
184
|
+
* Labels filter to apply to instances to return. Expects a comma-separated list of
|
|
185
|
+
* key=value pairs (e.g., env=prod,region=us-west).
|
|
186
|
+
*/
|
|
187
|
+
labelSelector?: string;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* State filter to apply to gradle instances to return. Each comma-separated state
|
|
191
|
+
* will be used as part of an OR clause, e.g. "assigned,ready" will return all
|
|
192
|
+
* instances that are either assigned or ready.
|
|
193
|
+
*
|
|
194
|
+
* Valid states: creating, assigned, ready, terminated, unknown
|
|
195
|
+
*/
|
|
196
|
+
state?: string;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export declare namespace GradleInstances {
|
|
200
|
+
export {
|
|
201
|
+
type GradleInstance as GradleInstance,
|
|
202
|
+
type GradleInstancesItems as GradleInstancesItems,
|
|
203
|
+
type GradleInstanceCreateParams as GradleInstanceCreateParams,
|
|
204
|
+
type GradleInstanceListParams as GradleInstanceListParams,
|
|
205
|
+
};
|
|
206
|
+
}
|
package/src/resources/index.ts
CHANGED
|
@@ -37,6 +37,12 @@ export {
|
|
|
37
37
|
type XcodeInstanceListParams,
|
|
38
38
|
type XcodeInstancesItems,
|
|
39
39
|
} from './xcode-instances';
|
|
40
|
+
export {
|
|
41
|
+
type GradleInstance,
|
|
42
|
+
type GradleInstanceCreateParams,
|
|
43
|
+
type GradleInstanceListParams,
|
|
44
|
+
type GradleInstancesItems,
|
|
45
|
+
} from './gradle-instances';
|
|
40
46
|
|
|
41
47
|
export { Assets, AssetGetOrUploadParams, AssetGetOrUploadResponse } from './assets-helpers';
|
|
42
48
|
export {
|
|
@@ -56,4 +62,13 @@ export {
|
|
|
56
62
|
type SimulatorDeviceInfo,
|
|
57
63
|
type SimulatorInstallState,
|
|
58
64
|
type RbeInstallResult,
|
|
65
|
+
type XcodeBuildLog,
|
|
66
|
+
type BazelBuildLog,
|
|
59
67
|
} from './xcode-instances-helpers';
|
|
68
|
+
export {
|
|
69
|
+
GradleInstances,
|
|
70
|
+
type GradleCreateClientParams,
|
|
71
|
+
type GradleClient,
|
|
72
|
+
type GradleSyncOptions,
|
|
73
|
+
type GradleBuildOptions,
|
|
74
|
+
} from './gradle-instances-helpers';
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import os from 'os';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import crypto from 'crypto';
|
|
4
|
-
|
|
5
1
|
import { XcodeInstances as GeneratedXcodeInstances, type XcodeInstance } from './xcode-instances';
|
|
6
2
|
import { type IosInstance } from './ios-instances';
|
|
3
|
+
import { APIPromise } from '../core/api-promise';
|
|
4
|
+
import { RequestOptions } from '../internal/request-options';
|
|
5
|
+
import { path } from '../internal/utils/path';
|
|
7
6
|
import { exec, type ExecChildProcess, type ExecRequest, type TestflightUploadConfig } from '../exec-client';
|
|
8
7
|
|
|
9
8
|
export type { TestflightUploadConfig } from '../exec-client';
|
|
@@ -13,6 +12,13 @@ import {
|
|
|
13
12
|
type FolderSyncOptions,
|
|
14
13
|
} from '../folder-sync';
|
|
15
14
|
import { createIgnoreFn } from '../folder-sync-ignore';
|
|
15
|
+
import {
|
|
16
|
+
createDaemonLogger,
|
|
17
|
+
deriveBasisCache,
|
|
18
|
+
mintAssetUploadUrls,
|
|
19
|
+
type LogLevel,
|
|
20
|
+
type SyncResult,
|
|
21
|
+
} from './daemon-client-shared';
|
|
16
22
|
import { nodeProxyTransport } from '../internal/proxy-transport';
|
|
17
23
|
import { directInstanceHttpError, isDirectInstanceHttpError } from '../internal/direct-instance-errors';
|
|
18
24
|
import { LimrunError } from '../core/error';
|
|
@@ -24,7 +30,10 @@ import { createEventSource } from 'eventsource-client';
|
|
|
24
30
|
|
|
25
31
|
export type { Tunnel } from '../tunnel';
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
// LogLevel and SyncResult are platform-neutral and now live in the shared
|
|
34
|
+
// daemon-client module; re-exported here so existing deep importers of this
|
|
35
|
+
// file keep working.
|
|
36
|
+
export type { LogLevel, SyncResult } from './daemon-client-shared';
|
|
28
37
|
|
|
29
38
|
export type SyncOptions = {
|
|
30
39
|
/**
|
|
@@ -59,17 +68,6 @@ export type SyncOptions = {
|
|
|
59
68
|
onSyncComplete?: FolderSyncOptions['onSyncComplete'];
|
|
60
69
|
};
|
|
61
70
|
|
|
62
|
-
export type SyncResult = {
|
|
63
|
-
/**
|
|
64
|
-
* Number of bytes transmitted to the server for this sync (full uploads plus
|
|
65
|
-
* delta patches, before transport compression). In watch mode, this reflects
|
|
66
|
-
* the initial sync only.
|
|
67
|
-
*/
|
|
68
|
-
bytesSent?: number;
|
|
69
|
-
/** Present only when watch=true; call to stop watching */
|
|
70
|
-
stopWatching?: () => Promise<void>;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
71
|
export type XcodeProjectConfig = {
|
|
74
72
|
workspace?: string;
|
|
75
73
|
project?: string;
|
|
@@ -402,22 +400,6 @@ export type XcodeCreateClientParams =
|
|
|
402
400
|
| { instance: XcodeInstance; logLevel?: LogLevel }
|
|
403
401
|
| { apiUrl: string; token: string; logLevel?: LogLevel };
|
|
404
402
|
|
|
405
|
-
function createLogger(logLevel: LogLevel) {
|
|
406
|
-
const shouldLog = (level: LogLevel) => {
|
|
407
|
-
const levels: LogLevel[] = ['none', 'error', 'warn', 'info', 'debug'];
|
|
408
|
-
return levels.indexOf(logLevel) >= levels.indexOf(level);
|
|
409
|
-
};
|
|
410
|
-
return (level: 'debug' | 'info' | 'warn' | 'error', msg: string) => {
|
|
411
|
-
if (!shouldLog(level)) return;
|
|
412
|
-
const prefix = '[XcodeInstance]';
|
|
413
|
-
if (level === 'error' || level === 'warn') {
|
|
414
|
-
console[level](prefix, msg);
|
|
415
|
-
} else {
|
|
416
|
-
console.log(prefix, msg);
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
|
|
421
403
|
function normalizeWorkspaceRelativePath(remotePath: string): string {
|
|
422
404
|
if (
|
|
423
405
|
remotePath === '' ||
|
|
@@ -512,28 +494,75 @@ async function readJsonResponse<T>(res: Response, operation: string): Promise<T>
|
|
|
512
494
|
return JSON.parse(text) as T;
|
|
513
495
|
}
|
|
514
496
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
)
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
497
|
+
// XcodeBuildLog is one persisted build record from the director's
|
|
498
|
+
// GET /v1/xcode_instances/{id}/build_logs (operationId listXcodeInstanceBuildLogs).
|
|
499
|
+
// Hand-written against api/public/director/openapiv3.yaml; if a Stainless
|
|
500
|
+
// regeneration ever picks that path up, reconcile with the generated surface
|
|
501
|
+
// instead of duplicating it.
|
|
502
|
+
export interface XcodeBuildLog {
|
|
503
|
+
/** Exec ID assigned by limbuild, e.g. build-1776140344112378000. */
|
|
504
|
+
id: string;
|
|
505
|
+
|
|
506
|
+
/** Terminal status reported by limbuild (e.g. SUCCEEDED, FAILED, CANCELLED). */
|
|
507
|
+
status: string;
|
|
508
|
+
|
|
509
|
+
/** Exit code of xcodebuild, if the build reached completion. */
|
|
510
|
+
exitCode?: number;
|
|
511
|
+
|
|
512
|
+
startedAt?: string;
|
|
513
|
+
|
|
514
|
+
finishedAt?: string;
|
|
515
|
+
|
|
516
|
+
/** Time spent running xcodebuild, in milliseconds. */
|
|
517
|
+
buildDurationMs?: number;
|
|
518
|
+
|
|
519
|
+
/** Error message captured by limbuild on failure, if any. */
|
|
520
|
+
error?: string;
|
|
521
|
+
|
|
522
|
+
/** Short-lived presigned URL for fetching the full .jsonl log from object storage. */
|
|
523
|
+
downloadUrl: string;
|
|
532
524
|
}
|
|
533
525
|
|
|
534
|
-
|
|
526
|
+
// BazelBuildLog is one persisted Bazel RBE invocation record from the
|
|
527
|
+
// director's GET /v1/xcode_instances/{id}/bazel_build_logs (operationId
|
|
528
|
+
// listBazelInstanceBuildLogs). Same hand-written caveat as XcodeBuildLog.
|
|
529
|
+
export interface BazelBuildLog {
|
|
530
|
+
/** Bazel invocation ID (UUID) from the build event stream. */
|
|
531
|
+
id: string;
|
|
532
|
+
|
|
533
|
+
/** Terminal status (SUCCEEDED, FAILED, CANCELLED, INCOMPLETE). */
|
|
534
|
+
status: string;
|
|
535
|
+
|
|
536
|
+
/** The build target patterns, if captured. */
|
|
537
|
+
pattern?: string[];
|
|
538
|
+
|
|
539
|
+
startedAt?: string;
|
|
540
|
+
|
|
541
|
+
/** Wall-clock duration of the invocation, in milliseconds. */
|
|
542
|
+
durationMs?: number;
|
|
543
|
+
|
|
544
|
+
/** Error message captured on failure, if any. */
|
|
545
|
+
error?: string;
|
|
546
|
+
|
|
547
|
+
/** Short-lived presigned URL for fetching the full .jsonl record from object storage. */
|
|
548
|
+
downloadUrl: string;
|
|
549
|
+
}
|
|
535
550
|
|
|
536
551
|
export class XcodeInstances extends GeneratedXcodeInstances {
|
|
552
|
+
/**
|
|
553
|
+
* List the instance's persisted build logs.
|
|
554
|
+
*/
|
|
555
|
+
listBuildLogs(id: string, options?: RequestOptions): APIPromise<XcodeBuildLog[]> {
|
|
556
|
+
return this._client.get(path`/v1/xcode_instances/${id}/build_logs`, options);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* List the instance's persisted Bazel RBE invocation logs.
|
|
561
|
+
*/
|
|
562
|
+
listBazelBuildLogs(id: string, options?: RequestOptions): APIPromise<BazelBuildLog[]> {
|
|
563
|
+
return this._client.get(path`/v1/xcode_instances/${id}/bazel_build_logs`, options);
|
|
564
|
+
}
|
|
565
|
+
|
|
537
566
|
async createClient(params: XcodeCreateClientParams): Promise<XcodeClient> {
|
|
538
567
|
let apiUrl: string;
|
|
539
568
|
let token: string;
|
|
@@ -548,7 +577,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
|
|
|
548
577
|
token = params.token;
|
|
549
578
|
}
|
|
550
579
|
|
|
551
|
-
const log =
|
|
580
|
+
const log = createDaemonLogger('[XcodeInstance]', params.logLevel ?? 'info');
|
|
552
581
|
const client = this._client;
|
|
553
582
|
let sandboxInfoPromise: Promise<{ homeDir: string }> | undefined;
|
|
554
583
|
const getSandboxInfo = () => {
|
|
@@ -612,11 +641,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
|
|
|
612
641
|
|
|
613
642
|
return {
|
|
614
643
|
async sync(localCodePath: string, opts?: SyncOptions): Promise<SyncResult> {
|
|
615
|
-
const
|
|
616
|
-
const folderName = path.basename(resolvedPath);
|
|
617
|
-
const hash = crypto.createHash('sha1').update(resolvedPath).digest('hex').slice(0, 8);
|
|
618
|
-
const cacheKey = `limsync-cache-${folderName}-${hash}`;
|
|
619
|
-
const basisCacheDir = opts?.basisCacheDir ?? path.join(os.tmpdir(), cacheKey);
|
|
644
|
+
const { cacheKey, basisCacheDir } = deriveBasisCache(localCodePath, opts?.basisCacheDir);
|
|
620
645
|
const sandboxInfo =
|
|
621
646
|
opts?.additionalFiles && opts.additionalFiles.length > 0 ? await getSandboxInfo() : undefined;
|
|
622
647
|
const additionalFiles = opts?.additionalFiles?.map((file) => ({
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.
|
|
1
|
+
export const VERSION = '0.42.1'; // x-release-please-version
|
package/version.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.42.1";
|
|
2
2
|
//# sourceMappingURL=version.d.mts.map
|
package/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.42.1";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/version.js
CHANGED
package/version.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = '0.
|
|
1
|
+
export const VERSION = '0.42.1'; // x-release-please-version
|
|
2
2
|
//# sourceMappingURL=version.mjs.map
|