detached 62bb17c

Refactor deploy command, move spinner logic into mvu package.

Also started working on bubble tea progressbar. But not mandatory for
now
commit
62bb17cf1c04c08b7ec1227a5aea29fc8bd58260
author
bnema · · 2023-11-07 19:00 UTC
committer
bnema · · 2023-11-07 21:29 UTC
parent
af49727
signature
unsigned
10 files · +132 -124
M .air.toml +6 -6
@@ -4,15 +4,15 @@ tmp_dir = "tmp"
44
55 [build]
66 args_bin = []
7- bin = "./tmp/main"
8- cmd = "go build -o ./tmp/main cmd/srv/main.go"
7+ bin = "./dist/tmp/main"
8+ cmd = "go build -o ./dist/tmp/main cmd/cli/main.go"
99 delay = 0
1010 exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules"]
1111 exclude_file = []
1212 exclude_regex = ["_test.go"]
1313 exclude_unchanged = false
1414 follow_symlink = false
15- full_bin = "./tmp/main"
15+ full_bin = "./dist/tmp/main"
1616 include_dir = []
1717 include_ext = ["go", "tpl", "tmpl", "html", "gohtml", "css", "js"]
1818 include_file = []
@@ -21,8 +21,8 @@ tmp_dir = "tmp"
2121 poll = false
2222 poll_interval = 0
2323 rerun = false
24- rerun_delay = 500
25- send_interrupt = false
24+ rerun_delay = 1000
25+ send_interrupt = true
2626 stop_on_error = false
2727
2828 [color]
@@ -40,5 +40,5 @@ tmp_dir = "tmp"
4040 clean_on_exit = false
4141
4242 [screen]
43- clear_on_rebuild = false
43+ clear_on_rebuild = true
4444 keep_scroll = true
@@ -4,15 +4,15 @@ tmp_dir = "tmp"
4 4
5 [build]5 [build]
6 args_bin = []6 args_bin = []
7- bin = "./tmp/main"7+ bin = "./dist/tmp/main"
8- cmd = "go build -o ./tmp/main cmd/srv/main.go"8+ cmd = "go build -o ./dist/tmp/main cmd/cli/main.go"
9 delay = 09 delay = 0
10 exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules"]10 exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules"]
11 exclude_file = []11 exclude_file = []
12 exclude_regex = ["_test.go"]12 exclude_regex = ["_test.go"]
13 exclude_unchanged = false13 exclude_unchanged = false
14 follow_symlink = false14 follow_symlink = false
15- full_bin = "./tmp/main"15+ full_bin = "./dist/tmp/main"
16 include_dir = []16 include_dir = []
17 include_ext = ["go", "tpl", "tmpl", "html", "gohtml", "css", "js"]17 include_ext = ["go", "tpl", "tmpl", "html", "gohtml", "css", "js"]
18 include_file = []18 include_file = []
@@ -21,8 +21,8 @@ tmp_dir = "tmp"
21 poll = false21 poll = false
22 poll_interval = 022 poll_interval = 0
23 rerun = false23 rerun = false
24- rerun_delay = 50024+ rerun_delay = 1000
25- send_interrupt = false25+ send_interrupt = true
26 stop_on_error = false26 stop_on_error = false
27 27
28 [color]28 [color]
@@ -40,5 +40,5 @@ tmp_dir = "tmp"
40 clean_on_exit = false40 clean_on_exit = false
41 41
42 [screen]42 [screen]
43- clear_on_rebuild = false43+ clear_on_rebuild = true
44 keep_scroll = true44 keep_scroll = true
M .gitignore +2 -1
@@ -13,4 +13,5 @@ dist/
1313 data/
1414 setup.sh
1515 .iscontainer
16-sqlite.db⏎̸
16+sqlite.db
17+.vscode/
@@ -13,4 +13,5 @@ dist/
13 data/13 data/
14 setup.sh14 setup.sh
15 .iscontainer15 .iscontainer
16-sqlite.db⏎̸16+sqlite.db
17+.vscode/
A assets/go-gopher.svg +2 -0
@@ -0,0 +1,2 @@
1+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2+<svg width="800px" height="800px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><title>file_type_go</title><path d="M5.987,17.15c.04.826.865.444,1.239.186.355-.245.459-.041.49-.525a7.937,7.937,0,0,0,.039-.955,2.342,2.342,0,0,0-1.56.4c-.227.165-.651.69-.208.9" style="fill:#f6d2a2;fill-rule:evenodd"/><path d="M6.416,17.732a.527.527,0,0,1-.236-.051.541.541,0,0,1-.27-.482.358.358,0,0,1-.2-.283,1.006,1.006,0,0,1,.432-.727,2.4,2.4,0,0,1,1.613-.413l.068.006,0,.068a5.667,5.667,0,0,1-.023.747c-.006.073-.011.145-.016.218-.022.343-.086.395-.263.456a.975.975,0,0,0-.261.129A1.788,1.788,0,0,1,6.416,17.732Zm1.11-1.808a2.2,2.2,0,0,0-1.286.394c-.162.118-.387.394-.368.584a.219.219,0,0,0,.147.177l.043.02,0,.047c.01.206.072.338.185.394.218.108.624-.058.931-.269a1.127,1.127,0,0,1,.3-.148c.116-.04.14-.049.157-.318,0-.073.01-.146.016-.22a6.148,6.148,0,0,0,.026-.658Q7.6,15.924,7.527,15.924Z"/><path d="M5.987,17.15a.65.65,0,0,0,.329-.185" style="fill:#c6b198;fill-rule:evenodd"/><path d="M5.987,17.229a.079.079,0,0,1-.027-.152l.053-.019a.473.473,0,0,0,.241-.139.079.079,0,1,1,.126.094.615.615,0,0,1-.314.193l-.051.018A.078.078,0,0,1,5.987,17.229Z"/><path d="M7.894,7.309C4.731,6.42,7.083,2.4,9.622,4.052Z" style="fill:#6ad7e5;fill-rule:evenodd"/><path d="M7.934,7.4l-.061-.017a1.927,1.927,0,0,1-1.562-1.5,2.094,2.094,0,0,1,.977-1.979,2.116,2.116,0,0,1,2.378.081l.06.039Zm.449-3.666a1.852,1.852,0,0,0-1.01.3,1.933,1.933,0,0,0-.907,1.823A1.755,1.755,0,0,0,7.854,7.216L9.519,4.079A2.17,2.17,0,0,0,8.383,3.736Z"/><path d="M21.944,3.75c2.5-1.758,4.765,2.209,1.856,3.207Z" style="fill:#6ad7e5;fill-rule:evenodd"/><path d="M23.765,7.052,21.84,3.727l.059-.041a2.066,2.066,0,0,1,2.295-.193,2.122,2.122,0,0,1,1.044,1.965,1.881,1.881,0,0,1-1.413,1.573ZM22.049,3.773,23.836,6.86a1.712,1.712,0,0,0,1.246-1.418,1.961,1.961,0,0,0-.967-1.814A1.857,1.857,0,0,0,22.049,3.773Z"/><path d="M22.483,27.317c.636.393,1.8,1.583.844,2.16-.916.839-1.429-.921-2.234-1.165A2.225,2.225,0,0,1,22.483,27.317Z" style="fill:#f6d2a2;fill-rule:evenodd"/><path d="M22.844,29.781h-.038c-.361-.022-.661-.374-.951-.715a2.041,2.041,0,0,0-.785-.677l-.106-.032.066-.09a2.323,2.323,0,0,1,1.441-1.026l.029,0,.025.015c.407.252,1.247.988,1.243,1.654a.73.73,0,0,1-.393.636A.791.791,0,0,1,22.844,29.781Zm-1.625-1.5a2.505,2.505,0,0,1,.756.687c.281.33.547.642.841.66a.626.626,0,0,0,.457-.2l.012-.009a.578.578,0,0,0,.324-.506c0-.532-.672-1.2-1.144-1.5A2.055,2.055,0,0,0,21.219,28.276Z"/><path d="M23.327,29.556a.079.079,0,0,1-.07-.043,2.918,2.918,0,0,1-.123-.3,1.309,1.309,0,0,0-.286-.518.079.079,0,0,1,.109-.113,1.447,1.447,0,0,1,.325.577,2.806,2.806,0,0,0,.116.28.079.079,0,0,1-.035.106A.078.078,0,0,1,23.327,29.556Z"/><path d="M11.124,28.662c-.746.115-1.166.789-1.788,1.131-.586.35-.811-.112-.863-.206-.092-.042-.084.039-.226-.1C7.7,28.625,8.812,28,9.39,27.572,10.195,27.409,10.7,28.107,11.124,28.662Z" style="fill:#f6d2a2;fill-rule:evenodd"/><path d="M8.939,30a.535.535,0,0,1-.141-.018.647.647,0,0,1-.38-.33H8.4c-.066,0-.115-.014-.213-.113l-.011-.013c-.523-.826.367-1.453.957-1.868.074-.052.144-.1.207-.148l.014-.01.017,0c.829-.168,1.353.518,1.773,1.069l.119.155-.131.02a2.263,2.263,0,0,0-1.105.634,4.023,4.023,0,0,1-.657.488A.859.859,0,0,1,8.939,30Zm-.527-.506a.213.213,0,0,1,.092.021l.023.011.02.035a.509.509,0,0,0,.292.269.6.6,0,0,0,.456-.1,3.909,3.909,0,0,0,.633-.471,2.629,2.629,0,0,1,1.056-.644c-.4-.526-.862-1.094-1.562-.963-.061.044-.127.091-.2.139-.6.424-1.352.952-.919,1.648.062.062.077.061.09.061Z"/><path d="M8.472,29.665H8.465a.079.079,0,0,1-.072-.085,1.2,1.2,0,0,1,.253-.572,1.852,1.852,0,0,0,.165-.276.079.079,0,0,1,.144.064,1.992,1.992,0,0,1-.178.3,1.065,1.065,0,0,0-.226.5A.079.079,0,0,1,8.472,29.665Z"/><path d="M8.026,6.2c-.417-.221-.723-.52-.468-1,.236-.447.675-.4,1.092-.178Z" style="fill-rule:evenodd"/><path d="M23.141,5.778c.417-.221.723-.52.468-1-.236-.447-.675-.4-1.092-.178Z" style="fill-rule:evenodd"/><path d="M26.013,17.046c-.04.826-.865.444-1.239.186-.355-.245-.459-.041-.49-.525a7.936,7.936,0,0,1-.039-.955,2.342,2.342,0,0,1,1.56.4c.227.165.651.69.208.9" style="fill:#f6d2a2;fill-rule:evenodd"/><path d="M25.584,17.627a1.788,1.788,0,0,1-.854-.331.975.975,0,0,0-.261-.129c-.177-.062-.241-.114-.263-.456,0-.073-.01-.145-.016-.218a5.676,5.676,0,0,1-.023-.747l0-.068.068-.006a2.4,2.4,0,0,1,1.613.413,1.006,1.006,0,0,1,.432.727.358.358,0,0,1-.2.283.541.541,0,0,1-.27.482A.527.527,0,0,1,25.584,17.627Zm-1.263-1.8a6.148,6.148,0,0,0,.026.658c.006.073.011.146.016.22.017.269.041.277.157.318a1.127,1.127,0,0,1,.3.148c.306.212.713.377.931.269.113-.056.175-.188.185-.394l0-.047.043-.02a.219.219,0,0,0,.147-.177c.018-.19-.206-.466-.368-.584A2.232,2.232,0,0,0,24.321,15.824Z"/><path d="M26.013,17.046a.65.65,0,0,1-.329-.185" style="fill:#c6b198;fill-rule:evenodd"/><path d="M26.013,17.124a.078.078,0,0,1-.027,0l-.051-.018a.614.614,0,0,1-.314-.193.079.079,0,1,1,.126-.094.473.473,0,0,0,.241.139l.053.019a.079.079,0,0,1-.027.152Z"/><path d="M15.683,2.079c3.166,0,6.134.453,7.681,3.495,1.389,3.409.895,7.086,1.107,10.689.182,3.094.585,6.668-.85,9.536-1.51,3.016-5.283,3.775-8.392,3.664-2.442-.087-5.391-.884-6.769-3.115-1.617-2.617-.851-6.509-.734-9.421.139-3.449-.934-6.91.2-10.285,1.176-3.5,4.346-4.293,7.758-4.563" style="fill:#6ad7e5;fill-rule:evenodd"/><path d="M15.77,29.55c-.2,0-.38,0-.543-.01-1.5-.054-5.176-.469-6.833-3.152-1.307-2.116-1.074-5.041-.869-7.622.052-.649.1-1.263.124-1.843a29.673,29.673,0,0,0-.157-3.666,16.216,16.216,0,0,1,.36-6.641,5.812,5.812,0,0,1,3.08-3.539A12.948,12.948,0,0,1,15.677,2h.006c3.091,0,6.17.429,7.751,3.538l0,.006a20.518,20.518,0,0,1,1.044,7.762c.005.969.011,1.972.068,2.952.017.3.037.6.057.9.184,2.841.392,6.062-.915,8.673C22.059,29.092,17.947,29.55,15.77,29.55ZM15.686,2.157c-3.294.261-6.5.988-7.687,4.509a16.1,16.1,0,0,0-.353,6.578A29.788,29.788,0,0,1,7.8,16.93c-.024.584-.072,1.2-.124,1.85-.2,2.557-.434,5.455.846,7.527,1.618,2.619,5.228,3.025,6.705,3.077,1.922.069,6.579-.147,8.318-3.621,1.288-2.573,1.081-5.771.9-8.592-.02-.3-.039-.606-.057-.9-.058-.984-.063-1.989-.068-2.96a20.408,20.408,0,0,0-1.031-7.7C21.75,2.576,18.725,2.158,15.686,2.157Z"/><path d="M16.242,6.329c.568,3.3,5.97,2.431,5.194-.9-.7-2.988-5.383-2.161-5.194.9" style="fill:#ffffff;fill-rule:evenodd"/><path d="M18.808,8.485a3.025,3.025,0,0,1-.988-.162,2.444,2.444,0,0,1-1.653-1.982V6.333a2.672,2.672,0,0,1,2.416-2.814A2.482,2.482,0,0,1,21.51,5.41a2.312,2.312,0,0,1-.673,2.343A3.072,3.072,0,0,1,18.808,8.485ZM16.318,6.32A2.3,2.3,0,0,0,17.87,8.18a2.987,2.987,0,0,0,2.865-.54,2.165,2.165,0,0,0,.627-2.2A2.335,2.335,0,0,0,18.6,3.67,2.524,2.524,0,0,0,16.318,6.32Z"/><path d="M9.788,6.863C10.524,9.74,15.13,9,14.956,6.118c-.208-3.456-5.873-2.789-5.169.745" style="fill:#ffffff;fill-rule:evenodd"/><path d="M12.245,8.761A2.826,2.826,0,0,1,10.9,8.435,2.384,2.384,0,0,1,9.716,6.882v0a2.411,2.411,0,0,1,.826-2.472,3.107,3.107,0,0,1,3.087-.394,2.335,2.335,0,0,1,1.4,2.1,2.529,2.529,0,0,1-2.29,2.609A3.2,3.2,0,0,1,12.245,8.761ZM9.86,6.847a2.4,2.4,0,0,0,2.858,1.729,2.385,2.385,0,0,0,2.165-2.454,2.193,2.193,0,0,0-1.314-1.976,2.953,2.953,0,0,0-2.935.374A2.269,2.269,0,0,0,9.86,6.847Z"/><path d="M16.894,10.294a7.45,7.45,0,0,1,.016,1.369c-.11.207-.325.229-.51.313a.751.751,0,0,1-.575-.449,9.72,9.72,0,0,1,.041-1.549Z" style="fill:#ffffff;fill-rule:evenodd"/><path d="M16.411,12.057l-.023,0a.828.828,0,0,1-.635-.5l0-.01v-.011a5.2,5.2,0,0,1,.009-1.063c.013-.163.026-.331.031-.5l0-.1,1.181.362v.058c0,.142.012.289.024.444a3.746,3.746,0,0,1-.008.939l0,.012-.006.011a.658.658,0,0,1-.414.3c-.047.017-.091.032-.134.052Zm-.509-.551a.671.671,0,0,0,.488.388c.041-.017.083-.032.123-.047a.525.525,0,0,0,.322-.211,3.683,3.683,0,0,0,0-.887c-.01-.137-.02-.268-.023-.4l-.875-.268c-.007.136-.017.271-.027.4A5.05,5.05,0,0,0,15.9,11.506Z"/><ellipse cx="11.058" cy="6.394" rx="0.778" ry="0.842"/><ellipse cx="11.412" cy="6.584" rx="0.183" ry="0.214" style="fill:#fff"/><ellipse cx="17.574" cy="6.184" rx="0.765" ry="0.842"/><ellipse cx="17.922" cy="6.374" rx="0.18" ry="0.214" style="fill:#fff"/><path d="M14.671,10.247c-.339.822.189,2.467,1.11,1.254a9.72,9.72,0,0,1,.041-1.549Z" style="fill:#ffffff;fill-rule:evenodd"/><path d="M15.166,12.013a.451.451,0,0,1-.226-.062,1.6,1.6,0,0,1-.341-1.733l.015-.036L15.9,9.85l0,.1c-.005.17-.019.34-.032.5a5,5,0,0,0-.01,1.031l0,.032-.019.026C15.574,11.9,15.345,12.013,15.166,12.013Zm-.436-1.7a1.458,1.458,0,0,0,.288,1.5c.238.136.5-.1.681-.335a5.349,5.349,0,0,1,.013-1.031c.01-.129.02-.261.027-.392Z"/><path d="M14.787,8.663a1,1,0,0,0-.821,1.4c.435.788,1.407-.07,2.013.011.7.014,1.268.737,1.827.131.622-.674-.268-1.331-.964-1.624Z" style="fill:#f6d2a2;fill-rule:evenodd"/><path d="M17.308,10.526a1.727,1.727,0,0,1-.641-.179,1.928,1.928,0,0,0-.689-.194h-.009a1.749,1.749,0,0,0-.712.152c-.491.17-1.048.362-1.36-.2a.946.946,0,0,1,.014-.917,1.118,1.118,0,0,1,.869-.6h0L16.857,8.5l.016.007c.517.218,1.106.617,1.207,1.083a.836.836,0,0,1-.772.934ZM15.985,10a2.056,2.056,0,0,1,.743.206c.393.165.7.295,1.021-.05a.58.58,0,0,0,.178-.528c-.087-.4-.642-.769-1.1-.964l-2.036.081a.973.973,0,0,0-.742.517.792.792,0,0,0-.015.767c.248.449.7.294,1.17.13A1.856,1.856,0,0,1,15.985,10Z" style="fill:#231f20"/><path d="M14.708,8.63C14.661,7.535,16.75,7.4,17,8.315s-2.186,1.128-2.289.316Z" style="fill-rule:evenodd"/></svg>⏎̸
@@ -0,0 +1,2 @@
1+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2+<svg width="800px" height="800px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><title>file_type_go</title><path d="M5.987,17.15c.04.826.865.444,1.239.186.355-.245.459-.041.49-.525a7.937,7.937,0,0,0,.039-.955,2.342,2.342,0,0,0-1.56.4c-.227.165-.651.69-.208.9" style="fill:#f6d2a2;fill-rule:evenodd"/><path d="M6.416,17.732a.527.527,0,0,1-.236-.051.541.541,0,0,1-.27-.482.358.358,0,0,1-.2-.283,1.006,1.006,0,0,1,.432-.727,2.4,2.4,0,0,1,1.613-.413l.068.006,0,.068a5.667,5.667,0,0,1-.023.747c-.006.073-.011.145-.016.218-.022.343-.086.395-.263.456a.975.975,0,0,0-.261.129A1.788,1.788,0,0,1,6.416,17.732Zm1.11-1.808a2.2,2.2,0,0,0-1.286.394c-.162.118-.387.394-.368.584a.219.219,0,0,0,.147.177l.043.02,0,.047c.01.206.072.338.185.394.218.108.624-.058.931-.269a1.127,1.127,0,0,1,.3-.148c.116-.04.14-.049.157-.318,0-.073.01-.146.016-.22a6.148,6.148,0,0,0,.026-.658Q7.6,15.924,7.527,15.924Z"/><path d="M5.987,17.15a.65.65,0,0,0,.329-.185" style="fill:#c6b198;fill-rule:evenodd"/><path d="M5.987,17.229a.079.079,0,0,1-.027-.152l.053-.019a.473.473,0,0,0,.241-.139.079.079,0,1,1,.126.094.615.615,0,0,1-.314.193l-.051.018A.078.078,0,0,1,5.987,17.229Z"/><path d="M7.894,7.309C4.731,6.42,7.083,2.4,9.622,4.052Z" style="fill:#6ad7e5;fill-rule:evenodd"/><path d="M7.934,7.4l-.061-.017a1.927,1.927,0,0,1-1.562-1.5,2.094,2.094,0,0,1,.977-1.979,2.116,2.116,0,0,1,2.378.081l.06.039Zm.449-3.666a1.852,1.852,0,0,0-1.01.3,1.933,1.933,0,0,0-.907,1.823A1.755,1.755,0,0,0,7.854,7.216L9.519,4.079A2.17,2.17,0,0,0,8.383,3.736Z"/><path d="M21.944,3.75c2.5-1.758,4.765,2.209,1.856,3.207Z" style="fill:#6ad7e5;fill-rule:evenodd"/><path d="M23.765,7.052,21.84,3.727l.059-.041a2.066,2.066,0,0,1,2.295-.193,2.122,2.122,0,0,1,1.044,1.965,1.881,1.881,0,0,1-1.413,1.573ZM22.049,3.773,23.836,6.86a1.712,1.712,0,0,0,1.246-1.418,1.961,1.961,0,0,0-.967-1.814A1.857,1.857,0,0,0,22.049,3.773Z"/><path d="M22.483,27.317c.636.393,1.8,1.583.844,2.16-.916.839-1.429-.921-2.234-1.165A2.225,2.225,0,0,1,22.483,27.317Z" style="fill:#f6d2a2;fill-rule:evenodd"/><path d="M22.844,29.781h-.038c-.361-.022-.661-.374-.951-.715a2.041,2.041,0,0,0-.785-.677l-.106-.032.066-.09a2.323,2.323,0,0,1,1.441-1.026l.029,0,.025.015c.407.252,1.247.988,1.243,1.654a.73.73,0,0,1-.393.636A.791.791,0,0,1,22.844,29.781Zm-1.625-1.5a2.505,2.505,0,0,1,.756.687c.281.33.547.642.841.66a.626.626,0,0,0,.457-.2l.012-.009a.578.578,0,0,0,.324-.506c0-.532-.672-1.2-1.144-1.5A2.055,2.055,0,0,0,21.219,28.276Z"/><path d="M23.327,29.556a.079.079,0,0,1-.07-.043,2.918,2.918,0,0,1-.123-.3,1.309,1.309,0,0,0-.286-.518.079.079,0,0,1,.109-.113,1.447,1.447,0,0,1,.325.577,2.806,2.806,0,0,0,.116.28.079.079,0,0,1-.035.106A.078.078,0,0,1,23.327,29.556Z"/><path d="M11.124,28.662c-.746.115-1.166.789-1.788,1.131-.586.35-.811-.112-.863-.206-.092-.042-.084.039-.226-.1C7.7,28.625,8.812,28,9.39,27.572,10.195,27.409,10.7,28.107,11.124,28.662Z" style="fill:#f6d2a2;fill-rule:evenodd"/><path d="M8.939,30a.535.535,0,0,1-.141-.018.647.647,0,0,1-.38-.33H8.4c-.066,0-.115-.014-.213-.113l-.011-.013c-.523-.826.367-1.453.957-1.868.074-.052.144-.1.207-.148l.014-.01.017,0c.829-.168,1.353.518,1.773,1.069l.119.155-.131.02a2.263,2.263,0,0,0-1.105.634,4.023,4.023,0,0,1-.657.488A.859.859,0,0,1,8.939,30Zm-.527-.506a.213.213,0,0,1,.092.021l.023.011.02.035a.509.509,0,0,0,.292.269.6.6,0,0,0,.456-.1,3.909,3.909,0,0,0,.633-.471,2.629,2.629,0,0,1,1.056-.644c-.4-.526-.862-1.094-1.562-.963-.061.044-.127.091-.2.139-.6.424-1.352.952-.919,1.648.062.062.077.061.09.061Z"/><path d="M8.472,29.665H8.465a.079.079,0,0,1-.072-.085,1.2,1.2,0,0,1,.253-.572,1.852,1.852,0,0,0,.165-.276.079.079,0,0,1,.144.064,1.992,1.992,0,0,1-.178.3,1.065,1.065,0,0,0-.226.5A.079.079,0,0,1,8.472,29.665Z"/><path d="M8.026,6.2c-.417-.221-.723-.52-.468-1,.236-.447.675-.4,1.092-.178Z" style="fill-rule:evenodd"/><path d="M23.141,5.778c.417-.221.723-.52.468-1-.236-.447-.675-.4-1.092-.178Z" style="fill-rule:evenodd"/><path d="M26.013,17.046c-.04.826-.865.444-1.239.186-.355-.245-.459-.041-.49-.525a7.936,7.936,0,0,1-.039-.955,2.342,2.342,0,0,1,1.56.4c.227.165.651.69.208.9" style="fill:#f6d2a2;fill-rule:evenodd"/><path d="M25.584,17.627a1.788,1.788,0,0,1-.854-.331.975.975,0,0,0-.261-.129c-.177-.062-.241-.114-.263-.456,0-.073-.01-.145-.016-.218a5.676,5.676,0,0,1-.023-.747l0-.068.068-.006a2.4,2.4,0,0,1,1.613.413,1.006,1.006,0,0,1,.432.727.358.358,0,0,1-.2.283.541.541,0,0,1-.27.482A.527.527,0,0,1,25.584,17.627Zm-1.263-1.8a6.148,6.148,0,0,0,.026.658c.006.073.011.146.016.22.017.269.041.277.157.318a1.127,1.127,0,0,1,.3.148c.306.212.713.377.931.269.113-.056.175-.188.185-.394l0-.047.043-.02a.219.219,0,0,0,.147-.177c.018-.19-.206-.466-.368-.584A2.232,2.232,0,0,0,24.321,15.824Z"/><path d="M26.013,17.046a.65.65,0,0,1-.329-.185" style="fill:#c6b198;fill-rule:evenodd"/><path d="M26.013,17.124a.078.078,0,0,1-.027,0l-.051-.018a.614.614,0,0,1-.314-.193.079.079,0,1,1,.126-.094.473.473,0,0,0,.241.139l.053.019a.079.079,0,0,1-.027.152Z"/><path d="M15.683,2.079c3.166,0,6.134.453,7.681,3.495,1.389,3.409.895,7.086,1.107,10.689.182,3.094.585,6.668-.85,9.536-1.51,3.016-5.283,3.775-8.392,3.664-2.442-.087-5.391-.884-6.769-3.115-1.617-2.617-.851-6.509-.734-9.421.139-3.449-.934-6.91.2-10.285,1.176-3.5,4.346-4.293,7.758-4.563" style="fill:#6ad7e5;fill-rule:evenodd"/><path d="M15.77,29.55c-.2,0-.38,0-.543-.01-1.5-.054-5.176-.469-6.833-3.152-1.307-2.116-1.074-5.041-.869-7.622.052-.649.1-1.263.124-1.843a29.673,29.673,0,0,0-.157-3.666,16.216,16.216,0,0,1,.36-6.641,5.812,5.812,0,0,1,3.08-3.539A12.948,12.948,0,0,1,15.677,2h.006c3.091,0,6.17.429,7.751,3.538l0,.006a20.518,20.518,0,0,1,1.044,7.762c.005.969.011,1.972.068,2.952.017.3.037.6.057.9.184,2.841.392,6.062-.915,8.673C22.059,29.092,17.947,29.55,15.77,29.55ZM15.686,2.157c-3.294.261-6.5.988-7.687,4.509a16.1,16.1,0,0,0-.353,6.578A29.788,29.788,0,0,1,7.8,16.93c-.024.584-.072,1.2-.124,1.85-.2,2.557-.434,5.455.846,7.527,1.618,2.619,5.228,3.025,6.705,3.077,1.922.069,6.579-.147,8.318-3.621,1.288-2.573,1.081-5.771.9-8.592-.02-.3-.039-.606-.057-.9-.058-.984-.063-1.989-.068-2.96a20.408,20.408,0,0,0-1.031-7.7C21.75,2.576,18.725,2.158,15.686,2.157Z"/><path d="M16.242,6.329c.568,3.3,5.97,2.431,5.194-.9-.7-2.988-5.383-2.161-5.194.9" style="fill:#ffffff;fill-rule:evenodd"/><path d="M18.808,8.485a3.025,3.025,0,0,1-.988-.162,2.444,2.444,0,0,1-1.653-1.982V6.333a2.672,2.672,0,0,1,2.416-2.814A2.482,2.482,0,0,1,21.51,5.41a2.312,2.312,0,0,1-.673,2.343A3.072,3.072,0,0,1,18.808,8.485ZM16.318,6.32A2.3,2.3,0,0,0,17.87,8.18a2.987,2.987,0,0,0,2.865-.54,2.165,2.165,0,0,0,.627-2.2A2.335,2.335,0,0,0,18.6,3.67,2.524,2.524,0,0,0,16.318,6.32Z"/><path d="M9.788,6.863C10.524,9.74,15.13,9,14.956,6.118c-.208-3.456-5.873-2.789-5.169.745" style="fill:#ffffff;fill-rule:evenodd"/><path d="M12.245,8.761A2.826,2.826,0,0,1,10.9,8.435,2.384,2.384,0,0,1,9.716,6.882v0a2.411,2.411,0,0,1,.826-2.472,3.107,3.107,0,0,1,3.087-.394,2.335,2.335,0,0,1,1.4,2.1,2.529,2.529,0,0,1-2.29,2.609A3.2,3.2,0,0,1,12.245,8.761ZM9.86,6.847a2.4,2.4,0,0,0,2.858,1.729,2.385,2.385,0,0,0,2.165-2.454,2.193,2.193,0,0,0-1.314-1.976,2.953,2.953,0,0,0-2.935.374A2.269,2.269,0,0,0,9.86,6.847Z"/><path d="M16.894,10.294a7.45,7.45,0,0,1,.016,1.369c-.11.207-.325.229-.51.313a.751.751,0,0,1-.575-.449,9.72,9.72,0,0,1,.041-1.549Z" style="fill:#ffffff;fill-rule:evenodd"/><path d="M16.411,12.057l-.023,0a.828.828,0,0,1-.635-.5l0-.01v-.011a5.2,5.2,0,0,1,.009-1.063c.013-.163.026-.331.031-.5l0-.1,1.181.362v.058c0,.142.012.289.024.444a3.746,3.746,0,0,1-.008.939l0,.012-.006.011a.658.658,0,0,1-.414.3c-.047.017-.091.032-.134.052Zm-.509-.551a.671.671,0,0,0,.488.388c.041-.017.083-.032.123-.047a.525.525,0,0,0,.322-.211,3.683,3.683,0,0,0,0-.887c-.01-.137-.02-.268-.023-.4l-.875-.268c-.007.136-.017.271-.027.4A5.05,5.05,0,0,0,15.9,11.506Z"/><ellipse cx="11.058" cy="6.394" rx="0.778" ry="0.842"/><ellipse cx="11.412" cy="6.584" rx="0.183" ry="0.214" style="fill:#fff"/><ellipse cx="17.574" cy="6.184" rx="0.765" ry="0.842"/><ellipse cx="17.922" cy="6.374" rx="0.18" ry="0.214" style="fill:#fff"/><path d="M14.671,10.247c-.339.822.189,2.467,1.11,1.254a9.72,9.72,0,0,1,.041-1.549Z" style="fill:#ffffff;fill-rule:evenodd"/><path d="M15.166,12.013a.451.451,0,0,1-.226-.062,1.6,1.6,0,0,1-.341-1.733l.015-.036L15.9,9.85l0,.1c-.005.17-.019.34-.032.5a5,5,0,0,0-.01,1.031l0,.032-.019.026C15.574,11.9,15.345,12.013,15.166,12.013Zm-.436-1.7a1.458,1.458,0,0,0,.288,1.5c.238.136.5-.1.681-.335a5.349,5.349,0,0,1,.013-1.031c.01-.129.02-.261.027-.392Z"/><path d="M14.787,8.663a1,1,0,0,0-.821,1.4c.435.788,1.407-.07,2.013.011.7.014,1.268.737,1.827.131.622-.674-.268-1.331-.964-1.624Z" style="fill:#f6d2a2;fill-rule:evenodd"/><path d="M17.308,10.526a1.727,1.727,0,0,1-.641-.179,1.928,1.928,0,0,0-.689-.194h-.009a1.749,1.749,0,0,0-.712.152c-.491.17-1.048.362-1.36-.2a.946.946,0,0,1,.014-.917,1.118,1.118,0,0,1,.869-.6h0L16.857,8.5l.016.007c.517.218,1.106.617,1.207,1.083a.836.836,0,0,1-.772.934ZM15.985,10a2.056,2.056,0,0,1,.743.206c.393.165.7.295,1.021-.05a.58.58,0,0,0,.178-.528c-.087-.4-.642-.769-1.1-.964l-2.036.081a.973.973,0,0,0-.742.517.792.792,0,0,0-.015.767c.248.449.7.294,1.17.13A1.856,1.856,0,0,1,15.985,10Z" style="fill:#231f20"/><path d="M14.708,8.63C14.661,7.535,16.75,7.4,17,8.315s-2.186,1.128-2.289.316Z" style="fill-rule:evenodd"/></svg>⏎̸
R internal/webui/public/assets/imgs/gordon-mascot-hq-trsp.png → assets/gordon-mascot-hq-trsp.png +0 -0 binary file: no text diff · renamed, 100% similar

