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
30 changes: 30 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ jobs:

- name: Build the CLI
run: npm run build

- name: Store version before release
id: version-before
run: echo "VERSION=$(node -p 'require("./package.json").version')" >> "$GITHUB_OUTPUT"

- name: Run npm release
run: npm run release
env:
Expand All @@ -45,3 +50,28 @@ jobs:
GIT_AUTHOR_EMAIL: [email protected]
GIT_COMMITTER_NAME: Tolgee Machine
GIT_COMMITTER_EMAIL: [email protected]

- name: Extract version after release
id: version-after
run: echo "VERSION=$(node -p 'require("./package.json").version')" >> "$GITHUB_OUTPUT"

- name: Check if version was released
id: version-check
run: |
if [ "${{ steps.version-before.outputs.VERSION }}" != "${{ steps.version-after.outputs.VERSION }}" ]; then
echo "RELEASED=true" >> "$GITHUB_OUTPUT"
echo "New version released: ${{ steps.version-before.outputs.VERSION }} -> ${{ steps.version-after.outputs.VERSION }}"
else
echo "RELEASED=false" >> "$GITHUB_OUTPUT"
echo "No new version released (version remains ${{ steps.version-before.outputs.VERSION }})"
fi

- name: Build and push Docker image
if: steps.version-check.outputs.RELEASED == 'true'
run: ./scripts/build-docker.sh latest linux/amd64,linux/arm64 push
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_PASSWORD }}
VERSION: ${{ steps.version-after.outputs.VERSION }}
GITHUB_SHA: ${{ github.event.workflow_run.head_sha || github.sha }}
BUILD_DATE: ${{ github.event.workflow_run.run_started_at || github.event.workflow_run.head_commit.timestamp }}
63 changes: 63 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Multi-stage Docker build for Tolgee CLI
# Stage 1: Build stage with dev dependencies
FROM node:24-alpine AS builder

# Set working directory
WORKDIR /app

# Copy package files first for better Docker layer caching
COPY package*.json ./

# Install ALL dependencies (including dev dependencies needed for building)
RUN npm ci && npm cache clean --force

# Copy source files
COPY src/ ./src/
COPY scripts/ ./scripts/
COPY tsconfig*.json ./

# Copy additional files needed for build
COPY textmate/ ./textmate/
COPY extractor.d.ts ./
COPY schema.json ./

# Build the CLI
RUN npm run build

# Stage 2: Production stage with only runtime dependencies
FROM node:24-alpine AS production

# Set working directory
WORKDIR /app

# Copy package files first for better Docker layer caching
COPY package*.json ./

# Install ONLY production dependencies
RUN npm ci --only=production && npm cache clean --force

# Copy built application files from builder stage
COPY --from=builder /app/dist/ ./dist/
COPY --from=builder /app/textmate/ ./textmate/
COPY --from=builder /app/extractor.d.ts ./
COPY --from=builder /app/schema.json ./

# Copy documentation files
COPY README.md ./
COPY LICENSE ./

# Make the CLI binary executable
RUN chmod +x ./dist/cli.js

# Create a non-root user for security
RUN addgroup -g 1001 -S tolgee && \
adduser -S tolgee -u 1001

# Switch to non-root user
USER tolgee

# Set the entrypoint to the CLI binary
ENTRYPOINT ["node", "./dist/cli.js"]

# Default command shows help
CMD ["--help"]
27 changes: 26 additions & 1 deletion HACKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ to get some work done.

## Toolchain

