diff options
-rw-r--r-- | .codespellrc | 6 | ||||
-rw-r--r-- | .github/workflows/codespell.yml | 19 | ||||
-rw-r--r-- | README.md | 51 | ||||
-rw-r--r-- | bridge/core/bridge.go | 2 | ||||
-rw-r--r-- | bridge/github/client.go | 2 | ||||
-rw-r--r-- | bridge/github/import_test.go | 4 | ||||
-rw-r--r-- | bridge/gitlab/export.go | 2 | ||||
-rw-r--r-- | bridge/jira/client.go | 2 | ||||
-rw-r--r-- | bridge/launchpad/launchpad_api.go | 4 | ||||
-rw-r--r-- | cache/subcache.go | 51 | ||||
-rw-r--r-- | doc/architecture.md | 4 | ||||
-rw-r--r-- | doc/feature_matrix.md | 137 | ||||
-rw-r--r-- | doc/howto-github.md | 2 | ||||
-rw-r--r-- | entities/identity/key.go | 2 | ||||
-rw-r--r-- | entity/dag/op_set_metadata_test.go | 8 | ||||
-rw-r--r-- | entity/dag/operation_pack.go | 2 | ||||
-rw-r--r-- | misc/git_integration/git-bug.go | 1 | ||||
-rw-r--r-- | webui/src/components/Header/Header.tsx | 2 |
18 files changed, 248 insertions, 53 deletions
diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 00000000..750692b5 --- /dev/null +++ b/.codespellrc @@ -0,0 +1,6 @@ +[codespell] +skip = .git,.venv,*.svg,package-lock.json +# ot,fo,te - used as short variable names +# optionall - OptionAll but codespell is case insensitive +# testing - TestIn +ignore-words-list = ot,fo,te,optionall,testin diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml new file mode 100644 index 00000000..5768d7c6 --- /dev/null +++ b/.github/workflows/codespell.yml @@ -0,0 +1,19 @@ +--- +name: Codespell + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + codespell: + name: Check for spelling errors + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Codespell + uses: codespell-project/actions-codespell@v1 @@ -25,8 +25,6 @@ - **integrates with your tooling**: use the UI you like (CLI, terminal, web) or integrate with your existing tools through the CLI or the GraphQL API - **bridges to other bug trackers**: use [bridges](#bridges) to import and export to other trackers. -:construction: This is now more than a proof of concept, but still not fully stable. Expect dragons and unfinished business. :construction: - ## Help needed! This project has grown bigger than I can handle by myself, especially with a day job. I'm looking for people to help on or maintain part of it: @@ -182,7 +180,7 @@ An interactive terminal UI is available using the command `git bug termui` to br ![Termui recording](misc/termui_recording.gif) -## Web UI (status: WIP) +## Web UI You can launch a rich Web UI with `git bug webui`. @@ -200,34 +198,28 @@ The web UI interact with the backend through a GraphQL API. The schema is availa ## Bridges +β
: working π : partial implementation β: not working + ### Importer implementations -| | Github | Gitlab | Jira | Launchpad | -|-------------------------------------------------|:------------------:|:------------------:|:------------------:|:------------------:| -| **incremental**<br/>(can import more than once) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | -| **with resume**<br/>(download only new data) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | -| **identities** | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| identities update | :x: | :x: | :x: | :x: | -| **bug** | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| comments | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| comment editions | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: | -| labels | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | -| status | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | -| title edition | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | -| **media/files** | :x: | :x: | :x: | :x: | -| **automated test suite** | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | +| | Github | Gitlab | Jira | Launchpad | +|-------------------------------------------------|:------:|:------:|:----:|:---------:| +| **incremental**<br/>(can import more than once) | β
| β
| β
| β | +| **with resume**<br/>(download only new data) | β
| β
| β
| β | +| **identities** | π | π | π | π | +| **bugs** | β
| β
| β
| π | +| **board** | β | β | β | β | +| **media/files** | β | β | β | β | +| **automated test suite** | β
| β
| β | β | ### Exporter implementations -| | Github | Gitlab | Jira | Launchpad | -|--------------------------|:------------------:|:------------------:|:------------------:|:---------:| -| **bug** | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | -| comments | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | -| comment editions | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | -| labels | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | -| status | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | -| title edition | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | -| **automated test suite** | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | +| | Github | Gitlab | Jira | Launchpad | +|--------------------------|:------:|:------:|:----:|:---------:| +| **identities** | π | π | π | π | +| **bug** | β
| β
| β
| β | +| **board** | β | β | β | β | +| **automated test suite** | β
| β
| β | β | #### Bridge usage @@ -281,14 +273,15 @@ See also all the [docs](doc). ## Planned features -- media embedding -- more bridges +The [feature matrix](doc/feature_matrix.md) gives a good overview of what is planned, without being exhaustive. + +Additional planned feature: - webUI that can be used as a public portal to accept user's input - inflatable raptor ## Contribute -PRs accepted. Drop by the [Gitter lobby](https://gitter.im/the-git-bug/Lobby) for a chat or browse the issues to see what is worked on or discussed. +PRs accepted. Drop by the [Gitter lobby](https://gitter.im/the-git-bug/Lobby) or the [Matrix room](https://matrix.to/#/#the-git-bug_Lobby:gitter.im) for a chat, look at the [feature matrix](doc/feature_matrix.md) or browse the issues to see what is worked on or discussed. ```shell git clone git@github.com:MichaelMure/git-bug.git diff --git a/bridge/core/bridge.go b/bridge/core/bridge.go index b410b470..1fc631f0 100644 --- a/bridge/core/bridge.go +++ b/bridge/core/bridge.go @@ -141,7 +141,7 @@ func DefaultBridge(repo *cache.RepoCache) (*Bridge, error) { } if len(bridges) > 1 { - return nil, fmt.Errorf("multiple bridge are configured, you need to select one explicitely") + return nil, fmt.Errorf("multiple bridge are configured, you need to select one explicitly") } return LoadBridge(repo, bridges[0]) diff --git a/bridge/github/client.go b/bridge/github/client.go index 361d0ee5..974c3067 100644 --- a/bridge/github/client.go +++ b/bridge/github/client.go @@ -20,7 +20,7 @@ type Client interface { Query(context.Context, interface{}, map[string]interface{}) error } -// rateLimitHandlerClient wrapps the Github client and adds improved error handling and handling of +// rateLimitHandlerClient wraps the Github client and adds improved error handling and handling of // Github's GraphQL rate limit. type rateLimitHandlerClient struct { sc Client diff --git a/bridge/github/import_test.go b/bridge/github/import_test.go index 5fafcce1..52a3f852 100644 --- a/bridge/github/import_test.go +++ b/bridge/github/import_test.go @@ -123,11 +123,11 @@ func TestGithubImporter(t *testing.T) { }, }, { - name: "transfered issue", + name: "transferred issue", url: "https://github.com/MichaelMure/git-bug-test-github-bridge/issues/8", bug: &bug.Snapshot{ Operations: []dag.Operation{ - bug.NewCreateOp(author, 0, "transfered issue", "", nil), + bug.NewCreateOp(author, 0, "transferred issue", "", nil), }, }, }, diff --git a/bridge/gitlab/export.go b/bridge/gitlab/export.go index b3a02447..3beaec38 100644 --- a/bridge/gitlab/export.go +++ b/bridge/gitlab/export.go @@ -356,7 +356,7 @@ func (ge *gitlabExporter) exportBug(ctx context.Context, b *cache.BugCache, out case *bug.LabelChangeOperation: // we need to set the actual list of labels at each label change operation - // because gitlab update issue requests need directly the latest list of the verison + // because gitlab update issue requests need directly the latest list of the version for _, label := range op.Added { labelSet[label.String()] = struct{}{} diff --git a/bridge/jira/client.go b/bridge/jira/client.go index c5fd1776..0e4e561f 100644 --- a/bridge/jira/client.go +++ b/bridge/jira/client.go @@ -1361,7 +1361,7 @@ func (client *Client) DoTransition(issueKeyOrID string, transitionID string) (ti // TODO(josh)[767ee72]: Figure out a good way to "configure" the // open/close state mapping. It would be *great* if we could actually - // *compute* the necessary transitions and prompt for missing metatdata... + // *compute* the necessary transitions and prompt for missing metadata... // but that is complex var buffer bytes.Buffer _, _ = fmt.Fprintf(&buffer, diff --git a/bridge/launchpad/launchpad_api.go b/bridge/launchpad/launchpad_api.go index 763e774e..d8b31e44 100644 --- a/bridge/launchpad/launchpad_api.go +++ b/bridge/launchpad/launchpad_api.go @@ -10,7 +10,7 @@ package launchpad * - SearchTasks should yield bugs one by one * * TODO (maybe): - * - Authentication (this might help retrieving email adresses) + * - Authentication (this might help retrieving email addresses) */ import ( @@ -83,7 +83,7 @@ func (lapi *launchpadAPI) SearchTasks(ctx context.Context, project string) ([]LP var bugs []LPBug // First, let us build the URL. Not all statuses are included by - // default, so we have to explicitely enumerate them. + // default, so we have to explicitly enumerate them. validStatuses := [13]string{ "New", "Incomplete", "Opinion", "Invalid", "Won't Fix", "Expired", "Confirmed", "Triaged", diff --git a/cache/subcache.go b/cache/subcache.go index 09e53c23..b0ba6e52 100644 --- a/cache/subcache.go +++ b/cache/subcache.go @@ -187,6 +187,27 @@ func (sc *SubCache[EntityT, ExcerptT, CacheT]) write() error { } func (sc *SubCache[EntityT, ExcerptT, CacheT]) Build() <-chan BuildEvent { + // value chosen experimentally as giving the fasted indexing, while + // not driving the cache size on disk too high. + // + // | batchCount | bugIndex (MB) | idIndex (kB) | time (s) | + // |:----------:|:-------------:|:------------:|:--------:| + // | 10 | 24 | 84 | 1,59 | + // | 30 | 26 | 84 | 1,388 | + // | 50 | 26 | 84 | 1,44 | + // | 60 | 26 | 80 | 1,377 | + // | 68 | 27 | 80 | 1,385 | + // | 75 | 26 | 84 | 1,32 | + // | 80 | 26 | 80 | 1,37 | + // | 85 | 27 | 80 | 1,317 | + // | 100 | 26 | 80 | 1,455 | + // | 150 | 26 | 80 | 2,066 | + // | 200 | 28 | 80 | 2,885 | + // | 250 | 30 | 72 | 3,555 | + // | 300 | 31 | 72 | 4,787 | + // | 500 | 23 | 72 | 5,4 | + const maxBatchCount = 75 + out := make(chan BuildEvent) go func() { @@ -221,6 +242,7 @@ func (sc *SubCache[EntityT, ExcerptT, CacheT]) Build() <-chan BuildEvent { } indexer, indexEnd := index.IndexBatch() + var batchCount int for e := range allEntities { if e.Err != nil { @@ -245,6 +267,21 @@ func (sc *SubCache[EntityT, ExcerptT, CacheT]) Build() <-chan BuildEvent { return } + batchCount++ + if batchCount >= maxBatchCount { + err = indexEnd() + if err != nil { + out <- BuildEvent{ + Typename: sc.typename, + Err: err, + } + return + } + + indexer, indexEnd = index.IndexBatch() + batchCount = 0 + } + out <- BuildEvent{ Typename: sc.typename, Event: BuildEventProgress, @@ -253,13 +290,15 @@ func (sc *SubCache[EntityT, ExcerptT, CacheT]) Build() <-chan BuildEvent { } } - err = indexEnd() - if err != nil { - out <- BuildEvent{ - Typename: sc.typename, - Err: err, + if batchCount > 0 { + err = indexEnd() + if err != nil { + out <- BuildEvent{ + Typename: sc.typename, + Err: err, + } + return } - return } err = sc.write() diff --git a/doc/architecture.md b/doc/architecture.md index 5f81462b..395886ea 100644 --- a/doc/architecture.md +++ b/doc/architecture.md @@ -88,7 +88,7 @@ The package `termui` contains the interactive terminal user interface, implement ## graphql -The package `graphql` implement the GraphQL API, mapping the data model and providing read/write access from outside of the process. This API is in particular used by the webUI but could be used to implement other user interfaces or bridges with other systems. +The package `graphql` implement the GraphQL API, mapping the data model and providing read/write access from outside the process. This API is in particular used by the webUI but could be used to implement other user interfaces or bridges with other systems. ## webui @@ -96,7 +96,7 @@ The package `webui` hold the web based user interface, implemented in both go an The javascript code is compiled and packaged inside the go binary, allowing for a single file distribution of git-bug. -When the webUI is started from the CLI command, a localhost HTTP server is started to serve the webUI resources (html, js, css), as well as the GraphQL API. When the webUI is loaded in the browser, it interact with the git-bug process through the GraphQL API to load and edit bugs. +When the webUI is started from the CLI command, a localhost HTTP server is started to serve the webUI resources (html, js, css), as well as the GraphQL API. When the webUI is loaded in the browser, it interacts with the git-bug process through the GraphQL API to load and edit bugs. ## bridge diff --git a/doc/feature_matrix.md b/doc/feature_matrix.md new file mode 100644 index 00000000..ee551bd4 --- /dev/null +++ b/doc/feature_matrix.md @@ -0,0 +1,137 @@ +# User facing capabilities + +This document tries to give an overview of what is currently supported, and by extension where effort can be focused to bring feature completion and parity. + +As git-bug is a free software project, accept and rely on contributor, those feature matrices kinda define a roadmap, in the sense than anything mentioned below is a planned feature and can be worked on. This does not mean that a feature not mentioned here should not be considered, just maybe check the issue tracker and come talk about it. + +This document however does not show all the untold work required to support those user-facing capabilities. There has been a ton of work behind the scene and more will be required over time. + +β
: working π : partial implementation β: not working + +## Other goals + +Some goals don't really fit below, so I'll mention them here: +- have the webUI accept external OAuth (Github, ...) and act as a public portal where user outside the project can browse and interact with the project +- project configuration (valid labels, ...) +- commit signature to fully authenticate user's interaction +- interface with the system keyring, to distribute and expose known public keys and allow checking signed commit in normal git workflow +- privileged roles (admin, ...) and enforcing the corresponding rules +- package the webui as a desktop app + +Additionally, some other are captured as [Github issues](https://github.com/MichaelMure/git-bug/issues) or [Discussions](https://github.com/MichaelMure/git-bug/discussions). + +## Entities + +The most high level overview of what kind of entities are supported and where. + +| | Core | CLI | TermUI | WebUI | +|----------------|:----:|:---:|:------:|:-----:| +| Identities | β
| β
| β
| β
| +| Bug | β
| β
| β
| β
| +| Board | π | π | β | β | +| Pull-request | β | β | β | β | +| Project Config | β | β | β | β | + +More specific features across the board. + +| | Core | CLI | TermUI | WebUI | +|--------------------|:----:|:---:|:------:|:-----:| +| Media embedding | π | β | β | β | +| Fast indexing | β
| β
| β
| β
| +| Markdown rendering | N/A | β | β | β
| + +#### Identities + +| | Core | CLI | TermUI | WebUI | +|-------------------------|:----:|:---:|:------:|:-----:| +| Public keys | π | β | β | β | +| Private keys management | π | β | β | β | +| Identity edition | β
| β
| β | β | +| Identity adoption | β
| β
| β | β | +| Identity protection | π | β | β | β | + +#### Bugs + +| | Core | CLI | TermUI | WebUI | +|-------------------|:----:|:---:|:------:|:-----:| +| Comments | β
| β
| β
| β
| +| Comments edition | β
| β
| β
| β
| +| Comments deletion | β
| β | β | β | +| Labels | β
| β
| β
| β
| +| Status | β
| β
| β
| β
| +| Title edition | β
| β
| β
| β
| +| Assignee | β | β | β | β | +| Milestone | β | β | β | β | + + +## Bridges + +### Importers + +General capabilities of importers: + +| | Github | Gitlab | Jira | Launchpad | +|-------------------------------------------------|:------:|:------:|:----:|:---------:| +| **incremental**<br/>(can import more than once) | β
| β
| β
| β | +| **with resume**<br/>(download only new data) | β
| β
| β
| β | +| **media/files** | β | β | β | β | +| **automated test suite** | β
| β
| β | β | + +Identity support: + +| | Github | Gitlab | Jira | Launchpad | +|-------------------|:------:|:------:|:----:|:---------:| +| **identities** | β
| β
| β
| β
| +| identities update | β | β | β | β | +| public keys | β | β | β | β | + +Bug support: + +| | Github | Gitlab | Jira | Launchpad | +|------------------|:------:|:------:|:----:|:---------:| +| **bug** | β
| β
| β
| β
| +| comments | β
| β
| β
| β
| +| comment editions | β
| β | β
| β | +| labels | β
| β
| β
| β | +| status | β
| β
| β
| β | +| title edition | β
| β
| β
| β | +| Assignee | β | β | β | β | +| Milestone | β | β | β | β | + +Board support: + +| | Github | Gitlab | Jira | Launchpad | +|-----------|:------:|:------:|:----:|:---------:| +| **board** | β | β | β | β | + +### Exporters + +**General capabilities of exporters**: + +| | Github | Gitlab | Jira | +|-------------------------------------------------|:------:|:------:|:----:| +| **incremental**<br/>(can export more than once) | β
| β
| β
| +| **with resume**<br/>(upload only new data) | β
| β
| β
| +| **automated test suite** | β
| β
| β | + +**Identity support**: + +| | Github | Gitlab | Jira | +|-------------------|:------:|:------:|:----:| +| **identities** | β
| β
| β
| +| identities update | β | β | β | + +Note: as the target bug tracker require accounts and credentials, there is only so much that an exporter can do about identities. A bridge should be able to load and use credentials for multiple remote account, but when they are not available, the corresponding changes can't be replicated. + +**Bug support**: + +| | Github | Gitlab | Jira | +|------------------|:------:|:------:|:----:| +| **bugs** | β
| β
| β
| +| comments | β
| β
| β
| +| comment editions | β
| β
| β
| +| labels | β
| β
| β
| +| status | β
| β
| β
| +| title edition | β
| β
| β
| +| Assignee | β | β | β | +| Milestone | β | β | β | diff --git a/doc/howto-github.md b/doc/howto-github.md index ba3e380c..47d93434 100644 --- a/doc/howto-github.md +++ b/doc/howto-github.md @@ -72,7 +72,7 @@ For a richer and more user friendly UI, `git-bug` proposes a web UI (read-only a ## Want more? -If you interested to read more about `git-bug`, have a look at the followings: +If you interested to read more about `git-bug`, have a look at the following: - [the project itself, with a more complete readme](https://github.com/MichaelMure/git-bug) - [a bird view of the internals](https://github.com/MichaelMure/git-bug/blob/master/doc/architecture.md) - [a description of the data model](https://github.com/MichaelMure/git-bug/blob/master/doc/model.md) diff --git a/entities/identity/key.go b/entities/identity/key.go index 82b9b95c..87271dd5 100644 --- a/entities/identity/key.go +++ b/entities/identity/key.go @@ -23,7 +23,7 @@ type Key struct { private *packet.PrivateKey } -// GenerateKey generate a keypair (public+private) +// GenerateKey generate a key pair (public+private) // The type and configuration of the key is determined by the default value in go's OpenPGP. func GenerateKey() *Key { entity, err := openpgp.NewEntity("", "", "", &packet.Config{ diff --git a/entity/dag/op_set_metadata_test.go b/entity/dag/op_set_metadata_test.go index 07ece013..a06f89da 100644 --- a/entity/dag/op_set_metadata_test.go +++ b/entity/dag/op_set_metadata_test.go @@ -54,7 +54,7 @@ func TestSetMetadata(t *testing.T) { target1Metadata := snap.AllOperations()[0].AllMetadata() require.Len(t, target1Metadata, 2) - // original key is not overrided + // original key is not overridden require.Equal(t, target1Metadata["key"], "value") // new key is set require.Equal(t, target1Metadata["key2"], "value") @@ -78,7 +78,7 @@ func TestSetMetadata(t *testing.T) { target2Metadata = snap.AllOperations()[1].AllMetadata() require.Len(t, target2Metadata, 2) - // original key is not overrided + // original key is not overridden require.Equal(t, target2Metadata["key2"], "value2") // new key is set require.Equal(t, target2Metadata["key3"], "value3") @@ -93,9 +93,9 @@ func TestSetMetadata(t *testing.T) { target1Metadata = snap.AllOperations()[0].AllMetadata() require.Len(t, target1Metadata, 2) - // original key is not overrided + // original key is not overridden require.Equal(t, target1Metadata["key"], "value") - // previously set key is not overrided + // previously set key is not overridden require.Equal(t, target1Metadata["key2"], "value") target2Metadata = snap.AllOperations()[1].AllMetadata() diff --git a/entity/dag/operation_pack.go b/entity/dag/operation_pack.go index cc6c81f4..c999ff23 100644 --- a/entity/dag/operation_pack.go +++ b/entity/dag/operation_pack.go @@ -82,7 +82,7 @@ func (opp *operationPack) Validate() error { } // Write writes the OperationPack in git, with zero, one or more parent commits. -// If the repository has a keypair able to sign (that is, with a private key), the resulting commit is signed with that key. +// If the repository has a key pair able to sign (that is, with a private key), the resulting commit is signed with that key. // Return the hash of the created commit. func (opp *operationPack) Write(def Definition, repo repository.Repo, parentCommit ...repository.Hash) (repository.Hash, error) { if err := opp.Validate(); err != nil { diff --git a/misc/git_integration/git-bug.go b/misc/git_integration/git-bug.go new file mode 100644 index 00000000..accb2674 --- /dev/null +++ b/misc/git_integration/git-bug.go @@ -0,0 +1 @@ +package git_integration diff --git a/webui/src/components/Header/Header.tsx b/webui/src/components/Header/Header.tsx index 961696d7..0bbbe3bb 100644 --- a/webui/src/components/Header/Header.tsx +++ b/webui/src/components/Header/Header.tsx @@ -69,7 +69,7 @@ function Header() { const location = useLocation(); // Prevents error of invalid tab selection in <Tabs> - // Will return a valid tab path or false if path is unkown. + // Will return a valid tab path or false if path is unknown. function highlightTab() { const validTabs = ['/', '/code', '/pulls', '/settings']; const tab = validTabs.find((tabPath) => tabPath === location.pathname); |