Add comments to all exported symbols, and other golint fixes

This commit is contained in:
Evan Elias 2016-11-09 02:31:39 -05:00
Родитель 8028adf2cf
Коммит cf6370373b
11 изменённых файлов: 90 добавлений и 41 удалений

Просмотреть файл

@ -14,7 +14,7 @@ Skeema supports a pull-request-based workflow for schema change submission, revi
Requires the [Go programming language toolchain](https://golang.org/dl/).
To download, build, and install skeema, run:
To download, build, and install Skeema, run:
`go get github.com/skeema/skeema`
@ -30,7 +30,9 @@ To download, build, and install skeema, run:
Skeema is currently in public alpha. Many edge cases are not yet supported, but are coming soon. Testing has primarily been performed against MySQL 5.6 and Percona Server 5.6 so far.
Likewise, several rare MySQL column types and InnoDB features (compression, partitioning, etc) are not yet supported. Skeema is able to *create* or *drop* tables using these features, but not *alter* them. The output of `skeema diff` and `skeema push` clearly displays when this is the case. You may still make such alters directly/manually (outside of Skeema), and then update the corresponding CREATE TABLE files via `skeema pull`.
Skeema is primarily being tested on Linux and macOS. For now, it cannot be compiled on Windows.
Several InnoDB features (compression, partitioning, etc) and rare/new MySQL column types are not yet supported. Skeema is able to *create* or *drop* tables using these features, but not *alter* them. The output of `skeema diff` and `skeema push` clearly displays when this is the case. You may still make such alters directly/manually (outside of Skeema), and then update the corresponding CREATE TABLE files via `skeema pull`.
## Authors

Просмотреть файл

@ -26,6 +26,7 @@ socket path.`
CommandSuite.AddSubCommand(cmd)
}
// AddEnvHandler is the handler method for `skeema add-environment`
func AddEnvHandler(cfg *mycli.Config) error {
AddGlobalConfigFiles(cfg)

Просмотреть файл

@ -33,6 +33,7 @@ differences were found, or 2+ if an error occurred.`
CommandSuite.AddSubCommand(cmd)
}
// DiffHandler is the handler method for `skeema diff`
func DiffHandler(cfg *mycli.Config) error {
AddGlobalConfigFiles(cfg)
dir, err := NewDir(".", cfg)

22
dir.go
Просмотреть файл

@ -12,6 +12,7 @@ import (
"github.com/skeema/tengo"
)
// Dir represents a directory that Skeema is interacting with.
type Dir struct {
Path string
Config *mycli.Config // Unified config including this dir's options file (and its parents' open files)
@ -59,10 +60,12 @@ func (dir *Dir) String() string {
return dir.Path
}
// BaseName returns the name of the directory without the rest of its path.
func (dir *Dir) BaseName() string {
return path.Base(dir.Path)
}
// CreateIfMissing creates the directory if it does not yet exist.
func (dir *Dir) CreateIfMissing() (created bool, err error) {
fi, err := os.Stat(dir.Path)
if err == nil {
@ -88,23 +91,34 @@ func (dir *Dir) Exists() bool {
return (err == nil)
}
// Delete unlinks the directory and all files within.
func (dir *Dir) Delete() error {
return os.RemoveAll(dir.Path)
}
// HasFile returns true if the specifed filename exists in dir.
func (dir *Dir) HasFile(name string) bool {
_, err := os.Stat(path.Join(dir.Path, name))
return (err == nil)
}
// HasOptionFile returns true if the directory contains a .skeema option file.
func (dir *Dir) HasOptionFile() bool {
return dir.HasFile(".skeema")
}
// HasHost returns true if the "host" configuration option has been defined for
// this directory's configuration (typically either in this directory's .skeema
// file, or in an ancestor directory .skeema file) for the current environment
// name.
func (dir *Dir) HasHost() bool {
return dir.Config.Changed("host")
}
// HasSchema returns true if the "schema" configuration option has been defined
// for this directory's configuration (typically just in this directory's
// .skeema file, since directories containing "schema" should not have subdirs
// of their own) for the current environment name.
func (dir *Dir) HasSchema() bool {
return dir.Config.Changed("schema")
}
@ -227,7 +241,7 @@ func (dir *Dir) Subdirs() ([]*Dir, error) {
return result, nil
}
// Subdir creates and returns a new subdir of the current dir.
// CreateSubdir creates and returns a new subdir of the current dir.
func (dir *Dir) CreateSubdir(name string, optionFile *mycli.File) (*Dir, error) {
subdir := &Dir{
Path: path.Join(dir.Path, name),
@ -249,6 +263,8 @@ func (dir *Dir) CreateSubdir(name string, optionFile *mycli.File) (*Dir, error)
return subdir, nil
}
// CreateOptionFile writes the supplied unwritten option file to this dir, and
// then adds it as a source for this dir's configuration.
func (dir *Dir) CreateOptionFile(optionFile *mycli.File) error {
optionFile.Dir = dir.Path
if err := optionFile.Write(false); err != nil {
@ -259,6 +275,10 @@ func (dir *Dir) CreateOptionFile(optionFile *mycli.File) error {
return nil
}
// Targets returns a channel for obtaining Target objects for this dir.
// The expand args do not yet have an effect, but will eventually control
// whether multi-host and multi-schema option values are expanded to all
// combinations vs just generating the first host and schema.
func (dir *Dir) Targets(expandInstances, expandSchemas bool) <-chan Target {
targets := make(chan Target)
go func() {

13
init.go
Просмотреть файл

@ -35,6 +35,7 @@ section of the file.`
CommandSuite.AddSubCommand(cmd)
}
// InitHandler is the handler method for `skeema init`
func InitHandler(cfg *mycli.Config) error {
AddGlobalConfigFiles(cfg)
@ -148,6 +149,12 @@ func InitHandler(cfg *mycli.Config) error {
return nil
}
// PopulateSchemaDir writes out *.sql files for all tables in the specified
// schema. If makeSubdir==true, a subdir with name matching the schema name
// will be created, and a .skeem option file will be created. Otherwise, the
// *.sql files will be put in parentDir, and it will be the caller's
// responsibility to ensure its .skeema option file exists and maps to the
// correct schema name.
func PopulateSchemaDir(s *tengo.Schema, parentDir *Dir, makeSubdir bool) error {
// Ignore any attempt to populate a dir for the temp schema
if s.Name == parentDir.Config.Get("temp-schema") {
@ -193,11 +200,11 @@ func PopulateSchemaDir(s *tengo.Schema, parentDir *Dir, makeSubdir bool) error {
FileName: fmt.Sprintf("%s.sql", t.Name),
Contents: createStmt,
}
if length, err := sf.Write(); err != nil {
var length int
if length, err = sf.Write(); err != nil {
return NewExitValue(CodeCantCreate, "Unable to write to %s: %s", sf.Path(), err)
} else {
fmt.Printf(" Wrote %s (%d bytes)\n", sf.Path(), length)
}
fmt.Printf(" Wrote %s (%d bytes)\n", sf.Path(), length)
}
return nil
}

Просмотреть файл

@ -31,6 +31,7 @@ file had SQL syntax errors or some other error occurred.`
CommandSuite.AddSubCommand(cmd)
}
// LintHandler is the handler method for `skeema lint`
func LintHandler(cfg *mycli.Config) error {
AddGlobalConfigFiles(cfg)
dir, err := NewDir(".", cfg)
@ -64,12 +65,12 @@ func LintHandler(cfg *mycli.Config) error {
}
if table.CreateStatement() != sf.Contents {
sf.Contents = table.CreateStatement()
if length, err := sf.Write(); err != nil {
var length int
if length, err = sf.Write(); err != nil {
return fmt.Errorf("Unable to write to %s: %s", sf.Path(), err)
} else {
fmt.Printf(" Wrote %s (%d bytes) -- updated file to normalize format\n", sf.Path(), length)
reformatCount++
}
fmt.Printf(" Wrote %s (%d bytes) -- updated file to normalize format\n", sf.Path(), length)
reformatCount++
}
}
}

13
pull.go
Просмотреть файл

@ -28,6 +28,7 @@ top of the file. If no environment name is supplied, the default is
CommandSuite.AddSubCommand(cmd)
}
// PullHandler is the handler method for `skeema pull`
func PullHandler(cfg *mycli.Config) error {
AddGlobalConfigFiles(cfg)
dir, err := NewDir(".", cfg)
@ -121,11 +122,11 @@ func PullHandler(cfg *mycli.Config) error {
FileName: fmt.Sprintf("%s.sql", table.Name),
Contents: createStmt,
}
if length, err := sf.Write(); err != nil {
var length int
if length, err = sf.Write(); err != nil {
return fmt.Errorf("Unable to write to %s: %s", sf.Path(), err)
} else {
fmt.Printf(" Wrote %s (%d bytes) -- updated file to reflect table alterations\n", sf.Path(), length)
}
fmt.Printf(" Wrote %s (%d bytes) -- updated file to reflect table alterations\n", sf.Path(), length)
case tengo.RenameTable:
panic(fmt.Errorf("Table renames not yet supported!"))
default:
@ -144,11 +145,11 @@ func PullHandler(cfg *mycli.Config) error {
}
if table.CreateStatement() != sf.Contents {
sf.Contents = table.CreateStatement()
if length, err := sf.Write(); err != nil {
var length int
if length, err = sf.Write(); err != nil {
return fmt.Errorf("Unable to write to %s: %s", sf.Path(), err)
} else {
fmt.Printf(" Wrote %s (%d bytes) -- updated file to normalize format\n", sf.Path(), length)
}
fmt.Printf(" Wrote %s (%d bytes) -- updated file to normalize format\n", sf.Path(), length)
}
}
}

Просмотреть файл

@ -31,6 +31,7 @@ top of the file. If no environment name is supplied, the default is
CommandSuite.AddSubCommand(cmd)
}
// PushHandler is the handler method for `skeema push`
func PushHandler(cfg *mycli.Config) error {
AddGlobalConfigFiles(cfg)
dir, err := NewDir(".", cfg)

Просмотреть файл

@ -1,6 +1,7 @@
package main
import (
"errors"
"fmt"
"os"
"path"
@ -16,10 +17,14 @@ const version = "0.1 (pre-release)"
const rootDesc = `Skeema is a MySQL schema management tool. It allows you to export a database
schema to the filesystem, and apply online schema changes by modifying files.`
// Root command suite is global. Subcommands get populated by init() functions
// in each command's source file.
// CommandSuite is the root command. It is global so that subcommands can be
// added to it via init() functions in each subcommand's source file.
var CommandSuite = mycli.NewCommandSuite("skeema", version, rootDesc)
// AddGlobalConfigFiles takes the mycli.Config generated from the CLI and adds
// global option files as sources. It also handles special processing for a few
// options. Generally, subcommand handlers should call AddGlobalConfigFiles at
// the top of the method.
func AddGlobalConfigFiles(cfg *mycli.Config) {
globalFilePaths := []string{"/etc/skeema", "/usr/local/etc/skeema"}
home := filepath.Clean(os.Getenv("HOME"))
@ -74,9 +79,15 @@ func AddGlobalConfigFiles(cfg *mycli.Config) {
}
}
// PromptPassword reads a password from STDIN without echoing the typed
// characters. Requires that STDIN is a TTY.
func PromptPassword() (string, error) {
stdin := int(syscall.Stdin)
if !terminal.IsTerminal(stdin) {
return "", errors.New("STDIN must be a TTY to read password")
}
fmt.Printf("Enter password: ")
bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
bytePassword, err := terminal.ReadPassword(stdin)
if err != nil {
return "", err
}

Просмотреть файл

@ -21,7 +21,8 @@ var reParseCreate = regexp.MustCompile(`(?i)^(.*)\s*create\s+table\s+(?:if\s+not
// We disallow CREATE TABLE SELECT and CREATE TABLE LIKE expressions
var reBodyDisallowed = regexp.MustCompile(`(?i)^(as\s+select|select|like|[(]\s+like)`)
// Forbid reading SQL files that are larger than 16KB; we assume legit CREATE TABLE statements should be smaller than this
// MaxSQLFileSize specifies the largest SQL file that is considered valid;
// we assume legit CREATE TABLE statements should always be under 16KB.
const MaxSQLFileSize = 16 * 1024
// IsSQLFile returns true if the supplied os.FileInfo has a .sql extension and
@ -37,6 +38,7 @@ func IsSQLFile(fi os.FileInfo) bool {
return true
}
// SQLFile represents a file containing a CREATE TABLE statement.
type SQLFile struct {
Dir *Dir
FileName string
@ -45,17 +47,14 @@ type SQLFile struct {
Warnings []error
}
// Path returns the full absolute path to a SQLFile.
func (sf *SQLFile) Path() string {
return path.Join(sf.Dir.Path, sf.FileName)
}
func (sf *SQLFile) TableName() string {
if !strings.HasSuffix(sf.FileName, ".sql") {
return ""
}
return sf.FileName[0 : len(sf.FileName)-4]
}
// Read reads the file. Its contents will be validated, and stored in
// sf.Contents. If the contents were valid, they will be returned; if not,
// a blank string and an error will be returned.
func (sf *SQLFile) Read() (string, error) {
byteContents, err := ioutil.ReadFile(sf.Path())
if err != nil {
@ -69,6 +68,8 @@ func (sf *SQLFile) Read() (string, error) {
return sf.Contents, nil
}
// Write writes the current value of sf.Contents to the file, returning the
// number of bytes written and any error.
func (sf *SQLFile) Write() (int, error) {
if !strings.HasSuffix(sf.FileName, ".sql") {
return 0, fmt.Errorf("Filename %s does not end in .sql extension", sf.FileName)
@ -84,6 +85,7 @@ func (sf *SQLFile) Write() (int, error) {
return len(value), nil
}
// Delete unlinks the file.
func (sf *SQLFile) Delete() error {
return os.Remove(sf.Path())
}

Просмотреть файл

@ -9,6 +9,10 @@ import (
"github.com/skeema/tengo"
)
// Target represents a unit of operation. For commands that operate recursively
// on a directory tree, one or more Targets are generated for each leaf
// directory -- the cartesian product of (instances this dir maps to) x (schemas
// that this dir maps to on each instance).
type Target struct {
Instance *tengo.Instance
SchemaFromInstance *tengo.Schema
@ -122,14 +126,13 @@ func (t *Target) obtainSchemaFromDir() {
if tx, err = t.lockTempSchema(30 * time.Second); err != nil {
t.Err = fmt.Errorf("obtainSchemaFromDir: %s", err)
return
} else {
defer func() {
unlockErr := t.unlockTempSchema(tx)
if unlockErr != nil && t.Err == nil {
t.Err = fmt.Errorf("obtainSchemaFromDir: %s", unlockErr)
}
}()
}
defer func() {
unlockErr := t.unlockTempSchema(tx)
if unlockErr != nil && t.Err == nil {
t.Err = fmt.Errorf("obtainSchemaFromDir: %s", unlockErr)
}
}()
tempSchema, err := t.Instance.Schema(tempSchemaName)
if err != nil {
@ -203,14 +206,13 @@ func (t Target) verifyDiff(diff *tengo.SchemaDiff) (err error) {
var tx *sql.Tx
if tx, err = t.lockTempSchema(30 * time.Second); err != nil {
return fmt.Errorf("verifyDiff: %s", err)
} else {
defer func() {
unlockErr := t.unlockTempSchema(tx)
if unlockErr != nil && err == nil {
err = fmt.Errorf("verifyDiff: %s", unlockErr)
}
}()
}
defer func() {
unlockErr := t.unlockTempSchema(tx)
if unlockErr != nil && err == nil {
err = fmt.Errorf("verifyDiff: %s", unlockErr)
}
}()
tempSchema, err := t.Instance.Schema(tempSchemaName)
if err != nil {