YAML · 6056 bytes Raw Blame History
1 name: release
2
3 # Tag-driven release. We don't publish to PyPI — DLM ships via a
4 # Homebrew tap (https://github.com/tenseleyFlow/homebrew-tap).
5 #
6 # What this workflow does on a `v*` tag:
7 #
8 # 1. Gate on the full CI suite (ruff + format + mypy + non-slow pytest
9 # + mkdocs strict build — we still prove docs build, even though we
10 # don't host them).
11 # 2. Build a "fat" source tarball that bundles `vendor/llama.cpp/`
12 # (source only, no build artifacts) so the Homebrew formula can
13 # drop the convert scripts into libexec without cloning submodules
14 # at install time.
15 # 3. Create a GitHub release with the tarball + a computed sha256
16 # pasted into the release notes so the tap formula can bump in one
17 # edit.
18 #
19 # Docs hosting is deferred — users read `docs/` in-repo and in the
20 # brew-installed tarball. Wire up Pages / a custom domain as a separate
21 # change when ready; the strict-build gate above ensures we never tag
22 # a release whose docs don't compile.
23
24 on:
25 push:
26 tags:
27 - "v*"
28
29 permissions:
30 contents: write # create release + upload asset
31
32 concurrency:
33 group: release-${{ github.ref }}
34 cancel-in-progress: false
35
36 env:
37 UV_VERSION: "0.11.6"
38 PYTHON_VERSION: "3.11"
39
40 jobs:
41 ci-gate:
42 name: full CI must pass
43 runs-on: ubuntu-latest
44 steps:
45 - uses: actions/checkout@v4
46
47 - name: Install uv
48 uses: astral-sh/setup-uv@v4
49 with:
50 version: ${{ env.UV_VERSION }}
51
52 - name: Sync (all groups)
53 run: uv sync --all-extras --dev --group docs
54
55 - name: Ruff
56 run: uv run ruff check .
57
58 - name: Ruff format
59 run: uv run ruff format --check .
60
61 - name: Mypy
62 run: uv run mypy src/dlm
63
64 - name: Pytest (non-slow)
65 run: uv run pytest -m "not slow and not online and not gpu"
66
67 - name: Mkdocs build --strict
68 run: uv run mkdocs build --strict --site-dir /tmp/mkdocs-check
69
70 build-release-tarball:
71 name: build fat source tarball
72 needs: ci-gate
73 runs-on: ubuntu-latest
74 outputs:
75 tarball_name: ${{ steps.build.outputs.tarball_name }}
76 sha256: ${{ steps.build.outputs.sha256 }}
77 steps:
78 - name: Checkout with submodules
79 uses: actions/checkout@v4
80 with:
81 submodules: recursive
82
83 - name: Build tarball
84 id: build
85 run: |
86 set -euxo pipefail
87 TAG="${GITHUB_REF_NAME}"
88 NAME="dlm-${TAG}"
89 # Write the tarball to RUNNER_TEMP (outside the source tree) to
90 # avoid tar's "file changed as we read it" when `.` includes the
91 # growing output file. Move it back to the workspace afterwards
92 # so upload-artifact can find it by relative path.
93 OUT="${RUNNER_TEMP}/${NAME}.tar.gz"
94 # Build the tarball with a top-level prefix matching NAME so
95 # `tar xzf` extracts into a clean subdir (Homebrew convention).
96 # Exclude CI noise + git metadata; KEEP `vendor/llama.cpp/` so
97 # the formula can use the vendored Python convert scripts.
98 tar czf "${OUT}" \
99 --transform="s,^\./,${NAME}/," \
100 --exclude="./.git" \
101 --exclude="./.github" \
102 --exclude="./.pytest_cache" \
103 --exclude="./.mypy_cache" \
104 --exclude="./.ruff_cache" \
105 --exclude="__pycache__" \
106 --exclude="*.pyc" \
107 --exclude="./vendor/llama.cpp/.git" \
108 --exclude="./vendor/llama.cpp/build" \
109 --exclude="./vendor/llama.cpp/.cache" \
110 --exclude="./tests" \
111 --exclude="./.docs" \
112 --exclude="./site" \
113 .
114 mv "${OUT}" "${NAME}.tar.gz"
115 SHA=$(sha256sum "${NAME}.tar.gz" | awk '{print $1}')
116 echo "Tarball: ${NAME}.tar.gz (sha256=${SHA})"
117 echo "tarball_name=${NAME}.tar.gz" >> "$GITHUB_OUTPUT"
118 echo "sha256=${SHA}" >> "$GITHUB_OUTPUT"
119
120 - name: Upload tarball artifact
121 uses: actions/upload-artifact@v4
122 with:
123 name: release-tarball
124 path: ${{ steps.build.outputs.tarball_name }}
125
126 publish-github-release:
127 name: publish GitHub release
128 needs: build-release-tarball
129 runs-on: ubuntu-latest
130 steps:
131 - uses: actions/download-artifact@v4
132 with:
133 name: release-tarball
134
135 - name: Create release
136 env:
137 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
138 TAG: ${{ github.ref_name }}
139 TARBALL: ${{ needs.build-release-tarball.outputs.tarball_name }}
140 SHA256: ${{ needs.build-release-tarball.outputs.sha256 }}
141 run: |
142 set -euxo pipefail
143 # Route prerelease tags (PEP 440: rc / a / b / dev suffixes)
144 # to GitHub "prerelease" flag — surfaces as the yellow banner
145 # on the releases page instead of "latest".
146 PRERELEASE_FLAG=""
147 if python3 -c "
148 import sys
149 from packaging.version import Version, InvalidVersion
150 tag='${TAG}'.lstrip('v')
151 try:
152 sys.exit(0 if Version(tag).is_prerelease else 1)
153 except InvalidVersion:
154 sys.exit(0)
155 "; then
156 PRERELEASE_FLAG="--prerelease"
157 fi
158
159 cat > release-notes.md <<EOF
160 ## DLM ${TAG}
161
162 Install via the Homebrew tap:
163
164 \`\`\`
165 brew tap tenseleyFlow/tap
166 brew install dlm
167 \`\`\`
168
169 ### Formula bump
170
171 When bumping \`Formula/dlm.rb\` in \`tenseleyFlow/homebrew-tap\`:
172
173 - \`url\` → https://github.com/tenseleyFlow/DocumentLanguageModel/releases/download/${TAG}/${TARBALL}
174 - \`sha256\` → \`${SHA256}\`
175
176 ### Build
177
178 Full changelog: see \`CHANGELOG.md\` in the tarball.
179 EOF
180
181 gh release create "${TAG}" \
182 --repo "${GITHUB_REPOSITORY}" \
183 --title "DLM ${TAG}" \
184 --notes-file release-notes.md \
185 ${PRERELEASE_FLAG} \
186 "${TARBALL}"
187