Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/release/ptd-cli.json.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": "@VERSION@",
"description": "CLI for PT-Depiler browser extension via Native Messaging",
"homepage": "https://github.com/@REPOSITORY@",
"license": "MIT",
"architecture": {"64bit": {"url": "@WINDOWS_X86_64_URL@", "hash": "@WINDOWS_X86_64_SHA@"}},
"bin": ["ptd.exe", "ptd-host.exe"],
"env_set": {"PTD_NATIVE_HOST_PATH": "$dir\\ptd-host.exe"},
"checkver": "github",
"autoupdate": {"architecture": {"64bit": {"url": "https://github.com/@REPOSITORY@/releases/download/v$version/ptd-cli-v$version-x86_64-pc-windows-msvc.zip"}}}
}
36 changes: 36 additions & 0 deletions .github/release/ptd-cli.rb.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class PtdCli < Formula
desc "CLI for PT-Depiler browser extension via Native Messaging"
homepage "https://github.com/@REPOSITORY@"
version "@VERSION@"
license "MIT"

on_macos do
if Hardware::CPU.arm?
url "@MACOS_ARM64_URL@"
sha256 "@MACOS_ARM64_SHA@"
else
url "@MACOS_X86_64_URL@"
sha256 "@MACOS_X86_64_SHA@"
end
end

on_linux do
if Hardware::CPU.arm?
url "@LINUX_ARM64_URL@"
sha256 "@LINUX_ARM64_SHA@"
else
url "@LINUX_X86_64_URL@"
sha256 "@LINUX_X86_64_SHA@"
end
end

def install
libexec.install "ptd", "ptd-host"
(bin/"ptd").write_env_script libexec/"ptd",
PTD_NATIVE_HOST_PATH: opt_libexec/"ptd-host"
end

test do
assert_match "ptd", shell_output("#{bin}/ptd --help")
end
end
32 changes: 32 additions & 0 deletions .github/release/render-manifests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail

VERSION="${1:?version is required}"
TAG="${2:?tag is required}"
REPOSITORY="${3:?repository is required}"
DIST="${4:?dist directory is required}"
OUTPUT="${GITHUB_WORKSPACE:-$(pwd)}/generated"
BASE_URL="https://github.com/${REPOSITORY}/releases/download/${TAG}"

sha() { sha256sum "$DIST/$1" | awk '{print $1}'; }
render() {
sed \
-e "s|@VERSION@|${VERSION}|g" \
-e "s|@REPOSITORY@|${REPOSITORY}|g" \
-e "s|@MACOS_ARM64_URL@|${BASE_URL}/ptd-cli-${TAG}-aarch64-apple-darwin.tar.gz|g" \
-e "s|@MACOS_ARM64_SHA@|$(sha "ptd-cli-${TAG}-aarch64-apple-darwin.tar.gz")|g" \
-e "s|@MACOS_X86_64_URL@|${BASE_URL}/ptd-cli-${TAG}-x86_64-apple-darwin.tar.gz|g" \
-e "s|@MACOS_X86_64_SHA@|$(sha "ptd-cli-${TAG}-x86_64-apple-darwin.tar.gz")|g" \
-e "s|@LINUX_ARM64_URL@|${BASE_URL}/ptd-cli-${TAG}-aarch64-unknown-linux-gnu.tar.gz|g" \
-e "s|@LINUX_ARM64_SHA@|$(sha "ptd-cli-${TAG}-aarch64-unknown-linux-gnu.tar.gz")|g" \
-e "s|@LINUX_X86_64_URL@|${BASE_URL}/ptd-cli-${TAG}-x86_64-unknown-linux-gnu.tar.gz|g" \
-e "s|@LINUX_X86_64_SHA@|$(sha "ptd-cli-${TAG}-x86_64-unknown-linux-gnu.tar.gz")|g" \
-e "s|@WINDOWS_X86_64_URL@|${BASE_URL}/ptd-cli-${TAG}-x86_64-pc-windows-msvc.zip|g" \
-e "s|@WINDOWS_X86_64_SHA@|$(sha "ptd-cli-${TAG}-x86_64-pc-windows-msvc.zip")|g" \
"$1" > "$2"
}

