Skip to content

Commit 2a47dc7

Browse files
committed
feat(docker): add Docker setup for GitHub Actions runner
- Introduces a setup script and YAML configuration to automate the installation of essential tools for the GitHub Actions runner. - Implements scripts for installing Python, PowerShell, Azure CLI, AzCopy, and Ansible, ensuring all installations include verification steps. - Establishes a multi-architecture Docker image with proper error handling and logging during the setup process. Provides users a streamlined experience for setting up the development environment.
1 parent 521cbd4 commit 2a47dc7

File tree

9 files changed

+352
-8
lines changed

9 files changed

+352
-8
lines changed

CLAUDE.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Repository Overview
6+
7+
This repository contains Docker images for GitHub Actions self-hosted runners with additional tools pre-installed. The main image is based on the official GitHub Actions runner image and includes Python, PowerShell, Azure CLI, AzCopy, and Ansible.
8+
9+
## Architecture
10+
11+
- **Base Image**: Built on `ghcr.io/actions/actions-runner:2.327.1`
12+
- **Additional Tools**: Multiple tools installed via a YAML-driven setup system
13+
- **Setup System**: Uses `setup.yaml` to define installation steps with `setup.sh` orchestrator
14+
- **Multi-Architecture Support**: Supports both AMD64 and ARM64 architectures
15+
- **CI/CD**: Uses GitHub Actions for automated building and releasing
16+
17+
## Key Commands
18+
19+
### Building the Docker Image
20+
21+
```bash
22+
# Build locally
23+
docker build -t github-actions-runner -f src/Dockerfile src/
24+
25+
# Build with specific platform
26+
docker buildx build --platform linux/amd64 -t github-actions-runner -f src/Dockerfile src/
27+
```
28+
29+
### Adding New Tools
30+
31+
To add a new tool to the image:
32+
1. Create an installation script in `src/scripts/` (e.g., `install-newtool.sh`)
33+
2. Add a step to `src/setup.yaml`:
34+
```yaml
35+
- name: "Install New Tool"
36+
script: "scripts/install-newtool.sh"
37+
description: "Description of what this installs"
38+
```
39+
OR use inline commands:
40+
```yaml
41+
- name: "Install New Tool"
42+
command: "apt-get install -y newtool"
43+
description: "Description of what this installs"
44+
```
45+
46+
### GitHub Actions Workflow
47+
48+
The repository uses GitVersion for semantic versioning. The pipeline is triggered on:
49+
- Push to any branch
50+
- Pull requests
51+
- Manual workflow dispatch
52+
53+
## Project Structure
54+
55+
- `src/Dockerfile`: Main Dockerfile that builds the runner image
56+
- `src/setup.yaml`: YAML configuration defining all installation steps
57+
- `src/setup.sh`: Main orchestrator that reads setup.yaml and executes steps
58+
- `src/scripts/`: Installation scripts for various tools
59+
- `install-python.sh`: Installs Python3, pip, pip3, and pipx
60+
- `install-powershell.sh`: Installs PowerShell Core
61+
- `install-azure-cli.sh`: Installs Azure CLI
62+
- `install-azcopy.sh`: Installs AzCopy with architecture detection
63+
- `.github/workflows/pipeline.yaml`: Main CI/CD pipeline
64+
- `GitVersion.yaml`: Semantic versioning configuration
65+
66+
## Versioning Strategy
67+
68+
The project uses GitVersion with:
69+
- Main branch: Continuous deployment mode
70+
- Develop branch: Minor version increments
71+
- Release branches: RC pre-releases
72+
- Feature branches: Named pre-releases
73+
- Commit messages control version bumps (+semver:major/minor/patch)
74+
75+
## Container Registries
76+
77+
Images are published to:
78+
- Docker Hub: `emberstack/github-actions-runner`
79+
- GitHub Container Registry: `ghcr.io/emberstack/github-actions-runner`
80+
81+
Both registries receive multi-architecture manifests supporting AMD64 and ARM64.

src/Dockerfile

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,26 @@ FROM ghcr.io/actions/actions-runner:2.327.1
22

33
USER root
44

