Skip to content

Build and release Git-Mastery CLI #164

Build and release Git-Mastery CLI

Build and release Git-Mastery CLI #164

Workflow file for this run

name: Build and release Git-Mastery CLI
on:
workflow_run:
workflows:
- Bump version tag on merge
types:
- completed
workflow_dispatch:
push:
tags:
- "v*.*.*"
permissions:
contents: write
pull-requests: write
packages: read
issues: read
jobs:
prepare:
uses: git-mastery/actions/.github/workflows/get-latest-tag.yml@main
secrets: inherit
linux-build:
needs: prepare
if: needs.prepare.outputs.should_publish == 'true'
strategy:
matrix:
include:
- os: ubuntu-latest
arch: amd64
- os: ubuntu-24.04-arm
arch: arm64
runs-on: ${{ matrix.os }}
env:
ARCHITECTURE: ${{ matrix.arch }}
VERSION_NUMBER: ${{ needs.prepare.outputs.version_number }}
FILENAME: gitmastery-${{ needs.prepare.outputs.version_number }}-linux-${{ matrix.arch }}
REF_NAME: ${{ needs.prepare.outputs.ref_name }}
steps:
- name: Checkout source
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Build binary
run: |
echo "__version__ = \"$REF_NAME\"" > app/version.py
pyinstaller --onefile main.py --name $FILENAME
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: dist/${{ env.FILENAME }}
tag_name: ${{ env.REF_NAME }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish package as artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.FILENAME }}
path: dist/${{ env.FILENAME }}
debian-build:
# We support both ARM64 and AMD64 since Debian comes with support for
# these two out of the box
needs: [prepare, linux-build]
if: needs.prepare.outputs.should_publish == 'true'
strategy:
matrix:
include:
- os: ubuntu-latest
arch: amd64
- os: ubuntu-24.04-arm
arch: arm64
runs-on: ${{ matrix.os }}
env:
ARCHITECTURE: ${{ matrix.arch }}
VERSION: ${{ needs.prepare.outputs.version_number }}
steps:
- name: Checkout source
uses: actions/checkout@v6
with:
path: "app"
fetch-depth: 0
- name: Extract variables
env:
REF_NAME: ${{ needs.prepare.outputs.ref_name }}
run: |
# Get the tag's commit message
cd app/
CHANGELOG_MESSAGE=$(git show ${REF_NAME} --no-patch --pretty=format:%s)
echo "CHANGELOG_MESSAGE=${CHANGELOG_MESSAGE}" >> $GITHUB_ENV
- name: Install Debian packaging tools
run: |
sudo apt-get install devscripts build-essential debhelper-compat
- name: Create folder structure for ${{ env.ARCHITECTURE }} distribution
run: |
mkdir gitmastery-${VERSION}-${ARCHITECTURE}
- name: Download ${{ env.ARCHITECTURE }} binaries from artifacts
uses: actions/download-artifact@v4
with:
name: gitmastery-${{ env.VERSION }}-linux-${{ env.ARCHITECTURE }}
path: gitmastery-${{ env.VERSION }}-${{ env.ARCHITECTURE }}/
- name: Create upstream tarball .orig.tar.gz
run: |
# Create .orig.tar.gz file
tar -czf gitmastery_${VERSION}.orig.tar.gz gitmastery-${VERSION}-${ARCHITECTURE}/gitmastery-${VERSION}-linux-${ARCHITECTURE}
- name: Generate Debian packaging files
working-directory: gitmastery-${{ env.VERSION }}-${{ env.ARCHITECTURE }}
# TODO: Update to something agnostic
env:
EMAIL: woojiahao1234@gmail.com
NAME: Jiahao, Woo
run: |
file gitmastery-${VERSION}-linux-${ARCHITECTURE}
# Create the debian folder
mkdir debian
# Generate the changelog
# TODO: Maybe detect if major version change, then make it urgent
dch --create -v ${VERSION}-1 -u low --package gitmastery "$CHANGELOG_MESSAGE"
# Create the control file
# TODO: Maybe detect if major version change, then make it mandatory
echo """Source: gitmastery
Maintainer: $NAME <$EMAIL>
Section: misc
Priority: optional
Standards-Version: 4.7.0
Build-Depends: debhelper-compat (= 13)
Package: gitmastery
Architecture: ${ARCHITECTURE}
Depends: ${shlibs:Depends}, ${misc:Depends}, libc6 (>= 2.35), python3
Description: execute Git-Mastery
gitmastery is a Git learning tool built by the National University of Singapore School of Computing
""" > debian/control
# Copy over the MIT license from the main app to this release
cat ../app/LICENSE > debian/copyright
mkdir debian/source
echo "3.0 (quilt)" > debian/source/format
# Provide the rules for installation, using -e to preserve the tab character as per:
# https://wiki.debian.org/Packaging/Intro
# $(DESTDIR) resolves to debian/binarypackage/ as seen in
# https://www.debian.org/doc/manuals/debmake-doc/ch06.en.html#ftn.idp1797
echo -e $"""#!/usr/bin/make -f
%:
\tdh \$@
\n
override_dh_auto_install:
\tinstall -D -m 0755 gitmastery-${VERSION}-linux-${ARCHITECTURE} debian/gitmastery/usr/bin/gitmastery
""" > debian/rules
echo """usr/bin
""" > debian/gitmastery.dirs
mkdir -p debian/source
echo """gitmastery-${VERSION}-linux-${ARCHITECTURE}
""" > debian/source/include-binaries
cat debian/rules
# Build the package
dpkg-buildpackage -us -uc -a ${ARCHITECTURE}
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: gitmastery_${{ env.VERSION }}-1_${{ env.ARCHITECTURE }}.deb
tag_name: ${{ needs.prepare.outputs.ref_name }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
debian-publish-apt:
needs: [prepare, debian-build]
if: needs.prepare.outputs.should_publish == 'true'
permissions: write-all
uses: git-mastery/gitmastery-apt-repo/.github/workflows/debian-apt-repo.yml@main
with:
version: ${{ needs.prepare.outputs.ref_name }}
secrets: inherit
arch-build:
needs: prepare
if: needs.prepare.outputs.should_publish == 'true'
runs-on: ubuntu-latest
env:
ARCHITECTURE: amd64
VERSION_NUMBER: ${{ needs.prepare.outputs.version_number }}
FILENAME: gitmastery-${{ needs.prepare.outputs.version_number }}-arch-amd64
REF_NAME: ${{ needs.prepare.outputs.ref_name }}
steps:
- name: Checkout source
uses: actions/checkout@v6
- name: Build binary
run: |
echo "__version__ = \"$REF_NAME\"" > app/version.py
docker run --rm \
-v $PWD:/pkg \
archlinux:base-devel \
bash -c "
pacman -Sy --noconfirm python python-pip openssl git &&
cd /pkg &&
python -m venv venv &&
source venv/bin/activate &&
pip install -r requirements.txt &&
pyinstaller --onefile main.py --name $FILENAME --distpath /pkg/dist
"
# Fix file ownership after Docker run
sudo chown -R $(id -u):$(id -g) .
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: dist/${{ env.FILENAME }}
tag_name: ${{ env.REF_NAME }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish package as artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.FILENAME }}
path: dist/${{ env.FILENAME }}
arch-publish:
# Since Arch linux currently only supports x86_64 out of the box, we will focus
# on supporting that first
needs: [prepare, arch-build]
if: needs.prepare.outputs.should_publish == 'true'
runs-on: ubuntu-latest
env:
ARCHITECTURE: amd64
VERSION: ${{ needs.prepare.outputs.version_number }}
environment: Main
steps:
- name: Checkout source
uses: actions/checkout@v6
with:
path: "app"
fetch-depth: 0
- name: Set environment variables
env:
REF_NAME: ${{ needs.prepare.outputs.ref_name }}
run: |
# Get the tag's commit message
cd app/
CHANGELOG_MESSAGE=$(git show ${REF_NAME} --no-patch --pretty=format:%s)
echo "CHANGELOG_MESSAGE=${CHANGELOG_MESSAGE}" >> $GITHUB_ENV
- name: Setup SSH for Github Actions
env:
AUR_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: |
mkdir -p ~/.ssh
echo "${AUR_PRIVATE_KEY}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts
# TODO: Maybe swap to a SoC specific account
git config --global user.name "Jiahao, Woo"
git config --global user.email "woojiahao1234@gmail.com"
git config --global init.defaultBranch master
- name: Create AUR package repository
run: git clone ssh://aur@aur.archlinux.org/gitmastery-bin.git aur-pkg
- name: Publish to AUR
env:
RELEASE_AMD64_URL: https://github.com/git-mastery/app/releases/download/${{ github.ref_name }}/gitmastery-${{ env.VERSION }}-arch-amd64
REF_NAME: ${{ needs.prepare.outputs.ref_name }}
run: |
cd aur-pkg
BINARY_NAME=gitmastery-${VERSION}-linux-${ARCHITECTURE}
echo -e $"""$CHANGELOG_MESSAGE
\n""" >> gitmastery.changelog
cat gitmastery.changelog
echo """# Maintainer: Jiahao, Woo <woojiahao1234@gmail.com>
pkgname=gitmastery-bin
pkgver=\"$REF_NAME\"
pkgrel=1
pkgdesc=\"Git-Mastery CLI for practicing Git\"
arch=('x86_64')
url='https://github.com/git-mastery/app'
license=('MIT')
depends=(
'python'
)
changelog=\"gitmastery.changelog\"
source=(\"${BINARY_NAME}::${RELEASE_AMD64_URL}\")
sha256sums=('SKIP')
package() {
install -D -m 0755 \"\$srcdir/$BINARY_NAME\" \"\$pkgdir/usr/bin/gitmastery\"
chmod 755 \"\$pkgdir/usr/bin/gitmastery\"
}
""" >> PKGBUILD
cat PKGBUILD
# Generate the .SRCINFO within a Docker container
# We attach the current directory (aur-pkg) as the pkg directory volume
docker run --rm \
-v $PWD:/pkg \
archlinux:base-devel \
bash -c "
pacman -Sy --noconfirm sudo git base-devel && \
useradd -m builder && \
echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers && \
chown -R builder:builder /pkg && \
su builder -c 'cd /pkg && makepkg --printsrcinfo > .SRCINFO'
"
# Fix file ownership after Docker run
sudo chown -R $(id -u):$(id -g) .
git add .
git commit -m "Update package"
git push --force
windows:
needs: prepare
if: needs.prepare.outputs.should_publish == 'true'
runs-on: windows-latest
steps:
- name: Checkout source
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Build binary
shell: pwsh
env:
REF_NAME: ${{ needs.prepare.outputs.ref_name }}
run: |
$version_content = '__version__ = "{0}"' -f $env:REF_NAME
$version_content | Out-File -FilePath app/version.py -Encoding utf8
pyinstaller --onefile --name gitmastery main.py
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: dist/gitmastery.exe
tag_name: ${{ needs.prepare.outputs.ref_name }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
macos-build:
needs: prepare
if: needs.prepare.outputs.should_publish == 'true'
# We use macos-15-intel since it's the latest image that currently supports AMD64
strategy:
matrix:
include:
- os: macos-15-intel
arch: amd64
- os: macos-latest
arch: arm64
runs-on: ${{ matrix.os }}
outputs:
sha256-arm64: ${{ steps.checksum-arm64.outputs.sha256 }}
sha256-amd64: ${{ steps.checksum-amd64.outputs.sha256 }}
env:
ARCHITECTURE: ${{ matrix.arch }}
REF_NAME: ${{ needs.prepare.outputs.ref_name }}
steps:
- name: Checkout source
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Build binary
run: |
echo "__version__ = \"$REF_NAME\"" > app/version.py
pyinstaller --onefile --name "gitmastery-$ARCHITECTURE" main.py
- name: Generate SHA256 (amd64)
if: matrix.arch == 'amd64'
id: checksum-amd64
run: |
FILENAME=gitmastery-amd64
SHA256=$(shasum -a 256 dist/$FILENAME | cut -d ' ' -f1)
echo "sha256=$SHA256" >> $GITHUB_OUTPUT
- name: Generate SHA256 (arm64)
if: matrix.arch == 'arm64'
id: checksum-arm64
run: |
FILENAME=gitmastery-arm64
SHA256=$(shasum -a 256 dist/$FILENAME | cut -d ' ' -f1)
echo "sha256=$SHA256" >> $GITHUB_OUTPUT
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: dist/gitmastery-${{ matrix.arch }}
tag_name: ${{ needs.prepare.outputs.ref_name }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
macos-publish:
needs: [prepare, macos-build]
if: needs.prepare.outputs.should_publish == 'true'
runs-on: ubuntu-latest
steps:
- name: Update Homebrew Tap
env:
GH_TOKEN: ${{ secrets.ORG_PAT }}
REF_NAME: ${{ needs.prepare.outputs.ref_name }}
VERSION_NUMBER: ${{ needs.prepare.outputs.version_number }}
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git clone https://x-access-token:${GH_TOKEN}@github.com/git-mastery/homebrew-gitmastery.git
cd homebrew-gitmastery
cat <<EOF > gitmastery.rb
class Gitmastery < Formula
desc "CLI tool for Git-Mastery"
homepage "https://github.com/git-mastery/cli"
version "$VERSION_NUMBER"
on_arm do
url "https://github.com/git-mastery/cli/releases/download/${REF_NAME}/gitmastery-arm64"
sha256 "${{ needs.macos-build.outputs.sha256-arm64 }}"
end
on_intel do
url "https://github.com/git-mastery/cli/releases/download/${REF_NAME}/gitmastery-amd64"
sha256 "${{ needs.macos-build.outputs.sha256-amd64 }}"
end
def install
if Hardware::CPU.arm?
chmod 0755, "gitmastery-arm64"
bin.install "gitmastery-arm64" => "gitmastery"
else
chmod 0755, "gitmastery-amd64"
bin.install "gitmastery-amd64" => "gitmastery"
end
end
test do
system "#{bin}/gitmastery", "--help"
end
end
EOF
git remote set-url origin https://x-access-token:${GH_TOKEN}@github.com/git-mastery/homebrew-gitmastery.git
git remote -v
git add gitmastery.rb
git commit -m "Update to ${REF_NAME}"
git push origin main
macos-test:
strategy:
matrix:
os: [macos-15-intel, macos-latest]
runs-on: ${{ matrix.os }}
needs: macos-publish
steps:
- run: |
brew tap git-mastery/gitmastery
brew install gitmastery
file "$(brew --prefix)/bin/gitmastery"
gitmastery --help