From b9acf6cc675adea704b2eaef4f2237d3182069a6 Mon Sep 17 00:00:00 2001 From: Todd Davis Date: Sat, 12 Jun 2021 08:54:28 -0500 Subject: [PATCH] Inital release of kanbn2md Renders json output of `kanbn board -j` as a markdown table. --- .github/workflows/release-please.yml | 20 +++++ .gitignore | 20 +++++ README.md | 27 +++++++ go.mod | 3 + main.go | 114 +++++++++++++++++++++++++++ 5 files changed, 184 insertions(+) create mode 100644 .github/workflows/release-please.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 go.mod create mode 100644 main.go diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..fb9860e --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,20 @@ +on: + push: + branches: + - master +name: release-please +jobs: + release-please: + runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs.release_created }} + steps: + - uses: GoogleCloudPlatform/release-please-action@v2 + id: release + with: + token: ${{ secrets.GITHUB_TOKEN }} + release-type: simple + bump-minor-pre-major: Yes + package-name: kanbn2md + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a7768e --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +#### joe made this: http://goel.io/joe + +#### go #### +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +.kanbn diff --git a/README.md b/README.md new file mode 100644 index 0000000..bbc4486 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# kanbn2md + +[Kanbn](https://github.com/basementuniverse/kanbn) is a CLI Kanban board. It stores +the task information in markdown format and has a [vscode extension](https://github.com/basementuniverse/vscode-kanbn) +for viewing and editing the board from vscode. What is missing is a way to render the board as a markdown table. +This simple program takes JSON output of `kanbn board -j` on stdin and renders a markdown table on stdout. + +## Installation + +Download a binary from the releases page or install via go with + +```bash +go install github.com/tjdavis3/kanbn2md +``` + +## Usage + +```bash +kanbn board -j | kanbn2md > board.md +``` + +## Example + +| Backlog | Todo | In Progress | Done | +| --- | --- | --- | --- | +| Comment the code | Create github repo | Create a README.md | Build app | +| Add tests | Add error handling | | | diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1c0dcab --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/tjdavis3/kanbn2md + +go 1.16 diff --git a/main.go b/main.go new file mode 100644 index 0000000..439c604 --- /dev/null +++ b/main.go @@ -0,0 +1,114 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "os" +) + +// KanbnColumns are the board columns as represented in the kanbn json +type KanbnColumns [][]struct { + Column string `json:"column"` + Comments []interface{} `json:"comments"` + Description string `json:"description"` + ID string `json:"id"` + Metadata struct { + Assigned string `json:"assigned"` + Created string `json:"created"` + Progress int `json:"progress"` + Started *string `json:"started,omitempty"` + Tags []interface{} `json:"tags"` + Updated string `json:"updated"` + } `json:"metadata"` + Name string `json:"name"` + Progress int `json:"progress"` + Relations []interface{} `json:"relations"` + RemainingWorkload int `json:"remainingWorkload"` + SubTasks []struct { + Completed bool `json:"completed"` + Text string `json:"text"` + } `json:"subTasks"` + Workload int `json:"workload"` +} + +// KanbnBoard represents the JSON output of `kanbn board -j` +type KanbnBoard struct { + Headings []struct { + Heading string `json:"heading"` + Name string `json:"name"` + } `json:"headings"` + Lanes []struct { + Columns KanbnColumns `json:"columns"` + Name string `json:"name"` + } `json:"lanes"` +} + +func main() { + err := renderBoard(os.Stdin, os.Stdout) + if err != nil { + log.Fatal(err) + } +} + +// renderBoard takes care of reading the kanbn json +// and rendering it as a markdown table +func renderBoard(r io.Reader, w io.Writer) error { + var kanbn KanbnBoard + var rows [][]string + board, err := ioutil.ReadAll(r) + if err != nil { + return err + } + if err = json.Unmarshal(board, &kanbn); err != nil { + return err + } + + // Determine how many columns exists and print the header row + columnCount := len(kanbn.Headings) + for _, col := range kanbn.Headings { + fmt.Fprintf(w, "| %s ", col.Name) + } + fmt.Fprintln(w, "|") + for i := 0; i < columnCount; i++ { + fmt.Fprint(w, "| --- ") + } + fmt.Fprintln(w, "|") + + // read the swimlanes + // The lanes themselves are not printed on the table + for _, lane := range kanbn.Lanes { + maxRows := determineMaxRows(lane.Columns) + // initialize an array of rows + rows = make([][]string, maxRows) + for i := range rows { + rows[i] = make([]string, columnCount) + } + // convert columns of rows into rows of columns + for curCol, column := range lane.Columns { + for colRow, task := range column { + rows[colRow][curCol] = task.Name + } + } + // print the rows + for _, row := range rows { + for _, col := range row { + fmt.Fprintf(w, "| %s ", col) + } + fmt.Fprintln(w, "|") + } + } + return nil +} + +func determineMaxRows(cols KanbnColumns) int { + var maxRows int + for _, column := range cols { + if len(column) > maxRows { + maxRows = len(column) + } + } + return maxRows +}