Skip to content

Commit

Permalink
Merge pull request #64 from kosli-dev/azure-apps-improvements
Browse files Browse the repository at this point in the history
Improve azure apps snapshoting
  • Loading branch information
arstanaly authored Dec 13, 2023
2 parents 39c398f + fba97e2 commit 6dc4e4f
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 57 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
github_access_token: ${{ secrets.KOSLI_GITHUB_TOKEN }}
gitlab_access_token: ${{ secrets.KOSLI_GITLAB_TOKEN }}
azure_access_token: ${{ secrets.KOSLI_AZURE_TOKEN }}
azure_service_token: ${{ secrets.KOSLI_AZURE_SERVICE_TOKEN }}
bitbucket_password: ${{ secrets.KOSLI_BITBUCKET_PASSWORD }}
jira_api_token: ${{ secrets.KOSLI_JIRA_API_TOKEN }}
slack_webhook: ${{ secrets.MERKELY_SLACK_CI_FAILURES_WEBHOOK }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
github_access_token: ${{ secrets.KOSLI_GITHUB_TOKEN }}
gitlab_access_token: ${{ secrets.KOSLI_GITLAB_TOKEN }}
azure_access_token: ${{ secrets.KOSLI_AZURE_TOKEN }}
azure_service_token: ${{ secrets.KOSLI_AZURE_SERVICE_TOKEN }}
bitbucket_password: ${{ secrets.KOSLI_BITBUCKET_PASSWORD }}
slack_webhook: ${{ secrets.MERKELY_SLACK_CI_FAILURES_WEBHOOK }}
slack_channel: ${{ secrets.MERKELY_SLACK_CI_FAILURES_CHANNEL }}
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ on:
required: true
azure_access_token:
required: true
azure_service_token:
required: true
bitbucket_password:
required: true
jira_api_token:
Expand Down Expand Up @@ -66,6 +68,7 @@ jobs:
KOSLI_GITHUB_TOKEN: ${{ secrets.github_access_token }}
KOSLI_GITLAB_TOKEN: ${{ secrets.gitlab_access_token }}
KOSLI_AZURE_TOKEN: ${{ secrets.azure_access_token }}
KOSLI_AZURE_CLIENT_SECRET: ${{ secrets.azure_service_token }}
KOSLI_BITBUCKET_PASSWORD: ${{ secrets.bitbucket_password }}
KOSLI_JIRA_API_TOKEN: ${{ secrets.jira_api_token }}
run: |
Expand Down
2 changes: 1 addition & 1 deletion cmd/kosli/beginTrail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (suite *BeginTrailCommandTestSuite) TestBeginTrailCmd() {
wantError: true,
name: "fails when name is considered invalid by the server",
cmd: fmt.Sprintf("begin trail foo?$bar --flow %s %s", suite.flowName, suite.defaultKosliArguments),
golden: "Error: Input payload validation failed: map[name:'foo?$bar' does not match '^[a-zA-Z][a-zA-Z0-9\\\\-_\\\\.~]*$']\n",
golden: "Error: Input payload validation failed: map[name:'foo?$bar' does not match '^[a-zA-Z0-9][a-zA-Z0-9\\\\-_\\\\.~]*$']\n",
},
{
wantError: true,
Expand Down
1 change: 1 addition & 0 deletions cmd/kosli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ The service principal needs to have the following permissions:
azureTenantIdFlag = "Azure tenant ID."
azureSubscriptionIdFlag = "Azure subscription ID."
azureResourceGroupNameFlag = "Azure resource group name."
azureDigestsSourceFlag = "[defaulted] Where to get the digests from. Valid values are 'acr' and 'logs'. Defaults to 'acr'"
githubTokenFlag = "Github token."
githubOrgFlag = "Github organization. (defaulted if you are running in GitHub Actions: https://docs.kosli.com/ci-defaults )."
githubBaseURLFlag = "[optional] GitHub base URL (only needed for GitHub Enterprise installations)."
Expand Down
24 changes: 22 additions & 2 deletions cmd/kosli/snapshotAzureApps.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,25 @@ const snapshotAzureAppsLongDesc = snapshotAzureAppsShortDesc + `
The reported data includes Azure app names, container image digests and creation timestamps.` + azureAuthDesc

const snapshotAzureAppsExample = `
kosli snapshot azure-apps yourEnvironmentName \
# Use Docker logs of Azure apps to get the digests for artifacts in a snapshot
kosli snapshot azure yourEnvironmentName \
--azure-client-id yourAzureClientID \
--azure-client-secret yourAzureClientSecret \
--azure-tenant-id yourAzureTenantID \
--azure-subscription-id yourAzureSubscriptionID \
--azure-resource-group-name yourAzureResourceGroupName \
--digests-source logs \
--api-token yourAPIToken \
--org yourOrgName
# Use Azure Container Registry to get the digests for artifacts in a snapshot
kosli snapshot azure yourEnvironmentName \
--azure-client-id yourAzureClientID \
--azure-client-secret yourAzureClientSecret \
--azure-tenant-id yourAzureTenantID \
--azure-subscription-id yourAzureSubscriptionID \
--azure-resource-group-name yourAzureResourceGroupName \
--digests-source acr \
--api-token yourAPIToken \
--org yourOrgName
`
Expand All @@ -34,7 +47,7 @@ func newSnapshotAzureAppsCmd(out io.Writer) *cobra.Command {
o := new(snapshotAzureAppsOptions)
o.azureStaticCredentials = new(azure.AzureStaticCredentials)
cmd := &cobra.Command{
Use: "azure-apps ENVIRONMENT-NAME",
Use: "azure ENVIRONMENT-NAME",
Short: snapshotAzureAppsShortDesc,
Long: snapshotAzureAppsLongDesc,
Example: snapshotAzureAppsExample,
Expand All @@ -45,6 +58,11 @@ func newSnapshotAzureAppsCmd(out io.Writer) *cobra.Command {
if err != nil {
return ErrorBeforePrintingUsage(cmd, err.Error())
}

if o.azureStaticCredentials.DigestsSource != "acr" && o.azureStaticCredentials.DigestsSource != "logs" {
return fmt.Errorf("invalid value for --digests-source flag. Valid values are 'acr' and 'logs'")
}

return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -57,6 +75,8 @@ func newSnapshotAzureAppsCmd(out io.Writer) *cobra.Command {
cmd.Flags().StringVar(&o.azureStaticCredentials.TenantId, "azure-tenant-id", "", azureTenantIdFlag)
cmd.Flags().StringVar(&o.azureStaticCredentials.SubscriptionId, "azure-subscription-id", "", azureSubscriptionIdFlag)
cmd.Flags().StringVar(&o.azureStaticCredentials.ResourceGroupName, "azure-resource-group-name", "", azureResourceGroupNameFlag)
cmd.Flags().BoolVar(&o.azureStaticCredentials.DownloadLogsAsZip, "zip", false, "Download logs from Azure as zip files")
cmd.Flags().StringVar(&o.azureStaticCredentials.DigestsSource, "digests-source", "acr", azureDigestsSourceFlag)
addDryRunFlag(cmd)

err := RequireFlags(cmd, []string{
Expand Down
103 changes: 103 additions & 0 deletions cmd/kosli/snapshotAzureApps_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package main

import (
"fmt"
"testing"

"github.com/kosli-dev/cli/internal/testHelpers"
"github.com/stretchr/testify/suite"
)

// Define the suite, and absorb the built-in basic suite
// functionality from testify - including a T() method which
// returns the current testing context
type SnapshotAzureAppsTestSuite struct {
suite.Suite
defaultKosliArguments string
defaultAzureArguments string
envName string
}

func (suite *SnapshotAzureAppsTestSuite) SetupTest() {
testHelpers.SkipIfEnvVarUnset(suite.T(), []string{"KOSLI_AZURE_CLIENT_SECRET"})

suite.envName = "snapshot-azure-env"
global = &GlobalOpts{
ApiToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6ImNkNzg4OTg5In0.e8i_lA_QrEhFncb05Xw6E_tkCHU9QfcY4OLTVUCHffY",
Org: "docs-cmd-test-user",
Host: "http://localhost:8001",
}
suite.defaultKosliArguments = fmt.Sprintf(" --host %s --org %s --api-token %s", global.Host, global.Org, global.ApiToken)
// AZURE-CLIENT-SECRET is set as a secret and passed as env variable to tests
suite.defaultAzureArguments = " --azure-client-id 0bfad7d5-eb5e-4144-95a0-0a0d66eb07cb --azure-tenant-id e52b5fba-43c2-4eaf-91c1-579dc6fae771 " +
"--azure-subscription-id 96cdee58-1fa8-419d-a65a-7233b3465632 --azure-resource-group-name EnvironmentReportingExperiment"
CreateEnv(global.Org, suite.envName, "azure-apps", suite.T())
}

func (suite *SnapshotAzureAppsTestSuite) TestSnapshotAzureAppsCmd() {
tests := []cmdTestCase{
{
wantError: true,
name: "snapshot azure fails if 2 args are provided",
cmd: fmt.Sprintf(`snapshot azure %s xxx %s %s`, suite.envName, suite.defaultKosliArguments, suite.defaultAzureArguments),
golden: "Error: accepts 1 arg(s), received 2\n",
},
{
wantError: true,
name: "snapshot azure fails if no args are set",
cmd: fmt.Sprintf(`snapshot azure %s %s`, suite.defaultKosliArguments, suite.defaultAzureArguments),
golden: "Error: accepts 1 arg(s), received 0\n",
},
{
wantError: true,
name: "snapshot azure fails if --digests-source flag is set to invalid value",
cmd: fmt.Sprintf(`snapshot azure %s %s %s --digests-source ghcr `, suite.envName, suite.defaultKosliArguments, suite.defaultAzureArguments),
golden: "Error: invalid value for --digests-source flag. Valid values are 'acr' and 'logs'\n",
},
{
name: "snapshot azure succeeds if all required flags are set",
cmd: fmt.Sprintf(`snapshot azure %s %s %s`, suite.envName, suite.defaultKosliArguments, suite.defaultAzureArguments),
},
{
name: "snapshot azure succeeds when digests-source is set to acr if all required flags are set",
cmd: fmt.Sprintf(`snapshot azure %s %s %s --digests-source acr`, suite.envName, suite.defaultKosliArguments, suite.defaultAzureArguments),
},
{
name: "snapshot azure succeeds when digests-source is set to logs if all required flags are set",
cmd: fmt.Sprintf(`snapshot azure %s %s %s --digests-source logs`, suite.envName, suite.defaultKosliArguments, suite.defaultAzureArguments),
},

{
wantError: true,
name: "snapshot azure fails when Azure client ID is not set",
cmd: fmt.Sprintf(`snapshot azure %s %s --azure-client-secret xxx --azure-tenant-id xxx --azure-subscription-id xxx --azure-resource-group-name xxx`, suite.envName, suite.defaultKosliArguments),
golden: "Error: required flag(s) \"azure-client-id\" not set\n",
},
{
wantError: true,
name: "snapshot azure fails when Azure tenant ID is not set",
cmd: fmt.Sprintf(`snapshot azure %s %s --azure-client-id xxx --azure-client-secret xxx --azure-subscription-id xxx --azure-resource-group-name xxx`, suite.envName, suite.defaultKosliArguments),
golden: "Error: required flag(s) \"azure-tenant-id\" not set\n",
},
{
wantError: true,
name: "snapshot azure fails when Azure subscription ID is not set",
cmd: fmt.Sprintf(`snapshot azure %s %s --azure-client-id xxx --azure-client-secret xxx --azure-tenant-id xxx --azure-resource-group-name xxx`, suite.envName, suite.defaultKosliArguments),
golden: "Error: required flag(s) \"azure-subscription-id\" not set\n",
},
{
wantError: true,
name: "snapshot azure fails when Azure resource group name is not set",
cmd: fmt.Sprintf(`snapshot azure %s %s --azure-client-id xxx --azure-client-secret xxx --azure-tenant-id xxx --azure-subscription-id xxx`, suite.envName, suite.defaultKosliArguments),
golden: "Error: required flag(s) \"azure-resource-group-name\" not set\n",
},
}

runTestCmd(suite.T(), tests)
}

// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestSnapshotAzureAppsTestSuite(t *testing.T) {
suite.Run(t, new(SnapshotAzureAppsTestSuite))
}
13 changes: 7 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ module github.com/kosli-dev/cli
go 1.20

require (
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.1.1
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0
github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0
github.com/andygrunwald/go-jira v1.16.0
github.com/aws/aws-sdk-go-v2 v1.17.7
github.com/aws/aws-sdk-go-v2/config v1.18.19
Expand All @@ -29,7 +31,7 @@ require (
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.2
github.com/stretchr/testify v1.8.4
github.com/xanzy/go-gitlab v0.81.0
github.com/xeonx/timeago v1.0.0-rc5
golang.org/x/oauth2 v0.7.0
Expand All @@ -41,8 +43,7 @@ require (
)

require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
Expand Down Expand Up @@ -94,7 +95,7 @@ require (
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
Expand Down
27 changes: 16 additions & 11 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,18 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.2 h1:t5+QXLCK9SVi0PPdaY0PrFvYUo24KwA0QwxnaHRSVd4=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.2/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1 h1:LNHhpdK7hzUcx/k1LIcuh5k7k1LGIWLQfCjaneSj7Fc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1/go.mod h1:uE9zaUfEQT/nbQjVi2IblCG9iaLtZsuYZ8ne+PuQ02M=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.1.1 h1:APDvQUs7TMuQTcclQHOT8kpCnz4qU3T6Po77XFIWVL8=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.1.1/go.mod h1:XQLwMCyOcUmKwvm1L1cZKZEZ7AWylzrrVRzswqsfd4Q=
github.com/Azure/azure-sdk-for-go v55.0.0+incompatible h1:L4/vUGbg1Xkw5L20LZD+hJI5I+ibWSytqQ68lTCfLwY=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 h1:fb8kj/Dh4CSwgsOzHeZY4Xh68cFVbzXx+ONXGMY//4w=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0/go.mod h1:uReU2sSxZExRPBAg3qKzmAucSi51+SP1OhohieR821Q=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs=
github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.0 h1:apA25AdhTvHEi9izOZFUm4NO1pJmHABncK673C/TZaY=
github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.0/go.mod h1:rL09sLEGjIG0TgfQZNMvJ9FtfAseOtBPbGD5eZQMCqw=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 h1:d81/ng9rET2YqdVkVwkb6EXeRrLJIwyGnJcAlAWKwhs=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0 h1:JI8PcWOImyvIUEZ0Bbmfe05FOlWkMi2KhjG+cAKaUms=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0/go.mod h1:nJLFPGJkyKfDDyJiPuHIXsCi/gpJkm07EvRgiX7SGlI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v0.6.0 h1:Z5/bDxQL2Zc9t6ZDwdRU60bpLHZvoKOeuaM7XVbf2z0=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
Expand Down Expand Up @@ -341,8 +345,9 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
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.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/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/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
Expand Down Expand Up @@ -629,8 +634,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
Expand Down
Loading

0 comments on commit 6dc4e4f

Please sign in to comment.