diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..1d81abb --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,103 @@ +name: Publish APIs & Clients +on: + push: + branches: [master] + +jobs: + docs: + name: Generate docs + runs-on: ubuntu-latest + steps: + - name: Set up Go 1.13 + uses: actions/setup-go@v2 + with: + go-version: 1.13 + id: go + + - name: Install Protoc + uses: arduino/setup-protoc@master + + - name: Check out this code + uses: actions/checkout@v2 + with: + path: services + + - name: Check out micro code + uses: actions/checkout@v2 + with: + repository: 'micro/micro' + path: 'micro' + ref: 'master' + + - name: Enable caching + uses: actions/cache@v2 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Install protoc gen micro plugin + working-directory: micro/cmd/protoc-gen-micro + run: | + go get -u github.com/golang/protobuf/protoc-gen-go + go install + + - name: Install redoc cli + run: | + # https://github.com/actions/virtual-environments/issues/599 + sudo npm install -g redoc-cli + + - name: Install openapi plugin + working-directory: micro/cmd/protoc-gen-openapi + run: | + go install + +# - name: Install hugo +# run: sudo snap install hugo --channel=extended + + - name: Generate openapi spec and publish the api + working-directory: services + run: | + go run cmd/publisher/main.go . + env: + MICRO_ADMIN_TOKEN: ${{ secrets.MICRO_ADMIN_TOKEN }} + +# - name: Deploy +# if: github.ref == 'refs/heads/master' +# uses: s0/git-publish-subdir-action@develop +# env: +# REPO: self +# BRANCH: gh-pages +# FOLDER: services/docs +# GITHUB_TOKEN: ${{ secrets.GH_PAT }} + + - name: Generate package + working-directory: services + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + go run cmd/clients/main.go . + + # publish to github first under micro/services + # .npmrc has settings for it + - uses: JS-DevTools/npm-publish@v1 + #if: github.ref == 'refs/heads/master' + with: + access: public + package: services/clients/ts/package.json + token: ${{ secrets.NPM_TOKEN }} + + # publish to npm m3o/services + - name: Change npm settings + working-directory: services + run: | + rm clients/ts/.npmrc + sed -i 's/micro/m3o/g' clients/ts/package.json + + - uses: JS-DevTools/npm-publish@v1 + #if: github.ref == 'refs/heads/master' + with: + access: public + package: services/clients/ts/package.json + token: ${{ secrets.NPM_SITE_TOKEN }} diff --git a/cmd/tsgen/gen_test.go b/cmd/clients/gen_test.go similarity index 100% rename from cmd/tsgen/gen_test.go rename to cmd/clients/gen_test.go diff --git a/cmd/tsgen/main.go b/cmd/clients/main.go similarity index 98% rename from cmd/tsgen/main.go rename to cmd/clients/main.go index ea20ac3..1407c41 100644 --- a/cmd/tsgen/main.go +++ b/cmd/clients/main.go @@ -16,11 +16,6 @@ import ( "github.com/stoewer/go-strcase" ) -const ( - postContentPath = "docs/hugo-tania/site/content/post" - docsURL = "services.m3o.com" -) - func main() { files, err := ioutil.ReadDir(os.Args[1]) if err != nil { diff --git a/cmd/publisher/README.md b/cmd/publisher/README.md new file mode 100644 index 0000000..0236da9 --- /dev/null +++ b/cmd/publisher/README.md @@ -0,0 +1,20 @@ +# API Publisher + +This scripts takes open api specs that are generated in each folder by `make proto` (see `api-users.json` and similar in each folder), and existing `README.md` and published the API. + +The readmes are taken verbatim and autogenerated client call examples are appended to them to produce an output readme, so there is no need to write curl or micro cli or any other examples. Focus on the describing the service in the readmes. + +Some rules on how to write protos so they nicely appear in the output of this script: + +- The request types (eg. `LoginRequest`) comments will be taken and used as a description for the endpoint (eg. `Login`) itself. This might change. +- The proto message field comments will be taken and displayed to craft them with care + +To provide example values use the following format: + +```shell +// rss feed name +// eg. a16z +string name = 1; +``` + +The part after the `eg. ` until the newline will be used as example value. diff --git a/cmd/publisher/main.go b/cmd/publisher/main.go new file mode 100644 index 0000000..1224cce --- /dev/null +++ b/cmd/publisher/main.go @@ -0,0 +1,127 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/getkin/kin-openapi/openapi3" +) + +func publishAPI(service, readme, openapiJSON, examplesJSON string, pricing map[string]int64) error { + client := &http.Client{} + + apiSpec := map[string]interface{}{ + "name": service, + "description": readme, + "open_api_json": openapiJSON, + "pricing": pricing, + "examples_json": examplesJSON, + } + + //Encode the data + postBody, _ := json.Marshal(map[string]interface{}{ + "api": apiSpec, + }) + rbody := bytes.NewBuffer(postBody) + + //Leverage Go's HTTP Post function to make request + req, err := http.NewRequest("POST", "https://api.m3o.com/publicapi/Publish", rbody) + + // Add auth headers here if needed + req.Header.Add("Authorization", `Bearer `+os.Getenv("MICRO_ADMIN_TOKEN")) + resp, err := client.Do(req) + + if err != nil { + return err + } + defer resp.Body.Close() + io.Copy(ioutil.Discard, resp.Body) + + return nil +} + +func main() { + files, err := ioutil.ReadDir(os.Args[1]) + if err != nil { + log.Fatal(err) + } + workDir, _ := os.Getwd() + + for _, f := range files { + if f.IsDir() && !strings.HasPrefix(f.Name(), ".") { + serviceDir := filepath.Join(workDir, f.Name()) + serviceFiles, err := ioutil.ReadDir(serviceDir) + if err != nil { + fmt.Println("Failed to read service dir", err) + os.Exit(1) + } + skip := false + for _, serviceFile := range serviceFiles { + if serviceFile.Name() == "skip" { + skip = true + } + } + if skip { + continue + } + + fmt.Println("Processing folder", serviceDir) + makeProto := exec.Command("make", "docs") + makeProto.Dir = serviceDir + fmt.Println(serviceDir) + outp, err := makeProto.CombinedOutput() + if err != nil { + fmt.Println("Failed to make docs", string(outp)) + os.Exit(1) + } + serviceName := f.Name() + dat, err := ioutil.ReadFile(filepath.Join(serviceDir, "README.md")) + if err != nil { + fmt.Println("Failed to read readme", string(outp)) + os.Exit(1) + } + + apiJSON := filepath.Join(serviceDir, "api-"+serviceName+".json") + js, err := ioutil.ReadFile(apiJSON) + if err != nil { + apiJSON := filepath.Join(serviceDir, "api-protobuf.json") + js, err = ioutil.ReadFile(apiJSON) + if err != nil { + fmt.Println("Failed to read json spec", err) + os.Exit(1) + } + } + spec := &openapi3.Swagger{} + err = json.Unmarshal(js, &spec) + if err != nil { + fmt.Println("Failed to unmarshal", err) + os.Exit(1) + } + + // not every service has examples + examples, _ := ioutil.ReadFile(filepath.Join(serviceDir, "examples.json")) + + pricingRaw, _ := ioutil.ReadFile(filepath.Join(serviceDir, "pricing.json")) + pricing := map[string]int64{} + if len(pricingRaw) > 0 { + json.Unmarshal(pricingRaw, &pricing) + } + + err = publishAPI(serviceName, string(dat), string(js), string(examples), pricing) + if err != nil { + fmt.Println("Failed to save data to publicapi service", err) + os.Exit(1) + } + } + } + +}