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
pnpm add -D @sanghyuk-2i/huh-clinpm install -D @sanghyuk-2i/huh-cliyarn add -D @sanghyuk-2i/huh-cliAfter installation, you can use the huh command.
Commands
huh init
Creates a .huh.config.ts config file template in the current directory.
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.
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
{
"source": {
"type": "google-sheets",
"sheetId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
"range": "Sheet1"
},
"output": "./src/huh.json"
}Authentication:
| Method | Configuration |
|---|---|
| API Key (env var) | Set GOOGLE_API_KEY environment variable |
| API Key (config) | Specify directly in source.apiKey |
| Service Account | Specify JSON key file path in source.credentials |
For details: Google Sheet Setup Guide
Airtable
{
"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
{
"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
{
"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
{
"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.
# Validate default path (src/huh.json)
npx huh validate
# Validate a specific file
npx huh validate ./path/to/errors.jsonOutput 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.
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# .husky/pre-commit
npx huh validatedefineConfig
A helper function for type-safe configuration.
import { defineConfig } from '@sanghyuk-2i/huh-cli';
import type { HuhCliConfig } from '@sanghyuk-2i/huh-cli';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;