resolved #52, resolved #59

This commit is contained in:
nrslib 2026-01-31 19:25:50 +09:00
parent f8fc9a7c83
commit 1e8909d512
5 changed files with 118 additions and 44 deletions

View File

@ -127,6 +127,9 @@ takt --task "fix the auth bug" -w magi -b feat/fix-auth
# Specify repository (for PR creation)
takt --task "fix the auth bug" --auto-pr --repo owner/repo
# Run workflow only — skip branch creation, commit, and push
takt --task "fix the auth bug" --skip-git
```
In pipeline mode, PRs are **not** created unless `--auto-pr` is explicitly specified.
@ -154,6 +157,7 @@ In pipeline mode, PRs are **not** created unless `--auto-pr` is explicitly speci
| `-w, --workflow <name>` | Workflow to use |
| `-b, --branch <name>` | Branch name (auto-generated if omitted) |
| `--auto-pr` | Create PR after execution (interactive: skip confirmation, pipeline: enable PR) |
| `--skip-git` | Skip branch creation, commit, and push (pipeline mode, workflow-only) |
| `--repo <owner/repo>` | Repository for PR creation |
## Workflows

View File

@ -125,6 +125,9 @@ takt --task "バグを修正" -w magi -b feat/fix-bug
# リポジトリ指定PR作成時
takt --task "バグを修正" --auto-pr --repo owner/repo
# ワークフロー実行のみブランチ作成・commit・pushをスキップ
takt --task "バグを修正" --skip-git
```
パイプラインモードでは `--auto-pr` を指定しない限りPRは作成されません。
@ -152,6 +155,7 @@ takt --task "バグを修正" --auto-pr --repo owner/repo
| `-w, --workflow <name>` | ワークフロー指定 |
| `-b, --branch <name>` | ブランチ名指定(省略時は自動生成) |
| `--auto-pr` | PR作成対話: 確認スキップ、パイプライン: PR有効化 |
| `--skip-git` | ブランチ作成・commit・pushをスキップパイプラインモード、ワークフロー実行のみ |
| `--repo <owner/repo>` | リポジトリ指定PR作成時 |
## ワークフロー

View File

@ -345,4 +345,57 @@ describe('executePipeline', () => {
);
});
});
describe('--skip-git', () => {
it('should skip branch creation, commit, push when skipGit is true', async () => {
mockExecuteTask.mockResolvedValueOnce(true);
const exitCode = await executePipeline({
task: 'Fix the bug',
workflow: 'default',
autoPr: false,
skipGit: true,
cwd: '/tmp/test',
});
expect(exitCode).toBe(0);
expect(mockExecuteTask).toHaveBeenCalledWith('Fix the bug', '/tmp/test', 'default');
// No git operations should have been called
const gitCalls = mockExecFileSync.mock.calls.filter(
(call: unknown[]) => call[0] === 'git',
);
expect(gitCalls).toHaveLength(0);
expect(mockPushBranch).not.toHaveBeenCalled();
});
it('should ignore --auto-pr when skipGit is true', async () => {
mockExecuteTask.mockResolvedValueOnce(true);
const exitCode = await executePipeline({
task: 'Fix the bug',
workflow: 'default',
autoPr: true,
skipGit: true,
cwd: '/tmp/test',
});
expect(exitCode).toBe(0);
expect(mockCreatePullRequest).not.toHaveBeenCalled();
});
it('should still return workflow failure exit code when skipGit is true', async () => {
mockExecuteTask.mockResolvedValueOnce(false);
const exitCode = await executePipeline({
task: 'Fix the bug',
workflow: 'default',
autoPr: false,
skipGit: true,
cwd: '/tmp/test',
});
expect(exitCode).toBe(3);
});
});
});

View File

