Refactor deploy command, move spinner logic into mvu package.
Also started working on bubble tea progressbar. But not mandatory for now
- commit
- 4236f6dc8e00d6c70bf1998079e852663db8d258
- author
- bnema · · 2023-11-08 12:03 UTC
- parent
- c9911a0
- signature
- unsigned
10 files · +132 -124
M .air.toml +6 -6
| @@ -4,15 +4,15 @@ tmp_dir = "tmp" | |||
| 4 | 4 | ||
| 5 | 5 | [build] | |
| 6 | 6 | 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" | |
| 9 | 9 | delay = 0 | |
| 10 | 10 | exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules"] | |
| 11 | 11 | exclude_file = [] | |
| 12 | 12 | exclude_regex = ["_test.go"] | |
| 13 | 13 | exclude_unchanged = false | |
| 14 | 14 | follow_symlink = false | |
| 15 | - | full_bin = "./tmp/main" | |
| 15 | + | full_bin = "./dist/tmp/main" | |
| 16 | 16 | include_dir = [] | |
| 17 | 17 | include_ext = ["go", "tpl", "tmpl", "html", "gohtml", "css", "js"] | |
| 18 | 18 | include_file = [] | |
| @@ -21,8 +21,8 @@ tmp_dir = "tmp" | |||
| 21 | 21 | poll = false | |
| 22 | 22 | poll_interval = 0 | |
| 23 | 23 | rerun = false | |
| 24 | - | rerun_delay = 500 | |
| 25 | - | send_interrupt = false | |
| 24 | + | rerun_delay = 1000 | |
| 25 | + | send_interrupt = true | |
| 26 | 26 | stop_on_error = false | |
| 27 | 27 | ||
| 28 | 28 | [color] | |
| @@ -40,5 +40,5 @@ tmp_dir = "tmp" | |||
| 40 | 40 | clean_on_exit = false | |
| 41 | 41 | ||
| 42 | 42 | [screen] | |
| 43 | - | clear_on_rebuild = false | |
| 43 | + | clear_on_rebuild = true | |
| 44 | 44 | 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 = 0 | 9 | 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 = false | 13 | exclude_unchanged = false | ||
| 14 | follow_symlink = false | 14 | 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 = false | 21 | poll = false | ||
| 22 | poll_interval = 0 | 22 | poll_interval = 0 | ||
| 23 | rerun = false | 23 | rerun = false | ||
| 24 | - | rerun_delay = 500 | 24 | + | rerun_delay = 1000 |
| 25 | - | send_interrupt = false | 25 | + | send_interrupt = true |
| 26 | stop_on_error = false | 26 | stop_on_error = false | ||
| 27 | 27 | ||||
| 28 | [color] | 28 | [color] | ||
| @@ -40,5 +40,5 @@ tmp_dir = "tmp" | |||||
| 40 | clean_on_exit = false | 40 | clean_on_exit = false | ||
| 41 | 41 | ||||
| 42 | [screen] | 42 | [screen] | ||
| 43 | - | clear_on_rebuild = false | 43 | + | clear_on_rebuild = true |
| 44 | keep_scroll = true | 44 | keep_scroll = true | ||
M .gitignore +2 -1
| @@ -13,4 +13,5 @@ dist/ | |||
| 13 | 13 | data/ | |
| 14 | 14 | setup.sh | |
| 15 | 15 | .iscontainer | |
| 16 | - | sqlite.db⏎̸ | |
| 16 | + | sqlite.db | |
| 17 | + | .vscode/ | |
| @@ -13,4 +13,5 @@ dist/ | |||||
| 13 | data/ | 13 | data/ | ||
| 14 | setup.sh | 14 | setup.sh | ||
| 15 | .iscontainer | 15 | .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 ( | |||
| 11 | 11 | ||
| 12 | 12 | "github.com/bnema/gordon/internal/cli" | |
| 13 | 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 | 15 | "github.com/bnema/gordon/internal/common" | |
| 16 | 16 | "github.com/bnema/gordon/pkg/docker" | |
| 17 | + | "github.com/cheggaaa/pb" | |
| 17 | 18 | "github.com/fatih/color" | |
| 18 | 19 | "github.com/spf13/cobra" | |
| 19 | 20 | ) | |
| @@ -34,7 +35,7 @@ func NewDeployCommand(a *cli.App) *cobra.Command { | |||
| 34 | 35 | }, | |
| 35 | 36 | Run: func(cmd *cobra.Command, args []string) { | |
| 36 | 37 | imageName := args[0] | |
| 37 | - | fmt.Println("Exporting image:", imageName) | |
| 38 | + | color.Blue("Deploying image: %s", imageName) | |
| 38 | 39 | ||
| 39 | 40 | // Validate the image name | |
| 40 | 41 | if err := handler.ValidateImageName(imageName); err != nil { | |
| @@ -57,13 +58,18 @@ func NewDeployCommand(a *cli.App) *cobra.Command { | |||
| 57 | 58 | return | |
| 58 | 59 | } | |
| 59 | 60 | ||
| 61 | + | // Export the image to a reader and return its true size | |
| 60 | 62 | reader, actualSize, err := exportDockerImage(imageName) | |
| 61 | 63 | if err != nil { | |
| 62 | 64 | fmt.Println("Error exporting image:", err) | |
| 63 | 65 | 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 | 74 | // Create a RequestPayload and populate it | |
| 69 | 75 | reqPayload := common.RequestPayload{ | |
| @@ -72,15 +78,23 @@ func NewDeployCommand(a *cli.App) *cobra.Command { | |||
| 72 | 78 | Ports: port, | |
| 73 | 79 | TargetDomain: targetDomain, | |
| 74 | 80 | ImageName: imageName, | |
| 75 | - | Data: reader, | |
| 81 | + | Data: progressReader, | |
| 76 | 82 | }, | |
| 77 | 83 | } | |
| 84 | + | ||
| 85 | + | // Start the progress bar | |
| 86 | + | bar.Start() | |
| 78 | 87 | // Send the request to the backend | |
| 79 | 88 | resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push") | |
| 80 | 89 | if err != nil { | |
| 81 | 90 | fmt.Println("Error sending HTTP request:", err) | |
| 82 | 91 | 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 | 99 | // Check the response | |
| 86 | 100 | targetDomain := string(resp.Body) | |
| @@ -103,7 +117,7 @@ func NewDeployCommand(a *cli.App) *cobra.Command { | |||
| 103 | 117 | } | |
| 104 | 118 | ||
| 105 | 119 | // Run the TUI program | |
| 106 | - | finalModel, err := model.RunDeploymentTUI(client, imageName, targetDomain, port) | |
| 120 | + | finalModel, err := mvu.RunDeploymentTUI(client, imageName, targetDomain, port) | |
| 107 | 121 | if err != nil { | |
| 108 | 122 | fmt.Println("Error running deployment TUI:", err) | |
| 109 | 123 | return | |
| @@ -144,5 +158,7 @@ func exportDockerImage(imageName string) (io.ReadCloser, int64, error) { | |||
| 144 | 158 | if err != nil { | |
| 145 | 159 | return nil, 0, fmt.Errorf("error exporting image: %w", err) | |
| 146 | 160 | } | |
| 161 | + | ||
| 162 | + | // Return the wrapped reader. | |
| 147 | 163 | return reader, actualSize, nil | |
| 148 | 164 | } | |
| @@ -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 name | 40 | // 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 | return | 58 | 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 | return | 65 | 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 it | 74 | // 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 backend | 87 | // 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 | return | 91 | 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 response | 99 | // 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 program | 119 | // 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 | return | 123 | 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, nil | 163 | return reader, actualSize, nil | ||
| 148 | } | 164 | } | ||
M internal/cli/handler/regexcheck.go +2 -2
| @@ -9,7 +9,7 @@ import ( | |||
| 9 | 9 | func ValidateImageName(imageName string) error { | |
| 10 | 10 | match, _ := regexp.MatchString("^([a-zA-Z0-9_\\-\\.]+\\/)*[a-zA-Z0-9_\\-\\.]+(:[a-zA-Z0-9_\\-\\.]+)?$", imageName) | |
| 11 | 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 | 14 | return nil | |
| 15 | 15 | } | |
| @@ -23,7 +23,7 @@ func EnsureImageTag(imageName *string) { | |||
| 23 | 23 | func ValidatePortMapping(port string) error { | |
| 24 | 24 | match, _ := regexp.MatchString("^[0-9]+:[0-9]+(\\/(tcp|udp))?$", port) | |
| 25 | 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 | 28 | return nil | |
| 29 | 29 | } | |
| @@ -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 nil | 14 | 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 nil | 28 | 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 | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "crypto/tls" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package model | 1 | + | package mvu |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "crypto/tls" | 4 | "crypto/tls" | ||