Build Git #6
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 Git | |
| on: | |
| schedule: | |
| # Runs at 00:00 UTC on Mondays, Wednesdays, and Fridays | |
| - cron: '0 0 * * 1,3,5' | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| # Force all GitHub Actions to use Node.js 24 to prevent deprecation failures | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| NODE_OPTIONS: "--no-deprecation" | |
| jobs: | |
| check-version: | |
| name: Safety Measure & Version Check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 140 | |
| outputs: | |
| build_needed: ${{ steps.check.outputs.build_needed }} | |
| version: ${{ steps.check.outputs.version }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Check Upstream Version vs Latest Release | |
| id: check | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| echo "Fetching Latest Git Source..." | |
| # Uses advanced RegEx to extract ONLY stable versions (ignores -rc release candidates) | |
| GIT_VERSION=$(curl -fsSL "https://mirrors.edge.kernel.org/pub/software/scm/git/" | grep -oP '(?<=git-)\d+\.\d+\.\d+(?=\.tar\.gz)' | sort -Vu | tail -n 1) | |
| test -n "$GIT_VERSION" || { echo "❌ FATAL ERROR: Failed to detect latest Git version."; exit 1; } | |
| echo "💡 Latest Git version detected: $GIT_VERSION" | |
| # Fetch latest release tag safely using strict JSON parsing | |
| LATEST_RELEASE=$(gh release list --limit 1 --json tagName --jq '.[0].tagName // "none"' 2>/dev/null || echo "none") | |
| echo "💡 Current repository latest release: $LATEST_RELEASE" | |
| if [ "v$GIT_VERSION" == "$LATEST_RELEASE" ]; then | |
| echo "✅ Version v$GIT_VERSION is already built. Exiting workflow early with success." | |
| echo "build_needed=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "🚀 New version detected! Proceeding to build v$GIT_VERSION." | |
| echo "build_needed=true" >> $GITHUB_OUTPUT | |
| echo "version=$GIT_VERSION" >> $GITHUB_OUTPUT | |
| fi | |
| build-binaries: | |
| name: Compile Binaries (${{ matrix.visual_arch }}) | |
| needs: check-version | |
| if: needs.check-version.outputs.build_needed == 'true' | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 240 # 4-hour safety net for heavy compilation | |
| strategy: | |
| matrix: | |
| include: | |
| - arch: amd64 | |
| visual_arch: x64 | |
| runner: ubuntu-latest | |
| ext_name: linux64 | |
| - arch: arm64 | |
| visual_arch: ARM64 | |
| runner: ubuntu-24.04-arm | |
| ext_name: linuxarm64 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build, Extract, and Compress Binary | |
| run: | | |
| set -euo pipefail | |
| mkdir -p ./output ./artifacts | |
| docker buildx build \ | |
| --platform linux/${{ matrix.arch }} \ | |
| --build-arg GIT_VERSION=${{ needs.check-version.outputs.version }} \ | |
| --output type=local,dest=./output \ | |
| --cache-to type=gha,mode=max,scope=git-${{ matrix.arch }} \ | |
| --cache-from type=gha,scope=git-${{ matrix.arch }} \ | |
| -f Dockerfile . | |
| # Navigate to output and compress the entire Git directory structure into a tar.xz archive | |
| cd ./output | |
| TAR_FILENAME="git-master-latest-${{ matrix.ext_name }}-gpl.tar.xz" | |
| tar -cJf "../artifacts/$TAR_FILENAME" . | |
| cd .. | |
| - name: Upload Artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: git-${{ matrix.arch }} | |
| path: ./artifacts/git-master-latest-${{ matrix.ext_name }}-gpl.tar.xz | |
| retention-days: 1 | |
| release-and-cleanup: | |
| name: Publish Release & Purge Old | |
| needs: [check-version, build-binaries] | |
| if: needs.check-version.outputs.build_needed == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 72 # Safety net for GitHub API uploads | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 2 | |
| - name: Download Artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: ./artifacts | |
| merge-multiple: true | |
| - name: Generate Checksums | |
| run: | | |
| set -euo pipefail | |
| cd ./artifacts | |
| echo "Generating SHA256 checksums..." | |
| sha256sum * > checksums.sha256 | |
| cat checksums.sha256 | |
| - name: Strip 'Latest' Tag From Previous Release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| # Safely fetch the old release tag using JSON. Fallback to "none" if empty. | |
| OLD_RELEASE_TAG=$(gh release list --limit 1 --json tagName --jq '.[0].tagName // "none"' 2>/dev/null || echo "none") | |
| if [ "$OLD_RELEASE_TAG" != "none" ] && [ "$OLD_RELEASE_TAG" != "null" ]; then | |
| OLD_TITLE=$(gh release view "$OLD_RELEASE_TAG" --json name -q .name) | |
| NEW_OLD_TITLE=$(echo "$OLD_TITLE" | sed 's/ - Latest//g') | |
| echo "Renaming old release '$OLD_TITLE' to '$NEW_OLD_TITLE'" | |
| gh release edit "$OLD_RELEASE_TAG" --title "$NEW_OLD_TITLE" || true | |
| else | |
| echo "✅ No previous Release found. Skipping Rename." | |
| fi | |
| - name: Create GitHub Release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ needs.check-version.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| TAG_NAME="v$VERSION" | |
| # Defensive check: Ensure release doesn't already exist to prevent race conditions | |
| if gh release view "$TAG_NAME" >/dev/null 2>&1; then | |
| echo "✅ Release $TAG_NAME already exists. Skipping creation." | |
| exit 0 | |
| fi | |
| DATE_STR=$(date -u +'%d/%m/%Y At %H:%M') | |
| COMMIT_MSG=$(git log -1 --pretty=%B | head -n 1) | |
| PREV_HASH=$(git log -2 --pretty=%H | tail -n 1) | |
| cat <<EOF > release_notes.txt | |
| This Latest Stable Automated Build Of Git Created On $DATE_STR | |
| $COMMIT_MSG | |
| This reverts commit $PREV_HASH. | |
| EOF | |
| echo "Creating release for $TAG_NAME..." | |
| gh release create "$TAG_NAME" ./artifacts/* \ | |
| --title "Git $VERSION (Executable) - Latest" \ | |
| --notes-file release_notes.txt | |
| - name: Cleanup Old Releases (Keep Last 4) | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| echo "Fetching all releases safely via JSON..." | |
| RELEASES=$(gh release list --limit 100 --json tagName --jq '.[].tagName') | |
| COUNT=0 | |
| for REL in $RELEASES; do | |
| if [ -z "$REL" ] || [ "$REL" == "null" ]; then continue; fi | |
| COUNT=$((COUNT+1)) | |
| if [ $COUNT -gt 4 ]; then | |
| echo "🗑️ Deleting old release: $REL" | |
| gh release delete "$REL" --cleanup-tag -y | |
| else | |
| echo "✅ Keeping release: $REL" | |
| fi | |
| done | |
| keepalive: | |
| name: Repository Keepalive | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'schedule' | |
| timeout-minutes: 61 # Safety net for git push | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Push Keepalive Commit | |
| run: | | |
| set -euo pipefail | |
| git config --global user.name "github-actions[bot]" | |
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
| git checkout -B keepalive | |
| git commit --allow-empty -m "chore: keepalive to prevent GitHub Actions cron pause [skip ci]" | |
| git push origin keepalive --force |