binary file: no text diff · renamed, 100% similar

R internal/webui/public/assets/imgs/gordon-mascot.png → assets/gordon-mascot.png +0 -0 binary file: no text diff · renamed, 100% similar

binary file: no text diff · renamed, 100% similar

M internal/cli/cmd/deploy.go +21 -5
@@ -11,9 +11,10 @@ import (
1111
1212 "github.com/bnema/gordon/internal/cli"
1313 "github.com/bnema/gordon/internal/cli/handler"
14- "github.com/bnema/gordon/internal/cli/model"
14+ "github.com/bnema/gordon/internal/cli/mvu"
1515 "github.com/bnema/gordon/internal/common"
1616 "github.com/bnema/gordon/pkg/docker"
17+ "github.com/cheggaaa/pb"
1718 "github.com/fatih/color"
1819 "github.com/spf13/cobra"
1920 )
@@ -34,7 +35,7 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
3435 },
3536 Run: func(cmd *cobra.Command, args []string) {
3637 imageName := args[0]
37- fmt.Println("Exporting image:", imageName)
38+ color.Blue("Deploying image: %s", imageName)
3839
3940 // Validate the image name
4041 if err := handler.ValidateImageName(imageName); err != nil {
@@ -57,13 +58,18 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
5758 return
5859 }
5960
61+ // Export the image to a reader and return its true size
6062 reader, actualSize, err := exportDockerImage(imageName)
6163 if err != nil {
6264 fmt.Println("Error exporting image:", err)
6365 return
6466 }
6567
66- fmt.Print(actualSize)
68+ // Initialize the progress bar
69+ bar := pb.New64(actualSize)
70+
71+ // Wrap the ReadCounter with the bar proxy
72+ progressReader := bar.NewProxyReader(reader)
6773
6874 // Create a RequestPayload and populate it
6975 reqPayload := common.RequestPayload{
@@ -72,15 +78,23 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
7278 Ports: port,
7379 TargetDomain: targetDomain,
7480 ImageName: imageName,
75- Data: reader,
81+ Data: progressReader,
7682 },
7783 }
84+
85+ // Start the progress bar
86+ bar.Start()
7887 // Send the request to the backend
7988 resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push")
8089 if err != nil {
8190 fmt.Println("Error sending HTTP request:", err)
8291 return
8392 }
93+ // Before finishing, ensure progress does not exceed 100%
94+ bar.Set64(actualSize)
95+
96+ // Stop the progress bar
97+ bar.Finish()
8498
8599 // Check the response
86100 targetDomain := string(resp.Body)
@@ -103,7 +117,7 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
103117 }
104118
105119 // Run the TUI program
106- finalModel, err := model.RunDeploymentTUI(client, imageName, targetDomain, port)
120+ finalModel, err := mvu.RunDeploymentTUI(client, imageName, targetDomain, port)
107121 if err != nil {
108122 fmt.Println("Error running deployment TUI:", err)
109123 return
@@ -144,5 +158,7 @@ func exportDockerImage(imageName string) (io.ReadCloser, int64, error) {
144158 if err != nil {
145159 return nil, 0, fmt.Errorf("error exporting image: %w", err)
146160 }
161+
162+ // Return the wrapped reader.
147163 return reader, actualSize, nil
148164 }
@@ -11,9 +11,10 @@ import (
11 11
12 "github.com/bnema/gordon/internal/cli"12 "github.com/bnema/gordon/internal/cli"
13 "github.com/bnema/gordon/internal/cli/handler"13 "github.com/bnema/gordon/internal/cli/handler"
14- "github.com/bnema/gordon/internal/cli/model"14+ "github.com/bnema/gordon/internal/cli/mvu"
15 "github.com/bnema/gordon/internal/common"15 "github.com/bnema/gordon/internal/common"
16 "github.com/bnema/gordon/pkg/docker"16 "github.com/bnema/gordon/pkg/docker"
17+ "github.com/cheggaaa/pb"
17 "github.com/fatih/color"18 "github.com/fatih/color"
18 "github.com/spf13/cobra"19 "github.com/spf13/cobra"
19 )20 )
@@ -34,7 +35,7 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
34 },35 },
35 Run: func(cmd *cobra.Command, args []string) {36 Run: func(cmd *cobra.Command, args []string) {
36 imageName := args[0]37 imageName := args[0]
37- fmt.Println("Exporting image:", imageName)38+ color.Blue("Deploying image: %s", imageName)
38 39
39 // Validate the image name40 // Validate the image name
40 if err := handler.ValidateImageName(imageName); err != nil {41 if err := handler.ValidateImageName(imageName); err != nil {
@@ -57,13 +58,18 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
57 return58 return
58 }59 }
59 60
61+ // Export the image to a reader and return its true size
60 reader, actualSize, err := exportDockerImage(imageName)62 reader, actualSize, err := exportDockerImage(imageName)
61 if err != nil {63 if err != nil {
62 fmt.Println("Error exporting image:", err)64 fmt.Println("Error exporting image:", err)
63 return65 return
64 }66 }
65 67
66- fmt.Print(actualSize)68+ // Initialize the progress bar
69+ bar := pb.New64(actualSize)
70+
71+ // Wrap the ReadCounter with the bar proxy
72+ progressReader := bar.NewProxyReader(reader)
67 73
68 // Create a RequestPayload and populate it74 // Create a RequestPayload and populate it
69 reqPayload := common.RequestPayload{75 reqPayload := common.RequestPayload{
@@ -72,15 +78,23 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
72 Ports: port,78 Ports: port,
73 TargetDomain: targetDomain,79 TargetDomain: targetDomain,
74 ImageName: imageName,80 ImageName: imageName,
75- Data: reader,81+ Data: progressReader,
76 },82 },
77 }83 }
84+
85+ // Start the progress bar
86+ bar.Start()
78 // Send the request to the backend87 // Send the request to the backend
79 resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push")88 resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push")
80 if err != nil {89 if err != nil {
81 fmt.Println("Error sending HTTP request:", err)90 fmt.Println("Error sending HTTP request:", err)
82 return91 return
83 }92 }
93+ // Before finishing, ensure progress does not exceed 100%
94+ bar.Set64(actualSize)
95+
96+ // Stop the progress bar
97+ bar.Finish()
84 98
85 // Check the response99 // Check the response
86 targetDomain := string(resp.Body)100 targetDomain := string(resp.Body)
@@ -103,7 +117,7 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
103 }117 }
104 118
105 // Run the TUI program119 // Run the TUI program
106- finalModel, err := model.RunDeploymentTUI(client, imageName, targetDomain, port)120+ finalModel, err := mvu.RunDeploymentTUI(client, imageName, targetDomain, port)
107 if err != nil {121 if err != nil {
108 fmt.Println("Error running deployment TUI:", err)122 fmt.Println("Error running deployment TUI:", err)
109 return123 return
@@ -144,5 +158,7 @@ func exportDockerImage(imageName string) (io.ReadCloser, int64, error) {
144 if err != nil {158 if err != nil {
145 return nil, 0, fmt.Errorf("error exporting image: %w", err)159 return nil, 0, fmt.Errorf("error exporting image: %w", err)
146 }160 }
161+
162+ // Return the wrapped reader.
147 return reader, actualSize, nil163 return reader, actualSize, nil
148 }164 }
M internal/cli/handler/regexcheck.go +2 -2
@@ -9,7 +9,7 @@ import (
99 func ValidateImageName(imageName string) error {
1010 match, _ := regexp.MatchString("^([a-zA-Z0-9_\\-\\.]+\\/)*[a-zA-Z0-9_\\-\\.]+(:[a-zA-Z0-9_\\-\\.]+)?$", imageName)
1111 if !match {
12- return fmt.Errorf("invalid image name format")
12+ return fmt.Errorf("You must specify a valid image name in the form (registry/)image:tag, check your container engine image list")
1313 }
1414 return nil
1515 }
@@ -23,7 +23,7 @@ func EnsureImageTag(imageName *string) {
2323 func ValidatePortMapping(port string) error {
2424 match, _ := regexp.MatchString("^[0-9]+:[0-9]+(\\/(tcp|udp))?$", port)
2525 if !match {
26- return fmt.Errorf("invalid port mapping format")
26+ return fmt.Errorf("You must specify a port mapping in the form port:port/proto, if no protocol is specified, TCP is used")
2727 }
2828 return nil
2929 }
@@ -9,7 +9,7 @@ import (
9 func ValidateImageName(imageName string) error {9 func ValidateImageName(imageName string) error {
10 match, _ := regexp.MatchString("^([a-zA-Z0-9_\\-\\.]+\\/)*[a-zA-Z0-9_\\-\\.]+(:[a-zA-Z0-9_\\-\\.]+)?$", imageName)10 match, _ := regexp.MatchString("^([a-zA-Z0-9_\\-\\.]+\\/)*[a-zA-Z0-9_\\-\\.]+(:[a-zA-Z0-9_\\-\\.]+)?$", imageName)
11 if !match {11 if !match {
12- return fmt.Errorf("invalid image name format")12+ return fmt.Errorf("You must specify a valid image name in the form (registry/)image:tag, check your container engine image list")
13 }13 }
14 return nil14 return nil
15 }15 }
@@ -23,7 +23,7 @@ func EnsureImageTag(imageName *string) {
23 func ValidatePortMapping(port string) error {23 func ValidatePortMapping(port string) error {
24 match, _ := regexp.MatchString("^[0-9]+:[0-9]+(\\/(tcp|udp))?$", port)24 match, _ := regexp.MatchString("^[0-9]+:[0-9]+(\\/(tcp|udp))?$", port)
25 if !match {25 if !match {
26- return fmt.Errorf("invalid port mapping format")26+ return fmt.Errorf("You must specify a port mapping in the form port:port/proto, if no protocol is specified, TCP is used")
27 }27 }
28 return nil28 return nil
29 }29 }
D internal/cli/model/_progressbar.go +0 -109
@@ -1,109 +0,0 @@
1-package model
2-
3-import (
4- "io"
5- "log"
6- "time"
7-
8- "github.com/charmbracelet/bubbles/progress"
9- tea "github.com/charmbracelet/bubbletea"
10-)
11-
12-const (
13- padding = 2
14- maxWidth = 80
15-)
16-
17-type progressMsg float64
18-
19-type ProgressBarModel struct {
20- Width int
21- progress progress.Model
22- progressChan chan float64
23-}
24-
25-func NewProgressBarModel() ProgressBarModel {
26- progressBar := progress.New(progress.WithScaledGradient("#FF7CCB", "#FDFF8C"))
27- progressChan := make(chan float64)
28- return ProgressBarModel{
29- progress: progressBar,
30- progressChan: progressChan,
31- }
32-}
33-
34-func (m ProgressBarModel) Init() tea.Cmd {
35- return tea.Batch(m.progress.Tick, m.tick)
36-}
37-
38-func updateProgressCmd(progress float64) tea.Cmd {
39- return func() tea.Msg {
40- return progressMsg(progress)
41- }
42-}
43-
44-type tickMsg time.Time
45-
46-type ProgressReader struct {
47- reader io.Reader
48- totalSize int64
49- readBytes int64
50- progressFn func(readBytes int64)
51-}
52-
53-// NewProgressReader creates a new ProgressReader.
54-func NewProgressReader(reader io.Reader, totalSize int64, progressFn func(readBytes int64)) *ProgressReader {
55- return &ProgressReader{
56- reader: reader,
57- totalSize: totalSize,
58- progressFn: progressFn,
59- }
60-}
61-
62-// Read implements the io.Reader interface.
63-func (pr *ProgressReader) Read(p []byte) (int, error) {
64- n, err := pr.reader.Read(p)
65- pr.readBytes += int64(n)
66- if pr.progressFn != nil {
67- pr.progressFn(pr.readBytes)
68- // Debug log to check progress
69- log.Printf("ProgressReader: Read %d bytes, total %d bytes", n, pr.readBytes)
70- }
71- if err != nil {
72- // Log error if not nil
73- log.Printf("ProgressReader encountered an error: %v", err)
74- }
75- return n, err
76-}
77-
78-// Close implements the io.Closer interface.
79-func (pr *ProgressReader) Close() error {
80- if closer, ok := pr.reader.(io.Closer); ok {
81- return closer.Close()
82- }
83- // If the underlying reader does not implement io.Closer, return nil
84- return nil
85-}
86-
87-func runProgressBarTUI(progressChan <-chan float64) {
88- m := NewProgressBarModel()
89-
90- // Update model to handle progress updates
91- m.Update = func(msg tea.Msg) (tea.Model, tea.Cmd) {
92- switch msg := msg.(type) {
93- case float64:
94- m.progress.SetValue(msg) // Update the progress bar's value
95- return m, tea.Batch(m.progress.Animate(), time.After(time.Millisecond*100))
96- case tea.KeyMsg:
97- if msg.String() == "q" {
98- return m, tea.Quit
99- }
100- }
101- return m, nil
102- }
103-
104- // Start the TUI
105- p := tea.NewProgram(m)
106- if err := p.Start(); err != nil {
107- log.Fatal(err)
108- }
109-}
@@ -1,109 +0,0 @@
1-package model
2-
3-import (
4- "io"
5- "log"
6- "time"
7-
8- "github.com/charmbracelet/bubbles/progress"
9- tea "github.com/charmbracelet/bubbletea"
10-)
11-
12-const (
13- padding = 2
14- maxWidth = 80
15-)
16-
17-type progressMsg float64
18-
19-type ProgressBarModel struct {
20- Width int
21- progress progress.Model
22- progressChan chan float64
23-}
24-
25-func NewProgressBarModel() ProgressBarModel {
26- progressBar := progress.New(progress.WithScaledGradient("#FF7CCB", "#FDFF8C"))
27- progressChan := make(chan float64)
28- return ProgressBarModel{
29- progress: progressBar,
30- progressChan: progressChan,
31- }
32-}
33-
34-func (m ProgressBarModel) Init() tea.Cmd {
35- return tea.Batch(m.progress.Tick, m.tick)
36-}
37-
38-func updateProgressCmd(progress float64) tea.Cmd {
39- return func() tea.Msg {
40- return progressMsg(progress)
41- }
42-}
43-
44-type tickMsg time.Time
45-
46-type ProgressReader struct {
47- reader io.Reader
48- totalSize int64
49- readBytes int64
50- progressFn func(readBytes int64)
51-}
52-
53-// NewProgressReader creates a new ProgressReader.
54-func NewProgressReader(reader io.Reader, totalSize int64, progressFn func(readBytes int64)) *ProgressReader {
55- return &ProgressReader{
56- reader: reader,
57- totalSize: totalSize,
58- progressFn: progressFn,
59- }
60-}
61-
62-// Read implements the io.Reader interface.
63-func (pr *ProgressReader) Read(p []byte) (int, error) {
64- n, err := pr.reader.Read(p)
65- pr.readBytes += int64(n)
66- if pr.progressFn != nil {
67- pr.progressFn(pr.readBytes)
68- // Debug log to check progress
69- log.Printf("ProgressReader: Read %d bytes, total %d bytes", n, pr.readBytes)
70- }
71- if err != nil {
72- // Log error if not nil
73- log.Printf("ProgressReader encountered an error: %v", err)
74- }
75- return n, err
76-}
77-
78-// Close implements the io.Closer interface.
79-func (pr *ProgressReader) Close() error {
80- if closer, ok := pr.reader.(io.Closer); ok {
81- return closer.Close()
82- }
83- // If the underlying reader does not implement io.Closer, return nil
84- return nil
85-}
86-
87-func runProgressBarTUI(progressChan <-chan float64) {
88- m := NewProgressBarModel()
89-
90- // Update model to handle progress updates
91- m.Update = func(msg tea.Msg) (tea.Model, tea.Cmd) {
92- switch msg := msg.(type) {
93- case float64:
94- m.progress.SetValue(msg) // Update the progress bar's value
95- return m, tea.Batch(m.progress.Animate(), time.After(time.Millisecond*100))
96- case tea.KeyMsg:
97- if msg.String() == "q" {
98- return m, tea.Quit
99- }
100- }
101- return m, nil
102- }
103-
104- // Start the TUI
105- p := tea.NewProgram(m)
106- if err := p.Start(); err != nil {
107- log.Fatal(err)
108- }
109-}
A internal/cli/mvu/_progressbar.go +98 -0
@@ -0,0 +1,98 @@
1+package mvu
2+
3+import (
4+ "fmt"
5+ "strings"
6+
7+ "github.com/charmbracelet/bubbles/progress"
8+ tea "github.com/charmbracelet/bubbletea"
9+ "github.com/charmbracelet/lipgloss"
10+)
11+
12+const (
13+ padding = 2
14+ maxWidth = 80
15+)
16+
17+type progressMsg int64
18+
19+type Model struct {
20+ progress progress.Model
21+ actualSize int64
22+}
23+
24+var helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")).Render
25+
26+// Update your model to listen to the progress channel.
27+func NewProgressBarModel(actualSize int64, progressCh <-chan int64) Model {
28+ m := Model{
29+ progress: progress.New(progress.WithDefaultGradient()),
30+ actualSize: actualSize,
31+ }
32+
33+ // Start the program and listen to the channel in a separate goroutine
34+ go func() {
35+ p := tea.NewProgram(m)
36+ for n := range progressCh {
37+ percent := float64(n) / float64(m.actualSize)
38+ p.Send(progressMsg(int64(percent * 100))) // Send update as percent
39+ }
40+ }()
41+
42+ return m
43+}
44+
45+func (m Model) Init() tea.Cmd {
46+ // Start the channel listener
47+ m.progress.SetPercent(100)
48+ return nil
49+}
50+
51+func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
52+ switch msg := msg.(type) {
53+ case tea.KeyMsg:
54+ return m, tea.Quit
55+
56+ case tea.WindowSizeMsg:
57+ m.progress.Width = msg.Width - padding*2 - 4
58+ if m.progress.Width > maxWidth {
59+ m.progress.Width = maxWidth
60+ }
61+ return m, nil
62+
63+ case progressMsg:
64+ percent := float64(msg) / 100.0 // Convert percentage back to a float
65+ m.progress.SetPercent(percent)
66+ fmt.Printf("Progress message received. Percent: %.2f%%\n", percent*100)
67+ if percent >= 1.0 {
68+ return m, tea.Quit
69+ }
70+ return m, nil
71+
72+ // FrameMsg is sent when the progress bar wants to animate itself
73+ case progress.FrameMsg:
74+ progressModel, cmd := m.progress.Update(msg)
75+ m.progress = progressModel.(progress.Model)
76+ return m, cmd
77+
78+ default:
79+ return m, nil
80+ }
81+}
82+
83+func (m Model) View() string {
84+ pad := strings.Repeat(" ", padding)
85+ return "\n" +
86+ pad + m.progress.View() + "\n\n" +
87+ pad + helpStyle("Press any key to quit")
88+}
89+
90+// p.Send(mvu.ProgressMsg(int64(percent * 100))) // Send update as percent
91+
92+func (m Model) Send(msg progressMsg) {
93+ m.progress.Update(msg)
94+}
95+
96+func ProgressMsg(percent int64) progressMsg {
97+ return progressMsg(percent)
98+}
@@ -0,0 +1,98 @@
1+package mvu
2+
3+import (
4+ "fmt"
5+ "strings"
6+
7+ "github.com/charmbracelet/bubbles/progress"
8+ tea "github.com/charmbracelet/bubbletea"
9+ "github.com/charmbracelet/lipgloss"
10+)
11+
12+const (
13+ padding = 2
14+ maxWidth = 80
15+)
16+
17+type progressMsg int64
18+
19+type Model struct {
20+ progress progress.Model
21+ actualSize int64
22+}
23+
24+var helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")).Render
25+
26+// Update your model to listen to the progress channel.
27+func NewProgressBarModel(actualSize int64, progressCh <-chan int64) Model {
28+ m := Model{
29+ progress: progress.New(progress.WithDefaultGradient()),
30+ actualSize: actualSize,
31+ }
32+
33+ // Start the program and listen to the channel in a separate goroutine
34+ go func() {
35+ p := tea.NewProgram(m)
36+ for n := range progressCh {
37+ percent := float64(n) / float64(m.actualSize)
38+ p.Send(progressMsg(int64(percent * 100))) // Send update as percent
39+ }
40+ }()
41+
42+ return m
43+}
44+
45+func (m Model) Init() tea.Cmd {
46+ // Start the channel listener
47+ m.progress.SetPercent(100)
48+ return nil
49+}
50+
51+func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
52+ switch msg := msg.(type) {
53+ case tea.KeyMsg:
54+ return m, tea.Quit
55+
56+ case tea.WindowSizeMsg:
57+ m.progress.Width = msg.Width - padding*2 - 4
58+ if m.progress.Width > maxWidth {
59+ m.progress.Width = maxWidth
60+ }
61+ return m, nil
62+
63+ case progressMsg:
64+ percent := float64(msg) / 100.0 // Convert percentage back to a float
65+ m.progress.SetPercent(percent)
66+ fmt.Printf("Progress message received. Percent: %.2f%%\n", percent*100)
67+ if percent >= 1.0 {
68+ return m, tea.Quit
69+ }
70+ return m, nil
71+
72+ // FrameMsg is sent when the progress bar wants to animate itself
73+ case progress.FrameMsg:
74+ progressModel, cmd := m.progress.Update(msg)
75+ m.progress = progressModel.(progress.Model)
76+ return m, cmd
77+
78+ default:
79+ return m, nil
80+ }
81+}
82+
83+func (m Model) View() string {
84+ pad := strings.Repeat(" ", padding)
85+ return "\n" +
86+ pad + m.progress.View() + "\n\n" +
87+ pad + helpStyle("Press any key to quit")
88+}
89+
90+// p.Send(mvu.ProgressMsg(int64(percent * 100))) // Send update as percent
91+
92+func (m Model) Send(msg progressMsg) {
93+ m.progress.Update(msg)
94+}
95+
96+func ProgressMsg(percent int64) progressMsg {
97+ return progressMsg(percent)
98+}
R internal/cli/model/spinner.go → internal/cli/mvu/spinner.go +1 -1 renamed, 99% similar
@@ -1,4 +1,4 @@
1-package model
1+package mvu
22
33 import (
44 "crypto/tls"
@@ -1,4 +1,4 @@
1-package model1+package mvu
2 2
3 import (3 import (
4 "crypto/tls"4 "crypto/tls"

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close