Skip to content

Commit

Permalink
Improve code documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed May 17, 2021
1 parent 7526fa5 commit cdaa1d3
Show file tree
Hide file tree
Showing 14 changed files with 99 additions and 19 deletions.
7 changes: 7 additions & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func Main(versionInfo VersionInfo, args []string) int {
return 0
}

// boolAnnotation returns whether cmd is annotated with key.
func boolAnnotation(cmd *cobra.Command, key string) bool {
value, ok := cmd.Annotations[key]
if !ok {
Expand All @@ -107,6 +108,7 @@ func boolAnnotation(cmd *cobra.Command, key string) bool {
return boolValue
}

// example returns command's example.
func example(command string) string {
help, ok := helps[command]
if !ok {
Expand All @@ -115,6 +117,7 @@ func example(command string) string {
return help.example
}

// extractHelps returns the helps parse from r.
func extractHelps(r io.Reader) (map[string]*help, error) {
longStyleConfig := glamour.ASCIIStyleConfig
longStyleConfig.Code.StylePrimitive.BlockPrefix = ""
Expand Down Expand Up @@ -227,6 +230,7 @@ FOR:
return helps, nil
}

// markPersistentFlagsRequired marks all of flags as required for cmd.
func markPersistentFlagsRequired(cmd *cobra.Command, flags ...string) {
for _, flag := range flags {
if err := cmd.MarkPersistentFlagRequired(flag); err != nil {
Expand All @@ -235,6 +239,8 @@ func markPersistentFlagsRequired(cmd *cobra.Command, flags ...string) {
}
}

// mustLongHelp returns the long help for command or panics if no long help
// exists.
func mustLongHelp(command string) string {
help, ok := helps[command]
if !ok {
Expand All @@ -243,6 +249,7 @@ func mustLongHelp(command string) string {
return help.long
}

// runMain runs chezmoi's main function.
func runMain(versionInfo VersionInfo, args []string) error {
config, err := newConfig(
withVersionInfo(versionInfo),
Expand Down
2 changes: 2 additions & 0 deletions cmd/upgradecmd_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ func getMethod(fileSystem vfs.Stater, executableAbsPath chezmoi.AbsPath) (string
}
}

// getPackageType returns the distributions package type based on is OS release.
func getPackageType(fileSystem vfs.FS) (string, error) {
osRelease, err := chezmoi.OSRelease(fileSystem)
if err != nil {
Expand All @@ -483,6 +484,7 @@ func getPackageType(fileSystem vfs.FS) (string, error) {
return packageTypeNone, fmt.Errorf("could not determine package type (ID=%q, ID_LIKE=%q)", osRelease["ID"], osRelease["ID_LIKE"])
}

// getReleaseAssetByName returns the release asset from rr with the given name.
func getReleaseAssetByName(rr *github.RepositoryRelease, name string) *github.ReleaseAsset {
for i, ra := range rr.Assets {
if ra.GetName() == name {
Expand Down
15 changes: 15 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# chezmoi Architecture

<!--- toc --->
* [Introduction](#introduction)
* [Directory structure](#directory-structure)
* [Key concepts](#key-concepts)
* [`internal/chezmoi`](#internalchezmoi)
Expand All @@ -11,6 +12,20 @@
* [`run_once_` scripts](#run_once_-scripts)
* [Testing](#testing)

## Introduction

This document gives a high-level overview of chezmoi's source code for anyone
interested in contributing to chezmoi.

You can generate Go documentation for chezmoi's source code with `go doc`, for
example:

go doc -all -u github.com/twpayne/chezmoi/v2/internal/chezmoi

You can also [browse chezmoi's generated documentation
online](https://pkg.go.dev/github.com/twpayne/chezmoi/v2) but this only includes
exported symbols.

## Directory structure

The important directories in chezmoi are:
Expand Down
2 changes: 2 additions & 0 deletions internal/chezmoi/ageencryption.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func (e *AGEEncryption) EncryptedSuffix() string {
return e.Suffix
}

// decryptArgs returns the arguments for decryption.
func (e *AGEEncryption) decryptArgs() []string {
args := make([]string, 0, 1+2*(1+len(e.Identities)))
args = append(args, "--decrypt")
Expand All @@ -84,6 +85,7 @@ func (e *AGEEncryption) decryptArgs() []string {
return args
}

// encryptArgs returns the arguments for encryption.
func (e *AGEEncryption) encryptArgs() []string {
args := make([]string, 0, 1+2*(1+len(e.Recipients))+2*(1+len(e.RecipientsFiles)))
args = append(args, "--armor")
Expand Down
3 changes: 2 additions & 1 deletion internal/chezmoi/boltpersistentstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
// A BoltPersistentStateMode is a mode for opening a PersistentState.
type BoltPersistentStateMode int

// PersistentStateModes.
// Persistent state modes.
const (
BoltPersistentStateReadOnly BoltPersistentStateMode = iota
BoltPersistentStateReadWrite
Expand Down Expand Up @@ -216,6 +216,7 @@ func (b *BoltPersistentState) open() error {
return nil
}

// copyByteSlice returns a copy of value.
func copyByteSlice(value []byte) []byte {
if value == nil {
return nil
Expand Down
1 change: 1 addition & 0 deletions internal/chezmoi/chezmoi.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ func isEmpty(data []byte) bool {
return len(bytes.TrimSpace(data)) == 0
}

// modeTypeName returns a string representation of mode.
func modeTypeName(mode fs.FileMode) string {
if name, ok := modeTypeNames[mode.Type()]; ok {
return name
Expand Down
6 changes: 6 additions & 0 deletions internal/chezmoi/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ func DiffPatch(path RelPath, fromData []byte, fromMode fs.FileMode, toData []byt
}, nil
}

// diffChunks returns the
// github.com/go-git/go-git/v5/plumbing/format/diff.Chunks required to transform
// from into to.
func diffChunks(from, to string) []diff.Chunk {
dmp := diffmatchpatch.New()
dmp.DiffTimeout = time.Second
Expand All @@ -118,6 +121,8 @@ func diffChunks(from, to string) []diff.Chunk {
return chunks
}

// diffFileMode converts an io/fs.FileMode into a
// github.com/go-git/go-git/v5/plumbing/format/diff.FileMode.
func diffFileMode(mode fs.FileMode) (filemode.FileMode, error) {
fileMode, err := filemode.NewFromOSFileMode(mode)
if err != nil {
Expand All @@ -127,6 +132,7 @@ func diffFileMode(mode fs.FileMode) (filemode.FileMode, error) {
return fileMode, nil
}

// isBinary returns true if data contains binary (non-human-readable) data.
func isBinary(data []byte) bool {
return len(data) != 0 && !strings.HasPrefix(http.DetectContentType(data), "text/")
}
7 changes: 6 additions & 1 deletion internal/chezmoi/gitdiffsystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ type GitDiffSystem struct {
unifiedEncoder *diff.UnifiedEncoder
}

// NewGitDiffSystem returns a new GitDiffSystem.
// NewGitDiffSystem returns a new GitDiffSystem. Output is written to w, the
// dirAbsPath is stripped from paths, and color controls whether the output
// contains ANSI color escape sequences.
func NewGitDiffSystem(system System, w io.Writer, dirAbsPath AbsPath, color bool) *GitDiffSystem {
unifiedEncoder := diff.NewUnifiedEncoder(w, diff.DefaultContextLines)
if color {
Expand Down Expand Up @@ -192,6 +194,8 @@ func (s *GitDiffSystem) WriteSymlink(oldname string, newname AbsPath) error {
return s.system.WriteSymlink(oldname, newname)
}

// encodeDiff encodes the diff between the actual state of absPath and the
// target state of toData and toMode.
func (s *GitDiffSystem) encodeDiff(absPath AbsPath, toData []byte, toMode fs.FileMode) error {
var fromData []byte
var fromMode fs.FileMode
Expand Down Expand Up @@ -223,6 +227,7 @@ func (s *GitDiffSystem) encodeDiff(absPath AbsPath, toData []byte, toMode fs.Fil
return s.unifiedEncoder.Encode(diffPatch)
}

// trimPrefix removes s's directory prefix from absPath.
func (s *GitDiffSystem) trimPrefix(absPath AbsPath) RelPath {
return absPath.MustTrimDirPrefix(s.dirAbsPath)
}
1 change: 1 addition & 0 deletions internal/chezmoi/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ func StringToAbsPathHookFunc() mapstructure.DecodeHookFunc {
}
}

// homeDirAbsPath returns the user's home directory as an AbsPath.
func homeDirAbsPath() (AbsPath, error) {
userHomeDir, err := os.UserHomeDir()
if err != nil {
Expand Down
50 changes: 39 additions & 11 deletions internal/chezmoi/sourcestate.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ func WithTemplateOptions(templateOptions []string) SourceStateOption {
}
}

// A targetStateEntryFunc returns a TargetStateEntry based on reading an AbsPath
// on a System.
type targetStateEntryFunc func(System, AbsPath) (TargetStateEntry, error)

// NewSourceState creates a new source state with the given options.
Expand All @@ -120,17 +122,17 @@ type PreAddFunc func(targetRelPath RelPath, newSourceStateEntry, oldSourceStateE

// AddOptions are options to SourceState.Add.
type AddOptions struct {
AutoTemplate bool
Create bool
Empty bool
Encrypt bool
EncryptedSuffix string
Exact bool
Include *EntryTypeSet
PreAddFunc PreAddFunc
RemoveDir RelPath
Template bool
TemplateSymlinks bool
AutoTemplate bool // Automatically create templates, if possible.
Create bool // Add create_ entries instead of normal entries.
Empty bool // Add the empty_ attribute to added files.
Encrypt bool // Encrypt files.
EncryptedSuffix string // Suffix for encrypted files.
Exact bool // Add the exact_ attribute to added directories.
Include *EntryTypeSet // Only add types in this set.
PreAddFunc PreAddFunc // Function to be called before the source entry is added.
RemoveDir RelPath // Directory to remove before adding.
Template bool // Add the .tmpl attribute to added files.
TemplateSymlinks bool // Add symlinks with targets in the source or home directories as templates.
}

// Add adds destAbsPathInfos to s.
Expand Down Expand Up @@ -910,6 +912,9 @@ func (s *SourceState) newSourceStateDir(sourceRelPath SourceRelPath, dirAttr Dir
}
}

// newCreateTargetStateEntryFunc returns a targetStateEntryFunc that returns a
// file with sourceLazyContents if the file does not already exist, or returns
// the actual file's contents unchanged if the file already exists.
func (s *SourceState) newCreateTargetStateEntryFunc(fileAttr FileAttr, sourceLazyContents *lazyContents) targetStateEntryFunc {
return func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) {
contents, err := destSystem.ReadFile(destAbsPath)
Expand All @@ -931,6 +936,8 @@ func (s *SourceState) newCreateTargetStateEntryFunc(fileAttr FileAttr, sourceLaz
}
}

// newFileTargetStateEntryFunc returns a targetStateEntryFunc that returns a
// file with sourceLazyContents.
func (s *SourceState) newFileTargetStateEntryFunc(sourceRelPath SourceRelPath, fileAttr FileAttr, sourceLazyContents *lazyContents) targetStateEntryFunc {
return func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) {
contentsFunc := func() ([]byte, error) {
Expand All @@ -954,6 +961,8 @@ func (s *SourceState) newFileTargetStateEntryFunc(sourceRelPath SourceRelPath, f
}
}

// newModifyTargetStateEntryFunc returns a targetStateEntryFunc that returns a
// file with the contents modified by running the sourceLazyContents script.
func (s *SourceState) newModifyTargetStateEntryFunc(sourceRelPath SourceRelPath, fileAttr FileAttr, sourceLazyContents *lazyContents) targetStateEntryFunc {
return func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) {
contentsFunc := func() (contents []byte, err error) {
Expand Down Expand Up @@ -1016,6 +1025,8 @@ func (s *SourceState) newModifyTargetStateEntryFunc(sourceRelPath SourceRelPath,
}
}

// newScriptTargetStateEntryFunc returns a targetStateEntryFunc that returns a
// script with sourceLazyContents.
func (s *SourceState) newScriptTargetStateEntryFunc(sourceRelPath SourceRelPath, fileAttr FileAttr, targetRelPath RelPath, sourceLazyContents *lazyContents) targetStateEntryFunc {
return func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) {
contentsFunc := func() ([]byte, error) {
Expand All @@ -1039,6 +1050,8 @@ func (s *SourceState) newScriptTargetStateEntryFunc(sourceRelPath SourceRelPath,
}
}

// newSymlinkTargetStateEntryFunc returns a targetStateEntryFunc that returns a
// symlink with the linkname sourceLazyContents.
func (s *SourceState) newSymlinkTargetStateEntryFunc(sourceRelPath SourceRelPath, fileAttr FileAttr, sourceLazyContents *lazyContents) targetStateEntryFunc {
return func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) {
linknameFunc := func() (string, error) {
Expand Down Expand Up @@ -1100,6 +1113,11 @@ func (s *SourceState) newSourceStateFile(sourceRelPath SourceRelPath, fileAttr F
}
}

// newSourceStateDirEntry returns a SourceStateEntry constructed from a
// directory in s.
//
// We return a SourceStateEntry rather than a *SourceStateDir to simplify nil
// checks later.
func (s *SourceState) newSourceStateDirEntry(info fs.FileInfo, parentSourceRelPath SourceRelPath, options *AddOptions) (SourceStateEntry, error) {
dirAttr := DirAttr{
TargetName: info.Name(),
Expand All @@ -1116,6 +1134,11 @@ func (s *SourceState) newSourceStateDirEntry(info fs.FileInfo, parentSourceRelPa
}, nil
}

// newSourceStateFileEntryFromFile returns a SourceStateEntry constructed from a
// file in s.
//
// We return a SourceStateEntry rather than a *SourceStateFile to simplify nil
// checks later.
func (s *SourceState) newSourceStateFileEntryFromFile(actualStateFile *ActualStateFile, info fs.FileInfo, parentSourceRelPath SourceRelPath, options *AddOptions) (SourceStateEntry, error) {
fileAttr := FileAttr{
TargetName: info.Name(),
Expand Down Expand Up @@ -1164,6 +1187,11 @@ func (s *SourceState) newSourceStateFileEntryFromFile(actualStateFile *ActualSta
}, nil
}

// newSourceStateFileEntryFromSymlink returns a SourceStateEntry constructed
// from a symlink in s.
//
// We return a SourceStateEntry rather than a *SourceStateFile to simplify nil
// checks later.
func (s *SourceState) newSourceStateFileEntryFromSymlink(actualStateSymlink *ActualStateSymlink, info fs.FileInfo, parentSourceRelPath SourceRelPath, options *AddOptions) (SourceStateEntry, error) {
linkname, err := actualStateSymlink.Linkname()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/chezmoi/sourcestateentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type SourceStateFile struct {
*lazyContents
Attr FileAttr
sourceRelPath SourceRelPath
targetStateEntryFunc func(destSystem System, destDirAbsPath AbsPath) (TargetStateEntry, error)
targetStateEntryFunc targetStateEntryFunc
targetStateEntry TargetStateEntry
targetStateEntryErr error
}
Expand Down
8 changes: 5 additions & 3 deletions internal/chezmoitest/chezmoitest.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,13 @@ func AGEGenerateKey(filename string) (publicKey, privateKeyFile string, err erro
return
}

// GPGCommand returns the GPG command, if it can be found.
// GPGCommand returns the path to gpg, if it can be found.
func GPGCommand() (string, error) {
return exec.LookPath("gpg")
}

// GPGGenerateKey generates and returns a GPG key in homeDir.
// GPGGenerateKey generates GPG key in homeDir and returns the key and the
// passphrase.
func GPGGenerateKey(command, homeDir string) (key, passphrase string, err error) {
//nolint:gosec
passphrase = "chezmoi-test-gpg-passphrase"
Expand Down Expand Up @@ -130,7 +131,8 @@ func WithTestFS(t *testing.T, root interface{}, f func(vfs.FS)) {
f(fileSystem)
}

func mustParseFilemode(s string) fs.FileMode {
// mustParseFileMode parses s as a fs.FileMode and panics on any error.
func mustParseFileMode(s string) fs.FileMode {
i, err := strconv.ParseInt(s, 0, 32)
if err != nil {
panic(err)
Expand Down
6 changes: 5 additions & 1 deletion internal/chezmoitest/chezmoitest_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import (
)

var (
// umaskStr is the umask used in tests represented as a string so it can be
// set with the
// -ldflags="-X github.com/twpayne/chezmoi/internal/chezmoitest.umaskStr=..."
// option to go build and go test.
umaskStr = "0o022"

// Umask is the umask used in tests.
Expand All @@ -17,7 +21,7 @@ var (
// irrespective of what it is set to. Be aware that the process's umask is a
// process-level property and cannot be locally changed within individual
// tests.
Umask = mustParseFilemode(umaskStr)
Umask = mustParseFileMode(umaskStr)
)

func init() {
Expand Down
8 changes: 7 additions & 1 deletion internal/chezmoitest/chezmoitest_windows.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package chezmoitest

var (
// umaskStr is the umask used in tests represented as a string so it can be
// set with the
// -ldflags="-X github.com/twpayne/chezmoi/internal/chezmoitest.umaskStr=..."
// option to go build and go test.
umaskStr = "0"

// Umask is the umask used in tests.
Umask = mustParseFilemode(umaskStr)
//
// On Windows, Umask is zero as Windows does not use POSIX-style permissions.
Umask = mustParseFileMode(umaskStr)
)

0 comments on commit cdaa1d3

Please sign in to comment.