From cc6f365ee7751a9a38db513cb43ab1439bf9a56b Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Mon, 14 Mar 2022 18:00:23 -0700 Subject: [PATCH 01/13] Update version to 0.2.0 --- README.md | 2 +- mix.exs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 663fc85..d0a951f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ by adding `todo_txt` to your list of dependencies in `mix.exs`: ```elixir def deps do [ - {:todo_txt, "~> 0.1.0"} + {:todo_txt, "~> 0.2.0"} ] end ``` diff --git a/mix.exs b/mix.exs index 9086a1c..8106495 100644 --- a/mix.exs +++ b/mix.exs @@ -16,8 +16,8 @@ defmodule TodoTxt.MixProject do plt_file: {:no_warn, "todo_txt.plt"} ], app: :todo_txt, - version: "0.1.0", - elixir: "~> 1.12.2", + version: "0.2.0", + elixir: "~> 1.13.2", start_permanent: Mix.env() == :prod, deps: deps(), description: description(), From cbfa803b82684af76a9deb349ed4d4b814fbe3b2 Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Wed, 11 Jan 2023 20:28:39 -0800 Subject: [PATCH 02/13] Move Headless Functions Out of to_string Pipe --- lib/todo.ex | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/todo.ex b/lib/todo.ex index fe6b56e..e1a412c 100644 --- a/lib/todo.ex +++ b/lib/todo.ex @@ -67,8 +67,8 @@ defmodule Todo do description |> todo_date_to_string(creation_date) |> todo_date_to_string(completion_date) - |> (fn str -> if priority == :none, do: str, else: "(#{priority}) #{str}" end).() - |> (fn str -> if done, do: "x #{str}", else: str end).() + |> add_priority_to_string(priority) + |> add_done_to_string(done) |> add_atom_list("@", contexts) |> add_atom_list("+", projects) |> todo_due_date_to_string(due_date) @@ -76,6 +76,14 @@ defmodule Todo do |> add_pomo(pomodori) end + defp add_priority_to_string(str, :none), do: str + + defp add_priority_to_string(str, priority) when is_atom(priority), do: "(#{priority}) #{str}" + + defp add_done_to_string(str, true), do: "x #{str}" + + defp add_done_to_string(str, false), do: str + defp add_pomo(str, :none), do: str defp add_pomo(str, {completed, total}) do From 03994a8360b7bd86a6b98851057dc621bdcde77c Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Wed, 11 Jan 2023 20:30:40 -0800 Subject: [PATCH 03/13] Upgrade Elixir Version and Deps --- .tool-versions | 1 + mix.exs | 6 +++--- mix.lock | 22 +++++++++++----------- 3 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 .tool-versions diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..7663267 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +elixir 1.14.2-otp-25 diff --git a/mix.exs b/mix.exs index 9086a1c..450927e 100644 --- a/mix.exs +++ b/mix.exs @@ -17,7 +17,7 @@ defmodule TodoTxt.MixProject do ], app: :todo_txt, version: "0.1.0", - elixir: "~> 1.12.2", + elixir: "~> 1.14.2", start_permanent: Mix.env() == :prod, deps: deps(), description: description(), @@ -57,11 +57,11 @@ defmodule TodoTxt.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:credo, "~> 1.5.5", only: :dev, runtime: false}, + {:credo, "~> 1.6.7", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 1.0.0", only: :dev, runtime: false}, {:ex_doc, "~> 0.23.0", only: :dev, runtime: false}, {:excoveralls, "~> 0.13.0", only: :test}, - {:git_hooks, "~> 0.5.2", only: :dev, runtime: false}, + {:git_hooks, "~> 0.7.3", only: [:dev], runtime: false}, {:mix_test_watch, "~> 1.0.2", only: :dev, runtime: false}, {:nimble_parsec, "~> 1.1.0"}, {:recon, "~> 2.5.2", only: :dev, runtime: false} diff --git a/mix.lock b/mix.lock index 34a2266..b7133fc 100644 --- a/mix.lock +++ b/mix.lock @@ -1,28 +1,28 @@ %{ "blankable": {:hex, :blankable, "1.0.0", "89ab564a63c55af117e115144e3b3b57eb53ad43ba0f15553357eb283e0ed425", [:mix], [], "hexpm", "7cf11aac0e44f4eedbee0c15c1d37d94c090cb72a8d9fddf9f7aec30f9278899"}, - "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, - "certifi": {:hex, :certifi, "2.6.1", "dbab8e5e155a0763eea978c913ca280a6b544bfa115633fa20249c3d396d9493", [:rebar3], [], "hexpm", "524c97b4991b3849dd5c17a631223896272c6b0af446778ba4675a1dff53bb7e"}, - "credo": {:hex, :credo, "1.5.5", "e8f422026f553bc3bebb81c8e8bf1932f498ca03339856c7fec63d3faac8424b", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "dd8623ab7091956a855dc9f3062486add9c52d310dfd62748779c4315d8247de"}, + "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, + "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, + "credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"}, "dialyxir": {:hex, :dialyxir, "1.0.0", "6a1fa629f7881a9f5aaf3a78f094b2a51a0357c843871b8bc98824e7342d00a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "aeb06588145fac14ca08d8061a142d52753dbc2cf7f0d00fc1013f53f8654654"}, "earmark": {:hex, :earmark, "1.4.5", "62ffd3bd7722fb7a7b1ecd2419ea0b458c356e7168c1f5d65caf09b4fbdd13c8", [:mix], [], "hexpm", "b7d0e6263d83dc27141a523467799a685965bf8b13b6743413f19a7079843f4f"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.13", "0c98163e7d04a15feb62000e1a891489feb29f3d10cb57d4f845c405852bbef8", [:mix], [], "hexpm", "d602c26af3a0af43d2f2645613f65841657ad6efc9f0e361c3b6c06b578214ba"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_doc": {:hex, :ex_doc, "0.23.0", "a069bc9b0bf8efe323ecde8c0d62afc13d308b1fa3d228b65bca5cf8703a529d", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "f5e2c4702468b2fd11b10d39416ddadd2fcdd173ba2a0285ebd92c39827a5a16"}, "excoveralls": {:hex, :excoveralls, "0.13.4", "7b0baee01fe150ef81153e6ffc0fc68214737f54570dc257b3ca4da8e419b812", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "faae00b3eee35cdf0342c10b669a7c91f942728217d2a7c7f644b24d391e6190"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, - "git_hooks": {:hex, :git_hooks, "0.5.2", "61160eb52112d37b018c94ef5383a31d2187b00b046ecb10e802e71556549e85", [:mix], [{:blankable, "~> 1.0.0", [hex: :blankable, repo: "hexpm", optional: false]}, {:recase, "~> 0.7.0", [hex: :recase, repo: "hexpm", optional: false]}], "hexpm", "38e452c3cbd407d0ec30f8327f54d59d2ada1ecea62ecc13a3fa2ddd799881de"}, - "hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"}, - "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, - "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"}, + "git_hooks": {:hex, :git_hooks, "0.7.3", "09489e94d88dfc767662e22aff2b6208bd7cf555a19dd0e1477cca4683ce0701", [:mix], [{:blankable, "~> 1.0.0", [hex: :blankable, repo: "hexpm", optional: false]}, {:recase, "~> 0.7.0", [hex: :recase, repo: "hexpm", optional: false]}], "hexpm", "d6ddedeb4d3a8602bc3f84e087a38f6150a86d9e790628ed8bc70e6d90681659"}, + "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~> 2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, + "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, + "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, - "mix_test_watch": {:hex, :mix_test_watch, "1.0.2", "34900184cbbbc6b6ed616ed3a8ea9b791f9fd2088419352a6d3200525637f785", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "47ac558d8b06f684773972c6d04fcc15590abdb97aeb7666da19fcbfdc441a07"}, + "mix_test_watch": {:hex, :mix_test_watch, "1.0.3", "63d5b21e9278abf519f359e6d59aed704ed3c72ec38be6ab22306ae5dc9a2e06", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "7352e91952d9748fb4f8aebe0a60357cdaf4bd6d6c42b5139c78fbcda6a0d7a2"}, "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "recase": {:hex, :recase, "0.7.0", "3f2f719f0886c7a3b7fe469058ec539cb7bbe0023604ae3bce920e186305e5ae", [:mix], [], "hexpm", "36f5756a9f552f4a94b54a695870e32f4e72d5fad9c25e61bc4a3151c08a4e0c"}, - "recon": {:hex, :recon, "2.5.2", "cba53fa8db83ad968c9a652e09c3ed7ddcc4da434f27c3eaa9ca47ffb2b1ff03", [:mix, :rebar3], [], "hexpm", "2c7523c8dee91dff41f6b3d63cba2bd49eb6d2fe5bf1eec0df7f87eb5e230e1c"}, + "recon": {:hex, :recon, "2.5.3", "739107b9050ea683c30e96de050bc59248fd27ec147696f79a8797ff9fa17153", [:mix, :rebar3], [], "hexpm", "6c6683f46fd4a1dfd98404b9f78dcabc7fcd8826613a89dcb984727a8c3099d7"}, "recon_ex": {:hex, :recon_ex, "0.9.1", "51558b6eb347753dcf9c884ba68d49dbbec607fc4296815b72a0c8995d5d3203", [:mix], [{:recon, "~> 2.3.1", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm", "ca124759162c082adbb29078eadb9ab102425493c94387221d1e11acd0e050f7"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, From c6cce67b92931b08b69d3a01b9fe526c6532861c Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Wed, 11 Jan 2023 20:31:39 -0800 Subject: [PATCH 04/13] Update Git Hooks Config and Clean Up Config Files --- config/config.exs | 27 +++++++++++++++++++++++++++ config/dev.exs | 18 ------------------ config/prod.exs | 1 - config/test.exs | 1 - 4 files changed, 27 insertions(+), 20 deletions(-) delete mode 100644 config/dev.exs delete mode 100644 config/prod.exs delete mode 100644 config/test.exs diff --git a/config/config.exs b/config/config.exs index 8b13789..0850722 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1 +1,28 @@ +import Config +# somewhere in your config file +if Mix.env() == :dev do + config :git_hooks, + auto_install: true, + verbose: true, + branches: [ + blacklist: ["main"] + ], + hooks: [ + pre_commit: [ + tasks: [ + {:cmd, "mix format --check-formatted"} + ] + ], + pre_push: [ + verbose: false, + tasks: [ + {:cmd, "mix clean"}, + {:cmd, "mix compile --warnings-as-errors"}, + {:cmd, "mix credo --strict"}, + {:cmd, "mix test --color"}, + {:cmd, "echo 'success!'"} + ] + ] + ] +end diff --git a/config/dev.exs b/config/dev.exs deleted file mode 100644 index 5c5bc72..0000000 --- a/config/dev.exs +++ /dev/null @@ -1,18 +0,0 @@ -config :git_hooks, - hooks: [ - pre_commit: [ - verbose: true, - tasks: [ - "mix format --check-formatted --dry-run --check-equivalent" - ] - ], - pre_push: [ - verbose: true, - tasks: [ - "mix clean", - "mix compile --warnings-as-errors", - "mix credo --strict", - "mix dialyzer --halt-exit-status" - ] - ] - ] diff --git a/config/prod.exs b/config/prod.exs deleted file mode 100644 index 8b13789..0000000 --- a/config/prod.exs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/config/test.exs b/config/test.exs deleted file mode 100644 index 8b13789..0000000 --- a/config/test.exs +++ /dev/null @@ -1 +0,0 @@ - From 6e76d293645c963047fc36955faac675f27175dc Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Wed, 11 Jan 2023 20:54:56 -0800 Subject: [PATCH 05/13] Run Dialyzer and Fix Errors --- .gitignore | 4 ++++ config/config.exs | 1 + lib/todo.ex | 13 ++----------- mix.exs | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index b263cd1..6033528 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ erl_crash.dump *.beam /config/*.secret.exs .elixir_ls/ + +# Dialyzer Files +/priv/plts/*.plt +/priv/plts/*.plt.hash diff --git a/config/config.exs b/config/config.exs index 0850722..8897813 100644 --- a/config/config.exs +++ b/config/config.exs @@ -21,6 +21,7 @@ if Mix.env() == :dev do {:cmd, "mix compile --warnings-as-errors"}, {:cmd, "mix credo --strict"}, {:cmd, "mix test --color"}, + {:cmd, "mix dialyzer --format dialyxir"}, {:cmd, "echo 'success!'"} ] ] diff --git a/lib/todo.ex b/lib/todo.ex index e1a412c..1e500cb 100644 --- a/lib/todo.ex +++ b/lib/todo.ex @@ -302,17 +302,8 @@ defmodule Todo do end defp set_from_parsed({:description, description}, todo) do - contexts = - case context_parser(description) do - {:ok, contexts, _, _, _, _} -> contexts - {:error, message, _, _, _, _} -> {:error, message} - end - - projects = - case project_parser(description) do - {:ok, projects, _, _, _, _} -> projects - {:error, message, _, _, _, _} -> {:error, message} - end + {:ok, contexts, _, _, _, _} = context_parser(description) + {:ok, projects, _, _, _, _} = project_parser(description) Map.put(todo, :description, description) |> Map.put(:contexts, contexts) diff --git a/mix.exs b/mix.exs index 450927e..574ead6 100644 --- a/mix.exs +++ b/mix.exs @@ -13,7 +13,7 @@ defmodule TodoTxt.MixProject do dialyzer: [ ignore_warnings: ".dialyzer_ignore.exs", list_unused_filters: true, - plt_file: {:no_warn, "todo_txt.plt"} + plt_file: {:no_warn, "priv/plts/dialyzer.plt"} ], app: :todo_txt, version: "0.1.0", From 7b0d8d9f3a13ed2be54462a251e80dcef9114d8e Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Wed, 11 Jan 2023 21:10:08 -0800 Subject: [PATCH 06/13] Add GitHub Actions Workflow For CI Checks --- .github/workflows/ci_check.yml | 65 ++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 .github/workflows/ci_check.yml diff --git a/.github/workflows/ci_check.yml b/.github/workflows/ci_check.yml new file mode 100644 index 0000000..5fb4ecc --- /dev/null +++ b/.github/workflows/ci_check.yml @@ -0,0 +1,65 @@ +name: Elixir CI + +on: + push: + branches: [$default-branch] + pull_request: + branches: [$default-branch] + +permissions: + contents: read + +jobs: + build: + name: Build and Test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Elixir + id: beam + uses: erlef/setup-beam@v1 + with: + elixir-version: "1.14.2" + otp-version: "25.1.2" + + - name: Restore dependencies cache + uses: actions/cache@v3 + with: + path: deps + key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} + restore-keys: ${{ runner.os }}-mix- + + - name: Install dependencies + run: mix deps.get + + - name: compile + run: mix compile --warnings-as-errors + + - name: credo + run: mix credo --strict + + - name: Run tests + run: mix test + + # Don't cache PLTs based on mix.lock hash, as Dialyzer can incrementally update even old ones + # Cache key based on Elixir & Erlang version (also useful when running in matrix) + - name: Restore PLT cache + uses: actions/cache@v3 + id: plt_cache + with: + key: | + ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plt + restore-keys: | + ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plt + path: | + priv/plts + + # Create PLTs if no cache was found + - name: Create PLTs + if: steps.plt_cache.outputs.cache-hit != 'true' + run: mix dialyzer --plt + + - name: Run dialyzer + run: mix dialyzer --format github From 73c58a2358bbfe6e0049ddc7e8f171ed4c69b281 Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Wed, 11 Jan 2023 21:19:25 -0800 Subject: [PATCH 07/13] Change GH Actions Workflow To Run On Pushes To All Branches --- .github/workflows/ci_check.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_check.yml b/.github/workflows/ci_check.yml index 5fb4ecc..ec11806 100644 --- a/.github/workflows/ci_check.yml +++ b/.github/workflows/ci_check.yml @@ -2,7 +2,8 @@ name: Elixir CI on: push: - branches: [$default-branch] + branches: + - "*" pull_request: branches: [$default-branch] From 156528238caf404de7a4943475aa6c7804752712 Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Wed, 11 Jan 2023 21:27:05 -0800 Subject: [PATCH 08/13] Update CI Checks Workflow To Ignore Pushes To Main --- .github/workflows/ci_check.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci_check.yml b/.github/workflows/ci_check.yml index ec11806..b6dc430 100644 --- a/.github/workflows/ci_check.yml +++ b/.github/workflows/ci_check.yml @@ -1,11 +1,8 @@ -name: Elixir CI +name: CI Checks on: push: - branches: - - "*" - pull_request: - branches: [$default-branch] + branches-ignore: [main] permissions: contents: read From 8356e0e89f3550c64d7e4f2dd99089748b672068 Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Wed, 11 Jan 2023 21:34:14 -0800 Subject: [PATCH 09/13] Upgrade ex_doc --- mix.exs | 2 +- mix.lock | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 574ead6..8030ff5 100644 --- a/mix.exs +++ b/mix.exs @@ -59,7 +59,7 @@ defmodule TodoTxt.MixProject do [ {:credo, "~> 1.6.7", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 1.0.0", only: :dev, runtime: false}, - {:ex_doc, "~> 0.23.0", only: :dev, runtime: false}, + {:ex_doc, "~> 0.29.1", only: :dev, runtime: false}, {:excoveralls, "~> 0.13.0", only: :test}, {:git_hooks, "~> 0.7.3", only: [:dev], runtime: false}, {:mix_test_watch, "~> 1.0.2", only: :dev, runtime: false}, diff --git a/mix.lock b/mix.lock index b7133fc..3be3d4b 100644 --- a/mix.lock +++ b/mix.lock @@ -7,7 +7,7 @@ "earmark": {:hex, :earmark, "1.4.5", "62ffd3bd7722fb7a7b1ecd2419ea0b458c356e7168c1f5d65caf09b4fbdd13c8", [:mix], [], "hexpm", "b7d0e6263d83dc27141a523467799a685965bf8b13b6743413f19a7079843f4f"}, "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_doc": {:hex, :ex_doc, "0.23.0", "a069bc9b0bf8efe323ecde8c0d62afc13d308b1fa3d228b65bca5cf8703a529d", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "f5e2c4702468b2fd11b10d39416ddadd2fcdd173ba2a0285ebd92c39827a5a16"}, + "ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"}, "excoveralls": {:hex, :excoveralls, "0.13.4", "7b0baee01fe150ef81153e6ffc0fc68214737f54570dc257b3ca4da8e419b812", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "faae00b3eee35cdf0342c10b669a7c91f942728217d2a7c7f644b24d391e6190"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "git_hooks": {:hex, :git_hooks, "0.7.3", "09489e94d88dfc767662e22aff2b6208bd7cf555a19dd0e1477cca4683ce0701", [:mix], [{:blankable, "~> 1.0.0", [hex: :blankable, repo: "hexpm", optional: false]}, {:recase, "~> 0.7.0", [hex: :recase, repo: "hexpm", optional: false]}], "hexpm", "d6ddedeb4d3a8602bc3f84e087a38f6150a86d9e790628ed8bc70e6d90681659"}, @@ -16,6 +16,7 @@ "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, "makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mix_test_watch": {:hex, :mix_test_watch, "1.0.3", "63d5b21e9278abf519f359e6d59aed704ed3c72ec38be6ab22306ae5dc9a2e06", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "7352e91952d9748fb4f8aebe0a60357cdaf4bd6d6c42b5139c78fbcda6a0d7a2"}, From 045589d674e21883574590d9ca68ca5098acad52 Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Wed, 11 Jan 2023 21:36:54 -0800 Subject: [PATCH 10/13] Update Version --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 8030ff5..d3ae0a1 100644 --- a/mix.exs +++ b/mix.exs @@ -16,7 +16,7 @@ defmodule TodoTxt.MixProject do plt_file: {:no_warn, "priv/plts/dialyzer.plt"} ], app: :todo_txt, - version: "0.1.0", + version: "0.2.0-dev", elixir: "~> 1.14.2", start_permanent: Mix.env() == :prod, deps: deps(), From 868728c45176d95d9388321fd79789ff782e86de Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Fri, 20 Jan 2023 20:01:30 -0800 Subject: [PATCH 11/13] Update README with CI/CD Flow Charts --- .github/workflows/{ci_check.yml => ci.yml} | 0 .github/workflows/deploy_to_hex.yml | 66 ++++++++++++++++++++++ README.md | 64 +++++++++++++++++++-- 3 files changed, 124 insertions(+), 6 deletions(-) rename .github/workflows/{ci_check.yml => ci.yml} (100%) create mode 100644 .github/workflows/deploy_to_hex.yml diff --git a/.github/workflows/ci_check.yml b/.github/workflows/ci.yml similarity index 100% rename from .github/workflows/ci_check.yml rename to .github/workflows/ci.yml diff --git a/.github/workflows/deploy_to_hex.yml b/.github/workflows/deploy_to_hex.yml new file mode 100644 index 0000000..024d341 --- /dev/null +++ b/.github/workflows/deploy_to_hex.yml @@ -0,0 +1,66 @@ +name: Publish Docs + +on: + push: + branches: + - "v*-dev" + tags: + - "v*" + +permissions: + contents: read + +jobs: + build: + name: Build and Test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Elixir + id: beam + uses: erlef/setup-beam@v1 + with: + elixir-version: "1.14.2" + otp-version: "25.1.2" + + - name: Restore dependencies cache + uses: actions/cache@v3 + with: + path: deps + key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} + restore-keys: ${{ runner.os }}-mix- + + - name: Install dependencies + run: mix deps.get + + - name: compile + run: mix compile --warnings-as-errors + + - name: credo + run: mix credo --strict + + - name: Run tests + run: mix test + + # Don't cache PLTs based on mix.lock hash, as Dialyzer can incrementally update even old ones + # Cache key based on Elixir & Erlang version (also useful when running in matrix) + - name: Restore PLT cache + uses: actions/cache@v3 + id: plt_cache + with: + key: | + ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plt + restore-keys: | + ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plt + path: | + priv/plts + + # Create PLTs if no cache was found + - name: Create PLTs + if: steps.plt_cache.outputs.cache-hit != 'true' + run: mix dialyzer --plt + + - name: Run dialyzer + run: mix dialyzer --format github diff --git a/README.md b/README.md index d0a951f..cbf7e68 100644 --- a/README.md +++ b/README.md @@ -27,18 +27,18 @@ end ### Read Todos From todo.txt File ```elixir -todos = - File.read!("todo.txt") - |> String.split("\n") +todos = + File.read!("todo.txt") + |> String.split("\n") |> Enum.map(&Todo.parse/1) ``` ### Write Todos To todo.txt File ```elixir -to_write = - todos - |> Enum.map(&Todo.to_string/1) +to_write = + todos + |> Enum.map(&Todo.to_string/1) |> Enum.join("\n") File.write!("todo.txt.diff", to_write) @@ -53,6 +53,58 @@ File.write!("todo.txt", Enum.join(Enum.map(todo, &Todo.to_string/1), "\n")) File.write!("done.txt", Enum.join(Enum.map(done, &Todo.to_string/1), "\n")) ``` +## CI/CD Publishing +GitHub Actions Workflows are used to manage the publishing of new versions to [Hex PM](https://hexdocs.pm/) + +### CI Workflows +- [ci.yml](.github/workflows/ci.yml) runs each time a PR is merged + +### CD +This process assumes each merged PR passes CI checks before being merged + +#### New Branch Workflow +- [check_new_branch.yml](.github/workflows/check_version_branch.yml) +```mermaid +graph TD + A[New $BRANCH] --> A1["Run check_new_branch.yml workflow"] + A1 --> B($BRANCH's $VERSION greater than main's?) + B -->|Yes| C["Create New `v$VERSION-dev` branch"] + B -->|No| D("Existing branch named `v$VERSION-dev`?") + D -->|Yes| E["Create PR for $BRANCH into `v$VERSION-dev` branch"] + D -->|No| C + C --> E +``` + +#### Merge Into Dev Branch Workflow +- [dev_branch.yml](.github/workflows/dev_branch.yml) +- [deploy_to_hex.yml](.github/workflows/deploy_to_hex.yml) +```mermaid +graph TD + E[$BRANCH PR Merged] --> E1["run ci.ym workflow"] + E1 -->|CI Checks Pass| E2["run dev_branch.yml workflow"] + E2 --> F("PR for `v$VERSION-dev` into main exists?") + F -->|Yes| G["Run deploy_to_hex.yml workflow"] + F -->|No| I["Create PR for `v$VERSION-dev` into main"] + I --> G + G --> G1["*Generate docs and commit to `v$VERSION-dev`"] + G1 --> G2["Publish `v$Version` to Hex"] + G2 --> G3["Check CHANGELOG.md for $VERSION"] +``` + +#### Merge Into Main Branch Workflow +- [deploy_new_version.yml](.github/workflows/merged_into_dev_branch.yml) +- [deploy_to_hex.yml](.github/workflows/deploy_to_hex.yml) +```mermaid +graph TD + J["`v$VERSION-dev` PR merged into main"] --> J1["run ci.yml workflow"] + J1 -->|CI Checks Pass| J2["run deploy_new_version.yml workflow"] + J2 --> J3["Create `v$VERSION` tag"] + J3 --> K["Create `v$VERSION` release"] + K --> L["Run deploy_to_hex.yml workflow"] + L --> L1["Publish `v$Version` to Hex"] + L1 --> L2["Delete v$VERSION-dev` package from Hex"] +``` + ## Roadmap - Add a File Watcher for Local todo.txt - Add a File Watcher for Google Drive todo.txt From 847d3c7db4f69fa27c7bcabeb96bff613916cd59 Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Fri, 20 Jan 2023 20:02:18 -0800 Subject: [PATCH 12/13] Update CI/CD Flow Charts In README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index cbf7e68..5ddba59 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ GitHub Actions Workflows are used to manage the publishing of new versions to [H This process assumes each merged PR passes CI checks before being merged #### New Branch Workflow +- [ci.yml](.github/workflows/ci.yml) - [check_new_branch.yml](.github/workflows/check_version_branch.yml) ```mermaid graph TD @@ -76,6 +77,7 @@ graph TD ``` #### Merge Into Dev Branch Workflow +- [ci.yml](.github/workflows/ci.yml) - [dev_branch.yml](.github/workflows/dev_branch.yml) - [deploy_to_hex.yml](.github/workflows/deploy_to_hex.yml) ```mermaid @@ -92,6 +94,7 @@ graph TD ``` #### Merge Into Main Branch Workflow +- [ci.yml](.github/workflows/ci.yml) - [deploy_new_version.yml](.github/workflows/merged_into_dev_branch.yml) - [deploy_to_hex.yml](.github/workflows/deploy_to_hex.yml) ```mermaid From db9085d516e3a51d04957e15caa6f75eac982cc4 Mon Sep 17 00:00:00 2001 From: Kyle San Clemente Date: Fri, 20 Jan 2023 23:11:14 -0800 Subject: [PATCH 13/13] Update Mix and Create Dev Branch Check Workflow --- .github/workflows/deploy_to_hex.yml | 66 -------------------------- .github/workflows/dev_branch_check.yml | 14 ++++++ mix.exs | 8 ++-- 3 files changed, 18 insertions(+), 70 deletions(-) delete mode 100644 .github/workflows/deploy_to_hex.yml create mode 100644 .github/workflows/dev_branch_check.yml diff --git a/.github/workflows/deploy_to_hex.yml b/.github/workflows/deploy_to_hex.yml deleted file mode 100644 index 024d341..0000000 --- a/.github/workflows/deploy_to_hex.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: Publish Docs - -on: - push: - branches: - - "v*-dev" - tags: - - "v*" - -permissions: - contents: read - -jobs: - build: - name: Build and Test - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Set up Elixir - id: beam - uses: erlef/setup-beam@v1 - with: - elixir-version: "1.14.2" - otp-version: "25.1.2" - - - name: Restore dependencies cache - uses: actions/cache@v3 - with: - path: deps - key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} - restore-keys: ${{ runner.os }}-mix- - - - name: Install dependencies - run: mix deps.get - - - name: compile - run: mix compile --warnings-as-errors - - - name: credo - run: mix credo --strict - - - name: Run tests - run: mix test - - # Don't cache PLTs based on mix.lock hash, as Dialyzer can incrementally update even old ones - # Cache key based on Elixir & Erlang version (also useful when running in matrix) - - name: Restore PLT cache - uses: actions/cache@v3 - id: plt_cache - with: - key: | - ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plt - restore-keys: | - ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plt - path: | - priv/plts - - # Create PLTs if no cache was found - - name: Create PLTs - if: steps.plt_cache.outputs.cache-hit != 'true' - run: mix dialyzer --plt - - - name: Run dialyzer - run: mix dialyzer --format github diff --git a/.github/workflows/dev_branch_check.yml b/.github/workflows/dev_branch_check.yml new file mode 100644 index 0000000..acadee4 --- /dev/null +++ b/.github/workflows/dev_branch_check.yml @@ -0,0 +1,14 @@ +name: Dev Branch Deploy + +on: + push: + branches: + - "v*.*.*-dev" + +jobs: + build: + name: No Op + runs-on: ubuntu-latest + + steps: + - run: echo "Yep It's a Dev Branch" diff --git a/mix.exs b/mix.exs index d3ae0a1..3f8acbd 100644 --- a/mix.exs +++ b/mix.exs @@ -16,7 +16,7 @@ defmodule TodoTxt.MixProject do plt_file: {:no_warn, "priv/plts/dialyzer.plt"} ], app: :todo_txt, - version: "0.2.0-dev", + version: "0.2.0", elixir: "~> 1.14.2", start_permanent: Mix.env() == :prod, deps: deps(), @@ -27,7 +27,7 @@ defmodule TodoTxt.MixProject do source_url: "https://github.com/KTSCode/todo_txt", docs: [ # The main page in the docs - main: "Todo", + main: "TodoTxt", logo: "todotxt_logo_2012.png", extras: ["README.md"] ] @@ -42,7 +42,7 @@ defmodule TodoTxt.MixProject do end defp description() do - "A library for reading/parseing/writing todo.txt files" + "A library for reading/parsing/writing todo.txt files" end defp package() do @@ -50,7 +50,7 @@ defmodule TodoTxt.MixProject do # These are the default files included in the package files: ~w(lib .formatter.exs mix.exs README* LICENSE*), licenses: ["MIT"], - links: %{"GitHub" => "https://github.com/KTSCode/todo_txt"} + links: %{"GitHub" => "https://github.com/KTSCode/todo_txt_elixir"} ] end