Skip to content

Commit

Permalink
Add 'v2/' from commit 'd184da41b02eca5aa382d9094bcc71146c4617b0'
Browse files Browse the repository at this point in the history
git-subtree-dir: v2
git-subtree-mainline: 4313f63
git-subtree-split: d184da4
  • Loading branch information
akerl committed Nov 20, 2019
2 parents 4313f63 + d184da4 commit 81f024d
Show file tree
Hide file tree
Showing 24 changed files with 824 additions and 0 deletions.
9 changes: 9 additions & 0 deletions v2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/.gopath
/bin
/vendor/
/pkg/
/.github
/payload.zip
/.tools/
/Dockerfile
prospectus
3 changes: 3 additions & 0 deletions v2/.gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "meta"]
path = meta
url = https://github.com/akerl/pkgforge-golang-helper
30 changes: 30 additions & 0 deletions v2/.pkgforge
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name 'prospectus'
org 'akerl'

source(
type: 'git',
path: '.'
)

build do
run 'make local'
cp 'bin'
end

package(
type: 'file',
artifacts: [
{
source: "bin/#{@forge.name}-ng_darwin",
name: "#{@forge.name}_darwin"
},
{
source: "bin/#{@forge.name}-ng_linux",
name: "#{@forge.name}_linux"
}
]
)

test do
# TODO: add tests
end
13 changes: 13 additions & 0 deletions v2/.prospectus
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env ruby

require 'prospectus'

Prospectus.extra_dep('file', 'prospectus_travis')
Prospectus.extra_dep('file', 'prospectus_golang')

item do
noop

extend ProspectusGolang::Deps.new
extend ProspectusTravis::Build.new('akerl/prospectus-ng')
end
16 changes: 16 additions & 0 deletions v2/.travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
sudo: required
dist: xenial
services:
- docker
env:
global:
- PKGFORGE_STATEFILE=/tmp/pkgforge
script: make
deploy:
provider: script
script: make release || travis_terminate 1
skip_cleanup: true
on:
tags: true
notifications:
email: false
22 changes: 22 additions & 0 deletions v2/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
The MIT License (MIT)

Copyright (c) 2019 Les Aker

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

1 change: 1 addition & 0 deletions v2/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include meta/Makefile
1 change: 1 addition & 0 deletions v2/Makefile.local
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PACKAGE = prospectus
43 changes: 43 additions & 0 deletions v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
prospectus-ng
=========

