-
Notifications
You must be signed in to change notification settings - Fork 0
TypeScript Rewrite: Workflow command #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
d4mation
wants to merge
15
commits into
ENG-219/app-bootstrap
Choose a base branch
from
ENG-219/command-workflow
base: ENG-219/app-bootstrap
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
5b7db4b
ENG-219: Add workflow command
d4mation 64c3411
Merge branch 'ENG-219/app-bootstrap' into ENG-219/command-workflow
d4mation 89adf8e
Merge branch 'ENG-219/app-bootstrap' into ENG-219/command-workflow
d4mation 15e3b80
Merge branch 'ENG-219/app-bootstrap' into ENG-219/command-workflow
d4mation f762e33
Merge branch 'ENG-219/app-bootstrap' into ENG-219/command-workflow
d4mation 2ef21e9
ENG-219: Use createTempProject in workflow tests for isolation
d4mation 0e5356d
ENG-219: Fix unused variable lint error in workflow command
d4mation 2a11f4c
Merge branch 'ENG-219/app-bootstrap' into ENG-219/command-workflow
d4mation ef9b094
ENG-219: Add tests for workflow --root flag and clarify its description
d4mation 5dfe278
Merge branch 'ENG-219/app-bootstrap' into ENG-219/command-workflow
d4mation 4e4ec1d
Merge branch 'ENG-219/app-bootstrap' into ENG-219/command-workflow
d4mation 16ace9d
Merge branch 'ENG-219/app-bootstrap' into ENG-219/command-workflow
d4mation b4480be
Merge branch 'ENG-219/app-bootstrap' into ENG-219/command-workflow
d4mation d79cd20
ENG-219: Merge app-bootstrap, update imports to .ts, remove envVarNames
d4mation b2e4c68
ENG-219: Remove unused workingDir arg from getConfig() call
d4mation File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| import type { Command } from 'commander'; | ||
| import { getConfig } from '../config.ts'; | ||
| import { runCommand } from '../utils/process.ts'; | ||
| import * as output from '../utils/output.ts'; | ||
|
|
||
| /** | ||
| * Registers the `workflow` (and `do` alias) command with the CLI program. | ||
| * | ||
| * @since TBD | ||
| * | ||
| * @param {Command} program - The Commander.js program instance. | ||
| * | ||
| * @returns {void} | ||
| */ | ||
| export function registerWorkflowCommand(program: Command): void { | ||
| program | ||
| .command('workflow <workflow>') | ||
| .alias('do') | ||
| .description('Run a command workflow.') | ||
| .option('--root <dir>', 'Run workflow commands in the given directory instead of the current working directory.') | ||
| .allowUnknownOption(true) | ||
| .action( | ||
| async ( | ||
| workflowName: string, | ||
| options: { root?: string }, | ||
| command: Command | ||
| ) => { | ||
| await executeWorkflow(workflowName, options, command); | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Executes a named workflow's commands sequentially, passing through extra arguments. | ||
| * | ||
| * @since TBD | ||
| * | ||
| * @param {string} workflowName - The name of the workflow to execute. | ||
| * @param {object} options - The options object. | ||
| * @param {string} [options.root] - The root directory for running commands. | ||
| * @param {Command} command - The Commander.js command instance (used to extract extra arguments). | ||
| * | ||
| * @returns {Promise<void>} | ||
| */ | ||
| async function executeWorkflow( | ||
| workflowName: string, | ||
| options: { root?: string }, | ||
| command: Command | ||
| ): Promise<void> { | ||
| const config = getConfig(); | ||
| const workflows = config.getWorkflows(); | ||
| const cwd = options.root ?? config.getWorkingDir(); | ||
|
|
||
| const workflow = workflows.get(workflowName); | ||
| if (!workflow) { | ||
| output.error(`The workflow '${workflowName}' does not exist.`); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| // Extract extra args passed after -- | ||
| const extraArgs = command.args.slice(1); // First arg is the workflow name | ||
|
|
||
| output.log(`Running workflow: ${workflowName}`); | ||
|
|
||
| for (const step of workflow.commands) { | ||
| let cmd = step; | ||
| let bailOnFailure = true; | ||
|
|
||
| if (cmd.startsWith('@')) { | ||
| bailOnFailure = false; | ||
| cmd = cmd.slice(1); | ||
| } | ||
|
|
||
| // Append extra args | ||
| if (extraArgs.length > 0) { | ||
| cmd += ' ' + extraArgs.join(' '); | ||
| } | ||
|
|
||
| output.section(`> ${cmd}`); | ||
|
|
||
| const result = await runCommand(cmd, { | ||
| cwd, | ||
| }); | ||
|
|
||
| if (result.exitCode !== 0) { | ||
| output.error(`[FAIL] Workflow step failed: ${cmd}`); | ||
| if (bailOnFailure) { | ||
| output.error('Exiting...'); | ||
| process.exit(result.exitCode); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| output.success('Workflow complete.'); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| import path from 'node:path'; | ||
| import { | ||
| runPup, | ||
| writePuprc, | ||
| getPuprc, | ||
| createTempProject, | ||
| cleanupTempProjects, | ||
| fixturesDir, | ||
| } from '../helpers/setup.js'; | ||
|
|
||
| describe('workflow command', () => { | ||
| let projectDir: string; | ||
|
|
||
| beforeEach(() => { | ||
| projectDir = createTempProject(); | ||
| writePuprc(getPuprc(), projectDir); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| cleanupTempProjects(); | ||
| }); | ||
|
|
||
| it('should run a workflow', async () => { | ||
| const puprc = getPuprc(); | ||
| puprc.workflows = { | ||
| 'my-workflow': ['echo "workflow step 1"', 'echo "workflow step 2"'], | ||
| }; | ||
| writePuprc(puprc, projectDir); | ||
|
|
||
| const result = await runPup('workflow my-workflow', { cwd: projectDir }); | ||
| expect(result.exitCode).toBe(0); | ||
| expect(result.output).toContain('workflow step 1'); | ||
| expect(result.output).toContain('workflow step 2'); | ||
| }); | ||
|
|
||
| it('should support "do" alias', async () => { | ||
| const puprc = getPuprc(); | ||
| puprc.workflows = { | ||
| 'my-workflow': ['echo "workflow via do"'], | ||
| }; | ||
| writePuprc(puprc, projectDir); | ||
|
|
||
| const result = await runPup('do my-workflow', { cwd: projectDir }); | ||
| expect(result.exitCode).toBe(0); | ||
| expect(result.output).toContain('workflow via do'); | ||
| }); | ||
|
|
||
| it('should error when workflow does not exist', async () => { | ||
| const result = await runPup('workflow nonexistent', { cwd: projectDir }); | ||
| expect(result.exitCode).not.toBe(0); | ||
| }); | ||
|
|
||
| it('should pass extra args to workflow', async () => { | ||
| const scriptPath = path.resolve(fixturesDir, 'test-workflow-script.sh'); | ||
| const puprc = getPuprc(); | ||
| puprc.workflows = { | ||
| 'my-workflow': [`bash ${scriptPath}`], | ||
| }; | ||
| writePuprc(puprc, projectDir); | ||
|
|
||
| const result = await runPup('workflow my-workflow -- --option1 value1', { cwd: projectDir }); | ||
| expect(result.exitCode).toBe(0); | ||
| }); | ||
|
|
||
| it('should support multiple workflow commands', async () => { | ||
| const puprc = getPuprc(); | ||
| puprc.workflows = { | ||
| 'my-workflow': [ | ||
| 'echo "step 1"', | ||
| 'echo "step 2"', | ||
| 'echo "step 3"', | ||
| ], | ||
| }; | ||
| writePuprc(puprc, projectDir); | ||
|
|
||
| const result = await runPup('workflow my-workflow', { cwd: projectDir }); | ||
| expect(result.exitCode).toBe(0); | ||
| expect(result.output).toContain('step 1'); | ||
| expect(result.output).toContain('step 2'); | ||
| expect(result.output).toContain('step 3'); | ||
| }); | ||
|
|
||
| it('should handle soft-fail commands in workflows', async () => { | ||
| const puprc = getPuprc(); | ||
| puprc.workflows = { | ||
| 'my-workflow': [ | ||
| '@false', | ||
| 'echo "still running"', | ||
| ], | ||
| }; | ||
| writePuprc(puprc, projectDir); | ||
|
|
||
| const result = await runPup('workflow my-workflow', { cwd: projectDir }); | ||
| expect(result.exitCode).toBe(0); | ||
| expect(result.output).toContain('still running'); | ||
| }); | ||
|
|
||
| describe('--root flag', () => { | ||
| let rootDir: string; | ||
|
|
||
| beforeEach(() => { | ||
| rootDir = createTempProject(); | ||
| }); | ||
|
|
||
| it('should run workflow commands in the --root directory', async () => { | ||
| const puprc = getPuprc(); | ||
| puprc.workflows = { | ||
| 'pwd-workflow': ['pwd'], | ||
| }; | ||
| writePuprc(puprc, projectDir); | ||
|
|
||
| const result = await runPup(`workflow pwd-workflow --root ${rootDir}`, { cwd: projectDir }); | ||
| expect(result.exitCode).toBe(0); | ||
| expect(result.output).toContain(rootDir); | ||
| }); | ||
|
|
||
| it('should work with the "do" alias and --root', async () => { | ||
| const puprc = getPuprc(); | ||
| puprc.workflows = { | ||
| 'my-workflow': ['pwd'], | ||
| }; | ||
| writePuprc(puprc, projectDir); | ||
|
|
||
| const result = await runPup(`do my-workflow --root ${rootDir}`, { cwd: projectDir }); | ||
| expect(result.exitCode).toBe(0); | ||
| expect(result.output).toContain(rootDir); | ||
| }); | ||
|
|
||
| it('should pass extra args with --root', async () => { | ||
| const scriptPath = path.resolve(fixturesDir, 'test-workflow-script.sh'); | ||
| const puprc = getPuprc(); | ||
| puprc.workflows = { | ||
| 'my-workflow': [`bash ${scriptPath}`], | ||
| }; | ||
| writePuprc(puprc, projectDir); | ||
|
|
||
| const result = await runPup(`workflow my-workflow --root ${rootDir} -- --option1 value1`, { cwd: projectDir }); | ||
| expect(result.exitCode).toBe(0); | ||
| }); | ||
| }); | ||
| }); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is how this flag worked in the PHP version as well. The description for it just wasn't clear.