Skip to content

Manual Release

Manual Release #1

name: Manual Release
on:
workflow_dispatch:
inputs:
version:
description: 'Version number (e.g., 1.0.0)'
required: true
type: string
prerelease:
description: 'Mark as pre-release'
required: false
default: false
type: boolean
draft:
description: 'Create as draft'
required: false
default: true
type: boolean
permissions:
contents: write
jobs:
validate:
name: Validate Release
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
version_with_v: ${{ steps.version.outputs.version_with_v }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set version outputs
id: version
run: |
VERSION="${{ github.event.inputs.version }}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "version_with_v=v${VERSION}" >> $GITHUB_OUTPUT
- name: Validate version format
run: |
VERSION="${{ github.event.inputs.version }}"
if ! echo "$VERSION" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?$'; then
echo "Error: Invalid version format. Please use semantic versioning (e.g., 1.0.0 or 1.0.0-beta.1)"
exit 1
fi
- name: Check if tag exists
run: |
VERSION="v${{ github.event.inputs.version }}"
if git rev-parse "$VERSION" >/dev/null 2>&1; then
echo "Error: Tag $VERSION already exists"
exit 1
fi
echo "Tag $VERSION does not exist, proceeding..."
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.22"
- name: Run tests
run: |
go test -v -race ./...
build:
name: Build Release Binaries
needs: validate
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
goos: linux
goarch: amd64
name: linux-amd64
ext: ""
- os: macos-latest
goos: darwin
goarch: amd64
name: darwin-amd64
ext: ""
- os: macos-latest
goos: darwin
goarch: arm64
name: darwin-arm64
ext: ""
- os: windows-latest
goos: windows
goarch: amd64
name: windows-amd64
ext: ".exe"
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.22"
- name: Setup Windows build environment
if: matrix.goos == 'windows'
shell: bash
run: |
# Install MSYS2 for complete build environment
choco install msys2 -y
# Initialize MSYS2 and install required packages
C:/tools/msys64/usr/bin/bash -lc 'pacman --noconfirm -S mingw-w64-x86_64-gcc mingw-w64-x86_64-pkg-config mingw-w64-x86_64-sqlite3'
# Add MSYS2 MinGW64 to PATH
echo "C:\\tools\\msys64\\mingw64\\bin" >> $GITHUB_PATH
# Verify installation
C:/tools/msys64/mingw64/bin/gcc --version || echo "GCC not found"
- name: Build binary
shell: bash
run: |
VERSION="${{ needs.validate.outputs.version }}"
BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S')
GIT_COMMIT=$(git rev-parse --short HEAD)
mkdir -p dist
if [ "${{ matrix.goos }}" = "windows" ]; then
# Set up CGO environment for Windows with MSYS2
export CGO_ENABLED=1
export CC=C:/tools/msys64/mingw64/bin/gcc.exe
export CXX=C:/tools/msys64/mingw64/bin/g++.exe
export PKG_CONFIG_PATH="C:/tools/msys64/mingw64/lib/pkgconfig"
export PATH="C:/tools/msys64/mingw64/bin:$PATH"
fi
CGO_ENABLED=1 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build \
-ldflags "-X main.Version=${VERSION} -X main.BuildTime=${BUILD_TIME} -X main.GitCommit=${GIT_COMMIT}" \
-o dist/ml-notes-${{ matrix.name }}${{ matrix.ext }} .
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ml-notes-${{ matrix.name }}
path: dist/ml-notes-${{ matrix.name }}${{ matrix.ext }}
retention-days: 1
release:
name: Create Manual Release
needs: [validate, build]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create release archives
run: |
VERSION="v${{ needs.validate.outputs.version }}"
mkdir -p dist/release
# Move artifacts to dist directory and create archives
find artifacts -name "ml-notes-*" -exec cp {} dist/ \;
# Linux AMD64
if [ -f "dist/ml-notes-linux-amd64" ]; then
tar -czf dist/release/ml-notes-${VERSION}-linux-amd64.tar.gz -C dist ml-notes-linux-amd64
fi
# macOS AMD64 (Intel)
if [ -f "dist/ml-notes-darwin-amd64" ]; then
tar -czf dist/release/ml-notes-${VERSION}-darwin-amd64.tar.gz -C dist ml-notes-darwin-amd64
fi
# macOS ARM64 (Apple Silicon)
if [ -f "dist/ml-notes-darwin-arm64" ]; then
tar -czf dist/release/ml-notes-${VERSION}-darwin-arm64.tar.gz -C dist ml-notes-darwin-arm64
fi
# Windows AMD64
if [ -f "dist/ml-notes-windows-amd64.exe" ]; then
cd dist && zip release/ml-notes-${VERSION}-windows-amd64.zip ml-notes-windows-amd64.exe
cd ..
fi
# Create checksums
cd dist/release
sha256sum * > checksums.txt
# Create installation README
cat > README.md << EOF
# ML Notes ${VERSION} - Release Packages
## Download the right package for your platform:
### Linux
- **Linux x64**: \`ml-notes-${VERSION}-linux-amd64.tar.gz\`
### macOS
- **macOS Intel**: \`ml-notes-${VERSION}-darwin-amd64.tar.gz\`
- **macOS Apple Silicon**: \`ml-notes-${VERSION}-darwin-arm64.tar.gz\`
### Windows
- **Windows x64**: \`ml-notes-${VERSION}-windows-amd64.zip\`
## Installation
### Linux/macOS
1. Download the appropriate \`.tar.gz\` file
2. Extract: \`tar -xzf ml-notes-${VERSION}-<platform>.tar.gz\`
3. Install: \`sudo mv ml-notes-<platform> /usr/local/bin/ml-notes\`
4. Initialize: \`ml-notes init\`
### Windows
1. Download \`ml-notes-${VERSION}-windows-amd64.zip\`
2. Extract the ZIP file
3. Add the extracted directory to your PATH
4. Initialize: \`ml-notes init\`
## Verification
Verify your installation:
\`\`\`bash
ml-notes --version
\`\`\`
## Checksums
Verify download integrity using \`checksums.txt\`:
\`\`\`bash
sha256sum -c checksums.txt
\`\`\`
EOF
- name: Generate changelog
id: changelog
run: |
VERSION="v${{ github.event.inputs.version }}"
# Get the previous tag
PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
echo "## Release $VERSION" > CHANGELOG.md
echo "" >> CHANGELOG.md
echo "### What's Changed" >> CHANGELOG.md
echo "" >> CHANGELOG.md
if [ -z "$PREV_TAG" ]; then
echo "Initial release! 🎉" >> CHANGELOG.md
else
# Generate commit list since last tag
git log ${PREV_TAG}..HEAD --pretty=format:"* %s (%h)" >> CHANGELOG.md
echo "" >> CHANGELOG.md
echo "" >> CHANGELOG.md
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...${VERSION}" >> CHANGELOG.md
fi
# Output for release body
echo "changelog<<EOF" >> $GITHUB_OUTPUT
cat CHANGELOG.md >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ github.event.inputs.version }}
name: v${{ github.event.inputs.version }}
body: ${{ steps.changelog.outputs.changelog }}
draft: ${{ github.event.inputs.draft }}
prerelease: ${{ github.event.inputs.prerelease }}
files: |
dist/release/*.tar.gz
dist/release/*.zip
dist/release/checksums.txt
dist/release/README.md
fail_on_unmatched_files: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Summary
run: |
VERSION="v${{ github.event.inputs.version }}"
echo "## Release Created Successfully! 🎉" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Version**: $VERSION" >> $GITHUB_STEP_SUMMARY
echo "- **Draft**: ${{ github.event.inputs.draft }}" >> $GITHUB_STEP_SUMMARY
echo "- **Pre-release**: ${{ github.event.inputs.prerelease }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Next Steps" >> $GITHUB_STEP_SUMMARY
if [ "${{ github.event.inputs.draft }}" = "true" ]; then
echo "1. Review the draft release at: https://github.com/${{ github.repository }}/releases" >> $GITHUB_STEP_SUMMARY
echo "2. Edit the release notes if needed" >> $GITHUB_STEP_SUMMARY
echo "3. Publish the release when ready" >> $GITHUB_STEP_SUMMARY
else
echo "The release has been published and is now available at:" >> $GITHUB_STEP_SUMMARY
echo "https://github.com/${{ github.repository }}/releases/tag/$VERSION" >> $GITHUB_STEP_SUMMARY
fi