Skip to content

โฌ…๏ธ Back to Table of Contents

๐Ÿ“„ backport

๐Ÿ“Š Analysis Summary

Metric Count
๐Ÿ”ง Functions 9
๐Ÿ“ฆ Imports 5
๐Ÿ“Š Variables & Constants 6
โšก Async/Await Patterns 1
๐Ÿ“ Interfaces 1

๐Ÿ“š Table of Contents

๐Ÿ› ๏ธ File Location:

๐Ÿ“‚ scripts/backport.ts

๐Ÿ“ฆ Imports

Name Source
execFileSync node:child_process
process node:process
parseArgs node:util
consola consola
colors consola/utils

Variables & Constants

Name Type Kind Value Exported
SEP "\u001F" const '\x1F' โœ—
EOL "\u001E" const '\x1E' โœ—
limit number const args.limit ? Number.parseInt(args.limit, 10) : 0 โœ—
selected Commit[] const await selectCommits(listed) โœ—
done number let/var 0 โœ—
skipped number let/var 0 โœ—

Async/Await Patterns

Type Function Await Expressions Promise Chains
async-function selectCommits consola.prompt('Select the commits to backport', { type: 'multiselect', requi... none

Functions

git(cmd: string[]): string

Parameters:

  • cmd string[]

Returns: string

Calls:

  • execFileSync('git', cmd, { encoding: 'utf-8' }).trim
Code
function git(...cmd: string[]): string {
  return execFileSync('git', cmd, { encoding: 'utf-8' }).trim()
}

tryGit(cmd: string[]): string | undefined

Parameters:

  • cmd string[]

Returns: string | undefined

Calls:

  • git
Code
function tryGit(...cmd: string[]): string | undefined {
  try {
    return git(...cmd)
  }
  catch {
    return undefined
  }
}

fatal(message: string, hint: string): never

Parameters:

  • message string
  • hint string

Returns: never

Calls:

  • consola.error
  • consola.info
  • process.exit
Code
function fatal(message: string, hint?: string): never {
  consola.error(message)
  if (hint)
    consola.info(hint)
  process.exit(1)
}

isBreaking(subject: string, body: string): boolean

Parameters:

  • subject string
  • body string

Returns: boolean

Calls:

  • /^\w+(?:\([^)]*\))?!:/.test
  • /^BREAKING[ -]CHANGE:/m.test

Internal Comments:

// `feat(core)!: ...` or a `BREAKING CHANGE:` footer

Code
function isBreaking(subject: string, body: string): boolean {
  // `feat(core)!: ...` or a `BREAKING CHANGE:` footer
  return /^\w+(?:\([^)]*\))?!:/.test(subject)
    || /^BREAKING[ -]CHANGE:/m.test(body)
}

parseCommits(raw: string): Commit[]

Parameters:

  • raw string

Returns: Commit[]

Calls:

  • raw .split(EOL) .map(i => i.trim()) .filter(Boolean) .map
  • entry.split
  • hash.slice
  • isBreaking
Code
function parseCommits(raw: string): Commit[] {
  return raw
    .split(EOL)
    .map(i => i.trim())
    .filter(Boolean)
    .map((entry) => {
      const [hash, subject, author, date, body = ''] = entry.split(SEP)
      return {
        hash,
        short: hash.slice(0, 7),
        subject,
        author,
        date,
        body,
        breaking: isBreaking(subject, body),
      }
    })
}

resolveSource(ref: string): string

Parameters:

  • ref string

Returns: string

Calls:

  • tryGit
  • ref.includes
  • fatal
  • colors.yellow

Internal Comments:

// fall back to the remote tracking branch, so `--from main` works on a
// checkout that never created a local `main`

Code
function resolveSource(ref: string): string {
  if (tryGit('rev-parse', '--verify', '--quiet', `${ref}^{commit}`))
    return ref
  // fall back to the remote tracking branch, so `--from main` works on a
  // checkout that never created a local `main`
  if (!ref.includes('/') && tryGit('rev-parse', '--verify', '--quiet', `origin/${ref}^{commit}`))
    return `origin/${ref}`
  return fatal(`Cannot resolve source ref ${colors.yellow(ref)}`)
}

getPickedHashes(source: string): Set<string>

Commits already backported with git cherry-pick -x. Patch-id matching (git log --cherry-pick) misses those that needed conflict resolution, the recorded source hash does not.

Raw JSDoc
/**
 * Commits already backported with `git cherry-pick -x`. Patch-id matching
 * (`git log --cherry-pick`) misses those that needed conflict resolution,
 * the recorded source hash does not.
 */

Calls:

  • tryGit
  • log.matchAll
  • hashes.add
  • hash.toLowerCase
Code
function getPickedHashes(source: string): Set<string> {
  const base = tryGit('merge-base', 'HEAD', source)
  const range = base ? `${base}..HEAD` : 'HEAD'
  const log = tryGit('log', range, '--format=%B') ?? ''
  const hashes = new Set<string>()
  for (const [, hash] of log.matchAll(/cherry picked from commit ([0-9a-f]{7,40})/gi))
    hashes.add(hash.toLowerCase())
  return hashes
}

label(commit: Commit): string

Parameters:

  • commit Commit

Returns: string

Calls:

  • colors.red
  • colors.yellow
Code
function label(commit: Commit): string {
  const flag = commit.breaking ? `${colors.red('!')} ` : ''
  return `${colors.yellow(commit.short)} ${flag}${commit.subject}`
}

selectCommits(commits: Commit[]): Promise<Commit[]>

Parameters:

  • commits Commit[]

Returns: Promise<Commit[]>

Calls:

  • commits.filter
  • fatal
  • colors.cyan
  • consola.prompt
  • commits.map
  • label
  • commits.filter(i => !i.breaking).map
  • set.has
Code
async function selectCommits(commits: Commit[]): Promise<Commit[]> {
  if (args.yes)
    return commits.filter(i => !i.breaking)

  if (!process.stdin.isTTY)
    fatal(`Not a TTY, pass ${colors.cyan('--yes')} to run non-interactively`)

  const answer = await consola.prompt('Select the commits to backport', {
    type: 'multiselect',
    required: false,
    cancel: 'symbol',
    options: commits.map(commit => ({
      value: commit.hash,
      label: label(commit),
      hint: `${commit.date} ยท ${commit.author}${commit.breaking ? ' ยท breaking' : ''}`,
    })),
    initial: commits.filter(i => !i.breaking).map(i => i.hash),
  }) as unknown as string[] | symbol

  if (typeof answer === 'symbol')
    fatal('Aborted')

  const set = new Set(answer)
  return commits.filter(i => set.has(i.hash))
}

Interfaces

Commit

Interface Code
interface Commit {
  hash: string
  short: string
  subject: string
  body: string
  author: string
  date: string
  breaking: boolean
}

Properties

Name Type Optional Description
hash string โœ— not shown
short string โœ— not shown
subject string โœ— not shown
body string โœ— not shown
author string โœ— not shown
date string โœ— not shown
breaking boolean โœ— not shown

Generated by Syntax Scribe