mkdir -p "$OUTPUT"
render .github/release/ptd-cli.rb.in "$OUTPUT/ptd-cli.rb"
render .github/release/ptd-cli.json.in "$OUTPUT/ptd-cli.json"
jq empty "$OUTPUT/ptd-cli.json"
196 changes: 161 additions & 35 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,189 @@ name: Release
on:
release:
types: [created]
workflow_dispatch:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This removes the existing release.created entry point and adds neither a release nor tag trigger, so creating a GitHub release through the previous workflow no longer builds or uploads anything. It also conflicts with the PR description's claim that updates happen on release/tag pushes. Please retain a compatible trigger, or obtain explicit maintainer consent for the breaking release-process migration and update the PR/release documentation accordingly. Verify the selected release event actually starts the workflow.

@NanamiMio NanamiMio Aug 25, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 48e3783. The compatible release.created trigger is restored, while manual dispatch remains build/test-only. I verified the event independently in the standalone release-test repository with a minimal workflow on its temporary default branch; the log records action=created and completed successfully: https://github.com/NanamiMio/ptd-cli-release-test/actions/runs/32801398025

inputs:
version:
description: Version without the v prefix (for example, 0.1.4)
required: true
type: string

permissions:
contents: write
contents: read

concurrency:
group: release-${{ github.event.release.tag_name || inputs.version }}
cancel-in-progress: false

jobs:
prepare:
runs-on: ubuntu-22.04
outputs:
source_sha: ${{ steps.source.outputs.sha }}
version: ${{ steps.source.outputs.version }}
tag: ${{ steps.source.outputs.tag }}
publish: ${{ steps.source.outputs.publish }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Validate release inputs
id: source
shell: bash
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
INPUT_VERSION: ${{ inputs.version }}
run: |
if [[ -n "$RELEASE_TAG" ]]; then
[[ "$RELEASE_TAG" == v* ]] || { echo "Release tag must start with v" >&2; exit 1; }
TAG="$RELEASE_TAG"
VERSION="${TAG#v}"
SOURCE_SHA="$(git rev-parse "${RELEASE_TAG}^{commit}")"
PUBLISH=true
else
VERSION="$INPUT_VERSION"
TAG="v${VERSION}"
SOURCE_SHA="$(git rev-parse HEAD)"
PUBLISH=false
fi
[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]] || { echo "Invalid semantic version" >&2; exit 1; }
{
echo "sha=$SOURCE_SHA"
echo "version=$VERSION"
echo "tag=$TAG"
echo "publish=$PUBLISH"
} >> "$GITHUB_OUTPUT"

build:
needs: prepare
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-apple-darwin
os: macos-latest
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-pc-windows-msvc
os: windows-latest

- {target: x86_64-unknown-linux-gnu, os: ubuntu-22.04}
- {target: aarch64-unknown-linux-gnu, os: ubuntu-22.04}
- {target: x86_64-apple-darwin, os: macos-15}
- {target: aarch64-apple-darwin, os: macos-15}
- {target: x86_64-pc-windows-msvc, os: windows-2022}
runs-on: ${{ matrix.os }}

env:
VERSION: ${{ needs.prepare.outputs.version }}
TAG: ${{ needs.prepare.outputs.tag }}
steps:
- uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
ref: ${{ needs.prepare.outputs.source_sha }}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security: contents: write is currently workflow-wide, and checkout v4 persists the token by default. This means every matrix build runs Cargo dependencies and build scripts with push-capable Git credentials even though only publish needs to mutate the repository. Please default the workflow to contents: read, add persist-credentials: false to the prepare/build checkouts, and grant contents: write only to the publish job. Verify that an authenticated push from a build job is rejected while publishing still succeeds.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 48e3783. Workflow-level permissions now default to contents: read; prepare, build, and compatibility checkouts use persist-credentials: false; only the publish job receives contents: write. The standalone end-to-end release run confirms that the restricted build jobs complete and the job-scoped publisher can still upload assets and push the manifest commit: https://github.com/NanamiMio/ptd-cli-release-test/actions/runs/32728854010

persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}

- name: Install cross-compilation tools (aarch64-linux)
- name: Install cross-compilation tools
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV

echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV"
- name: Set package version
shell: bash
run: |
sed -i.bak -E "s/^version = \".*\"/version = \"${VERSION}\"/" Cargo.toml
cargo update -p ptd-cli
- name: Test
if: matrix.target == 'x86_64-unknown-linux-gnu'
run: cargo test --locked
- name: Build
run: cargo build --release --target ${{ matrix.target }}

- name: Package (unix)
run: cargo build --locked --release --target "${{ matrix.target }}"
- name: Smoke test native executable
if: matrix.target == 'x86_64-unknown-linux-gnu'
run: target/${{ matrix.target }}/release/ptd --help
- name: Package Unix binaries
if: runner.os != 'Windows'
run: |
cd target/${{ matrix.target }}/release
tar czf ../../../ptd-cli-${{ github.ref_name }}-${{ matrix.target }}.tar.gz ptd ptd-host
cd ../../..

- name: Package (windows)
run: tar -C "target/${{ matrix.target }}/release" -czf "ptd-cli-${TAG}-${{ matrix.target }}.tar.gz" ptd ptd-host
- name: Package Windows binaries
if: runner.os == 'Windows'
shell: pwsh
run: Compress-Archive -Path "target/${{ matrix.target }}/release/ptd.exe","target/${{ matrix.target }}/release/ptd-host.exe" -DestinationPath "ptd-cli-$env:TAG-${{ matrix.target }}.zip"
- uses: actions/upload-artifact@v4
with:
name: release-${{ matrix.target }}
path: ptd-cli-${{ env.TAG }}-${{ matrix.target }}.*
if-no-files-found: error

compatibility:
name: Ubuntu 22.04 and Homebrew smoke test
needs: [prepare, build]
runs-on: ubuntu-22.04
env:
VERSION: ${{ needs.prepare.outputs.version }}
TAG: ${{ needs.prepare.outputs.tag }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.prepare.outputs.source_sha }}
persist-credentials: false
- uses: Homebrew/actions/setup-homebrew@a657b8b0cd35d0f65cce41fce9b24cf054b49869
- uses: actions/download-artifact@v4
with:
name: release-x86_64-unknown-linux-gnu
path: dist
- name: Test packaged executable and formula
shell: bash
run: |
Compress-Archive -Path "target/${{ matrix.target }}/release/ptd.exe","target/${{ matrix.target }}/release/ptd-host.exe" -DestinationPath "ptd-cli-${{ github.ref_name }}-${{ matrix.target }}.zip"
tar -xzf "dist/ptd-cli-${TAG}-x86_64-unknown-linux-gnu.tar.gz" -C dist
dist/ptd --help
SHA="$(sha256sum "dist/ptd-cli-${TAG}-x86_64-unknown-linux-gnu.tar.gz" | awk '{print $1}')"
sed -e "s/@VERSION@/${VERSION}/g" \
-e "s|@REPOSITORY@|${GITHUB_REPOSITORY}|g" \
-e "s|@LINUX_X86_64_URL@|file://${GITHUB_WORKSPACE}/dist/ptd-cli-${TAG}-x86_64-unknown-linux-gnu.tar.gz|g" \
-e "s/@LINUX_X86_64_SHA@/${SHA}/g" \
.github/release/ptd-cli.rb.in > /tmp/ptd-cli.rb
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
brew tap-new ptd-test/release
cp /tmp/ptd-cli.rb "$(brew --repository ptd-test/release)/Formula/ptd-cli.rb"
brew install --formula ptd-test/release/ptd-cli
brew test ptd-test/release/ptd-cli

