diff --git a/.github/scripts/update-homebrew-tap.sh b/.github/scripts/update-homebrew-tap.sh new file mode 100644 index 00000000..216000e7 --- /dev/null +++ b/.github/scripts/update-homebrew-tap.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +# Update the Homebrew tap at Hmbown/homebrew-deepseek-tui after a release. +# +# Expected environment: +# TAG – git tag, e.g. "v0.8.31" +# MANIFEST – path to deepseek-artifacts-sha256.txt +# TAP_REPO – owner/repo of the Homebrew tap +# TOKEN – PAT with contents:write on TAP_REPO (optional; skips if unset) + +set -euo pipefail + +: "${TAG:?}" +: "${MANIFEST:?}" +: "${TAP_REPO:?}" + +if [ -z "${TOKEN:-}" ]; then + echo "No Homebrew tap token configured; skipping." + exit 0 +fi + +VERSION="${TAG#v}" + +die() { echo "::error::${1}" >&2; exit 1; } + +sha() { + local file="${1:?}" + local val + val="$(awk -v f="${file}" '$2 == f {print $1; exit}' "${MANIFEST}")" + if [ -z "${val}" ]; then + die "Missing binary in checksum manifest: ${file}" + fi + echo "${val}" +} + +# --- read checksums --------------------------------------------------- + +readonly SHA_DISP_MACOS_ARM="$(sha deepseek-macos-arm64)" +readonly SHA_TUI_MACOS_ARM="$(sha deepseek-tui-macos-arm64)" +readonly SHA_DISP_MACOS_X64="$(sha deepseek-macos-x64)" +readonly SHA_TUI_MACOS_X64="$(sha deepseek-tui-macos-x64)" +readonly SHA_DISP_LINUX_ARM="$(sha deepseek-linux-arm64)" +readonly SHA_TUI_LINUX_ARM="$(sha deepseek-tui-linux-arm64)" +readonly SHA_DISP_LINUX_X64="$(sha deepseek-linux-x64)" +readonly SHA_TUI_LINUX_X64="$(sha deepseek-tui-linux-x64)" + +# --- temp dirs -------------------------------------------------------- + +FORMULA_FILE="$(mktemp)" +TAP_DIR="$(mktemp -d)" +trap 'rm -rf "${TAP_DIR}" "${FORMULA_FILE}"' EXIT + +# --- generate formula -------------------------------------------------- + +readonly BASE_URL="https://github.com/Hmbown/DeepSeek-TUI/releases/download/${TAG}" + +cat > "${FORMULA_FILE}" << EOF +class DeepseekTui < Formula + desc "Terminal-native coding agent for DeepSeek V4" + homepage "https://github.com/Hmbown/DeepSeek-TUI" + version "${VERSION}" + license "MIT" + + on_macos do + if Hardware::CPU.arm? + url "${BASE_URL}/deepseek-macos-arm64", using: :nounzip + sha256 "${SHA_DISP_MACOS_ARM}" + resource "tui" do + url "${BASE_URL}/deepseek-tui-macos-arm64", using: :nounzip + sha256 "${SHA_TUI_MACOS_ARM}" + end + else + url "${BASE_URL}/deepseek-macos-x64", using: :nounzip + sha256 "${SHA_DISP_MACOS_X64}" + resource "tui" do + url "${BASE_URL}/deepseek-tui-macos-x64", using: :nounzip + sha256 "${SHA_TUI_MACOS_X64}" + end + end + end + + on_linux do + if Hardware::CPU.arm? + url "${BASE_URL}/deepseek-linux-arm64", using: :nounzip + sha256 "${SHA_DISP_LINUX_ARM}" + resource "tui" do + url "${BASE_URL}/deepseek-tui-linux-arm64", using: :nounzip + sha256 "${SHA_TUI_LINUX_ARM}" + end + else + url "${BASE_URL}/deepseek-linux-x64", using: :nounzip + sha256 "${SHA_DISP_LINUX_X64}" + resource "tui" do + url "${BASE_URL}/deepseek-tui-linux-x64", using: :nounzip + sha256 "${SHA_TUI_LINUX_X64}" + end + end + end + + def install + bin.install Dir["*"].first => "deepseek" + resource("tui").stage { bin.install Dir["*"].first => "deepseek-tui" } + end + + test do + system "#{bin}/deepseek", "--version" + end +end +EOF + +# --- push to tap repo -------------------------------------------------- + +ENCODED_TOKEN="$(printf '%s' "${TOKEN}" | python3 -c 'import sys,urllib.parse;print(urllib.parse.quote(sys.stdin.read(),safe=""))')" +TAP_URL="https://x-access-token:${ENCODED_TOKEN}@github.com/${TAP_REPO}.git" + +git clone --depth 1 "${TAP_URL}" "${TAP_DIR}" + +mkdir -p "${TAP_DIR}/Formula" +cp "${FORMULA_FILE}" "${TAP_DIR}/Formula/deepseek-tui.rb" + +cd "${TAP_DIR}" +git config user.name "github-actions[bot]" +git config user.email "github-actions[bot]@users.noreply.github.com" + +git add Formula/deepseek-tui.rb + +if git diff --cached --quiet; then + echo "Formula unchanged (already at ${VERSION}); nothing to push." + exit 0 +fi + +git commit -m "chore: bump formula to ${VERSION} + +Automated update from the release workflow." + +git push origin HEAD:main +echo "Pushed formula update to ${TAP_REPO} (v${VERSION})" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 50290a25..43810df4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -333,6 +333,48 @@ jobs: See [CHANGELOG.md](https://github.com/Hmbown/DeepSeek-TUI/blob/main/CHANGELOG.md) for the full notes for this release. + homebrew: + needs: [release, resolve] + if: ${{ !cancelled() && needs.release.result == 'success' }} + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Check Homebrew tap token + id: homebrew-token + env: + TOKEN: ${{ secrets.HOMEBREW_TAP_PAT || secrets.RELEASE_TAG_PAT }} + run: | + if [ -z "${TOKEN:-}" ]; then + echo "No Homebrew tap token configured; skipping tap update." + echo "available=false" >> "${GITHUB_OUTPUT}" + else + echo "available=true" >> "${GITHUB_OUTPUT}" + fi + # Checkout main (not the tag) so the release-infra script is always + # available, even for tags created before this workflow was added. + - uses: actions/checkout@v4 + if: steps.homebrew-token.outputs.available == 'true' + with: + ref: main + - name: Download checksum manifest + if: steps.homebrew-token.outputs.available == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release download ${{ needs.resolve.outputs.tag }} \ + --repo ${{ github.repository }} \ + --pattern 'deepseek-artifacts-sha256.txt' \ + --dir /tmp + - name: Update Homebrew tap + if: steps.homebrew-token.outputs.available == 'true' + env: + TAG: ${{ needs.resolve.outputs.tag }} + MANIFEST: /tmp/deepseek-artifacts-sha256.txt + TAP_REPO: Hmbown/homebrew-deepseek-tui + TOKEN: ${{ secrets.HOMEBREW_TAP_PAT || secrets.RELEASE_TAG_PAT }} + run: bash .github/scripts/update-homebrew-tap.sh + # npm publish is intentionally not automated. The npm account requires 2FA OTP # on every publish, and a granular automation token that bypasses 2FA has not # been provisioned. Release the npm wrapper manually from a developer machine diff --git a/.gitignore b/.gitignore index b53f8acf..bb019b13 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ docs/*.pdf # Local dev scripts and temp files *.sh !scripts/** +!.github/scripts/** test.txt TODO*.md todo*.md