Skip to content

Phase 3: Improve PPA and GPG CI workflows #82

Phase 3: Improve PPA and GPG CI workflows

Phase 3: Improve PPA and GPG CI workflows #82

Workflow file for this run

# Main PPA build workflow
# Triggers on PPA updates to build all packages
name: PPA Build (Main)
on:
push:
branches: [main]
paths-ignore:
- "**.md"
- "docs/**"
# Generated APT repo files (pushed by build jobs, synced to gh-pages)
- "dists/**"
- "pool/**"
- "db/**"
- "lists/**"
- "key.gpg"
- "Release.gpg"
- "keyrings/**"
- "conf/**"
pull_request:
branches: [main]
paths-ignore:
- "**.md"
- "docs/**"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-workflows:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Lint shell scripts
run: |
set -euo pipefail
echo "Checking shell syntax..."
find . -name "*.sh" -not -path "./.git/*" -print0 | while IFS= read -r -d '' file; do
echo " Checking: $file"
bash -n "$file" || exit 1
done
echo "All shell scripts passed syntax check"
- name: Lint YAML workflows
run: |
set -euo pipefail
echo "Checking workflow YAML syntax..."
for f in .github/workflows/*.yml; do
echo " OK: $f"
python3 -c "import yaml; yaml.safe_load(open('$f'))" || exit 1
done
detect-changes:
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.detect.outputs.packages }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Detect changed submodules
id: detect
run: |
set -euo pipefail
# Get list of all submodules (as JSON array)
SUBMODULES=$(git submodule status | awk '{print $2}' | grep '^src/diepxuan/' | sed 's|^src/diepxuan/||' | sed 's|/$||' | jq -R -s -c 'split("\n") | map(select(length > 0))')
# For manual trigger, build all
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "packages=$SUBMODULES" >> $GITHUB_OUTPUT
echo "All packages will be built (manual trigger)"
exit 0
fi
# Get base ref
BASE_REF="${{ github.base_ref }}"
if [[ -z "$BASE_REF" ]]; then
BASE_REF="main"
fi
# Get changed files
CHANGES=$(git diff --name-only "origin/$BASE_REF...HEAD" 2>/dev/null || git diff --name-only HEAD~1...HEAD 2>/dev/null || echo "")
echo "Changes detected: $CHANGES"
# Find which submodules have changes (as JSON array)
CHANGED_PACKAGES="[]"
if [[ -n "$CHANGES" ]]; then
for submodule in $(git submodule status | awk '{print $2}' | grep '^src/diepxuan/' | sed 's|^src/diepxuan/||' | sed 's|/$||'); do
if echo "$CHANGES" | grep -q "^src/diepxuan/$submodule/"; then
echo "Package '$submodule' has changes"
CHANGED_PACKAGES=$(echo "$CHANGED_PACKAGES" | jq -c ". + [\"$submodule\"]")
fi
done
fi
if [[ "$CHANGED_PACKAGES" == "[]" ]]; then
echo "No package changes detected"
echo "packages=[]" >> $GITHUB_OUTPUT
else
echo "packages=$CHANGED_PACKAGES" >> $GITHUB_OUTPUT
echo "Will build: $CHANGED_PACKAGES"
fi
package-build:
needs: detect-changes
if: needs.detect-changes.outputs.packages != '[]' && needs.detect-changes.outputs.packages != ''
permissions:
contents: write
strategy:
max-parallel: 1
fail-fast: false
matrix:
package: ${{ fromJson(needs.detect-changes.outputs.packages) }}
uses: diepxuan/.github/.github/workflows/debian-package-ppa.yml@main
with:
module: ${{ matrix.package }}
secrets:
GPG_KEY: ${{ secrets.GPG_KEY }}
GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }}
GIT_COMMITTER_EMAIL: ${{ secrets.GIT_COMMITTER_EMAIL }}
SSH_ID_RSA: ${{ secrets.SSH_ID_RSA }}
verify-build:
needs: package-build
if: success() && needs.package-build.result == 'success'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: true
- name: Setup GPG
uses: diepxuan/.github/.github/actions/gpg-setup@main
with:
gpg_key: ${{ secrets.GPG_KEY }}
gpg_key_id: ${{ secrets.GPG_KEY_ID }}
committer_email: ${{ secrets.GIT_COMMITTER_EMAIL }}
committer_name: ${{ github.repository_owner }}
- name: Verify .deb packages
run: |
set -euo pipefail
echo "Verifying .deb packages in pool..."
DEB_COUNT=0
for deb in pool/main/*/*.deb; do
[ -f "$deb" ] || continue
DEB_COUNT=$((DEB_COUNT + 1))
echo " Verifying: $deb"
dpkg-deb --info "$deb" > /dev/null 2>&1 || { echo "INVALID: $deb"; exit 1; }
dpkg-deb --field "$deb" Package Version Architecture > /dev/null 2>&1 || { echo "MISSING FIELDS: $deb"; exit 1; }
done
echo "Verified $DEB_COUNT .deb packages"
- name: Verify APT repository metadata
run: |
set -euo pipefail
echo "Verifying APT repository metadata..."
for release in dists/*/Release; do
[ -f "$release" ] || continue
DIST=$(echo "$release" | cut -d/ -f2)
echo " Checking dist: $DIST"
gpg --verify "dists/$DIST/Release.gpg" "dists/$DIST/Release" 2>&1 || { echo "GPG VERIFY FAILED: $DIST"; exit 1; }
done
echo "All APT metadata verified"
sync-gh-pages:
needs: [detect-changes, package-build]
if: |
always() &&
github.ref == 'refs/heads/main' &&
needs.detect-changes.outputs.packages != '[]' &&
needs.detect-changes.outputs.packages != '' &&
needs.package-build.result == 'success'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
ref: main
path: main-worktree
fetch-depth: 1
persist-credentials: false
- name: Checkout gh-pages
uses: actions/checkout@v4
with:
ref: gh-pages
path: gh-pages-worktree
fetch-depth: 1
persist-credentials: false
token: ${{ secrets.GITHUB_TOKEN }}
- name: Sync generated files to gh-pages
run: |
set -euo pipefail
cd gh-pages-worktree
git config user.name "DiepXuan CI"
git config user.email "ci@diepxuan.com"
# Remove old generated files
rm -rf dists pool db lists key.gpg Release.gpg keyrings conf
# Copy new generated files from main
if [ -d ../main-worktree/dists ]; then cp -r ../main-worktree/dists ./dists; else mkdir -p dists; fi
if [ -d ../main-worktree/pool ]; then cp -r ../main-worktree/pool ./pool; else mkdir -p pool; fi
if [ -d ../main-worktree/db ]; then cp -r ../main-worktree/db ./db; else mkdir -p db; fi
if [ -d ../main-worktree/lists ]; then cp -r ../main-worktree/lists ./lists; else mkdir -p lists; fi
if [ -f ../main-worktree/key.gpg ]; then cp ../main-worktree/key.gpg ./key.gpg; fi
if [ -f ../main-worktree/Release.gpg ]; then cp ../main-worktree/Release.gpg ./Release.gpg; fi
if [ -d ../main-worktree/keyrings ]; then cp -r ../main-worktree/keyrings ./keyrings; fi
if [ -d ../main-worktree/conf ]; then cp -r ../main-worktree/conf ./conf; fi
# Commit and push to gh-pages
git add -A
if ! git diff --cached --quiet; then
git commit -m "ci: sync generated files from main@${{ github.sha }}"
git push origin gh-pages
else
echo "No changes to sync to gh-pages"
fi
- name: Clean generated files from main
run: |
set -euo pipefail
cd main-worktree
git config user.name "DiepXuan CI"
git config user.email "ci@diepxuan.com"
# Remove generated files from main tracking
# These paths are ignored in the workflow trigger, so this push won't trigger a new CI run
git rm -rf --cached dists pool db lists key.gpg Release.gpg keyrings 2>/dev/null || true
if ! git diff --cached --quiet; then
git commit -m "ci: remove generated files (synced to gh-pages)"
git push origin main
else
echo "No generated files to clean from main"
fi