diff --git a/README.md b/README.md index abb6df4..e238700 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,9 @@ Custom features for dev containers used in development of Omoxyz software projec ## Features - **Lefthook** ([lefthook](./src/lefthook/README.md)) – fast polyglot Git hooks manager to automate code checks, formatting, and tests before commits and pushes. -- **Air** ([go-air](./src/go-air/README.md)) - live reloader for Go apps -- **Protoc** ([protoc](./src/protoc/README.md)) +- **Air** ([go-air](./src/go-air/README.md)) - live reloader for Go apps. +- **Protoc** ([protoc](./src/protoc/README.md)) - protocol buffer compiler. +- **Protolint** ([protolint](./src/protolint/README.md)) - linter and fixer to enforce Protocol Buffer style and conventions. ## Usage diff --git a/src/protolint/NOTES.md b/src/protolint/NOTES.md new file mode 100644 index 0000000..e69de29 diff --git a/src/protolint/devcontainer-feature.json b/src/protolint/devcontainer-feature.json new file mode 100644 index 0000000..fd79066 --- /dev/null +++ b/src/protolint/devcontainer-feature.json @@ -0,0 +1,20 @@ +{ + "name": "Protolint", + "id": "protolint", + "version": "1.0.0", + "documentationURL": "http://github.com/omoxyz/devcontainer-features/tree/main/src/protolint", + "description": "Install Protolint linter and fixer to enforce Protocol Buffer style and conventions.", + "options": { + "version": { + "default": "latest", + "description": "Select the version to install.", + "proposals": [ + "latest" + ], + "type": "string" + } + }, + "installsAfter": [ + "ghcr.io/devcontainers/features/git" + ] +} \ No newline at end of file diff --git a/src/protolint/install.sh b/src/protolint/install.sh new file mode 100644 index 0000000..ea4ad29 --- /dev/null +++ b/src/protolint/install.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Scripts must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +source ./utils.sh + +PROTOLINT_VERSION=${VERSION:-"latest"} +GITHUB_REPO=https://github.com/yoheimuta/protolint + +# Exit immediately if a command exits with a non-zero status. +set -e + +apt_get_update + +# Clean up +rm -rf /var/lib/apt/lists/* + +export DEBIAN_FRONTEND=noninteractive + +get_github_filename() { + local version=$1 + local arch=$2 + echo "protolint_${version}_linux_${arch}.tar.gz" +} + +install_from_github() { + local version_list=$(git ls-remote --tags ${GITHUB_REPO}) + + # Get 2 latest appropriate versions + versions=($(find_latest_versions $PROTOLINT_VERSION version_list "tags/v")) + if [ $? -eq 1 ]; then + echo "Can't find appropriate version" + exit 1 + fi + + latest_version=${versions[0]} + prev_version=${versions[1]} + + echo "Downloading protolint v${latest_version}...." + + check_packages wget + + # Get architecture + local arch=$(dpkg --print-architecture) + + local filename=$(get_github_filename $latest_version $arch) + + set +e + + # Create temporary directory + mkdir -p /tmp/protolint + pushd /tmp/protolint + + # Download zip file + wget ${GITHUB_REPO}/releases/download/v${latest_version}/${filename} + local exit_code=$? + + set -e + + if [ "$exit_code" != "0" ]; then + # Handle situation where git tags are ahead of what was is available to actually download + echo "(!) protolint version ${latest_version} failed to download. Attempting to fall back to ${prev_version} to retry..." + filename=$(get_github_filename $prev_version $arch) + wget ${GITHUB_REPO}/releases/download/v${prev_version}/${filename} + fi + + tar -xvzf /tmp/protolint/${filename} -C /tmp/protolint + + # Install binaries + cp -r /tmp/protolint/protolint /usr/local/bin/protolint + cp -r /tmp/protolint/protoc-gen-protolint /usr/local/bin/protoc-gen-protolint + + # Remove temporary directory + popd + rm -rf /tmp/protolint +} + +# Install curl, unzip if missing +check_packages curl ca-certificates unzip git + +install_from_github + +# Clean up +rm -rf /var/lib/apt/lists/* \ No newline at end of file diff --git a/src/protolint/utils.sh b/src/protolint/utils.sh new file mode 100644 index 0000000..82a1243 --- /dev/null +++ b/src/protolint/utils.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash + +# Refresh the local package index if no package list entries are stored on the system. +apt_get_update() { + if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update -y + fi +} + +# Checks if packages are installed and installs them if not +check_packages() { + for pkg in "$@"; do + # Check if it's a command in PATH + if command -v "$pkg" &> /dev/null; then + echo "[OK] $pkg found in PATH" + continue + fi + + # Check if it's a Debian/Ubuntu package installed + if dpkg -s "$pkg" &> /dev/null; then + echo "[OK] $pkg package installed" + continue + fi + + # If not found, install package + echo "$pkg not found. Installing..." + apt_get_update + apt-get install -y --no-install-recommends $pkg + + done +} + +# Find 2 latest versions that appropriate to requested version +find_latest_versions() { + local requested_version=$1 + local version_list=${!2} + + # Version prefix such as "tags/v" + local prefix_regex=${3:-''} + + # Version number part separator such as "." in "1.0.0" + local separator=${4:-"."} + local escaped_separator=${separator//./\\.} + + local suffix_regex=${5:-''} + + # Format and sort version list + local version_regex="${prefix_regex}\\K[0-9]+(${escaped_separator}[0-9]+){0,2}${suffix_regex}$" + version_list="$(printf "%s\n" "${version_list[@]}" | grep -oP $version_regex| tr -d ' ' | tr $separator '.' | sort -rV)" + + if [ "${requested_version}" = "latest" ]; then + echo "$(echo "${version_list}" | head -n 2)" + else + # Try to get latest matching version + + set +e + local regex="^" + + # Get major version or exit + local major="$(echo "${requested_version}" | grep -oE '^[0-9]+')" + if [ $major != '' ]; then + regex="${regex}${major}" + else + echo "Invalid version \"${requested_version}\". Use \"latest\" or MAJOR[.MINOR][.PATCH]" + return 1 + fi + + # Get minor number or accept any + local minor="$(echo "${requested_version}" | grep -oP '^[0-9]+\.\K[0-9]+')" + regex="${regex}$([ "$minor" != '' ] && echo "${escaped_separator}${minor}" || echo "(${escaped_separator}[0-9]+)?")" + + + # Get patch number or accept any + local patch="$(echo "${requested_version}" | grep -oP '^[0-9]+\.[0-9]+\.\K[0-9]+')" + regex="${regex}$([ "$patch" != '' ] && echo "${escaped_separator}${patch}" || echo "(${escaped_separator}[0-9]+)?")" + set -e + + echo "$(echo "${version_list}" | grep -E -m 2 "^${regex}$")" + fi +} \ No newline at end of file diff --git a/test/protolint/scenarios.json b/test/protolint/scenarios.json new file mode 100644 index 0000000..eb2c92f --- /dev/null +++ b/test/protolint/scenarios.json @@ -0,0 +1,8 @@ +{ + "test": { + "image": "mcr.microsoft.com/devcontainers/base:debian", + "features": { + "protoc": {} + } + } +} \ No newline at end of file diff --git a/test/protolint/test.sh b/test/protolint/test.sh new file mode 100644 index 0000000..4c08663 --- /dev/null +++ b/test/protolint/test.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +source dev-container-features-test-lib + +check "protolint version" protolint version +check "protoc-gen-protolint " protoc-gen-protolint version + +reportResults \ No newline at end of file