Skip to content

A CLI tool that fetches error content from various data sources (Google Sheets, Airtable, Notion, CSV, XLSX) and converts it to a JSON file.

Installation

bash
pnpm add -D @sanghyuk-2i/huh-cli
bash
npm install -D @sanghyuk-2i/huh-cli
bash
yarn add -D @sanghyuk-2i/huh-cli

After installation, you can use the huh command.


Commands

huh init

Creates a .huh.config.ts config file template in the current directory.

bash\nnpx

The generated file includes configuration examples for all 5 data sources: Google Sheets, Airtable, Notion, CSV, and XLSX. Uncomment the source you want to use.

TIP

If a config file already exists, it will not be overwritten and a warning will be displayed.


huh pull

Fetches data from the configured data source and generates a JSON file.

bash\nnpx

With i18n configuration, per-locale files (ko.json, en.json, index.ts) are generated in the output directory. See the i18n Multi-Language Support guide for details.

Execution flow:

Read config fileRead .huh.config.json from the project root.

Fetch data

Fetch data from the configured data source via the Adapter pattern. In i18n mode, data is fetched for each locale separately.

Convert to JSON DSLConvert fetched data with parseSheetData.

Validate

Validate each locale with validateConfig. In i18n mode, additionally runs validateLocales for cross-locale validation. - If only warnings, print them and continue - If errors exist, print errors and exit (exit code 1)

Generate JSON file

Generate the JSON file at the specified output path. In i18n mode, generates per-locale JSON files and an index.ts barrel file.


Config File

The current v0.1 supports the .huh.config.json format.

Google Sheets

json
{
  "source": {
    "type": "google-sheets",
    "sheetId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
    "range": "Sheet1"
  },
  "output": "./src/huh.json"
}

Authentication:

MethodConfiguration
API Key (env var)Set GOOGLE_API_KEY environment variable
API Key (config)Specify directly in source.apiKey
Service AccountSpecify JSON key file path in source.credentials
bash\nGOOGLE_API_KEYAIza...

For details: Google Sheet Setup Guide

Airtable

json
{
  "source": {
    "type": "airtable",
    "baseId": "appXXXXXXXXXXXXXX",
    "tableId": "tblYYYYYYYYYYYYYY"
  },
  "output": "./src/huh.json"
}

Authentication: AIRTABLE_TOKEN environment variable or specify directly in source.token

For details: Airtable Integration Guide

Notion

json
{
  "source": {
    "type": "notion",
    "databaseId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  },
  "output": "./src/huh.json"
}

Authentication: NOTION_TOKEN environment variable or specify directly in source.token

For details: Notion Integration Guide

CSV

json
{
  "source": {
    "type": "csv",
    "filePath": "./errors.csv"
  },
  "output": "./src/huh.json"
}

No authentication required. Uses an RFC 4180 compatible CSV parser.

For details: CSV File Guide

XLSX

json
{
  "source": {
    "type": "xlsx",
    "filePath": "./errors.xlsx",
    "sheet": "Sheet1"
  },
  "output": "./src/huh.json"
}

No authentication required. If sheet is not specified, the first sheet is used.

For details: XLSX File Guide


huh validate [file]

Validates a generated JSON file.

bash
# Validate default path (src/huh.json)
npx huh validate

# Validate a specific file
npx huh validate ./path/to/errors.json

Output example (success):

Validating ./src/huh.json...
Found 5 error entries.

Validation passed!

Output example (warnings + errors):

Validating ./src/huh.json...
Found 3 error entries.

2 warning(s):
  - [ERR_TOAST] title: Toast errors typically do not display a title
  - [ERR_PAGE] action: Page errors should provide an action for user navigation

1 error(s):
  - [ERR_REDIRECT] action.target: Action type "REDIRECT" requires a target URL

Validation failed.

Exits with exit code 1 if there is at least one error. Passes if there are only warnings.


CI/CD Integration

WARNING

Never commit API tokens or secrets directly to your repository. Always use environment variables or your CI/CD platform's secret management to pass sensitive credentials like GOOGLE_API_KEY, AIRTABLE_TOKEN, and NOTION_TOKEN.

yaml\nname:
on:
  schedule:
    - cron: '0 9 * * 1-5' # Weekdays at 9 AM
  workflow_dispatch:

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v2
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm

      - run: pnpm install

      - name: Pull error content
        run: npx huh pull
        env:
          GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
          # For Airtable: AIRTABLE_TOKEN: ${{ secrets.AIRTABLE_TOKEN }}
          # For Notion: NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }}

      - name: Validate
        run: npx huh validate

      - name: Commit changes
        run: |
          git config user.name "github-actions"
          git config user.email "github-actions@github.com"
          git add src/huh.json
          git diff --staged --quiet || git commit -m "chore: sync error content"
          git push
bash
# .husky/pre-commit
npx huh validate

defineConfig

A helper function for type-safe configuration.

ts
import { defineConfig } from '@sanghyuk-2i/huh-cli';
import type { HuhCliConfig } from '@sanghyuk-2i/huh-cli';
ts
interface HuhCliConfig {
  source: HuhSource;
  output: string; // JSON file output path (directory path in i18n mode)
  i18n?: I18nConfig; // Multi-language config (optional)
}

interface I18nConfig {
  defaultLocale: string;
  locales: Record<string, LocaleSourceOverride>;
}

interface LocaleSourceOverride {
  sheet?: string; // XLSX sheet name
  range?: string; // Google Sheets tab name
  filePath?: string; // CSV file path
  tableId?: string; // Airtable table ID
  databaseId?: string; // Notion database ID
}

// Google Sheets
type GoogleSheetsSource = {
  type: 'google-sheets';
  sheetId: string;
  range?: string; // Default: 'Sheet1'
  apiKey?: string;
  credentials?: string; // Service account JSON key file path
};

// Airtable
type AirtableSource = {
  type: 'airtable';
  baseId: string;
  tableId: string;
  token?: string;
};

// Notion
type NotionSource = {
  type: 'notion';
  databaseId: string;
  token?: string;
};

// CSV (local file)
type CsvSource = {
  type: 'csv';
  filePath: string;
};

// XLSX (local file)
type XlsxSource = {
  type: 'xlsx';
  filePath: string;
  sheet?: string; // Uses first sheet if not specified
};

type HuhSource = GoogleSheetsSource | AirtableSource | NotionSource | CsvSource | XlsxSource;

Released under the MIT License.