diff --git a/server/config_example.toml b/server/config_example.toml index b1c1d6dc0c..86151d68a1 100644 --- a/server/config_example.toml +++ b/server/config_example.toml @@ -1,6 +1,9 @@ # Specify the port number for the server application to run on. port = 8080 +# Specify the address on which the server listens +address = "" + # Set the log level for the server log-level = "INFO" @@ -28,6 +31,15 @@ key = "" # Set the connection string for the PostgreSQL database. database = "postgres://admin:supersecret@localhost:5432/scrumlr?sslmode=disable" +# Set the database host name for the PsotgreSQL database +database-host = "" + +# Set the database username for the PostgreSQL database +database-username = "" + +# Set the database password for the PostgreSQL database +database-password = "" + # Define the base path for the application. base-path = "/" @@ -40,8 +52,11 @@ allow-anonymous-custom-templates = false # Enable/Disable the board creation for anonymous clients allow-anonymous-board-creation = true +# Enable/Disable history for anonymous clients +allow-anonymous-history = false + # enables/disables experimental file system store, in order to allow larger session cookie sizes -auth-experimental-file-system-store = false +auth-enable-experimental-file-system-store = false # Set the protocol and host for the auth provider callbacks (e.g. https://scrumlr.io) auth-callback-host = "" @@ -94,8 +109,14 @@ auth-oidc-user-ident-scope = "" # Set the JWT scope to request from the IDP for user name information. auth-oidc-user-name-scope = "" -# Enable or disable verbose logging. -verbose = true +# Set the session secret for the authentication providers +session-secret = "" + +# Set the OTel grpc endpoint for sending logs, traces and metrics +otel-grpc = "" + +# Set the OTel http endpoint for sending logs, traces and metrics +otel-http = "" # Disable check origin (strongly suggestion to only use this for development) disable-check-origin = true @@ -103,6 +124,5 @@ disable-check-origin = true # Specify the URL for the feedback webhook. feedback-webhook-url = "" -# Sepcify the OTel endpoint for sending logs, traces and metrics -otel-grpc = "" -otel-http = "" +# Enable/Disable the swagger page +enable-swagger = false diff --git a/server/e2e-tests/go.mod b/server/e2e-tests/go.mod index 4edc3eeabb..fb08e73510 100644 --- a/server/e2e-tests/go.mod +++ b/server/e2e-tests/go.mod @@ -15,7 +15,6 @@ require ( github.com/ajg/form v1.9.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/coder/websocket v1.8.15 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-chi/chi/v5 v5.3.1 // indirect github.com/go-chi/render v1.0.3 // indirect @@ -32,7 +31,6 @@ require ( github.com/redis/go-redis/extra/rediscmd/v9 v9.22.0 // indirect github.com/redis/go-redis/extra/redisotel/v9 v9.22.0 // indirect github.com/redis/go-redis/v9 v9.22.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -41,11 +39,10 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect github.com/uptrace/bun v1.2.18 // indirect - github.com/urfave/cli/v2 v2.27.7 // indirect + github.com/urfave/cli/v3 v3.10.1 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/weppos/publicsuffix-go v0.50.3 // indirect - github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/bridges/otelzap v0.20.0 // indirect go.opentelemetry.io/otel v1.45.0 // indirect diff --git a/server/e2e-tests/go.sum b/server/e2e-tests/go.sum index 7b41c54ef1..3d62a7fb8a 100644 --- a/server/e2e-tests/go.sum +++ b/server/e2e-tests/go.sum @@ -31,16 +31,14 @@ github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpS github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= -github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= -github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= -github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= +github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= -github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY= +github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc= @@ -82,8 +80,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= -github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= @@ -94,18 +92,18 @@ github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5L github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= -github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= -github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= -github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= -github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw= -github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g= +github.com/moby/go-archive v0.3.0 h1:nos4BtzzUIqB406BgQnWGMI4qib9BZ8XUHU+ucv/n1c= +github.com/moby/go-archive v0.3.0/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE= +github.com/moby/moby/api v1.55.0 h1:2/sexvQyqIWS8pRSCFddBfpW2qE7vR7FCL+vN8pxwMc= +github.com/moby/moby/api v1.55.0/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.5.0 h1:5XhyPk2fuOWf6RlSFa3MkIIgDZkF25xToXW8Q/BH7cc= +github.com/moby/moby/client v0.5.0/go.mod h1:rcVpF8ncl9vo5gaIBdol6CnbEtSj1uxMvEV/UrykF/s= github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= -github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= -github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/sequential v0.7.0 h1:ASQNGNROJSuOO6LL6bPHbKvuZu6NU8P4ldPWk31zj/8= +github.com/moby/sys/sequential v0.7.0/go.mod h1:NfSTAp6V3fw4tmkD62PEcOKeZKquXT8VKCkf7aVR79o= +github.com/moby/sys/user v0.4.1 h1:RgjRlaDKi/Xmyrz4t8lyzXT6v2ooFeO/7xtchmhVWE0= +github.com/moby/sys/user v0.4.1/go.mod h1:E9QsW5WRe1kUAf7kW8hXKwu1uhsZEAdPLYHYSDudF4Y= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= @@ -136,12 +134,10 @@ github.com/redis/go-redis/v9 v9.22.0 h1:laDvpYXTJtZLloinw1fA5Kqd6HAEH2XKxOkG/PDq github.com/redis/go-redis/v9 v9.22.0/go.mod h1:y2g0Wj8rQvuK0ELM+oxSudcLtC09JScs98I/X9gRWY4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= -github.com/shirou/gopsutil/v4 v4.26.5 h1:RPcBXkpz7kOj9PqGFQOlBPZHsyaPvPVQc098y9RmCNM= -github.com/shirou/gopsutil/v4 v4.26.5/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs= +github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= @@ -158,18 +154,18 @@ github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8 github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/testcontainers/testcontainers-go v0.43.0 h1:oEQx5MW2DGd9z3AeEQfB2lPM0eLs7ztyaGRu75bFo5A= -github.com/testcontainers/testcontainers-go v0.43.0/go.mod h1:+VxkT2NQnKOZPKi6praMuMKYHYyOGXr0XSBSlSMCzFo= -github.com/testcontainers/testcontainers-go/modules/nats v0.43.0 h1:n1KcDXc18F687ODlTra411qWs+U+SH8VdnMM4kwpGo0= -github.com/testcontainers/testcontainers-go/modules/nats v0.43.0/go.mod h1:nIBpPkiorwXM5LDOQKTD9Osxr/cWv1W4FcAx43kM7jY= -github.com/testcontainers/testcontainers-go/modules/postgres v0.43.0 h1:ShNOFYAF4lKHvdIG258hi69bSxC88uXnxJkJvNs/IVs= -github.com/testcontainers/testcontainers-go/modules/postgres v0.43.0/go.mod h1:vdq5/RqmGfWeefzyfcVI/pID1rzmc1TDvqXa15bPJks= -github.com/testcontainers/testcontainers-go/modules/redis v0.43.0 h1:qzATMhrltLr07KcGl/d674ouqI0AFtf6wnQb3VnqP7M= -github.com/testcontainers/testcontainers-go/modules/redis v0.43.0/go.mod h1:ygEcEUIZzmIlOKpjBfnPn/lUIRNorr1kPj3XfFPTQXM= -github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= -github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= -github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= -github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= +github.com/testcontainers/testcontainers-go v0.44.0 h1:/Fwh6HY1mIikhnm9e7HwoxGycx0lzRAE0f5VQpjFxzI= +github.com/testcontainers/testcontainers-go v0.44.0/go.mod h1:IcnwQrYTO86xHXu5bvMaBH7ATlbS3Qn1M1QWW3c66rE= +github.com/testcontainers/testcontainers-go/modules/nats v0.44.0 h1:xGgxnCy6BnmIUUQXQmlYVl7hLx/gwXjJ2S6ccOz+JbA= +github.com/testcontainers/testcontainers-go/modules/nats v0.44.0/go.mod h1:UfIi/50Rj5pl3ixym03CO6kLQL5MIogZnGZj4OTJbh0= +github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0 h1:8fdv/9y3JMxjQ+ULAcOG8RtgeNu5t9XF9LolSXDuTwM= +github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0/go.mod h1:CFr2LncGYokw+OKjXcr8ARCKG1SaC2UEnGxFBovE86g= +github.com/testcontainers/testcontainers-go/modules/redis v0.44.0 h1:43EH7N6yB5B2tY/9uhPit487tMLm5iQiyKQaXWXNbnk= +github.com/testcontainers/testcontainers-go/modules/redis v0.44.0/go.mod h1:k4nnCSzm3z8yRMBKBn3rhsllbFjjhVn/2JjWNxxArg8= +github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= +github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= +github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= +github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= github.com/uptrace/bun v1.2.18 h1:3HnRcMfS6OBPMG1eSOzlbFJ/X/AyMEJb7rMxE6VQvDU= @@ -182,16 +178,14 @@ github.com/uptrace/bun/extra/bunotel v1.2.18 h1:idfBT+IJGOLSwqkNv+Yiw4fG0tgasXat github.com/uptrace/bun/extra/bunotel v1.2.18/go.mod h1:IdnKewPjPXZQHyap29M9PM2l+f0u5fLONeW90bAat88= github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 h1:ZjUj9BLYf9PEqBn8W/OapxhPjVRdC6CsXTdULHsyk5c= github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2/go.mod h1:O8bHQfyinKwTXKkiKNGmLQS7vRsqRxIQTFZpYpHK3IQ= -github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= -github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= +github.com/urfave/cli/v3 v3.10.1 h1:7Kx9H50hrHbRbyxgO1KP6/BcbiGRz0uYh5YyQ30JEEY= +github.com/urfave/cli/v3 v3.10.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/weppos/publicsuffix-go v0.50.3 h1:eT5dcjHQcVDNc0igpFEsGHKIip30feuB2zuuI9eJxiE= github.com/weppos/publicsuffix-go v0.50.3/go.mod h1:/rOa781xBykZhHK/I3QeHo92qdDKVmKZKF7s8qAEM/4= -github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= -github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= diff --git a/server/src/auth_provider_configuration_test.go b/server/src/auth_provider_configuration_test.go index 6ffadf9107..9bbc06053e 100644 --- a/server/src/auth_provider_configuration_test.go +++ b/server/src/auth_provider_configuration_test.go @@ -1,20 +1,22 @@ package main import ( - "flag" "testing" "github.com/stretchr/testify/assert" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" "scrumlr.io/server/common" ) func TestConfigureAuthProviderNone(t *testing.T) { + ctx := t.Context() basePath := "/" - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - init := cli.NewContext(nil, flagset, nil) - providerMap, err := configureAuthProvider(init, basePath) + init := &cli.Command{ + Flags: []cli.Flag{}, + } + + providerMap, err := configureAuthProvider(ctx, init, basePath) assert.Nil(t, err) assert.NotNil(t, providerMap) @@ -22,30 +24,56 @@ func TestConfigureAuthProviderNone(t *testing.T) { } func TestConfigureAuthProviderNoSessionSecret(t *testing.T) { + ctx := t.Context() basePath := "/" - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("auth-callback-host", "http://localhost:8080/callback", "") - flagset.String("auth-oidc-discovery-url", "http://localhost:8070/.well-known/openid-configuration", "") - flagset.String("auth-oidc-client-id", "oidcClientID", "") - flagset.String("auth-oidc-client-secret", "oidcClientSecret", "") - init := cli.NewContext(nil, flagset, nil) - providerMap, err := configureAuthProvider(init, basePath) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "auth-callback-host"}, + &cli.StringFlag{Name: "auth-oidc-discovery-url"}, + &cli.StringFlag{Name: "auth-oidc-client-id"}, + &cli.StringFlag{Name: "auth-oidc-client-secret"}, + }, + } + + err := init.Set("auth-callback-host", "http://localhost:8080/callback") + assert.NoError(t, err) + err = init.Set("auth-oidc-discovery-url", "http://localhost:8070/.well-known/openid-configuration") + assert.NoError(t, err) + err = init.Set("auth-oidc-client-id", "oidcClientID") + assert.NoError(t, err) + err = init.Set("auth-oidc-client-secret", "oidcClientSecret") + assert.NoError(t, err) + + providerMap, err := configureAuthProvider(ctx, init, basePath) assert.Error(t, err) assert.Nil(t, providerMap) } func TestConfigureAuthProviderGoogle(t *testing.T) { + ctx := t.Context() basePath := "/" - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("auth-callback-host", "http://localhost:8080/callback", "") - flagset.String("auth-google-client-id", "googleClientID", "") - flagset.String("auth-google-client-secret", "googleClientSecret", "") - flagset.String("session-secret", "ThisIsNotASecureSessessionSecret", "") - init := cli.NewContext(nil, flagset, nil) - providerMap, err := configureAuthProvider(init, basePath) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "auth-callback-host"}, + &cli.StringFlag{Name: "auth-google-client-id"}, + &cli.StringFlag{Name: "auth-google-client-secret"}, + &cli.StringFlag{Name: "session-secret"}, + }, + } + + err := init.Set("auth-callback-host", "http://localhost:8080/callback") + assert.NoError(t, err) + err = init.Set("auth-google-client-id", "googleClientID") + assert.NoError(t, err) + err = init.Set("auth-google-client-secret", "googleClientSecret") + assert.NoError(t, err) + err = init.Set("session-secret", "ThisIsNotASecureSessessionSecret") + assert.NoError(t, err) + + providerMap, err := configureAuthProvider(ctx, init, basePath) assert.Nil(t, err) assert.NotNil(t, providerMap) @@ -59,15 +87,28 @@ func TestConfigureAuthProviderGoogle(t *testing.T) { } func TestConfigureAuthProviderGitHub(t *testing.T) { + ctx := t.Context() basePath := "/" - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("auth-callback-host", "http://localhost:8080/callback", "") - flagset.String("auth-github-client-id", "githubClientID", "") - flagset.String("auth-github-client-secret", "githubClientSecret", "") - flagset.String("session-secret", "ThisIsNotASecureSessessionSecret", "") - init := cli.NewContext(nil, flagset, nil) - providerMap, err := configureAuthProvider(init, basePath) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "auth-callback-host"}, + &cli.StringFlag{Name: "auth-github-client-id"}, + &cli.StringFlag{Name: "auth-github-client-secret"}, + &cli.StringFlag{Name: "session-secret"}, + }, + } + + err := init.Set("auth-callback-host", "http://localhost:8080/callback") + assert.NoError(t, err) + err = init.Set("auth-github-client-id", "githubClientID") + assert.NoError(t, err) + err = init.Set("auth-github-client-secret", "githubClientSecret") + assert.NoError(t, err) + err = init.Set("session-secret", "ThisIsNotASecureSessessionSecret") + assert.NoError(t, err) + + providerMap, err := configureAuthProvider(ctx, init, basePath) assert.Nil(t, err) assert.NotNil(t, providerMap) @@ -81,15 +122,28 @@ func TestConfigureAuthProviderGitHub(t *testing.T) { } func TestConfigureAuthProviderMicrosoft(t *testing.T) { + ctx := t.Context() basePath := "/" - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("auth-callback-host", "http://localhost:8080/callback", "") - flagset.String("auth-microsoft-client-id", "microsoftClientID", "") - flagset.String("auth-microsoft-client-secret", "microsoftClientSecret", "") - flagset.String("session-secret", "ThisIsNotASecureSessessionSecret", "") - init := cli.NewContext(nil, flagset, nil) - providerMap, err := configureAuthProvider(init, basePath) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "auth-callback-host"}, + &cli.StringFlag{Name: "auth-microsoft-client-id"}, + &cli.StringFlag{Name: "auth-microsoft-client-secret"}, + &cli.StringFlag{Name: "session-secret"}, + }, + } + + err := init.Set("auth-callback-host", "http://localhost:8080/callback") + assert.NoError(t, err) + err = init.Set("auth-microsoft-client-id", "microsoftClientID") + assert.NoError(t, err) + err = init.Set("auth-microsoft-client-secret", "microsoftClientSecret") + assert.NoError(t, err) + err = init.Set("session-secret", "ThisIsNotASecureSessessionSecret") + assert.NoError(t, err) + + providerMap, err := configureAuthProvider(ctx, init, basePath) assert.Nil(t, err) assert.NotNil(t, providerMap) @@ -103,16 +157,31 @@ func TestConfigureAuthProviderMicrosoft(t *testing.T) { } func TestConfigureAuthProviderAzure(t *testing.T) { + ctx := t.Context() basePath := "/" - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("auth-callback-host", "http://localhost:8080/callback", "") - flagset.String("auth-azure-ad-tenant-id", "azureTenantID", "") - flagset.String("auth-azure-ad-client-id", "azureClientID", "") - flagset.String("auth-azure-ad-client-secret", "azureClientSecret", "") - flagset.String("session-secret", "ThisIsNotASecureSessessionSecret", "") - init := cli.NewContext(nil, flagset, nil) - providerMap, err := configureAuthProvider(init, basePath) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "auth-callback-host"}, + &cli.StringFlag{Name: "auth-azure-ad-tenant-id"}, + &cli.StringFlag{Name: "auth-azure-ad-client-id"}, + &cli.StringFlag{Name: "auth-azure-ad-client-secret"}, + &cli.StringFlag{Name: "session-secret"}, + }, + } + + err := init.Set("auth-callback-host", "http://localhost:8080/callback") + assert.NoError(t, err) + err = init.Set("auth-azure-ad-tenant-id", "azureTenantID") + assert.NoError(t, err) + err = init.Set("auth-azure-ad-client-id", "azureClientID") + assert.NoError(t, err) + err = init.Set("auth-azure-ad-client-secret", "azureClientSecret") + assert.NoError(t, err) + err = init.Set("session-secret", "ThisIsNotASecureSessessionSecret") + assert.NoError(t, err) + + providerMap, err := configureAuthProvider(ctx, init, basePath) assert.Nil(t, err) assert.NotNil(t, providerMap) @@ -127,15 +196,28 @@ func TestConfigureAuthProviderAzure(t *testing.T) { } func TestConfigureAuthProviderApple(t *testing.T) { + ctx := t.Context() basePath := "/" - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("auth-callback-host", "http://localhost:8080/callback", "") - flagset.String("auth-apple-client-id", "appleClientID", "") - flagset.String("auth-apple-client-secret", "appleClientSecret", "") - flagset.String("session-secret", "ThisIsNotASecureSessessionSecret", "") - init := cli.NewContext(nil, flagset, nil) - providerMap, err := configureAuthProvider(init, basePath) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "auth-callback-host"}, + &cli.StringFlag{Name: "auth-apple-client-id"}, + &cli.StringFlag{Name: "auth-apple-client-secret"}, + &cli.StringFlag{Name: "session-secret"}, + }, + } + + err := init.Set("auth-callback-host", "http://localhost:8080/callback") + assert.NoError(t, err) + err = init.Set("auth-apple-client-id", "appleClientID") + assert.NoError(t, err) + err = init.Set("auth-apple-client-secret", "appleClientSecret") + assert.NoError(t, err) + err = init.Set("session-secret", "ThisIsNotASecureSessessionSecret") + assert.NoError(t, err) + + providerMap, err := configureAuthProvider(ctx, init, basePath) assert.Nil(t, err) assert.NotNil(t, providerMap) @@ -149,16 +231,31 @@ func TestConfigureAuthProviderApple(t *testing.T) { } func TestConfigureAuthProviderOIDC(t *testing.T) { + ctx := t.Context() basePath := "/" - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("auth-callback-host", "http://localhost:8080/callback", "") - flagset.String("auth-oidc-discovery-url", "http://localhost:8070/.well-known/openid-configuration", "") - flagset.String("auth-oidc-client-id", "oidcClientID", "") - flagset.String("auth-oidc-client-secret", "oidcClientSecret", "") - flagset.String("session-secret", "ThisIsNotASecureSessessionSecret", "") - init := cli.NewContext(nil, flagset, nil) - providerMap, err := configureAuthProvider(init, basePath) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "auth-callback-host"}, + &cli.StringFlag{Name: "auth-oidc-discovery-url"}, + &cli.StringFlag{Name: "auth-oidc-client-id"}, + &cli.StringFlag{Name: "auth-oidc-client-secret"}, + &cli.StringFlag{Name: "session-secret"}, + }, + } + + err := init.Set("auth-callback-host", "http://localhost:8080/callback") + assert.NoError(t, err) + err = init.Set("auth-oidc-discovery-url", "http://localhost:8070/.well-known/openid-configuration") + assert.NoError(t, err) + err = init.Set("auth-oidc-client-id", "oidcClientID") + assert.NoError(t, err) + err = init.Set("auth-oidc-client-secret", "oidcClientSecret") + assert.NoError(t, err) + err = init.Set("session-secret", "ThisIsNotASecureSessessionSecret") + assert.NoError(t, err) + + providerMap, err := configureAuthProvider(ctx, init, basePath) assert.Nil(t, err) assert.NotNil(t, providerMap) @@ -175,27 +272,64 @@ func TestConfigureAuthProviderOIDC(t *testing.T) { } func TestConfigureAuthProvider(t *testing.T) { + ctx := t.Context() basePath := "/" - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("auth-callback-host", "http://localhost:8080/callback", "") - flagset.String("auth-google-client-id", "googleClientID", "") - flagset.String("auth-google-client-secret", "googleClientSecret", "") - flagset.String("auth-github-client-id", "githubClientID", "") - flagset.String("auth-github-client-secret", "githubClientSecret", "") - flagset.String("auth-microsoft-client-id", "microsoftClientID", "") - flagset.String("auth-microsoft-client-secret", "microsoftClientSecret", "") - flagset.String("auth-azure-ad-tenant-id", "azureTenantID", "") - flagset.String("auth-azure-ad-client-id", "azureClientID", "") - flagset.String("auth-azure-ad-client-secret", "azureClientSecret", "") - flagset.String("auth-apple-client-id", "appleClientID", "") - flagset.String("auth-apple-client-secret", "appleClientSecret", "") - flagset.String("auth-oidc-discovery-url", "http://localhost:8070/.well-known/openid-configuration", "") - flagset.String("auth-oidc-client-id", "oidcClientID", "") - flagset.String("auth-oidc-client-secret", "oidcClientSecret", "") - flagset.String("session-secret", "ThisIsNotASecureSessessionSecret", "") - init := cli.NewContext(nil, flagset, nil) - - providerMap, err := configureAuthProvider(init, basePath) + + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "auth-callback-host"}, + &cli.StringFlag{Name: "auth-google-client-id"}, + &cli.StringFlag{Name: "auth-google-client-secret"}, + &cli.StringFlag{Name: "auth-github-client-id"}, + &cli.StringFlag{Name: "auth-github-client-secret"}, + &cli.StringFlag{Name: "auth-microsoft-client-id"}, + &cli.StringFlag{Name: "auth-microsoft-client-secret"}, + &cli.StringFlag{Name: "auth-azure-ad-tenant-id"}, + &cli.StringFlag{Name: "auth-azure-ad-client-id"}, + &cli.StringFlag{Name: "auth-azure-ad-client-secret"}, + &cli.StringFlag{Name: "auth-apple-client-id"}, + &cli.StringFlag{Name: "auth-apple-client-secret"}, + &cli.StringFlag{Name: "auth-oidc-discovery-url"}, + &cli.StringFlag{Name: "auth-oidc-client-id"}, + &cli.StringFlag{Name: "auth-oidc-client-secret"}, + &cli.StringFlag{Name: "session-secret"}, + }, + } + + err := init.Set("auth-callback-host", "http://localhost:8080/callback") + assert.NoError(t, err) + err = init.Set("auth-google-client-id", "googleClientID") + assert.NoError(t, err) + err = init.Set("auth-google-client-secret", "googleClientSecret") + assert.NoError(t, err) + err = init.Set("auth-github-client-id", "githubClientID") + assert.NoError(t, err) + err = init.Set("auth-github-client-secret", "githubClientSecret") + assert.NoError(t, err) + err = init.Set("auth-microsoft-client-id", "microsoftClientID") + assert.NoError(t, err) + err = init.Set("auth-microsoft-client-secret", "microsoftClientSecret") + assert.NoError(t, err) + err = init.Set("auth-azure-ad-tenant-id", "azureTenantID") + assert.NoError(t, err) + err = init.Set("auth-azure-ad-client-id", "azureClientID") + assert.NoError(t, err) + err = init.Set("auth-azure-ad-client-secret", "azureClientSecret") + assert.NoError(t, err) + err = init.Set("auth-apple-client-id", "appleClientID") + assert.NoError(t, err) + err = init.Set("auth-apple-client-secret", "appleClientSecret") + assert.NoError(t, err) + err = init.Set("auth-oidc-discovery-url", "http://localhost:8070/.well-known/openid-configuration") + assert.NoError(t, err) + err = init.Set("auth-oidc-client-id", "oidcClientID") + assert.NoError(t, err) + err = init.Set("auth-oidc-client-secret", "oidcClientSecret") + assert.NoError(t, err) + err = init.Set("session-secret", "ThisIsNotASecureSessessionSecret") + assert.NoError(t, err) + + providerMap, err := configureAuthProvider(ctx, init, basePath) assert.Nil(t, err) assert.NotNil(t, providerMap) diff --git a/server/src/cache/cache.go b/server/src/cache/cache.go index 963d807219..e427b96c36 100644 --- a/server/src/cache/cache.go +++ b/server/src/cache/cache.go @@ -5,7 +5,7 @@ import ( "errors" "time" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" "scrumlr.io/server/logger" @@ -24,16 +24,16 @@ type Cache struct { Con Client } -func InitializeCache(ctx *cli.Context) (*Cache, error) { - log := logger.FromContext(ctx.Context) +func InitializeCache(ctx context.Context, cli *cli.Command) (*Cache, error) { + log := logger.FromContext(ctx) var cache *Cache - if ctx.String("redis-address") != "" { + if cli.String("redis-address") != "" { redis := RedisServer{ - Addr: ctx.String("redis-address"), - Username: ctx.String("redis-username"), - Password: ctx.String("redis-password"), + Addr: cli.String("redis-address"), + Username: cli.String("redis-username"), + Password: cli.String("redis-password"), } log.Infof("Connecting to redis at %v as cache", redis.Addr) @@ -42,8 +42,8 @@ func InitializeCache(ctx *cli.Context) (*Cache, error) { return cache, err } - if ctx.String("nats") != "" { - address := ctx.String("nats") + if cli.String("nats") != "" { + address := cli.String("nats") log.Infof("Connecting to nats at %v as cache", address) diff --git a/server/src/cache/cache_test.go b/server/src/cache/cache_test.go index 10d94da2a9..3f414a1268 100644 --- a/server/src/cache/cache_test.go +++ b/server/src/cache/cache_test.go @@ -2,28 +2,31 @@ package cache import ( "errors" - "flag" "testing" "github.com/stretchr/testify/assert" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" "scrumlr.io/server/initialize" ) func TestInitializeCache_Nats(t *testing.T) { + ctx := t.Context() + container, connection := initialize.StartTestNats() - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("redis-address", "", "") - flagset.String("redis-usernamet", "", "") - flagset.String("redis-password", "", "") - flagset.String("nats", "", "") - init := cli.NewContext(nil, flagset, nil) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "redis-address"}, + &cli.StringFlag{Name: "redis-username"}, + &cli.StringFlag{Name: "redis-password"}, + &cli.StringFlag{Name: "nats"}, + }, + } err := init.Set("nats", connection) assert.NoError(t, err) - cache, err := InitializeCache(init) + cache, err := InitializeCache(ctx, init) assert.NoError(t, err) assert.NotNil(t, cache) @@ -33,19 +36,23 @@ func TestInitializeCache_Nats(t *testing.T) { } func TestInitializeCache_Redis(t *testing.T) { + ctx := t.Context() + container, connection := initialize.StartTestRedis() - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("redis-address", "", "") - flagset.String("redis-usernamet", "", "") - flagset.String("redis-password", "", "") - flagset.String("nats", "", "") - init := cli.NewContext(nil, flagset, nil) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "redis-address"}, + &cli.StringFlag{Name: "redis-username"}, + &cli.StringFlag{Name: "redis-password"}, + &cli.StringFlag{Name: "nats"}, + }, + } err := init.Set("redis-address", connection) assert.NoError(t, err) - cache, err := InitializeCache(init) + cache, err := InitializeCache(ctx, init) assert.NoError(t, err) assert.NotNil(t, cache) @@ -55,14 +62,18 @@ func TestInitializeCache_Redis(t *testing.T) { } func TestInitializeCache_NotConfigured(t *testing.T) { - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("redis-address", "", "") - flagset.String("redis-usernamet", "", "") - flagset.String("redis-password", "", "") - flagset.String("nats", "", "") - init := cli.NewContext(nil, flagset, nil) + ctx := t.Context() - cache, err := InitializeCache(init) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "redis-address"}, + &cli.StringFlag{Name: "redis-username"}, + &cli.StringFlag{Name: "redis-password"}, + &cli.StringFlag{Name: "nats"}, + }, + } + + cache, err := InitializeCache(ctx, init) assert.Error(t, err) assert.Equal(t, errors.New("no valid cache configuration found"), err) @@ -70,21 +81,25 @@ func TestInitializeCache_NotConfigured(t *testing.T) { } func TestInitializeCache_PrefereRedis(t *testing.T) { + ctx := t.Context() + container, connection := initialize.StartTestRedis() - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("redis-address", "", "") - flagset.String("redis-usernamet", "", "") - flagset.String("redis-password", "", "") - flagset.String("nats", "", "") - init := cli.NewContext(nil, flagset, nil) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "redis-address"}, + &cli.StringFlag{Name: "redis-username"}, + &cli.StringFlag{Name: "redis-password"}, + &cli.StringFlag{Name: "nats"}, + }, + } err := init.Set("redis-address", connection) assert.NoError(t, err) err = init.Set("nats", "not valide connection") assert.NoError(t, err) - cache, err := InitializeCache(init) + cache, err := InitializeCache(ctx, init) assert.NoError(t, err) assert.NotNil(t, cache) diff --git a/server/src/go.mod b/server/src/go.mod index beb6336d93..1d634b87ff 100644 --- a/server/src/go.mod +++ b/server/src/go.mod @@ -30,7 +30,8 @@ require ( github.com/uptrace/bun/dialect/pgdialect v1.2.18 github.com/uptrace/bun/extra/bundebug v1.2.18 github.com/uptrace/bun/extra/bunotel v1.2.18 - github.com/urfave/cli/v2 v2.27.7 + github.com/urfave/cli-altsrc/v3 v3.1.0 + github.com/urfave/cli/v3 v3.10.1 github.com/weppos/publicsuffix-go v0.50.3 go.opentelemetry.io/contrib/bridges/otelzap v0.20.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 @@ -68,7 +69,6 @@ require ( github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-connections v0.7.0 // indirect @@ -133,7 +133,6 @@ require ( github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect github.com/redis/go-redis/extra/rediscmd/v9 v9.22.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/segmentio/asm v1.2.1 // indirect github.com/shirou/gopsutil/v4 v4.26.6 // indirect github.com/sirupsen/logrus v1.9.4 // indirect @@ -146,7 +145,6 @@ require ( github.com/valyala/fastjson v1.6.10 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zeebo/xxh3 v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect diff --git a/server/src/go.sum b/server/src/go.sum index 5640474649..7bdd8ddb31 100644 --- a/server/src/go.sum +++ b/server/src/go.sum @@ -38,8 +38,6 @@ github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpS github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= -github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= -github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -245,8 +243,6 @@ github.com/redis/go-redis/v9 v9.22.0 h1:laDvpYXTJtZLloinw1fA5Kqd6HAEH2XKxOkG/PDq github.com/redis/go-redis/v9 v9.22.0/go.mod h1:y2g0Wj8rQvuK0ELM+oxSudcLtC09JScs98I/X9gRWY4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= @@ -292,8 +288,10 @@ github.com/uptrace/bun/extra/bunotel v1.2.18 h1:idfBT+IJGOLSwqkNv+Yiw4fG0tgasXat github.com/uptrace/bun/extra/bunotel v1.2.18/go.mod h1:IdnKewPjPXZQHyap29M9PM2l+f0u5fLONeW90bAat88= github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 h1:ZjUj9BLYf9PEqBn8W/OapxhPjVRdC6CsXTdULHsyk5c= github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2/go.mod h1:O8bHQfyinKwTXKkiKNGmLQS7vRsqRxIQTFZpYpHK3IQ= -github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= -github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= +github.com/urfave/cli-altsrc/v3 v3.1.0 h1:6E5+kXeAWmRxXlPgdEVf9VqVoTJ2MJci0UMpUi/w/bA= +github.com/urfave/cli-altsrc/v3 v3.1.0/go.mod h1:VcWVTGXcL3nrXUDJZagHAeUX702La3PKeWav7KpISqA= +github.com/urfave/cli/v3 v3.10.1 h1:7Kx9H50hrHbRbyxgO1KP6/BcbiGRz0uYh5YyQ30JEEY= +github.com/urfave/cli/v3 v3.10.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4= github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= @@ -302,8 +300,6 @@ github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAh github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/weppos/publicsuffix-go v0.50.3 h1:eT5dcjHQcVDNc0igpFEsGHKIip30feuB2zuuI9eJxiE= github.com/weppos/publicsuffix-go v0.50.3/go.mod h1:/rOa781xBykZhHK/I3QeHo92qdDKVmKZKF7s8qAEM/4= -github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= -github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= diff --git a/server/src/initialize/database.go b/server/src/initialize/database.go index 949ce5379c..9fef3b2755 100644 --- a/server/src/initialize/database.go +++ b/server/src/initialize/database.go @@ -1,6 +1,7 @@ package initialize import ( + "context" "database/sql" "embed" "errors" @@ -15,7 +16,7 @@ import ( "github.com/uptrace/bun/dialect/pgdialect" "github.com/uptrace/bun/extra/bundebug" "github.com/uptrace/bun/extra/bunotel" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" @@ -30,14 +31,14 @@ var Migrations embed.FS var traceProvider trace.TracerProvider = otel.GetTracerProvider() var meterProvider metric.MeterProvider = otel.GetMeterProvider() -func InitializeDatabase(ctx *cli.Context) (*bun.DB, error) { - log := logger.FromContext(ctx.Context) +func InitializeDatabase(ctx context.Context, cli *cli.Command) (*bun.DB, error) { + log := logger.FromContext(ctx) var databaseUrl string - if ctx.String("database") != "" { - databaseUrl = ctx.String("database") - } else if ctx.String("database-host") != "" && ctx.String("database-username") != "" && ctx.String("database-password") != "" { - databaseUrl = fmt.Sprintf("postgresql://%s:%s@%s", ctx.String("database-username"), ctx.String("database-password"), ctx.String("database-host")) + if cli.String("database") != "" { + databaseUrl = cli.String("database") + } else if cli.String("database-host") != "" && cli.String("database-username") != "" && cli.String("database-password") != "" { + databaseUrl = fmt.Sprintf("postgresql://%s:%s@%s", cli.String("database-username"), cli.String("database-password"), cli.String("database-host")) } else { return nil, errors.New("no valid database connection found") } diff --git a/server/src/initialize/database_test.go b/server/src/initialize/database_test.go index 4775b7dd4e..04fc44476d 100644 --- a/server/src/initialize/database_test.go +++ b/server/src/initialize/database_test.go @@ -3,14 +3,13 @@ package initialize import ( "context" "errors" - "flag" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/postgres" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) const POSTGRES_IMAGE = "postgres:18.4-alpine" @@ -37,17 +36,19 @@ func TestInitializeDatabase_DatabaseUrl(t *testing.T) { host, err := container.Host(ctx) assert.NoError(t, err, "failed to get host from container") - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("database", "", "") - flagset.String("database-host", "", "") - flagset.String("database-username", "", "") - flagset.String("database-password", "", "") - init := cli.NewContext(nil, flagset, nil) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "database"}, + &cli.StringFlag{Name: "database-host"}, + &cli.StringFlag{Name: "database-username"}, + &cli.StringFlag{Name: "database-password"}, + }, + } err = init.Set("database", fmt.Sprintf("postgresql://%s:%s@%s:%s/%s?sslmode=disable", DATABASE_USERNAME, DATABASE_PASSWORD, host, mappedPort.Port(), DATABASE_NAME)) assert.NoError(t, err) - bun, err := InitializeDatabase(init) + bun, err := InitializeDatabase(ctx, init) assert.NoError(t, err) assert.NotNil(t, bun) @@ -77,12 +78,14 @@ func TestInitializeDatabase_DatabaseParameter(t *testing.T) { host, err := container.Host(ctx) assert.NoError(t, err, "failed to get host from container") - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("database", "", "") - flagset.String("database-host", "", "") - flagset.String("database-username", "", "") - flagset.String("database-password", "", "") - init := cli.NewContext(nil, flagset, nil) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "database"}, + &cli.StringFlag{Name: "database-host"}, + &cli.StringFlag{Name: "database-username"}, + &cli.StringFlag{Name: "database-password"}, + }, + } err = init.Set("database-host", fmt.Sprintf("%s:%s/%s?sslmode=disable", host, mappedPort.Port(), DATABASE_NAME)) assert.NoError(t, err) @@ -91,7 +94,7 @@ func TestInitializeDatabase_DatabaseParameter(t *testing.T) { err = init.Set("database-password", DATABASE_PASSWORD) assert.NoError(t, err) - bun, err := InitializeDatabase(init) + bun, err := InitializeDatabase(ctx, init) assert.NoError(t, err) assert.NotNil(t, bun) @@ -115,14 +118,16 @@ func TestInitializeDatabase_NotConfigured(t *testing.T) { assert.NoError(t, err, "failed to create test container") - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("database", "", "") - flagset.String("database-host", "", "") - flagset.String("database-username", "", "") - flagset.String("database-password", "", "") - init := cli.NewContext(nil, flagset, nil) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "database"}, + &cli.StringFlag{Name: "database-host"}, + &cli.StringFlag{Name: "database-username"}, + &cli.StringFlag{Name: "database-password"}, + }, + } - bun, err := InitializeDatabase(init) + bun, err := InitializeDatabase(ctx, init) assert.Error(t, err) assert.Equal(t, errors.New("no valid database connection found"), err) @@ -152,12 +157,14 @@ func TestInitializeDatabase_PrefereDatabaseUrl(t *testing.T) { host, err := container.Host(ctx) assert.NoError(t, err, "failed to get host from container") - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("database", "", "") - flagset.String("database-host", "", "") - flagset.String("database-username", "", "") - flagset.String("database-password", "", "") - init := cli.NewContext(nil, flagset, nil) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "database"}, + &cli.StringFlag{Name: "database-host"}, + &cli.StringFlag{Name: "database-username"}, + &cli.StringFlag{Name: "database-password"}, + }, + } err = init.Set("database", fmt.Sprintf("postgresql://%s:%s@%s:%s/%s?sslmode=disable", DATABASE_USERNAME, DATABASE_PASSWORD, host, mappedPort.Port(), DATABASE_NAME)) assert.NoError(t, err) @@ -168,7 +175,7 @@ func TestInitializeDatabase_PrefereDatabaseUrl(t *testing.T) { err = init.Set("database-password", DATABASE_PASSWORD) assert.NoError(t, err) - bun, err := InitializeDatabase(init) + bun, err := InitializeDatabase(ctx, init) assert.NoError(t, err) assert.NotNil(t, bun) diff --git a/server/src/main.go b/server/src/main.go index 3b72d39d72..b04bbb075c 100644 --- a/server/src/main.go +++ b/server/src/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "errors" "fmt" "log" @@ -17,8 +18,9 @@ import ( "scrumlr.io/server/auth" - "github.com/urfave/cli/v2" - "github.com/urfave/cli/v2/altsrc" + altsrc "github.com/urfave/cli-altsrc/v3" + "github.com/urfave/cli-altsrc/v3/toml" + "github.com/urfave/cli/v3" "scrumlr.io/server/logger" "scrumlr.io/server/realtime" ) @@ -31,337 +33,467 @@ import ( // @license.name MIT // @license.url https://github.com/inovex/scrumlr.io/blob/main/LICENSE func main() { - app := &cli.App{ + var tomlconfigFile string + + app := &cli.Command{ Name: "scrumlr.io", Usage: "Awesome & scalable server for the scrumlr.io web application", - HelpName: "scrumlr", UsageText: "scrumlr [global options]", Action: run, Flags: []cli.Flag{ - altsrc.NewIntFlag(&cli.IntFlag{ + &cli.IntFlag{ Name: "port", Aliases: []string{"p"}, - EnvVars: []string{"SCRUMLR_SERVER_PORT"}, - Usage: "the `port` of the server to launch", - Value: 8080, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "address", - EnvVars: []string{"SCRUMLR_SERVER_LISTEN_ADDRESS"}, - Usage: "the `address` on which the server listens", - Value: "", - }), - altsrc.NewStringFlag(&cli.StringFlag{ + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_SERVER_PORT"), + toml.TOML("port", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "the `port` of the server to launch", + Value: 8080, + }, + &cli.StringFlag{ + Name: "address", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_SERVER_LISTEN_ADDRESS"), + toml.TOML("address", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "the `address` on which the server listens", + Value: "", + }, + &cli.StringFlag{ Name: "nats", Aliases: []string{"n"}, - EnvVars: []string{"SCRUMLR_SERVER_NATS_URL"}, - Usage: "the `url` of the nats server", - Value: "nats://localhost:4222", // nats://nats:4222 - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "redis-address", - EnvVars: []string{"SCRUMLR_SERVER_REDIS_HOST"}, - Usage: "the `address` of the redis server. Example `localhost:6379`. If redis-address is set, it's used over the default nats", - Value: "", - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "redis-username", - EnvVars: []string{"SCRUMLR_SERVER_REDIS_USERNAME"}, - Usage: "the redis user (if required)", - Value: "", - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "redis-password", - EnvVars: []string{"SCRUMLR_SERVER_REDIS_PASSWORD"}, - Usage: "the redis password (if required)", - Value: "", - }), - altsrc.NewBoolFlag(&cli.BoolFlag{ + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_SERVER_NATS_URL"), + toml.TOML("nats", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "the `url` of the nats server", + Value: "nats://localhost:4222", // nats://nats:4222 + }, + &cli.StringFlag{ + Name: "redis-address", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_SERVER_REDIS_HOST"), + toml.TOML("redis-address", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "the `address` of the redis server. Example `localhost:6379`. If redis-address is set, it's used over the default nats", + Value: "", + }, + &cli.StringFlag{ + Name: "redis-username", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_SERVER_REDIS_USERNAME"), + toml.TOML("redis-username", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "the redis user (if required)", + Value: "", + }, + &cli.StringFlag{ + Name: "redis-password", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_SERVER_REDIS_PASSWORD"), + toml.TOML("redis-password", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "the redis password (if required)", + Value: "", + }, + &cli.BoolFlag{ Name: "insecure", Aliases: []string{"i"}, - EnvVars: []string{"SCRUMLR_INSECURE"}, - Usage: "use default and embedded key to sign jwt's", - Value: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "unsafe-key", - EnvVars: []string{"SCRUMLR_UNSAFE_PRIVATE_KEY"}, - Usage: "the private key which should be replaced by the new key, that'll be used to sign the jwt's - needed in ES512 (ecdsa)", - Value: "", - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "key", - EnvVars: []string{"SCRUMLR_PRIVATE_KEY"}, - Usage: "the private key, used to sign the jwt's - needed in ES512 (ecdsa)", - }), - altsrc.NewStringFlag(&cli.StringFlag{ + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_INSECURE"), + toml.TOML("insecure", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "use default and embedded key to sign jwt's", + Value: false, + }, + &cli.StringFlag{ + Name: "unsafe-key", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_UNSAFE_PRIVATE_KEY"), + toml.TOML("unsafe-key", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "the private key which should be replaced by the new key, that'll be used to sign the jwt's - needed in ES512 (ecdsa)", + Value: "", + }, + &cli.StringFlag{ + Name: "key", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_PRIVATE_KEY"), + toml.TOML("key", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "the private key, used to sign the jwt's - needed in ES512 (ecdsa)", + }, + &cli.StringFlag{ Name: "database", Aliases: []string{"d"}, - EnvVars: []string{"SCRUMLR_SERVER_DATABASE_URL"}, - Usage: "the complete connection `url` for the database, e.g. `postgresql://YourUserName:YourPassword:@YourHostname:5432/YourDatabaseName?sslmode=disable`", - Value: "", - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "database-host", - EnvVars: []string{"SCRUMLR_SERVER_DATABASE_HOST"}, - Usage: "the host name of the database with port e.g. `YourHostName:5432/YourDatabaseName?sslmode=disable`. The host name will be combined with the username and password to form the connection url", - Value: "", - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "database-username", - EnvVars: []string{"SCRUMLR_SERVER_DATABASE_USER"}, - Usage: "the user name for the database connection. The username name will be combined with the host and password to form the connection url", - Value: "", - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "database-password", - EnvVars: []string{"SCRUMLR_SERVER_DATABASE_PASSWORD"}, - Usage: "the password for the database connection. The password will be combined with the host and username to form the connection url", - Value: "", - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "base-path", - Aliases: []string{"b"}, - EnvVars: []string{"SCRUMLR_BASE_PATH"}, + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_SERVER_DATABASE_URL"), + toml.TOML("database", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "the complete connection `url` for the database, e.g. `postgresql://YourUserName:YourPassword:@YourHostname:5432/YourDatabaseName?sslmode=disable`", + Value: "", + }, + &cli.StringFlag{ + Name: "database-host", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_SERVER_DATABASE_HOST"), + toml.TOML("database-host", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "the host name of the database with port e.g. `YourHostName:5432/YourDatabaseName?sslmode=disable`. The host name will be combined with the username and password to form the connection url", + Value: "", + }, + &cli.StringFlag{ + Name: "database-username", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_SERVER_DATABASE_USER"), + toml.TOML("database-username", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "the user name for the database connection. The username name will be combined with the host and password to form the connection url", + Value: "", + }, + &cli.StringFlag{ + Name: "database-password", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_SERVER_DATABASE_PASSWORD"), + toml.TOML("database-password", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "the password for the database connection. The password will be combined with the host and username to form the connection url", + Value: "", + }, + &cli.StringFlag{ + Name: "base-path", + Aliases: []string{"b"}, + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_BASE_PATH"), + toml.TOML("base-path", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the base `path` of the application (e.g. '/api'); must start with '/'", Required: false, Value: "/", - }), - altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: "disable-anonymous-login", - EnvVars: []string{"SCRUMLR_DISABLE_ANONYMOUS_LOGIN"}, + }, + &cli.BoolFlag{ + Name: "disable-anonymous-login", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_DISABLE_ANONYMOUS_LOGIN"), + toml.TOML("disable-anonymous-login", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "enables/disables the login of anonymous clients", Required: false, Value: false, - }), - altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: "allow-anonymous-custom-templates", - EnvVars: []string{"SCRUMLR_ALLOW_ANONYMOUS_CUSTOM_TEMPLATES"}, + }, + &cli.BoolFlag{ + Name: "allow-anonymous-custom-templates", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_ALLOW_ANONYMOUS_CUSTOM_TEMPLATES"), + toml.TOML("allow-anonymous-custom-templates", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "allows custom templates to be used for anonymous clients", Required: false, Value: false, - }), - altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: "allow-anonymous-board-creation", - EnvVars: []string{"SCRUMLR_ALLOW_ANONYMOUS_BOARD_CREATION"}, + }, + &cli.BoolFlag{ + Name: "allow-anonymous-board-creation", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_ALLOW_ANONYMOUS_BOARD_CREATION"), + toml.TOML("allow-anonymous-board-creation", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "allows anonymous clients to create new boards", Required: false, Value: true, - }), - altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: "allow-anonymous-history", - EnvVars: []string{"SCRUMLR_ALLOW_ANONYMOUS_HISTORY"}, + }, + &cli.BoolFlag{ + Name: "allow-anonymous-history", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_ALLOW_ANONYMOUS_HISTORY"), + toml.TOML("allow-anonymous-history", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "allows anonymous clients to view their history", Required: false, Value: false, - }), - altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: "auth-enable-experimental-file-system-store", - EnvVars: []string{"SCRUMLR_ENABLE_EXPERIMENTAL_AUTH_FILE_SYSTEM_STORE"}, + }, + &cli.BoolFlag{ + Name: "auth-enable-experimental-file-system-store", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_ENABLE_EXPERIMENTAL_AUTH_FILE_SYSTEM_STORE"), + toml.TOML("auth-enable-experimental-file-system-store", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "enables/disables experimental file system store, in order to allow larger session cookie sizes", Required: false, Value: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-callback-host", - Aliases: []string{"c"}, - EnvVars: []string{"SCRUMLR_AUTH_CALLBACK_HOST"}, + }, + &cli.StringFlag{ + Name: "auth-callback-host", + Aliases: []string{"c"}, + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_CALLBACK_HOST"), + toml.TOML("auth-callback-host", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the protocol and host for the auth provider callbacks (e.g. https://scrumlr.io)", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-google-client-id", - EnvVars: []string{"SCRUMLR_AUTH_GOOGLE_CLIENT_ID"}, + }, + &cli.StringFlag{ + Name: "auth-google-client-id", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_GOOGLE_CLIENT_ID"), + toml.TOML("auth-google-client-id", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the client `id` for Google", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-google-client-secret", - EnvVars: []string{"SCRUMLR_AUTH_GOOGLE_CLIENT_SECRET"}, + }, + &cli.StringFlag{ + Name: "auth-google-client-secret", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_GOOGLE_CLIENT_SECRET"), + toml.TOML("auth-google-client-secret", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the client `secret` for Google", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-github-client-id", - EnvVars: []string{"SCRUMLR_AUTH_GITHUB_CLIENT_ID"}, + }, + &cli.StringFlag{ + Name: "auth-github-client-id", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_GITHUB_CLIENT_ID"), + toml.TOML("auth-github-client-id", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the client `id` for GitHub", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-github-client-secret", - EnvVars: []string{"SCRUMLR_AUTH_GITHUB_CLIENT_SECRET"}, + }, + &cli.StringFlag{ + Name: "auth-github-client-secret", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_GITHUB_CLIENT_SECRET"), + toml.TOML("auth-github-client-secret", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the client `secret` for GitHub", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-microsoft-client-id", - EnvVars: []string{"SCRUMLR_AUTH_MICROSOFT_CLIENT_ID"}, + }, + &cli.StringFlag{ + Name: "auth-microsoft-client-id", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_MICROSOFT_CLIENT_ID"), + toml.TOML("auth-microsoft-client-id", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the client `id` for Microsoft", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-microsoft-client-secret", - EnvVars: []string{"SCRUMLR_AUTH_MICROSOFT_CLIENT_SECRET"}, + }, + &cli.StringFlag{ + Name: "auth-microsoft-client-secret", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_MICROSOFT_CLIENT_SECRET"), + toml.TOML("auth-microsoft-client-secret", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the client `secret` for Microsoft", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-azure-ad-tenant-id", - EnvVars: []string{"SCRUMLR_AUTH_AZURE_AD_TENANT_ID"}, + }, + &cli.StringFlag{ + Name: "auth-azure-ad-tenant-id", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_AZURE_AD_TENANT_ID"), + toml.TOML("auth-azure-ad-tenant-id", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the tenant `id` for Azure AD", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-azure-ad-client-id", - EnvVars: []string{"SCRUMLR_AUTH_AZURE_AD_CLIENT_ID"}, + }, + &cli.StringFlag{ + Name: "auth-azure-ad-client-id", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_AZURE_AD_CLIENT_ID"), + toml.TOML("auth-azure-ad-client-id", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the client `id` for Azure AD", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-azure-ad-client-secret", - EnvVars: []string{"SCRUMLR_AUTH_AZURE_AD_CLIENT_SECRET"}, + }, + &cli.StringFlag{ + Name: "auth-azure-ad-client-secret", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_AZURE_AD_CLIENT_SECRET"), + toml.TOML("auth-azure-ad-client-secret", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the client `secret` for Azure AD", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-apple-client-id", - EnvVars: []string{"SCRUMLR_AUTH_APPLE_CLIENT_ID"}, + }, + &cli.StringFlag{ + Name: "auth-apple-client-id", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_APPLE_CLIENT_ID"), + toml.TOML("auth-apple-client-id", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the client `id` for Apple", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-apple-client-secret", - EnvVars: []string{"SCRUMLR_AUTH_APPLE_CLIENT_SECRET"}, + }, + &cli.StringFlag{ + Name: "auth-apple-client-secret", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_APPLE_CLIENT_SECRET"), + toml.TOML("auth-apple-client-secret", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the client `secret` for Apple", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-oidc-client-id", - EnvVars: []string{"SCRUMLR_AUTH_OIDC_CLIENT_ID"}, + }, + &cli.StringFlag{ + Name: "auth-oidc-client-id", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_OIDC_CLIENT_ID"), + toml.TOML("auth-oidc-client-id", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the client `id` for OpenID Connect", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-oidc-client-secret", - EnvVars: []string{"SCRUMLR_AUTH_OIDC_CLIENT_SECRET"}, + }, + &cli.StringFlag{ + Name: "auth-oidc-client-secret", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_OIDC_CLIENT_SECRET"), + toml.TOML("auth-oidc-client-secret", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the client `secret` for OpenID Connect", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-oidc-discovery-url", - EnvVars: []string{"SCRUMLR_AUTH_OIDC_DISCOVERY_URL"}, + }, + &cli.StringFlag{ + Name: "auth-oidc-discovery-url", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_OIDC_DISCOVERY_URL"), + toml.TOML("auth-oidc-discovery-url", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "URL hosting the OIDC discovery document", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-oidc-user-ident-scope", - EnvVars: []string{"SCRUMLR_AUTH_OIDC_USER_IDENT_SCOPE"}, - Usage: "JWT claim to request for the user identifier", - Value: "openid", - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "auth-oidc-user-name-scope", - EnvVars: []string{"SCRUMLR_AUTH_OIDC_USER_NAME_SCOPE"}, - Usage: "JWT claim to request for the user name", - Value: "profile", - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "session-secret", - EnvVars: []string{"SESSION_SECRET"}, + }, + &cli.StringFlag{ + Name: "auth-oidc-user-ident-scope", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_OIDC_USER_IDENT_SCOPE"), + toml.TOML("auth-oidc-user-ident-scope", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "JWT claim to request for the user identifier", + Value: "openid", + }, + &cli.StringFlag{ + Name: "auth-oidc-user-name-scope", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_AUTH_OIDC_USER_NAME_SCOPE"), + toml.TOML("auth-oidc-user-name-scope", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), + Usage: "JWT claim to request for the user name", + Value: "profile", + }, + &cli.StringFlag{ + Name: "session-secret", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SESSION_SECRET"), + toml.TOML("session-secret", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "Session secret for the authentication provider. Must be provided if an authentication provider is used.", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "otel-grpc", - EnvVars: []string{"SCRUMLR_OTEL_GRPC"}, + }, + &cli.StringFlag{ + Name: "otel-grpc", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_OTEL_GRPC"), + toml.TOML("otel-grpc", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "grpc connection string for an OpenTelemetry collector", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "otel-http", - EnvVars: []string{"SCRUMLR_OTEL_HTTP"}, + }, + &cli.StringFlag{ + Name: "otel-http", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_OTEL_HTTP"), + toml.TOML("otel-http", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "http connection string for an OpenTelemetry collector", Required: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "log-level", - EnvVars: []string{"SCRUMLR_LOG_LEVEL"}, + }, + &cli.StringFlag{ + Name: "log-level", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_LOG_LEVEL"), + toml.TOML("log-level", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Aliases: []string{"l"}, Usage: "Log level for the logger. Can be one of 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'. Defaults to INFO.", Required: false, Value: "INFO", - }), - altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: "disable-check-origin", + }, + &cli.BoolFlag{ + Name: "disable-check-origin", + Sources: cli.NewValueSourceChain( + toml.TOML("disable-check-origin", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "disable check origin (strongly suggestion to only use this for development)", Value: false, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "feedback-webhook-url", - EnvVars: []string{"SCRUMLR_FEEDBACK_WEBHOOK_URL"}, + }, + &cli.StringFlag{ + Name: "feedback-webhook-url", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_FEEDBACK_WEBHOOK_URL"), + toml.TOML("feedback-webhook-url", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "the url where feedback will be sent to", Required: false, - }), - altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: "enable-swagger", - EnvVars: []string{"SCRUMLR_ENABLE_SWAGGER"}, + }, + &cli.BoolFlag{ + Name: "enable-swagger", + Sources: cli.NewValueSourceChain( + cli.EnvVar("SCRUMLR_ENABLE_SWAGGER"), + toml.TOML("enable-swagger", altsrc.NewStringPtrSourcer(&tomlconfigFile)), + ), Usage: "enable the swagger page", Value: false, Required: false, - }), + }, &cli.StringFlag{ - Name: "config", - EnvVars: []string{"SCRUMLR_CONFIG_PATH"}, - Usage: "TOML `filepath` to be loaded ", - Required: false, + Name: "config", + Sources: cli.EnvVars("SCRUMLR_CONFIG_PATH"), + Usage: "TOML `filepath` to be loaded ", + Required: false, + Destination: &tomlconfigFile, }, }, } - app.Before = altsrc.InitInputSourceWithContext(app.Flags, altsrc.NewTomlSourceFromFlagFunc("config")) - if err := app.Run(os.Args); err != nil { + if err := app.Run(context.Background(), os.Args); err != nil { log.Fatal(err) } } -func run(ctx *cli.Context) error { - logger.SetLogLevel(ctx.String("log-level")) - log := logger.FromContext(ctx.Context) +func run(ctx context.Context, cli *cli.Command) error { + logger.SetLogLevel(cli.String("log-level")) + log := logger.FromContext(ctx) - otelShutdown, err := initialize.SetupOTelSDK(ctx.Context, ctx.String("otel-grpc"), ctx.String("otel-http")) + otelShutdown, err := initialize.SetupOTelSDK(ctx, cli.String("otel-grpc"), cli.String("otel-http")) if err != nil { log.Errorf("failed to setup OpenTelemetry: %w", err) return err } defer func() { - err = errors.Join(err, otelShutdown(ctx.Context)) + err = errors.Join(err, otelShutdown(ctx)) }() - db, err := initialize.InitializeDatabase(ctx) + db, err := initialize.InitializeDatabase(ctx, cli) if err != nil { log.Errorf("failed to initialize database: %w", err) return err } - if !ctx.Bool("insecure") && ctx.String("key") == "" { + if !cli.Bool("insecure") && cli.String("key") == "" { return errors.New("you may not start the application without a private key. Use 'insecure' flag with caution if you want to use default keypair to sign jwt's") } - rt, err := realtime.InitializeRealtime(ctx) + rt, err := realtime.InitializeRealtime(ctx, cli) if err != nil { log.Fatalf("failed to connect to message broker: %v", err) return err } - c, err := cache.InitializeCache(ctx) + c, err := cache.InitializeCache(ctx, cli) if err != nil { log.Fatalf("failed to connect to cache: %v", err) return err } basePath := "/" - if ctx.IsSet("base-path") { - basePath = ctx.String("base-path") + if cli.IsSet("base-path") { + basePath = cli.String("base-path") if !strings.HasPrefix(basePath, "/") { return errors.New("base path must start with '/'") } @@ -371,7 +503,7 @@ func run(ctx *cli.Context) error { } } - providersMap, err := configureAuthProvider(ctx, basePath) + providersMap, err := configureAuthProvider(ctx, cli, basePath) if err != nil { log.Fatalf("failed to configure auth provider: %v", err) return err @@ -381,7 +513,7 @@ func run(ctx *cli.Context) error { wsService := initializer.InitializeWebSocketService() websocket := initializer.InitializeSessionRequestWebsocket(wsService) - feedbackService := initializer.InitializeFeedbackService(ctx.String("feedback-webhook-url")) + feedbackService := initializer.InitializeFeedbackService(cli.String("feedback-webhook-url")) healthService := initializer.InitializeHealthService() boardReactionService := initializer.InitializeBoardReactionService() @@ -399,8 +531,8 @@ func run(ctx *cli.Context) error { userService := initializer.InitializeUserService(sessionService, noteService) - keyWithNewlines := strings.ReplaceAll(ctx.String("key"), "\\n", "\n") - unsafeKeyWithNewlines := strings.ReplaceAll(ctx.String("unsafe-key"), "\\n", "\n") + keyWithNewlines := strings.ReplaceAll(cli.String("key"), "\\n", "\n") + unsafeKeyWithNewlines := strings.ReplaceAll(cli.String("unsafe-key"), "\\n", "\n") authConfig, err := auth.NewAuthConfiguration(providersMap, unsafeKeyWithNewlines, keyWithNewlines, db, userService) if err != nil { return fmt.Errorf("unable to setup authentication: %w", err) @@ -410,7 +542,7 @@ func run(ctx *cli.Context) error { apiInitializer := serviceinitialize.NewApiInitializer(basePath) sessionApi := apiInitializer.InitializeSessionApi(sessionService) - userApi := apiInitializer.InitializeUserApi(userService, sessionService, ctx.Bool("allow-anonymous-board-creation"), ctx.Bool("allow-anonymous-custom-templates")) + userApi := apiInitializer.InitializeUserApi(userService, sessionService, cli.Bool("allow-anonymous-board-creation"), cli.Bool("allow-anonymous-custom-templates")) routesInitializer := serviceinitialize.NewRoutesInitializer() userRoutes := routesInitializer.InitializeUserRoutes(userApi, sessionApi) @@ -442,93 +574,93 @@ func run(ctx *cli.Context) error { columnTemplateService, logger.GetLogLevel() == zap.DebugLevel, - !ctx.Bool("disable-check-origin"), - ctx.Bool("disable-anonymous-login"), - ctx.Bool("allow-anonymous-custom-templates"), - ctx.Bool("allow-anonymous-board-creation"), - ctx.Bool("allow-anonymous-history"), - ctx.Bool("auth-enable-experimental-file-system-store"), - ctx.Bool("enable-swagger"), + !cli.Bool("disable-check-origin"), + cli.Bool("disable-anonymous-login"), + cli.Bool("allow-anonymous-custom-templates"), + cli.Bool("allow-anonymous-board-creation"), + cli.Bool("allow-anonymous-history"), + cli.Bool("auth-enable-experimental-file-system-store"), + cli.Bool("enable-swagger"), ) - listen := fmt.Sprintf("%s:%d", ctx.String("address"), ctx.Int("port")) + listen := fmt.Sprintf("%s:%d", cli.String("address"), cli.Int("port")) logger.Get().Infow("starting server", "base-path", basePath, "listen", listen) return http.ListenAndServe(listen, s) } -func configureAuthProvider(ctx *cli.Context, basePath string) (map[string]auth.AuthProviderConfiguration, error) { - log := logger.FromContext(ctx.Context) +func configureAuthProvider(ctx context.Context, cli *cli.Command, basePath string) (map[string]auth.AuthProviderConfiguration, error) { + log := logger.FromContext(ctx) providersMap := make(map[string]auth.AuthProviderConfiguration) log.Debug("configuring auth provider") - callbackHost := ctx.String("auth-callback-host") + callbackHost := cli.String("auth-callback-host") if callbackHost == "" { log.Info("No auth callback host configured. Can not configure any auth provider") return providersMap, nil } - if ctx.String("auth-google-client-id") != "" && ctx.String("auth-google-client-secret") != "" { + if cli.String("auth-google-client-id") != "" && cli.String("auth-google-client-secret") != "" { log.Info("Using google authentication") providersMap[(string)(common.Google)] = auth.AuthProviderConfiguration{ - ClientId: ctx.String("auth-google-client-id"), - ClientSecret: ctx.String("auth-google-client-secret"), + ClientId: cli.String("auth-google-client-id"), + ClientSecret: cli.String("auth-google-client-secret"), RedirectUri: fmt.Sprintf("%s%s/login/google/callback", strings.TrimSuffix(callbackHost, "/"), strings.TrimSuffix(basePath, "/")), } } - if ctx.String("auth-github-client-id") != "" && ctx.String("auth-github-client-secret") != "" { + if cli.String("auth-github-client-id") != "" && cli.String("auth-github-client-secret") != "" { log.Info("Using github authentication") providersMap[(string)(common.GitHub)] = auth.AuthProviderConfiguration{ - ClientId: ctx.String("auth-github-client-id"), - ClientSecret: ctx.String("auth-github-client-secret"), + ClientId: cli.String("auth-github-client-id"), + ClientSecret: cli.String("auth-github-client-secret"), RedirectUri: fmt.Sprintf("%s%s/login/github/callback", strings.TrimSuffix(callbackHost, "/"), strings.TrimSuffix(basePath, "/")), } } - if ctx.String("auth-microsoft-client-id") != "" && ctx.String("auth-microsoft-client-secret") != "" { + if cli.String("auth-microsoft-client-id") != "" && cli.String("auth-microsoft-client-secret") != "" { log.Info("Using microsoft authentication") providersMap[(string)(common.Microsoft)] = auth.AuthProviderConfiguration{ - ClientId: ctx.String("auth-microsoft-client-id"), - ClientSecret: ctx.String("auth-microsoft-client-secret"), + ClientId: cli.String("auth-microsoft-client-id"), + ClientSecret: cli.String("auth-microsoft-client-secret"), RedirectUri: fmt.Sprintf("%s%s/login/microsoft/callback", strings.TrimSuffix(callbackHost, "/"), strings.TrimSuffix(basePath, "/")), } } - if ctx.String("auth-azure-ad-tenant-id") != "" && ctx.String("auth-azure-ad-client-id") != "" && ctx.String("auth-azure-ad-client-secret") != "" { + if cli.String("auth-azure-ad-tenant-id") != "" && cli.String("auth-azure-ad-client-id") != "" && cli.String("auth-azure-ad-client-secret") != "" { log.Info("Using azure authentication") providersMap[(string)(common.AzureAd)] = auth.AuthProviderConfiguration{ - TenantId: ctx.String("auth-azure-ad-tenant-id"), - ClientId: ctx.String("auth-azure-ad-client-id"), - ClientSecret: ctx.String("auth-azure-ad-client-secret"), + TenantId: cli.String("auth-azure-ad-tenant-id"), + ClientId: cli.String("auth-azure-ad-client-id"), + ClientSecret: cli.String("auth-azure-ad-client-secret"), RedirectUri: fmt.Sprintf("%s%s/login/azure_ad/callback", strings.TrimSuffix(callbackHost, "/"), strings.TrimSuffix(basePath, "/")), } } - if ctx.String("auth-apple-client-id") != "" && ctx.String("auth-apple-client-secret") != "" { + if cli.String("auth-apple-client-id") != "" && cli.String("auth-apple-client-secret") != "" { log.Info("Using apple authentication.") providersMap[(string)(common.Apple)] = auth.AuthProviderConfiguration{ - ClientId: ctx.String("auth-apple-client-id"), - ClientSecret: ctx.String("auth-apple-client-secret"), + ClientId: cli.String("auth-apple-client-id"), + ClientSecret: cli.String("auth-apple-client-secret"), RedirectUri: fmt.Sprintf("%s%s/login/apple/callback", strings.TrimSuffix(callbackHost, "/"), strings.TrimSuffix(basePath, "/")), } } - if ctx.String("auth-oidc-discovery-url") != "" && ctx.String("auth-oidc-client-id") != "" && ctx.String("auth-oidc-client-secret") != "" { + if cli.String("auth-oidc-discovery-url") != "" && cli.String("auth-oidc-client-id") != "" && cli.String("auth-oidc-client-secret") != "" { log.Info("Using oidc authentication.") providersMap[(string)(common.TypeOIDC)] = auth.AuthProviderConfiguration{ - ClientId: ctx.String("auth-oidc-client-id"), - ClientSecret: ctx.String("auth-oidc-client-secret"), + ClientId: cli.String("auth-oidc-client-id"), + ClientSecret: cli.String("auth-oidc-client-secret"), RedirectUri: fmt.Sprintf("%s%s/login/oidc/callback", strings.TrimSuffix(callbackHost, "/"), strings.TrimSuffix(basePath, "/")), - DiscoveryUri: ctx.String("auth-oidc-discovery-url"), - UserIdentScope: ctx.String("auth-oidc-user-ident-scope"), - UserNameScope: ctx.String("auth-oidc-user-name-scope"), + DiscoveryUri: cli.String("auth-oidc-discovery-url"), + UserIdentScope: cli.String("auth-oidc-user-ident-scope"), + UserNameScope: cli.String("auth-oidc-user-name-scope"), } } // session secret is used by the auth lib github.com/markbates/goth // the lib takes the session secret from the env var - if ctx.String("session-secret") == "" && len(providersMap) != 0 { + if cli.String("session-secret") == "" && len(providersMap) != 0 { return nil, errors.New("you may not start the application without a session secret if an authentication provider is configured") } diff --git a/server/src/realtime/broker.go b/server/src/realtime/broker.go index f106b68713..6b5af2a3c7 100644 --- a/server/src/realtime/broker.go +++ b/server/src/realtime/broker.go @@ -4,7 +4,7 @@ import ( "context" "errors" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" @@ -35,16 +35,16 @@ type Broker struct { Con Client } -func InitializeRealtime(ctx *cli.Context) (*Broker, error) { - log := logger.FromContext(ctx.Context) +func InitializeRealtime(ctx context.Context, cli *cli.Command) (*Broker, error) { + log := logger.FromContext(ctx) var broker *Broker - if ctx.String("redis-address") != "" { + if cli.String("redis-address") != "" { redis := RedisServer{ - Addr: ctx.String("redis-address"), - Username: ctx.String("redis-username"), - Password: ctx.String("redis-password"), + Addr: cli.String("redis-address"), + Username: cli.String("redis-username"), + Password: cli.String("redis-password"), } log.Infof("Connecting to redis at %v as message broker", redis.Addr) @@ -53,8 +53,8 @@ func InitializeRealtime(ctx *cli.Context) (*Broker, error) { return broker, err } - if ctx.String("nats") != "" { - address := ctx.String("nats") + if cli.String("nats") != "" { + address := cli.String("nats") log.Infof("Connecting to nats at %v as message broker", address) diff --git a/server/src/realtime/broker_test.go b/server/src/realtime/broker_test.go index 95d039914c..4a1410cdc5 100644 --- a/server/src/realtime/broker_test.go +++ b/server/src/realtime/broker_test.go @@ -2,28 +2,30 @@ package realtime import ( "errors" - "flag" "testing" "github.com/stretchr/testify/assert" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" "scrumlr.io/server/initialize" ) func TestInitializeRealtime_Nats(t *testing.T) { + ctx := t.Context() container, connection := initialize.StartTestNats() - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("redis-address", "", "") - flagset.String("redis-usernamet", "", "") - flagset.String("redis-password", "", "") - flagset.String("nats", "", "") - init := cli.NewContext(nil, flagset, nil) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "redis-address"}, + &cli.StringFlag{Name: "redis-username"}, + &cli.StringFlag{Name: "redis-password"}, + &cli.StringFlag{Name: "nats"}, + }, + } err := init.Set("nats", connection) assert.NoError(t, err) - broker, err := InitializeRealtime(init) + broker, err := InitializeRealtime(ctx, init) assert.NoError(t, err) assert.NotNil(t, broker) @@ -33,19 +35,22 @@ func TestInitializeRealtime_Nats(t *testing.T) { } func TestInitializeRealtime_Redis(t *testing.T) { + ctx := t.Context() container, connection := initialize.StartTestRedis() - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("redis-address", "", "") - flagset.String("redis-usernamet", "", "") - flagset.String("redis-password", "", "") - flagset.String("nats", "", "") - init := cli.NewContext(nil, flagset, nil) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "redis-address"}, + &cli.StringFlag{Name: "redis-username"}, + &cli.StringFlag{Name: "redis-password"}, + &cli.StringFlag{Name: "nats"}, + }, + } err := init.Set("redis-address", connection) assert.NoError(t, err) - broker, err := InitializeRealtime(init) + broker, err := InitializeRealtime(ctx, init) assert.NoError(t, err) assert.NotNil(t, broker) @@ -55,14 +60,18 @@ func TestInitializeRealtime_Redis(t *testing.T) { } func TestInitializeRealtime_NotConfigured(t *testing.T) { - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("redis-address", "", "") - flagset.String("redis-usernamet", "", "") - flagset.String("redis-password", "", "") - flagset.String("nats", "", "") - init := cli.NewContext(nil, flagset, nil) + ctx := t.Context() - broker, err := InitializeRealtime(init) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "redis-address"}, + &cli.StringFlag{Name: "redis-username"}, + &cli.StringFlag{Name: "redis-password"}, + &cli.StringFlag{Name: "nats"}, + }, + } + + broker, err := InitializeRealtime(ctx, init) assert.Error(t, err) assert.Equal(t, errors.New("no valid message broker configuration found"), err) @@ -70,21 +79,25 @@ func TestInitializeRealtime_NotConfigured(t *testing.T) { } func TestInitializeRealtime_PrefereRedis(t *testing.T) { + ctx := t.Context() + container, connection := initialize.StartTestRedis() - flagset := flag.NewFlagSet("scrumlr-tests", flag.ExitOnError) - flagset.String("redis-address", "", "") - flagset.String("redis-usernamet", "", "") - flagset.String("redis-password", "", "") - flagset.String("nats", "", "") - init := cli.NewContext(nil, flagset, nil) + init := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{Name: "redis-address"}, + &cli.StringFlag{Name: "redis-username"}, + &cli.StringFlag{Name: "redis-password"}, + &cli.StringFlag{Name: "nats"}, + }, + } err := init.Set("redis-address", connection) assert.NoError(t, err) err = init.Set("nats", "not valide connection") assert.NoError(t, err) - broker, err := InitializeRealtime(init) + broker, err := InitializeRealtime(ctx, init) assert.NoError(t, err) assert.NotNil(t, broker)