5-
# Install required dependencies
5+
# Install required dependencies including yq and jq
66
RUN apt-get update && \
77
apt-get install -y --no-install-recommends \
88
wget \
99
curl \
1010
ca-certificates \
11+
jq \
1112
&& rm -rf /var/lib/apt/lists/*
1213

14+
# Install yq
15+
RUN wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 && \
16+
chmod +x /usr/local/bin/yq
17+
1318
WORKDIR /install
1419

1520
COPY /scripts /scripts
21+
COPY setup.yaml /
22+
COPY setup.sh /
1623

17-
RUN for script in /scripts/install*.sh; do \
18-
echo "Running $script"; \
19-
bash -e $script || { echo "Script $script failed" >&2; exit 1; }; \
20-
done && \
21-
rm -rf /scripts
24+
# Run setup script with proper error handling
25+
RUN bash -e /setup.sh
2226

2327
USER runner

src/scripts/install-ansible.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/bash
2+
3+
# Install Ansible using pip
4+
5+
# Install ansible globally using pip
6+
pip install ansible
7+
8+
# Verify installation
9+
echo "Checking installed ansible executables:"
10+
ls -la /usr/local/bin/ansible* 2>/dev/null || echo "Ansible binaries not in /usr/local/bin"
11+
12+
# Verify installation
13+
echo "Verifying Ansible installation..."
14+
if ! command -v ansible &> /dev/null; then
15+
echo "ERROR: ansible command not found after installation"
16+
echo "PATH: $PATH"
17+
# Try to find where pip installed it
18+
echo "Searching for ansible executable..."
19+
find /usr -name "ansible" -type f 2>/dev/null | head -5
20+
exit 1
21+
fi
22+
23+
# Check for ansible-playbook which is commonly used
24+
if ! command -v ansible-playbook &> /dev/null; then
25+
echo "ERROR: ansible-playbook command not found after installation"
26+
exit 1
27+
fi
28+
29+
echo "Ansible version:"
30+
ansible --version
31+
32+
echo "Ansible installation completed successfully!"

src/scripts/install-azcopy.sh

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,20 @@ ln -sf /usr/local/bin/azcopy /usr/local/bin/azcopy10
3232
# Cleanup
3333
rm -f azcopy.tar.gz
3434

35+
# Verify installation
36+
echo "Verifying AzCopy installation..."
37+
if ! command -v azcopy &> /dev/null; then
38+
echo "ERROR: azcopy command not found after installation"
39+
exit 1
40+
fi
41+
42+
if ! command -v azcopy10 &> /dev/null; then
43+
echo "ERROR: azcopy10 symlink not created properly"
44+
exit 1
45+
fi
46+
47+
echo "AzCopy version:"
48+
azcopy --version
3549
azcopy10 --version
36-
azcopy --version
50+
51+
echo "AzCopy installation completed successfully!"

src/scripts/install-azure-cli.sh

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,13 @@ apt-get update
3232
apt-get install -y azure-cli
3333

3434
# Verify installation
35-
az --version
35+
echo "Verifying Azure CLI installation..."
36+
if ! command -v az &> /dev/null; then
37+
echo "ERROR: az command not found after installation"
38+
exit 1
39+
fi
40+
41+
echo "Azure CLI version:"
42+
az --version
43+
44+
echo "Azure CLI installation completed successfully!"

src/scripts/install-powershell.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/bin/bash
2+
3+
# Install PowerShell following official documentation
4+
# https://learn.microsoft.com/en-us/powershell/scripting/install/install-ubuntu
5+
6+
# Update packages and install required dependencies
7+
apt-get update
8+
apt-get install -y --no-install-recommends \
9+
wget \
10+
apt-transport-https \
11+
software-properties-common
12+
13+
# Detect architecture and Ubuntu version
14+
ARCH=$(uname -m)
15+
UBUNTU_VERSION=$(lsb_release -rs)
16+
17+
# Download the Microsoft repository GPG keys
18+
wget -q "https://packages.microsoft.com/config/ubuntu/${UBUNTU_VERSION}/packages-microsoft-prod.deb"
19+
20+
# Register the Microsoft repository GPG keys
21+
dpkg -i packages-microsoft-prod.deb
22+
23+
# Delete the Microsoft repository GPG keys file
24+
rm packages-microsoft-prod.deb
25+
26+
# Update the list of packages after we added packages.microsoft.com
27+
apt-get update
28+
29+
# Install PowerShell
30+
apt-get install -y powershell
31+
32+
# Create a symbolic link for 'powershell' command
33+
ln -sf /usr/bin/pwsh /usr/bin/powershell
34+
35+
# Verify installation
36+
echo "Verifying PowerShell installation..."
37+
if ! command -v pwsh &> /dev/null; then
38+
echo "ERROR: pwsh command not found after installation"
39+
exit 1
40+
fi
41+
42+
if ! command -v powershell &> /dev/null; then
43+
echo "ERROR: powershell symlink not created properly"
44+
exit 1
45+
fi
46+
47+
echo "PowerShell version:"
48+
pwsh --version
49+
50+
echo "PowerShell installation completed successfully!"

src/scripts/install-python.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/bin/bash
2+
3+
# Install Python and pip
4+
5+
# Update packages and install Python3 and pip
6+
apt-get update
7+
apt-get install -y --no-install-recommends \
8+
python3 \
9+
python3-pip \
10+
python3-venv \
11+
python3-dev
12+
13+
# Create symbolic links for pip (if not already present)
14+
if [ ! -f /usr/bin/pip ]; then
15+
ln -s /usr/bin/pip3 /usr/bin/pip
16+
fi
17+
18+
# Upgrade pip to latest version
19+
python3 -m pip install --upgrade pip
20+
21+
# Verify installations
22+
echo "Verifying Python installation..."
23+
if ! command -v python3 &> /dev/null; then
24+
echo "ERROR: python3 command not found after installation"
25+
exit 1
26+
fi
27+
echo "Python version:"
28+
python3 --version
29+
30+
echo "Verifying pip installation..."
31+
if ! command -v pip &> /dev/null; then
32+
echo "ERROR: pip command not found after installation"
33+
exit 1
34+
fi
35+
echo "pip version:"
36+
pip --version
37+
38+
echo "Verifying pip3 installation..."
39+
if ! command -v pip3 &> /dev/null; then
40+
echo "ERROR: pip3 command not found after installation"
41+
exit 1
42+
fi
43+
echo "pip3 version:"
44+
pip3 --version
45+
46+
echo "Python and pip installation completed successfully!"

src/setup.sh

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/bin/bash
2+
set -e # Exit on error
3+
set -o pipefail # Exit on pipe failure
4+
5+
# Colors for output
6+
RED='\033[0;31m'
7+
GREEN='\033[0;32m'
8+
BLUE='\033[0;34m'
9+
NC='\033[0m' # No Color
10+
11+
# Function to log with timestamp
12+
log() {
13+
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
14+
}
15+
16+
# Function to log errors
17+
error() {
18+
echo -e "${RED}[ERROR]${NC} $1" >&2
19+
}
20+
21+
# Function to log success
22+
success() {
23+
echo -e "${GREEN}[SUCCESS]${NC} $1"
24+
}
25+
26+
# Change to root directory where setup.yaml is located
27+
cd /
28+
29+
# Check if setup.yaml exists
30+
if [ ! -f "setup.yaml" ]; then
31+
error "setup.yaml not found in root directory"
32+
exit 1
33+
fi
34+
35+
log "Starting setup process..."
36+
37+
# Parse and execute each step
38+
# Get the number of steps
39+
step_count=$(yq eval '.setup.steps | length' setup.yaml)
40+
41+
# Iterate through each step by index
42+
for ((i=0; i<$step_count; i++)); do
43+
# Extract step properties
44+
name=$(yq eval ".setup.steps[$i].name" setup.yaml)
45+
script=$(yq eval ".setup.steps[$i].script" setup.yaml)
46+
command=$(yq eval ".setup.steps[$i].command" setup.yaml)
47+
description=$(yq eval ".setup.steps[$i].description" setup.yaml)
48+
49+
log "Starting: $name"
50+
if [ "$description" != "null" ]; then
51+
log "Description: $description"
52+
fi
53+
54+
# Execute based on precedence (script > command)
55+
if [ "$script" != "null" ]; then
56+
log "Executing script: $script"
57+
if bash -e "$script"; then
58+
success "Completed: $name"
59+
else
60+
error "Failed: $name (script: $script)"
61+
exit 1
62+
fi
63+
elif [ "$command" != "null" ]; then
64+
log "Executing command..."
65+
if bash -e -c "$command"; then
66+
success "Completed: $name"
67+
else
68+
error "Failed: $name"
69+
exit 1
70+
fi
71+
else
72+
error "Step '$name' has no script or command defined"
73+
exit 1
74+
fi
75+
76+
echo "" # Empty line between steps
77+
done
78+
79+
success "All setup steps completed successfully!"

0 commit comments

Comments
 (0)