@ -198,7 +198,8 @@ program
.option('-b, --branch <name>', 'Branch name (auto-generated if omitted)')
.option('--auto-pr', 'Create PR after successful execution')
.option('--repo <owner/repo>', 'Repository (defaults to current)')
.option('-t, --task <string>', 'Task content (triggers pipeline/non-interactive mode)');
.option('-t, --task <string>', 'Task content (triggers pipeline/non-interactive mode)')
.option('--skip-git', 'Skip branch creation, commit, and push (pipeline mode)');
// Common initialization for all commands
program.hook('preAction', async () => {
@ -327,6 +328,7 @@ program
branch: opts.branch as string | undefined,
autoPr: opts.autoPr === true,
repo: opts.repo as string | undefined,
skipGit: opts.skipGit === true,
cwd: resolvedCwd,
});

View File

@ -39,6 +39,8 @@ export interface PipelineExecutionOptions {
autoPr: boolean;
/** Repository in owner/repo format */
repo?: string;
/** Skip branch creation, commit, and push (workflow-only execution) */
skipGit?: boolean;
/** Working directory */
cwd: string;
}
@ -135,7 +137,7 @@ function buildPipelinePrBody(
* Returns a process exit code (0 on success, 2-5 on specific failures).
*/
export async function executePipeline(options: PipelineExecutionOptions): Promise<number> {
const { cwd, workflow, autoPr } = options;
const { cwd, workflow, autoPr, skipGit } = options;
const globalConfig = loadGlobalConfig();
const pipelineConfig = globalConfig.pipeline;
let issue: GitHubIssue | undefined;
@ -164,8 +166,10 @@ export async function executePipeline(options: PipelineExecutionOptions): Promis
return EXIT_ISSUE_FETCH_FAILED;
}
// --- Step 2: Create branch ---
const branch = options.branch ?? generatePipelineBranchName(pipelineConfig, options.issueNumber);
// --- Step 2: Create branch (skip if --skip-git) ---
let branch: string | undefined;
if (!skipGit) {
branch = options.branch ?? generatePipelineBranchName(pipelineConfig, options.issueNumber);
info(`Creating branch: ${branch}`);
try {
createBranch(cwd, branch);
@ -174,10 +178,11 @@ export async function executePipeline(options: PipelineExecutionOptions): Promis
error(`Failed to create branch: ${err instanceof Error ? err.message : String(err)}`);
return EXIT_GIT_OPERATION_FAILED;
}
}
// --- Step 3: Run workflow ---
info(`Running workflow: ${workflow}`);
log.info('Pipeline workflow execution starting', { workflow, branch, issueNumber: options.issueNumber });
log.info('Pipeline workflow execution starting', { workflow, branch, skipGit, issueNumber: options.issueNumber });
const taskSuccess = await executeTask(task, cwd, workflow);
@ -187,7 +192,8 @@ export async function executePipeline(options: PipelineExecutionOptions): Promis
}
success(`Workflow '${workflow}' completed`);
// --- Step 4: Commit & push ---
// --- Step 4: Commit & push (skip if --skip-git) ---
if (!skipGit && branch) {
const commitMessage = buildCommitMessage(pipelineConfig, issue, options.task);
info('Committing changes...');
@ -206,9 +212,13 @@ export async function executePipeline(options: PipelineExecutionOptions): Promis
error(`Git operation failed: ${err instanceof Error ? err.message : String(err)}`);
return EXIT_GIT_OPERATION_FAILED;
}
}
// --- Step 5: Create PR (if --auto-pr) ---
if (autoPr) {
if (skipGit) {
info('--auto-pr is ignored when --skip-git is specified (no push was performed)');
} else if (branch) {
info('Creating pull request...');
const prTitle = issue ? issue.title : (options.task ?? 'Pipeline task');
const report = `Workflow \`${workflow}\` completed successfully.`;
@ -228,11 +238,12 @@ export async function executePipeline(options: PipelineExecutionOptions): Promis
return EXIT_PR_CREATION_FAILED;
}
}
}
// --- Summary ---
console.log();
status('Issue', issue ? `#${issue.number} "${issue.title}"` : 'N/A');
status('Branch', branch);
status('Branch', branch ?? '(current)');
status('Workflow', workflow);
status('Result', 'Success', 'green');