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
103 changes: 35 additions & 68 deletions .github/workflows/release-dmr.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
name: Release dmr

# Builds the standalone dmr binary for macOS (arm64), Linux (amd64/arm64),
# and Windows (amd64), publishes a GitHub Release with the archives, then
# opens the corresponding Homebrew formula and WinGet manifest updates so
# `brew install docker/tap/dmr` and `winget install Docker.dmr` pick up the
# new version.
# and Windows (amd64) and publishes a GitHub Release with the (initially
# unsigned) archives.
#
# macOS/Windows code signing + notarization and the Homebrew cask update live
# in docker/inference-engine-llama.cpp's release-dmr.yml — an internal repo
# that holds the Docker code-signing credentials (this repo is public and
# cannot use them). This workflow auto-triggers it after the release is
# created; it downloads the macOS/Windows archives from this release, signs
# and notarizes them, re-uploads the signed versions here, and opens the
# Homebrew cask PR against docker/homebrew-tap. WinGet is published below.
#
# Triggered by pushing a tag of the form dmr-vX.Y.Z (kept distinct from the
# model-runner container image release tags used by release.yml).
Expand Down Expand Up @@ -94,77 +100,38 @@ jobs:
--generate-notes \
dist/*

publish-homebrew:
name: Publish Homebrew formula
trigger-signing:
name: Trigger signing + Homebrew cask
needs: release
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Compute release checksums
id: sha
run: |
set -euo pipefail
for target in darwin-arm64 linux-amd64 linux-arm64; do
curl -sL -o "${target}.tar.gz" \
"https://github.com/${{ github.repository }}/releases/download/${TAG}/dmr-${target}.tar.gz"
sum=$(sha256sum "${target}.tar.gz" | cut -d' ' -f1)
echo "${target//-/_}=${sum}" >> "$GITHUB_OUTPUT"
done
# Cross-repo automation uses a GitHub App, never a PAT (org security
# guidance). This dedicated App has only Actions: write on
# docker/inference-engine-llama.cpp — the minimum needed to start that
# repo's signing workflow. That internal repo owns the code-signing
# credentials this public repo cannot hold; it signs the macOS/Windows
# archives, re-uploads them to this release, and opens the Homebrew cask
# PR.
- name: Mint trigger token
id: trigger-token
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
with:
app-id: ${{ vars.DMR_TRIGGER_APP_ID }}
private-key: ${{ secrets.DMR_TRIGGER_APP_PRIVATE_KEY }}
owner: docker
repositories: inference-engine-llama.cpp

- name: Generate formula
run: |
set -euo pipefail
mkdir -p formula
cat > formula/dmr.rb <<RUBY
class Dmr < Formula
desc "Standalone Docker Model Runner — run local models with no Docker Desktop required"
homepage "https://github.com/${{ github.repository }}"
version "${{ needs.release.outputs.version }}"
license "Apache-2.0"

on_macos do
on_arm do
url "https://github.com/${{ github.repository }}/releases/download/${TAG}/dmr-darwin-arm64.tar.gz"
sha256 "${{ steps.sha.outputs.darwin_arm64 }}"
end
end

on_linux do
on_intel do
url "https://github.com/${{ github.repository }}/releases/download/${TAG}/dmr-linux-amd64.tar.gz"
sha256 "${{ steps.sha.outputs.linux_amd64 }}"
end
on_arm do
url "https://github.com/${{ github.repository }}/releases/download/${TAG}/dmr-linux-arm64.tar.gz"
sha256 "${{ steps.sha.outputs.linux_arm64 }}"
end
end

def install
bin.install "dmr"
end

test do
assert_match version.to_s, shell_output("#{bin}/dmr version")
end
end
RUBY

- name: Open pull request against docker/homebrew-tap
- name: Dispatch release-dmr.yml in docker/inference-engine-llama.cpp
env:
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
GH_TOKEN: ${{ steps.trigger-token.outputs.token }}
run: |
set -euo pipefail
branch="dmr-${{ needs.release.outputs.version }}"
gh repo clone docker/homebrew-tap tap -- --depth=1
cp formula/dmr.rb tap/Formula/dmr.rb
cd tap
git checkout -b "$branch"
git -c user.name="docker-tools-robot" -c user.email="docker-tools-robot@users.noreply.github.com" \
commit -am "dmr ${{ needs.release.outputs.version }}"
git push origin "$branch"
gh pr create --repo docker/homebrew-tap \
--title "dmr ${{ needs.release.outputs.version }}" \
--body "Automated formula update for dmr ${{ needs.release.outputs.version }}."
gh workflow run release-dmr.yml \
--repo docker/inference-engine-llama.cpp \
--ref main \
-f tag="$TAG"

publish-winget:
name: Publish WinGet manifest
Expand Down
44 changes: 30 additions & 14 deletions packaging/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,30 @@

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

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

There are no static package manifests checked into this repository: the
[`release-dmr.yml`](../.github/workflows/release-dmr.yml) workflow builds the
binaries, creates the GitHub release, generates the Homebrew formula and
opens a PR against `docker/homebrew-tap`, and submits the WinGet manifest to
`microsoft/winget-pkgs` via `wingetcreate`, all from the version being
released. This mirrors the packaging approach used by
[`docker/sandboxes`](https://github.com/docker/sandboxes)'s
`publish-brew.yml`/`publish-winget.yml`.
There are no static package manifests checked into this repository. The
release is a **two-repo, two-step** flow because macOS/Windows code signing
requires Docker's private signing credentials, which cannot live in this
public repository:

1. [`release-dmr.yml`](../.github/workflows/release-dmr.yml) (here) builds all
targets, creates the GitHub release with the (unsigned) archives, submits
the WinGet manifest, and auto-triggers step 2.
2. `release-dmr.yml` in the internal
[`docker/inference-engine-llama.cpp`](https://github.com/docker/inference-engine-llama.cpp)
repo downloads the macOS/Windows archives from this release, signs and
notarizes them (via `docker/desktop-action-private`), re-uploads the signed
versions to this same release, and opens the Homebrew **cask** PR against
`docker/homebrew-tap`.

This mirrors how [`docker/sandboxes`](https://github.com/docker/sandboxes)
signs and publishes `sbx` as a cask.

## Cutting a release

Expand All @@ -24,12 +34,18 @@ git tag dmr-v0.1.0
git push origin dmr-v0.1.0
```

This triggers `release-dmr.yml`, which requires the following repository
secrets:
This triggers `release-dmr.yml`, which requires (cross-repo automation uses a
GitHub App, never a PAT):

- `DMR_TRIGGER_APP_ID` (variable) + `DMR_TRIGGER_APP_PRIVATE_KEY` (secret) — a
dedicated GitHub App with only `Actions: write` on
`docker/inference-engine-llama.cpp`, used to dispatch the signing workflow
there (step 2).
- `WINGET_GH_KEY` (secret) — token used by `wingetcreate` to submit to
`microsoft/winget-pkgs`.

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

## Local cross-compilation

Expand Down
Loading