Automation Tutorial

Learn how to build powerful automation workflows that leverage Gemini CLI's AI capabilities.

One of the most transformative ways to use Gemini CLI is to move beyond interactive sessions and embed AI reasoning directly into your development workflows. By piping files through Gemini CLI in shell scripts, scheduling those scripts with cron, and wiring them into CI/CD pipelines like GitHub Actions, you can add a layer of AI-powered analysis, review, and generation to every commit, pull request, or nightly build — without any manual steps. This tutorial walks through real, production-ready script patterns for each of those scenarios, explains how batch mode works, and covers the pitfalls that catch most people when they first try to automate Gemini CLI.

Running Gemini CLI Non-Interactively

Before building complex scripts, understand the two input patterns Gemini CLI supports in non-interactive mode. Both suppress the interactive prompt and return output directly to stdout, making them composable with standard Unix tools.

Pattern 1 — Piping a prompt

# Prompt comes from stdin via echo

echo "Summarize the key points of this topic" | gemini

Use this pattern when you want to generate text from a static prompt without reading a file.

Pattern 2 — File input redirection

# File contents are sent as context along with the prompt

gemini "Review this code for security issues" < src/auth.js

gemini "Generate JSDoc comments" < utils.js > utils-documented.js

Use output redirection (>) to save the result to a file instead of printing to the terminal.

Shell Script Examples

These scripts are production-ready starting points. Adapt the prompts and file patterns to your project's conventions.

Code Review Automation

This script compares the last commit to HEAD and asks Gemini CLI to review each changed file. It skips binary files and saves each review to a reviews/directory, preserving the original filename for easy reference.

#!/bin/bash

set -euo pipefail

# auto-review.sh — AI code review for recent changes

REVIEW_DIR="reviews/$(date +%Y%m%d)"

mkdir -p "$REVIEW_DIR"

git diff --name-only HEAD~1 HEAD | while read -r file; do

# Skip binary and non-existent files

[[ -f "$file" ]] || continue

file "$file" | grep -q text || continue

echo "Reviewing: $file"

OUTFILE="$REVIEW_DIR/$(basename "$file").review"

gemini "Review this code for correctness, security issues, and style violations." < "$file" > "$OUTFILE"

sleep 1 # Respect rate limits

done

echo "Reviews saved to $REVIEW_DIR"

Make executable with chmod +x auto-review.sh then run with ./auto-review.sh.

Documentation Generation

This script scans your src/ directory for JavaScript files and generates Markdown documentation for each one. It mirrors the source tree structure under docs/ so documentation is easy to navigate.

#!/bin/bash

set -euo pipefail

# generate-docs.sh — AI-powered documentation generation

SRC_DIR="${1:-src}"

DOCS_DIR="docs"

find "$SRC_DIR" -name "*.js" -o -name "*.ts" | while read -r file; do

# Mirror source tree in docs/

rel_path="${file#$SRC_DIR/}"

doc_file="$DOCS_DIR/${rel_path%.*}.md"

mkdir -p "$(dirname "$doc_file")"

echo "Documenting: $file -> $doc_file"

gemini "Generate comprehensive Markdown documentation including: purpose, parameters, return values, and usage examples." < "$file" > "$doc_file"

sleep 1

done

Keep documentation up-to-date automatically by wiring this script to a pre-commit hook or a nightly CI job.

Test Generation

Bootstrapping a test suite for legacy code is one of the most time-consuming tasks in software development. This script automates the first pass by generating unit test files for each source file.

#!/bin/bash

set -euo pipefail

# generate-tests.sh — generate unit test scaffolding

TEST_DIR="tests"

mkdir -p "$TEST_DIR"

