Skip to content

Update changelog

Update changelog #85

Workflow file for this run

name: Build and Publish Packages
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to build (e.g., 1.0.0)'
required: true
rebuild_rpm_repo:
description: 'Download all RPM files and rebuild repo metadata from scratch'
type: boolean
default: false
permissions:
contents: write
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v7
with:
go-version-file: 'go.mod'
- name: Get version
id: version
env:
REQUESTED_VERSION: ${{ inputs.version }}
run: |
if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then
VERSION="${REQUESTED_VERSION}"
else
VERSION="${GITHUB_REF_NAME#v}"
fi
if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Release version must use numeric X.Y.Z format."
exit 1
fi
printf 'RELEASE_VERSION=%s\n' "${VERSION}" >> "${GITHUB_ENV}"
printf 'VERSION=%s\n' "${VERSION}" >> "${GITHUB_OUTPUT}"
echo "Building version: ${VERSION}"
- name: Prepare release notes
if: startsWith(github.ref, 'refs/tags/')
env:
RELEASE_NOTES_PATH: ${{ runner.temp }}/release-notes.md
run: |
./bin/changelog-section.sh "${RELEASE_VERSION}" > "${RELEASE_NOTES_PATH}"
printf '\n\n---\n\n%s\n' 'More information: [installation instructions](https://github.com/chieftools/flowguard-proxy#quick-install) · [upgrade instructions](https://github.com/chieftools/flowguard-proxy#upgrading) · [configuration instructions](https://github.com/chieftools/flowguard-proxy#configure-your-server)' >> "${RELEASE_NOTES_PATH}"
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y apt-utils createrepo-c rpm gpg rsync
- name: Import GPG key
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: |
printf '%s\n' "${GPG_PRIVATE_KEY}" | gpg --batch --import
printf '%s' "${GPG_PASSPHRASE}" > /tmp/gpg-passphrase
- name: Build DEB packages
run: |
chmod +x ./bin/build-deb.sh
./bin/build-deb.sh "${RELEASE_VERSION}" amd64
./bin/build-deb.sh "${RELEASE_VERSION}" arm64
- name: Build RPM packages
run: |
chmod +x ./bin/build-rpm.sh
./bin/build-rpm.sh "${RELEASE_VERSION}" 1 x86_64
./bin/build-rpm.sh "${RELEASE_VERSION}" 1 aarch64
- name: Setup SSH
env:
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: |
mkdir -p ~/.ssh
printf '%s\n' "${SSH_PRIVATE_KEY}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
SSH_HOSTNAME="${SSH_HOST#*@}"
ssh-keyscan -H "${SSH_HOSTNAME}" >> ~/.ssh/known_hosts
- name: Download existing repository metadata
env:
REBUILD_RPM_REPO: ${{ inputs.rebuild_rpm_repo }}
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_PATH: ${{ secrets.SSH_PATH }}
run: |
SSH_CMD="ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no"
# Download only DEB metadata (not the pool with all .deb archives)
mkdir -p repo-sync/deb/dists
rsync -avz -e "${SSH_CMD}" \
"${SSH_HOST}:${SSH_PATH}/deb/dists/" repo-sync/deb/dists/ || true
# Download RPM repositories
for RPM_ARCH in x86_64 aarch64; do
mkdir -p repo-sync/rpm/stable/${RPM_ARCH}
if [ "${REBUILD_RPM_REPO}" = "true" ]; then
echo "Rebuilding RPM ${RPM_ARCH} repo: downloading all packages..."
rsync -avz -e "${SSH_CMD}" \
"${SSH_HOST}:${SSH_PATH}/rpm/stable/${RPM_ARCH}/" \
repo-sync/rpm/stable/${RPM_ARCH}/ || true
else
mkdir -p repo-sync/rpm/stable/${RPM_ARCH}/repodata
rsync -avz -e "${SSH_CMD}" \
"${SSH_HOST}:${SSH_PATH}/rpm/stable/${RPM_ARCH}/repodata/" \
repo-sync/rpm/stable/${RPM_ARCH}/repodata/ || true
fi
done
- name: Update DEB repository
run: |
VERSION="${RELEASE_VERSION}"
POOL_DIR="repo-sync/deb/pool/main/f/flowguard"
mkdir -p "${POOL_DIR}"
for DEB_ARCH in amd64 arm64; do
DEB_FILE="flowguard_${VERSION}_${DEB_ARCH}.deb"
PACKAGES_DIR="repo-sync/deb/dists/stable/main/binary-${DEB_ARCH}"
# Place new package in pool directory structure
cp "${DEB_FILE}" "${POOL_DIR}/"
# Generate Packages entries for all .deb files in pool
NEW_ENTRY=$(cd repo-sync/deb && apt-ftparchive packages pool/)
# Prepare Packages file
mkdir -p "${PACKAGES_DIR}"
touch "${PACKAGES_DIR}/Packages"
# Remove any existing entry for this version (in case of re-release)
if [ -s "${PACKAGES_DIR}/Packages" ]; then
awk -v RS='\n\n' -v ORS='\n\n' -v ver="flowguard_${VERSION}_${DEB_ARCH}.deb" \
'$0 !~ ver && NF' "${PACKAGES_DIR}/Packages" > "${PACKAGES_DIR}/Packages.tmp"
mv "${PACKAGES_DIR}/Packages.tmp" "${PACKAGES_DIR}/Packages"
fi
# Append new entry — filter to only entries matching this architecture
printf '%s' "${NEW_ENTRY}" | awk -v RS='\n\n' -v ORS='\n\n' -v arch="${DEB_ARCH}" \
'$0 ~ "Architecture: "arch' >> "${PACKAGES_DIR}/Packages"
# Generate compressed versions
gzip -9c "${PACKAGES_DIR}/Packages" > "${PACKAGES_DIR}/Packages.gz"
bzip2 -9c "${PACKAGES_DIR}/Packages" > "${PACKAGES_DIR}/Packages.bz2"
done
# Remove any stale files from old aptly-generated repo that we don't manage
find repo-sync/deb/dists/ -name "*.xz" -delete 2>/dev/null || true
find repo-sync/deb/dists/ -name "Contents-*" -delete 2>/dev/null || true
# Generate Release file
cd repo-sync/deb
apt-ftparchive \
-o APT::FTPArchive::Release::Origin="FlowGuard" \
-o APT::FTPArchive::Release::Label="FlowGuard" \
-o APT::FTPArchive::Release::Suite="stable" \
-o APT::FTPArchive::Release::Codename="stable" \
-o APT::FTPArchive::Release::Components="main" \
-o APT::FTPArchive::Release::Architectures="amd64 arm64" \
release dists/stable/ > dists/stable/Release
# Sign Release file
rm -f dists/stable/InRelease dists/stable/Release.gpg
gpg --batch --yes --clearsign \
--pinentry-mode loopback \
--passphrase-file=/tmp/gpg-passphrase \
-o dists/stable/InRelease dists/stable/Release
gpg --batch --yes --detach-sign --armor \
--pinentry-mode loopback \
--passphrase-file=/tmp/gpg-passphrase \
-o dists/stable/Release.gpg dists/stable/Release
cd ../..
- name: Setup RPM repository
env:
REBUILD_RPM_REPO: ${{ inputs.rebuild_rpm_repo }}
run: |
VERSION="${RELEASE_VERSION}"
# RPM signing macros with explicit passphrase handling
echo "%_gpg_name FlowGuard Team <hello@flowguard.network>" > ~/.rpmmacros
echo "%_signature gpg" >> ~/.rpmmacros
echo '%__gpg_sign_cmd %{__gpg} gpg --batch --verbose --no-armor --pinentry-mode loopback --passphrase-file /tmp/gpg-passphrase --no-secmem-warning -u "%{_gpg_name}" -sbo %{__signature_filename} --digest-algo sha256 %{__plaintext_filename}' >> ~/.rpmmacros
for RPM_ARCH in x86_64 aarch64; do
RPM_FILE="flowguard-${VERSION}-1.${RPM_ARCH}.rpm"
REPO_DIR="repo-sync/rpm/stable/${RPM_ARCH}"
# Sign the RPM
rpm --addsign --define "_gpg_name FlowGuard Team <hello@flowguard.network>" \
"${RPM_FILE}" || echo "RPM signing failed for ${RPM_ARCH}, continuing..."
# Copy new RPM to repo directory
mkdir -p "${REPO_DIR}"
cp "${RPM_FILE}" "${REPO_DIR}/"
if [ "${REBUILD_RPM_REPO}" = "true" ]; then
# Full rebuild: all RPM files are present, generate fresh metadata
createrepo_c "${REPO_DIR}"
else
# Check if existing repo metadata exists for incremental update
OLD_PRIMARY=$(find "${REPO_DIR}/repodata" -name "*-primary.xml.gz" 2>/dev/null | head -1)
if [ -n "${OLD_PRIMARY}" ]; then
# Create placeholders for old RPMs so createrepo_c --update
# preserves their metadata without needing the actual files
gunzip -c "${OLD_PRIMARY}" | grep -oP 'href="\K[^"]+' | while read -r rpm; do
[ -f "${REPO_DIR}/${rpm}" ] || touch "${REPO_DIR}/${rpm}"
done
createrepo_c --update --skip-stat "${REPO_DIR}"
else
# No existing metadata (new architecture), create fresh repo
createrepo_c "${REPO_DIR}"
fi
fi
# Sign the repository metadata
gpg --batch --yes --detach-sign --armor \
--pinentry-mode loopback \
--passphrase-file=/tmp/gpg-passphrase \
"${REPO_DIR}/repodata/repomd.xml"
# Clean up empty placeholder files
find "${REPO_DIR}" -maxdepth 1 -name "*.rpm" -empty -delete
done
- name: Create repository configuration files
run: |
# Create install script for users in root
cat > repo-sync/install.sh << 'INSTALL_EOF'
#!/bin/bash
set -e
# Detect architecture
MACHINE_ARCH=$(uname -m)
case "${MACHINE_ARCH}" in
x86_64) RPM_ARCH="x86_64"; DEB_ARCH="amd64" ;;
aarch64) RPM_ARCH="aarch64"; DEB_ARCH="arm64" ;;
arm64) RPM_ARCH="aarch64"; DEB_ARCH="arm64" ;;
*) echo "Unsupported architecture: ${MACHINE_ARCH}"; exit 1 ;;
esac
# Detect OS
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
else
echo "Cannot detect OS"
exit 1
fi
case $OS in
ubuntu|debian)
echo "Setting up FlowGuard repository for Debian/Ubuntu..."
# Create a dedicated keyring directory
install -d -m 0755 /etc/apt/keyrings
# Migrate any legacy FlowGuard APT config
rm -f /etc/apt/sources.list.d/flowguard.list /etc/apt/trusted.gpg.d/flowguard.gpg
# Add GPG key
curl -fsSL https://pkg.flowguard.network/gpg.key | gpg --dearmor --yes -o /etc/apt/keyrings/flowguard.gpg
chmod a+r /etc/apt/keyrings/flowguard.gpg
# Add repository using deb822 format
printf '%s\n' \
'Types: deb' \
'URIs: https://pkg.flowguard.network/deb' \
'Suites: stable' \
'Components: main' \
"Architectures: ${DEB_ARCH}" \
'Signed-By: /etc/apt/keyrings/flowguard.gpg' \
> /etc/apt/sources.list.d/flowguard.sources
# Refresh package metadata. Unrelated third-party repos can make
# "apt-get update" return non-zero even when FlowGuard fetched
# successfully, so only fail if FlowGuard itself is unavailable.
if ! apt-get update; then
echo "apt-get update reported errors; continuing because unrelated repositories may be broken."
fi
if ! apt-cache show flowguard >/dev/null 2>&1; then
echo "FlowGuard package metadata not present after general update; refreshing the FlowGuard repo only..."
apt-get update \
-o Dir::Etc::sourcelist='sources.list.d/flowguard.sources' \
-o Dir::Etc::sourceparts='-' \
-o APT::Get::List-Cleanup='0'
fi
apt-get install -y flowguard
;;
centos|rhel|rocky|almalinux|fedora)
echo "Setting up FlowGuard repository for RHEL/CentOS/Rocky/Alma/Fedora..."
# Add repository
cat > /etc/yum.repos.d/flowguard.repo << EOF
[flowguard]
name=FlowGuard Repository
baseurl=https://pkg.flowguard.network/rpm/stable/$RPM_ARCH
enabled=1
gpgcheck=1
gpgkey=https://pkg.flowguard.network/gpg.key
EOF
# Install
yum install -y flowguard || dnf install -y flowguard
;;
*)
echo "Unsupported OS: $OS"
echo "Please install manually from https://github.com/chieftools/flowguard-proxy/releases"
exit 1
;;
esac
echo ""
echo "FlowGuard installed successfully!"
echo "Configure it at: /etc/flowguard/config.json or use setup instructions from https://flowguard.network"
echo "Start the service: systemctl start flowguard"
echo "Start the service on boot: systemctl enable flowguard"
INSTALL_EOF
chmod +x repo-sync/install.sh
# Export GPG public key
gpg --armor --export "FlowGuard Team <hello@flowguard.network>" > repo-sync/gpg.key
# Copy config schema to root
cp config.schema.json repo-sync/config.schema.json
- name: Upload to server
env:
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_PATH: ${{ secrets.SSH_PATH }}
run: |
SSH_CMD="ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no"
# Upload new .deb to pool (additive only, old packages remain on server)
rsync -avz -e "${SSH_CMD}" \
repo-sync/deb/pool/ "${SSH_HOST}:${SSH_PATH}/deb/pool/"
# Upload updated DEB metadata
rsync -avz --delete -e "${SSH_CMD}" \
repo-sync/deb/dists/ "${SSH_HOST}:${SSH_PATH}/deb/dists/"
# Upload RPM packages and metadata for each architecture
for RPM_ARCH in x86_64 aarch64; do
# Upload new RPM package (additive only, old packages remain on server)
rsync -avz -e "${SSH_CMD}" \
"repo-sync/rpm/stable/${RPM_ARCH}/flowguard-${RELEASE_VERSION}-1.${RPM_ARCH}.rpm" \
"${SSH_HOST}:${SSH_PATH}/rpm/stable/${RPM_ARCH}/"
# Upload updated RPM metadata
rsync -avz --delete -e "${SSH_CMD}" \
repo-sync/rpm/stable/${RPM_ARCH}/repodata/ \
"${SSH_HOST}:${SSH_PATH}/rpm/stable/${RPM_ARCH}/repodata/"
done
# Upload root config files
rsync -avz -e "${SSH_CMD}" \
repo-sync/install.sh repo-sync/gpg.key repo-sync/config.schema.json \
"${SSH_HOST}:${SSH_PATH}/"
- name: Clean up
if: always()
run: |
rm -f /tmp/gpg-passphrase
rm -f ~/.ssh/deploy_key
rm -rf ~/.gnupg/private-keys-v1.d/*
- name: Upload artifacts
uses: actions/upload-artifact@v7
with:
name: packages
path: |
flowguard_*.deb
flowguard-*.rpm
retention-days: 30
- name: Create GitHub Release
uses: softprops/action-gh-release@v3
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
flowguard_${{ steps.version.outputs.VERSION }}_amd64.deb
flowguard_${{ steps.version.outputs.VERSION }}_arm64.deb
flowguard-${{ steps.version.outputs.VERSION }}-1.x86_64.rpm
flowguard-${{ steps.version.outputs.VERSION }}-1.aarch64.rpm
body_path: ${{ runner.temp }}/release-notes.md
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}