diff --git a/src/basecamp-fizzy-cli/NOTES.md b/src/basecamp-fizzy-cli/NOTES.md new file mode 100644 index 0000000..40a86ea --- /dev/null +++ b/src/basecamp-fizzy-cli/NOTES.md @@ -0,0 +1,17 @@ +# basecamp-fizzy-cli + +## Project + +- [fizzy-cli](https://github.com/basecamp/fizzy-cli) + +## Description + +`fizzy` is a command-line interface for [Fizzy](https://fizzy.do), a project management tool by Basecamp. Manage boards, cards, comments, and more from your terminal or through AI agents. It works standalone or with any AI agent (Claude, Codex, Copilot, Gemini) and returns structured JSON output with breadcrumbs for easy navigation. + +## Installation Method + +Downloaded as a pre-compiled binary from the [GitHub releases page](https://github.com/basecamp/fizzy-cli/releases) and placed in `/usr/local/bin`. + +## Other Notes + +_No additional notes._ diff --git a/src/basecamp-fizzy-cli/devcontainer-feature.json b/src/basecamp-fizzy-cli/devcontainer-feature.json new file mode 100644 index 0000000..77d83f2 --- /dev/null +++ b/src/basecamp-fizzy-cli/devcontainer-feature.json @@ -0,0 +1,17 @@ +{ + "name": "basecamp-fizzy-cli", + "id": "basecamp-fizzy-cli", + "version": "1.0.0", + "description": "Install \"fizzy\" CLI binary", + "documentationURL": "https://github.com/devcontainer-community/devcontainer-features/tree/main/src/basecamp-fizzy-cli", + "options": { + "version": { + "type": "string", + "default": "latest", + "proposals": [ + "latest" + ], + "description": "Version of \"fizzy\" to install." + } + } +} diff --git a/src/basecamp-fizzy-cli/install.sh b/src/basecamp-fizzy-cli/install.sh new file mode 100755 index 0000000..d37d1ce --- /dev/null +++ b/src/basecamp-fizzy-cli/install.sh @@ -0,0 +1,139 @@ +#!/bin/bash +set -o errexit +set -o pipefail +set -o noclobber +set -o nounset +set -o allexport +readonly githubRepository='basecamp/fizzy-cli' +readonly binaryName='fizzy' +readonly versionArgument='--version' +readonly binaryTargetFolder='/usr/local/bin' +readonly name="${githubRepository##*/}" +apt_get_update() { + if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update -y + fi +} +apt_get_checkinstall() { + if ! dpkg -s "$@" >/dev/null 2>&1; then + apt_get_update + DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends --no-install-suggests --option 'Debug::pkgProblemResolver=true' --option 'Debug::pkgAcquire::Worker=1' "$@" + fi +} +apt_get_cleanup() { + apt-get clean + rm -rf /var/lib/apt/lists/* +} +check_curl_envsubst_installed() { + declare -a requiredAptPackagesMissing=() + if ! [ -r '/etc/ssl/certs/ca-certificates.crt' ]; then + requiredAptPackagesMissing+=('ca-certificates') + fi + if ! command -v curl >/dev/null 2>&1; then + requiredAptPackagesMissing+=('curl') + fi + if ! command -v envsubst >/dev/null 2>&1; then + requiredAptPackagesMissing+=('gettext-base') + fi + declare -i requiredAptPackagesMissingCount=${#requiredAptPackagesMissing[@]} + if [ $requiredAptPackagesMissingCount -gt 0 ]; then + apt_get_update + apt_get_checkinstall "${requiredAptPackagesMissing[@]}" + apt_get_cleanup + fi +} +curl_check_url() { + local url=$1 + local status_code + status_code=$(curl -s -o /dev/null -w '%{http_code}' "$url") + if [ "$status_code" -ne 200 ] && [ "$status_code" -ne 302 ]; then + echo "Failed to download '$url'. Status code: $status_code." + return 1 + fi +} +curl_download_file() { + local url=$1 + local target=$2 + curl \ + --silent \ + --location \ + --output "$target" \ + --connect-timeout 5 \ + "$url" +} +debian_get_arch() { + echo "$(dpkg --print-architecture)" +} +debian_get_target_arch() { + case $(debian_get_arch) in + amd64) echo 'amd64' ;; + arm64) echo 'arm64' ;; + *) echo 'unknown' ;; + esac +} +echo_banner() { + local text="$1" + echo -e "\e[1m\e[97m\e[41m$text\e[0m" +} +github_list_releases() { + if [ -z "$1" ]; then + echo "Usage: list_github_releases " + return 1 + fi + local repo="$1" + local url="https://api.github.com/repos/$repo/releases" + curl -s "$url" | grep -Po '"tag_name": "\K.*?(?=")' | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | sed 's/^v//' +} +github_get_latest_release() { + if [ -z "$1" ]; then + echo "Usage: get_latest_github_release " + return 1 + fi + github_list_releases "$1" | head -n 1 +} +github_get_tag_for_version() { + if [ -z "$1" ] || [ -z "$2" ]; then + echo "Usage: github_get_tag_for_version " + return 1 + fi + local repo="$1" + local _version="$2" + local url="https://api.github.com/repos/$repo/releases" + local escaped_version + escaped_version="$(printf '%s' "$_version" | sed 's/\./\\./g')" + curl -s "$url" | grep -Po '"tag_name": "\K.*?(?=")' | grep -E "^v?${escaped_version}$" | head -n 1 +} +utils_check_version() { + local version=$1 + if ! [[ "${version:-}" =~ ^(latest|[0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + printf >&2 '=== [ERROR] Option "version" (value: "%s") is not "latest" or valid semantic version format "X.Y.Z" !\n' \ + "$version" + exit 1 + fi +} +install() { + utils_check_version "$VERSION" + check_curl_envsubst_installed + readonly architecture="$(debian_get_target_arch)" + readonly binaryTargetPath="${binaryTargetFolder}/${binaryName}" + if [ "$VERSION" == 'latest' ] || [ -z "$VERSION" ]; then + VERSION=$(github_get_latest_release "$githubRepository") + fi + readonly version="${VERSION:?}" + readonly releaseTag="$(github_get_tag_for_version "$githubRepository" "$version")" + if [ -z "$releaseTag" ]; then + printf >&2 '=== [ERROR] Could not find release tag for version "%s" in "%s"!\n' "$version" "$githubRepository" + exit 1 + fi + readonly downloadUrlTemplate='https://github.com/${githubRepository}/releases/download/${releaseTag}/fizzy-linux-${architecture}' + readonly downloadUrl="$(echo -n "$downloadUrlTemplate" | envsubst)" + curl_check_url "$downloadUrl" + curl_download_file "$downloadUrl" "$binaryTargetPath" + chmod 755 "$binaryTargetPath" + apt_get_cleanup +} +echo_banner "devcontainer.community" +echo "Installing $name..." +install "$@" +echo "(*) Done!" diff --git a/test/basecamp-fizzy-cli/test.sh b/test/basecamp-fizzy-cli/test.sh new file mode 100755 index 0000000..05aa318 --- /dev/null +++ b/test/basecamp-fizzy-cli/test.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +# Optional: Import test library bundled with the devcontainer CLI +# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib +# Provides the 'check' and 'reportResults' commands. +source dev-container-features-test-lib + +# Feature-specific tests +# The 'check' command comes from the dev-container-features-test-lib. Syntax is... +# check