[![Build Status](https://img.shields.io/travis/com/akerl/prospectus-ng.svg)](https://travis-ci.com/akerl/prospectus)
[![GitHub release](https://img.shields.io/github/release/akerl/prospectus-ng.svg)](https://github.com/akerl/prospectus-ng/releases)
[![MIT Licensed](https://img.shields.io/badge/license-MIT-green.svg)](https://tldrlegal.com/license/mit-license)

Tool to check for changes in expected vs. actual state

## Usage

### Check specification

Checks must implement responses for the following commands:

### load

The `load` command accepts a hash with a single key, the directory being checked, and returns an array of checks with optional metadata.

Input: `{"dir": "/path/to/main/dir"}`
Output: `[{"name": "check_N", "metadata": {"foo": "bar"}}, ...]`

### execute

The `execute` command accepts a hash representing the check object. Metadata provided during the `load` call is included. The return value must be a Result object for the given check.

Input: `{"dir": "/path/to/main/dir", "file": "/path/to/main/dir/.prospectus.d/checkfile", "name": "check_N", "metadata": {"foo": "bar"}}`
Output: `{"actual": "unhappy", "expected": {"type": "string", "data": {"expected": "happy"}}}`

### fix

The `fix` command can attempt to fix a failed check automatically. It accepts a hash representing the failed result, which includes the originating check. The return value must be a Result object for the given check.

**Note:** The check must respond to the `fix` command, but if it does not support automatic fixes, it can respond by emiting the same result object it was given.

Input: `{"actual": "unhappy", "expected": {"type": "string", "data": {"expected": "happy"}}, "check": {"dir": "/path/to/main/dir", "file": "/path/to/main/dir/.prospectus.d/checkfile", "name": "check_N", "metadata": {"foo": "bar"}}}`
Output: `{"actual": "happy", "expected": {"type": "string", "data": {"expected": "happy"}}}`

## Installation

## License

prospectus-ng is released under the MIT License. See the bundled LICENSE file for details.
221 changes: 221 additions & 0 deletions v2/checks/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package checks

import (
"encoding/json"
"fmt"
"io/ioutil"
"os/exec"
"path/filepath"
"strings"

"github.com/akerl/prospectus/expectations"
)

const (
prospectusDirName = ".prospectus.d"
)

// TODO: add timber logging
// TODO: add parallelization

// Check defines a single check that is ready for execution
type Check struct {
Dir string `json:"dir"`
File string `json:"file"`
Name string `json:"name"`
Metadata map[string]string `json:"metadata"`
}

// CheckSet defines a group of Checks
type CheckSet []Check

// Result defines the results of executing a Check
type Result struct {
Actual string `json:"actual"`
Expected expectations.Wrapper `json:"expected"`
Check Check `json:"check"`
}

// ResultSet defines a group of Results
type ResultSet []Result

type loadCheckInput struct {
Dir string `json:"dir"`
}

// NewSet returns a CheckSet based on a provided list of directories
func NewSet(relativeDirs []string) (CheckSet, error) {
var err error

dirs := make([]string, len(relativeDirs))
for index, item := range relativeDirs {
dirs[index], err = filepath.Abs(item)
if err != nil {
return CheckSet{}, err
}
}

var cs CheckSet
for _, item := range dirs {
newSet, err := newSetFromDir(item)
if err != nil {
return CheckSet{}, err
}
cs = append(cs, newSet...)
}

return cs, nil
}

func newSetFromDir(absoluteDir string) (CheckSet, error) {
prospectusDir := filepath.Join(absoluteDir, prospectusDirName)

fileObjs, err := ioutil.ReadDir(prospectusDir)
if err != nil {
return CheckSet{}, err
}

var cs CheckSet
for _, fileObj := range fileObjs {
file := filepath.Join(prospectusDir, fileObj.Name())
newSet, err := newSetFromFile(absoluteDir, file)
if err != nil {
return CheckSet{}, err
}
cs = append(cs, newSet...)
}

return cs, nil
}

func newSetFromFile(dir, file string) (CheckSet, error) {
cs := CheckSet{}
input := loadCheckInput{Dir: dir}
err := execProspectusFile(file, "load", input, &cs)
if err != nil {
return CheckSet{}, fmt.Errorf("Failed loading %s: %s", file, err)
}
for index := range cs {
cs[index].Dir = dir
cs[index].File = file
}
return cs, nil
}

func execProspectusFile(file, command string, input interface{}, output interface{}) error {
cmd := exec.Command(file, command)

inputBytes, err := json.Marshal(input)
if err != nil {
return err
}

stdin, err := cmd.StdinPipe()
if err != nil {
return err
}
stdin.Write(inputBytes)
stdin.Close()

stdout, err := cmd.Output()
if err != nil {
return err
}

return json.Unmarshal(stdout, output)
}

// Execute returns the Results from a CheckSet by calling Execute on each Check
func (cs CheckSet) Execute() ResultSet {
resultSet := make(ResultSet, len(cs))
for index, item := range cs {
resultSet[index] = item.Execute()
}
return resultSet
}

// Execute runs the Check and returns Results
func (c Check) Execute() Result {
return execProspectusForResult("execute", c, c)
}

func execProspectusForResult(method string, c Check, input interface{}) Result {
r := Result{}
err := execProspectusFile(c.File, method, input, &r)
if err != nil {
return NewErrorResult(fmt.Sprintf("%s error: %s", method, err), c)
}
r.Check = c
return r
}

// String returns the Result as a human-readable string
func (c Check) String() string {
return fmt.Sprintf(
"%s::%s",
c.Dir,
c.Name,
)
}

// Changed filters a ResultSet to only Results which do not match
func (rs ResultSet) Changed() ResultSet {
var newResultSet ResultSet
for _, item := range rs {
if !item.Matches() {
newResultSet = append(newResultSet, item)
}
}
return newResultSet
}

// Matches returns true if the Expected and Actual values of the Result match
func (r Result) Matches() bool {
return r.Expected.Matches(r.Actual)
}

// JSON returns the ResultsSet as a marshalled JSON string
func (rs ResultSet) JSON() (string, error) {
data, err := json.MarshalIndent(rs, "", " ")
if err != nil {
return "", err
}
return string(data), nil
}

// String returns the ResultsSet as a human-readable string
func (rs ResultSet) String() string {
var b strings.Builder
for _, item := range rs {
b.WriteString(item.String())
b.WriteString("\n")
}
return b.String()
}

// String returns the Result as a human-readable string
func (r Result) String() string {
return fmt.Sprintf(
"%s: %s / %s",
r.Check,
r.Actual,
r.Expected.String(),
)
}

// Fix attempts to resolve a mismatched expectation
func (r Result) Fix() Result {
return execProspectusForResult("fix", r.Check, r)
}

// NewErrorResult creates an error result from a given string
func NewErrorResult(msg string, c Check) Result {
return Result{
Actual: "error",
Expected: expectations.Wrapper{
Type: "error",
Data: map[string]string{"msg": msg},
},
Check: c,
}
}
Loading

0 comments on commit 81f024d

Please sign in to comment.