<!-- json
{
  "sourceId": "autograph-cli-orchestration",
  "repositoryId": "autograph-typesetter",
  "filePath": "support/cli.render.ts",
  "capabilityTags": ["project", "cli", "orchestration"],
  "operationIds": ["project-bootstrap"],
  "applicability": ["sample-project"],
  "notes": [
    "Reference complex CLI orchestration and staged execution patterns."
  ]
}
-->

# autograph-cli-orchestration

- Source: `support/cli.render.ts`
- Capability tags: project, cli, orchestration
- Applicability: sample-project
- Notes:
  - Reference complex CLI orchestration and staged execution patterns.

## Reference Code

```typescript
import cp from 'node:child_process';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';

import { Context, ContextResourceProvider } from '@autograph/context';
import { TimeIt } from '@autograph/core';
import { MainParser } from '@autograph/parse';
import { MainPreparer } from '@autograph/prepare';
import { MainRenderer } from '@autograph/render';

import { CliCommand } from '@travetto/cli';
import { Registry } from '@travetto/registry';
import { Env, ExecUtil, Runtime } from '@travetto/runtime';
import { Specifier } from '@travetto/schema';

import { GdriveSupport } from './gdrive.ts';

@CliCommand({ runTarget: true })
export class TypesetApp {
  #gdrive: GdriveSupport;

  /** Output Folder */
  folder = 'gen';

  /** File name */
  fileName: string = 'book';

  kindleMount: string = os.platform() === 'linux' ? '/media/tim/Kindle' : '/Volumes/Kindle';

  kindleDevice?: string;

  #folder(name: string): string {
    return Runtime.workspaceRelative(this.folder, name);
  }

  #getOutput(ext: string): string {
    return this.#folder(`${this.fileName}${ext}`);
  }

  @TimeIt('total')
  async main(@Specifier('file', 'ext:md', 'ext:yml', 'ext:yaml') file: string, sync = false): Promise<void> {
    Env.TRV_RESOURCES.add('@@#data/fonts');

    await this.initDirs();

    await Registry.init();

    const resource = ContextResourceProvider.for(file);
    const input = `book:/${path.basename(file)}`;

    const { book, metadata, config } = await MainParser.parse(resource, input);

    const ctx = await Context.get(resource, book, metadata, config);

    await MainPreparer.prepare(ctx);
    await MainRenderer.render(ctx, path.resolve(this.folder, this.fileName));

    const epub = this.#getOutput('.epub');

    if (await fs.stat(epub).catch(() => false)) {
      await ExecUtil.getResult(cp.spawn('unzip', [this.#getOutput('.epub'), '-d', this.#folder('unpacked')]));

      if (this.kindleDevice) {
        await this.#generateAwz3();
        await this.#sendToKindle(this.kindleMount, this.kindleDevice);
      }
    }

    if (sync) {
      const clientName = path.basename(path.dirname(file));

      this.#gdrive = new GdriveSupport('Autograph-Books', 'autograph.json', this.#folder('gdrive'), clientName);

      if (metadata.email) {
        await this.#gdrive.shareFolder(metadata.email);
      }
      await this.#gdrive.uploadFiles(this.#folder('.'));
    }
  }

  async initDirs(): Promise<void> {
    // Clean
    await fs.rm(this.#folder('unpacked'), { recursive: true, force: true });

    await fs.mkdir(this.#folder('.'), { recursive: true });
    await fs.mkdir(this.#folder('unpacked'), { recursive: true });
    await fs.mkdir(this.#folder('json'), { recursive: true });

    // Remove all files
    for (const file of await fs.readdir(this.#folder('.'))) {
      if (file.includes('.')) {
        await fs.rm(this.#folder(file), { force: true });
      }
    }
  }

  async #generateAwz3(): Promise<void> {
    await ExecUtil.getResult(
      cp.spawn('ebook-convert', [
        this.#getOutput('.epub'),
        this.#getOutput('.azw3'),
        '--disable-markup-chapter-headings',
        '--disable-italicize-common-cases',
        '--disable-fix-indents',
        '--disable-unwrap-lines',
        '--disable-delete-blank-paragraphs',
        '--disable-format-scene-breaks',
        '--disable-dehyphenate',
        '--disable-renumber-headings',
        '--disable-remove-fake-margins',
        '--disable-font-rescaling'
        // '--disable-trim'
      ])
    );
  }

  async #sendToKindle(kindleMount: string, device: string): Promise<void> {
    const mountScript = Runtime.workspaceRelative('scripts/mount.sh');
    const epub = this.#getOutput('.epub');
    const base = path.basename(epub, '.epub');

    await ExecUtil.getResult(cp.spawn(mountScript, ['mount', kindleMount, device]), { catch: true });
    console.log('Deleting', `${kindleMount}/documents/${base}.*.*`);
    await ExecUtil.getResult(cp.spawn('rm', ['-rf', `${kindleMount}/documents/${base}.*.*`], { shell: true }));
    await fs.copyFile(this.#getOutput('.azw3'), `${kindleMount}/documents/${base}.${Date.now()}.azw3`);
    await ExecUtil.getResult(cp.spawn(mountScript, ['unmount', kindleMount, device]), { catch: true });
  }
}
```
