harn-stdlib 0.8.26

Embedded Harn standard library source catalog
Documentation
/** std/gha - GitHub Actions output, summary, and annotation helpers. */
type GhaAnnotationOptions = {
  file?: string,
  line?: int,
  endLine?: int,
  col?: int,
  endColumn?: int,
  title?: string,
}

fn __gha_escape_data(value) -> string {
  let text = to_string(value ?? "")
  return replace(replace(replace(text, "%", "%25"), "\r", "%0D"), "\n", "%0A")
}

fn __gha_escape_property(value) -> string {
  return replace(replace(__gha_escape_data(value), ":", "%3A"), ",", "%2C")
}

fn __gha_kv(key, value) {
  if value == nil {
    return nil
  }
  return key + "=" + __gha_escape_property(value)
}

fn __gha_file_path(path, fallback) {
  let explicit = path ?? fallback
  if explicit != nil && explicit != "" {
    return explicit
  }
  return nil
}

/**
 * Escape text for the data portion of a GitHub Actions workflow command.
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: stable
 * @example: gha_escape_data(value)
 */
pub fn gha_escape_data(value) -> string {
  return __gha_escape_data(value)
}

/**
 * Escape text for a GitHub Actions workflow-command property.
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: stable
 * @example: gha_escape_property(value)
 */
pub fn gha_escape_property(value) -> string {
  return __gha_escape_property(value)
}

/**
 * Build a workflow annotation line (`::warning ...::message`).
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: stable
 * @example: gha_annotation(kind, message, options)
 */
pub fn gha_annotation(kind: string, message: string, options: GhaAnnotationOptions = {}) -> string {
  let opts = options ?? {}
  var props = []
  for pair in [
    __gha_kv("file", opts?.file),
    __gha_kv("line", opts?.line),
    __gha_kv("endLine", opts?.endLine),
    __gha_kv("col", opts?.col),
    __gha_kv("endColumn", opts?.endColumn),
    __gha_kv("title", opts?.title),
  ] {
    if pair != nil {
      props = props.push(pair)
    }
  }
  let suffix = if len(props) > 0 {
    " " + join(props, ",")
  } else {
    ""
  }
  let annotation_kind = kind ?? "notice"
  return "::" + annotation_kind + suffix + "::" + __gha_escape_data(message)
}

/**
 * Print a GitHub Actions notice.
 *
 * @effects: [stdio.write]
 * @allocation: heap
 * @errors: []
 * @api_stability: stable
 * @example: gha_notice(message, options)
 */
pub fn gha_notice(message: string, options: GhaAnnotationOptions = {}) {
  println(gha_annotation("notice", message, options))
}

/**
 * Print a GitHub Actions warning.
 *
 * @effects: [stdio.write]
 * @allocation: heap
 * @errors: []
 * @api_stability: stable
 * @example: gha_warning(message, options)
 */
pub fn gha_warning(message: string, options: GhaAnnotationOptions = {}) {
  println(gha_annotation("warning", message, options))
}

/**
 * Print a GitHub Actions error annotation.
 *
 * @effects: [stdio.write]
 * @allocation: heap
 * @errors: []
 * @api_stability: stable
 * @example: gha_error(message, options)
 */
pub fn gha_error(message: string, options: GhaAnnotationOptions = {}) {
  println(gha_annotation("error", message, options))
}

/**
 * Build the multiline-safe entry used by `$GITHUB_OUTPUT` and `$GITHUB_ENV`.
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: stable
 * @example: gha_env_block(name, value, delimiter)
 */
pub fn gha_env_block(name: string, value, delimiter: string? = nil) -> string {
  let delim = delimiter ?? ("harn_" + uuid_v7())
  return name + "<<" + delim + "\n" + to_string(value ?? "") + "\n" + delim + "\n"
}

/**
 * Append an output value to `$GITHUB_OUTPUT`, or to `path` when supplied.
 *
 * @effects: []
 * @allocation: stack-only
 * @errors: []
 * @api_stability: stable
 * @example: gha_write_output(name, value, path)
 */
pub fn gha_write_output(name: string, value, path: string? = nil) -> bool {
  let target = __gha_file_path(path, env("GITHUB_OUTPUT"))
  if target == nil {
    return false
  }
  append_file(target, gha_env_block(name, value))
  return true
}

/**
 * Append an environment value to `$GITHUB_ENV`, or to `path` when supplied.
 *
 * @effects: []
 * @allocation: stack-only
 * @errors: []
 * @api_stability: stable
 * @example: gha_write_env(name, value, path)
 */
pub fn gha_write_env(name: string, value, path: string? = nil) -> bool {
  let target = __gha_file_path(path, env("GITHUB_ENV"))
  if target == nil {
    return false
  }
  append_file(target, gha_env_block(name, value))
  return true
}

/**
 * Append Markdown to `$GITHUB_STEP_SUMMARY`, or to `path` when supplied.
 *
 * @effects: []
 * @allocation: stack-only
 * @errors: []
 * @api_stability: stable
 * @example: gha_append_summary(markdown, path)
 */
pub fn gha_append_summary(markdown: string, path: string? = nil) -> bool {
  let target = __gha_file_path(path, env("GITHUB_STEP_SUMMARY"))
  if target == nil {
    return false
  }
  append_file(target, markdown ?? "")
  if !ends_with(markdown ?? "", "\n") {
    append_file(target, "\n")
  }
  return true
}