+
+
+
couchdb
+
argocd
scala
netlify
pycharm
astro
+
symfony
+
couchdb
+
+
vb
vite
pytest
+
surrealdb
strapi
+
โˆˆ
+
+
+
+
xcode
+
+
+
scala
+
+
+
protobuf
istio
perl
+
+
+
+
+
websocket
micronaut
+
+
neo4j
+
scala
+
mint
+
perl
+
xgboost
+
scipy
rs
ios
+
hapi
clj
macos
+
+
+
+
+
esbuild
++
sublime
json
+
+
+
choo
+
stimulus
+
+
+
+
+
+
+
weaviate
d
koa
Back to Blog
๐Ÿ”„ Setting Up Continuous Integration for Packages: Simple Guide
Alpine Linux DevOps Beginner

๐Ÿ”„ Setting Up Continuous Integration for Packages: Simple Guide

Published Jun 13, 2025

Easy tutorial on implementing CI/CD pipelines for Alpine Linux packages. Perfect for beginners to automate testing, building, and releasing packages.

9 min read
0 views
Table of Contents

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

![CI](https://github.com/yourusername/hello-world/workflows/CI/badge.svg)
![Alpine 3.18](https://img.shields.io/badge/Alpine-3.18-blue)
![License](https://img.shields.io/badge/License-MIT-green)

## 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:

  1. Fork a simple package
  2. Add CI configuration
  3. Make a change
  4. 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

  1. Test everything

    - Run unit tests
    - Integration tests
    - Package installation tests
  2. Use branch protection

    • Require CI to pass
    • Code review required
    • No direct pushes to main
  3. Monitor CI metrics

    • Build success rate
    • Average build time
    • Test coverage
  4. Security scanning

    # Add to CI
    - trivy scan .
    - clamav scan
  5. 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! ๐Ÿ”„