Add msgvault backup: init, create, list, verify, and restore commands… #51
Workflow file for this run
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: Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| permissions: | |
| contents: write | |
| jobs: | |
| build-linux: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - goarch: amd64 | |
| runner: ubuntu-latest | |
| go_arch_name: amd64 | |
| - goarch: arm64 | |
| runner: ubuntu-24.04-arm | |
| go_arch_name: arm64 | |
| runs-on: ${{ matrix.runner }} | |
| container: | |
| image: ubuntu:22.04 | |
| steps: | |
| - name: Install build tools | |
| env: | |
| DEBIAN_FRONTEND: noninteractive | |
| run: | | |
| apt-get update | |
| # libsqlite3-dev provides sqlite3.h, which sqlite-vec's CGo | |
| # binding includes unconditionally with -DSQLITE_CORE. mattn's | |
| # sqlite3 driver still links its own bundled amalgamation at | |
| # runtime; the header is only needed at compile time. | |
| apt-get install -y gcc g++ make git curl tar gzip file binutils libsqlite3-dev | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Install Go | |
| run: | | |
| GO_VERSION=$(grep '^go ' go.mod | awk '{print $2}') | |
| curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${{ matrix.go_arch_name }}.tar.gz" -o go.tar.gz | |
| tar -C /usr/local -xzf go.tar.gz | |
| rm go.tar.gz | |
| echo "/usr/local/go/bin" >> "$GITHUB_PATH" | |
| echo "$HOME/go/bin" >> "$GITHUB_PATH" | |
| - name: Build | |
| env: | |
| GOOS: linux | |
| GOARCH: ${{ matrix.goarch }} | |
| CGO_ENABLED: '1' | |
| run: | | |
| export PATH="/usr/local/go/bin:$HOME/go/bin:$PATH" | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| mkdir -p dist | |
| LDFLAGS="-s -w -X go.kenn.io/msgvault/cmd/msgvault/cmd.Version=v${VERSION} -X go.kenn.io/msgvault/cmd/msgvault/cmd.Commit=$(printf '%s' "$GITHUB_SHA" | cut -c1-8) -X go.kenn.io/msgvault/cmd/msgvault/cmd.BuildDate=$(date -u +%Y-%m-%dT%H:%M:%SZ) -extldflags '-lstdc++ -lm'" | |
| go build -tags "fts5 sqlite_vec" -trimpath -buildvcs=false -ldflags="$LDFLAGS" -o dist/msgvault ./cmd/msgvault | |
| echo "--- Binary info ---" | |
| file dist/msgvault | |
| ldd dist/msgvault || true | |
| # Verify runtime version requirements are reasonable. DuckDB's | |
| # prebuilt static library still links against libstdc++ symbols. | |
| echo "--- Runtime requirements ---" | |
| objdump -T dist/msgvault 2>/dev/null | grep -oP 'GLIBC_\d+\.\d+' | sort -uV | tail -1 || true | |
| objdump -T dist/msgvault 2>/dev/null | grep -oP 'GLIBCXX_\d+\.\d+(\.\d+)?' | sort -uV | tail -1 || true | |
| # Smoke test | |
| echo "--- Smoke test ---" | |
| SMOKE_OUT=$(dist/msgvault version 2>&1) | |
| echo "$SMOKE_OUT" | |
| echo "$SMOKE_OUT" | grep -q "v${VERSION}" || { echo "FATAL: version output doesn't match tag"; exit 1; } | |
| cd dist | |
| ARCHIVE="msgvault_${VERSION}_linux_${{ matrix.goarch }}.tar.gz" | |
| tar czf "$ARCHIVE" msgvault | |
| rm msgvault | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: msgvault-linux-${{ matrix.goarch }} | |
| path: dist/*.tar.gz | |
| build-darwin: | |
| strategy: | |
| matrix: | |
| include: | |
| - goarch: amd64 | |
| runner: macos-15 | |
| - goarch: arm64 | |
| runner: macos-15 | |
| runs-on: ${{ matrix.runner }} | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version-file: go.mod | |
| - name: Build | |
| env: | |
| GOOS: darwin | |
| GOARCH: ${{ matrix.goarch }} | |
| CGO_ENABLED: 1 | |
| run: | | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| mkdir -p dist | |
| LDFLAGS="-s -w -X go.kenn.io/msgvault/cmd/msgvault/cmd.Version=v${VERSION} -X go.kenn.io/msgvault/cmd/msgvault/cmd.Commit=$(printf '%s' "$GITHUB_SHA" | cut -c1-8) -X go.kenn.io/msgvault/cmd/msgvault/cmd.BuildDate=$(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| go build -tags "fts5 sqlite_vec" -trimpath -ldflags="$LDFLAGS" -o dist/msgvault ./cmd/msgvault | |
| echo "--- Binary info ---" | |
| file dist/msgvault | |
| # Verify no libstdc++ dependency (macOS should only link libc++ and system libs) | |
| echo "--- Dynamic libraries ---" | |
| otool -L dist/msgvault | |
| if otool -L dist/msgvault | grep -q 'libstdc++'; then | |
| echo "FATAL: binary links libstdc++ — this will cause compatibility issues" | |
| exit 1 | |
| fi | |
| # Smoke test — only for native arch (amd64 is cross-compiled from arm64) | |
| NATIVE_ARCH=$(uname -m) | |
| if [ "${{ matrix.goarch }}" = "arm64" ] && [ "$NATIVE_ARCH" = "arm64" ]; then | |
| echo "--- Smoke test ---" | |
| SMOKE_OUT=$(dist/msgvault version 2>&1) | |
| echo "$SMOKE_OUT" | |
| echo "$SMOKE_OUT" | grep -q "v${VERSION}" || { echo "FATAL: version output doesn't match tag"; exit 1; } | |
| else | |
| echo "--- Smoke test skipped (cross-compiled) ---" | |
| fi | |
| cd dist | |
| ARCHIVE="msgvault_${VERSION}_darwin_${{ matrix.goarch }}.tar.gz" | |
| tar czf "$ARCHIVE" msgvault | |
| rm msgvault | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: msgvault-darwin-${{ matrix.goarch }} | |
| path: dist/*.tar.gz | |
| build-windows: | |
| runs-on: windows-latest | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version-file: go.mod | |
| # sqlite-vec's CGo bindings need sqlite3.h at compile time and | |
| # arrow-go/v18's cdata helpers need GNU89 inline semantics on | |
| # MinGW; see .github/workflows/ci.yml for details. | |
| - name: Install sqlite3 dev headers | |
| shell: pwsh | |
| run: | | |
| C:\msys64\usr\bin\pacman.exe -S --noconfirm --needed mingw-w64-x86_64-sqlite3 | |
| - name: Build | |
| env: | |
| GOOS: windows | |
| GOARCH: amd64 | |
| CGO_ENABLED: '1' | |
| CGO_CFLAGS: "-IC:/msys64/mingw64/include -fgnu89-inline" | |
| CGO_LDFLAGS: "-Wl,--allow-multiple-definition" | |
| run: | | |
| VERSION="${GITHUB_REF#refs/tags/v}" | |
| mkdir -p dist | |
| LDFLAGS="-s -w -X go.kenn.io/msgvault/cmd/msgvault/cmd.Version=v${VERSION} -X go.kenn.io/msgvault/cmd/msgvault/cmd.Commit=$(printf '%s' "$GITHUB_SHA" | cut -c1-8) -X go.kenn.io/msgvault/cmd/msgvault/cmd.BuildDate=$(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| go build -tags "fts5 sqlite_vec" -trimpath -ldflags="$LDFLAGS" -o dist/msgvault.exe ./cmd/msgvault | |
| # Smoke test | |
| echo "--- Smoke test ---" | |
| SMOKE_OUT=$(dist/msgvault.exe version 2>&1) | |
| echo "$SMOKE_OUT" | |
| echo "$SMOKE_OUT" | grep -q "v${VERSION}" || { echo "FATAL: version output doesn't match tag"; exit 1; } | |
| # Package as zip | |
| ARCHIVE="msgvault_${VERSION}_windows_amd64.zip" | |
| cd dist | |
| powershell -Command "Compress-Archive -Path msgvault.exe -DestinationPath '$ARCHIVE'" | |
| rm msgvault.exe | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: msgvault-windows-amd64 | |
| path: dist/*.zip | |
| release: | |
| needs: [build-linux, build-darwin, build-windows] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| path: artifacts | |
| merge-multiple: true | |
| - name: Validate artifacts | |
| run: | | |
| echo "--- Validating all archives ---" | |
| for archive in artifacts/*.tar.gz; do | |
| echo "Checking $archive..." | |
| tmpdir=$(mktemp -d) | |
| tar -xzf "$archive" -C "$tmpdir" | |
| test -f "$tmpdir/msgvault" || { echo "FATAL: msgvault binary missing from $archive"; exit 1; } | |
| test -x "$tmpdir/msgvault" || { echo "FATAL: msgvault binary not executable in $archive"; exit 1; } | |
| # Smoke test linux amd64 artifact (runs natively on ubuntu runner) | |
| if echo "$archive" | grep -q "linux_amd64"; then | |
| "$tmpdir/msgvault" version > /tmp/smoke_out.txt 2>&1 | |
| cat /tmp/smoke_out.txt | |
| VERSION="${GITHUB_REF#refs/tags/v}" | |
| grep -q "v${VERSION}" /tmp/smoke_out.txt || { echo "FATAL: extracted linux_amd64 binary version mismatch"; exit 1; } | |
| echo " linux_amd64 smoke: OK" | |
| fi | |
| file "$tmpdir/msgvault" | |
| rm -rf "$tmpdir" | |
| echo " OK" | |
| done | |
| for archive in artifacts/*.zip; do | |
| [ -e "$archive" ] || continue | |
| echo "Checking $archive..." | |
| tmpdir=$(mktemp -d) | |
| unzip -q "$archive" -d "$tmpdir" | |
| test -f "$tmpdir/msgvault.exe" || { echo "FATAL: msgvault.exe missing from $archive"; exit 1; } | |
| file "$tmpdir/msgvault.exe" | |
| rm -rf "$tmpdir" | |
| echo " OK" | |
| done | |
| - name: Generate checksums | |
| run: | | |
| cd artifacts | |
| sha256sum *.tar.gz *.zip > SHA256SUMS | |
| cat SHA256SUMS | |
| - name: Get tag message | |
| id: tag_message | |
| run: | | |
| TAG_NAME="${GITHUB_REF#refs/tags/}" | |
| TAG_TYPE=$(git cat-file -t "$TAG_NAME") | |
| if [ "$TAG_TYPE" != "tag" ]; then | |
| echo "Warning: $TAG_NAME is a lightweight tag, using auto-generated notes" | |
| echo "has_body=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| TAG_MSG=$(git tag -l --format='%(contents:body)' "$TAG_NAME") | |
| if [ -n "$TAG_MSG" ]; then | |
| DELIM="TAGMSG_$(date +%s%N)" | |
| echo "body<<$DELIM" >> $GITHUB_OUTPUT | |
| echo "$TAG_MSG" >> $GITHUB_OUTPUT | |
| echo "$DELIM" >> $GITHUB_OUTPUT | |
| echo "has_body=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_body=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Create Release | |
| uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 | |
| with: | |
| files: | | |
| artifacts/*.tar.gz | |
| artifacts/*.zip | |
| artifacts/SHA256SUMS | |
| body: ${{ steps.tag_message.outputs.has_body == 'true' && steps.tag_message.outputs.body || '' }} | |
| generate_release_notes: ${{ steps.tag_message.outputs.has_body != 'true' }} |