@determinate-systems/detsys-ts 0.1.0 → 2.0.0

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/dist/index.d.mts CHANGED
@@ -1,4 +1,7 @@
1
- import { UUID } from "node:crypto";
1
+ import * as actionsCore from "@actions/core";
2
+ import * as otelApi from "@opentelemetry/api";
3
+ import { Attributes } from "@opentelemetry/api";
4
+ import { Logger } from "@opentelemetry/api-logs";
2
5
  import { Got } from "got";
3
6
  //#region src/check-in.d.ts
4
7
  type CheckIn = {
@@ -37,7 +40,12 @@ type Feature = {
37
40
  //#endregion
38
41
  //#region src/correlation.d.ts
39
42
  /**
40
- * JSON sent to server.
43
+ * The hashed, non-identifying description of this run.
44
+ *
45
+ * Two consumers fix these names. The check-in evaluates feature flags against
46
+ * them, and the programs an Action runs read them from
47
+ * `$DETSYS_CORRELATION`. The OpenTelemetry data carries the same values under
48
+ * `detsys.` attribute names.
41
49
  */
42
50
  type CorrelationProperties = {
43
51
  $anon_distinct_id: string;
@@ -76,6 +84,13 @@ declare class IdsHost {
76
84
  isUrlSubjectToDynamicUrls(url: URL): boolean;
77
85
  getDynamicRootUrl(): Promise<URL | undefined>;
78
86
  getRootUrl(): Promise<URL>;
87
+ /**
88
+ * The diagnostics endpoint of the current backend.
89
+ *
90
+ * This library reports nothing there: its telemetry is OpenTelemetry. The
91
+ * URL is for the programs an Action runs, which have diagnostics of their
92
+ * own.
93
+ */
79
94
  getDiagnosticsUrl(): Promise<URL | undefined>;
80
95
  private getUrlsByPreference;
81
96
  }
@@ -138,6 +153,40 @@ declare const getStringOrNull: (name: string) => string | null;
138
153
  * Get a string input from the Action's configuration by name or return `undefined` if not set.
139
154
  */
140
155
  declare const getStringOrUndefined: (name: string) => string | undefined;
156
+ declare namespace log_d_exports {
157
+ export { debug, error, group, info, notice, setFailed, warning };
158
+ }
159
+ /**
160
+ * `@actions/core` accepts an Error in place of a message for the annotation
161
+ * functions, and renders it via `toString()`.
162
+ */
163
+ type Message = string | Error;
164
+ /**
165
+ * Write a debug message. Only visible in the workflow log when the user has
166
+ * enabled step debug logging, but always exported to OpenTelemetry.
167
+ */
168
+ declare function debug(message: string, attributes?: Attributes): void;
169
+ /** Write an informational message to the workflow log. */
170
+ declare function info(message: string, attributes?: Attributes): void;
171
+ /** Write a notice annotation to the workflow log. */
172
+ declare function notice(message: Message, properties?: actionsCore.AnnotationProperties, attributes?: Attributes): void;
173
+ /** Write a warning annotation to the workflow log. */
174
+ declare function warning(message: Message, properties?: actionsCore.AnnotationProperties, attributes?: Attributes): void;
175
+ /** Write an error annotation to the workflow log. */
176
+ declare function error(message: Message, properties?: actionsCore.AnnotationProperties, attributes?: Attributes): void;
177
+ /**
178
+ * Fail the workflow step, recording the reason as an OpenTelemetry error log.
179
+ */
180
+ declare function setFailed(message: Message, attributes?: Attributes): void;
181
+ /**
182
+ * Run `fn` inside both a collapsible group in the workflow log and an active
183
+ * OpenTelemetry span of the same name.
184
+ *
185
+ * This is the replacement for a `startGroup`/`endGroup` pair: the group closes
186
+ * and the span ends even if `fn` throws, and a throwing `fn` marks the span
187
+ * failed before re-throwing.
188
+ */
189
+ declare function group<T>(name: string, fn: () => Promise<T>, attributes?: Attributes): Promise<T>;
141
190
  declare namespace platform_d_exports {
142
191
  export { getArchOs, getNixPlatform };
143
192
  }
@@ -150,6 +199,62 @@ declare function getArchOs(): string;
150
199
  */
151
200
  declare function getNixPlatform(archOs: string): string;
152
201
  //#endregion
202
+ //#region src/telemetry.d.ts
203
+ /** The instrumentation scope name for everything this library emits. */
204
+ declare const SCOPE_NAME = "detsys-ts";
205
+ /** The severities we map GitHub Actions' log levels onto. */
206
+ type LogLevel = "debug" | "info" | "notice" | "warning" | "error";
207
+ /**
208
+ * The tracer for this library. Returns a no-op tracer until {@link
209
+ * Telemetry.start} has run, so this is always safe to call.
210
+ */
211
+ declare function getTracer(): otelApi.Tracer;
212
+ /**
213
+ * The logger for this library. Returns a no-op logger until {@link
214
+ * Telemetry.start} has run, so this is always safe to call.
215
+ */
216
+ declare function getLogger(): Logger;
217
+ /**
218
+ * Serialize a span as a W3C `traceparent` header value, suitable for stashing
219
+ * in the Action's state or handing to a child process.
220
+ *
221
+ * Returns undefined when telemetry is disabled, since the no-op span's context
222
+ * is all zeroes and would not be a valid parent.
223
+ */
224
+ declare function traceparentOf(span: otelApi.Span | undefined): string | undefined;
225
+ /**
226
+ * The W3C trace context headers of the operation in progress, for an outgoing
227
+ * HTTP request.
228
+ *
229
+ * Put these headers on the request.
230
+ * The service that answers it can then put its own work in this trace.
231
+ *
232
+ * The headers describe the span that is active now.
233
+ * When no span is active yet -- a request the Action makes before it starts a
234
+ * span of its own -- they describe the span that `$TRACEPARENT` names, which is
235
+ * the span the Action announced, or the span of the workflow job.
236
+ *
237
+ * The result is empty when the export is off.
238
+ * A no-op span's context is all zeroes, and is not a valid parent.
239
+ */
240
+ declare function traceContextHeaders(): Record<string, string>;
241
+ /**
242
+ * Rebuild a Context from a W3C `traceparent` value, so a span started in one
243
+ * process can parent spans started in another. Falls back to the root context
244
+ * when `traceparent` is absent or unparseable.
245
+ */
246
+ declare function contextFromTraceparent(traceparent: string | undefined): otelApi.Context;
247
+ /**
248
+ * Mark `span` as failed and attach the exception to it.
249
+ */
250
+ declare function recordSpanError(span: otelApi.Span, error: unknown): void;
251
+ /**
252
+ * Run `fn` inside a new active span, ending the span when it settles and
253
+ * marking it failed if it throws. The error is always re-thrown: this records,
254
+ * it does not swallow.
255
+ */
256
+ declare function withSpan<T>(name: string, fn: (span: otelApi.Span) => Promise<T>, attributes?: otelApi.Attributes): Promise<T>;
257
+ //#endregion
153
258
  //#region src/index.d.ts
154
259
  /**
155
260
  * An enum for describing different "fetch suffixes" for i.d.s.
@@ -184,13 +289,10 @@ type NixStoreTrust = "trusted" | "untrusted" | "unknown";
184
289
  type ActionOptions = {
185
290
  name: string;
186
291
  idsProjectName?: string;
187
- eventPrefix?: string;
188
292
  fetchStyle: FetchSuffixStyle;
189
293
  legacySourcePrefix?: string;
190
294
  requireNix: NixRequirementHandling;
191
295
  diagnosticsSuffix?: string;
192
- binaryNamePrefixes?: string[];
193
- binaryNamesDenyList?: string[];
194
296
  };
195
297
  /**
196
298
  * A confident version of Options, where defaults have been resolved into final values.
@@ -198,23 +300,10 @@ type ActionOptions = {
198
300
  type ConfidentActionOptions = {
199
301
  name: string;
200
302
  idsProjectName: string;
201
- eventPrefix: string;
202
303
  fetchStyle: FetchSuffixStyle;
203
304
  legacySourcePrefix?: string;
204
305
  requireNix: NixRequirementHandling;
205
306
  providedDiagnosticsUrl?: URL;
206
- binaryNamePrefixes: string[];
207
- binaryNamesDenyList: string[];
208
- };
209
- /**
210
- * An event to send to the diagnostic endpoint of i.d.s.
211
- */
212
- type DiagnosticEvent = {
213
- name: string;
214
- distinct_id?: string;
215
- uuid: UUID;
216
- timestamp: Date;
217
- properties: Record<string, unknown>;
218
307
  };
219
308
  declare abstract class DetSysAction {
220
309
  nixStoreTrust: NixStoreTrust;
@@ -226,21 +315,24 @@ declare abstract class DetSysAction {
226
315
  private nixSystem;
227
316
  private architectureFetchSuffix;
228
317
  private sourceParameters;
229
- private facts;
230
- private events;
231
318
  private identity;
232
319
  private idsHost;
233
320
  private features;
234
- private featureEventMetadata;
321
+ private telemetry;
322
+ private systemDetails;
323
+ private phaseSpan?;
324
+ private pendingAttributes;
235
325
  private determineExecutionPhase;
236
326
  constructor(actionOptions: ActionOptions);
237
327
  /**
238
- * Attach a file to the diagnostics data in error conditions.
328
+ * Attach a file to the telemetry for this run, to be emitted if the Action
329
+ * fails.
239
330
  *
240
331
  * The file at `location` doesn't need to exist when stapleFile is called.
241
332
  *
242
- * If the file doesn't exist or is unreadable when trying to staple the attachments, the JS error will be stored in a context value at `staple_failure_{name}`.
243
- * If the file is readable, the file's contents will be stored in a context value at `staple_value_{name}`.
333
+ * Each attachment becomes one OpenTelemetry log record, correlated to the
334
+ * phase's span: the file's contents as the body if it can be read, the
335
+ * reason it could not be read otherwise.
244
336
  */
245
337
  stapleFile(name: string, location: string): void;
246
338
  /**
@@ -256,12 +348,40 @@ declare abstract class DetSysAction {
256
348
  */
257
349
  execute(): void;
258
350
  getTemporaryName(): string;
259
- addFact(key: string, value: string | boolean | number): void;
351
+ /**
352
+ * Describe this run with an attribute.
353
+ *
354
+ * The attribute lands on the phase's root span, not on whichever span
355
+ * happens to be active, because it describes the run as a whole. Set it
356
+ * whenever the value becomes known: attributes set before the span opens
357
+ * are replayed onto it.
358
+ *
359
+ * Namespace your keys, as OpenTelemetry expects: `detsys.nix.version`, not
360
+ * `nix_version`.
361
+ */
362
+ setAttribute(key: string, value: otelApi.AttributeValue): void;
363
+ /**
364
+ * The diagnostics endpoint for the programs this Action runs, such as
365
+ * `nix-installer` and `magic-nix-cache`.
366
+ *
367
+ * This library reports nothing there. Its own telemetry is OpenTelemetry;
368
+ * see {@link getTelemetryEnvironment} for putting a child process's
369
+ * telemetry in this run's trace.
370
+ */
260
371
  getDiagnosticsUrl(): Promise<URL | undefined>;
261
372
  getUniqueId(): string;
262
373
  getCrossPhaseId(): string;
263
374
  getCorrelationHashes(): CorrelationProperties;
264
- recordEvent(eventName: string, context?: Record<string, boolean | string | number | undefined | Record<string, boolean | string | number | undefined>>): void;
375
+ /**
376
+ * Record that something happened, as a span event.
377
+ *
378
+ * The event lands on whichever span is active, so that it sits on the
379
+ * operation that produced it, and on the phase's root span when there is no
380
+ * nested span in progress.
381
+ *
382
+ * Namespace your attribute keys, as OpenTelemetry expects.
383
+ */
384
+ addEvent(name: string, attributes?: otelApi.Attributes): void;
265
385
  /**
266
386
  * Unpacks the closure returned by `fetchArtifact()`, imports the
267
387
  * contents into the Nix store, and returns the path of the executable at
@@ -276,10 +396,119 @@ declare abstract class DetSysAction {
276
396
  private get isMain();
277
397
  private get isPost();
278
398
  private executeAsync;
399
+ /**
400
+ * Run `fn` with the phase's root span as the active span, so anything it
401
+ * starts is parented into this phase's trace.
402
+ */
403
+ private withPhaseSpanActive;
404
+ /**
405
+ * Start the OpenTelemetry export.
406
+ *
407
+ * All runs export their data.
408
+ * To stop the export, set `OTEL_SDK_DISABLED` to `true`, or set
409
+ * `OTEL_EXPORTER_OTLP_ENDPOINT` to an empty value.
410
+ * The SDK then does not start.
411
+ * The OpenTelemetry API stays in its no-op state.
412
+ * Each span and log record then does nothing.
413
+ * Thus the call sites do not test if the export is on.
414
+ */
415
+ private startTelemetry;
416
+ /**
417
+ * Put every Action of this workflow job in one trace.
418
+ *
419
+ * A job runs each Action as a process of its own.
420
+ * Thus the Actions can only agree on a trace through the job's environment.
421
+ * The first Action to run makes the identity of the job's span and exports it
422
+ * as `$TRACEPARENT`.
423
+ * Each later step finds it there: the other Actions, and the programs the
424
+ * workflow runs, such as Nix.
425
+ *
426
+ * The span itself starts and ends in the post phase of the Action that
427
+ * announced it.
428
+ * GitHub Actions runs the post phases in the reverse of the order of the main
429
+ * phases, thus that phase is the last one of the job.
430
+ * The span then covers the whole job.
431
+ * See {@link endJobSpan}.
432
+ *
433
+ * A `$TRACEPARENT` that is already set belongs to an earlier Action, or to the
434
+ * system that started the workflow.
435
+ * Do not change it, and join that trace.
436
+ */
437
+ private announceJobTrace;
438
+ /**
439
+ * End the job's span, if this Action is the one that announced it.
440
+ *
441
+ * The span also starts here.
442
+ * A span belongs to the process that ends it, and the process that made the
443
+ * announcement stopped long ago.
444
+ * See {@link announceJobTrace}.
445
+ */
446
+ private endJobSpan;
447
+ /**
448
+ * Start the root span of this execution phase.
449
+ *
450
+ * The span starts at the moment the phase did, and thus covers the start of
451
+ * the SDK, which comes before it.
452
+ *
453
+ * `main` and `post` are separate processes.
454
+ * Thus the main phase saves the identity of its span in the Action's state,
455
+ * and the post phase makes its span a child of it.
456
+ * A `$TRACEPARENT` in the environment is the span of the workflow job, or of
457
+ * the system that started the workflow.
458
+ */
459
+ private startPhaseSpan;
460
+ /**
461
+ * The stable, run-scoped attributes attached to every span and log record.
462
+ *
463
+ * The correlation data here is hashed and does not identify a repository,
464
+ * an organization, or a person.
465
+ */
466
+ private telemetryResourceAttributes;
467
+ /**
468
+ * The W3C `traceparent` identifying the span currently in progress.
469
+ *
470
+ * Hand this to a child process -- as `$TRACEPARENT` -- so that its own
471
+ * OpenTelemetry data joins this Action's trace. Returns undefined when
472
+ * OpenTelemetry export is disabled for this run.
473
+ */
474
+ getTraceparent(): string | undefined;
475
+ /**
476
+ * The environment variables that let a child process add data to this
477
+ * Action's trace: the current `$TRACEPARENT` and the OTLP export settings.
478
+ *
479
+ * Add these variables to the environment of each child process to trace.
480
+ * A child that inherits this process's environment already has the OTLP
481
+ * settings; only `$TRACEPARENT` changes as the run proceeds.
482
+ *
483
+ * The result is empty if the OpenTelemetry export is off.
484
+ * Thus it is always safe to add them.
485
+ */
486
+ getTelemetryEnvironment(): Promise<Record<string, string>>;
279
487
  getClient(): Promise<Got>;
488
+ /**
489
+ * Check in, and tell the user about the incidents and the maintenance the
490
+ * check-in reports.
491
+ */
280
492
  private checkIn;
493
+ /**
494
+ * The variant of a feature flag this run resolved, if the check-in returned
495
+ * one.
496
+ *
497
+ * Each variant this Action asks for becomes an attribute of the run, under
498
+ * `detsys.feature.`, so the telemetry can be sliced by the flags that
499
+ * changed what the run did.
500
+ */
281
501
  getFeature(name: string): Feature | undefined;
282
- private recordGroup;
502
+ /**
503
+ * The person properties the check-in evaluates feature flags against.
504
+ *
505
+ * These names are the flag-targeting contract with the feature flag
506
+ * service, which is why they keep their `$`-prefixed spelling. They are not
507
+ * telemetry: nothing here is reported anywhere. The telemetry for this run
508
+ * is OpenTelemetry, and it names the same values the way OpenTelemetry
509
+ * does.
510
+ */
511
+ private checkInPersonProperties;
283
512
  /**
284
513
  * Check in to install.determinate.systems, to accomplish three things:
285
514
  *
@@ -321,19 +550,22 @@ declare abstract class DetSysAction {
321
550
  */
322
551
  failOnError(msg: string): void;
323
552
  private downloadFile;
553
+ private download;
324
554
  private complete;
325
555
  private getCheckInUrl;
326
556
  private getSourceUrl;
327
557
  private cacheKey;
328
558
  private getCachedVersion;
329
559
  private saveCachedVersion;
330
- private collectBacktraceSetup;
331
- private collectBacktraces;
560
+ /**
561
+ * Emit the files `stapleFile` collected, as log records correlated to this
562
+ * phase's span. The Action has already failed by the time this runs.
563
+ */
564
+ private emitAttachments;
332
565
  private preflightRequireNix;
333
566
  private preflightNixStoreInfo;
334
567
  private preflightNixVersion;
335
- private submitEvents;
336
568
  }
337
569
  //#endregion
338
- export { ActionOptions, type CheckIn, ConfidentActionOptions, type CorrelationProperties, DetSysAction, DiagnosticEvent, ExecutionPhase, type Feature, FetchSuffixStyle, IdsHost, type Incident, type Maintenance, NixRequirementHandling, NixStoreTrust, type Page, type SourceDef, type StatusSummary, inputs_d_exports as inputs, platform_d_exports as platform, stringifyError };
570
+ export { ActionOptions, type CheckIn, ConfidentActionOptions, type CorrelationProperties, DetSysAction, ExecutionPhase, type Feature, FetchSuffixStyle, IdsHost, type Incident, type LogLevel, type Maintenance, NixRequirementHandling, NixStoreTrust, type Page, SCOPE_NAME, type SourceDef, type StatusSummary, contextFromTraceparent, getLogger, getTracer, inputs_d_exports as inputs, log_d_exports as log, platform_d_exports as platform, recordSpanError, stringifyError, traceContextHeaders, traceparentOf, withSpan };
339
571
  //# sourceMappingURL=index.d.mts.map