Files
plugin-drone-npm/plugin.go
T
2016-05-25 12:16:26 -07:00

236 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"os/exec"
"path"
"strings"
log "github.com/Sirupsen/logrus"
)
type (
Config struct {
Username string
Password string
Email string
Registry string
Folder string
AlwaysAuth bool
}
NpmPackage struct {
Name string `json:"name"`
Version string `json:"version"`
}
Plugin struct {
Config Config
}
)
const GlobalRegistry = "https://registry.npmjs.org"
func (p Plugin) Exec() error {
// read the package
packagePath := path.Join(p.Config.Folder, "package.json")
npmPackage, err := readPackageFile(packagePath)
if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Error("Could not read package.json")
return err
}
log.WithFields(log.Fields{
"name": npmPackage.Name,
"version": npmPackage.Version,
}).Info("Found package")
// see if the package should be published
publish, err := shouldPublishPackage(p.Config, npmPackage)
if publish {
log.Info("Attempting to publish package")
// write the npmrc file
err := writeNpmrcFile(p.Config)
if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Error("Problem creating npmrc file")
return err
}
var cmds []*exec.Cmd
// write registry command
if p.Config.Registry != GlobalRegistry {
cmds = append(cmds, registryCommand(p.Config.Registry))
}
// write auth command
if p.Config.AlwaysAuth {
cmds = append(cmds, alwaysAuthCommand())
}
// write the publish command
cmds = append(cmds, publishCommand())
// run the commands
err = runCommands(cmds)
if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Error("Package was not published")
}
} else {
log.WithFields(log.Fields{
"reason": err,
}).Info("Package will not be published")
}
return nil
}
/// Reads the package file at the given path
func readPackageFile(path string) (*NpmPackage, error) {
// read the file
file, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
// unmarshal the json data
npmPackage := NpmPackage{}
err = json.Unmarshal(file, &npmPackage)
if err != nil {
return nil, err
}
// make sure values are present
if len(npmPackage.Name) == 0 {
return nil, errors.New("No package name present")
}
if len(npmPackage.Version) == 0 {
return nil, errors.New("No package version present")
}
return &npmPackage, nil
}
/// Determines if the package should be published
func shouldPublishPackage(config Config, npmPackage *NpmPackage) (bool, error) {
// get the url for the package
//
// encoding the portion of the url for the package name in case of scopes
packageUrl := fmt.Sprintf("%s/%s/%s", config.Registry, url.QueryEscape(npmPackage.Name), npmPackage.Version)
log.WithFields(log.Fields{
"url": packageUrl,
}).Info("Requesting package information")
// create a request for the package
req, err := http.NewRequest("GET", packageUrl, nil)
if err != nil {
return false, err
}
// set authentication if needed
// NPM uses basic http auth
if config.AlwaysAuth {
req.SetBasicAuth(config.Username, config.Password)
}
// get the response
resp, err := http.DefaultClient.Do(req)
if err != nil {
return false, err
}
defer resp.Body.Close()
statusCode := resp.StatusCode
// look for a 404 to see if the package should be published
if statusCode == http.StatusNotFound {
return true, nil
} else if statusCode == http.StatusOK {
return false, errors.New("Package already published")
} else {
contents, err := ioutil.ReadAll(resp.Body)
if err != nil {
return false, err
}
return false, errors.New(fmt.Sprintf("Error Occurred. Status %d\nBody:%s", statusCode, contents))
}
}
/// Writes the npmrc file
func writeNpmrcFile(config Config) error {
// get the base64 encoded string
authString := fmt.Sprintf("%s:%s", config.Username, config.Password)
encoded := base64.StdEncoding.EncodeToString([]byte(authString))
// create the file contents
contents := fmt.Sprintf("_auth = %s\nemail = %s", encoded, config.Email)
// write the file
return ioutil.WriteFile("/root/.npmrc", []byte(contents), 0644)
}
// Sets the npm registry
func registryCommand(registry string) *exec.Cmd {
return exec.Command("npm", "config", "set", "registry", registry)
}
// Sets the always off flag
func alwaysAuthCommand() *exec.Cmd {
return exec.Command("npm", "config", "set", "always-auth", "true")
}
// Publishes the package
func publishCommand() *exec.Cmd {
return exec.Command("npm", "publish")
}
// Trace writes each command to standard error (preceded by a $ ) before it
// is executed. Used for debugging your build.
func trace(cmd *exec.Cmd) {
fmt.Fprintf(os.Stdout, "+ %s\n", strings.Join(cmd.Args, " "))
}
func runCommands(cmds []*exec.Cmd) error {
for _, cmd := range cmds {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
trace(cmd)
err := cmd.Run()
if err != nil {
return err
}
}
return nil
}