Package signing #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build Redis CE RPM | |
on: | |
pull_request: | |
branches: | |
- release/8.0 | |
push: | |
branches: | |
- release/8.0 | |
env: | |
VERSION: "8.0.0" | |
jobs: | |
build-containers: | |
name: Build Builder Containers | |
uses: ./.github/workflows/build_workflow_containers.yaml | |
with: | |
os: ${{ matrix.os }} | |
strategy: | |
fail-fast: false | |
matrix: | |
os: ["rockylinux8", "rockylinux9"] | |
package-rpm: | |
name: Build and Package | |
needs: build-containers | |
runs-on: ${{ matrix.platform == 'arm64' && 'ubuntu24-arm64-2-8' || 'ubuntu-latest-8-cores' }} | |
permissions: | |
packages: read | |
contents: read | |
strategy: | |
fail-fast: false | |
matrix: | |
platform: [amd64, arm64] | |
os: | |
- name: rockylinux | |
version: 8 | |
- name: rockylinux | |
version: 9 | |
container: | |
image: ghcr.io/${{ github.repository }}/builder:${{ matrix.os.name }}${{ matrix.os.version }} | |
credentials: | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Build Redis | |
run: | | |
# Source gcc-toolset-13 environment, since GitHub Actions's shell is invoked with `--norc` | |
source /etc/profile.d/gcc-toolset-13.sh | |
export BUILD_WITH_MODULES=yes | |
export INSTALL_RUST_TOOLCHAIN=yes | |
export DISABLE_WERRORS=yes | |
export BUILD_TLS=yes | |
export USE_SYSTEMD=yes | |
# Download and extract Redis source | |
curl -L "https://github.com/redis/redis/archive/refs/tags/${{ env.VERSION }}.tar.gz" -o redis.tar.gz | |
tar xzf redis.tar.gz | |
cd redis-${{ env.VERSION }} | |
# Build Redis | |
make -j "$(nproc)" all | |
make install PREFIX=/usr/local | |
echo "Contents of /usr/local/lib/redis/modules/:" | |
ls -la /usr/local/lib/redis/modules/ | |
- name: Set up Go | |
uses: actions/setup-go@v4 | |
with: | |
go-version: '1.21' | |
- name: Install nfpm | |
run: | | |
go install github.com/goreleaser/nfpm/v2/cmd/nfpm@latest | |
- name: Build RPM package | |
run: | | |
cp templates/nfpm.yaml.tpl nfpm.yaml | |
mkdir -p dist | |
VERSION=${{ env.VERSION }} ARCH=${{ matrix.platform }} nfpm package --packager rpm --target dist | |
- name: Upload RPM artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: redis-${{ matrix.os.version }}-${{ matrix.platform }}-rpm | |
path: dist/*.rpm | |
test-rpm: | |
name: Sanity Tests | |
needs: package-rpm | |
runs-on: ${{ matrix.platform == 'arm64' && 'ubuntu24-arm64-2-8' || 'ubuntu-latest' }} | |
container: | |
image: "${{ matrix.distro }}:${{ matrix.distro == 'quay.io/centos/centos' && 'stream' || '' }}${{ matrix.distro_version }}" | |
strategy: | |
fail-fast: false | |
matrix: | |
exclude: | |
- distro: quay.io/centos/centos | |
distro_version: 8 | |
platform: [amd64, arm64] | |
distro: | |
- rockylinux | |
- almalinux | |
- quay.io/centos/centos | |
distro_version: | |
- 8 | |
- 9 | |
steps: | |
- name: Download RPM artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: redis-${{ matrix.distro_version }}-${{ matrix.platform }}-rpm | |
path: ./rpm | |
- name: Setup systemctl replacement | |
run: | | |
# For CentOS 8, update mirrors to vault first | |
if [ "${{ matrix.distro }}" = "quay.io/centos/centos" ] && [ "${{ matrix.distro_version }}" = "8" ]; then | |
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* | |
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* | |
fi | |
# Install curl if not present | |
dnf install -y --allowerasing curl python3 | |
# Download systemctl replacement first as .py file | |
curl -L https://raw.githubusercontent.com/gdraheim/docker-systemctl-replacement/master/files/docker/systemctl3.py -o /usr/bin/systemctl3.py | |
# Handle Python interpreter path based on distribution | |
if [ "${{ matrix.distro }}" = "quay.io/centos/centos" ] && [ "${{ matrix.distro_version }}" = "8" ]; then | |
# CentOS 8 uses platform-python | |
pythonpath="/usr/libexec/platform-python" | |
else | |
# Other distributions use regular python3 | |
pythonpath="/usr/bin/python3" | |
fi | |
echo "Using Python interpreter: $pythonpath" | |
sed -i -e "s|/usr/bin/python3|$pythonpath|" /usr/bin/systemctl3.py | |
# Copy to final location after modification | |
cp /usr/bin/systemctl3.py /usr/bin/systemctl | |
chmod +x /usr/bin/systemctl | |
# Create required systemd directory structure | |
mkdir -p /run/systemd/system/ | |
- name: Update CentOS 8 mirror URLs to vault | |
if: matrix.distro == 'quay.io/centos/centos' && matrix.distro_version == '8' | |
run: | | |
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* | |
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* | |
- name: Install RPM dependencies | |
run: | | |
dnf install -y ./rpm/*.rpm || true | |
systemctl enable redis | |
systemctl start redis | |
- name: Basic Sanity Tests | |
run: | | |
for i in {1..5}; do redis-cli ping &>/dev/null && break || echo "Waiting for Redis... $i" && sleep 1; done | |
redis-cli info server || { echo "Cannot get server info"; exit 1; } | |
- name: Verify installed modules | |
run: | | |
modules=$(redis-cli module list) | |
echo "Installed modules:" | |
echo "$modules" | |
missing_modules=() | |
for module in "bf" "search" "timeseries" "ReJSON"; do | |
if ! echo "$modules" | grep -q "$module"; then | |
missing_modules+=("$module") | |
fi | |
done | |
if [ ${#missing_modules[@]} -eq 0 ]; then | |
echo "All required modules are installed" | |
else | |
echo "The following modules are missing: ${missing_modules[*]}" | |
exit 1 | |
fi | |
- name: Test RedisBloom | |
run: | | |
redis-cli BF.ADD popular_keys "redis:hash" | |
redis-cli BF.ADD popular_keys "redis:set" | |
[ "$(redis-cli BF.EXISTS popular_keys "redis:hash")" = "1" ] || { echo "RedisBloom test failed: redis:hash not found"; exit 1; } | |
[ "$(redis-cli BF.EXISTS popular_keys "redis:list")" = "0" ] || { echo "RedisBloom test failed: redis:list found unexpectedly"; exit 1; } | |
echo "RedisBloom test passed successfully" | |
- name: Test RediSearch | |
run: | | |
redis-cli FT.CREATE redis_commands ON HASH PREFIX 1 cmd: SCHEMA name TEXT SORTABLE description TEXT | |
redis-cli HSET cmd:set name "SET" description "Set the string value of a key" | |
redis-cli HSET cmd:get name "GET" description "Get the value of a key" | |
result=$(redis-cli FT.SEARCH redis_commands "value") | |
if echo "$result" | grep -q "Set the string value of a key" && \ | |
echo "$result" | grep -q "Get the value of a key"; then | |
echo "RediSearch test passed successfully" | |
else | |
echo "RediSearch test failed: expected commands not found" | |
exit 1 | |
fi | |
- name: Test RedisTimeSeries | |
run: | | |
redis-cli TS.CREATE redis:cpu:usage RETENTION 86400 | |
redis-cli TS.ADD redis:cpu:usage "*" 80 | |
redis-cli TS.ADD redis:cpu:usage "*" 65 | |
redis-cli TS.ADD redis:cpu:usage "*" 70 | |
result=$(redis-cli TS.RANGE redis:cpu:usage - + COUNT 3) | |
if echo "$result" | grep -q "80" && \ | |
echo "$result" | grep -q "65" && \ | |
echo "$result" | grep -q "70"; then | |
echo "RedisTimeSeries test passed successfully" | |
else | |
echo "RedisTimeSeries test failed" | |
exit 1 | |
fi | |
- name: Test ReJSON | |
run: | | |
redis-cli JSON.SET redis:config $ '{"maxmemory":"2gb","maxmemory-policy":"allkeys-lru"}' | |
result=$(redis-cli JSON.GET redis:config $.maxmemory-policy) | |
cleaned_result=$(echo $result | tr -d "[]\"") | |
if [ "$cleaned_result" = "allkeys-lru" ]; then | |
echo "ReJSON test passed successfully" | |
else | |
echo "ReJSON test failed: expected allkeys-lru, got $result" | |
exit 1 | |
fi | |
upload-rpm: | |
name: Upload RPM to S3 | |
needs: test-rpm | |
if: github.ref == 'refs/heads/release/8.0' | |
runs-on: ubuntu-latest | |
permissions: | |
id-token: write | |
strategy: | |
fail-fast: false | |
matrix: | |
platform: [amd64, arm64] | |
os: | |
- name: rockylinux | |
version: 8 | |
- name: rockylinux | |
version: 9 | |
steps: | |
- uses: actions/download-artifact@v4 | |
with: | |
name: redis-${{ matrix.os.version }}-${{ matrix.platform }}-rpm | |
path: s3uploads | |
- name: Configure AWS Credentials | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-region: ${{ secrets.RPM_S3_REGION }} | |
role-to-assume: ${{ secrets.RPM_S3_IAM_ARN }} | |
- name: Install GPG key | |
run: | | |
echo -e "${{ secrets.GPG_KEY }}" | gpg --batch --import | |
- name: Get GPG key ID | |
id: gpg_id | |
run: | | |
GPG_ID=$(gpg --list-keys --with-colons | grep pub | cut -d':' -f5) | |
echo "GPG_ID=$GPG_ID" >> $GITHUB_OUTPUT | |
- name: Get GPG email | |
id: gpg_email | |
run: | | |
GPG_EMAIL=$(gpg --list-keys --with-colons | grep uid | head -n1 | cut -d':' -f10 | sed 's/.*<\(.*\)>.*/\1/') | |
echo "GPG_EMAIL=$GPG_EMAIL" >> $GITHUB_OUTPUT | |
- name: Get GPG keygrip | |
id: gpg_keygrip | |
run: | | |
KEYGRIP=$(gpg --list-keys --with-keygrip | grep Keygrip | head -n1 | awk '{print $3}') | |
echo "KEYGRIP=$KEYGRIP" >> $GITHUB_OUTPUT | |
- name: Sign RPM packages | |
run: | | |
# Install required tools | |
sudo apt-get update | |
sudo apt-get install -y rpm createrepo-c s3cmd | |
# Export and import GPG key for RPM | |
gpg --export -a "${{ steps.gpg_email.outputs.GPG_EMAIL }}" > rpm-gpg-key.asc | |
sudo rpm --import rpm-gpg-key.asc | |
# Configure GPG agent for signing | |
mkdir -p ~/.gnupg | |
echo "allow-preset-passphrase" > ~/.gnupg/gpg-agent.conf | |
gpg-connect-agent reloadagent /bye | |
# Preset passphrase for non-interactive signing | |
/usr/lib/gnupg/gpg-preset-passphrase -P "${{ secrets.GPG_PASSWORD }}" -c "${{ steps.gpg_keygrip.outputs.KEYGRIP }}" | |
# Sign all RPM packages | |
cd s3uploads | |
find . -name "*.rpm" -exec rpmsign --addsign --key-id "${{ steps.gpg_id.outputs.GPG_ID }}" {} \; | |
# Create repository metadata with signatures | |
createrepo_c . | |
- name: Update packages and publish to private repo | |
env: | |
RPM_S3_BUCKET: ${{ secrets.RPM_S3_BUCKET }} | |
RPM_S3_REGION: ${{ secrets.RPM_S3_REGION }} | |
run: | | |
s3cmd sync --acl-public --region=${{ env.RPM_S3_REGION }} s3uploads/* s3://${{ env.RPM_S3_BUCKET }}/rpm/${{ matrix.os.name }}${{ matrix.os.version }}/ |