Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,29 +62,29 @@ function wrapCommandAction<T extends (...args: any[]) => Promise<any>>(action: T

program
.name('localhero')
.description('CLI tool for automatic I18n translations with LocalHero.ai, more info at https://localhero.ai.')
.description('CLI tool for automatic I18n translations with Localhero.ai, more info at https://localhero.ai.')
.version(getVersion())
.option('--debug', 'Show debug information when errors occur')
.action(() => {
console.log('LocalHero.ai is an automatic I18n translation service that easily integrates with your dev workflow.');
console.log('Localhero.ai is an automatic I18n translation service that easily integrates with your dev workflow.');
console.log(`\nVersion: ${getVersion()}`);
console.log('\n👏 Set up your project with `npx @localheroai/cli init`');
console.log('💡 Use --help to see available commands');
console.log('🔗 Visit https://localhero.ai for more information');
console.log('\nWe´re LocalHero.ai, a small, bootstrapped company working to make');
console.log("\nWe're Localhero.ai, a small, bootstrapped company working to make");
console.log('i18n simpler for developers like you. If you have any questions or');
console.log('feedback, just reach out to us at hi@localhero.ai. Thanks 🙌');
});

program
.command('login')
.description('Authenticate with LocalHero.ai using an API key')
.description('Authenticate with Localhero.ai using an API key')
.option('--api-key <key>', 'API key for non-interactive authentication')
.action(wrapCommandAction((options: { apiKey?: string }) => login({ apiKey: options.apiKey })));

program
.command('init')
.description('Initialize a new LocalHero.ai project')
.description('Initialize a new Localhero.ai project')
.option('-y, --yes', 'Non-interactive mode: use flag values and never prompt')
.option('--project-id <id>', 'Use an existing project (skips creation)')
.option('--project-name <name>', 'Name for a new project (defaults to current directory name)')
Expand Down Expand Up @@ -115,14 +115,14 @@ program

program
.command('pull')
.description('Pull updates from LocalHero.ai to your local files')
.description('Pull updates from Localhero.ai to your local files')
.option('-v, --verbose', 'Show detailed progress information')
.option('--changed-only', 'Only pull translations for keys changed in current branch')
.action(wrapCommandAction((options: { verbose?: boolean; changedOnly?: boolean }) => pull(options)));

program
.command('push')
.description('Push updates from your local files to LocalHero.ai')
.description('Push updates from your local files to Localhero.ai')
.option('-v, --verbose', 'Show detailed progress information')
.option('-y, --yes', 'Skip confirmation prompt')
.option('-f, --force', 'Push all files regardless of git changes')
Expand All @@ -134,7 +134,7 @@ program

program
.command('clone')
.description('Clone all translations from LocalHero.ai to your local files')
.description('Clone all translations from Localhero.ai to your local files')
.option('-v, --verbose', 'Show detailed progress information')
.option('-f, --force', 'Force, override existing files')
.action(wrapCommandAction((options: { verbose?: boolean; force?: boolean }) => clone(options)));
Expand Down
2 changes: 1 addition & 1 deletion src/commands/ci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ async function runSyncMode(
const { console, configUtils, githubUtils, syncApi, updateTranslationFile } = deps;
const verbose = options?.verbose || false;

console.log(chalk.blue('🔄 Syncing translations from LocalHero...\n'));
console.log(chalk.blue('🔄 Syncing translations from Localhero...\n'));

const allFiles: SyncFile[] = [];
let currentPage = 1;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export async function login(deps: LoginDependencies = {}): Promise<void> {

const apiKey = providedApiKey || process.env.LOCALHERO_API_KEY || (
console.log('\n→ Get your API key from: https://localhero.ai/api-keys'),
console.log('→ New to LocalHero? Sign up at: https://localhero.ai/signup'),
console.log('→ New to Localhero? Sign up at: https://localhero.ai/signup'),
console.log('\nThe API key will be saved to .localhero_key, and automatically added to your .gitignore file.\n'),
await promptService.getApiKey()
);
Expand Down
2 changes: 1 addition & 1 deletion src/commands/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export async function push(
} else if (!yes) {
const message = prune
? 'This will push your local translations and identify stale keys to prune.\nContinue?'
: 'This will push your local translations to LocalHero.ai.\nYour changes will be versioned in the API for easy tracking.\nContinue?';
: 'This will push your local translations to Localhero.ai.\nYour changes will be versioned in the API for easy tracking.\nContinue?';

const confirmed = await prompt.confirm({
message,
Expand Down
16 changes: 11 additions & 5 deletions src/utils/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ ${buildPythonToolSetup(pythonInstall)} - name: Extract messages
sudo apt-get install -y -qq gettext
${pythonInstall}
${buildMakemessagesCommand(locales, pythonInstall)}

# Optional: makemessages rewrites the creation-date header on every run,
# so without this line every CI run shows a one-line diff in every catalog.
${STRIP_POT_DATE}

`;
Expand Down Expand Up @@ -450,12 +453,15 @@ ${buildExtractStep(options)} - name: Translate

if (appToken) {
log.log('✓ Using GitHub App token');
} else if (errorCode === 'invalid_api_key') {
log.warn('⚠️ Warning: API authentication failed. Using GITHUB_TOKEN instead (workflows will not trigger).');
} else if (errorCode === 'github_app_not_installed') {
// A push authenticated with GITHUB_TOKEN never triggers downstream
// workflows, so required checks on this commit are left waiting with
// nothing to report. Staying silent here leaves that PR unexplained.
log.warn('⚠️ Warning: The Localhero GitHub App is not installed for this project. Using GITHUB_TOKEN instead, so checks on this commit will not run. Install the app to enable them.');
} else {
if (errorCode === 'invalid_api_key') {
log.warn('⚠️ Warning: API authentication failed. Using GITHUB_TOKEN instead (workflows will not trigger).');
} else if (errorCode !== 'github_app_not_installed') {
log.warn('⚠️ Warning: Failed to fetch GitHub App token. Using GITHUB_TOKEN instead (workflows will not trigger).');
}
log.warn('⚠️ Warning: Failed to fetch GitHub App token. Using GITHUB_TOKEN instead (workflows will not trigger).');
}

return finalToken;
Expand Down
8 changes: 4 additions & 4 deletions tests/cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ describe('CLI basics', () => {

it('displays info when no command is provided', () => {
const output = execSync('node dist/cli.js').toString();
expect(output).toContain('LocalHero.ai');
expect(output).toContain('Localhero.ai');
expect(output).toContain('Visit https://localhero.ai for more information');
});

// If these works the commands should be possible to trigger
describe('command help texts', () => {
it('login command help', () => {
const output = execSync('node dist/cli.js login --help').toString();
expect(output).toContain('Authenticate with LocalHero.ai');
expect(output).toContain('Authenticate with Localhero.ai');
});

it('init command help', () => {
const output = execSync('node dist/cli.js init --help').toString();
expect(output).toContain('Initialize a new LocalHero.ai project');
expect(output).toContain('Initialize a new Localhero.ai project');
});

it('translate command help', () => {
Expand All @@ -37,7 +37,7 @@ describe('CLI basics', () => {

it('sync command help', () => {
const output = execSync('node dist/cli.js sync --help').toString();
expect(output).toContain('Pull updates from LocalHero.ai');
expect(output).toContain('Pull updates from Localhero.ai');
});
});
});
6 changes: 4 additions & 2 deletions tests/utils/github.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ describe('githubService', () => {
);
});

it('silently falls back to GITHUB_TOKEN when GitHub App not installed', async () => {
it('warns when falling back to GITHUB_TOKEN because the GitHub App is not installed', async () => {
mockEnv.GITHUB_ACTIONS = 'true';
mockEnv.GITHUB_HEAD_REF = 'feature-branch';
mockEnv.GITHUB_TOKEN = 'github-token';
Expand Down Expand Up @@ -639,7 +639,9 @@ describe('githubService', () => {
'git remote set-url origin https://x-access-token:github-token@github.com/owner/repo.git',
{ stdio: 'pipe' }
);
expect(mockConsole.warn).not.toHaveBeenCalled();
expect(mockConsole.warn).toHaveBeenCalledWith(
'⚠️ Warning: The Localhero GitHub App is not installed for this project. Using GITHUB_TOKEN instead, so checks on this commit will not run. Install the app to enable them.'
);
});

it('retries push on failure and succeeds on second attempt', async () => {
Expand Down
Loading