2019-11-07 09:44:14 +01:00

185 lines
4.7 KiB
Go

// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package mage
import (
"context"
"fmt"
"log"
"net"
"net/http"
"os"
"os/exec"
"os/signal"
"path/filepath"
"runtime"
"strconv"
"syscall"
"github.com/magefile/mage/sh"
"github.com/pkg/errors"
)
const (
elasticDocsRepoURL = "https://github.com/elastic/docs.git"
)
type docsBuilder struct{}
type asciidocParams struct {
name string
indexFile string
}
// DocsOption is a documentation generation option for controlling how the docs
// are built.
type DocsOption func(params *asciidocParams)
// DocsName specifies the documentation's name (default to BeatName).
func DocsName(name string) DocsOption {
return func(params *asciidocParams) {
params.name = name
}
}
// DocsIndexFile specifies the index file (defaults to docs/index.asciidoc).
func DocsIndexFile(file string) DocsOption {
return func(params *asciidocParams) {
params.indexFile = file
}
}
// Docs holds the utilities for building documentation.
var Docs = docsBuilder{}
// FieldDocs generates docs/fields.asciidoc from the specified fields.yml file.
func (docsBuilder) FieldDocs(fieldsYML string) error {
// Run the docs_collector.py script.
ve, err := PythonVirtualenv()
if err != nil {
return err
}
python, err := LookVirtualenvPath(ve, "python")
if err != nil {
return err
}
esBeats, err := ElasticBeatsDir()
if err != nil {
return err
}
// TODO: Port this script to Go.
log.Println(">> Generating docs/fields.asciidoc for", BeatName)
return sh.Run(python, LibbeatDir("scripts/generate_fields_docs.py"),
fieldsYML, // Path to fields.yml.
BeatName, // Beat title.
esBeats, // Path to general beats folder.
"--output_path", OSSBeatDir()) // It writes to {output_path}/docs/fields.asciidoc.
}
func (b docsBuilder) AsciidocBook(opts ...DocsOption) error {
params := asciidocParams{
name: BeatName,
indexFile: CWD("docs/index.asciidoc"),
}
for _, opt := range opts {
opt(&params)
}
repo, err := GetProjectRepoInfo()
if err != nil {
return err
}
cloneDir := CreateDir(filepath.Join(repo.RootDir, "build/elastic_docs_repo"))
// Clone if elastic_docs_repo does not exist.
if _, err := os.Stat(cloneDir); err != nil {
log.Println("Cloning elastic/docs to", cloneDir)
if err = sh.Run("git", "clone", "--depth=1", elasticDocsRepoURL, cloneDir); err != nil {
return err
}
} else {
log.Println("Using existing elastic/docs at", cloneDir)
}
// Render HTML.
htmlDir := CWD("build/html_docs", params.name)
buildDocsScript := filepath.Join(cloneDir, "build_docs")
args := []string{
"--chunk=1",
"--doc", params.indexFile,
"--out", htmlDir,
}
fmt.Println(">> Building HTML docs at", filepath.Join(htmlDir, "index.html"))
if err := sh.Run(buildDocsScript, args...); err != nil {
return err
}
// Serve docs with and HTTP server and open the browser.
if preview, _ := strconv.ParseBool(os.Getenv("PREVIEW")); preview {
srv := b.servePreview(htmlDir)
url := "http://" + srv.Addr
fmt.Println("Serving docs preview at", url)
b.openBrowser(url)
// Wait
fmt.Println("Ctrl+C to stop")
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
<-sigs
srv.Shutdown(context.Background())
}
return nil
}
// open opens the specified URL in the default browser.
func (docsBuilder) openBrowser(url string) error {
var cmd string
var args []string
switch runtime.GOOS {
case "darwin":
cmd = "open"
case "windows":
cmd = "cmd"
args = []string{"/c", "start"}
default:
cmd = "xdg-open"
}
args = append(args, url)
return exec.Command(cmd, args...).Start()
}
func (docsBuilder) servePreview(dir string) *http.Server {
server := &http.Server{
Addr: net.JoinHostPort("localhost", EnvOr("PREVIEW_PORT", "8000")),
Handler: http.FileServer(http.Dir(dir)),
}
go func() {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
panic(errors.Wrap(err, "failed to start docs preview"))
}
}()
return server
}