From 0f1ff57542b90c399b7e5675c16f1bd1a09d970c Mon Sep 17 00:00:00 2001 From: corinnesollows Date: Mon, 19 Sep 2022 11:19:35 -0300 Subject: [PATCH 01/84] Charm Prompt changes (#781) --- go.mod | 20 ++++++++++++++++---- go.sum | 47 +++++++++++++++++++++++++++++++++++++++++------ prompt/prompt.go | 38 ++++++++++++++------------------------ 3 files changed, 71 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index dfe27631e..20b36f83b 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect github.com/google/uuid v1.3.0 github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf // indirect - github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/mapstructure v1.4.1 github.com/olekukonko/tablewriter v0.0.5 github.com/onsi/ginkgo v1.16.4 @@ -31,9 +31,16 @@ require ( gotest.tools/v3 v3.0.3 ) +require github.com/erikgeiser/promptkit v0.7.0 + require ( github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect + github.com/atotto/clipboard v0.1.4 // indirect + github.com/charmbracelet/bubbles v0.11.0 // indirect + github.com/charmbracelet/bubbletea v0.21.0 // indirect + github.com/charmbracelet/lipgloss v0.5.0 // indirect + github.com/containerd/console v1.0.3 // indirect github.com/emirpasic/gods v1.12.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/ghodss/yaml v1.0.0 // indirect @@ -48,14 +55,19 @@ require ( github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.4 // indirect - github.com/mattn/go-runewidth v0.0.10 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect + github.com/muesli/cancelreader v0.2.0 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.12.0 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/open-policy-agent/opa v0.43.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect - github.com/rivo/uniseg v0.1.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/vektah/gqlparser/v2 v2.4.7 // indirect github.com/xanzy/ssh-agent v0.2.1 // indirect @@ -65,7 +77,7 @@ require ( golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced // indirect golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect golang.org/x/text v0.3.7 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index 49bc8474c..8a5287c5c 100644 --- a/go.sum +++ b/go.sum @@ -132,6 +132,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 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.43.16/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= @@ -170,6 +172,13 @@ 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.11.0 h1:fBLyY0PvJnd56Vlu5L84JJH6f4axhgIJ9P3NET78f0Q= +github.com/charmbracelet/bubbles v0.11.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= +github.com/charmbracelet/bubbletea v0.21.0 h1:f3y+kanzgev5PA916qxmDybSHU3N804uOnKnhRPXTcI= +github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= +github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= +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= @@ -216,6 +225,7 @@ github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -383,6 +393,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/erikgeiser/promptkit v0.7.0 h1:Yi28iN6JRs8/0x+wjQRPfWb+vWz1pFmZ5fu2uoFipD8= +github.com/erikgeiser/promptkit v0.7.0/go.mod h1:Jj9bhN+N8RbMjB1jthkr9A4ydmczZ1WZJ8xTXnP12dg= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -681,7 +693,10 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 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 v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= +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/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -698,12 +713,14 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 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 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= 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.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= @@ -749,6 +766,18 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA= +github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/cancelreader v0.2.0 h1:SOpr+CfyVNce341kKqvbhhzQhBPyJRXQaCtn03Pae1Q= +github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +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.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc= +github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A= 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= @@ -887,8 +916,9 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rhysd/go-github-selfupdate v0.0.0-20180520142321-41c1bbb0804a h1:YNh/SV+Z0p7kQDUE9Ux+46ruTucvQP43XB06DfZa8Es= github.com/rhysd/go-github-selfupdate v0.0.0-20180520142321-41c1bbb0804a/go.mod h1:mOFQaTkPA4plTgFW6Gnejb/RsEIqAoIqOACC2XaZX04= -github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= 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.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -898,6 +928,7 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD 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/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= @@ -1274,7 +1305,6 @@ golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1327,6 +1357,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1336,7 +1367,10 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs= golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1344,8 +1378,9 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR 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-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= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/prompt/prompt.go b/prompt/prompt.go index 90934b019..cb2bd52dd 100644 --- a/prompt/prompt.go +++ b/prompt/prompt.go @@ -1,48 +1,38 @@ package prompt -import "github.com/AlecAivazis/survey/v2" +import ( + "github.com/erikgeiser/promptkit/confirmation" + "github.com/erikgeiser/promptkit/textinput" +) // ReadSecretStringFromUser can be used to read a value from the user by masking their input. // It's useful for token input in our case. func ReadSecretStringFromUser(message string) (string, error) { secret := "" - prompt := &survey.Password{ - Message: message, - } - err := survey.AskOne(prompt, &secret) + input := textinput.New(message) + input.Hidden = true + secret, err := input.RunPrompt() if err != nil { return "", err } - return secret, nil } // ReadStringFromUser can be used to read any value from the user or the defaultValue when provided. func ReadStringFromUser(message string, defaultValue string) string { - token := "" - prompt := &survey.Input{ - Message: message, - } - - if defaultValue != "" { - prompt.Default = defaultValue - } - - err := survey.AskOne(prompt, &token) + input := textinput.New(message) + input.Placeholder = defaultValue + input.InitialValue = defaultValue + result, err := input.RunPrompt() if err != nil { panic(err) } - - return token + return result } // AskUserToConfirm will prompt the user to confirm with the provided message. func AskUserToConfirm(message string) bool { - result := true - prompt := &survey.Confirm{ - Message: message, - } - - err := survey.AskOne(prompt, &result) + input := confirmation.New(message, confirmation.No) + result, err := input.RunPrompt() return err == nil && result } From 3b49940db83d496241211861c4d3e209d48f1afb Mon Sep 17 00:00:00 2001 From: corinnesollows Date: Wed, 21 Sep 2022 15:21:33 -0300 Subject: [PATCH 02/84] Hack week help menu (#782) * Overwrote default cobra command help menu * Added testing env var --- .circleci/config.yml | 5 +- Makefile | 4 +- cmd/context.go | 9 +- cmd/policy/policy.go | 5 +- cmd/root.go | 133 ++++++++++++++++-- go.mod | 6 +- .../features/root_commands.feature | 6 +- 7 files changed, 143 insertions(+), 25 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ab76af1fb..1d7c27638 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -89,6 +89,8 @@ jobs: - run: setx GOPATH %USERPROFILE%\go - run: go get gotest.tools/gotestsum - run: mkdir test_results + - run: setx TESTING "true" + - run: name: Run tests command: | @@ -133,7 +135,8 @@ jobs: - run: command: bundle exec cucumber working_directory: integration_tests - + environment: + TESTING: "true" test: executor: go steps: diff --git a/Makefile b/Makefile index cd4d130de..604d50148 100644 --- a/Makefile +++ b/Makefile @@ -18,11 +18,11 @@ clean: .PHONY: test test: - go test -v ./... + TESTING=true go test -v ./... .PHONY: cover cover: - go test -race -coverprofile=coverage.txt ./... + TESTING=true go test -race -coverprofile=coverage.txt ./... .PHONY: lint lint: diff --git a/cmd/context.go b/cmd/context.go index 67f8b1aae..8369d0439 100644 --- a/cmd/context.go +++ b/cmd/context.go @@ -47,9 +47,12 @@ func newContextCommand(config *settings.Config) *cobra.Command { } command := &cobra.Command{ - Use: "context", - Short: "Contexts provide a mechanism for securing and sharing environment variables across projects. The environment variables are defined as name/value pairs and are injected at runtime.", - } + Use: "context", + Long: ` +Contexts provide a mechanism for securing and sharing environment variables across +projects. The environment variables are defined as name/value pairs and +are injected at runtime.`, + Short: "For securing and sharing environment variables across projects"} listCommand := &cobra.Command{ Short: "List all contexts", diff --git a/cmd/policy/policy.go b/cmd/policy/policy.go index a4972a121..31b3ca47c 100644 --- a/cmd/policy/policy.go +++ b/cmd/policy/policy.go @@ -28,8 +28,9 @@ func NewCommand(config *settings.Config, preRunE validator.Validator) *cobra.Com cmd := &cobra.Command{ Use: "policy", PersistentPreRunE: preRunE, - Short: "Policies ensures security of build configs via security policy management framework. " + - "This group of commands allows the management of polices to be verified against build configs.", + Short: "Manage security policies", + Long: `Policies ensures security of build configs via security policy management framework. +This group of commands allows the management of polices to be verified against build configs.`, } policyBaseURL := cmd.PersistentFlags().String("policy-base-url", "https://internal.circleci.com", "base url for policy api") diff --git a/cmd/root.go b/cmd/root.go index 5261e5829..174f764eb 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,11 +2,10 @@ package cmd import ( "fmt" + "log" "os" "strings" - "github.com/spf13/cobra" - "github.com/CircleCI-Public/circleci-cli/api/header" "github.com/CircleCI-Public/circleci-cli/cmd/info" "github.com/CircleCI-Public/circleci-cli/cmd/policy" @@ -15,11 +14,14 @@ import ( "github.com/CircleCI-Public/circleci-cli/md_docs" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/CircleCI-Public/circleci-cli/version" + "github.com/charmbracelet/lipgloss" + "github.com/spf13/cobra" ) var defaultEndpoint = "graphql-unstable" var defaultHost = "https://circleci.com" var defaultRestEndpoint = "api/v2" +var trueString = "true" // rootCmd is used internally and global to the package but not exported // therefore we can use it in other commands, like `usage` @@ -88,6 +90,7 @@ Global Flags: Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} + ` // MakeCommands creates the top level commands @@ -108,8 +111,9 @@ func MakeCommands() *cobra.Command { rootOptions.Data = &data.Data rootCmd = &cobra.Command{ - Use: "circleci", - Long: rootHelpLong(rootOptions), + Use: "circleci", + Long: rootHelpLong(), + Short: rootHelpShort(rootOptions), PersistentPreRunE: func(_ *cobra.Command, _ []string) error { return rootCmdPreRun(rootOptions) }, @@ -119,6 +123,11 @@ func MakeCommands() *cobra.Command { cobra.AddTemplateFunc("HasAnnotations", hasAnnotations) cobra.AddTemplateFunc("PositionalArgs", md_docs.PositionalArgs) cobra.AddTemplateFunc("FormatPositionalArg", md_docs.FormatPositionalArg) + + if os.Getenv("TESTING") != trueString { + helpCmd := helpCmd{cmd: rootCmd} + rootCmd.SetHelpFunc(helpCmd.helpTemplate) + } rootCmd.SetUsageTemplate(usageTemplate) rootCmd.DisableAutoGenTag = true @@ -279,21 +288,125 @@ func isUpdateIncluded(packageManager string) bool { } } -func rootHelpLong(config *settings.Config) string { - long := `Use CircleCI from the command line. +func skipUpdateByDefault() bool { + return os.Getenv("CI") == trueString || os.Getenv("CIRCLECI_CLI_SKIP_UPDATE_CHECK") == trueString +} +/**************** Help Menu Functions ****************/ + +//rootHelpLong creates content for the long field in the command +func rootHelpLong() string { + long := ` + /?? /?? /?? + |__/ | ?? |__/ + /???????? /??????? /?? /?????? /???????| ?? /?????? /??????? /?? + /_______/?? /??_____/| ?? /??__ ?? /??_____/| ?? /??__ ?? /??_____/| ?? + /?? | ?? | ?? | ??| ?? \__/| ?? | ??| ???????? | ?? | ?? + |__/ | ?? | ?? | ??| ?? | ?? | ??| ??_____/ | ?? | ?? + /???????? | ???????| ??| ?? | ???????| ??| ??????? | ???????| ?? + /________/ \_______/|__/|__/ \_______/|__/ \_______/ \_______/|__/` + return long +} + +// rootHelpShort creates content for the short feild in the command +func rootHelpShort(config *settings.Config) string { + short := `Use CircleCI from the command line. This project is the seed for CircleCI's new command-line application.` // We should only print this for cloud users if config.Host != defaultHost { - return long + return short } return fmt.Sprintf(`%s +For more help, see the documentation here: %s`, short, config.Data.Links.CLIDocs) +} -For more help, see the documentation here: %s`, long, config.Data.Links.CLIDocs) +type helpCmd struct { + cmd *cobra.Command } -func skipUpdateByDefault() bool { - return os.Getenv("CI") == "true" || os.Getenv("CIRCLECI_CLI_SKIP_UPDATE_CHECK") == "true" +// helpTemplate Building a custom help template with more finess and pizazz +func (helpCmd *helpCmd) helpTemplate(cmd *cobra.Command, s []string) { + + /***Styles ***/ + titleStyle := lipgloss.NewStyle().Bold(true). + Foreground(lipgloss.AdaptiveColor{Light: `#003740`, Dark: `#3B6385`}). + BorderBottom(true). + Margin(1, 0, 1, 0). + Padding(0, 1, 0, 1).Align(lipgloss.Center) + subCmdStyle := lipgloss.NewStyle(). + Foreground(lipgloss.AdaptiveColor{Light: `#161616`, Dark: `#FFFFFF`}). + Padding(0, 4, 0, 4).Align(lipgloss.Left) + subCmdInfoStyle := lipgloss.NewStyle(). + Foreground(lipgloss.AdaptiveColor{Light: `#161616`, Dark: `#FFFFFF`}).Bold(true) + textStyle := lipgloss.NewStyle(). + Foreground(lipgloss.AdaptiveColor{Light: `#161616`, Dark: `#FFFFFF`}).Align(lipgloss.Left).Margin(0).Padding(0) + + /** Building Usage String **/ + usageText := strings.Builder{} + + //get command path + usageText.WriteString(titleStyle.Render(cmd.CommandPath())) + + //get command short or long + cmdDesc := titleStyle.Render(cmd.Long) + if strings.TrimSpace(cmdDesc) == "" || cmd.Name() == "circleci" { + if cmd.Name() == "circleci" { + cmdDesc += "\n\n" //add some spaces for circleci command + } + cmdDesc += subCmdStyle.Render(cmd.Short) + } + usageText.WriteString(cmdDesc + "\n") + + if len(cmd.Aliases) > 0 { + aliases := titleStyle.Render("Aliases:") + aliases += textStyle.Render(cmd.NameAndAliases()) + usageText.WriteString(aliases + "\n") + } + + if cmd.Runnable() { + usage := titleStyle.Render("Usage:") + usage += textStyle.Render(cmd.UseLine()) + usageText.WriteString(usage + "\n") + } + + if cmd.HasExample() { + examples := titleStyle.Render("Example:") + examples += textStyle.Render(cmd.Example) + usageText.WriteString(examples + "\n") + } + + if cmd.HasAvailableSubCommands() { + subCmds := cmd.Commands() + subTitle := titleStyle.Render("Available Commands:") + subs := "" + for i := range subCmds { + if subCmds[i].IsAvailableCommand() { + subs += subCmdStyle.Render(subCmds[i].Name()) + subCmdInfoStyle. + PaddingLeft(subCmds[i].NamePadding()-len(subCmds[i].Name())+1).Render(subCmds[i].Short) + "\n" + } + } + usageText.WriteString(lipgloss.JoinVertical(lipgloss.Left, subTitle, subs)) + } + + if cmd.HasAvailableLocalFlags() { + flags := titleStyle.Render("Local Flags:") + flags += textStyle.Render("\n" + cmd.LocalFlags().FlagUsages()) + usageText.WriteString(flags) + } + if cmd.HasAvailableInheritedFlags() { + flags := titleStyle.Render("Global Flags:") + flags += textStyle.Render("\n" + cmd.InheritedFlags().FlagUsages()) + usageText.WriteString(flags) + } + + //Border styles + borderStyle := lipgloss.NewStyle(). + Padding(0, 1, 0, 1). + Width(120). + BorderForeground(lipgloss.AdaptiveColor{Light: `#3B6385`, Dark: `#47A359`}). + Border(lipgloss.ThickBorder()) + + log.Println("\n" + borderStyle.Render(usageText.String()+"\n")) } diff --git a/go.mod b/go.mod index 20b36f83b..d515fd50c 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,10 @@ require ( gotest.tools/v3 v3.0.3 ) -require github.com/erikgeiser/promptkit v0.7.0 +require ( + github.com/charmbracelet/lipgloss v0.5.0 + github.com/erikgeiser/promptkit v0.7.0 +) require ( github.com/OneOfOne/xxhash v1.2.8 // indirect @@ -39,7 +42,6 @@ require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/charmbracelet/bubbles v0.11.0 // indirect github.com/charmbracelet/bubbletea v0.21.0 // indirect - github.com/charmbracelet/lipgloss v0.5.0 // indirect github.com/containerd/console v1.0.3 // indirect github.com/emirpasic/gods v1.12.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect diff --git a/integration_tests/features/root_commands.feature b/integration_tests/features/root_commands.feature index 13da2602f..3b3fa5d61 100644 --- a/integration_tests/features/root_commands.feature +++ b/integration_tests/features/root_commands.feature @@ -7,11 +7,7 @@ Feature: Root Commands When I run `circleci help` Then the output should contain: """ - Use CircleCI from the command line. - - This project is the seed for CircleCI's new command-line application. - - For more help, see the documentation here: https://circleci.com/docs/2.0/local-cli/ + circleci """ And the exit status should be 0 From eec8fd6dbad9b117245f3bd593ce9bbc9a212b8b Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Fri, 23 Sep 2022 14:34:26 -0400 Subject: [PATCH 03/84] chore: Dynamically print flag options in error message (#785) * chore: Dynamically print flag options Signed-off-by: Adam Harvey * fix: Forcibly sort keys to ensure consistency Signed-off-by: Adam Harvey * fix: Alphabetical sorting of flag values Signed-off-by: Adam Harvey Signed-off-by: Adam Harvey --- cmd/orb.go | 16 ++++++++++++---- cmd/orb_test.go | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/cmd/orb.go b/cmd/orb.go index 16ac6f160..2650a720e 100644 --- a/cmd/orb.go +++ b/cmd/orb.go @@ -590,12 +590,20 @@ var validSortFlag = map[string]bool{ "projects": true, "orgs": true} -func validateSortFlag(sort string) error { - if _, valid := validSortFlag[sort]; valid { +func validateSortFlag(sortFlag string) error { + if _, valid := validSortFlag[sortFlag]; valid { return nil } - // TODO(zzak): we could probably reuse the map above to print the valid values - return fmt.Errorf("expected `%s` to be one of \"builds\", \"projects\", or \"orgs\"", sort) + + keys := make([]string, 0, len(validSortFlag)) + for key := range validSortFlag { + keys = append(keys, key) + } + sort.Strings(keys) + + validFlags := fmt.Sprint(strings.Join(keys, ", ")) + + return fmt.Errorf("expected `%s` to be one of: %s", sortFlag, validFlags) } func listOrbs(opts orbOptions) error { diff --git a/cmd/orb_test.go b/cmd/orb_test.go index 8d0b89cb6..f10100bb7 100644 --- a/cmd/orb_test.go +++ b/cmd/orb_test.go @@ -1979,7 +1979,7 @@ Search, filter, and view sources for all Orbs online at https://circleci.com/dev Eventually(session).Should(clitest.ShouldFail()) stderr := session.Wait().Err.Contents() - Expect(string(stderr)).To(Equal("Error: expected `idontknow` to be one of \"builds\", \"projects\", or \"orgs\"\n")) + Expect(string(stderr)).To(Equal("Error: expected `idontknow` to be one of: builds, orgs, projects\n")) }) }) From 7587e2d6fd65a9063e717d24d129e7be1d81c2a7 Mon Sep 17 00:00:00 2001 From: Shogo Tsutsumi Date: Sat, 24 Sep 2022 03:42:59 +0900 Subject: [PATCH 04/84] Fix: replace the HTTP request header Accept-Type with Accept (#784) Co-authored-by: threepipes Co-authored-by: corinnesollows --just pulled in master --- api/rest/client.go | 2 +- api/rest/client_test.go | 6 +++--- api/runner/runner_test.go | 18 +++++++++--------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/api/rest/client.go b/api/rest/client.go index 1167a9481..d745b887e 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -58,7 +58,7 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req } req.Header.Set("Circle-Token", c.circleToken) - req.Header.Set("Accept-Type", "application/json") + req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", version.UserAgent()) commandStr := header.GetCommandStr() if commandStr != "" { diff --git a/api/rest/client_test.go b/api/rest/client_test.go index b4761cac2..a45d4bc13 100644 --- a/api/rest/client_test.go +++ b/api/rest/client_test.go @@ -46,7 +46,7 @@ func TestClient_DoRequest(t *testing.T) { assert.Check(t, cmp.Equal(fix.Method(), "PUT")) assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, - "Accept-Type": {"application/json"}, + "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "Content-Length": {"20"}, "Content-Type": {"application/json"}, @@ -77,7 +77,7 @@ func TestClient_DoRequest(t *testing.T) { assert.Check(t, cmp.Equal(fix.Method(), http.MethodGet)) assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, - "Accept-Type": {"application/json"}, + "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "User-Agent": {version.UserAgent()}, })) @@ -109,7 +109,7 @@ func TestClient_DoRequest(t *testing.T) { assert.Check(t, cmp.Equal(fix.Method(), http.MethodGet)) assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, - "Accept-Type": {"application/json"}, + "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "User-Agent": {version.UserAgent()}, })) diff --git a/api/runner/runner_test.go b/api/runner/runner_test.go index 77d9dcfe6..32bc27d64 100644 --- a/api/runner/runner_test.go +++ b/api/runner/runner_test.go @@ -46,7 +46,7 @@ func TestRunner_CreateResourceClass(t *testing.T) { assert.Check(t, cmp.Equal(fix.method, "POST")) assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, - "Accept-Type": {"application/json"}, + "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "Content-Length": {"86"}, "Content-Type": {"application/json"}, @@ -86,7 +86,7 @@ func TestRunner_GetResourceClassByName(t *testing.T) { assert.Check(t, cmp.Equal(fix.method, http.MethodGet)) assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, - "Accept-Type": {"application/json"}, + "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "User-Agent": {version.UserAgent()}, })) @@ -137,7 +137,7 @@ func TestRunner_GetResourceClassesByNamespace(t *testing.T) { assert.Check(t, cmp.Equal(fix.method, http.MethodGet)) assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, - "Accept-Type": {"application/json"}, + "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "User-Agent": {version.UserAgent()}, })) @@ -160,7 +160,7 @@ func TestRunner_DeleteResourceClass(t *testing.T) { assert.Check(t, cmp.Equal(fix.method, "DELETE")) assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, - "Accept-Type": {"application/json"}, + "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "User-Agent": {version.UserAgent()}, })) @@ -180,7 +180,7 @@ func TestRunner_DeleteResourceClass_Force(t *testing.T) { assert.Check(t, cmp.Equal(fix.method, "DELETE")) assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, - "Accept-Type": {"application/json"}, + "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "User-Agent": {version.UserAgent()}, })) @@ -232,7 +232,7 @@ func TestRunner_CreateToken(t *testing.T) { assert.Check(t, cmp.Equal(fix.method, "POST")) assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, - "Accept-Type": {"application/json"}, + "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "Content-Length": {"80"}, "Content-Type": {"application/json"}, @@ -302,7 +302,7 @@ func TestRunner_GetRunnerTokensByResourceClass(t *testing.T) { assert.Check(t, cmp.Equal(fix.method, http.MethodGet)) assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, - "Accept-Type": {"application/json"}, + "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "User-Agent": {version.UserAgent()}, })) @@ -325,7 +325,7 @@ func TestRunner_DeleteToken(t *testing.T) { assert.Check(t, cmp.Equal(fix.method, "DELETE")) assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, - "Accept-Type": {"application/json"}, + "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "User-Agent": {version.UserAgent()}, })) @@ -413,7 +413,7 @@ func TestRunner_GetRunnerInstances_ByNamespace(t *testing.T) { assert.Check(t, cmp.Equal(fix.method, http.MethodGet)) assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, - "Accept-Type": {"application/json"}, + "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "User-Agent": {version.UserAgent()}, })) From 02556566da28b0c68582368667affdb68331a48b Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Wed, 28 Sep 2022 16:29:23 +0100 Subject: [PATCH 05/84] Removes the dependency on API-Service for config compilation and validation --- .circleci/config.yml | 6 +- Dockerfile | 2 +- api/api.go | 120 ----- api/rest/client.go | 37 +- cmd/config.go | 27 +- cmd/config_test.go | 467 +++--------------- cmd/root.go | 14 +- config/config.go | 94 ++++ .../deprecated_images.go | 7 +- go.mod | 2 +- local/local.go | 13 +- settings/settings.go | 1 + 12 files changed, 245 insertions(+), 545 deletions(-) create mode 100644 config/config.go rename cmd/deprecated-images.go => config/deprecated_images.go (93%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1d7c27638..3f8297e46 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ orbs: executors: go: docker: - - image: cimg/go:1.18 + - image: cimg/go:1.19 environment: CGO_ENABLED: 0 mac: @@ -105,8 +105,8 @@ jobs: - checkout - run: | brew update - brew install go@1.18 - echo 'export PATH="/usr/local/opt/go@1.18/bin:$PATH"' >> ~/.bash_profile + brew install go@1.19 + echo 'export PATH="/usr/local/opt/go@1.19/bin:$PATH"' >> ~/.bash_profile - gomod - run: make test build: diff --git a/Dockerfile b/Dockerfile index c94a4659f..0d6d09f17 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM cimg/go:1.18.3 +FROM cimg/go:1.19.1 ENV CIRCLECI_CLI_SKIP_UPDATE_CHECK true diff --git a/api/api.go b/api/api.go index 674ae112c..eabbc5ef0 100644 --- a/api/api.go +++ b/api/api.go @@ -11,7 +11,6 @@ import ( "strings" "github.com/CircleCI-Public/circleci-cli/api/graphql" - "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/references" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/Masterminds/semver" @@ -513,125 +512,6 @@ func WhoamiQuery(cl *graphql.Client) (*WhoamiResponse, error) { return &response, nil } -// ConfigQueryLegacy calls the GQL API to validate and process config with the legacy orgSlug -func ConfigQueryLegacy(cl *graphql.Client, configPath string, orgSlug string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { - var response BuildConfigResponse - var query string - config, err := loadYaml(configPath) - if err != nil { - return nil, err - } - // GraphQL isn't forwards-compatible, so we are unusually selective here about - // passing only non-empty fields on to the API, to minimize user impact if the - // backend is out of date. - var fieldAddendums string - if orgSlug != "" { - fieldAddendums += ", orgSlug: $orgSlug" - } - if len(params) > 0 { - fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" - } - query = fmt.Sprintf( - `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }`, - fieldAddendums) - - request := graphql.NewRequest(query) - request.Var("config", config) - - if values != nil { - request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) - } - if params != nil { - pipelineParameters, err := json.Marshal(params) - if err != nil { - return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) - } - request.Var("pipelineParametersJson", string(pipelineParameters)) - } - - if orgSlug != "" { - request.Var("orgSlug", orgSlug) - } - - request.SetToken(cl.Token) - - err = cl.Run(request, &response) - if err != nil { - return nil, errors.Wrap(err, "Unable to validate config") - } - if len(response.BuildConfig.ConfigResponse.Errors) > 0 { - return nil, &response.BuildConfig.ConfigResponse.Errors - } - - return &response.BuildConfig.ConfigResponse, nil -} - -// ConfigQuery calls the GQL API to validate and process config with the org id -func ConfigQuery(cl *graphql.Client, configPath string, orgId string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { - var response BuildConfigResponse - var query string - config, err := loadYaml(configPath) - if err != nil { - return nil, err - } - // GraphQL isn't forwards-compatible, so we are unusually selective here about - // passing only non-empty fields on to the API, to minimize user impact if the - // backend is out of date. - var fieldAddendums string - if orgId != "" { - fieldAddendums += ", orgId: $orgId" - } - if len(params) > 0 { - fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" - } - query = fmt.Sprintf( - `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }`, - fieldAddendums) - - request := graphql.NewRequest(query) - request.Var("config", config) - - if values != nil { - request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) - } - if params != nil { - pipelineParameters, err := json.Marshal(params) - if err != nil { - return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) - } - request.Var("pipelineParametersJson", string(pipelineParameters)) - } - - if orgId != "" { - request.Var("orgId", orgId) - } - request.SetToken(cl.Token) - - err = cl.Run(request, &response) - if err != nil { - return nil, errors.Wrap(err, "Unable to validate config") - } - if len(response.BuildConfig.ConfigResponse.Errors) > 0 { - return nil, &response.BuildConfig.ConfigResponse.Errors - } - - return &response.BuildConfig.ConfigResponse, nil -} - // OrbQuery validated and processes an orb. func OrbQuery(cl *graphql.Client, configPath string) (*ConfigResponse, error) { var response OrbConfigResponse diff --git a/api/rest/client.go b/api/rest/client.go index d745b887e..e8a4a4a68 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -18,6 +18,7 @@ import ( type Client struct { baseURL *url.URL + apiURL *url.URL circleToken string client *http.Client } @@ -29,13 +30,14 @@ func New(host string, config *settings.Config) *Client { endpoint += "/" } - u, _ := url.Parse(host) - + baseURL, _ := url.Parse(host) + apiURL, _ := url.Parse(config.ConfigAPIHost) client := config.HTTPClient client.Timeout = 10 * time.Second return &Client{ - baseURL: u.ResolveReference(&url.URL{Path: endpoint}), + apiURL: apiURL.ResolveReference(&url.URL{Path: endpoint}), + baseURL: baseURL.ResolveReference(&url.URL{Path: endpoint}), circleToken: config.Token, client: client, } @@ -70,6 +72,35 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req return req, nil } +func (c *Client) NewAPIRequest(method string, u *url.URL, payload interface{}) (req *http.Request, err error) { + var r io.Reader + if payload != nil { + buf := &bytes.Buffer{} + r = buf + err = json.NewEncoder(buf).Encode(payload) + if err != nil { + return nil, err + } + } + + req, err = http.NewRequest(method, c.apiURL.ResolveReference(u).String(), r) + if err != nil { + return nil, err + } + + req.Header.Set("Circle-Token", c.circleToken) + req.Header.Set("Accept", "application/json") + req.Header.Set("User-Agent", version.UserAgent()) + commandStr := header.GetCommandStr() + if commandStr != "" { + req.Header.Set("Circleci-Cli-Command", commandStr) + } + if payload != nil { + req.Header.Set("Content-Type", "application/json") + } + return req, nil +} + func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int, err error) { httpResp, err := c.client.Do(req) if err != nil { diff --git a/cmd/config.go b/cmd/config.go index 40344a079..c3f8a4831 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -5,8 +5,8 @@ import ( "io/ioutil" "strings" - "github.com/CircleCI-Public/circleci-cli/api" - "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/config" "github.com/CircleCI-Public/circleci-cli/filetree" "github.com/CircleCI-Public/circleci-cli/local" "github.com/CircleCI-Public/circleci-cli/pipeline" @@ -20,7 +20,7 @@ import ( type configOptions struct { cfg *settings.Config - cl *graphql.Client + rest *rest.Client args []string } @@ -63,7 +63,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Check that the config file is well formed.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) + opts.rest = rest.New(config.Host, config) }, RunE: func(cmd *cobra.Command, _ []string) error { return validateConfig(opts, cmd.Flags()) @@ -85,7 +85,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Validate config and display expanded configuration.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) + opts.rest = rest.New(config.Host, config) }, RunE: func(cmd *cobra.Command, _ []string) error { return processConfig(opts, cmd.Flags()) @@ -125,7 +125,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { // The arg is actually optional, in order to support compatibility with the --path flag. func validateConfig(opts configOptions, flags *pflag.FlagSet) error { var err error - var response *api.ConfigResponse + var response *config.ConfigResponse path := local.DefaultConfigPath // First, set the path to configPath set by --path flag for compatibility if configPath != "" { @@ -140,13 +140,16 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = api.ConfigQuery(opts.cl, path, orgID, nil, pipeline.LocalPipelineValues()) + + response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } + } else { orgSlug, _ := flags.GetString("org-slug") - response, err = api.ConfigQueryLegacy(opts.cl, path, orgSlug, nil, pipeline.LocalPipelineValues()) + // response, err = api.ConfigQueryLegacy(opts.cl, path, orgSlug, nil, pipeline.LocalPipelineValues()) + response, err = config.ConfigQuery(opts.rest, path, orgSlug, nil, pipeline.LocalPipelineValues()) if err != nil { return err } @@ -156,7 +159,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { // link here to blog post when available // returns an error if a deprecated image is used if !ignoreDeprecatedImages { - err := deprecatedImageCheck(response) + err := config.DeprecatedImageCheck(response) if err != nil { return err } @@ -173,7 +176,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { func processConfig(opts configOptions, flags *pflag.FlagSet) error { paramsYaml, _ := flags.GetString("pipeline-parameters") - var response *api.ConfigResponse + var response *config.ConfigResponse var params pipeline.Parameters var err error @@ -194,13 +197,13 @@ func processConfig(opts configOptions, flags *pflag.FlagSet) error { //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = api.ConfigQuery(opts.cl, opts.args[0], orgID, params, pipeline.LocalPipelineValues()) + response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - response, err = api.ConfigQueryLegacy(opts.cl, opts.args[0], orgSlug, params, pipeline.LocalPipelineValues()) + response, err = config.ConfigQuery(opts.rest, opts.args[0], orgSlug, params, pipeline.LocalPipelineValues()) if err != nil { return err } diff --git a/cmd/config_test.go b/cmd/config_test.go index e03177f8d..6c622f564 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -1,20 +1,13 @@ package cmd_test import ( - "encoding/json" "fmt" - "io" - "net/http" "os/exec" "path/filepath" - "time" - "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/clitest" - "github.com/CircleCI-Public/circleci-cli/pipeline" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/onsi/gomega/gbytes" "github.com/onsi/gomega/gexec" "gotest.tools/v3/golden" ) @@ -25,7 +18,7 @@ var _ = Describe("Config", func() { command *exec.Cmd results []byte tempSettings *clitest.TempSettings - token string = "testtoken" + // token string = "testtoken" ) BeforeEach(func() { @@ -36,395 +29,95 @@ var _ = Describe("Config", func() { tempSettings.Close() }) - Describe("a .circleci folder with config.yml and local orbs folder containing the hugo orb", func() { - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/hugo-pack/.circleci") - results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) - }) - - It("pack all YAML contents as expected", func() { - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) - }) - - Describe("local orbs folder with mixed inline and local commands, jobs, etc", func() { - BeforeEach(func() { - var path string = "nested-orbs-and-local-commands-etc" - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - filepath.Join("testdata", path, "test")) - results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) - }) - - It("pack all YAML contents as expected", func() { - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) + It("packs all YAML contents as expected", func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/hugo-pack/.circleci") + results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) }) - Describe("an orb containing local executors and commands in folder", func() { - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/myorb/test") - - results = golden.Get(GinkgoT(), filepath.FromSlash("myorb/result.yml")) - }) - - It("pack all YAML contents as expected", func() { - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) + It("given a .circleci folder with config.ymla nd local orb, packs all YAML contents as expected", func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/hugo-pack/.circleci") + results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) }) - Describe("with a large nested config including rails orb", func() { - BeforeEach(func() { - var path string = "test-with-large-nested-rails-orb" - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - filepath.Join("testdata", path, "test")) - results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) - }) - - It("pack all YAML contents as expected", func() { - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) - }) - - Context("config is a list and not a map", func() { - var config *clitest.TmpFile - - BeforeEach(func() { - config = clitest.OpenTmpFile(filepath.Join(tempSettings.Home, "myorb"), "config.yaml") - - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - config.RootDir, - ) - }) - - AfterEach(func() { - config.Close() - }) - - It("prints an error about invalid YAML", func() { - config.Write([]byte(`[]`)) - - expected := fmt.Sprintf("Error: Failed trying to marshal the tree to YAML : expected a map, got a `[]interface {}` which is not supported at this time for \"%s\"\n", config.Path) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - - Expect(err).ShouldNot(HaveOccurred()) - - stderr := session.Wait().Err.Contents() - Expect(string(stderr)).To(Equal(expected)) - Eventually(session).Should(clitest.ShouldFail()) - }) - }) - - Describe("validating configs", func() { - config := "version: 2.1" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "validate", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.Variables["config"] = config - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns an error when validating a config", func() { - expResp := `{ - "buildConfig": { - "errors": [ - {"message": "error1"} - ] - } - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: error1")) - Eventually(session).Should(clitest.ShouldFail()) - }) - - It("returns successfully when validating a config", func() { - expResp := `{ - "buildConfig": {} - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) - Eventually(session).Should(gexec.Exit(0)) - }) + It("given a local orbs folder with mixed inline and local commands, jobs, etc, packs all YAML contents as expected", func() { + var path string = "nested-orbs-and-local-commands-etc" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) }) - Describe("validating configs with pipeline parameters", func() { - config := "version: 2.1" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "process", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "--pipeline-parameters", `{"foo": "test", "bar": true, "baz": 10}`, - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues, pipelineParametersJson: $pipelineParametersJson) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.Variables["config"] = config - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - pipelineParams, err := json.Marshal(pipeline.Parameters{ - "foo": "test", - "bar": true, - "baz": 10, - }) - Expect(err).ToNot(HaveOccurred()) - r.Variables["pipelineParametersJson"] = string(pipelineParams) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns successfully when validating a config", func() { - expResp := `{ - "buildConfig": {} - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session).Should(gexec.Exit(0)) - }) + It("packs successfully given an orb containing local executors and commands in folder", func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/myorb/test") + + results = golden.Get(GinkgoT(), filepath.FromSlash("myorb/result.yml")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) }) - Describe("validating configs with private orbs", func() { - config := "version: 2.1" - orgId := "bb604b45-b6b0-4b81-ad80-796f15eddf87" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "validate", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "--org-id", orgId, - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgId: $orgId) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.Variables["config"] = config - r.Variables["orgId"] = orgId - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns an error when validating a config with a private orb", func() { - expResp := `{ - "buildConfig": { - "errors": [ - {"message": "permission denied"} - ] - } - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) - Eventually(session).Should(clitest.ShouldFail()) - }) + It("packs as expected given a large nested config including rails orbs", func() { + var path string = "test-with-large-nested-rails-orb" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) }) - Describe("validating configs with private orbs Legacy", func() { - config := "version: 2.1" - orgSlug := "circleci" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "validate", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "--org-slug", orgSlug, - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgSlug: $orgSlug) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.Variables["config"] = config - r.Variables["orgSlug"] = orgSlug - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns an error when validating a config with a private orb", func() { - expResp := `{ - "buildConfig": { - "errors": [ - {"message": "permission denied"} - ] - } - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) - Eventually(session).Should(clitest.ShouldFail()) - }) - - It("returns successfully when validating a config with private orbs", func() { - expResp := `{ - "buildConfig": {} - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) - Eventually(session).Should(gexec.Exit(0)) - }) + It("prints an error given a config which is a list and not a map", func() { + config := clitest.OpenTmpFile(filepath.Join(tempSettings.Home, "myorb"), "config.yaml") + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + config.RootDir, + ) + config.Write([]byte(`[]`)) + + expected := fmt.Sprintf("Error: Failed trying to marshal the tree to YAML : expected a map, got a `[]interface {}` which is not supported at this time for \"%s\"\n", config.Path) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + stderr := session.Wait().Err.Contents() + Expect(string(stderr)).To(Equal(expected)) + Eventually(session).Should(clitest.ShouldFail()) + config.Close() }) }) }) diff --git a/cmd/root.go b/cmd/root.go index 174f764eb..a11a9391f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -20,6 +20,7 @@ import ( var defaultEndpoint = "graphql-unstable" var defaultHost = "https://circleci.com" +var defaultConfigApiHost = "https://api.circleci.com" var defaultRestEndpoint = "api/v2" var trueString = "true" @@ -96,12 +97,13 @@ Use "{{.CommandPath}} [command] --help" for more information about a command.{{e // MakeCommands creates the top level commands func MakeCommands() *cobra.Command { rootOptions = &settings.Config{ - Debug: false, - Token: "", - Host: defaultHost, - RestEndpoint: defaultRestEndpoint, - Endpoint: defaultEndpoint, - GitHubAPI: "https://api.github.com/", + Debug: false, + Token: "", + Host: defaultHost, + ConfigAPIHost: defaultConfigApiHost, + RestEndpoint: defaultRestEndpoint, + Endpoint: defaultEndpoint, + GitHubAPI: "https://api.github.com/", } if err := rootOptions.Load(); err != nil { diff --git a/config/config.go b/config/config.go new file mode 100644 index 000000000..3db3a4f94 --- /dev/null +++ b/config/config.go @@ -0,0 +1,94 @@ +package config + +import ( + "fmt" + "io/ioutil" + "net/url" + "os" + + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/pipeline" + "github.com/pkg/errors" +) + +type ConfigError struct { + Message string `json:"message"` +} + +type ConfigResponse struct { + Valid bool `json:"valid"` + SourceYaml string `json:"source-yaml"` + OutputYaml string `json:"output-yaml"` + Errors []ConfigError `json:"errors"` +} + +type CompileConfigRequest struct { + ConfigYaml string `json:"config_yaml"` + Options Options `json:"options"` +} + +type Options struct { + PipelineValues pipeline.Values `json:"pipeline_values"` +} + +// #nosec +func loadYaml(path string) (string, error) { + var err error + var config []byte + if path == "-" { + config, err = ioutil.ReadAll(os.Stdin) + } else { + config, err = ioutil.ReadFile(path) + } + + if err != nil { + return "", errors.Wrapf(err, "Could not load config file at %s", path) + } + + return string(config), nil +} + +func ConfigQuery( + rest *rest.Client, + configPath string, + orgID string, + params pipeline.Parameters, + values pipeline.Values, +) (*ConfigResponse, error) { + + configString, err := loadYaml(configPath) + if err != nil { + return nil, err + } + + req, err := rest.NewAPIRequest( + "POST", + &url.URL{ + Path: "compile-config-with-defaults", + }, + CompileConfigRequest{ + ConfigYaml: configString, + Options: Options{ + PipelineValues: values, + }, + }, + ) + if err != nil { + return nil, err + } + + configCompilationResp := &ConfigResponse{} + statusCode, err := rest.DoRequest(req, configCompilationResp) + if err != nil { + return nil, err + } + if statusCode != 200 { + return nil, errors.New("non 200 status code") + } + + if len(configCompilationResp.Errors) > 0 { + return nil, errors.New(fmt.Sprintf("config compilation contains errors: %s", configCompilationResp.Errors)) + } + + return configCompilationResp, nil +} diff --git a/cmd/deprecated-images.go b/config/deprecated_images.go similarity index 93% rename from cmd/deprecated-images.go rename to config/deprecated_images.go index 607edfc88..1579af3de 100644 --- a/cmd/deprecated-images.go +++ b/config/deprecated_images.go @@ -1,8 +1,6 @@ -package cmd +package config import ( - "github.com/CircleCI-Public/circleci-cli/api" - "github.com/pkg/errors" "gopkg.in/yaml.v3" ) @@ -40,8 +38,7 @@ type processedConfig struct { } // Processes the config down to v2.0, then checks image used against the block list -func deprecatedImageCheck(response *api.ConfigResponse) error { - +func DeprecatedImageCheck(response *ConfigResponse) error { aConfig := processedConfig{} err := yaml.Unmarshal([]byte(response.OutputYaml), &aConfig) if err != nil { diff --git a/go.mod b/go.mod index d515fd50c..fc76cc3ef 100644 --- a/go.mod +++ b/go.mod @@ -90,4 +90,4 @@ require ( // fix vulnerability: CVE-2020-15114 in etcd v3.3.10+incompatible replace github.com/coreos/etcd => github.com/coreos/etcd v3.3.24+incompatible -go 1.18 +go 1.19 diff --git a/local/local.go b/local/local.go index e139be8f4..0177ba7a1 100644 --- a/local/local.go +++ b/local/local.go @@ -10,8 +10,8 @@ import ( "strings" "syscall" - "github.com/CircleCI-Public/circleci-cli/api" - "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/config" "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/pkg/errors" @@ -24,21 +24,20 @@ const DefaultConfigPath = ".circleci/config.yml" func Execute(flags *pflag.FlagSet, cfg *settings.Config) error { var err error - var configResponse *api.ConfigResponse - cl := graphql.NewClient(cfg.HTTPClient, cfg.Host, cfg.Endpoint, cfg.Token, cfg.Debug) - + var configResponse *config.ConfigResponse + restClient := rest.New(cfg.Host, cfg) processedArgs, configPath := buildAgentArguments(flags) //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - configResponse, err = api.ConfigQuery(cl, configPath, orgID, nil, pipeline.LocalPipelineValues()) + configResponse, err = config.ConfigQuery(restClient, configPath, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - configResponse, err = api.ConfigQueryLegacy(cl, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) + configResponse, err = config.ConfigQuery(restClient, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) if err != nil { return err } diff --git a/settings/settings.go b/settings/settings.go index d9f0953b4..03cd4099c 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -35,6 +35,7 @@ type Config struct { GitHubAPI string `yaml:"-"` SkipUpdateCheck bool `yaml:"-"` OrbPublishing OrbPublishingInfo `yaml:"orb_publishing"` + ConfigAPIHost string `yaml:"-"` } type OrbPublishingInfo struct { From 5aea07054c56853f732c127a04bdf225ba5f9fa6 Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Wed, 28 Sep 2022 16:49:08 +0100 Subject: [PATCH 06/84] Adds a Taskfile in line with internal Go dev standards and also formats the code --- Taskfile.yml | 60 +++++++++++++++++++++++++++++++++++++++++++++ api/context_rest.go | 4 +-- api/info/info.go | 2 +- cmd/context.go | 4 +-- cmd/follow.go | 4 +-- cmd/info/info.go | 6 ++--- cmd/open.go | 4 +-- cmd/root.go | 2 +- update/update.go | 28 ++++++++++----------- 9 files changed, 87 insertions(+), 27 deletions(-) create mode 100644 Taskfile.yml diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 000000000..ad2d19df9 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,60 @@ +version: '3' + +tasks: + lint: + desc: Lint code + cmds: + - golangci-lint run -c .golangci.yml + summary: Lint the project with golangci-lint + + clean: + desc: Cleans out the build, out, docs, and dist directories + cmds: + - GO111MODULE=off go clean -i + - rm -rf build out docs dist + + fmt: + desc: Run `go fmt` to format the code + cmds: + - go fmt ./... + + test: + desc: Run the tests + cmds: + - TESTING=true go test -v ./... + + tidy: + desc: Run 'go mod tidy' to clean up module files. + cmds: + - go mod tidy -v + + doc: + desc: run's the godocs + cmds: + - godoc -http=:6060 + + check-go-mod: + desc: Check go.mod is tidy + cmds: + - go mod tidy -v + - git diff --exit-code -- go.mod go.sum + + vendor: + desc: go mod vendor + cmds: + - go mod vendor + + build: + desc: Build main + cmds: + - go build -v -o build/darwin/amd64/circleci . + + build-linux: + desc: Build main + cmds: + - go build -v -o build/linux/amd64/circleci . + + cover: + desc: tests and generates a cover profile + cmds: + - TESTING=true go test -race -coverprofile=coverage.txt ./... \ No newline at end of file diff --git a/api/context_rest.go b/api/context_rest.go index fcc169a8a..d953bc7aa 100644 --- a/api/context_rest.go +++ b/api/context_rest.go @@ -359,7 +359,7 @@ func (c *ContextRestClient) listContexts(params *listContextsParams) (*listConte return &dest, nil } -//newCreateContextRequest posts a new context creation with orgname and vcs type using a slug +// newCreateContextRequest posts a new context creation with orgname and vcs type using a slug func (c *ContextRestClient) newCreateContextRequest(vcs, org, name string) (*http.Request, error) { var err error queryURL, err := url.Parse(c.server) @@ -398,7 +398,7 @@ func (c *ContextRestClient) newCreateContextRequest(vcs, org, name string) (*htt return c.newHTTPRequest("POST", queryURL.String(), bodyReader) } -//newCreateContextRequestWithOrgID posts a new context creation with an orgID +// newCreateContextRequestWithOrgID posts a new context creation with an orgID func (c *ContextRestClient) newCreateContextRequestWithOrgID(orgID *string, name string) (*http.Request, error) { var err error queryURL, err := url.Parse(c.server) diff --git a/api/info/info.go b/api/info/info.go index 3ba9c65ce..cb7fae40b 100644 --- a/api/info/info.go +++ b/api/info/info.go @@ -31,7 +31,7 @@ type InfoRESTClient struct { client *http.Client } -//organization json org info +// organization json org info type Organization struct { ID string `json:"id"` Name string `json:"name"` diff --git a/cmd/context.go b/cmd/context.go index 8369d0439..aebdd6558 100644 --- a/cmd/context.go +++ b/cmd/context.go @@ -199,8 +199,8 @@ func readSecretValue() (string, error) { } } -//createContext determines if the context is being created via orgid or vcs and org name -//and navigates to corresponding function accordingly +// createContext determines if the context is being created via orgid or vcs and org name +// and navigates to corresponding function accordingly func createContext(cmd *cobra.Command, client api.ContextInterface, args []string) error { //skip if no orgid provided if orgID != nil && strings.TrimSpace(*orgID) != "" && len(args) == 1 { diff --git a/cmd/follow.go b/cmd/follow.go index 3471c0cd7..31eb3bbae 100644 --- a/cmd/follow.go +++ b/cmd/follow.go @@ -14,7 +14,7 @@ type options struct { cfg *settings.Config } -//followProject gets the remote data and attempts to follow its git project +// followProject gets the remote data and attempts to follow its git project func followProject(opts options) error { remote, err := git.InferProjectFromGitRemotes() @@ -45,7 +45,7 @@ func followProject(opts options) error { return nil } -//followProjectCommand follow cobra command creation +// followProjectCommand follow cobra command creation func followProjectCommand(config *settings.Config) *cobra.Command { opts := options{ cfg: config, diff --git a/cmd/info/info.go b/cmd/info/info.go index d0ba3e0e6..1ed3028fe 100644 --- a/cmd/info/info.go +++ b/cmd/info/info.go @@ -9,7 +9,7 @@ import ( "github.com/spf13/cobra" ) -//infoOptions info command options +// infoOptions info command options type infoOptions struct { cfg *settings.Config validator validator.Validator @@ -33,7 +33,7 @@ func NewInfoCommand(config *settings.Config, preRunE validator.Validator) *cobra return infoCommand } -//orgInfoCommand organization information subcommand cobra command creation +// orgInfoCommand organization information subcommand cobra command creation func orgInfoCommand(client info.InfoClient, opts infoOptions) *cobra.Command { return &cobra.Command{ Use: "org", @@ -48,7 +48,7 @@ func orgInfoCommand(client info.InfoClient, opts infoOptions) *cobra.Command { } } -//getOrgInformation gets all of the users organizations' information +// getOrgInformation gets all of the users organizations' information func getOrgInformation(cmd *cobra.Command, client info.InfoClient) error { resp, err := client.GetInfo() if err != nil { diff --git a/cmd/open.go b/cmd/open.go index 8300aec65..6f1772b1d 100644 --- a/cmd/open.go +++ b/cmd/open.go @@ -16,7 +16,7 @@ var errorMessage = ` This command is intended to be run from a git repository with a remote named 'origin' that is hosted on Github or Bitbucket only. We are not currently supporting any other hosts.` -//projectUrl uses the provided values to create the url to open +// projectUrl uses the provided values to create the url to open func projectUrl(remote *git.Remote) string { return fmt.Sprintf("https://app.circleci.com/pipelines/%s/%s/%s", url.PathEscape(strings.ToLower(string(remote.VcsType))), @@ -24,7 +24,7 @@ func projectUrl(remote *git.Remote) string { url.PathEscape(remote.Project)) } -//openProjectInBrowser takes the created url and opens a browser to it +// openProjectInBrowser takes the created url and opens a browser to it func openProjectInBrowser() error { remote, err := git.InferProjectFromGitRemotes() if err != nil { diff --git a/cmd/root.go b/cmd/root.go index 174f764eb..f47ff96ea 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -294,7 +294,7 @@ func skipUpdateByDefault() bool { /**************** Help Menu Functions ****************/ -//rootHelpLong creates content for the long field in the command +// rootHelpLong creates content for the long field in the command func rootHelpLong() string { long := ` /?? /?? /?? diff --git a/update/update.go b/update/update.go index a82f126f0..77e1aa9ca 100644 --- a/update/update.go +++ b/update/update.go @@ -136,20 +136,20 @@ func checkFromHomebrew(check *Options) error { // HomebrewOutdated wraps the JSON output from running `brew outdated --json=v2` // We're specifically looking for this kind of structured data from the command: // -// { -// "formulae": [ -// { -// "name": "circleci", -// "installed_versions": [ -// "0.1.1248" -// ], -// "current_version": "0.1.3923", -// "pinned": false, -// "pinned_version": null -// } -// ], -// "casks": [] -// } +// { +// "formulae": [ +// { +// "name": "circleci", +// "installed_versions": [ +// "0.1.1248" +// ], +// "current_version": "0.1.3923", +// "pinned": false, +// "pinned_version": null +// } +// ], +// "casks": [] +// } type HomebrewOutdated struct { Formulae []struct { Name string `json:"name"` From 79819a86cdfa09eaaef6a6697998b3f56f7f0907 Mon Sep 17 00:00:00 2001 From: corinnesollows Date: Mon, 3 Oct 2022 14:07:52 -0300 Subject: [PATCH 07/84] Revert "PIPE-2315/new branch to iteratively test" (#790) --- .circleci/config.yml | 6 +- Dockerfile | 2 +- api/api.go | 120 +++++ api/rest/client.go | 37 +- cmd/config.go | 27 +- cmd/config_test.go | 467 +++++++++++++++--- .../deprecated-images.go | 7 +- cmd/root.go | 14 +- config/config.go | 94 ---- go.mod | 2 +- local/local.go | 13 +- settings/settings.go | 1 - 12 files changed, 545 insertions(+), 245 deletions(-) rename config/deprecated_images.go => cmd/deprecated-images.go (93%) delete mode 100644 config/config.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 3f8297e46..1d7c27638 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ orbs: executors: go: docker: - - image: cimg/go:1.19 + - image: cimg/go:1.18 environment: CGO_ENABLED: 0 mac: @@ -105,8 +105,8 @@ jobs: - checkout - run: | brew update - brew install go@1.19 - echo 'export PATH="/usr/local/opt/go@1.19/bin:$PATH"' >> ~/.bash_profile + brew install go@1.18 + echo 'export PATH="/usr/local/opt/go@1.18/bin:$PATH"' >> ~/.bash_profile - gomod - run: make test build: diff --git a/Dockerfile b/Dockerfile index 0d6d09f17..c94a4659f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM cimg/go:1.19.1 +FROM cimg/go:1.18.3 ENV CIRCLECI_CLI_SKIP_UPDATE_CHECK true diff --git a/api/api.go b/api/api.go index eabbc5ef0..674ae112c 100644 --- a/api/api.go +++ b/api/api.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/references" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/Masterminds/semver" @@ -512,6 +513,125 @@ func WhoamiQuery(cl *graphql.Client) (*WhoamiResponse, error) { return &response, nil } +// ConfigQueryLegacy calls the GQL API to validate and process config with the legacy orgSlug +func ConfigQueryLegacy(cl *graphql.Client, configPath string, orgSlug string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { + var response BuildConfigResponse + var query string + config, err := loadYaml(configPath) + if err != nil { + return nil, err + } + // GraphQL isn't forwards-compatible, so we are unusually selective here about + // passing only non-empty fields on to the API, to minimize user impact if the + // backend is out of date. + var fieldAddendums string + if orgSlug != "" { + fieldAddendums += ", orgSlug: $orgSlug" + } + if len(params) > 0 { + fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" + } + query = fmt.Sprintf( + `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }`, + fieldAddendums) + + request := graphql.NewRequest(query) + request.Var("config", config) + + if values != nil { + request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) + } + if params != nil { + pipelineParameters, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) + } + request.Var("pipelineParametersJson", string(pipelineParameters)) + } + + if orgSlug != "" { + request.Var("orgSlug", orgSlug) + } + + request.SetToken(cl.Token) + + err = cl.Run(request, &response) + if err != nil { + return nil, errors.Wrap(err, "Unable to validate config") + } + if len(response.BuildConfig.ConfigResponse.Errors) > 0 { + return nil, &response.BuildConfig.ConfigResponse.Errors + } + + return &response.BuildConfig.ConfigResponse, nil +} + +// ConfigQuery calls the GQL API to validate and process config with the org id +func ConfigQuery(cl *graphql.Client, configPath string, orgId string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { + var response BuildConfigResponse + var query string + config, err := loadYaml(configPath) + if err != nil { + return nil, err + } + // GraphQL isn't forwards-compatible, so we are unusually selective here about + // passing only non-empty fields on to the API, to minimize user impact if the + // backend is out of date. + var fieldAddendums string + if orgId != "" { + fieldAddendums += ", orgId: $orgId" + } + if len(params) > 0 { + fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" + } + query = fmt.Sprintf( + `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }`, + fieldAddendums) + + request := graphql.NewRequest(query) + request.Var("config", config) + + if values != nil { + request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) + } + if params != nil { + pipelineParameters, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) + } + request.Var("pipelineParametersJson", string(pipelineParameters)) + } + + if orgId != "" { + request.Var("orgId", orgId) + } + request.SetToken(cl.Token) + + err = cl.Run(request, &response) + if err != nil { + return nil, errors.Wrap(err, "Unable to validate config") + } + if len(response.BuildConfig.ConfigResponse.Errors) > 0 { + return nil, &response.BuildConfig.ConfigResponse.Errors + } + + return &response.BuildConfig.ConfigResponse, nil +} + // OrbQuery validated and processes an orb. func OrbQuery(cl *graphql.Client, configPath string) (*ConfigResponse, error) { var response OrbConfigResponse diff --git a/api/rest/client.go b/api/rest/client.go index e8a4a4a68..d745b887e 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -18,7 +18,6 @@ import ( type Client struct { baseURL *url.URL - apiURL *url.URL circleToken string client *http.Client } @@ -30,14 +29,13 @@ func New(host string, config *settings.Config) *Client { endpoint += "/" } - baseURL, _ := url.Parse(host) - apiURL, _ := url.Parse(config.ConfigAPIHost) + u, _ := url.Parse(host) + client := config.HTTPClient client.Timeout = 10 * time.Second return &Client{ - apiURL: apiURL.ResolveReference(&url.URL{Path: endpoint}), - baseURL: baseURL.ResolveReference(&url.URL{Path: endpoint}), + baseURL: u.ResolveReference(&url.URL{Path: endpoint}), circleToken: config.Token, client: client, } @@ -72,35 +70,6 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req return req, nil } -func (c *Client) NewAPIRequest(method string, u *url.URL, payload interface{}) (req *http.Request, err error) { - var r io.Reader - if payload != nil { - buf := &bytes.Buffer{} - r = buf - err = json.NewEncoder(buf).Encode(payload) - if err != nil { - return nil, err - } - } - - req, err = http.NewRequest(method, c.apiURL.ResolveReference(u).String(), r) - if err != nil { - return nil, err - } - - req.Header.Set("Circle-Token", c.circleToken) - req.Header.Set("Accept", "application/json") - req.Header.Set("User-Agent", version.UserAgent()) - commandStr := header.GetCommandStr() - if commandStr != "" { - req.Header.Set("Circleci-Cli-Command", commandStr) - } - if payload != nil { - req.Header.Set("Content-Type", "application/json") - } - return req, nil -} - func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int, err error) { httpResp, err := c.client.Do(req) if err != nil { diff --git a/cmd/config.go b/cmd/config.go index c3f8a4831..40344a079 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -5,8 +5,8 @@ import ( "io/ioutil" "strings" - "github.com/CircleCI-Public/circleci-cli/api/rest" - "github.com/CircleCI-Public/circleci-cli/config" + "github.com/CircleCI-Public/circleci-cli/api" + "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/filetree" "github.com/CircleCI-Public/circleci-cli/local" "github.com/CircleCI-Public/circleci-cli/pipeline" @@ -20,7 +20,7 @@ import ( type configOptions struct { cfg *settings.Config - rest *rest.Client + cl *graphql.Client args []string } @@ -63,7 +63,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Check that the config file is well formed.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.rest = rest.New(config.Host, config) + opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) }, RunE: func(cmd *cobra.Command, _ []string) error { return validateConfig(opts, cmd.Flags()) @@ -85,7 +85,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Validate config and display expanded configuration.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.rest = rest.New(config.Host, config) + opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) }, RunE: func(cmd *cobra.Command, _ []string) error { return processConfig(opts, cmd.Flags()) @@ -125,7 +125,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { // The arg is actually optional, in order to support compatibility with the --path flag. func validateConfig(opts configOptions, flags *pflag.FlagSet) error { var err error - var response *config.ConfigResponse + var response *api.ConfigResponse path := local.DefaultConfigPath // First, set the path to configPath set by --path flag for compatibility if configPath != "" { @@ -140,16 +140,13 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - - response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) + response, err = api.ConfigQuery(opts.cl, path, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } - } else { orgSlug, _ := flags.GetString("org-slug") - // response, err = api.ConfigQueryLegacy(opts.cl, path, orgSlug, nil, pipeline.LocalPipelineValues()) - response, err = config.ConfigQuery(opts.rest, path, orgSlug, nil, pipeline.LocalPipelineValues()) + response, err = api.ConfigQueryLegacy(opts.cl, path, orgSlug, nil, pipeline.LocalPipelineValues()) if err != nil { return err } @@ -159,7 +156,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { // link here to blog post when available // returns an error if a deprecated image is used if !ignoreDeprecatedImages { - err := config.DeprecatedImageCheck(response) + err := deprecatedImageCheck(response) if err != nil { return err } @@ -176,7 +173,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { func processConfig(opts configOptions, flags *pflag.FlagSet) error { paramsYaml, _ := flags.GetString("pipeline-parameters") - var response *config.ConfigResponse + var response *api.ConfigResponse var params pipeline.Parameters var err error @@ -197,13 +194,13 @@ func processConfig(opts configOptions, flags *pflag.FlagSet) error { //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, pipeline.LocalPipelineValues()) + response, err = api.ConfigQuery(opts.cl, opts.args[0], orgID, params, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - response, err = config.ConfigQuery(opts.rest, opts.args[0], orgSlug, params, pipeline.LocalPipelineValues()) + response, err = api.ConfigQueryLegacy(opts.cl, opts.args[0], orgSlug, params, pipeline.LocalPipelineValues()) if err != nil { return err } diff --git a/cmd/config_test.go b/cmd/config_test.go index 6c622f564..e03177f8d 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -1,13 +1,20 @@ package cmd_test import ( + "encoding/json" "fmt" + "io" + "net/http" "os/exec" "path/filepath" + "time" + "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/clitest" + "github.com/CircleCI-Public/circleci-cli/pipeline" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" "github.com/onsi/gomega/gexec" "gotest.tools/v3/golden" ) @@ -18,7 +25,7 @@ var _ = Describe("Config", func() { command *exec.Cmd results []byte tempSettings *clitest.TempSettings - // token string = "testtoken" + token string = "testtoken" ) BeforeEach(func() { @@ -29,95 +36,395 @@ var _ = Describe("Config", func() { tempSettings.Close() }) - It("packs all YAML contents as expected", func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/hugo-pack/.circleci") - results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) + Describe("a .circleci folder with config.yml and local orbs folder containing the hugo orb", func() { + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/hugo-pack/.circleci") + results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) + }) + + It("pack all YAML contents as expected", func() { + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + }) + + Describe("local orbs folder with mixed inline and local commands, jobs, etc", func() { + BeforeEach(func() { + var path string = "nested-orbs-and-local-commands-etc" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + }) + + It("pack all YAML contents as expected", func() { + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) }) - It("given a .circleci folder with config.ymla nd local orb, packs all YAML contents as expected", func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/hugo-pack/.circleci") - results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) + Describe("an orb containing local executors and commands in folder", func() { + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/myorb/test") + + results = golden.Get(GinkgoT(), filepath.FromSlash("myorb/result.yml")) + }) + + It("pack all YAML contents as expected", func() { + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) }) - It("given a local orbs folder with mixed inline and local commands, jobs, etc, packs all YAML contents as expected", func() { - var path string = "nested-orbs-and-local-commands-etc" - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - filepath.Join("testdata", path, "test")) - results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) + Describe("with a large nested config including rails orb", func() { + BeforeEach(func() { + var path string = "test-with-large-nested-rails-orb" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + }) + + It("pack all YAML contents as expected", func() { + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + }) + + Context("config is a list and not a map", func() { + var config *clitest.TmpFile + + BeforeEach(func() { + config = clitest.OpenTmpFile(filepath.Join(tempSettings.Home, "myorb"), "config.yaml") + + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + config.RootDir, + ) + }) + + AfterEach(func() { + config.Close() + }) + + It("prints an error about invalid YAML", func() { + config.Write([]byte(`[]`)) + + expected := fmt.Sprintf("Error: Failed trying to marshal the tree to YAML : expected a map, got a `[]interface {}` which is not supported at this time for \"%s\"\n", config.Path) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + + Expect(err).ShouldNot(HaveOccurred()) + + stderr := session.Wait().Err.Contents() + Expect(string(stderr)).To(Equal(expected)) + Eventually(session).Should(clitest.ShouldFail()) + }) + }) + + Describe("validating configs", func() { + config := "version: 2.1" + var expReq string + + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "validate", + "--skip-update-check", + "--token", token, + "--host", tempSettings.TestServer.URL(), + "-", + ) + + stdin, err := command.StdinPipe() + Expect(err).ToNot(HaveOccurred()) + _, err = io.WriteString(stdin, config) + Expect(err).ToNot(HaveOccurred()) + stdin.Close() + + query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }` + + r := graphql.NewRequest(query) + r.Variables["config"] = config + r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) + + req, err := r.Encode() + Expect(err).ShouldNot(HaveOccurred()) + expReq = req.String() + }) + + It("returns an error when validating a config", func() { + expResp := `{ + "buildConfig": { + "errors": [ + {"message": "error1"} + ] + } + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: error1")) + Eventually(session).Should(clitest.ShouldFail()) + }) + + It("returns successfully when validating a config", func() { + expResp := `{ + "buildConfig": {} + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) + Eventually(session).Should(gexec.Exit(0)) + }) }) - It("packs successfully given an orb containing local executors and commands in folder", func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/myorb/test") - - results = golden.Get(GinkgoT(), filepath.FromSlash("myorb/result.yml")) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) + Describe("validating configs with pipeline parameters", func() { + config := "version: 2.1" + var expReq string + + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "process", + "--skip-update-check", + "--token", token, + "--host", tempSettings.TestServer.URL(), + "--pipeline-parameters", `{"foo": "test", "bar": true, "baz": 10}`, + "-", + ) + + stdin, err := command.StdinPipe() + Expect(err).ToNot(HaveOccurred()) + _, err = io.WriteString(stdin, config) + Expect(err).ToNot(HaveOccurred()) + stdin.Close() + + query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues, pipelineParametersJson: $pipelineParametersJson) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }` + + r := graphql.NewRequest(query) + r.Variables["config"] = config + r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) + + pipelineParams, err := json.Marshal(pipeline.Parameters{ + "foo": "test", + "bar": true, + "baz": 10, + }) + Expect(err).ToNot(HaveOccurred()) + r.Variables["pipelineParametersJson"] = string(pipelineParams) + + req, err := r.Encode() + Expect(err).ShouldNot(HaveOccurred()) + expReq = req.String() + }) + + It("returns successfully when validating a config", func() { + expResp := `{ + "buildConfig": {} + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session).Should(gexec.Exit(0)) + }) }) - It("packs as expected given a large nested config including rails orbs", func() { - var path string = "test-with-large-nested-rails-orb" - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - filepath.Join("testdata", path, "test")) - results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) + Describe("validating configs with private orbs", func() { + config := "version: 2.1" + orgId := "bb604b45-b6b0-4b81-ad80-796f15eddf87" + var expReq string + + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "validate", + "--skip-update-check", + "--token", token, + "--host", tempSettings.TestServer.URL(), + "--org-id", orgId, + "-", + ) + + stdin, err := command.StdinPipe() + Expect(err).ToNot(HaveOccurred()) + _, err = io.WriteString(stdin, config) + Expect(err).ToNot(HaveOccurred()) + stdin.Close() + + query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgId: $orgId) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }` + + r := graphql.NewRequest(query) + r.Variables["config"] = config + r.Variables["orgId"] = orgId + r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) + + req, err := r.Encode() + Expect(err).ShouldNot(HaveOccurred()) + expReq = req.String() + }) + + It("returns an error when validating a config with a private orb", func() { + expResp := `{ + "buildConfig": { + "errors": [ + {"message": "permission denied"} + ] + } + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) + Eventually(session).Should(clitest.ShouldFail()) + }) }) - It("prints an error given a config which is a list and not a map", func() { - config := clitest.OpenTmpFile(filepath.Join(tempSettings.Home, "myorb"), "config.yaml") - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - config.RootDir, - ) - config.Write([]byte(`[]`)) - - expected := fmt.Sprintf("Error: Failed trying to marshal the tree to YAML : expected a map, got a `[]interface {}` which is not supported at this time for \"%s\"\n", config.Path) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - stderr := session.Wait().Err.Contents() - Expect(string(stderr)).To(Equal(expected)) - Eventually(session).Should(clitest.ShouldFail()) - config.Close() + Describe("validating configs with private orbs Legacy", func() { + config := "version: 2.1" + orgSlug := "circleci" + var expReq string + + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "validate", + "--skip-update-check", + "--token", token, + "--host", tempSettings.TestServer.URL(), + "--org-slug", orgSlug, + "-", + ) + + stdin, err := command.StdinPipe() + Expect(err).ToNot(HaveOccurred()) + _, err = io.WriteString(stdin, config) + Expect(err).ToNot(HaveOccurred()) + stdin.Close() + + query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgSlug: $orgSlug) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }` + + r := graphql.NewRequest(query) + r.Variables["config"] = config + r.Variables["orgSlug"] = orgSlug + r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) + + req, err := r.Encode() + Expect(err).ShouldNot(HaveOccurred()) + expReq = req.String() + }) + + It("returns an error when validating a config with a private orb", func() { + expResp := `{ + "buildConfig": { + "errors": [ + {"message": "permission denied"} + ] + } + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) + Eventually(session).Should(clitest.ShouldFail()) + }) + + It("returns successfully when validating a config with private orbs", func() { + expResp := `{ + "buildConfig": {} + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) + Eventually(session).Should(gexec.Exit(0)) + }) }) }) }) diff --git a/config/deprecated_images.go b/cmd/deprecated-images.go similarity index 93% rename from config/deprecated_images.go rename to cmd/deprecated-images.go index 1579af3de..607edfc88 100644 --- a/config/deprecated_images.go +++ b/cmd/deprecated-images.go @@ -1,6 +1,8 @@ -package config +package cmd import ( + "github.com/CircleCI-Public/circleci-cli/api" + "github.com/pkg/errors" "gopkg.in/yaml.v3" ) @@ -38,7 +40,8 @@ type processedConfig struct { } // Processes the config down to v2.0, then checks image used against the block list -func DeprecatedImageCheck(response *ConfigResponse) error { +func deprecatedImageCheck(response *api.ConfigResponse) error { + aConfig := processedConfig{} err := yaml.Unmarshal([]byte(response.OutputYaml), &aConfig) if err != nil { diff --git a/cmd/root.go b/cmd/root.go index a51f10ee4..f47ff96ea 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -20,7 +20,6 @@ import ( var defaultEndpoint = "graphql-unstable" var defaultHost = "https://circleci.com" -var defaultConfigApiHost = "https://api.circleci.com" var defaultRestEndpoint = "api/v2" var trueString = "true" @@ -97,13 +96,12 @@ Use "{{.CommandPath}} [command] --help" for more information about a command.{{e // MakeCommands creates the top level commands func MakeCommands() *cobra.Command { rootOptions = &settings.Config{ - Debug: false, - Token: "", - Host: defaultHost, - ConfigAPIHost: defaultConfigApiHost, - RestEndpoint: defaultRestEndpoint, - Endpoint: defaultEndpoint, - GitHubAPI: "https://api.github.com/", + Debug: false, + Token: "", + Host: defaultHost, + RestEndpoint: defaultRestEndpoint, + Endpoint: defaultEndpoint, + GitHubAPI: "https://api.github.com/", } if err := rootOptions.Load(); err != nil { diff --git a/config/config.go b/config/config.go deleted file mode 100644 index 3db3a4f94..000000000 --- a/config/config.go +++ /dev/null @@ -1,94 +0,0 @@ -package config - -import ( - "fmt" - "io/ioutil" - "net/url" - "os" - - "github.com/CircleCI-Public/circleci-cli/api/rest" - "github.com/CircleCI-Public/circleci-cli/pipeline" - "github.com/pkg/errors" -) - -type ConfigError struct { - Message string `json:"message"` -} - -type ConfigResponse struct { - Valid bool `json:"valid"` - SourceYaml string `json:"source-yaml"` - OutputYaml string `json:"output-yaml"` - Errors []ConfigError `json:"errors"` -} - -type CompileConfigRequest struct { - ConfigYaml string `json:"config_yaml"` - Options Options `json:"options"` -} - -type Options struct { - PipelineValues pipeline.Values `json:"pipeline_values"` -} - -// #nosec -func loadYaml(path string) (string, error) { - var err error - var config []byte - if path == "-" { - config, err = ioutil.ReadAll(os.Stdin) - } else { - config, err = ioutil.ReadFile(path) - } - - if err != nil { - return "", errors.Wrapf(err, "Could not load config file at %s", path) - } - - return string(config), nil -} - -func ConfigQuery( - rest *rest.Client, - configPath string, - orgID string, - params pipeline.Parameters, - values pipeline.Values, -) (*ConfigResponse, error) { - - configString, err := loadYaml(configPath) - if err != nil { - return nil, err - } - - req, err := rest.NewAPIRequest( - "POST", - &url.URL{ - Path: "compile-config-with-defaults", - }, - CompileConfigRequest{ - ConfigYaml: configString, - Options: Options{ - PipelineValues: values, - }, - }, - ) - if err != nil { - return nil, err - } - - configCompilationResp := &ConfigResponse{} - statusCode, err := rest.DoRequest(req, configCompilationResp) - if err != nil { - return nil, err - } - if statusCode != 200 { - return nil, errors.New("non 200 status code") - } - - if len(configCompilationResp.Errors) > 0 { - return nil, errors.New(fmt.Sprintf("config compilation contains errors: %s", configCompilationResp.Errors)) - } - - return configCompilationResp, nil -} diff --git a/go.mod b/go.mod index fc76cc3ef..d515fd50c 100644 --- a/go.mod +++ b/go.mod @@ -90,4 +90,4 @@ require ( // fix vulnerability: CVE-2020-15114 in etcd v3.3.10+incompatible replace github.com/coreos/etcd => github.com/coreos/etcd v3.3.24+incompatible -go 1.19 +go 1.18 diff --git a/local/local.go b/local/local.go index 0177ba7a1..e139be8f4 100644 --- a/local/local.go +++ b/local/local.go @@ -10,8 +10,8 @@ import ( "strings" "syscall" - "github.com/CircleCI-Public/circleci-cli/api/rest" - "github.com/CircleCI-Public/circleci-cli/config" + "github.com/CircleCI-Public/circleci-cli/api" + "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/pkg/errors" @@ -24,20 +24,21 @@ const DefaultConfigPath = ".circleci/config.yml" func Execute(flags *pflag.FlagSet, cfg *settings.Config) error { var err error - var configResponse *config.ConfigResponse - restClient := rest.New(cfg.Host, cfg) + var configResponse *api.ConfigResponse + cl := graphql.NewClient(cfg.HTTPClient, cfg.Host, cfg.Endpoint, cfg.Token, cfg.Debug) + processedArgs, configPath := buildAgentArguments(flags) //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - configResponse, err = config.ConfigQuery(restClient, configPath, orgID, nil, pipeline.LocalPipelineValues()) + configResponse, err = api.ConfigQuery(cl, configPath, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - configResponse, err = config.ConfigQuery(restClient, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) + configResponse, err = api.ConfigQueryLegacy(cl, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) if err != nil { return err } diff --git a/settings/settings.go b/settings/settings.go index 03cd4099c..d9f0953b4 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -35,7 +35,6 @@ type Config struct { GitHubAPI string `yaml:"-"` SkipUpdateCheck bool `yaml:"-"` OrbPublishing OrbPublishingInfo `yaml:"orb_publishing"` - ConfigAPIHost string `yaml:"-"` } type OrbPublishingInfo struct { From 2fa2d3c10bedbaed53305ee3464db2de31389781 Mon Sep 17 00:00:00 2001 From: nav Date: Thu, 15 Sep 2022 15:40:29 -0700 Subject: [PATCH 08/84] Feat: clone default transport to ignore bad certs --- api/graphql/client.go | 2 +- settings/settings.go | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/api/graphql/client.go b/api/graphql/client.go index e5593ad84..9392324f9 100644 --- a/api/graphql/client.go +++ b/api/graphql/client.go @@ -29,7 +29,7 @@ type Client struct { // NewClient returns a reference to a Client. func NewClient(httpClient *http.Client, host, endpoint, token string, debug bool) *Client { return &Client{ - httpClient: http.DefaultClient, + httpClient: httpClient, Endpoint: endpoint, Host: host, Token: token, diff --git a/settings/settings.go b/settings/settings.go index d9f0953b4..24bc07f33 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -223,15 +223,17 @@ func (cfg *Config) WithHTTPClient() error { tlsConfig.RootCAs = pool } + // clone default http transport to retain default transport config values + customTransport := http.DefaultTransport.(*http.Transport).Clone() + customTransport.ExpectContinueTimeout = time.Second + customTransport.IdleConnTimeout = 90 * time.Second + customTransport.MaxIdleConns = 10 + customTransport.TLSHandshakeTimeout = 10 * time.Second + customTransport.TLSClientConfig = tlsConfig + cfg.HTTPClient = &http.Client{ - Timeout: 60 * time.Second, - Transport: &http.Transport{ - ExpectContinueTimeout: 1 * time.Second, - IdleConnTimeout: 90 * time.Second, - MaxIdleConns: 10, - TLSHandshakeTimeout: 10 * time.Second, - TLSClientConfig: tlsConfig, - }, + Timeout: 60 * time.Second, + Transport: customTransport, } return nil From 97049b800fb7eef5adb4135f698249b986f1388b Mon Sep 17 00:00:00 2001 From: Sagar Gupta Date: Tue, 4 Oct 2022 16:59:24 -0400 Subject: [PATCH 09/84] [SECENG-839] added command to get/set policy settings (#791) --- api/policy/policy.go | 69 ++++++++ api/policy/policy_test.go | 162 ++++++++++++++++++ cmd/policy/policy.go | 47 +++++ cmd/policy/policy_test.go | 122 +++++++++++++ cmd/policy/testdata/policy-expected-usage.txt | 1 + .../policy/settings-expected-usage.txt | 13 ++ 6 files changed, 414 insertions(+) create mode 100644 cmd/policy/testdata/policy/settings-expected-usage.txt diff --git a/api/policy/policy.go b/api/policy/policy.go index 7f7bf99f1..605d31558 100644 --- a/api/policy/policy.go +++ b/api/policy/policy.go @@ -216,6 +216,75 @@ type DecisionRequest struct { Metadata map[string]interface{} `json:"metadata,omitempty"` } +// GetSettings calls the GET decision-settings API of policy-service. +func (c Client) GetSettings(ownerID string, context string) (interface{}, error) { + path := fmt.Sprintf("%s/api/v1/owner/%s/context/%s/decision/settings", c.serverUrl, ownerID, context) + req, err := http.NewRequest("GET", path, nil) + if err != nil { + return nil, fmt.Errorf("failed to construct request: %v", err) + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var payload httpError + if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil { + return nil, fmt.Errorf("unexpected status-code: %d", resp.StatusCode) + } + return nil, fmt.Errorf("unexpected status-code: %d - %s", resp.StatusCode, payload.Error) + } + + var body interface{} + if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { + return nil, fmt.Errorf("failed to decode response body: %v", err) + } + + return body, nil +} + +// DecisionSettings represents a request to Policy-Service to configure decision settings. +type DecisionSettings struct { + Enabled *bool `json:"enabled,omitempty"` +} + +// SetSettings calls the PATCH decision-settings API of policy-service. +func (c Client) SetSettings(ownerID string, context string, request DecisionSettings) (interface{}, error) { + payload, err := json.Marshal(request) + if err != nil { + return nil, fmt.Errorf("failed to marshal request: %w", err) + } + path := fmt.Sprintf("%s/api/v1/owner/%s/context/%s/decision/settings", c.serverUrl, ownerID, context) + req, err := http.NewRequest("PATCH", path, bytes.NewReader(payload)) + if err != nil { + return nil, fmt.Errorf("failed to construct request: %v", err) + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var payload httpError + if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil { + return nil, fmt.Errorf("unexpected status-code: %d", resp.StatusCode) + } + return nil, fmt.Errorf("unexpected status-code: %d - %s", resp.StatusCode, payload.Error) + } + + var body interface{} + if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { + return nil, fmt.Errorf("failed to decode response body: %v", err) + } + + return body, nil +} + // MakeDecision sends a requests to Policy-Service public decision endpoint and returns the decision response func (c Client) MakeDecision(ownerID string, context string, req DecisionRequest) (*cpa.Decision, error) { payload, err := json.Marshal(req) diff --git a/api/policy/policy_test.go b/api/policy/policy_test.go index d1008a163..a112c1e60 100644 --- a/api/policy/policy_test.go +++ b/api/policy/policy_test.go @@ -632,3 +632,165 @@ func TestMakeDecision(t *testing.T) { }) } } + +func TestGetSettings(t *testing.T) { + testcases := []struct { + Name string + OwnerID string + Handler http.HandlerFunc + ExpectedError error + ExpectedSettings interface{} + }{ + { + Name: "gets expected response", + OwnerID: "test-owner", + Handler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.URL.Path, "/api/v1/owner/test-owner/context/config/decision/settings") + assert.Equal(t, r.Method, "GET") + assert.Equal(t, r.Header.Get("Circle-Token"), "test-token") + _ = json.NewEncoder(w).Encode(interface{}(`{"enabled": true}`)) + }, + ExpectedSettings: interface{}(`{"enabled": true}`), + }, + { + Name: "unexpected status code", + OwnerID: "test-owner", + Handler: func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(400) + _, _ = io.WriteString(w, `{"error":"that was a bad request!"}`) + }, + ExpectedError: errors.New("unexpected status-code: 400 - that was a bad request!"), + }, + + { + Name: "unexpected status code no body", + OwnerID: "test-owner", + Handler: func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(204) + }, + ExpectedError: errors.New("unexpected status-code: 204"), + }, + { + Name: "bad decoding", + OwnerID: "test-owner", + Handler: func(w http.ResponseWriter, _ *http.Request) { + _, _ = io.WriteString(w, "not a json response") + }, + ExpectedError: errors.New("failed to decode response body: invalid character 'o' in literal null (expecting 'u')"), + }, + } + + for _, tc := range testcases { + t.Run(tc.Name, func(t *testing.T) { + svr := httptest.NewServer(tc.Handler) + defer svr.Close() + + client := NewClient(svr.URL, &settings.Config{Token: "test-token", HTTPClient: http.DefaultClient}) + + settings, err := client.GetSettings(tc.OwnerID, "config") + if tc.ExpectedError == nil { + assert.NilError(t, err) + } else { + assert.Error(t, err, tc.ExpectedError.Error()) + return + } + + assert.DeepEqual(t, settings, tc.ExpectedSettings) + }) + } +} + +func TestSetSettings(t *testing.T) { + trueVar := true + falseVar := false + + testcases := []struct { + Name string + OwnerID string + Settings DecisionSettings + Handler http.HandlerFunc + ExpectedError error + ExpectedStatus int + ExpectedResponse interface{} + }{ + { + Name: "sends expected request (enabled=true)", + OwnerID: "test-owner", + Settings: DecisionSettings{Enabled: &trueVar}, + Handler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.URL.Path, "/api/v1/owner/test-owner/context/config/decision/settings") + assert.Equal(t, r.Method, "PATCH") + assert.Equal(t, r.Header.Get("Circle-Token"), "test-token") + var payload map[string]interface{} + assert.NilError(t, json.NewDecoder(r.Body).Decode(&payload)) + assert.DeepEqual(t, payload, map[string]interface{}{ + "enabled": true, + }) + _ = json.NewEncoder(w).Encode(interface{}(`{"enabled": true}`)) + }, + ExpectedStatus: 200, + ExpectedResponse: interface{}(`{"enabled": true}`), + }, + { + Name: "sends expected request (enabled=false)", + OwnerID: "test-owner", + Settings: DecisionSettings{Enabled: &falseVar}, + Handler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.URL.Path, "/api/v1/owner/test-owner/context/config/decision/settings") + assert.Equal(t, r.Method, "PATCH") + assert.Equal(t, r.Header.Get("Circle-Token"), "test-token") + var payload map[string]interface{} + assert.NilError(t, json.NewDecoder(r.Body).Decode(&payload)) + assert.DeepEqual(t, payload, map[string]interface{}{ + "enabled": false, + }) + _ = json.NewEncoder(w).Encode(interface{}(`{"enabled": false}`)) + }, + ExpectedStatus: 200, + ExpectedResponse: interface{}(`{"enabled": false}`), + }, + { + Name: "sends expected request (enabled=nil)", + OwnerID: "test-owner", + Settings: DecisionSettings{Enabled: nil}, + Handler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.URL.Path, "/api/v1/owner/test-owner/context/config/decision/settings") + assert.Equal(t, r.Method, "PATCH") + assert.Equal(t, r.Header.Get("Circle-Token"), "test-token") + var payload map[string]interface{} + assert.NilError(t, json.NewDecoder(r.Body).Decode(&payload)) + assert.DeepEqual(t, payload, map[string]interface{}{}) + _ = json.NewEncoder(w).Encode(interface{}(`{}`)) + }, + ExpectedStatus: 200, + ExpectedResponse: interface{}(`{}`), + }, + { + Name: "unexpected status code", + OwnerID: "test-owner", + Handler: func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(400) + _, _ = io.WriteString(w, `{"error":"that was a bad request!"}`) + }, + ExpectedError: errors.New("unexpected status-code: 400 - that was a bad request!"), + }, + } + + for _, tc := range testcases { + t.Run(tc.Name, func(t *testing.T) { + svr := httptest.NewServer(tc.Handler) + defer svr.Close() + + client := NewClient(svr.URL, &settings.Config{Token: "test-token", HTTPClient: http.DefaultClient}) + + response, err := client.SetSettings(tc.OwnerID, "config", tc.Settings) + if tc.ExpectedError == nil { + assert.NilError(t, err) + } else { + assert.Error(t, err, tc.ExpectedError.Error()) + return + } + assert.DeepEqual(t, response, tc.ExpectedResponse) + }) + } +} diff --git a/cmd/policy/policy.go b/cmd/policy/policy.go index 31b3ca47c..667dde79c 100644 --- a/cmd/policy/policy.go +++ b/cmd/policy/policy.go @@ -380,12 +380,59 @@ This group of commands allows the management of polices to be verified against b return cmd }() + settings := func() *cobra.Command { + var ( + ownerID string + context string + enabled bool + request policy.DecisionSettings + ) + + cmd := &cobra.Command{ + + Short: "get/set policy decision settings (To read settings: run command without any settings flags)", + Use: "settings", + RunE: func(cmd *cobra.Command, args []string) error { + client := policy.NewClient(*policyBaseURL, config) + + response, err := func() (interface{}, error) { + if cmd.Flag("enabled").Changed { + request.Enabled = &enabled + return client.SetSettings(ownerID, context, request) + } + return client.GetSettings(ownerID, context) + }() + if err != nil { + return fmt.Errorf("failed to run settings : %w", err) + } + + if err = prettyJSONEncoder(cmd.OutOrStdout()).Encode(response); err != nil { + return fmt.Errorf("failed to encode settings: %w", err) + } + + return nil + }, + Args: cobra.ExactArgs(0), + Example: `policy settings --enabled=true`, + } + + cmd.Flags().StringVar(&ownerID, "owner-id", "", "the id of the policy's owner") + cmd.Flags().StringVar(&context, "context", "config", "policy context for decision") + cmd.Flags().BoolVar(&enabled, "enabled", false, "enable/disable policy decision evaluation in build pipeline") + if err := cmd.MarkFlagRequired("owner-id"); err != nil { + panic(err) + } + + return cmd + }() + cmd.AddCommand(push) cmd.AddCommand(diff) cmd.AddCommand(fetch) cmd.AddCommand(logs) cmd.AddCommand(decide) cmd.AddCommand(eval) + cmd.AddCommand(settings) return cmd } diff --git a/cmd/policy/policy_test.go b/cmd/policy/policy_test.go index 8ca131660..15fa423be 100644 --- a/cmd/policy/policy_test.go +++ b/cmd/policy/policy_test.go @@ -874,6 +874,128 @@ func TestRawOPAEvaluationCommand(t *testing.T) { } } +func TestGetSetSettings(t *testing.T) { + testcases := []struct { + Name string + Args []string + ServerHandler http.HandlerFunc + ExpectedOutput string + ExpectedErr string + }{ + { + Name: "requires owner-id", + Args: []string{"settings"}, + ExpectedErr: "required flag(s) \"owner-id\" not set", + }, + { + Name: "gets error response", + Args: []string{"settings", "--owner-id", "ownerID", "--context", "someContext"}, + ExpectedErr: "failed to run settings : unexpected status-code: 403 - Forbidden", + ServerHandler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Method, "GET") + assert.Equal(t, r.URL.String(), "/api/v1/owner/ownerID/context/someContext/decision/settings") + w.WriteHeader(http.StatusForbidden) + _, err := w.Write([]byte(`{"error": "Forbidden"}`)) + assert.NilError(t, err) + }, + }, + { + Name: "successfully fetches settings", + Args: []string{"settings", "--owner-id", "462d67f8-b232-4da4-a7de-0c86dd667d3f"}, + ServerHandler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Method, "GET") + assert.Equal(t, r.URL.String(), "/api/v1/owner/462d67f8-b232-4da4-a7de-0c86dd667d3f/context/config/decision/settings") + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{"enabled": true}`)) + assert.NilError(t, err) + }, + ExpectedOutput: `{ + "enabled": true +} +`, + }, + { + Name: "successfully sets settings (--enabled)", + Args: []string{"settings", "--owner-id", "462d67f8-b232-4da4-a7de-0c86dd667d3f", "--enabled"}, + ServerHandler: func(w http.ResponseWriter, r *http.Request) { + var body map[string]interface{} + assert.Equal(t, r.Method, "PATCH") + assert.Equal(t, r.URL.String(), "/api/v1/owner/462d67f8-b232-4da4-a7de-0c86dd667d3f/context/config/decision/settings") + assert.NilError(t, json.NewDecoder(r.Body).Decode(&body)) + assert.DeepEqual(t, body, map[string]interface{}{"enabled": true}) + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{"enabled": true}`)) + assert.NilError(t, err) + }, + ExpectedOutput: `{ + "enabled": true +} +`, + }, + { + Name: "successfully sets settings (--enabled=true)", + Args: []string{"settings", "--owner-id", "462d67f8-b232-4da4-a7de-0c86dd667d3f", "--enabled=true"}, + ServerHandler: func(w http.ResponseWriter, r *http.Request) { + var body map[string]interface{} + assert.Equal(t, r.Method, "PATCH") + assert.Equal(t, r.URL.String(), "/api/v1/owner/462d67f8-b232-4da4-a7de-0c86dd667d3f/context/config/decision/settings") + assert.NilError(t, json.NewDecoder(r.Body).Decode(&body)) + assert.DeepEqual(t, body, map[string]interface{}{"enabled": true}) + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{"enabled": true}`)) + assert.NilError(t, err) + }, + ExpectedOutput: `{ + "enabled": true +} +`, + }, + { + Name: "successfully sets settings (--enabled=false)", + Args: []string{"settings", "--owner-id", "462d67f8-b232-4da4-a7de-0c86dd667d3f", "--enabled=false"}, + ServerHandler: func(w http.ResponseWriter, r *http.Request) { + var body map[string]interface{} + assert.Equal(t, r.Method, "PATCH") + assert.Equal(t, r.URL.String(), "/api/v1/owner/462d67f8-b232-4da4-a7de-0c86dd667d3f/context/config/decision/settings") + assert.NilError(t, json.NewDecoder(r.Body).Decode(&body)) + assert.DeepEqual(t, body, map[string]interface{}{"enabled": false}) + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{"enabled": false}`)) + assert.NilError(t, err) + }, + ExpectedOutput: `{ + "enabled": false +} +`, + }, + } + + for _, tc := range testcases { + t.Run(tc.Name, func(t *testing.T) { + if tc.ServerHandler == nil { + tc.ServerHandler = func(w http.ResponseWriter, r *http.Request) {} + } + + svr := httptest.NewServer(tc.ServerHandler) + defer svr.Close() + + cmd, stdout, _ := makeCMD() + + cmd.SetArgs(append(tc.Args, "--policy-base-url", svr.URL)) + + err := cmd.Execute() + if tc.ExpectedErr == "" { + assert.NilError(t, err) + } else { + assert.Error(t, err, tc.ExpectedErr) + return + } + + assert.Equal(t, stdout.String(), tc.ExpectedOutput) + }) + } +} + func makeCMD() (*cobra.Command, *bytes.Buffer, *bytes.Buffer) { config := &settings.Config{Token: "testtoken", HTTPClient: http.DefaultClient} cmd := NewCommand(config, nil) diff --git a/cmd/policy/testdata/policy-expected-usage.txt b/cmd/policy/testdata/policy-expected-usage.txt index bbab15737..bbdc184d8 100644 --- a/cmd/policy/testdata/policy-expected-usage.txt +++ b/cmd/policy/testdata/policy-expected-usage.txt @@ -8,6 +8,7 @@ Available Commands: fetch Fetch policy bundle (or a single policy) logs Get policy decision logs / Get decision log (or policy bundle) by decision ID push push policy bundle + settings get/set policy decision settings (To read settings: run command without any settings flags) Flags: --policy-base-url string base url for policy api (default "https://internal.circleci.com") diff --git a/cmd/policy/testdata/policy/settings-expected-usage.txt b/cmd/policy/testdata/policy/settings-expected-usage.txt new file mode 100644 index 000000000..b0e9b7764 --- /dev/null +++ b/cmd/policy/testdata/policy/settings-expected-usage.txt @@ -0,0 +1,13 @@ +Usage: + policy settings [flags] + +Examples: +policy settings --enabled=true + +Flags: + --context string policy context for decision (default "config") + --enabled enable/disable policy decision evaluation in build pipeline + --owner-id string the id of the policy's owner + +Global Flags: + --policy-base-url string base url for policy api (default "https://internal.circleci.com") From ad724f0038322b9b311ce4ca0e8d4b9620b1b3ab Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Wed, 5 Oct 2022 10:19:48 +0100 Subject: [PATCH 10/84] Migrates away from API-Service and calls orb-service directly --- .circleci/config.yml | 6 +- Dockerfile | 2 +- api/api.go | 120 ----- api/rest/client.go | 38 +- cmd/config.go | 69 ++- cmd/config_test.go | 467 +++--------------- cmd/root.go | 14 +- config/config.go | 114 +++++ .../deprecated_images.go | 7 +- go.mod | 2 +- local/local.go | 13 +- settings/settings.go | 1 + 12 files changed, 307 insertions(+), 546 deletions(-) create mode 100644 config/config.go rename cmd/deprecated-images.go => config/deprecated_images.go (93%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1d7c27638..3f8297e46 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ orbs: executors: go: docker: - - image: cimg/go:1.18 + - image: cimg/go:1.19 environment: CGO_ENABLED: 0 mac: @@ -105,8 +105,8 @@ jobs: - checkout - run: | brew update - brew install go@1.18 - echo 'export PATH="/usr/local/opt/go@1.18/bin:$PATH"' >> ~/.bash_profile + brew install go@1.19 + echo 'export PATH="/usr/local/opt/go@1.19/bin:$PATH"' >> ~/.bash_profile - gomod - run: make test build: diff --git a/Dockerfile b/Dockerfile index c94a4659f..0d6d09f17 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM cimg/go:1.18.3 +FROM cimg/go:1.19.1 ENV CIRCLECI_CLI_SKIP_UPDATE_CHECK true diff --git a/api/api.go b/api/api.go index 674ae112c..eabbc5ef0 100644 --- a/api/api.go +++ b/api/api.go @@ -11,7 +11,6 @@ import ( "strings" "github.com/CircleCI-Public/circleci-cli/api/graphql" - "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/references" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/Masterminds/semver" @@ -513,125 +512,6 @@ func WhoamiQuery(cl *graphql.Client) (*WhoamiResponse, error) { return &response, nil } -// ConfigQueryLegacy calls the GQL API to validate and process config with the legacy orgSlug -func ConfigQueryLegacy(cl *graphql.Client, configPath string, orgSlug string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { - var response BuildConfigResponse - var query string - config, err := loadYaml(configPath) - if err != nil { - return nil, err - } - // GraphQL isn't forwards-compatible, so we are unusually selective here about - // passing only non-empty fields on to the API, to minimize user impact if the - // backend is out of date. - var fieldAddendums string - if orgSlug != "" { - fieldAddendums += ", orgSlug: $orgSlug" - } - if len(params) > 0 { - fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" - } - query = fmt.Sprintf( - `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }`, - fieldAddendums) - - request := graphql.NewRequest(query) - request.Var("config", config) - - if values != nil { - request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) - } - if params != nil { - pipelineParameters, err := json.Marshal(params) - if err != nil { - return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) - } - request.Var("pipelineParametersJson", string(pipelineParameters)) - } - - if orgSlug != "" { - request.Var("orgSlug", orgSlug) - } - - request.SetToken(cl.Token) - - err = cl.Run(request, &response) - if err != nil { - return nil, errors.Wrap(err, "Unable to validate config") - } - if len(response.BuildConfig.ConfigResponse.Errors) > 0 { - return nil, &response.BuildConfig.ConfigResponse.Errors - } - - return &response.BuildConfig.ConfigResponse, nil -} - -// ConfigQuery calls the GQL API to validate and process config with the org id -func ConfigQuery(cl *graphql.Client, configPath string, orgId string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { - var response BuildConfigResponse - var query string - config, err := loadYaml(configPath) - if err != nil { - return nil, err - } - // GraphQL isn't forwards-compatible, so we are unusually selective here about - // passing only non-empty fields on to the API, to minimize user impact if the - // backend is out of date. - var fieldAddendums string - if orgId != "" { - fieldAddendums += ", orgId: $orgId" - } - if len(params) > 0 { - fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" - } - query = fmt.Sprintf( - `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }`, - fieldAddendums) - - request := graphql.NewRequest(query) - request.Var("config", config) - - if values != nil { - request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) - } - if params != nil { - pipelineParameters, err := json.Marshal(params) - if err != nil { - return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) - } - request.Var("pipelineParametersJson", string(pipelineParameters)) - } - - if orgId != "" { - request.Var("orgId", orgId) - } - request.SetToken(cl.Token) - - err = cl.Run(request, &response) - if err != nil { - return nil, errors.Wrap(err, "Unable to validate config") - } - if len(response.BuildConfig.ConfigResponse.Errors) > 0 { - return nil, &response.BuildConfig.ConfigResponse.Errors - } - - return &response.BuildConfig.ConfigResponse, nil -} - // OrbQuery validated and processes an orb. func OrbQuery(cl *graphql.Client, configPath string) (*ConfigResponse, error) { var response OrbConfigResponse diff --git a/api/rest/client.go b/api/rest/client.go index d745b887e..6fe2bed8c 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -18,6 +18,7 @@ import ( type Client struct { baseURL *url.URL + apiURL *url.URL circleToken string client *http.Client } @@ -29,13 +30,14 @@ func New(host string, config *settings.Config) *Client { endpoint += "/" } - u, _ := url.Parse(host) - + baseURL, _ := url.Parse(host) + apiURL, _ := url.Parse(config.ConfigAPIHost) client := config.HTTPClient client.Timeout = 10 * time.Second return &Client{ - baseURL: u.ResolveReference(&url.URL{Path: endpoint}), + apiURL: apiURL.ResolveReference(&url.URL{Path: endpoint}), + baseURL: baseURL.ResolveReference(&url.URL{Path: endpoint}), circleToken: config.Token, client: client, } @@ -70,6 +72,36 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req return req, nil } +// NewAPIRequest - similar to NewRequest except it uses the apiURL as the base URL. +func (c *Client) NewAPIRequest(method string, u *url.URL, payload interface{}) (req *http.Request, err error) { + var r io.Reader + if payload != nil { + buf := &bytes.Buffer{} + r = buf + err = json.NewEncoder(buf).Encode(payload) + if err != nil { + return nil, err + } + } + + req, err = http.NewRequest(method, c.apiURL.ResolveReference(u).String(), r) + if err != nil { + return nil, err + } + + req.Header.Set("Circle-Token", c.circleToken) + req.Header.Set("Accept", "application/json") + req.Header.Set("User-Agent", version.UserAgent()) + commandStr := header.GetCommandStr() + if commandStr != "" { + req.Header.Set("Circleci-Cli-Command", commandStr) + } + if payload != nil { + req.Header.Set("Content-Type", "application/json") + } + return req, nil +} + func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int, err error) { httpResp, err := c.client.Do(req) if err != nil { diff --git a/cmd/config.go b/cmd/config.go index 40344a079..52d05683c 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -3,10 +3,11 @@ package cmd import ( "fmt" "io/ioutil" + "net/url" "strings" - "github.com/CircleCI-Public/circleci-cli/api" - "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/config" "github.com/CircleCI-Public/circleci-cli/filetree" "github.com/CircleCI-Public/circleci-cli/local" "github.com/CircleCI-Public/circleci-cli/pipeline" @@ -20,7 +21,7 @@ import ( type configOptions struct { cfg *settings.Config - cl *graphql.Client + rest *rest.Client args []string } @@ -63,7 +64,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Check that the config file is well formed.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) + opts.rest = rest.New(config.Host, config) }, RunE: func(cmd *cobra.Command, _ []string) error { return validateConfig(opts, cmd.Flags()) @@ -85,7 +86,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Validate config and display expanded configuration.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) + opts.rest = rest.New(config.Host, config) }, RunE: func(cmd *cobra.Command, _ []string) error { return processConfig(opts, cmd.Flags()) @@ -125,7 +126,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { // The arg is actually optional, in order to support compatibility with the --path flag. func validateConfig(opts configOptions, flags *pflag.FlagSet) error { var err error - var response *api.ConfigResponse + var response *config.ConfigResponse path := local.DefaultConfigPath // First, set the path to configPath set by --path flag for compatibility if configPath != "" { @@ -138,15 +139,22 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { } //if no orgId provided use org slug - orgID, _ := flags.GetString("org-id") + var orgID string + orgID, _ = flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = api.ConfigQuery(opts.cl, path, orgID, nil, pipeline.LocalPipelineValues()) + orgID, _ = flags.GetString("org-id") + response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - response, err = api.ConfigQueryLegacy(opts.cl, path, orgSlug, nil, pipeline.LocalPipelineValues()) + orgs, err := GetOrgCollaborations(opts.rest) + if err != nil { + fmt.Println(err.Error()) + } + orgID = GetOrgIdFromSlug(orgSlug, orgs) + response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } @@ -156,7 +164,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { // link here to blog post when available // returns an error if a deprecated image is used if !ignoreDeprecatedImages { - err := deprecatedImageCheck(response) + err := config.DeprecatedImageCheck(response) if err != nil { return err } @@ -173,7 +181,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { func processConfig(opts configOptions, flags *pflag.FlagSet) error { paramsYaml, _ := flags.GetString("pipeline-parameters") - var response *api.ConfigResponse + var response *config.ConfigResponse var params pipeline.Parameters var err error @@ -194,13 +202,18 @@ func processConfig(opts configOptions, flags *pflag.FlagSet) error { //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = api.ConfigQuery(opts.cl, opts.args[0], orgID, params, pipeline.LocalPipelineValues()) + response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - response, err = api.ConfigQueryLegacy(opts.cl, opts.args[0], orgSlug, params, pipeline.LocalPipelineValues()) + orgs, err := GetOrgCollaborations(opts.rest) + if err != nil { + fmt.Println(err.Error()) + } + orgID = GetOrgIdFromSlug(orgSlug, orgs) + response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, pipeline.LocalPipelineValues()) if err != nil { return err } @@ -227,3 +240,33 @@ func packConfig(opts configOptions) error { func migrateConfig(opts configOptions) error { return proxy.Exec([]string{"config", "migrate"}, opts.args) } + +type CollaborationResult struct { + VcsTye string `json:"vcs_type"` + OrgSlug string `json:"slug"` + OrgName string `json:"name"` + OrgId string `json:"id"` + AvatarUrl string `json:"avatar_url"` +} + +// GetOrgCollaborations - fetches all the collaborations for a given user. +func GetOrgCollaborations(client *rest.Client) ([]CollaborationResult, error) { + req, err := client.NewRequest("GET", &url.URL{Path: "me/collaborations"}, nil) + if err != nil { + return nil, err + } + + var resp []CollaborationResult + _, err = client.DoRequest(req, &resp) + return resp, err +} + +// GetOrgIdFromSlug - converts a slug into an orgID. +func GetOrgIdFromSlug(slug string, collaborations []CollaborationResult) string { + for _, v := range collaborations { + if v.OrgSlug == slug { + return v.OrgId + } + } + return "" +} diff --git a/cmd/config_test.go b/cmd/config_test.go index e03177f8d..6c622f564 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -1,20 +1,13 @@ package cmd_test import ( - "encoding/json" "fmt" - "io" - "net/http" "os/exec" "path/filepath" - "time" - "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/clitest" - "github.com/CircleCI-Public/circleci-cli/pipeline" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/onsi/gomega/gbytes" "github.com/onsi/gomega/gexec" "gotest.tools/v3/golden" ) @@ -25,7 +18,7 @@ var _ = Describe("Config", func() { command *exec.Cmd results []byte tempSettings *clitest.TempSettings - token string = "testtoken" + // token string = "testtoken" ) BeforeEach(func() { @@ -36,395 +29,95 @@ var _ = Describe("Config", func() { tempSettings.Close() }) - Describe("a .circleci folder with config.yml and local orbs folder containing the hugo orb", func() { - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/hugo-pack/.circleci") - results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) - }) - - It("pack all YAML contents as expected", func() { - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) - }) - - Describe("local orbs folder with mixed inline and local commands, jobs, etc", func() { - BeforeEach(func() { - var path string = "nested-orbs-and-local-commands-etc" - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - filepath.Join("testdata", path, "test")) - results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) - }) - - It("pack all YAML contents as expected", func() { - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) + It("packs all YAML contents as expected", func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/hugo-pack/.circleci") + results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) }) - Describe("an orb containing local executors and commands in folder", func() { - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/myorb/test") - - results = golden.Get(GinkgoT(), filepath.FromSlash("myorb/result.yml")) - }) - - It("pack all YAML contents as expected", func() { - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) + It("given a .circleci folder with config.ymla nd local orb, packs all YAML contents as expected", func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/hugo-pack/.circleci") + results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) }) - Describe("with a large nested config including rails orb", func() { - BeforeEach(func() { - var path string = "test-with-large-nested-rails-orb" - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - filepath.Join("testdata", path, "test")) - results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) - }) - - It("pack all YAML contents as expected", func() { - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) - }) - - Context("config is a list and not a map", func() { - var config *clitest.TmpFile - - BeforeEach(func() { - config = clitest.OpenTmpFile(filepath.Join(tempSettings.Home, "myorb"), "config.yaml") - - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - config.RootDir, - ) - }) - - AfterEach(func() { - config.Close() - }) - - It("prints an error about invalid YAML", func() { - config.Write([]byte(`[]`)) - - expected := fmt.Sprintf("Error: Failed trying to marshal the tree to YAML : expected a map, got a `[]interface {}` which is not supported at this time for \"%s\"\n", config.Path) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - - Expect(err).ShouldNot(HaveOccurred()) - - stderr := session.Wait().Err.Contents() - Expect(string(stderr)).To(Equal(expected)) - Eventually(session).Should(clitest.ShouldFail()) - }) - }) - - Describe("validating configs", func() { - config := "version: 2.1" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "validate", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.Variables["config"] = config - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns an error when validating a config", func() { - expResp := `{ - "buildConfig": { - "errors": [ - {"message": "error1"} - ] - } - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: error1")) - Eventually(session).Should(clitest.ShouldFail()) - }) - - It("returns successfully when validating a config", func() { - expResp := `{ - "buildConfig": {} - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) - Eventually(session).Should(gexec.Exit(0)) - }) + It("given a local orbs folder with mixed inline and local commands, jobs, etc, packs all YAML contents as expected", func() { + var path string = "nested-orbs-and-local-commands-etc" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) }) - Describe("validating configs with pipeline parameters", func() { - config := "version: 2.1" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "process", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "--pipeline-parameters", `{"foo": "test", "bar": true, "baz": 10}`, - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues, pipelineParametersJson: $pipelineParametersJson) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.Variables["config"] = config - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - pipelineParams, err := json.Marshal(pipeline.Parameters{ - "foo": "test", - "bar": true, - "baz": 10, - }) - Expect(err).ToNot(HaveOccurred()) - r.Variables["pipelineParametersJson"] = string(pipelineParams) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns successfully when validating a config", func() { - expResp := `{ - "buildConfig": {} - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session).Should(gexec.Exit(0)) - }) + It("packs successfully given an orb containing local executors and commands in folder", func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/myorb/test") + + results = golden.Get(GinkgoT(), filepath.FromSlash("myorb/result.yml")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) }) - Describe("validating configs with private orbs", func() { - config := "version: 2.1" - orgId := "bb604b45-b6b0-4b81-ad80-796f15eddf87" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "validate", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "--org-id", orgId, - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgId: $orgId) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.Variables["config"] = config - r.Variables["orgId"] = orgId - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns an error when validating a config with a private orb", func() { - expResp := `{ - "buildConfig": { - "errors": [ - {"message": "permission denied"} - ] - } - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) - Eventually(session).Should(clitest.ShouldFail()) - }) + It("packs as expected given a large nested config including rails orbs", func() { + var path string = "test-with-large-nested-rails-orb" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) }) - Describe("validating configs with private orbs Legacy", func() { - config := "version: 2.1" - orgSlug := "circleci" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "validate", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "--org-slug", orgSlug, - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgSlug: $orgSlug) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.Variables["config"] = config - r.Variables["orgSlug"] = orgSlug - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns an error when validating a config with a private orb", func() { - expResp := `{ - "buildConfig": { - "errors": [ - {"message": "permission denied"} - ] - } - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) - Eventually(session).Should(clitest.ShouldFail()) - }) - - It("returns successfully when validating a config with private orbs", func() { - expResp := `{ - "buildConfig": {} - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) - Eventually(session).Should(gexec.Exit(0)) - }) + It("prints an error given a config which is a list and not a map", func() { + config := clitest.OpenTmpFile(filepath.Join(tempSettings.Home, "myorb"), "config.yaml") + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + config.RootDir, + ) + config.Write([]byte(`[]`)) + + expected := fmt.Sprintf("Error: Failed trying to marshal the tree to YAML : expected a map, got a `[]interface {}` which is not supported at this time for \"%s\"\n", config.Path) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + stderr := session.Wait().Err.Contents() + Expect(string(stderr)).To(Equal(expected)) + Eventually(session).Should(clitest.ShouldFail()) + config.Close() }) }) }) diff --git a/cmd/root.go b/cmd/root.go index f47ff96ea..a51f10ee4 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -20,6 +20,7 @@ import ( var defaultEndpoint = "graphql-unstable" var defaultHost = "https://circleci.com" +var defaultConfigApiHost = "https://api.circleci.com" var defaultRestEndpoint = "api/v2" var trueString = "true" @@ -96,12 +97,13 @@ Use "{{.CommandPath}} [command] --help" for more information about a command.{{e // MakeCommands creates the top level commands func MakeCommands() *cobra.Command { rootOptions = &settings.Config{ - Debug: false, - Token: "", - Host: defaultHost, - RestEndpoint: defaultRestEndpoint, - Endpoint: defaultEndpoint, - GitHubAPI: "https://api.github.com/", + Debug: false, + Token: "", + Host: defaultHost, + ConfigAPIHost: defaultConfigApiHost, + RestEndpoint: defaultRestEndpoint, + Endpoint: defaultEndpoint, + GitHubAPI: "https://api.github.com/", } if err := rootOptions.Load(); err != nil { diff --git a/config/config.go b/config/config.go new file mode 100644 index 000000000..cd3ec9d8d --- /dev/null +++ b/config/config.go @@ -0,0 +1,114 @@ +package config + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/url" + "os" + + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/pipeline" + "github.com/pkg/errors" +) + +type ConfigError struct { + Message string `json:"message"` +} + +type ConfigResponse struct { + Valid bool `json:"valid"` + SourceYaml string `json:"source-yaml"` + OutputYaml string `json:"output-yaml"` + Errors []ConfigError `json:"errors"` +} + +type CompileConfigRequest struct { + ConfigYaml string `json:"config_yaml"` + Options Options `json:"options"` +} + +type Options struct { + OwnerID string `json:"owner_id,omitempty"` + PipelineParameters string `json:"pipeline_parameters,omitempty"` + PipelineValues map[string]string `json:"pipeline_values,omitempty"` +} + +// #nosec +func loadYaml(path string) (string, error) { + var err error + var config []byte + if path == "-" { + config, err = ioutil.ReadAll(os.Stdin) + } else { + config, err = ioutil.ReadFile(path) + } + + if err != nil { + return "", errors.Wrapf(err, "Could not load config file at %s", path) + } + + return string(config), nil +} + +// ConfigQuery - attempts to compile or validate a given config file with the +// passed in params/values. +// If the orgID is passed in, the config-compilation with private orbs should work. +func ConfigQuery( + rest *rest.Client, + configPath string, + orgID string, + params pipeline.Parameters, + values pipeline.Values, +) (*ConfigResponse, error) { + + configString, err := loadYaml(configPath) + if err != nil { + return nil, err + } + + compileRequest := CompileConfigRequest{ + ConfigYaml: configString, + Options: Options{ + PipelineValues: values, + }, + } + + if orgID != "" { + compileRequest.Options.OwnerID = orgID + } + + if len(params) >= 1 { + pipelineParamsString, err := json.Marshal(params) + if err != nil { + return nil, err + } + compileRequest.Options.PipelineParameters = string(pipelineParamsString) + } + + req, err := rest.NewAPIRequest( + "POST", + &url.URL{ + Path: "compile-config-with-defaults", + }, + compileRequest, + ) + if err != nil { + return nil, err + } + + configCompilationResp := &ConfigResponse{} + statusCode, err := rest.DoRequest(req, configCompilationResp) + if err != nil { + return nil, err + } + if statusCode != 200 { + return nil, errors.New("non 200 status code") + } + + if len(configCompilationResp.Errors) > 0 { + return nil, errors.New(fmt.Sprintf("config compilation contains errors: %s", configCompilationResp.Errors)) + } + + return configCompilationResp, nil +} diff --git a/cmd/deprecated-images.go b/config/deprecated_images.go similarity index 93% rename from cmd/deprecated-images.go rename to config/deprecated_images.go index 607edfc88..1579af3de 100644 --- a/cmd/deprecated-images.go +++ b/config/deprecated_images.go @@ -1,8 +1,6 @@ -package cmd +package config import ( - "github.com/CircleCI-Public/circleci-cli/api" - "github.com/pkg/errors" "gopkg.in/yaml.v3" ) @@ -40,8 +38,7 @@ type processedConfig struct { } // Processes the config down to v2.0, then checks image used against the block list -func deprecatedImageCheck(response *api.ConfigResponse) error { - +func DeprecatedImageCheck(response *ConfigResponse) error { aConfig := processedConfig{} err := yaml.Unmarshal([]byte(response.OutputYaml), &aConfig) if err != nil { diff --git a/go.mod b/go.mod index d515fd50c..fc76cc3ef 100644 --- a/go.mod +++ b/go.mod @@ -90,4 +90,4 @@ require ( // fix vulnerability: CVE-2020-15114 in etcd v3.3.10+incompatible replace github.com/coreos/etcd => github.com/coreos/etcd v3.3.24+incompatible -go 1.18 +go 1.19 diff --git a/local/local.go b/local/local.go index e139be8f4..0177ba7a1 100644 --- a/local/local.go +++ b/local/local.go @@ -10,8 +10,8 @@ import ( "strings" "syscall" - "github.com/CircleCI-Public/circleci-cli/api" - "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/config" "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/pkg/errors" @@ -24,21 +24,20 @@ const DefaultConfigPath = ".circleci/config.yml" func Execute(flags *pflag.FlagSet, cfg *settings.Config) error { var err error - var configResponse *api.ConfigResponse - cl := graphql.NewClient(cfg.HTTPClient, cfg.Host, cfg.Endpoint, cfg.Token, cfg.Debug) - + var configResponse *config.ConfigResponse + restClient := rest.New(cfg.Host, cfg) processedArgs, configPath := buildAgentArguments(flags) //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - configResponse, err = api.ConfigQuery(cl, configPath, orgID, nil, pipeline.LocalPipelineValues()) + configResponse, err = config.ConfigQuery(restClient, configPath, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - configResponse, err = api.ConfigQueryLegacy(cl, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) + configResponse, err = config.ConfigQuery(restClient, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) if err != nil { return err } diff --git a/settings/settings.go b/settings/settings.go index 24bc07f33..1459725d6 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -35,6 +35,7 @@ type Config struct { GitHubAPI string `yaml:"-"` SkipUpdateCheck bool `yaml:"-"` OrbPublishing OrbPublishingInfo `yaml:"orb_publishing"` + ConfigAPIHost string `yaml:"-"` } type OrbPublishingInfo struct { From e7f5036747470773e71f5a6f0109ff830280a72b Mon Sep 17 00:00:00 2001 From: Ai Mi Bui Date: Wed, 5 Oct 2022 12:31:54 -0700 Subject: [PATCH 11/84] updated listNamespaceOrbs to include details flag --- api/api.go | 15 +++++++++++---- cmd/orb.go | 2 +- cmd/orb_import.go | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/api/api.go b/api/api.go index 674ae112c..ce10fff5a 100644 --- a/api/api.go +++ b/api/api.go @@ -1673,7 +1673,7 @@ query namespaceOrbs ($namespace: String, $after: String!) { // ListNamespaceOrbs queries the API to find all orbs belonging to the given // namespace. // Returns a collection of Orb objects containing their relevant data. -func ListNamespaceOrbs(cl *graphql.Client, namespace string, isPrivate bool) (*OrbsForListing, error) { +func ListNamespaceOrbs(cl *graphql.Client, namespace string, isPrivate, showDetails bool) (*OrbsForListing, error) { l := log.New(os.Stderr, "", 0) query := ` @@ -1685,9 +1685,15 @@ query namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType edges { cursor node { - versions { - source - version + versions ` + + if showDetails { + query+=`(count: 1){ source,` + } else { + query+=`{` + } + + query+=` version } name statistics { @@ -1705,6 +1711,7 @@ query namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType } } ` + var orbs OrbsForListing var result NamespaceOrbResponse currentCursor := "" diff --git a/cmd/orb.go b/cmd/orb.go index 2650a720e..435504563 100644 --- a/cmd/orb.go +++ b/cmd/orb.go @@ -636,7 +636,7 @@ func listOrbs(opts orbOptions) error { func listNamespaceOrbs(opts orbOptions) error { namespace := opts.args[0] - orbs, err := api.ListNamespaceOrbs(opts.cl, namespace, opts.private) + orbs, err := api.ListNamespaceOrbs(opts.cl, namespace, opts.private, opts.listDetails) if err != nil { return errors.Wrapf(err, "Failed to list orbs in namespace `%s`", namespace) } diff --git a/cmd/orb_import.go b/cmd/orb_import.go index 5ddb18168..fca5512b9 100644 --- a/cmd/orb_import.go +++ b/cmd/orb_import.go @@ -216,7 +216,7 @@ func deleteNamespace(nsOpts namespaceOptions) error { // Currently, private orbs will not be included in the list of orbs to be deleted. // This can be changed once we have 'listBothPublicAndPrivateOrbs' functionality. - orbs, err := api.ListNamespaceOrbs(nsOpts.cl, namespaceArg, false) + orbs, err := api.ListNamespaceOrbs(nsOpts.cl, namespaceArg, false, false) if err != nil { return fmt.Errorf("unable to list orbs: %s", err.Error()) } From 989a1af1d6183c7f5d3f69e7e5dfe2945dae219e Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Thu, 6 Oct 2022 14:34:55 +0100 Subject: [PATCH 12/84] Revert "Migrates away from API-Service and calls orb-service directly for config compilation and validation" --- .circleci/config.yml | 6 +- Dockerfile | 2 +- api/api.go | 120 +++++ api/rest/client.go | 38 +- cmd/config.go | 69 +-- cmd/config_test.go | 467 +++++++++++++++--- .../deprecated-images.go | 7 +- cmd/root.go | 14 +- config/config.go | 114 ----- go.mod | 2 +- local/local.go | 13 +- settings/settings.go | 1 - 12 files changed, 546 insertions(+), 307 deletions(-) rename config/deprecated_images.go => cmd/deprecated-images.go (93%) delete mode 100644 config/config.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 3f8297e46..1d7c27638 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ orbs: executors: go: docker: - - image: cimg/go:1.19 + - image: cimg/go:1.18 environment: CGO_ENABLED: 0 mac: @@ -105,8 +105,8 @@ jobs: - checkout - run: | brew update - brew install go@1.19 - echo 'export PATH="/usr/local/opt/go@1.19/bin:$PATH"' >> ~/.bash_profile + brew install go@1.18 + echo 'export PATH="/usr/local/opt/go@1.18/bin:$PATH"' >> ~/.bash_profile - gomod - run: make test build: diff --git a/Dockerfile b/Dockerfile index 0d6d09f17..c94a4659f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM cimg/go:1.19.1 +FROM cimg/go:1.18.3 ENV CIRCLECI_CLI_SKIP_UPDATE_CHECK true diff --git a/api/api.go b/api/api.go index eabbc5ef0..674ae112c 100644 --- a/api/api.go +++ b/api/api.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/references" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/Masterminds/semver" @@ -512,6 +513,125 @@ func WhoamiQuery(cl *graphql.Client) (*WhoamiResponse, error) { return &response, nil } +// ConfigQueryLegacy calls the GQL API to validate and process config with the legacy orgSlug +func ConfigQueryLegacy(cl *graphql.Client, configPath string, orgSlug string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { + var response BuildConfigResponse + var query string + config, err := loadYaml(configPath) + if err != nil { + return nil, err + } + // GraphQL isn't forwards-compatible, so we are unusually selective here about + // passing only non-empty fields on to the API, to minimize user impact if the + // backend is out of date. + var fieldAddendums string + if orgSlug != "" { + fieldAddendums += ", orgSlug: $orgSlug" + } + if len(params) > 0 { + fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" + } + query = fmt.Sprintf( + `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }`, + fieldAddendums) + + request := graphql.NewRequest(query) + request.Var("config", config) + + if values != nil { + request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) + } + if params != nil { + pipelineParameters, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) + } + request.Var("pipelineParametersJson", string(pipelineParameters)) + } + + if orgSlug != "" { + request.Var("orgSlug", orgSlug) + } + + request.SetToken(cl.Token) + + err = cl.Run(request, &response) + if err != nil { + return nil, errors.Wrap(err, "Unable to validate config") + } + if len(response.BuildConfig.ConfigResponse.Errors) > 0 { + return nil, &response.BuildConfig.ConfigResponse.Errors + } + + return &response.BuildConfig.ConfigResponse, nil +} + +// ConfigQuery calls the GQL API to validate and process config with the org id +func ConfigQuery(cl *graphql.Client, configPath string, orgId string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { + var response BuildConfigResponse + var query string + config, err := loadYaml(configPath) + if err != nil { + return nil, err + } + // GraphQL isn't forwards-compatible, so we are unusually selective here about + // passing only non-empty fields on to the API, to minimize user impact if the + // backend is out of date. + var fieldAddendums string + if orgId != "" { + fieldAddendums += ", orgId: $orgId" + } + if len(params) > 0 { + fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" + } + query = fmt.Sprintf( + `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }`, + fieldAddendums) + + request := graphql.NewRequest(query) + request.Var("config", config) + + if values != nil { + request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) + } + if params != nil { + pipelineParameters, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) + } + request.Var("pipelineParametersJson", string(pipelineParameters)) + } + + if orgId != "" { + request.Var("orgId", orgId) + } + request.SetToken(cl.Token) + + err = cl.Run(request, &response) + if err != nil { + return nil, errors.Wrap(err, "Unable to validate config") + } + if len(response.BuildConfig.ConfigResponse.Errors) > 0 { + return nil, &response.BuildConfig.ConfigResponse.Errors + } + + return &response.BuildConfig.ConfigResponse, nil +} + // OrbQuery validated and processes an orb. func OrbQuery(cl *graphql.Client, configPath string) (*ConfigResponse, error) { var response OrbConfigResponse diff --git a/api/rest/client.go b/api/rest/client.go index 6fe2bed8c..d745b887e 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -18,7 +18,6 @@ import ( type Client struct { baseURL *url.URL - apiURL *url.URL circleToken string client *http.Client } @@ -30,14 +29,13 @@ func New(host string, config *settings.Config) *Client { endpoint += "/" } - baseURL, _ := url.Parse(host) - apiURL, _ := url.Parse(config.ConfigAPIHost) + u, _ := url.Parse(host) + client := config.HTTPClient client.Timeout = 10 * time.Second return &Client{ - apiURL: apiURL.ResolveReference(&url.URL{Path: endpoint}), - baseURL: baseURL.ResolveReference(&url.URL{Path: endpoint}), + baseURL: u.ResolveReference(&url.URL{Path: endpoint}), circleToken: config.Token, client: client, } @@ -72,36 +70,6 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req return req, nil } -// NewAPIRequest - similar to NewRequest except it uses the apiURL as the base URL. -func (c *Client) NewAPIRequest(method string, u *url.URL, payload interface{}) (req *http.Request, err error) { - var r io.Reader - if payload != nil { - buf := &bytes.Buffer{} - r = buf - err = json.NewEncoder(buf).Encode(payload) - if err != nil { - return nil, err - } - } - - req, err = http.NewRequest(method, c.apiURL.ResolveReference(u).String(), r) - if err != nil { - return nil, err - } - - req.Header.Set("Circle-Token", c.circleToken) - req.Header.Set("Accept", "application/json") - req.Header.Set("User-Agent", version.UserAgent()) - commandStr := header.GetCommandStr() - if commandStr != "" { - req.Header.Set("Circleci-Cli-Command", commandStr) - } - if payload != nil { - req.Header.Set("Content-Type", "application/json") - } - return req, nil -} - func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int, err error) { httpResp, err := c.client.Do(req) if err != nil { diff --git a/cmd/config.go b/cmd/config.go index 52d05683c..40344a079 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -3,11 +3,10 @@ package cmd import ( "fmt" "io/ioutil" - "net/url" "strings" - "github.com/CircleCI-Public/circleci-cli/api/rest" - "github.com/CircleCI-Public/circleci-cli/config" + "github.com/CircleCI-Public/circleci-cli/api" + "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/filetree" "github.com/CircleCI-Public/circleci-cli/local" "github.com/CircleCI-Public/circleci-cli/pipeline" @@ -21,7 +20,7 @@ import ( type configOptions struct { cfg *settings.Config - rest *rest.Client + cl *graphql.Client args []string } @@ -64,7 +63,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Check that the config file is well formed.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.rest = rest.New(config.Host, config) + opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) }, RunE: func(cmd *cobra.Command, _ []string) error { return validateConfig(opts, cmd.Flags()) @@ -86,7 +85,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Validate config and display expanded configuration.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.rest = rest.New(config.Host, config) + opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) }, RunE: func(cmd *cobra.Command, _ []string) error { return processConfig(opts, cmd.Flags()) @@ -126,7 +125,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { // The arg is actually optional, in order to support compatibility with the --path flag. func validateConfig(opts configOptions, flags *pflag.FlagSet) error { var err error - var response *config.ConfigResponse + var response *api.ConfigResponse path := local.DefaultConfigPath // First, set the path to configPath set by --path flag for compatibility if configPath != "" { @@ -139,22 +138,15 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { } //if no orgId provided use org slug - var orgID string - orgID, _ = flags.GetString("org-id") + orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - orgID, _ = flags.GetString("org-id") - response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) + response, err = api.ConfigQuery(opts.cl, path, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - orgs, err := GetOrgCollaborations(opts.rest) - if err != nil { - fmt.Println(err.Error()) - } - orgID = GetOrgIdFromSlug(orgSlug, orgs) - response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) + response, err = api.ConfigQueryLegacy(opts.cl, path, orgSlug, nil, pipeline.LocalPipelineValues()) if err != nil { return err } @@ -164,7 +156,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { // link here to blog post when available // returns an error if a deprecated image is used if !ignoreDeprecatedImages { - err := config.DeprecatedImageCheck(response) + err := deprecatedImageCheck(response) if err != nil { return err } @@ -181,7 +173,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { func processConfig(opts configOptions, flags *pflag.FlagSet) error { paramsYaml, _ := flags.GetString("pipeline-parameters") - var response *config.ConfigResponse + var response *api.ConfigResponse var params pipeline.Parameters var err error @@ -202,18 +194,13 @@ func processConfig(opts configOptions, flags *pflag.FlagSet) error { //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, pipeline.LocalPipelineValues()) + response, err = api.ConfigQuery(opts.cl, opts.args[0], orgID, params, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - orgs, err := GetOrgCollaborations(opts.rest) - if err != nil { - fmt.Println(err.Error()) - } - orgID = GetOrgIdFromSlug(orgSlug, orgs) - response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, pipeline.LocalPipelineValues()) + response, err = api.ConfigQueryLegacy(opts.cl, opts.args[0], orgSlug, params, pipeline.LocalPipelineValues()) if err != nil { return err } @@ -240,33 +227,3 @@ func packConfig(opts configOptions) error { func migrateConfig(opts configOptions) error { return proxy.Exec([]string{"config", "migrate"}, opts.args) } - -type CollaborationResult struct { - VcsTye string `json:"vcs_type"` - OrgSlug string `json:"slug"` - OrgName string `json:"name"` - OrgId string `json:"id"` - AvatarUrl string `json:"avatar_url"` -} - -// GetOrgCollaborations - fetches all the collaborations for a given user. -func GetOrgCollaborations(client *rest.Client) ([]CollaborationResult, error) { - req, err := client.NewRequest("GET", &url.URL{Path: "me/collaborations"}, nil) - if err != nil { - return nil, err - } - - var resp []CollaborationResult - _, err = client.DoRequest(req, &resp) - return resp, err -} - -// GetOrgIdFromSlug - converts a slug into an orgID. -func GetOrgIdFromSlug(slug string, collaborations []CollaborationResult) string { - for _, v := range collaborations { - if v.OrgSlug == slug { - return v.OrgId - } - } - return "" -} diff --git a/cmd/config_test.go b/cmd/config_test.go index 6c622f564..e03177f8d 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -1,13 +1,20 @@ package cmd_test import ( + "encoding/json" "fmt" + "io" + "net/http" "os/exec" "path/filepath" + "time" + "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/clitest" + "github.com/CircleCI-Public/circleci-cli/pipeline" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" "github.com/onsi/gomega/gexec" "gotest.tools/v3/golden" ) @@ -18,7 +25,7 @@ var _ = Describe("Config", func() { command *exec.Cmd results []byte tempSettings *clitest.TempSettings - // token string = "testtoken" + token string = "testtoken" ) BeforeEach(func() { @@ -29,95 +36,395 @@ var _ = Describe("Config", func() { tempSettings.Close() }) - It("packs all YAML contents as expected", func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/hugo-pack/.circleci") - results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) + Describe("a .circleci folder with config.yml and local orbs folder containing the hugo orb", func() { + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/hugo-pack/.circleci") + results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) + }) + + It("pack all YAML contents as expected", func() { + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + }) + + Describe("local orbs folder with mixed inline and local commands, jobs, etc", func() { + BeforeEach(func() { + var path string = "nested-orbs-and-local-commands-etc" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + }) + + It("pack all YAML contents as expected", func() { + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) }) - It("given a .circleci folder with config.ymla nd local orb, packs all YAML contents as expected", func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/hugo-pack/.circleci") - results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) + Describe("an orb containing local executors and commands in folder", func() { + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/myorb/test") + + results = golden.Get(GinkgoT(), filepath.FromSlash("myorb/result.yml")) + }) + + It("pack all YAML contents as expected", func() { + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) }) - It("given a local orbs folder with mixed inline and local commands, jobs, etc, packs all YAML contents as expected", func() { - var path string = "nested-orbs-and-local-commands-etc" - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - filepath.Join("testdata", path, "test")) - results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) + Describe("with a large nested config including rails orb", func() { + BeforeEach(func() { + var path string = "test-with-large-nested-rails-orb" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + }) + + It("pack all YAML contents as expected", func() { + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + }) + + Context("config is a list and not a map", func() { + var config *clitest.TmpFile + + BeforeEach(func() { + config = clitest.OpenTmpFile(filepath.Join(tempSettings.Home, "myorb"), "config.yaml") + + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + config.RootDir, + ) + }) + + AfterEach(func() { + config.Close() + }) + + It("prints an error about invalid YAML", func() { + config.Write([]byte(`[]`)) + + expected := fmt.Sprintf("Error: Failed trying to marshal the tree to YAML : expected a map, got a `[]interface {}` which is not supported at this time for \"%s\"\n", config.Path) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + + Expect(err).ShouldNot(HaveOccurred()) + + stderr := session.Wait().Err.Contents() + Expect(string(stderr)).To(Equal(expected)) + Eventually(session).Should(clitest.ShouldFail()) + }) + }) + + Describe("validating configs", func() { + config := "version: 2.1" + var expReq string + + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "validate", + "--skip-update-check", + "--token", token, + "--host", tempSettings.TestServer.URL(), + "-", + ) + + stdin, err := command.StdinPipe() + Expect(err).ToNot(HaveOccurred()) + _, err = io.WriteString(stdin, config) + Expect(err).ToNot(HaveOccurred()) + stdin.Close() + + query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }` + + r := graphql.NewRequest(query) + r.Variables["config"] = config + r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) + + req, err := r.Encode() + Expect(err).ShouldNot(HaveOccurred()) + expReq = req.String() + }) + + It("returns an error when validating a config", func() { + expResp := `{ + "buildConfig": { + "errors": [ + {"message": "error1"} + ] + } + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: error1")) + Eventually(session).Should(clitest.ShouldFail()) + }) + + It("returns successfully when validating a config", func() { + expResp := `{ + "buildConfig": {} + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) + Eventually(session).Should(gexec.Exit(0)) + }) }) - It("packs successfully given an orb containing local executors and commands in folder", func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/myorb/test") - - results = golden.Get(GinkgoT(), filepath.FromSlash("myorb/result.yml")) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) + Describe("validating configs with pipeline parameters", func() { + config := "version: 2.1" + var expReq string + + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "process", + "--skip-update-check", + "--token", token, + "--host", tempSettings.TestServer.URL(), + "--pipeline-parameters", `{"foo": "test", "bar": true, "baz": 10}`, + "-", + ) + + stdin, err := command.StdinPipe() + Expect(err).ToNot(HaveOccurred()) + _, err = io.WriteString(stdin, config) + Expect(err).ToNot(HaveOccurred()) + stdin.Close() + + query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues, pipelineParametersJson: $pipelineParametersJson) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }` + + r := graphql.NewRequest(query) + r.Variables["config"] = config + r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) + + pipelineParams, err := json.Marshal(pipeline.Parameters{ + "foo": "test", + "bar": true, + "baz": 10, + }) + Expect(err).ToNot(HaveOccurred()) + r.Variables["pipelineParametersJson"] = string(pipelineParams) + + req, err := r.Encode() + Expect(err).ShouldNot(HaveOccurred()) + expReq = req.String() + }) + + It("returns successfully when validating a config", func() { + expResp := `{ + "buildConfig": {} + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session).Should(gexec.Exit(0)) + }) }) - It("packs as expected given a large nested config including rails orbs", func() { - var path string = "test-with-large-nested-rails-orb" - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - filepath.Join("testdata", path, "test")) - results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) + Describe("validating configs with private orbs", func() { + config := "version: 2.1" + orgId := "bb604b45-b6b0-4b81-ad80-796f15eddf87" + var expReq string + + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "validate", + "--skip-update-check", + "--token", token, + "--host", tempSettings.TestServer.URL(), + "--org-id", orgId, + "-", + ) + + stdin, err := command.StdinPipe() + Expect(err).ToNot(HaveOccurred()) + _, err = io.WriteString(stdin, config) + Expect(err).ToNot(HaveOccurred()) + stdin.Close() + + query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgId: $orgId) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }` + + r := graphql.NewRequest(query) + r.Variables["config"] = config + r.Variables["orgId"] = orgId + r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) + + req, err := r.Encode() + Expect(err).ShouldNot(HaveOccurred()) + expReq = req.String() + }) + + It("returns an error when validating a config with a private orb", func() { + expResp := `{ + "buildConfig": { + "errors": [ + {"message": "permission denied"} + ] + } + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) + Eventually(session).Should(clitest.ShouldFail()) + }) }) - It("prints an error given a config which is a list and not a map", func() { - config := clitest.OpenTmpFile(filepath.Join(tempSettings.Home, "myorb"), "config.yaml") - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - config.RootDir, - ) - config.Write([]byte(`[]`)) - - expected := fmt.Sprintf("Error: Failed trying to marshal the tree to YAML : expected a map, got a `[]interface {}` which is not supported at this time for \"%s\"\n", config.Path) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - stderr := session.Wait().Err.Contents() - Expect(string(stderr)).To(Equal(expected)) - Eventually(session).Should(clitest.ShouldFail()) - config.Close() + Describe("validating configs with private orbs Legacy", func() { + config := "version: 2.1" + orgSlug := "circleci" + var expReq string + + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "validate", + "--skip-update-check", + "--token", token, + "--host", tempSettings.TestServer.URL(), + "--org-slug", orgSlug, + "-", + ) + + stdin, err := command.StdinPipe() + Expect(err).ToNot(HaveOccurred()) + _, err = io.WriteString(stdin, config) + Expect(err).ToNot(HaveOccurred()) + stdin.Close() + + query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgSlug: $orgSlug) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }` + + r := graphql.NewRequest(query) + r.Variables["config"] = config + r.Variables["orgSlug"] = orgSlug + r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) + + req, err := r.Encode() + Expect(err).ShouldNot(HaveOccurred()) + expReq = req.String() + }) + + It("returns an error when validating a config with a private orb", func() { + expResp := `{ + "buildConfig": { + "errors": [ + {"message": "permission denied"} + ] + } + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) + Eventually(session).Should(clitest.ShouldFail()) + }) + + It("returns successfully when validating a config with private orbs", func() { + expResp := `{ + "buildConfig": {} + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) + Eventually(session).Should(gexec.Exit(0)) + }) }) }) }) diff --git a/config/deprecated_images.go b/cmd/deprecated-images.go similarity index 93% rename from config/deprecated_images.go rename to cmd/deprecated-images.go index 1579af3de..607edfc88 100644 --- a/config/deprecated_images.go +++ b/cmd/deprecated-images.go @@ -1,6 +1,8 @@ -package config +package cmd import ( + "github.com/CircleCI-Public/circleci-cli/api" + "github.com/pkg/errors" "gopkg.in/yaml.v3" ) @@ -38,7 +40,8 @@ type processedConfig struct { } // Processes the config down to v2.0, then checks image used against the block list -func DeprecatedImageCheck(response *ConfigResponse) error { +func deprecatedImageCheck(response *api.ConfigResponse) error { + aConfig := processedConfig{} err := yaml.Unmarshal([]byte(response.OutputYaml), &aConfig) if err != nil { diff --git a/cmd/root.go b/cmd/root.go index a51f10ee4..f47ff96ea 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -20,7 +20,6 @@ import ( var defaultEndpoint = "graphql-unstable" var defaultHost = "https://circleci.com" -var defaultConfigApiHost = "https://api.circleci.com" var defaultRestEndpoint = "api/v2" var trueString = "true" @@ -97,13 +96,12 @@ Use "{{.CommandPath}} [command] --help" for more information about a command.{{e // MakeCommands creates the top level commands func MakeCommands() *cobra.Command { rootOptions = &settings.Config{ - Debug: false, - Token: "", - Host: defaultHost, - ConfigAPIHost: defaultConfigApiHost, - RestEndpoint: defaultRestEndpoint, - Endpoint: defaultEndpoint, - GitHubAPI: "https://api.github.com/", + Debug: false, + Token: "", + Host: defaultHost, + RestEndpoint: defaultRestEndpoint, + Endpoint: defaultEndpoint, + GitHubAPI: "https://api.github.com/", } if err := rootOptions.Load(); err != nil { diff --git a/config/config.go b/config/config.go deleted file mode 100644 index cd3ec9d8d..000000000 --- a/config/config.go +++ /dev/null @@ -1,114 +0,0 @@ -package config - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/url" - "os" - - "github.com/CircleCI-Public/circleci-cli/api/rest" - "github.com/CircleCI-Public/circleci-cli/pipeline" - "github.com/pkg/errors" -) - -type ConfigError struct { - Message string `json:"message"` -} - -type ConfigResponse struct { - Valid bool `json:"valid"` - SourceYaml string `json:"source-yaml"` - OutputYaml string `json:"output-yaml"` - Errors []ConfigError `json:"errors"` -} - -type CompileConfigRequest struct { - ConfigYaml string `json:"config_yaml"` - Options Options `json:"options"` -} - -type Options struct { - OwnerID string `json:"owner_id,omitempty"` - PipelineParameters string `json:"pipeline_parameters,omitempty"` - PipelineValues map[string]string `json:"pipeline_values,omitempty"` -} - -// #nosec -func loadYaml(path string) (string, error) { - var err error - var config []byte - if path == "-" { - config, err = ioutil.ReadAll(os.Stdin) - } else { - config, err = ioutil.ReadFile(path) - } - - if err != nil { - return "", errors.Wrapf(err, "Could not load config file at %s", path) - } - - return string(config), nil -} - -// ConfigQuery - attempts to compile or validate a given config file with the -// passed in params/values. -// If the orgID is passed in, the config-compilation with private orbs should work. -func ConfigQuery( - rest *rest.Client, - configPath string, - orgID string, - params pipeline.Parameters, - values pipeline.Values, -) (*ConfigResponse, error) { - - configString, err := loadYaml(configPath) - if err != nil { - return nil, err - } - - compileRequest := CompileConfigRequest{ - ConfigYaml: configString, - Options: Options{ - PipelineValues: values, - }, - } - - if orgID != "" { - compileRequest.Options.OwnerID = orgID - } - - if len(params) >= 1 { - pipelineParamsString, err := json.Marshal(params) - if err != nil { - return nil, err - } - compileRequest.Options.PipelineParameters = string(pipelineParamsString) - } - - req, err := rest.NewAPIRequest( - "POST", - &url.URL{ - Path: "compile-config-with-defaults", - }, - compileRequest, - ) - if err != nil { - return nil, err - } - - configCompilationResp := &ConfigResponse{} - statusCode, err := rest.DoRequest(req, configCompilationResp) - if err != nil { - return nil, err - } - if statusCode != 200 { - return nil, errors.New("non 200 status code") - } - - if len(configCompilationResp.Errors) > 0 { - return nil, errors.New(fmt.Sprintf("config compilation contains errors: %s", configCompilationResp.Errors)) - } - - return configCompilationResp, nil -} diff --git a/go.mod b/go.mod index fc76cc3ef..d515fd50c 100644 --- a/go.mod +++ b/go.mod @@ -90,4 +90,4 @@ require ( // fix vulnerability: CVE-2020-15114 in etcd v3.3.10+incompatible replace github.com/coreos/etcd => github.com/coreos/etcd v3.3.24+incompatible -go 1.19 +go 1.18 diff --git a/local/local.go b/local/local.go index 0177ba7a1..e139be8f4 100644 --- a/local/local.go +++ b/local/local.go @@ -10,8 +10,8 @@ import ( "strings" "syscall" - "github.com/CircleCI-Public/circleci-cli/api/rest" - "github.com/CircleCI-Public/circleci-cli/config" + "github.com/CircleCI-Public/circleci-cli/api" + "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/pkg/errors" @@ -24,20 +24,21 @@ const DefaultConfigPath = ".circleci/config.yml" func Execute(flags *pflag.FlagSet, cfg *settings.Config) error { var err error - var configResponse *config.ConfigResponse - restClient := rest.New(cfg.Host, cfg) + var configResponse *api.ConfigResponse + cl := graphql.NewClient(cfg.HTTPClient, cfg.Host, cfg.Endpoint, cfg.Token, cfg.Debug) + processedArgs, configPath := buildAgentArguments(flags) //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - configResponse, err = config.ConfigQuery(restClient, configPath, orgID, nil, pipeline.LocalPipelineValues()) + configResponse, err = api.ConfigQuery(cl, configPath, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - configResponse, err = config.ConfigQuery(restClient, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) + configResponse, err = api.ConfigQueryLegacy(cl, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) if err != nil { return err } diff --git a/settings/settings.go b/settings/settings.go index 1459725d6..24bc07f33 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -35,7 +35,6 @@ type Config struct { GitHubAPI string `yaml:"-"` SkipUpdateCheck bool `yaml:"-"` OrbPublishing OrbPublishingInfo `yaml:"orb_publishing"` - ConfigAPIHost string `yaml:"-"` } type OrbPublishingInfo struct { From e46985649b21e1c3abe3f93676b5182b629a8334 Mon Sep 17 00:00:00 2001 From: Ai Mi Bui Date: Fri, 7 Oct 2022 11:50:12 -0700 Subject: [PATCH 13/84] fixed tests --- cmd/admin_test.go | 6 +++--- cmd/orb_test.go | 17 +++++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/cmd/admin_test.go b/cmd/admin_test.go index ae8764f06..21baab91e 100644 --- a/cmd/admin_test.go +++ b/cmd/admin_test.go @@ -181,7 +181,7 @@ var _ = Describe("Namespace integration tests", func() { }` expectedListOrbsRequest := `{ - "query": "\nquery namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType) {\n\tregistryNamespace(name: $namespace) {\n\t\tname\n id\n\t\torbs(first: 20, after: $after, view: $view) {\n\t\t\tedges {\n\t\t\t\tcursor\n\t\t\t\tnode {\n\t\t\t\t\tversions {\n\t\t\t\t\t\tsource\n\t\t\t\t\t\tversion\n\t\t\t\t\t}\n\t\t\t\t\tname\n\t statistics {\n\t\t last30DaysBuildCount,\n\t\t last30DaysProjectCount,\n\t\t last30DaysOrganizationCount\n\t }\n\t\t\t\t}\n\t\t\t}\n\t\t\ttotalCount\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t}\n\t}\n}\n", + "query": "\nquery namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType) {\n\tregistryNamespace(name: $namespace) {\n\t\tname\n id\n\t\torbs(first: 20, after: $after, view: $view) {\n\t\t\tedges {\n\t\t\t\tcursor\n\t\t\t\tnode {\n\t\t\t\t\tversions { version\n\t\t\t\t\t}\n\t\t\t\t\tname\n\t statistics {\n\t\t last30DaysBuildCount,\n\t\t last30DaysProjectCount,\n\t\t last30DaysOrganizationCount\n\t }\n\t\t\t\t}\n\t\t\t}\n\t\t\ttotalCount\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t}\n\t}\n}\n", "variables": { "after": "", "namespace": "foo-ns", @@ -236,7 +236,7 @@ var _ = Describe("Namespace integration tests", func() { }` expectedListOrbsRequest := `{ - "query": "\nquery namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType) {\n\tregistryNamespace(name: $namespace) {\n\t\tname\n id\n\t\torbs(first: 20, after: $after, view: $view) {\n\t\t\tedges {\n\t\t\t\tcursor\n\t\t\t\tnode {\n\t\t\t\t\tversions {\n\t\t\t\t\t\tsource\n\t\t\t\t\t\tversion\n\t\t\t\t\t}\n\t\t\t\t\tname\n\t statistics {\n\t\t last30DaysBuildCount,\n\t\t last30DaysProjectCount,\n\t\t last30DaysOrganizationCount\n\t }\n\t\t\t\t}\n\t\t\t}\n\t\t\ttotalCount\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t}\n\t}\n}\n", + "query": "\nquery namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType) {\n\tregistryNamespace(name: $namespace) {\n\t\tname\n id\n\t\torbs(first: 20, after: $after, view: $view) {\n\t\t\tedges {\n\t\t\t\tcursor\n\t\t\t\tnode {\n\t\t\t\t\tversions { version\n\t\t\t\t\t}\n\t\t\t\t\tname\n\t statistics {\n\t\t last30DaysBuildCount,\n\t\t last30DaysProjectCount,\n\t\t last30DaysOrganizationCount\n\t }\n\t\t\t\t}\n\t\t\t}\n\t\t\ttotalCount\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t}\n\t}\n}\n", "variables": { "after": "", "namespace": "foo-ns", @@ -310,7 +310,7 @@ var _ = Describe("Namespace integration tests", func() { }` expectedListOrbsRequest := `{ - "query": "\nquery namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType) {\n\tregistryNamespace(name: $namespace) {\n\t\tname\n id\n\t\torbs(first: 20, after: $after, view: $view) {\n\t\t\tedges {\n\t\t\t\tcursor\n\t\t\t\tnode {\n\t\t\t\t\tversions {\n\t\t\t\t\t\tsource\n\t\t\t\t\t\tversion\n\t\t\t\t\t}\n\t\t\t\t\tname\n\t statistics {\n\t\t last30DaysBuildCount,\n\t\t last30DaysProjectCount,\n\t\t last30DaysOrganizationCount\n\t }\n\t\t\t\t}\n\t\t\t}\n\t\t\ttotalCount\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t}\n\t}\n}\n", + "query": "\nquery namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType) {\n\tregistryNamespace(name: $namespace) {\n\t\tname\n id\n\t\torbs(first: 20, after: $after, view: $view) {\n\t\t\tedges {\n\t\t\t\tcursor\n\t\t\t\tnode {\n\t\t\t\t\tversions { version\n\t\t\t\t\t}\n\t\t\t\t\tname\n\t statistics {\n\t\t last30DaysBuildCount,\n\t\t last30DaysProjectCount,\n\t\t last30DaysOrganizationCount\n\t }\n\t\t\t\t}\n\t\t\t}\n\t\t\ttotalCount\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t}\n\t}\n}\n", "variables": { "after": "", "namespace": "foo-ns", diff --git a/cmd/orb_test.go b/cmd/orb_test.go index f10100bb7..16290b896 100644 --- a/cmd/orb_test.go +++ b/cmd/orb_test.go @@ -2288,6 +2288,7 @@ Search, filter, and view sources for all Orbs online at https://circleci.com/dev tmpBytes := golden.Get(GinkgoT(), filepath.FromSlash("gql_orb_list_details/pretty_json_output.json")) expectedOutput := string(tmpBytes) completeOutput := string(session.Wait().Out.Contents()) + Expect(completeOutput).Should(MatchJSON(expectedOutput)) Expect(tempSettings.TestServer.ReceivedRequests()).Should(HaveLen(1)) }) @@ -2313,9 +2314,7 @@ query namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType edges { cursor node { - versions { - source - version + versions (count: 1){ source, version } name statistics { @@ -2390,6 +2389,7 @@ query namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType "orb", "list", "circleci", "--skip-update-check", "--host", tempSettings.TestServer.URL(), + "--details", "--json", ) }) @@ -2406,7 +2406,7 @@ query namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType Expect(completeOutput).Should(MatchJSON(expectedOutput)) Eventually(session).Should(gexec.Exit(0)) Expect(tempSettings.TestServer.ReceivedRequests()).Should(HaveLen(2)) - }) + }) }) }) @@ -2429,9 +2429,7 @@ query namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType edges { cursor node { - versions { - source - version + versions { version } name statistics { @@ -2467,6 +2465,7 @@ query namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType }) }) + It("returns an error", func() { By("running the command") session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) @@ -2491,9 +2490,7 @@ query namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType edges { cursor node { - versions { - source - version + versions { version } name statistics { From ff36709c7a7be0b797fd39c547385dfe7884aa56 Mon Sep 17 00:00:00 2001 From: corinnesollows Date: Fri, 7 Oct 2022 15:57:41 -0300 Subject: [PATCH 14/84] linted --- api/api.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/api/api.go b/api/api.go index ce10fff5a..a88272e85 100644 --- a/api/api.go +++ b/api/api.go @@ -1686,14 +1686,14 @@ query namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType cursor node { versions ` - - if showDetails { - query+=`(count: 1){ source,` - } else { - query+=`{` - } - query+=` version + if showDetails { + query += `(count: 1){ source,` + } else { + query += `{` + } + + query += ` version } name statistics { From 5b2349529e87dfd39bf5b99f3533ecc36f8ca594 Mon Sep 17 00:00:00 2001 From: corinnesollows Date: Fri, 7 Oct 2022 16:07:19 -0300 Subject: [PATCH 15/84] lint again --- cmd/orb_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/orb_test.go b/cmd/orb_test.go index 16290b896..ed2ae913f 100644 --- a/cmd/orb_test.go +++ b/cmd/orb_test.go @@ -2406,7 +2406,7 @@ query namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType Expect(completeOutput).Should(MatchJSON(expectedOutput)) Eventually(session).Should(gexec.Exit(0)) Expect(tempSettings.TestServer.ReceivedRequests()).Should(HaveLen(2)) - }) + }) }) }) @@ -2465,7 +2465,6 @@ query namespaceOrbs ($namespace: String, $after: String!, $view: OrbListViewType }) }) - It("returns an error", func() { By("running the command") session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) From 5ff92b4ddd64da789d351c1a070ef48340a23b41 Mon Sep 17 00:00:00 2001 From: corinnesollows Date: Mon, 17 Oct 2022 14:52:59 -0300 Subject: [PATCH 16/84] Adding to PR Template (#799) --- .github/PULL_REQUEST_TEMPLATE.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7d4cba85f..096fcb5c8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -8,6 +8,10 @@ - [ ] This is not a security issue (which should be reported here: https://circleci.com/security/) - [ ] I have read [Contribution Guidelines](https://github.com/CircleCI-Public/circleci-cli/blob/master/CONTRIBUTING.md). +### Internal Checklist +- [ ] I am requesting a review from my own team as well as the owning team +- [ ] I have a plan in place for the monitoring of the changes that I am making (this can include new monitors, logs to be aware of, etc...) + ## Changes ======= From dbeb3c0fe57b176af00aa477fbf5db19238a05d4 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 19 Oct 2022 10:30:24 -0400 Subject: [PATCH 17/84] feat: allow initializing existing orbs --- cmd/orb.go | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/cmd/orb.go b/cmd/orb.go index 435504563..e8f18b1f2 100644 --- a/cmd/orb.go +++ b/cmd/orb.go @@ -1196,11 +1196,30 @@ func initOrb(opts orbOptions) error { Message: "Orb name", Default: orbName, } + + orbExists := true + err = survey.AskOne(iprompt, &orbName) if err != nil { return errors.Wrap(err, "Unexpected error") } + _, err = api.OrbInfo(opts.cl, namespace+"/"+orbName) + if err != nil { + orbExists = false + } + + if orbExists { + mprompt := &survey.Confirm{ + Message: fmt.Sprintf("Orb %s/%s already exists, would you like to continue?", namespace, orbName), + } + confirmation := false + err = survey.AskOne(mprompt, &confirmation) + if err != nil { + return errors.Wrap(err, "Orb already exists") + } + } + registryCategories, err := api.ListOrbCategories(opts.cl) if err != nil { return errors.Wrapf(err, "Failed to list orb categories") @@ -1273,9 +1292,11 @@ func initOrb(opts orbOptions) error { }() if !gitAction { - _, err = api.CreateOrb(opts.cl, namespace, orbName, opts.private) - if err != nil { - return errors.Wrap(err, "Unable to create orb") + if !orbExists { + _, err = api.CreateOrb(opts.cl, namespace, orbName, opts.private) + if err != nil { + return errors.Wrap(err, "Unable to create orb") + } } for _, v := range categories { err = api.AddOrRemoveOrbCategorization(opts.cl, namespace, orbName, v, api.Add) @@ -1410,10 +1431,12 @@ func initOrb(opts orbOptions) error { } // Push a dev version of the orb. - _, err = api.CreateOrb(opts.cl, namespace, orbName, opts.private) + if !orbExists { + _, err = api.CreateOrb(opts.cl, namespace, orbName, opts.private) if err != nil { return errors.Wrap(err, "Unable to create orb") } + } for _, v := range categories { err = api.AddOrRemoveOrbCategorization(opts.cl, namespace, orbName, v, api.Add) if err != nil { From 9f875ff82039839fe692be654d6fb850e94b2664 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 20 Oct 2022 09:49:01 -0400 Subject: [PATCH 18/84] chore: gofmt --- cmd/orb.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/orb.go b/cmd/orb.go index e8f18b1f2..e7c4be593 100644 --- a/cmd/orb.go +++ b/cmd/orb.go @@ -1433,9 +1433,9 @@ func initOrb(opts orbOptions) error { // Push a dev version of the orb. if !orbExists { _, err = api.CreateOrb(opts.cl, namespace, orbName, opts.private) - if err != nil { - return errors.Wrap(err, "Unable to create orb") - } + if err != nil { + return errors.Wrap(err, "Unable to create orb") + } } for _, v := range categories { err = api.AddOrRemoveOrbCategorization(opts.cl, namespace, orbName, v, api.Add) From 7cd9cc4df4679b2527daff299ae38f623e11750e Mon Sep 17 00:00:00 2001 From: Sagar Gupta Date: Wed, 9 Nov 2022 10:44:43 -0500 Subject: [PATCH 19/84] update circle-policy-agent version (#805) --- go.mod | 18 +- go.sum | 1116 ++------------------------------------------------------ 2 files changed, 34 insertions(+), 1100 deletions(-) diff --git a/go.mod b/go.mod index d515fd50c..31c11e671 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/CircleCI-Public/circleci-cli require ( github.com/AlecAivazis/survey/v2 v2.1.1 - github.com/CircleCI-Public/circle-policy-agent v0.0.137 + github.com/CircleCI-Public/circle-policy-agent v0.0.208 github.com/Masterminds/semver v1.4.2 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/blang/semver v3.5.1+incompatible @@ -50,7 +50,7 @@ require ( github.com/go-git/go-billy/v5 v5.0.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.7 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -67,20 +67,20 @@ require ( github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.12.0 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/open-policy-agent/opa v0.43.0 // indirect + github.com/open-policy-agent/opa v0.45.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect - github.com/vektah/gqlparser/v2 v2.4.7 // indirect + github.com/tchap/go-patricia/v2 v2.3.1 // indirect github.com/xanzy/ssh-agent v0.2.1 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/yashtewari/glob-intersection v0.1.0 // indirect - golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect - golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced // indirect - golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 // indirect - golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/crypto v0.1.0 // indirect + golang.org/x/net v0.1.0 // indirect + golang.org/x/sys v0.1.0 // indirect + golang.org/x/term v0.1.0 // indirect + golang.org/x/text v0.4.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index 8a5287c5c..9f5cd5617 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -15,11 +13,6 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -28,7 +21,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -39,139 +31,41 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eimREzLWI= github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CircleCI-Public/circle-policy-agent v0.0.137 h1:/fjxEoZN7Pr2MUc8zewXlKkcu7xwG4JHcmag1mZ3fiU= -github.com/CircleCI-Public/circle-policy-agent v0.0.137/go.mod h1:oggrt0v/95EQdYKWMb5525GreoF6ZGey9PcAoItI9OY= +github.com/CircleCI-Public/circle-policy-agent v0.0.208 h1:m0gGytVrgNQwf8hinXfn7FIzF69lOkbgKVo60I2blfU= +github.com/CircleCI-Public/circle-policy-agent v0.0.208/go.mod h1:6NN52smcKmEmY8IM3UW26TppLTxuzPD3O9yg5r1Q8cM= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= -github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= -github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim v0.9.3/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -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/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -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.43.16/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/briandowns/spinner v1.18.1 h1:yhQmQtM1zsqFsouh09Bk/jCjd50pC3EOGsh28gLVvwY= github.com/briandowns/spinner v1.18.1/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/bytecodealliance/wasmtime-go v0.36.0 h1:B6thr7RMM9xQmouBtUqm1RpkJjuLS37m6nxX+iwsQSc= -github.com/bytecodealliance/wasmtime-go v0.36.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/bytecodealliance/wasmtime-go v1.0.0 h1:9u9gqaUiaJeN5IoD1L7egD8atOnTGyJcNp8BhkL9cUU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -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.11.0 h1:fBLyY0PvJnd56Vlu5L84JJH6f4axhgIJ9P3NET78f0Q= github.com/charmbracelet/bubbles v0.11.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= github.com/charmbracelet/bubbletea v0.21.0 h1:f3y+kanzgev5PA916qxmDybSHU3N804uOnKnhRPXTcI= @@ -179,245 +73,41 @@ github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= 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= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= -github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= -github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= -github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= -github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= -github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= -github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE= -github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= -github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= -github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.6/go.mod h1:BWtoWl5ghVymxu6MBjg79W9NZrCRyHIdUtk4cauMe34= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= -github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= -github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= -github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= -github.com/containerd/imgcrypt v1.1.4/go.mod h1:LorQnPtzL/T0IyCeftcsMEO7AqxUDbdO8j/tSUpgxvo= -github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= -github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= -github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= -github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= -github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= -github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= -github.com/containernetworking/cni v1.1.1/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= -github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= -github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19sZPp3ry5uHSkI4LPxV8= -github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.3/go.mod h1:xpdkbVAuaH3WzbEabUd5yDsl9SwJA5pABH85425Es2g= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.24+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/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/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -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= -github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= -github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8= -github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M= github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= -github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= -github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269/go.mod h1:28YO/VJk9/64+sTGNuYaBjWxrXTPrj0C0XmgTIOjxX4= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/erikgeiser/promptkit v0.7.0 h1:Yi28iN6JRs8/0x+wjQRPfWb+vWz1pFmZ5fu2uoFipD8= github.com/erikgeiser/promptkit v0.7.0/go.mod h1:Jj9bhN+N8RbMjB1jthkr9A4ydmczZ1WZJ8xTXnP12dg= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897 h1:E52jfcE64UG42SwLmrW0QByONfGynWuzBvm86BoB9z8= -github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= @@ -433,69 +123,16 @@ github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJA github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-ini/ini v1.66.6/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -503,8 +140,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -518,20 +153,13 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= -github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -539,24 +167,15 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v15.0.0+incompatible h1:jlPg2Cpsxb/FyEV/MFiIE9tW/2RAevQNZDPeHbf5a94= github.com/google/go-github v15.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -564,208 +183,67 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 h1:AgcIVYPa6XJnU3phs104wLj8l5GEththEw6+F79YsIY= github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7VjIE6z8dIvMsI4/s+1qr5EL+zoIGev1BQj1eoJ8= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= -github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ= github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 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 v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= 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/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 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.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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 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.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= -github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= @@ -778,140 +256,34 @@ github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc= github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A= -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= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/networkplumbing/go-nft v0.2.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/open-policy-agent/opa v0.43.0 h1:UKTpyFUPMs4wYYL1qsXpYQQurBCPoEjFbxaD/4V46gY= -github.com/open-policy-agent/opa v0.43.0/go.mod h1:xfTsKQEMvy7CxxgsCFoYuzT9jA/8C4JWLignCkN4Dzw= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/open-policy-agent/opa v0.45.0 h1:P5nuhVRtR+e58fk3CMMbiqr6ZFyWQPNOC3otsorGsFs= +github.com/open-policy-agent/opa v0.45.0/go.mod h1:/OnsYljNEWJ6DXeFOOnoGn8CvwZGMUS4iRqzYdJvmBI= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rhysd/go-github-selfupdate v0.0.0-20180520142321-41c1bbb0804a h1:YNh/SV+Z0p7kQDUE9Ux+46ruTucvQP43XB06DfZa8Es= @@ -919,223 +291,58 @@ github.com/rhysd/go-github-selfupdate v0.0.0-20180520142321-41c1bbb0804a/go.mod 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.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -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/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= +github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw= github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vektah/gqlparser/v2 v2.4.6/go.mod h1:flJWIR04IMQPGz+BXLrORkrARBxv/rtyIAFvd/MceW0= -github.com/vektah/gqlparser/v2 v2.4.7 h1:yub2WLoSIr+chP1zMv6bjrsgTasfubxGZJeC8ISEpgE= -github.com/vektah/gqlparser/v2 v2.4.7/go.mod h1:flJWIR04IMQPGz+BXLrORkrARBxv/rtyIAFvd/MceW0= -github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg= github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.32.0/go.mod h1:5eCOqeGphOyz6TsY3ZDNjE33SM/TFAK3RGuCL2naTgY= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= -go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0/go.mod h1:M1hVZHNxcbkAlcvrOMlpQ4YOO3Awf+4N2dxkZL3xm04= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0/go.mod h1:ceUgdyfNv4h4gLxHR0WNfDiiVmZFodZhZSbOLhpxqXE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0/go.mod h1:E+/KKhwOSw8yoPxSSuUHG6vKppkvhN+S1Jc7Nib3k3o= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/metric v0.30.0/go.mod h1:/ShZ7+TS4dHzDFmfi1kSXMhMVubNoP0oIaBp70J6UXU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= -go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= -go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= -go.opentelemetry.io/proto/otlp v0.16.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1158,8 +365,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -1168,35 +373,19 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1212,41 +401,15 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced h1:3dYNDff0VT5xj+mbj2XucFst9WKk6PdGOrb9n+SbIvw= -golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1258,59 +421,30 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1318,112 +452,53 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs= -golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1443,35 +518,17 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1488,11 +545,6 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1501,13 +553,11 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -1516,7 +566,6 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -1525,42 +574,18 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -1569,20 +594,6 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1595,51 +606,27 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1649,59 +636,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= -k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= From 19bba9e09c51c8902a3fc102203a68f37f9cc346 Mon Sep 17 00:00:00 2001 From: Sagar Gupta Date: Mon, 28 Nov 2022 11:04:39 -0500 Subject: [PATCH 20/84] update circle-policy-agent version, process ERROR decision state (#809) --- .circleci/config.yml | 15 +++--- cmd/policy/policy.go | 4 +- cmd/policy/policy_test.go | 50 +++++++++++++++++++ .../testdata/test3/runtime_error_policy.rego | 8 +++ go.mod | 11 ++-- go.sum | 30 +++++------ 6 files changed, 91 insertions(+), 27 deletions(-) create mode 100644 cmd/policy/testdata/test3/runtime_error_policy.rego diff --git a/.circleci/config.yml b/.circleci/config.yml index 1d7c27638..63683b77a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 orbs: shellcheck: circleci/shellcheck@1.2.0 - windows: circleci/windows@2.2.0 + windows: circleci/windows@5.0.0 executors: go: @@ -82,19 +82,22 @@ commands: jobs: test_windows: - executor: windows/default + executor: + name: windows/default + shell: bash --login -eo pipefail steps: - run: git config --global core.autocrlf false - checkout - - run: setx GOPATH %USERPROFILE%\go - - run: go get gotest.tools/gotestsum - run: mkdir test_results - - run: setx TESTING "true" - run: name: Run tests command: | - C:\Users\circleci\go\bin\gotestsum.exe --junitfile test_results/windows.xml + export GOBIN=/c/go/bin + export PATH=$GOBIN:$PATH + export TESTING="true" + go install gotest.tools/gotestsum@latest + gotestsum --junitfile test_results/windows.xml - store_test_results: path: test_results - store_artifacts: diff --git a/cmd/policy/policy.go b/cmd/policy/policy.go index 667dde79c..025a36d02 100644 --- a/cmd/policy/policy.go +++ b/cmd/policy/policy.go @@ -304,8 +304,8 @@ This group of commands allows the management of polices to be verified against b return fmt.Errorf("failed to make decision: %w", err) } - if strict && decision.Status == cpa.StatusHardFail { - return fmt.Errorf("policy decision status: HARD_FAIL") + if strict && (decision.Status == cpa.StatusHardFail || decision.Status == cpa.StatusError) { + return fmt.Errorf("policy decision status: %s", decision.Status) } if err := prettyJSONEncoder(cmd.OutOrStdout()).Encode(decision); err != nil { diff --git a/cmd/policy/policy_test.go b/cmd/policy/policy_test.go index 15fa423be..99ddfec0f 100644 --- a/cmd/policy/policy_test.go +++ b/cmd/policy/policy_test.go @@ -642,6 +642,42 @@ func TestMakeDecisionCommand(t *testing.T) { }, ExpectedErr: "policy decision status: HARD_FAIL", }, + { + Name: "passes when decision status = ERROR AND --strict is OFF", + Args: []string{"decide", "--owner-id", "test-owner", "--input", "./testdata/test1/test.yml"}, + ServerHandler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Method, "POST") + assert.Equal(t, r.URL.Path, "/api/v1/owner/test-owner/context/config/decision") + + var payload map[string]interface{} + assert.NilError(t, json.NewDecoder(r.Body).Decode(&payload)) + + assert.DeepEqual(t, payload, map[string]interface{}{ + "input": "test: config\n", + }) + + _, _ = io.WriteString(w, `{"status":"ERROR", "reason": "some reason"}`) + }, + ExpectedOutput: "{\n \"status\": \"ERROR\",\n \"reason\": \"some reason\"\n}\n", + }, + { + Name: "fails when decision status = ERROR AND --strict is ON", + Args: []string{"decide", "--owner-id", "test-owner", "--input", "./testdata/test1/test.yml", "--strict"}, + ServerHandler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Method, "POST") + assert.Equal(t, r.URL.Path, "/api/v1/owner/test-owner/context/config/decision") + + var payload map[string]interface{} + assert.NilError(t, json.NewDecoder(r.Body).Decode(&payload)) + + assert.DeepEqual(t, payload, map[string]interface{}{ + "input": "test: config\n", + }) + + _, _ = io.WriteString(w, `{"status":"ERROR", "reason": "some reason"}`) + }, + ExpectedErr: "policy decision status: ERROR", + }, { Name: "sends expected request with context", Args: []string{"decide", "--owner-id", "test-owner", "--input", "./testdata/test1/test.yml", "--context", "custom"}, @@ -745,6 +781,20 @@ func TestMakeDecisionCommand(t *testing.T) { Args: []string{"decide", "./testdata/test2/hard_fail_policy.rego", "--input", "./testdata/test0/config.yml", "--strict"}, ExpectedErr: "policy decision status: HARD_FAIL", }, + { + Name: "successfully performs decision for policy FILE provided locally, passes when decision = ERROR and strict = OFF", + Args: []string{"decide", "./testdata/test3/runtime_error_policy.rego", "--input", "./testdata/test0/config.yml"}, + ExpectedOutput: `{ + "status": "ERROR", + "reason": "./testdata/test3/runtime_error_policy.rego:8: eval_conflict_error: complete rules must not produce multiple outputs" +} +`, + }, + { + Name: "successfully performs decision for policy FILE provided locally, fails when decision = ERROR and strict = ON", + Args: []string{"decide", "./testdata/test3/runtime_error_policy.rego", "--input", "./testdata/test0/config.yml", "--strict"}, + ExpectedErr: "policy decision status: ERROR", + }, { Name: "successfully performs decision with metadata for policy FILE provided locally", Args: []string{ diff --git a/cmd/policy/testdata/test3/runtime_error_policy.rego b/cmd/policy/testdata/test3/runtime_error_policy.rego new file mode 100644 index 000000000..86d3adc36 --- /dev/null +++ b/cmd/policy/testdata/test3/runtime_error_policy.rego @@ -0,0 +1,8 @@ +package org + +policy_name["some_name"] + +enable_rule["some_rule"] + +some_rule = false +some_rule = true \ No newline at end of file diff --git a/go.mod b/go.mod index 31c11e671..e355d62b2 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/CircleCI-Public/circleci-cli require ( github.com/AlecAivazis/survey/v2 v2.1.1 - github.com/CircleCI-Public/circle-policy-agent v0.0.208 + github.com/CircleCI-Public/circle-policy-agent v0.0.259 github.com/Masterminds/semver v1.4.2 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/blang/semver v3.5.1+incompatible @@ -21,7 +21,7 @@ require ( github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 github.com/pkg/errors v0.9.1 github.com/rhysd/go-github-selfupdate v0.0.0-20180520142321-41c1bbb0804a - github.com/spf13/cobra v1.5.0 + github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 github.com/tcnksm/go-gitconfig v0.1.2 // indirect github.com/ulikunitz/xz v0.5.9 // indirect @@ -44,7 +44,7 @@ require ( github.com/charmbracelet/bubbletea v0.21.0 // indirect github.com/containerd/console v1.0.3 // indirect github.com/emirpasic/gods v1.12.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.0.0 // indirect @@ -53,7 +53,7 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 // indirect github.com/imdario/mergo v0.3.12 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect @@ -67,7 +67,7 @@ require ( github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.12.0 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/open-policy-agent/opa v0.45.0 // indirect + github.com/open-policy-agent/opa v0.46.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect @@ -77,6 +77,7 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/yashtewari/glob-intersection v0.1.0 // indirect golang.org/x/crypto v0.1.0 // indirect + golang.org/x/exp v0.0.0-20221114191408-850992195362 // indirect golang.org/x/net v0.1.0 // indirect golang.org/x/sys v0.1.0 // indirect golang.org/x/term v0.1.0 // indirect diff --git a/go.sum b/go.sum index 9f5cd5617..c680a8e99 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eim github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CircleCI-Public/circle-policy-agent v0.0.208 h1:m0gGytVrgNQwf8hinXfn7FIzF69lOkbgKVo60I2blfU= -github.com/CircleCI-Public/circle-policy-agent v0.0.208/go.mod h1:6NN52smcKmEmY8IM3UW26TppLTxuzPD3O9yg5r1Q8cM= +github.com/CircleCI-Public/circle-policy-agent v0.0.259 h1:VSxnALjYpwkbXOHHrXtVpXpTr0cXCTFBU5F57cHV+qg= +github.com/CircleCI-Public/circle-policy-agent v0.0.259/go.mod h1:XngdeVTP72Nf3Ml/vApyMtZE83Tx/l/Gxiepnoo5les= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= @@ -85,8 +85,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8= -github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= +github.com/dgraph-io/badger/v3 v3.2103.3 h1:s63J1pisDhKpzWslXFe+ChuthuZptpwTE6qEKoczPb4= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= @@ -106,8 +106,8 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897 h1:E52jfcE64UG42SwLmrW0QByONfGynWuzBvm86BoB9z8= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= @@ -200,8 +200,8 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7VjIE6z8dIvMsI4/s+1qr5EL+zoIGev1BQj1eoJ8= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -270,8 +270,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/open-policy-agent/opa v0.45.0 h1:P5nuhVRtR+e58fk3CMMbiqr6ZFyWQPNOC3otsorGsFs= -github.com/open-policy-agent/opa v0.45.0/go.mod h1:/OnsYljNEWJ6DXeFOOnoGn8CvwZGMUS4iRqzYdJvmBI= +github.com/open-policy-agent/opa v0.46.1 h1:iG998SLK0rzalex7Hyekeq17b9WtUexM0AuyHrQ7fCc= +github.com/open-policy-agent/opa v0.46.1/go.mod h1:DY9ZkCyz+DKoWI5gDuLw5rGC2RSb37QUeEf+9VjsWkI= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -279,7 +279,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= +github.com/prometheus/client_golang v1.13.1 h1:3gMjIY2+/hzmqhtUC/aQNYldJA6DtH3CgQvwS+02K1c= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= @@ -298,8 +298,8 @@ github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -353,6 +353,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20221114191408-850992195362 h1:NoHlPRbyl1VFI6FjwHtPQCN7wAMXI6cKcqrmXhOOfBQ= +golang.org/x/exp v0.0.0-20221114191408-850992195362/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -463,8 +465,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From 9a336a4c68d1d1fe9156fd718ce410c204115449 Mon Sep 17 00:00:00 2001 From: abdelDriowya Date: Tue, 6 Dec 2022 09:57:06 +0100 Subject: [PATCH 21/84] move ownership to devex team --- .github/CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 138eb5b32..5d57cd581 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,5 +1,5 @@ -* @CircleCI-Public/Extensibility -*orb*.go @CircleCI-Public/CPEng @CircleCI-Public/Extensibility +* @CircleCI-Public/developer-experience +*orb*.go @CircleCI-Public/CPEng @CircleCI-Public/developer-experience /api/runner @CircleCI-Public/runner /cmd/runner @CircleCI-Public/runner From c92cb9c6f28774fe0be46e760df4d07eebfe9356 Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Wed, 14 Dec 2022 15:18:28 +0100 Subject: [PATCH 22/84] feat: added command circleci orb diff --- cmd/orb.go | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 ++ 3 files changed, 100 insertions(+) diff --git a/cmd/orb.go b/cmd/orb.go index e7c4be593..1d48fea00 100644 --- a/cmd/orb.go +++ b/cmd/orb.go @@ -25,6 +25,7 @@ import ( "github.com/CircleCI-Public/circleci-cli/references" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/CircleCI-Public/circleci-cli/version" + "github.com/fatih/color" "github.com/pkg/errors" "github.com/spf13/cobra" "gopkg.in/yaml.v3" @@ -34,6 +35,10 @@ import ( "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" + + "github.com/hexops/gotextdiff" + "github.com/hexops/gotextdiff/myers" + "github.com/hexops/gotextdiff/span" ) type orbOptions struct { @@ -41,6 +46,8 @@ type orbOptions struct { cl *graphql.Client args []string + color string + listUncertified bool listJSON bool listDetails bool @@ -320,6 +327,18 @@ Please note that at this time all orbs created in the registry are world-readabl } orbInit.PersistentFlags().BoolVarP(&opts.private, "private", "", false, "initialize a private orb") + orbDiff := &cobra.Command{ + Use: "diff ", + Short: "Shows the difference between two versions of the same orb", + RunE: func(_ *cobra.Command, _ []string) error { + return orbDiff(opts) + }, + Args: cobra.ExactArgs(3), + Annotations: make(map[string]string), + } + orbDiff.Annotations[""] = "An orb with only a namespace and a name. This takes this form namespace/orb" + orbDiff.PersistentFlags().StringVar(&opts.color, "color", "auto", "Show colored diff. Can be one of always, never, or auto") + orbCreate.Flags().BoolVar(&opts.integrationTesting, "integration-testing", false, "Enable test mode to bypass interactive UI.") if err := orbCreate.Flags().MarkHidden("integration-testing"); err != nil { panic(err) @@ -354,6 +373,7 @@ Please note that at this time all orbs created in the registry are world-readabl orbCommand.AddCommand(removeCategorizationFromOrbCommand) orbCommand.AddCommand(listCategoriesCommand) orbCommand.AddCommand(orbInit) + orbCommand.AddCommand(orbDiff) return orbCommand } @@ -1609,3 +1629,80 @@ func orbTemplate(fileContents string, projectName string, orgName string, orbNam return x } + +func orbDiff(opts orbOptions) error { + colorOpt := opts.color + if colorOpt != "auto" && colorOpt != "always" && colorOpt != "never" { + return fmt.Errorf("option `color' expects \"always\", \"auto\", or \"never\"") + } + + orbName := opts.args[0] + version1 := opts.args[1] + version2 := opts.args[2] + orb1 := fmt.Sprintf("%s@%s", orbName, version1) + orb2 := fmt.Sprintf("%s@%s", orbName, version2) + + orb1Source, err := api.OrbSource(opts.cl, orb1) + if err != nil { + return errors.Wrapf(err, "Failed to get source for '%s'", orb1) + } + orb2Source, err := api.OrbSource(opts.cl, orb2) + if err != nil { + return errors.Wrapf(err, "Failed to get source for '%s'", orb2) + } + + edits := myers.ComputeEdits(span.URIFromPath(orb1), orb1Source, orb2Source) + unified := gotextdiff.ToUnified(orb1, orb2, orb1Source, edits) + diff := stringifyDiff(unified, colorOpt) + if diff == "" { + fmt.Println("No diff found") + } else { + fmt.Println(diff) + } + + return nil +} + +// Stringifies the unified diff passed as argument, and colorize it depending on the colorOpt value +func stringifyDiff(diff gotextdiff.Unified, colorOpt string) string { + if len(diff.Hunks) == 0 { + return "" + } + + headerColor := color.New(color.BgYellow, color.FgBlack) + diffStartColor := color.New(color.BgBlue, color.FgWhite) + deleteColor := color.New(color.FgRed) + insertColor := color.New(color.FgGreen) + untouchedColor := color.New(color.Reset) + + // The color library already takes care of disabling the color when stdout is redirected so we + // just enforce the color behavior for "never" and "always" and let the library handle the 'auto' + // case + oldNoColor := color.NoColor + if colorOpt == "never" { + color.NoColor = true + } + if colorOpt == "always" { + color.NoColor = false + } + + diffString := fmt.Sprintf("%s", diff) + lines := strings.Split(diffString, "\n") + + for i, line := range lines { + if strings.HasPrefix(line, "--- ") || strings.HasPrefix(line, "+++ ") { + lines[i] = headerColor.Sprint(line) + } else if strings.HasPrefix(line, "@@ ") { + lines[i] = diffStartColor.Sprint(line) + } else if strings.HasPrefix(line, "-") { + lines[i] = deleteColor.Sprint(line) + } else if strings.HasPrefix(line, "+") { + lines[i] = insertColor.Sprint(line) + } else { + lines[i] = untouchedColor.Sprint(line) + } + } + + color.NoColor = oldNoColor + return strings.Join(lines, "\n") +} diff --git a/go.mod b/go.mod index e355d62b2..1079a5001 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( require ( github.com/charmbracelet/lipgloss v0.5.0 github.com/erikgeiser/promptkit v0.7.0 + github.com/hexops/gotextdiff v1.0.3 ) require ( diff --git a/go.sum b/go.sum index c680a8e99..d892d5372 100644 --- a/go.sum +++ b/go.sum @@ -190,6 +190,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 h1:AgcIVYPa6XJnU3phs104wLj8l5GEththEw6+F79YsIY= github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= From d09b3de788e93990ee0d3cdf09d97ecf9a2388d4 Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Thu, 15 Dec 2022 09:48:38 +0100 Subject: [PATCH 23/84] test: added test for orb diff --- cmd/orb_test.go | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/cmd/orb_test.go b/cmd/orb_test.go index ed2ae913f..3a2f60dc4 100644 --- a/cmd/orb_test.go +++ b/cmd/orb_test.go @@ -9,6 +9,7 @@ import ( "os/exec" "path/filepath" "strconv" + "time" "gotest.tools/v3/golden" @@ -3338,4 +3339,91 @@ Windows Server 2010 }) }) + + Describe("Orb diff", func() { + var ( + token string + tempSettings *clitest.TempSettings + command *exec.Cmd + ) + + BeforeEach(func() { + token = "testtoken" + tempSettings = clitest.WithTempSettings() + }) + + AfterEach(func() { + tempSettings.Close() + }) + + DescribeTable("Shows the expected diff", func(source1, source2, expected, color string) { + orbName := "somenamespace/someorb" + version1 := "1.0.0" + orb1 := fmt.Sprintf("%s@%s", orbName, version1) + version2 := "2.0.0" + orb2 := fmt.Sprintf("%s@%s", orbName, version2) + command = exec.Command(pathCLI, "orb", "diff", orbName, version1, version2, + "--token", token, + "--host", tempSettings.TestServer.URL()) + + mockOrbSource(source1, orb1, token, tempSettings) + mockOrbSource(source2, orb2, token, tempSettings) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + + Eventually(session.Out).WithTimeout(5 * time.Second).Should(gbytes.Say(expected)) + Eventually(session).Should(gexec.Exit(0)) + }, + Entry("Detect identical sources", "orb-source", "orb-source", "No diff found", "auto"), + Entry( + "Detect difference", + "line1\\nline3\\n", + "line1\\nline2\\n", + `--- somenamespace/someorb@1.0.0 +\+\+\+ somenamespace/someorb@2.0.0 +@@ -1,2 \+1,2 @@ + line1 +-line3 +\+line2`, + "auto", + ), + ) + }) }) + +func mockOrbSource(source, orbVersion, token string, tempSettings *clitest.TempSettings) { + requestStruct := struct { + Query string `json:"query"` + Variables struct { + OrbVersionRef string `json:"orbVersionRef"` + } `json:"variables"` + }{ + Query: `query($orbVersionRef: String!) { + orbVersion(orbVersionRef: $orbVersionRef) { + id + version + orb { id } + source + } + }`, + Variables: struct { + OrbVersionRef string `json:"orbVersionRef"` + }{OrbVersionRef: orbVersion}, + } + request, err := json.Marshal(requestStruct) + Expect(err).ToNot(HaveOccurred()) + response := fmt.Sprintf(`{ + "orbVersion": { + "id": "some-id", + "version": "some-version", + "orb": { "id": "some-id" }, + "source": "%s" + } +}`, source) + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: string(request), + Response: response, + }) +} From fdce758e37fd3d20a1783cfd0947848baba51081 Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Fri, 16 Dec 2022 10:57:45 +0100 Subject: [PATCH 24/84] style: addressed pr comments --- cmd/orb.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/orb.go b/cmd/orb.go index 1d48fea00..096829be3 100644 --- a/cmd/orb.go +++ b/cmd/orb.go @@ -28,6 +28,7 @@ import ( "github.com/fatih/color" "github.com/pkg/errors" "github.com/spf13/cobra" + "golang.org/x/exp/slices" "gopkg.in/yaml.v3" "github.com/AlecAivazis/survey/v2" @@ -337,7 +338,7 @@ Please note that at this time all orbs created in the registry are world-readabl Annotations: make(map[string]string), } orbDiff.Annotations[""] = "An orb with only a namespace and a name. This takes this form namespace/orb" - orbDiff.PersistentFlags().StringVar(&opts.color, "color", "auto", "Show colored diff. Can be one of always, never, or auto") + orbDiff.PersistentFlags().StringVar(&opts.color, "color", "auto", "Show colored diff. Can be one of \"always\", \"never\", or \"auto\"") orbCreate.Flags().BoolVar(&opts.integrationTesting, "integration-testing", false, "Enable test mode to bypass interactive UI.") if err := orbCreate.Flags().MarkHidden("integration-testing"); err != nil { @@ -1632,7 +1633,8 @@ func orbTemplate(fileContents string, projectName string, orgName string, orbNam func orbDiff(opts orbOptions) error { colorOpt := opts.color - if colorOpt != "auto" && colorOpt != "always" && colorOpt != "never" { + allowedColorOpts := []string{"auto", "always", "never"} + if !slices.Contains(allowedColorOpts, colorOpt) { return fmt.Errorf("option `color' expects \"always\", \"auto\", or \"never\"") } From a272a9fc86292902be6ed2b59374eb768dea32ac Mon Sep 17 00:00:00 2001 From: David Desmarais-Michaud Date: Thu, 22 Dec 2022 12:43:22 -0500 Subject: [PATCH 25/84] added policy test command (#822) --- cmd/policy/policy.go | 68 +++++++++++++++- cmd/policy/policy_test.go | 80 +++++++++++++++++++ cmd/policy/testdata/policy-expected-usage.txt | 1 + .../testdata/policy/test-expected-usage.txt | 14 ++++ cmd/policy/testdata/test_policies/policy.rego | 7 ++ .../testdata/test_policies/policy_test.yaml | 17 ++++ go.mod | 23 +++--- go.sum | 54 ++++++++----- 8 files changed, 231 insertions(+), 33 deletions(-) create mode 100644 cmd/policy/testdata/policy/test-expected-usage.txt create mode 100644 cmd/policy/testdata/test_policies/policy.rego create mode 100644 cmd/policy/testdata/test_policies/policy_test.yaml diff --git a/cmd/policy/policy.go b/cmd/policy/policy.go index 025a36d02..8a3089de2 100644 --- a/cmd/policy/policy.go +++ b/cmd/policy/policy.go @@ -3,15 +3,19 @@ package policy import ( "context" "encoding/json" + "errors" "fmt" "io" "io/fs" "os" "path/filepath" + "regexp" "strings" "time" "github.com/CircleCI-Public/circle-policy-agent/cpa" + "github.com/CircleCI-Public/circle-policy-agent/cpa/tester" + "github.com/araddon/dateparse" "github.com/briandowns/spinner" "github.com/spf13/cobra" @@ -223,7 +227,6 @@ This group of commands allows the management of polices to be verified against b } return getAllDecisionLogs(client, ownerID, context, request, cmd.ErrOrStderr()) }() - if err != nil { return fmt.Errorf("failed to get policy decision logs: %v", err) } @@ -389,7 +392,6 @@ This group of commands allows the management of polices to be verified against b ) cmd := &cobra.Command{ - Short: "get/set policy decision settings (To read settings: run command without any settings flags)", Use: "settings", RunE: func(cmd *cobra.Command, args []string) error { @@ -426,6 +428,67 @@ This group of commands allows the management of polices to be verified against b return cmd }() + test := func() *cobra.Command { + var ( + run string + verbose bool + debug bool + useJSON bool + ) + + cmd := &cobra.Command{ + Use: "test [path]", + Short: "runs policy tests", + RunE: func(cmd *cobra.Command, args []string) (err error) { + var include *regexp.Regexp + if run != "" { + include, err = regexp.Compile(run) + if err != nil { + return fmt.Errorf("--run value is not a valid regular expression: %w", err) + } + } + + runnerOpts := tester.RunnerOptions{ + Path: args[0], + Include: include, + } + + runner, err := tester.NewRunner(runnerOpts) + if err != nil { + return fmt.Errorf("cannot instantite runner: %w", err) + } + + handlerOpts := tester.ResultHandlerOptions{ + Verbose: verbose, + Debug: debug, + Dst: cmd.OutOrStdout(), + } + + handler := func() tester.ResultHandler { + if useJSON { + return tester.MakeJSONResultHandler(handlerOpts) + } + return tester.MakeDefaultResultHandler(handlerOpts) + }() + + if !runner.RunAndHandleResults(handler) { + return errors.New("unsuccessful run") + } + + return nil + }, + Args: cobra.ExactArgs(1), + Example: "circleci policy test ./policies/...", + } + + cmd.Flags().StringVar(&run, "run", "", "select which tests to run based on regular expression") + cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "print all tests instead of only failed tests") + cmd.Flags().BoolVar(&debug, "debug", false, "print test debug context. Sets verbose to true") + cmd.Flags().BoolVar(&useJSON, "json", false, "print json test results instead of standard output format") + + return cmd + }() + cmd.AddCommand(push) cmd.AddCommand(diff) cmd.AddCommand(fetch) @@ -433,6 +496,7 @@ This group of commands allows the management of polices to be verified against b cmd.AddCommand(decide) cmd.AddCommand(eval) cmd.AddCommand(settings) + cmd.AddCommand(test) return cmd } diff --git a/cmd/policy/policy_test.go b/cmd/policy/policy_test.go index 99ddfec0f..d8279d4a0 100644 --- a/cmd/policy/policy_test.go +++ b/cmd/policy/policy_test.go @@ -9,6 +9,7 @@ import ( "net/http/httptest" "path" "path/filepath" + "strings" "sync" "testing" "time" @@ -1046,6 +1047,85 @@ func TestGetSetSettings(t *testing.T) { } } +func TestTestRunner(t *testing.T) { + cases := []struct { + Name string + Verbose bool + Debug bool + Run string + Json bool + Expected func(*testing.T, string) + }{ + { + Name: "default options", + Expected: func(t *testing.T, s string) { + assert.Check(t, strings.Contains(s, "testdata/test_policies")) + assert.Check(t, strings.Contains(s, "2/2 tests passed")) + assert.Check(t, !strings.Contains(s, "test_feature"), "should not have verbose output") + }, + }, + { + Name: "verbose", + Verbose: true, + Expected: func(t *testing.T, s string) { + assert.Check(t, strings.Contains(s, "test_feature")) + assert.Check(t, strings.Contains(s, "test_main")) + assert.Check(t, strings.Contains(s, "2/2 tests passed")) + }, + }, + { + Name: "verbose with run", + Verbose: true, + Run: "test_main", + Expected: func(t *testing.T, s string) { + assert.Check(t, strings.Contains(s, "test_main")) + assert.Check(t, !strings.Contains(s, "test_feature")) + assert.Check(t, strings.Contains(s, "1/1 tests passed")) + }, + }, + { + Name: "debug", + Debug: true, + Expected: func(t *testing.T, s string) { + assert.Check(t, strings.Contains(s, "---- Debug Test Context ----")) + }, + }, + { + Name: "json", + Json: true, + Expected: func(t *testing.T, s string) { + assert.Check(t, s[0] == '[') + assert.Check(t, s[len(s)-2] == ']') + }, + }, + } + + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + cmd, stdout, _ := makeCMD() + + args := []string{"test", "./testdata/test_policies"} + if tc.Verbose { + args = append(args, "-v") + } + if tc.Debug { + args = append(args, "--debug") + } + if tc.Run != "" { + args = append(args, "--run", tc.Run) + } + if tc.Json { + args = append(args, "--json") + } + + cmd.SetArgs(args) + + assert.NilError(t, cmd.Execute(), stdout.String()) + tc.Expected(t, stdout.String()) + }) + } +} + func makeCMD() (*cobra.Command, *bytes.Buffer, *bytes.Buffer) { config := &settings.Config{Token: "testtoken", HTTPClient: http.DefaultClient} cmd := NewCommand(config, nil) diff --git a/cmd/policy/testdata/policy-expected-usage.txt b/cmd/policy/testdata/policy-expected-usage.txt index bbdc184d8..799af4e49 100644 --- a/cmd/policy/testdata/policy-expected-usage.txt +++ b/cmd/policy/testdata/policy-expected-usage.txt @@ -9,6 +9,7 @@ Available Commands: logs Get policy decision logs / Get decision log (or policy bundle) by decision ID push push policy bundle settings get/set policy decision settings (To read settings: run command without any settings flags) + test runs policy tests Flags: --policy-base-url string base url for policy api (default "https://internal.circleci.com") diff --git a/cmd/policy/testdata/policy/test-expected-usage.txt b/cmd/policy/testdata/policy/test-expected-usage.txt new file mode 100644 index 000000000..e4ab0fb0a --- /dev/null +++ b/cmd/policy/testdata/policy/test-expected-usage.txt @@ -0,0 +1,14 @@ +Usage: + policy test [path] [flags] + +Examples: +circleci policy test ./policies/... + +Flags: + --debug print test debug context. Sets verbose to true + --json print json test results instead of standard output format + --run string select which tests to run based on regular expression + -v, --verbose print all tests instead of only failed tests + +Global Flags: + --policy-base-url string base url for policy api (default "https://internal.circleci.com") diff --git a/cmd/policy/testdata/test_policies/policy.rego b/cmd/policy/testdata/test_policies/policy.rego new file mode 100644 index 000000000..2499ff857 --- /dev/null +++ b/cmd/policy/testdata/test_policies/policy.rego @@ -0,0 +1,7 @@ +package org + +policy_name["test"] + +enable_rule["fail_if_not_main"] + +fail_if_not_main = "branch must be main!" { data.meta.branch != "main" } diff --git a/cmd/policy/testdata/test_policies/policy_test.yaml b/cmd/policy/testdata/test_policies/policy_test.yaml new file mode 100644 index 000000000..5d93f32df --- /dev/null +++ b/cmd/policy/testdata/test_policies/policy_test.yaml @@ -0,0 +1,17 @@ +test_main: + meta: + branch: main + decision: &root_decision + status: PASS + enabled_rules: + - fail_if_not_main + +test_feature: + meta: + branch: feature + decision: + <<: *root_decision + status: SOFT_FAIL + soft_failures: + - rule: fail_if_not_main + reason: branch must be main! diff --git a/go.mod b/go.mod index 1079a5001..02bcf8e65 100644 --- a/go.mod +++ b/go.mod @@ -2,18 +2,18 @@ module github.com/CircleCI-Public/circleci-cli require ( github.com/AlecAivazis/survey/v2 v2.1.1 - github.com/CircleCI-Public/circle-policy-agent v0.0.259 + github.com/CircleCI-Public/circle-policy-agent v0.0.383 github.com/Masterminds/semver v1.4.2 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/blang/semver v3.5.1+incompatible github.com/briandowns/spinner v1.18.1 - github.com/fatih/color v1.9.0 // indirect + github.com/fatih/color v1.9.0 github.com/go-git/go-git/v5 v5.1.0 github.com/google/go-github v15.0.0+incompatible // indirect github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect github.com/google/uuid v1.3.0 github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect github.com/mitchellh/mapstructure v1.4.1 github.com/olekukonko/tablewriter v0.0.5 github.com/onsi/ginkgo v1.16.4 @@ -35,6 +35,7 @@ require ( github.com/charmbracelet/lipgloss v0.5.0 github.com/erikgeiser/promptkit v0.7.0 github.com/hexops/gotextdiff v1.0.3 + golang.org/x/exp v0.0.0-20221114191408-850992195362 ) require ( @@ -61,6 +62,7 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.4 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect @@ -68,21 +70,22 @@ require ( github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.12.0 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/open-policy-agent/opa v0.46.1 // indirect + github.com/open-policy-agent/opa v0.47.3 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect github.com/xanzy/ssh-agent v0.2.1 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/yashtewari/glob-intersection v0.1.0 // indirect - golang.org/x/crypto v0.1.0 // indirect - golang.org/x/exp v0.0.0-20221114191408-850992195362 // indirect - golang.org/x/net v0.1.0 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/term v0.1.0 // indirect - golang.org/x/text v0.4.0 // indirect + github.com/yazgazan/jaydiff v0.3.1 // indirect + golang.org/x/crypto v0.3.0 // indirect + golang.org/x/net v0.4.0 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/term v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index d892d5372..2266e2ee9 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eim github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CircleCI-Public/circle-policy-agent v0.0.259 h1:VSxnALjYpwkbXOHHrXtVpXpTr0cXCTFBU5F57cHV+qg= -github.com/CircleCI-Public/circle-policy-agent v0.0.259/go.mod h1:XngdeVTP72Nf3Ml/vApyMtZE83Tx/l/Gxiepnoo5les= +github.com/CircleCI-Public/circle-policy-agent v0.0.383 h1:lgnCkN7GN1oFq9LtE3mY3fP8kkHkJXjC5eaFBbOuDzM= +github.com/CircleCI-Public/circle-policy-agent v0.0.383/go.mod h1:8tm87Gw46Jht5JNcvAaspjZt18fRvDkyL0/WRU41l0A= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= @@ -62,7 +62,7 @@ github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdn github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/briandowns/spinner v1.18.1 h1:yhQmQtM1zsqFsouh09Bk/jCjd50pC3EOGsh28gLVvwY= github.com/briandowns/spinner v1.18.1/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU= -github.com/bytecodealliance/wasmtime-go v1.0.0 h1:9u9gqaUiaJeN5IoD1L7egD8atOnTGyJcNp8BhkL9cUU= +github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -85,7 +85,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgraph-io/badger/v3 v3.2103.3 h1:s63J1pisDhKpzWslXFe+ChuthuZptpwTE6qEKoczPb4= +github.com/dgraph-io/badger/v3 v3.2103.4 h1:WE1B07YNTTJTtG9xjBcSW2wn0RJLyiV99h959RKZqM4= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= @@ -215,8 +215,8 @@ github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ= github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -226,19 +226,24 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 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/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 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.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 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/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d h1:eAS2t2Vy+6psf9LZ4T5WXWsbkBt3Tu5PWekJy5AGyEU= +github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d/go.mod h1:3YMHqrw2Qu3Liy82v4QdAG17e9k91HZ7w3hqlpWqhDo= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= @@ -272,8 +277,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/open-policy-agent/opa v0.46.1 h1:iG998SLK0rzalex7Hyekeq17b9WtUexM0AuyHrQ7fCc= -github.com/open-policy-agent/opa v0.46.1/go.mod h1:DY9ZkCyz+DKoWI5gDuLw5rGC2RSb37QUeEf+9VjsWkI= +github.com/open-policy-agent/opa v0.47.3 h1:Uj8zw+q6Cvv1iiQFh704Q6sl3fKVvk35WZNJLsd6mgk= +github.com/open-policy-agent/opa v0.47.3/go.mod h1:I5DbT677OGqfk9gvu5i54oIt0rrVf4B5pedpqDquAXo= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -281,9 +286,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.13.1 h1:3gMjIY2+/hzmqhtUC/aQNYldJA6DtH3CgQvwS+02K1c= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= @@ -294,6 +299,8 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ 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/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= @@ -310,7 +317,7 @@ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw= @@ -325,6 +332,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg= github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= +github.com/yazgazan/jaydiff v0.3.1 h1:5vUlbCCRm8LNckEZhMkU3GjZk1RQZxA/+XuOYrqkhvI= +github.com/yazgazan/jaydiff v0.3.1/go.mod h1:9AvhZxcMJX51L4eQdw7Wi2mhC8i3HQo0HRvftL0VROw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -335,6 +344,7 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -343,8 +353,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -407,8 +417,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -427,6 +437,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -468,22 +479,23 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 2c99912a708608b310e462b6b7c05b34cfbd7576 Mon Sep 17 00:00:00 2001 From: rlegan Date: Mon, 9 Jan 2023 17:07:51 +0100 Subject: [PATCH 26/84] security: added the flag `--ssl-reqd` to the curl command inside the installation script --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index cac9c1989..16a9993f0 100755 --- a/install.sh +++ b/install.sh @@ -51,7 +51,7 @@ esac RELEASE_URL="${GITHUB_BASE_URL}/releases/download/v${VERSION}/circleci-cli_${VERSION}_${OS}_amd64.tar.gz" # Download & unpack the release tarball. -curl -sL --retry 3 "${RELEASE_URL}" | tar zx --strip 1 +curl --ssl-reqd -sL --retry 3 "${RELEASE_URL}" | tar zx --strip 1 echo "Installing to $DESTDIR" install circleci "$DESTDIR" From 162f425a002717d3c5f214b022976869b88e1524 Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Thu, 12 Jan 2023 09:42:50 +0100 Subject: [PATCH 27/84] feat: enabled the configuration of the build-agent through argument of config file --- cmd/build.go | 2 ++ local/local.go | 66 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index 002e9682f..1d6192f2c 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -16,6 +16,8 @@ func newLocalExecuteCommand(config *settings.Config) *cobra.Command { } local.AddFlagsForDocumentation(buildCommand.Flags()) + buildAgentVersionUsage := `The version of the build agent image you want to use. This can be configured by writing in $HOME/.circleci/build_agent_settings.json: '{"LatestSha256":""}'` + buildCommand.Flags().String("build-agent-version", "", buildAgentVersionUsage) buildCommand.Flags().StringP("org-slug", "o", "", "organization slug (for example: github/example-org), used when a config depends on private orbs belonging to that org") buildCommand.Flags().String("org-id", "", "organization id, used when a config depends on private orbs belonging to that org") diff --git a/local/local.go b/local/local.go index e139be8f4..bed9cc42a 100644 --- a/local/local.go +++ b/local/local.go @@ -1,11 +1,13 @@ package local import ( + "encoding/json" "fmt" "io" "io/ioutil" "os" "os/exec" + "path" "regexp" "strings" "syscall" @@ -73,7 +75,8 @@ func Execute(flags *pflag.FlagSet, cfg *settings.Config) error { return err } - image, err := picardImage(os.Stdout) + picardVersion, _ := flags.GetString("build-agent-version") + image, err := picardImage(os.Stdout, picardVersion) if err != nil { return errors.Wrap(err, "Could not find picard image") @@ -131,7 +134,7 @@ func buildAgentArguments(flags *pflag.FlagSet) ([]string, string) { // build a list of all supplied flags, that we will pass on to build-agent flags.Visit(func(flag *pflag.Flag) { - if flag.Name != "org-slug" && flag.Name != "config" && flag.Name != "debug" && flag.Name != "org-id" { + if flag.Name != "build-agent-version" && flag.Name != "org-slug" && flag.Name != "config" && flag.Name != "debug" && flag.Name != "org-id" { result = append(result, unparseFlag(flags, flag)...) } }) @@ -142,11 +145,11 @@ func buildAgentArguments(flags *pflag.FlagSet) ([]string, string) { return result, configPath } -func picardImage(output io.Writer) (string, error) { +func picardImage(output io.Writer, picardVersion string) (string, error) { fmt.Fprintf(output, "Fetching latest build environment...\n") - sha, err := findLatestPicardSha() + sha, err := getPicardSha(output, picardVersion) if err != nil { return "", err } @@ -155,6 +158,31 @@ func picardImage(output io.Writer) (string, error) { return fmt.Sprintf("%s@%s", picardRepo, sha), nil } +func getPicardSha(output io.Writer, picardVersion string) (string, error) { + // If the version was passed as argument, we take it + if picardVersion != "" { + return picardVersion, nil + } + + var sha string + var err error + + sha, err = loadBuildAgentShaFromConfig() + if sha != "" && err == nil { + return sha, nil + } + if err != nil && !os.IsNotExist(err) { + fmt.Fprintf(output, "Unable to parse JSON file %s because: %s\n", buildAgentSettingsPath(), err) + fmt.Fprintf(output, "Falling back to latest build-agent version\n") + } + + sha, err = findLatestPicardSha() + if err != nil { + return "", err + } + return sha, nil +} + func ensureDockerIsAvailable() (string, error) { dockerPath, err := exec.LookPath("docker") @@ -196,6 +224,36 @@ func findLatestPicardSha() (string, error) { return latest, nil } +type buildAgentSettings struct { + LatestSha256 string +} + +func loadBuildAgentShaFromConfig() (string, error) { + if _, err := os.Stat(buildAgentSettingsPath()); os.IsNotExist(err) { + // Settings file does not exist. + return "", nil + } + + file, err := os.Open(buildAgentSettingsPath()) + if err != nil { + return "", errors.Wrap(err, "Could not open build settings config") + } + defer file.Close() + + var settings buildAgentSettings + + if err := json.NewDecoder(file).Decode(&settings); err != nil { + + return "", errors.Wrap(err, "Could not parse build settings config") + } + + return settings.LatestSha256, nil +} + +func buildAgentSettingsPath() string { + return path.Join(settings.SettingsPath(), "build_agent_settings.json") +} + // Write data to a temp file, and return the path to that file. func writeStringToTempFile(data string) (string, error) { // It's important to specify `/tmp` here as the location of the temp file. From 240bc9a6bf44aee097a46e111be799c3940e95d3 Mon Sep 17 00:00:00 2001 From: rlegan Date: Wed, 11 Jan 2023 14:47:19 +0100 Subject: [PATCH 28/84] fix: Making the job mandatory when running `circleci local execute` --- cmd/build.go | 10 ++++++++-- local/local.go | 10 +++++----- local/local_test.go | 7 ++++--- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index 1d6192f2c..9d1d773f2 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -7,12 +7,18 @@ import ( ) func newLocalExecuteCommand(config *settings.Config) *cobra.Command { + var args []string buildCommand := &cobra.Command{ - Use: "execute", + Use: "execute ", Short: "Run a job in a container on the local machine", + PreRunE: func(cmd *cobra.Command, _args []string) error { + args = _args + return nil + }, RunE: func(cmd *cobra.Command, _ []string) error { - return local.Execute(cmd.Flags(), config) + return local.Execute(cmd.Flags(), config, args) }, + Args: cobra.MinimumNArgs(1), } local.AddFlagsForDocumentation(buildCommand.Flags()) diff --git a/local/local.go b/local/local.go index bed9cc42a..373a88dd2 100644 --- a/local/local.go +++ b/local/local.go @@ -24,7 +24,7 @@ var picardRepo = "circleci/picard" const DefaultConfigPath = ".circleci/config.yml" -func Execute(flags *pflag.FlagSet, cfg *settings.Config) error { +func Execute(flags *pflag.FlagSet, cfg *settings.Config, args []string) error { var err error var configResponse *api.ConfigResponse cl := graphql.NewClient(cfg.HTTPClient, cfg.Host, cfg.Endpoint, cfg.Token, cfg.Debug) @@ -82,7 +82,8 @@ func Execute(flags *pflag.FlagSet, cfg *settings.Config) error { return errors.Wrap(err, "Could not find picard image") } - arguments := generateDockerCommand(processedConfigPath, image, pwd, processedArgs...) + job := args[0] + arguments := generateDockerCommand(processedConfigPath, image, pwd, job, processedArgs...) if cfg.Debug { _, err = fmt.Fprintf(os.Stderr, "Starting docker with args: %s", arguments) @@ -107,7 +108,6 @@ func Execute(flags *pflag.FlagSet, cfg *settings.Config) error { // are public in the original command. func AddFlagsForDocumentation(flags *pflag.FlagSet) { flags.StringP("config", "c", DefaultConfigPath, "config file") - flags.String("job", "build", "job to be executed") flags.Int("node-total", 1, "total number of parallel nodes") flags.Int("index", 0, "node index of parallelism") flags.Bool("skip-checkout", true, "use local path as-is") @@ -275,7 +275,7 @@ func writeStringToTempFile(data string) (string, error) { return f.Name(), nil } -func generateDockerCommand(configPath, image, pwd string, arguments ...string) []string { +func generateDockerCommand(configPath, image, pwd string, job string, arguments ...string) []string { const configPathInsideContainer = "/tmp/local_build_config.yml" core := []string{"docker", "run", "--interactive", "--tty", "--rm", "--volume", "/var/run/docker.sock:/var/run/docker.sock", @@ -283,7 +283,7 @@ func generateDockerCommand(configPath, image, pwd string, arguments ...string) [ "--volume", fmt.Sprintf("%s:%s", pwd, pwd), "--volume", fmt.Sprintf("%s:/root/.circleci", settings.SettingsPath()), "--workdir", pwd, - image, "circleci", "build", "--config", configPathInsideContainer} + image, "circleci", "build", "--config", configPathInsideContainer, "--job", job} return append(core, arguments...) } diff --git a/local/local_test.go b/local/local_test.go index 6b9630745..c316f045b 100644 --- a/local/local_test.go +++ b/local/local_test.go @@ -17,7 +17,7 @@ var _ = Describe("build", func() { It("can generate a command line", func() { home, err := os.UserHomeDir() Expect(err).NotTo(HaveOccurred()) - Expect(generateDockerCommand("/config/path", "docker-image-name", "/current/directory", "extra-1", "extra-2")).To(ConsistOf( + Expect(generateDockerCommand("/config/path", "docker-image-name", "/current/directory", "build", "extra-1", "extra-2")).To(ConsistOf( "docker", "run", "--interactive", @@ -30,6 +30,7 @@ var _ = Describe("build", func() { "--workdir", "/current/directory", "docker-image-name", "circleci", "build", "--config", "/tmp/local_build_config.yml", + "--job", "build", "extra-1", "extra-2", )) }) @@ -92,9 +93,9 @@ var _ = Describe("build", func() { }), Entry("many args", TestCase{ - input: []string{"--job", "horse", "--config", "foo", "--index", "9", "d"}, + input: []string{"--config", "foo", "--index", "9", "d"}, expectedConfigPath: "foo", - expectedArgs: []string{"--index", "9", "--job", "horse", "d"}, + expectedArgs: []string{"--index", "9", "d"}, }), Entry("many args, multiple envs", TestCase{ From 99feff36ca9eca7348f7a5e3074d3004cbbc5136 Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Tue, 17 Jan 2023 17:10:19 +0100 Subject: [PATCH 29/84] renamed master to main --- .circleci/config.yml | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 4 ++-- README.md | 14 +++++++------- git/git.go | 2 +- md_docs/md_docs.go | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 63683b77a..b4c0273ce 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -345,4 +345,4 @@ workflows: - shellcheck/check filters: branches: - only: master + only: main diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 096fcb5c8..e0775f58e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,7 +6,7 @@ - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have checked for similar issues and haven't found anything relevant. - [ ] This is not a security issue (which should be reported here: https://circleci.com/security/) -- [ ] I have read [Contribution Guidelines](https://github.com/CircleCI-Public/circleci-cli/blob/master/CONTRIBUTING.md). +- [ ] I have read [Contribution Guidelines](https://github.com/CircleCI-Public/circleci-cli/blob/main/CONTRIBUTING.md). ### Internal Checklist - [ ] I am requesting a review from my own team as well as the owning team @@ -45,7 +45,7 @@ Image or gif where change can be clearly seen ## **Here are some helpful tips you can follow when submitting a pull request:** -1. Fork [the repository](https://github.com/CircleCI-Public/circleci-cli) and create your branch from `master`. +1. Fork [the repository](https://github.com/CircleCI-Public/circleci-cli) and create your branch from `main`. 2. Run `make build` in the repository root. 3. If you've fixed a bug or added code that should be tested, add tests! 4. Ensure the test suite passes (`make test`). diff --git a/README.md b/README.md index f5411624a..a90c85c92 100644 --- a/README.md +++ b/README.md @@ -41,25 +41,25 @@ choco install circleci-cli -y You can also install the CLI binary by running our install script on most Unix platforms: ``` -curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/master/install.sh | bash +curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | bash ``` By default, the `circleci` app will be installed to the ``/usr/local/bin`` directory. If you do not have write permissions to `/usr/local/bin`, you may need to run the above command with `sudo`: ``` -curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/master/install.sh | sudo bash +curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | sudo bash ``` Alternatively, you can install to an alternate location by defining the `DESTDIR` environment variable when invoking `bash`: ``` -curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/master/install.sh | DESTDIR=/opt/bin bash +curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | DESTDIR=/opt/bin bash ``` You can also set a specific version of the CLI to install with the `VERSION` environment variable: ``` -curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/master/install.sh | VERSION=0.1.5222 sudo bash +curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | VERSION=0.1.5222 sudo bash ``` ### Updating @@ -135,17 +135,17 @@ The following commands are affected: ## Platforms, Deployment and Package Managers -The tool is deployed through a number of channels. The primary release channel is through [GitHub Releases](https://github.com/CircleCI-Public/circleci-cli/releases). Green builds on the `master` branch will publish a new GitHub release. These releases contain binaries for macOS, Linux and Windows. These releases are published from (CircleCI)[https://app.circleci.com/pipelines/github/CircleCI-Public/circleci-cli] using (GoReleaser)[https://goreleaser.com/]. +The tool is deployed through a number of channels. The primary release channel is through [GitHub Releases](https://github.com/CircleCI-Public/circleci-cli/releases). Green builds on the `main` branch will publish a new GitHub release. These releases contain binaries for macOS, Linux and Windows. These releases are published from (CircleCI)[https://app.circleci.com/pipelines/github/CircleCI-Public/circleci-cli] using (GoReleaser)[https://goreleaser.com/]. ### Homebrew -We publish the tool to [Homebrew](https://brew.sh/). The tool is [part of `homebrew-core`](https://github.com/Homebrew/homebrew-core/blob/master/Formula/circleci.rb), and therefore the maintainers of the tool are obligated to follow the guidelines for acceptable Homebrew formulae. You should [familairise yourself with the guidelines](https://docs.brew.sh/Acceptable-Formulae#we-dont-like-tools-that-upgrade-themselves) before making changes to the Homebrew deployment system. +We publish the tool to [Homebrew](https://brew.sh/). The tool is [part of `homebrew-core`](https://github.com/Homebrew/homebrew-core/blob/main/Formula/circleci.rb), and therefore the maintainers of the tool are obligated to follow the guidelines for acceptable Homebrew formulae. You should [familairise yourself with the guidelines](https://docs.brew.sh/Acceptable-Formulae#we-dont-like-tools-that-upgrade-themselves) before making changes to the Homebrew deployment system. The particular considerations that we make are: 1. Since Homebrew [doesn't "like tools that upgrade themselves"](https://docs.brew.sh/Acceptable-Formulae#we-dont-like-tools-that-upgrade-themselves), we disable the `circleci update` command when the tool is released through homebrew. We do this by [defining the PackageManager](https://github.com/Homebrew/homebrew-core/blob/eb1fdb84e2924289bcc8c85ee45081bf83dc024d/Formula/circleci.rb#L28) constant to `homebrew`, which allows us to [disable the `update` command at runtime](https://github.com/CircleCI-Public/circleci-cli/blob/67c7d52bace63846f87a1ed79f67f257c94a55b4/cmd/root.go#L119-L123). -1. We want to avoid every push to `master` from creating a Pull Request to the `circleci` formula on Homebrew. We want to avoid overloading the Homebrew team with pull requests to update our formula for small changes (changes to docs or other files that don't change functionality in the tool). +1. We want to avoid every push to `main` from creating a Pull Request to the `circleci` formula on Homebrew. We want to avoid overloading the Homebrew team with pull requests to update our formula for small changes (changes to docs or other files that don't change functionality in the tool). ### Snap diff --git a/git/git.go b/git/git.go index c5802e7b7..23c67453f 100644 --- a/git/git.go +++ b/git/git.go @@ -119,7 +119,7 @@ func Branch() string { // `git rev-parse` works in all versions. return commandOutputOrDefault( exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD"), - "master") + "main") } func Revision() string { diff --git a/md_docs/md_docs.go b/md_docs/md_docs.go index 9106511d7..dc2aae99a 100644 --- a/md_docs/md_docs.go +++ b/md_docs/md_docs.go @@ -15,9 +15,9 @@ import ( var introHeader = ` [Readme](https://github.com/CircleCI-Public/circleci-cli#readme) | -[Code of Conduct](https://github.com/CircleCI-Public/circleci-cli/blob/master/CODE_OF_CONDUCT.md) | -[Contribution Guidelines](https://github.com/CircleCI-Public/circleci-cli/blob/master/CONTRIBUTING.md) | -[Hacking](https://github.com/CircleCI-Public/circleci-cli/blob/master/HACKING.md) +[Code of Conduct](https://github.com/CircleCI-Public/circleci-cli/blob/main/CODE_OF_CONDUCT.md) | +[Contribution Guidelines](https://github.com/CircleCI-Public/circleci-cli/blob/main/CONTRIBUTING.md) | +[Hacking](https://github.com/CircleCI-Public/circleci-cli/blob/main/HACKING.md) [![CircleCI](https://circleci.com/gh/CircleCI-Public/circleci-cli.svg?style=svg)](https://circleci.com/gh/CircleCI-Public/circleci-cli) [![GitHub release](https://img.shields.io/github/tag/CircleCI-Public/circleci-cli.svg?label=latest)](https://github.com/CircleCI-Public/circleci-cli/releases) From a8d8a2edd7304034adaca7a380060864ceab3e9d Mon Sep 17 00:00:00 2001 From: rlegan Date: Tue, 24 Jan 2023 11:27:25 +0100 Subject: [PATCH 30/84] fix: make sure we're using token in every graphql request --- api/api.go | 9 +++++---- cmd/config_test.go | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/api/api.go b/api/api.go index a88272e85..75fd208a9 100644 --- a/api/api.go +++ b/api/api.go @@ -543,6 +543,7 @@ func ConfigQueryLegacy(cl *graphql.Client, configPath string, orgSlug string, pa fieldAddendums) request := graphql.NewRequest(query) + request.SetToken(cl.Token) request.Var("config", config) if values != nil { @@ -560,8 +561,6 @@ func ConfigQueryLegacy(cl *graphql.Client, configPath string, orgSlug string, pa request.Var("orgSlug", orgSlug) } - request.SetToken(cl.Token) - err = cl.Run(request, &response) if err != nil { return nil, errors.Wrap(err, "Unable to validate config") @@ -603,6 +602,7 @@ func ConfigQuery(cl *graphql.Client, configPath string, orgId string, params pip fieldAddendums) request := graphql.NewRequest(query) + request.SetToken(cl.Token) request.Var("config", config) if values != nil { @@ -619,7 +619,6 @@ func ConfigQuery(cl *graphql.Client, configPath string, orgId string, params pip if orgId != "" { request.Var("orgId", orgId) } - request.SetToken(cl.Token) err = cl.Run(request, &response) if err != nil { @@ -1634,6 +1633,7 @@ query namespaceOrbs ($namespace: String, $after: String!) { for { request := graphql.NewRequest(query) + request.SetToken(cl.Token) request.Var("after", currentCursor) request.Var("namespace", namespace) @@ -1813,7 +1813,7 @@ func OrbCategoryID(cl *graphql.Client, name string) (*OrbCategoryIDResponse, err }` request := graphql.NewRequest(query) - + request.SetToken(cl.Token) request.Var("name", name) err := cl.Run(request, &response) @@ -1917,6 +1917,7 @@ func ListOrbCategories(cl *graphql.Client) (*OrbCategoriesForListing, error) { for { request := graphql.NewRequest(query) + request.SetToken(cl.Token) request.Var("after", currentCursor) err := cl.Run(request, &result) diff --git a/cmd/config_test.go b/cmd/config_test.go index e03177f8d..85b6e8632 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -176,6 +176,7 @@ var _ = Describe("Config", func() { }` r := graphql.NewRequest(query) + r.SetToken(token) r.Variables["config"] = config r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) @@ -253,6 +254,7 @@ var _ = Describe("Config", func() { }` r := graphql.NewRequest(query) + r.SetToken(token) r.Variables["config"] = config r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) @@ -317,6 +319,7 @@ var _ = Describe("Config", func() { }` r := graphql.NewRequest(query) + r.SetToken(token) r.Variables["config"] = config r.Variables["orgId"] = orgId r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) @@ -379,6 +382,7 @@ var _ = Describe("Config", func() { }` r := graphql.NewRequest(query) + r.SetToken(token) r.Variables["config"] = config r.Variables["orgSlug"] = orgSlug r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) From 2fea0c67bcef33317703db61ec0ffa421f75cb5b Mon Sep 17 00:00:00 2001 From: Sujeet Kumar Date: Wed, 25 Jan 2023 21:03:32 -0500 Subject: [PATCH 31/84] update circle-policy-agent to v0.0.457 - pretty-print etal --- go.mod | 20 ++++++++++---------- go.sum | 52 +++++++++++++++++++++++++++------------------------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index 02bcf8e65..1114a1afc 100644 --- a/go.mod +++ b/go.mod @@ -2,18 +2,18 @@ module github.com/CircleCI-Public/circleci-cli require ( github.com/AlecAivazis/survey/v2 v2.1.1 - github.com/CircleCI-Public/circle-policy-agent v0.0.383 + github.com/CircleCI-Public/circle-policy-agent v0.0.457 github.com/Masterminds/semver v1.4.2 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/blang/semver v3.5.1+incompatible github.com/briandowns/spinner v1.18.1 - github.com/fatih/color v1.9.0 + github.com/fatih/color v1.13.0 github.com/go-git/go-git/v5 v5.1.0 github.com/google/go-github v15.0.0+incompatible // indirect github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect github.com/google/uuid v1.3.0 github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/mitchellh/mapstructure v1.4.1 github.com/olekukonko/tablewriter v0.0.5 github.com/onsi/ginkgo v1.16.4 @@ -35,7 +35,7 @@ require ( github.com/charmbracelet/lipgloss v0.5.0 github.com/erikgeiser/promptkit v0.7.0 github.com/hexops/gotextdiff v1.0.3 - golang.org/x/exp v0.0.0-20221114191408-850992195362 + golang.org/x/exp v0.0.0-20230118134722-a68e582fa157 ) require ( @@ -60,7 +60,7 @@ require ( github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-colorable v0.1.4 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect @@ -70,7 +70,7 @@ require ( github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.12.0 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/open-policy-agent/opa v0.47.3 // indirect + github.com/open-policy-agent/opa v0.48.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect @@ -82,10 +82,10 @@ require ( github.com/yashtewari/glob-intersection v0.1.0 // indirect github.com/yazgazan/jaydiff v0.3.1 // indirect golang.org/x/crypto v0.3.0 // indirect - golang.org/x/net v0.4.0 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/term v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect + golang.org/x/net v0.5.0 // indirect + golang.org/x/sys v0.4.0 // indirect + golang.org/x/term v0.4.0 // indirect + golang.org/x/text v0.6.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index 2266e2ee9..45bf0f77d 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eim github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CircleCI-Public/circle-policy-agent v0.0.383 h1:lgnCkN7GN1oFq9LtE3mY3fP8kkHkJXjC5eaFBbOuDzM= -github.com/CircleCI-Public/circle-policy-agent v0.0.383/go.mod h1:8tm87Gw46Jht5JNcvAaspjZt18fRvDkyL0/WRU41l0A= +github.com/CircleCI-Public/circle-policy-agent v0.0.457 h1:dsNC6/uo5Fk2Ji7wYAQ+H4ZOMVXhYxH3tDKFJ3Zazog= +github.com/CircleCI-Public/circle-policy-agent v0.0.457/go.mod h1:lsP7UOPtBj0+IYFdL46vdoPaPJitURRvhmhiE4uRpeg= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= @@ -85,7 +85,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgraph-io/badger/v3 v3.2103.4 h1:WE1B07YNTTJTtG9xjBcSW2wn0RJLyiV99h959RKZqM4= +github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= @@ -99,8 +99,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/erikgeiser/promptkit v0.7.0 h1:Yi28iN6JRs8/0x+wjQRPfWb+vWz1pFmZ5fu2uoFipD8= github.com/erikgeiser/promptkit v0.7.0/go.mod h1:Jj9bhN+N8RbMjB1jthkr9A4ydmczZ1WZJ8xTXnP12dg= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897 h1:E52jfcE64UG42SwLmrW0QByONfGynWuzBvm86BoB9z8= @@ -214,7 +214,7 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= +github.com/klauspost/compress v1.15.14 h1:i7WCKDToww0wA+9qrUZ1xOjp218vfFo3nTU6UHp+gOc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -228,20 +228,22 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 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.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 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/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d h1:eAS2t2Vy+6psf9LZ4T5WXWsbkBt3Tu5PWekJy5AGyEU= github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d/go.mod h1:3YMHqrw2Qu3Liy82v4QdAG17e9k91HZ7w3hqlpWqhDo= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= @@ -277,8 +279,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/open-policy-agent/opa v0.47.3 h1:Uj8zw+q6Cvv1iiQFh704Q6sl3fKVvk35WZNJLsd6mgk= -github.com/open-policy-agent/opa v0.47.3/go.mod h1:I5DbT677OGqfk9gvu5i54oIt0rrVf4B5pedpqDquAXo= +github.com/open-policy-agent/opa v0.48.0 h1:s2K823yohAUu/HB4MOPWDhBh88JMKQv7uTr6S89fbM0= +github.com/open-policy-agent/opa v0.48.0/go.mod h1:CsQcksP+qGBxO9oEBj1NnZqKcjgjmTJbRNTzjZB/DXQ= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -343,7 +345,7 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -365,8 +367,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20221114191408-850992195362 h1:NoHlPRbyl1VFI6FjwHtPQCN7wAMXI6cKcqrmXhOOfBQ= -golang.org/x/exp v0.0.0-20221114191408-850992195362/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230118134722-a68e582fa157 h1:fiNkyhJPUvxbRPbCqY/D9qdjmPzfHcpK3P4bM4gioSY= +golang.org/x/exp v0.0.0-20230118134722-a68e582fa157/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -417,8 +419,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -452,11 +454,11 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -481,21 +483,21 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 05d4308638b889499e635c547530aa8bfdb58792 Mon Sep 17 00:00:00 2001 From: Sujeet Kumar Date: Fri, 27 Jan 2023 11:10:31 -0500 Subject: [PATCH 32/84] trigger CI From 7f5868fba92a7ecac6fc949fa28d69d4eb81f42f Mon Sep 17 00:00:00 2001 From: Sujeet Kumar Date: Wed, 1 Feb 2023 13:24:47 -0500 Subject: [PATCH 33/84] trigger CI2 From b6e1c9ab65e96c310424036f8131619832327ffe Mon Sep 17 00:00:00 2001 From: Sujeet Kumar Date: Wed, 1 Feb 2023 13:27:09 -0500 Subject: [PATCH 34/84] trigger CI3 From 7c17232f84a1497aca6320e5f8fa4dd8b2d30592 Mon Sep 17 00:00:00 2001 From: rlegan Date: Wed, 1 Feb 2023 17:33:38 +0100 Subject: [PATCH 35/84] doc: Added doc about checksum verification --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index a90c85c92..3f9fdb9e4 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,22 @@ You can also set a specific version of the CLI to install with the `VERSION` env curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | VERSION=0.1.5222 sudo bash ``` +#### Checksum verification + +If you would like to verify the checksum yourself, you can download the checksum file from the [GitHub releases page](https://github.com/CircleCI-Public/circleci-cli/releases) and verify the checksum of the archive using the `circleci-cli__checksums.txt` inside the assets of the release you'd like to install: + +On macOS and Linux: +```sh +shasum -a 256 circleci-cli__.tar.gz +``` + +and on Windows: +```powershell +Get-FileHash .\circleci-cli__.tar.gz -Algorithm SHA256 | Format-List +``` + +And compare it to the right checksum depending on the downloaded version in the `circleci-cli__checksums.txt` file. + ### Updating If you installed the CLI without a package manager, you can use its built-in update command to check for pending updates and download them: From c8491913c3a318eba0e90aefca2ef25c04e12900 Mon Sep 17 00:00:00 2001 From: Sagar Gupta Date: Mon, 6 Feb 2023 08:26:52 -0500 Subject: [PATCH 36/84] bump circle-policy-agent version (#837) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1114a1afc..594654606 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/CircleCI-Public/circleci-cli require ( github.com/AlecAivazis/survey/v2 v2.1.1 - github.com/CircleCI-Public/circle-policy-agent v0.0.457 + github.com/CircleCI-Public/circle-policy-agent v0.0.462 github.com/Masterminds/semver v1.4.2 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/blang/semver v3.5.1+incompatible diff --git a/go.sum b/go.sum index 45bf0f77d..eb5e66a96 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eim github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CircleCI-Public/circle-policy-agent v0.0.457 h1:dsNC6/uo5Fk2Ji7wYAQ+H4ZOMVXhYxH3tDKFJ3Zazog= -github.com/CircleCI-Public/circle-policy-agent v0.0.457/go.mod h1:lsP7UOPtBj0+IYFdL46vdoPaPJitURRvhmhiE4uRpeg= +github.com/CircleCI-Public/circle-policy-agent v0.0.462 h1:WYSrA9MQGsqjjg19KJ3xve7CpH7KPiYg1JLD06SK/3k= +github.com/CircleCI-Public/circle-policy-agent v0.0.462/go.mod h1:lsP7UOPtBj0+IYFdL46vdoPaPJitURRvhmhiE4uRpeg= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= From 5cbf90e58053271341e25c0142b044a77a96271f Mon Sep 17 00:00:00 2001 From: threepipes Date: Tue, 16 Aug 2022 00:57:37 +0900 Subject: [PATCH 37/84] Commands added: - `circleci project list-secrets` Other changes: - Replace a request header `Accept-Type` with `Accept` --- api/project/project.go | 13 +++ api/project/project_rest.go | 104 ++++++++++++++++++ api/project/project_rest_test.go | 160 ++++++++++++++++++++++++++++ cmd/project/environment_variable.go | 46 ++++++++ cmd/project/project.go | 34 ++++++ cmd/project/project_test.go | 138 ++++++++++++++++++++++++ cmd/root.go | 2 + cmd/root_test.go | 2 +- 8 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 api/project/project.go create mode 100644 api/project/project_rest.go create mode 100644 api/project/project_rest_test.go create mode 100644 cmd/project/environment_variable.go create mode 100644 cmd/project/project.go create mode 100644 cmd/project/project_test.go diff --git a/api/project/project.go b/api/project/project.go new file mode 100644 index 000000000..442ea7576 --- /dev/null +++ b/api/project/project.go @@ -0,0 +1,13 @@ +package project + +// ProjectEnvironmentVariable is a Environment Variable of a Project +type ProjectEnvironmentVariable struct { + Name string + Value string +} + +// ProjectClient is the interface to interact with project and it's +// components. +type ProjectClient interface { + ListAllEnvironmentVariables(vcs, org, project string) ([]*ProjectEnvironmentVariable, error) +} diff --git a/api/project/project_rest.go b/api/project/project_rest.go new file mode 100644 index 000000000..1536f55ca --- /dev/null +++ b/api/project/project_rest.go @@ -0,0 +1,104 @@ +package project + +import ( + "fmt" + "net/url" + + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/settings" +) + +type projectRestClient struct { + token string + server string + client *rest.Client +} + +var _ ProjectClient = &projectRestClient{} + +type listProjectEnvVarsParams struct { + vcs string + org string + project string + pageToken string +} + +type projectEnvVarResponse struct { + Name string + Value string +} + +type listAllProjectEnvVarsResponse struct { + Items []projectEnvVarResponse + NextPageToken string `json:"next_page_token"` +} + +// NewProjectRestClient returns a new projectRestClient satisfying the api.ProjectInterface +// interface via the REST API. +func NewProjectRestClient(config settings.Config) (*projectRestClient, error) { + serverURL, err := config.ServerURL() + if err != nil { + return nil, err + } + + client := &projectRestClient{ + token: config.Token, + server: serverURL.String(), + client: rest.New(config.Host, &config), + } + + return client, nil +} + +// ListAllEnvironmentVariables returns all of the environment variables owned by the +// given project. Note that pagination is not supported - we get all +// pages of env vars and return them all. +func (p *projectRestClient) ListAllEnvironmentVariables(vcs, org, project string) ([]*ProjectEnvironmentVariable, error) { + res := make([]*ProjectEnvironmentVariable, 0) + var nextPageToken string + for { + resp, err := p.listEnvironmentVariables(&listProjectEnvVarsParams{ + vcs: vcs, + org: org, + project: project, + pageToken: nextPageToken, + }) + if err != nil { + return nil, err + } + + for _, ev := range resp.Items { + res = append(res, &ProjectEnvironmentVariable{ + Name: ev.Name, + Value: ev.Value, + }) + } + + if resp.NextPageToken == "" { + break + } + + nextPageToken = resp.NextPageToken + } + return res, nil +} + +func (c *projectRestClient) listEnvironmentVariables(params *listProjectEnvVarsParams) (*listAllProjectEnvVarsResponse, error) { + path := fmt.Sprintf("project/%s/%s/%s/envvar", params.vcs, params.org, params.project) + urlParams := url.Values{} + if params.pageToken != "" { + urlParams.Add("page-token", params.pageToken) + } + + req, err := c.client.NewRequest("GET", &url.URL{Path: path, RawQuery: urlParams.Encode()}, nil) + if err != nil { + return nil, err + } + + var resp listAllProjectEnvVarsResponse + _, err = c.client.DoRequest(req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/api/project/project_rest_test.go b/api/project/project_rest_test.go new file mode 100644 index 000000000..29d3305e1 --- /dev/null +++ b/api/project/project_rest_test.go @@ -0,0 +1,160 @@ +package project_test + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "reflect" + "testing" + + "github.com/CircleCI-Public/circleci-cli/api/project" + "github.com/CircleCI-Public/circleci-cli/settings" + "github.com/CircleCI-Public/circleci-cli/version" + "gotest.tools/v3/assert" +) + +func getProjectRestClient(server *httptest.Server) (project.ProjectClient, error) { + client := &http.Client{} + + return project.NewProjectRestClient(settings.Config{ + RestEndpoint: "api/v2", + Host: server.URL, + HTTPClient: client, + Token: "token", + }) +} + +func Test_projectRestClient_ListAllEnvironmentVariables(t *testing.T) { + const ( + vcsType = "github" + orgName = "test-org" + projName = "test-proj" + ) + tests := []struct { + name string + handler http.HandlerFunc + want []*project.ProjectEnvironmentVariable + wantErr bool + }{ + { + name: "Should handle a successful request with ListAllEnvironmentVariables", + handler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Header.Get("circle-token"), "token") + assert.Equal(t, r.Header.Get("accept"), "application/json") + assert.Equal(t, r.Header.Get("user-agent"), version.UserAgent()) + + assert.Equal(t, r.Method, "GET") + assert.Equal(t, r.URL.Path, fmt.Sprintf("/api/v2/project/%s/%s/%s/envvar", vcsType, orgName, projName)) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(` + { + "items": [{ + "name": "foo", + "value": "xxxx1234" + }], + "next_page_token": "" + }`)) + assert.NilError(t, err) + }, + want: []*project.ProjectEnvironmentVariable{ + { + Name: "foo", + Value: "xxxx1234", + }, + }, + }, + { + name: "Should handle a request containing next_page_token with ListAllEnvironmentVariables", + handler: func(w http.ResponseWriter, r *http.Request) { + u, err := url.ParseQuery(r.URL.RawQuery) + assert.NilError(t, err) + + w.Header().Set("content-type", "application/json") + w.WriteHeader(http.StatusOK) + if tk := u.Get("page-token"); tk == "" { + _, err := w.Write([]byte(` + { + "items": [ + { + "name": "foo1", + "value": "xxxx1234" + }, + { + "name": "foo2", + "value": "xxxx2345" + } + ], + "next_page_token": "pagetoken" + }`)) + assert.NilError(t, err) + } else { + assert.Equal(t, tk, "pagetoken") + _, err := w.Write([]byte(` + { + "items": [ + { + "name": "bar1", + "value": "xxxxabcd" + }, + { + "name": "bar2", + "value": "xxxxbcde" + } + ], + "next_page_token": "" + }`)) + assert.NilError(t, err) + } + }, + want: []*project.ProjectEnvironmentVariable{ + { + Name: "foo1", + Value: "xxxx1234", + }, + { + Name: "foo2", + Value: "xxxx2345", + }, + { + Name: "bar1", + Value: "xxxxabcd", + }, + { + Name: "bar2", + Value: "xxxxbcde", + }, + }, + }, + { + name: "Should handle an error request with ListAllEnvironmentVariables", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("content-type", "application/json") + w.WriteHeader(http.StatusInternalServerError) + _, err := w.Write([]byte(`{"message": "error"}`)) + assert.NilError(t, err) + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server := httptest.NewServer(tt.handler) + defer server.Close() + + p, err := getProjectRestClient(server) + assert.NilError(t, err) + + got, err := p.ListAllEnvironmentVariables(vcsType, orgName, projName) + if (err != nil) != tt.wantErr { + t.Errorf("projectRestClient.ListAllEnvironmentVariables() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("projectRestClient.ListAllEnvironmentVariables() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/project/environment_variable.go b/cmd/project/environment_variable.go new file mode 100644 index 000000000..2d97cf7ef --- /dev/null +++ b/cmd/project/environment_variable.go @@ -0,0 +1,46 @@ +package project + +import ( + projectapi "github.com/CircleCI-Public/circleci-cli/api/project" + "github.com/CircleCI-Public/circleci-cli/cmd/validator" + "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" +) + +func newProjectEnvironmentVariableCommand(ops *projectOpts, preRunE validator.Validator) *cobra.Command { + cmd := &cobra.Command{ + Use: "secret", + Short: "Operate on environment variables of projects", + } + + listVarsCommand := &cobra.Command{ + Short: "List all environment variables of a project", + Use: "list ", + PreRunE: preRunE, + RunE: func(cmd *cobra.Command, args []string) error { + return listProjectEnvironmentVariables(cmd, ops.client, args[0], args[1], args[2]) + }, + Args: cobra.ExactArgs(3), + } + + cmd.AddCommand(listVarsCommand) + return cmd +} + +func listProjectEnvironmentVariables(cmd *cobra.Command, client projectapi.ProjectClient, vcsType, orgName, projName string) error { + envVars, err := client.ListAllEnvironmentVariables(vcsType, orgName, projName) + if err != nil { + return err + } + + table := tablewriter.NewWriter(cmd.OutOrStdout()) + + table.SetHeader([]string{"Environment Variable", "Value"}) + + for _, envVar := range envVars { + table.Append([]string{envVar.Name, envVar.Value}) + } + table.Render() + + return nil +} diff --git a/cmd/project/project.go b/cmd/project/project.go new file mode 100644 index 000000000..2d7b20f06 --- /dev/null +++ b/cmd/project/project.go @@ -0,0 +1,34 @@ +package project + +import ( + projectapi "github.com/CircleCI-Public/circleci-cli/api/project" + "github.com/CircleCI-Public/circleci-cli/cmd/validator" + + "github.com/CircleCI-Public/circleci-cli/settings" + "github.com/spf13/cobra" +) + +type projectOpts struct { + client projectapi.ProjectClient +} + +// NewProjectCommand generates a cobra command for managing projects +func NewProjectCommand(config *settings.Config, preRunE validator.Validator) *cobra.Command { + var opts projectOpts + command := &cobra.Command{ + Use: "project", + Short: "Operate on projects", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + client, err := projectapi.NewProjectRestClient(*config) + if err != nil { + return err + } + opts.client = client + return nil + }, + } + + command.AddCommand(newProjectEnvironmentVariableCommand(&opts, preRunE)) + + return command +} diff --git a/cmd/project/project_test.go b/cmd/project/project_test.go new file mode 100644 index 000000000..1bf2674a7 --- /dev/null +++ b/cmd/project/project_test.go @@ -0,0 +1,138 @@ +package project_test + +import ( + "bytes" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/CircleCI-Public/circleci-cli/cmd/project" + "github.com/CircleCI-Public/circleci-cli/cmd/validator" + "github.com/CircleCI-Public/circleci-cli/settings" + "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" + "gotest.tools/v3/assert" +) + +const ( + vcsType = "github" + orgName = "test-org" + projectName = "test-project" +) + +func tableString(header []string, rows [][]string) string { + res := &strings.Builder{} + table := tablewriter.NewWriter(res) + table.SetHeader(header) + for _, r := range rows { + table.Append(r) + } + table.Render() + return res.String() +} + +func getListProjectsArg() []string { + return []string{ + "secret", + "list", + vcsType, + orgName, + projectName, + } +} + +func TestListSecrets(t *testing.T) { + var handler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Method, "GET") + assert.Equal(t, r.URL.String(), fmt.Sprintf("/project/%s/%s/%s/envvar", vcsType, orgName, projectName)) + response := `{ + "items": [{ + "name": "foo", + "value": "xxxx1234" + }], + "next_page_token": "" + }` + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(response)) + assert.NilError(t, err) + } + server := httptest.NewServer(handler) + defer server.Close() + + cmd, stdout, _ := scaffoldCMD( + server.URL, + func(cmd *cobra.Command, args []string) error { + return nil + }, + ) + cmd.SetArgs(getListProjectsArg()) + err := cmd.Execute() + assert.NilError(t, err) + + expect := tableString( + []string{"Environment Variable", "Value"}, + [][]string{{"foo", "xxxx1234"}}, + ) + res := stdout.String() + assert.Equal(t, res, expect) +} + +func TestListSecretsErrorWithValidator(t *testing.T) { + const errorMsg = "validator error" + var handler http.HandlerFunc = func(_ http.ResponseWriter, _ *http.Request) {} + server := httptest.NewServer(handler) + defer server.Close() + + cmd, _, _ := scaffoldCMD( + server.URL, + func(_ *cobra.Command, _ []string) error { + return fmt.Errorf(errorMsg) + }, + ) + cmd.SetArgs(getListProjectsArg()) + err := cmd.Execute() + assert.Error(t, err, errorMsg) +} + +func TestListSecretsErrorWithAPIResponse(t *testing.T) { + const errorMsg = "api error" + var handler http.HandlerFunc = func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + _, err := w.Write([]byte(fmt.Sprintf(`{"message": "%s"}`, errorMsg))) + assert.NilError(t, err) + } + server := httptest.NewServer(handler) + defer server.Close() + + cmd, _, _ := scaffoldCMD( + server.URL, + func(cmd *cobra.Command, args []string) error { + return nil + }, + ) + cmd.SetArgs(getListProjectsArg()) + err := cmd.Execute() + assert.Error(t, err, errorMsg) +} + +func scaffoldCMD( + baseURL string, + validator validator.Validator, +) (*cobra.Command, *bytes.Buffer, *bytes.Buffer) { + config := &settings.Config{ + Token: "testtoken", + HTTPClient: http.DefaultClient, + Host: baseURL, + } + cmd := project.NewProjectCommand(config, validator) + + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + cmd.SetOut(stdout) + cmd.SetErr(stderr) + + return cmd, stdout, stderr +} diff --git a/cmd/root.go b/cmd/root.go index f47ff96ea..6a636cd0c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -9,6 +9,7 @@ import ( "github.com/CircleCI-Public/circleci-cli/api/header" "github.com/CircleCI-Public/circleci-cli/cmd/info" "github.com/CircleCI-Public/circleci-cli/cmd/policy" + "github.com/CircleCI-Public/circleci-cli/cmd/project" "github.com/CircleCI-Public/circleci-cli/cmd/runner" "github.com/CircleCI-Public/circleci-cli/data" "github.com/CircleCI-Public/circleci-cli/md_docs" @@ -138,6 +139,7 @@ func MakeCommands() *cobra.Command { rootCmd.AddCommand(newOpenCommand()) rootCmd.AddCommand(newTestsCommand()) rootCmd.AddCommand(newContextCommand(rootOptions)) + rootCmd.AddCommand(project.NewProjectCommand(rootOptions, validator)) rootCmd.AddCommand(newQueryCommand(rootOptions)) rootCmd.AddCommand(newConfigCommand(rootOptions)) rootCmd.AddCommand(newOrbCommand(rootOptions)) diff --git a/cmd/root_test.go b/cmd/root_test.go index 01fbd7205..6ea4c5667 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -16,7 +16,7 @@ var _ = Describe("Root", func() { Describe("subcommands", func() { It("can create commands", func() { commands := cmd.MakeCommands() - Expect(len(commands.Commands())).To(Equal(22)) + Expect(len(commands.Commands())).To(Equal(23)) }) }) From 8d5e0a2646481355be248ff0f44b4540fff33a45 Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Tue, 14 Feb 2023 17:54:38 +0100 Subject: [PATCH 38/84] update dependencies for /api/policy (#843) --- go.mod | 6 +++--- go.sum | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 594654606..3585f3c1f 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/CircleCI-Public/circleci-cli require ( github.com/AlecAivazis/survey/v2 v2.1.1 - github.com/CircleCI-Public/circle-policy-agent v0.0.462 + github.com/CircleCI-Public/circle-policy-agent v0.0.469 github.com/Masterminds/semver v1.4.2 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/blang/semver v3.5.1+incompatible @@ -35,7 +35,7 @@ require ( github.com/charmbracelet/lipgloss v0.5.0 github.com/erikgeiser/promptkit v0.7.0 github.com/hexops/gotextdiff v1.0.3 - golang.org/x/exp v0.0.0-20230118134722-a68e582fa157 + golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb ) require ( @@ -70,7 +70,7 @@ require ( github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.12.0 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/open-policy-agent/opa v0.48.0 // indirect + github.com/open-policy-agent/opa v0.49.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect diff --git a/go.sum b/go.sum index eb5e66a96..b1a6e90a2 100644 --- a/go.sum +++ b/go.sum @@ -37,6 +37,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CircleCI-Public/circle-policy-agent v0.0.462 h1:WYSrA9MQGsqjjg19KJ3xve7CpH7KPiYg1JLD06SK/3k= github.com/CircleCI-Public/circle-policy-agent v0.0.462/go.mod h1:lsP7UOPtBj0+IYFdL46vdoPaPJitURRvhmhiE4uRpeg= +github.com/CircleCI-Public/circle-policy-agent v0.0.469 h1:qsMuMObnT+LVYra1ekK25ayot4qQ8j7WGv0IInT2heI= +github.com/CircleCI-Public/circle-policy-agent v0.0.469/go.mod h1:lsP7UOPtBj0+IYFdL46vdoPaPJitURRvhmhiE4uRpeg= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= @@ -281,6 +283,8 @@ github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/open-policy-agent/opa v0.48.0 h1:s2K823yohAUu/HB4MOPWDhBh88JMKQv7uTr6S89fbM0= github.com/open-policy-agent/opa v0.48.0/go.mod h1:CsQcksP+qGBxO9oEBj1NnZqKcjgjmTJbRNTzjZB/DXQ= +github.com/open-policy-agent/opa v0.49.0 h1:TIlpCT1B5FSm8Dqo/a4t23gKmHkQysC3+7W77F99P4k= +github.com/open-policy-agent/opa v0.49.0/go.mod h1:WTLWtu498/QNTDkiHx76Xj7jaJUPvLJAPtdMkCcst0w= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -369,6 +373,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20230118134722-a68e582fa157 h1:fiNkyhJPUvxbRPbCqY/D9qdjmPzfHcpK3P4bM4gioSY= golang.org/x/exp v0.0.0-20230118134722-a68e582fa157/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= From 901243e2ba1206e85a52f795a06748e8a951f559 Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Tue, 14 Feb 2023 18:08:40 +0100 Subject: [PATCH 39/84] fix: Fix the github pages ASCII art (#840) --- md_docs/md_docs.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/md_docs/md_docs.go b/md_docs/md_docs.go index dc2aae99a..36cef982a 100644 --- a/md_docs/md_docs.go +++ b/md_docs/md_docs.go @@ -170,6 +170,15 @@ func GenMarkdownTree(cmd *cobra.Command, dir string) error { // GenMarkdownTreeCustom is the the same as GenMarkdownTree, but // with custom filePrepender and linkHandler. func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error { + // There is a problem with the tool transforming the markdown into html, the tool transforms the + // circleci ascii art bad. By forcing it into a codeblock the formatting problem disappear + if cmd.Root() == cmd { + oldLong := cmd.Long + cmd.Long = fmt.Sprintf("```%s\n```", oldLong) + defer func() { + cmd.Long = oldLong + }() + } for _, c := range cmd.Commands() { if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() { continue From a9f9d15ea8595689014d7f6442eb0b3eaffc2385 Mon Sep 17 00:00:00 2001 From: Shogo Tsutsumi Date: Sat, 24 Sep 2022 19:13:24 +0900 Subject: [PATCH 40/84] A command `project secret create` added to create or update an environment variable --- api/project/project.go | 2 + api/project/project_rest.go | 53 +++++++ api/project/project_rest_test.go | 163 +++++++++++++++++++++ cmd/project/environment_variable.go | 59 ++++++++ cmd/project/project.go | 49 ++++++- cmd/project/project_test.go | 216 +++++++++++++++++++++++++++- 6 files changed, 537 insertions(+), 5 deletions(-) diff --git a/api/project/project.go b/api/project/project.go index 442ea7576..07947e43d 100644 --- a/api/project/project.go +++ b/api/project/project.go @@ -10,4 +10,6 @@ type ProjectEnvironmentVariable struct { // components. type ProjectClient interface { ListAllEnvironmentVariables(vcs, org, project string) ([]*ProjectEnvironmentVariable, error) + GetEnvironmentVariable(vcs, org, project, envName string) (*ProjectEnvironmentVariable, error) + CreateEnvironmentVariable(vcs, org, project string, v ProjectEnvironmentVariable) (*ProjectEnvironmentVariable, error) } diff --git a/api/project/project_rest.go b/api/project/project_rest.go index 1536f55ca..aae48f283 100644 --- a/api/project/project_rest.go +++ b/api/project/project_rest.go @@ -33,6 +33,11 @@ type listAllProjectEnvVarsResponse struct { NextPageToken string `json:"next_page_token"` } +type createProjectEnvVarRequest struct { + Name string `json:"name"` + Value string `json:"value"` +} + // NewProjectRestClient returns a new projectRestClient satisfying the api.ProjectInterface // interface via the REST API. func NewProjectRestClient(config settings.Config) (*projectRestClient, error) { @@ -102,3 +107,51 @@ func (c *projectRestClient) listEnvironmentVariables(params *listProjectEnvVarsP } return &resp, nil } + +// GetEnvironmentVariable retrieves and returns a variable with the given name. +// If the response status code is 404, nil is returned. +func (c *projectRestClient) GetEnvironmentVariable(vcs string, org string, project string, envName string) (*ProjectEnvironmentVariable, error) { + path := fmt.Sprintf("project/%s/%s/%s/envvar/%s", vcs, org, project, envName) + req, err := c.client.NewRequest("GET", &url.URL{Path: path}, nil) + if err != nil { + return nil, err + } + + var resp projectEnvVarResponse + code, err := c.client.DoRequest(req, &resp) + if err != nil { + if code == 404 { + // Note: 404 may mean that the project isn't found. + // The cause can't be distinguished except by the response text. + return nil, nil + } + return nil, err + } + return &ProjectEnvironmentVariable{ + Name: resp.Name, + Value: resp.Value, + }, nil +} + +// CreateEnvironmentVariable creates a variable on the given project. +// This returns the variable if successfully created. +func (c *projectRestClient) CreateEnvironmentVariable(vcs string, org string, project string, v ProjectEnvironmentVariable) (*ProjectEnvironmentVariable, error) { + path := fmt.Sprintf("project/%s/%s/%s/envvar", vcs, org, project) + req, err := c.client.NewRequest("POST", &url.URL{Path: path}, &createProjectEnvVarRequest{ + Name: v.Name, + Value: v.Value, + }) + if err != nil { + return nil, err + } + + var resp projectEnvVarResponse + _, err = c.client.DoRequest(req, &resp) + if err != nil { + return nil, err + } + return &ProjectEnvironmentVariable{ + Name: resp.Name, + Value: resp.Value, + }, nil +} diff --git a/api/project/project_rest_test.go b/api/project/project_rest_test.go index 29d3305e1..947512c82 100644 --- a/api/project/project_rest_test.go +++ b/api/project/project_rest_test.go @@ -1,6 +1,7 @@ package project_test import ( + "encoding/json" "fmt" "net/http" "net/http/httptest" @@ -158,3 +159,165 @@ func Test_projectRestClient_ListAllEnvironmentVariables(t *testing.T) { }) } } + +func Test_projectRestClient_GetEnvironmentVariable(t *testing.T) { + const ( + vcsType = "github" + orgName = "test-org" + projName = "test-proj" + ) + tests := []struct { + name string + handler http.HandlerFunc + envName string + want *project.ProjectEnvironmentVariable + wantErr bool + }{ + { + name: "Should handle a successful request", + handler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Header.Get("circle-token"), "token") + assert.Equal(t, r.Header.Get("accept"), "application/json") + assert.Equal(t, r.Header.Get("user-agent"), version.UserAgent()) + + assert.Equal(t, r.Method, "GET") + assert.Equal(t, r.URL.Path, fmt.Sprintf("/api/v2/project/%s/%s/%s/envvar/test1", vcsType, orgName, projName)) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(` + { + "name": "foo", + "value": "xxxx1234" + }`)) + assert.NilError(t, err) + }, + envName: "test1", + want: &project.ProjectEnvironmentVariable{ + Name: "foo", + Value: "xxxx1234", + }, + }, + { + name: "Should handle an error request", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("content-type", "application/json") + w.WriteHeader(http.StatusInternalServerError) + _, err := w.Write([]byte(`{"message": "error"}`)) + assert.NilError(t, err) + }, + wantErr: true, + }, + { + name: "Should handle an 404 error as a valid request", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("content-type", "application/json") + w.WriteHeader(http.StatusNotFound) + _, err := w.Write([]byte(`{"message": "Environment variable not found."}`)) + assert.NilError(t, err) + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server := httptest.NewServer(tt.handler) + defer server.Close() + + p, err := getProjectRestClient(server) + assert.NilError(t, err) + + got, err := p.GetEnvironmentVariable(vcsType, orgName, projName, tt.envName) + if (err != nil) != tt.wantErr { + t.Errorf("projectRestClient.GetEnvironmentVariable() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("projectRestClient.GetEnvironmentVariable() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_projectRestClient_CreateEnvironmentVariable(t *testing.T) { + const ( + vcsType = "github" + orgName = "test-org" + projName = "test-proj" + ) + tests := []struct { + name string + handler http.HandlerFunc + variable project.ProjectEnvironmentVariable + want *project.ProjectEnvironmentVariable + wantErr bool + }{ + { + name: "Should handle a successful request", + handler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Header.Get("circle-token"), "token") + assert.Equal(t, r.Header.Get("accept"), "application/json") + assert.Equal(t, r.Header.Get("user-agent"), version.UserAgent()) + + assert.Equal(t, r.Method, "POST") + assert.Equal(t, r.URL.Path, fmt.Sprintf("/api/v2/project/%s/%s/%s/envvar", vcsType, orgName, projName)) + var pv project.ProjectEnvironmentVariable + err := json.NewDecoder(r.Body).Decode(&pv) + assert.NilError(t, err) + assert.Equal(t, pv, project.ProjectEnvironmentVariable{ + Name: "foo", + Value: "test1234", + }) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, err = w.Write([]byte(` + { + "name": "foo", + "value": "xxxx1234" + }`)) + assert.NilError(t, err) + }, + variable: project.ProjectEnvironmentVariable{ + Name: "foo", + Value: "test1234", + }, + want: &project.ProjectEnvironmentVariable{ + Name: "foo", + Value: "xxxx1234", + }, + }, + { + name: "Should handle an error request", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("content-type", "application/json") + w.WriteHeader(http.StatusInternalServerError) + _, err := w.Write([]byte(`{"message": "error"}`)) + assert.NilError(t, err) + }, + variable: project.ProjectEnvironmentVariable{ + Name: "bar", + Value: "testbar", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server := httptest.NewServer(tt.handler) + defer server.Close() + + p, err := getProjectRestClient(server) + assert.NilError(t, err) + + got, err := p.CreateEnvironmentVariable(vcsType, orgName, projName, tt.variable) + if (err != nil) != tt.wantErr { + t.Errorf("projectRestClient.CreateEnvironmentVariable() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("projectRestClient.CreateEnvironmentVariable() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/project/environment_variable.go b/cmd/project/environment_variable.go index 2d97cf7ef..ff0b5907f 100644 --- a/cmd/project/environment_variable.go +++ b/cmd/project/environment_variable.go @@ -1,6 +1,9 @@ package project import ( + "fmt" + "strings" + projectapi "github.com/CircleCI-Public/circleci-cli/api/project" "github.com/CircleCI-Public/circleci-cli/cmd/validator" "github.com/olekukonko/tablewriter" @@ -23,7 +26,21 @@ func newProjectEnvironmentVariableCommand(ops *projectOpts, preRunE validator.Va Args: cobra.ExactArgs(3), } + var envValue string + createVarCommand := &cobra.Command{ + Short: "Create an environment variable of a project. The value is read from stdin.", + Use: "create ", + PreRunE: preRunE, + RunE: func(cmd *cobra.Command, args []string) error { + return createProjectEnvironmentVariable(cmd, ops.client, ops.reader, args[0], args[1], args[2], args[3], envValue) + }, + Args: cobra.ExactArgs(4), + } + + createVarCommand.Flags().StringVar(&envValue, "env-value", "", "An environment variable value to be created. You can also pass it by stdin without this option.") + cmd.AddCommand(listVarsCommand) + cmd.AddCommand(createVarCommand) return cmd } @@ -44,3 +61,45 @@ func listProjectEnvironmentVariables(cmd *cobra.Command, client projectapi.Proje return nil } + +func createProjectEnvironmentVariable(cmd *cobra.Command, client projectapi.ProjectClient, r UserInputReader, vcsType, orgName, projName, name, value string) error { + if value == "" { + val, err := r.ReadSecretString("Enter an environment variable value and press enter") + if err != nil { + return err + } + if val == "" { + return fmt.Errorf("the environment variable value must not be empty") + } + value = val + } + value = strings.Trim(value, "\r\n") + + existV, err := client.GetEnvironmentVariable(vcsType, orgName, projName, name) + if err != nil { + return err + } + if existV != nil { + msg := fmt.Sprintf("The environment variable name=%s value=%s already exists. Do you overwrite it?", existV.Name, existV.Value) + if !r.AskConfirm(msg) { + fmt.Fprintln(cmd.OutOrStdout(), "Canceled") + return nil + } + } + + v, err := client.CreateEnvironmentVariable(vcsType, orgName, projName, projectapi.ProjectEnvironmentVariable{ + Name: name, + Value: value, + }) + if err != nil { + return err + } + + table := tablewriter.NewWriter(cmd.OutOrStdout()) + + table.SetHeader([]string{"Environment Variable", "Value"}) + table.Append([]string{v.Name, v.Value}) + table.Render() + + return nil +} diff --git a/cmd/project/project.go b/cmd/project/project.go index 2d7b20f06..d0275619a 100644 --- a/cmd/project/project.go +++ b/cmd/project/project.go @@ -3,18 +3,46 @@ package project import ( projectapi "github.com/CircleCI-Public/circleci-cli/api/project" "github.com/CircleCI-Public/circleci-cli/cmd/validator" + "github.com/CircleCI-Public/circleci-cli/prompt" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/spf13/cobra" ) +// UserInputReader displays a message and reads a user input value +type UserInputReader interface { + ReadSecretString(msg string) (string, error) + AskConfirm(msg string) bool +} + type projectOpts struct { client projectapi.ProjectClient + reader UserInputReader +} + +// ProjectOption configures a command created by NewProjectCommand +type ProjectOption interface { + apply(*projectOpts) +} + +type promptReader struct{} + +func (p promptReader) ReadSecretString(msg string) (string, error) { + return prompt.ReadSecretStringFromUser(msg) +} + +func (p promptReader) AskConfirm(msg string) bool { + return prompt.AskUserToConfirm(msg) } // NewProjectCommand generates a cobra command for managing projects -func NewProjectCommand(config *settings.Config, preRunE validator.Validator) *cobra.Command { - var opts projectOpts +func NewProjectCommand(config *settings.Config, preRunE validator.Validator, opts ...ProjectOption) *cobra.Command { + pos := projectOpts{ + reader: &promptReader{}, + } + for _, o := range opts { + o.apply(&pos) + } command := &cobra.Command{ Use: "project", Short: "Operate on projects", @@ -23,12 +51,25 @@ func NewProjectCommand(config *settings.Config, preRunE validator.Validator) *co if err != nil { return err } - opts.client = client + pos.client = client return nil }, } - command.AddCommand(newProjectEnvironmentVariableCommand(&opts, preRunE)) + command.AddCommand(newProjectEnvironmentVariableCommand(&pos, preRunE)) return command } + +type customReaderProjectOption struct { + r UserInputReader +} + +func (c customReaderProjectOption) apply(opts *projectOpts) { + opts.reader = c.r +} + +// CustomReader returns a ProjectOption that sets a given UserInputReader to a project command +func CustomReader(r UserInputReader) ProjectOption { + return customReaderProjectOption{r} +} diff --git a/cmd/project/project_test.go b/cmd/project/project_test.go index 1bf2674a7..21f006a62 100644 --- a/cmd/project/project_test.go +++ b/cmd/project/project_test.go @@ -2,9 +2,12 @@ package project_test import ( "bytes" + "encoding/json" "fmt" + "io" "net/http" "net/http/httptest" + "reflect" "strings" "testing" @@ -33,6 +36,17 @@ func tableString(header []string, rows [][]string) string { return res.String() } +func equalJSON(j1, j2 string) (bool, error) { + var j1i, j2i interface{} + if err := json.Unmarshal([]byte(j1), &j1i); err != nil { + return false, fmt.Errorf("failed to convert in equalJSON from '%s': %w", j1, err) + } + if err := json.Unmarshal([]byte(j2), &j2i); err != nil { + return false, fmt.Errorf("failed to convert in equalJSON from '%s': %w", j2, err) + } + return reflect.DeepEqual(j1i, j2i), nil +} + func getListProjectsArg() []string { return []string{ "secret", @@ -118,16 +132,216 @@ func TestListSecretsErrorWithAPIResponse(t *testing.T) { assert.Error(t, err, errorMsg) } +type testCreateSecretArgs struct { + variableVal string // ignored if --env-value flag is contained + statusCodeGet int + statusCodePost int // ignored if overwriting is canceled + isOverwrite bool // ignored if statusCodeGet is http.StatusNotFound + extraArgs []string +} + +func TestCreateSecret(t *testing.T) { + const ( + variableVal = "testvar1234" + variableKey = "foo" + ) + tests := []struct { + name string + args testCreateSecretArgs + want string + wantErr bool + }{ + { + name: "Create successfully without an existing key", + args: testCreateSecretArgs{ + variableVal: variableVal, + statusCodeGet: http.StatusNotFound, + statusCodePost: http.StatusOK, + extraArgs: []string{variableKey}, + }, + want: tableString( + []string{"Environment Variable", "Value"}, + [][]string{{"foo", "xxxx1234"}}, + ), + }, + { + name: "Overwrite successfully with an existing key", + args: testCreateSecretArgs{ + variableVal: variableVal, + statusCodeGet: http.StatusOK, + statusCodePost: http.StatusOK, + isOverwrite: true, + extraArgs: []string{variableKey}, + }, + want: tableString( + []string{"Environment Variable", "Value"}, + [][]string{{"foo", "xxxx1234"}}, + ), + }, + { + name: "Cancel overwriting an existing key", + args: testCreateSecretArgs{ + variableVal: variableVal, + statusCodeGet: http.StatusOK, + isOverwrite: false, + extraArgs: []string{variableKey}, + }, + want: fmt.Sprintln("Canceled"), + }, + { + name: "Pass a variable through a commandline argument", + args: testCreateSecretArgs{ + statusCodeGet: http.StatusNotFound, + statusCodePost: http.StatusOK, + extraArgs: []string{variableKey, "--env-value", variableVal}, + }, + want: tableString( + []string{"Environment Variable", "Value"}, + [][]string{{"foo", "xxxx1234"}}, + ), + }, + { + name: "Handle an error request from GetEnvironmentVariable", + args: testCreateSecretArgs{ + variableVal: variableVal, + statusCodeGet: http.StatusInternalServerError, + statusCodePost: http.StatusOK, + extraArgs: []string{variableKey}, + }, + wantErr: true, + }, + { + name: "Handle an error request from CreateEnvironmentVariable", + args: testCreateSecretArgs{ + variableVal: variableVal, + statusCodeGet: http.StatusNotFound, + statusCodePost: http.StatusInternalServerError, + extraArgs: []string{variableKey}, + }, + wantErr: true, + }, + { + name: "The process should be rejected if the passed value is empty", + args: testCreateSecretArgs{ + variableVal: "", + statusCodeGet: http.StatusNotFound, + statusCodePost: http.StatusOK, + extraArgs: []string{variableKey}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := testCreateSecret(t, &tt.args) + if (err != nil) != tt.wantErr { + t.Errorf("Create secret command: error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Create secret command: got = %v, want %v", got, tt.want) + } + }) + } +} + +type testInputReader struct { + secret string + yesNo bool +} + +func (s testInputReader) ReadSecretString(msg string) (string, error) { + return s.secret, nil +} + +func (s testInputReader) AskConfirm(msg string) bool { + return s.yesNo +} + +func testCreateSecret(t *testing.T, args *testCreateSecretArgs) (string, error) { + const apiResponseBody = `{ + "name": "foo", + "value": "xxxx1234" + }` + var handler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case "GET": + assert.Equal(t, r.URL.String(), fmt.Sprintf("/project/%s/%s/%s/envvar/foo", vcsType, orgName, projectName)) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(args.statusCodeGet) + if args.statusCodeGet == http.StatusOK { + _, err := w.Write([]byte(apiResponseBody)) + assert.NilError(t, err) + } + case "POST": + expect := `{ + "name": "foo", + "value": "testvar1234" + }` + assert.Equal(t, r.URL.String(), fmt.Sprintf("/project/%s/%s/%s/envvar", vcsType, orgName, projectName)) + isRequestBodyValid(t, r, expect) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(args.statusCodePost) + if args.statusCodePost == http.StatusOK { + _, err := w.Write([]byte(apiResponseBody)) + assert.NilError(t, err) + } + } + } + + server := httptest.NewServer(handler) + defer server.Close() + + cmd, stdout, _ := scaffoldCMD( + server.URL, + func(cmd *cobra.Command, args []string) error { + return nil + }, + project.CustomReader(testInputReader{ + secret: args.variableVal, + yesNo: args.isOverwrite, + }), + ) + cmd.SetArgs(append(getCreateSecretArgBase(), args.extraArgs...)) + + err := cmd.Execute() + if err != nil { + return "", err + } + + return stdout.String(), nil +} + +func getCreateSecretArgBase() []string { + return []string{ + "secret", + "create", + vcsType, + orgName, + projectName, + } +} + +func isRequestBodyValid(t *testing.T, r *http.Request, expect string) { + b, err := io.ReadAll(r.Body) + assert.NilError(t, err) + eq, err := equalJSON(string(b), expect) + assert.NilError(t, err) + assert.Equal(t, eq, true) +} + func scaffoldCMD( baseURL string, validator validator.Validator, + opts ...project.ProjectOption, ) (*cobra.Command, *bytes.Buffer, *bytes.Buffer) { config := &settings.Config{ Token: "testtoken", HTTPClient: http.DefaultClient, Host: baseURL, } - cmd := project.NewProjectCommand(config, validator) + cmd := project.NewProjectCommand(config, validator, opts...) stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) From 634103aef52c3f6360207a7e63ae419ac1b1baec Mon Sep 17 00:00:00 2001 From: David Desmarais-Michaud Date: Fri, 17 Feb 2023 10:21:54 -0500 Subject: [PATCH 41/84] Support policy test junit (#845) --- cmd/policy/policy.go | 17 +++++++--- cmd/policy/policy_test.go | 31 +++++++++++++++++++ .../testdata/policy/test-expected-usage.txt | 8 ++--- go.mod | 2 +- go.sum | 10 ++---- 5 files changed, 51 insertions(+), 17 deletions(-) diff --git a/cmd/policy/policy.go b/cmd/policy/policy.go index 8a3089de2..3a47ea2f1 100644 --- a/cmd/policy/policy.go +++ b/cmd/policy/policy.go @@ -434,6 +434,7 @@ This group of commands allows the management of polices to be verified against b verbose bool debug bool useJSON bool + format string ) cmd := &cobra.Command{ @@ -465,10 +466,17 @@ This group of commands allows the management of polices to be verified against b } handler := func() tester.ResultHandler { - if useJSON { + switch strings.ToLower(format) { + case "json": return tester.MakeJSONResultHandler(handlerOpts) + case "junit": + return tester.MakeJUnitResultHandler(handlerOpts) + default: + if useJSON { + return tester.MakeJSONResultHandler(handlerOpts) + } + return tester.MakeDefaultResultHandler(handlerOpts) } - return tester.MakeDefaultResultHandler(handlerOpts) }() if !runner.RunAndHandleResults(handler) { @@ -484,8 +492,9 @@ This group of commands allows the management of polices to be verified against b cmd.Flags().StringVar(&run, "run", "", "select which tests to run based on regular expression") cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "print all tests instead of only failed tests") cmd.Flags().BoolVar(&debug, "debug", false, "print test debug context. Sets verbose to true") - cmd.Flags().BoolVar(&useJSON, "json", false, "print json test results instead of standard output format") - + cmd.Flags().BoolVar(&useJSON, "json", false, "sprints json test results instead of standard output format") + _ = cmd.Flags().MarkDeprecated("json", "use --format=json to print json test results") + cmd.Flags().StringVar(&format, "format", "", "select desired format between json or junit") return cmd }() diff --git a/cmd/policy/policy_test.go b/cmd/policy/policy_test.go index d8279d4a0..f83135c0e 100644 --- a/cmd/policy/policy_test.go +++ b/cmd/policy/policy_test.go @@ -1047,6 +1047,8 @@ func TestGetSetSettings(t *testing.T) { } } +const jsonDeprecationMessage = "Flag --json has been deprecated, use --format=json to print json test results\n" + func TestTestRunner(t *testing.T) { cases := []struct { Name string @@ -1054,6 +1056,7 @@ func TestTestRunner(t *testing.T) { Debug bool Run string Json bool + Format string Expected func(*testing.T, string) }{ { @@ -1093,11 +1096,36 @@ func TestTestRunner(t *testing.T) { { Name: "json", Json: true, + Expected: func(t *testing.T, s string) { + assert.Check(t, strings.HasPrefix(s, jsonDeprecationMessage)) + assert.Check(t, s[len(jsonDeprecationMessage)] == '[') + assert.Check(t, s[len(s)-2] == ']') + }, + }, + { + Name: "format:json", + Format: "json", Expected: func(t *testing.T, s string) { assert.Check(t, s[0] == '[') assert.Check(t, s[len(s)-2] == ']') }, }, + { + Name: "format:junit", + Format: "junit", + Expected: func(t *testing.T, s string) { + assert.Check(t, strings.Contains(s, " Date: Sat, 18 Feb 2023 01:01:25 +0100 Subject: [PATCH 42/84] ci(release): Using devex-release as context --- .circleci/config.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b4c0273ce..b98345b83 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -270,9 +270,9 @@ jobs: brew-deploy: executor: mac environment: - - USER: circleci - - TRAVIS: circleci - - DESTDIR: /Users/distiller/dest + USER: circleci + TRAVIS: circleci + DESTDIR: /Users/distiller/dest steps: - checkout - force-http-1 @@ -335,6 +335,8 @@ workflows: - brew-deploy: requires: - run-brew-deploy-gate + context: + - devex-release - deploy: requires: - test @@ -346,3 +348,5 @@ workflows: filters: branches: only: main + context: + - devex-release From 106807c360ac0147e9644b2c969643f53d07bc7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 15:49:14 +0000 Subject: [PATCH 43/84] Bump golang.org/x/net from 0.5.0 to 0.7.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.5.0 to 0.7.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/compare/v0.5.0...v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index ef45e77e2..08936e94f 100644 --- a/go.mod +++ b/go.mod @@ -82,10 +82,10 @@ require ( github.com/yashtewari/glob-intersection v0.1.0 // indirect github.com/yazgazan/jaydiff v0.3.1 // indirect golang.org/x/crypto v0.3.0 // indirect - golang.org/x/net v0.5.0 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/term v0.4.0 // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index b1df311e3..3867081f7 100644 --- a/go.sum +++ b/go.sum @@ -419,8 +419,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -483,21 +483,21 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From c6dc6bf2a4af4eb8e4fc1fb51e9119f4bbc8ead9 Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Mon, 20 Feb 2023 16:53:15 +0100 Subject: [PATCH 44/84] fix: circleci orb list --uncertified --details no longer crashes (#851) --- cmd/orb.go | 33 ++++++++++++++++++---- cmd/orb_unit_test.go | 66 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 cmd/orb_unit_test.go diff --git a/cmd/orb.go b/cmd/orb.go index 096829be3..bb7fc910a 100644 --- a/cmd/orb.go +++ b/cmd/orb.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "io/ioutil" + "log" "net/http" "os" "path" @@ -390,6 +391,22 @@ func orbHelpLong(config *settings.Config) string { See a full explanation and documentation on orbs here: %s`, config.Data.Links.OrbDocs) } +// Transform a boolean parameter into a string. Because the value can be a boolean but can also be +// a string, we need to first parse it as a boolean and then if it is not a boolean, parse it as +// a string +// +// Documentation reference: https://circleci.com/docs/reusing-config/#boolean +func booleanParameterDefaultToString(parameter api.OrbElementParameter) string { + if v, ok := parameter.Default.(bool); ok { + return fmt.Sprintf("%t", v) + } + v, ok := parameter.Default.(string) + if !ok { + log.Panicf("Unable to parse boolean parameter with value %+v", v) + } + return v +} + func parameterDefaultToString(parameter api.OrbElementParameter) string { defaultValue := " (default: '" @@ -401,12 +418,18 @@ func parameterDefaultToString(parameter api.OrbElementParameter) string { } switch parameter.Type { - case "enum": - defaultValue += parameter.Default.(string) - case "string": - defaultValue += parameter.Default.(string) + case "enum", "string": + if v, ok := parameter.Default.(string); ok { + defaultValue += v + break + } + if v, ok := parameter.Default.(fmt.Stringer); ok { + defaultValue += v.String() + break + } + log.Panicf("Unable to parse parameter default with value %+v because it's neither a string nor a stringer", parameter.Default) case "boolean": - defaultValue += fmt.Sprintf("%t", parameter.Default.(bool)) + defaultValue += booleanParameterDefaultToString(parameter) default: defaultValue += "" } diff --git a/cmd/orb_unit_test.go b/cmd/orb_unit_test.go new file mode 100644 index 000000000..378d5650d --- /dev/null +++ b/cmd/orb_unit_test.go @@ -0,0 +1,66 @@ +package cmd + +import ( + "time" + + "github.com/CircleCI-Public/circleci-cli/api" + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" +) + +var _ = Describe("Orb unit tests", func() { + Describe("Orb formatters", func() { + DescribeTable( + "parameterDefaultToString", + func(input api.OrbElementParameter, expected string) { + Expect(parameterDefaultToString(input)).To(Equal(expected)) + }, + Entry( + "Normal behaviour for string", + api.OrbElementParameter{ + Type: "string", + Description: "", + Default: "Normal behavior", + }, + " (default: 'Normal behavior')", + ), + Entry( + "Normal behaviour for enum", + api.OrbElementParameter{ + Type: "enum", + Description: "", + Default: "Normal behavior", + }, + " (default: 'Normal behavior')", + ), + Entry( + "Normal behaviour for boolean", + api.OrbElementParameter{ + Type: "boolean", + Description: "", + Default: true, + }, + " (default: 'true')", + ), + Entry( + "String value for boolean", + api.OrbElementParameter{ + Type: "boolean", + Description: "", + Default: "yes", + }, + " (default: 'yes')", + ), + Entry( + "Time value for string", + api.OrbElementParameter{ + Type: "string", + Description: "", + Default: time.Date(2023, 02, 20, 11, 9, 0, 0, time.Now().UTC().Location()), + }, + " (default: '2023-02-20 11:09:00 +0000 UTC')", + ), + ) + }) +}) From 4983470d9be6fbc3a759a1a418ec1475a0e936d8 Mon Sep 17 00:00:00 2001 From: zbenhadi Date: Tue, 28 Feb 2023 10:32:50 +0100 Subject: [PATCH 45/84] task: Print filled pipeline values on validate & process --- cmd/config.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index 40344a079..e45ea86f9 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -138,15 +138,17 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { } //if no orgId provided use org slug + values := pipeline.LocalPipelineValues() + fmt.Println("Validating config with following values :\n", values) orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = api.ConfigQuery(opts.cl, path, orgID, nil, pipeline.LocalPipelineValues()) + response, err = api.ConfigQuery(opts.cl, path, orgID, nil, values) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - response, err = api.ConfigQueryLegacy(opts.cl, path, orgSlug, nil, pipeline.LocalPipelineValues()) + response, err = api.ConfigQueryLegacy(opts.cl, path, orgSlug, nil, values) if err != nil { return err } @@ -192,15 +194,17 @@ func processConfig(opts configOptions, flags *pflag.FlagSet) error { } //if no orgId provided use org slug + values := pipeline.LocalPipelineValues() + fmt.Println("Processing config with following values: \n", values) orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = api.ConfigQuery(opts.cl, opts.args[0], orgID, params, pipeline.LocalPipelineValues()) + response, err = api.ConfigQuery(opts.cl, opts.args[0], orgID, params, values) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - response, err = api.ConfigQueryLegacy(opts.cl, opts.args[0], orgSlug, params, pipeline.LocalPipelineValues()) + response, err = api.ConfigQueryLegacy(opts.cl, opts.args[0], orgSlug, params, values) if err != nil { return err } From 2fc068b1172a5d8dc3a480fa11645a36433a33d6 Mon Sep 17 00:00:00 2001 From: rooneyshuman Date: Tue, 28 Feb 2023 09:59:41 -0800 Subject: [PATCH 46/84] Bump policy agent to v0.0.564 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ef45e77e2..b51eade88 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/CircleCI-Public/circleci-cli require ( github.com/AlecAivazis/survey/v2 v2.1.1 - github.com/CircleCI-Public/circle-policy-agent v0.0.534 + github.com/CircleCI-Public/circle-policy-agent v0.0.564 github.com/Masterminds/semver v1.4.2 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/blang/semver v3.5.1+incompatible diff --git a/go.sum b/go.sum index b1df311e3..5b6a0383b 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eim github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CircleCI-Public/circle-policy-agent v0.0.534 h1:NekWjox7QQ2+K1QtNNPRP244JwLCLPze9Km3IOWTB+I= -github.com/CircleCI-Public/circle-policy-agent v0.0.534/go.mod h1:oySWkeJOAnvT56DRaGL0n2lHmeCEFwrhGg0Bvj9jiI8= +github.com/CircleCI-Public/circle-policy-agent v0.0.564 h1:i7CkwfUVL9fItTc1X9evcCgwmuiRsTOODcCmYrByu7s= +github.com/CircleCI-Public/circle-policy-agent v0.0.564/go.mod h1:oySWkeJOAnvT56DRaGL0n2lHmeCEFwrhGg0Bvj9jiI8= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= From b3c40caf9b290e4f60684bb726e149d9e4b46154 Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Thu, 2 Mar 2023 16:52:00 +0100 Subject: [PATCH 47/84] fix: Fix icon for chocolatey --- chocolatey/circleci-cli/circleci-cli.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chocolatey/circleci-cli/circleci-cli.nuspec b/chocolatey/circleci-cli/circleci-cli.nuspec index c8b7dbad4..c5c977d0c 100644 --- a/chocolatey/circleci-cli/circleci-cli.nuspec +++ b/chocolatey/circleci-cli/circleci-cli.nuspec @@ -14,7 +14,7 @@ circleci-cli (Install) CircleCI https://circleci.com/docs/2.0/local-cli/ - https://github.com/CircleCI-Public/circleci-cli/raw/master/chocolatey/icons/circleci-128x.png + https://github.com/CircleCI-Public/circleci-cli/raw/main/chocolatey/icons/circleci-128x.png https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/master/LICENSE true https://github.com/CircleCI-Public/circleci-cli From be54c9696b427d39a7de1bafcbbefe40c21043fe Mon Sep 17 00:00:00 2001 From: zbenhadi Date: Tue, 28 Feb 2023 15:14:21 +0100 Subject: [PATCH 48/84] task: Print filled pipeline values on validate & process --- cmd/config.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index e45ea86f9..5ae76fcde 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -139,7 +139,9 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { //if no orgId provided use org slug values := pipeline.LocalPipelineValues() - fmt.Println("Validating config with following values :\n", values) + fmt.Println("Validating config with following values") + printValues(values) + orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { response, err = api.ConfigQuery(opts.cl, path, orgID, nil, values) @@ -195,7 +197,9 @@ func processConfig(opts configOptions, flags *pflag.FlagSet) error { //if no orgId provided use org slug values := pipeline.LocalPipelineValues() - fmt.Println("Processing config with following values: \n", values) + fmt.Println("Processing config with following values") + printValues(values) + orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { response, err = api.ConfigQuery(opts.cl, opts.args[0], orgID, params, values) @@ -231,3 +235,9 @@ func packConfig(opts configOptions) error { func migrateConfig(opts configOptions) error { return proxy.Exec([]string{"config", "migrate"}, opts.args) } + +func printValues(values pipeline.Values) { + for key, value := range values { + fmt.Printf("\t%s:\t%s", key, value) + } +} From 137b0bcab0c9f124340d5e4c55a4340160c9d895 Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Thu, 23 Feb 2023 15:05:25 +0100 Subject: [PATCH 49/84] style: Added text informing your orb is not private --- cmd/orb.go | 16 ++++++++++------ cmd/orb_test.go | 13 ++++++++----- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/cmd/orb.go b/cmd/orb.go index bb7fc910a..bbd81858e 100644 --- a/cmd/orb.go +++ b/cmd/orb.go @@ -871,6 +871,16 @@ If you change your mind about the name, you will have to create a new orb with t `, namespace, orbName) } + if opts.private { + fmt.Printf(`This orb will not be listed on the registry and is usable only by org users. + +`) + } else { + fmt.Printf(`Please note that any versions you publish of this orb will be world readable unless you create it with the '--private' flag + +`) + } + confirm := fmt.Sprintf("Are you sure you wish to create the orb: `%s/%s`", namespace, orbName) if opts.noPrompt || opts.tty.askUserToConfirm(confirm) { @@ -880,13 +890,7 @@ If you change your mind about the name, you will have to create a new orb with t return err } - confirmationString := "Please note that any versions you publish of this orb are world-readable." - if opts.private { - confirmationString = "This orb will not be listed on the registry and is usable only by org users." - } - fmt.Printf("Orb `%s` created.\n", opts.args[0]) - fmt.Println(confirmationString) fmt.Printf("You can now register versions of `%s` using `circleci orb publish`.\n", opts.args[0]) } diff --git a/cmd/orb_test.go b/cmd/orb_test.go index 3a2f60dc4..e5a5f42b3 100644 --- a/cmd/orb_test.go +++ b/cmd/orb_test.go @@ -1167,8 +1167,9 @@ See a full explanation and documentation on orbs here: https://circleci.com/docs Eventually(session).Should(gexec.Exit(0)) stdout := session.Wait().Out.Contents() - Expect(string(stdout)).To(ContainSubstring(fmt.Sprintf(`Orb %s created. -Please note that any versions you publish of this orb are world-readable. + Expect(string(stdout)).To(ContainSubstring(fmt.Sprintf(`Please note that any versions you publish of this orb will be world readable unless you create it with the '--private' flag + +Orb %s created. You can now register versions of %s using %s`, "`bar-ns/foo-orb`", "`bar-ns/foo-orb`", "`circleci orb publish`"))) }) @@ -1232,8 +1233,9 @@ You can now register versions of %s using %s`, "`bar-ns/foo-orb`", "`bar-ns/foo- Eventually(session).Should(gexec.Exit(0)) stdout := session.Wait().Out.Contents() - Expect(string(stdout)).To(ContainSubstring(fmt.Sprintf(`Orb %s created. -This orb will not be listed on the registry and is usable only by org users. + Expect(string(stdout)).To(ContainSubstring(fmt.Sprintf(`This orb will not be listed on the registry and is usable only by org users. + +Orb %s created. You can now register versions of %s using %s`, "`bar-ns/foo-orb`", "`bar-ns/foo-orb`", "`circleci orb publish`"))) }) @@ -1365,9 +1367,10 @@ You will not be able to change the name of this orb. If you change your mind about the name, you will have to create a new orb with the new name. +Please note that any versions you publish of this orb will be world readable unless you create it with the '--private' flag + Are you sure you wish to create the orb: %s Orb %s created. -Please note that any versions you publish of this orb are world-readable. You can now register versions of %s using %s.`, "bar-ns/foo-orb", "`bar-ns/foo-orb`", "`bar-ns/foo-orb`", "`bar-ns/foo-orb`", "`circleci orb publish`"))) }) From b1392f2ca95bb6a054372d15818d425136110df6 Mon Sep 17 00:00:00 2001 From: threepipes Date: Tue, 7 Mar 2023 22:36:43 +0900 Subject: [PATCH 50/84] fix: adjust the window size of the help output --- cmd/root.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 6a636cd0c..6f3f33c7c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -17,6 +17,7 @@ import ( "github.com/CircleCI-Public/circleci-cli/version" "github.com/charmbracelet/lipgloss" "github.com/spf13/cobra" + "golang.org/x/term" ) var defaultEndpoint = "graphql-unstable" @@ -111,9 +112,16 @@ func MakeCommands() *cobra.Command { rootOptions.Data = &data.Data + helpWidth := getHelpWidth() + // CircleCI Logo will only appear with enough window width + longHelp := "" + if helpWidth > 85 { + longHelp = rootHelpLong() + } + rootCmd = &cobra.Command{ Use: "circleci", - Long: rootHelpLong(), + Long: longHelp, Short: rootHelpShort(rootOptions), PersistentPreRunE: func(_ *cobra.Command, _ []string) error { return rootCmdPreRun(rootOptions) @@ -126,7 +134,7 @@ func MakeCommands() *cobra.Command { cobra.AddTemplateFunc("FormatPositionalArg", md_docs.FormatPositionalArg) if os.Getenv("TESTING") != trueString { - helpCmd := helpCmd{cmd: rootCmd} + helpCmd := helpCmd{width: helpWidth} rootCmd.SetHelpFunc(helpCmd.helpTemplate) } rootCmd.SetUsageTemplate(usageTemplate) @@ -325,7 +333,7 @@ For more help, see the documentation here: %s`, short, config.Data.Links.CLIDocs } type helpCmd struct { - cmd *cobra.Command + width int } // helpTemplate Building a custom help template with more finess and pizazz @@ -406,9 +414,21 @@ func (helpCmd *helpCmd) helpTemplate(cmd *cobra.Command, s []string) { //Border styles borderStyle := lipgloss.NewStyle(). Padding(0, 1, 0, 1). - Width(120). + Width(helpCmd.width - 2). BorderForeground(lipgloss.AdaptiveColor{Light: `#3B6385`, Dark: `#47A359`}). Border(lipgloss.ThickBorder()) log.Println("\n" + borderStyle.Render(usageText.String()+"\n")) } + +func getHelpWidth() int { + const defaultHelpWidth = 122 + if !term.IsTerminal(0) { + return defaultHelpWidth + } + w, _, err := term.GetSize(0) + if err == nil && w < defaultHelpWidth { + return w + } + return defaultHelpWidth +} From 1b0fd6816f0b0566993f3e482255a34537f795f9 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 9 Mar 2023 23:10:06 -0500 Subject: [PATCH 51/84] chore: Fix typos in code comments Signed-off-by: Adam Harvey --- api/schedule_rest.go | 2 +- cmd/root.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/schedule_rest.go b/api/schedule_rest.go index 6b35d700f..91b595fae 100644 --- a/api/schedule_rest.go +++ b/api/schedule_rest.go @@ -375,7 +375,7 @@ func (c *ScheduleRestClient) newDeleteScheduleRequest(scheduleID string) (*http. return c.newHTTPRequest("DELETE", queryURL.String(), nil) } -// Builds a requeest to list schedules according to params. +// Builds a request to list schedules according to params. func (c *ScheduleRestClient) newListSchedulesRequest(vcs, org, project string, params *listSchedulesParams) (*http.Request, error) { var err error queryURL, err := url.Parse(c.server) diff --git a/cmd/root.go b/cmd/root.go index 6a636cd0c..027fe29a2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -328,7 +328,7 @@ type helpCmd struct { cmd *cobra.Command } -// helpTemplate Building a custom help template with more finess and pizazz +// helpTemplate Building a custom help template with more finesse and pizazz func (helpCmd *helpCmd) helpTemplate(cmd *cobra.Command, s []string) { /***Styles ***/ From 817a36edfa6ddc53d11981d2ec0108eeace7f298 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 9 Mar 2023 23:10:36 -0500 Subject: [PATCH 52/84] refactor(test): Enforce camelCase to a test var --- cmd/admin_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/admin_test.go b/cmd/admin_test.go index 21baab91e..40f330e63 100644 --- a/cmd/admin_test.go +++ b/cmd/admin_test.go @@ -251,7 +251,7 @@ var _ = Describe("Namespace integration tests", func() { } }` - expectedDeleteNamespacerequest := `{ + expectedDeleteNamespaceRequest := `{ "query": "\nmutation($id: UUID!) {\n deleteNamespaceAndRelatedOrbs(namespaceId: $id) {\n deleted\n errors {\n type\n message\n }\n }\n}\n", "variables": { "id": "f13a9e13-538c-435c-8f61-78596661acd6" @@ -270,7 +270,7 @@ var _ = Describe("Namespace integration tests", func() { tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ Status: http.StatusOK, - Request: expectedDeleteNamespacerequest, + Request: expectedDeleteNamespaceRequest, Response: gqlDeleteNamespaceResponse}) session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) @@ -324,7 +324,7 @@ var _ = Describe("Namespace integration tests", func() { } }` - expectedDeleteNamespacerequest := `{ + expectedDeleteNamespaceRequest := `{ "query": "\nmutation($id: UUID!) {\n deleteNamespaceAndRelatedOrbs(namespaceId: $id) {\n deleted\n errors {\n type\n message\n }\n }\n}\n", "variables": { "id": "f13a9e13-538c-435c-8f61-78596661acd6" @@ -343,7 +343,7 @@ var _ = Describe("Namespace integration tests", func() { tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ Status: http.StatusOK, - Request: expectedDeleteNamespacerequest, + Request: expectedDeleteNamespaceRequest, Response: gqlDeleteNamespaceResponse}) session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) From 81b449817162211b369442f04a662263d4effb8f Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 9 Mar 2023 23:10:54 -0500 Subject: [PATCH 53/84] fix(policy): Typo in error message Signed-off-by: Adam Harvey --- cmd/policy/policy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/policy/policy.go b/cmd/policy/policy.go index 3a47ea2f1..d364f135e 100644 --- a/cmd/policy/policy.go +++ b/cmd/policy/policy.go @@ -456,7 +456,7 @@ This group of commands allows the management of polices to be verified against b runner, err := tester.NewRunner(runnerOpts) if err != nil { - return fmt.Errorf("cannot instantite runner: %w", err) + return fmt.Errorf("cannot instantiate runner: %w", err) } handlerOpts := tester.ResultHandlerOptions{ From 5469dabd86fcfcf042175aa6e12a1a2f87d61c81 Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Thu, 9 Mar 2023 14:16:27 +0000 Subject: [PATCH 54/84] First pass at removing the api-service dependency for config compilation and validation --- api/api.go | 119 ------ api/rest/client.go | 44 +- api/rest/client_test.go | 37 ++ cmd/config.go | 73 +++- cmd/config_test.go | 398 +++++------------- cmd/root.go | 7 + config/config.go | 109 +++++ {cmd => config}/deprecated-images.go | 6 +- .../features/circleci_config.feature | 203 +++++++++ local/local.go | 12 +- settings/settings.go | 8 + 11 files changed, 578 insertions(+), 438 deletions(-) create mode 100644 config/config.go rename {cmd => config}/deprecated-images.go (93%) diff --git a/api/api.go b/api/api.go index 75fd208a9..ff6ca667d 100644 --- a/api/api.go +++ b/api/api.go @@ -11,7 +11,6 @@ import ( "strings" "github.com/CircleCI-Public/circleci-cli/api/graphql" - "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/references" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/Masterminds/semver" @@ -513,124 +512,6 @@ func WhoamiQuery(cl *graphql.Client) (*WhoamiResponse, error) { return &response, nil } -// ConfigQueryLegacy calls the GQL API to validate and process config with the legacy orgSlug -func ConfigQueryLegacy(cl *graphql.Client, configPath string, orgSlug string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { - var response BuildConfigResponse - var query string - config, err := loadYaml(configPath) - if err != nil { - return nil, err - } - // GraphQL isn't forwards-compatible, so we are unusually selective here about - // passing only non-empty fields on to the API, to minimize user impact if the - // backend is out of date. - var fieldAddendums string - if orgSlug != "" { - fieldAddendums += ", orgSlug: $orgSlug" - } - if len(params) > 0 { - fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" - } - query = fmt.Sprintf( - `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }`, - fieldAddendums) - - request := graphql.NewRequest(query) - request.SetToken(cl.Token) - request.Var("config", config) - - if values != nil { - request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) - } - if params != nil { - pipelineParameters, err := json.Marshal(params) - if err != nil { - return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) - } - request.Var("pipelineParametersJson", string(pipelineParameters)) - } - - if orgSlug != "" { - request.Var("orgSlug", orgSlug) - } - - err = cl.Run(request, &response) - if err != nil { - return nil, errors.Wrap(err, "Unable to validate config") - } - if len(response.BuildConfig.ConfigResponse.Errors) > 0 { - return nil, &response.BuildConfig.ConfigResponse.Errors - } - - return &response.BuildConfig.ConfigResponse, nil -} - -// ConfigQuery calls the GQL API to validate and process config with the org id -func ConfigQuery(cl *graphql.Client, configPath string, orgId string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { - var response BuildConfigResponse - var query string - config, err := loadYaml(configPath) - if err != nil { - return nil, err - } - // GraphQL isn't forwards-compatible, so we are unusually selective here about - // passing only non-empty fields on to the API, to minimize user impact if the - // backend is out of date. - var fieldAddendums string - if orgId != "" { - fieldAddendums += ", orgId: $orgId" - } - if len(params) > 0 { - fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" - } - query = fmt.Sprintf( - `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }`, - fieldAddendums) - - request := graphql.NewRequest(query) - request.SetToken(cl.Token) - request.Var("config", config) - - if values != nil { - request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) - } - if params != nil { - pipelineParameters, err := json.Marshal(params) - if err != nil { - return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) - } - request.Var("pipelineParametersJson", string(pipelineParameters)) - } - - if orgId != "" { - request.Var("orgId", orgId) - } - - err = cl.Run(request, &response) - if err != nil { - return nil, errors.Wrap(err, "Unable to validate config") - } - if len(response.BuildConfig.ConfigResponse.Errors) > 0 { - return nil, &response.BuildConfig.ConfigResponse.Errors - } - - return &response.BuildConfig.ConfigResponse, nil -} - // OrbQuery validated and processes an orb. func OrbQuery(cl *graphql.Client, configPath string) (*ConfigResponse, error) { var response OrbConfigResponse diff --git a/api/rest/client.go b/api/rest/client.go index d745b887e..918f64c2e 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -17,7 +17,12 @@ import ( ) type Client struct { - baseURL *url.URL + baseURL *url.URL + // The config api host differs for both cloud and server setups. + // For cloud, the base domain will be https://api.circleci.com + // for server, this should match the host as we don't have the same + // api subdomain setup + apiURL *url.URL circleToken string client *http.Client } @@ -29,13 +34,15 @@ func New(host string, config *settings.Config) *Client { endpoint += "/" } - u, _ := url.Parse(host) + baseURL, _ := url.Parse(host) + apiURL, _ := url.Parse(config.ConfigAPIHost) client := config.HTTPClient client.Timeout = 10 * time.Second return &Client{ - baseURL: u.ResolveReference(&url.URL{Path: endpoint}), + apiURL: apiURL.ResolveReference(&url.URL{Path: endpoint}), + baseURL: baseURL.ResolveReference(&url.URL{Path: endpoint}), circleToken: config.Token, client: client, } @@ -57,6 +64,34 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req return nil, err } + c.enrichRequestHeaders(req, payload) + return req, nil +} + +// NewAPIRequest - similar to NewRequest except it uses the apiURL as the base URL. +func (c *Client) NewAPIRequest(method string, u *url.URL, payload interface{}) (req *http.Request, err error) { + var r io.Reader + if payload != nil { + buf := &bytes.Buffer{} + r = buf + err = json.NewEncoder(buf).Encode(payload) + if err != nil { + fmt.Printf("failed to encode payload as json: %s\n", err.Error()) + return nil, err + } + } + + req, err = http.NewRequest(method, c.apiURL.ResolveReference(u).String(), r) + if err != nil { + fmt.Printf("failed to create new http request: %s\n", err.Error()) + return nil, err + } + + c.enrichRequestHeaders(req, payload) + return req, nil +} + +func (c *Client) enrichRequestHeaders(req *http.Request, payload interface{}) { req.Header.Set("Circle-Token", c.circleToken) req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", version.UserAgent()) @@ -67,12 +102,12 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req if payload != nil { req.Header.Set("Content-Type", "application/json") } - return req, nil } func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int, err error) { httpResp, err := c.client.Do(req) if err != nil { + fmt.Printf("failed to make http request: %s\n", err.Error()) return 0, err } defer httpResp.Body.Close() @@ -83,6 +118,7 @@ func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int, }{} err = json.NewDecoder(httpResp.Body).Decode(&httpError) if err != nil { + fmt.Printf("failed to decode body: %s", err.Error()) return httpResp.StatusCode, err } return httpResp.StatusCode, &HTTPError{Code: httpResp.StatusCode, Message: httpError.Message} diff --git a/api/rest/client_test.go b/api/rest/client_test.go index a45d4bc13..f66281f7b 100644 --- a/api/rest/client_test.go +++ b/api/rest/client_test.go @@ -118,6 +118,43 @@ func TestClient_DoRequest(t *testing.T) { }) } +func TestAPIRequest(t *testing.T) { + fix := &fixture{} + c, cleanup := fix.Run(http.StatusCreated, `{"key": "value"}`) + defer cleanup() + + t.Run("test new api request sets the default headers", func(t *testing.T) { + req, err := c.NewAPIRequest("GET", &url.URL{}, struct{}{}) + assert.NilError(t, err) + + assert.Equal(t, req.Header.Get("User-Agent"), "circleci-cli/0.0.0-dev+dirty-local-tree (source)") + assert.Equal(t, req.Header.Get("Circle-Token"), c.circleToken) + assert.Equal(t, req.Header.Get("Accept"), "application/json") + }) + + type testPayload struct { + Message string + } + + t.Run("test new api request sets the default headers", func(t *testing.T) { + req, err := c.NewAPIRequest("GET", &url.URL{}, testPayload{Message: "hello"}) + assert.NilError(t, err) + + assert.Equal(t, req.Header.Get("Circleci-Cli-Command"), "") + assert.Equal(t, req.Header.Get("Content-Type"), "application/json") + }) + + t.Run("test new api request doesn't set content-type with empty payload", func(t *testing.T) { + req, err := c.NewAPIRequest("GET", &url.URL{}, nil) + assert.NilError(t, err) + + assert.Equal(t, req.Header.Get("Circleci-Cli-Command"), "") + if req.Header.Get("Content-Type") != "" { + t.Fail() + } + }) +} + type fixture struct { mu sync.Mutex url url.URL diff --git a/cmd/config.go b/cmd/config.go index 5ae76fcde..7d05200be 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -3,10 +3,11 @@ package cmd import ( "fmt" "io/ioutil" + "net/url" "strings" - "github.com/CircleCI-Public/circleci-cli/api" - "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/config" "github.com/CircleCI-Public/circleci-cli/filetree" "github.com/CircleCI-Public/circleci-cli/local" "github.com/CircleCI-Public/circleci-cli/pipeline" @@ -18,9 +19,13 @@ import ( "gopkg.in/yaml.v3" ) +var ( + CollaborationsPath = "me/collaborations" +) + type configOptions struct { cfg *settings.Config - cl *graphql.Client + rest *rest.Client args []string } @@ -63,7 +68,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Check that the config file is well formed.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) + opts.rest = rest.New(config.Host, config) }, RunE: func(cmd *cobra.Command, _ []string) error { return validateConfig(opts, cmd.Flags()) @@ -85,7 +90,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Validate config and display expanded configuration.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) + opts.rest = rest.New(config.Host, config) }, RunE: func(cmd *cobra.Command, _ []string) error { return processConfig(opts, cmd.Flags()) @@ -97,6 +102,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { processCommand.Flags().StringP("org-slug", "o", "", "organization slug (for example: github/example-org), used when a config depends on private orbs belonging to that org") processCommand.Flags().String("org-id", "", "organization id used when a config depends on private orbs belonging to that org") processCommand.Flags().StringP("pipeline-parameters", "", "", "YAML/JSON map of pipeline parameters, accepts either YAML/JSON directly or file path (for example: my-params.yml)") + processCommand.Flags().StringP("circleci-api-host", "", "", "the api-host you want to use for config processing and validation") migrateCommand := &cobra.Command{ Use: "migrate", @@ -125,7 +131,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { // The arg is actually optional, in order to support compatibility with the --path flag. func validateConfig(opts configOptions, flags *pflag.FlagSet) error { var err error - var response *api.ConfigResponse + var response *config.ConfigResponse path := local.DefaultConfigPath // First, set the path to configPath set by --path flag for compatibility if configPath != "" { @@ -142,15 +148,21 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { fmt.Println("Validating config with following values") printValues(values) - orgID, _ := flags.GetString("org-id") + var orgID string + orgID, _ = flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = api.ConfigQuery(opts.cl, path, orgID, nil, values) + response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - response, err = api.ConfigQueryLegacy(opts.cl, path, orgSlug, nil, values) + orgs, err := GetOrgCollaborations(opts.rest) + if err != nil { + fmt.Println(err.Error()) + } + orgID = GetOrgIdFromSlug(orgSlug, orgs) + response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } @@ -160,7 +172,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { // link here to blog post when available // returns an error if a deprecated image is used if !ignoreDeprecatedImages { - err := deprecatedImageCheck(response) + err := config.DeprecatedImageCheck(response) if err != nil { return err } @@ -177,7 +189,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { func processConfig(opts configOptions, flags *pflag.FlagSet) error { paramsYaml, _ := flags.GetString("pipeline-parameters") - var response *api.ConfigResponse + var response *config.ConfigResponse var params pipeline.Parameters var err error @@ -202,13 +214,18 @@ func processConfig(opts configOptions, flags *pflag.FlagSet) error { orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = api.ConfigQuery(opts.cl, opts.args[0], orgID, params, values) + response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, values) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - response, err = api.ConfigQueryLegacy(opts.cl, opts.args[0], orgSlug, params, values) + orgs, err := GetOrgCollaborations(opts.rest) + if err != nil { + fmt.Println(err.Error()) + } + orgID = GetOrgIdFromSlug(orgSlug, orgs) + response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, values) if err != nil { return err } @@ -241,3 +258,33 @@ func printValues(values pipeline.Values) { fmt.Printf("\t%s:\t%s", key, value) } } + +type CollaborationResult struct { + VcsTye string `json:"vcs_type"` + OrgSlug string `json:"slug"` + OrgName string `json:"name"` + OrgId string `json:"id"` + AvatarUrl string `json:"avatar_url"` +} + +// GetOrgCollaborations - fetches all the collaborations for a given user. +func GetOrgCollaborations(client *rest.Client) ([]CollaborationResult, error) { + req, err := client.NewRequest("GET", &url.URL{Path: CollaborationsPath}, nil) + if err != nil { + return nil, err + } + + var resp []CollaborationResult + _, err = client.DoRequest(req, &resp) + return resp, err +} + +// GetOrgIdFromSlug - converts a slug into an orgID. +func GetOrgIdFromSlug(slug string, collaborations []CollaborationResult) string { + for _, v := range collaborations { + if v.OrgSlug == slug { + return v.OrgId + } + } + return "" +} diff --git a/cmd/config_test.go b/cmd/config_test.go index 85b6e8632..d0ed9016d 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -1,20 +1,13 @@ package cmd_test import ( - "encoding/json" "fmt" - "io" - "net/http" "os/exec" "path/filepath" - "time" - "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/clitest" - "github.com/CircleCI-Public/circleci-cli/pipeline" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/onsi/gomega/gbytes" "github.com/onsi/gomega/gexec" "gotest.tools/v3/golden" ) @@ -25,7 +18,6 @@ var _ = Describe("Config", func() { command *exec.Cmd results []byte tempSettings *clitest.TempSettings - token string = "testtoken" ) BeforeEach(func() { @@ -95,6 +87,112 @@ var _ = Describe("Config", func() { }) }) + It("packs all YAML contents as expected", func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/hugo-pack/.circleci") + results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + + It("given a .circleci folder with config.yml and local orb, packs all YAML contents as expected", func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/hugo-pack/.circleci") + results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + + It("given a local orbs folder with mixed inline and local commands, jobs, etc, packs all YAML contents as expected", func() { + var path string = "nested-orbs-and-local-commands-etc" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + + It("returns an error when validating a config", func() { + var path string = "nested-orbs-and-local-commands-etc" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + + It("packs successfully given an orb containing local executors and commands in folder", func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/myorb/test") + + results = golden.Get(GinkgoT(), filepath.FromSlash("myorb/result.yml")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + + It("packs as expected given a large nested config including rails orbs", func() { + var path string = "test-with-large-nested-rails-orb" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + + It("prints an error given a config which is a list and not a map", func() { + config := clitest.OpenTmpFile(filepath.Join(tempSettings.Home, "myorb"), "config.yaml") + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + config.RootDir, + ) + config.Write([]byte(`[]`)) + + expected := fmt.Sprintf("Error: Failed trying to marshal the tree to YAML : expected a map, got a `[]interface {}` which is not supported at this time for \"%s\"\n", config.Path) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + stderr := session.Wait().Err.Contents() + Expect(string(stderr)).To(Equal(expected)) + Eventually(session).Should(clitest.ShouldFail()) + config.Close() + }) + Describe("with a large nested config including rails orb", func() { BeforeEach(func() { var path string = "test-with-large-nested-rails-orb" @@ -146,289 +244,5 @@ var _ = Describe("Config", func() { Eventually(session).Should(clitest.ShouldFail()) }) }) - - Describe("validating configs", func() { - config := "version: 2.1" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "validate", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.SetToken(token) - r.Variables["config"] = config - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns an error when validating a config", func() { - expResp := `{ - "buildConfig": { - "errors": [ - {"message": "error1"} - ] - } - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: error1")) - Eventually(session).Should(clitest.ShouldFail()) - }) - - It("returns successfully when validating a config", func() { - expResp := `{ - "buildConfig": {} - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) - Eventually(session).Should(gexec.Exit(0)) - }) - }) - - Describe("validating configs with pipeline parameters", func() { - config := "version: 2.1" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "process", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "--pipeline-parameters", `{"foo": "test", "bar": true, "baz": 10}`, - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues, pipelineParametersJson: $pipelineParametersJson) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.SetToken(token) - r.Variables["config"] = config - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - pipelineParams, err := json.Marshal(pipeline.Parameters{ - "foo": "test", - "bar": true, - "baz": 10, - }) - Expect(err).ToNot(HaveOccurred()) - r.Variables["pipelineParametersJson"] = string(pipelineParams) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns successfully when validating a config", func() { - expResp := `{ - "buildConfig": {} - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session).Should(gexec.Exit(0)) - }) - }) - - Describe("validating configs with private orbs", func() { - config := "version: 2.1" - orgId := "bb604b45-b6b0-4b81-ad80-796f15eddf87" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "validate", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "--org-id", orgId, - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgId: $orgId) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.SetToken(token) - r.Variables["config"] = config - r.Variables["orgId"] = orgId - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns an error when validating a config with a private orb", func() { - expResp := `{ - "buildConfig": { - "errors": [ - {"message": "permission denied"} - ] - } - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) - Eventually(session).Should(clitest.ShouldFail()) - }) - }) - - Describe("validating configs with private orbs Legacy", func() { - config := "version: 2.1" - orgSlug := "circleci" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "validate", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "--org-slug", orgSlug, - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgSlug: $orgSlug) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.SetToken(token) - r.Variables["config"] = config - r.Variables["orgSlug"] = orgSlug - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns an error when validating a config with a private orb", func() { - expResp := `{ - "buildConfig": { - "errors": [ - {"message": "permission denied"} - ] - } - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) - Eventually(session).Should(clitest.ShouldFail()) - }) - - It("returns successfully when validating a config with private orbs", func() { - expResp := `{ - "buildConfig": {} - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) - Eventually(session).Should(gexec.Exit(0)) - }) - }) }) }) diff --git a/cmd/root.go b/cmd/root.go index 6a636cd0c..7f7ed4ec1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -21,6 +21,7 @@ import ( var defaultEndpoint = "graphql-unstable" var defaultHost = "https://circleci.com" +var defaultAPIHost = "https://api.circleci.com" var defaultRestEndpoint = "api/v2" var trueString = "true" @@ -103,6 +104,11 @@ func MakeCommands() *cobra.Command { RestEndpoint: defaultRestEndpoint, Endpoint: defaultEndpoint, GitHubAPI: "https://api.github.com/", + // The config api host differs for both cloud and server setups. + // For cloud, the base domain will be https://api.circleci.com + // for server, this should match the host as we don't have the same + // api subdomain setup + ConfigAPIHost: defaultAPIHost, } if err := rootOptions.Load(); err != nil { @@ -174,6 +180,7 @@ func MakeCommands() *cobra.Command { flags.StringVar(&rootOptions.Host, "host", rootOptions.Host, "URL to your CircleCI host, also CIRCLECI_CLI_HOST") flags.StringVar(&rootOptions.Endpoint, "endpoint", rootOptions.Endpoint, "URI to your CircleCI GraphQL API endpoint") flags.StringVar(&rootOptions.GitHubAPI, "github-api", "https://api.github.com/", "Change the default endpoint to GitHub API for retrieving updates") + flags.StringVar(&rootOptions.ConfigAPIHost, "config-api-host", "https://api.circleci.com", "Change the default endpoint for the config api host") flags.BoolVar(&rootOptions.SkipUpdateCheck, "skip-update-check", skipUpdateByDefault(), "Skip the check for updates check run before every command.") hidden := []string{"github-api", "debug", "endpoint"} diff --git a/config/config.go b/config/config.go new file mode 100644 index 000000000..8605690a6 --- /dev/null +++ b/config/config.go @@ -0,0 +1,109 @@ +package config + +import ( + "fmt" + "io/ioutil" + "net/url" + "os" + + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/pipeline" + "github.com/pkg/errors" +) + +type ConfigError struct { + Message string `json:"message"` +} + +type ConfigResponse struct { + Valid bool `json:"valid"` + SourceYaml string `json:"source-yaml"` + OutputYaml string `json:"output-yaml"` + Errors []ConfigError `json:"errors"` +} + +type CompileConfigRequest struct { + ConfigYaml string `json:"config_yaml"` + Options Options `json:"options"` +} + +type Options struct { + OwnerID string `json:"owner-id,omitempty"` + PipelineParameters map[string]interface{} `json:"pipeline_parameters,omitempty"` + PipelineValues map[string]string `json:"pipeline_values,omitempty"` +} + +// #nosec +func loadYaml(path string) (string, error) { + var err error + var config []byte + if path == "-" { + config, err = ioutil.ReadAll(os.Stdin) + } else { + config, err = ioutil.ReadFile(path) + } + + if err != nil { + return "", errors.Wrapf(err, "Could not load config file at %s", path) + } + + return string(config), nil +} + +// ConfigQuery - attempts to compile or validate a given config file with the +// passed in params/values. +// If the orgID is passed in, the config-compilation with private orbs should work. +func ConfigQuery( + rest *rest.Client, + configPath string, + orgID string, + params pipeline.Parameters, + values pipeline.Values, +) (*ConfigResponse, error) { + + configString, err := loadYaml(configPath) + if err != nil { + return nil, err + } + + compileRequest := CompileConfigRequest{ + ConfigYaml: configString, + Options: Options{ + PipelineValues: values, + }, + } + + if orgID != "" { + compileRequest.Options.OwnerID = orgID + } + + if len(params) >= 1 { + compileRequest.Options.PipelineParameters = params + } + + req, err := rest.NewAPIRequest( + "POST", + &url.URL{ + Path: "compile-config-with-defaults", + }, + compileRequest, + ) + if err != nil { + return nil, err + } + + configCompilationResp := &ConfigResponse{} + statusCode, err := rest.DoRequest(req, configCompilationResp) + if err != nil { + return nil, err + } + if statusCode != 200 { + return nil, errors.New("non 200 status code") + } + + if len(configCompilationResp.Errors) > 0 { + return nil, errors.New(fmt.Sprintf("config compilation contains errors: %s", configCompilationResp.Errors)) + } + + return configCompilationResp, nil +} diff --git a/cmd/deprecated-images.go b/config/deprecated-images.go similarity index 93% rename from cmd/deprecated-images.go rename to config/deprecated-images.go index 607edfc88..fcc998fc9 100644 --- a/cmd/deprecated-images.go +++ b/config/deprecated-images.go @@ -1,8 +1,6 @@ -package cmd +package config import ( - "github.com/CircleCI-Public/circleci-cli/api" - "github.com/pkg/errors" "gopkg.in/yaml.v3" ) @@ -40,7 +38,7 @@ type processedConfig struct { } // Processes the config down to v2.0, then checks image used against the block list -func deprecatedImageCheck(response *api.ConfigResponse) error { +func DeprecatedImageCheck(response *ConfigResponse) error { aConfig := processedConfig{} err := yaml.Unmarshal([]byte(response.OutputYaml), &aConfig) diff --git a/integration_tests/features/circleci_config.feature b/integration_tests/features/circleci_config.feature index d9085cc2b..439103b78 100644 --- a/integration_tests/features/circleci_config.feature +++ b/integration_tests/features/circleci_config.feature @@ -15,6 +15,209 @@ Feature: Config checking Then the exit status should be 0 And the output should contain "Config file at config.yml is valid." + Scenario: Checking a valid config file with an orb + Given a file named "config.yml" with: + """ + version: 2.1 + + orbs: + node: circleci/node@5.0.3 + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci config validate --skip-update-check -c config.yml` + Then the exit status should be 0 + And the output should contain "Config file at config.yml is valid" + + Scenario: Checking a valid config against the k9s server + Given a file named "config.yml" with: + """ + version: 2.1 + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci --config-api-host https://k9s.sphereci.com config validate --skip-update-check -c config.yml` + Then the exit status should be 0 + And the output should contain "Config file at config.yml is valid" + + Scenario: Checking a valid config file with an orb + Given a file named "config.yml" with: + """ + version: 2.1 + + orbs: + node: circleci/node@5.0.3 + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci config validate --skip-update-check -c config.yml` + Then the exit status should be 0 + And the output should contain "Config file at config.yml is valid" + + Scenario: Checking a valid config file with a private org + Given a file named "config.yml" with: + """ + version: 2.1 + + orbs: + node: circleci/node@5.0.3 + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci config validate --skip-update-check --org-id bb604b45-b6b0-4b81-ad80-796f15eddf87 -c config.yml` + Then the output should contain "Config file at config.yml is valid" + And the exit status should be 0 + + Scenario: Checking a valid config file with a non-existant orb + Given a file named "config.yml" with: + """ + version: 2.1 + + orbs: + node: circleci/doesnt-exist@5.0.3 + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci config validate --skip-update-check -c config.yml` + Then the exit status should be 255 + And the output should contain "config compilation contains errors" + + Scenario: Checking a valid config file with pipeline-parameters + Given a file named "config.yml" with: + """ + version: 2.1 + + parameters: + foo: + type: string + default: "bar" + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + echo << pipeline.parameters.foo >> + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci config process config.yml --pipeline-parameters "foo: fighters"` + Then the output should contain "fighters" + And the exit status should be 0 + + Scenario: Checking a valid config file with default pipeline params + Given a file named "config.yml" with: + """ + version: 2.1 + + parameters: + foo: + type: string + default: "bar" + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + echo << pipeline.parameters.foo >> + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci config process config.yml` + Then the output should contain "bar" + And the exit status should be 0 + + Scenario: Checking a valid config file with file pipeline-parameters + Given a file named "config.yml" with: + """ + version: 2.1 + + parameters: + foo: + type: string + default: "bar" + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + echo << pipeline.parameters.foo >> + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + And I write to "params.yml" with: + """ + foo: "totallyawesome" + """ + When I run `circleci config process config.yml --pipeline-parameters params.yml` + Then the output should contain "totallyawesome" + And the exit status should be 0 + + Scenario: Checking an invalid config file Given a file named "config.yml" with: """ diff --git a/local/local.go b/local/local.go index 373a88dd2..2137cf355 100644 --- a/local/local.go +++ b/local/local.go @@ -12,8 +12,8 @@ import ( "strings" "syscall" - "github.com/CircleCI-Public/circleci-cli/api" - "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/config" "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/pkg/errors" @@ -26,21 +26,21 @@ const DefaultConfigPath = ".circleci/config.yml" func Execute(flags *pflag.FlagSet, cfg *settings.Config, args []string) error { var err error - var configResponse *api.ConfigResponse - cl := graphql.NewClient(cfg.HTTPClient, cfg.Host, cfg.Endpoint, cfg.Token, cfg.Debug) + var configResponse *config.ConfigResponse + restClient := rest.New(cfg.Host, cfg) processedArgs, configPath := buildAgentArguments(flags) //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - configResponse, err = api.ConfigQuery(cl, configPath, orgID, nil, pipeline.LocalPipelineValues()) + configResponse, err = config.ConfigQuery(restClient, configPath, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - configResponse, err = api.ConfigQueryLegacy(cl, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) + configResponse, err = config.ConfigQuery(restClient, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) if err != nil { return err } diff --git a/settings/settings.go b/settings/settings.go index 24bc07f33..241538018 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -35,6 +35,10 @@ type Config struct { GitHubAPI string `yaml:"-"` SkipUpdateCheck bool `yaml:"-"` OrbPublishing OrbPublishingInfo `yaml:"orb_publishing"` + // Represents the API host we want to use for config compilation and validation + // requests - this is typically on the api.circleci.com subdomain for cloud, or the + // same domain for server instances. + ConfigAPIHost string `yaml:"-"` } type OrbPublishingInfo struct { @@ -128,6 +132,10 @@ func (cfg *Config) WriteToDisk() error { func (cfg *Config) LoadFromEnv(prefix string) { if host := ReadFromEnv(prefix, "host"); host != "" { cfg.Host = host + // If the user is a server customer and overwrites the default + // https://circleci.com host - we then have to use this as the host for + // any config compilation or validation requests as opposed to https://api.circleci.com + cfg.ConfigAPIHost = host } if restEndpoint := ReadFromEnv(prefix, "rest_endpoint"); restEndpoint != "" { From 7a94680cea0afdb356ae6add2ad5e77b2d578776 Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Mon, 20 Mar 2023 13:53:45 +0000 Subject: [PATCH 55/84] docs: correct installation of given version with sudo --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f9fdb9e4..1ba7e4d76 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,11 @@ curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/i You can also set a specific version of the CLI to install with the `VERSION` environment variable: ``` -curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | VERSION=0.1.5222 sudo bash +curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | sudo VERSION=0.1.5222 bash ``` +Take note that additional environment variables should be passed between sudo and invoking bash. + #### Checksum verification If you would like to verify the checksum yourself, you can download the checksum file from the [GitHub releases page](https://github.com/CircleCI-Public/circleci-cli/releases) and verify the checksum of the archive using the `circleci-cli__checksums.txt` inside the assets of the release you'd like to install: From 092ba1a5e54e21953eb8cc79140d459572dc9d15 Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Mon, 20 Mar 2023 16:46:41 +0000 Subject: [PATCH 56/84] Revert "PIPE-2315 - Removes API-Service from config compilation" --- api/api.go | 119 ++++++ api/rest/client.go | 44 +- api/rest/client_test.go | 37 -- cmd/config.go | 73 +--- cmd/config_test.go | 398 +++++++++++++----- {config => cmd}/deprecated-images.go | 6 +- cmd/root.go | 7 - config/config.go | 109 ----- .../features/circleci_config.feature | 203 --------- local/local.go | 12 +- settings/settings.go | 8 - 11 files changed, 438 insertions(+), 578 deletions(-) rename {config => cmd}/deprecated-images.go (93%) delete mode 100644 config/config.go diff --git a/api/api.go b/api/api.go index ff6ca667d..75fd208a9 100644 --- a/api/api.go +++ b/api/api.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/references" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/Masterminds/semver" @@ -512,6 +513,124 @@ func WhoamiQuery(cl *graphql.Client) (*WhoamiResponse, error) { return &response, nil } +// ConfigQueryLegacy calls the GQL API to validate and process config with the legacy orgSlug +func ConfigQueryLegacy(cl *graphql.Client, configPath string, orgSlug string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { + var response BuildConfigResponse + var query string + config, err := loadYaml(configPath) + if err != nil { + return nil, err + } + // GraphQL isn't forwards-compatible, so we are unusually selective here about + // passing only non-empty fields on to the API, to minimize user impact if the + // backend is out of date. + var fieldAddendums string + if orgSlug != "" { + fieldAddendums += ", orgSlug: $orgSlug" + } + if len(params) > 0 { + fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" + } + query = fmt.Sprintf( + `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }`, + fieldAddendums) + + request := graphql.NewRequest(query) + request.SetToken(cl.Token) + request.Var("config", config) + + if values != nil { + request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) + } + if params != nil { + pipelineParameters, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) + } + request.Var("pipelineParametersJson", string(pipelineParameters)) + } + + if orgSlug != "" { + request.Var("orgSlug", orgSlug) + } + + err = cl.Run(request, &response) + if err != nil { + return nil, errors.Wrap(err, "Unable to validate config") + } + if len(response.BuildConfig.ConfigResponse.Errors) > 0 { + return nil, &response.BuildConfig.ConfigResponse.Errors + } + + return &response.BuildConfig.ConfigResponse, nil +} + +// ConfigQuery calls the GQL API to validate and process config with the org id +func ConfigQuery(cl *graphql.Client, configPath string, orgId string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { + var response BuildConfigResponse + var query string + config, err := loadYaml(configPath) + if err != nil { + return nil, err + } + // GraphQL isn't forwards-compatible, so we are unusually selective here about + // passing only non-empty fields on to the API, to minimize user impact if the + // backend is out of date. + var fieldAddendums string + if orgId != "" { + fieldAddendums += ", orgId: $orgId" + } + if len(params) > 0 { + fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" + } + query = fmt.Sprintf( + `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }`, + fieldAddendums) + + request := graphql.NewRequest(query) + request.SetToken(cl.Token) + request.Var("config", config) + + if values != nil { + request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) + } + if params != nil { + pipelineParameters, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) + } + request.Var("pipelineParametersJson", string(pipelineParameters)) + } + + if orgId != "" { + request.Var("orgId", orgId) + } + + err = cl.Run(request, &response) + if err != nil { + return nil, errors.Wrap(err, "Unable to validate config") + } + if len(response.BuildConfig.ConfigResponse.Errors) > 0 { + return nil, &response.BuildConfig.ConfigResponse.Errors + } + + return &response.BuildConfig.ConfigResponse, nil +} + // OrbQuery validated and processes an orb. func OrbQuery(cl *graphql.Client, configPath string) (*ConfigResponse, error) { var response OrbConfigResponse diff --git a/api/rest/client.go b/api/rest/client.go index 918f64c2e..d745b887e 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -17,12 +17,7 @@ import ( ) type Client struct { - baseURL *url.URL - // The config api host differs for both cloud and server setups. - // For cloud, the base domain will be https://api.circleci.com - // for server, this should match the host as we don't have the same - // api subdomain setup - apiURL *url.URL + baseURL *url.URL circleToken string client *http.Client } @@ -34,15 +29,13 @@ func New(host string, config *settings.Config) *Client { endpoint += "/" } - baseURL, _ := url.Parse(host) - apiURL, _ := url.Parse(config.ConfigAPIHost) + u, _ := url.Parse(host) client := config.HTTPClient client.Timeout = 10 * time.Second return &Client{ - apiURL: apiURL.ResolveReference(&url.URL{Path: endpoint}), - baseURL: baseURL.ResolveReference(&url.URL{Path: endpoint}), + baseURL: u.ResolveReference(&url.URL{Path: endpoint}), circleToken: config.Token, client: client, } @@ -64,34 +57,6 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req return nil, err } - c.enrichRequestHeaders(req, payload) - return req, nil -} - -// NewAPIRequest - similar to NewRequest except it uses the apiURL as the base URL. -func (c *Client) NewAPIRequest(method string, u *url.URL, payload interface{}) (req *http.Request, err error) { - var r io.Reader - if payload != nil { - buf := &bytes.Buffer{} - r = buf - err = json.NewEncoder(buf).Encode(payload) - if err != nil { - fmt.Printf("failed to encode payload as json: %s\n", err.Error()) - return nil, err - } - } - - req, err = http.NewRequest(method, c.apiURL.ResolveReference(u).String(), r) - if err != nil { - fmt.Printf("failed to create new http request: %s\n", err.Error()) - return nil, err - } - - c.enrichRequestHeaders(req, payload) - return req, nil -} - -func (c *Client) enrichRequestHeaders(req *http.Request, payload interface{}) { req.Header.Set("Circle-Token", c.circleToken) req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", version.UserAgent()) @@ -102,12 +67,12 @@ func (c *Client) enrichRequestHeaders(req *http.Request, payload interface{}) { if payload != nil { req.Header.Set("Content-Type", "application/json") } + return req, nil } func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int, err error) { httpResp, err := c.client.Do(req) if err != nil { - fmt.Printf("failed to make http request: %s\n", err.Error()) return 0, err } defer httpResp.Body.Close() @@ -118,7 +83,6 @@ func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int, }{} err = json.NewDecoder(httpResp.Body).Decode(&httpError) if err != nil { - fmt.Printf("failed to decode body: %s", err.Error()) return httpResp.StatusCode, err } return httpResp.StatusCode, &HTTPError{Code: httpResp.StatusCode, Message: httpError.Message} diff --git a/api/rest/client_test.go b/api/rest/client_test.go index f66281f7b..a45d4bc13 100644 --- a/api/rest/client_test.go +++ b/api/rest/client_test.go @@ -118,43 +118,6 @@ func TestClient_DoRequest(t *testing.T) { }) } -func TestAPIRequest(t *testing.T) { - fix := &fixture{} - c, cleanup := fix.Run(http.StatusCreated, `{"key": "value"}`) - defer cleanup() - - t.Run("test new api request sets the default headers", func(t *testing.T) { - req, err := c.NewAPIRequest("GET", &url.URL{}, struct{}{}) - assert.NilError(t, err) - - assert.Equal(t, req.Header.Get("User-Agent"), "circleci-cli/0.0.0-dev+dirty-local-tree (source)") - assert.Equal(t, req.Header.Get("Circle-Token"), c.circleToken) - assert.Equal(t, req.Header.Get("Accept"), "application/json") - }) - - type testPayload struct { - Message string - } - - t.Run("test new api request sets the default headers", func(t *testing.T) { - req, err := c.NewAPIRequest("GET", &url.URL{}, testPayload{Message: "hello"}) - assert.NilError(t, err) - - assert.Equal(t, req.Header.Get("Circleci-Cli-Command"), "") - assert.Equal(t, req.Header.Get("Content-Type"), "application/json") - }) - - t.Run("test new api request doesn't set content-type with empty payload", func(t *testing.T) { - req, err := c.NewAPIRequest("GET", &url.URL{}, nil) - assert.NilError(t, err) - - assert.Equal(t, req.Header.Get("Circleci-Cli-Command"), "") - if req.Header.Get("Content-Type") != "" { - t.Fail() - } - }) -} - type fixture struct { mu sync.Mutex url url.URL diff --git a/cmd/config.go b/cmd/config.go index 7d05200be..5ae76fcde 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -3,11 +3,10 @@ package cmd import ( "fmt" "io/ioutil" - "net/url" "strings" - "github.com/CircleCI-Public/circleci-cli/api/rest" - "github.com/CircleCI-Public/circleci-cli/config" + "github.com/CircleCI-Public/circleci-cli/api" + "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/filetree" "github.com/CircleCI-Public/circleci-cli/local" "github.com/CircleCI-Public/circleci-cli/pipeline" @@ -19,13 +18,9 @@ import ( "gopkg.in/yaml.v3" ) -var ( - CollaborationsPath = "me/collaborations" -) - type configOptions struct { cfg *settings.Config - rest *rest.Client + cl *graphql.Client args []string } @@ -68,7 +63,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Check that the config file is well formed.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.rest = rest.New(config.Host, config) + opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) }, RunE: func(cmd *cobra.Command, _ []string) error { return validateConfig(opts, cmd.Flags()) @@ -90,7 +85,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Validate config and display expanded configuration.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.rest = rest.New(config.Host, config) + opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) }, RunE: func(cmd *cobra.Command, _ []string) error { return processConfig(opts, cmd.Flags()) @@ -102,7 +97,6 @@ func newConfigCommand(config *settings.Config) *cobra.Command { processCommand.Flags().StringP("org-slug", "o", "", "organization slug (for example: github/example-org), used when a config depends on private orbs belonging to that org") processCommand.Flags().String("org-id", "", "organization id used when a config depends on private orbs belonging to that org") processCommand.Flags().StringP("pipeline-parameters", "", "", "YAML/JSON map of pipeline parameters, accepts either YAML/JSON directly or file path (for example: my-params.yml)") - processCommand.Flags().StringP("circleci-api-host", "", "", "the api-host you want to use for config processing and validation") migrateCommand := &cobra.Command{ Use: "migrate", @@ -131,7 +125,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { // The arg is actually optional, in order to support compatibility with the --path flag. func validateConfig(opts configOptions, flags *pflag.FlagSet) error { var err error - var response *config.ConfigResponse + var response *api.ConfigResponse path := local.DefaultConfigPath // First, set the path to configPath set by --path flag for compatibility if configPath != "" { @@ -148,21 +142,15 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { fmt.Println("Validating config with following values") printValues(values) - var orgID string - orgID, _ = flags.GetString("org-id") + orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) + response, err = api.ConfigQuery(opts.cl, path, orgID, nil, values) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - orgs, err := GetOrgCollaborations(opts.rest) - if err != nil { - fmt.Println(err.Error()) - } - orgID = GetOrgIdFromSlug(orgSlug, orgs) - response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) + response, err = api.ConfigQueryLegacy(opts.cl, path, orgSlug, nil, values) if err != nil { return err } @@ -172,7 +160,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { // link here to blog post when available // returns an error if a deprecated image is used if !ignoreDeprecatedImages { - err := config.DeprecatedImageCheck(response) + err := deprecatedImageCheck(response) if err != nil { return err } @@ -189,7 +177,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { func processConfig(opts configOptions, flags *pflag.FlagSet) error { paramsYaml, _ := flags.GetString("pipeline-parameters") - var response *config.ConfigResponse + var response *api.ConfigResponse var params pipeline.Parameters var err error @@ -214,18 +202,13 @@ func processConfig(opts configOptions, flags *pflag.FlagSet) error { orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, values) + response, err = api.ConfigQuery(opts.cl, opts.args[0], orgID, params, values) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - orgs, err := GetOrgCollaborations(opts.rest) - if err != nil { - fmt.Println(err.Error()) - } - orgID = GetOrgIdFromSlug(orgSlug, orgs) - response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, values) + response, err = api.ConfigQueryLegacy(opts.cl, opts.args[0], orgSlug, params, values) if err != nil { return err } @@ -258,33 +241,3 @@ func printValues(values pipeline.Values) { fmt.Printf("\t%s:\t%s", key, value) } } - -type CollaborationResult struct { - VcsTye string `json:"vcs_type"` - OrgSlug string `json:"slug"` - OrgName string `json:"name"` - OrgId string `json:"id"` - AvatarUrl string `json:"avatar_url"` -} - -// GetOrgCollaborations - fetches all the collaborations for a given user. -func GetOrgCollaborations(client *rest.Client) ([]CollaborationResult, error) { - req, err := client.NewRequest("GET", &url.URL{Path: CollaborationsPath}, nil) - if err != nil { - return nil, err - } - - var resp []CollaborationResult - _, err = client.DoRequest(req, &resp) - return resp, err -} - -// GetOrgIdFromSlug - converts a slug into an orgID. -func GetOrgIdFromSlug(slug string, collaborations []CollaborationResult) string { - for _, v := range collaborations { - if v.OrgSlug == slug { - return v.OrgId - } - } - return "" -} diff --git a/cmd/config_test.go b/cmd/config_test.go index d0ed9016d..85b6e8632 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -1,13 +1,20 @@ package cmd_test import ( + "encoding/json" "fmt" + "io" + "net/http" "os/exec" "path/filepath" + "time" + "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/clitest" + "github.com/CircleCI-Public/circleci-cli/pipeline" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" "github.com/onsi/gomega/gexec" "gotest.tools/v3/golden" ) @@ -18,6 +25,7 @@ var _ = Describe("Config", func() { command *exec.Cmd results []byte tempSettings *clitest.TempSettings + token string = "testtoken" ) BeforeEach(func() { @@ -87,112 +95,6 @@ var _ = Describe("Config", func() { }) }) - It("packs all YAML contents as expected", func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/hugo-pack/.circleci") - results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) - - It("given a .circleci folder with config.yml and local orb, packs all YAML contents as expected", func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/hugo-pack/.circleci") - results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) - - It("given a local orbs folder with mixed inline and local commands, jobs, etc, packs all YAML contents as expected", func() { - var path string = "nested-orbs-and-local-commands-etc" - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - filepath.Join("testdata", path, "test")) - results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) - - It("returns an error when validating a config", func() { - var path string = "nested-orbs-and-local-commands-etc" - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - filepath.Join("testdata", path, "test")) - results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) - - It("packs successfully given an orb containing local executors and commands in folder", func() { - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - "testdata/myorb/test") - - results = golden.Get(GinkgoT(), filepath.FromSlash("myorb/result.yml")) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) - - It("packs as expected given a large nested config including rails orbs", func() { - var path string = "test-with-large-nested-rails-orb" - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - filepath.Join("testdata", path, "test")) - results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - session.Wait() - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err.Contents()).Should(BeEmpty()) - Eventually(session.Out.Contents()).Should(MatchYAML(results)) - Eventually(session).Should(gexec.Exit(0)) - }) - - It("prints an error given a config which is a list and not a map", func() { - config := clitest.OpenTmpFile(filepath.Join(tempSettings.Home, "myorb"), "config.yaml") - command = exec.Command(pathCLI, - "config", "pack", - "--skip-update-check", - config.RootDir, - ) - config.Write([]byte(`[]`)) - - expected := fmt.Sprintf("Error: Failed trying to marshal the tree to YAML : expected a map, got a `[]interface {}` which is not supported at this time for \"%s\"\n", config.Path) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - stderr := session.Wait().Err.Contents() - Expect(string(stderr)).To(Equal(expected)) - Eventually(session).Should(clitest.ShouldFail()) - config.Close() - }) - Describe("with a large nested config including rails orb", func() { BeforeEach(func() { var path string = "test-with-large-nested-rails-orb" @@ -244,5 +146,289 @@ var _ = Describe("Config", func() { Eventually(session).Should(clitest.ShouldFail()) }) }) + + Describe("validating configs", func() { + config := "version: 2.1" + var expReq string + + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "validate", + "--skip-update-check", + "--token", token, + "--host", tempSettings.TestServer.URL(), + "-", + ) + + stdin, err := command.StdinPipe() + Expect(err).ToNot(HaveOccurred()) + _, err = io.WriteString(stdin, config) + Expect(err).ToNot(HaveOccurred()) + stdin.Close() + + query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }` + + r := graphql.NewRequest(query) + r.SetToken(token) + r.Variables["config"] = config + r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) + + req, err := r.Encode() + Expect(err).ShouldNot(HaveOccurred()) + expReq = req.String() + }) + + It("returns an error when validating a config", func() { + expResp := `{ + "buildConfig": { + "errors": [ + {"message": "error1"} + ] + } + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: error1")) + Eventually(session).Should(clitest.ShouldFail()) + }) + + It("returns successfully when validating a config", func() { + expResp := `{ + "buildConfig": {} + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) + Eventually(session).Should(gexec.Exit(0)) + }) + }) + + Describe("validating configs with pipeline parameters", func() { + config := "version: 2.1" + var expReq string + + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "process", + "--skip-update-check", + "--token", token, + "--host", tempSettings.TestServer.URL(), + "--pipeline-parameters", `{"foo": "test", "bar": true, "baz": 10}`, + "-", + ) + + stdin, err := command.StdinPipe() + Expect(err).ToNot(HaveOccurred()) + _, err = io.WriteString(stdin, config) + Expect(err).ToNot(HaveOccurred()) + stdin.Close() + + query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues, pipelineParametersJson: $pipelineParametersJson) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }` + + r := graphql.NewRequest(query) + r.SetToken(token) + r.Variables["config"] = config + r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) + + pipelineParams, err := json.Marshal(pipeline.Parameters{ + "foo": "test", + "bar": true, + "baz": 10, + }) + Expect(err).ToNot(HaveOccurred()) + r.Variables["pipelineParametersJson"] = string(pipelineParams) + + req, err := r.Encode() + Expect(err).ShouldNot(HaveOccurred()) + expReq = req.String() + }) + + It("returns successfully when validating a config", func() { + expResp := `{ + "buildConfig": {} + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session).Should(gexec.Exit(0)) + }) + }) + + Describe("validating configs with private orbs", func() { + config := "version: 2.1" + orgId := "bb604b45-b6b0-4b81-ad80-796f15eddf87" + var expReq string + + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "validate", + "--skip-update-check", + "--token", token, + "--host", tempSettings.TestServer.URL(), + "--org-id", orgId, + "-", + ) + + stdin, err := command.StdinPipe() + Expect(err).ToNot(HaveOccurred()) + _, err = io.WriteString(stdin, config) + Expect(err).ToNot(HaveOccurred()) + stdin.Close() + + query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgId: $orgId) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }` + + r := graphql.NewRequest(query) + r.SetToken(token) + r.Variables["config"] = config + r.Variables["orgId"] = orgId + r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) + + req, err := r.Encode() + Expect(err).ShouldNot(HaveOccurred()) + expReq = req.String() + }) + + It("returns an error when validating a config with a private orb", func() { + expResp := `{ + "buildConfig": { + "errors": [ + {"message": "permission denied"} + ] + } + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) + Eventually(session).Should(clitest.ShouldFail()) + }) + }) + + Describe("validating configs with private orbs Legacy", func() { + config := "version: 2.1" + orgSlug := "circleci" + var expReq string + + BeforeEach(func() { + command = exec.Command(pathCLI, + "config", "validate", + "--skip-update-check", + "--token", token, + "--host", tempSettings.TestServer.URL(), + "--org-slug", orgSlug, + "-", + ) + + stdin, err := command.StdinPipe() + Expect(err).ToNot(HaveOccurred()) + _, err = io.WriteString(stdin, config) + Expect(err).ToNot(HaveOccurred()) + stdin.Close() + + query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgSlug: $orgSlug) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }` + + r := graphql.NewRequest(query) + r.SetToken(token) + r.Variables["config"] = config + r.Variables["orgSlug"] = orgSlug + r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) + + req, err := r.Encode() + Expect(err).ShouldNot(HaveOccurred()) + expReq = req.String() + }) + + It("returns an error when validating a config with a private orb", func() { + expResp := `{ + "buildConfig": { + "errors": [ + {"message": "permission denied"} + ] + } + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) + Eventually(session).Should(clitest.ShouldFail()) + }) + + It("returns successfully when validating a config with private orbs", func() { + expResp := `{ + "buildConfig": {} + }` + + tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ + Status: http.StatusOK, + Request: expReq, + Response: expResp, + }) + + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) + Eventually(session).Should(gexec.Exit(0)) + }) + }) }) }) diff --git a/config/deprecated-images.go b/cmd/deprecated-images.go similarity index 93% rename from config/deprecated-images.go rename to cmd/deprecated-images.go index fcc998fc9..607edfc88 100644 --- a/config/deprecated-images.go +++ b/cmd/deprecated-images.go @@ -1,6 +1,8 @@ -package config +package cmd import ( + "github.com/CircleCI-Public/circleci-cli/api" + "github.com/pkg/errors" "gopkg.in/yaml.v3" ) @@ -38,7 +40,7 @@ type processedConfig struct { } // Processes the config down to v2.0, then checks image used against the block list -func DeprecatedImageCheck(response *ConfigResponse) error { +func deprecatedImageCheck(response *api.ConfigResponse) error { aConfig := processedConfig{} err := yaml.Unmarshal([]byte(response.OutputYaml), &aConfig) diff --git a/cmd/root.go b/cmd/root.go index 7f7ed4ec1..6a636cd0c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -21,7 +21,6 @@ import ( var defaultEndpoint = "graphql-unstable" var defaultHost = "https://circleci.com" -var defaultAPIHost = "https://api.circleci.com" var defaultRestEndpoint = "api/v2" var trueString = "true" @@ -104,11 +103,6 @@ func MakeCommands() *cobra.Command { RestEndpoint: defaultRestEndpoint, Endpoint: defaultEndpoint, GitHubAPI: "https://api.github.com/", - // The config api host differs for both cloud and server setups. - // For cloud, the base domain will be https://api.circleci.com - // for server, this should match the host as we don't have the same - // api subdomain setup - ConfigAPIHost: defaultAPIHost, } if err := rootOptions.Load(); err != nil { @@ -180,7 +174,6 @@ func MakeCommands() *cobra.Command { flags.StringVar(&rootOptions.Host, "host", rootOptions.Host, "URL to your CircleCI host, also CIRCLECI_CLI_HOST") flags.StringVar(&rootOptions.Endpoint, "endpoint", rootOptions.Endpoint, "URI to your CircleCI GraphQL API endpoint") flags.StringVar(&rootOptions.GitHubAPI, "github-api", "https://api.github.com/", "Change the default endpoint to GitHub API for retrieving updates") - flags.StringVar(&rootOptions.ConfigAPIHost, "config-api-host", "https://api.circleci.com", "Change the default endpoint for the config api host") flags.BoolVar(&rootOptions.SkipUpdateCheck, "skip-update-check", skipUpdateByDefault(), "Skip the check for updates check run before every command.") hidden := []string{"github-api", "debug", "endpoint"} diff --git a/config/config.go b/config/config.go deleted file mode 100644 index 8605690a6..000000000 --- a/config/config.go +++ /dev/null @@ -1,109 +0,0 @@ -package config - -import ( - "fmt" - "io/ioutil" - "net/url" - "os" - - "github.com/CircleCI-Public/circleci-cli/api/rest" - "github.com/CircleCI-Public/circleci-cli/pipeline" - "github.com/pkg/errors" -) - -type ConfigError struct { - Message string `json:"message"` -} - -type ConfigResponse struct { - Valid bool `json:"valid"` - SourceYaml string `json:"source-yaml"` - OutputYaml string `json:"output-yaml"` - Errors []ConfigError `json:"errors"` -} - -type CompileConfigRequest struct { - ConfigYaml string `json:"config_yaml"` - Options Options `json:"options"` -} - -type Options struct { - OwnerID string `json:"owner-id,omitempty"` - PipelineParameters map[string]interface{} `json:"pipeline_parameters,omitempty"` - PipelineValues map[string]string `json:"pipeline_values,omitempty"` -} - -// #nosec -func loadYaml(path string) (string, error) { - var err error - var config []byte - if path == "-" { - config, err = ioutil.ReadAll(os.Stdin) - } else { - config, err = ioutil.ReadFile(path) - } - - if err != nil { - return "", errors.Wrapf(err, "Could not load config file at %s", path) - } - - return string(config), nil -} - -// ConfigQuery - attempts to compile or validate a given config file with the -// passed in params/values. -// If the orgID is passed in, the config-compilation with private orbs should work. -func ConfigQuery( - rest *rest.Client, - configPath string, - orgID string, - params pipeline.Parameters, - values pipeline.Values, -) (*ConfigResponse, error) { - - configString, err := loadYaml(configPath) - if err != nil { - return nil, err - } - - compileRequest := CompileConfigRequest{ - ConfigYaml: configString, - Options: Options{ - PipelineValues: values, - }, - } - - if orgID != "" { - compileRequest.Options.OwnerID = orgID - } - - if len(params) >= 1 { - compileRequest.Options.PipelineParameters = params - } - - req, err := rest.NewAPIRequest( - "POST", - &url.URL{ - Path: "compile-config-with-defaults", - }, - compileRequest, - ) - if err != nil { - return nil, err - } - - configCompilationResp := &ConfigResponse{} - statusCode, err := rest.DoRequest(req, configCompilationResp) - if err != nil { - return nil, err - } - if statusCode != 200 { - return nil, errors.New("non 200 status code") - } - - if len(configCompilationResp.Errors) > 0 { - return nil, errors.New(fmt.Sprintf("config compilation contains errors: %s", configCompilationResp.Errors)) - } - - return configCompilationResp, nil -} diff --git a/integration_tests/features/circleci_config.feature b/integration_tests/features/circleci_config.feature index 439103b78..d9085cc2b 100644 --- a/integration_tests/features/circleci_config.feature +++ b/integration_tests/features/circleci_config.feature @@ -15,209 +15,6 @@ Feature: Config checking Then the exit status should be 0 And the output should contain "Config file at config.yml is valid." - Scenario: Checking a valid config file with an orb - Given a file named "config.yml" with: - """ - version: 2.1 - - orbs: - node: circleci/node@5.0.3 - - jobs: - datadog-hello-world: - docker: - - image: cimg/base:stable - steps: - - run: | - echo "doing something really cool" - workflows: - datadog-hello-world: - jobs: - - datadog-hello-world - """ - When I run `circleci config validate --skip-update-check -c config.yml` - Then the exit status should be 0 - And the output should contain "Config file at config.yml is valid" - - Scenario: Checking a valid config against the k9s server - Given a file named "config.yml" with: - """ - version: 2.1 - - jobs: - datadog-hello-world: - docker: - - image: cimg/base:stable - steps: - - run: | - echo "doing something really cool" - workflows: - datadog-hello-world: - jobs: - - datadog-hello-world - """ - When I run `circleci --config-api-host https://k9s.sphereci.com config validate --skip-update-check -c config.yml` - Then the exit status should be 0 - And the output should contain "Config file at config.yml is valid" - - Scenario: Checking a valid config file with an orb - Given a file named "config.yml" with: - """ - version: 2.1 - - orbs: - node: circleci/node@5.0.3 - - jobs: - datadog-hello-world: - docker: - - image: cimg/base:stable - steps: - - run: | - echo "doing something really cool" - workflows: - datadog-hello-world: - jobs: - - datadog-hello-world - """ - When I run `circleci config validate --skip-update-check -c config.yml` - Then the exit status should be 0 - And the output should contain "Config file at config.yml is valid" - - Scenario: Checking a valid config file with a private org - Given a file named "config.yml" with: - """ - version: 2.1 - - orbs: - node: circleci/node@5.0.3 - - jobs: - datadog-hello-world: - docker: - - image: cimg/base:stable - steps: - - run: | - echo "doing something really cool" - workflows: - datadog-hello-world: - jobs: - - datadog-hello-world - """ - When I run `circleci config validate --skip-update-check --org-id bb604b45-b6b0-4b81-ad80-796f15eddf87 -c config.yml` - Then the output should contain "Config file at config.yml is valid" - And the exit status should be 0 - - Scenario: Checking a valid config file with a non-existant orb - Given a file named "config.yml" with: - """ - version: 2.1 - - orbs: - node: circleci/doesnt-exist@5.0.3 - - jobs: - datadog-hello-world: - docker: - - image: cimg/base:stable - steps: - - run: | - echo "doing something really cool" - workflows: - datadog-hello-world: - jobs: - - datadog-hello-world - """ - When I run `circleci config validate --skip-update-check -c config.yml` - Then the exit status should be 255 - And the output should contain "config compilation contains errors" - - Scenario: Checking a valid config file with pipeline-parameters - Given a file named "config.yml" with: - """ - version: 2.1 - - parameters: - foo: - type: string - default: "bar" - - jobs: - datadog-hello-world: - docker: - - image: cimg/base:stable - steps: - - run: | - echo "doing something really cool" - echo << pipeline.parameters.foo >> - workflows: - datadog-hello-world: - jobs: - - datadog-hello-world - """ - When I run `circleci config process config.yml --pipeline-parameters "foo: fighters"` - Then the output should contain "fighters" - And the exit status should be 0 - - Scenario: Checking a valid config file with default pipeline params - Given a file named "config.yml" with: - """ - version: 2.1 - - parameters: - foo: - type: string - default: "bar" - - jobs: - datadog-hello-world: - docker: - - image: cimg/base:stable - steps: - - run: | - echo "doing something really cool" - echo << pipeline.parameters.foo >> - workflows: - datadog-hello-world: - jobs: - - datadog-hello-world - """ - When I run `circleci config process config.yml` - Then the output should contain "bar" - And the exit status should be 0 - - Scenario: Checking a valid config file with file pipeline-parameters - Given a file named "config.yml" with: - """ - version: 2.1 - - parameters: - foo: - type: string - default: "bar" - - jobs: - datadog-hello-world: - docker: - - image: cimg/base:stable - steps: - - run: | - echo "doing something really cool" - echo << pipeline.parameters.foo >> - workflows: - datadog-hello-world: - jobs: - - datadog-hello-world - """ - And I write to "params.yml" with: - """ - foo: "totallyawesome" - """ - When I run `circleci config process config.yml --pipeline-parameters params.yml` - Then the output should contain "totallyawesome" - And the exit status should be 0 - - Scenario: Checking an invalid config file Given a file named "config.yml" with: """ diff --git a/local/local.go b/local/local.go index 2137cf355..373a88dd2 100644 --- a/local/local.go +++ b/local/local.go @@ -12,8 +12,8 @@ import ( "strings" "syscall" - "github.com/CircleCI-Public/circleci-cli/api/rest" - "github.com/CircleCI-Public/circleci-cli/config" + "github.com/CircleCI-Public/circleci-cli/api" + "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/pkg/errors" @@ -26,21 +26,21 @@ const DefaultConfigPath = ".circleci/config.yml" func Execute(flags *pflag.FlagSet, cfg *settings.Config, args []string) error { var err error - var configResponse *config.ConfigResponse - restClient := rest.New(cfg.Host, cfg) + var configResponse *api.ConfigResponse + cl := graphql.NewClient(cfg.HTTPClient, cfg.Host, cfg.Endpoint, cfg.Token, cfg.Debug) processedArgs, configPath := buildAgentArguments(flags) //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - configResponse, err = config.ConfigQuery(restClient, configPath, orgID, nil, pipeline.LocalPipelineValues()) + configResponse, err = api.ConfigQuery(cl, configPath, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - configResponse, err = config.ConfigQuery(restClient, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) + configResponse, err = api.ConfigQueryLegacy(cl, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) if err != nil { return err } diff --git a/settings/settings.go b/settings/settings.go index 241538018..24bc07f33 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -35,10 +35,6 @@ type Config struct { GitHubAPI string `yaml:"-"` SkipUpdateCheck bool `yaml:"-"` OrbPublishing OrbPublishingInfo `yaml:"orb_publishing"` - // Represents the API host we want to use for config compilation and validation - // requests - this is typically on the api.circleci.com subdomain for cloud, or the - // same domain for server instances. - ConfigAPIHost string `yaml:"-"` } type OrbPublishingInfo struct { @@ -132,10 +128,6 @@ func (cfg *Config) WriteToDisk() error { func (cfg *Config) LoadFromEnv(prefix string) { if host := ReadFromEnv(prefix, "host"); host != "" { cfg.Host = host - // If the user is a server customer and overwrites the default - // https://circleci.com host - we then have to use this as the host for - // any config compilation or validation requests as opposed to https://api.circleci.com - cfg.ConfigAPIHost = host } if restEndpoint := ReadFromEnv(prefix, "rest_endpoint"); restEndpoint != "" { From 1e8b8c7f116806de24da2405398820611090614d Mon Sep 17 00:00:00 2001 From: Mark Fowler Date: Tue, 21 Mar 2023 10:56:51 +0000 Subject: [PATCH 57/84] Dlc purge (#869) * Delete unused code These fields are never read from, so we can delete them. * Delete code that doesn't do anything This code has no effect (the endpoint variable is never used, nor the value being written back to the struct it came from). * Allow creating rest clients directly not from conf Split the New into two different functions, one that takes exactly what is needed to create the struct, and another that is able to assemble these values from the config. * Create a client for speaking to dl.circleci.com - Also, add code for calling the DLC purge endpoint * Add the command to purge DLC --------- Co-authored-by: Abraham Tewa <4184985+AbrahamTewa@users.noreply.github.com> Co-authored-by: JulesFaucherre --- api/dl/dl.go | 6 ++ api/dl/dl_rest.go | 80 +++++++++++++++++++++++ api/dl/dl_rest_test.go | 81 +++++++++++++++++++++++ api/dl/err.go | 27 ++++++++ api/project/project.go | 6 ++ api/project/project_rest.go | 36 +++++++---- api/project/project_rest_test.go | 74 ++++++++++++++++++++- api/rest/client.go | 20 ++++-- api/rest/client_test.go | 2 +- api/runner/runner_test.go | 2 +- cmd/project/dlc.go | 59 +++++++++++++++++ cmd/project/environment_variable.go | 4 +- cmd/project/project.go | 11 ++-- cmd/project/project_test.go | 99 ++++++++++++++++++++++++++++- cmd/runner/runner.go | 2 +- settings/settings.go | 4 +- 16 files changed, 481 insertions(+), 32 deletions(-) create mode 100644 api/dl/dl.go create mode 100644 api/dl/dl_rest.go create mode 100644 api/dl/dl_rest_test.go create mode 100644 api/dl/err.go create mode 100644 cmd/project/dlc.go diff --git a/api/dl/dl.go b/api/dl/dl.go new file mode 100644 index 000000000..148e37763 --- /dev/null +++ b/api/dl/dl.go @@ -0,0 +1,6 @@ +package dl + +// ProjectClient is the interface to interact with dl +type DlClient interface { + PurgeDLC(projectid string) error +} diff --git a/api/dl/dl_rest.go b/api/dl/dl_rest.go new file mode 100644 index 000000000..92db226f1 --- /dev/null +++ b/api/dl/dl_rest.go @@ -0,0 +1,80 @@ +package dl + +import ( + "fmt" + "net/url" + "time" + + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/settings" +) + +const defaultDlHost = "https://dl.circleci.com" + +type dlRestClient struct { + client *rest.Client +} + +// NewDlRestClient returns a new dlRestClient instance initialized with the +// values from the config. +func NewDlRestClient(config settings.Config) (*dlRestClient, error) { // + // We don't want the user to use this with Server as that's nor supported + // at them moment. In order to detect this we look if there's a config file + // or cli option that sets "host" to anything different than the default + if config.Host != "" && config.Host != "https://circleci.com" { + // Only error if there's no custom DlHost set. Since the end user can't + // a custom value set this in the config file, this has to have been + // manually been set in the code, presumably by the test suite to allow + // talking to a mock server, and we want to allow that. + if config.DlHost == "" { + return nil, &CloudOnlyErr{} + } + } + + // what's the base URL? + unparsedURL := defaultDlHost + if config.DlHost != "" { + unparsedURL = config.DlHost + } + + baseURL, err := url.Parse(unparsedURL) + if err != nil { + return nil, fmt.Errorf("cannot parse dl host URL '%s'", unparsedURL) + } + + httpclient := config.HTTPClient + httpclient.Timeout = 10 * time.Second + + // the dl endpoint is hardcoded to https://dl.circleci.com, since currently + // this implementation always refers to the cloud dl service + return &dlRestClient{ + client: rest.New( + baseURL, + config.Token, + httpclient, + ), + }, nil +} + +func (c dlRestClient) PurgeDLC(projectid string) error { + // this calls a private circleci endpoint. We make no guarantees about + // this still existing in the future. + path := fmt.Sprintf("private/output/project/%s/dlc", projectid) + req, err := c.client.NewRequest("DELETE", &url.URL{Path: path}, nil) + if err != nil { + return err + } + + status, err := c.client.DoRequest(req, nil) + + // Futureproofing: If CircleCI ever removes the private backend endpoint + // this call uses, by having the endpoint return a 410 status code CircleCI + // can get everyone running an outdated client to display a helpful error + // telling them to upgrade (presumably by this point a version without this + // logic will have been released) + if status == 410 { + return &GoneErr{} + } + + return err +} diff --git a/api/dl/dl_rest_test.go b/api/dl/dl_rest_test.go new file mode 100644 index 000000000..7262b46ab --- /dev/null +++ b/api/dl/dl_rest_test.go @@ -0,0 +1,81 @@ +package dl + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + "gotest.tools/v3/assert" + + "github.com/CircleCI-Public/circleci-cli/settings" + "github.com/CircleCI-Public/circleci-cli/version" +) + +// getDlRestClient returns a dlRestClient hooked up to the passed server +func getDlRestClient(server *httptest.Server) (*dlRestClient, error) { + return NewDlRestClient(settings.Config{ + DlHost: server.URL, + HTTPClient: http.DefaultClient, + Token: "token", + }) +} + +func Test_DLCPurge(t *testing.T) { + tests := []struct { + name string + handler http.HandlerFunc + wantErr bool + }{ + { + name: "Should handle a successful request", + handler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Header.Get("circle-token"), "token") + assert.Equal(t, r.Header.Get("accept"), "application/json") + assert.Equal(t, r.Header.Get("user-agent"), version.UserAgent()) + + assert.Equal(t, r.Method, "DELETE") + assert.Equal(t, r.URL.Path, fmt.Sprintf("/private/output/project/%s/dlc", "projectid")) + + // check the request was made with an empty body + br := r.Body + b, err := io.ReadAll(br) + assert.NilError(t, err) + assert.Equal(t, string(b), "") + assert.NilError(t, br.Close()) + + // send response as empty 200 + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, err = w.Write([]byte(``)) + assert.NilError(t, err) + }, + }, + { + name: "Should handle an error request", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("content-type", "application/json") + w.WriteHeader(http.StatusInternalServerError) + _, err := w.Write([]byte(`{"message": "error"}`)) + assert.NilError(t, err) + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server := httptest.NewServer(tt.handler) + defer server.Close() + + c, err := getDlRestClient(server) + assert.NilError(t, err) + + err = c.PurgeDLC("projectid") + if (err != nil) != tt.wantErr { + t.Errorf("PurgeDLC() error = %#v (%s), wantErr %v", err, err, tt.wantErr) + return + } + }) + } +} diff --git a/api/dl/err.go b/api/dl/err.go new file mode 100644 index 000000000..e75ffc5b3 --- /dev/null +++ b/api/dl/err.go @@ -0,0 +1,27 @@ +package dl + +type CloudOnlyErr struct{} + +func (e *CloudOnlyErr) Error() string { + return "Misconfiguration.\n" + + "You have configured a custom API endpoint host for the circleci CLI.\n" + + "However, this functionality is only supported on circleci.com API endpoints." +} + +func IsCloudOnlyErr(err error) bool { + _, ok := err.(*CloudOnlyErr) + return ok +} + +type GoneErr struct{} + +func (e *GoneErr) Error() string { + return "No longer supported.\n" + + "This functionality is no longer supported by this version of the circleci CLI.\n" + + "Please upgrade to the latest version of the circleci CLI." +} + +func IsGoneErr(err error) bool { + _, ok := err.(*GoneErr) + return ok +} diff --git a/api/project/project.go b/api/project/project.go index 07947e43d..d1d745959 100644 --- a/api/project/project.go +++ b/api/project/project.go @@ -6,9 +6,15 @@ type ProjectEnvironmentVariable struct { Value string } +// ProjectInfo is the info of a Project +type ProjectInfo struct { + Id string +} + // ProjectClient is the interface to interact with project and it's // components. type ProjectClient interface { + ProjectInfo(vcs, org, project string) (*ProjectInfo, error) ListAllEnvironmentVariables(vcs, org, project string) ([]*ProjectEnvironmentVariable, error) GetEnvironmentVariable(vcs, org, project, envName string) (*ProjectEnvironmentVariable, error) CreateEnvironmentVariable(vcs, org, project string, v ProjectEnvironmentVariable) (*ProjectEnvironmentVariable, error) diff --git a/api/project/project_rest.go b/api/project/project_rest.go index aae48f283..9116c2e0d 100644 --- a/api/project/project_rest.go +++ b/api/project/project_rest.go @@ -9,8 +9,6 @@ import ( ) type projectRestClient struct { - token string - server string client *rest.Client } @@ -38,20 +36,18 @@ type createProjectEnvVarRequest struct { Value string `json:"value"` } +// projectInfo is the info returned by "Get a project" API endpoint. +// This struct does not contain all the fields returned by the API. +type projectInfo struct { + Id string `json:"id"` +} + // NewProjectRestClient returns a new projectRestClient satisfying the api.ProjectInterface // interface via the REST API. func NewProjectRestClient(config settings.Config) (*projectRestClient, error) { - serverURL, err := config.ServerURL() - if err != nil { - return nil, err - } - client := &projectRestClient{ - token: config.Token, - server: serverURL.String(), - client: rest.New(config.Host, &config), + client: rest.NewFromConfig(config.Host, &config), } - return client, nil } @@ -155,3 +151,21 @@ func (c *projectRestClient) CreateEnvironmentVariable(vcs string, org string, pr Value: resp.Value, }, nil } + +// ProjectInfo retrieves and returns the project info. +func (c *projectRestClient) ProjectInfo(vcs string, org string, project string) (*ProjectInfo, error) { + path := fmt.Sprintf("project/%s/%s/%s", vcs, org, project) + req, err := c.client.NewRequest("GET", &url.URL{Path: path}, nil) + if err != nil { + return nil, err + } + + var resp projectInfo + _, err = c.client.DoRequest(req, &resp) + if err != nil { + return nil, err + } + return &ProjectInfo{ + Id: resp.Id, + }, nil +} diff --git a/api/project/project_rest_test.go b/api/project/project_rest_test.go index 947512c82..0badf5a9a 100644 --- a/api/project/project_rest_test.go +++ b/api/project/project_rest_test.go @@ -3,16 +3,18 @@ package project_test import ( "encoding/json" "fmt" + "io" "net/http" "net/http/httptest" "net/url" "reflect" "testing" + "gotest.tools/v3/assert" + "github.com/CircleCI-Public/circleci-cli/api/project" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/CircleCI-Public/circleci-cli/version" - "gotest.tools/v3/assert" ) func getProjectRestClient(server *httptest.Server) (project.ProjectClient, error) { @@ -321,3 +323,73 @@ func Test_projectRestClient_CreateEnvironmentVariable(t *testing.T) { }) } } + +func Test_projectRestClient_ProjectInfo(t *testing.T) { + const ( + vcsType = "github" + orgName = "test-org" + projName = "test-proj" + ) + tests := []struct { + name string + handler http.HandlerFunc + want *project.ProjectInfo + wantErr bool + }{ + { + name: "Should handle a successful request", + handler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Header.Get("circle-token"), "token") + assert.Equal(t, r.Header.Get("accept"), "application/json") + assert.Equal(t, r.Header.Get("user-agent"), version.UserAgent()) + + assert.Equal(t, r.Method, "GET") + assert.Equal(t, r.URL.Path, fmt.Sprintf("/api/v2/project/%s/%s/%s", vcsType, orgName, projName)) + br := r.Body + b, err := io.ReadAll(br) + assert.NilError(t, err) + assert.Equal(t, string(b), "") + assert.NilError(t, br.Close()) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, err = w.Write([]byte(` + { + "id": "this-is-the-id" + }`)) + assert.NilError(t, err) + }, + want: &project.ProjectInfo{ + Id: "this-is-the-id", + }, + }, + { + name: "Should handle an error request", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("content-type", "application/json") + w.WriteHeader(http.StatusInternalServerError) + _, err := w.Write([]byte(`{"message": "error"}`)) + assert.NilError(t, err) + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server := httptest.NewServer(tt.handler) + defer server.Close() + + p, err := getProjectRestClient(server) + assert.NilError(t, err) + + got, err := p.ProjectInfo(vcsType, orgName, projName) + if (err != nil) != tt.wantErr { + t.Errorf("projectRestClient.ProjectInfo() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("projectRestClient.ProjectInfo() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/api/rest/client.go b/api/rest/client.go index d745b887e..9955b3767 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -22,7 +22,15 @@ type Client struct { client *http.Client } -func New(host string, config *settings.Config) *Client { +func New(baseURL *url.URL, token string, httpClient *http.Client) *Client { + return &Client{ + baseURL: baseURL, + circleToken: token, + client: httpClient, + } +} + +func NewFromConfig(host string, config *settings.Config) *Client { // Ensure endpoint ends with a slash endpoint := config.RestEndpoint if !strings.HasSuffix(endpoint, "/") { @@ -34,11 +42,11 @@ func New(host string, config *settings.Config) *Client { client := config.HTTPClient client.Timeout = 10 * time.Second - return &Client{ - baseURL: u.ResolveReference(&url.URL{Path: endpoint}), - circleToken: config.Token, - client: client, - } + return New( + u.ResolveReference(&url.URL{Path: endpoint}), + config.Token, + client, + ) } func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req *http.Request, err error) { diff --git a/api/rest/client_test.go b/api/rest/client_test.go index a45d4bc13..bf2a06947 100644 --- a/api/rest/client_test.go +++ b/api/rest/client_test.go @@ -176,5 +176,5 @@ func (f *fixture) Run(statusCode int, respBody string) (c *Client, cleanup func( HTTPClient: http.DefaultClient, } - return New(server.URL, cfg), server.Close + return NewFromConfig(server.URL, cfg), server.Close } diff --git a/api/runner/runner_test.go b/api/runner/runner_test.go index 32bc27d64..cd822ffd4 100644 --- a/api/runner/runner_test.go +++ b/api/runner/runner_test.go @@ -498,5 +498,5 @@ func (f *fixture) Run(statusCode int, respBody string) (r *Runner, cleanup func( HTTPClient: http.DefaultClient, } - return New(rest.New(server.URL, cfg)), server.Close + return New(rest.NewFromConfig(server.URL, cfg)), server.Close } diff --git a/cmd/project/dlc.go b/cmd/project/dlc.go new file mode 100644 index 000000000..92bf0e4d3 --- /dev/null +++ b/cmd/project/dlc.go @@ -0,0 +1,59 @@ +package project + +import ( + "github.com/spf13/cobra" + + "github.com/CircleCI-Public/circleci-cli/api/dl" + projectapi "github.com/CircleCI-Public/circleci-cli/api/project" + "github.com/CircleCI-Public/circleci-cli/cmd/validator" + "github.com/CircleCI-Public/circleci-cli/settings" +) + +func newProjectDLCCommand(config *settings.Config, ops *projectOpts, preRunE validator.Validator) *cobra.Command { + cmd := &cobra.Command{ + Use: "dlc", + Short: "Manage dlc for projects", + } + + purgeCommand := &cobra.Command{ + Short: "Purge DLC for a project", + Use: "purge ", + PreRunE: preRunE, + RunE: func(cmd *cobra.Command, args []string) error { + dlClient, err := dl.NewDlRestClient(*config) + if err != nil { + if dl.IsCloudOnlyErr(err) { + cmd.SilenceUsage = true + } + return err + } + + return dlcPurge(cmd, ops.projectClient, dlClient, args[0], args[1], args[2]) + }, + Args: cobra.ExactArgs(3), + } + + cmd.AddCommand(purgeCommand) + return cmd +} + +func dlcPurge(cmd *cobra.Command, projClient projectapi.ProjectClient, dlClient dl.DlClient, vcsType, orgName, projName string) error { + // first we need to work out the project id + projectInfo, err := projClient.ProjectInfo(vcsType, orgName, projName) + if err != nil { + return err + } + projectId := projectInfo.Id + + // now we issue the purge request + err = dlClient.PurgeDLC(projectId) + if err != nil { + if dl.IsGoneErr(err) { + cmd.SilenceUsage = true + } + return err + } + + cmd.Println("Purged DLC for project") + return nil +} diff --git a/cmd/project/environment_variable.go b/cmd/project/environment_variable.go index ff0b5907f..aded40cac 100644 --- a/cmd/project/environment_variable.go +++ b/cmd/project/environment_variable.go @@ -21,7 +21,7 @@ func newProjectEnvironmentVariableCommand(ops *projectOpts, preRunE validator.Va Use: "list ", PreRunE: preRunE, RunE: func(cmd *cobra.Command, args []string) error { - return listProjectEnvironmentVariables(cmd, ops.client, args[0], args[1], args[2]) + return listProjectEnvironmentVariables(cmd, ops.projectClient, args[0], args[1], args[2]) }, Args: cobra.ExactArgs(3), } @@ -32,7 +32,7 @@ func newProjectEnvironmentVariableCommand(ops *projectOpts, preRunE validator.Va Use: "create ", PreRunE: preRunE, RunE: func(cmd *cobra.Command, args []string) error { - return createProjectEnvironmentVariable(cmd, ops.client, ops.reader, args[0], args[1], args[2], args[3], envValue) + return createProjectEnvironmentVariable(cmd, ops.projectClient, ops.reader, args[0], args[1], args[2], args[3], envValue) }, Args: cobra.ExactArgs(4), } diff --git a/cmd/project/project.go b/cmd/project/project.go index d0275619a..d7673c00a 100644 --- a/cmd/project/project.go +++ b/cmd/project/project.go @@ -1,12 +1,12 @@ package project import ( + "github.com/spf13/cobra" + projectapi "github.com/CircleCI-Public/circleci-cli/api/project" "github.com/CircleCI-Public/circleci-cli/cmd/validator" "github.com/CircleCI-Public/circleci-cli/prompt" - "github.com/CircleCI-Public/circleci-cli/settings" - "github.com/spf13/cobra" ) // UserInputReader displays a message and reads a user input value @@ -16,8 +16,8 @@ type UserInputReader interface { } type projectOpts struct { - client projectapi.ProjectClient - reader UserInputReader + projectClient projectapi.ProjectClient + reader UserInputReader } // ProjectOption configures a command created by NewProjectCommand @@ -51,12 +51,13 @@ func NewProjectCommand(config *settings.Config, preRunE validator.Validator, opt if err != nil { return err } - pos.client = client + pos.projectClient = client return nil }, } command.AddCommand(newProjectEnvironmentVariableCommand(&pos, preRunE)) + command.AddCommand(newProjectDLCCommand(config, &pos, preRunE)) return command } diff --git a/cmd/project/project_test.go b/cmd/project/project_test.go index 21f006a62..1706a1ba2 100644 --- a/cmd/project/project_test.go +++ b/cmd/project/project_test.go @@ -11,12 +11,13 @@ import ( "strings" "testing" - "github.com/CircleCI-Public/circleci-cli/cmd/project" - "github.com/CircleCI-Public/circleci-cli/cmd/validator" - "github.com/CircleCI-Public/circleci-cli/settings" "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" "gotest.tools/v3/assert" + + "github.com/CircleCI-Public/circleci-cli/cmd/project" + "github.com/CircleCI-Public/circleci-cli/cmd/validator" + "github.com/CircleCI-Public/circleci-cli/settings" ) const ( @@ -340,6 +341,7 @@ func scaffoldCMD( Token: "testtoken", HTTPClient: http.DefaultClient, Host: baseURL, + DlHost: baseURL, } cmd := project.NewProjectCommand(config, validator, opts...) @@ -350,3 +352,94 @@ func scaffoldCMD( return cmd, stdout, stderr } + +func TestDLCPurge(t *testing.T) { + noValidator := func(_ *cobra.Command, _ []string) error { + return nil + } + + t.Run("Happy path", func(t *testing.T) { + handlers := []http.HandlerFunc{ + func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Method, "GET") + assert.Equal(t, r.URL.String(), fmt.Sprintf("/project/%s/%s/%s", "gh", "whom", "what")) + assert.DeepEqual(t, r.Header["Circle-Token"], []string{"testtoken"}) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{ "id": "this-is-the-project-id" }`)) + assert.NilError(t, err) + }, + func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Method, "DELETE") + assert.Equal(t, r.URL.String(), "/private/output/project/this-is-the-project-id/dlc") + assert.DeepEqual(t, r.Header["Circle-Token"], []string{"testtoken"}) + w.WriteHeader(http.StatusOK) + }, + } + var h http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { + var handler http.HandlerFunc + handler, handlers = handlers[0], handlers[1:] + handler(w, r) + } + server := httptest.NewServer(h) + defer server.Close() + cmd, outbuf, errbuf := scaffoldCMD(server.URL, noValidator) + cmd.SetArgs([]string{"dlc", "purge", "gh", "whom", "what"}) + err := cmd.Execute() + assert.NilError(t, err) + assert.Equal(t, outbuf.String(), "Purged DLC for project\n") + assert.Equal(t, errbuf.String(), "") + }) + t.Run("Gone", func(t *testing.T) { + handlers := []http.HandlerFunc{ + func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{ "id": "this-is-the-project-id" }`)) + assert.NilError(t, err) + }, + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusGone) + }, + } + var h http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { + var handler http.HandlerFunc + handler, handlers = handlers[0], handlers[1:] + handler(w, r) + } + server := httptest.NewServer(h) + defer server.Close() + cmd, outbuf, errbuf := scaffoldCMD(server.URL, noValidator) + cmd.SetArgs([]string{"dlc", "purge", "gh", "whom", "what"}) + err := cmd.Execute() + assert.Assert(t, err != nil) + + assert.Equal(t, outbuf.String(), "") + assert.Equal(t, errbuf.String(), + "Error: No longer supported.\n"+ + "This functionality is no longer supported by this version of the circleci CLI.\n"+ + "Please upgrade to the latest version of the circleci CLI.\n", + ) + }) + t.Run("Not cloud", func(t *testing.T) { + // (this test doesn't use httptest because it's testing a + // misconfiguration and doesn't get as far as making a http request) + cmd := project.NewProjectCommand(&settings.Config{ + Host: "some custom value but dlhost is not set", + HTTPClient: http.DefaultClient, + }, noValidator) + outbuf := new(bytes.Buffer) + errbuf := new(bytes.Buffer) + cmd.SetOut(outbuf) + cmd.SetErr(errbuf) + cmd.SetArgs([]string{"dlc", "purge", "gh", "whom", "what"}) + err := cmd.Execute() + assert.Assert(t, err != nil) + assert.Equal(t, outbuf.String(), "") + assert.Equal(t, errbuf.String(), + "Error: Misconfiguration.\n"+ + "You have configured a custom API endpoint host for the circleci CLI.\n"+ + "However, this functionality is only supported on circleci.com API endpoints.\n", + ) + }) +} diff --git a/cmd/runner/runner.go b/cmd/runner/runner.go index fd647c6d7..72424c1b6 100644 --- a/cmd/runner/runner.go +++ b/cmd/runner/runner.go @@ -27,7 +27,7 @@ func NewCommand(config *settings.Config, preRunE validator.Validator) *cobra.Com } else { host = config.Host } - opts.r = runner.New(rest.New(host, config)) + opts.r = runner.New(rest.NewFromConfig(host, config)) }, } diff --git a/settings/settings.go b/settings/settings.go index 24bc07f33..f963df686 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -15,13 +15,15 @@ import ( "strings" "time" - "github.com/CircleCI-Public/circleci-cli/data" yaml "gopkg.in/yaml.v3" + + "github.com/CircleCI-Public/circleci-cli/data" ) // Config is used to represent the current state of a CLI instance. type Config struct { Host string `yaml:"host"` + DlHost string `yaml:"-"` Endpoint string `yaml:"endpoint"` Token string `yaml:"token"` RestEndpoint string `yaml:"rest_endpoint"` From c4e7392a4a036046e7e4a3dae744276628d2a59b Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Wed, 22 Mar 2023 10:19:15 +0000 Subject: [PATCH 58/84] Migrates config compilation and validation requests away from GQL resolvers to an internal service --- api/api.go | 119 ------ api/rest/client.go | 12 +- api/rest/client_test.go | 115 +++-- cmd/config.go | 78 +++- cmd/config_test.go | 398 +++++------------- cmd/root.go | 7 + config/config.go | 109 +++++ {cmd => config}/deprecated-images.go | 6 +- go.mod | 3 + go.sum | 6 + .../features/circleci_config.feature | 203 +++++++++ local/local.go | 12 +- settings/settings.go | 8 + 13 files changed, 610 insertions(+), 466 deletions(-) create mode 100644 config/config.go rename {cmd => config}/deprecated-images.go (93%) diff --git a/api/api.go b/api/api.go index 75fd208a9..ff6ca667d 100644 --- a/api/api.go +++ b/api/api.go @@ -11,7 +11,6 @@ import ( "strings" "github.com/CircleCI-Public/circleci-cli/api/graphql" - "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/references" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/Masterminds/semver" @@ -513,124 +512,6 @@ func WhoamiQuery(cl *graphql.Client) (*WhoamiResponse, error) { return &response, nil } -// ConfigQueryLegacy calls the GQL API to validate and process config with the legacy orgSlug -func ConfigQueryLegacy(cl *graphql.Client, configPath string, orgSlug string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { - var response BuildConfigResponse - var query string - config, err := loadYaml(configPath) - if err != nil { - return nil, err - } - // GraphQL isn't forwards-compatible, so we are unusually selective here about - // passing only non-empty fields on to the API, to minimize user impact if the - // backend is out of date. - var fieldAddendums string - if orgSlug != "" { - fieldAddendums += ", orgSlug: $orgSlug" - } - if len(params) > 0 { - fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" - } - query = fmt.Sprintf( - `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }`, - fieldAddendums) - - request := graphql.NewRequest(query) - request.SetToken(cl.Token) - request.Var("config", config) - - if values != nil { - request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) - } - if params != nil { - pipelineParameters, err := json.Marshal(params) - if err != nil { - return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) - } - request.Var("pipelineParametersJson", string(pipelineParameters)) - } - - if orgSlug != "" { - request.Var("orgSlug", orgSlug) - } - - err = cl.Run(request, &response) - if err != nil { - return nil, errors.Wrap(err, "Unable to validate config") - } - if len(response.BuildConfig.ConfigResponse.Errors) > 0 { - return nil, &response.BuildConfig.ConfigResponse.Errors - } - - return &response.BuildConfig.ConfigResponse, nil -} - -// ConfigQuery calls the GQL API to validate and process config with the org id -func ConfigQuery(cl *graphql.Client, configPath string, orgId string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) { - var response BuildConfigResponse - var query string - config, err := loadYaml(configPath) - if err != nil { - return nil, err - } - // GraphQL isn't forwards-compatible, so we are unusually selective here about - // passing only non-empty fields on to the API, to minimize user impact if the - // backend is out of date. - var fieldAddendums string - if orgId != "" { - fieldAddendums += ", orgId: $orgId" - } - if len(params) > 0 { - fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" - } - query = fmt.Sprintf( - `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }`, - fieldAddendums) - - request := graphql.NewRequest(query) - request.SetToken(cl.Token) - request.Var("config", config) - - if values != nil { - request.Var("pipelineValues", pipeline.PrepareForGraphQL(values)) - } - if params != nil { - pipelineParameters, err := json.Marshal(params) - if err != nil { - return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) - } - request.Var("pipelineParametersJson", string(pipelineParameters)) - } - - if orgId != "" { - request.Var("orgId", orgId) - } - - err = cl.Run(request, &response) - if err != nil { - return nil, errors.Wrap(err, "Unable to validate config") - } - if len(response.BuildConfig.ConfigResponse.Errors) > 0 { - return nil, &response.BuildConfig.ConfigResponse.Errors - } - - return &response.BuildConfig.ConfigResponse, nil -} - // OrbQuery validated and processes an orb. func OrbQuery(cl *graphql.Client, configPath string) (*ConfigResponse, error) { var response OrbConfigResponse diff --git a/api/rest/client.go b/api/rest/client.go index 9955b3767..57c7bfcc5 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -37,13 +37,13 @@ func NewFromConfig(host string, config *settings.Config) *Client { endpoint += "/" } - u, _ := url.Parse(host) + baseURL, _ := url.Parse(host) client := config.HTTPClient client.Timeout = 10 * time.Second return New( - u.ResolveReference(&url.URL{Path: endpoint}), + baseURL.ResolveReference(&url.URL{Path: endpoint}), config.Token, client, ) @@ -65,6 +65,11 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req return nil, err } + c.enrichRequestHeaders(req, payload) + return req, nil +} + +func (c *Client) enrichRequestHeaders(req *http.Request, payload interface{}) { req.Header.Set("Circle-Token", c.circleToken) req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", version.UserAgent()) @@ -75,12 +80,12 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req if payload != nil { req.Header.Set("Content-Type", "application/json") } - return req, nil } func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int, err error) { httpResp, err := c.client.Do(req) if err != nil { + fmt.Printf("failed to make http request: %s\n", err.Error()) return 0, err } defer httpResp.Body.Close() @@ -91,6 +96,7 @@ func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int, }{} err = json.NewDecoder(httpResp.Body).Decode(&httpError) if err != nil { + fmt.Printf("failed to decode body: %s", err.Error()) return httpResp.StatusCode, err } return httpResp.StatusCode, &HTTPError{Code: httpResp.StatusCode, Message: httpError.Message} diff --git a/api/rest/client_test.go b/api/rest/client_test.go index bf2a06947..30f552092 100644 --- a/api/rest/client_test.go +++ b/api/rest/client_test.go @@ -9,8 +9,7 @@ import ( "sync" "testing" - "gotest.tools/v3/assert" - "gotest.tools/v3/assert/cmp" + "github.com/stretchr/testify/assert" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/CircleCI-Public/circleci-cli/version" @@ -30,29 +29,29 @@ func TestClient_DoRequest(t *testing.T) { A: "aaa", B: 123, }) - assert.NilError(t, err) + assert.Nil(t, err) resp := make(map[string]interface{}) statusCode, err := c.DoRequest(r, &resp) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, statusCode, http.StatusCreated) - assert.Check(t, cmp.DeepEqual(resp, map[string]interface{}{ + assert.Equal(t, resp, map[string]interface{}{ "key": "value", - })) + }) }) t.Run("Check request", func(t *testing.T) { - assert.Check(t, cmp.Equal(fix.URL(), url.URL{Path: "/api/v2/my/endpoint"})) - assert.Check(t, cmp.Equal(fix.Method(), "PUT")) - assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ + assert.Equal(t, fix.URL(), url.URL{Path: "/api/v2/my/endpoint"}) + assert.Equal(t, fix.Method(), "PUT") + assert.Equal(t, fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "Content-Length": {"20"}, "Content-Type": {"application/json"}, "User-Agent": {version.UserAgent()}, - })) - assert.Check(t, cmp.Equal(fix.Body(), `{"A":"aaa","B":123}`+"\n")) + }) + assert.Equal(t, fix.Body(), `{"A":"aaa","B":123}`+"\n") }) }) @@ -63,25 +62,25 @@ func TestClient_DoRequest(t *testing.T) { t.Run("Check result", func(t *testing.T) { r, err := c.NewRequest(http.MethodGet, &url.URL{Path: "my/error/endpoint"}, nil) - assert.NilError(t, err) + assert.Nil(t, err) resp := make(map[string]interface{}) statusCode, err := c.DoRequest(r, &resp) assert.Error(t, err, "the error message") assert.Equal(t, statusCode, http.StatusBadRequest) - assert.Check(t, cmp.DeepEqual(resp, map[string]interface{}{})) + assert.Equal(t, resp, map[string]interface{}{}) }) t.Run("Check request", func(t *testing.T) { - assert.Check(t, cmp.Equal(fix.URL(), url.URL{Path: "/api/v2/my/error/endpoint"})) - assert.Check(t, cmp.Equal(fix.Method(), http.MethodGet)) - assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ + assert.Equal(t, fix.URL(), url.URL{Path: "/api/v2/my/error/endpoint"}) + assert.Equal(t, fix.Method(), http.MethodGet) + assert.Equal(t, fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "User-Agent": {version.UserAgent()}, - })) - assert.Check(t, cmp.Equal(fix.Body(), "")) + }) + assert.Equal(t, fix.Body(), "") }) }) @@ -92,29 +91,91 @@ func TestClient_DoRequest(t *testing.T) { t.Run("Check result", func(t *testing.T) { r, err := c.NewRequest(http.MethodGet, &url.URL{Path: "path"}, nil) - assert.NilError(t, err) + assert.Nil(t, err) resp := make(map[string]interface{}) statusCode, err := c.DoRequest(r, &resp) - assert.NilError(t, err) + assert.Nil(t, err) assert.Equal(t, statusCode, http.StatusCreated) - assert.Check(t, cmp.DeepEqual(resp, map[string]interface{}{ + assert.Equal(t, resp, map[string]interface{}{ "a": "abc", "b": true, - })) + }) }) t.Run("Check request", func(t *testing.T) { - assert.Check(t, cmp.Equal(fix.URL(), url.URL{Path: "/api/v2/path"})) - assert.Check(t, cmp.Equal(fix.Method(), http.MethodGet)) - assert.Check(t, cmp.DeepEqual(fix.Header(), http.Header{ + assert.Equal(t, fix.URL(), url.URL{Path: "/api/v2/path"}) + assert.Equal(t, fix.Method(), http.MethodGet) + assert.Equal(t, fix.Header(), http.Header{ "Accept-Encoding": {"gzip"}, "Accept": {"application/json"}, "Circle-Token": {"fake-token"}, "User-Agent": {version.UserAgent()}, - })) - assert.Check(t, cmp.Equal(fix.Body(), "")) + }) + assert.Equal(t, fix.Body(), "") + }) + }) +} + +func TestAPIRequest(t *testing.T) { + fix := &fixture{} + c, cleanup := fix.Run(http.StatusCreated, `{"key": "value"}`) + defer cleanup() + + t.Run("test new api request sets the default headers", func(t *testing.T) { + req, err := c.NewRequest("GET", &url.URL{}, struct{}{}) + assert.Nil(t, err) + assert.Equal(t, req.Header.Get("User-Agent"), "circleci-cli/0.0.0-dev+dirty-local-tree (source)") + assert.Equal(t, req.Header.Get("Circle-Token"), c.circleToken) + assert.Equal(t, req.Header.Get("Accept"), "application/json") + }) + + type testPayload struct { + Message string + } + + t.Run("test new api request sets the default headers", func(t *testing.T) { + req, err := c.NewRequest("GET", &url.URL{}, testPayload{Message: "hello"}) + assert.Nil(t, err) + assert.Equal(t, req.Header.Get("Circleci-Cli-Command"), "") + assert.Equal(t, req.Header.Get("Content-Type"), "application/json") + }) + + t.Run("test new api request doesn't set content-type with empty payload", func(t *testing.T) { + req, err := c.NewRequest("GET", &url.URL{}, nil) + assert.Nil(t, err) + assert.Equal(t, req.Header.Get("Circleci-Cli-Command"), "") + assert.Equal(t, req.Header.Get("Content-Type"), "") + }) + + type Options struct { + OwnerID string `json:"owner_id,omitempty"` + PipelineParameters map[string]interface{} `json:"pipeline_parameters,omitempty"` + PipelineValues map[string]string `json:"pipeline_values,omitempty"` + } + + type CompileConfigRequest struct { + ConfigYaml string `json:"config_yaml"` + Options Options `json:"options"` + } + + t.Run("config compile and validate payloads have expected shape", func(t *testing.T) { + req, err := c.NewRequest("GET", &url.URL{}, CompileConfigRequest{ + ConfigYaml: "test-config", + Options: Options{ + OwnerID: "1234", + PipelineValues: map[string]string{ + "key": "val", + }, + }, }) + assert.Nil(t, err) + assert.Equal(t, req.Header.Get("Circleci-Cli-Command"), "") + assert.Equal(t, req.Header.Get("Content-Type"), "application/json") + + reqBody, _ := io.ReadAll(req.Body) + assert.Contains(t, string(reqBody), `"config_yaml":"test-config"`) + assert.Contains(t, string(reqBody), `"owner_id":"1234"`) }) } diff --git a/cmd/config.go b/cmd/config.go index 5ae76fcde..603209a59 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -3,10 +3,11 @@ package cmd import ( "fmt" "io/ioutil" + "net/url" "strings" - "github.com/CircleCI-Public/circleci-cli/api" - "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/config" "github.com/CircleCI-Public/circleci-cli/filetree" "github.com/CircleCI-Public/circleci-cli/local" "github.com/CircleCI-Public/circleci-cli/pipeline" @@ -18,9 +19,13 @@ import ( "gopkg.in/yaml.v3" ) +var ( + CollaborationsPath = "me/collaborations" +) + type configOptions struct { cfg *settings.Config - cl *graphql.Client + rest *rest.Client args []string } @@ -63,7 +68,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Check that the config file is well formed.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) + opts.rest = rest.NewFromConfig(config.ConfigAPIHost, config) }, RunE: func(cmd *cobra.Command, _ []string) error { return validateConfig(opts, cmd.Flags()) @@ -85,7 +90,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Validate config and display expanded configuration.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug) + opts.rest = rest.NewFromConfig(config.ConfigAPIHost, config) }, RunE: func(cmd *cobra.Command, _ []string) error { return processConfig(opts, cmd.Flags()) @@ -97,6 +102,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { processCommand.Flags().StringP("org-slug", "o", "", "organization slug (for example: github/example-org), used when a config depends on private orbs belonging to that org") processCommand.Flags().String("org-id", "", "organization id used when a config depends on private orbs belonging to that org") processCommand.Flags().StringP("pipeline-parameters", "", "", "YAML/JSON map of pipeline parameters, accepts either YAML/JSON directly or file path (for example: my-params.yml)") + processCommand.Flags().StringP("circleci-api-host", "", "", "the api-host you want to use for config processing and validation") migrateCommand := &cobra.Command{ Use: "migrate", @@ -125,7 +131,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { // The arg is actually optional, in order to support compatibility with the --path flag. func validateConfig(opts configOptions, flags *pflag.FlagSet) error { var err error - var response *api.ConfigResponse + var response *config.ConfigResponse path := local.DefaultConfigPath // First, set the path to configPath set by --path flag for compatibility if configPath != "" { @@ -142,15 +148,21 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { fmt.Println("Validating config with following values") printValues(values) - orgID, _ := flags.GetString("org-id") + var orgID string + orgID, _ = flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = api.ConfigQuery(opts.cl, path, orgID, nil, values) + response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - response, err = api.ConfigQueryLegacy(opts.cl, path, orgSlug, nil, values) + orgs, err := GetOrgCollaborations(opts.cfg) + if err != nil { + fmt.Println(err.Error()) + } + orgID = GetOrgIdFromSlug(orgSlug, orgs) + response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } @@ -160,16 +172,16 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { // link here to blog post when available // returns an error if a deprecated image is used if !ignoreDeprecatedImages { - err := deprecatedImageCheck(response) + err := config.DeprecatedImageCheck(response) if err != nil { return err } } if path == "-" { - fmt.Printf("Config input is valid.\n") + fmt.Printf("\nConfig input is valid.\n") } else { - fmt.Printf("Config file at %s is valid.\n", path) + fmt.Printf("\nConfig file at %s is valid.\n", path) } return nil @@ -177,7 +189,7 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { func processConfig(opts configOptions, flags *pflag.FlagSet) error { paramsYaml, _ := flags.GetString("pipeline-parameters") - var response *api.ConfigResponse + var response *config.ConfigResponse var params pipeline.Parameters var err error @@ -202,13 +214,18 @@ func processConfig(opts configOptions, flags *pflag.FlagSet) error { orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - response, err = api.ConfigQuery(opts.cl, opts.args[0], orgID, params, values) + response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, values) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - response, err = api.ConfigQueryLegacy(opts.cl, opts.args[0], orgSlug, params, values) + orgs, err := GetOrgCollaborations(opts.cfg) + if err != nil { + fmt.Println(err.Error()) + } + orgID = GetOrgIdFromSlug(orgSlug, orgs) + response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, values) if err != nil { return err } @@ -241,3 +258,34 @@ func printValues(values pipeline.Values) { fmt.Printf("\t%s:\t%s", key, value) } } + +type CollaborationResult struct { + VcsTye string `json:"vcs_type"` + OrgSlug string `json:"slug"` + OrgName string `json:"name"` + OrgId string `json:"id"` + AvatarUrl string `json:"avatar_url"` +} + +// GetOrgCollaborations - fetches all the collaborations for a given user. +func GetOrgCollaborations(cfg *settings.Config) ([]CollaborationResult, error) { + baseClient := rest.NewFromConfig(cfg.Host, cfg) + req, err := baseClient.NewRequest("GET", &url.URL{Path: CollaborationsPath}, nil) + if err != nil { + return nil, err + } + + var resp []CollaborationResult + _, err = baseClient.DoRequest(req, &resp) + return resp, err +} + +// GetOrgIdFromSlug - converts a slug into an orgID. +func GetOrgIdFromSlug(slug string, collaborations []CollaborationResult) string { + for _, v := range collaborations { + if v.OrgSlug == slug { + return v.OrgId + } + } + return "" +} diff --git a/cmd/config_test.go b/cmd/config_test.go index 85b6e8632..d0ed9016d 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -1,20 +1,13 @@ package cmd_test import ( - "encoding/json" "fmt" - "io" - "net/http" "os/exec" "path/filepath" - "time" - "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/clitest" - "github.com/CircleCI-Public/circleci-cli/pipeline" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/onsi/gomega/gbytes" "github.com/onsi/gomega/gexec" "gotest.tools/v3/golden" ) @@ -25,7 +18,6 @@ var _ = Describe("Config", func() { command *exec.Cmd results []byte tempSettings *clitest.TempSettings - token string = "testtoken" ) BeforeEach(func() { @@ -95,6 +87,112 @@ var _ = Describe("Config", func() { }) }) + It("packs all YAML contents as expected", func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/hugo-pack/.circleci") + results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + + It("given a .circleci folder with config.yml and local orb, packs all YAML contents as expected", func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/hugo-pack/.circleci") + results = golden.Get(GinkgoT(), filepath.FromSlash("hugo-pack/result.yml")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + + It("given a local orbs folder with mixed inline and local commands, jobs, etc, packs all YAML contents as expected", func() { + var path string = "nested-orbs-and-local-commands-etc" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + + It("returns an error when validating a config", func() { + var path string = "nested-orbs-and-local-commands-etc" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + + It("packs successfully given an orb containing local executors and commands in folder", func() { + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + "testdata/myorb/test") + + results = golden.Get(GinkgoT(), filepath.FromSlash("myorb/result.yml")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + + It("packs as expected given a large nested config including rails orbs", func() { + var path string = "test-with-large-nested-rails-orb" + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + filepath.Join("testdata", path, "test")) + results = golden.Get(GinkgoT(), filepath.FromSlash(fmt.Sprintf("%s/result.yml", path))) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + session.Wait() + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session.Err.Contents()).Should(BeEmpty()) + Eventually(session.Out.Contents()).Should(MatchYAML(results)) + Eventually(session).Should(gexec.Exit(0)) + }) + + It("prints an error given a config which is a list and not a map", func() { + config := clitest.OpenTmpFile(filepath.Join(tempSettings.Home, "myorb"), "config.yaml") + command = exec.Command(pathCLI, + "config", "pack", + "--skip-update-check", + config.RootDir, + ) + config.Write([]byte(`[]`)) + + expected := fmt.Sprintf("Error: Failed trying to marshal the tree to YAML : expected a map, got a `[]interface {}` which is not supported at this time for \"%s\"\n", config.Path) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + stderr := session.Wait().Err.Contents() + Expect(string(stderr)).To(Equal(expected)) + Eventually(session).Should(clitest.ShouldFail()) + config.Close() + }) + Describe("with a large nested config including rails orb", func() { BeforeEach(func() { var path string = "test-with-large-nested-rails-orb" @@ -146,289 +244,5 @@ var _ = Describe("Config", func() { Eventually(session).Should(clitest.ShouldFail()) }) }) - - Describe("validating configs", func() { - config := "version: 2.1" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "validate", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.SetToken(token) - r.Variables["config"] = config - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns an error when validating a config", func() { - expResp := `{ - "buildConfig": { - "errors": [ - {"message": "error1"} - ] - } - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: error1")) - Eventually(session).Should(clitest.ShouldFail()) - }) - - It("returns successfully when validating a config", func() { - expResp := `{ - "buildConfig": {} - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) - Eventually(session).Should(gexec.Exit(0)) - }) - }) - - Describe("validating configs with pipeline parameters", func() { - config := "version: 2.1" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "process", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "--pipeline-parameters", `{"foo": "test", "bar": true, "baz": 10}`, - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues, pipelineParametersJson: $pipelineParametersJson) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.SetToken(token) - r.Variables["config"] = config - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - pipelineParams, err := json.Marshal(pipeline.Parameters{ - "foo": "test", - "bar": true, - "baz": 10, - }) - Expect(err).ToNot(HaveOccurred()) - r.Variables["pipelineParametersJson"] = string(pipelineParams) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns successfully when validating a config", func() { - expResp := `{ - "buildConfig": {} - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session).Should(gexec.Exit(0)) - }) - }) - - Describe("validating configs with private orbs", func() { - config := "version: 2.1" - orgId := "bb604b45-b6b0-4b81-ad80-796f15eddf87" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "validate", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "--org-id", orgId, - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgId: $orgId) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.SetToken(token) - r.Variables["config"] = config - r.Variables["orgId"] = orgId - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns an error when validating a config with a private orb", func() { - expResp := `{ - "buildConfig": { - "errors": [ - {"message": "permission denied"} - ] - } - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) - Eventually(session).Should(clitest.ShouldFail()) - }) - }) - - Describe("validating configs with private orbs Legacy", func() { - config := "version: 2.1" - orgSlug := "circleci" - var expReq string - - BeforeEach(func() { - command = exec.Command(pathCLI, - "config", "validate", - "--skip-update-check", - "--token", token, - "--host", tempSettings.TestServer.URL(), - "--org-slug", orgSlug, - "-", - ) - - stdin, err := command.StdinPipe() - Expect(err).ToNot(HaveOccurred()) - _, err = io.WriteString(stdin, config) - Expect(err).ToNot(HaveOccurred()) - stdin.Close() - - query := `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { - buildConfig(configYaml: $config, pipelineValues: $pipelineValues, orgSlug: $orgSlug) { - valid, - errors { message }, - sourceYaml, - outputYaml - } - }` - - r := graphql.NewRequest(query) - r.SetToken(token) - r.Variables["config"] = config - r.Variables["orgSlug"] = orgSlug - r.Variables["pipelineValues"] = pipeline.PrepareForGraphQL(pipeline.LocalPipelineValues()) - - req, err := r.Encode() - Expect(err).ShouldNot(HaveOccurred()) - expReq = req.String() - }) - - It("returns an error when validating a config with a private orb", func() { - expResp := `{ - "buildConfig": { - "errors": [ - {"message": "permission denied"} - ] - } - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Err, time.Second*3).Should(gbytes.Say("Error: permission denied")) - Eventually(session).Should(clitest.ShouldFail()) - }) - - It("returns successfully when validating a config with private orbs", func() { - expResp := `{ - "buildConfig": {} - }` - - tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{ - Status: http.StatusOK, - Request: expReq, - Response: expResp, - }) - - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(session.Out, time.Second*3).Should(gbytes.Say("Config input is valid.")) - Eventually(session).Should(gexec.Exit(0)) - }) - }) }) }) diff --git a/cmd/root.go b/cmd/root.go index 6a636cd0c..7f7ed4ec1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -21,6 +21,7 @@ import ( var defaultEndpoint = "graphql-unstable" var defaultHost = "https://circleci.com" +var defaultAPIHost = "https://api.circleci.com" var defaultRestEndpoint = "api/v2" var trueString = "true" @@ -103,6 +104,11 @@ func MakeCommands() *cobra.Command { RestEndpoint: defaultRestEndpoint, Endpoint: defaultEndpoint, GitHubAPI: "https://api.github.com/", + // The config api host differs for both cloud and server setups. + // For cloud, the base domain will be https://api.circleci.com + // for server, this should match the host as we don't have the same + // api subdomain setup + ConfigAPIHost: defaultAPIHost, } if err := rootOptions.Load(); err != nil { @@ -174,6 +180,7 @@ func MakeCommands() *cobra.Command { flags.StringVar(&rootOptions.Host, "host", rootOptions.Host, "URL to your CircleCI host, also CIRCLECI_CLI_HOST") flags.StringVar(&rootOptions.Endpoint, "endpoint", rootOptions.Endpoint, "URI to your CircleCI GraphQL API endpoint") flags.StringVar(&rootOptions.GitHubAPI, "github-api", "https://api.github.com/", "Change the default endpoint to GitHub API for retrieving updates") + flags.StringVar(&rootOptions.ConfigAPIHost, "config-api-host", "https://api.circleci.com", "Change the default endpoint for the config api host") flags.BoolVar(&rootOptions.SkipUpdateCheck, "skip-update-check", skipUpdateByDefault(), "Skip the check for updates check run before every command.") hidden := []string{"github-api", "debug", "endpoint"} diff --git a/config/config.go b/config/config.go new file mode 100644 index 000000000..268e23467 --- /dev/null +++ b/config/config.go @@ -0,0 +1,109 @@ +package config + +import ( + "fmt" + "io/ioutil" + "net/url" + "os" + + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/pipeline" + "github.com/pkg/errors" +) + +type ConfigError struct { + Message string `json:"message"` +} + +type ConfigResponse struct { + Valid bool `json:"valid"` + SourceYaml string `json:"source-yaml"` + OutputYaml string `json:"output-yaml"` + Errors []ConfigError `json:"errors"` +} + +type CompileConfigRequest struct { + ConfigYaml string `json:"config_yaml"` + Options Options `json:"options"` +} + +type Options struct { + OwnerID string `json:"owner_id,omitempty"` + PipelineParameters map[string]interface{} `json:"pipeline_parameters,omitempty"` + PipelineValues map[string]string `json:"pipeline_values,omitempty"` +} + +// #nosec +func loadYaml(path string) (string, error) { + var err error + var config []byte + if path == "-" { + config, err = ioutil.ReadAll(os.Stdin) + } else { + config, err = ioutil.ReadFile(path) + } + + if err != nil { + return "", errors.Wrapf(err, "Could not load config file at %s", path) + } + + return string(config), nil +} + +// ConfigQuery - attempts to compile or validate a given config file with the +// passed in params/values. +// If the orgID is passed in, the config-compilation with private orbs should work. +func ConfigQuery( + rest *rest.Client, + configPath string, + orgID string, + params pipeline.Parameters, + values pipeline.Values, +) (*ConfigResponse, error) { + + configString, err := loadYaml(configPath) + if err != nil { + return nil, err + } + + compileRequest := CompileConfigRequest{ + ConfigYaml: configString, + Options: Options{ + PipelineValues: values, + }, + } + + if orgID != "" { + compileRequest.Options.OwnerID = orgID + } + + if len(params) >= 1 { + compileRequest.Options.PipelineParameters = params + } + + req, err := rest.NewRequest( + "POST", + &url.URL{ + Path: "compile-config-with-defaults", + }, + compileRequest, + ) + if err != nil { + return nil, err + } + + configCompilationResp := &ConfigResponse{} + statusCode, err := rest.DoRequest(req, configCompilationResp) + if err != nil { + return nil, err + } + if statusCode != 200 { + return nil, errors.New("unable to validate or compile config") + } + + if len(configCompilationResp.Errors) > 0 { + return nil, errors.New(fmt.Sprintf("config compilation contains errors: %s", configCompilationResp.Errors)) + } + + return configCompilationResp, nil +} diff --git a/cmd/deprecated-images.go b/config/deprecated-images.go similarity index 93% rename from cmd/deprecated-images.go rename to config/deprecated-images.go index 607edfc88..fcc998fc9 100644 --- a/cmd/deprecated-images.go +++ b/config/deprecated-images.go @@ -1,8 +1,6 @@ -package cmd +package config import ( - "github.com/CircleCI-Public/circleci-cli/api" - "github.com/pkg/errors" "gopkg.in/yaml.v3" ) @@ -40,7 +38,7 @@ type processedConfig struct { } // Processes the config down to v2.0, then checks image used against the block list -func deprecatedImageCheck(response *api.ConfigResponse) error { +func DeprecatedImageCheck(response *ConfigResponse) error { aConfig := processedConfig{} err := yaml.Unmarshal([]byte(response.OutputYaml), &aConfig) diff --git a/go.mod b/go.mod index b97d92c58..422116354 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,7 @@ require ( github.com/charmbracelet/bubbles v0.11.0 // indirect github.com/charmbracelet/bubbletea v0.21.0 // indirect github.com/containerd/console v1.0.3 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/emirpasic/gods v1.12.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect @@ -71,10 +72,12 @@ require ( github.com/muesli/termenv v0.12.0 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/open-policy-agent/opa v0.49.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect + github.com/stretchr/testify v1.8.2 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect github.com/xanzy/ssh-agent v0.2.1 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect diff --git a/go.sum b/go.sum index edb48fa6b..ebcbb5ce8 100644 --- a/go.sum +++ b/go.sum @@ -315,11 +315,17 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw= diff --git a/integration_tests/features/circleci_config.feature b/integration_tests/features/circleci_config.feature index d9085cc2b..439103b78 100644 --- a/integration_tests/features/circleci_config.feature +++ b/integration_tests/features/circleci_config.feature @@ -15,6 +15,209 @@ Feature: Config checking Then the exit status should be 0 And the output should contain "Config file at config.yml is valid." + Scenario: Checking a valid config file with an orb + Given a file named "config.yml" with: + """ + version: 2.1 + + orbs: + node: circleci/node@5.0.3 + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci config validate --skip-update-check -c config.yml` + Then the exit status should be 0 + And the output should contain "Config file at config.yml is valid" + + Scenario: Checking a valid config against the k9s server + Given a file named "config.yml" with: + """ + version: 2.1 + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci --config-api-host https://k9s.sphereci.com config validate --skip-update-check -c config.yml` + Then the exit status should be 0 + And the output should contain "Config file at config.yml is valid" + + Scenario: Checking a valid config file with an orb + Given a file named "config.yml" with: + """ + version: 2.1 + + orbs: + node: circleci/node@5.0.3 + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci config validate --skip-update-check -c config.yml` + Then the exit status should be 0 + And the output should contain "Config file at config.yml is valid" + + Scenario: Checking a valid config file with a private org + Given a file named "config.yml" with: + """ + version: 2.1 + + orbs: + node: circleci/node@5.0.3 + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci config validate --skip-update-check --org-id bb604b45-b6b0-4b81-ad80-796f15eddf87 -c config.yml` + Then the output should contain "Config file at config.yml is valid" + And the exit status should be 0 + + Scenario: Checking a valid config file with a non-existant orb + Given a file named "config.yml" with: + """ + version: 2.1 + + orbs: + node: circleci/doesnt-exist@5.0.3 + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci config validate --skip-update-check -c config.yml` + Then the exit status should be 255 + And the output should contain "config compilation contains errors" + + Scenario: Checking a valid config file with pipeline-parameters + Given a file named "config.yml" with: + """ + version: 2.1 + + parameters: + foo: + type: string + default: "bar" + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + echo << pipeline.parameters.foo >> + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci config process config.yml --pipeline-parameters "foo: fighters"` + Then the output should contain "fighters" + And the exit status should be 0 + + Scenario: Checking a valid config file with default pipeline params + Given a file named "config.yml" with: + """ + version: 2.1 + + parameters: + foo: + type: string + default: "bar" + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + echo << pipeline.parameters.foo >> + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + When I run `circleci config process config.yml` + Then the output should contain "bar" + And the exit status should be 0 + + Scenario: Checking a valid config file with file pipeline-parameters + Given a file named "config.yml" with: + """ + version: 2.1 + + parameters: + foo: + type: string + default: "bar" + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" + echo << pipeline.parameters.foo >> + workflows: + datadog-hello-world: + jobs: + - datadog-hello-world + """ + And I write to "params.yml" with: + """ + foo: "totallyawesome" + """ + When I run `circleci config process config.yml --pipeline-parameters params.yml` + Then the output should contain "totallyawesome" + And the exit status should be 0 + + Scenario: Checking an invalid config file Given a file named "config.yml" with: """ diff --git a/local/local.go b/local/local.go index 373a88dd2..845f5b33b 100644 --- a/local/local.go +++ b/local/local.go @@ -12,8 +12,8 @@ import ( "strings" "syscall" - "github.com/CircleCI-Public/circleci-cli/api" - "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/api/rest" + "github.com/CircleCI-Public/circleci-cli/config" "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/pkg/errors" @@ -26,21 +26,21 @@ const DefaultConfigPath = ".circleci/config.yml" func Execute(flags *pflag.FlagSet, cfg *settings.Config, args []string) error { var err error - var configResponse *api.ConfigResponse - cl := graphql.NewClient(cfg.HTTPClient, cfg.Host, cfg.Endpoint, cfg.Token, cfg.Debug) + var configResponse *config.ConfigResponse + restClient := rest.NewFromConfig(cfg.Host, cfg) processedArgs, configPath := buildAgentArguments(flags) //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - configResponse, err = api.ConfigQuery(cl, configPath, orgID, nil, pipeline.LocalPipelineValues()) + configResponse, err = config.ConfigQuery(restClient, configPath, orgID, nil, pipeline.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - configResponse, err = api.ConfigQueryLegacy(cl, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) + configResponse, err = config.ConfigQuery(restClient, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) if err != nil { return err } diff --git a/settings/settings.go b/settings/settings.go index f963df686..ce542af26 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -37,6 +37,10 @@ type Config struct { GitHubAPI string `yaml:"-"` SkipUpdateCheck bool `yaml:"-"` OrbPublishing OrbPublishingInfo `yaml:"orb_publishing"` + // Represents the API host we want to use for config compilation and validation + // requests - this is typically on the api.circleci.com subdomain for cloud, or the + // same domain for server instances. + ConfigAPIHost string `yaml:"-"` } type OrbPublishingInfo struct { @@ -130,6 +134,10 @@ func (cfg *Config) WriteToDisk() error { func (cfg *Config) LoadFromEnv(prefix string) { if host := ReadFromEnv(prefix, "host"); host != "" { cfg.Host = host + // If the user is a server customer and overwrites the default + // https://circleci.com host - we then have to use this as the host for + // any config compilation or validation requests as opposed to https://api.circleci.com + cfg.ConfigAPIHost = host } if restEndpoint := ReadFromEnv(prefix, "rest_endpoint"); restEndpoint != "" { From 5298cf61d3b668f93dca9b6433bb196beae216fa Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Wed, 22 Mar 2023 15:27:48 +0000 Subject: [PATCH 59/84] Changes behaviour for the config process and validate commands to respect the --host flag when it's set to a non-default host --- cmd/config.go | 12 ++++++++++-- cmd/config_test.go | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index 603209a59..a30c82d78 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -38,6 +38,14 @@ var configAnnotations = map[string]string{ "": "The path to your config (use \"-\" for STDIN)", } +func GetConfigAPIHost(cfg *settings.Config) string { + if cfg.Host != defaultHost { + return cfg.Host + } else { + return cfg.ConfigAPIHost + } +} + func newConfigCommand(config *settings.Config) *cobra.Command { opts := configOptions{ cfg: config, @@ -68,7 +76,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Check that the config file is well formed.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.rest = rest.NewFromConfig(config.ConfigAPIHost, config) + opts.rest = rest.NewFromConfig(GetConfigAPIHost(opts.cfg), config) }, RunE: func(cmd *cobra.Command, _ []string) error { return validateConfig(opts, cmd.Flags()) @@ -90,7 +98,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Short: "Validate config and display expanded configuration.", PreRun: func(cmd *cobra.Command, args []string) { opts.args = args - opts.rest = rest.NewFromConfig(config.ConfigAPIHost, config) + opts.rest = rest.NewFromConfig(GetConfigAPIHost(opts.cfg), config) }, RunE: func(cmd *cobra.Command, _ []string) error { return processConfig(opts, cmd.Flags()) diff --git a/cmd/config_test.go b/cmd/config_test.go index d0ed9016d..1d0967e79 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -4,11 +4,15 @@ import ( "fmt" "os/exec" "path/filepath" + "testing" "github.com/CircleCI-Public/circleci-cli/clitest" + "github.com/CircleCI-Public/circleci-cli/cmd" + "github.com/CircleCI-Public/circleci-cli/settings" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" + "github.com/stretchr/testify/assert" "gotest.tools/v3/golden" ) @@ -246,3 +250,17 @@ var _ = Describe("Config", func() { }) }) }) + +func TestGetConfigAPIHost(t *testing.T) { + t.Run("tests that we correctly get the config api host when the host is not the default one", func(t *testing.T) { + // if the host isn't equal to `https://circleci.com` then this is likely a server instance and + // wont have the api.X.com subdomain so we should instead just respect the host for config commands + host := cmd.GetConfigAPIHost(&settings.Config{Host: "test"}) + assert.Equal(t, host, "test") + + // If the host passed in is the same as the defaultHost 'https://circleci.com' - then we know this is cloud + // and as such should use the `api.circleci.com` subdomain + host = cmd.GetConfigAPIHost(&settings.Config{Host: "https://circleci.com", ConfigAPIHost: "https://api.circleci.com"}) + assert.Equal(t, host, "https://api.circleci.com") + }) +} From dc43137315f72053725afedf4d7e45574e913b30 Mon Sep 17 00:00:00 2001 From: William Yardley Date: Tue, 7 Mar 2023 16:14:10 -0800 Subject: [PATCH 60/84] feat: add verbose flag for pipeline values - Add a `-v` / `--verbose` flag to enable verbose output - Print the config values one line at a time, and add newlines - Write to stderr so that outputting to a file won't generate invalid YAML when writing files to stdout Fixes #860 --- cmd/config.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index a30c82d78..dce808c14 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "net/url" + "os" "strings" "github.com/CircleCI-Public/circleci-cli/api/rest" @@ -33,6 +34,7 @@ type configOptions struct { // Used to for compatibility with `circleci config validate --path` var configPath string var ignoreDeprecatedImages bool // should we ignore deprecated images warning +var verboseOutput bool // Enable extra debugging output var configAnnotations = map[string]string{ "": "The path to your config (use \"-\" for STDIN)", @@ -86,6 +88,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { } validateCommand.Annotations[""] = configAnnotations[""] validateCommand.PersistentFlags().StringVarP(&configPath, "config", "c", ".circleci/config.yml", "path to config file") + validateCommand.PersistentFlags().BoolVarP(&verboseOutput, "verbose", "v", false, "Enable verbose output") validateCommand.PersistentFlags().BoolVar(&ignoreDeprecatedImages, "ignore-deprecated-images", false, "ignores the deprecated images error") if err := validateCommand.PersistentFlags().MarkHidden("config"); err != nil { panic(err) @@ -153,8 +156,9 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { //if no orgId provided use org slug values := pipeline.LocalPipelineValues() - fmt.Println("Validating config with following values") - printValues(values) + if verboseOutput { + printValues(values) + } var orgID string orgID, _ = flags.GetString("org-id") @@ -217,7 +221,6 @@ func processConfig(opts configOptions, flags *pflag.FlagSet) error { //if no orgId provided use org slug values := pipeline.LocalPipelineValues() - fmt.Println("Processing config with following values") printValues(values) orgID, _ := flags.GetString("org-id") @@ -262,8 +265,9 @@ func migrateConfig(opts configOptions) error { } func printValues(values pipeline.Values) { + fmt.Fprintln(os.Stderr, "Processing config with following values:") for key, value := range values { - fmt.Printf("\t%s:\t%s", key, value) + fmt.Fprintf(os.Stderr, "%-18s %s\n", key+":", value) } } From 8abc86a1a8941f69891431ec58949c1befc53eb7 Mon Sep 17 00:00:00 2001 From: David Desmarais-Michaud Date: Thu, 23 Mar 2023 08:27:16 -0400 Subject: [PATCH 61/84] use go1.20 (#882) --- .circleci/config.yml | 6 +++--- Dockerfile | 2 +- go.mod | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b98345b83..79dc8cb66 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ orbs: executors: go: docker: - - image: cimg/go:1.18 + - image: cimg/go:1.20 environment: CGO_ENABLED: 0 mac: @@ -108,8 +108,8 @@ jobs: - checkout - run: | brew update - brew install go@1.18 - echo 'export PATH="/usr/local/opt/go@1.18/bin:$PATH"' >> ~/.bash_profile + brew install go@1.20 + echo 'export PATH="/usr/local/opt/go@1.20/bin:$PATH"' >> ~/.bash_profile - gomod - run: make test build: diff --git a/Dockerfile b/Dockerfile index c94a4659f..b5194eb1f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM cimg/go:1.18.3 +FROM cimg/go:1.20 ENV CIRCLECI_CLI_SKIP_UPDATE_CHECK true diff --git a/go.mod b/go.mod index 422116354..0133b1211 100644 --- a/go.mod +++ b/go.mod @@ -98,4 +98,4 @@ require ( // fix vulnerability: CVE-2020-15114 in etcd v3.3.10+incompatible replace github.com/coreos/etcd => github.com/coreos/etcd v3.3.24+incompatible -go 1.18 +go 1.20 From 31c41779b61e9279deea29c819bfe5f17b26bd5b Mon Sep 17 00:00:00 2001 From: David Desmarais-Michaud Date: Thu, 23 Mar 2023 12:29:59 -0400 Subject: [PATCH 62/84] update circle policy agent (#885) --- cmd/policy/policy_test.go | 28 +++++++++++----- .../meta-policy-subdir/meta-policy.rego | 2 +- cmd/policy/testdata/test1/meta.yml | 3 +- cmd/policy/testdata/test_policies/policy.rego | 2 +- .../testdata/test_policies/policy_test.yaml | 6 ++-- go.mod | 16 +++++----- go.sum | 32 +++++++++---------- 7 files changed, 52 insertions(+), 37 deletions(-) diff --git a/cmd/policy/policy_test.go b/cmd/policy/policy_test.go index f83135c0e..cf98e1a10 100644 --- a/cmd/policy/policy_test.go +++ b/cmd/policy/policy_test.go @@ -711,7 +711,7 @@ func TestMakeDecisionCommand(t *testing.T) { "input": "test: config\n", "metadata": map[string]interface{}{ "project_id": "test-project-id", - "branch": "main", + "vcs": map[string]any{"branch": "main"}, }, }) @@ -868,12 +868,15 @@ func TestRawOPAEvaluationCommand(t *testing.T) { { Name: "successfully performs raw opa evaluation for policy FILE provided locally, input and metadata", Args: []string{ - "eval", "./testdata/test0/subdir/meta-policy-subdir/meta-policy.rego", "--metafile", - "./testdata/test1/meta.yml", "--input", "./testdata/test0/config.yml", + "eval", "./testdata/test0/subdir/meta-policy-subdir/meta-policy.rego", + "--metafile", "./testdata/test1/meta.yml", + "--input", "./testdata/test0/config.yml", }, ExpectedOutput: `{ "meta": { - "branch": "main", + "vcs": { + "branch": "main" + }, "project_id": "test-project-id" }, "org": { @@ -890,8 +893,10 @@ func TestRawOPAEvaluationCommand(t *testing.T) { { Name: "successfully performs raw opa evaluation for policy FILE provided locally, input, metadata and query", Args: []string{ - "eval", "./testdata/test0/subdir/meta-policy-subdir/meta-policy.rego", "--metafile", - "./testdata/test1/meta.yml", "--input", "./testdata/test0/config.yml", "--query", "data.org.enable_rule", + "eval", "./testdata/test0/subdir/meta-policy-subdir/meta-policy.rego", + "--metafile", "./testdata/test1/meta.yml", + "--input", "./testdata/test0/config.yml", + "--query", "data.org.enable_rule", }, ExpectedOutput: `[ "enabled" @@ -911,7 +916,9 @@ func TestRawOPAEvaluationCommand(t *testing.T) { cmd, stdout, _ := makeCMD() - cmd.SetArgs(append(tc.Args, "--policy-base-url", svr.URL)) + args := append(tc.Args, "--policy-base-url", svr.URL) + + cmd.SetArgs(args) err := cmd.Execute() if tc.ExpectedErr == "" { @@ -920,7 +927,12 @@ func TestRawOPAEvaluationCommand(t *testing.T) { assert.ErrorContains(t, err, tc.ExpectedErr) return } - assert.Equal(t, stdout.String(), tc.ExpectedOutput) + + var actual, expected any + assert.NilError(t, json.Unmarshal(stdout.Bytes(), &actual)) + assert.NilError(t, json.Unmarshal([]byte(tc.ExpectedOutput), &expected)) + + assert.DeepEqual(t, actual, expected) }) } } diff --git a/cmd/policy/testdata/test0/subdir/meta-policy-subdir/meta-policy.rego b/cmd/policy/testdata/test0/subdir/meta-policy-subdir/meta-policy.rego index 3553e4c74..6b22fb441 100644 --- a/cmd/policy/testdata/test0/subdir/meta-policy-subdir/meta-policy.rego +++ b/cmd/policy/testdata/test0/subdir/meta-policy-subdir/meta-policy.rego @@ -1,5 +1,5 @@ package org policy_name["meta_policy_test"] -enable_rule["enabled"] { data.meta.branch == "main" } +enable_rule["enabled"] { data.meta.vcs.branch == "main" } enable_rule["disabled"] { data.meta.project_id != "test-project-id" } diff --git a/cmd/policy/testdata/test1/meta.yml b/cmd/policy/testdata/test1/meta.yml index ed8d9bde4..89cabab0a 100644 --- a/cmd/policy/testdata/test1/meta.yml +++ b/cmd/policy/testdata/test1/meta.yml @@ -1,2 +1,3 @@ project_id: test-project-id -branch: main +vcs: + branch: main diff --git a/cmd/policy/testdata/test_policies/policy.rego b/cmd/policy/testdata/test_policies/policy.rego index 2499ff857..be57316cd 100644 --- a/cmd/policy/testdata/test_policies/policy.rego +++ b/cmd/policy/testdata/test_policies/policy.rego @@ -4,4 +4,4 @@ policy_name["test"] enable_rule["fail_if_not_main"] -fail_if_not_main = "branch must be main!" { data.meta.branch != "main" } +fail_if_not_main = "branch must be main!" { data.meta.vcs.branch != "main" } diff --git a/cmd/policy/testdata/test_policies/policy_test.yaml b/cmd/policy/testdata/test_policies/policy_test.yaml index 5d93f32df..e351025c9 100644 --- a/cmd/policy/testdata/test_policies/policy_test.yaml +++ b/cmd/policy/testdata/test_policies/policy_test.yaml @@ -1,6 +1,7 @@ test_main: meta: - branch: main + vcs: + branch: main decision: &root_decision status: PASS enabled_rules: @@ -8,7 +9,8 @@ test_main: test_feature: meta: - branch: feature + vcs: + branch: feature decision: <<: *root_decision status: SOFT_FAIL diff --git a/go.mod b/go.mod index 0133b1211..4b3ee6084 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/CircleCI-Public/circleci-cli require ( github.com/AlecAivazis/survey/v2 v2.1.1 - github.com/CircleCI-Public/circle-policy-agent v0.0.564 + github.com/CircleCI-Public/circle-policy-agent v0.0.583 github.com/Masterminds/semver v1.4.2 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/blang/semver v3.5.1+incompatible @@ -35,6 +35,7 @@ require ( github.com/charmbracelet/lipgloss v0.5.0 github.com/erikgeiser/promptkit v0.7.0 github.com/hexops/gotextdiff v1.0.3 + github.com/stretchr/testify v1.8.2 golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb ) @@ -52,7 +53,7 @@ require ( github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.0.0 // indirect github.com/gobwas/glob v0.2.3 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 // indirect github.com/imdario/mergo v0.3.12 // indirect @@ -71,13 +72,12 @@ require ( github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.12.0 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/open-policy-agent/opa v0.49.0 // indirect + github.com/open-policy-agent/opa v0.50.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect - github.com/stretchr/testify v1.8.2 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect github.com/xanzy/ssh-agent v0.2.1 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect @@ -85,10 +85,10 @@ require ( github.com/yashtewari/glob-intersection v0.1.0 // indirect github.com/yazgazan/jaydiff v0.3.1 // indirect golang.org/x/crypto v0.3.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index ebcbb5ce8..284582ffd 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eim github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CircleCI-Public/circle-policy-agent v0.0.564 h1:i7CkwfUVL9fItTc1X9evcCgwmuiRsTOODcCmYrByu7s= -github.com/CircleCI-Public/circle-policy-agent v0.0.564/go.mod h1:oySWkeJOAnvT56DRaGL0n2lHmeCEFwrhGg0Bvj9jiI8= +github.com/CircleCI-Public/circle-policy-agent v0.0.583 h1:c4Q48txEoO182l77CjSWGNhRHUUPOwlbm/mr/LG+nBs= +github.com/CircleCI-Public/circle-policy-agent v0.0.583/go.mod h1:zD3HRb1jYVe8wnbQz5UPF/Ol1lEsDxC47v3NysIY9Fw= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= @@ -65,7 +65,7 @@ github.com/briandowns/spinner v1.18.1/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/pp github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/charmbracelet/bubbles v0.11.0 h1:fBLyY0PvJnd56Vlu5L84JJH6f4axhgIJ9P3NET78f0Q= github.com/charmbracelet/bubbles v0.11.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= github.com/charmbracelet/bubbletea v0.21.0 h1:f3y+kanzgev5PA916qxmDybSHU3N804uOnKnhRPXTcI= @@ -103,7 +103,7 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= -github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897 h1:E52jfcE64UG42SwLmrW0QByONfGynWuzBvm86BoB9z8= +github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -154,8 +154,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -279,8 +280,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/open-policy-agent/opa v0.49.0 h1:TIlpCT1B5FSm8Dqo/a4t23gKmHkQysC3+7W77F99P4k= -github.com/open-policy-agent/opa v0.49.0/go.mod h1:WTLWtu498/QNTDkiHx76Xj7jaJUPvLJAPtdMkCcst0w= +github.com/open-policy-agent/opa v0.50.2 h1:iD2kKLFkflgSCTMtrC/3jLmOQ7IWyDXMg6+VQA0tSC0= +github.com/open-policy-agent/opa v0.50.2/go.mod h1:9jKfDk0L5b9rnhH4M0nq10cGHbYOxqygxzTT3dsvhec= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -323,7 +324,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= @@ -425,8 +425,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -489,21 +489,21 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From f9bb7733b73364137d104198533bd8160dd28afe Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Fri, 24 Mar 2023 11:19:08 +0000 Subject: [PATCH 63/84] Migrates the old calls to the deprecated ioutil package to io and os respectively --- api/api.go | 6 +++--- api/context_rest.go | 17 ++++++++--------- api/context_rest_test.go | 4 ++-- api/graphql/client.go | 6 +++--- api/graphql/client_test.go | 7 +++---- api/info/info.go | 3 +-- api/schedule_rest.go | 13 ++++++------- clitest/clitest.go | 11 +++++------ cmd/config.go | 4 ++-- cmd/context.go | 4 ++-- cmd/orb.go | 21 ++++++++++----------- cmd/orb_import_test.go | 4 ++-- cmd/query.go | 6 +++--- cmd/setup_test.go | 4 ++-- config/config.go | 6 +++--- filetree/filetree.go | 3 +-- filetree/filetree_test.go | 9 ++++----- local/local.go | 3 +-- local/local_test.go | 6 +++--- mock/http.go | 4 ++-- process/process.go | 4 ++-- settings/settings.go | 11 +++++------ 22 files changed, 73 insertions(+), 83 deletions(-) diff --git a/api/api.go b/api/api.go index ff6ca667d..3ea130e0d 100644 --- a/api/api.go +++ b/api/api.go @@ -3,7 +3,7 @@ package api import ( "encoding/json" "fmt" - "io/ioutil" + "io" "log" "net/http" "os" @@ -484,9 +484,9 @@ func loadYaml(path string) (string, error) { var err error var config []byte if path == "-" { - config, err = ioutil.ReadAll(os.Stdin) + config, err = io.ReadAll(os.Stdin) } else { - config, err = ioutil.ReadFile(path) + config, err = os.ReadFile(path) } if err != nil { diff --git a/api/context_rest.go b/api/context_rest.go index d953bc7aa..ce484ea7c 100644 --- a/api/context_rest.go +++ b/api/context_rest.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" @@ -71,7 +70,7 @@ func (c *ContextRestClient) DeleteEnvironmentVariable(contextID, variable string return err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return err @@ -99,7 +98,7 @@ func (c *ContextRestClient) CreateContextWithOrgID(orgID *string, name string) e return err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return err @@ -131,7 +130,7 @@ func (c *ContextRestClient) CreateContext(vcs, org, name string) error { return err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return err @@ -162,7 +161,7 @@ func (c *ContextRestClient) CreateEnvironmentVariable(contextID, variable, value return err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return err @@ -190,7 +189,7 @@ func (c *ContextRestClient) DeleteContext(contextID string) error { return err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return err @@ -300,7 +299,7 @@ func (c *ContextRestClient) listEnvironmentVariables(params *listEnvironmentVari return nil, err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return nil, err @@ -334,7 +333,7 @@ func (c *ContextRestClient) listContexts(params *listContextsParams) (*listConte return nil, err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return nil, err @@ -580,7 +579,7 @@ func (c *ContextRestClient) EnsureExists() error { return errors.New("API v2 test request failed.") } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return err diff --git a/api/context_rest_test.go b/api/context_rest_test.go index 1c969721d..005c64908 100644 --- a/api/context_rest_test.go +++ b/api/context_rest_test.go @@ -2,7 +2,7 @@ package api import ( "fmt" - "io/ioutil" + "io" "net/http" "github.com/CircleCI-Public/circleci-cli/settings" @@ -32,7 +32,7 @@ func appendRESTPostHandler(server *ghttp.Server, combineHandlers ...MockRequestR ghttp.VerifyRequest("POST", "/api/v2/context"), ghttp.VerifyContentType("application/json"), func(w http.ResponseWriter, req *http.Request) { - body, err := ioutil.ReadAll(req.Body) + body, err := io.ReadAll(req.Body) Expect(err).ShouldNot(HaveOccurred()) err = req.Body.Close() Expect(err).ShouldNot(HaveOccurred()) diff --git a/api/graphql/client.go b/api/graphql/client.go index 9392324f9..d5c5d8613 100644 --- a/api/graphql/client.go +++ b/api/graphql/client.go @@ -5,7 +5,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "log" "net/http" "net/url" @@ -254,7 +254,7 @@ func (cl *Client) Run(request *Request, resp interface{}) error { if cl.Debug { var bodyBytes []byte if res.Body != nil { - bodyBytes, err = ioutil.ReadAll(res.Body) + bodyBytes, err = io.ReadAll(res.Body) if err != nil { return errors.Wrap(err, "reading response") } @@ -262,7 +262,7 @@ func (cl *Client) Run(request *Request, resp interface{}) error { l.Printf("<< %s", string(bodyBytes)) // Restore the io.ReadCloser to its original state - res.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) + res.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) } } diff --git a/api/graphql/client_test.go b/api/graphql/client_test.go index b0a27d471..2edf3ef9c 100644 --- a/api/graphql/client_test.go +++ b/api/graphql/client_test.go @@ -2,7 +2,6 @@ package graphql import ( "io" - "io/ioutil" "net/http" "net/http/httptest" "regexp" @@ -53,7 +52,7 @@ func TestDoJSON(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { calls++ - b, err := ioutil.ReadAll(r.Body) + b, err := io.ReadAll(r.Body) if err != nil { t.Errorf(err.Error()) } @@ -96,7 +95,7 @@ func TestQueryJSON(t *testing.T) { var calls int srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { calls++ - b, err := ioutil.ReadAll(r.Body) + b, err := io.ReadAll(r.Body) if err != nil { t.Errorf(err.Error()) } @@ -146,7 +145,7 @@ func TestDoJSONErr(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, req *http.Request) { calls++ - body, err := ioutil.ReadAll(req.Body) + body, err := io.ReadAll(req.Body) if err != nil { t.Errorf(err.Error()) } diff --git a/api/info/info.go b/api/info/info.go index cb7fae40b..df1c79455 100644 --- a/api/info/info.go +++ b/api/info/info.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net/http" "net/url" @@ -58,7 +57,7 @@ func (c *InfoRESTClient) GetInfo() (*[]Organization, error) { return nil, err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return nil, err diff --git a/api/schedule_rest.go b/api/schedule_rest.go index 6b35d700f..10dd2088e 100644 --- a/api/schedule_rest.go +++ b/api/schedule_rest.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" @@ -49,7 +48,7 @@ func (c *ScheduleRestClient) CreateSchedule(vcs, org, project, name, description return nil, err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return nil, err @@ -86,7 +85,7 @@ func (c *ScheduleRestClient) UpdateSchedule(scheduleID, name, description string return nil, err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return nil, err @@ -121,7 +120,7 @@ func (c *ScheduleRestClient) DeleteSchedule(scheduleID string) error { return err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return err @@ -157,7 +156,7 @@ func (c *ScheduleRestClient) ScheduleByID(scheduleID string) (*Schedule, error) return nil, err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return nil, err @@ -231,7 +230,7 @@ func (c *ScheduleRestClient) listSchedules(vcs, org, project string, params *lis return nil, err } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return nil, err @@ -438,7 +437,7 @@ func (c *ScheduleRestClient) EnsureExists() error { return errors.New("API v2 test request failed.") } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return err diff --git a/clitest/clitest.go b/clitest/clitest.go index e6ae4de59..6a0010d5c 100644 --- a/clitest/clitest.go +++ b/clitest/clitest.go @@ -5,7 +5,6 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -50,7 +49,7 @@ func (tempSettings TempSettings) AssertConfigRereadMatches(contents string) { file, err := os.Open(tempSettings.Config.Path) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - reread, err := ioutil.ReadAll(file) + reread, err := io.ReadAll(file) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(string(reread)).To(gomega.ContainSubstring(contents)) } @@ -61,7 +60,7 @@ func WithTempSettings() *TempSettings { tempSettings := &TempSettings{} - tempSettings.Home, err = ioutil.TempDir("", "circleci-cli-test-") + tempSettings.Home, err = os.MkdirTemp("", "circleci-cli-test-") gomega.Expect(err).ToNot(gomega.HaveOccurred()) settingsPath := filepath.Join(tempSettings.Home, ".circleci") @@ -101,7 +100,7 @@ func (tempSettings *TempSettings) AppendRESTPostHandler(combineHandlers ...MockR ghttp.VerifyRequest("POST", "/api/v2/context"), ghttp.VerifyContentType("application/json"), func(w http.ResponseWriter, req *http.Request) { - body, err := ioutil.ReadAll(req.Body) + body, err := io.ReadAll(req.Body) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) err = req.Body.Close() gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) @@ -131,7 +130,7 @@ func (tempSettings *TempSettings) AppendPostHandler(authToken string, combineHan // VerifyContentType("application/json") check // that fails with "application/json; charset=utf-8" func(w http.ResponseWriter, req *http.Request) { - body, err := ioutil.ReadAll(req.Body) + body, err := io.ReadAll(req.Body) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) err = req.Body.Close() gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) @@ -152,7 +151,7 @@ func (tempSettings *TempSettings) AppendPostHandler(authToken string, combineHan // VerifyContentType("application/json") check // that fails with "application/json; charset=utf-8" func(w http.ResponseWriter, req *http.Request) { - body, err := ioutil.ReadAll(req.Body) + body, err := io.ReadAll(req.Body) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) err = req.Body.Close() gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) diff --git a/cmd/config.go b/cmd/config.go index a30c82d78..9585bb3af 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -2,8 +2,8 @@ package cmd import ( "fmt" - "io/ioutil" "net/url" + "os" "strings" "github.com/CircleCI-Public/circleci-cli/api/rest" @@ -204,7 +204,7 @@ func processConfig(opts configOptions, flags *pflag.FlagSet) error { if len(paramsYaml) > 0 { // The 'src' value can be a filepath, or a yaml string. If the file cannot be read successfully, // proceed with the assumption that the value is already valid yaml. - raw, err := ioutil.ReadFile(paramsYaml) + raw, err := os.ReadFile(paramsYaml) if err != nil { raw = []byte(paramsYaml) } diff --git a/cmd/context.go b/cmd/context.go index aebdd6558..bc9ae2d8f 100644 --- a/cmd/context.go +++ b/cmd/context.go @@ -3,7 +3,7 @@ package cmd import ( "bufio" "fmt" - "io/ioutil" + "io" "os" "strings" "time" @@ -189,7 +189,7 @@ func showContext(client api.ContextInterface, vcsType, orgName, contextName stri func readSecretValue() (string, error) { stat, _ := os.Stdin.Stat() if (stat.Mode() & os.ModeCharDevice) == 0 { - bytes, err := ioutil.ReadAll(os.Stdin) + bytes, err := io.ReadAll(os.Stdin) return string(bytes), err } else { fmt.Print("Enter secret value and press enter: ") diff --git a/cmd/orb.go b/cmd/orb.go index bbd81858e..33a06c855 100644 --- a/cmd/orb.go +++ b/cmd/orb.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "log" "net/http" "os" @@ -1122,7 +1121,7 @@ func initOrb(opts orbOptions) error { return errors.Wrap(err, "Unexpected error") } - body, err := ioutil.ReadAll(req.Body) + body, err := io.ReadAll(req.Body) if err != nil { return errors.Wrap(err, "Unexpected error") } @@ -1385,46 +1384,46 @@ func initOrb(opts orbOptions) error { return y[0] }() - circleConfigSetup, err := ioutil.ReadFile(path.Join(orbPath, ".circleci", "config.yml")) + circleConfigSetup, err := os.ReadFile(path.Join(orbPath, ".circleci", "config.yml")) if err != nil { return err } configSetupString := string(circleConfigSetup) - err = ioutil.WriteFile(path.Join(orbPath, ".circleci", "config.yml"), []byte(orbTemplate(configSetupString, projectName, ownerName, orbName, namespace)), 0644) + err = os.WriteFile(path.Join(orbPath, ".circleci", "config.yml"), []byte(orbTemplate(configSetupString, projectName, ownerName, orbName, namespace)), 0644) if err != nil { return err } - circleConfigDeploy, err := ioutil.ReadFile(path.Join(orbPath, ".circleci", "test-deploy.yml")) + circleConfigDeploy, err := os.ReadFile(path.Join(orbPath, ".circleci", "test-deploy.yml")) if err != nil { return err } configDeployString := string(circleConfigDeploy) - err = ioutil.WriteFile(path.Join(orbPath, ".circleci", "test-deploy.yml"), []byte(orbTemplate(configDeployString, projectName, ownerName, orbName, namespace)), 0644) + err = os.WriteFile(path.Join(orbPath, ".circleci", "test-deploy.yml"), []byte(orbTemplate(configDeployString, projectName, ownerName, orbName, namespace)), 0644) if err != nil { return err } - readme, err := ioutil.ReadFile(path.Join(orbPath, "README.md")) + readme, err := os.ReadFile(path.Join(orbPath, "README.md")) if err != nil { return err } readmeString := string(readme) - err = ioutil.WriteFile(path.Join(orbPath, "README.md"), []byte(orbTemplate(readmeString, projectName, ownerName, orbName, namespace)), 0644) + err = os.WriteFile(path.Join(orbPath, "README.md"), []byte(orbTemplate(readmeString, projectName, ownerName, orbName, namespace)), 0644) if err != nil { return err } - orbRoot, err := ioutil.ReadFile(path.Join(orbPath, "src", "@orb.yml")) + orbRoot, err := os.ReadFile(path.Join(orbPath, "src", "@orb.yml")) if err != nil { return err } orbRootString := string(orbRoot) - err = ioutil.WriteFile(path.Join(orbPath, "src", "@orb.yml"), []byte(orbTemplate(orbRootString, projectName, ownerName, orbName, namespace)), 0644) + err = os.WriteFile(path.Join(orbPath, "src", "@orb.yml"), []byte(orbTemplate(orbRootString, projectName, ownerName, orbName, namespace)), 0644) if err != nil { return err } @@ -1504,7 +1503,7 @@ func initOrb(opts orbOptions) error { } tempOrbFile := filepath.Join(tempOrbDir, "orb.yml") - err = ioutil.WriteFile(tempOrbFile, []byte(packedOrb), 0644) + err = os.WriteFile(tempOrbFile, []byte(packedOrb), 0644) if err != nil { return errors.Wrap(err, "Unable to write packed orb") } diff --git a/cmd/orb_import_test.go b/cmd/orb_import_test.go index d5c66d20d..70412be03 100644 --- a/cmd/orb_import_test.go +++ b/cmd/orb_import_test.go @@ -3,7 +3,7 @@ package cmd import ( "bytes" "fmt" - "io/ioutil" + "io" "net/http" "github.com/CircleCI-Public/circleci-cli/api" @@ -678,7 +678,7 @@ The following orb versions already exist: ('namespace1/orb@0.0.3') ` - actual, err := ioutil.ReadAll(&b) + actual, err := io.ReadAll(&b) Expect(err).ShouldNot(HaveOccurred()) Expect(string(actual)).To(Equal(expOutput)) }) diff --git a/cmd/query.go b/cmd/query.go index 14a833642..e871426c6 100644 --- a/cmd/query.go +++ b/cmd/query.go @@ -3,7 +3,7 @@ package cmd import ( "encoding/json" "fmt" - "io/ioutil" + "io" "os" "github.com/CircleCI-Public/circleci-cli/api/graphql" @@ -51,9 +51,9 @@ func query(opts queryOptions) error { var resp map[string]interface{} if opts.args[0] == "-" { - q, err = ioutil.ReadAll(os.Stdin) + q, err = io.ReadAll(os.Stdin) } else { - q, err = ioutil.ReadFile(opts.args[0]) + q, err = os.ReadFile(opts.args[0]) } if err != nil { diff --git a/cmd/setup_test.go b/cmd/setup_test.go index 8a1c025d9..591a6197d 100644 --- a/cmd/setup_test.go +++ b/cmd/setup_test.go @@ -2,7 +2,7 @@ package cmd_test import ( "fmt" - "io/ioutil" + "io" "os" "os/exec" "regexp" @@ -248,7 +248,7 @@ Your configuration has been saved to %s. file, err := os.Open(tempSettings.Config.Path) Expect(err).ShouldNot(HaveOccurred()) - reread, err := ioutil.ReadAll(file) + reread, err := io.ReadAll(file) Expect(err).ShouldNot(HaveOccurred()) Expect(string(reread)).To(Equal(`host: https://zomg.com endpoint: graphql-unstable diff --git a/config/config.go b/config/config.go index 268e23467..0abbaa84d 100644 --- a/config/config.go +++ b/config/config.go @@ -2,7 +2,7 @@ package config import ( "fmt" - "io/ioutil" + "io" "net/url" "os" @@ -38,9 +38,9 @@ func loadYaml(path string) (string, error) { var err error var config []byte if path == "-" { - config, err = ioutil.ReadAll(os.Stdin) + config, err = io.ReadAll(os.Stdin) } else { - config, err = ioutil.ReadFile(path) + config, err = os.ReadFile(path) } if err != nil { diff --git a/filetree/filetree.go b/filetree/filetree.go index f8c64e110..9de10d4d5 100644 --- a/filetree/filetree.go +++ b/filetree/filetree.go @@ -2,7 +2,6 @@ package filetree import ( "fmt" - "io/ioutil" "os" "path/filepath" "regexp" @@ -117,7 +116,7 @@ func (n Node) marshalLeaf() (interface{}, error) { return content, nil } - buf, err := ioutil.ReadFile(n.FullPath) + buf, err := os.ReadFile(n.FullPath) if err != nil { return content, err diff --git a/filetree/filetree_test.go b/filetree/filetree_test.go index 3bf258415..56847f6ff 100644 --- a/filetree/filetree_test.go +++ b/filetree/filetree_test.go @@ -1,7 +1,6 @@ package filetree_test import ( - "io/ioutil" "os" "path/filepath" "sort" @@ -22,7 +21,7 @@ var _ = Describe("filetree", func() { BeforeEach(func() { var err error - tempRoot, err = ioutil.TempDir("", "circleci-cli-test-") + tempRoot, err = os.MkdirTemp("", "circleci-cli-test-") Expect(err).ToNot(HaveOccurred()) }) @@ -39,7 +38,7 @@ var _ = Describe("filetree", func() { emptyDir = filepath.Join(tempRoot, "empty_dir") Expect(os.Mkdir(subDir, 0700)).To(Succeed()) - Expect(ioutil.WriteFile(subDirFile, []byte("foo:\n bar:\n baz"), 0600)).To(Succeed()) + Expect(os.WriteFile(subDirFile, []byte("foo:\n bar:\n baz"), 0600)).To(Succeed()) Expect(os.Mkdir(emptyDir, 0700)).To(Succeed()) }) @@ -48,7 +47,7 @@ var _ = Describe("filetree", func() { anotherDir := filepath.Join(tempRoot, "another_dir") anotherDirFile := filepath.Join(tempRoot, "another_dir", "another_dir_file.yml") Expect(os.Mkdir(anotherDir, 0700)).To(Succeed()) - Expect(ioutil.WriteFile(anotherDirFile, []byte("1some: in: valid: yaml"), 0600)).To(Succeed()) + Expect(os.WriteFile(anotherDirFile, []byte("1some: in: valid: yaml"), 0600)).To(Succeed()) tree, err := filetree.NewTree(tempRoot) Expect(err).ToNot(HaveOccurred()) @@ -118,7 +117,7 @@ sub_dir: emptyDir = filepath.Join(tempRoot, "empty_dir") Expect(os.Mkdir(subDir, 0700)).To(Succeed()) - Expect(ioutil.WriteFile(subDirFile, []byte("foo:\n bar:\n baz"), 0600)).To(Succeed()) + Expect(os.WriteFile(subDirFile, []byte("foo:\n bar:\n baz"), 0600)).To(Succeed()) Expect(os.Mkdir(emptyDir, 0700)).To(Succeed()) }) diff --git a/local/local.go b/local/local.go index 845f5b33b..066eafee7 100644 --- a/local/local.go +++ b/local/local.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "os" "os/exec" "path" @@ -262,7 +261,7 @@ func writeStringToTempFile(data string) (string, error) { // > The path /var/folders/q0/2g2lcf6j79df6vxqm0cg_0zm0000gn/T/287575618-config.yml // > is not shared from OS X and is not known to Docker. // Docker has `/tmp` shared by default. - f, err := ioutil.TempFile("/tmp", "*_circleci_config.yml") + f, err := os.CreateTemp("/tmp", "*_circleci_config.yml") if err != nil { return "", errors.Wrap(err, "Error creating temporary config file") diff --git a/local/local_test.go b/local/local_test.go index c316f045b..598806ca9 100644 --- a/local/local_test.go +++ b/local/local_test.go @@ -1,7 +1,7 @@ package local import ( - "io/ioutil" + "io" "os" . "github.com/onsi/ginkgo" @@ -39,7 +39,7 @@ var _ = Describe("build", func() { path, err := writeStringToTempFile("cynosure") Expect(err).NotTo(HaveOccurred()) defer os.Remove(path) - Expect(ioutil.ReadFile(path)).To(BeEquivalentTo("cynosure")) + Expect(os.ReadFile(path)).To(BeEquivalentTo("cynosure")) }) }) @@ -51,7 +51,7 @@ var _ = Describe("build", func() { // add a 'debug' flag - the build command will inherit this from the // root command when not testing in isolation. flags.Bool("debug", false, "Enable debug logging.") - flags.SetOutput(ioutil.Discard) + flags.SetOutput(io.Discard) err := flags.Parse(args) return flags, err } diff --git a/mock/http.go b/mock/http.go index 8f93a6a81..f905ca0a3 100644 --- a/mock/http.go +++ b/mock/http.go @@ -1,7 +1,7 @@ package mock import ( - "io/ioutil" + "io" "net/http" "strings" ) @@ -23,6 +23,6 @@ func NewHTTPClient(f func(*http.Request) (*http.Response, error)) *http.Client { func NewHTTPResponse(code int, body string) *http.Response { return &http.Response{ StatusCode: code, - Body: ioutil.NopCloser(strings.NewReader(body)), + Body: io.NopCloser(strings.NewReader(body)), } } diff --git a/process/process.go b/process/process.go index cb7745ae1..216f9ad69 100644 --- a/process/process.go +++ b/process/process.go @@ -2,7 +2,7 @@ package process import ( "fmt" - "io/ioutil" + "os" "path/filepath" "regexp" "strings" @@ -31,7 +31,7 @@ func MaybeIncludeFile(s string, orbDirectory string) (string, error) { } filepath := filepath.Join(orbDirectory, subMatch) - file, err := ioutil.ReadFile(filepath) + file, err := os.ReadFile(filepath) if err != nil { return "", fmt.Errorf("could not open %s for inclusion", filepath) } diff --git a/settings/settings.go b/settings/settings.go index ce542af26..a612cd26d 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -5,7 +5,6 @@ import ( "crypto/x509" "errors" "fmt" - "io/ioutil" "net/http" "net/url" "os" @@ -65,7 +64,7 @@ func (upd *UpdateCheck) Load() error { upd.FileUsed = path - content, err := ioutil.ReadFile(path) // #nosec + content, err := os.ReadFile(path) // #nosec if err != nil { return err } @@ -81,7 +80,7 @@ func (upd *UpdateCheck) WriteToDisk() error { return err } - err = ioutil.WriteFile(upd.FileUsed, enc, 0600) + err = os.WriteFile(upd.FileUsed, enc, 0600) return err } @@ -106,7 +105,7 @@ func (cfg *Config) LoadFromDisk() error { cfg.FileUsed = path - content, err := ioutil.ReadFile(path) // #nosec + content, err := os.ReadFile(path) // #nosec if err != nil { return err } @@ -126,7 +125,7 @@ func (cfg *Config) WriteToDisk() error { return err } - err = ioutil.WriteFile(cfg.FileUsed, enc, 0600) + err = os.WriteFile(cfg.FileUsed, enc, 0600) return err } @@ -220,7 +219,7 @@ func (cfg *Config) WithHTTPClient() error { return fmt.Errorf("invalid tls cert provided: %s", err.Error()) } - pemData, err := ioutil.ReadFile(cfg.TLSCert) + pemData, err := os.ReadFile(cfg.TLSCert) if err != nil { return fmt.Errorf("unable to read tls cert: %s", err.Error()) } From 62deb45d8743b9b54b63181ff5874ef3665d5a7b Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Fri, 24 Mar 2023 11:39:34 +0000 Subject: [PATCH 64/84] Merging in from develop --- README.md | 5 + api/rest/client.go | 12 +- cmd/config.go | 247 ++++-------------- cmd/config_test.go | 18 -- cmd/root.go | 7 - config/collaborators.go | 39 +++ config/collaborators_test.go | 43 +++ config/commands.go | 160 ++++++++++++ config/commands_test.go | 163 ++++++++++++ config/config.go | 106 +++++--- config/config_test.go | 139 ++++++++++ .../{deprecated-images.go => deprecated.go} | 3 +- config/deprecated_test.go | 35 +++ {pipeline => config}/pipeline.go | 27 +- config/testdata/config-no-orb.yml | 13 + config/testdata/config.yml | 16 ++ config/testdata/test.yml | 1 + .../features/circleci_config.feature | 2 +- local/local.go | 10 +- settings/settings.go | 8 - 20 files changed, 740 insertions(+), 314 deletions(-) create mode 100644 config/collaborators.go create mode 100644 config/collaborators_test.go create mode 100644 config/commands.go create mode 100644 config/commands_test.go create mode 100644 config/config_test.go rename config/{deprecated-images.go => deprecated.go} (96%) create mode 100644 config/deprecated_test.go rename {pipeline => config}/pipeline.go (64%) create mode 100644 config/testdata/config-no-orb.yml create mode 100644 config/testdata/config.yml create mode 100644 config/testdata/test.yml diff --git a/README.md b/README.md index 3f9fdb9e4..fc5c695b5 100644 --- a/README.md +++ b/README.md @@ -176,3 +176,8 @@ Development instructions for the CircleCI CLI can be found in [HACKING.md](HACKI ## More Please see the [documentation](https://circleci-public.github.io/circleci-cli) or `circleci help` for more. + + +## Version Compatibility + +As of homebrew version `0.1.24705` - we no longer support Server 3.x instances. In order to upgrade the CLI to the latest version, you'll need to update your instance of server to 4.x. \ No newline at end of file diff --git a/api/rest/client.go b/api/rest/client.go index 57c7bfcc5..f056be12a 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -17,14 +17,14 @@ import ( ) type Client struct { - baseURL *url.URL + BaseURL *url.URL circleToken string client *http.Client } -func New(baseURL *url.URL, token string, httpClient *http.Client) *Client { +func New(BaseURL *url.URL, token string, httpClient *http.Client) *Client { return &Client{ - baseURL: baseURL, + BaseURL: BaseURL, circleToken: token, client: httpClient, } @@ -37,13 +37,13 @@ func NewFromConfig(host string, config *settings.Config) *Client { endpoint += "/" } - baseURL, _ := url.Parse(host) + BaseURL, _ := url.Parse(host) client := config.HTTPClient client.Timeout = 10 * time.Second return New( - baseURL.ResolveReference(&url.URL{Path: endpoint}), + BaseURL.ResolveReference(&url.URL{Path: endpoint}), config.Token, client, ) @@ -60,7 +60,7 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req } } - req, err = http.NewRequest(method, c.baseURL.ResolveReference(u).String(), r) + req, err = http.NewRequest(method, c.BaseURL.ResolveReference(u).String(), r) if err != nil { return nil, err } diff --git a/cmd/config.go b/cmd/config.go index 9c46e444b..85387c89c 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -2,33 +2,16 @@ package cmd import ( "fmt" - "net/url" - "os" - "strings" - "github.com/CircleCI-Public/circleci-cli/api/rest" "github.com/CircleCI-Public/circleci-cli/config" "github.com/CircleCI-Public/circleci-cli/filetree" - "github.com/CircleCI-Public/circleci-cli/local" - "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/proxy" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/pflag" "gopkg.in/yaml.v3" ) -var ( - CollaborationsPath = "me/collaborations" -) - -type configOptions struct { - cfg *settings.Config - rest *rest.Client - args []string -} - // Path to the config.yml file to operate on. // Used to for compatibility with `circleci config validate --path` var configPath string @@ -39,19 +22,7 @@ var configAnnotations = map[string]string{ "": "The path to your config (use \"-\" for STDIN)", } -func GetConfigAPIHost(cfg *settings.Config) string { - if cfg.Host != defaultHost { - return cfg.Host - } else { - return cfg.ConfigAPIHost - } -} - -func newConfigCommand(config *settings.Config) *cobra.Command { - opts := configOptions{ - cfg: config, - } - +func newConfigCommand(globalConfig *settings.Config) *cobra.Command { configCmd := &cobra.Command{ Use: "config", Short: "Operate on build config files", @@ -60,11 +31,8 @@ func newConfigCommand(config *settings.Config) *cobra.Command { packCommand := &cobra.Command{ Use: "pack ", Short: "Pack up your CircleCI configuration into a single file.", - PreRun: func(cmd *cobra.Command, args []string) { - opts.args = args - }, - RunE: func(_ *cobra.Command, _ []string) error { - return packConfig(opts) + RunE: func(_ *cobra.Command, args []string) error { + return packConfig(args) }, Args: cobra.ExactArgs(1), Annotations: make(map[string]string), @@ -75,12 +43,24 @@ func newConfigCommand(config *settings.Config) *cobra.Command { Use: "validate ", Aliases: []string{"check"}, Short: "Check that the config file is well formed.", - PreRun: func(cmd *cobra.Command, args []string) { - opts.args = args - opts.rest = rest.NewFromConfig(GetConfigAPIHost(opts.cfg), config) - }, - RunE: func(cmd *cobra.Command, _ []string) error { - return validateConfig(opts, cmd.Flags()) + RunE: func(cmd *cobra.Command, args []string) error { + compiler := config.New(globalConfig) + orgID, _ := cmd.Flags().GetString("org-id") + orgSlug, _ := cmd.Flags().GetString("org-slug") + path := config.DefaultConfigPath + if configPath != "" { + path = configPath + } + if len(args) == 1 { + path = args[0] + } + return compiler.ValidateConfig(config.ValidateConfigOpts{ + ConfigPath: path, + OrgID: orgID, + OrgSlug: orgSlug, + IgnoreDeprecatedImages: ignoreDeprecatedImages, + VerboseOutput: verboseOutput, + }) }, Args: cobra.MaximumNArgs(1), Annotations: make(map[string]string), @@ -89,6 +69,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { validateCommand.PersistentFlags().StringVarP(&configPath, "config", "c", ".circleci/config.yml", "path to config file") validateCommand.PersistentFlags().BoolVarP(&verboseOutput, "verbose", "v", false, "Enable verbose output") validateCommand.PersistentFlags().BoolVar(&ignoreDeprecatedImages, "ignore-deprecated-images", false, "ignores the deprecated images error") + if err := validateCommand.PersistentFlags().MarkHidden("config"); err != nil { panic(err) } @@ -98,12 +79,22 @@ func newConfigCommand(config *settings.Config) *cobra.Command { processCommand := &cobra.Command{ Use: "process ", Short: "Validate config and display expanded configuration.", - PreRun: func(cmd *cobra.Command, args []string) { - opts.args = args - opts.rest = rest.NewFromConfig(GetConfigAPIHost(opts.cfg), config) - }, - RunE: func(cmd *cobra.Command, _ []string) error { - return processConfig(opts, cmd.Flags()) + RunE: func(cmd *cobra.Command, args []string) error { + compiler := config.New(globalConfig) + pipelineParamsFilePath, _ := cmd.Flags().GetString("pipeline-parameters") + orgID, _ := cmd.Flags().GetString("org-id") + orgSlug, _ := cmd.Flags().GetString("org-slug") + configPath := config.DefaultConfigPath + if len(args) >= 1 { + configPath = args[0] + } + return compiler.ProcessConfig(config.ProcessConfigOpts{ + ConfigPath: configPath, + OrgID: orgID, + OrgSlug: orgSlug, + PipelineParamsFilePath: pipelineParamsFilePath, + VerboseOutput: verboseOutput, + }) }, Args: cobra.ExactArgs(1), Annotations: make(map[string]string), @@ -112,16 +103,13 @@ func newConfigCommand(config *settings.Config) *cobra.Command { processCommand.Flags().StringP("org-slug", "o", "", "organization slug (for example: github/example-org), used when a config depends on private orbs belonging to that org") processCommand.Flags().String("org-id", "", "organization id used when a config depends on private orbs belonging to that org") processCommand.Flags().StringP("pipeline-parameters", "", "", "YAML/JSON map of pipeline parameters, accepts either YAML/JSON directly or file path (for example: my-params.yml)") - processCommand.Flags().StringP("circleci-api-host", "", "", "the api-host you want to use for config processing and validation") + processCommand.PersistentFlags().BoolVar(&verboseOutput, "verbose", false, "adds verbose output to the command") migrateCommand := &cobra.Command{ Use: "migrate", Short: "Migrate a pre-release 2.0 config to the official release version", - PreRun: func(cmd *cobra.Command, args []string) { - opts.args = args - }, - RunE: func(_ *cobra.Command, _ []string) error { - return migrateConfig(opts) + RunE: func(_ *cobra.Command, args []string) error { + return migrateConfig(args) }, Hidden: true, DisableFlagParsing: true, @@ -138,115 +126,8 @@ func newConfigCommand(config *settings.Config) *cobra.Command { return configCmd } -// The arg is actually optional, in order to support compatibility with the --path flag. -func validateConfig(opts configOptions, flags *pflag.FlagSet) error { - var err error - var response *config.ConfigResponse - path := local.DefaultConfigPath - // First, set the path to configPath set by --path flag for compatibility - if configPath != "" { - path = configPath - } - - // Then, if an arg is passed in, choose that instead - if len(opts.args) == 1 { - path = opts.args[0] - } - - //if no orgId provided use org slug - values := pipeline.LocalPipelineValues() - if verboseOutput { - printValues(values) - } - - var orgID string - orgID, _ = flags.GetString("org-id") - if strings.TrimSpace(orgID) != "" { - response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) - if err != nil { - return err - } - } else { - orgSlug, _ := flags.GetString("org-slug") - orgs, err := GetOrgCollaborations(opts.cfg) - if err != nil { - fmt.Println(err.Error()) - } - orgID = GetOrgIdFromSlug(orgSlug, orgs) - response, err = config.ConfigQuery(opts.rest, path, orgID, nil, pipeline.LocalPipelineValues()) - if err != nil { - return err - } - } - - // check if a deprecated Linux VM image is being used - // link here to blog post when available - // returns an error if a deprecated image is used - if !ignoreDeprecatedImages { - err := config.DeprecatedImageCheck(response) - if err != nil { - return err - } - } - - if path == "-" { - fmt.Printf("\nConfig input is valid.\n") - } else { - fmt.Printf("\nConfig file at %s is valid.\n", path) - } - - return nil -} - -func processConfig(opts configOptions, flags *pflag.FlagSet) error { - paramsYaml, _ := flags.GetString("pipeline-parameters") - var response *config.ConfigResponse - var params pipeline.Parameters - var err error - - if len(paramsYaml) > 0 { - // The 'src' value can be a filepath, or a yaml string. If the file cannot be read successfully, - // proceed with the assumption that the value is already valid yaml. - raw, err := os.ReadFile(paramsYaml) - if err != nil { - raw = []byte(paramsYaml) - } - - err = yaml.Unmarshal(raw, ¶ms) - if err != nil { - return fmt.Errorf("invalid 'pipeline-parameters' provided: %s", err.Error()) - } - } - - //if no orgId provided use org slug - values := pipeline.LocalPipelineValues() - printValues(values) - - orgID, _ := flags.GetString("org-id") - if strings.TrimSpace(orgID) != "" { - response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, values) - if err != nil { - return err - } - } else { - orgSlug, _ := flags.GetString("org-slug") - orgs, err := GetOrgCollaborations(opts.cfg) - if err != nil { - fmt.Println(err.Error()) - } - orgID = GetOrgIdFromSlug(orgSlug, orgs) - response, err = config.ConfigQuery(opts.rest, opts.args[0], orgID, params, values) - if err != nil { - return err - } - } - - fmt.Print(response.OutputYaml) - return nil -} - -func packConfig(opts configOptions) error { - tree, err := filetree.NewTree(opts.args[0]) +func packConfig(args []string) error { + tree, err := filetree.NewTree(args[0]) if err != nil { return errors.Wrap(err, "An error occurred trying to build the tree") } @@ -259,44 +140,6 @@ func packConfig(opts configOptions) error { return nil } -func migrateConfig(opts configOptions) error { - return proxy.Exec([]string{"config", "migrate"}, opts.args) -} - -func printValues(values pipeline.Values) { - fmt.Fprintln(os.Stderr, "Processing config with following values:") - for key, value := range values { - fmt.Fprintf(os.Stderr, "%-18s %s\n", key+":", value) - } -} - -type CollaborationResult struct { - VcsTye string `json:"vcs_type"` - OrgSlug string `json:"slug"` - OrgName string `json:"name"` - OrgId string `json:"id"` - AvatarUrl string `json:"avatar_url"` -} - -// GetOrgCollaborations - fetches all the collaborations for a given user. -func GetOrgCollaborations(cfg *settings.Config) ([]CollaborationResult, error) { - baseClient := rest.NewFromConfig(cfg.Host, cfg) - req, err := baseClient.NewRequest("GET", &url.URL{Path: CollaborationsPath}, nil) - if err != nil { - return nil, err - } - - var resp []CollaborationResult - _, err = baseClient.DoRequest(req, &resp) - return resp, err -} - -// GetOrgIdFromSlug - converts a slug into an orgID. -func GetOrgIdFromSlug(slug string, collaborations []CollaborationResult) string { - for _, v := range collaborations { - if v.OrgSlug == slug { - return v.OrgId - } - } - return "" +func migrateConfig(args []string) error { + return proxy.Exec([]string{"config", "migrate"}, args) } diff --git a/cmd/config_test.go b/cmd/config_test.go index 1d0967e79..d0ed9016d 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -4,15 +4,11 @@ import ( "fmt" "os/exec" "path/filepath" - "testing" "github.com/CircleCI-Public/circleci-cli/clitest" - "github.com/CircleCI-Public/circleci-cli/cmd" - "github.com/CircleCI-Public/circleci-cli/settings" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" - "github.com/stretchr/testify/assert" "gotest.tools/v3/golden" ) @@ -250,17 +246,3 @@ var _ = Describe("Config", func() { }) }) }) - -func TestGetConfigAPIHost(t *testing.T) { - t.Run("tests that we correctly get the config api host when the host is not the default one", func(t *testing.T) { - // if the host isn't equal to `https://circleci.com` then this is likely a server instance and - // wont have the api.X.com subdomain so we should instead just respect the host for config commands - host := cmd.GetConfigAPIHost(&settings.Config{Host: "test"}) - assert.Equal(t, host, "test") - - // If the host passed in is the same as the defaultHost 'https://circleci.com' - then we know this is cloud - // and as such should use the `api.circleci.com` subdomain - host = cmd.GetConfigAPIHost(&settings.Config{Host: "https://circleci.com", ConfigAPIHost: "https://api.circleci.com"}) - assert.Equal(t, host, "https://api.circleci.com") - }) -} diff --git a/cmd/root.go b/cmd/root.go index 4b20cce1a..6f3f33c7c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -22,7 +22,6 @@ import ( var defaultEndpoint = "graphql-unstable" var defaultHost = "https://circleci.com" -var defaultAPIHost = "https://api.circleci.com" var defaultRestEndpoint = "api/v2" var trueString = "true" @@ -105,11 +104,6 @@ func MakeCommands() *cobra.Command { RestEndpoint: defaultRestEndpoint, Endpoint: defaultEndpoint, GitHubAPI: "https://api.github.com/", - // The config api host differs for both cloud and server setups. - // For cloud, the base domain will be https://api.circleci.com - // for server, this should match the host as we don't have the same - // api subdomain setup - ConfigAPIHost: defaultAPIHost, } if err := rootOptions.Load(); err != nil { @@ -188,7 +182,6 @@ func MakeCommands() *cobra.Command { flags.StringVar(&rootOptions.Host, "host", rootOptions.Host, "URL to your CircleCI host, also CIRCLECI_CLI_HOST") flags.StringVar(&rootOptions.Endpoint, "endpoint", rootOptions.Endpoint, "URI to your CircleCI GraphQL API endpoint") flags.StringVar(&rootOptions.GitHubAPI, "github-api", "https://api.github.com/", "Change the default endpoint to GitHub API for retrieving updates") - flags.StringVar(&rootOptions.ConfigAPIHost, "config-api-host", "https://api.circleci.com", "Change the default endpoint for the config api host") flags.BoolVar(&rootOptions.SkipUpdateCheck, "skip-update-check", skipUpdateByDefault(), "Skip the check for updates check run before every command.") hidden := []string{"github-api", "debug", "endpoint"} diff --git a/config/collaborators.go b/config/collaborators.go new file mode 100644 index 000000000..30da51d17 --- /dev/null +++ b/config/collaborators.go @@ -0,0 +1,39 @@ +package config + +import ( + "net/url" +) + +var ( + CollaborationsPath = "me/collaborations" +) + +type CollaborationResult struct { + VcsTye string `json:"vcs_type"` + OrgSlug string `json:"slug"` + OrgName string `json:"name"` + OrgId string `json:"id"` + AvatarUrl string `json:"avatar_url"` +} + +// GetOrgCollaborations - fetches all the collaborations for a given user. +func (c *ConfigCompiler) GetOrgCollaborations() ([]CollaborationResult, error) { + req, err := c.collaboratorRestClient.NewRequest("GET", &url.URL{Path: CollaborationsPath}, nil) + if err != nil { + return nil, err + } + + var resp []CollaborationResult + _, err = c.collaboratorRestClient.DoRequest(req, &resp) + return resp, err +} + +// GetOrgIdFromSlug - converts a slug into an orgID. +func GetOrgIdFromSlug(slug string, collaborations []CollaborationResult) string { + for _, v := range collaborations { + if v.OrgSlug == slug { + return v.OrgId + } + } + return "" +} diff --git a/config/collaborators_test.go b/config/collaborators_test.go new file mode 100644 index 000000000..115c4f8c9 --- /dev/null +++ b/config/collaborators_test.go @@ -0,0 +1,43 @@ +package config + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/CircleCI-Public/circleci-cli/settings" + "github.com/stretchr/testify/assert" +) + +func TestGetOrgCollaborations(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `[{"vcs_type":"circleci","slug":"gh/test","id":"2345"}]`) + })) + defer svr.Close() + compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + t.Run("assert compiler has correct host", func(t *testing.T) { + assert.Equal(t, "http://"+compiler.collaboratorRestClient.BaseURL.Host, svr.URL) + }) + + t.Run("getOrgCollaborations can parse response correctly", func(t *testing.T) { + collabs, err := compiler.GetOrgCollaborations() + assert.NoError(t, err) + assert.Equal(t, 1, len(collabs)) + assert.Equal(t, "circleci", collabs[0].VcsTye) + }) + + t.Run("can fetch orgID from a slug", func(t *testing.T) { + expected := "1234" + actual := GetOrgIdFromSlug("gh/test", []CollaborationResult{{OrgSlug: "gh/test", OrgId: "1234"}}) + assert.Equal(t, expected, actual) + }) + + t.Run("returns empty if no slug match", func(t *testing.T) { + expected := "" + actual := GetOrgIdFromSlug("gh/doesntexist", []CollaborationResult{{OrgSlug: "gh/test", OrgId: "1234"}}) + assert.Equal(t, expected, actual) + }) +} diff --git a/config/commands.go b/config/commands.go new file mode 100644 index 000000000..6dff218dc --- /dev/null +++ b/config/commands.go @@ -0,0 +1,160 @@ +package config + +import ( + "fmt" + "os" + "strings" + + "github.com/pkg/errors" + "gopkg.in/yaml.v3" +) + +func printValues(values Values) { + for key, value := range values { + fmt.Printf("\t%s:\t%s", key, value) + } +} + +type ProcessConfigOpts struct { + ConfigPath string + OrgID string + OrgSlug string + PipelineParamsFilePath string + + VerboseOutput bool +} + +func (c *ConfigCompiler) getOrgID( + optsOrgID string, + optsOrgSlug string, +) (string, error) { + if optsOrgID == "" && optsOrgSlug == "" { + fmt.Println("No org id or slug has been provided") + return "", nil + } + + var orgID string + if strings.TrimSpace(optsOrgID) != "" { + orgID = optsOrgID + } else { + orgs, err := c.GetOrgCollaborations() + if err != nil { + return "", err + } + orgID = GetOrgIdFromSlug(optsOrgSlug, orgs) + if orgID == "" { + fmt.Println("Could not fetch a valid org-id from collaborators endpoint.") + fmt.Println("Check if you have access to this org by hitting https://circleci.com/api/v2/me/collaborations") + fmt.Println("Continuing on - private orb resolution will not work as intended") + } + } + + return orgID, nil +} + +func (c *ConfigCompiler) ProcessConfig(opts ProcessConfigOpts) error { + var response *ConfigResponse + var params Parameters + var err error + + if len(opts.PipelineParamsFilePath) > 0 { + // The 'src' value can be a filepath, or a yaml string. If the file cannot be read successfully, + // proceed with the assumption that the value is already valid yaml. + raw, err := os.ReadFile(opts.PipelineParamsFilePath) + if err != nil { + raw = []byte(opts.PipelineParamsFilePath) + } + + err = yaml.Unmarshal(raw, ¶ms) + if err != nil { + return fmt.Errorf("invalid 'pipeline-parameters' provided: %s", err.Error()) + } + } + + //if no orgId provided use org slug + values := LocalPipelineValues() + if opts.VerboseOutput { + fmt.Println("Processing config with following values") + printValues(values) + } + + orgID, err := c.getOrgID(opts.OrgID, opts.OrgSlug) + if err != nil { + return fmt.Errorf("failed to get the appropriate org-id: %s", err.Error()) + } + + response, err = c.ConfigQuery( + opts.ConfigPath, + orgID, + params, + values, + ) + if err != nil { + return err + } + + if !response.Valid { + fmt.Println(response.Errors) + return errors.New("config is invalid") + } + + fmt.Print(response.OutputYaml) + return nil +} + +type ValidateConfigOpts struct { + ConfigPath string + OrgID string + OrgSlug string + + IgnoreDeprecatedImages bool + VerboseOutput bool +} + +// func determineConfigPath(configPath string) + +// The arg is actually optional, in order to support compatibility with the --path flag. +func (c *ConfigCompiler) ValidateConfig(opts ValidateConfigOpts) error { + var err error + var response *ConfigResponse + + //if no orgId provided use org slug + values := LocalPipelineValues() + if opts.VerboseOutput { + fmt.Println("Validating config with following values") + printValues(values) + } + + orgID, err := c.getOrgID(opts.OrgID, opts.OrgSlug) + if err != nil { + return fmt.Errorf("failed to get the appropriate org-id: %s", err.Error()) + } + + response, err = c.ConfigQuery( + opts.ConfigPath, + orgID, + nil, + LocalPipelineValues(), + ) + if err != nil { + return err + } + + if !response.Valid { + fmt.Println(response.Errors) + return errors.New("config is invalid") + } + + // check if a deprecated Linux VM image is being used + // link here to blog post when available + // returns an error if a deprecated image is used + if !opts.IgnoreDeprecatedImages { + err := deprecatedImageCheck(response) + if err != nil { + return err + } + } + + fmt.Printf("\nConfig file at %s is valid.\n", opts.ConfigPath) + return nil +} diff --git a/config/commands_test.go b/config/commands_test.go new file mode 100644 index 000000000..0588f6d4b --- /dev/null +++ b/config/commands_test.go @@ -0,0 +1,163 @@ +package config + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/CircleCI-Public/circleci-cli/settings" + "github.com/stretchr/testify/assert" +) + +func TestGetOrgID(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `[{"vcs_type":"circleci","slug":"gh/test","id":"2345"}]`) + })) + defer svr.Close() + compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + t.Run("returns the original org-id passed if it is set", func(t *testing.T) { + expected := "1234" + actual, err := compiler.getOrgID("1234", "") + assert.NoError(t, err) + assert.Equal(t, expected, actual) + }) + + t.Run("returns the correct org id from org-slug", func(t *testing.T) { + expected := "2345" + actual, err := compiler.getOrgID("", "gh/test") + assert.NoError(t, err) + assert.Equal(t, expected, actual) + }) + + t.Run("returns the correct org id with org-id and org-slug both set", func(t *testing.T) { + expected := "1234" + actual, err := compiler.getOrgID("1234", "gh/test") + assert.NoError(t, err) + assert.Equal(t, expected, actual) + }) + + t.Run("does not return an error if org-id cannot be found", func(t *testing.T) { + expected := "" + actual, err := compiler.getOrgID("", "gh/doesntexist") + assert.NoError(t, err) + assert.Equal(t, expected, actual) + }) + +} + +var testYaml = `version: 2.1\n\norbs:\n node: circleci/node@5.0.3\n\njobs:\n datadog-hello-world:\n docker:\n - image: cimg/base:stable\n steps:\n - run: |\n echo \"doing something really cool\"\nworkflows:\n datadog-hello-world:\n jobs:\n - datadog-hello-world` + +func TestValidateConfig(t *testing.T) { + t.Run("validate config works as expected", func(t *testing.T) { + t.Run("validate config is able to send a request with no owner-id", func(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + reqBody, err := io.ReadAll(r.Body) + assert.NoError(t, err) + + var req CompileConfigRequest + err = json.Unmarshal(reqBody, &req) + assert.NoError(t, err) + fmt.Fprintf(w, `{"valid":true,"source-yaml":"%s","output-yaml":"%s","errors":[]}`, testYaml, testYaml) + })) + defer svr.Close() + compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + err := compiler.ValidateConfig(ValidateConfigOpts{ + ConfigPath: "testdata/config.yml", + }) + assert.NoError(t, err) + }) + + t.Run("validate config is able to send a request with owner-id", func(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + reqBody, err := io.ReadAll(r.Body) + assert.NoError(t, err) + + var req CompileConfigRequest + err = json.Unmarshal(reqBody, &req) + assert.NoError(t, err) + assert.Equal(t, "1234", req.Options.OwnerID) + fmt.Fprintf(w, `{"valid":true,"source-yaml":"%s","output-yaml":"%s","errors":[]}`, testYaml, testYaml) + })) + defer svr.Close() + compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + err := compiler.ValidateConfig(ValidateConfigOpts{ + ConfigPath: "testdata/config.yml", + OrgID: "1234", + }) + assert.NoError(t, err) + }) + + t.Run("validate config is able to send a request with owner-id from slug", func(t *testing.T) { + mux := http.NewServeMux() + + mux.HandleFunc("/compile-config-with-defaults", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + reqBody, err := io.ReadAll(r.Body) + assert.NoError(t, err) + + var req CompileConfigRequest + err = json.Unmarshal(reqBody, &req) + assert.NoError(t, err) + assert.Equal(t, "2345", req.Options.OwnerID) + fmt.Fprintf(w, `{"valid":true,"source-yaml":"%s","output-yaml":"%s","errors":[]}`, testYaml, testYaml) + }) + + mux.HandleFunc("/me/collaborations", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `[{"vcs_type":"circleci","slug":"gh/test","id":"2345"}]`) + }) + + svr := httptest.NewServer(mux) + defer svr.Close() + + compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + err := compiler.ValidateConfig(ValidateConfigOpts{ + ConfigPath: "testdata/config.yml", + OrgSlug: "gh/test", + }) + assert.NoError(t, err) + }) + + t.Run("validate config is able to send a request with no owner-id after failed collaborations lookup", func(t *testing.T) { + mux := http.NewServeMux() + + mux.HandleFunc("/compile-config-with-defaults", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + reqBody, err := io.ReadAll(r.Body) + assert.NoError(t, err) + + var req CompileConfigRequest + err = json.Unmarshal(reqBody, &req) + assert.NoError(t, err) + assert.Equal(t, "", req.Options.OwnerID) + fmt.Fprintf(w, `{"valid":true,"source-yaml":"%s","output-yaml":"%s","errors":[]}`, testYaml, testYaml) + }) + + mux.HandleFunc("/me/collaborations", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `[{"vcs_type":"circleci","slug":"gh/test","id":"2345"}]`) + }) + + svr := httptest.NewServer(mux) + defer svr.Close() + + compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + err := compiler.ValidateConfig(ValidateConfigOpts{ + ConfigPath: "testdata/config.yml", + OrgSlug: "gh/nonexistent", + }) + assert.NoError(t, err) + }) + }) +} diff --git a/config/config.go b/config/config.go index 0abbaa84d..d65e58dc0 100644 --- a/config/config.go +++ b/config/config.go @@ -7,14 +7,47 @@ import ( "os" "github.com/CircleCI-Public/circleci-cli/api/rest" - "github.com/CircleCI-Public/circleci-cli/pipeline" + "github.com/CircleCI-Public/circleci-cli/settings" "github.com/pkg/errors" ) +var ( + defaultHost = "https://circleci.com" + defaultAPIHost = "https://api.circleci.com" + + // Making this the one true source for default config path + DefaultConfigPath = ".circleci/config.yml" +) + +type ConfigCompiler struct { + host string + compileRestClient *rest.Client + collaboratorRestClient *rest.Client +} + +func New(cfg *settings.Config) *ConfigCompiler { + hostValue := getCompileHost(cfg.Host) + configCompiler := &ConfigCompiler{ + host: hostValue, + compileRestClient: rest.NewFromConfig(hostValue, cfg), + collaboratorRestClient: rest.NewFromConfig(cfg.Host, cfg), + } + return configCompiler +} + +func getCompileHost(cfgHost string) string { + if cfgHost != defaultHost { + return cfgHost + } else { + return defaultAPIHost + } +} + type ConfigError struct { Message string `json:"message"` } +// ConfigResponse - the structure of what is returned from the downstream compilation endpoint type ConfigResponse struct { Valid bool `json:"valid"` SourceYaml string `json:"source-yaml"` @@ -22,6 +55,7 @@ type ConfigResponse struct { Errors []ConfigError `json:"errors"` } +// CompileConfigRequest - the structure of the data we send to the downstream compilation service. type CompileConfigRequest struct { ConfigYaml string `json:"config_yaml"` Options Options `json:"options"` @@ -33,55 +67,30 @@ type Options struct { PipelineValues map[string]string `json:"pipeline_values,omitempty"` } -// #nosec -func loadYaml(path string) (string, error) { - var err error - var config []byte - if path == "-" { - config, err = io.ReadAll(os.Stdin) - } else { - config, err = os.ReadFile(path) - } - - if err != nil { - return "", errors.Wrapf(err, "Could not load config file at %s", path) - } - - return string(config), nil -} - // ConfigQuery - attempts to compile or validate a given config file with the // passed in params/values. // If the orgID is passed in, the config-compilation with private orbs should work. -func ConfigQuery( - rest *rest.Client, +func (c *ConfigCompiler) ConfigQuery( configPath string, orgID string, - params pipeline.Parameters, - values pipeline.Values, + params Parameters, + values Values, ) (*ConfigResponse, error) { - configString, err := loadYaml(configPath) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to load yaml config from config path provider: %w", err) } compileRequest := CompileConfigRequest{ ConfigYaml: configString, Options: Options{ - PipelineValues: values, + OwnerID: orgID, + PipelineValues: values, + PipelineParameters: params, }, } - if orgID != "" { - compileRequest.Options.OwnerID = orgID - } - - if len(params) >= 1 { - compileRequest.Options.PipelineParameters = params - } - - req, err := rest.NewRequest( + req, err := c.compileRestClient.NewRequest( "POST", &url.URL{ Path: "compile-config-with-defaults", @@ -89,21 +98,42 @@ func ConfigQuery( compileRequest, ) if err != nil { - return nil, err + return nil, fmt.Errorf("an error occurred creating the request: %w", err) } configCompilationResp := &ConfigResponse{} - statusCode, err := rest.DoRequest(req, configCompilationResp) + statusCode, err := c.compileRestClient.DoRequest(req, configCompilationResp) if err != nil { - return nil, err + if statusCode == 404 { + return nil, errors.New("this version of the CLI does not support your instance of server, please refer to https://github.com/CircleCI-Public/circleci-cli for version compatibility") + } + return nil, fmt.Errorf("config compilation request returned an error: %w", err) } + if statusCode != 200 { return nil, errors.New("unable to validate or compile config") } if len(configCompilationResp.Errors) > 0 { - return nil, errors.New(fmt.Sprintf("config compilation contains errors: %s", configCompilationResp.Errors)) + return nil, fmt.Errorf("config compilation contains errors: %s", configCompilationResp.Errors) } return configCompilationResp, nil } + +// #nosec +func loadYaml(path string) (string, error) { + var err error + var config []byte + if path == "-" { + config, err = io.ReadAll(os.Stdin) + } else { + config, err = os.ReadFile(path) + } + + if err != nil { + return "", errors.Wrapf(err, "Could not load config file at %s", path) + } + + return string(config), nil +} diff --git a/config/config_test.go b/config/config_test.go new file mode 100644 index 000000000..d78cb8252 --- /dev/null +++ b/config/config_test.go @@ -0,0 +1,139 @@ +package config + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/CircleCI-Public/circleci-cli/settings" + "github.com/stretchr/testify/assert" +) + +func TestCompiler(t *testing.T) { + t.Run("test compiler setup", func(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `[{"vcs_type":"circleci","slug":"gh/test","id":"2345"}]`) + })) + defer svr.Close() + compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + t.Run("assert compiler has correct host", func(t *testing.T) { + assert.Equal(t, "http://"+compiler.compileRestClient.BaseURL.Host, svr.URL) + }) + + t.Run("assert compiler has default api host", func(t *testing.T) { + newCompiler := New(&settings.Config{Host: defaultHost, HTTPClient: http.DefaultClient}) + assert.Equal(t, "https://"+newCompiler.compileRestClient.BaseURL.Host, defaultAPIHost) + }) + + t.Run("tests that we correctly get the config api host when the host is not the default one", func(t *testing.T) { + // if the host isn't equal to `https://circleci.com` then this is likely a server instance and + // wont have the api.X.com subdomain so we should instead just respect the host for config commands + host := getCompileHost("test") + assert.Equal(t, host, "test") + + // If the host passed in is the same as the defaultHost 'https://circleci.com' - then we know this is cloud + // and as such should use the `api.circleci.com` subdomain + host = getCompileHost("https://circleci.com") + assert.Equal(t, host, "https://api.circleci.com") + }) + }) + + t.Run("test ConfigQuery", func(t *testing.T) { + t.Run("returns the correct configCompilation response", func(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `{"valid":true,"source-yaml":"source","output-yaml":"output","errors":[]}`) + })) + defer svr.Close() + compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + result, err := compiler.ConfigQuery("testdata/config.yml", "1234", Parameters{}, Values{}) + assert.NoError(t, err) + assert.Equal(t, true, result.Valid) + assert.Equal(t, "output", result.OutputYaml) + assert.Equal(t, "source", result.SourceYaml) + }) + + t.Run("returns error when config file could not be found", func(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `{"valid":true,"source-yaml":"source","output-yaml":"output","errors":[]}`) + })) + defer svr.Close() + compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + _, err := compiler.ConfigQuery("testdata/nonexistent.yml", "1234", Parameters{}, Values{}) + assert.Error(t, err) + assert.Contains(t, err.Error(), "Could not load config file at testdata/nonexistent.yml") + }) + + t.Run("handles 404 status correctly", func(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + defer svr.Close() + compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + _, err := compiler.ConfigQuery("testdata/config.yml", "1234", Parameters{}, Values{}) + assert.Error(t, err) + assert.Contains(t, err.Error(), "this version of the CLI does not support your instance of server") + }) + + t.Run("handles non-200 status correctly", func(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + defer svr.Close() + compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + _, err := compiler.ConfigQuery("testdata/config.yml", "1234", Parameters{}, Values{}) + assert.Error(t, err) + assert.Contains(t, err.Error(), "config compilation request returned an error") + }) + + t.Run("server gets correct information owner ID", func(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + reqBody, err := io.ReadAll(r.Body) + assert.NoError(t, err) + + var req CompileConfigRequest + err = json.Unmarshal(reqBody, &req) + assert.NoError(t, err) + assert.Equal(t, "1234", req.Options.OwnerID) + assert.Equal(t, "test: test", req.ConfigYaml) + fmt.Fprintf(w, `{"valid":true,"source-yaml":"source","output-yaml":"output","errors":[]}`) + })) + defer svr.Close() + compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + resp, err := compiler.ConfigQuery("testdata/test.yml", "1234", Parameters{}, Values{}) + assert.NoError(t, err) + assert.Equal(t, true, resp.Valid) + assert.Equal(t, "output", resp.OutputYaml) + assert.Equal(t, "source", resp.SourceYaml) + }) + + }) + +} + +func TestLoadYaml(t *testing.T) { + t.Run("tests load yaml", func(t *testing.T) { + expected := `test: test` + actual, err := loadYaml("testdata/test.yml") + assert.NoError(t, err) + assert.Equal(t, expected, actual) + }) + + t.Run("returns error for non-existent yml file", func(t *testing.T) { + actual, err := loadYaml("testdata/non-existent.yml") + assert.Error(t, err) + assert.Equal(t, "", actual) + }) +} diff --git a/config/deprecated-images.go b/config/deprecated.go similarity index 96% rename from config/deprecated-images.go rename to config/deprecated.go index fcc998fc9..fc8501ae0 100644 --- a/config/deprecated-images.go +++ b/config/deprecated.go @@ -38,8 +38,7 @@ type processedConfig struct { } // Processes the config down to v2.0, then checks image used against the block list -func DeprecatedImageCheck(response *ConfigResponse) error { - +func deprecatedImageCheck(response *ConfigResponse) error { aConfig := processedConfig{} err := yaml.Unmarshal([]byte(response.OutputYaml), &aConfig) if err != nil { diff --git a/config/deprecated_test.go b/config/deprecated_test.go new file mode 100644 index 000000000..8f7c53c00 --- /dev/null +++ b/config/deprecated_test.go @@ -0,0 +1,35 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var TestErrorCase = ` +jobs: + job: + machine: circleci/classic:201710-01 +` + +var TestHappyCase = ` +jobs: + job: + image: non/deprecated +` + +func TestDeprecatedImageCheck(t *testing.T) { + t.Run("happy path - tests deprecated image check works", func(t *testing.T) { + err := deprecatedImageCheck(&ConfigResponse{ + OutputYaml: TestErrorCase, + }) + assert.Error(t, err) + }) + + t.Run("happy path - no error if image used", func(t *testing.T) { + err := deprecatedImageCheck(&ConfigResponse{ + OutputYaml: TestHappyCase, + }) + assert.Nil(t, err) + }) +} diff --git a/pipeline/pipeline.go b/config/pipeline.go similarity index 64% rename from pipeline/pipeline.go rename to config/pipeline.go index 7daefa96b..09375d918 100644 --- a/pipeline/pipeline.go +++ b/config/pipeline.go @@ -1,8 +1,7 @@ -package pipeline +package config import ( "fmt" - "sort" "github.com/CircleCI-Public/circleci-cli/git" ) @@ -45,27 +44,3 @@ func LocalPipelineValues() Values { return vals } - -// TODO: type Parameters map[string]string - -// KeyVal is a data structure specifically for passing pipeline data to GraphQL which doesn't support free-form maps. -type KeyVal struct { - Key string `json:"key"` - Val string `json:"val"` -} - -// PrepareForGraphQL takes a golang homogenous map, and transforms it into a list of keyval pairs, since GraphQL does not support homogenous maps. -func PrepareForGraphQL(kvMap Values) []KeyVal { - // we need to create the slice of KeyVals in a deterministic order for testing purposes - keys := make([]string, 0, len(kvMap)) - for k := range kvMap { - keys = append(keys, k) - } - sort.Strings(keys) - - kvs := make([]KeyVal, 0, len(kvMap)) - for _, k := range keys { - kvs = append(kvs, KeyVal{Key: k, Val: kvMap[k]}) - } - return kvs -} diff --git a/config/testdata/config-no-orb.yml b/config/testdata/config-no-orb.yml new file mode 100644 index 000000000..0832df3e7 --- /dev/null +++ b/config/testdata/config-no-orb.yml @@ -0,0 +1,13 @@ +version: 2.1 + +jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" +workflows: + datadog-hello-world: + jobs: + - datadog-hello-world \ No newline at end of file diff --git a/config/testdata/config.yml b/config/testdata/config.yml new file mode 100644 index 000000000..84c85b42a --- /dev/null +++ b/config/testdata/config.yml @@ -0,0 +1,16 @@ +version: 2.1 + +orbs: + node: circleci/node@5.0.3 + +jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + steps: + - run: | + echo "doing something really cool" +workflows: + datadog-hello-world: + jobs: + - datadog-hello-world \ No newline at end of file diff --git a/config/testdata/test.yml b/config/testdata/test.yml new file mode 100644 index 000000000..b8d503804 --- /dev/null +++ b/config/testdata/test.yml @@ -0,0 +1 @@ +test: test \ No newline at end of file diff --git a/integration_tests/features/circleci_config.feature b/integration_tests/features/circleci_config.feature index 439103b78..3f645f936 100644 --- a/integration_tests/features/circleci_config.feature +++ b/integration_tests/features/circleci_config.feature @@ -56,7 +56,7 @@ Feature: Config checking jobs: - datadog-hello-world """ - When I run `circleci --config-api-host https://k9s.sphereci.com config validate --skip-update-check -c config.yml` + When I run `circleci --host https://k9s.sphereci.com config validate --skip-update-check -c config.yml` Then the exit status should be 0 And the output should contain "Config file at config.yml is valid" diff --git a/local/local.go b/local/local.go index 066eafee7..320789c66 100644 --- a/local/local.go +++ b/local/local.go @@ -11,9 +11,7 @@ import ( "strings" "syscall" - "github.com/CircleCI-Public/circleci-cli/api/rest" "github.com/CircleCI-Public/circleci-cli/config" - "github.com/CircleCI-Public/circleci-cli/pipeline" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/pkg/errors" "github.com/spf13/pflag" @@ -26,20 +24,20 @@ const DefaultConfigPath = ".circleci/config.yml" func Execute(flags *pflag.FlagSet, cfg *settings.Config, args []string) error { var err error var configResponse *config.ConfigResponse - restClient := rest.NewFromConfig(cfg.Host, cfg) - processedArgs, configPath := buildAgentArguments(flags) + compiler := config.New(cfg) + //if no orgId provided use org slug orgID, _ := flags.GetString("org-id") if strings.TrimSpace(orgID) != "" { - configResponse, err = config.ConfigQuery(restClient, configPath, orgID, nil, pipeline.LocalPipelineValues()) + configResponse, err = compiler.ConfigQuery(configPath, orgID, nil, config.LocalPipelineValues()) if err != nil { return err } } else { orgSlug, _ := flags.GetString("org-slug") - configResponse, err = config.ConfigQuery(restClient, configPath, orgSlug, nil, pipeline.LocalPipelineValues()) + configResponse, err = compiler.ConfigQuery(configPath, orgSlug, nil, config.LocalPipelineValues()) if err != nil { return err } diff --git a/settings/settings.go b/settings/settings.go index a612cd26d..36928fac5 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -36,10 +36,6 @@ type Config struct { GitHubAPI string `yaml:"-"` SkipUpdateCheck bool `yaml:"-"` OrbPublishing OrbPublishingInfo `yaml:"orb_publishing"` - // Represents the API host we want to use for config compilation and validation - // requests - this is typically on the api.circleci.com subdomain for cloud, or the - // same domain for server instances. - ConfigAPIHost string `yaml:"-"` } type OrbPublishingInfo struct { @@ -133,10 +129,6 @@ func (cfg *Config) WriteToDisk() error { func (cfg *Config) LoadFromEnv(prefix string) { if host := ReadFromEnv(prefix, "host"); host != "" { cfg.Host = host - // If the user is a server customer and overwrites the default - // https://circleci.com host - we then have to use this as the host for - // any config compilation or validation requests as opposed to https://api.circleci.com - cfg.ConfigAPIHost = host } if restEndpoint := ReadFromEnv(prefix, "rest_endpoint"); restEndpoint != "" { From 670cd2df25ae8b0991a0a5551191a211de0c11c9 Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Fri, 24 Mar 2023 11:54:12 +0000 Subject: [PATCH 65/84] Adds an editorconfig file to the repo to ensure that all files have newline characters --- .editorconfig | 9 +++++++++ api/rest/client.go | 8 ++++---- cmd/config.go | 11 +++++++---- config/commands.go | 2 -- config/commands_test.go | 2 +- config/config.go | 1 - config/config_test.go | 5 +++-- config/testdata/config-no-orb.yml | 2 +- config/testdata/config.yml | 2 +- config/testdata/test.yml | 2 +- 10 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..0385e3430 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true diff --git a/api/rest/client.go b/api/rest/client.go index f056be12a..219363301 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -22,9 +22,9 @@ type Client struct { client *http.Client } -func New(BaseURL *url.URL, token string, httpClient *http.Client) *Client { +func New(baseURL *url.URL, token string, httpClient *http.Client) *Client { return &Client{ - BaseURL: BaseURL, + BaseURL: baseURL, circleToken: token, client: httpClient, } @@ -37,13 +37,13 @@ func NewFromConfig(host string, config *settings.Config) *Client { endpoint += "/" } - BaseURL, _ := url.Parse(host) + baseURL, _ := url.Parse(host) client := config.HTTPClient client.Timeout = 10 * time.Second return New( - BaseURL.ResolveReference(&url.URL{Path: endpoint}), + baseURL.ResolveReference(&url.URL{Path: endpoint}), config.Token, client, ) diff --git a/cmd/config.go b/cmd/config.go index 85387c89c..d2afbef52 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -84,12 +84,15 @@ func newConfigCommand(globalConfig *settings.Config) *cobra.Command { pipelineParamsFilePath, _ := cmd.Flags().GetString("pipeline-parameters") orgID, _ := cmd.Flags().GetString("org-id") orgSlug, _ := cmd.Flags().GetString("org-slug") - configPath := config.DefaultConfigPath - if len(args) >= 1 { - configPath = args[0] + path := config.DefaultConfigPath + if configPath != "" { + path = configPath + } + if len(args) == 1 { + path = args[0] } return compiler.ProcessConfig(config.ProcessConfigOpts{ - ConfigPath: configPath, + ConfigPath: path, OrgID: orgID, OrgSlug: orgSlug, PipelineParamsFilePath: pipelineParamsFilePath, diff --git a/config/commands.go b/config/commands.go index 6dff218dc..2167e6859 100644 --- a/config/commands.go +++ b/config/commands.go @@ -111,8 +111,6 @@ type ValidateConfigOpts struct { VerboseOutput bool } -// func determineConfigPath(configPath string) - // The arg is actually optional, in order to support compatibility with the --path flag. func (c *ConfigCompiler) ValidateConfig(opts ValidateConfigOpts) error { var err error diff --git a/config/commands_test.go b/config/commands_test.go index 0588f6d4b..dc6e7a16a 100644 --- a/config/commands_test.go +++ b/config/commands_test.go @@ -50,7 +50,7 @@ func TestGetOrgID(t *testing.T) { } -var testYaml = `version: 2.1\n\norbs:\n node: circleci/node@5.0.3\n\njobs:\n datadog-hello-world:\n docker:\n - image: cimg/base:stable\n steps:\n - run: |\n echo \"doing something really cool\"\nworkflows:\n datadog-hello-world:\n jobs:\n - datadog-hello-world` +var testYaml = `version: 2.1\n\norbs:\n node: circleci/node@5.0.3\n\njobs:\n datadog-hello-world:\n docker:\n - image: cimg/base:stable\n steps:\n - run: |\n echo \"doing something really cool\"\nworkflows:\n datadog-hello-world:\n jobs:\n - datadog-hello-world\n` func TestValidateConfig(t *testing.T) { t.Run("validate config works as expected", func(t *testing.T) { diff --git a/config/config.go b/config/config.go index d65e58dc0..7d17b75e5 100644 --- a/config/config.go +++ b/config/config.go @@ -121,7 +121,6 @@ func (c *ConfigCompiler) ConfigQuery( return configCompilationResp, nil } -// #nosec func loadYaml(path string) (string, error) { var err error var config []byte diff --git a/config/config_test.go b/config/config_test.go index d78cb8252..deeba3c00 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -106,7 +106,7 @@ func TestCompiler(t *testing.T) { err = json.Unmarshal(reqBody, &req) assert.NoError(t, err) assert.Equal(t, "1234", req.Options.OwnerID) - assert.Equal(t, "test: test", req.ConfigYaml) + assert.Equal(t, "test: test\n", req.ConfigYaml) fmt.Fprintf(w, `{"valid":true,"source-yaml":"source","output-yaml":"output","errors":[]}`) })) defer svr.Close() @@ -125,7 +125,8 @@ func TestCompiler(t *testing.T) { func TestLoadYaml(t *testing.T) { t.Run("tests load yaml", func(t *testing.T) { - expected := `test: test` + expected := `test: test +` actual, err := loadYaml("testdata/test.yml") assert.NoError(t, err) assert.Equal(t, expected, actual) diff --git a/config/testdata/config-no-orb.yml b/config/testdata/config-no-orb.yml index 0832df3e7..35f2573f2 100644 --- a/config/testdata/config-no-orb.yml +++ b/config/testdata/config-no-orb.yml @@ -10,4 +10,4 @@ jobs: workflows: datadog-hello-world: jobs: - - datadog-hello-world \ No newline at end of file + - datadog-hello-world diff --git a/config/testdata/config.yml b/config/testdata/config.yml index 84c85b42a..d5f89b865 100644 --- a/config/testdata/config.yml +++ b/config/testdata/config.yml @@ -13,4 +13,4 @@ jobs: workflows: datadog-hello-world: jobs: - - datadog-hello-world \ No newline at end of file + - datadog-hello-world diff --git a/config/testdata/test.yml b/config/testdata/test.yml index b8d503804..e5239010e 100644 --- a/config/testdata/test.yml +++ b/config/testdata/test.yml @@ -1 +1 @@ -test: test \ No newline at end of file +test: test From dae03ee99e681c30b1cb7b781e03709bcdfee538 Mon Sep 17 00:00:00 2001 From: Sagar Gupta Date: Tue, 28 Mar 2023 11:40:17 -0400 Subject: [PATCH 66/84] [SNC-102] Add meta flag to config policies decisions (#891) --- cmd/policy/policy.go | 51 ++++++---- cmd/policy/policy_test.go | 92 ++++++++++++++++++- .../testdata/policy/decide-expected-usage.txt | 1 + .../testdata/policy/eval-expected-usage.txt | 1 + 4 files changed, 122 insertions(+), 23 deletions(-) diff --git a/cmd/policy/policy.go b/cmd/policy/policy.go index 3a47ea2f1..c2d989156 100644 --- a/cmd/policy/policy.go +++ b/cmd/policy/policy.go @@ -261,6 +261,7 @@ This group of commands allows the management of polices to be verified against b var ( inputPath string policyPath string + meta string metaFile string ownerID string context string @@ -284,15 +285,9 @@ This group of commands allows the management of polices to be verified against b return fmt.Errorf("failed to read input file: %w", err) } - var metadata map[string]interface{} - if metaFile != "" { - raw, err := os.ReadFile(metaFile) - if err != nil { - return fmt.Errorf("failed to read meta file: %w", err) - } - if err := yaml.Unmarshal(raw, &metadata); err != nil { - return fmt.Errorf("failed to decode meta content: %w", err) - } + metadata, err := readMetadata(meta, metaFile) + if err != nil { + return fmt.Errorf("failed to read metadata: %w", err) } decision, err := func() (*cpa.Decision, error) { @@ -324,6 +319,7 @@ This group of commands allows the management of polices to be verified against b cmd.Flags().StringVar(&ownerID, "owner-id", "", "the id of the policy's owner") cmd.Flags().StringVar(&context, "context", "config", "policy context for decision") cmd.Flags().StringVar(&inputPath, "input", "", "path to input file") + cmd.Flags().StringVar(&meta, "meta", "", "decision metadata (json string)") cmd.Flags().StringVar(&metaFile, "metafile", "", "decision metadata file") cmd.Flags().BoolVar(&strict, "strict", false, "return non-zero status code for decision resulting in HARD_FAIL") @@ -335,7 +331,7 @@ This group of commands allows the management of polices to be verified against b }() eval := func() *cobra.Command { - var inputPath, metaFile, query string + var inputPath, meta, metaFile, query string cmd := &cobra.Command{ Short: "perform raw opa evaluation locally", Use: "eval ", @@ -346,15 +342,9 @@ This group of commands allows the management of polices to be verified against b return fmt.Errorf("failed to read input file: %w", err) } - var metadata map[string]interface{} - if metaFile != "" { - raw, err := os.ReadFile(metaFile) - if err != nil { - return fmt.Errorf("failed to read meta file: %w", err) - } - if err := yaml.Unmarshal(raw, &metadata); err != nil { - return fmt.Errorf("failed to decode meta content: %w", err) - } + metadata, err := readMetadata(meta, metaFile) + if err != nil { + return fmt.Errorf("failed to read metadata: %w", err) } decision, err := getPolicyEvaluationLocally(policyPath, input, metadata, query) @@ -373,6 +363,7 @@ This group of commands allows the management of polices to be verified against b } cmd.Flags().StringVar(&inputPath, "input", "", "path to input file") + cmd.Flags().StringVar(&meta, "meta", "", "decision metadata (json string)") cmd.Flags().StringVar(&metaFile, "metafile", "", "decision metadata file") cmd.Flags().StringVar(&query, "query", "data", "policy decision query") @@ -510,6 +501,28 @@ This group of commands allows the management of polices to be verified against b return cmd } +func readMetadata(meta string, metaFile string) (map[string]interface{}, error) { + var metadata map[string]interface{} + if meta != "" && metaFile != "" { + return nil, fmt.Errorf("use either --meta or --metafile flag, but not both") + } + if meta != "" { + if err := json.Unmarshal([]byte(meta), &metadata); err != nil { + return nil, fmt.Errorf("failed to decode meta content: %w", err) + } + } + if metaFile != "" { + raw, err := os.ReadFile(metaFile) + if err != nil { + return nil, fmt.Errorf("failed to read meta file: %w", err) + } + if err := yaml.Unmarshal(raw, &metadata); err != nil { + return nil, fmt.Errorf("failed to decode metafile content: %w", err) + } + } + return metadata, nil +} + // prettyJSONEncoder takes a writer and returns a new json encoder with indent set to two space characters func prettyJSONEncoder(dst io.Writer) *json.Encoder { enc := json.NewEncoder(dst) diff --git a/cmd/policy/policy_test.go b/cmd/policy/policy_test.go index cf98e1a10..0c833c241 100644 --- a/cmd/policy/policy_test.go +++ b/cmd/policy/policy_test.go @@ -698,7 +698,29 @@ func TestMakeDecisionCommand(t *testing.T) { ExpectedOutput: "{\n \"status\": \"PASS\"\n}\n", }, { - Name: "sends expected request with metadata", + Name: "sends expected request with meta", + Args: []string{"decide", "--owner-id", "test-owner", "--input", "./testdata/test1/test.yml", "--context", "custom", "--meta", `{"project_id": "test-project-id","vcs": {"branch": "main"}}`}, + ServerHandler: func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Method, "POST") + assert.Equal(t, r.URL.Path, "/api/v1/owner/test-owner/context/custom/decision") + + var payload map[string]interface{} + assert.NilError(t, json.NewDecoder(r.Body).Decode(&payload)) + + assert.DeepEqual(t, payload, map[string]interface{}{ + "input": "test: config\n", + "metadata": map[string]interface{}{ + "project_id": "test-project-id", + "vcs": map[string]any{"branch": "main"}, + }, + }) + + _, _ = io.WriteString(w, `{"status":"PASS"}`) + }, + ExpectedOutput: "{\n \"status\": \"PASS\"\n}\n", + }, + { + Name: "sends expected request with metafile", Args: []string{"decide", "--owner-id", "test-owner", "--input", "./testdata/test1/test.yml", "--context", "custom", "--metafile", "./testdata/test1/meta.yml"}, ServerHandler: func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, r.Method, "POST") @@ -749,6 +771,11 @@ func TestMakeDecisionCommand(t *testing.T) { Args: []string{"decide", "./testdata/no_such_file.rego", "--input", "./testdata/test1/test.yml"}, ExpectedErr: "failed to make decision: failed to load policy files: failed to walk root: ", }, + { + Name: "fails if both meta and metafile are provided", + Args: []string{"decide", "./testdata/test0/policy.rego", "--input", "./testdata/test1/test.yml", "--meta", "{}", "--metafile", "somefile"}, + ExpectedErr: "failed to read metadata: use either --meta or --metafile flag, but not both", + }, { Name: "successfully performs decision for policy FILE provided locally", Args: []string{"decide", "./testdata/test0/policy.rego", "--input", "./testdata/test0/config.yml"}, @@ -797,7 +824,21 @@ func TestMakeDecisionCommand(t *testing.T) { ExpectedErr: "policy decision status: ERROR", }, { - Name: "successfully performs decision with metadata for policy FILE provided locally", + Name: "successfully performs decision with meta for policy FILE provided locally", + Args: []string{ + "decide", "./testdata/test0/subdir/meta-policy-subdir/meta-policy.rego", "--meta", + `{"project_id": "test-project-id","vcs": {"branch": "main"}}`, "--input", "./testdata/test0/config.yml", + }, + ExpectedOutput: `{ + "status": "PASS", + "enabled_rules": [ + "enabled" + ] +} +`, + }, + { + Name: "successfully performs decision with metafile for policy FILE provided locally", Args: []string{ "decide", "./testdata/test0/subdir/meta-policy-subdir/meta-policy.rego", "--metafile", "./testdata/test1/meta.yml", "--input", "./testdata/test0/config.yml", @@ -866,7 +907,37 @@ func TestRawOPAEvaluationCommand(t *testing.T) { ExpectedErr: "failed to make decision: failed to load policy files: failed to walk root: ", }, { - Name: "successfully performs raw opa evaluation for policy FILE provided locally, input and metadata", + Name: "fails if both meta and metafile are provided", + Args: []string{"eval", "./testdata/test0/policy.rego", "--input", "./testdata/test1/test.yml", "--meta", "{}", "--metafile", "somefile"}, + ExpectedErr: "failed to read metadata: use either --meta or --metafile flag, but not both", + }, + { + Name: "successfully performs raw opa evaluation for policy FILE provided locally, input and meta", + Args: []string{ + "eval", "./testdata/test0/subdir/meta-policy-subdir/meta-policy.rego", + "--meta", `{"project_id": "test-project-id","vcs": {"branch": "main"}}`, + "--input", "./testdata/test0/config.yml", + }, + ExpectedOutput: `{ + "meta": { + "vcs": { + "branch": "main" + }, + "project_id": "test-project-id" + }, + "org": { + "enable_rule": [ + "enabled" + ], + "policy_name": [ + "meta_policy_test" + ] + } +} +`, + }, + { + Name: "successfully performs raw opa evaluation for policy FILE provided locally, input and metafile", Args: []string{ "eval", "./testdata/test0/subdir/meta-policy-subdir/meta-policy.rego", "--metafile", "./testdata/test1/meta.yml", @@ -891,7 +962,20 @@ func TestRawOPAEvaluationCommand(t *testing.T) { `, }, { - Name: "successfully performs raw opa evaluation for policy FILE provided locally, input, metadata and query", + Name: "successfully performs raw opa evaluation for policy FILE provided locally, input, meta and query", + Args: []string{ + "eval", "./testdata/test0/subdir/meta-policy-subdir/meta-policy.rego", + "--meta", `{"project_id": "test-project-id","vcs": {"branch": "main"}}`, + "--input", "./testdata/test0/config.yml", + "--query", "data.org.enable_rule", + }, + ExpectedOutput: `[ + "enabled" +] +`, + }, + { + Name: "successfully performs raw opa evaluation for policy FILE provided locally, input, metafile and query", Args: []string{ "eval", "./testdata/test0/subdir/meta-policy-subdir/meta-policy.rego", "--metafile", "./testdata/test1/meta.yml", diff --git a/cmd/policy/testdata/policy/decide-expected-usage.txt b/cmd/policy/testdata/policy/decide-expected-usage.txt index cd6f3bf3e..20307a876 100644 --- a/cmd/policy/testdata/policy/decide-expected-usage.txt +++ b/cmd/policy/testdata/policy/decide-expected-usage.txt @@ -7,6 +7,7 @@ policy decide ./policies --input ./.circleci/config.yml Flags: --context string policy context for decision (default "config") --input string path to input file + --meta string decision metadata (json string) --metafile string decision metadata file --owner-id string the id of the policy's owner --strict return non-zero status code for decision resulting in HARD_FAIL diff --git a/cmd/policy/testdata/policy/eval-expected-usage.txt b/cmd/policy/testdata/policy/eval-expected-usage.txt index 68624f672..9f4e23364 100644 --- a/cmd/policy/testdata/policy/eval-expected-usage.txt +++ b/cmd/policy/testdata/policy/eval-expected-usage.txt @@ -6,6 +6,7 @@ policy eval ./policies --input ./.circleci/config.yml Flags: --input string path to input file + --meta string decision metadata (json string) --metafile string decision metadata file --query string policy decision query (default "data") From fa5ea62358dcc173a3c915d5410e65aca21de997 Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Wed, 29 Mar 2023 09:48:55 +0100 Subject: [PATCH 67/84] Update README.md Co-authored-by: Ryan William O'Hara --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc5c695b5..0b4767ea9 100644 --- a/README.md +++ b/README.md @@ -180,4 +180,4 @@ Please see the [documentation](https://circleci-public.github.io/circleci-cli) o ## Version Compatibility -As of homebrew version `0.1.24705` - we no longer support Server 3.x instances. In order to upgrade the CLI to the latest version, you'll need to update your instance of server to 4.x. \ No newline at end of file +As of version `0.1.24705` - we no longer support Server 3.x instances. In order to upgrade the CLI to the latest version, you'll need to update your instance of server to 4.x. \ No newline at end of file From 6ebbb56cb5c916fcd380f4b22138d80fedbae02d Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Wed, 29 Mar 2023 13:26:55 +0100 Subject: [PATCH 68/84] Updated readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b4767ea9..f694825a7 100644 --- a/README.md +++ b/README.md @@ -180,4 +180,6 @@ Please see the [documentation](https://circleci-public.github.io/circleci-cli) o ## Version Compatibility -As of version `0.1.24705` - we no longer support Server 3.x instances. In order to upgrade the CLI to the latest version, you'll need to update your instance of server to 4.x. \ No newline at end of file +As of version `0.1.24705` - we no longer support Server 3.x instances. In order to upgrade the CLI to the latest version, you'll need to update your instance of server to 4.x. + +`0.1.23845` is the last version to support Server 3.x and 2.x. From dcc6934a4648f395abc0ecd918f74dccf01bc039 Mon Sep 17 00:00:00 2001 From: "Kyle a.k.a. TechSquidTV" <33272306+KyleTryon@users.noreply.github.com> Date: Wed, 29 Mar 2023 09:41:08 -0400 Subject: [PATCH 69/84] chore: update CODEOWNERS with orb team --- .github/CODEOWNERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5d57cd581..ec9fce118 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,6 +1,5 @@ * @CircleCI-Public/developer-experience -*orb*.go @CircleCI-Public/CPEng @CircleCI-Public/developer-experience +*orb*.go @CircleCI-Public/orb-publishers @CircleCI-Public/developer-experience /api/runner @CircleCI-Public/runner /cmd/runner @CircleCI-Public/runner - From 8b2a4d0a5ffbfb298d785ca8d39d5807cddf37da Mon Sep 17 00:00:00 2001 From: David Desmarais-Michaud Date: Wed, 29 Mar 2023 10:46:43 -0400 Subject: [PATCH 70/84] udpated circle-policy-agent to v0.0.608 (#892) --- go.mod | 6 ++---- go.sum | 16 ++++------------ 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 4b3ee6084..c73f2c9a2 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/CircleCI-Public/circleci-cli require ( github.com/AlecAivazis/survey/v2 v2.1.1 - github.com/CircleCI-Public/circle-policy-agent v0.0.583 + github.com/CircleCI-Public/circle-policy-agent v0.0.608 github.com/Masterminds/semver v1.4.2 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/blang/semver v3.5.1+incompatible @@ -36,7 +36,7 @@ require ( github.com/erikgeiser/promptkit v0.7.0 github.com/hexops/gotextdiff v1.0.3 github.com/stretchr/testify v1.8.2 - golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 ) require ( @@ -64,7 +64,6 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect @@ -83,7 +82,6 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/yashtewari/glob-intersection v0.1.0 // indirect - github.com/yazgazan/jaydiff v0.3.1 // indirect golang.org/x/crypto v0.3.0 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/sys v0.6.0 // indirect diff --git a/go.sum b/go.sum index 284582ffd..8591b37e6 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eim github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CircleCI-Public/circle-policy-agent v0.0.583 h1:c4Q48txEoO182l77CjSWGNhRHUUPOwlbm/mr/LG+nBs= -github.com/CircleCI-Public/circle-policy-agent v0.0.583/go.mod h1:zD3HRb1jYVe8wnbQz5UPF/Ol1lEsDxC47v3NysIY9Fw= +github.com/CircleCI-Public/circle-policy-agent v0.0.608 h1:3Bhimsdrhwiz2J7ssx90vVH5o8dviZb5+pWUzuMg9E0= +github.com/CircleCI-Public/circle-policy-agent v0.0.608/go.mod h1:EJlgvMigkiPmhzRJrnqzucVtIYB0TlbNzaSAB2gEL9Q= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= @@ -227,12 +227,10 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 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/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= @@ -245,8 +243,6 @@ github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRC 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/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d h1:eAS2t2Vy+6psf9LZ4T5WXWsbkBt3Tu5PWekJy5AGyEU= -github.com/mb0/diff v0.0.0-20131118162322-d8d9a906c24d/go.mod h1:3YMHqrw2Qu3Liy82v4QdAG17e9k91HZ7w3hqlpWqhDo= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= @@ -340,8 +336,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg= github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= -github.com/yazgazan/jaydiff v0.3.1 h1:5vUlbCCRm8LNckEZhMkU3GjZk1RQZxA/+XuOYrqkhvI= -github.com/yazgazan/jaydiff v0.3.1/go.mod h1:9AvhZxcMJX51L4eQdw7Wi2mhC8i3HQo0HRvftL0VROw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -352,7 +346,6 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -373,8 +366,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= -golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -445,7 +438,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= From 4aa74cb97c645667256c32f7bf4b7e0ce3ab8d95 Mon Sep 17 00:00:00 2001 From: William Yardley Date: Thu, 30 Mar 2023 10:25:43 -0700 Subject: [PATCH 71/84] Update formatting for config validation Reintroduces the output style introduced in #861 to resolve #860, but reverted in #884 --- config/commands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/commands.go b/config/commands.go index 2167e6859..73c7b8091 100644 --- a/config/commands.go +++ b/config/commands.go @@ -11,7 +11,7 @@ import ( func printValues(values Values) { for key, value := range values { - fmt.Printf("\t%s:\t%s", key, value) + fmt.Fprintf(os.Stderr, "%-18s %s\n", key+":", value) } } From 2766f0e710c8727e346e247f6c779079a79b010c Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Fri, 31 Mar 2023 10:23:18 +0100 Subject: [PATCH 72/84] Adding failing test case --- .../features/circleci_config.feature | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/integration_tests/features/circleci_config.feature b/integration_tests/features/circleci_config.feature index 3f645f936..9628d3bf3 100644 --- a/integration_tests/features/circleci_config.feature +++ b/integration_tests/features/circleci_config.feature @@ -159,6 +159,36 @@ Feature: Config checking Then the output should contain "fighters" And the exit status should be 0 + Scenario: Testing new type casting works as expected + Given a file named "config.yml" with: + """ + version: 2.1 + + jobs: + datadog-hello-world: + docker: + - image: cimg/base:stable + parameters: + an-integer: + description: a test case to ensure parameters are passed correctly + type: integer + default: -1 + steps: + - unless: + condition: + equal: [<< parameters.an-integer >>, -1] + steps: + - run: echo "<< parameters.an-integer >> - test" + workflows: + main-workflow: + jobs: + - datadog-hello-world: + an-integer: << pipeline.number >> + """ + When I run `circleci config process config.yml` + Then the output should contain "1 - test" + And the exit status should be 0 + Scenario: Checking a valid config file with default pipeline params Given a file named "config.yml" with: """ From deec041056d3c715288125aa0fa647b1e9d123ff Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Fri, 31 Mar 2023 10:26:12 +0100 Subject: [PATCH 73/84] Converts the map from [string]string to [string]interface{} to allow for integer and bool values --- config/commands.go | 1 - config/config.go | 2 +- config/pipeline.go | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/config/commands.go b/config/commands.go index 2167e6859..a90f83466 100644 --- a/config/commands.go +++ b/config/commands.go @@ -29,7 +29,6 @@ func (c *ConfigCompiler) getOrgID( optsOrgSlug string, ) (string, error) { if optsOrgID == "" && optsOrgSlug == "" { - fmt.Println("No org id or slug has been provided") return "", nil } diff --git a/config/config.go b/config/config.go index 7d17b75e5..896d2a337 100644 --- a/config/config.go +++ b/config/config.go @@ -64,7 +64,7 @@ type CompileConfigRequest struct { type Options struct { OwnerID string `json:"owner_id,omitempty"` PipelineParameters map[string]interface{} `json:"pipeline_parameters,omitempty"` - PipelineValues map[string]string `json:"pipeline_values,omitempty"` + PipelineValues map[string]interface{} `json:"pipeline_values,omitempty"` } // ConfigQuery - attempts to compile or validate a given config file with the diff --git a/config/pipeline.go b/config/pipeline.go index 09375d918..923630c4e 100644 --- a/config/pipeline.go +++ b/config/pipeline.go @@ -7,7 +7,7 @@ import ( ) // CircleCI provides various `<< pipeline.x >>` values to be used in your config, but sometimes we need to fabricate those values when validating config. -type Values map[string]string +type Values map[string]interface{} // Static typing is bypassed using an empty interface here due to pipeline parameters supporting multiple types. type Parameters map[string]interface{} @@ -31,9 +31,9 @@ func LocalPipelineValues() Values { } } - vals := map[string]string{ + vals := map[string]interface{}{ "id": "00000000-0000-0000-0000-000000000001", - "number": "1", + "number": 1, "project.git_url": gitUrl, "project.type": projectType, "git.tag": git.Tag(), From cb2d3e635e5be8bbaf1c8e18eb0fad4cdadd3322 Mon Sep 17 00:00:00 2001 From: William Yardley Date: Tue, 4 Apr 2023 19:57:22 -0700 Subject: [PATCH 74/84] Additional formatting updates for config validation Resolve some issues from #861 and #896 - Suppress some whitespace when verbose output is not enabled - Write to stderr again - Use "%v" instead of "%s" so that "number" in pipeline values gets printed correctly - Sort values to provide stable output order for pipeline values --- config/commands.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/config/commands.go b/config/commands.go index 5b8fd3309..64504939f 100644 --- a/config/commands.go +++ b/config/commands.go @@ -3,6 +3,7 @@ package config import ( "fmt" "os" + "sort" "strings" "github.com/pkg/errors" @@ -10,9 +11,19 @@ import ( ) func printValues(values Values) { - for key, value := range values { - fmt.Fprintf(os.Stderr, "%-18s %s\n", key+":", value) + // Provide a stable sort order for printed values + keys := make([]string, 0, len(values)) + for k := range values { + keys = append(keys, k) } + sort.Strings(keys) + + for _, key := range keys { + fmt.Fprintf(os.Stderr, "%-18s %v\n", key+":", values[key]) + } + + // Add empty newline at end + fmt.Fprintf(os.Stderr, "\n") } type ProcessConfigOpts struct { @@ -73,7 +84,7 @@ func (c *ConfigCompiler) ProcessConfig(opts ProcessConfigOpts) error { //if no orgId provided use org slug values := LocalPipelineValues() if opts.VerboseOutput { - fmt.Println("Processing config with following values") + fmt.Fprintln(os.Stderr, "Processing config with following values:") printValues(values) } @@ -118,7 +129,7 @@ func (c *ConfigCompiler) ValidateConfig(opts ValidateConfigOpts) error { //if no orgId provided use org slug values := LocalPipelineValues() if opts.VerboseOutput { - fmt.Println("Validating config with following values") + fmt.Fprintln(os.Stderr, "Validating config with following values:") printValues(values) } @@ -152,6 +163,6 @@ func (c *ConfigCompiler) ValidateConfig(opts ValidateConfigOpts) error { } } - fmt.Printf("\nConfig file at %s is valid.\n", opts.ConfigPath) + fmt.Printf("Config file at %s is valid.\n", opts.ConfigPath) return nil } From bc3c0121224a26360e65d45d805a851c3e460dae Mon Sep 17 00:00:00 2001 From: rooneyshuman Date: Mon, 10 Apr 2023 15:28:12 -0700 Subject: [PATCH 75/84] Update docs for policy command --- cmd/policy/policy.go | 14 +++++++------- .../testdata/policy/create-expected-usage.txt | 13 ------------- .../testdata/policy/decide-expected-usage.txt | 2 +- .../testdata/policy/delete-expected-usage.txt | 11 ----------- cmd/policy/testdata/policy/diff-expected-usage.txt | 2 +- cmd/policy/testdata/policy/eval-expected-usage.txt | 2 +- .../testdata/policy/fetch-expected-usage.txt | 2 +- cmd/policy/testdata/policy/get-expected-usage.txt | 11 ----------- cmd/policy/testdata/policy/list-expected-usage.txt | 11 ----------- cmd/policy/testdata/policy/logs-expected-usage.txt | 2 +- cmd/policy/testdata/policy/push-expected-usage.txt | 2 +- .../testdata/policy/settings-expected-usage.txt | 2 +- .../testdata/policy/update-expected-usage.txt | 13 ------------- 13 files changed, 14 insertions(+), 73 deletions(-) delete mode 100644 cmd/policy/testdata/policy/create-expected-usage.txt delete mode 100644 cmd/policy/testdata/policy/delete-expected-usage.txt delete mode 100644 cmd/policy/testdata/policy/get-expected-usage.txt delete mode 100644 cmd/policy/testdata/policy/list-expected-usage.txt delete mode 100644 cmd/policy/testdata/policy/update-expected-usage.txt diff --git a/cmd/policy/policy.go b/cmd/policy/policy.go index d225347b4..f809a4571 100644 --- a/cmd/policy/policy.go +++ b/cmd/policy/policy.go @@ -92,7 +92,7 @@ This group of commands allows the management of polices to be verified against b return nil }, Args: cobra.ExactArgs(1), - Example: `policy push ./policies --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f`, + Example: `circleci policy push ./policies --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f`, } cmd.Flags().StringVar(&context, "context", "config", "policy context") @@ -127,7 +127,7 @@ This group of commands allows the management of polices to be verified against b return prettyJSONEncoder(cmd.OutOrStdout()).Encode(diff) }, Args: cobra.ExactArgs(1), - Example: `policy diff ./policies --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f`, + Example: `circleci policy diff ./policies --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f`, } cmd.Flags().StringVar(&context, "context", "config", "policy context") cmd.Flags().StringVar(&ownerID, "owner-id", "", "the id of the policy's owner") @@ -159,7 +159,7 @@ This group of commands allows the management of polices to be verified against b return nil }, Args: cobra.MaximumNArgs(1), - Example: `policy fetch --owner-id 516425b2-e369-421b-838d-920e1f51b0f5`, + Example: `circleci policy fetch --owner-id 516425b2-e369-421b-838d-920e1f51b0f5`, } cmd.Flags().StringVar(&context, "context", "config", "policy context") @@ -238,7 +238,7 @@ This group of commands allows the management of polices to be verified against b return nil }, Args: cobra.MaximumNArgs(1), - Example: `policy logs --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f --after 2022/03/14 --out output.json`, + Example: `circleci policy logs --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f --after 2022/03/14 --out output.json`, } cmd.Flags().StringVar(&request.Status, "status", "", "filter decision logs based on their status") @@ -313,7 +313,7 @@ This group of commands allows the management of polices to be verified against b return nil }, Args: cobra.MaximumNArgs(1), - Example: `policy decide ./policies --input ./.circleci/config.yml`, + Example: `circleci policy decide ./policies --input ./.circleci/config.yml`, } cmd.Flags().StringVar(&ownerID, "owner-id", "", "the id of the policy's owner") @@ -359,7 +359,7 @@ This group of commands allows the management of polices to be verified against b return nil }, Args: cobra.ExactArgs(1), - Example: `policy eval ./policies --input ./.circleci/config.yml`, + Example: `circleci policy eval ./policies --input ./.circleci/config.yml`, } cmd.Flags().StringVar(&inputPath, "input", "", "path to input file") @@ -406,7 +406,7 @@ This group of commands allows the management of polices to be verified against b return nil }, Args: cobra.ExactArgs(0), - Example: `policy settings --enabled=true`, + Example: `circleci policy settings --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f --enabled=true`, } cmd.Flags().StringVar(&ownerID, "owner-id", "", "the id of the policy's owner") diff --git a/cmd/policy/testdata/policy/create-expected-usage.txt b/cmd/policy/testdata/policy/create-expected-usage.txt deleted file mode 100644 index 120646a05..000000000 --- a/cmd/policy/testdata/policy/create-expected-usage.txt +++ /dev/null @@ -1,13 +0,0 @@ -Usage: - policy create [flags] - -Examples: -policy create --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f --name policy_name --policy ./policy.rego - -Flags: - --context string policy context (default "config") - --owner-id string the id of the policy's owner - --policy string path to rego policy file - -Global Flags: - --policy-base-url string base url for policy api (default "https://internal.circleci.com") diff --git a/cmd/policy/testdata/policy/decide-expected-usage.txt b/cmd/policy/testdata/policy/decide-expected-usage.txt index 20307a876..4836cad3b 100644 --- a/cmd/policy/testdata/policy/decide-expected-usage.txt +++ b/cmd/policy/testdata/policy/decide-expected-usage.txt @@ -2,7 +2,7 @@ Usage: policy decide [policy_file_or_dir_path] [flags] Examples: -policy decide ./policies --input ./.circleci/config.yml +circleci policy decide ./policies --input ./.circleci/config.yml Flags: --context string policy context for decision (default "config") diff --git a/cmd/policy/testdata/policy/delete-expected-usage.txt b/cmd/policy/testdata/policy/delete-expected-usage.txt deleted file mode 100644 index 63845009e..000000000 --- a/cmd/policy/testdata/policy/delete-expected-usage.txt +++ /dev/null @@ -1,11 +0,0 @@ -Usage: - policy delete [flags] - -Examples: -policy delete 60b7e1a5-c1d7-4422-b813-7a12d353d7c6 --owner-id 516425b2-e369-421b-838d-920e1f51b0f5 - -Flags: - --owner-id string the id of the policy's owner - -Global Flags: - --policy-base-url string base url for policy api (default "https://internal.circleci.com") diff --git a/cmd/policy/testdata/policy/diff-expected-usage.txt b/cmd/policy/testdata/policy/diff-expected-usage.txt index ae69485ed..6d81adbaa 100644 --- a/cmd/policy/testdata/policy/diff-expected-usage.txt +++ b/cmd/policy/testdata/policy/diff-expected-usage.txt @@ -2,7 +2,7 @@ Usage: policy diff [flags] Examples: -policy diff ./policies --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f +circleci policy diff ./policies --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f Flags: --context string policy context (default "config") diff --git a/cmd/policy/testdata/policy/eval-expected-usage.txt b/cmd/policy/testdata/policy/eval-expected-usage.txt index 9f4e23364..f35daf210 100644 --- a/cmd/policy/testdata/policy/eval-expected-usage.txt +++ b/cmd/policy/testdata/policy/eval-expected-usage.txt @@ -2,7 +2,7 @@ Usage: policy eval [flags] Examples: -policy eval ./policies --input ./.circleci/config.yml +circleci policy eval ./policies --input ./.circleci/config.yml Flags: --input string path to input file diff --git a/cmd/policy/testdata/policy/fetch-expected-usage.txt b/cmd/policy/testdata/policy/fetch-expected-usage.txt index 189051768..28e2bfbdc 100644 --- a/cmd/policy/testdata/policy/fetch-expected-usage.txt +++ b/cmd/policy/testdata/policy/fetch-expected-usage.txt @@ -2,7 +2,7 @@ Usage: policy fetch [policy_name] [flags] Examples: -policy fetch --owner-id 516425b2-e369-421b-838d-920e1f51b0f5 +circleci policy fetch --owner-id 516425b2-e369-421b-838d-920e1f51b0f5 Flags: --context string policy context (default "config") diff --git a/cmd/policy/testdata/policy/get-expected-usage.txt b/cmd/policy/testdata/policy/get-expected-usage.txt deleted file mode 100644 index e30225f48..000000000 --- a/cmd/policy/testdata/policy/get-expected-usage.txt +++ /dev/null @@ -1,11 +0,0 @@ -Usage: - policy get [flags] - -Examples: -policy get 60b7e1a5-c1d7-4422-b813-7a12d353d7c6 --owner-id 516425b2-e369-421b-838d-920e1f51b0f5 - -Flags: - --owner-id string the id of the policy's owner - -Global Flags: - --policy-base-url string base url for policy api (default "https://internal.circleci.com") diff --git a/cmd/policy/testdata/policy/list-expected-usage.txt b/cmd/policy/testdata/policy/list-expected-usage.txt deleted file mode 100644 index 803a3040d..000000000 --- a/cmd/policy/testdata/policy/list-expected-usage.txt +++ /dev/null @@ -1,11 +0,0 @@ -Usage: - policy list [flags] - -Examples: -policy list --owner-id 516425b2-e369-421b-838d-920e1f51b0f5 - -Flags: - --owner-id string the id of the policy's owner - -Global Flags: - --policy-base-url string base url for policy api (default "https://internal.circleci.com") diff --git a/cmd/policy/testdata/policy/logs-expected-usage.txt b/cmd/policy/testdata/policy/logs-expected-usage.txt index 3cd586b1f..7a0152630 100644 --- a/cmd/policy/testdata/policy/logs-expected-usage.txt +++ b/cmd/policy/testdata/policy/logs-expected-usage.txt @@ -2,7 +2,7 @@ Usage: policy logs [decision_id] [flags] Examples: -policy logs --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f --after 2022/03/14 --out output.json +circleci policy logs --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f --after 2022/03/14 --out output.json Flags: --after string filter decision logs triggered AFTER this datetime diff --git a/cmd/policy/testdata/policy/push-expected-usage.txt b/cmd/policy/testdata/policy/push-expected-usage.txt index 940461be3..1ec9fca05 100644 --- a/cmd/policy/testdata/policy/push-expected-usage.txt +++ b/cmd/policy/testdata/policy/push-expected-usage.txt @@ -2,7 +2,7 @@ Usage: policy push [flags] Examples: -policy push ./policies --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f +circleci policy push ./policies --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f Flags: --context string policy context (default "config") diff --git a/cmd/policy/testdata/policy/settings-expected-usage.txt b/cmd/policy/testdata/policy/settings-expected-usage.txt index b0e9b7764..96a949ba4 100644 --- a/cmd/policy/testdata/policy/settings-expected-usage.txt +++ b/cmd/policy/testdata/policy/settings-expected-usage.txt @@ -2,7 +2,7 @@ Usage: policy settings [flags] Examples: -policy settings --enabled=true +circleci policy settings --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f --enabled=true Flags: --context string policy context for decision (default "config") diff --git a/cmd/policy/testdata/policy/update-expected-usage.txt b/cmd/policy/testdata/policy/update-expected-usage.txt deleted file mode 100644 index 799799873..000000000 --- a/cmd/policy/testdata/policy/update-expected-usage.txt +++ /dev/null @@ -1,13 +0,0 @@ -Usage: - policy update [flags] - -Examples: -policy update e9e300d1-5bab-4704-b610-addbd6e03b0b --owner-id 462d67f8-b232-4da4-a7de-0c86dd667d3f --name policy_name --policy ./policy.rego - -Flags: - --context string policy context (if set, must be config) - --owner-id string the id of the policy's owner - --policy string path to rego file containing the updated policy - -Global Flags: - --policy-base-url string base url for policy api (default "https://internal.circleci.com") From abdc119bbd5963d7ed2a2acbfb3ffb187e6f57ca Mon Sep 17 00:00:00 2001 From: rlegan Date: Wed, 12 Apr 2023 11:30:22 +0200 Subject: [PATCH 76/84] internal: Update CLI not to set the `Circle-Token` header when there is no token present --- api/api.go | 4 +++- api/context_rest.go | 4 +++- api/info/info.go | 4 +++- api/policy/policy.go | 4 +++- api/rest/client.go | 4 +++- api/schedule_rest.go | 4 +++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/api/api.go b/api/api.go index 3ea130e0d..ac29d7c1f 100644 --- a/api/api.go +++ b/api/api.go @@ -1834,7 +1834,9 @@ func FollowProject(config settings.Config, vcs string, owner string, projectName } r.Header.Set("Content-Type", "application/json; charset=utf-8") r.Header.Set("Accept", "application/json; charset=utf-8") - r.Header.Set("Circle-Token", config.Token) + if config.Token != "" { + r.Header.Set("Circle-Token", config.Token) + } response, err := config.HTTPClient.Do(r) if err != nil { diff --git a/api/context_rest.go b/api/context_rest.go index ce484ea7c..17b2ff21d 100644 --- a/api/context_rest.go +++ b/api/context_rest.go @@ -544,7 +544,9 @@ func (c *ContextRestClient) newHTTPRequest(method, url string, body io.Reader) ( if err != nil { return nil, err } - req.Header.Add("circle-token", c.token) + if c.token != "" { + req.Header.Add("circle-token", c.token) + } req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") req.Header.Add("User-Agent", version.UserAgent()) diff --git a/api/info/info.go b/api/info/info.go index df1c79455..d8feb74e4 100644 --- a/api/info/info.go +++ b/api/info/info.go @@ -86,7 +86,9 @@ func (c *InfoRESTClient) newHTTPRequest(method, url string, body io.Reader) (*ht if err != nil { return nil, err } - req.Header.Add("circle-token", c.token) + if c.token != "" { + req.Header.Add("circle-token", c.token) + } req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") req.Header.Add("User-Agent", version.UserAgent()) diff --git a/api/policy/policy.go b/api/policy/policy.go index 605d31558..19babab9a 100644 --- a/api/policy/policy.go +++ b/api/policy/policy.go @@ -341,7 +341,9 @@ func NewClient(baseURL string, config *settings.Config) *Client { // releasing the semaphore after a second ensuring client doesn't make more than cap(sem)/second time.AfterFunc(time.Second, func() { <-sem }) - r.Header.Add("circle-token", config.Token) + if config.Token != "" { + r.Header.Add("circle-token", config.Token) + } r.Header.Add("Accept", "application/json") r.Header.Add("Content-Type", "application/json") r.Header.Add("User-Agent", version.UserAgent()) diff --git a/api/rest/client.go b/api/rest/client.go index 219363301..93a89ac9b 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -70,7 +70,9 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req } func (c *Client) enrichRequestHeaders(req *http.Request, payload interface{}) { - req.Header.Set("Circle-Token", c.circleToken) + if c.circleToken != "" { + req.Header.Set("Circle-Token", c.circleToken) + } req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", version.UserAgent()) commandStr := header.GetCommandStr() diff --git a/api/schedule_rest.go b/api/schedule_rest.go index c422b66a0..efa8fac5c 100644 --- a/api/schedule_rest.go +++ b/api/schedule_rest.go @@ -402,7 +402,9 @@ func (c *ScheduleRestClient) newHTTPRequest(method, url string, body io.Reader) if err != nil { return nil, err } - req.Header.Add("circle-token", c.token) + if c.token != "" { + req.Header.Add("circle-token", c.token) + } req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") req.Header.Add("User-Agent", version.UserAgent()) From 5b836baa4da3ef2908cb3250e301715a835a094d Mon Sep 17 00:00:00 2001 From: "Kyle a.k.a. TechSquidTV" <33272306+KyleTryon@users.noreply.github.com> Date: Wed, 19 Apr 2023 10:22:21 -0400 Subject: [PATCH 77/84] feat: add env subst command [CPE-1773] (#910) * feat: add env subst command * chore: lint --- cmd/env.go | 47 +++++++++++++++++++++++ cmd/env_test.go | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ cmd/root.go | 1 + cmd/root_test.go | 2 +- go.mod | 1 + go.sum | 2 + 6 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 cmd/env.go create mode 100644 cmd/env_test.go diff --git a/cmd/env.go b/cmd/env.go new file mode 100644 index 000000000..9d9ed5fe0 --- /dev/null +++ b/cmd/env.go @@ -0,0 +1,47 @@ +package cmd + +import ( + "fmt" + "io" + + "github.com/a8m/envsubst" + "github.com/spf13/cobra" +) + +func newEnvCmd() *cobra.Command { + var envCmd = &cobra.Command{ + Use: "env", + Short: "Manage environment variables", + } + var substCmd = &cobra.Command{ + Use: "subst", + Short: "Substitute environment variables in a string", + RunE: substRunE, + } + envCmd.AddCommand(substCmd) + return envCmd +} + +// Accepts a string as an argument, or reads from stdin if no argument is provided. +func substRunE(cmd *cobra.Command, args []string) error { + var input string + if len(args) > 0 { + input = args[0] + } else { + // Read from stdin + b, err := io.ReadAll(cmd.InOrStdin()) + if err != nil { + return err + } + input = string(b) + } + if input == "" { + return nil + } + output, err := envsubst.String(input) + if err != nil { + return err + } + _, err = fmt.Fprint(cmd.OutOrStdout(), output) + return err +} diff --git a/cmd/env_test.go b/cmd/env_test.go new file mode 100644 index 000000000..a8ea0e27e --- /dev/null +++ b/cmd/env_test.go @@ -0,0 +1,97 @@ +package cmd + +import ( + "bytes" + "os" + "testing" + + "gotest.tools/v3/assert" +) + +func TestSubstRunE(t *testing.T) { + // Set environment variables for testing + err := os.Setenv("ENV_NAME", "world") + if err != nil { + t.Fatal(err) + } + + testCases := []struct { + name string + input string + output string + }{ + { + name: "substitute variables", + input: "Hello $ENV_NAME!", + output: "Hello world!", + }, + { + name: "no variables to substitute", + input: "Hello, world!", + output: "Hello, world!", + }, + { + name: "empty input", + input: "", + output: "", + }, + { + name: "no variables JSON", + input: `{"foo": "bar"}`, + output: `{"foo": "bar"}`, + }, + { + name: "substitute variables JSON", + input: `{"foo": "$ENV_NAME"}`, + output: `{"foo": "world"}`, + }, + { + name: "no variables key=value", + input: `foo=bar`, + output: `foo=bar`, + }, + } + + // Run tests for each test case as argument + for _, tc := range testCases { + t.Run("arg: "+tc.name, func(t *testing.T) { + // Set up test command + cmd := newEnvCmd() + + // Capture output + outputBuf := bytes.Buffer{} + cmd.SetOut(&outputBuf) + + // Run command + cmd.SetArgs([]string{"subst", tc.input}) + err := cmd.Execute() + + // Check output and error + assert.NilError(t, err) + assert.Equal(t, tc.output, outputBuf.String()) + }) + } + // Run tests for each test case as stdin + for _, tc := range testCases { + t.Run("stdin: "+tc.name, func(t *testing.T) { + // Set up test command + cmd := newEnvCmd() + + // Set up input + inputBuf := bytes.NewBufferString(tc.input) + cmd.SetIn(inputBuf) + + // Capture output + outputBuf := bytes.Buffer{} + cmd.SetOut(&outputBuf) + + // Run command + cmd.SetArgs([]string{"subst"}) + err = cmd.Execute() + + // Check output and error + assert.NilError(t, err) + assert.Equal(t, tc.output, outputBuf.String()) + }) + } +} diff --git a/cmd/root.go b/cmd/root.go index a6257771f..942278c69 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -174,6 +174,7 @@ func MakeCommands() *cobra.Command { rootCmd.AddCommand(newSwitchCommand(rootOptions)) rootCmd.AddCommand(newAdminCommand(rootOptions)) rootCmd.AddCommand(newCompletionCommand()) + rootCmd.AddCommand(newEnvCmd()) flags := rootCmd.PersistentFlags() diff --git a/cmd/root_test.go b/cmd/root_test.go index 6ea4c5667..def65ae10 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -16,7 +16,7 @@ var _ = Describe("Root", func() { Describe("subcommands", func() { It("can create commands", func() { commands := cmd.MakeCommands() - Expect(len(commands.Commands())).To(Equal(23)) + Expect(len(commands.Commands())).To(Equal(24)) }) }) diff --git a/go.mod b/go.mod index c73f2c9a2..b20bd23c5 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,7 @@ require ( require ( github.com/OneOfOne/xxhash v1.2.8 // indirect + github.com/a8m/envsubst v1.4.2 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/charmbracelet/bubbles v0.11.0 // indirect diff --git a/go.sum b/go.sum index 8591b37e6..3b83fe785 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,8 @@ github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nB github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg= +github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= From d03951c6a960aa58c4be2b5c55407043a6dc6712 Mon Sep 17 00:00:00 2001 From: rlegan Date: Fri, 14 Apr 2023 15:31:18 +0200 Subject: [PATCH 78/84] Add warning on some CLI commands that orbs cannot be deleted once they are created --- cmd/orb.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmd/orb.go b/cmd/orb.go index 33a06c855..b0724c18c 100644 --- a/cmd/orb.go +++ b/cmd/orb.go @@ -719,6 +719,7 @@ func processOrb(opts orbOptions) error { } func publishOrb(opts orbOptions) error { + orbInformThatOrbCannotBeDeletedMessage() path := opts.args[0] ref := opts.args[1] namespace, orb, version, err := references.SplitIntoOrbNamespaceAndVersion(ref) @@ -794,6 +795,8 @@ func validateSegmentArg(label string) error { } func incrementOrb(opts orbOptions) error { + orbInformThatOrbCannotBeDeletedMessage() + ref := opts.args[1] segment := opts.args[2] @@ -822,6 +825,8 @@ func incrementOrb(opts orbOptions) error { } func promoteOrb(opts orbOptions) error { + orbInformThatOrbCannotBeDeletedMessage() + ref := opts.args[0] segment := opts.args[1] @@ -1104,6 +1109,8 @@ func initOrb(opts orbOptions) error { var err error fmt.Println("Note: This command is in preview. Please report any bugs! https://github.com/CircleCI-Public/circleci-cli/issues/new/choose") + orbInformThatOrbCannotBeDeletedMessage() + fullyAutomated := 0 prompt := &survey.Select{ Message: "Would you like to perform an automated setup of this orb?", @@ -1734,3 +1741,8 @@ func stringifyDiff(diff gotextdiff.Unified, colorOpt string) string { color.NoColor = oldNoColor return strings.Join(lines, "\n") } + +func orbInformThatOrbCannotBeDeletedMessage() { + fmt.Println("Once an orb is created it cannot be deleted. Orbs are semver compliant, and each published version is immutable. Publicly released orbs are potential dependencies for other projects.") + fmt.Println("Therefore, allowing orb deletion would make users susceptible to unexpected loss of functionality.") +} From 4a4fb0775d45baea1a5b0e8b5ec967f1ccfe5b65 Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Thu, 20 Apr 2023 11:49:59 +0100 Subject: [PATCH 79/84] In order to improve clarity around version support for server instances and the CLI I've created a more in-depth version compatibility matrix --- README.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4358c64a6..563f174b9 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,37 @@ Please see the [documentation](https://circleci-public.github.io/circleci-cli) o ## Version Compatibility -As of version `0.1.24705` - we no longer support Server 3.x instances. In order to upgrade the CLI to the latest version, you'll need to update your instance of server to 4.x. +| Server Release | Support Dates | Component | Supported Version | +| ----------- | ----------- | ----------- | ----------- | +| 4.2.0 | | | | +| | | CircleCI CLI | 0.1.24705 | +| 4.1.2 | | | | +| | | CircleCI CLI | 0.1.23845 | +| 4.1.1 | | | | +| | | CircleCI CLI | 0.1.23845 | +| 4.1.0 | | | | +| | | CircleCI CLI | 0.1.23845 | +| 4.0.4 | | | | +| | | CircleCI CLI | 0.1.23845 | +| 4.0.3 | | | | +| | | CircleCI CLI | 0.1.23845 | +| 4.0.2 | | | | +| | | CircleCI CLI | 0.1.23845 | +| 4.0.1 | | | | +| | | CircleCI CLI | 0.1.23845 | +| 4.0.0 | | | | +| | | CircleCI CLI | 0.1.23845 | +| 3.4.6 | | | | +| | | CircleCI CLI | 0.1.23845 | +| 3.4.5 | | | | +| | | CircleCI CLI | 0.1.23845 | +| 3.4.4 | | | | +| | | CircleCI CLI | 0.1.23845 | +| 3.4.3 | | | | +| | | CircleCI CLI | 0.1.23845 | +| 3.x | | | | +| | | CircleCI CLI | 0.1.23845 | +| 2.x | | | | +| | | CircleCI CLI | 0.1.23845 | + -`0.1.23845` is the last version to support Server 3.x and 2.x. From 7eb6cd98df56b819d8a97eaccf9e11a0e6427988 Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Thu, 20 Apr 2023 11:59:13 +0100 Subject: [PATCH 80/84] Improving readability --- README.md | 49 +++++++++++++++++-------------------------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 563f174b9..45eddd846 100644 --- a/README.md +++ b/README.md @@ -182,37 +182,22 @@ Please see the [documentation](https://circleci-public.github.io/circleci-cli) o ## Version Compatibility -| Server Release | Support Dates | Component | Supported Version | -| ----------- | ----------- | ----------- | ----------- | -| 4.2.0 | | | | -| | | CircleCI CLI | 0.1.24705 | -| 4.1.2 | | | | -| | | CircleCI CLI | 0.1.23845 | -| 4.1.1 | | | | -| | | CircleCI CLI | 0.1.23845 | -| 4.1.0 | | | | -| | | CircleCI CLI | 0.1.23845 | -| 4.0.4 | | | | -| | | CircleCI CLI | 0.1.23845 | -| 4.0.3 | | | | -| | | CircleCI CLI | 0.1.23845 | -| 4.0.2 | | | | -| | | CircleCI CLI | 0.1.23845 | -| 4.0.1 | | | | -| | | CircleCI CLI | 0.1.23845 | -| 4.0.0 | | | | -| | | CircleCI CLI | 0.1.23845 | -| 3.4.6 | | | | -| | | CircleCI CLI | 0.1.23845 | -| 3.4.5 | | | | -| | | CircleCI CLI | 0.1.23845 | -| 3.4.4 | | | | -| | | CircleCI CLI | 0.1.23845 | -| 3.4.3 | | | | -| | | CircleCI CLI | 0.1.23845 | -| 3.x | | | | -| | | CircleCI CLI | 0.1.23845 | -| 2.x | | | | -| | | CircleCI CLI | 0.1.23845 | +| Server Release | CircleCI Supported Version | +| ----------- | ----------- | +| 4.2.0 | 0.1.24705 | +| 4.1.2 | 0.1.23845 | +| 4.1.1 | 0.1.23845 | +| 4.1.0 | 0.1.23845 | +| 4.0.4 | 0.1.23845 | +| 4.0.3 | 0.1.23845 | +| 4.0.2 | 0.1.23845 | +| 4.0.1 | 0.1.23845 | +| 4.0.0 | 0.1.23845 | +| 3.4.6 | 0.1.23845 | +| 3.4.5 | 0.1.23845 | +| 3.4.4 | 0.1.23845 | +| 3.4.3 | 0.1.23845 | +| 3.x | 0.1.23845 | +| 2.x | 0.1.23845 | From f0116179e43c3f4605c6d239dcef01d6a28b67f1 Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Thu, 20 Apr 2023 11:49:59 +0100 Subject: [PATCH 81/84] In order to improve clarity around version support for server instances and the CLI I've created a more in-depth version compatibility matrix --- api/rest/client.go | 1 - config/config.go | 19 ++++-- config/config_test.go | 23 +++---- config/legacy.go | 141 ++++++++++++++++++++++++++++++++++++++++++ config/legacy_test.go | 76 +++++++++++++++++++++++ 5 files changed, 244 insertions(+), 16 deletions(-) create mode 100644 config/legacy.go create mode 100644 config/legacy_test.go diff --git a/api/rest/client.go b/api/rest/client.go index 93a89ac9b..2d1628f41 100644 --- a/api/rest/client.go +++ b/api/rest/client.go @@ -98,7 +98,6 @@ func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int, }{} err = json.NewDecoder(httpResp.Body).Decode(&httpError) if err != nil { - fmt.Printf("failed to decode body: %s", err.Error()) return httpResp.StatusCode, err } return httpResp.StatusCode, &HTTPError{Code: httpResp.StatusCode, Message: httpError.Message} diff --git a/config/config.go b/config/config.go index 896d2a337..61b770c65 100644 --- a/config/config.go +++ b/config/config.go @@ -6,6 +6,7 @@ import ( "net/url" "os" + "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/api/rest" "github.com/CircleCI-Public/circleci-cli/settings" "github.com/pkg/errors" @@ -23,6 +24,9 @@ type ConfigCompiler struct { host string compileRestClient *rest.Client collaboratorRestClient *rest.Client + + cfg *settings.Config + legacyGraphQLClient *graphql.Client } func New(cfg *settings.Config) *ConfigCompiler { @@ -31,7 +35,10 @@ func New(cfg *settings.Config) *ConfigCompiler { host: hostValue, compileRestClient: rest.NewFromConfig(hostValue, cfg), collaboratorRestClient: rest.NewFromConfig(cfg.Host, cfg), + cfg: cfg, } + + configCompiler.legacyGraphQLClient = graphql.NewClient(cfg.HTTPClient, cfg.Host, cfg.Endpoint, cfg.Token, cfg.Debug) return configCompiler } @@ -102,11 +109,15 @@ func (c *ConfigCompiler) ConfigQuery( } configCompilationResp := &ConfigResponse{} - statusCode, err := c.compileRestClient.DoRequest(req, configCompilationResp) - if err != nil { - if statusCode == 404 { - return nil, errors.New("this version of the CLI does not support your instance of server, please refer to https://github.com/CircleCI-Public/circleci-cli for version compatibility") + statusCode, originalErr := c.compileRestClient.DoRequest(req, configCompilationResp) + if statusCode == 404 { + legacyResponse, err := c.legacyConfigQueryByOrgID(configString, orgID, params, values, c.cfg) + if err != nil { + return nil, err } + return legacyResponse, nil + } + if originalErr != nil { return nil, fmt.Errorf("config compilation request returned an error: %w", err) } diff --git a/config/config_test.go b/config/config_test.go index deeba3c00..036d042d1 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -72,17 +72,18 @@ func TestCompiler(t *testing.T) { assert.Contains(t, err.Error(), "Could not load config file at testdata/nonexistent.yml") }) - t.Run("handles 404 status correctly", func(t *testing.T) { - svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotFound) - })) - defer svr.Close() - compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) - - _, err := compiler.ConfigQuery("testdata/config.yml", "1234", Parameters{}, Values{}) - assert.Error(t, err) - assert.Contains(t, err.Error(), "this version of the CLI does not support your instance of server") - }) + // commenting this out - we have a legacy_test.go unit test that covers this behaviour + // t.Run("handles 404 status correctly", func(t *testing.T) { + // svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // w.WriteHeader(http.StatusNotFound) + // })) + // defer svr.Close() + // compiler := New(&settings.Config{Host: svr.URL, HTTPClient: http.DefaultClient}) + + // _, err := compiler.ConfigQuery("testdata/config.yml", "1234", Parameters{}, Values{}) + // assert.Error(t, err) + // assert.Contains(t, err.Error(), "this version of the CLI does not support your instance of server") + // }) t.Run("handles non-200 status correctly", func(t *testing.T) { svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/config/legacy.go b/config/legacy.go new file mode 100644 index 000000000..02581bb74 --- /dev/null +++ b/config/legacy.go @@ -0,0 +1,141 @@ +package config + +import ( + "encoding/json" + "fmt" + "sort" + "strings" + + "github.com/CircleCI-Public/circleci-cli/api/graphql" + "github.com/CircleCI-Public/circleci-cli/settings" + "github.com/pkg/errors" +) + +// GQLErrorsCollection is a slice of errors returned by the GraphQL server. +// Each error is made up of a GQLResponseError type. +type GQLErrorsCollection []GQLResponseError + +// BuildConfigResponse wraps the GQL result of the ConfigQuery +type BuildConfigResponse struct { + BuildConfig struct { + LegacyConfigResponse + } +} + +// Error turns a GQLErrorsCollection into an acceptable error string that can be printed to the user. +func (errs GQLErrorsCollection) Error() string { + messages := []string{} + + for i := range errs { + messages = append(messages, errs[i].Message) + } + + return strings.Join(messages, "\n") +} + +// LegacyConfigResponse is a structure that matches the result of the GQL +// query, so that we can use mapstructure to convert from +// nested maps to a strongly typed struct. +type LegacyConfigResponse struct { + Valid bool + SourceYaml string + OutputYaml string + + Errors GQLErrorsCollection +} + +// GQLResponseError is a mapping of the data returned by the GraphQL server of key-value pairs. +// Typically used with the structure "Message: string", but other response errors provide additional fields. +type GQLResponseError struct { + Message string + Value string + AllowedValues []string + EnumType string + Type string +} + +// PrepareForGraphQL takes a golang homogenous map, and transforms it into a list of keyval pairs, since GraphQL does not support homogenous maps. +func PrepareForGraphQL(kvMap Values) []KeyVal { + // we need to create the slice of KeyVals in a deterministic order for testing purposes + keys := make([]string, 0, len(kvMap)) + for k := range kvMap { + keys = append(keys, k) + } + sort.Strings(keys) + + kvs := make([]KeyVal, 0, len(kvMap)) + for _, k := range keys { + kvs = append(kvs, KeyVal{Key: k, Val: kvMap[k]}) + } + return kvs +} + +func (c *ConfigCompiler) legacyConfigQueryByOrgID( + configString string, + orgID string, + params Parameters, + values Values, + cfg *settings.Config, +) (*ConfigResponse, error) { + var response BuildConfigResponse + // GraphQL isn't forwards-compatible, so we are unusually selective here about + // passing only non-empty fields on to the API, to minimize user impact if the + // backend is out of date. + var fieldAddendums string + if orgID != "" { + fieldAddendums += ", orgId: $orgId" + } + if len(params) > 0 { + fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson" + } + query := fmt.Sprintf( + `query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) { + buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) { + valid, + errors { message }, + sourceYaml, + outputYaml + } + }`, + fieldAddendums, + ) + + request := graphql.NewRequest(query) + request.SetToken(cfg.Token) + request.Var("config", configString) + + if values != nil { + request.Var("pipelineValues", PrepareForGraphQL(values)) + } + if params != nil { + pipelineParameters, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error()) + } + request.Var("pipelineParametersJson", string(pipelineParameters)) + } + + if orgID != "" { + request.Var("orgId", orgID) + } + + err := c.legacyGraphQLClient.Run(request, &response) + if err != nil { + return nil, errors.Wrap(err, "Unable to validate config") + } + if len(response.BuildConfig.LegacyConfigResponse.Errors) > 0 { + return nil, &response.BuildConfig.LegacyConfigResponse.Errors + } + + return &ConfigResponse{ + Valid: response.BuildConfig.LegacyConfigResponse.Valid, + SourceYaml: response.BuildConfig.LegacyConfigResponse.SourceYaml, + OutputYaml: response.BuildConfig.LegacyConfigResponse.OutputYaml, + }, nil +} + +// KeyVal is a data structure specifically for passing pipeline data to GraphQL which doesn't support free-form maps. +type KeyVal struct { + Key string `json:"key"` + Val interface{} `json:"val"` +} diff --git a/config/legacy_test.go b/config/legacy_test.go new file mode 100644 index 000000000..516232273 --- /dev/null +++ b/config/legacy_test.go @@ -0,0 +1,76 @@ +package config + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/CircleCI-Public/circleci-cli/settings" + "github.com/stretchr/testify/assert" +) + +func TestLegacyFlow(t *testing.T) { + t.Run("tests that the compiler defaults to the graphQL resolver should the original API request fail with 404", func(t *testing.T) { + mux := http.NewServeMux() + + mux.HandleFunc("/compile-config-with-defaults", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + }) + + mux.HandleFunc("/me/collaborations", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `[{"vcs_type":"circleci","slug":"gh/test","id":"2345"}]`) + }) + + mux.HandleFunc("/graphql-unstable", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `{"data":{"buildConfig": {"valid":true,"sourceYaml":"%s","outputYaml":"%s","errors":[]}}}`, testYaml, testYaml) + }) + + svr := httptest.NewServer(mux) + defer svr.Close() + + compiler := New(&settings.Config{ + Host: svr.URL, + Endpoint: "/graphql-unstable", + HTTPClient: http.DefaultClient, + Token: "", + }) + resp, err := compiler.ConfigQuery("testdata/config.yml", "1234", Parameters{}, Values{}) + + assert.Equal(t, true, resp.Valid) + assert.NoError(t, err) + }) + + t.Run("tests that the compiler handles errors properly when returned from the graphQL endpoint", func(t *testing.T) { + mux := http.NewServeMux() + + mux.HandleFunc("/compile-config-with-defaults", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + }) + + mux.HandleFunc("/me/collaborations", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `[{"vcs_type":"circleci","slug":"gh/test","id":"2345"}]`) + }) + + mux.HandleFunc("/graphql-unstable", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `{"data":{"buildConfig":{"errors":[{"message": "failed to validate"}]}}}`) + }) + + svr := httptest.NewServer(mux) + defer svr.Close() + + compiler := New(&settings.Config{ + Host: svr.URL, + Endpoint: "/graphql-unstable", + HTTPClient: http.DefaultClient, + Token: "", + }) + _, err := compiler.ConfigQuery("testdata/config.yml", "1234", Parameters{}, Values{}) + assert.Error(t, err) + assert.Contains(t, "failed to validate", err.Error()) + }) +} From d40051701df4f94fa3651d6660593858d88da3a2 Mon Sep 17 00:00:00 2001 From: Elliot Forbes Date: Thu, 20 Apr 2023 16:21:10 +0100 Subject: [PATCH 82/84] Readme removal --- README.md | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/README.md b/README.md index 45eddd846..ec03bea08 100644 --- a/README.md +++ b/README.md @@ -179,25 +179,3 @@ Development instructions for the CircleCI CLI can be found in [HACKING.md](HACKI Please see the [documentation](https://circleci-public.github.io/circleci-cli) or `circleci help` for more. - -## Version Compatibility - -| Server Release | CircleCI Supported Version | -| ----------- | ----------- | -| 4.2.0 | 0.1.24705 | -| 4.1.2 | 0.1.23845 | -| 4.1.1 | 0.1.23845 | -| 4.1.0 | 0.1.23845 | -| 4.0.4 | 0.1.23845 | -| 4.0.3 | 0.1.23845 | -| 4.0.2 | 0.1.23845 | -| 4.0.1 | 0.1.23845 | -| 4.0.0 | 0.1.23845 | -| 3.4.6 | 0.1.23845 | -| 3.4.5 | 0.1.23845 | -| 3.4.4 | 0.1.23845 | -| 3.4.3 | 0.1.23845 | -| 3.x | 0.1.23845 | -| 2.x | 0.1.23845 | - - From 7d09d97adf5811177bf90da5dfc2e6d3f77dbaa8 Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Thu, 20 Apr 2023 17:31:46 +0200 Subject: [PATCH 83/84] style: Added a log when going the legacy path --- config/config.go | 1 + config/legacy_test.go | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 61b770c65..abc451b87 100644 --- a/config/config.go +++ b/config/config.go @@ -111,6 +111,7 @@ func (c *ConfigCompiler) ConfigQuery( configCompilationResp := &ConfigResponse{} statusCode, originalErr := c.compileRestClient.DoRequest(req, configCompilationResp) if statusCode == 404 { + fmt.Fprintf(os.Stderr, "You are using a old version of CircleCI Server, please consider updating") legacyResponse, err := c.legacyConfigQueryByOrgID(configString, orgID, params, values, c.cfg) if err != nil { return nil, err diff --git a/config/legacy_test.go b/config/legacy_test.go index 516232273..5175a136f 100644 --- a/config/legacy_test.go +++ b/config/legacy_test.go @@ -71,6 +71,41 @@ func TestLegacyFlow(t *testing.T) { }) _, err := compiler.ConfigQuery("testdata/config.yml", "1234", Parameters{}, Values{}) assert.Error(t, err) - assert.Contains(t, "failed to validate", err.Error()) + assert.Contains(t, err.Error(), "failed to validate") + }) + + t.Run("tests that the compiler fails out completely when a non-404 is returned from the http endpoint", func(t *testing.T) { + mux := http.NewServeMux() + gqlHitCounter := 0 + + mux.HandleFunc("/compile-config-with-defaults", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + + }) + + mux.HandleFunc("/me/collaborations", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `[{"vcs_type":"circleci","slug":"gh/test","id":"2345"}]`) + }) + + mux.HandleFunc("/graphql-unstable", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `{"data":{"buildConfig":{"errors":[{"message": "failed to validate"}]}}}`) + gqlHitCounter++ + }) + + svr := httptest.NewServer(mux) + defer svr.Close() + + compiler := New(&settings.Config{ + Host: svr.URL, + Endpoint: "/graphql-unstable", + HTTPClient: http.DefaultClient, + Token: "", + }) + _, err := compiler.ConfigQuery("testdata/config.yml", "1234", Parameters{}, Values{}) + assert.Error(t, err) + assert.Contains(t, err.Error(), "config compilation request returned an error:") + assert.Equal(t, 0, gqlHitCounter) }) } From 936362a747a992a4713bd5a3546b3b74d420570f Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Thu, 20 Apr 2023 19:02:24 +0200 Subject: [PATCH 84/84] fix: Remove typo --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index abc451b87..b8cb5921b 100644 --- a/config/config.go +++ b/config/config.go @@ -111,7 +111,7 @@ func (c *ConfigCompiler) ConfigQuery( configCompilationResp := &ConfigResponse{} statusCode, originalErr := c.compileRestClient.DoRequest(req, configCompilationResp) if statusCode == 404 { - fmt.Fprintf(os.Stderr, "You are using a old version of CircleCI Server, please consider updating") + fmt.Fprintf(os.Stderr, "You are using a old version of CircleCI Server, please consider updating\n") legacyResponse, err := c.legacyConfigQueryByOrgID(configString, orgID, params, values, c.cfg) if err != nil { return nil, err