diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index ae475136a..8d45f09a9 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -6,17 +6,17 @@ jobs: name: Build runs-on: ubuntu-latest steps: - - name: Set up Go 1.15 + - name: Set up Go 1.16 uses: actions/setup-go@v1 with: - go-version: 1.15 + go-version: 1.16 id: go - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Install golanglint-ci - run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.23.6 + run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.37.1 - name: ci run: PATH=$(go env GOPATH)/bin:$PATH make ci diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 4ba810003..74e23fe14 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -14,10 +14,10 @@ jobs: - name: Unshallow run: git fetch --prune --unshallow - - name: Set up Go 1.15 + - name: Set up Go 1.16 uses: actions/setup-go@v1 with: - go-version: 1.15 + go-version: 1.16 - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2.4.1 diff --git a/go.mod b/go.mod index 4def956a0..bff712678 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/auth0/auth0-cli -go 1.14 +go 1.16 require ( github.com/AlecAivazis/survey/v2 v2.2.7 diff --git a/internal/cli/data/quickstarts.json b/internal/cli/data/quickstarts.json new file mode 100644 index 000000000..9d8d8b107 --- /dev/null +++ b/internal/cli/data/quickstarts.json @@ -0,0 +1,364 @@ +{ + "backend": [ + { + "name": "ASP.NET Core Web API", + "samples": [ + "Quickstart/01-Authorization" + ], + "org": "auth0-samples", + "repo": "auth0-aspnetcore-webapi-samples" + }, + { + "name": "ASP.NET Core Web API v2.1", + "samples": [ + "Quickstart/01-Authorization" + ], + "org": "auth0-samples", + "repo": "auth0-aspnetcore-webapi-samples", + "branch": "netcore2.1" + }, + { + "name": "Django API", + "samples": [ + "01-Authorization" + ], + "org": "auth0-samples", + "repo": "auth0-django-api" + }, + { + "name": "Go", + "samples": [ + "01-Authorization-RS256" + ], + "org": "auth0-samples", + "repo": "auth0-golang-api-samples" + }, + { + "name": "Spring Security 4 Java API", + "samples": [ + "01-Authorization" + ], + "org": "auth0-samples", + "repo": "auth0-spring-security-api-sample" + }, + { + "name": "Spring Security 5 Java API", + "samples": [ + "01-Authorization-MVC" + ], + "org": "auth0-samples", + "repo": "auth0-spring-security5-api-sample" + }, + { + "name": "Laravel API", + "samples": [ + "01-Authorization-RS256" + ], + "org": "auth0-samples", + "repo": "auth0-laravel-api-samples" + }, + { + "name": "Node (Express) API", + "samples": [ + "01-Authorization-RS256" + ], + "org": "auth0-samples", + "repo": "auth0-express-api-samples" + }, + { + "name": "PHP API", + "samples": [ + "01-Authorization-RS256" + ], + "org": "auth0-samples", + "repo": "auth0-php-api-samples" + }, + { + "name": "Python API", + "samples": [ + "00-Starter-Seed" + ], + "org": "auth0-samples", + "repo": "auth0-python-api-samples" + }, + { + "name": "Ruby On Rails API", + "samples": [ + "01-Authentication-RS256" + ], + "org": "auth0-samples", + "repo": "auth0-rubyonrails-api-samples" + }, + { + "name": "ASP.NET Web API (OWIN)", + "samples": [ + "Quickstart/01-Authorization" + ], + "org": "auth0-samples", + "repo": "auth0-aspnet-owin-webapi-samples" + } + ], + "native": [ + { + "name": "Android", + "samples": [ + "00-Login-Kt" + ], + "org": "auth0-samples", + "repo": "auth0-android-sample" + }, + { + "name": "Android - Facebook Login", + "samples": [ + "00-login-facebook" + ], + "org": "auth0-samples", + "repo": "auth0-android-native-social-sample" + }, + { + "name": "Cordova", + "samples": [ + "01-Login" + ], + "org": "auth0-samples", + "repo": "auth0-cordova-samples" + }, + { + "name": "Ionic 4", + "samples": [ + "01-Login" + ], + "org": "auth0-samples", + "repo": "auth0-ionic4-samples" + }, + { + "name": "iOS Swift", + "samples": [ + "00-Login", + "03-User-Sessions", + "04-Calling-APIs", + "05-5uthorization", + "07-Linking-Accounts", + "08-Credentials-TouchID" + ], + "org": "auth0-samples", + "repo": "auth0-ios-swift-sample" + }, + { + "name": "iOS Swift - Facebook Login", + "samples": [ + "00-login-facebook" + ], + "org": "auth0-samples", + "repo": "auth0-ios-swift-native-social-samples" + }, + { + "name": "iOS Swift - Sign In With Apple", + "samples": [ + "00-login-siwa" + ], + "org": "auth0-samples", + "repo": "auth0-ios-swift-native-social-samples" + }, + { + "name": "React Native", + "samples": [ + "00-Login" + ], + "org": "auth0-samples", + "repo": "auth0-react-native-sample" + }, + { + "name": "Windows Universal App C#", + "samples": [ + "Quickstart/00-Starter-Seed" + ], + "org": "auth0-samples", + "repo": "auth0-uwp-oidc-samples" + }, + { + "name": "WPF / Winforms", + "samples": [ + "Quickstart/00-Starter-Seed" + ], + "org": "auth0-samples", + "repo": "auth0-WinFormsWPF-oidc-samples" + }, + { + "name": "Xamarin", + "samples": [ + "Quickstart/01-Login" + ], + "org": "auth0-samples", + "repo": "auth0-xamarin-oidc-samples" + } + ], + "spa": [ + { + "name": "Angular", + "samples": [ + "Sample-01" + ], + "org": "auth0-samples", + "repo": "auth0-angular-samples" + }, + { + "name": "React", + "samples": [ + "Sample-01" + ], + "org": "auth0-samples", + "repo": "auth0-react-samples" + }, + { + "name": "JavaScript", + "samples": [ + "01-Login", + "02-Calling-an-API" + ], + "org": "auth0-samples", + "repo": "auth0-javascript-samples" + }, + { + "name": "Vue", + "samples": [ + "01-Login", + "02-Calling-an-API" + ], + "org": "auth0-samples", + "repo": "auth0-vue-samples" + } + ], + "webapp": [ + { + "name": "ASP.NET Core", + "samples": [ + "Quickstart/01-Login", + "Quickstart/01-User-Profile", + "Quickstart/01-Authorization" + ], + "org": "auth0-samples", + "repo": "auth0-aspnetcore-mvc-samples" + }, + { + "name": "ASP.NET Core v2.1", + "samples": [ + "Quickstart/01-Login", + "Quickstart/01-User-Profile", + "Quickstart/01-Authorization" + ], + "org": "auth0-samples", + "repo": "auth0-aspnetcore-mvc-samples", + "branch": "netcore2.1" + }, + { + "name": "ASP.NET (OWIN)", + "samples": [ + "Quickstart/01-Login", + "Quickstart/01-User-Profile", + "Quickstart/01-Authorization" + ], + "org": "auth0-samples", + "repo": "auth0-aspnet-owin-mvc-samples" + }, + { + "name": "Django", + "samples": [ + "01-Login" + ], + "org": "auth0-samples", + "repo": "auth0-django-web-app" + }, + { + "name": "Express", + "samples": [ + "01-Login" + ], + "org": "auth0-samples", + "repo": "auth0-express-webapp-sample" + }, + { + "name": "Go", + "samples": [ + "01-Login" + ], + "org": "auth0-samples", + "repo": "auth0-golang-web-app" + }, + { + "name": "Java", + "samples": [ + "01-Login" + ], + "org": "auth0-samples", + "repo": "auth0-servlet-sample" + }, + { + "name": "Java EE", + "samples": [ + "01-Login" + ], + "org": "auth0-samples", + "repo": "auth0-java-ee-sample" + }, + { + "name": "Java Spring Boot", + "samples": [ + "01-Login" + ], + "org": "auth0-samples", + "repo": "auth0-spring-boot-login-samples" + }, + { + "name": "PHP (Laravel)", + "samples": [ + "01-Login" + ], + "org": "auth0-samples", + "repo": "auth0-laravel-php-web-app" + }, + { + "name": "Next.js", + "samples": [ + "Sample-01" + ], + "org": "auth0-samples", + "repo": "auth0-nextjs-samples", + "branch": "main" + }, + { + "name": "Node.js", + "samples": [ + "01-Login" + ], + "org": "auth0-samples", + "repo": "auth0-nodejs-webapp-sample" + }, + { + "name": "PHP", + "samples": [ + "00-Starter-Seed" + ], + "org": "auth0-samples", + "repo": "auth0-php-web-app" + }, + { + "name": "Python", + "samples": [ + "01-Login" + ], + "org": "auth0-samples", + "repo": "auth0-python-web-app" + }, + { + "name": "Ruby On Rails", + "samples": [ + "01-Login", + "02-Session-Handling", + "03-User-Profile" + ], + "org": "auth0-samples", + "repo": "auth0-rubyonrails-sample" + } + ] + } diff --git a/internal/cli/quickstarts.go b/internal/cli/quickstarts.go index ed7928555..2a0d3ab75 100644 --- a/internal/cli/quickstarts.go +++ b/internal/cli/quickstarts.go @@ -3,6 +3,7 @@ package cli import ( "bytes" "context" + _ "embed" "encoding/json" "fmt" "io" @@ -11,13 +12,42 @@ import ( "os" "path" "regexp" + "sort" "github.com/auth0/auth0-cli/internal/ansi" + "github.com/auth0/auth0-cli/internal/prompt" "github.com/mholt/archiver/v3" "github.com/spf13/cobra" "gopkg.in/auth0.v5/management" ) +var ( + //go:embed data/quickstarts.json + qsBuf []byte + quickstartsByType = func() (qs map[string][]quickstart) { + if err := json.Unmarshal(qsBuf, &qs); err != nil { + panic(err) + } + return + }() + quickstartTypes = func() []string { + keys := make([]string, 0, len(quickstartsByType)) + for k := range quickstartsByType { + keys = append(keys, k) + } + sort.Strings(keys) + return keys + }() +) + +type quickstart struct { + Name string `json:"name"` + Samples []string `json:"samples"` + Org string `json:"org"` + Repo string `json:"repo"` + Branch string `json:"branch,omitempty"` +} + func quickstartsCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "quickstarts", @@ -48,6 +78,26 @@ func downloadQuickstart(cli *cli) *cobra.Command { return err } + selectedType := flags.Type + selectedStack := flags.Stack + + if selectedType == "" { + input := prompt.SelectInput("type", "Quickstart Type:", "Type of quickstart to download", quickstartTypes, true) + if err := prompt.AskOne(input, &selectedType); err != nil { + return err + } + } + if selectedStack == "" { + stacks, err := quickstartStacksFromType(selectedType) + if err != nil { + return err + } + input := prompt.SelectInput("stack", "Quickstart Stack:", "Tech/Language to use for quickstart", stacks, true) + if err := prompt.AskOne(input, &selectedStack); err != nil { + return err + } + } + target, exists, err := quickstartPathFor(client) if err != nil { return err @@ -60,8 +110,13 @@ func downloadQuickstart(cli *cli) *cobra.Command { return nil } + q, err := getQuickstart(selectedType, selectedStack) + if err != nil { + return err + } + err = ansi.Spinner("Downloading quickstart", func() error { - return downloadQuickStart(context.TODO(), cli, client, flags.Stack, target) + return downloadQuickStart(context.TODO(), cli, client, target, q) }) if err != nil { @@ -77,7 +132,7 @@ func downloadQuickstart(cli *cli) *cobra.Command { cmd.Flags().StringVar(&flags.ClientID, "client-id", "", "ID of the client.") cmd.Flags().StringVarP(&flags.Type, "type", "t", "", "Type of the quickstart to download.") cmd.Flags().StringVarP(&flags.Stack, "stack", "s", "", "Tech stack of the quickstart to use.") - mustRequireFlags(cmd, "client-id", "type", "stack") + mustRequireFlags(cmd, "client-id") return cmd } @@ -89,7 +144,7 @@ const ( quickstartDefaultCallbackURL = `https://YOUR_APP/callback` ) -func downloadQuickStart(ctx context.Context, cli *cli, client *management.Client, target, stack string) error { +func downloadQuickStart(ctx context.Context, cli *cli, client *management.Client, target string, q quickstart) error { var payload struct { Branch string `json:"branch"` Org string `json:"org"` @@ -110,12 +165,12 @@ func downloadQuickStart(ctx context.Context, cli *cli, client *management.Client payload.Tenant = ten.Name payload.Domain = ten.Domain - // FIXME(cyx): these are hard coded. We can followup with a lookup - // table -- which I don't know if there's a canonical place for that - // already. - payload.Branch = "master" - payload.Repo = "auth0-cordova-samples" - payload.Path = "01-Login" + // FIXME(copland): Default to first item from list of samples. + // Eventually we should add a forced survey for user to select one if + // there are multiple. + payload.Branch = q.Branch + payload.Repo = q.Repo + payload.Path = q.Samples[0] // These appear to be largely constant and refers to the github // username they're under. @@ -195,3 +250,28 @@ func quickstartPathFor(client *management.Client) (p string, exists bool, err er return target, exists, nil } + +func getQuickstart(typ, stack string) (quickstart, error) { + quickstarts, ok := quickstartsByType[typ] + if !ok { + return quickstart{}, fmt.Errorf("unknown quickstart type %s", typ) + } + for _, q := range quickstarts { + if q.Name == stack { + return q, nil + } + } + return quickstart{}, fmt.Errorf("quickstart not found for %s/%s", typ, stack) +} + +func quickstartStacksFromType(t string) ([]string, error) { + _, ok := quickstartsByType[t] + if !ok { + return nil, fmt.Errorf("unknown quickstart type %s", t) + } + stacks := make([]string, 0, len(quickstartsByType[t])) + for _, s := range quickstartsByType[t] { + stacks = append(stacks, s.Name) + } + return stacks, nil +}