Skip to content

Commit ab34777

Browse files
committed
ci(dmr): sign/notarize via internal repo and publish a Homebrew cask
model-runner is public and can't use Docker's private signing action, so release-dmr.yml now only builds the (unsigned) archives, creates the release, and publishes WinGet — then auto-triggers release-dmr.yml in the internal docker/inference-engine-llama.cpp repo (which holds the signing credentials) to sign+notarize the macOS/Windows binaries, re-upload them to this release, and open the Homebrew cask PR. - Drop the unsigned Homebrew *formula* publish (replaced by a signed *cask* generated from the internal repo). - Add a trigger-signing job that dispatches the internal workflow with the tag (needs DMR_SIGN_TRIGGER_TOKEN: Actions:write on the internal repo). - Update packaging/README to document the two-repo, two-step flow. Signed-off-by: Dorin Geman <dorin.geman@docker.com>
1 parent 2221f17 commit ab34777

2 files changed

Lines changed: 65 additions & 82 deletions

File tree

.github/workflows/release-dmr.yml

Lines changed: 35 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
name: Release dmr
22

33
# Builds the standalone dmr binary for macOS (arm64), Linux (amd64/arm64),
4-
# and Windows (amd64), publishes a GitHub Release with the archives, then
5-
# opens the corresponding Homebrew formula and WinGet manifest updates so
6-
# `brew install docker/tap/dmr` and `winget install Docker.dmr` pick up the
7-
# new version.
4+
# and Windows (amd64) and publishes a GitHub Release with the (initially
5+
# unsigned) archives.
6+
#
7+
# macOS/Windows code signing + notarization and the Homebrew cask update live
8+
# in docker/inference-engine-llama.cpp's release-dmr.yml — an internal repo
9+
# that holds the Docker code-signing credentials (this repo is public and
10+
# cannot use them). This workflow auto-triggers it after the release is
11+
# created; it downloads the macOS/Windows archives from this release, signs
12+
# and notarizes them, re-uploads the signed versions here, and opens the
13+
# Homebrew cask PR against docker/homebrew-tap. WinGet is published below.
814
#
915
# Triggered by pushing a tag of the form dmr-vX.Y.Z (kept distinct from the
1016
# model-runner container image release tags used by release.yml).
@@ -94,77 +100,38 @@ jobs:
94100
--generate-notes \
95101
dist/*
96102
97-
publish-homebrew:
98-
name: Publish Homebrew formula
103+
trigger-signing:
104+
name: Trigger signing + Homebrew cask
99105
needs: release
100106
runs-on: ubuntu-latest
107+
permissions:
108+
contents: read
101109
steps:
102-
- name: Compute release checksums
103-
id: sha
104-
run: |
105-
set -euo pipefail
106-
for target in darwin-arm64 linux-amd64 linux-arm64; do
107-
curl -sL -o "${target}.tar.gz" \
108-
"https://github.com/${{ github.repository }}/releases/download/${TAG}/dmr-${target}.tar.gz"
109-
sum=$(sha256sum "${target}.tar.gz" | cut -d' ' -f1)
110-
echo "${target//-/_}=${sum}" >> "$GITHUB_OUTPUT"
111-
done
110+
# Cross-repo automation uses a GitHub App, never a PAT (org security
111+
# guidance). This dedicated App has only Actions: write on
112+
# docker/inference-engine-llama.cpp — the minimum needed to start that
113+
# repo's signing workflow. That internal repo owns the code-signing
114+
# credentials this public repo cannot hold; it signs the macOS/Windows
115+
# archives, re-uploads them to this release, and opens the Homebrew cask
116+
# PR.
117+
- name: Mint trigger token
118+
id: trigger-token
119+
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
120+
with:
121+
app-id: ${{ vars.DMR_TRIGGER_APP_ID }}
122+
private-key: ${{ secrets.DMR_TRIGGER_APP_PRIVATE_KEY }}
123+
owner: docker
124+
repositories: inference-engine-llama.cpp
112125

113-
- name: Generate formula
114-
run: |
115-
set -euo pipefail
116-
mkdir -p formula
117-
cat > formula/dmr.rb <<RUBY
118-
class Dmr < Formula
119-
desc "Standalone Docker Model Runner — run local models with no Docker Desktop required"
120-
homepage "https://github.com/${{ github.repository }}"
121-
version "${{ needs.release.outputs.version }}"
122-
license "Apache-2.0"
123-
124-
on_macos do
125-
on_arm do
126-
url "https://github.com/${{ github.repository }}/releases/download/${TAG}/dmr-darwin-arm64.tar.gz"
127-
sha256 "${{ steps.sha.outputs.darwin_arm64 }}"
128-
end
129-
end
130-
131-
on_linux do
132-
on_intel do
133-
url "https://github.com/${{ github.repository }}/releases/download/${TAG}/dmr-linux-amd64.tar.gz"
134-
sha256 "${{ steps.sha.outputs.linux_amd64 }}"
135-
end
136-
on_arm do
137-
url "https://github.com/${{ github.repository }}/releases/download/${TAG}/dmr-linux-arm64.tar.gz"
138-
sha256 "${{ steps.sha.outputs.linux_arm64 }}"
139-
end
140-
end
141-
142-
def install
143-
bin.install "dmr"
144-
end
145-
146-
test do
147-
assert_match version.to_s, shell_output("#{bin}/dmr version")
148-
end
149-
end
150-
RUBY
151-
152-
- name: Open pull request against docker/homebrew-tap
126+
- name: Dispatch release-dmr.yml in docker/inference-engine-llama.cpp
153127
env:
154-
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
128+
GH_TOKEN: ${{ steps.trigger-token.outputs.token }}
155129
run: |
156130
set -euo pipefail
157-
branch="dmr-${{ needs.release.outputs.version }}"
158-
gh repo clone docker/homebrew-tap tap -- --depth=1
159-
cp formula/dmr.rb tap/Formula/dmr.rb
160-
cd tap
161-
git checkout -b "$branch"
162-
git -c user.name="docker-tools-robot" -c user.email="docker-tools-robot@users.noreply.github.com" \
163-
commit -am "dmr ${{ needs.release.outputs.version }}"
164-
git push origin "$branch"
165-
gh pr create --repo docker/homebrew-tap \
166-
--title "dmr ${{ needs.release.outputs.version }}" \
167-
--body "Automated formula update for dmr ${{ needs.release.outputs.version }}."
131+
gh workflow run release-dmr.yml \
132+
--repo docker/inference-engine-llama.cpp \
133+
--ref main \
134+
-f tag="$TAG"
168135
169136
publish-winget:
170137
name: Publish WinGet manifest

packaging/README.md

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,30 @@
22

33
The standalone `dmr` binary (see [`cmd/dmr`](../cmd/dmr)) is distributed via:
44

5-
- **Homebrew**`brew install docker/tap/dmr` (macOS, arm64)
5+
- **Homebrew**`brew install --cask docker/tap/dmr` (macOS, arm64;
6+
signed + notarized)
67
- **WinGet**`winget install Docker.dmr` (Windows, amd64)
78
- **Direct download** — cross-compiled archives attached to each
89
[GitHub release](https://github.com/docker/model-runner/releases) tagged
910
`dmr-vX.Y.Z` (macOS arm64, Linux amd64/arm64, Windows amd64)
1011

11-
There are no static package manifests checked into this repository: the
12-
[`release-dmr.yml`](../.github/workflows/release-dmr.yml) workflow builds the
13-
binaries, creates the GitHub release, generates the Homebrew formula and
14-
opens a PR against `docker/homebrew-tap`, and submits the WinGet manifest to
15-
`microsoft/winget-pkgs` via `wingetcreate`, all from the version being
16-
released. This mirrors the packaging approach used by
17-
[`docker/sandboxes`](https://github.com/docker/sandboxes)'s
18-
`publish-brew.yml`/`publish-winget.yml`.
12+
There are no static package manifests checked into this repository. The
13+
release is a **two-repo, two-step** flow because macOS/Windows code signing
14+
requires Docker's private signing credentials, which cannot live in this
15+
public repository:
16+
17+
1. [`release-dmr.yml`](../.github/workflows/release-dmr.yml) (here) builds all
18+
targets, creates the GitHub release with the (unsigned) archives, submits
19+
the WinGet manifest, and auto-triggers step 2.
20+
2. `release-dmr.yml` in the internal
21+
[`docker/inference-engine-llama.cpp`](https://github.com/docker/inference-engine-llama.cpp)
22+
repo downloads the macOS/Windows archives from this release, signs and
23+
notarizes them (via `docker/desktop-action-private`), re-uploads the signed
24+
versions to this same release, and opens the Homebrew **cask** PR against
25+
`docker/homebrew-tap`.
26+
27+
This mirrors how [`docker/sandboxes`](https://github.com/docker/sandboxes)
28+
signs and publishes `sbx` as a cask.
1929

2030
## Cutting a release
2131

@@ -24,12 +34,18 @@ git tag dmr-v0.1.0
2434
git push origin dmr-v0.1.0
2535
```
2636

27-
This triggers `release-dmr.yml`, which requires the following repository
28-
secrets:
37+
This triggers `release-dmr.yml`, which requires (cross-repo automation uses a
38+
GitHub App, never a PAT):
39+
40+
- `DMR_TRIGGER_APP_ID` (variable) + `DMR_TRIGGER_APP_PRIVATE_KEY` (secret) — a
41+
dedicated GitHub App with only `Actions: write` on
42+
`docker/inference-engine-llama.cpp`, used to dispatch the signing workflow
43+
there (step 2).
44+
- `WINGET_GH_KEY` (secret) — token used by `wingetcreate` to submit to
45+
`microsoft/winget-pkgs`.
2946

30-
- `HOMEBREW_TAP_TOKEN` — token with push/PR access to `docker/homebrew-tap`
31-
- `WINGET_GH_KEY` — token used by `wingetcreate` to submit to
32-
`microsoft/winget-pkgs`
47+
Step 2 runs in the internal `docker/inference-engine-llama.cpp` repo, which
48+
holds its own signing and release credentials.
3349

3450
## Local cross-compilation
3551

0 commit comments

Comments
 (0)