Iโll show you how to set up continuous integration (CI) for Alpine Linux packages! CI automatically tests and builds your packages whenever you make changes. Itโs like having a robot assistant that checks your work and packages everything for you!
๐ค What is Continuous Integration?
Continuous Integration automatically builds and tests your code every time you push changes. For Alpine packages, this means automatically creating APK files, running tests, and even publishing to repositories. No more manual building - just push your code and let CI do the work!
Why use CI for packages?
- Catch errors early
- Consistent builds
- Automated testing
- Easy collaboration
- Professional workflow
๐ฏ What You Need
Before starting, youโll need:
- Alpine Linux installed
- Git repository (GitHub/GitLab)
- Package source code
- Basic Git knowledge
- About 30 minutes
๐ Step 1: Prepare Your Package
First, set up your package structure:
# Create package directory
mkdir -p ~/my-package
cd ~/my-package
# Initialize git repository
git init
# Create APKBUILD file
cat > APKBUILD << 'EOF'
# Contributor: Your Name <[email protected]>
# Maintainer: Your Name <[email protected]>
pkgname=hello-world
pkgver=1.0.0
pkgrel=0
pkgdesc="A simple hello world package"
url="https://github.com/yourusername/hello-world"
arch="all"
license="MIT"
makedepends="gcc musl-dev"
source="$pkgname-$pkgver.tar.gz"
build() {
make
}
check() {
make test
}
package() {
make DESTDIR="$pkgdir" install
}
EOF
# Create source code
mkdir -p src
cat > src/Makefile << 'EOF'
CC = gcc
CFLAGS = -Wall -O2
hello: hello.c
$(CC) $(CFLAGS) -o hello hello.c
test: hello
./hello | grep -q "Hello, World!"
@echo "Tests passed!"
install: hello
install -Dm755 hello $(DESTDIR)/usr/bin/hello
clean:
rm -f hello
EOF
cat > src/hello.c << 'EOF'
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
EOF
# Create .gitignore
cat > .gitignore << 'EOF'
*.o
hello
*.tar.gz
src/
pkg/
*.apk
EOF
๐ Step 2: Set Up GitHub Actions
Configure CI with GitHub Actions:
# Create GitHub Actions workflow
mkdir -p .github/workflows
cat > .github/workflows/ci.yml << 'EOF'
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
container:
image: alpine:latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install dependencies
run: |
apk update
apk add alpine-sdk sudo
- name: Setup build user
run: |
adduser -D builder
addgroup builder abuild
echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
- name: Generate signing key
run: |
su - builder -c "abuild-keygen -a -n"
- name: Build package
run: |
cp APKBUILD /home/builder/
su - builder -c "cd ~ && abuild -r"
- name: Run tests
run: |
su - builder -c "cd ~ && abuild check"
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: packages
path: /home/builder/packages/
test:
runs-on: ubuntu-latest
container:
image: alpine:latest
needs: build
steps:
- name: Download artifacts
uses: actions/download-artifact@v3
with:
name: packages
- name: Install package
run: |
apk add --allow-untrusted packages/*/*.apk
- name: Test installed package
run: |
hello | grep -q "Hello, World!"
echo "Package test passed!"
EOF
# Commit and push
git add .
git commit -m "Initial CI setup"
git remote add origin https://github.com/yourusername/hello-world.git
git push -u origin main
๐ Step 3: GitLab CI Configuration
Alternative setup for GitLab:
# Create GitLab CI configuration
cat > .gitlab-ci.yml << 'EOF'
image: alpine:latest
stages:
- build
- test
- package
- deploy
variables:
PACKAGE_NAME: "hello-world"
PACKAGE_VERSION: "1.0.0"
before_script:
- apk update
- apk add alpine-sdk sudo
build:
stage: build
script:
- adduser -D builder
- addgroup builder abuild
- echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
- su - builder -c "abuild-keygen -a -n"
- cp APKBUILD /home/builder/
- su - builder -c "cd ~ && abuild -r"
artifacts:
paths:
- /home/builder/packages/
expire_in: 1 week
test:
stage: test
dependencies:
- build
script:
- apk add --allow-untrusted /home/builder/packages/*/*.apk
- hello | grep -q "Hello, World!"
- echo "Tests passed!"
package:
stage: package
dependencies:
- build
script:
- mkdir -p public
- cp -r /home/builder/packages/* public/
- cd public && apk index -o APKINDEX.tar.gz *.apk
- abuild-sign APKINDEX.tar.gz
artifacts:
paths:
- public/
expire_in: 1 month
deploy:
stage: deploy
dependencies:
- package
script:
- echo "Deploy to repository server"
# Add deployment commands here
only:
- tags
- main
EOF
๐ Step 4: Local CI Testing
Test CI locally before pushing:
# Install CI testing tools
apk add docker docker-compose act
# Start Docker
rc-service docker start
# Create local CI test script
cat > test-ci-local.sh << 'EOF'
#!/bin/sh
# Test CI locally
echo "๐ Testing CI Pipeline Locally"
echo "============================"
# Create temporary Alpine container
docker run -it --rm \
-v $(pwd):/workspace \
-w /workspace \
alpine:latest sh -c '
echo "๐ฆ Setting up build environment..."
apk update
apk add alpine-sdk sudo
echo "๐ค Creating build user..."
adduser -D builder
addgroup builder abuild
echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
echo "๐ Generating keys..."
su - builder -c "abuild-keygen -a -n"
echo "๐๏ธ Building package..."
cp /workspace/APKBUILD /home/builder/
su - builder -c "cd ~ && abuild -r"
echo "๐งช Running tests..."
su - builder -c "cd ~ && abuild check"
echo "โ
CI test completed!"
ls -la /home/builder/packages/
'
EOF
chmod +x test-ci-local.sh
# Run local test
./test-ci-local.sh
๐ Step 5: Advanced CI Features
Add more CI capabilities:
# Enhanced CI workflow with matrix builds
cat > .github/workflows/advanced-ci.yml << 'EOF'
name: Advanced CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * 0' # Weekly build
jobs:
build:
strategy:
matrix:
alpine-version: ['3.17', '3.18', 'edge']
arch: ['x86_64', 'aarch64']
runs-on: ubuntu-latest
container:
image: alpine:${{ matrix.alpine-version }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ~/packages
key: ${{ runner.os }}-${{ matrix.alpine-version }}-${{ hashFiles('APKBUILD') }}
- name: Setup build environment
run: |
apk add --no-cache alpine-sdk
abuild-keygen -a -n
- name: Lint APKBUILD
run: |
apk add --no-cache apkbuild-lint
apkbuild-lint APKBUILD
- name: Security scan
run: |
apk add --no-cache clamav
freshclam
clamscan -r .
- name: Build package
run: |
abuild -r
- name: Test package
run: |
abuild check
- name: Generate checksums
run: |
cd ~/packages
sha256sum *.apk > SHA256SUMS
- name: Upload packages
uses: actions/upload-artifact@v3
with:
name: packages-${{ matrix.alpine-version }}-${{ matrix.arch }}
path: ~/packages/
release:
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Download all artifacts
uses: actions/download-artifact@v3
- name: Create release
uses: softprops/action-gh-release@v1
with:
files: |
packages-*/*.apk
packages-*/SHA256SUMS
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
EOF
๐ Step 6: Package Repository Setup
Create automated package repository:
# Repository generation script
cat > scripts/generate-repo.sh << 'EOF'
#!/bin/sh
# Generate APK repository
REPO_DIR="repository"
ARCH="x86_64"
echo "๐ฆ Generating APK Repository"
echo "=========================="
# Create repository structure
mkdir -p $REPO_DIR/$ARCH
# Copy packages
cp ~/packages/*/$ARCH/*.apk $REPO_DIR/$ARCH/
# Generate index
cd $REPO_DIR/$ARCH
apk index -o APKINDEX.tar.gz *.apk
# Sign index
abuild-sign APKINDEX.tar.gz
# Create repository info
cat > ../index.html << HTML
<!DOCTYPE html>
<html>
<head>
<title>Alpine Package Repository</title>
<style>
body { font-family: Arial; margin: 40px; }
.package { background: #f0f0f0; padding: 10px; margin: 10px 0; }
</style>
</head>
<body>
<h1>Alpine Package Repository</h1>
<p>Add to /etc/apk/repositories:</p>
<code>https://your-domain.com/repository</code>
<h2>Available Packages:</h2>
$(for pkg in *.apk; do
echo "<div class='package'>$pkg</div>"
done)
</body>
</html>
HTML
echo "โ
Repository generated!"
EOF
chmod +x scripts/generate-repo.sh
# Add to CI workflow
cat >> .github/workflows/ci.yml << 'EOF'
publish:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Download packages
uses: actions/download-artifact@v3
- name: Generate repository
run: |
./scripts/generate-repo.sh
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./repository
EOF
๐ Step 7: CI Monitoring
Monitor your CI pipelines:
# Create CI status badge
cat >> README.md << 'EOF'
## Build Status



## Installation
```bash
# Add repository
echo "https://yourusername.github.io/hello-world/repository" >> /etc/apk/repositories
# Install package
apk add hello-world
EOF
Create build notification script
cat > scripts/notify-build.sh << โEOFโ #!/bin/sh
Send build notifications
STATUS=$1 COMMIT=$2
if [ โ$STATUSโ = โsuccessโ ]; then MESSAGE=โโ Build successful for commit $COMMITโ else MESSAGE=โโ Build failed for commit $COMMITโ fi
Send to Discord webhook
curl -X POST -H โContent-Type: application/jsonโ
-d โ{โcontentโ: โ$MESSAGEโ}โ
$DISCORD_WEBHOOK_URL
Send email notification
echo โ$MESSAGEโ | mail -s โCI Build Statusโ [email protected] EOF
chmod +x scripts/notify-build.sh
## ๐ Step 8: CI Best Practices
Implement CI best practices:
```bash
# Pre-commit hooks
cat > .pre-commit-config.yaml << 'EOF'
repos:
- repo: local
hooks:
- id: apkbuild-lint
name: Lint APKBUILD
entry: apkbuild-lint
language: system
files: APKBUILD
- id: shellcheck
name: Check shell scripts
entry: shellcheck
language: system
files: \.sh$
- id: test-build
name: Test build locally
entry: ./test-ci-local.sh
language: system
pass_filenames: false
EOF
# Install pre-commit
pip install pre-commit
pre-commit install
# CI optimization script
cat > scripts/optimize-ci.sh << 'EOF'
#!/bin/sh
# Optimize CI performance
echo "๐ CI Optimization Report"
echo "======================="
# Check for caching opportunities
echo "๐ฆ Package Cache:"
find . -name "*.apk" -mtime +7 -exec rm {} \;
echo " Cleaned old packages"
# Parallel build check
echo -e "\n๐ Parallel Build Status:"
grep -q "JOBS=" APKBUILD || echo " โ ๏ธ Consider adding JOBS=\$(nproc)"
# Dependency optimization
echo -e "\n๐ Dependencies:"
grep "makedepends" APKBUILD | wc -w | \
awk '{if($1>10) print " โ ๏ธ Many dependencies ("$1"), consider splitting"}'
# Build time analysis
echo -e "\nโฑ๏ธ Build Time Tips:"
echo " - Use ccache for C/C++ projects"
echo " - Enable parallel make with -j\$(nproc)"
echo " - Cache downloaded sources"
EOF
chmod +x scripts/optimize-ci.sh
๐ฎ Practice Exercise
Create a complete CI pipeline:
- Fork a simple package
- Add CI configuration
- Make a change
- Watch CI run
# Example: Create calculator package
mkdir calculator-pkg
cd calculator-pkg
# Create APKBUILD
cat > APKBUILD << 'EOF'
pkgname=calculator
pkgver=1.0.0
pkgrel=0
pkgdesc="Simple calculator"
url="https://github.com/yourusername/calculator"
arch="all"
license="MIT"
makedepends="gcc musl-dev"
source="calc.c"
build() {
gcc -o calculator calc.c -lm
}
check() {
./calculator 2 + 2 | grep -q "4"
}
package() {
install -Dm755 calculator "$pkgdir"/usr/bin/calculator
}
EOF
# Add CI and push
cp ../.github/workflows/ci.yml .github/workflows/
git add .
git commit -m "Add calculator package with CI"
git push
๐จ Troubleshooting Common Issues
Build Failures
Debug CI build issues:
# Check build logs
# In GitHub: Actions tab โ Select workflow โ View logs
# Common fixes:
# Missing dependencies
echo "makedepends=\"$makedepends new-dependency\"" >> APKBUILD
# Permission issues
# Ensure builder user has correct permissions
chown -R builder:builder /home/builder/
# Key issues
su - builder -c "abuild-keygen -a -n"
Test Failures
Fix failing tests:
# Debug test environment
docker run -it alpine:latest sh
apk add your-dependencies
# Run tests manually
# Add test debugging
cat >> APKBUILD << 'EOF'
check() {
set -x # Enable debug output
make test || {
echo "Test output:"
cat test.log
return 1
}
}
EOF
Artifact Issues
Handle artifact problems:
# Increase artifact retention
# In workflow file:
artifacts:
expire_in: 1 month # Instead of 1 week
# Fix upload paths
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: packages
path: |
/home/builder/packages/**/*.apk
/home/builder/packages/**/APKINDEX.tar.gz
๐ก Pro Tips
Tip 1: Speed Up Builds
Cache everything possible:
# Docker layer caching
- name: Cache Docker layers
uses: actions/cache@v3
with:
path: /var/lib/docker
key: docker-${{ runner.os }}-${{ hashFiles('Dockerfile') }}
# ccache for C/C++
- name: Setup ccache
run: |
apk add ccache
export PATH="/usr/lib/ccache/bin:$PATH"
ccache -M 500M
Tip 2: Multi-Architecture
Build for multiple architectures:
# Use QEMU for cross-compilation
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Build multi-arch
run: |
for arch in x86_64 aarch64 armv7; do
abuild -A $arch -r
done
Tip 3: Automated Releases
Create releases automatically:
# Semantic versioning
- name: Bump version
run: |
VERSION=$(git describe --tags --abbrev=0)
NEW_VERSION=$(echo $VERSION | awk -F. '{$NF = $NF + 1;} 1' | sed 's/ /./g')
sed -i "s/pkgver=.*/pkgver=$NEW_VERSION/" APKBUILD
git tag $NEW_VERSION
โ Best Practices
-
Test everything
- Run unit tests - Integration tests - Package installation tests
-
Use branch protection
- Require CI to pass
- Code review required
- No direct pushes to main
-
Monitor CI metrics
- Build success rate
- Average build time
- Test coverage
-
Security scanning
# Add to CI - trivy scan . - clamav scan
-
Documentation
- Document CI process
- Include troubleshooting
- Update regularly
๐ What You Learned
Amazing work! You can now:
- โ Set up CI/CD pipelines
- โ Automate package building
- โ Run automated tests
- โ Deploy to repositories
- โ Monitor build status
Your packages now have professional CI/CD!
๐ฏ Whatโs Next?
Now that you have CI/CD, explore:
- Container-based builds
- Security scanning pipelines
- Performance testing
- Automated dependency updates
Keep automating! ๐