Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ Shipped as gh stack #9 (draft PRs, bottom to top):
custom `TokenSource`) (#7).
- [ ] MCP `list_builds` tool doesn't expose `BuildFilter` yet, so it lags
the CLI.
- [x] Documented `list_builds` limit=200 as page size, not a cap
(sitiai finding #6, PR #24).
- [x] `asc apps await-processing` poll loop built on `BuildsApi::list_builds`
(sitiai finding #5, PR #26) — a non-blocking alternative to altool
`--wait`.

## Docs
- [x] README crate table — builds API surface, altool upload.
Expand Down
28 changes: 25 additions & 3 deletions .agents/plans/feature-plan-from-sitiai-release-session.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ Companion plan: [`feature-plan-from-klepon-xcode-release-session.md`](./feature-
upload *mechanism*; this one hardens the *pre-flight and verification* around
it. Shared context between the two:

> **Progress (2025-10-11, this session):** gh stack #27 (bottom→top)
> #24 `docs/list-builds-pagination` → #25 `feat/signing-doctor` →
> #26 `feat/apps-await-processing`, rebased onto `development`, all CI-clean
> (fmt / clippy `-D warnings` / test / `RUSTDOCFLAGS=-D warnings` doc /
> lower-crate solo builds), signed+verified, auto-merge enabled. This landed
> finding **#2** (signing doctor), finding **#5** (await-processing), and
> finding **#6** (list_builds pagination doc). Still open from this plan:
> **#1** (pre-upload `altool --validate-app`), **#3** (body-aware `success`),
> **#4** (`.app`-vs-`.pkg` message). The one live item for all of them is the
> ASC smoke test, still blocked on credentials.

- Both sessions hit the same root pain: a **stale private key / signing
identity with the right id but wrong contents**, which surfaces as an
indistinguishable 401 (klepon plan, "Separate / unaddressed bug") or, in
Expand Down Expand Up @@ -50,7 +61,7 @@ exposes `--validate-app` for exactly this: a fast server-side pre-flight
- **Value:** turns a ~minutes-late failure into a seconds-early one; catches
the common "wrong version / bad entitlement" class before upload.

### 2. No local-keychain `signing doctor` — HIGH
### 2. No local-keychain `signing doctor` — HIGH ✅ SHIPPED (PR #25)
The `signing` crate (`certificate.rs`, `profile.rs`, `csr.rs`) talks **only
to the ASC REST API**. `security find-identity` is called nowhere in the
repo; there is no local-keychain code at all. So the exact stale-identity
Expand Down Expand Up @@ -92,7 +103,7 @@ just not exported yet. People routinely point the tool at the `.app`.
"directory / wrong kind" from "path missing".
- **Value:** tiny, same-file, removes a recurring confusing error.

### 5. No non-blocking `await-processing` loop — MEDIUM
### 5. No non-blocking `await-processing` loop — MEDIUM ✅ SHIPPED (PR #26)
The only way to wait for ASC to finish processing a build is altool's
`--wait`, which blocks the altool process for the whole duration. There's
no poll-based path that uses the builds API (`BuildsApi::list_builds`,
Expand All @@ -103,8 +114,15 @@ caller poll on its own cadence.
with backoff. Complements klepon's builds API; an MCP-friendly
alternative to a blocking `--wait`.
- **Value:** better UX for CI/agents; builds on existing klepon surface.
- **Shipped (PR #26):** `asc apps await-processing <APP_ID>` with
`--version`, `--pre-release-version`, `--interval` (30s),
`--timeout` (1800s). Shared poll loop + pure `classify()` /
`AwaitConfig` / `AwaitOutcome` in `crates/frontend/src/await_processing.rs`
(serializes without key material; 7 in-module tests). Terminal map:
VALID → success, INVALID/FAILED → failure, else timeout → non-zero exit.
CLI-only for now; the pure core is MCP-ready if a tool is added later.

### 6. `list_builds` limit=200 pagination — LOW (doc-only)
### 6. `list_builds` limit=200 pagination — LOW (doc-only) ✅ SHIPPED (PR #24)
`list_builds` caps at limit=200 with no cursor follow-through. Fine for
normal apps; note it as a known limit. Overlaps klepon's open "MCP
`list_builds` doesn't expose `BuildFilter`" item — fold into that rather
Expand All @@ -128,3 +146,7 @@ module, shared `--dry-run`/`altool_args` test scaffolding; #4 is cheap to
fold in alongside). #2 on its own branch (new macOS-only keychain surface,
`security` subprocess, cross-check against `CertificateApi`). Exercise #1
and #3 under klepon's still-open "Live smoke test against ASC" item.

**Status:** #2 shipped (PR #25). The `upload.rs` cluster (#3 + #1, with #4
folded in) is still the next natural branch — one file, shared test
scaffolding, all inside what ascapi already owns.
147 changes: 147 additions & 0 deletions .github/workflows/release-github.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
name: GitHub Release

on:
workflow_dispatch:
inputs:
tag:
description: "Release tag (e.g. v0.2.0)"
required: true

permissions:
contents: write

env:
PROJECT_NAME: asc
CARGO_TERM_COLOR: always

jobs:
build:
name: Build binary (${{ matrix.name }})
runs-on: ${{ matrix.runner }}

strategy:
fail-fast: false
matrix:
include:
- name: linux-amd64
runner: ubuntu-latest
target: x86_64-unknown-linux-gnu
- name: linux-arm64
runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
- name: win-amd64
runner: windows-latest
target: x86_64-pc-windows-msvc
- name: win-arm64
runner: windows-latest
target: aarch64-pc-windows-msvc
- name: macos-amd64
runner: macos-15-intel
target: x86_64-apple-darwin
- name: macos-arm64
runner: macos-latest
target: aarch64-apple-darwin

steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ github.event.inputs.tag || github.ref }}

- name: Set the release version
shell: bash
run: |
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
release_version="${GITHUB_REF_NAME#v}"
else
release_version="${{ github.event.inputs.tag }}"
release_version="${release_version#v}"
fi

echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"

- name: Install toolkit
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
with:
toolchain: stable
targets: ${{ matrix.target }}

- name: Setup Rust cache
uses: Swatinem/rust-cache@v2
with:
key: github-${{ matrix.target }}

- name: Build binary
shell: bash
run: cargo build --locked --release --target ${{ matrix.target }} --package smbcloud-ascapi-cli

- name: Package binary
shell: bash
run: |
BIN_SUFFIX=""
if [[ "${{ matrix.runner }}" == "windows-latest" ]]; then
BIN_SUFFIX=".exe"
fi

BIN_OUTPUT="target/${{ matrix.target }}/release/${PROJECT_NAME}${BIN_SUFFIX}"
BIN_RELEASE="${PROJECT_NAME}-${{ matrix.name }}${BIN_SUFFIX}"

mkdir -p ./release
mv "${BIN_OUTPUT}" "./release/${BIN_RELEASE}"

# Windows runners use bash from Git for Windows, which has sha256sum;
# macOS only has shasum.
- name: Checksum release assets
shell: bash
run: |
cd release
for asset in *; do
case "${asset}" in *.sha256) continue ;; esac

if command -v sha256sum >/dev/null 2>&1; then
sha256sum "${asset}" | awk '{print $1}' > "${asset}.sha256"
else
shasum -a 256 "${asset}" | awk '{print $1}' > "${asset}.sha256"
fi

echo "${asset}: $(cat "${asset}.sha256")"
done

- name: Upload binary artifact
uses: actions/upload-artifact@v7
with:
name: binary-${{ matrix.name }}
path: release/*

release:
name: Create GitHub Release
needs: build
runs-on: ubuntu-latest
if: github.ref_type == 'tag' || github.event_name == 'workflow_dispatch'

steps:
- name: Resolve tag
id: tag
shell: bash
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "tag=${{ github.event.inputs.tag }}" >> "$GITHUB_OUTPUT"
else
echo "tag=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
fi

- name: Download binary artifacts
uses: actions/download-artifact@v7
with:
pattern: "binary-*"
path: release
merge-multiple: true

- name: Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag.outputs.tag }}
files: release/*
# Build the notes from the PRs since the previous tag, the same
# format as the "Generate release notes" button on the web UI.
generate_release_notes: true
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading