From 21abb026f237bb007f086a7373db22d0f0283d7c Mon Sep 17 00:00:00 2001 From: Jacob Gillespie Date: Thu, 24 Mar 2022 10:55:27 +0000 Subject: [PATCH] Add `depot init` command --- cmd/depot/main.go | 2 - go.mod | 12 +++ go.sum | 38 ++++++++- pkg/api/api.go | 21 +++++ pkg/cmd/build/build.go | 11 ++- pkg/cmd/init/init.go | 175 +++++++++++++++++++++++++++++++++++++++++ pkg/cmd/root/root.go | 2 + pkg/project/config.go | 82 +++++++++++++++++++ 8 files changed, 339 insertions(+), 4 deletions(-) create mode 100644 pkg/cmd/init/init.go create mode 100644 pkg/project/config.go diff --git a/cmd/depot/main.go b/cmd/depot/main.go index 181bdb81..cb94af4b 100644 --- a/cmd/depot/main.go +++ b/cmd/depot/main.go @@ -81,8 +81,6 @@ func checkForUpdate(currentVersion string) (*update.ReleaseInfo, error) { return nil, err } - fmt.Println(stateFilePath) - return update.CheckForUpdate(client, stateFilePath, currentVersion) } diff --git a/go.mod b/go.mod index 0bb454d5..97931c2a 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,9 @@ go 1.18 require ( github.com/adrg/xdg v0.4.0 github.com/briandowns/spinner v1.18.1 + github.com/charmbracelet/bubbles v0.10.3 + github.com/charmbracelet/bubbletea v0.20.0 + github.com/charmbracelet/lipgloss v0.5.0 github.com/cli/safeexec v1.0.0 github.com/docker/buildx v0.8.0 github.com/docker/cli v20.10.12+incompatible @@ -30,6 +33,7 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.5.1 // indirect github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect + github.com/atotto/clipboard v0.1.4 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect @@ -38,6 +42,7 @@ require ( github.com/containerd/continuity v0.2.2 // indirect github.com/containerd/ttrpc v1.1.0 // indirect github.com/containerd/typeurl v1.0.2 // indirect + github.com/creack/pty v1.1.17 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/docker-credential-helpers v0.6.4 // indirect github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect @@ -63,8 +68,10 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/miekg/pkcs11 v1.0.3 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect @@ -75,6 +82,9 @@ require ( github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5 // indirect github.com/pelletier/go-toml v1.9.4 // indirect @@ -82,6 +92,8 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/sahilm/fuzzy v0.1.0 // indirect github.com/spf13/afero v1.6.0 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/go.sum b/go.sum index 04a06d1a..1efef6df 100644 --- a/go.sum +++ b/go.sum @@ -187,6 +187,8 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.15.90/go.mod h1:es1KtYUFs7le0xQ3rOihkuoVD90z7D0fR2Qm4S00/gU= @@ -250,6 +252,15 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/bubbles v0.10.3 h1:fKarbRaObLn/DCsZO4Y3vKCwRUzynQD9L+gGev1E/ho= +github.com/charmbracelet/bubbles v0.10.3/go.mod h1:jOA+DUF1rjZm7gZHcNyIVW+YrBPALKfpGVdJu8UiJsA= +github.com/charmbracelet/bubbletea v0.19.3/go.mod h1:VuXF2pToRxDUHcBUcPmCRUHRvFATM4Ckb/ql1rBl3KA= +github.com/charmbracelet/bubbletea v0.20.0 h1:/b8LEPgCbNr7WWZ2LuE/BV1/r4t5PyYJtDb+J3vpwxc= +github.com/charmbracelet/bubbletea v0.20.0/go.mod h1:zpkze1Rioo4rJELjRyGlm9T2YNou1Fm4LIJQSa5QMEM= +github.com/charmbracelet/harmonica v0.1.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= +github.com/charmbracelet/lipgloss v0.4.0/go.mod h1:vmdkHvce7UzX6xkyf4cca8WlwdQ5RQr8fzta+xl7BOM= +github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8= +github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= @@ -418,8 +429,9 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= +github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= @@ -901,11 +913,15 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= @@ -935,9 +951,14 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= @@ -1013,6 +1034,15 @@ github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1: github.com/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mrunalp/fileutils v0.0.0-20200520151820-abd8a0e76976/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw= +github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 h1:QANkGiGr39l1EESqrE0gZw0/AJNYzIvoGLhIoVYtluI= +github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -1165,6 +1195,9 @@ github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1: github.com/quasilyte/go-ruleguard v0.1.2-0.20200318202121-b00d7a75d3d8/go.mod h1:CGFX09Ci3pq9QZdj86B+VGIdNj4VyCo2iPOGS9esB/k= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -1182,6 +1215,8 @@ github.com/ryancurrah/gomodguard v1.1.0/go.mod h1:4O8tr7hBODaGE6VIhfJDHcwzh5GUcc github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= +github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= @@ -1732,6 +1767,7 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210422114643-f5beecf764ed/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/pkg/api/api.go b/pkg/api/api.go index 5dc53666..6f6e86ed 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -70,3 +70,24 @@ func (d *Depot) LatestRelease() (*ReleaseResponse, error) { nil, ) } + +type Project struct { + ID string `json:"id"` + Name string `json:"name"` + OrgID string `json:"orgID"` + OrgName string `json:"orgName"` +} + +type GetProjectsResponse struct { + OK bool `json:"ok"` + Projects []*Project `json:"projects"` +} + +func (d *Depot) GetProjects() (*GetProjectsResponse, error) { + return apiRequest[GetProjectsResponse]( + "GET", + fmt.Sprintf("%s/api/internal/cli/projects", d.BaseURL), + d.token, + nil, + ) +} diff --git a/pkg/cmd/build/build.go b/pkg/cmd/build/build.go index ed5ef8ea..68ccd101 100644 --- a/pkg/cmd/build/build.go +++ b/pkg/cmd/build/build.go @@ -3,8 +3,10 @@ package build import ( "fmt" "os" + "path/filepath" "github.com/depot/cli/pkg/config" + "github.com/depot/cli/pkg/project" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" cliflags "github.com/docker/cli/cli/flags" @@ -28,7 +30,14 @@ func NewCmdBuild() *cobra.Command { options.project = os.Getenv("DEPOT_PROJECT_ID") } if options.project == "" { - return errors.Errorf("unknown project ID (use --project or $DEPOT_PROJECT_ID)") + cwd, _ := filepath.Abs(args[0]) + config, _, err := project.ReadConfig(cwd) + if err == nil { + options.project = config.ID + } + } + if options.project == "" { + return errors.Errorf("unknown project ID (run `depot init` or use --project or $DEPOT_PROJECT_ID)") } // TODO: make this a helper diff --git a/pkg/cmd/init/init.go b/pkg/cmd/init/init.go new file mode 100644 index 00000000..217e8f8a --- /dev/null +++ b/pkg/cmd/init/init.go @@ -0,0 +1,175 @@ +package init + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/charmbracelet/bubbles/list" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/depot/cli/pkg/api" + "github.com/depot/cli/pkg/config" + "github.com/depot/cli/pkg/project" + "github.com/docker/cli/cli" + "github.com/spf13/cobra" +) + +func NewCmdInit() *cobra.Command { + var projectID string + + cmd := &cobra.Command{ + Use: "init [flags] []", + Hidden: true, + Args: cli.RequiresMaxArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + force, _ := cmd.Flags().GetBool("force") + context := "." + if len(args) > 0 { + context = args[0] + } + + absContext, err := filepath.Abs(context) + if err != nil { + return err + } + + _, existingFile, _ := project.ReadConfig(absContext) + if existingFile != "" && !force { + return fmt.Errorf("Project configuration %s already exists at path \"%s\", re-run with `--force` to overwrite", filepath.Base(existingFile), context) + } + + client, err := api.NewDepotFromEnv(config.GetApiToken()) + if err != nil { + return err + } + + projects, err := client.GetProjects() + if err != nil { + return err + } + + if len(projects.Projects) == 0 { + fmt.Printf("No projects found. Please create a project first.\n") + return nil + } + + if projectID == "" { + projectID, err = getProjectID(projects) + if err != nil { + return err + } + } + + var selectedProject *api.Project + for _, p := range projects.Projects { + if p.ID == projectID { + selectedProject = p + break + } + } + + if selectedProject == nil { + return fmt.Errorf("Project with ID %s not found", projectID) + } + + configFilepath := existingFile + if configFilepath == "" { + configFilepath = filepath.Join(absContext, "depot.json") + } + err = project.WriteConfig(configFilepath, &project.ProjectConfig{ID: selectedProject.ID}) + if err != nil { + return err + } + + fmt.Printf("Project %s (%s) initialized in directory %s\n", selectedProject.Name, selectedProject.OrgName, context) + + return nil + }, + } + + cmd.Flags().Bool("force", false, "Overwrite any existing project configuration") + cmd.Flags().StringVar(&projectID, "project", "", "The ID of the project to initialize") + + return cmd +} + +func getProjectID(projects *api.GetProjectsResponse) (string, error) { + items := []list.Item{} + for _, p := range projects.Projects { + items = append(items, item{id: p.ID, title: p.Name, desc: p.OrgName}) + } + + m := model{list: list.New(items, list.NewDefaultDelegate(), 0, 0), ctrlC: false} + m.list.Title = "Choose a project" + + p := tea.NewProgram(m, tea.WithAltScreen()) + + final, err := p.StartReturningModel() + if err != nil { + fmt.Println("Error running program:", err) + os.Exit(1) + } + m, ok := final.(model) + if !ok { + return "", fmt.Errorf("final model is not the expected type") + } + + if m.ctrlC { + os.Exit(1) + } + + if m.choice == nil { + return "", fmt.Errorf("No project selected") + } + + return m.choice.id, nil +} + +var docStyle = lipgloss.NewStyle().Margin(1, 2) + +type item struct { + id, title, desc string +} + +func (i item) Title() string { return i.title } +func (i item) Description() string { return i.desc } +func (i item) FilterValue() string { return i.title } + +type model struct { + list list.Model + choice *item + ctrlC bool +} + +func (m model) Init() tea.Cmd { + return nil +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + if msg.String() == "ctrl+c" { + m.ctrlC = true + return m, tea.Quit + } + + if msg.String() == "enter" { + if i, ok := m.list.SelectedItem().(item); ok { + m.choice = &i + } + return m, tea.Quit + } + case tea.WindowSizeMsg: + h, v := docStyle.GetFrameSize() + m.list.SetSize(msg.Width-h, msg.Height-v) + } + + var cmd tea.Cmd + m.list, cmd = m.list.Update(msg) + return m, cmd +} + +func (m model) View() string { + return docStyle.Render(m.list.View()) +} diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go index 4ebb97b5..80f5ac5e 100644 --- a/pkg/cmd/root/root.go +++ b/pkg/cmd/root/root.go @@ -4,6 +4,7 @@ import ( "github.com/spf13/cobra" buildCmd "github.com/depot/cli/pkg/cmd/build" + initCmd "github.com/depot/cli/pkg/cmd/init" loginCmd "github.com/depot/cli/pkg/cmd/login" versionCmd "github.com/depot/cli/pkg/cmd/version" "github.com/depot/cli/pkg/config" @@ -30,6 +31,7 @@ func NewCmdRoot(version, buildDate string) *cobra.Command { // Child commands cmd.AddCommand(buildCmd.NewCmdBuild()) + cmd.AddCommand(initCmd.NewCmdInit()) cmd.AddCommand(loginCmd.NewCmdLogin()) cmd.AddCommand(versionCmd.NewCmdVersion(version, buildDate)) diff --git a/pkg/project/config.go b/pkg/project/config.go new file mode 100644 index 00000000..0f9ca2cf --- /dev/null +++ b/pkg/project/config.go @@ -0,0 +1,82 @@ +package project + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + + "gopkg.in/yaml.v2" +) + +type ProjectConfig struct { + ID string `json:"id" yaml:"id"` +} + +func ReadConfig(cwd string) (*ProjectConfig, string, error) { + filename, err := FindConfigFileUp(cwd) + if err != nil { + return nil, "", err + } + + data, err := os.ReadFile(filename) + if err != nil { + return nil, "", err + } + + var config ProjectConfig + err = yaml.Unmarshal(data, &config) + if err != nil { + return nil, "", err + } + + return &config, filename, nil +} + +func WriteConfig(filename string, config *ProjectConfig) error { + ext := filepath.Ext(filename) + var data []byte + var err error + + switch ext { + case ".json": + data, err = json.Marshal(config) + if err != nil { + return err + } + + case ".yaml", ".yml": + data, err = yaml.Marshal(config) + if err != nil { + return err + } + + default: + return fmt.Errorf("Unsupported config file extension: %s", ext) + } + + return os.WriteFile(filename, data, 0644) +} + +func FindConfigFileUp(current string) (string, error) { + for { + path := filepath.Join(current, "depot.json") + if _, err := os.Stat(path); err == nil { + return path, nil + } + path = filepath.Join(current, "depot.yml") + if _, err := os.Stat(path); err == nil { + return path, nil + } + path = filepath.Join(current, "depot.yaml") + if _, err := os.Stat(path); err == nil { + return path, nil + } + next := filepath.Dir(current) + if next == current { + break + } + current = next + } + return "", fmt.Errorf("No project config found") +}