~artemis/cap

83ae2594926d9a54bf726f8b1806b8d843557b68 — Artémis 9 months ago 42c7711 v0.3.2
added prometheus metrics, added dev dependencies in the nix shell
6 files changed, 76 insertions(+), 13 deletions(-)

M builder.go
M config.go
M domain/index.go
M handler.go
M shell.nix
M types.go
M builder.go => builder.go +7 -2
@@ 174,6 174,13 @@ func Build(root string, args *Args) (*Config, error) {
			}
		}
	}
	// 9b. Generate the metadata files
	if cfg.BuildMetadataGeneration.Enabled {
		err = fb.HandleBuildMetadata()
		if err != nil {
			return cfg, err
		}
	}

	// 9. Run all post-build commands, if applicable
	if !fb.RunPostBuildCommands() {


@@ 268,7 275,5 @@ func (f *FsBuilder) RunPostBuildCommands() bool {
		domain.L.Infof("- [%s] %s", cmd, afterLabel)
	}

	println()

	return true
}

M config.go => config.go +3 -0
@@ 42,6 42,9 @@ func LoadConfig(path string) (*Config, error) {
			CommandTimeout: 5,
		},
		Redirects: map[string]Redirect{},
		BuildMetadataGeneration: BuildMetadataGeneration{
			OutputFolder: "/.meta",
		},
	}
	mergo.Merge(&outputCfg, cfg, mergo.WithOverride)
	if len(outputCfg.PostBuildCommands) > 0 {

M domain/index.go => domain/index.go +1 -0
@@ 19,6 19,7 @@ type Link struct {
}

// Index holds the list of all files, found links, and derived dead links.
// It only handles relative / "internal" links, not links to other domains
type Index struct {
	Files      map[string]ObjectType
	DeadLinks  []*Link

M handler.go => handler.go +41 -2
@@ 304,6 304,8 @@ func (f *FsBuilder) HandleMediaCompression(media *domain.ContentFile, optimizati

	ext := filepath.Ext(to)[1:]
	if len(optimizationParameters.Format) != 0 && ext != optimizationParameters.Format {
		// This should not be done here
		// ideally the index should not be modified anymore in the handler step
		newFrom := strings.TrimSuffix(from, ext) + optimizationParameters.Format
		to = strings.TrimSuffix(to, ext) + optimizationParameters.Format
		delete(f.Index.Files, from)


@@ 342,7 344,7 @@ func (f *FsBuilder) HandleFile(file *domain.ContentFile) error {
		domain.L.Debugf("Copying file from `%s` to `%s`", from, to)
		fileSrc, err := os.Open(from)
		if err != nil {
			domain.L.Debugf("Failed to find source file at %s", from)
			domain.L.Errorf("Failed to find source file at %s", from)
			return err
		}
		defer fileSrc.Close()


@@ 351,7 353,7 @@ func (f *FsBuilder) HandleFile(file *domain.ContentFile) error {

	dest, err := os.Create(to)
	if err != nil {
		domain.L.Debugf("Failed to create dest file at %s", to)
		domain.L.Errorf("Failed to create destination file at %s", to)
		return err
	}
	defer dest.Close()


@@ 359,3 361,40 @@ func (f *FsBuilder) HandleFile(file *domain.ContentFile) error {
	_, err = io.Copy(dest, src)
	return err
}

func (f *FsBuilder) HandleBuildMetadata() error {
	outputPath := filepath.Join(f.Config.BuildMetadataGeneration.OutputFolder, "metrics")
	buildTime := time.Now().Unix()

	metricsText := strings.Join([]string{
		"# HELP last_build A placeholder value of 1, its timestamp marks the time at which this file was last built",
		fmt.Sprintf("last_build 1 %d", buildTime),
		"",
		"# HELP files The count of files in the capsule",
		"# TYPE files counter",
		fmt.Sprintf("files %d", len(f.Index.Files)),
		"",
		"# HELP internal_links The count of internal links between pages and files in the capsule",
		"# TYPE internal_links counter",
		fmt.Sprintf("internal_links{match=\"found\"} %d", len(f.Index.FoundLinks)),
		fmt.Sprintf("internal_links{match=\"redirects\"} %d", len(f.Config.Redirects)),
		fmt.Sprintf("internal_links{match=\"dead\"} %d", len(f.Index.DeadLinks)),
		"",
	}, "\n")

	// TODO: move mkdir perms to constant
	metricsPath := f.GetPath(f.Config.BuildMetadataGeneration.OutputFolder, true)
	err := os.MkdirAll(metricsPath, 0755)

	if err != nil {
		return err
	}

	f.HandleFile(&domain.ContentFile{
		Path:         outputPath,
		RelativePath: outputPath,
		Raw:          []byte(metricsText),
		Kind:         domain.ContentFileTypeRaw,
	})
	return err
}

M shell.nix => shell.nix +4 -1
@@ 1,4 1,7 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  nativeBuildInputs = with pkgs; [ go python3Full imagemagick ];
  nativeBuildInputs = with pkgs; [
    go gopls delve
    python3Full imagemagick
  ];
}

M types.go => types.go +20 -8
@@ 58,6 58,19 @@ type PostBuildConfig struct {
	CommandTimeout time.Duration `toml:"command_timeout"`
}

// BuildMetadataGeneration allows configuring the generation of various build-related metadata made to simplify
// subsequent builds or offer simple ways to keep track of build stats (incl. issues)
type BuildMetadataGeneration struct {
	Enabled bool `toml:"enabled"`
	// Absolute path relative to the capsule root, this folder will contain the build metadata files.
	// Try to dedicate a path to it to avoid cases where cap could overwrite some files you made
	// (defaults to `/.meta/`)
	OutputFolder string `toml:"output_folder"`
}

// Redirect stores a redirection to another page alongside the title to give the redirection (e.g. the new page title)
// of the format "~notebook/recipe.gmi" => "~notebook/recipes/index.gmi" for example
// all paths are relative to the capsule root
type Redirect struct {
	To    string `toml:"to"`
	Title string `toml:"title"`


@@ 65,14 78,13 @@ type Redirect struct {

// Config stores the capsule's configuration, including metadata, media optimization info, post-build commands, etc.
type Config struct {
	Capsule           CapsuleConfig
	Thumbnails        OptimizationParameters
	PostBuildCommands []string        `toml:"post_build_commands"`
	PostBuild         PostBuildConfig `toml:"post_build"`
	FoldersToIgnore   []string        `toml:"ignore"`
	// of the format "~notebook/recipe.gmi" = "~notebook/recipes/index.gmi" for example
	// all paths are relative to the capsule root
	Redirects map[string]Redirect `toml:"redirects"`
	Capsule                 CapsuleConfig
	Thumbnails              OptimizationParameters
	PostBuildCommands       []string                `toml:"post_build_commands"`
	PostBuild               PostBuildConfig         `toml:"post_build"`
	FoldersToIgnore         []string                `toml:"ignore"`
	Redirects               map[string]Redirect     `toml:"redirects"`
	BuildMetadataGeneration BuildMetadataGeneration `toml:"build_metadata"`
}

// WatchCMD contains the subcommand's flags and arguments