Ripissue as a Poor Man's Issue Tracker
Solving the limitations of online issue tracking tools: how Ripissue and a git-based simple method can be a game-changing alternative
Published at: 2023-10-23
Contents
Motivation
I have always believed that there is something wrong with online tools for issue tracking or task management. Platforms like Github, Gitlab, and others provide great resources for working with issues, but relying solely on these web-based tools raised some concerns that I wanted to address, such as:
I do not own the data: All information (issues/tasks, along with their respective metadata such as descriptions and discussions) is stored on its own platform. They do not belong to me or remain within my projects repositories. Therefore, I am dependent on a third-party entity.
Issues and code are decoupled: Third-party issue trackers naturally separates the issues from the code being produced. If I want to associate the code with its corresponding commit, I would need to replicate the history saved in the issue metadata within the commit message.
Manual and repetitive work: One way to associate each issue with its corresponding commit is to copy the issue ID generated by the web interface (such as Github’s issue tracker) or its respective URL, and then paste it into the commit message for every commit. This task is repetitive, and susceptible to human errors. Additionally, this process relies on the information stored in the online tool.
Point-and-click…: Finally, there is the “point-and-click” workflow. Yes, I don’t like it. Not a single bit. “Open the website.” “Click to create an issue.” “Click to comment on an issue.” “Click to close an issue.” Point-and-click is wrong on so many levels…
Solution: CLI Issue Tracker
While I was dealing with my struggles, I came across the article Poor Man’s Issue Tracker, and it completely amazed me. The principles underlying this “method” are quite straightforward yet incredibly impactful:
- Issues are just directories, files and plain text
- Git is the ultimate tool for distributed projects
So why do I need a third-party-point-and-click-bloated-tool when all I need is: git; a method; and possibly a tool to assist me in working with them together?
To assist me with that, Dave MacFarlane (a.k.a. driusan) (the author of Poor Man’s Issue Tracker) also developed a command-line tool, written in Go, called Bug, which I have extensively utilized in my projects. It is a straightforward tool that aids in managing issues, which are essentially directories and files within our own project. These issues can be created, edited, or closed in conjunction with changes made to the source code. All modifications made in plain text are recorded within the commit, encompassing both the code and the issues’ history. This history is intended to document the progression and rationale behind the implemented code.
That was everything I needed to eliminate web-based issue trackers. A simple CLI tool automates the correct method implementation, keeps every information within the project, is platform agnostic, and completely distributed (due to Git).
Ripissue
Improvement:However, when I used the Bug CLI, I noticed that I would need additional features or modifications to certain behaviors. For instance, when I closed an issue using the bug close
command, the corresponding directory was deleted, causing me to lose the information about that issue within the repository. While the history remains within the commits, accessing that information would require diving into the git internal history, since the files are no longer available.
To solve that problem and implement several other automation helpers we [^1] developed “yet another issue tracker” tool: Ripissue. This is a command-line program written in Rust, designed to enhance the development workflow and project management.
There is a lot to talk about the features of Ripissue and how we can implement a fully distributed workflow using it with git and filesystem. However, the goal of this article is to focus on the basic usage of the tool and how we can start managing our project immediately.
Ripissue Installation
Currently, Ripissue can only be installed using Rust and the toolchain provided by Cargo. Therefore, let’s start by installing Rust (access: https://www.rust-lang.org/tools/install for more details about installation):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Now, install the Ripissue crate using the cargo install
command (https://doc.rust-lang.org/cargo/commands/cargo-install.html):
cargo install ripissue
Make sure the program is installed correctly by calling Ripissue from the command-line using the ripi
command (which is the command used to execute Ripissue in the terminal):
ripi --version
Ripissue to create an issue
UsingThe basic process to work with issues in your project is:
- Create a new issue.
- Continuously implement code changes and/or update the issue data.
- Once everything is completed, close the issue.
In order to do that, we need to be inside a git repository that will manage the code being produced. Additionally, all ripi
commands in the terminal must be executed at the project root, which is where the .git
directory is located.
Let’s begin with the issue creation. To create a new issue, simply run the following command:
ripi issue create "my first issue"
If the program was executed correctly, two things happened:
Step 1: Ripissue creates a directory structure and basic files
Firstly, Ripissue created the initial directory structure for all the issues, including the new issue directory itself:
my-project
.
└── ripi
└── Issue
└── my_first_issue
└── description.md
Ripissue can manage elements other than issues, such as sprints, epics, and initiatives. The ripi
directory is where those elements are stored and managed, with each of them having its own subdirectories, such as the Issue
directory. (To view all the elements that Ripissue manages, simply run the command ripi --help
).
The directory my_first_issue
is where all the information regarding this particular issue is stored, and the name of this directory is considered as the issue ID by Ripissue as the issue ID (yes, it must be unique).
Step 2: Ripissue commits the new issue to git
Additionally, the ripi create
command also added and committed the corresponding issue to git. This automation is implemented by Ripissue where every command automatically commits its changes to git with a commit message that refers to the issue ID. The git log history should contain something similar to this:
commit 274c22b85333296e88f2bdf8a3d82072dbd0d80e (HEAD -> master)
Author: Gustavo Basso <gubasso@eambar.net>
Date: Mon Oct 23 15:39:06 2023 -0300
(created) Issue #my_first_issue.
Where the commit message contains the operation that was done with the issue ID ((created) Issue #my_first_issue.
).
This is the default behavior. The concept here is that all actions performed within the project and managed by Ripissue must be recorded in git. However, if you want to execute the command without involving any git operations, simply include the --dry
flag. When this flag is used, ripi
will run as usual but the git operations will not be carried out (nor added or committed to git). For instance:
ripi issue create "my first issue" --dry
This command will create the issue in the filesystem but no operations will be performed in git.
Update your code and/or the issue
Note that the ripi
command also created the description.md
file, where you can include all the details about this issue in any format you prefer. For example, we can edit the issue description adding some subtasks to it:
my-project/ripi/Issue/my_first_issue/description.md
# my_first_issue (Issue)
- [ ] subtask 1
- [ ] subtask 2
- [ ] subtask 3
The beauty of this management method is that it is just a directory with text files! You can edit it and put information in any way you want. You can use git normally with or without Ripissue, and you can create any other complementary files within the ripi
directory.
Since you have edited the issue details, you may have also included some new code that implements what has being described in the issue. Let’s create a sample code file, such as:
my-project/some-code.rs
fn example() {
println!("hello");
}
And update our issue description:
my-project/ripi/Issue/my_first_issue/description.md
# my_first_issue (Issue)
- [x] subtask 1
- [ ] subtask 2
- [ ] subtask 3
Now git status
command will show some changes made to our repository:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: ripi/Issue/my_first_issue/description.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
some-code.rs
no changes added to commit (use "git add" and/or "git commit -a")
Another central concept behind the Poor Man’s Issue Tracker, which is also implemented in Ripissue, raises the following questions:
- Why should additional descriptions be provided if the commit diff already captures all the changes made?
- With the issue description now been committed with the code, providing detailed context for the changes, is there still a need to further describe the code modifications?
Since this issue tracker is based on plain text, distributed with git, and managed with some organizational method, all the necessary information about each code change is in the commit itself. We do not need to rewrite this information in the commit message or replicate (copy/paste) some redundant description to it. All we need is to associate the commit with the respective issue and let Ripissue to fill up the commit message for us.
To record our changes and update our issue, we simply need to add all the modifications to git and commit them using the ripi commit
command:
git add -A
ripi issue commit my_first_issue
To simplify the process, we can execute both commands (add + commit) by including the --add
flag, to add all changed files to git staging area. The aforementioned commands can be substituted with:
ripi issue commit my_first_issue --add
A new commit was made to git with the following information:
commit e101d4eb74dd8429a4d9f885045b71ed667b5b1e (HEAD -> master)
Author: Gustavo Basso <gubasso@eambar.net>
Date: Mon Oct 23 16:10:19 2023 -0300
(up) Issue #my_first_issue.
Now, the prefix (up)
has been added to our commit message instead of (created)
, indicating that this commit is recording an update related to the my_first_issue
along with the changed code.
As the project progresses and more code gets linked to the issue, each change will automatically include the corresponding issue ID in the commit message. This ensures seamless tracking of the code history and its association with detailed descriptions, all consolidated within the issue directory and files.
Closing an issue
Let’s simulate the whole implementation of #my_first_issue
:
my-project/some-code.rs
fn example() {
println!("everything is ready!");
}
my-project/ripi/Issue/my_first_issue/description.md
# my_first_issue (Issue)
- [x] subtask 1
- [x] subtask 2
- [x] subtask 3
Our git status
should be like this:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: ripi/Issue/my_first_issue/description.md
modified: some-code.rs
no changes added to commit (use "git add" and/or "git commit -a")
To close the issue all we need to do is to run the ripi close
command:
ripi issue close my_first_issue --add
This command will execute the following processes:
- Ripissue will add all the files to git (due to the
--add
flag). - Before closing the issue, it will run the
ripi commit
command one last time. This is done to ensure that any configuredpre-commit
hook is executed and any potential failures are addressed before closing the issue. - Ripissue will move the issue inside
ripi
directory:
from:
ripi/Issue/my_first_issue
to:
ripi/.closed/Issue/my_first_issue
- This change, which involves moving the issue directory to
.closed
, will be added to git regardless of the--add
flag. - A new commit will be created with the prefix
closed
before the issue ID.
Now, our git log
has two new commits:
commit ad486932d86cc9e899176853c3dc8636e8790141 (HEAD -> master)
Author: Gustavo Basso <gubasso@eambar.net>
Date: Mon Oct 23 16:23:56 2023 -0300
(closed) Issue #my_first_issue.
commit 475ca98936cbc92daf62c246f3d2fefe911bed4a
Author: Gustavo Basso <gubasso@eambar.net>
Date: Mon Oct 23 16:23:56 2023 -0300
(up) Issue #my_first_issue.
And the ripi
directory structure is like this:
ripi
├── .closed
│ └── Issue
│ └── my_first_issue
│ └── description.md
└── Issue
This is one main difference in behavior from the Bug CLI: Ripissue does not simply delete issues when we close them (although it still has the ripi issue delete
command that does that). All the closed issues are stored inside the repository, within the .closed
directory. This way, all the history is preserved in plain text files, and with git commands and/or GUI tools, we can access the issue directory history to see all the related code changes.
Everything is kept in one place, in plain text, fully integrated with our commit history. Isn’t that amazing?!
Other Functionalities and Workflow
Ripissue offers more functionalities than those detailed in this article. It can work with other elements such as sprints, epics, initiatives, etc. Within each of these elements, Ripissue can manage and associate the following:
- Tags
- Assignees
- Status
- Working and operating over git branches
However, the purpose here is to provide a brief introduction and a simple use case that, in my experience, is sufficient to avoid using third- party tools, at least for personal projects.
We plan to continue writing about Ripissue and presenting all its functionalities and a complete workflow that can replace the need to use any web-based git platform such as Github and Gitlab for project management.
Did you know that it’s possible to manage an entire project with any number of developers using just git and Ripissue in a pure, simple, and powerful way? That’s what we plan to discuss in the upcoming articles! Stay tuned and sign up for my RSS channel!
We are the founders and developers of the software development company cwnt.io
[^1]: We are Gustavo Basso (@gubasso) and Ismael Pamplona (@ismaelpamplona), the founders and developers of the cwnt.io (@cwnt-io) company.