diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 686b68c..4d005cb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,23 +2,41 @@ name: Build on: push: + workflow_dispatch: + inputs: + version: + description: 'Version to build (e.g., 1.0.0)' + required: false + type: string jobs: - build: + build-linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: 'Get Previous tag' - id: previoustag - uses: "WyriHaximus/github-action-get-previous-tag@v1" - - name: Set version + - name: Determine version + id: version run: | - export VERSION=${{ steps.previoustag.outputs.tag }} + # For manual runs (workflow_dispatch) allow explicit version; otherwise use the ref name + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + VERSION="${{ github.event.inputs.version }}" + else + VERSION="${{ github.ref_name }}" + fi + # strip any leading v + VERSION="${VERSION#v}" + echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT + + - name: Show chosen version + run: | + echo "Resolved VERSION=${{ steps.version.outputs.VERSION }}" - name: Setup tools - run: sudo apt-get install pandoc + run: sudo apt-get install -y pandoc - name: Set up Go uses: actions/setup-go@v5 with: go-version: '1.21' - name: Build - run: VERSION="${{ steps.previoustag.outputs.tag }}" make all + run: VERSION="${{ steps.version.outputs.VERSION }}" make all + + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 158ac52..ddf5dd6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,112 +4,127 @@ on: push: tags: - '*' + jobs: - build: + build-binaries: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: 'Get Previous tag' - id: previoustag - uses: "WyriHaximus/github-action-get-previous-tag@v1" + - name: Get tag version + id: version + run: | + VERSION="${{ github.ref_name }}" + # Remove leading 'v' if present + VERSION="${VERSION#v}" + echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT + + - name: Show chosen version + run: | + echo "Resolved VERSION=${{ steps.version.outputs.VERSION }}" + - name: Setup tools - run: sudo apt-get install pandoc + run: sudo apt-get install -y pandoc + - name: Set up Go uses: actions/setup-go@v5 with: go-version: '1.21' - - name: Build - run: | - VERSION="${{ steps.previoustag.outputs.tag }}" make all - VERSION="${{ steps.previoustag.outputs.tag }}" make deb - - name: Release - uses: actions/create-release@v1 - id: create_release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - draft: true - prerelease: false - release_name: ${{ steps.previoustag.outputs.tag }} - tag_name: ${{ steps.previoustag.outputs.tag }} - - name: upload linux amd64 artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./mdview-${{ steps.previoustag.outputs.tag }}-linux-amd64.tar.gz - asset_name: mdview-${{ steps.previoustag.outputs.tag }}-linux-amd64.tar.gz - asset_content_type: application/gzip - - name: upload linux i386 artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./mdview-${{ steps.previoustag.outputs.tag }}-linux-i386.tar.gz - asset_name: mdview-${{ steps.previoustag.outputs.tag }}-linux-i386.tar.gz - asset_content_type: application/gzip - - name: upload linux arm64 artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./mdview-${{ steps.previoustag.outputs.tag }}-linux-arm64.tar.gz - asset_name: mdview-${{ steps.previoustag.outputs.tag }}-linux-arm64.tar.gz - asset_content_type: application/gzip - - name: upload darwin amd64 artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./mdview-${{ steps.previoustag.outputs.tag }}-darwin-amd64.tar.gz - asset_name: mdview-${{ steps.previoustag.outputs.tag }}-darwin-amd64.tar.gz - asset_content_type: application/gzip - - name: upload darwin arm64 artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} + + - name: Build all binaries + run: VERSION=${{ steps.version.outputs.VERSION }} make all deb + + - name: Upload artifacts + uses: actions/upload-artifact@v4 with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./mdview-${{ steps.previoustag.outputs.tag }}-darwin-arm64.tar.gz - asset_name: mdview-${{ steps.previoustag.outputs.tag }}-darwin-arm64.tar.gz - asset_content_type: application/gzip - - name: upload windows amd64 artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} + name: binaries + path: | + mdview-*.tar.gz + mdview-*.zip + mdview_*.deb + + build-rpm: + runs-on: ubuntu-latest + container: + image: fedora:latest + steps: + - name: Install dependencies + run: | + dnf install -y \ + rpm-build \ + golang \ + pandoc \ + make \ + git \ + tar \ + gzip + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Mark repo safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Get tag version + id: version + run: | + VERSION="${{ github.ref_name }}" + VERSION="${VERSION#v}" + echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT + + - name: Show chosen version + run: | + echo "Resolved VERSION=${{ steps.version.outputs.VERSION }}" + + - name: Build RPM package + run: VERSION=${{ steps.version.outputs.VERSION }} make rpm + + - name: Copy RPMs to workspace + run: | + mkdir -p dist + cp ${HOME}/rpmbuild/RPMS/x86_64/mdview-*.x86_64.rpm dist/ || true + cp ${HOME}/rpmbuild/SRPMS/mdview-*.src.rpm dist/ || true + ls -lh dist/ + + - name: Upload RPM artifacts + uses: actions/upload-artifact@v4 with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./mdview-${{ steps.previoustag.outputs.tag }}-windows-amd64.zip - asset_name: mdview-${{ steps.previoustag.outputs.tag }}-windows-amd64.zip - asset_content_type: application/zip - - name: upload freebsd amd64 artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} + name: rpm-packages + path: dist/mdview-*.rpm + + release: + needs: [build-binaries, build-rpm] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Get tag version + id: version + run: | + VERSION="${{ github.ref_name }}" + VERSION="${VERSION#v}" + echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT + + - name: Show chosen version + run: | + echo "Resolved VERSION=${{ steps.version.outputs.VERSION }}" + + - name: Download all artifacts + uses: actions/download-artifact@v4 with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./mdview-${{ steps.previoustag.outputs.tag }}-freebsd-amd64.tar.gz - asset_name: mdview-${{ steps.previoustag.outputs.tag }}-freebsd-amd64.tar.gz - asset_content_type: application/gzip - - name: upload linux amd64 deb - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} + path: release-files + + - name: Prepare release files + run: | + mkdir -p release + cp release-files/binaries/* release/ || true + cp release-files/rpm-packages/* release/ || true + ls -lh release/ + + - name: Create Release + uses: softprops/action-gh-release@v1 with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./mdview_${{ steps.previoustag.outputs.tag }}_amd64.deb - asset_name: mdview_${{ steps.previoustag.outputs.tag }}_amd64.deb - asset_content_type: application/vnd.debian.binary-package - - name: upload linux arm64 deb - uses: actions/upload-release-asset@v1 + files: release/* + draft: false + prerelease: false env: - GITHUB_TOKEN: ${{ github.token }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./mdview_${{ steps.previoustag.outputs.tag }}_arm64.deb - asset_name: mdview_${{ steps.previoustag.outputs.tag }}_arm64.deb - asset_content_type: application/vnd.debian.binary-package \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8736479..f7f5fba 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ parts/ package/* *.deb mdview.1 +dist/ diff --git a/Makefile b/Makefile index 9e29a3e..8776dbb 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ MANSECTION ?= 1 SHELL=/bin/bash -.PHONY: clean snap +# Sanitize VERSION for use in filenames and tar transform expressions +# Replace forward slashes with dashes to avoid issues with tar --transform +VERSION_SAFE = $(subst /,-,$(VERSION)) +.PHONY: clean snap rpm rpm-setup rpm-local rpm-clean ci-sim-ubuntu ci-sim-fedora ci-sim default: linux all: linux windows darwin freebsd @@ -20,7 +23,7 @@ deb/linux-amd64: bin/linux-amd64/mdview cp -r bin/linux-amd64/mdview package/usr/bin/mdview cp mdview.1 package/usr/share/man/man1/ dpkg-deb --build package - mv package.deb mdview_$(VERSION)_amd64.deb + mv package.deb mdview_$(VERSION_SAFE)_amd64.deb deb/linux-arm64: bin/linux-arm64/mdview mkdir -p package/DEBIAN @@ -31,44 +34,44 @@ deb/linux-arm64: bin/linux-arm64/mdview cp -r bin/linux-arm64/mdview package/usr/bin/mdview cp mdview.1 package/usr/share/man/man1/ dpkg-deb --build package - mv package.deb mdview_$(VERSION)_arm64.deb + mv package.deb mdview_$(VERSION_SAFE)_arm64.deb snap: snapcraft pack bin/linux-amd64/mdview: manpage - env GOOS=linux GOARCH=amd64 go build -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/linux-amd64/mdview + env GOOS=linux GOARCH=amd64 go build -buildvcs=false -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/linux-amd64/mdview cp mdview.1 bin/linux-amd64/ - tar czvf mdview-$(VERSION)-linux-amd64.tar.gz --transform s/linux-amd64/mdview-$(VERSION)/ -C bin linux-amd64 + tar czvf mdview-$(VERSION_SAFE)-linux-amd64.tar.gz --transform 's,^linux-amd64,mdview-$(VERSION_SAFE),' -C bin linux-amd64 bin/linux-i386/mdview: - env GOOS=linux GOARCH=386 go build -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/linux-i386/mdview + env GOOS=linux GOARCH=386 go build -buildvcs=false -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/linux-i386/mdview cp mdview.1 bin/linux-i386/ - tar czvf mdview-$(VERSION)-linux-i386.tar.gz --transform s/linux-i386/mdview-$(VERSION)/ -C bin linux-i386 + tar czvf mdview-$(VERSION_SAFE)-linux-i386.tar.gz --transform 's,^linux-i386,mdview-$(VERSION_SAFE),' -C bin linux-i386 bin/linux-arm64/mdview: - env GOOS=linux GOARCH=arm64 go build -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/linux-arm64/mdview + env GOOS=linux GOARCH=arm64 go build -buildvcs=false -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/linux-arm64/mdview cp mdview.1 bin/linux-arm64/ - tar czvf mdview-$(VERSION)-linux-arm64.tar.gz --transform s/linux-arm64/mdview-$(VERSION)/ -C bin linux-arm64 + tar czvf mdview-$(VERSION_SAFE)-linux-arm64.tar.gz --transform 's,^linux-arm64,mdview-$(VERSION_SAFE),' -C bin linux-arm64 bin/windows-amd64/mdview.exe: - env GOOS=windows GOARCH=amd64 go build -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/windows-amd64/mdview.exe - zip -j mdview-$(VERSION)-windows-amd64.zip bin/windows-amd64/mdview.exe + env GOOS=windows GOARCH=amd64 go build -buildvcs=false -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/windows-amd64/mdview.exe + zip -j mdview-$(VERSION_SAFE)-windows-amd64.zip bin/windows-amd64/mdview.exe bin/darwin-amd64/mdview: - env GOOS=darwin GOARCH=amd64 go build -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/darwin-amd64/mdview + env GOOS=darwin GOARCH=amd64 go build -buildvcs=false -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/darwin-amd64/mdview cp mdview.1 bin/darwin-amd64/ - tar czvf mdview-$(VERSION)-darwin-amd64.tar.gz --transform s/darwin-amd64/mdview-$(VERSION)/ -C bin darwin-amd64 + tar czvf mdview-$(VERSION_SAFE)-darwin-amd64.tar.gz --transform 's,^darwin-amd64,mdview-$(VERSION_SAFE),' -C bin darwin-amd64 bin/darwin-arm64/mdview: - env GOOS=darwin GOARCH=arm64 go build -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/darwin-arm64/mdview + env GOOS=darwin GOARCH=arm64 go build -buildvcs=false -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/darwin-arm64/mdview cp mdview.1 bin/darwin-arm64/ - tar czvf mdview-$(VERSION)-darwin-arm64.tar.gz --transform s/darwin-arm64/mdview-$(VERSION)/ -C bin darwin-arm64 + tar czvf mdview-$(VERSION_SAFE)-darwin-arm64.tar.gz --transform 's,^darwin-arm64,mdview-$(VERSION_SAFE),' -C bin darwin-arm64 bin/freebsd-amd64/mdview: - env GOOS=freebsd GOARCH=amd64 go build -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/freebsd-amd64/mdview + env GOOS=freebsd GOARCH=amd64 go build -buildvcs=false -ldflags "-X main.appVersion=$(VERSION)" -o ./bin/freebsd-amd64/mdview cp mdview.1 bin/freebsd-amd64/mdview - tar czvf mdview-$(VERSION)-freebsd-amd64.tar.gz --transform s/freebsd-amd64/mdview-$(VERSION)/ -C bin freebsd-amd64 + tar czvf mdview-$(VERSION_SAFE)-freebsd-amd64.tar.gz --transform 's,^freebsd-amd64,mdview-$(VERSION_SAFE),' -C bin freebsd-amd64 clean: rm -rf bin @@ -82,4 +85,47 @@ clean: # snapcraft clean mdview -s pull manpage: - pandoc --standalone --to man mdview.1.md -o mdview.1 \ No newline at end of file + pandoc --standalone --to man mdview.1.md -o mdview.1 + +# RPM Packaging targets +rpm-setup: + @mkdir -p $(HOME)/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} + @echo '%_topdir %{getenv:HOME}/rpmbuild' > $(HOME)/.rpmmacros + +rpm: rpm-setup manpage bin/linux-amd64/mdview + @echo "Building RPM for version $(VERSION)" + @mkdir -p $(HOME)/rpmbuild/SOURCES + @mkdir -p $(HOME)/rpmbuild/SPECS + # sanitize VERSION for RPM (Version field cannot contain some chars like '-') + RPM_VERSION=$(shell echo "$(VERSION)" | sed 's/[^A-Za-z0-9._]/./g') ; \ + echo "Using RPM version: $$RPM_VERSION (from $(VERSION))" ; \ + git archive --prefix=mdview-$$RPM_VERSION/ -o $(HOME)/rpmbuild/SOURCES/mdview-$$RPM_VERSION.tar.gz HEAD ; \ + cp mdview.spec $(HOME)/rpmbuild/SPECS/ ; \ + rpmbuild -ba -D "_version $$RPM_VERSION" $(HOME)/rpmbuild/SPECS/mdview.spec ; \ + echo "RPM build complete. Output in $(HOME)/rpmbuild/RPMS/" + +rpm-local: rpm + @mkdir -p dist + cp $(HOME)/rpmbuild/RPMS/x86_64/mdview-*-*.x86_64.rpm dist/ 2>/dev/null || true + cp $(HOME)/rpmbuild/SRPMS/mdview-*-*.src.rpm dist/ 2>/dev/null || true + @ls -lh dist/mdview-*-*.rpm 2>/dev/null || echo "No RPMs found in dist/" + +rpm-clean: + rm -rf $(HOME)/rpmbuild + rm -rf dist/mdview-*.rpm + +# Docker-based CI simulation targets +DOCKER_UBUNTU_IMAGE ?= ubuntu:24.04 +DOCKER_FEDORA_IMAGE ?= fedora:43 + +.PHONY: ci-sim-ubuntu ci-sim-fedora ci-sim +ci-sim-ubuntu: + @echo "Running Ubuntu CI simulation in Docker (image: $(DOCKER_UBUNTU_IMAGE))" + # Only include necessary files to avoid leaking sensitive data + +ci-sim-fedora: + @echo "Running Fedora RPM CI simulation in Docker (image: $(DOCKER_FEDORA_IMAGE))" + # Only include necessary files to avoid leaking sensitive data + +ci-sim: ci-sim-ubuntu ci-sim-fedora + @echo "Docker CI simulation finished" \ No newline at end of file diff --git a/PACKAGING.md b/PACKAGING.md new file mode 100644 index 0000000..687ab7d --- /dev/null +++ b/PACKAGING.md @@ -0,0 +1,262 @@ +# Building Packages for mdview + +This guide covers building Debian (.deb) and RPM packages for mdview, both locally and via GitHub Actions. + +## Prerequisites + +### For Debian Packages (Ubuntu/Debian) +```bash +sudo apt-get install -y build-essential golang pandoc fakeroot dpkg-dev +``` + +### For RPM Packages (Fedora) +```bash +sudo dnf install -y rpm-build golang pandoc make git +``` + +### Cross-platform with Docker +You can build packages on any system using containers. See "CI Simulation" section below. + +## Building Locally + +### Debian Packages + +Build all Debian packages (amd64, arm64, i386): +```bash +make deb VERSION=1.6.4 +``` + +Find built packages in `dist/`: +- `mdview_1.6.4_amd64.deb` +- `mdview_1.6.4_arm64.deb` +- `mdview_1.6.4_i386.deb` + +### RPM Packages + +Build RPM packages (x86_64, src.rpm): +```bash +make rpm VERSION=1.6.4 +``` + +Built packages appear in: +- Binary RPM: `~/rpmbuild/RPMS/x86_64/mdview-*.x86_64.rpm` +- Source RPM: `~/rpmbuild/SRPMS/mdview-*.src.rpm` + +Alternatively, build and copy to `dist/`: +```bash +make rpm-local VERSION=1.6.4 +``` + +**Note:** Use `X.Y.Z` format (e.g., `1.6.4`), not `vX.Y.Z`. + +### Available Makefile Targets + +| Target | Purpose | +|--------|---------| +| `make deb VERSION=X.Y.Z` | Build all Debian packages | +| `make rpm VERSION=X.Y.Z` | Build RPM packages in `~/rpmbuild/` | +| `make rpm-local VERSION=X.Y.Z` | Build RPM and copy to `dist/` | +| `make rpm-setup` | Initialize RPM build environment | +| `make rpm-clean` | Clean RPM build artifacts | +| `make ci-sim-ubuntu` | Simulate Ubuntu CI build locally | +| `make ci-sim-fedora` | Simulate Fedora CI build locally | +| `make ci-sim` | Run both CI simulations | + +## Installing Built Packages + +### Debian +```bash +sudo dpkg -i dist/mdview_*.deb +# Or +sudo apt install ./dist/mdview_*.deb +``` + +### RPM +```bash +sudo dnf install ~/rpmbuild/RPMS/x86_64/mdview-*.rpm +# Or for local dist/ packages +sudo dnf install ./dist/mdview-*.rpm +``` + +## Verify Installation + +```bash +which mdview +mdview --version +man mdview +``` + +The package installs: +- Binary: `/usr/bin/mdview` +- Man page: `/usr/share/man/man1/mdview.1.gz` + +## CI Simulation + +You can simulate GitHub Actions builds locally using containers. This is useful for testing changes before pushing. + +### Simulate Ubuntu Build +```bash +make ci-sim-ubuntu VERSION=1.6.4 +``` + +This runs the full Ubuntu build process in a container: +- Installs dependencies (including Go 1.21.1) +- Builds binaries +- Creates Debian packages + +### Simulate Fedora RPM Build +```bash +make ci-sim-fedora VERSION=1.6.4 +``` + +This runs the Fedora RPM build in a container: +- Installs RPM build tools +- Builds RPM packages +- Outputs to `dist/` + +### Simulate Both +```bash +make ci-sim VERSION=1.6.4 +``` + +These commands stream your repository into containers to avoid permission issues with volume mounts. + +## GitHub Actions Automation + +### Automatic Builds on Release + +When you push a git tag (e.g., `v1.6.4`), the release workflow automatically: + +1. Builds binaries for all platforms: + - Linux (amd64, arm64, i386) + - Windows (amd64, i386) + - Darwin/macOS (amd64, arm64) + - FreeBSD (amd64) + +2. Builds packages: + - Debian packages (amd64, arm64, i386) + - RPM packages (x86_64, src.rpm) + +3. Creates a GitHub release with all artifacts + +**Trigger a release:** +```bash +git tag v1.6.4 +git push origin v1.6.4 +``` + +### Build Workflow + +The build workflow runs on every push to validate: +- All platform binaries compile successfully +- No build or test failures + +It does **not** build packages or create releases on regular pushes. + +## Package Specifications + +### Debian Package Structure + +Controlled by files in `package/DEBIAN/`: +- `control` - Package metadata, dependencies, description +- Files installed via `package/usr/` directory structure + +The Makefile creates architecture-specific packages by copying files and adjusting the control file. + +### RPM Package Structure + +Controlled by `mdview.spec`: +- Metadata, dependencies, build requirements +- Build steps (compile binary, generate manpage) +- Install steps (copy to /usr/bin and /usr/share/man) + +The spec file uses `%{_version}` macro which is passed at build time. + +## Troubleshooting + +### Debian Build Issues + +**Missing fakeroot:** +```bash +sudo apt-get install fakeroot +``` + +**Architecture not supported:** +Check if your system has the cross-compilation tools: +```bash +sudo apt-get install gcc-aarch64-linux-gnu # for arm64 +sudo apt-get install gcc-arm-linux-gnueabi # for arm +``` + +### RPM Build Issues + +**pandoc not found:** +```bash +sudo dnf install pandoc +``` + +**Go version too old:** +mdview requires Go 1.21+. On older Fedora versions: +```bash +sudo dnf install golang +go version # verify >= 1.21 +``` + +**rpmbuild directory not initialized:** +```bash +make rpm-setup +``` + +### CI Simulation Issues + +**Docker not available:** +Ensure Docker is installed and running: +```bash +docker --version +sudo systemctl start docker +``` + +**Permission denied:** +Add your user to the docker group: +```bash +sudo usermod -aG docker $USER +newgrp docker +``` + +**Go version mismatch in Ubuntu container:** +The `ci-sim-ubuntu` script automatically installs Go 1.21.1 if needed. + +## Publishing to Repositories + +### Fedora COPR + +To make RPM packages available via `dnf install mdview`: + +1. Create account at https://copr.fedorainfracloud.org/ +2. Create a new project +3. Upload spec file and source tarball +4. Enable builds for desired Fedora versions +5. Users can then add your COPR repo and install + +### Ubuntu PPA + +For Debian packages on Ubuntu: + +1. Create Launchpad account +2. Create PPA +3. Upload source package (requires signing with GPG key) +4. Launchpad builds for all Ubuntu versions + +### Package Manager Integration + +The project is already available via: +- **AUR** (Arch User Repository): `yay -S mdview` +- **deb-get**: `deb-get install mdview` +- **Snap**: `snap install mdview` + +## Version Handling + +- Git tags should use format `vX.Y.Z` (e.g., `v1.6.4`) +- Makefile VERSION parameter should use `X.Y.Z` (no `v` prefix) +- The build system automatically strips the `v` prefix where needed +- RPM VERSION is sanitized to replace invalid characters with dots diff --git a/README.md b/README.md index fe81ff2..9a1427b 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,24 @@ To remove the package: sudo dpkg --remove mdview ``` +### RPM Package (Fedora) + +If you're running Fedora or other RPM-based distributions, you can download and install the RPM package from the [Releases](https://github.com/mapitman/mdview/releases) page. + +```sh +curl -s https://api.github.com/repos/mapitman/mdview/releases/latest \ +| grep "browser_download_url.*x86_64.rpm" \ +| cut -d '"' -f 4 \ +| xargs curl -L -o mdview-latest.x86_64.rpm +sudo dnf install ./mdview-latest.x86_64.rpm +``` + +To remove the package: + +```sh +sudo dnf remove mdview +``` + ### Snap Package _Update: The snap package has been fixed and the latest version is now available as a snap._ 🥳 diff --git a/RELEASE_CHECKLIST.md b/RELEASE_CHECKLIST.md new file mode 100644 index 0000000..8ea9c36 --- /dev/null +++ b/RELEASE_CHECKLIST.md @@ -0,0 +1,149 @@ +# Release Checklist + +Use this checklist when preparing a new release. + +## Pre-Release + +- [ ] Ensure all code changes are committed +- [ ] Update version in code (if needed) +- [ ] Update CHANGELOG.md with release notes +- [ ] Review changes: `git log --oneline [previous-tag]..HEAD` +- [ ] All tests passing locally +- [ ] Test local builds (optional): + ```bash + make rpm-local VERSION=1.0.0 + make deb VERSION=1.0.0 + ``` + +## Build Verification (Optional) + +Test packages locally before releasing: + +- [ ] RPM installs without errors: + ```bash + sudo dnf install ./dist/mdview-*.rpm + ``` +- [ ] DEB installs without errors: + ```bash + sudo dpkg -i ./dist/mdview-*.deb + ``` +- [ ] Binary works: + ```bash + which mdview + mdview --version + mdview --help + ``` +- [ ] Man page is accessible: + ```bash + man mdview + ``` + +## Create Release Tag + +- [ ] Commit all changes +- [ ] Create tag: + ```bash + git tag -a v1.0.0 -m "Release v1.0.0" + ``` +- [ ] Verify tag: + ```bash + git show v1.0.0 + ``` +- [ ] Push tag: + ```bash + git push origin v1.0.0 + ``` + +## GitHub Actions Verification + +- [ ] Go to Actions tab +- [ ] Check "Release" workflow: + - [ ] build-binaries job completed + - [ ] build-rpm job completed + - [ ] release job completed +- [ ] All jobs passed (green checkmarks) +- [ ] No warnings or errors in logs + +## Release Page Verification + +- [ ] Go to Releases page +- [ ] Release created with correct version +- [ ] Check release contains: + - [ ] Binary distributions for all platforms (tar.gz, zip) + - [ ] RPM packages (.x86_64.rpm, .src.rpm) + - [ ] Debian packages (.deb files for amd64, arm64, i386) + - [ ] Release notes/description +- [ ] Test download and install: + ```bash + # For RPM + wget https://github.com/mapitman/mdview/releases/download/v1.0.0/mdview-1.0.0-1.fc*.x86_64.rpm + sudo dnf install mdview-1.0.0-1.fc*.x86_64.rpm + + # For DEB + wget https://github.com/mapitman/mdview/releases/download/v1.0.0/mdview_1.0.0_amd64.deb + sudo dpkg -i mdview_1.0.0_amd64.deb + + # Verify + mdview --version + ``` + +## Post-Release + +- [ ] Uninstall test version (if installed) +- [ ] Announce release (if applicable) +- [ ] Update README with new version info (if needed) +- [ ] Tag appears in GitHub releases list +- [ ] Local cleanup: `make rpm-clean` + +## Emergency Procedures + +### If workflow fails: +1. Check GitHub Actions logs for errors +2. Fix issues locally using `make ci-sim` or package-specific builds +3. Delete tag: `git tag -d v1.0.0` +4. Delete remote tag: `git push origin --delete v1.0.0` +5. Fix issues and retry + +### If release is incorrect: +1. Delete release from GitHub releases page +2. Delete tag: `git tag -d v1.0.0 && git push origin --delete v1.0.0` +3. Fix issues locally +4. Create new tag and push again + +### If package build fails but other builds succeed: +1. Check specific job logs in GitHub Actions +2. Test locally with CI simulation: `make ci-sim` +3. Fix issues and create new tag + +## Version Format Notes + +- Use semantic versioning: `v1.0.0`, `v1.0.1`, `v1.1.0`, etc. +- Git tags include `v` prefix +- Makefile commands use no prefix: `make rpm VERSION=1.0.0` +- RPM packages appear as `1.0.0-1.fc*.x86_64.rpm` +- DEB packages appear as `mdview_1.0.0_amd64.deb` + +## Rollback Procedure + +If released version has critical bugs: +1. Fix issues in code +2. Create new tag for patch: + ```bash + git tag v1.0.1 + git push origin v1.0.1 + ``` +3. Document issue in old release notes + +## Notes + +- All builds happen automatically in GitHub Actions on tag push +- Packages are built in containers for consistency +- Multiple output formats included in each release +- Consider using pre-release flag for testing +- See `PACKAGING.md` for detailed build instructions + +--- + +**Last Updated**: December 2024 +**For**: mdview project +**Maintainer**: mapitman diff --git a/build-rpm.sh b/build-rpm.sh new file mode 100755 index 0000000..b2a28e8 --- /dev/null +++ b/build-rpm.sh @@ -0,0 +1,251 @@ +#!/bin/bash +# mdview RPM build helper script + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +print_usage() { + cat << EOF +${BLUE}mdview RPM Build Helper${NC} + +Usage: $0 [COMMAND] [OPTIONS] + +Commands: + build [VERSION] Build RPM (default: latest git tag) + local [VERSION] Build and copy to dist/ (default: latest git tag) + setup Initialize RPM build environment + clean Clean all RPM build artifacts + info Show build information + help Show this help message + +Examples: + $0 build # Build using latest git tag + $0 build 1.0.0 # Build specific version + $0 local 1.0.0 # Build and copy to dist/ + $0 setup # Set up build environment + $0 clean # Clean build artifacts + $0 info # Show environment info + +EOF +} + +check_dependencies() { + # Command name -> Package name mapping for Fedora + declare -A deps=( + ["rpmbuild"]="rpm-build" + ["go"]="golang" + ["pandoc"]="pandoc" + ["make"]="make" + ["git"]="git" + ) + local missing_cmds=() + local missing_pkgs=() + + for cmd in "${!deps[@]}"; do + if ! command -v "$cmd" &> /dev/null; then + missing_cmds+=("$cmd") + missing_pkgs+=("${deps[$cmd]}") + fi + done + + if [ ${#missing_cmds[@]} -ne 0 ]; then + echo -e "${RED}Error: Missing required commands: ${missing_cmds[*]}${NC}" + echo + echo -e "${YELLOW}These commands are required but not found in your PATH.${NC}" + echo + echo -e "${YELLOW}To install on Fedora:${NC}" + echo " sudo dnf install -y ${missing_pkgs[*]}" + echo + echo -e "${YELLOW}If packages are already installed, ensure the commands are in your PATH.${NC}" + exit 1 + fi +} + +get_version() { + local provided_version="$1" + + if [ -n "$provided_version" ]; then + # Remove leading 'v' if present + echo "${provided_version#v}" + else + # Try to get from git tag + local tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "") + if [ -n "$tag" ]; then + echo "${tag#v}" + else + echo "dev" + fi + fi +} + +show_info() { + echo -e "${BLUE}mdview RPM Build Information${NC}" + echo + + check_dependencies + + echo -e "${GREEN}✓ Dependencies: OK${NC}" + echo + + echo "Environment:" + echo " RPM Build Dir: $HOME/rpmbuild/" + echo " .rpmmacros: $HOME/.rpmmacros" + echo + + # Get version + local version=$(get_version "") + echo "Version Information:" + echo " Current version: $version" + echo + + echo "Installed packages:" + rpmbuild --version 2>/dev/null || echo " rpmbuild: not found" + go version 2>/dev/null || echo " go: not found" + pandoc --version 2>/dev/null | head -1 || echo " pandoc: not found" + make --version 2>/dev/null | head -1 || echo " make: not found" +} + +build_rpm() { + local version="$1" + + check_dependencies + + echo -e "${BLUE}Building mdview RPM${NC}" + echo "Version: $version" + echo + + # Run make + if make rpm VERSION="$version"; then + echo + echo -e "${GREEN}✓ RPM build successful!${NC}" + echo + echo "Packages created in:" + echo " Binary RPM: $HOME/rpmbuild/RPMS/x86_64/" + echo " Source RPM: $HOME/rpmbuild/SRPMS/" + echo + + # Show what was built + if [ -d "$HOME/rpmbuild/RPMS/x86_64" ]; then + echo "Built packages:" + ls -lh "$HOME/rpmbuild/RPMS/x86_64/mdview-"* 2>/dev/null || echo " (none found)" + fi + if [ -d "$HOME/rpmbuild/SRPMS" ]; then + ls -lh "$HOME/rpmbuild/SRPMS/mdview-"* 2>/dev/null || true + fi + echo + echo -e "${YELLOW}To install:${NC}" + echo " sudo dnf install $HOME/rpmbuild/RPMS/x86_64/mdview-*.rpm" + else + echo -e "${RED}✗ RPM build failed!${NC}" + exit 1 + fi +} + +build_local() { + local version="$1" + + check_dependencies + + echo -e "${BLUE}Building mdview RPM (local mode)${NC}" + echo "Version: $version" + echo + + # Run make + if make rpm-local VERSION="$version"; then + echo + echo -e "${GREEN}✓ RPM build and copy successful!${NC}" + echo + + # Show what was copied + if [ -d "dist" ]; then + echo "Packages in dist/:" + ls -lh dist/mdview-* 2>/dev/null || echo " (none found)" + fi + echo + echo -e "${YELLOW}To install:${NC}" + echo " sudo dnf install ./dist/mdview-*.rpm" + else + echo -e "${RED}✗ RPM build failed!${NC}" + exit 1 + fi +} + +setup_environment() { + echo -e "${BLUE}Setting up RPM build environment${NC}" + + if make rpm-setup; then + echo -e "${GREEN}✓ RPM build environment setup complete!${NC}" + echo + echo "Directory structure created:" + echo " $HOME/rpmbuild/BUILD" + echo " $HOME/rpmbuild/RPMS" + echo " $HOME/rpmbuild/SOURCES" + echo " $HOME/rpmbuild/SPECS" + echo " $HOME/rpmbuild/SRPMS" + else + echo -e "${RED}✗ Setup failed!${NC}" + exit 1 + fi +} + +clean_artifacts() { + echo -e "${BLUE}Cleaning RPM build artifacts${NC}" + + if [ -d "$HOME/rpmbuild" ]; then + echo "Removing: $HOME/rpmbuild" + rm -rf "$HOME/rpmbuild" + echo -e "${GREEN}✓ Removed rpmbuild directory${NC}" + fi + + if [ -d "dist" ]; then + echo "Removing: dist/mdview-*.rpm" + rm -f dist/mdview-*.rpm + if [ -z "$(ls -A dist/ 2>/dev/null)" ]; then + rmdir dist 2>/dev/null || true + fi + echo -e "${GREEN}✓ Removed local dist files${NC}" + fi + + echo + echo -e "${GREEN}✓ Cleanup complete!${NC}" +} + +# Main +main() { + local command="${1:-help}" + local arg="${2:-}" + + case "$command" in + build) + build_rpm "$(get_version "$arg")" + ;; + local) + build_local "$(get_version "$arg")" + ;; + setup) + setup_environment + ;; + clean) + clean_artifacts + ;; + info) + show_info + ;; + help) + print_usage + ;; + *) + echo -e "${RED}Unknown command: $command${NC}" + print_usage + exit 1 + ;; + esac +} + +main "$@" diff --git a/mdview.1.md b/mdview.1.md index 30ac148..cc1d70a 100644 --- a/mdview.1.md +++ b/mdview.1.md @@ -1,4 +1,4 @@ -% MDVIEW(1) Version 1.4.0 | "Markdown View" Documentation +% MDVIEW(1) Version XXX | "Markdown View" Documentation # NAME diff --git a/mdview.spec b/mdview.spec new file mode 100644 index 0000000..2dfad9c --- /dev/null +++ b/mdview.spec @@ -0,0 +1,43 @@ +%define debug_package %{nil} + +Name: mdview +Version: %{_version} +Release: 1%{?dist} +Summary: Formats markdown and launches it in a browser + +License: MIT +URL: https://github.com/mapitman/mdview +Source0: %{name}-%{version}.tar.gz + +BuildRequires: golang >= 1.21 +BuildRequires: pandoc + +Requires: xdg-utils + +%description +Markdown View is a utility that formats markdown and launches it in your +default browser. It supports custom styling and can write output to a file. + +%prep +%setup -q + +%build +# Build the binary with version information +%{__make} VERSION=%{version} bin/linux-amd64/mdview + +%install +# Install the binary +%{__install} -Dp -m 0755 bin/linux-amd64/mdview %{buildroot}%{_bindir}/mdview + +# Install the man page +%{__install} -Dp -m 0644 mdview.1 %{buildroot}%{_mandir}/man1/mdview.1 + +%files +%doc README.md CHANGELOG.md +%license LICENSE +%{_bindir}/mdview +%{_mandir}/man1/mdview.1* + +%changelog +* %(date +"%a %b %d %Y") Mark Pitman - %{version}-1 +- Initial RPM package diff --git a/scripts/ci-sim-fedora.sh b/scripts/ci-sim-fedora.sh new file mode 100755 index 0000000..5b3152a --- /dev/null +++ b/scripts/ci-sim-fedora.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Minimal Fedora-based CI simulation for the RPM build job +# Mount point: repository is mounted at /workdir + +VERSION="${VERSION:-$(git describe --tags --abbrev=0 2>/dev/null || echo 1.6.4)}" +VERSION="${VERSION#v}" +echo "Using VERSION=${VERSION}" + +# Install required packages for building RPMs +# Ignore failures from 'dnf upgrade' as package upgrades are not critical for CI; required packages are installed explicitly below. +dnf -y upgrade --refresh || true +dnf install -y --setopt=tsflags=nodocs rpm-build golang make git pandoc rpmdevtools + +cd /workdir +export VERSION +git config --global --add safe.directory /workdir || true +make rpm-local diff --git a/scripts/ci-sim-ubuntu.sh b/scripts/ci-sim-ubuntu.sh new file mode 100755 index 0000000..3f0318c --- /dev/null +++ b/scripts/ci-sim-ubuntu.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Minimal Ubuntu-based CI simulation for the 'build' job +# Mount point: repository is mounted at /workdir + +VERSION="${VERSION:-$(git describe --tags --abbrev=0 2>/dev/null || echo 1.6.4)}" +VERSION="${VERSION#v}" +echo "Using VERSION=${VERSION}" + +# Ensure required system packages are present +apt-get update -y +apt-get install -y --no-install-recommends build-essential wget tar git pandoc make ca-certificates + +# Ensure a compatible Go version (go.mod requires 1.21.1) +GO_VERSION=1.21.1 +if command -v go >/dev/null 2>&1; then + INSTALLED=$(go version | awk '{print $3}' | sed 's/go//') +else + INSTALLED=0 +fi + +version_ge() { + # compare semantic-like versions, returns 0 if $1 >= $2 + printf "%s\n%s\n" "$1" "$2" | sort -V | tail -n1 | grep -qx "$1" +} + +if ! version_ge "$INSTALLED" "$GO_VERSION"; then + echo "Installing Go ${GO_VERSION}" + TARFILE=/tmp/go${GO_VERSION}.linux-amd64.tar.gz + CHECKSUM_FILE=/tmp/go${GO_VERSION}.linux-amd64.tar.gz.sha256 + wget -q "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" -O "$TARFILE" + wget -q "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz.sha256" -O "$CHECKSUM_FILE" + # Verify checksum + grep -oE '^[0-9a-f]+' "$CHECKSUM_FILE" | awk '{print $1 " " ENVIRON["TARFILE"]}' > "$CHECKSUM_FILE.checked" + sha256sum -c "$CHECKSUM_FILE.checked" + rm -rf /usr/local/go + tar -C /usr/local -xzf "$TARFILE" + export PATH=/usr/local/go/bin:$PATH +else + echo "Found Go ${INSTALLED}, OK" +fi + +cd /workdir +export VERSION +export PATH=/usr/local/go/bin:$PATH +make all