Compare commits

...

77 Commits
v0.0.8 ... main

Author SHA1 Message Date
Nick Dumas e95c747899 restructuring subcommands for hugo tooling
continuous-integration/drone/tag Build is passing Details
2 years ago
Nick Dumas add10c2d1a Hugo bundle directory scaffolding
continuous-integration/drone/tag Build is passing Details
2 years ago
Nick Dumas 0dda41e3f8 Namespacing your viper binds is important 2 years ago
Nick Dumas b631b717b6 debugging viper 2 years ago
Nick Dumas 93fa411750 saving my work 2 years ago
Nick Dumas 4f76a1d571 First step: inventory posts in provided directory 2 years ago
Nick Dumas 94701a01b2 Cleaning up dockerfile 2 years ago
Nick Dumas 3252693509 more hugo command drafting 2 years ago
Nick Dumas 7271830829 drafting the hugo subcommand 2 years ago
Nick Dumas cff10a36ca starting on hugo command 2 years ago
Nick Dumas f479ba4d4c Proper error handling for the walkfunc
continuous-integration/drone/tag Build is passing Details
2 years ago
Nick Dumas 72d5ed9d53 This is the happy case.
continuous-integration/drone/tag Build is failing Details
2 years ago
Nick Dumas 56f806a1f5 No more contextless error returns
continuous-integration/drone/tag Build is passing Details
2 years ago
Nick Dumas 8de6452c64 refactoring makefile, adding error detail 2 years ago
Nick Dumas 5813113d9f Not sure why I changed this return statement
continuous-integration/drone/tag Build is passing Details
2 years ago
Nick Dumas 3036a6a717 Cleaning up copy command
continuous-integration/drone/tag Build is passing Details
2 years ago
Nick Dumas 84aa6ce9ca Now the alpine build injects build info
continuous-integration/drone/tag Build is passing Details
2 years ago
Nick Dumas 755c213b91 Alpine build works, without version injection 2 years ago
Nick Dumas 2524fd772a more convenience tools for releasing 2 years ago
Nick Dumas 78fb258b5d Reordering target definitions 2 years ago
Nick Dumas d8d58acc36 Making version tagging easier 2 years ago
Nick Dumas 869ca7b52a adding a positive test case
continuous-integration/drone/tag Build is passing Details
2 years ago
Nick Dumas 06d7bc3c56 Only build docker images on tags 2 years ago
Nick Dumas 5c48fe6b22 Unit tests and linting cleanup 2 years ago
Nick Dumas 13e1abd65a more condensing
continuous-integration/drone/push Build is passing Details
2 years ago
Nick Dumas 0dd2d0dae3 condensing more stuff together for sanity/cleanup 2 years ago
Nick Dumas 60e96cce18 Now that's what i call linting 2 years ago
Nick Dumas 54e60b113e also working on unit tests 2 years ago
Nick Dumas 1dcd39afa3 Starting big time cleanup 2 years ago
Nick Dumas 230d8b2160 Adding some failing tests 2 years ago
Nick Dumas 0aec42aceb gotta run setup for the tests
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build is passing Details
2 years ago
Nick Dumas 0bea3ce6d0 updating to match new makefile
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
Nick Dumas c91faf4ca0 Don't need that debug statement anymore 2 years ago
Nick Dumas 4f1e52731c duplicate step name
continuous-integration/drone/push Build is passing Details
2 years ago
Nick Dumas 270922f330 We don't actually have a git repo
continuous-integration/drone/push Build encountered an error Details
2 years ago
Nick Dumas c366f8989f more debug
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 0709289a02 debug time
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 11f7b5362f this variable doesn't get set by drone
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 2188af0b78 successfully automated tagged+versioned docker image builds and pushes
continuous-integration/drone/tag Build is failing Details
2 years ago
Nick Dumas 135591cd0b before things get out of hand 2 years ago
Nick Dumas 04919ea208 Finally got versions baked into the builds
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 7c38d9ae71 trying to implement a version command 2 years ago
Nick Dumas b22519cd96 Adding version and tag info for the build to inject 2 years ago
Nick Dumas 70d0453120 ignore reports 2 years ago
Nick Dumas cf4d107c98 builds work, but i need to adjust the matrix 2 years ago
Nick Dumas d2c47e82b5 update gitignore 2 years ago
Nick Dumas 468fd8ae1b Modernizing the go invocations 2 years ago
Nick Dumas cfa4465027 Upgrading the makefile in a major way
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 47c6d2ac0e such a simple typo
continuous-integration/drone/push Build is passing Details
2 years ago
Nick Dumas 36832f9bb2 Quoting variables is important too I guess
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 6622f5ef46 debug time
continuous-integration/drone/push Build is failing Details
continuous-integration/drone Build is failing Details
2 years ago
Nick Dumas d64d913602 debug time
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 7d6af47591 Maybe I do need to ditch ~
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 8e8a6a9ebd whoops, gotta use the right secret
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 75ec3dd05a single character typos will get you
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 75f33bc0e4 removing this, don't need it if i have credentials file
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 991b6f6e04 maybe ~ doesn't resolve properly 2 years ago
Nick Dumas 061a59b428 ????
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas a3244c3684 Refactoring the makefile for sanity and pipelining
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 79bcaa4eb4 Adding phonies
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 5201d0a4d5 Secret token time?
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 055ceea0ad the token seems to be wrong but this should be better
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 2c858460fe still can't read
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas a06b400ee4 reading the docs is hard
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 65ad2bf182 fucking tabs
continuous-integration/drone/push Build encountered an error Details
2 years ago
Nick Dumas 8b734ac9f6 hopefully authenticating into the container registry
continuous-integration/drone/push Build encountered an error Details
2 years ago
Nick Dumas 8d53d196ad I need docker, I guess
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 25dfb1aae1 install some tooling
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 3e38783817 better naming for steps
continuous-integration/drone/push Build is failing Details
2 years ago
Nick Dumas 2504e6d5fa first pass at the drone pipeline 2 years ago
Nick Dumas 0bf889b60a Docker builds and makefile to match 2 years ago
Nick Dumas 93869409c3 correcting workdir value 2 years ago
Nick Dumas 8156b60f03 this IS the final piece 2 years ago
Nick Dumas 515f7562b8 this might be the missing piece 2 years ago
Nick Dumas b234039e12 do I have to be more precise? 2 years ago
Nick Dumas 1971fc1bf9 flew too close to the sun there 2 years ago
Nick Dumas 11c97623c2 Changing workdir so goreleaser can build 2 years ago

