update message

This commit is contained in:
nrslib 2026-02-22 02:27:47 +09:00
parent cb0b7a04ca
commit 9e6e7e3550
3 changed files with 58 additions and 6 deletions

View File

@ -19,6 +19,7 @@ import {
validateMinVersion, validateMinVersion,
isVersionCompatible, isVersionCompatible,
checkPackageHasContent, checkPackageHasContent,
checkPackageHasContentWithContext,
validateRealpathInsideRoot, validateRealpathInsideRoot,
resolvePackConfigPath, resolvePackConfigPath,
} from '../../features/ensemble/takt-pack-config.js'; } from '../../features/ensemble/takt-pack-config.js';
@ -247,6 +248,14 @@ describe('checkPackageHasContent', () => {
expect(() => checkPackageHasContent(tempDir)).toThrow(); expect(() => checkPackageHasContent(tempDir)).toThrow();
}); });
it('should include manifest/path/hint details in contextual error', () => {
const manifestPath = join(tempDir, '.takt', 'takt-package.yaml');
expect(() => checkPackageHasContentWithContext(tempDir, {
manifestPath,
configuredPath: '.',
})).toThrow(/path: \.takt/);
});
it('should not throw when only faceted/ exists', () => { it('should not throw when only faceted/ exists', () => {
// Given: package with faceted/ only // Given: package with faceted/ only
mkdirSync(join(tempDir, 'faceted'), { recursive: true }); mkdirSync(join(tempDir, 'faceted'), { recursive: true });

View File

@ -10,6 +10,7 @@ import { mkdirSync, copyFileSync, existsSync, readFileSync, writeFileSync, rmSyn
import { join, dirname } from 'node:path'; import { join, dirname } from 'node:path';
import { tmpdir } from 'node:os'; import { tmpdir } from 'node:os';
import { execFileSync } from 'node:child_process'; import { execFileSync } from 'node:child_process';
import { createRequire } from 'node:module';
import { stringify as stringifyYaml } from 'yaml'; import { stringify as stringifyYaml } from 'yaml';
import { getEnsemblePackageDir } from '../../infra/config/paths.js'; import { getEnsemblePackageDir } from '../../infra/config/paths.js';
import { parseGithubSpec } from '../../features/ensemble/github-spec.js'; import { parseGithubSpec } from '../../features/ensemble/github-spec.js';
@ -18,7 +19,7 @@ import {
validateTaktPackPath, validateTaktPackPath,
validateMinVersion, validateMinVersion,
isVersionCompatible, isVersionCompatible,
checkPackageHasContent, checkPackageHasContentWithContext,
validateRealpathInsideRoot, validateRealpathInsideRoot,
resolvePackConfigPath, resolvePackConfigPath,
} from '../../features/ensemble/takt-pack-config.js'; } from '../../features/ensemble/takt-pack-config.js';
@ -33,7 +34,7 @@ import { confirm } from '../../shared/prompt/index.js';
import { info, success } from '../../shared/ui/index.js'; import { info, success } from '../../shared/ui/index.js';
import { createLogger, getErrorMessage } from '../../shared/utils/index.js'; import { createLogger, getErrorMessage } from '../../shared/utils/index.js';
// eslint-disable-next-line @typescript-eslint/no-require-imports const require = createRequire(import.meta.url);
const { version: TAKT_VERSION } = require('../../../package.json') as { version: string }; const { version: TAKT_VERSION } = require('../../../package.json') as { version: string };
const log = createLogger('ensemble-add'); const log = createLogger('ensemble-add');
@ -63,16 +64,15 @@ export async function ensembleAddCommand(spec: string): Promise<void> {
mkdirSync(tmpExtractDir, { recursive: true }); mkdirSync(tmpExtractDir, { recursive: true });
info(`📦 ${owner}/${repo} @${ref} をダウンロード中...`); info(`📦 ${owner}/${repo} @${ref} をダウンロード中...`);
execFileSync( const tarballBuffer = execFileSync(
'gh', 'gh',
[ [
'api', 'api',
`/repos/${owner}/${repo}/tarball/${ref}`, `/repos/${owner}/${repo}/tarball/${ref}`,
'--header', 'Accept: application/octet-stream',
'--output', tmpTarPath,
], ],
{ stdio: ['inherit', 'pipe', 'pipe'] }, { stdio: ['inherit', 'pipe', 'pipe'] },
); );
writeFileSync(tmpTarPath, tarballBuffer);
const tarVerboseList = execFileSync('tar', ['tvzf', tmpTarPath], { const tarVerboseList = execFileSync('tar', ['tvzf', tmpTarPath], {
encoding: 'utf-8', encoding: 'utf-8',
@ -112,7 +112,10 @@ export async function ensembleAddCommand(spec: string): Promise<void> {
validateRealpathInsideRoot(packageRoot, tmpExtractDir); validateRealpathInsideRoot(packageRoot, tmpExtractDir);
checkPackageHasContent(packageRoot); checkPackageHasContentWithContext(packageRoot, {
manifestPath: packConfigPath,
configuredPath: config.path,
});
const targets = collectCopyTargets(packageRoot); const targets = collectCopyTargets(packageRoot);
const facetFiles = targets.filter(t => t.relativePath.startsWith('faceted/')); const facetFiles = targets.filter(t => t.relativePath.startsWith('faceted/'));

View File

@ -23,6 +23,11 @@ export interface TaktPackConfig {
}; };
} }
interface PackageContentCheckContext {
manifestPath?: string;
configuredPath?: string;
}
const SEMVER_PATTERN = /^\d+\.\d+\.\d+$/; const SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
/** /**
@ -113,6 +118,41 @@ export function checkPackageHasContent(packageRoot: string): void {
} }
} }
/**
* Check package content and include user-facing diagnostics when empty.
*
* Adds manifest/configured-path details and a practical hint for nested layouts
* (e.g. when actual content is under ".takt/" but path remains ".").
*/
export function checkPackageHasContentWithContext(
packageRoot: string,
context: PackageContentCheckContext,
): void {
const hasFaceted = existsSync(join(packageRoot, 'faceted'));
const hasPieces = existsSync(join(packageRoot, 'pieces'));
if (hasFaceted || hasPieces) return;
const checkedFaceted = join(packageRoot, 'faceted');
const checkedPieces = join(packageRoot, 'pieces');
const configuredPath = context.configuredPath ?? '.';
const manifestPath = context.manifestPath ?? '(unknown)';
const hint = configuredPath === '.'
? `hint: If your package content is under ".takt/", set "path: .takt" in ${TAKT_PACKAGE_MANIFEST_FILENAME}.`
: `hint: Verify "path: ${configuredPath}" points to a directory containing faceted/ or pieces/.`;
throw new Error(
[
'Package content not found.',
`manifest: ${manifestPath}`,
`configured path: ${configuredPath}`,
`resolved package root: ${packageRoot}`,
`checked: ${checkedFaceted}`,
`checked: ${checkedPieces}`,
hint,
].join('\n'),
);
}
/** /**
* Resolve the path to takt-package.yaml within an extracted tarball directory. * Resolve the path to takt-package.yaml within an extracted tarball directory.
* *