diff --git a/.github/scripts/calculate-revision.sh b/.github/scripts/calculate-revision.sh new file mode 100755 index 0000000..d43185b --- /dev/null +++ b/.github/scripts/calculate-revision.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Calculate the next revision number for a given upstream version +# Finds all git tags matching v+ or v+_pre +# Returns the next N value (highest N + 1), or 1 if no matching tags exist +# +# Usage: calculate-revision.sh + +set -euo pipefail + +UPSTREAM_VERSION="${1:-}" + +if [ -z "$UPSTREAM_VERSION" ]; then + echo "Error: Upstream version is required" >&2 + echo "Usage: $0 " >&2 + exit 1 +fi + +# Remove 'v' prefix if present +UPSTREAM_VERSION="${UPSTREAM_VERSION#v}" + +# Find all tags matching the pattern: v{version}+{N} or v{version}+{N}_pre +PATTERN="v${UPSTREAM_VERSION}+*" +MATCHING_TAGS=$(git tag -l "$PATTERN" 2>/dev/null || true) + +if [ -z "$MATCHING_TAGS" ]; then + # No matching tags, this is the first build + echo "1" + exit 0 +fi + +# Extract revision numbers from tags and find max +MAX_REVISION=0 +while IFS= read -r tag; do + # Extract the number between '+' and either end of string or '_' + if [[ $tag =~ \+([0-9]+)(_.*)?$ ]]; then + REVISION="${BASH_REMATCH[1]}" + if [ "$REVISION" -gt "$MAX_REVISION" ]; then + MAX_REVISION="$REVISION" + fi + fi +done <<< "$MATCHING_TAGS" + +# Return next revision number +NEXT_REVISION=$((MAX_REVISION + 1)) +echo "$NEXT_REVISION" diff --git a/.github/scripts/generate-changelog.sh b/.github/scripts/generate-changelog.sh new file mode 100755 index 0000000..de1de2d --- /dev/null +++ b/.github/scripts/generate-changelog.sh @@ -0,0 +1,81 @@ +#!/bin/bash +set -euo pipefail + +# Generate debian/changelog dynamically for CI builds +# Usage: generate-changelog.sh --upstream --revision + +UPSTREAM="" +REVISION="" + +while [[ $# -gt 0 ]]; do + case $1 in + --upstream) + UPSTREAM="$2" + shift 2 + ;; + --revision) + REVISION="$2" + shift 2 + ;; + *) + echo "Error: Unknown option $1" >&2 + echo "Usage: $0 --upstream --revision " >&2 + exit 1 + ;; + esac +done + +if [ -z "$UPSTREAM" ] || [ -z "$REVISION" ]; then + echo "Error: Both --upstream and --revision are required" >&2 + echo "Usage: $0 --upstream --revision " >&2 + exit 1 +fi + +# Package name +PACKAGE_NAME="halpi2-daemon" + +# Debian version format: upstream-revision +DEBIAN_VERSION="${UPSTREAM}-${REVISION}" + +# Distribution (unstable for CI builds) +DISTRIBUTION="unstable" + +# Urgency +URGENCY="medium" + +# Maintainer information +MAINTAINER_NAME="${MAINTAINER_NAME:-Hat Labs}" +MAINTAINER_EMAIL="${MAINTAINER_EMAIL:-info@hatlabs.fi}" + +# Date in RFC 2822 format +DATE=$(date -R) + +# Get recent changes from git log +# Get commits since last published release tag +LAST_TAG=$(git tag -l "v*" --sort=-version:refname | grep -v "_pre" | head -n1 || echo "") + +if [ -n "$LAST_TAG" ]; then + CHANGES=$(git log "${LAST_TAG}"..HEAD --pretty=format:" * %s" --no-merges || echo " * Build ${REVISION}") +else + # No previous tags, use recent commits + CHANGES=$(git log -10 --pretty=format:" * %s" --no-merges || echo " * Build ${REVISION}") +fi + +# If no changes (shouldn't happen), use a default message +if [ -z "$CHANGES" ]; then + CHANGES=" * Build ${REVISION}" +fi + +# Generate debian/changelog entry +cat > debian/changelog < ${DATE} +EOF + +echo "Generated debian/changelog:" +echo " Version: ${DEBIAN_VERSION}" +echo " Distribution: ${DISTRIBUTION}" +cat debian/changelog diff --git a/.github/scripts/rename-packages.sh b/.github/scripts/rename-packages.sh new file mode 100755 index 0000000..b8763c1 --- /dev/null +++ b/.github/scripts/rename-packages.sh @@ -0,0 +1,56 @@ +#!/bin/bash +set -euo pipefail + +# Rename packages with distro+component suffix +# Usage: rename-packages.sh --version --distro --component +# +# This script handles the ARM64 architecture package naming for HALPI2 daemon + +VERSION="" +DISTRO="" +COMPONENT="" + +while [[ $# -gt 0 ]]; do + case $1 in + --version) + VERSION="$2" + shift 2 + ;; + --distro) + DISTRO="$2" + shift 2 + ;; + --component) + COMPONENT="$2" + shift 2 + ;; + *) + echo "Error: Unknown option $1" >&2 + echo "Usage: $0 --version --distro --component " >&2 + exit 1 + ;; + esac +done + +if [ -z "$VERSION" ] || [ -z "$DISTRO" ] || [ -z "$COMPONENT" ]; then + echo "Error: All options are required" >&2 + exit 1 +fi + +# Package name and architecture +PACKAGE_NAME="halpi2-daemon" +ARCH="arm64" + +OLD_NAME="${PACKAGE_NAME}_${VERSION}_${ARCH}.deb" +NEW_NAME="${PACKAGE_NAME}_${VERSION}_${ARCH}+${DISTRO}+${COMPONENT}.deb" + +if [ -f "$OLD_NAME" ]; then + echo "Renaming package: $OLD_NAME -> $NEW_NAME" + mv "$OLD_NAME" "$NEW_NAME" + echo "Package renamed successfully" +else + echo "Error: Expected package not found: $OLD_NAME" >&2 + echo "Available .deb files:" >&2 + ls -la *.deb 2>/dev/null || echo "None found" >&2 + exit 1 +fi diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index eef88a4..34e1140 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,156 +4,13 @@ on: push: branches: [main] -permissions: - contents: write - -env: - APT_DISTRO: ${{ vars.APT_DISTRO || 'trixie' }} - APT_COMPONENT: ${{ vars.APT_COMPONENT || 'hatlabs' }} - jobs: - test: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v6 - - - name: Run tests - uses: ./.github/actions/run-tests - - build-and-release: - needs: test - runs-on: ubuntu-latest-arm64 - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Read version from VERSION - id: version - run: .github/scripts/read-version.sh - - - name: Check if published release exists - id: check - run: .github/scripts/check-release-exists.sh "${{ steps.version.outputs.version }}" prerelease - env: - GH_TOKEN: ${{ github.token }} - - - name: Build .deb package - if: steps.check.outputs.action == 'create' - uses: ./.github/actions/build-deb - - - name: Rename package with distro+component suffix - if: steps.check.outputs.action == 'create' - run: | - VERSION="${{ steps.version.outputs.version }}" - OLD_NAME="halpi2-daemon_${VERSION}_arm64.deb" - NEW_NAME="halpi2-daemon_${VERSION}_arm64+${APT_DISTRO}+${APT_COMPONENT}.deb" - - if [ -f "$OLD_NAME" ]; then - echo "📦 Renaming package: $(basename $OLD_NAME) → $(basename $NEW_NAME)" - mv "$OLD_NAME" "$NEW_NAME" - echo "✅ Package renamed successfully" - echo "Package location: $NEW_NAME" - else - echo "❌ Error: Expected package not found: $OLD_NAME" - exit 1 - fi - - - name: Generate release notes - if: steps.check.outputs.action == 'create' - id: notes - run: .github/scripts/generate-release-notes.sh "${{ steps.version.outputs.version }}" "${{ steps.version.outputs.tag_version }}" prerelease - env: - GH_TOKEN: ${{ github.token }} - - - name: Delete existing pre-release - if: steps.check.outputs.action == 'create' - run: | - TAG_VERSION="${{ steps.version.outputs.tag_version }}" - if gh release view "v${TAG_VERSION}" &>/dev/null; then - IS_PRERELEASE=$(gh release view "v${TAG_VERSION}" --json isPrerelease --jq '.isPrerelease') - if [ "$IS_PRERELEASE" = "true" ]; then - echo "🗑️ Deleting existing pre-release v${TAG_VERSION}" - gh release delete "v${TAG_VERSION}" --yes --cleanup-tag - fi - fi - env: - GH_TOKEN: ${{ github.token }} - - - name: Create pre-release - if: steps.check.outputs.action == 'create' - run: | - TAG_VERSION="${{ steps.version.outputs.tag_version }}" - if ! ls *.deb 1> /dev/null 2>&1; then - echo "❌ Error: No .deb files found" - exit 1 - fi - echo "📦 Creating pre-release v${TAG_VERSION}" - gh release create "v${TAG_VERSION}" \ - --prerelease \ - --title "v${TAG_VERSION} (Pre-release)" \ - --notes-file release_notes.md \ - *.deb - echo "✅ Pre-release created successfully" - env: - GH_TOKEN: ${{ github.token }} - - - name: Create draft release - if: steps.check.outputs.action == 'create' - run: | - VERSION="${{ steps.version.outputs.version }}" - TAG_VERSION="${{ steps.version.outputs.tag_version }}" - - # Check if draft release already exists - if gh release view "v${VERSION}" &>/dev/null; then - IS_DRAFT=$(gh release view "v${VERSION}" --json isDraft --jq '.isDraft') - IS_PRERELEASE=$(gh release view "v${VERSION}" --json isPrerelease --jq '.isPrerelease') - - if [ "$IS_DRAFT" = "true" ] && [ "$IS_PRERELEASE" = "false" ]; then - echo "Draft release v${VERSION} already exists, skipping creation" - fi - else - # Generate release notes for draft - .github/scripts/generate-release-notes.sh "${VERSION}" "${VERSION}" draft - - echo "📋 Creating draft release v${VERSION} with .deb package" - gh release create "v${VERSION}" \ - --draft \ - --title "v${VERSION}" \ - --notes-file release_notes.md \ - *.deb - echo "✅ Draft release created successfully with .deb attached" - fi - env: - GH_TOKEN: ${{ github.token }} - - - name: Dispatch to APT repository - if: steps.check.outputs.action == 'create' - uses: peter-evans/repository-dispatch@v4 - with: - token: ${{ secrets.REPO_DISPATCH_PAT }} - repository: hatlabs/apt.hatlabs.fi - event-type: package-updated - client-payload: | - { - "repository": "${{ github.repository }}", - "distro": "${{ env.APT_DISTRO }}", - "channel": "unstable", - "component": "${{ env.APT_COMPONENT }}" - } - - - name: Report success - if: steps.check.outputs.action == 'create' - run: | - VERSION="${{ steps.version.outputs.version }}" - TAG_VERSION="${{ steps.version.outputs.tag_version }}" - echo "=== Main Branch CI/CD Complete ===" - echo "Package version: ${VERSION}" - echo "Release tag: v${TAG_VERSION}" - echo "Release URL: https://github.com/${{ github.repository }}/releases/tag/v${TAG_VERSION}" - echo "" - echo "✅ Pre-release created with .deb package" - echo "✅ Draft release created with .deb attached" - echo "✅ Dispatched to apt.hatlabs.fi unstable channel" + build-release: + uses: hatlabs/shared-workflows/.github/workflows/build-release.yml@main + with: + package-name: halpi2-daemon + package-description: 'HALPI2 hardware daemon (Python implementation)' + apt-component: hatlabs + runs-on: ubuntu-latest-arm64 + secrets: + APT_REPO_PAT: ${{ secrets.APT_REPO_PAT }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 56874c1..0e5f97f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -2,18 +2,8 @@ name: Pull Request Checks on: pull_request: - branches: [ main ] - -permissions: - contents: read + branches: [main] jobs: - tests: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v6 - - - name: Run tests - uses: ./.github/actions/run-tests + checks: + uses: hatlabs/shared-workflows/.github/workflows/pr-checks.yml@main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53a56ca..546ab59 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,54 +4,10 @@ on: release: types: [published] -permissions: - contents: read - -env: - APT_DISTRO: ${{ vars.APT_DISTRO || 'trixie' }} - APT_COMPONENT: ${{ vars.APT_COMPONENT || 'hatlabs' }} - jobs: - dispatch-to-apt: - runs-on: ubuntu-latest - # Only handle stable releases - pre-releases are handled by main.yml - if: ${{ github.event.release.prerelease == false }} - steps: - - name: Verify release has assets - run: | - STABLE_TAG="${{ github.event.release.tag_name }}" - ASSET_COUNT=$(gh release view "$STABLE_TAG" --repo ${{ github.repository }} --json assets --jq '.assets | length') - - if [ "$ASSET_COUNT" -eq 0 ]; then - echo "❌ Error: Release has no assets attached" - echo "The .deb package should have been attached when the draft was created" - exit 1 - fi - - echo "✅ Release has $ASSET_COUNT asset(s)" - env: - GH_TOKEN: ${{ github.token }} - - - name: Trigger APT repository - uses: peter-evans/repository-dispatch@v4 - with: - token: ${{ secrets.REPO_DISPATCH_PAT }} - repository: hatlabs/apt.hatlabs.fi - event-type: package-updated - client-payload: | - { - "repository": "${{ github.repository }}", - "distro": "${{ env.APT_DISTRO }}", - "channel": "stable", - "component": "${{ env.APT_COMPONENT }}" - } - - - name: Report success - run: | - STABLE_TAG="${{ github.event.release.tag_name }}" - - echo "=== Stable Release Published ===" - echo "Stable tag: $STABLE_TAG" - echo "Release URL: https://github.com/${{ github.repository }}/releases/tag/$STABLE_TAG" - echo "" - echo "✅ Dispatched to apt.hatlabs.fi stable channel" + publish: + uses: hatlabs/shared-workflows/.github/workflows/publish-stable.yml@main + with: + apt-component: hatlabs + secrets: + APT_REPO_PAT: ${{ secrets.APT_REPO_PAT }}