@ -0,0 +1,43 @@
kind: pipeline
name: publish-docker-image
clone:
disable: true
steps:
- name: clone
image: alpine/git
commands:
- git clone https://code.ndumas.com/ndumas/obsidian-pipeline.git .
- name: build-and-public-docker-image
image: docker:dind
volumes:
- name: dockersock
path: /var/run
commands:
- apk add git docker make
- mkdir $HOME/.docker/
- echo "$GITEA_DOCKER_REGISTRY_TOKEN" > $HOME/.docker/config.json
- make debug
- make docker
environment:
GITEA_DOCKER_REGISTRY_TOKEN:
from_secret: GITEA_DOCKER_REGISTRY_TOKEN
services:
- name: docker
image: docker:dind
privileged: true
volumes:
- name: dockersock
path: /var/run
volumes:
- name: dockersock
temp: {}
trigger:
ref:
include:
- refs/tags/*

@ -21,7 +21,7 @@ jobs:
with:
go-version: ">=1.20.0"
- name: Test
run: make test
run: make setup && make check
goreleaser:
needs: test
@ -40,6 +40,7 @@ jobs:
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
workdir: "./cmd/obp/"
distribution: goreleaser
version: ${{ env.GITHUB_REF_NAME }}
args: release --clean

3
.gitignore vendored

@ -1,3 +1,4 @@
node_modules/*
package*.json
bin/*
dist/*
reports/*

@ -0,0 +1,4 @@
builds:
id: "obp-release"
main: "./cmd/obp/"
binary: "obp"

@ -0,0 +1,37 @@
# syntax=docker/dockerfile:1
# Build the application from source
FROM golang:latest AS build-stage
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . ./
# This definitely works
# RUN CGO_ENABLED=0 GOOS=linux go build -o obp ./cmd/obp/
RUN make build-alpine
# Deploy the application binary into a lean image
FROM alpine:latest AS build-release-stage
WORKDIR /
ARG VERSION=version
COPY --from=build-stage /app/dist/*-alpine/obp /bin/
RUN chmod +x /bin/obp
ARG USER=default
ENV HOME /home/$USER
RUN apk add --update sudo
RUN adduser -D $USER \
&& echo "$USER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/$USER \
&& chmod 0440 /etc/sudoers.d/$USER
USER $USER
WORKDIR $HOME

@ -1,18 +1,217 @@
BINARY_NAME=obp
# This file is intended as a starting point for a customized makefile for a Go project.
#
# Targets:
# all: Format, check, build, and test the code
# setup: Install build/test toolchain dependencies (e.g. gox)
# lint: Run linters against source code
# bump-{major,minor,patch}: create a new semver git tag
# release-{major,minor,patch}: push a tagged release
# format: Format the source files
# build: Build the command(s) for target OS/arch combinations
# install: Install the command(s)
# clean: Clean the build/test artifacts
# report: Generate build/test reports
# check: Run tests
# bench: Run benchmarks
# dist: zip/tar binaries & documentation
# debug: print parameters
#
# Parameters:
# VERSION: release version in semver format
# BUILD_TAGS: additional build tags to pass to go build
# DISTDIR: path to save distribution files
# RPTDIR: path to save build/test reports
#
# Assumptions:
# - Your package contains a cmd/ package, containing a directory for each produced binary.
# - You have cloc installed and accessible in the PATH.
# - Your GOPATH and GOROOT are set correctly.
# - Your makefile is in the root of your package and does not have a space in its file name.
# - Your root package contains global string variables Version and Build, to receive the bild version number and commit ID, respectively.
#
# Features:
# - report generates files that can be consumed by Jenkins, as well as a list of external dependencies.
# - setup installs all the tools aside from cloc.
# - Works on Windows and with paths containing spaces.
# - Works when executing from outside the makefile directory using -f.
# - Targets are useful both in CI and developer workstations.
# - Handles cross-compiation for multiple OSes and architectures.
# - Bundles binaries and documentation into compressed archives, using tar/gz for Linux and Darwin, and zip for Windows.
build-all:
GOOS=darwin GOARCH=amd64 go build -o bin/${BINARY_NAME}-darwin cmd/obp/*.go
GOOS=linux GOARCH=amd64 go build -o bin/${BINARY_NAME}-linux cmd/obp/*.go
GOOS=windows GOARCH=amd64 go build -o bin/${BINARY_NAME}-windows.exe cmd/obp/*.go
clean-all:
go clean
rm bin/${BINARY_NAME}-darwin
rm bin/${BINARY_NAME}-linux
rm bin/${BINARY_NAME}-windows.exe
# Parameters
PKG = code.ndumas.com/ndumas/obsidian-pipeline
NAME = obsidian-pipeline
DOC = README.md LICENSE
test:
go test ./...
test_coverage:
go test ./... -coverprofile=coverage.out
# Replace backslashes with forward slashes for use on Windows.
# Make is !@#$ing weird.
E :=
BSLASH := \$E
FSLASH := /
# Directories
WD := $(subst $(BSLASH),$(FSLASH),$(shell pwd))
MD := $(subst $(BSLASH),$(FSLASH),$(shell dirname "$(realpath $(lastword $(MAKEFILE_LIST)))"))
PKGDIR = $(MD)
CMDDIR = $(PKGDIR)/cmd
DISTDIR ?= $(WD)/dist
RPTDIR ?= $(WD)/reports
GP = $(subst $(BSLASH),$(FSLASH),$(GOPATH))
# Parameters
VERSION ?= $(shell git -C "$(MD)" describe --tags --dirty=-dev)
COMMIT_ID := $(shell git -C "$(MD)" rev-parse HEAD | head -c8)
BUILD_TAGS ?= release
CMDPKG = $(PKG)/cmd
CMDS := $(shell find "$(CMDDIR)/" -mindepth 1 -maxdepth 1 -type d | sed 's/ /\\ /g' | xargs -n1 basename)
BENCHCPUS ?= 1,2,4
# Commands
GOCMD = go
ARCHES ?= amd64 386
OSES ?= windows linux darwin
OUTTPL = $(DISTDIR)/$(NAME)-$(VERSION)-{{.OS}}_{{.Arch}}/{{.Dir}}
LDFLAGS = -X $(PKG).Version=$(VERSION) -X $(PKG).Build=$(COMMIT_ID)
GOBUILD = gox -osarch="!darwin/386" -rebuild -gocmd="$(GOCMD)" -arch="$(ARCHES)" -os="$(OSES)" -output="$(OUTTPL)" -tags "$(BUILD_TAGS)" -ldflags "$(LDFLAGS)"
GOCLEAN = $(GOCMD) clean
GOINSTALL = $(GOCMD) install -a -tags "$(BUILD_TAGS)" -ldflags "$(LDFLAGS)"
GOTEST = $(GOCMD) test -v -tags "$(BUILD_TAGS)"
DISABLED_LINTERS = varnamelen,interfacer,ifshort,exhaustivestruct,maligned,varcheck,scopelint,structcheck,deadcode,nosnakecase,golint,depguard
GOLINT = golangci-lint run --enable-all --disable "$(DISABLED_LINTERS)" --timeout=30s --tests
GODEP = $(GOCMD) get -d -t
GOFMT = goreturns -w
GOBENCH = $(GOCMD) test -v -tags "$(BUILD_TAGS)" -cpu=$(BENCHCPUS) -run=NOTHING -bench=. -benchmem -outputdir "$(RPTDIR)"
GZCMD = tar -czf
ZIPCMD = zip
SHACMD = sha256sum
SLOCCMD = cloc --by-file --xml --exclude-dir="vendor" --include-lang="Go"
XUCMD = go2xunit
DOCKER_CMD=docker --config ~/.docker/
# Dynamic Targets
INSTALL_TARGETS := $(addprefix install-,$(CMDS))
.PHONY: all
all: debug setup dep format lint test bench build dist
git-push:
git push origin main --tags
git push github main --tags
release-major: bump-major git-push
release-minor: bump-minor git-push
release-patch: bump-patch git-push
setup: setup-dirs setup-build setup-format setup-lint setup-reports setup-bump
setup-bump:
go install github.com/guilhem/bump@latest
bump-major: setup-bump
bump major
bump-minor: setup-bump
bump minor
bump-patch: setup-bump
bump patch
setup-reports: setup-dirs
go install github.com/tebeka/go2xunit@latest
setup-build: setup-dirs
go install github.com/mitchellh/gox@latest
setup-format: setup-dirs
go install github.com/sqs/goreturns@latest
setup-lint: setup-dirs
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.1
setup-dirs:
mkdir -p "$(RPTDIR)"
mkdir -p "$(DISTDIR)"
clean:
$(GOCLEAN) $(PKG)
rm -vrf "$(DISTDIR)"/*
rm -vf "$(RPTDIR)"/*
format:
$(GOFMT) "$(PKGDIR)"
dep:
$(GODEP) $(PKG)/...
lint: setup-dirs dep
$(GOLINT) "$(PKGDIR)" | tee "$(RPTDIR)/lint.out"
check: setup-dirs clean dep
$(GOTEST) $$(go list "$(PKG)/..." | grep -v /vendor/) | tee "$(RPTDIR)/test.out"
bench: setup-dirs clean dep
$(GOBENCH) $$(go list "$(PKG)/..." | grep -v /vendor/) | tee "$(RPTDIR)/bench.out"
report: check
cd "$(PKGDIR)";$(SLOCCMD) --out="$(RPTDIR)/cloc.xml" . | tee "$(RPTDIR)/cloc.out"
cat "$(RPTDIR)/test.out" | $(XUCMD) -output "$(RPTDIR)/tests.xml"
go list -f '{{join .Deps "\n"}}' "$(CMDPKG)/..." | sort | uniq | xargs -I {} sh -c "go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' {} | tee -a '$(RPTDIR)/deps.out'"
build: $(CMDS)
$(CMDS): setup-dirs dep
$(GOBUILD) "$(CMDPKG)/$@" | tee "$(RPTDIR)/build-$@.out"
install: $(INSTALL_TARGETS)
$(INSTALL_TARGETS):
$(GOINSTALL) "$(CMDPKG)/$(subst install-,,$@)"
dist: clean build
for docfile in $(DOC); do \
for dir in "$(DISTDIR)"/*; do \
cp "$(PKGDIR)/$$docfile" "$$dir/"; \
done; \
done
cd "$(DISTDIR)"; for dir in ./*linux*; do $(GZCMD) "$(basename "$$dir").tar.gz" "$$dir"; done
cd "$(DISTDIR)"; for dir in ./*windows*; do $(ZIPCMD) "$(basename "$$dir").zip" "$$dir"; done
cd "$(DISTDIR)"; for dir in ./*darwin*; do $(GZCMD) "$(basename "$$dir").tar.gz" "$$dir"; done
cd "$(DISTDIR)"; find . -maxdepth 1 -type f -printf "$(SHACMD) %P | tee \"./%P.sha\"\n" | sh
$(info "Built v$(VERSION), build $(COMMIT_ID)")
.PHONY: docker
docker: docker-image docker-push
.PHONY: docker-push
docker-push:
$(DOCKER_CMD) tag code.ndumas.com/ndumas/obsidian-pipeline:$(VERSION) code.ndumas.com/ndumas/obsidian-pipeline:latest
$(DOCKER_CMD) push code.ndumas.com/ndumas/obsidian-pipeline:latest
$(DOCKER_CMD) push code.ndumas.com/ndumas/obsidian-pipeline:$(VERSION)
.PHONY: docker-image
docker-image:
$(DOCKER_CMD) build --build-arg VERSION=$(VERSION) -t code.ndumas.com/ndumas/obsidian-pipeline:$(VERSION) .
.PHONY: build-alpine
build-alpine:
# this version breaks build variable injection
# CGO_ENABLED=0 GOOS=linux go build -ldflags="buildmode=exe $(LDFLAGS) -linkmode external -w -extldflags '-static' " -o $(DISTDIR)/$(NAME)-$(VERSION)-alpine/obp cmd/obp/*.go
CGO_ENABLED=0 GOOS=linux go build -ldflags="$(LDFLAGS)" -o $(DISTDIR)/$(NAME)-$(VERSION)-alpine/obp cmd/obp/*.go
debug:
$(info MD=$(MD))
$(info WD=$(WD))
$(info PKG=$(PKG))
$(info PKGDIR=$(PKGDIR))
$(info DISTDIR=$(DISTDIR))
$(info VERSION=$(VERSION))
$(info COMMIT_ID=$(COMMIT_ID))
$(info BUILD_TAGS=$(BUILD_TAGS))
$(info CMDS=$(CMDS))
$(info BUILD_TARGETS=$(BUILD_TARGETS))
$(info INSTALL_TARGETS=$(INSTALL_TARGETS))

@ -1,11 +0,0 @@
package obp
func (p *Pipeline) FindAttachments() error {
return nil
}
func (p *Pipeline) MoveAttachments(post string) error {
return nil
}

@ -1,142 +0,0 @@
package obp
import (
"fmt"
"io/fs"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"go.uber.org/zap"
)
func (p *Pipeline) Walk() error {
notesRoot := os.DirFS(p.Source)
blogRoot := os.DirFS(p.Target)
err := fs.WalkDir(notesRoot, ".", p.findAttachments)
if err != nil {
return fmt.Errorf("error scanning for attachments: %w", err)
}
err = fs.WalkDir(notesRoot, ".", p.findNotes)
if err != nil {
return fmt.Errorf("error scanning vault for posts: %w", err)
}
err = fs.WalkDir(blogRoot, ".", p.findPosts)
if err != nil {
return fmt.Errorf("error scanning blog for posts: %w", err)
}
return nil
}
func (p *Pipeline) findNotes(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
walkLogger := p.L.Named("FindNotes").With(zap.String("path", path))
if strings.HasSuffix(path, ".md") && strings.Contains(path, p.BlogDir) {
walkLogger.Info("found blog post to publish, adding to index")
p.Notes = append(p.Notes, path)
}
return nil
}
func (p *Pipeline) findAttachments(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
walkLogger := p.L.Named("FindAttachments").With(zap.String("path", path))
if strings.Contains(path, p.AttachmentsDir) {
walkLogger.Info("found attachment file, adding to index")
absPath, err := filepath.Abs(filepath.Join(p.Source, path))
if err != nil {
return fmt.Errorf("error generating absolute path for attachment %q: %w", path, err)
}
walkLogger.Info("adding Attachment",
zap.String("key", filepath.Base(absPath)),
zap.String("value", absPath),
)
p.Attachments[filepath.Base(absPath)] = absPath
}
return nil
}
func (p *Pipeline) findPosts(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
walkLogger := p.L.Named("FindPosts").With(zap.String("path", path))
if strings.HasSuffix(path, "index.md") {
walkLogger.Info("found index.md, adding to index")
p.Posts = append(p.Posts, path)
}
return nil
}
func (p *Pipeline) Move() error {
moveLogger := p.L.Named("Move")
moveLogger.Info("scanning posts", zap.Strings("posts", p.Posts))
for _, post := range p.Notes {
// log.Printf("scanning %q for attachment links", post)
linkedAttachments, err := extractAttachments(filepath.Join(p.Source, post))
if err != nil {
return fmt.Errorf("could not extract attachment links from %q: %w", post, err)
}
for _, attachment := range linkedAttachments {
att, ok := p.Attachments[attachment]
if !ok {
return fmt.Errorf("Attachment is linked by post %q but doesn't exist in attachments directory %q", post, p.AttachmentsDir)
}
moveAttachment(post, att, p.L.Named("moveAttachment"))
}
}
return nil
}
func moveAttachment(post, attachment string, l *zap.Logger) error {
l.Info("moving attachment",
zap.String("post", post),
zap.String("attachment", attachment),
)
return nil
}
func extractAttachments(post string) ([]string, error) {
pat := regexp.MustCompile(`\[\[Resources\/attachments\/(.*)?\]\]`)
attachments := make([]string, 0)
postBody, err := ioutil.ReadFile(post)
if err != nil {
return attachments, fmt.Errorf("error opening post to scan for attachment links: %w", err)
}
for _, att := range pat.FindAllSubmatch(postBody, -1) {
filename := string(att[1])
attachments = append(attachments, filename)
}
return attachments, nil
}

@ -4,40 +4,15 @@ Copyright © 2023 NAME HERE <EMAIL ADDRESS>
package cmd
import (
// "fmt"
"github.com/spf13/cobra"
)
var (
source, target string
)
// rootCmd represents the base command when called without any subcommands
var hugoCmd = &cobra.Command{
Use: "hugo",
Short: "convert a set of Obsidian notes into a Hugo compatible directory structure",
Long: `long description`,
PreRunE: func(cmd *cobra.Command, args []string) error {
// here is where I validate arguments, open and parse config files, etc
return nil
},
Short: "manage your hugo blog using your vault as a source of truth",
Long: `manage your hugo blog using your vault as a source of truth`,
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
hugoCmd.PersistentFlags().StringVar(&source, "source", "", "directory containing ready-to-publish posts")
hugoCmd.PersistentFlags().StringVar(&target, "target", "", "target Hugo directory (typically content/posts)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
// rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
// rootCmd.SetHelpFunc(gloss.CharmHelp)
// rootCmd.SetUsageFunc(gloss.CharmUsage)
rootCmd.AddCommand(hugoCmd)
}

@ -0,0 +1,62 @@
/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/
package cmd
import (
"fmt"
"log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"code.ndumas.com/ndumas/obsidian-pipeline"
)
var hugoBundleCmd = &cobra.Command{
Use: "bundle",
Short: "convert a set of Obsidian notes into a Hugo compatible directory structure",
Long: `generate hugo content from your vault`,
PreRunE: func(cmd *cobra.Command, args []string) error {
// here is where I validate arguments, open and parse config files, etc
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
source := viper.GetString("hugo.source")
target := viper.GetString("hugo.target")
err := obp.CopyPosts(source, target)
if err != nil {
return fmt.Errorf("error copying posts in %q: %w", source, err)
}
err = obp.Sanitize(source)
if err != nil {
return fmt.Errorf("error sanitizing posts in %q: %w", source, err)
}
err = obp.GatherMedia(source)
if err != nil {
return fmt.Errorf("error gathering media in %q: %w", source, err)
}
return nil
},
}
func init() {
hugoBundleCmd.Flags().StringP("source", "s", "", "path to vault directory containing hugo posts")
err := viper.BindPFlag("hugo.source", hugoBundleCmd.Flags().Lookup("source"))
if err != nil {
log.Panicln("error binding viper to source flag:", err)
}
hugoBundleCmd.Flags().StringP("target", "t", "", "hugo content/ directory")
err = viper.BindPFlag("hugo.target", hugoBundleCmd.Flags().Lookup("target"))
if err != nil {
log.Panicln("error binding viper to target flag:", err)
}
hugoBundleCmd.MarkFlagsRequiredTogether("source", "target")
hugoCmd.AddCommand(hugoBundleCmd)
}

@ -25,8 +25,8 @@ var validateCmd = &cobra.Command{
Long: `Validate YAML frontmatter with jsonschema
`,
RunE: func(cmd *cobra.Command, args []string) error {
schema := viper.GetString("schema")
target := viper.GetString("target")
schema := viper.GetString("validate.schema")
target := viper.GetString("validate.target")
if target == "" {
return fmt.Errorf("target flag must not be empty")
}
@ -34,7 +34,7 @@ var validateCmd = &cobra.Command{
err := fs.WalkDir(root, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
return fmt.Errorf("could not walk %q: %w", path, err)
}
if d.IsDir() {
@ -45,23 +45,25 @@ var validateCmd = &cobra.Command{
if err != nil {
return fmt.Errorf("error generating absolute path for %q", target)
}
target, err := os.Open(absPath)
file, err := os.Open(absPath)
if err != nil {
return fmt.Errorf("could not open target file: %w", err)
}
defer target.Close()
err = obp.Validate(schema, target)
defer file.Close()
err = obp.Validate(schema, file)
if err != nil {
details, ok := err.(*jsonschema.ValidationError)
if !ok {
return err
return fmt.Errorf("eror validating %q: %w", path, err)
}
obp.PrettyDetails(cmd.OutOrStdout(), viper.GetString("format"), details.DetailedOutput(), absPath)
}
return nil
})
return err
if err != nil {
return fmt.Errorf("walkfunc failed: %w", err)
}
return nil
},
}
@ -73,21 +75,21 @@ func init() {
validateCmd.Flags().StringP("target", "t", "", "directory containing validation targets")
validateCmd.MarkFlagsRequiredTogether("schema", "target")
validateCmd.PersistentFlags().StringVar(&format, "format", "markdown", "output format [markdown, json, csv]")
rootCmd.AddCommand(validateCmd)
err := viper.BindPFlag("schema", validateCmd.Flags().Lookup("schema"))
if err != nil {
log.Panicln("error binding viper to schema flag:", err)
}
err = viper.BindPFlag("target", validateCmd.Flags().Lookup("target"))
err = viper.BindPFlag("validate.target", validateCmd.Flags().Lookup("target"))
if err != nil {
log.Panicln("error binding viper to target flag:", err)
}
err = viper.BindPFlag("format", validateCmd.PersistentFlags().Lookup("format"))
err = viper.BindPFlag("validate.format", validateCmd.PersistentFlags().Lookup("format"))
if err != nil {
log.Panicln("error binding viper to format flag:", err)
}
rootCmd.AddCommand(validateCmd)
}

@ -0,0 +1,31 @@
/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"code.ndumas.com/ndumas/obsidian-pipeline"
)
// rootCmd represents the base command when called without any subcommands
var versionCmd = &cobra.Command{
Use: "version",
Short: "prints obp version info",
Long: `displays git tag and sha
`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Version: %s Build: %s\n", obp.Version, obp.Build)
},
}
func init() {
// Cobra also supports local flags, which will only run
// when this action is called directly.
// rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
rootCmd.AddCommand(versionCmd)
}

@ -3,7 +3,9 @@ Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/
package main
import "code.ndumas.com/ndumas/obsidian-pipeline/cmd/obp/cmd"
import (
"code.ndumas.com/ndumas/obsidian-pipeline/cmd/obp/cmd"
)
func main() {
cmd.Execute()

@ -6,33 +6,47 @@ require (
github.com/charmbracelet/lipgloss v0.7.1
github.com/santhosh-tekuri/jsonschema/v5 v5.3.0
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.15.0
github.com/spf13/viper v1.4.0
go.uber.org/zap v1.24.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/alecthomas/gometalinter v3.0.0+incompatible // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/hashicorp/go-version v1.0.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mitchellh/gox v1.0.1 // indirect
github.com/mitchellh/iochan v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.1 // indirect
github.com/nicksnyder/go-i18n v1.10.1 // indirect
github.com/pelletier/go-toml v1.2.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/tebeka/go2xunit v1.4.10 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.5.0 // indirect
golang.org/x/tools v0.1.12 // indirect
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20191105091915-95d230a53780 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect
)

107
go.sum

@ -38,10 +38,21 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/gometalinter v3.0.0+incompatible h1:e9Zfvfytsw/e6Kd/PYd75wggK+/kX5Xn8IYDUKyc5fU=
github.com/alecthomas/gometalinter v3.0.0+incompatible/go.mod h1:qfIpQGGz3d+NmgyPBqv+LSh50emm1pt72EtcX2vKYQk=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@ -51,10 +62,17 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@ -62,12 +80,21 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -103,6 +130,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -118,10 +146,18 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8=
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@ -130,10 +166,15 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -141,6 +182,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
@ -148,41 +190,76 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nicksnyder/go-i18n v1.10.1 h1:isfg77E/aCD7+0lD/D00ebR2MV5vgeQ276WYyDaCRQc=
github.com/nicksnyder/go-i18n v1.10.1/go.mod h1:e4Di5xjP9oTVrC6y3C7C0HoSYXjSbhh/dU0eUV32nB4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.0 h1:uIkTLo0AGRc8l7h5l9r+GcYi9qfVPt6lD4/bhmzfiKo=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU=
github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA=
github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518 h1:iD+PFTQwKEmbwSdwfvP5ld2WEI/g7qbdhmHJ2ASfYGs=
github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -196,24 +273,35 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/tebeka/go2xunit v1.4.10 h1:0UO+9YoLpXTZ0DL9XbTmIIibgmKBGiwroo8uhFMSyR0=
github.com/tebeka/go2xunit v1.4.10/go.mod h1:wmc9jKT7KlU4QLU6DNTaIXNnYNOjKKNlp6mjOS0UrqY=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -254,14 +342,19 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -305,6 +398,9 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -354,6 +450,7 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -401,6 +498,8 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -469,6 +568,7 @@ google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
@ -493,12 +593,19 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20191105091915-95d230a53780 h1:CEBpW6C191eozfEuWdUmIAHn7lwlLxJ7HVdr2e2Tsrw=
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20191105091915-95d230a53780/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -0,0 +1,92 @@
package obp
import (
"fmt"
"io"
"io/fs"
// "log"
"os"
"path/filepath"
"strings"
)
func copy(src, dst string) (int64, error) {
sourceFileStat, err := os.Stat(src)
if err != nil {
return 0, err
}
if !sourceFileStat.Mode().IsRegular() {
return 0, fmt.Errorf("%s is not a regular file", src)
}
source, err := os.Open(src)
if err != nil {
return 0, err
}
defer source.Close()
destination, err := os.Create(dst)
if err != nil {
return 0, err
}
defer destination.Close()
nBytes, err := io.Copy(destination, source)
return nBytes, err
}
func CopyPosts(src, dst string) error {
posts := make([]string, 0)
srcRoot := os.DirFS(src)
err := fs.WalkDir(srcRoot, ".", func(path string, d fs.DirEntry, err error) error {
// here's where I walk through the source directory and collect all the markdown notes
if err != nil {
return fmt.Errorf("could not walk %q: %w", path, err)
}
if d.IsDir() {
return nil
}
if strings.HasSuffix(path, ".md") {
posts = append(posts, filepath.Join(src, path))
}
return nil
})
if err != nil {
return fmt.Errorf("walkfunc failed: %w", err)
}
for _, post := range posts {
base := filepath.Base(post)
splitPostName := strings.Split(base, ".")
postName := strings.Join(splitPostName[:len(splitPostName)-1], ".")
postDir := filepath.Join(dst, postName)
err := os.MkdirAll(postDir, 0777)
if err != nil && !os.IsExist(err) {
return fmt.Errorf("error creating target directory %q: %w", dst, err)
}
_, err = copy(post, filepath.Join(postDir, "index.md"))
if err != nil {
return fmt.Errorf("error opening %q for copying: %w", post, err)
}
}
return nil
}
func Sanitize(src string) error {
return nil
}
func GatherMedia(src string) error {
return nil
}

@ -1,17 +1,27 @@
package obp
import (
"fmt"
"io/fs"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"go.uber.org/zap"
)
func NewPipeline(dev bool) *Pipeline {
var p Pipeline
var l *zap.Logger
l, _ = zap.NewProduction()
if dev {
l, _ = zap.NewDevelopment()
}
p.L = l
p.Attachments = make(map[string]string)
p.Posts = make([]string, 0)
@ -26,3 +36,169 @@ type Pipeline struct {
L *zap.Logger
BlogDir, AttachmentsDir string
}
func (p *Pipeline) Walk() error {
notesRoot := os.DirFS(p.Source)
blogRoot := os.DirFS(p.Target)
err := fs.WalkDir(notesRoot, ".", p.findAttachments)
if err != nil {
return fmt.Errorf("error scanning for attachments: %w", err)
}
err = fs.WalkDir(notesRoot, ".", p.findNotes)
if err != nil {
return fmt.Errorf("error scanning vault for posts: %w", err)
}
err = fs.WalkDir(blogRoot, ".", p.findPosts)
if err != nil {
return fmt.Errorf("error scanning blog for posts: %w", err)
}
return nil
}
func (p *Pipeline) findNotes(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
walkLogger := p.L.Named("FindNotes").With(zap.String("path", path))
if strings.HasSuffix(path, ".md") && strings.Contains(path, p.BlogDir) {
walkLogger.Info("found blog post to publish, adding to index")
p.Notes = append(p.Notes, path)
}
return nil
}
func (p *Pipeline) findAttachments(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
walkLogger := p.L.Named("FindAttachments").With(zap.String("path", path))
if strings.Contains(path, p.AttachmentsDir) {
walkLogger.Info("found attachment file, adding to index")
absPath, err := filepath.Abs(filepath.Join(p.Source, path))
if err != nil {
return fmt.Errorf("error generating absolute path for attachment %q: %w", path, err)
}
walkLogger.Info("adding Attachment",
zap.String("key", filepath.Base(absPath)),
zap.String("value", absPath),
)
p.Attachments[filepath.Base(absPath)] = absPath
}
return nil
}
func (p *Pipeline) findPosts(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
walkLogger := p.L.Named("FindPosts").With(zap.String("path", path))
if strings.HasSuffix(path, "index.md") {
walkLogger.Info("found index.md, adding to index")
p.Posts = append(p.Posts, path)
}
return nil
}
func (p *Pipeline) Move() error {
moveLogger := p.L.Named("Move")
moveLogger.Info("scanning posts", zap.Strings("posts", p.Posts))
for _, post := range p.Notes {
// log.Printf("scanning %q for attachment links", post)
linkedAttachments, err := extractAttachments(filepath.Join(p.Source, post))
if err != nil {
return fmt.Errorf("could not extract attachment links from %q: %w", post, err)
}
for _, attachment := range linkedAttachments {
att, ok := p.Attachments[attachment]
if !ok {
return fmt.Errorf("Attachment is linked by post %q but doesn't exist in attachments directory %q", post, p.AttachmentsDir)
}
err := moveAttachment(post, att, p.L.Named("moveAttachment"))
if err != nil {
return fmt.Errorf("error moving attachments: %w", err)
}
}
}
return nil
}
func moveAttachment(post, attachment string, l *zap.Logger) error {
l.Info("moving attachment",
zap.String("post", post),
zap.String("attachment", attachment),
)
return nil
}
func extractAttachments(post string) ([]string, error) {
pat := regexp.MustCompile(`\[\[Resources\/attachments\/(.*)?\]\]`)
attachments := make([]string, 0)
postBody, err := ioutil.ReadFile(post)
if err != nil {
return attachments, fmt.Errorf("error opening post to scan for attachment links: %w", err)
}
for _, att := range pat.FindAllSubmatch(postBody, -1) {
filename := string(att[1])
attachments = append(attachments, filename)
}
return attachments, nil
}
func (p *Pipeline) FindAttachments() error {
return nil
}
func (p *Pipeline) MoveAttachments(post string) error {
return nil
}
func (p *Pipeline) FindPosts() error {
return nil
}
func (p *Pipeline) SanitizePost(post string) error {
return nil
}
func (p *Pipeline) CopyPost(post string) error {
return nil
}

@ -1,15 +0,0 @@
package obp
func (p *Pipeline) FindPosts() error {
return nil
}
func (p *Pipeline) SanitizePost(post string) error {
return nil
}
func (p *Pipeline) CopyPost(post string) error {
return nil
}

@ -2,34 +2,41 @@ package obp
import (
"encoding/json"
"errors"
"fmt"
"io"
"github.com/santhosh-tekuri/jsonschema/v5"
// allow the jsonschema validator to auto-download http-hosted schemas.
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
"gopkg.in/yaml.v3"
)
var ErrUnsupportedOutputFormat = errors.New("unspported output format")
// Validate accepts a Markdown file as input via the Reader
// and parses the frontmatter present, if any. It then
// applies the schema fetched from schemaURL against the
// decoded YAML.
func Validate(schemaURL string, r io.Reader) error {
var m interface{}
var frontmatter interface{}
dec := yaml.NewDecoder(r)
err := dec.Decode(&m)
err := dec.Decode(&frontmatter)
if err != nil {
return fmt.Errorf("error decoding YAML: %w", err)
}
compiler := jsonschema.NewCompiler()
schema, err := compiler.Compile(schemaURL)
if err != nil {
return fmt.Errorf("error compiling schema: %w", err)
}
if err := schema.Validate(m); err != nil {
return err
if err != nil {
return fmt.Errorf("frontmatter failed validation: %w", schema.Validate(frontmatter))
}
return nil
@ -39,6 +46,7 @@ func recurseDetails(detailed jsonschema.Detailed, acc map[string]jsonschema.Deta
if detailed.Error != "" {
acc[detailed.AbsoluteKeywordLocation] = detailed
}
for _, e := range detailed.Errors {
acc = recurseDetails(e, acc)
}
@ -49,29 +57,30 @@ func recurseDetails(detailed jsonschema.Detailed, acc map[string]jsonschema.Deta
// PrettyDetails takes error output from jsonschema.Validate
// and pretty-prints it to stdout.
//
// Supported formats are: JSON, Markdown
func PrettyDetails(w io.Writer, format string, details jsonschema.Detailed, filename string) error {
// Supported formats are: JSON, Markdown.
func PrettyDetails(writer io.Writer, format string, details jsonschema.Detailed, filename string) error {
// acc := make([]jsonschema.Detailed, 0)
acc := make(map[string]jsonschema.Detailed)
errors := recurseDetails(details, acc)
switch format {
case "json":
enc := json.NewEncoder(w)
enc := json.NewEncoder(writer)
err := enc.Encode(details)
if err != nil {
return fmt.Errorf("error writing JSON payload to provided writer: %w", err)
}
case "markdown":
fmt.Fprintf(w, "# Validation Errors for %q\n", filename)
fmt.Fprintf(w, "Validation Rule|Failing Property|Error\n")
fmt.Fprintf(w, "--|---|---\n")
fmt.Fprintf(writer, "# Validation Errors for %q\n", filename)
fmt.Fprintf(writer, "Validation Rule|Failing Property|Error\n")
fmt.Fprintf(writer, "--|---|---\n")
for _, e := range errors {
fmt.Fprintf(w, "%s|%s|%s\n", e.KeywordLocation, e.InstanceLocation, e.Error)
fmt.Fprintf(writer, "%s|%s|%s\n", e.KeywordLocation, e.InstanceLocation, e.Error)
}
default:
return fmt.Errorf("unknown format")
return ErrUnsupportedOutputFormat
}
return nil

@ -0,0 +1,77 @@
package obp_test
import (
"bytes"
"testing"
"code.ndumas.com/ndumas/obsidian-pipeline"
)
func Test_BasicValidation(t *testing.T) {
t.Parallel()
tt := []struct {
name string
b *bytes.Buffer
expected error
}{
{
name: "KeyMissing",
b: bytes.NewBufferString(`
---
boop: "bop"
---
# Markdown Content
`),
expected: nil,
},
{
name: "KeyTypeMismatch",
b: bytes.NewBufferString(`
---
title: 2
---
# Markdown Content
`),
expected: nil,
},
{
name: "GoodSchema",
b: bytes.NewBufferString(`
---
draft: false
title: "Mapping Aardwolf with Graphviz and Golang"
aliases: ["Mapping Aardwolf with Graphviz"]
series: ["mapping-aardwolf"]
date: "2023-04-06"
author: "Nick Dumas"
cover: ""
keywords: [""]
description: "Maxing out your CPU for fun and profit with dense graphs, or how I'm attempting to follow through on my plan to work on projects with more visual
outputs"
showFullContent: false
tags:
- graphviz
- graph
- aardwolf
- golang
---
## Textual Cartography
Aardwolf has a fairly active developer community, people who write and maintain plugins and try to map the game world and its contents.
`),
expected: nil,
},
}
for _, tc := range tt {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
err := obp.Validate("https://schemas.ndumas.com/obsidian/note.schema.json", tc.b)
if err == tc.expected {
t.Log("Expected Validate() to fail on input")
t.Fail()
}
})
}
}

@ -0,0 +1,5 @@
package obp
var (
Version, Build string
)
Loading…
Cancel
Save