From 2639f290b58d709b7732e44f4c71bd1080b1d654 Mon Sep 17 00:00:00 2001 From: Jan De Dobbeleer Date: Sun, 29 Dec 2024 09:31:27 +0100 Subject: [PATCH] feat(link): create parent folder with mkdir: true resolves #227 --- src/shell/link.go | 43 +++++++++++++++++++++++++++++++++---- src/shell/link_test.go | 12 +++++------ website/docs/setup/link.mdx | 13 +++++------ 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/shell/link.go b/src/shell/link.go index 827daf1..08724e8 100644 --- a/src/shell/link.go +++ b/src/shell/link.go @@ -1,20 +1,37 @@ package shell import ( + "os" + "path/filepath" + "github.com/jandedobbeleer/aliae/src/context" ) type Links []*Link type Link struct { - Name Template `yaml:"name"` - Target Template `yaml:"target"` - If If `yaml:"if"` - + Name Template `yaml:"name"` + Target Template `yaml:"target"` + If If `yaml:"if"` template string + MkDir bool `yaml:"mkdir"` + force bool } func (l *Link) string() string { + // avoid parsing multiple times + l.Name = l.Name.Parse() + l.Target = l.Target.Parse() + + // do not process if the link already exists or the target does not exist + if l.exists(string(l.Name)) || (!l.force && !l.exists(string(l.Target))) { + return "" + } + + if l.MkDir { + l.buildPath() + } + switch context.Current.Shell { case ZSH, BASH, FISH, XONSH: return l.zsh().render() @@ -31,6 +48,24 @@ func (l *Link) string() string { } } +func (l *Link) exists(path string) bool { + _, err := os.Stat(path) + return err == nil +} + +func (l *Link) buildPath() { + parent := filepath.Dir(string(l.Name)) + + _, err := os.Stat(parent) + if err == nil { + return + } + + if os.IsNotExist(err) { + _ = os.MkdirAll(parent, 0644) + } +} + func (l *Link) render() string { script, err := parse(l.template, l) if err != nil { diff --git a/src/shell/link_test.go b/src/shell/link_test.go index 16517f6..8b91387 100644 --- a/src/shell/link_test.go +++ b/src/shell/link_test.go @@ -9,7 +9,7 @@ import ( ) func TestLinkCommand(t *testing.T) { - link := &Link{Name: "foo", Target: "bar"} + link := &Link{Name: "foo", Target: "bar", force: true} cases := []struct { Case string Shell string @@ -80,15 +80,15 @@ func TestLinkRender(t *testing.T) { { Case: "Single link", Links: Links{ - &Link{Name: "FOO", Target: "bar"}, + &Link{Name: "FOO", Target: "bar", force: true}, }, Expected: "ln -sf bar FOO", }, { Case: "Double link", Links: Links{ - &Link{Name: "FOO", Target: "bar"}, - &Link{Name: "BAR", Target: "foo"}, + &Link{Name: "FOO", Target: "bar", force: true}, + &Link{Name: "BAR", Target: "foo", force: true}, }, Expected: `ln -sf bar FOO ln -sf foo BAR`, @@ -96,7 +96,7 @@ ln -sf foo BAR`, { Case: "Filtered out", Links: Links{ - &Link{Name: "FOO", Target: "bar", If: `eq .Shell "fish"`}, + &Link{Name: "FOO", Target: "bar", If: `eq .Shell "fish"`, force: true}, }, }, } @@ -133,7 +133,7 @@ func TestLinkWithTemplate(t *testing.T) { } for _, tc := range cases { - link := &Link{Name: "/tmp/l", Target: tc.Target} + link := &Link{Name: "/tmp/l", Target: tc.Target, force: true} context.Current = &context.Runtime{Shell: BASH, Home: "/Users/jan", OS: context.WINDOWS} assert.Equal(t, tc.Expected, link.string(), tc.Case) } diff --git a/website/docs/setup/link.mdx b/website/docs/setup/link.mdx index f6787aa..18ed6e2 100644 --- a/website/docs/setup/link.mdx +++ b/website/docs/setup/link.mdx @@ -4,7 +4,7 @@ title: Symbolic Link sidebar_label: 🔗 Symbolic Link --- -Create symlinks to files and directories. Useful for dotfiles. +Create symlinks to files and directories. Will validate the targets exist before creating the link. ### Syntax @@ -21,11 +21,12 @@ link: ### Link -| Name | Type | Description | -| -------- | -------- | ------------------------------------------------------------------------------ | -| `name` | `string` | the link name, supports [templating][templates] | -| `target` | `string` | the name of the file or directory to link to, supports [templating][templates] | -| `if` | `string` | golang [template][go-text-template] conditional statement, see [if][if] | +| Name | Type | Description | +| -------- | --------- | ------------------------------------------------------------------------------ | +| `name` | `string` | the link name, supports [templating][templates] | +| `target` | `string` | the name of the file or directory to link to, supports [templating][templates] | +| `if` | `string` | golang [template][go-text-template] conditional statement, see [if][if] | +| `mkdir` | `boolean` | create `name`'s parent folder when it does not exist | [go-text-template]: https://golang.org/pkg/text/template/ [if]: if.mdx