Creating An Install Script For Marmite A Step-by-Step Guide

by Sebastian Müller 60 views

Hey guys! Today, we're diving into creating an install script for Marmite, making it super easy for everyone to get started. This is going to be a game-changer for new users, allowing them to install Marmite with just a single command. Let's break down the process step by step.

Understanding the Goal

The main goal here is to create a script that automatically detects the user's operating system and architecture, fetches the correct Marmite binary from the latest release, and installs it in the appropriate directory. This script will be served at https://marmite.blog/install.sh, making it accessible to anyone.

Why an Install Script?

Having an install script simplifies the installation process significantly. Instead of manually downloading and configuring Marmite, users can simply run a single command. This reduces friction and makes it more likely that people will try out Marmite. Plus, it's just cool!

Step-by-Step Implementation

Let’s walk through the steps required to create this awesome install script.

1. Script Location and Setup

First things first, we need to set up the script within our project. We'll start by adding an install.sh script to the example/scripts/ directory. This keeps our project organized and ensures the script is version-controlled.

mkdir -p example/scripts
touch example/scripts/install.sh
chmod +x example/scripts/install.sh

Next, we need to tell Marmite to serve this script. We'll add a file mapping in .github/marmite.yaml that points scripts/install.sh to install.sh. This ensures that the script is accessible via the https://marmite.blog/install.sh URL.

file_mappings:
  - source: scripts/install.sh
    destination: install.sh

2. Gathering System Information

The install script needs to know the user's operating system (Linux, Windows, macOS) and architecture (x86_64, aarch64, etc.) to download the correct binary. We can use standard shell commands to gather this information.

#!/bin/bash

os=$(uname -s)
arch=$(uname -m)

echo "Detected OS: $os"
echo "Detected Architecture: $arch"

This snippet uses uname -s to get the operating system and uname -m to get the architecture. It's super important to handle different cases and variations in these outputs to ensure the script works reliably across various systems.

3. Fetching the Latest Release Information

To download the correct Marmite binary, the install script needs to fetch the latest release information from the GitHub API. We can use curl to make a request to https://api.github.com/repos/rochacbruno/marmite/releases/latest.

