Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions _demo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This is the .gitignore file. It defines which files and directories should be ignored by git.

# Ignore the .doth directory, it contains local state and should not be committed to version control.
.doth/

# target folders
target/example/**
31 changes: 31 additions & 0 deletions _demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# doth demo

## Run it

Execute `./doth.sh deploy -c config.yaml -d` to see what it would do.

Execute `./doth.sh deploy -c config.yaml` to actually run it (or `./doth.sh deploy -c config.yaml -v` for a verbose version).

You don't need anything to run this demo besides the demo folder itself. doth.sh is the so called "doth wrapper", which will automatically setup the most current version of doth for you.

## What it does

The demo has a single module (modules/example), which has six configured steps in it's module.yaml:

```yaml
files:
- name: copy.yml # copies copy.yml
strategy: copy
- name: link.yml # creates a symlink for link.yml
strategy: link
- name: render.yml # renders render.yml using the provided config
strategy: render
- name: copy-folder # copy an entire folder
strategy: copy
- name: link-folder # link an entire folder
strategy: link
- name: render-folder # render an entire folder using the provided config
strategy: render
```

The output folder is configured to be ./target/example - so take a look into this folder after you ran the demo!
1 change: 1 addition & 0 deletions _demo/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
value: '"THIS IS FOR THE RENDER!"'
94 changes: 94 additions & 0 deletions _demo/doth.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/bin/bash

# This is a wrapper script for doth. It ensures that go and doth are installed, and if not, it installs it. Then it calls doth with the provided arguments.

ROOT_DIR=$(dirname "$(realpath "$0")")

GO_VERSION=1.26.2
TARGET_FOLDER=${ROOT_DIR}/.doth/go
GOPATH_FOLDER=${ROOT_DIR}/.doth/gopath

GO_COMMAND=${TARGET_FOLDER}/bin/go

DOTH_LOCK=${ROOT_DIR}/doth.lock

# set paths for this script
export GOPATH=${GOPATH_FOLDER}
export GOBIN=${GOPATH_FOLDER}/bin
export PATH=${TARGET_FOLDER}/bin:${GOBIN}:$PATH

detect_platform() {
local os arch
os=$(uname -s | tr '[:upper:]' '[:lower:]')
arch=$(uname -m)

case "$os" in
linux) GO_OS="linux" ;;
darwin) GO_OS="darwin" ;;
*) echo "Unsupported OS: $os"; exit 1 ;;
esac

case "$arch" in
x86_64) GO_ARCH="amd64" ;;
aarch64|arm64) GO_ARCH="arm64" ;;
armv6l|armv7l) GO_ARCH="armv6l" ;;
*) echo "Unsupported architecture: $arch"; exit 1 ;;
esac
}

setup_go() {
# check if Go is already installed
if command -v ${GO_COMMAND} &> /dev/null; then
return
fi

detect_platform
echo "Installing Go ${GO_VERSION} (${GO_OS}-${GO_ARCH})..."

# Install Go
mkdir -p ${TARGET_FOLDER}
mkdir -p ${GOBIN}
curl -Ls -o ${ROOT_DIR}/.doth/go.tar.gz https://go.dev/dl/go${GO_VERSION}.${GO_OS}-${GO_ARCH}.tar.gz
tar -C ${TARGET_FOLDER} --strip-components=1 -xzf ${ROOT_DIR}/.doth/go.tar.gz
rm ${ROOT_DIR}/.doth/go.tar.gz
}

setup_doth() {
if [[ -f "${DOTH_LOCK}" ]]; then
NEW_VERSION=$(cat "${DOTH_LOCK}")
else
NEW_VERSION=$(curl -Ls https://github.com/5000K/doth/releases/latest/download/version.txt)
fi
echo "Installing doth ${NEW_VERSION}..."
${GO_COMMAND} install github.com/5000K/doth@$NEW_VERSION
doth wrapper > "$0"
}

update_doth() {
if [[ -f "${DOTH_LOCK}" ]]; then
return
fi

NEW_VERSION=$(curl -Ls https://github.com/5000K/doth/releases/latest/download/version.txt)
CURRENT_VERSION=$(doth version --raw)
if [ "$NEW_VERSION" != "$CURRENT_VERSION" ]; then
echo "A new version of doth is available: $NEW_VERSION. You have $CURRENT_VERSION. Updating..."
chmod -R u+w ${GOPATH_FOLDER}/**
rm -rf ${GOPATH_FOLDER}
${GO_COMMAND} install github.com/5000K/doth@$NEW_VERSION
echo "Updating wrapper script..."
doth wrapper > "$0"
chmod +x "$0"
fi
}

# is doth available?
if ! command -v doth &> /dev/null; then
setup_go
setup_doth
else
update_doth
fi

# call doth with the provided arguments
doth "$@"
19 changes: 19 additions & 0 deletions _demo/doth.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This is the doth.yaml file. It defines the configuration for your doth project.

# The directory where your modules are located. Relative to the current working directory.
modulePath: "./modules"

# Set to true if the deploy command should fail if no config files are provided.
requireConfig: true

# Here, you can define dependencies you want to install, and valid package sources for them.
# This is used by the "install" command to determine how to install a dependency based on the package sources configured/detected.
deps:
- name: curl
packages:
pacman: curl
apt: curl
brew: curl

# This is the version of the doth file format. Do not edit this manually.
dothVersionDoNotEditManually: 1
1 change: 1 addition & 0 deletions _demo/modules/example/copy-folder/fileinfolder.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This file should also be copied over, by simply being within a folder mentioned in the module.yaml.
1 change: 1 addition & 0 deletions _demo/modules/example/copy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# dummy file - should be copied over
1 change: 1 addition & 0 deletions _demo/modules/example/link-folder/fileinfolder.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This file should be untouched, the folder it is in will simply be symlinked.
1 change: 1 addition & 0 deletions _demo/modules/example/link.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# dummy file - should be symlinked
26 changes: 26 additions & 0 deletions _demo/modules/example/module.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
skip: false
target: ./target/example
files:
- name: copy.yml
strategy: copy
- name: link.yml
strategy: link
- name: render.yml
strategy: render
- name: copy-folder # copy an entire folder
strategy: copy
- name: link-folder # link an entire folder
strategy: link
- name: render-folder # render an entire folder
strategy: render
deps:
- name: curl
packages:
pacman: curl
apt: curl
brew: curl
- name: wget
packages:
pacman: wget
apt: wget
brew: wget
2 changes: 2 additions & 0 deletions _demo/modules/example/render-folder/fileinfolder.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This file will be rendered, because the folder it is in is rendered.
Here is a rendered output: {{ .value }}
1 change: 1 addition & 0 deletions _demo/modules/example/render.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
renderedValue: {{ .value }}
3 changes: 3 additions & 0 deletions _demo/target/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# target

This is where the output of doth goes. Run `./doth.sh deploy -c config.yaml` in the \_demo directory to see what it does.
13 changes: 11 additions & 2 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ If a module has dependencies, they will be installed first using the appropriate

_ = len(configs)

planDeploy(configs).Run(
planDeploy(configs, configPaths).Run(
dry, verbose, model.CreatePipelineConfig(
true, autoconfirm,
),
Expand Down Expand Up @@ -106,7 +106,16 @@ func readModules() []model.Module {
return modules
}

func planDeploy(configs model.ConfigMap) *model.Pipeline {
func planDeploy(configs model.ConfigMap, configPaths []string) *model.Pipeline {
doth, err := model.LoadDothFileFromCwd()
if err != nil {
panic("failed to load doth file: " + err.Error())
}

if (len(configPaths) == 0) && doth.RequireConfig {
panic("no config provided, but required to deploy this doth project")
}

pipeline := model.NewPipeline()
modules := readModules()

Expand Down
23 changes: 13 additions & 10 deletions cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,55 +63,55 @@ var installCmd = &cobra.Command{
if cmd.Flags().Changed("apt") {
sources = append(sources, model.PackageSource{
Name: "apt",
Command: "sudo apt install --yes {package}",
Command: "sudo apt install --yes '{package}'",
})
}
if cmd.Flags().Changed("apt-get") {
sources = append(sources, model.PackageSource{
Name: "apt-get",
Command: "sudo apt-get install --yes {package}",
Command: "sudo apt-get install --yes '{package}'",
})
}
if cmd.Flags().Changed("dnf") {
sources = append(sources, model.PackageSource{
Name: "dnf",
Command: "sudo dnf install --assumeyes {package}",
Command: "sudo dnf install --assumeyes '{package}'",
})
}
if cmd.Flags().Changed("pacman") {
sources = append(sources, model.PackageSource{
Name: "pacman",
Command: "sudo pacman -S --noconfirm {package}",
Command: "sudo pacman -S --noconfirm '{package}'",
})
}
if cmd.Flags().Changed("yay") {
sources = append(sources, model.PackageSource{
Name: "yay",
Command: "yay -S --noconfirm {package}",
Command: "yay -S --noconfirm '{package}'",
})
}
if cmd.Flags().Changed("paru") {
sources = append(sources, model.PackageSource{
Name: "paru",
Command: "paru -S --noconfirm {package}",
Command: "paru -S --noconfirm '{package}'",
})
}
if cmd.Flags().Changed("go") {
sources = append(sources, model.PackageSource{
Name: "go",
Command: "go install {package}",
Command: "go install '{package}'",
})
}
if cmd.Flags().Changed("npm") {
sources = append(sources, model.PackageSource{
Name: "npm",
Command: "npm install -g {package}",
Command: "npm install -g '{package}'",
})
}
if cmd.Flags().Changed("brew") {
sources = append(sources, model.PackageSource{
Name: "brew",
Command: "yes | brew install {package}",
Command: "yes | brew install '{package}'",
})
}

Expand Down Expand Up @@ -189,7 +189,7 @@ func planInstall(sources []model.PackageSource, silent bool) *model.Pipeline {
}

pipeline := model.NewPipeline().
AddModule(model.NewConfirmStep("doth install gives this doth project shell execution rights. Use -d to preview the commands that would run.\nProceed?"))
AddModule(model.NewConfirmStep("\"doth install\" could be unsafe to run in some projects. Use -d to preview the commands that would run.\nProceed?"))

deps := collectDependencies()

Expand All @@ -209,6 +209,9 @@ func planInstall(sources []model.PackageSource, silent bool) *model.Pipeline {
pipeline.AddModule(model.NewLogStep(fmt.Sprintf("\nInstall dependency %s using source %s ", dep.Name, source.Name), false))
}

packageName = strings.TrimSpace(packageName)
packageName = strings.Trim(packageName, "\"'") // remove any surrounding quotes

command := strings.ReplaceAll(source.Command, "{package}", packageName)
pipeline.AddModule(model.NewExecuteShellCommandStep(command, silent))
break
Expand Down