Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(policy): add policy enforcement; enforce git commit policy #17

Merged
merged 1 commit into from
Jul 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions conform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@ metadata:

default: image

policies:
git:
types:
- "docs"
- "style"
- "refactor"
- "perf"
- "test"
- "chore"
scopes:
- "ci"
- "docker"
- "git"
- "policy"
- "*"

scripts:
init : |
#!/bin/bash
Expand Down
12 changes: 12 additions & 0 deletions conform/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Config struct {
Debug bool
Default *string `yaml:"default"`
Metadata *Metadata `yaml:"metadata"`
Policies *Policies `yaml:"policies"`
Scripts map[string]string `yaml:"scripts"`
Templates map[string]string `yaml:"templates"`
Rules map[string]*Rule `yaml:"rules"`
Expand All @@ -23,6 +24,17 @@ type Metadata struct {
Registry *string `yaml:"registry"`
}

// Policies contains policies that are enforced.
type Policies struct {
Git *Git `yaml:"git"`
}

// Git contains git specific policies.
type Git struct {
Types []string `yaml:"types"`
Scopes []string `yaml:"scopes"`
}

// Rule contains rules.
type Rule struct {
Templates []string `yaml:"templates"`
Expand Down
89 changes: 31 additions & 58 deletions conform/enforce.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"text/template"

"github.com/Masterminds/sprig"
"github.com/autonomy/conform/conform/config"
"github.com/autonomy/conform/conform/git"
"github.com/autonomy/conform/conform/policy"
)

// Enforcer performs all the build actions for a rule.
Expand All @@ -29,10 +29,6 @@ func NewEnforcer(rule string) (enforcer *Enforcer, err error) {
if err != nil {
return
}
err = exportAll(gitInfo)
if err != nil {
return
}
date := []byte{}
if gitInfo.IsTag {
date, err = exec.Command("/bin/date").Output()
Expand Down Expand Up @@ -146,8 +142,38 @@ func (e *Enforcer) ExecuteScript(script string) error {
return fmt.Errorf("Script %q is not defined in conform.yaml", script)
}

// EnforcePolicies enforces all defined polcies. In the case that the working
// tree is dirty, all git policies are skipped.
func (e *Enforcer) EnforcePolicies() {
if !e.GitInfo.IsDirty {
enforceGitPolicy(
e.GitInfo,
&git.ConventionalCommitsOptions{
Message: e.GitInfo.Message,
Types: e.config.Policies.Git.Types,
Scopes: e.config.Policies.Git.Scopes,
},
)
}
}

func enforceGitPolicy(p policy.Policy, opts *git.ConventionalCommitsOptions) {
report, err := p.Compliance(opts)
if err != nil {
fmt.Print(err)
os.Exit(1)
}
if !report.Valid {
for _, err := range report.Errors {
fmt.Printf("%s", err)
os.Exit(1)
}
}
}

// ExecuteRule performs all the relevant actions specified in its' declaration.
func (e *Enforcer) ExecuteRule() error {
e.EnforcePolicies()
if t, ok := e.config.Rules[e.rule]; ok {
fmt.Printf("Enforcing %q\n", e.rule)
for _, s := range t.Before {
Expand Down Expand Up @@ -177,59 +203,6 @@ func (e *Enforcer) ExecuteRule() error {
return fmt.Errorf("Rule %q is not defined in conform.yaml", e.rule)
}

func exportAll(gitInfo *git.Info) (err error) {
fmt.Printf("Branch: %s\n", gitInfo.Branch)
err = ExportConformVar("branch", gitInfo.Branch)
if err != nil {
return
}
fmt.Printf("SHA: %s\n", gitInfo.SHA)
err = ExportConformVar("sha", gitInfo.SHA)
if err != nil {
return
}
fmt.Printf("Tag: %s\n", gitInfo.Tag)
err = ExportConformVar("tag", gitInfo.Tag)
if err != nil {
return
}
fmt.Printf("IsTag: %s\n", strconv.FormatBool(gitInfo.IsTag))
err = ExportConformVar("is_tag", strconv.FormatBool(gitInfo.IsTag))
if err != nil {
return
}
fmt.Printf("Prerelease: %s\n", gitInfo.Prerelease)
err = ExportConformVar("prerelease", gitInfo.Prerelease)
if err != nil {
return
}
fmt.Printf("IsPrerelease: %s\n", strconv.FormatBool(gitInfo.IsPrerelease))
err = ExportConformVar("is_prerelease", strconv.FormatBool(gitInfo.IsPrerelease))
if err != nil {
return
}
fmt.Printf("Status: \n%s\n", strings.TrimRight(gitInfo.Status, "\n"))
err = ExportConformVar("status", strconv.FormatBool(gitInfo.IsDirty))
if err != nil {
return
}
fmt.Printf("IsDirty: %s\n", strconv.FormatBool(gitInfo.IsDirty))
err = ExportConformVar("is_dirty", strconv.FormatBool(gitInfo.IsDirty))
if err != nil {
return
}

return
}

// ExportConformVar exports variable prefixed with CONFORM_
func ExportConformVar(name, value string) (err error) {
variable := fmt.Sprintf("CONFORM_%s", strings.ToUpper(name))
err = os.Setenv(variable, value)

return
}

// FormatImageNameDirty formats the image name.
func (e *Enforcer) FormatImageNameDirty() string {
return fmt.Sprintf("%s:%s", *e.config.Metadata.Repository, "dirty")
Expand Down
98 changes: 97 additions & 1 deletion conform/git/git.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package git

import (
"fmt"
"strconv"
"strings"

"github.com/Masterminds/semver"
// git "github.com/libgit2/git2go"
"github.com/autonomy/conform/conform/utilities"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
)
Expand All @@ -14,6 +18,7 @@ type Info struct {
Tag string
Prerelease string
Status string
Message string
IsTag bool
IsPrerelease bool
IsDirty bool
Expand Down Expand Up @@ -51,12 +56,18 @@ func NewInfo() (info *Info, err error) {
return
}

message, err := Message(repo, isDirty)
if err != nil {
return
}

info = &Info{
Branch: branch,
SHA: sha,
Tag: tag,
Prerelease: prerelease,
Status: status,
Message: message,
IsTag: isTag,
IsPrerelease: isPrerelease,
IsDirty: isDirty,
Expand All @@ -75,6 +86,12 @@ func Branch(repo *git.Repository) (branch string, err error) {
branch = ref.Name().Short()
}

fmt.Printf("Branch: %s\n", branch)
err = utilities.ExportConformVar("branch", branch)
if err != nil {
return
}

return
}

Expand All @@ -86,6 +103,12 @@ func SHA(repo *git.Repository) (sha string, err error) {
}
sha = ref.Hash().String()[0:7]

fmt.Printf("SHA: %s\n", sha)
err = utilities.ExportConformVar("sha", sha)
if err != nil {
return
}

return
}

Expand All @@ -111,6 +134,17 @@ func Tag(repo *git.Repository) (tag string, isTag bool, err error) {
return
}

fmt.Printf("Tag: %s\n", tag)
err = utilities.ExportConformVar("tag", tag)
if err != nil {
return
}
fmt.Printf("IsTag: %s\n", strconv.FormatBool(isTag))
err = utilities.ExportConformVar("is_tag", strconv.FormatBool(isTag))
if err != nil {
return
}

return
}

Expand All @@ -128,6 +162,17 @@ func Prerelease(tag string, isTag bool) (prerelease string, isPrerelease bool, e
}
}

fmt.Printf("Prerelease: %s\n", prerelease)
err = utilities.ExportConformVar("prerelease", prerelease)
if err != nil {
return
}
fmt.Printf("IsPrerelease: %s\n", strconv.FormatBool(isPrerelease))
err = utilities.ExportConformVar("is_prerelease", strconv.FormatBool(isPrerelease))
if err != nil {
return
}

return
}

Expand All @@ -138,12 +183,63 @@ func Status(repo *git.Repository) (status string, isDirty bool, err error) {
return
}
worktreeStatus, err := worktree.Status()
if err != nil {
return
}
if worktreeStatus.IsClean() {
status = " nothing to commit, working tree clean"
} else {
isDirty = true
status = worktreeStatus.String()
}

fmt.Printf("Status: \n%s\n", strings.TrimRight(status, "\n"))
err = utilities.ExportConformVar("status", strconv.FormatBool(isDirty))
if err != nil {
return
}
fmt.Printf("IsDirty: %s\n", strconv.FormatBool(isDirty))
err = utilities.ExportConformVar("is_dirty", strconv.FormatBool(isDirty))
if err != nil {
return
}

return
}

// Message returns the commit message. In the case that a commit has multiple
// parents, the message of the last parent is returned.
func Message(repo *git.Repository, isDirty bool) (message string, err error) {
ref, err := repo.Head()
if err != nil {
return
}
commit, err := repo.CommitObject(ref.Hash())
if err != nil {
return
}
if commit.NumParents() != 1 {
parents := commit.Parents()
for i := 1; i <= commit.NumParents(); i++ {
next, err := parents.Next()
if err != nil {
return "", err
}
if i == commit.NumParents() {
message = next.Message
}
}
} else {
message = commit.Message
}

if !isDirty {
fmt.Printf("Message: %s\n", strings.TrimRight(message, "\n"))
err = utilities.ExportConformVar("message", message)
if err != nil {
return
}
}

return
}
Loading