Skip to content

MoonMoon1919/mayi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

63 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

MAYI

Go package for programmatically managing CODEOWNERS files. Handles conflict detection and resolution, supports all pattern types (files, directories, globs, extensions), and provides a clean API for automated rule management and ownership queries across repositories.

Features

  • πŸ–ŠοΈ Add, update, and remove rules
  • βœ… Validate rules and accuracy
  • πŸ”Ž Check who can review files & directories
  • πŸ“„ Generate files

Quick Start

Installation

go get github.com/MoonMoon1919/mayi

Basic usage

Create a document and add a rule:

package samples

import (
	"fmt"

	"github.com/MoonMoon1919/mayi"
	"github.com/MoonMoon1919/mayi/pkg/owners"
	"github.com/MoonMoon1919/mayi/pkg/rules"
	"github.com/MoonMoon1919/mayi/pkg/service"
)

func doItYourself() {
	co := mayi.NewCodeOwners()

	result, err := co.AddRule(
		"docs/*",
		owners.FromStrings("@MoonMoon1919"),
		rules.INCLUDE,
	)
	if err != nil {
		panic(err)
	}

	fmt.Print(result)
}

func usingServices() {
	repo := service.NewFileRepository(mayi.RenderOptions{})
	service := service.New(repo)

	// create an empty file
	if err := service.Init(".github/CODEOWNERS"); err != nil {
		panic(err)
	}

	result, err := service.AddRule(".github/CODEOWNERS", "docs/*", []string{"@MoonMoon1919"}, rules.INCLUDE)
	if err != nil {
		panic(err)
	}

	fmt.Print(result)
}

Core concepts

Rule Type

The type of rule to add. Mayi current supports file, extension, directory, and glob rules

Pattern

The pattern you would like to match - uses gitignore like semantics Mayi automatically determines the rule type from the given pattern.

Actions

Determes if a pattern should be included or excluded (e.g., don't require review on a specific pattern) Mayi supports 'include' and 'exclude'

Owners

Individual or group that will be responsible for reviewing all code that matches a pattern Most providers support an individual or group alias (GitHub/Gitlab handle or group name) or email

Rules

A combination of a action, pattern, and owner This is eventually written to the destination (e.g., CODEOWNERS file)

Conflict detection

Mayi automatically detects four types of conflicts:

Semantic

Same pattern with different action OR same action and different owners

README.md @MoonMoon1919
README.md @someoneelse123

Redundant

Same pattern (or pattern subsumes) with same action and owners

README.md @MoonMoon1919
README.md @MoonMoon1919

Unreachable

Broader rule makes specific on meaningless

app.log @MoonMoon1919
*.log @MoonMoon1919

Ineffective

Rule subsumes another rule with different owners

build/important.txt @MoonMoon1919
build/** @Example

Advanced usage

Parsing existing files

package samples

import (
	"fmt"

	"github.com/MoonMoon1919/mayi"
	"github.com/MoonMoon1919/mayi/pkg/files"
)

func parseExisting() {
	content := `*.md @MoonMoon1919
	build/ @MoonMoon1919
	docs/* @MoonMoon1919
	`

	var codeOwners mayi.CodeOwners
	if err := files.Parse(content, &codeOwners); err != nil {
		panic(err)
	}

	fmt.Print(codeOwners)
}

Rule reordering

package samples

import (
	"fmt"

	"github.com/MoonMoon1919/mayi"
	"github.com/MoonMoon1919/mayi/pkg/service"
)

func reorder() {
	repo := service.NewFileRepository(mayi.RenderOptions{})
	svc := service.New(repo)

	result, err := svc.MoveRule(
		".github/CODEOWNERS",
		"*.md",
		"CONTRIBUTING.md",
		mayi.BEFORE,
	)
	if err != nil {
		panic(err)
	}

	fmt.Print(result)
}

Testing

The library includes comprehensive test coverage. If you would like to write your own tests, the repository interface is simple to fake.

package samples

import (
	"errors"
	"fmt"
	"strings"

	"github.com/MoonMoon1919/mayi"
	"github.com/MoonMoon1919/mayi/pkg/files"
	"github.com/MoonMoon1919/mayi/pkg/service"
)

// Create a fake repository
type FakeRepository struct {
	files map[string]string
}

func (f *FakeRepository) Load(path string, codeOwners *mayi.CodeOwners) error {
	content, ok := f.files[path]
	if !ok {
		return errors.New("file read error")
	}

	return files.Load(strings.NewReader(content), codeOwners)
}

func (f *FakeRepository) Save(path string, codeOwners *mayi.CodeOwners) error {
	content := codeOwners.Render(&mayi.RenderOptions{})
	f.files[path] = content

	return nil
}

func NewFakeRepository() FakeRepository {
	return FakeRepository{
		files: make(map[string]string),
	}
}

func tester() {
	repo := NewFakeRepository()
	svc := service.New(&repo)

	// Write a test here
	fmt.Printf("svc: %v\n", svc)
}

Contributing

See CONTRIBUTING for details.

License

MIT License - see LICENSE for details.

Disclaimers

This work does not represent the interests or technologies of any employer, past or present. It is a personal project only.

About

Go package for managing CODEOWNERS files

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published