release_info=$(curl -sS https://api.github.com/repos/rochacbruno/marmite/releases/latest)

echo "Fetched release info: $release_info"

This command fetches the JSON response containing the release information. We'll need to parse this JSON to extract the asset URLs.

4. Parsing JSON and Finding the Correct Artifact

Parsing JSON in shell scripts can be a bit tricky, but tools like jq make it much easier. We'll use jq to extract the assets array from the JSON response and then iterate through them to find a matching artifact based on the OS and architecture.

if ! command -v jq &> /dev/null;
then
  echo "jq is required to parse JSON. Please install it."
  exit 1
fi

assets=$(echo "$release_info" | jq -r '.assets')

for asset in $(echo "$assets" | jq -r '.[] | @base64'); do
  asset_decoded=$(echo "$asset" | base64 --decode)
  name=$(echo "$asset_decoded" | jq -r '.name')
  browser_download_url=$(echo "$asset_decoded" | jq -r '.browser_download_url')

  if [[ "$name" == *"$os"* && "$name" == *"$arch"* ]]; then
    echo "Found matching artifact: $name"
    download_url=$browser_download_url
    break
  fi
done

if [ -z "$download_url" ]; then
  echo "No matching artifact found for OS: $os and Architecture: $arch"
  exit 1
fi

echo "Download URL: $download_url"

This snippet first checks if jq is installed and exits if it's not. Then, it iterates through the assets, decodes the base64 encoded JSON, and checks if the asset name matches the detected OS and architecture. If a match is found, it stores the download URL and breaks the loop.

5. Downloading the Artifact

With the download URL in hand, we can use curl to download the artifact. We'll also use wget as a fallback in case curl is not available.

download_file() {
  url=$1
  output_file=$2
  if command -v curl &> /dev/null; then
    curl -sSLo "$output_file" "$url"
  elif command -v wget &> /dev/null; then
    wget -q -O "$output_file" "$url"
  else
    echo "curl or wget is required to download the artifact. Please install one of them."
    exit 1
  fi
}

filename=$(basename "$download_url")
download_file "$download_url" "$filename"

echo "Downloaded: $filename"

This function download_file downloads the artifact using curl or wget, handling cases where neither is available. It’s crucial to provide clear error messages to the user if dependencies are missing.

6. Unpacking the Binary

Once the artifact is downloaded, we need to unpack it. The script needs to handle different archive types (tar.gz, zip, etc.).

unpack_archive() {
  archive=$1
  if [[ "$archive" == *.tar.gz ]]; then
    tar -xzf "$archive"
  elif [[ "$archive" == *.zip ]]; then
    unzip "$archive"
  else
    echo "Unsupported archive type: $archive"
    exit 1
  fi
}

unpack_archive "$filename"

echo "Unpacked: $filename"

This function unpack_archive checks the file extension and uses the appropriate command to unpack the archive. It’s essential to support all archive types that Marmite releases might use.

7. Installing the Binary

After unpacking, the script needs to move the marmite binary to a suitable location. We'll try to install it in the user's default binary directory (~/.local/bin) or any other discoverable directory. If that fails, we'll write the file to the current running folder.

install_binary() {
  binary_name=marmite
  if [ -d "$HOME/.local/bin" ]; then
    install_dir="$HOME/.local/bin"
  elif command -v brew &> /dev/null && [ -d "$(brew --prefix)/bin" ]; then
    install_dir="$(brew --prefix)/bin"
  else
    install_dir="."
  fi

  cp "$binary_name" "$install_dir"
  if [ "$install_dir" != "." ]; then
    echo "Installed marmite to $install_dir"
  else
    echo "Failed to install to standard locations, installed to current directory."
  fi

  if [ "$install_dir" != "." ]; then
    chmod +x "$install_dir/$binary_name"
  else
    chmod +x "./$binary_name"
  fi
}

install_binary

This function install_binary first determines the installation directory, then copies the binary, and sets execute permissions. It's important to inform the user if the installation fails to a standard location.

8. Warning the User About PATH

Finally, the script should warn the user about ensuring the installation location is in their PATH environment variable.

echo "Please ensure $install_dir is in your PATH."

This is crucial because the user needs to be able to run marmite from any terminal location.

Complete Install Script

Here's the complete install.sh script:

#!/bin/bash

os=$(uname -s)
arch=$(uname -m)

echo "Detected OS: $os"
echo "Detected Architecture: $arch"

if ! command -v jq &> /dev/null;
then
  echo "jq is required to parse JSON. Please install it."
  exit 1
fi

release_info=$(curl -sS https://api.github.com/repos/rochacbruno/marmite/releases/latest)

assets=$(echo "$release_info" | jq -r '.assets')

for asset in $(echo "$assets" | jq -r '.[] | @base64'); do
  asset_decoded=$(echo "$asset" | base64 --decode)
  name=$(echo "$asset_decoded" | jq -r '.name')
  browser_download_url=$(echo "$asset_decoded" | jq -r '.browser_download_url')

  if [[ "$name" == *"$os"* && "$name" == *"$arch"* ]]; then
    echo "Found matching artifact: $name"
    download_url=$browser_download_url
    break
  fi
done

if [ -z "$download_url" ]; then
  echo "No matching artifact found for OS: $os and Architecture: $arch"
  exit 1
fi

echo "Download URL: $download_url"

download_file() {
  url=$1
  output_file=$2
  if command -v curl &> /dev/null; then
    curl -sSLo "$output_file" "$url"
  elif command -v wget &> /dev/null; then
    wget -q -O "$output_file" "$url"
  else
    echo "curl or wget is required to download the artifact. Please install one of them."
    exit 1
  fi
}

filename=$(basename "$download_url")
download_file "$download_url" "$filename"

echo "Downloaded: $filename"

unpack_archive() {
  archive=$1
  if [[ "$archive" == *.tar.gz ]]; then
    tar -xzf "$archive"
  elif [[ "$archive" == *.zip ]]; then
    unzip "$archive"
  else
    echo "Unsupported archive type: $archive"
    exit 1
  fi
}

unpack_archive "$filename"

echo "Unpacked: $filename"

install_binary() {
  binary_name=marmite
  if [ -d "$HOME/.local/bin" ]; then
    install_dir="$HOME/.local/bin"
  elif command -v brew &> /dev/null && [ -d "$(brew --prefix)/bin" ]; then
    install_dir="$(brew --prefix)/bin"
  else
    install_dir="."
  fi

  cp "$binary_name" "$install_dir"
  if [ "$install_dir" != "." ]; then
    echo "Installed marmite to $install_dir"
  else
    echo "Failed to install to standard locations, installed to current directory."
  fi

  if [ "$install_dir" != "." ]; then
    chmod +x "$install_dir/$binary_name"
  else
    chmod +x "./$binary_name"
  fi
}

install_binary

echo "Please ensure $install_dir is in your PATH."

9. Testing the Script

Before deploying, it's crucial to test the script thoroughly on different platforms and architectures. You can use virtual machines or cloud instances to simulate various environments.

10. Adding Command-Line Options

To make the install script more flexible, we can add command-line options. For example, users might want to specify a custom installation directory. We can use getopts to parse command-line arguments.

while getopts d: option
do
  case "$option" in
    d)
      custom_bin_dir="$OPTARG"
      ;;
    \?)
      echo "Usage: $0 [-d install_directory]"
      exit 1
      ;;
  esac
