Manual Release #1
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: 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 |