A CLI tool made to help developers using Git Flow and Conventional Commits.
Currently, it can be Installed via:
brew install KaueSabinoSRV17/homebrew-flower/flower
wget https://github.com/KaueSabinoSRV17/Flower/releases/download/1.0.6/Flower_1.0.6_linux_amd64.tar.gz
tar -xzf Flower_1.0.6_linux_amd64.tar.gz
mv ./flow /usr/local/bin/flow
rm Flower_1.0.6_linux_amd64.tar.gz
Currently, it has only the commit
command:
flow commit
It will ask you what files you are going to add to git, what is going to be the Conventional Commit prefix
and the Commit Message. After that, you can run git log
to validate the commit.
The Structure can and probaly should be changed, it would be very welcome some advices on how to organize
the files and folders properly, since i come from Typescript
Development.
Where we store yaml
files for the ci/cd runs in Github Actions. Currently, i use goreleaser
to automate
the publishing to brew
.
The Pipeline is triggered when a new tag (version) is pushed follows these steps:
- Checkouts the project code:
name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- Installs Go:
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.20.3
- Runs
go mod vendor
, due to some errors without it:
name: Run Mod Vendor
run: go mod vendor
- Installs and Runs
goreleaser --clean
(without --clean did not work). It uses the pushed tag as the version and a github token to publish to thehomebrew repo
:
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
distribution: goreleaser
version: ${{ env.GITHUB_REF_NAME }}
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.RELEASER_TOKEN }}
Where we store Go
files generated by Cobra
. They add the functionality of a CLI to the business logic inside
use_cases
.
The Commit Command
can be called without any arguments, and that is actually the main Use Case.
It is configured in the commit.go
file, inside the cmd
directory. The function inside the Run
Attribute of the Cobra Command
is the following:
func(cmd *cobra.Command, args []string) {
repo := use_cases.GetRepository(".")
var message string
unstagedFiles := use_cases.GetUnstaggedFiles(repo)
if len(unstagedFiles) > 0 {
filesToStage := use_cases.AskWhatFilesToAddForStaging(unstagedFiles)
go use_cases.StageFiles(filesToStage, repo)
}
prefix := use_cases.AskCommitPrefix()
if len(args) == 0 {
message = use_cases.ResolveCommitMessage()
} else {
message = args[0]
}
use_cases.ConventionalCommit(prefix, message, repo)
}
Where we store terraform
files that describe and mainteen any infrastructure that we need to publish the CLI.
Currently there is only a S3 Bucket
in AWS
, that we are trying to use as a repository for apt
, making it
easier to install on Linux.
Where we store business logic to implement the Use Cases
of the CLI (Currently only Conventional Commits).
The logic for the commit message is inside the conventional_commit.go
file, in the use_cases
directory.
The following is the implementation of the Use Case:
- Open the Git Repo via
go-git
package:
func GetRepository(pathToRepository string) *git.Worktree {
repository, err := git.PlainOpen(pathToRepository)
if err != nil {
log.Fatal("Could not open Git Repo")
}
worktree, err := repository.Worktree()
if err != nil {
log.Fatal("Could not get Work Tree")
}
return worktree
}
- Get all Unstagged Files:
func GetUnstaggedFiles(worktree *git.Worktree) []string {
status, err := worktree.Status()
if err != nil {
log.Fatal("Could not get Git Status")
}
var modifiedOrUntrackedFiles []string
for file, s := range status {
if s.Worktree == git.Modified || s.Worktree == git.Untracked {
modifiedOrUntrackedFiles = append(modifiedOrUntrackedFiles, file)
}
}
return modifiedOrUntrackedFiles
}
- If the quantity of Unstagged Files is above 0, it will ask what files to stage, via
survey
package:
func AskWhatFilesToAddForStaging(files []string) []string {
var filesToAdd []string
prompt := &survey.MultiSelect{
Message: "Chose the files to stage",
Options: files,
}
err := survey.AskOne(prompt, &filesToAdd)
if err != nil {
log.Fatal("Could not Ask Files to Stage")
}
return filesToAdd
}
- With the selected files, it will stage them with the
go-git
package:
func StageFiles(files []string, worktree *git.Worktree) {
for _, file := range files {
_, err := worktree.Add(file)
if err != nil {
log.Fatal("Could not add " + file + " file")
}
}
}
- After handling none or more staged files, it will ask for a Conventional Commit prefix:
func AskCommitPrefix() string {
var prefix string
err := survey.AskOne(
&survey.Select{
Message: "Select a Prefix for the commit:",
Options: []string{"chore", "feat", "fix", "refactor", "tests", "docs"},
},
&prefix,
)
if err != nil {
log.Fatal("Could not ask Commit Prefix")
}
return prefix
}
- If there are no arguments given via user input, it will ask for the Commit Message (can be passed via argument)
func ResolveCommitMessage() string {
var message string
err := survey.AskOne(
&survey.Input{
Message: "What did you do? (Commit Message)",
},
&message,
)
if err != nil {
log.Fatal("Could not ask Commit Message")
}
return message
}
- And Lastly, with the prefix and the message in mind, it will commit the changes to the worktree:
func ConventionalCommit(prefix string, message string, worktree *git.Worktree) {
formatedMessage := fmt.Sprintf("%s: %s", prefix, message)
worktree.Commit(formatedMessage, &git.CommitOptions{})
fmt.Println("Sucessfully added a commit!")
}