- name: Upload release assets
uses: softprops/action-gh-release@v2
publish:
name: Publish assets, then manifests
needs: [prepare, build, compatibility]
if: needs.prepare.outputs.publish == 'true'
runs-on: ubuntu-22.04
permissions:
contents: write
env:
VERSION: ${{ needs.prepare.outputs.version }}
TAG: ${{ needs.prepare.outputs.tag }}
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v4
with:
files: |
ptd-cli-${{ github.ref_name }}-${{ matrix.target }}.tar.gz
ptd-cli-${{ github.ref_name }}-${{ matrix.target }}.zip
ref: ${{ github.event.repository.default_branch }}
fetch-depth: 0
- uses: actions/download-artifact@v4
with:
pattern: release-*
path: dist
merge-multiple: true
- name: Upload and verify every release asset
shell: bash
run: |
gh release upload "$TAG" dist/* --clobber
ASSETS="$(gh release view "$TAG" --json assets --jq '.assets[].name')"
for asset in dist/*; do grep -Fxq "$(basename "$asset")" <<< "$ASSETS"; done
- name: Generate release manifests
run: .github/release/render-manifests.sh "$VERSION" "$TAG" "$GITHUB_REPOSITORY" dist
- name: Publish release metadata idempotently
shell: bash
run: |
git pull --ff-only origin "${{ github.event.repository.default_branch }}"
mkdir -p Formula bucket
cp generated/ptd-cli.rb Formula/ptd-cli.rb
cp generated/ptd-cli.json bucket/ptd-cli.json
git add Formula/ptd-cli.rb bucket/ptd-cli.json
if git diff --cached --quiet; then echo "Release metadata is already current"; exit 0; fi
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit -m "chore(release): publish ${TAG} [skip ci]"
git push origin HEAD:${{ github.event.repository.default_branch }}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sequence is not atomic or safely retryable. Once this push succeeds, a later tag or release failure leaves public manifests pointing to assets that do not exist. If the tag push succeeds but gh release create fails or uploads only some assets, the next run is rejected by the existing-tag check and requires manual repair. Please make each external write idempotent and avoid publishing manifest URLs until all release assets have been verified. Verification should inject a failure after each branch/tag/release mutation and show that rerunning reaches a complete release without manual cleanup.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 48e3783. The workflow no longer creates or rejects an existing tag: it starts from the existing GitHub Release, uploads every asset with --clobber, verifies every expected asset name, and only then renders and pushes the public manifests. A failure before the manifest commit leaves no public package URL; a failure after any asset upload is recoverable by rerunning. I ran the complete release once and then reran the same workflow without deleting the tag, release, assets, or manifests; both attempts completed successfully: https://github.com/NanamiMio/ptd-cli-release-test/actions/runs/32728854010

22 changes: 20 additions & 2 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,29 @@ Three components:

## Install

### 1. Download pre-built binaries
### 1. Install CLI

#### Homebrew (macOS / Linux)

```bash
# Add tap and install
brew tap pt-plugins/ptd-cli https://github.com/pt-plugins/ptd-cli
brew install ptd-cli
```

#### Scoop (Windows)

```powershell
# Add bucket and install
scoop bucket add ptd-cli https://github.com/pt-plugins/ptd-cli
scoop install ptd-cli
```

#### Manual Download (Pre-built Binaries)

Download the latest `ptd` and `ptd-host` from [GitHub Releases](https://github.com/pt-plugins/ptd-cli/releases), extract them into the same directory, and add it to your `PATH`.

> **AI Agent users:** Download pre-built binaries from the Release page instead of building from source.
> **AI Agent users:** Use package managers (Homebrew / Scoop) or download pre-built binaries from the Release page instead of building from source.

<details>
<summary>Build from source</summary>
Expand Down
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,29 @@ CLI <-- Unix socket <-- ptd-host <--stdin-- Chrome <------+

## 安装

### 1. 下载预编译二进制
### 1. 安装 CLI

#### Homebrew (macOS / Linux)

```bash
# 添加 tap 并安装
brew tap pt-plugins/ptd-cli https://github.com/pt-plugins/ptd-cli
brew install ptd-cli
```

#### Scoop (Windows)

```powershell
# 添加 bucket 并安装
scoop bucket add ptd-cli https://github.com/pt-plugins/ptd-cli
scoop install ptd-cli
```

#### 手动下载预编译二进制

从 [GitHub Releases](https://github.com/pt-plugins/ptd-cli/releases) 下载最新版本的 `ptd` 和 `ptd-host`,解压后将两个文件放在同一目录下,并添加到 `PATH`。

> **AI Agent 用户:** 请直接从 Release 页面下载预编译二进制,无需从源码构建。
> **AI Agent 用户:** 请优先使用包管理器(Homebrew / Scoop)或直接从 Release 页面下载预编译二进制,无需从源码构建。

<details>
<summary>从源码构建</summary>
Expand Down
Loading