diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..18baad7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,26 @@ +name: CI +on: + push: + branches-ignore: + - 'generated' + - 'codegen/**' + - 'integrated/**' + - 'stl-preview-head/**' + - 'stl-preview-base/**' + +jobs: + lint: + timeout-minutes: 10 + name: lint + runs-on: ${{ github.repository == 'stainless-sdks/stainless-v0-cli' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup go + uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + + - name: Run lints + run: ./scripts/lint diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 0000000..2634ef5 --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,31 @@ +--- +name: Publish Release +permissions: + contents: write + +concurrency: + group: publish + +on: + push: + tags: + - "v*" +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6.1.0 + with: + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 0000000..129afbc --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,19 @@ +name: Release Doctor +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + if: github.repository == 'stainless-api/stainless-api-cli' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v4 + + - name: Check release environment + run: | + bash ./bin/check-release-environment diff --git a/.gitignore b/.gitignore index 2b6c132..3b9c623 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .prism.log dist/ -stainless-v0-cli +stainless-api-cli diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..df19b40 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,57 @@ +project_name: stainless-api-cli +version: 2 + +builds: + - id: macos + goos: [darwin] + goarch: [amd64, arm64] + binary: '{{ .ProjectName }}' + mod_timestamp: "{{ .CommitTimestamp }}" + ldflags: + - "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}" + + - id: linux + goos: [linux] + goarch: ['386', arm, amd64, arm64] + env: + - CGO_ENABLED=0 + binary: '{{ .ProjectName }}' + mod_timestamp: "{{ .CommitTimestamp }}" + ldflags: + - "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}" + + - id: windows + goos: [windows] + goarch: ['386', amd64, arm64] + binary: '{{ .ProjectName }}' + mod_timestamp: "{{ .CommitTimestamp }}" + ldflags: + - "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}" + +archives: + - id: linux-archive + ids: [linux] + name_template: '{{ .ProjectName }}_{{ .Version }}_linux_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' + wrap_in_directory: true + formats: [tar.gz] + - id: macos-archive + ids: [macos] + name_template: '{{ .ProjectName }}_{{ .Version }}_macos_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' + wrap_in_directory: true + formats: [zip] + - id: windows-archive + ids: [windows] + name_template: '{{ .ProjectName }}_{{ .Version }}_windows_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' + wrap_in_directory: false + formats: [zip] + +snapshot: + version_template: "{{ .Tag }}-next" + +nfpms: + - license: Apache-2.0 + maintainer: + bindir: /usr + formats: + - deb + - rpm diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..ba6c348 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.1.0-alpha.1" +} \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index f76aa2e..e391227 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 6 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless%2Fstainless-v0-76dbc699edc35f7b97378d746e3e72be3e849c60373fdb27054c61f4d1df3490.yml -openapi_spec_hash: 1645053eb0d0cb91d4c799680e860293 -config_hash: f7864ea9299ef30788a1f8a76b695360 +configured_endpoints: 14 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/stainless%2Fstainless-v0-5005d23980f589ea52eb1678ed620336b7c1172e3b1493a5c819c6b00f22203e.yml +openapi_spec_hash: 2a29f55c69490ab4d0ee5eb32727ab46 +config_hash: 66be21d9e622533a4b22a18501a02005 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..355de8c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,57 @@ +# Changelog + +## 0.1.0-alpha.1 (2025-04-30) + +Full Changelog: [v0.0.1-alpha.0...v0.1.0-alpha.1](https://github.com/stainless-api/stainless-api-cli/compare/v0.0.1-alpha.0...v0.1.0-alpha.1) + +### Features + +* add typescript ([7a35228](https://github.com/stainless-api/stainless-api-cli/commit/7a3522871cb71d0511e8066d8c7052731d0fe56a)) +* **api:** configs ([24361de](https://github.com/stainless-api/stainless-api-cli/commit/24361de65a1e8f72b1f7fda391e158f6737c7691)) +* **api:** fix enum name conflict maybe ([e70ad16](https://github.com/stainless-api/stainless-api-cli/commit/e70ad1613201ddcbc500c372f904fd5dd6b33670)) +* **api:** manual updates ([640c152](https://github.com/stainless-api/stainless-api-cli/commit/640c152a0fa8a2de1d8f90e6fe7588a73f8dd06b)) +* **api:** manual updates ([d7fa9ae](https://github.com/stainless-api/stainless-api-cli/commit/d7fa9ae3231e881206843cd2044c1657f48c3455)) +* **api:** manual updates ([aeaafee](https://github.com/stainless-api/stainless-api-cli/commit/aeaafee818795dbb22170c3eb778aa6a88ec3815)) +* **api:** manual updates ([c694b82](https://github.com/stainless-api/stainless-api-cli/commit/c694b821b6720263e512c35d9e6c2fe71cb504f5)) +* **api:** manual updates ([79ceae1](https://github.com/stainless-api/stainless-api-cli/commit/79ceae11587809ff66d89987cb43767890976727)) +* **api:** manual updates ([c654f3d](https://github.com/stainless-api/stainless-api-cli/commit/c654f3dd0ebc143f724d7456920115e7eeff3543)) +* **api:** manual updates ([69fd666](https://github.com/stainless-api/stainless-api-cli/commit/69fd666912c10edf82d9fac0b9a34e8a8201c0fd)) +* **api:** manual updates ([514327b](https://github.com/stainless-api/stainless-api-cli/commit/514327b63421852a37eb64177e57987f64580620)) +* **api:** manual updates ([1cc704d](https://github.com/stainless-api/stainless-api-cli/commit/1cc704d81c9bde5de6d4a0049d58868701919ec6)) +* **api:** manual updates ([dfc8fa2](https://github.com/stainless-api/stainless-api-cli/commit/dfc8fa2e632544cf63d50ec02143ffe20cf30773)) +* **api:** manual updates ([611beb0](https://github.com/stainless-api/stainless-api-cli/commit/611beb0f2fcff7fc46609864b0b51a102a825aad)) +* **api:** manual updates ([0aeaa78](https://github.com/stainless-api/stainless-api-cli/commit/0aeaa78a2e8f59e8466cd0df451ee551245b4ef4)) +* **api:** manual updates ([bc90b96](https://github.com/stainless-api/stainless-api-cli/commit/bc90b965b2df3c688bad5cef93cda86aaf228edf)) +* **api:** manual updates ([15bda00](https://github.com/stainless-api/stainless-api-cli/commit/15bda00e341f32b2fa2a3ac059c311c014a17863)) +* **api:** manual updates ([f598f10](https://github.com/stainless-api/stainless-api-cli/commit/f598f10545abbedca7db1a8e59c19a21f3393c44)) +* **api:** manual updates ([947c01a](https://github.com/stainless-api/stainless-api-cli/commit/947c01ac614c6a0694cad2980150e9aaa6b8b76d)) +* **api:** manual updates ([676ed23](https://github.com/stainless-api/stainless-api-cli/commit/676ed23f267362057b36797fed487db9beea3e26)) +* **api:** parent build id ([540a1a0](https://github.com/stainless-api/stainless-api-cli/commit/540a1a06aef7294860f53326accc85379695b092)) +* **api:** remove discriminator thing ([b87d671](https://github.com/stainless-api/stainless-api-cli/commit/b87d671eb9cb433d2e25e1e642bd65101f5c5012)) +* **api:** rename api key ([b7e9c79](https://github.com/stainless-api/stainless-api-cli/commit/b7e9c790166bea892b438d58fe1c09a3fc9158ac)) +* **api:** update via SDK Studio ([57b41f3](https://github.com/stainless-api/stainless-api-cli/commit/57b41f3587fb39b81719ec363c9c8a559c45eb70)) +* **api:** update via SDK Studio ([8e817cc](https://github.com/stainless-api/stainless-api-cli/commit/8e817cc694231314fcd5f959497eeaade9819df5)) +* **api:** update via SDK Studio ([8e4f061](https://github.com/stainless-api/stainless-api-cli/commit/8e4f061fa25a4d1847fc0025bc9dfb28fdc12f8a)) +* **api:** update via SDK Studio ([#2](https://github.com/stainless-api/stainless-api-cli/issues/2)) ([10296a9](https://github.com/stainless-api/stainless-api-cli/commit/10296a982896077aa6923a67e697ba984177ef79)) +* **api:** update via SDK Studio ([#4](https://github.com/stainless-api/stainless-api-cli/issues/4)) ([0380cc7](https://github.com/stainless-api/stainless-api-cli/commit/0380cc735be7df8284ab846dc1e78ab7d56f7942)) +* **api:** use correct hashes ([fe39102](https://github.com/stainless-api/stainless-api-cli/commit/fe391029e2b2cfd7f56fbdeb7c6e3fe4b5efa86c)) +* change list endpoint ([9943696](https://github.com/stainless-api/stainless-api-cli/commit/99436969643e83cf65d2cb9d06bf696225d0bc01)) +* use urfave/cli library ([b1ae8e7](https://github.com/stainless-api/stainless-api-cli/commit/b1ae8e7ede73da7327e4b6764503e9152175923e)) + + +### Bug Fixes + +* don't overwrite headers ([#5](https://github.com/stainless-api/stainless-api-cli/issues/5)) ([2680134](https://github.com/stainless-api/stainless-api-cli/commit/2680134b981409ec9bdb5acaed2cf57c7cfb61be)) + + +### Chores + +* configure releases ([af7fad0](https://github.com/stainless-api/stainless-api-cli/commit/af7fad0fe84224aac40b03196412c3e285c57769)) +* go live ([#1](https://github.com/stainless-api/stainless-api-cli/issues/1)) ([c9feb17](https://github.com/stainless-api/stainless-api-cli/commit/c9feb179487869081f1632c06f437220e74c5d5d)) +* **internal:** codegen related update ([ec4f098](https://github.com/stainless-api/stainless-api-cli/commit/ec4f098eb4bb7a177acf30900dabc3b38ce08ca4)) +* **internal:** codegen related update ([5d4d480](https://github.com/stainless-api/stainless-api-cli/commit/5d4d480936c1921673e32f21e92bcfaa63944913)) + + +### Refactors + +* split up completion target into multiple lines ([#3](https://github.com/stainless-api/stainless-api-cli/issues/3)) ([caa5a30](https://github.com/stainless-api/stainless-api-cli/commit/caa5a300c058762fababf68a5082c43c53f65b32)) diff --git a/SECURITY.md b/SECURITY.md index 93e1847..c097ea1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -18,10 +18,6 @@ before making any information public. If you encounter security issues that are not directly related to SDKs but pertain to the services or products provided by Stainless V0 please follow the respective company's security reporting guidelines. -### Stainless V0 Terms and Policies - -Please contact dev-feedback@stainless-v0.com for any questions or concerns regarding security of our services. - --- Thank you for helping us keep the SDKs and systems they interact with secure. diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 0000000..1e951e9 --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +errors=() + +lenErrors=${#errors[@]} + +if [[ lenErrors -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" diff --git a/build.go b/build.go index 8399f08..4ef2fe2 100644 --- a/build.go +++ b/build.go @@ -3,49 +3,141 @@ package main import ( - "context" - "flag" - "fmt" - "net/http" - "os" - - "github.com/stainless-sdks/stainless-v0-go" - "github.com/stainless-sdks/stainless-v0-go/option" + "context" + "fmt" + "os" + + "github.com/stainless-api/stainless-api-go" + "github.com/stainless-api/stainless-api-go/option" + "github.com/urfave/cli/v3" ) -func createBuildsRetrieveSubcommand() (Subcommand) { - var buildID *string = nil - query := []byte("{}") - header := []byte("{}") - var flagSet = flag.NewFlagSet("builds.retrieve", flag.ExitOnError) - - flagSet.Func( - "build-id", - "", - func(string string) error { - buildID = &string - return nil - }, - ) - - return Subcommand{ - flagSet: flagSet, - handle: func(client *stainlessv0.Client) { - res, err := client.Builds.Get( - context.TODO(), - *buildID, - option.WithMiddleware(func(r *http.Request, mn option.MiddlewareNext) (*http.Response, error) { - r.URL.RawQuery = serializeQuery(query).Encode() - r.Header = serializeHeader(header) - return mn(r) - }), - ) - if err != nil { - fmt.Printf("%s\n", err) - os.Exit(1) - } - - fmt.Printf("%s\n", res.JSON.RawJSON()) - }, - } +var buildsCreate = cli.Command{ + Name: "create", + Usage: "Create a new build", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "project", + Action: getAPIFlagAction[string]("body", "project"), + }, + &cli.StringFlag{ + Name: "revision", + Action: getAPIFlagAction[string]("body", "revision"), + }, + &cli.BoolFlag{ + Name: "allow-empty", + Action: getAPIFlagAction[bool]("body", "allow_empty"), + }, + &cli.StringFlag{ + Name: "branch", + Action: getAPIFlagAction[string]("body", "branch"), + }, + &cli.StringFlag{ + Name: "commit-message", + Action: getAPIFlagAction[string]("body", "commit_message"), + }, + &cli.StringFlag{ + Name: "targets", + Action: getAPIFlagAction[string]("body", "targets.#"), + }, + &cli.StringFlag{ + Name: "+target", + Action: getAPIFlagAction[string]("body", "targets.-1"), + }, + }, + Before: initAPICommand, + Action: handleBuildsCreate, + HideHelpCommand: true, +} + +var buildsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Retrieve a build by ID", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "build-id", + }, + }, + Before: initAPICommand, + Action: handleBuildsRetrieve, + HideHelpCommand: true, +} + +var buildsList = cli.Command{ + Name: "list", + Usage: "List builds for a project", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "project", + Action: getAPIFlagAction[string]("query", "project"), + }, + &cli.StringFlag{ + Name: "branch", + Action: getAPIFlagAction[string]("query", "branch"), + }, + &cli.StringFlag{ + Name: "cursor", + Action: getAPIFlagAction[string]("query", "cursor"), + }, + &cli.FloatFlag{ + Name: "limit", + Action: getAPIFlagAction[float64]("query", "limit"), + }, + &cli.StringFlag{ + Name: "revision", + Action: getAPIFlagAction[string]("query", "revision"), + }, + }, + Before: initAPICommand, + Action: handleBuildsList, + HideHelpCommand: true, +} + +func handleBuildsCreate(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Builds.New( + context.TODO(), + stainlessv0.BuildNewParams{}, + option.WithMiddleware(cc.AsMiddleware()), + option.WithRequestBody("application/json", cc.body), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil +} + +func handleBuildsRetrieve(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Builds.Get( + context.TODO(), + cmd.Value("build-id").(string), + option.WithMiddleware(cc.AsMiddleware()), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil +} + +func handleBuildsList(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Builds.List( + context.TODO(), + stainlessv0.BuildListParams{}, + option.WithMiddleware(cc.AsMiddleware()), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil } diff --git a/buildtarget.go b/buildtarget.go deleted file mode 100644 index 572ad4a..0000000 --- a/buildtarget.go +++ /dev/null @@ -1,62 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -package main - -import ( - "context" - "flag" - "fmt" - "net/http" - "os" - - "github.com/stainless-sdks/stainless-v0-go" - "github.com/stainless-sdks/stainless-v0-go/option" -) - -func createBuildsTargetRetrieveSubcommand() (Subcommand) { - var buildID *string = nil - var targetName *string = nil - query := []byte("{}") - header := []byte("{}") - var flagSet = flag.NewFlagSet("builds.target.retrieve", flag.ExitOnError) - - flagSet.Func( - "build-id", - "", - func(string string) error { - buildID = &string - return nil - }, - ) - - flagSet.Func( - "target-name", - "", - func(string string) error { - targetName = &string - return nil - }, - ) - - return Subcommand{ - flagSet: flagSet, - handle: func(client *stainlessv0.Client) { - res, err := client.Builds.Target.Get( - context.TODO(), - *buildID, - stainlessv0.BuildTargetGetParamsTargetName(*targetName), - option.WithMiddleware(func(r *http.Request, mn option.MiddlewareNext) (*http.Response, error) { - r.URL.RawQuery = serializeQuery(query).Encode() - r.Header = serializeHeader(header) - return mn(r) - }), - ) - if err != nil { - fmt.Printf("%s\n", err) - os.Exit(1) - } - - fmt.Printf("%s\n", res.JSON.RawJSON()) - }, - } -} diff --git a/buildtargetartifact.go b/buildtargetartifact.go deleted file mode 100644 index 889f897..0000000 --- a/buildtargetartifact.go +++ /dev/null @@ -1,62 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -package main - -import ( - "context" - "flag" - "fmt" - "net/http" - "os" - - "github.com/stainless-sdks/stainless-v0-go" - "github.com/stainless-sdks/stainless-v0-go/option" -) - -func createBuildsTargetArtifactsRetrieveSourceSubcommand() (Subcommand) { - var buildID *string = nil - var targetName *string = nil - query := []byte("{}") - header := []byte("{}") - var flagSet = flag.NewFlagSet("builds.target.artifacts.retrieve_source", flag.ExitOnError) - - flagSet.Func( - "build-id", - "", - func(string string) error { - buildID = &string - return nil - }, - ) - - flagSet.Func( - "target-name", - "", - func(string string) error { - targetName = &string - return nil - }, - ) - - return Subcommand{ - flagSet: flagSet, - handle: func(client *stainlessv0.Client) { - res, err := client.Builds.Target.Artifacts.GetSource( - context.TODO(), - *buildID, - stainlessv0.BuildTargetArtifactGetSourceParamsTargetName(*targetName), - option.WithMiddleware(func(r *http.Request, mn option.MiddlewareNext) (*http.Response, error) { - r.URL.RawQuery = serializeQuery(query).Encode() - r.Header = serializeHeader(header) - return mn(r) - }), - ) - if err != nil { - fmt.Printf("%s\n", err) - os.Exit(1) - } - - fmt.Printf("%s\n", res.JSON.RawJSON()) - }, - } -} diff --git a/buildtargetoutput.go b/buildtargetoutput.go new file mode 100644 index 0000000..f264103 --- /dev/null +++ b/buildtargetoutput.go @@ -0,0 +1,55 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package main + +import ( + "context" + "fmt" + "os" + + "github.com/stainless-api/stainless-api-go" + "github.com/stainless-api/stainless-api-go/option" + "github.com/urfave/cli/v3" +) + +var buildTargetOutputsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Download the output of a build target", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "build-id", + Action: getAPIFlagAction[string]("query", "build_id"), + }, + &cli.StringFlag{ + Name: "target", + Action: getAPIFlagAction[string]("query", "target"), + }, + &cli.StringFlag{ + Name: "type", + Action: getAPIFlagAction[string]("query", "type"), + }, + &cli.StringFlag{ + Name: "output", + Action: getAPIFlagAction[string]("query", "output"), + }, + }, + Before: initAPICommand, + Action: handleBuildTargetOutputsRetrieve, + HideHelpCommand: true, +} + +func handleBuildTargetOutputsRetrieve(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.BuildTargetOutputs.Get( + context.TODO(), + stainlessv0.BuildTargetOutputGetParams{}, + option.WithMiddleware(cc.AsMiddleware()), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil +} diff --git a/completions.fish b/completions.fish deleted file mode 100644 index 39e5600..0000000 --- a/completions.fish +++ /dev/null @@ -1,32 +0,0 @@ -set -l subcommands projects.config.create_branch projects.config.create_commit projects.config.merge builds.retrieve builds.target.retrieve builds.target.artifacts.retrieve_source -complete -c stainless-v0-cli --no-files \ - -n "not __fish_seen_subcommand_from $subcommands" \ - -a "$subcommands" - -complete -c stainless-v0-cli --no-files \ - -n "__fish_seen_subcommand_from projects.config.create_branch" \ - -a "--project-name --branch --branch-from" -complete -c stainless-v0-cli --no-files \ - -n "__fish_seen_subcommand_from projects.config.create_commit" \ - -a "--project-name --branch --commit-message --openapi-spec --stainless-config --allow-empty" -complete -c stainless-v0-cli --no-files \ - -n "__fish_seen_subcommand_from projects.config.merge" \ - -a "--project-name --from --into" -complete -c stainless-v0-cli --no-files \ - -n "__fish_seen_subcommand_from builds.retrieve" \ - -a "--build-id" -complete -c stainless-v0-cli --no-files \ - -n "__fish_seen_subcommand_from builds.target.retrieve" \ - -a "--build-id --target-name" -complete -c stainless-v0-cli --no-files \ - -n "__fish_seen_subcommand_from builds.target.artifacts.retrieve_source" \ - -a "--build-id --target-name" - - complete -c stainless-v0-cli --no-files \ - -n "__fish_seen_subcommand_from builds.target.retrieve" \ - -l target-name \ - -ra "node typescript python go java kotlin ruby terraform cli" - complete -c stainless-v0-cli --no-files \ - -n "__fish_seen_subcommand_from builds.target.artifacts.retrieve_source" \ - -l target-name \ - -ra "node typescript python go java kotlin ruby terraform cli" \ No newline at end of file diff --git a/completions.sh b/completions.sh deleted file mode 100755 index 5f373e5..0000000 --- a/completions.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash - -# This also works for zsh: https://zsh.sourceforge.io/Doc/Release/Completion-System.html#Completion-System -_main() -{ - COMPREPLY=() - - local subcommands="projects.config.create_branch projects.config.create_commit projects.config.merge builds.retrieve builds.target.retrieve builds.target.artifacts.retrieve_source" - - if [[ "$COMP_CWORD" -eq 1 ]] - then - local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=( $(compgen -W "$subcommands" -- "$cur") ) - return - fi - - local subcommand="${COMP_WORDS[1]}" - local flags - case "$subcommand" in - projects.config.create_branch) - flags="--project-name --branch --branch-from" - ;; - projects.config.create_commit) - flags="--project-name --branch --commit-message --openapi-spec --stainless-config --allow-empty" - ;; - projects.config.merge) - flags="--project-name --from --into" - ;; - builds.retrieve) - flags="--build-id" - ;; - builds.target.retrieve) - flags="--build-id --target-name" - ;; - builds.target.artifacts.retrieve_source) - flags="--build-id --target-name" - ;; - *) - # Unknown subcommand - return - ;; - esac - - local cur="${COMP_WORDS[COMP_CWORD]}" - if [[ "$COMP_CWORD" -eq 2 || $cur == -* ]] ; then - COMPREPLY=( $(compgen -W "$flags" -- $cur) ) - return 0 - fi - - local prev="${COMP_WORDS[COMP_CWORD-1]}" - case "$subcommand" in - builds.target.retrieve) - case "$prev" in - --target-name) - COMPREPLY=( $(compgen -W "node typescript python go java kotlin ruby terraform cli" -- $cur) ) - ;; - esac - ;; - builds.target.artifacts.retrieve_source) - case "$prev" in - --target-name) - COMPREPLY=( $(compgen -W "node typescript python go java kotlin ruby terraform cli" -- $cur) ) - ;; - esac - ;; - esac -} -complete -F _main stainless-v0-cli \ No newline at end of file diff --git a/go.mod b/go.mod index 5a4eb4c..e719421 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,19 @@ -module github.com/stainless-sdks/stainless-v0-cli +module github.com/stainless-api/stainless-api-cli -go 1.21 +go 1.23.0 + +toolchain go1.23.8 + +require ( + github.com/stainless-api/stainless-api-go v0.2.0 + github.com/tidwall/gjson v1.17.0 + github.com/tidwall/pretty v1.2.1 + github.com/tidwall/sjson v1.2.5 + github.com/urfave/cli/v3 v3.3.2 + golang.org/x/term v0.31.0 +) require ( - github.com/stainless-sdks/stainless-v0-go v0.1.0 + github.com/tidwall/match v1.1.1 // indirect + golang.org/x/sys v0.32.0 // indirect ) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7c545fe --- /dev/null +++ b/go.sum @@ -0,0 +1,26 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stainless-api/stainless-api-go v0.2.0 h1:cHJvOPc0jONqwEiXKeMnG2ihEeJWZPcU3zyhe1rCB+4= +github.com/stainless-api/stainless-api-go v0.2.0/go.mod h1:9Q2t8xq6EFgw8HYOsVxqKEfSDVe9eqCoh1zC0HMRwTY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/urfave/cli/v3 v3.3.2 h1:BYFVnhhZ8RqT38DxEYVFPPmGFTEf7tJwySTXsVRrS/o= +github.com/urfave/cli/v3 v3.3.2/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index b37ec5f..ff25dea 100644 --- a/main.go +++ b/main.go @@ -3,59 +3,79 @@ package main import ( - "flag" - "fmt" - "log" - "os" + "context" + "log" + "os" - "github.com/stainless-sdks/stainless-v0-go" + "github.com/urfave/cli/v3" ) func main() { - if len(os.Args) < 2 { - fmt.Println("Expected subcommand") - os.Exit(1) - } - - subcommand := subcommands[os.Args[1]] - if subcommand == nil { - log.Fatalf("Unknown subcommand '%s'", os.Args[1]) - } - - subcommand.flagSet.Parse(os.Args[2:]) - - var client *stainlessv0.Client = stainlessv0.NewClient() - subcommand.handle(client) -} - -func init() { - initialBody := getStdInput() - if initialBody == nil { - initialBody = []byte("{}") - } - - var projectsConfigCreateBranchSubcommand = createProjectsConfigCreateBranchSubcommand(initialBody) - subcommands[projectsConfigCreateBranchSubcommand.flagSet.Name()] = &projectsConfigCreateBranchSubcommand - - var projectsConfigCreateCommitSubcommand = createProjectsConfigCreateCommitSubcommand(initialBody) - subcommands[projectsConfigCreateCommitSubcommand.flagSet.Name()] = &projectsConfigCreateCommitSubcommand - - var projectsConfigMergeSubcommand = createProjectsConfigMergeSubcommand(initialBody) - subcommands[projectsConfigMergeSubcommand.flagSet.Name()] = &projectsConfigMergeSubcommand - - var buildsRetrieveSubcommand = createBuildsRetrieveSubcommand() - subcommands[buildsRetrieveSubcommand.flagSet.Name()] = &buildsRetrieveSubcommand - - var buildsTargetRetrieveSubcommand = createBuildsTargetRetrieveSubcommand() - subcommands[buildsTargetRetrieveSubcommand.flagSet.Name()] = &buildsTargetRetrieveSubcommand - - var buildsTargetArtifactsRetrieveSourceSubcommand = createBuildsTargetArtifactsRetrieveSourceSubcommand() - subcommands[buildsTargetArtifactsRetrieveSourceSubcommand.flagSet.Name()] = &buildsTargetArtifactsRetrieveSourceSubcommand -} - -var subcommands = map[string]*Subcommand{} - -type Subcommand struct { - flagSet *flag.FlagSet - handle func(*stainlessv0.Client) + app := &cli.Command{ + Name: "stainless-api-cli", + Usage: "CLI for the stainless-v0 API", + Commands: []*cli.Command{ + { + Name: "projects", + Commands: []*cli.Command{ + &projectsRetrieve, + &projectsUpdate, + &projectsList, + }, + }, + + { + Name: "projects:branches", + Commands: []*cli.Command{ + &projectsBranchesCreate, + &projectsBranchesRetrieve, + }, + }, + + { + Name: "projects:configs", + Commands: []*cli.Command{ + &projectsConfigsRetrieve, + &projectsConfigsGuess, + }, + }, + + { + Name: "projects:snippets", + Commands: []*cli.Command{ + &projectsSnippetsCreateRequest, + }, + }, + + { + Name: "builds", + Commands: []*cli.Command{ + &buildsCreate, + &buildsRetrieve, + &buildsList, + }, + }, + + { + Name: "build_target_outputs", + Commands: []*cli.Command{ + &buildTargetOutputsRetrieve, + }, + }, + + { + Name: "orgs", + Commands: []*cli.Command{ + &orgsRetrieve, + &orgsList, + }, + }, + }, + EnableShellCompletion: true, + HideHelpCommand: true, + } + + if err := app.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + } } diff --git a/org.go b/org.go new file mode 100644 index 0000000..a20fe23 --- /dev/null +++ b/org.go @@ -0,0 +1,62 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package main + +import ( + "context" + "fmt" + "os" + + "github.com/stainless-api/stainless-api-go/option" + "github.com/urfave/cli/v3" +) + +var orgsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Retrieve an organization by name", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "org-name", + }, + }, + Before: initAPICommand, + Action: handleOrgsRetrieve, + HideHelpCommand: true, +} + +var orgsList = cli.Command{ + Name: "list", + Usage: "List organizations the user has access to", + Flags: []cli.Flag{}, + Before: initAPICommand, + Action: handleOrgsList, + HideHelpCommand: true, +} + +func handleOrgsRetrieve(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Orgs.Get( + context.TODO(), + cmd.Value("org-name").(string), + option.WithMiddleware(cc.AsMiddleware()), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil +} + +func handleOrgsList(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Orgs.List(context.TODO(), option.WithMiddleware(cc.AsMiddleware())) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil +} diff --git a/project.go b/project.go new file mode 100644 index 0000000..42e5e91 --- /dev/null +++ b/project.go @@ -0,0 +1,115 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package main + +import ( + "context" + "fmt" + "os" + + "github.com/stainless-api/stainless-api-go" + "github.com/stainless-api/stainless-api-go/option" + "github.com/urfave/cli/v3" +) + +var projectsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Retrieve a project by name", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "project-name", + }, + }, + Before: initAPICommand, + Action: handleProjectsRetrieve, + HideHelpCommand: true, +} + +var projectsUpdate = cli.Command{ + Name: "update", + Usage: "Update a project's properties", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "project-name", + }, + &cli.StringFlag{ + Name: "display-name", + Action: getAPIFlagAction[string]("body", "display_name"), + }, + }, + Before: initAPICommand, + Action: handleProjectsUpdate, + HideHelpCommand: true, +} + +var projectsList = cli.Command{ + Name: "list", + Usage: "List projects in an organization", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "org", + Action: getAPIFlagAction[string]("query", "org"), + }, + &cli.StringFlag{ + Name: "cursor", + Action: getAPIFlagAction[string]("query", "cursor"), + }, + &cli.FloatFlag{ + Name: "limit", + Action: getAPIFlagAction[float64]("query", "limit"), + }, + }, + Before: initAPICommand, + Action: handleProjectsList, + HideHelpCommand: true, +} + +func handleProjectsRetrieve(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Projects.Get( + context.TODO(), + cmd.Value("project-name").(string), + option.WithMiddleware(cc.AsMiddleware()), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil +} + +func handleProjectsUpdate(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Projects.Update( + context.TODO(), + cmd.Value("project-name").(string), + stainlessv0.ProjectUpdateParams{}, + option.WithMiddleware(cc.AsMiddleware()), + option.WithRequestBody("application/json", cc.body), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil +} + +func handleProjectsList(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Projects.List( + context.TODO(), + stainlessv0.ProjectListParams{}, + option.WithMiddleware(cc.AsMiddleware()), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil +} diff --git a/projectbranch.go b/projectbranch.go new file mode 100644 index 0000000..3571252 --- /dev/null +++ b/projectbranch.go @@ -0,0 +1,89 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package main + +import ( + "context" + "fmt" + "os" + + "github.com/stainless-api/stainless-api-go" + "github.com/stainless-api/stainless-api-go/option" + "github.com/urfave/cli/v3" +) + +var projectsBranchesCreate = cli.Command{ + Name: "create", + Usage: "Create a new branch for a project", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "project", + }, + &cli.StringFlag{ + Name: "branch", + Action: getAPIFlagAction[string]("body", "branch"), + }, + &cli.StringFlag{ + Name: "branch-from", + Action: getAPIFlagAction[string]("body", "branch_from"), + }, + &cli.BoolFlag{ + Name: "force", + Action: getAPIFlagAction[bool]("body", "force"), + }, + }, + Before: initAPICommand, + Action: handleProjectsBranchesCreate, + HideHelpCommand: true, +} + +var projectsBranchesRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Retrieve a project branch", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "project", + }, + &cli.StringFlag{ + Name: "branch", + }, + }, + Before: initAPICommand, + Action: handleProjectsBranchesRetrieve, + HideHelpCommand: true, +} + +func handleProjectsBranchesCreate(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Projects.Branches.New( + context.TODO(), + cmd.Value("project").(string), + stainlessv0.ProjectBranchNewParams{}, + option.WithMiddleware(cc.AsMiddleware()), + option.WithRequestBody("application/json", cc.body), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil +} + +func handleProjectsBranchesRetrieve(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Projects.Branches.Get( + context.TODO(), + cmd.Value("project").(string), + cmd.Value("branch").(string), + option.WithMiddleware(cc.AsMiddleware()), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil +} diff --git a/projectconfig.go b/projectconfig.go index 6abd898..3c1904b 100644 --- a/projectconfig.go +++ b/projectconfig.go @@ -3,249 +3,84 @@ package main import ( - "context" - "flag" - "fmt" - "net/http" - "os" + "context" + "fmt" + "os" - "github.com/stainless-sdks/stainless-v0-go" - "github.com/stainless-sdks/stainless-v0-go/option" + "github.com/stainless-api/stainless-api-go" + "github.com/stainless-api/stainless-api-go/option" + "github.com/urfave/cli/v3" ) -func createProjectsConfigCreateBranchSubcommand(initialBody []byte) (Subcommand) { - var projectName *string = nil - query := []byte("{}") - header := []byte("{}") - body := initialBody - var flagSet = flag.NewFlagSet("projects.config.create_branch", flag.ExitOnError) - - flagSet.Func( - "project-name", - "", - func(string string) error { - projectName = &string - return nil - }, - ) - - flagSet.Func( - "branch", - "", - func(string string) error { - var jsonErr error - body, jsonErr = jsonSet(body, "branch", string) - if jsonErr != nil { - return jsonErr - } - return nil - }, - ) - - flagSet.Func( - "branch-from", - "", - func(string string) error { - var jsonErr error - body, jsonErr = jsonSet(body, "branch_from", string) - if jsonErr != nil { - return jsonErr - } - return nil - }, - ) - - return Subcommand{ - flagSet: flagSet, - handle: func(client *stainlessv0.Client) { - res, err := client.Projects.Config.NewBranch( - context.TODO(), - *projectName, - stainlessv0.ProjectConfigNewBranchParams{}, - option.WithMiddleware(func(r *http.Request, mn option.MiddlewareNext) (*http.Response, error) { - r.URL.RawQuery = serializeQuery(query).Encode() - r.Header = serializeHeader(header) - return mn(r) - }), - option.WithRequestBody("application/json", body), - ) - if err != nil { - fmt.Printf("%s\n", err) - os.Exit(1) - } - - fmt.Printf("%s\n", res.JSON.RawJSON()) - }, - } +var projectsConfigsRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Retrieve configuration files for a project", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "project", + }, + &cli.StringFlag{ + Name: "branch", + Action: getAPIFlagAction[string]("query", "branch"), + }, + }, + Before: initAPICommand, + Action: handleProjectsConfigsRetrieve, + HideHelpCommand: true, } -func createProjectsConfigCreateCommitSubcommand(initialBody []byte) (Subcommand) { - var projectName *string = nil - query := []byte("{}") - header := []byte("{}") - body := initialBody - var flagSet = flag.NewFlagSet("projects.config.create_commit", flag.ExitOnError) - - flagSet.Func( - "project-name", - "", - func(string string) error { - projectName = &string - return nil - }, - ) - - flagSet.Func( - "branch", - "", - func(string string) error { - var jsonErr error - body, jsonErr = jsonSet(body, "branch", string) - if jsonErr != nil { - return jsonErr - } - return nil - }, - ) - - flagSet.Func( - "commit-message", - "", - func(string string) error { - var jsonErr error - body, jsonErr = jsonSet(body, "commit_message", string) - if jsonErr != nil { - return jsonErr - } - return nil - }, - ) - - flagSet.Func( - "openapi-spec", - "", - func(string string) error { - var jsonErr error - body, jsonErr = jsonSet(body, "openapi_spec", string) - if jsonErr != nil { - return jsonErr - } - return nil - }, - ) - - flagSet.Func( - "stainless-config", - "", - func(string string) error { - var jsonErr error - body, jsonErr = jsonSet(body, "stainless_config", string) - if jsonErr != nil { - return jsonErr - } - return nil - }, - ) - - flagSet.BoolFunc( - "allow-empty", - "", - func(_ string) error { - var jsonErr error - body, jsonErr = jsonSet(body, "allow_empty", true) - if jsonErr != nil { - return jsonErr - } - return nil - }, - ) - - return Subcommand{ - flagSet: flagSet, - handle: func(client *stainlessv0.Client) { - res, err := client.Projects.Config.NewCommit( - context.TODO(), - *projectName, - stainlessv0.ProjectConfigNewCommitParams{}, - option.WithMiddleware(func(r *http.Request, mn option.MiddlewareNext) (*http.Response, error) { - r.URL.RawQuery = serializeQuery(query).Encode() - r.Header = serializeHeader(header) - return mn(r) - }), - option.WithRequestBody("application/json", body), - ) - if err != nil { - fmt.Printf("%s\n", err) - os.Exit(1) - } - - fmt.Printf("%s\n", res.JSON.RawJSON()) - }, - } +var projectsConfigsGuess = cli.Command{ + Name: "guess", + Usage: "Generate configuration suggestions based on an OpenAPI spec", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "project", + }, + &cli.StringFlag{ + Name: "spec", + Action: getAPIFlagAction[string]("body", "spec"), + }, + &cli.StringFlag{ + Name: "branch", + Action: getAPIFlagAction[string]("body", "branch"), + }, + }, + Before: initAPICommand, + Action: handleProjectsConfigsGuess, + HideHelpCommand: true, } -func createProjectsConfigMergeSubcommand(initialBody []byte) (Subcommand) { - var projectName *string = nil - query := []byte("{}") - header := []byte("{}") - body := initialBody - var flagSet = flag.NewFlagSet("projects.config.merge", flag.ExitOnError) - - flagSet.Func( - "project-name", - "", - func(string string) error { - projectName = &string - return nil - }, - ) - - flagSet.Func( - "from", - "", - func(string string) error { - var jsonErr error - body, jsonErr = jsonSet(body, "from", string) - if jsonErr != nil { - return jsonErr - } - return nil - }, - ) - - flagSet.Func( - "into", - "", - func(string string) error { - var jsonErr error - body, jsonErr = jsonSet(body, "into", string) - if jsonErr != nil { - return jsonErr - } - return nil - }, - ) - - return Subcommand{ - flagSet: flagSet, - handle: func(client *stainlessv0.Client) { - res, err := client.Projects.Config.Merge( - context.TODO(), - *projectName, - stainlessv0.ProjectConfigMergeParams{}, - option.WithMiddleware(func(r *http.Request, mn option.MiddlewareNext) (*http.Response, error) { - r.URL.RawQuery = serializeQuery(query).Encode() - r.Header = serializeHeader(header) - return mn(r) - }), - option.WithRequestBody("application/json", body), - ) - if err != nil { - fmt.Printf("%s\n", err) - os.Exit(1) - } +func handleProjectsConfigsRetrieve(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Projects.Configs.Get( + context.TODO(), + cmd.Value("project").(string), + stainlessv0.ProjectConfigGetParams{}, + option.WithMiddleware(cc.AsMiddleware()), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil +} - fmt.Printf("%s\n", res.JSON.RawJSON()) - }, - } +func handleProjectsConfigsGuess(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Projects.Configs.Guess( + context.TODO(), + cmd.Value("project").(string), + stainlessv0.ProjectConfigGuessParams{}, + option.WithMiddleware(cc.AsMiddleware()), + option.WithRequestBody("application/json", cc.body), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil } diff --git a/projectsnippet.go b/projectsnippet.go new file mode 100644 index 0000000..c78fc4c --- /dev/null +++ b/projectsnippet.go @@ -0,0 +1,80 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package main + +import ( + "context" + "fmt" + "os" + + "github.com/stainless-api/stainless-api-go" + "github.com/stainless-api/stainless-api-go/option" + "github.com/urfave/cli/v3" +) + +var projectsSnippetsCreateRequest = cli.Command{ + Name: "create_request", + Usage: "Perform create_request operation", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "project-name", + }, + &cli.StringFlag{ + Name: "language", + Action: getAPIFlagAction[string]("body", "language"), + }, + &cli.StringFlag{ + Name: "request.method", + Action: getAPIFlagAction[string]("body", "request.method"), + }, + &cli.StringFlag{ + Name: "request.parameters.in", + Action: getAPIFlagAction[string]("body", "request.parameters.#.in"), + }, + &cli.StringFlag{ + Name: "request.parameters.name", + Action: getAPIFlagAction[string]("body", "request.parameters.#.name"), + }, + &cli.BoolFlag{ + Name: "request.+parameter", + Action: getAPIFlagActionWithValue[bool]("body", "request.parameters.-1", map[string]interface{}{}), + }, + &cli.StringFlag{ + Name: "request.path", + Action: getAPIFlagAction[string]("body", "request.path"), + }, + &cli.StringFlag{ + Name: "request.body.fileParam", + Action: getAPIFlagAction[string]("body", "request.body.fileParam"), + }, + &cli.StringFlag{ + Name: "request.body.filePath", + Action: getAPIFlagAction[string]("body", "request.body.filePath"), + }, + &cli.StringFlag{ + Name: "version", + Action: getAPIFlagAction[string]("body", "version"), + }, + }, + Before: initAPICommand, + Action: handleProjectsSnippetsCreateRequest, + HideHelpCommand: true, +} + +func handleProjectsSnippetsCreateRequest(ctx context.Context, cmd *cli.Command) error { + cc := getAPICommandContext(ctx, cmd) + + res, err := cc.client.Projects.Snippets.NewRequest( + context.TODO(), + cmd.Value("project-name").(string), + stainlessv0.ProjectSnippetNewRequestParams{}, + option.WithMiddleware(cc.AsMiddleware()), + option.WithRequestBody("application/json", cc.body), + ) + if err != nil { + return err + } + + fmt.Printf("%s\n", colorizeJSON(res.RawJSON(), os.Stdout)) + return nil +} diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..00afa69 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,63 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "simple" +} \ No newline at end of file diff --git a/scripts/link b/scripts/link index 2b26860..afeb9ba 100755 --- a/scripts/link +++ b/scripts/link @@ -13,4 +13,4 @@ fi echo "==> Linking with local directory" go mod tidy -e -go mod edit -replace github.com/stainless-sdks/stainless-v0-go="$LOCAL_GO" +go mod edit -replace github.com/stainless-api/stainless-api-go="$LOCAL_GO" diff --git a/scripts/unlink b/scripts/unlink index db53cb2..c6eeaaa 100755 --- a/scripts/unlink +++ b/scripts/unlink @@ -5,4 +5,4 @@ set -e cd "$(dirname "$0")/.." echo "==> Unlinking with local directory" -go mod edit -dropreplace github.com/stainless-sdks/stainless-v0-go +go mod edit -dropreplace github.com/stainless-api/stainless-api-go diff --git a/util.go b/util.go index a0aa175..a2da634 100644 --- a/util.go +++ b/util.go @@ -1,6 +1,7 @@ package main import ( + "context" "io" "log" "net/http" @@ -9,28 +10,16 @@ import ( "strconv" "strings" + "github.com/stainless-api/stainless-api-go" + "github.com/stainless-api/stainless-api-go/option" + "github.com/tidwall/gjson" + "github.com/tidwall/pretty" "github.com/tidwall/sjson" + "github.com/urfave/cli/v3" + "golang.org/x/term" ) -func parseInt(string string) (int64, error) { - integer, err := strconv.ParseInt(string, 10, 64) - if err != nil { - return 0, err - } - - return integer, nil -} - -func parseFloat(string string) (float64, error) { - number, err := strconv.ParseFloat(string, 64) - if err != nil { - return 0, err - } - - return number, nil -} - func jsonSet(json []byte, path string, value interface{}) ([]byte, error) { keys := strings.Split(path, ".") path = "" @@ -48,65 +37,144 @@ func jsonSet(json []byte, path string, value interface{}) ([]byte, error) { return sjson.SetBytes(json, path, value) } +type apiCommandKey string + +type apiCommandContext struct { + client stainlessv0.Client + body []byte + query []byte + header []byte +} + +func (c apiCommandContext) AsMiddleware() option.Middleware { + return func(r *http.Request, mn option.MiddlewareNext) (*http.Response, error) { + q := r.URL.Query() + for key, values := range serializeQuery(c.query) { + for _, value := range values { + q.Add(key, value) + } + } + r.URL.RawQuery = q.Encode() + + for key, values := range serializeHeader(c.header) { + for _, value := range values { + r.Header.Add(key, value) + } + } + + return mn(r) + } +} + +func initAPICommand(ctx context.Context, cmd *cli.Command) (context.Context, error) { + client := stainlessv0.NewClient() + body := getStdInput() + if body == nil { + body = []byte("{}") + } + var query = []byte("{}") + var header = []byte("{}") + + return context.WithValue(ctx, apiCommandKey(cmd.Name), &apiCommandContext{client, body, query, header}), nil +} + +func getAPICommandContext(ctx context.Context, cmd *cli.Command) *apiCommandContext { + return ctx.Value(apiCommandKey(cmd.Name)).(*apiCommandContext) +} + +func getAPIFlagAction[T any](kind string, path string) func(context.Context, *cli.Command, T) error { + return func(ctx context.Context, cmd *cli.Command, value T) (err error) { + commandContext := getAPICommandContext(ctx, cmd) + var dest *[]byte + switch kind { + case "body": + dest = &commandContext.body + case "query": + dest = &commandContext.query + case "header": + dest = &commandContext.header + } + *dest, err = jsonSet(*dest, path, value) + return err + } +} + +func getAPIFlagActionWithValue[T any](kind string, path string, value interface{}) func(context.Context, *cli.Command, T) error { + return func(ctx context.Context, cmd *cli.Command, unusedValue T) (err error) { + commandContext := getAPICommandContext(ctx, cmd) + var dest *[]byte + switch kind { + case "body": + dest = &commandContext.body + case "query": + dest = &commandContext.query + case "header": + dest = &commandContext.header + } + *dest, err = jsonSet(*dest, path, value) + return err + } +} + func serializeQuery(params []byte) url.Values { - serialized := url.Values{} - - var serialize func(value gjson.Result, path string) - serialize = func(res gjson.Result, path string) { - if res.IsObject() { - for key, value := range res.Map() { - newPath := path - if len(newPath) == 0 { - newPath += key - } else { - newPath = "[" + key + "]" - } - - serialize(value, newPath) - } - } else if res.IsArray() { - for _, value := range res.Array() { - serialize(value, path) - } - } else { - serialized.Add(path, res.String()) - } - } - serialize(gjson.GetBytes(params, "@this"), "") - - for key, values := range serialized { - serialized.Set(key, strings.Join(values, ",")) - } - - return serialized + serialized := url.Values{} + + var serialize func(value gjson.Result, path string) + serialize = func(res gjson.Result, path string) { + if res.IsObject() { + for key, value := range res.Map() { + newPath := path + if len(newPath) == 0 { + newPath += key + } else { + newPath = "[" + key + "]" + } + + serialize(value, newPath) + } + } else if res.IsArray() { + for _, value := range res.Array() { + serialize(value, path) + } + } else { + serialized.Add(path, res.String()) + } + } + serialize(gjson.GetBytes(params, "@this"), "") + + for key, values := range serialized { + serialized.Set(key, strings.Join(values, ",")) + } + + return serialized } func serializeHeader(params []byte) http.Header { - serialized := http.Header{} - - var serialize func(value gjson.Result, path string) - serialize = func(res gjson.Result, path string) { - if res.IsObject() { - for key, value := range res.Map() { - newPath := path - if len(newPath) > 0 { - newPath += "." - } - newPath += key - - serialize(value, newPath) - } - } else if res.IsArray() { - for _, value := range res.Array() { - serialize(value, path) - } - } else { - serialized.Add(path, res.String()) - } - } - serialize(gjson.GetBytes(params, "@this"), "") - - return serialized + serialized := http.Header{} + + var serialize func(value gjson.Result, path string) + serialize = func(res gjson.Result, path string) { + if res.IsObject() { + for key, value := range res.Map() { + newPath := path + if len(newPath) > 0 { + newPath += "." + } + newPath += key + + serialize(value, newPath) + } + } else if res.IsArray() { + for _, value := range res.Array() { + serialize(value, path) + } + } else { + serialized.Add(path, res.String()) + } + } + serialize(gjson.GetBytes(params, "@this"), "") + + return serialized } func getStdInput() []byte { @@ -125,3 +193,38 @@ func isInputPiped() bool { stat, _ := os.Stdin.Stat() return (stat.Mode() & os.ModeCharDevice) == 0 } + +func isTerminal(w io.Writer) bool { + switch v := w.(type) { + case *os.File: + return term.IsTerminal(int(v.Fd())) + default: + return false + } +} + +func shouldUseColors(w io.Writer) bool { + force, ok := os.LookupEnv("FORCE_COLOR") + + if ok { + if force == "1" { + return true + } + if force == "0" { + return false + } + } + + if isTerminal(w) { + return true + } + return false + +} + +func colorizeJSON(input string, w io.Writer) string { + if !shouldUseColors(w) { + return input + } + return string(pretty.Color(pretty.Pretty([]byte(input)), nil)) +}