name: Release on: push: tags: - 'v*' permissions: contents: write jobs: build: strategy: fail-fast: false matrix: include: - platform: macos-latest target: aarch64-apple-darwin - platform: macos-15-intel target: x86_64-apple-darwin - platform: ubuntu-22.04 target: x86_64-unknown-linux-gnu - platform: windows-latest target: x86_64-pc-windows-msvc runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 20 - name: Setup pnpm uses: pnpm/action-setup@v4 with: version: 9 - name: Install frontend dependencies run: pnpm install - name: Setup Rust uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.target }} - name: Compute Rust dependency hash id: deps-hash shell: bash run: | { find . -name Cargo.toml -not -path './target/*' -print0 \ | sort -z \ | xargs -0 sed -E '/^version = /d' grep -v '^version = ' Cargo.lock } | sha256sum | cut -d' ' -f1 | { read -r hash echo "hash=${hash:0:20}" >> "$GITHUB_OUTPUT" } - name: Rust cache uses: swatinem/rust-cache@v2 with: workspaces: './ -> target' shared-key: release-${{ matrix.target }}-${{ steps.deps-hash.outputs.hash }} add-rust-environment-hash-key: false cache-on-failure: true - name: Install system dependencies (macOS) if: startsWith(matrix.platform, 'macos') run: brew install unixodbc - name: Bundle ODBC library (macOS) if: startsWith(matrix.platform, 'macos') run: | ODBC_LIB="$(brew --prefix unixodbc)/lib/libodbc.2.dylib" LTDL_LIB="$(brew --prefix libtool)/lib/libltdl.7.dylib" install_name_tool -id "@rpath/libodbc.2.dylib" "$ODBC_LIB" install_name_tool -id "@rpath/libltdl.7.dylib" "$LTDL_LIB" install_name_tool -change "$LTDL_LIB" "@rpath/libltdl.7.dylib" "$ODBC_LIB" cp "$ODBC_LIB" "$LTDL_LIB" src-tauri/ - name: Install Apple certificate (macOS) if: startsWith(matrix.platform, 'macos') env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} run: | CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12 KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db echo -n "$APPLE_CERTIFICATE" | base64 --decode -o $CERTIFICATE_PATH security create-keychain -p "" $KEYCHAIN_PATH security set-keychain-settings -lut 21600 $KEYCHAIN_PATH security unlock-keychain -p "" $KEYCHAIN_PATH security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH security set-key-partition-list -S apple-tool:,apple: -k "" $KEYCHAIN_PATH security list-keychains -d user -s $KEYCHAIN_PATH login.keychain-db - name: Install system dependencies (Linux) if: startsWith(matrix.platform, 'ubuntu') run: | sudo apt-get update sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev unixodbc-dev - name: Setup Tauri signing key shell: bash run: | echo "${{ secrets.TAURI_SIGNING_PRIVATE_KEY_BASE64 }}" | base64 --decode > "$RUNNER_TEMP/updater.key" KEY_B64=$(base64 < "$RUNNER_TEMP/updater.key" | tr -d '\r\n') echo "TAURI_SIGNING_PRIVATE_KEY=$KEY_B64" >> "$GITHUB_ENV" if [ -n "${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}" ]; then echo "TAURI_SIGNING_PRIVATE_KEY_PASSWORD=${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}" >> "$GITHUB_ENV" fi - name: Build Tauri app uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} APPLE_SIGNING_IDENTITY: ${{ startsWith(matrix.platform, 'macos') && secrets.APPLE_SIGNING_IDENTITY || '' }} APPLE_ID: ${{ startsWith(matrix.platform, 'macos') && secrets.APPLE_ID || '' }} APPLE_PASSWORD: ${{ startsWith(matrix.platform, 'macos') && secrets.APPLE_PASSWORD || '' }} APPLE_TEAM_ID: ${{ startsWith(matrix.platform, 'macos') && secrets.APPLE_TEAM_ID || '' }} with: tagName: ${{ github.ref_name }} releaseName: 'DBX ${{ github.ref_name }}' releaseBody: 'See the assets below to download and install.' releaseDraft: true prerelease: false args: --target ${{ matrix.target }} - name: Upload Windows portable ZIP if: matrix.platform == 'windows-latest' shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | $version = "${env:GITHUB_REF_NAME}".TrimStart("v") $portableRoot = "portable" $portableDir = Join-Path $portableRoot "DBX_${version}_x64" $zipName = "DBX_${version}_x64-portable.zip" $exePath = "target/${{ matrix.target }}/release/dbx.exe" if (!(Test-Path $exePath)) { Write-Error "Missing Windows executable: $exePath" exit 1 } New-Item -ItemType Directory -Force -Path $portableDir | Out-Null Copy-Item $exePath (Join-Path $portableDir "DBX.exe") -Force Copy-Item "LICENSE" (Join-Path $portableDir "LICENSE") -Force Copy-Item "README.md" (Join-Path $portableDir "README.md") -Force Compress-Archive -Path (Join-Path $portableDir "*") -DestinationPath $zipName -Force gh release upload "${env:GITHUB_REF_NAME}" $zipName --repo "${env:GITHUB_REPOSITORY}" --clobber publish: needs: [build, docker-manifest] runs-on: ubuntu-latest steps: - name: Publish draft release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: gh release edit ${{ github.ref_name }} --repo ${{ github.repository }} --draft=false --prerelease packages: needs: publish runs-on: ubuntu-latest env: APP_NAME: dbx PRODUCT_NAME: DBX REPO_OWNER: t8y2 steps: - name: Extract version from tag id: version run: | TAG="${GITHUB_REF_NAME}" VERSION="${TAG#v}" if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then echo "::error::Tag '${TAG}' does not look like a semver version." exit 1 fi echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - name: Download release assets env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mkdir -p artifacts VERSION="${{ steps.version.outputs.version }}" TAG="${{ steps.version.outputs.tag }}" ASSETS=( "${PRODUCT_NAME}_${VERSION}_aarch64.dmg" "${PRODUCT_NAME}_${VERSION}_x64.dmg" "${PRODUCT_NAME}_${VERSION}_x64-setup.exe" ) for ASSET in "${ASSETS[@]}"; do echo "::group::Downloading ${ASSET}" gh release download "${TAG}" \ --repo "${{ github.repository }}" \ --pattern "${ASSET}" \ --dir artifacts \ --clobber echo "::endgroup::" done ls -lh artifacts/ - name: Compute SHA256 hashes id: hashes run: | VERSION="${{ steps.version.outputs.version }}" compute_hash() { local file="$1" name="$2" if [[ ! -f "${file}" ]]; then echo "::error::Missing artifact: ${file}" exit 1 fi local hash hash=$(sha256sum "${file}" | cut -d ' ' -f 1) echo "${name}=${hash}" >> "$GITHUB_OUTPUT" echo " ${name}: ${hash}" } echo "SHA256 hashes:" compute_hash "artifacts/${PRODUCT_NAME}_${VERSION}_aarch64.dmg" "sha_dmg_arm64" compute_hash "artifacts/${PRODUCT_NAME}_${VERSION}_x64.dmg" "sha_dmg_x64" compute_hash "artifacts/${PRODUCT_NAME}_${VERSION}_x64-setup.exe" "sha_exe" - name: Publish Homebrew Cask env: TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} run: | VERSION="${{ steps.version.outputs.version }}" SHA_ARM64="${{ steps.hashes.outputs.sha_dmg_arm64 }}" SHA_X64="${{ steps.hashes.outputs.sha_dmg_x64 }}" git clone --depth 1 \ "https://x-access-token:${TAP_GITHUB_TOKEN}@github.com/${REPO_OWNER}/homebrew-tap.git" \ homebrew-tap cd homebrew-tap mkdir -p Casks cat > Casks/dbx.rb <<'RUBY_EOF' cask "dbx" do version "__VERSION__" on_arm do url "https://github.com/__OWNER__/__APP__/releases/download/v#{version}/__PRODUCT___#{version}_aarch64.dmg", verified: "github.com/__OWNER__/__APP__/" sha256 "__SHA_ARM64__" end on_intel do url "https://github.com/__OWNER__/__APP__/releases/download/v#{version}/__PRODUCT___#{version}_x64.dmg", verified: "github.com/__OWNER__/__APP__/" sha256 "__SHA_X64__" end name "DBX" desc "Open-source database management tool" homepage "https://github.com/__OWNER__/__APP__" livecheck do url :url strategy :github_latest end depends_on macos: ">= :big_sur" app "DBX.app" zap trash: [ "~/Library/Application Support/com.dbx.app", "~/Library/Caches/com.dbx.app", "~/Library/Preferences/com.dbx.app.plist", "~/Library/Logs/com.dbx.app", ] end RUBY_EOF sed -i 's/^ //' Casks/dbx.rb sed -i "s/__VERSION__/${VERSION}/g" Casks/dbx.rb sed -i "s/__OWNER__/${REPO_OWNER}/g" Casks/dbx.rb sed -i "s/__APP__/${APP_NAME}/g" Casks/dbx.rb sed -i "s/__PRODUCT__/${PRODUCT_NAME}/g" Casks/dbx.rb sed -i "s/__SHA_ARM64__/${SHA_ARM64}/g" Casks/dbx.rb sed -i "s/__SHA_X64__/${SHA_X64}/g" Casks/dbx.rb git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add Casks/dbx.rb if git diff --cached --quiet; then echo "Homebrew Cask is already up to date." else git commit -m "dbx ${VERSION}" git push echo "::notice::Homebrew Cask updated to ${VERSION}" fi - name: Publish Scoop manifest env: TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} run: | VERSION="${{ steps.version.outputs.version }}" SHA_EXE="${{ steps.hashes.outputs.sha_exe }}" git clone --depth 1 \ "https://x-access-token:${TAP_GITHUB_TOKEN}@github.com/${REPO_OWNER}/scoop-bucket.git" \ scoop-bucket cd scoop-bucket cat > dbx.json <> "$GITHUB_OUTPUT" - name: Create manifest list and push working-directory: /tmp/digests run: | docker buildx imagetools create \ -t ${{ secrets.DOCKERHUB_USERNAME }}/dbx:${{ steps.version.outputs.version }} \ -t ${{ secrets.DOCKERHUB_USERNAME }}/dbx:latest \ $(printf '${{ secrets.DOCKERHUB_USERNAME }}/dbx@sha256:%s ' *)