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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Generated from private documentation source. Do not edit directly. Source SHA256: ec38d7acca3059eb364acc3b51a941e4df868b2f56436478d173874ad74cd4f9 -->
<!-- Generated from private documentation source. Do not edit directly. Source SHA256: ba46d6ed5eb52d766e45733ec7480d618099cf0af61a3bb59904a5c4473386f8 -->

# spritesmith

Expand All @@ -8,10 +8,10 @@ The client requires an API service and an account token. It does not contain a l

## Install

With Go 1.27.2 or later:
With Go 1.27.2 or later, install to `~/.local/bin` (add it to your PATH if necessary):

```sh
go install github.com/aviorstudio/spritesmith/cmd/spritesmith@latest
curl -fsSL https://spritesmith.ai/install | sh
```

## Configure
Expand Down
68 changes: 68 additions & 0 deletions cmd/spritesmith/installer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//go:build !windows

package main

import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
)

func TestCurlInstaller(t *testing.T) {
_, file, _, _ := runtime.Caller(0)
script := filepath.Join(filepath.Dir(file), "../../install.sh")
for _, fail := range []bool{false, true} {
name := "success"
if fail {
name = "failed build preserves existing executable"
}
t.Run(name, func(t *testing.T) {
root := t.TempDir()
bin, fake := filepath.Join(root, "bin"), filepath.Join(root, "commands")
for _, dir := range []string{bin, fake} {
if err := os.Mkdir(dir, 0700); err != nil {
t.Fatal(err)
}
}
if err := os.WriteFile(filepath.Join(bin, "spritesmith"), []byte("previous executable\n"), 0700); err != nil {
t.Fatal(err)
}
goCommand := `#!/bin/sh
set -eu
[ "$1" = install ]
[ "$2" = 'github.com/aviorstudio/spritesmith/cmd/spritesmith@latest' ]
printf 'new executable\n' > "$GOBIN/spritesmith"
if [ "${INSTALL_BUILD_FAIL:-0}" = 1 ]; then exit 42; fi
`
if err := os.WriteFile(filepath.Join(fake, "go"), []byte(goCommand), 0700); err != nil {
t.Fatal(err)
}
cmd := exec.Command("sh", script, "--install-dir", bin)
cmd.Env = append(os.Environ(), "PATH="+fake+string(os.PathListSeparator)+os.Getenv("PATH"))
if fail {
cmd.Env = append(cmd.Env, "INSTALL_BUILD_FAIL=1")
}
output, err := cmd.CombinedOutput()
if fail == (err == nil) {
t.Fatalf("unexpected result: %v\n%s", err, output)
}
body, err := os.ReadFile(filepath.Join(bin, "spritesmith"))
if err != nil {
t.Fatal(err)
}
want := "new executable\n"
if fail {
want = "previous executable\n"
}
if string(body) != want {
t.Fatalf("unexpected executable: %q", body)
}
if !fail && !strings.Contains(string(output), "Installed spritesmith") {
t.Fatalf("missing install receipt: %s", output)
}
})
}
}
58 changes: 58 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/sh
# Install spritesmith using the Go module's normal verification and build process.
set -eu

tool=spritesmith
module=github.com/aviorstudio/spritesmith/cmd/spritesmith
release=latest
install_dir=${INSTALL_DIR:-"$HOME/.local/bin"}
work_dir=
stage_dir=
fail() { printf '%s: %s\n' "$tool" "$*" >&2; exit 1; }
cleanup() {
[ -z "$work_dir" ] || rm -rf "$work_dir"
[ -z "$stage_dir" ] || rm -rf "$stage_dir"
}
trap cleanup 0
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
while [ "$#" -gt 0 ]; do
case "$1" in
--version|-v|--install-dir)
[ "$#" -ge 2 ] || fail "$1 needs a value."
case "$1" in
--version|-v) release=$2 ;;
--install-dir) install_dir=$2 ;;
esac
shift 2 ;;
--help|-h)
printf 'Install %s with Go: [--version VERSION|latest] [--install-dir /absolute/path]\nDefaults: latest Go module, ~/.local/bin. Requires Go.\n' "$tool"
exit 0 ;;
*) fail "Unknown option: $1" ;;
esac
done
case "$install_dir" in /*) ;; *) fail 'The install directory must be an absolute path.' ;; esac
command -v go >/dev/null 2>&1 || fail 'Install Go first, then run this command again.'
case "$release" in
latest) ;;
v[0-9]*) ;;
[0-9]*) release=v$release ;;
*) fail 'Use latest or a Go module version such as v1.2.3.' ;;
esac
work_dir=$(mktemp -d "${TMPDIR:-/tmp}/$tool-install.XXXXXXXX") || fail 'Could not create a temporary directory.'
mkdir "$work_dir/bin"
printf 'Building %s from %s@%s...\n' "$tool" "$module" "$release"
GOBIN="$work_dir/bin" go install "$module@$release" || fail 'Go installation failed; the installed binary was not changed.'
[ -f "$work_dir/bin/$tool" ] && [ ! -L "$work_dir/bin/$tool" ] || fail 'Go did not produce a regular executable.'
mkdir -p "$install_dir" || fail 'Choose a writable install directory.'
[ ! -d "$install_dir/$tool" ] || fail 'The install destination is a directory.'
stage_dir=$(mktemp -d "$install_dir/.$tool-install.XXXXXXXX") || fail 'Could not stage the executable.'
cp "$work_dir/bin/$tool" "$stage_dir/$tool"
chmod 755 "$stage_dir/$tool"
mv -f "$stage_dir/$tool" "$install_dir/$tool"
printf 'Installed %s at %s/%s\n' "$tool" "$install_dir" "$tool"
case ":$PATH:" in
*":$install_dir:"*) ;;
*) printf 'Add %s to your PATH to run %s by name.\n' "$install_dir" "$tool" ;;
esac