done

if [ -n "$custom_bin_dir" ]; then
  install_dir="$custom_bin_dir"
fi

echo "Installing to: $install_dir"

This snippet allows users to specify an installation directory using the -d option. It’s super useful for advanced users who have specific directory preferences.

Usage Instructions

Users can now install Marmite using the following command:

curl -sS https://marmite.blog/install.sh | sh

Or, to specify a custom installation directory:

curl -sS https://marmite.blog/install.sh | sh -s -- --bin-dir /custom/bin/folder

Follow-Up Tasks

To ensure the new installation method is well-documented and promoted, we need to complete a few follow-up tasks.

1. Update Installation Documentation

Create a new Markdown file in the example/content/ directory (e.g., DATE-installation.md) explaining all existing installation methods, including the new script. Clear documentation is key to user adoption.

2. Copy Hero File

Copy the hero file from example/content/_hero.md to .github/_hero.md to customize the landing page.

cp example/content/_hero.md .github/_hero.md

3. Edit Hero File

Edit .github/_hero.md to add a quick start section that includes the new installation command. This makes it easy for new users to get started.

## Quick Start

### Install
`curl -sS https://marmite.blog/install.sh | sh` or [Click Here for more install options](link-to-installation-docs)
### Create a blog

marmite myblog --init-site
--name Mysite
--tagline "My Articles and Notes"
--colorscheme nord
--toc true
--enable-search true

### Write a post

marmite myblog --new "My First Blog Post" -t "new,post"

### Serve the blog

marmite myblog --serve --watch

4. Update GitHub Actions Workflow

Edit .github/main.yml to copy the hero file to the Marmite site during the preparation step. This ensures the landing page is updated with the new installation instructions.

- name: Prepare Site
  run: |
    mkdir -p marmitesite/content
    cp example/content/* marmitesite/content/
    cp .github/_hero.md marmitesite/content/_hero.md

Conclusion

Creating an install script for Marmite is a significant step towards improving the user experience. By automating the installation process, we make it easier for new users to get started and explore the tool. Remember, thorough testing and clear documentation are crucial for success. So, let's get this script out there and make Marmite even more accessible!

By following these steps, we've created a robust and user-friendly installation script for Marmite. This not only simplifies the installation process but also makes Marmite more accessible to a wider audience. Keep up the great work, guys!