To work on this project, you will just need Node 16+ (and Docker to run tests). We use `npm` to manage dependencies,
To work on this project, you will just need Node 22+ (and Docker to run tests). We use `npm` to manage dependencies,
and [prettier](https://github.com/prettier/prettier) to lint our code.

## Scripts
Expand Down Expand Up @@ -47,6 +47,31 @@ TOLGEE_TEST_BACKEND_URL=http://localhost:8080 npm run test:e2e

When this environment variable is set, the Docker backend will not be started, and tests will use the specified URL instead.

## Building Docker Images

The project includes Docker support for containerized deployment. The Docker setup consists of:

- `Dockerfile`: Multi-stage build configuration using Node.js 22 Alpine
- `scripts/build-docker.sh`: Comprehensive build script with multi-platform support

### Prerequisites

- Docker installed and running
- For multi-platform builds: Docker Buildx
- For pushing images: Docker Hub credentials

### Building Docker Images

The `scripts/build-docker.sh` script provides several build options:

**Basic build (current platform only):**

```bash
./scripts/build-docker.sh
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the command be added to scripts in package.json? That way people would find it IMO easier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thanks for the feedback.

I was thinking about writing it in JS so it's multiplatform. But I am not sure about running bash scripts via npm run... AFAIK, it can break on Windows...

I don't think it's something devs would use often and if they search for it it's in the docs... 🤷‍♂️

```

More information about possible build options is documented in the `build-docker.sh`.

## Code & internals overview

### Command parsing
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ pnpm add --global @tolgee/cli

See our [documentation](https://tolgee.io/tolgee-cli/installation) for more information.

### Docker Installation
Alternatively, you can use the Docker image:

```sh
# Pull the latest image
docker pull tolgee/cli:latest

# Run directly
docker run --rm tolgee/cli:latest --help

# Create an alias for easier usage
alias tolgee="docker run --rm -v \$(pwd):/workspace -w /workspace tolgee/cli:latest"
```

The Docker images are available on [Docker Hub](https://hub.docker.com/r/tolgee/cli) and support multiple platforms (linux/amd64, linux/arm64).

## Usage
Once installed, you'll have access to the `tolgee` command. Run `tolgee help` to see all the supported commands, their
options and arguments.
Expand Down
189 changes: 189 additions & 0 deletions scripts/build-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#!/bin/bash

# Build Docker image for Tolgee CLI
# Usage:
# ./scripts/build-docker.sh [tag] [platform] [push]
#
# Examples:
# ./scripts/build-docker.sh # Build for current platform with default tag
# ./scripts/build-docker.sh latest # Build for current platform with specific tag
# ./scripts/build-docker.sh latest linux/arm64 # Build for specific single platform (available locally)
# ./scripts/build-docker.sh latest linux/amd64,linux/arm64 # Multi-platform build (NOT available locally)
# ./scripts/build-docker.sh latest linux/amd64,linux/arm64 push # Multi-platform build and push
#
# Environment variables for push:
# DOCKERHUB_USERNAME - Docker Hub username
# DOCKERHUB_TOKEN - Docker Hub token/password
# VERSION - Version for additional tagging (optional)
#
# Note: Multi-platform builds are stored in buildx cache and not available locally.
# To run the image locally after a multi-platform build, either:
# 1. Build for your current platform only (e.g., linux/arm64 on Apple Silicon)
# 2. Push the multi-platform image to a registry and pull it

set -euo pipefail

# Default values
TAG=${1:-"dev"}
PLATFORM=${2:-""}
PUSH=${3:-""}
IMAGE_NAME="tolgee/cli"

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

echo -e "${GREEN}Building Tolgee CLI Docker image...${NC}"
echo "Tag: ${TAG}"
echo "Image: ${IMAGE_NAME}:${TAG}"

# Check if Docker is running
if ! docker info > /dev/null 2>&1; then
echo -e "${RED}Error: Docker is not running. Please start Docker and try again.${NC}"
exit 1
fi

# Docker login if push is requested
if [ "$PUSH" = "push" ]; then
if [ -z "${DOCKERHUB_USERNAME:-}" ] || [ -z "${DOCKERHUB_TOKEN:-}" ]; then
echo -e "${RED}Error: DOCKERHUB_USERNAME and DOCKERHUB_TOKEN environment variables are required for push.${NC}"
exit 1
fi
echo -e "${GREEN}Logging in to Docker Hub...${NC}"
echo "$DOCKERHUB_TOKEN" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
fi

# Ensure we have the built files
if [ ! -d "dist" ]; then
echo -e "${YELLOW}Warning: dist directory not found. Building the CLI first...${NC}"
npm run build
fi

# Prepare tags
TAGS=( -t "${IMAGE_NAME}:${TAG}" )
if [ -n "${VERSION:-}" ] && [ "$TAG" = "latest" ]; then
TAGS+=( -t "${IMAGE_NAME}:${VERSION}" )
fi

# Prepare labels
LABELS=()
if [ "$PUSH" = "push" ]; then
LABELS+=( --label "org.opencontainers.image.title=Tolgee CLI" )
LABELS+=( --label "org.opencontainers.image.description=A tool to interact with the Tolgee Platform through CLI" )
LABELS+=( --label "org.opencontainers.image.url=https://github.com/tolgee/tolgee-cli" )
LABELS+=( --label "org.opencontainers.image.source=https://github.com/tolgee/tolgee-cli" )
LABELS+=( --label "org.opencontainers.image.licenses=MIT" )
if [ -n "${VERSION:-}" ]; then
LABELS+=( --label "org.opencontainers.image.version=${VERSION}" )
fi
if [ -n "${GITHUB_SHA:-}" ]; then
LABELS+=( --label "org.opencontainers.image.revision=${GITHUB_SHA}" )
fi
if [ -n "${BUILD_DATE:-}" ]; then
LABELS+=( --label "org.opencontainers.image.created=${BUILD_DATE}" )
fi
fi

# Build command
BUILD_CMD=( docker build )
BUILD_CMD+=( "${TAGS[@]}" )
if [ ${#LABELS[@]} -gt 0 ]; then
BUILD_CMD+=( "${LABELS[@]}" )
fi

if [ -n "$PLATFORM" ]; then
echo "Platform(s): ${PLATFORM}"
# For multi-platform builds, we need buildx
BUILD_CMD=( docker buildx build --platform "${PLATFORM}" )
BUILD_CMD+=( "${TAGS[@]}" )
if [ ${#LABELS[@]} -gt 0 ]; then
BUILD_CMD+=( "${LABELS[@]}" )
fi

# Check if buildx is available
if ! docker buildx version > /dev/null 2>&1; then
echo -e "${RED}Error: Docker buildx is required for multi-platform builds.${NC}"
echo "Please install Docker buildx or build for single platform."
exit 1
fi

# Create builder if it doesn't exist
if ! docker buildx inspect tolgee-builder > /dev/null 2>&1; then
echo -e "${YELLOW}Creating multi-platform builder...${NC}"
docker buildx create --name tolgee-builder --use
else
docker buildx use tolgee-builder
fi

# Check if this is a single platform build that can be loaded locally
PLATFORM_COUNT=$(echo "$PLATFORM" | tr ',' '\n' | wc -l)
if [ "$PLATFORM_COUNT" -eq 1 ] && [ "$PUSH" != "push" ]; then
# Single platform - we can load it to local Docker daemon
BUILD_CMD+=( --load )
echo -e "${GREEN}Single platform build - image will be available locally after build.${NC}"
elif [ "$PUSH" = "push" ]; then
# Push mode - add push flag
BUILD_CMD+=( --push )
echo -e "${GREEN}Build and push mode enabled.${NC}"
else
# Multi-platform build - cannot load to local Docker daemon
echo -e "${YELLOW}Multi-platform build - image will NOT be available locally.${NC}"
echo -e "${YELLOW}To run locally, build for your current platform only or push to a registry.${NC}"
fi
elif [ "$PUSH" = "push" ]; then
# Regular build with push - we need to build and then push
echo -e "${GREEN}Build and push mode enabled for single architecture.${NC}"
fi

BUILD_CMD+=( . )

echo -e "${GREEN}Running:${NC} ${BUILD_CMD[*]}"
"${BUILD_CMD[@]}"

if [ "$PUSH" = "push" ] && [ -z "$PLATFORM" ]; then
# For regular builds, we need to push separately
echo -e "${GREEN}Pushing images to registry...${NC}"
docker push ${IMAGE_NAME}:${TAG}
if [ -n "${VERSION:-}" ] && [ "$TAG" = "latest" ]; then
docker push ${IMAGE_NAME}:${VERSION}
fi
fi

if [ "$PUSH" = "push" ]; then
echo -e "${GREEN}✓ Docker image built and pushed successfully!${NC}"
else
echo -e "${GREEN}✓ Docker image built successfully!${NC}"
fi

# Show appropriate run instruction based on build type
if [ -n "$PLATFORM" ]; then
PLATFORM_COUNT=$(echo "$PLATFORM" | tr ',' '\n' | wc -l)
if [ "$PLATFORM_COUNT" -eq 1 ]; then
# Single platform - image is available locally
echo -e "${GREEN}Run with: docker run --rm ${IMAGE_NAME}:${TAG}${NC}"
else
# Multi-platform - image is NOT available locally
echo -e "${YELLOW}Multi-platform build completed. Image is not available locally.${NC}"
echo -e "${YELLOW}To run locally: build for your platform only or pull from registry after push.${NC}"
fi
else
# Default build for current platform - always available locally
echo -e "${GREEN}Run with: docker run --rm ${IMAGE_NAME}:${TAG}${NC}"
fi

# Show image info
echo -e "\n${YELLOW}Image information:${NC}"
if [ -n "$PLATFORM" ]; then
PLATFORM_COUNT=$(echo "$PLATFORM" | tr ',' '\n' | wc -l)
if [ "$PLATFORM_COUNT" -eq 1 ]; then
echo "Single-platform image built and loaded locally."
docker images ${IMAGE_NAME}:${TAG}
else
echo "Multi-platform image built. Use 'docker buildx imagetools inspect ${IMAGE_NAME}:${TAG}' to see details."
echo "Note: Multi-platform images are not available locally. Build for your current platform to run locally."
fi
else
docker images ${IMAGE_NAME}:${TAG}
fi
Loading