Skip to content

Commit

Permalink
feat: Add generate command to generate install script
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Jul 26, 2022
1 parent 150144e commit c0e2f53
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 35 deletions.
13 changes: 13 additions & 0 deletions assets/chezmoi.io/docs/reference/commands/generate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# `generate` *file*

Generates *file* for use with chezmoi. The currently supported *file*s are:

| File | Description |
| ------------ | ---------------------------------------------------------- |
| `install.sh` | An install script, suitable for use with Github Codespaces |

!!! example

```console
$ chezmoi generate install.sh > install.sh
```
47 changes: 12 additions & 35 deletions assets/chezmoi.io/docs/user-guide/machines/containers-and-vms.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,47 +44,24 @@ This sets the `codespaces` template variable, so you don't have to repeat `(env
"CODESPACES")` in your templates. It also sets the `sourceDir` configuration to
the `--source` argument passed in `chezmoi init`.

Second, create an `install.sh` script that installs chezmoi and your dotfiles:

```sh
#!/bin/sh

set -e # -e: exit on error

if [ ! "$(command -v chezmoi)" ]; then
bin_dir="$HOME/.local/bin"
chezmoi="$bin_dir/chezmoi"
if [ "$(command -v curl)" ]; then
sh -c "$(curl -fsLS https://chezmoi.io/get)" -- -b "$bin_dir"
elif [ "$(command -v wget)" ]; then
sh -c "$(wget -qO- https://chezmoi.io/get)" -- -b "$bin_dir"
else
echo "To install chezmoi, you must have curl or wget installed." >&2
exit 1
fi
else
chezmoi=chezmoi
fi

# POSIX way to get script's dir: https://stackoverflow.com/a/29834779/12156188
script_dir="$(cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P)"
# exec: replace current process with chezmoi init
exec "$chezmoi" init --apply "--source=$script_dir"
Second, create an `install.sh` script that installs chezmoi and your dotfiles
and add it to `.chezmoiignore` and your dotfiles repo:

```console
$ chezmoi generate install.sh > install.sh
$ chmod a+x install.sh
$ echo install.sh >> .chezmoiignore
$ git add install.sh .chezmoiignore
$ git commit -m "Add install.sh"
```

Ensure that this file is executable (`chmod a+x install.sh`), and add
`install.sh` to your `.chezmoiignore` file.

It installs the latest version of chezmoi in `~/.local/bin` if needed, and then
`chezmoi init ...` invokes chezmoi to create its configuration file and
initialize your dotfiles. `--apply` tells chezmoi to apply the changes
The generated script installs the latest version of chezmoi in `~/.local/bin` if
needed, and then `chezmoi init ...` invokes chezmoi to create its configuration
file and initialize your dotfiles. `--apply` tells chezmoi to apply the changes
immediately, and `--source=...` tells chezmoi where to find the cloned
`dotfiles` repo, which in this case is the same folder in which the script is
running from.

If you do not use a chezmoi configuration file template you can use `chezmoi
apply --source=$HOME/dotfiles` instead of `chezmoi init ...` in `install.sh`.

Finally, modify any of your templates to use the `codespaces` variable if
needed. For example, to install `vim-gtk` on Linux but not in Codespaces, your
`run_once_install-packages.sh.tmpl` might contain:
Expand Down
1 change: 1 addition & 0 deletions assets/chezmoi.io/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ nav:
- encrypt: reference/commands/encrypt.md
- execute-template: reference/commands/execute-template.md
- forget: reference/commands/forget.md
- generate: reference/commands/generate.md
- git: reference/commands/git.md
- help: reference/commands/help.md
- init: reference/commands/init.md
Expand Down
30 changes: 30 additions & 0 deletions assets/templates/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/sh

# -e: exit on error
# -u: exit on unset variables
set -eu

if ! chezmoi="$(command -v chezmoi)"; then
bin_dir="${HOME}/.local/bin"
chezmoi="${bin_dir}/chezmoi"
echo "Installing chezmoi to '${chezmoi}'" >&2
if command -v curl >/dev/null; then
chezmoi_install_script="$(curl -fsSL https://chezmoi.io/get)"
elif command -v wget >/dev/null; then
chezmoi_install_script="$(wget -qO- https://chezmoi.io/get)"
else
echo "To install chezmoi, you must have curl or wget installed." >&2
exit 1
fi
sh -c "${chezmoi_install_script}" -- -b "${bin_dir}"
unset chezmoi_install_script bin_dir
fi

# POSIX way to get script's dir: https://stackoverflow.com/a/29834779/12156188
script_dir="$(cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P)"

set -- init --apply --source="${script_dir}"

echo "Running 'chezmoi $*'" >&2
# exec: replace current process with chezmoi
exec "$chezmoi" "$@"
1 change: 1 addition & 0 deletions assets/templates/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ import "embed"

// FS contains all templates.
//
//go:embed *.sh
//go:embed *.tmpl
var FS embed.FS
1 change: 1 addition & 0 deletions pkg/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,7 @@ func (c *Config) newRootCmd() (*cobra.Command, error) {
c.newEncryptCommand(),
c.newExecuteTemplateCmd(),
c.newForgetCmd(),
c.newGenerateCmd(),
c.newGitCmd(),
c.newIgnoredCmd(),
c.newImportCmd(),
Expand Down
45 changes: 45 additions & 0 deletions pkg/cmd/generatecmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package cmd

import (
"fmt"
"strings"

"github.com/spf13/cobra"

"github.com/twpayne/chezmoi/v2/assets/templates"
)

func (c *Config) newGenerateCmd() *cobra.Command {
generateCmd := &cobra.Command{
Use: "generate file",
Short: "Generate a file for use with chezmoi",
Long: mustLongHelp("generate"),
Example: example("generate"),
Args: cobra.ExactArgs(1),
ValidArgs: []string{"install.sh"},
RunE: c.runGenerateCmd,
Annotations: map[string]string{
doesNotRequireValidConfig: "true",
},
}

return generateCmd
}

func (c *Config) runGenerateCmd(cmd *cobra.Command, args []string) error {
builder := strings.Builder{}
builder.Grow(16384)
switch args[0] {
case "install.sh":
data, err := templates.FS.ReadFile("install.sh")
if err != nil {
return err
}
if _, err := builder.Write(data); err != nil {
return err
}
default:
return fmt.Errorf("%s: unsupported file", args[0])
}
return c.writeOutputString(builder.String())
}
3 changes: 3 additions & 0 deletions pkg/cmd/testdata/scripts/generate.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# test that chezmoi generate install.sh generates a shell script
chezmoi generate install.sh
stdout '#!/bin/sh'

0 comments on commit c0e2f53

Please sign in to comment.