Skip to content

Commit

Permalink
add root branch selection (#35)
Browse files Browse the repository at this point in the history
* add root branch selection

* cleanup
  • Loading branch information
Opeyem1a authored Oct 16, 2024
1 parent 37ec5c8 commit e3e832a
Show file tree
Hide file tree
Showing 23 changed files with 456 additions and 98 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
node_modules
dist
.env

/.local-config

# IntelliJ IDEA
/.idea
Expand Down
43 changes: 7 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,15 @@ CLI for stacked Git commands

## Development

### First-Time Setup
### Getting started

Before installing packages, make sure to authenticate your local `~/.npmrc` to
Github Packages so it can read Github Packages.

1. Generate a PAT using this guide:
[Creating a Personal Access Token (classic)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic)

1. Make sure the PAT has atleast `read:packages` scope like so:
![image showing permissions required for GH PAT](static/images/ghpermissions.png)

2. Copy the contents `.npmrc.sample` file into a newly created `.npmrc` file in
the root of this project. Replace `<YOUR_TOKEN_HERE>` with the PAT generated
above.

1. Alternative methods and further reading available on GH Docs here:
[Authenticating to Github Packages](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry#authenticating-with-a-personal-access-token).

3. Run `npm i` to install dependencies as usual.

4. Link the package to your global npm packages:
1. Create an environment file.
```bash
cp sample.env .env
```
2. Run `npm i` to install dependencies as usual.

3. Link the package to your global npm packages:
```bash
$ npm link && npm install --global gumption
```

## Documentation

```
$ gumption --help

Usage
$ gumption

Options
--name Your name

Examples
$ gumption --name=Jane
Hello, Jane
```
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@types/node": "^20.12.8",
"@types/react": "^18.0.32",
"chalk": "^5.2.0",
"dotenv": "^16.4.5",
"eslint": "^8.49.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
Expand Down
2 changes: 2 additions & 0 deletions sample.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# development | production
NODE_ENV=development
15 changes: 8 additions & 7 deletions src/commands/branch/new.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import BranchNew from './new.js';
import React from 'react';
import { Loading } from '../../components/loading.js';
import { Text } from 'ink';
import { delay } from '../../utils/time.js';
import { describe, expect, it, vi } from 'vitest';
Expand Down Expand Up @@ -45,7 +46,10 @@ vi.mock('../../services/git.js', () => {
};
});

const LOADING_MESSAGE = 'Loading...';
vi.mock('../../services/store.js', async () => {
const { mockStoreService } = await import('../../utils/test-helpers.js');
return mockStoreService({ rootInitialized: true });
});

describe('correctly renders changes commit UI', () => {
it('runs as intended', async () => {
Expand Down Expand Up @@ -73,8 +77,8 @@ describe('correctly renders changes commit UI', () => {

const ExpectedComp = () => {
return (
<Text bold color="green">
New branch created - {newBranchName}
<Text color="green">
New branch created - <Text bold>{newBranchName}</Text>
</Text>
);
};
Expand Down Expand Up @@ -106,10 +110,7 @@ describe('correctly renders changes commit UI', () => {
/>
);

const ExpectedComp = () => {
return <Text color="cyan">{LOADING_MESSAGE}</Text>;
};
const expected = render(<ExpectedComp />);
const expected = render(<Loading />);

await delay(ARBITRARY_DELAY / 2);
expect(actual1.lastFrame()).to.equal(expected.lastFrame());
Expand Down
32 changes: 26 additions & 6 deletions src/commands/branch/new.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,47 @@
import ErrorDisplay from '../../components/error-display.js';
import React, { useCallback } from 'react';
import React, { useCallback, useEffect, useState } from 'react';

Check warning on line 2 in src/commands/branch/new.tsx

View workflow job for this annotation

GitHub Actions / lint_test

'useEffect' is defined but never used

Check warning on line 2 in src/commands/branch/new.tsx

View workflow job for this annotation

GitHub Actions / lint_test

'useState' is defined but never used
import { Action, useAction } from '../../hooks/use-action.js';
import {
CommandConfig,
CommandProps,
PropSanitationResult,
Valid,
} from '../../types.js';
import { Loading } from '../../components/loading.js';
import { SelectRootBranch } from '../../components/select-root-branch.js';
import { Text } from 'ink';
import { safeBranchNameFromCommitMessage } from '../../utils/naming.js';
import { useGit } from '../../hooks/use-git.js';
import { useTree } from '../../hooks/use-tree.js';

function BranchNew(props: CommandProps) {
const args = branchNewConfig.getProps(props) as Valid<
PropSanitationResult<CommandArgs>
>;
const { commitMessage } = args.props;

const result = useBranchNew({ message: commitMessage });
const { rootBranchName } = useTree();

const result = useBranchNew({
message: commitMessage,
enabled: Boolean(rootBranchName),
});

if (!result.isEnabled) {
return <SelectRootBranch />;
}

if (result.isError) {
return <ErrorDisplay error={result.error} />;
}

if (result.isLoading) {
return <Text color="cyan">Loading...</Text>;
return <Loading />;
}

return (
<Text bold color="green">
New branch created - {result.branchName}
<Text color="green">
New branch created - <Text bold>{result.branchName}</Text>
</Text>
);
}
Expand All @@ -38,7 +50,13 @@ type UseBranchNewAction = Action & {
branchName: string;
};

const useBranchNew = ({ message }: { message: string }): UseBranchNewAction => {
const useBranchNew = ({
message,
enabled,
}: {
message: string;
enabled: boolean;
}): UseBranchNewAction => {
const git = useGit();

const branchName = safeBranchNameFromCommitMessage(message);
Expand All @@ -52,9 +70,11 @@ const useBranchNew = ({ message }: { message: string }): UseBranchNewAction => {

const action = useAction({
asyncAction: performAction,
enabled,
});

return {
isEnabled: action.isEnabled,
isLoading: action.isLoading,
isError: action.isError,
error: action.error,
Expand Down
12 changes: 7 additions & 5 deletions src/commands/changes/add.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ChangesAdd from './add.js';
import React from 'react';
import { Loading } from '../../components/loading.js';
import { Text } from 'ink';
import { delay } from '../../utils/time.js';
import { describe, expect, it, vi } from 'vitest';
Expand Down Expand Up @@ -28,7 +29,11 @@ vi.mock('../../services/git.js', () => {
};
});

const LOADING_MESSAGE = 'Loading...';
vi.mock('../../services/store.js', async () => {
const { mockStoreService } = await import('../../utils/test-helpers.js');
return mockStoreService({ rootInitialized: false });
});

const SUCCESS_MESSAGE = 'Staged all changes';

describe('correctly renders changes add UI', () => {
Expand Down Expand Up @@ -88,10 +93,7 @@ describe('correctly renders changes add UI', () => {
/>
);

const ExpectedComp = () => {
return <Text color="cyan">{LOADING_MESSAGE}</Text>;
};
const expected = render(<ExpectedComp />);
const expected = render(<Loading />);

await delay(ARBITRARY_DELAY / 2);
expect(actual1.lastFrame()).to.equal(expected.lastFrame());
Expand Down
3 changes: 2 additions & 1 deletion src/commands/changes/add.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import ErrorDisplay from '../../components/error-display.js';
import React, { useCallback } from 'react';
import { Action, useAction } from '../../hooks/use-action.js';
import { CommandConfig, CommandProps } from '../../types.js';
import { Loading } from '../../components/loading.js';
import { Text } from 'ink';
import { useGit } from '../../hooks/use-git.js';

Expand All @@ -13,7 +14,7 @@ function ChangedAdd({}: CommandProps) {
}

if (result.isLoading) {
return <Text color="cyan">Loading...</Text>;
return <Loading />;
}

return (
Expand Down
12 changes: 7 additions & 5 deletions src/commands/changes/commit.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ChangesCommit from './commit.js';
import React from 'react';
import { Loading } from '../../components/loading.js';
import { Text } from 'ink';
import { delay } from '../../utils/time.js';
import { describe, expect, it, vi } from 'vitest';
Expand Down Expand Up @@ -34,7 +35,11 @@ vi.mock('../../services/git.js', () => {
};
});

const LOADING_MESSAGE = 'Loading...';
vi.mock('../../services/store.js', async () => {
const { mockStoreService } = await import('../../utils/test-helpers.js');
return mockStoreService({ rootInitialized: true });
});

const SUCCESS_MESSAGE = 'Committed all changes';

describe('correctly renders changes commit UI', () => {
Expand Down Expand Up @@ -94,10 +99,7 @@ describe('correctly renders changes commit UI', () => {
/>
);

const ExpectedComp = () => {
return <Text color="cyan">{LOADING_MESSAGE}</Text>;
};
const expected = render(<ExpectedComp />);
const expected = render(<Loading />);

await delay(ARBITRARY_DELAY / 2);
expect(actual1.lastFrame()).to.equal(expected.lastFrame());
Expand Down
26 changes: 23 additions & 3 deletions src/commands/changes/commit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,34 @@ import {
PropSanitationResult,
Valid,
} from '../../types.js';
import { Loading } from '../../components/loading.js';
import { SelectRootBranch } from '../../components/select-root-branch.js';
import { Text } from 'ink';
import { useGit } from '../../hooks/use-git.js';
import { useTree } from '../../hooks/use-tree.js';

function ChangesCommit(props: CommandProps) {
const args = changesCommitConfig.getProps(props) as Valid<
PropSanitationResult<CommandArgs>
>;
const result = useChangesCommit({ message: args.props.message });

const { rootBranchName } = useTree();

const result = useChangesCommit({
message: args.props.message,
enabled: Boolean(rootBranchName),
});

if (!result.isEnabled) {
return <SelectRootBranch />;
}

if (result.isError) {
return <ErrorDisplay error={result.error} />;
}

if (result.isLoading) {
return <Text color="cyan">Loading...</Text>;
return <Loading />;
}

return (
Expand All @@ -31,7 +44,13 @@ function ChangesCommit(props: CommandProps) {
);
}

const useChangesCommit = ({ message }: { message: string }): Action => {
const useChangesCommit = ({
message,
enabled,
}: {
message: string;
enabled: boolean;
}): Action => {
const git = useGit();

const performAction = useCallback(async () => {
Expand All @@ -41,6 +60,7 @@ const useChangesCommit = ({ message }: { message: string }): Action => {

return useAction({
asyncAction: performAction,
enabled,
});
};

Expand Down
7 changes: 6 additions & 1 deletion src/commands/hop.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import GumptionItemComponent from '../components/gumption-item-component.js';
import Hop from './hop.js';
import React from 'react';
import SelectInput from 'ink-select-input';
import { KEYS } from '../utils/test-helpers.js';
import { KEYS, mockStoreService } from '../utils/test-helpers.js';

Check warning on line 5 in src/commands/hop.test.tsx

View workflow job for this annotation

GitHub Actions / lint_test

'mockStoreService' is defined but never used
import { delay } from '../utils/time.js';
import { describe, expect, it, vi } from 'vitest';
import { render } from 'ink-testing-library';
Expand All @@ -29,6 +29,11 @@ vi.mock('../services/git.js', () => {
};
});

vi.mock('../services/store.js', async () => {
const { mockStoreService } = await import('../utils/test-helpers.js');
return mockStoreService({ rootInitialized: false });
});

describe('correctly renders hop UI', () => {
it('displays branch names in a list', async () => {
const actual1 = render(
Expand Down
Loading

0 comments on commit e3e832a

Please sign in to comment.