for file in src/*.js; do

base="$(basename "$file" .js)"

out="$TEST_DIR/${base}.test.js"

if [[ -f "$out" ]]; then

echo "Skipping $file — test file already exists"

continue

fi

echo "Generating tests for: $file"

gemini "Write Jest unit tests for every exported function. Include happy-path, edge-case, and error-path tests." < "$file" > "$out"

sleep 1

done

Always review generated tests before committing. Gemini CLI generates solid scaffolding, but tests benefit from a human sanity check before being merged.

Scheduling with Cron

Cron is the standard Unix job scheduler. It lets you run any script on a fixed schedule — perfect for nightly code health reports, weekly changelog summaries, or daily documentation rebuilds.

Cron syntax quick reference

# ┌───────────── minute (0-59)

# │ ┌───────────── hour (0-23)

# │ │ ┌───────────── day of month (1-31)

# │ │ │ ┌───────────── month (1-12)

# │ │ │ │ ┌───────────── day of week (0=Sun)

# │ │ │ │ │

* * * * * command

Adding a daily documentation job

Open your crontab with crontab -e and add the following lines. Use absolute paths — cron runs with a minimal environment and does not inherit your shell's PATH or exported variables.

# Run doc generation every day at 9 AM

0 9 * * * GOOGLE_AI_API_KEY=AIzaXXX /home/user/scripts/generate-docs.sh >> /home/user/logs/docs.log 2>&1

# Run code review every weekday at 6 PM

0 18 * * 1-5 GOOGLE_AI_API_KEY=AIzaXXX /home/user/scripts/auto-review.sh >> /home/user/logs/review.log 2>&1

Store the API key in a restricted file (chmod 600 ~/.env.gemini) and source it in your script rather than embedding it directly in the crontab, which is world-readable by default on some systems.

GitHub Actions Integration

GitHub Actions workflows give you full control over when and how Gemini CLI runs in your repository's CI pipeline. Store your API key as a GitHub secret and reference it as an environment variable inside the workflow.

Step 1 — Add the API key secret

Go to your repository on GitHub, navigate to Settings > Secrets and variables > Actions, and click New repository secret. Name it GOOGLE_AI_API_KEY and paste your key as the value.

Step 2 — Create the workflow file

Save this file to .github/workflows/ai-review.yml in your repository.

name: AI Code Analysis

on:

pull_request:

branches: [main, develop]

push:

branches: [main]

jobs:

ai_review:

runs-on: ubuntu-latest

env:

GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }}

steps:

- uses: actions/checkout@v4

with:

fetch-depth: 2 # needed for git diff

- uses: actions/setup-node@v4

with:

node-version: '20'

- name: Install Gemini CLI

run: npm install -g @google/gemini-cli

- name: AI Code Review

run: |

git diff --name-only HEAD~1 HEAD -- '*.js' '*.ts' | while read f; do

[ -f "$f" ] && gemini "Review for bugs and security issues" < "$f" >> review.md

sleep 1

done

- name: Upload Review Artifact

uses: actions/upload-artifact@v4

with:

name: ai-review

path: review.md

Batch changelog generation on release

name: Generate Changelog

on:

release:

types: [created]

jobs:

changelog:

runs-on: ubuntu-latest

env:

GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }}

steps:

- uses: actions/checkout@v4

with: {fetch-depth: 0}

- run: npm install -g @google/gemini-cli

- name: Draft changelog

run: |

git log --oneline $(git describe --tags --abbrev=0 HEAD~1)..HEAD > commits.txt

gemini "Generate a user-friendly CHANGELOG entry from these commits" < commits.txt > CHANGELOG_DRAFT.md

Batch Processing Mode

When processing large numbers of files, a few patterns help you stay within rate limits and keep scripts maintainable and debuggable.

Rate-limit-aware batch loop

#!/bin/bash

# Process files in batches of 5 with a pause between batches

count=0

for file in src/**/*.js; do

gemini "Add JSDoc to all functions" < "$file" > "docs/$(basename "$file").md"

count=$((count + 1))

if (( count % 5 == 0 )); then

echo "Processed $count files, pausing 10s..."

sleep 10

fi

done

Resume-safe processing with a processed list

For long-running batch jobs, track which files have already been processed so the script can be interrupted and restarted without re-processing completed files.

PROCESSED=".processed_files"

touch "$PROCESSED"

for file in src/*.js; do

grep -qxF "$file" "$PROCESSED" && continue # skip if already done

gemini "Summarize this module" < "$file" > "summaries/$(basename "$file").txt"

echo "$file" >> "$PROCESSED"

sleep 1

done

Frequently Asked Questions

How do I handle errors in automation scripts gracefully?

Add set -euo pipefail at the top of your bash scripts so any command failure immediately stops the script with a clear exit code. Wrap individual gemini calls in if-blocks or use the || operator to log failures without aborting the whole batch: gemini "..." < "$file" > out.md || echo "Failed on $file" >> errors.log.

How do I run Gemini CLI in a Docker container for CI?

Use the official Node.js Docker image as your base. Add RUN npm install -g @google/gemini-cli to your Dockerfile. Pass the API key at runtime via an environment variable: docker run -e GOOGLE_AI_API_KEY=AIzaXXX my-image ./review.sh. Never bake the API key into the Docker image.

Can I use Gemini CLI with GitLab CI or Azure DevOps?

Yes. The patterns are identical — install Gemini CLI with npm, expose the API key as a CI/CD secret variable, and run your shell scripts as pipeline steps. In GitLab CI, use the variables block with GOOGLE_AI_API_KEY: $GOOGLE_AI_API_KEY. In Azure DevOps, use a pipeline variable group and reference it with $(GOOGLE_AI_API_KEY).