|
| 1 | +--- |
| 2 | +name: Nightly Build and Publish |
| 3 | +'on': |
| 4 | + schedule: |
| 5 | + - cron: '0 3 * * 1,3,5' |
| 6 | + workflow_dispatch: null |
| 7 | +env: |
| 8 | + UV_SYSTEM_PYTHON: 1 |
| 9 | +jobs: |
| 10 | + check-changes: |
| 11 | + runs-on: ubuntu-latest |
| 12 | + outputs: |
| 13 | + has-changes: '${{ steps.check.outputs.has-changes }}' |
| 14 | + last-nightly-commit: '${{ steps.check.outputs.last-nightly-commit }}' |
| 15 | + current-commit: '${{ steps.check.outputs.current-commit }}' |
| 16 | + steps: |
| 17 | + - uses: actions/checkout@v4 |
| 18 | + with: |
| 19 | + fetch-depth: 0 |
| 20 | + - name: Check for changes since last nightly |
| 21 | + id: check |
| 22 | + run: > |
| 23 | + # Get the latest nightly release tag |
| 24 | +
|
| 25 | + LATEST_NIGHTLY_TAG=$(git tag --list "nightly-*" |
| 26 | + --sort=-version:refname | head -n 1) |
| 27 | +
|
| 28 | +
|
| 29 | + if [ -z "$LATEST_NIGHTLY_TAG" ]; then |
| 30 | + echo "No nightly tags found, will build (first time)" |
| 31 | + echo "has-changes=true" >> $GITHUB_OUTPUT |
| 32 | + echo "last-nightly-commit=" >> $GITHUB_OUTPUT |
| 33 | + echo "current-commit=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT |
| 34 | + exit 0 |
| 35 | + fi |
| 36 | +
|
| 37 | +
|
| 38 | + # Get the commit hash from the latest nightly tag |
| 39 | +
|
| 40 | + LATEST_NIGHTLY_COMMIT=$(git rev-list -n 1 "$LATEST_NIGHTLY_TAG") |
| 41 | +
|
| 42 | + CURRENT_COMMIT=$(git rev-parse HEAD) |
| 43 | +
|
| 44 | +
|
| 45 | + echo "last-nightly-commit=${LATEST_NIGHTLY_COMMIT}" >> $GITHUB_OUTPUT |
| 46 | +
|
| 47 | + echo "current-commit=${CURRENT_COMMIT}" >> $GITHUB_OUTPUT |
| 48 | +
|
| 49 | +
|
| 50 | + # Check if there are any commits since the last nightly |
| 51 | +
|
| 52 | + if [ "$LATEST_NIGHTLY_COMMIT" = "$CURRENT_COMMIT" ]; then |
| 53 | + echo "No changes since last nightly build" |
| 54 | + echo "has-changes=false" >> $GITHUB_OUTPUT |
| 55 | + else |
| 56 | + echo "Changes detected since last nightly build" |
| 57 | + echo "has-changes=true" >> $GITHUB_OUTPUT |
| 58 | + fi |
| 59 | + nightly-build: |
| 60 | + needs: check-changes |
| 61 | + if: needs.check-changes.outputs.has-changes == 'true' |
| 62 | + runs-on: ubuntu-latest |
| 63 | + strategy: |
| 64 | + matrix: |
| 65 | + python-version: |
| 66 | + - '3.9' |
| 67 | + - '3.10' |
| 68 | + - '3.11' |
| 69 | + - '3.12' |
| 70 | + - '3.13' |
| 71 | + name: 'Test py ${{ matrix.python-version }}' |
| 72 | + steps: |
| 73 | + - uses: actions/checkout@v4 |
| 74 | + - name: 'Set up Python ${{ matrix.python-version }}' |
| 75 | + uses: actions/setup-python@v5 |
| 76 | + with: |
| 77 | + python-version: '${{ matrix.python-version }}' |
| 78 | + - name: Install uv |
| 79 | + uses: astral-sh/setup-uv@v6 |
| 80 | + - name: Install dependencies |
| 81 | + run: | |
| 82 | + uv sync --all-extras |
| 83 | + - name: Test with pytest |
| 84 | + run: | |
| 85 | + uv run task test |
| 86 | + - name: Run linting checks |
| 87 | + run: | |
| 88 | + uv run task ruff-check |
| 89 | + uv run task type-check |
| 90 | + - name: Build documentation |
| 91 | + run: | |
| 92 | + uv run task doc-clean-build |
| 93 | + nightly-publish: |
| 94 | + needs: |
| 95 | + - check-changes |
| 96 | + - nightly-build |
| 97 | + if: needs.check-changes.outputs.has-changes == 'true' |
| 98 | + runs-on: ubuntu-latest |
| 99 | + steps: |
| 100 | + - uses: actions/checkout@v4 |
| 101 | + with: |
| 102 | + fetch-depth: 0 |
| 103 | + - name: Set up Python |
| 104 | + uses: actions/setup-python@v5 |
| 105 | + with: |
| 106 | + python-version: '3.12' |
| 107 | + - name: Install uv |
| 108 | + uses: astral-sh/setup-uv@v6 |
| 109 | + - name: Install dependencies |
| 110 | + run: | |
| 111 | + uv sync --all-extras |
| 112 | + - name: Generate nightly version |
| 113 | + id: version |
| 114 | + run: | |
| 115 | + # Generate nightly version based on current date and commit |
| 116 | + DATE=$(date +%Y%m%d) |
| 117 | + COMMIT=$(git rev-parse --short HEAD) |
| 118 | + NIGHTLY_VERSION="${DATE}.dev0+${COMMIT}" |
| 119 | + echo "nightly_version=${NIGHTLY_VERSION}" >> $GITHUB_OUTPUT |
| 120 | + echo "Generated nightly version: ${NIGHTLY_VERSION}" |
| 121 | + - name: Update version files |
| 122 | + run: > |
| 123 | + # Update version in __init__.py |
| 124 | +
|
| 125 | + sed -i "s/__version__ = .*/__version__ = \"${{ |
| 126 | + steps.version.outputs.nightly_version }}\"/" altair/__init__.py |
| 127 | +
|
| 128 | + # Update version in conf.py |
| 129 | +
|
| 130 | + sed -i "s/release = .*/release = \"${{ |
| 131 | + steps.version.outputs.nightly_version }}\"/" doc/conf.py |
| 132 | + - name: Build package |
| 133 | + run: | |
| 134 | + uv run task build |
| 135 | + - name: Sign wheel with Sigstore |
| 136 | + |
| 137 | + with: |
| 138 | + cosign-release: v2.2.0 |
| 139 | + - name: Sign wheel |
| 140 | + run: > |
| 141 | + cosign sign-blob dist/altair-${{ steps.version.outputs.nightly_version |
| 142 | + }}-py3-none-any.whl \ |
| 143 | + --output-signature dist/altair-${{ steps.version.outputs.nightly_version }}-py3-none-any.whl.sig \ |
| 144 | + --output-certificate dist/altair-${{ steps.version.outputs.nightly_version }}-py3-none-any.whl.cert |
| 145 | + - name: Generate dependency diff |
| 146 | + id: deps |
| 147 | + run: | |
| 148 | + # Get current dependencies |
| 149 | + uv pip freeze > current_deps.txt |
| 150 | +
|
| 151 | + # Get previous nightly dependencies if available |
| 152 | + if [ -f "previous_deps.txt" ]; then |
| 153 | + diff -u previous_deps.txt current_deps.txt > dependency_diff.txt || true |
| 154 | + echo "dependency_changes=true" >> $GITHUB_OUTPUT |
| 155 | + else |
| 156 | + echo "No previous dependencies found (first nightly build)" > dependency_diff.txt |
| 157 | + echo "dependency_changes=false" >> $GITHUB_OUTPUT |
| 158 | + fi |
| 159 | +
|
| 160 | + # Save current deps for next run |
| 161 | + cp current_deps.txt previous_deps.txt |
| 162 | + - name: Generate binary file checksums |
| 163 | + id: checksums |
| 164 | + run: > |
| 165 | + # Find all binary files in the project |
| 166 | +
|
| 167 | + find . -name "*.csv.gz" -o -name "*.parquet" -o -name "*.json.gz" | |
| 168 | + while read file; do |
| 169 | + if [ -f "$file" ]; then |
| 170 | + # Generate SHA256 checksum |
| 171 | + sha256sum "$file" >> binary_checksums.txt |
| 172 | + echo "Processed: $file" |
| 173 | + fi |
| 174 | + done |
| 175 | +
|
| 176 | +
|
| 177 | + # Compare with previous checksums if available |
| 178 | +
|
| 179 | + if [ -f "previous_checksums.txt" ]; then |
| 180 | + echo "=== Binary File Changes ===" > binary_changes.txt |
| 181 | + diff -u previous_checksums.txt binary_checksums.txt >> binary_changes.txt || true |
| 182 | + echo "binary_changes=true" >> $GITHUB_OUTPUT |
| 183 | + else |
| 184 | + echo "No previous binary checksums found (first nightly build)" > binary_changes.txt |
| 185 | + echo "binary_changes=false" >> $GITHUB_OUTPUT |
| 186 | + fi |
| 187 | +
|
| 188 | +
|
| 189 | + # Save current checksums for next run |
| 190 | +
|
| 191 | + cp binary_checksums.txt previous_checksums.txt |
| 192 | + - name: Create GitHub release with assets |
| 193 | + id: create_release |
| 194 | + uses: actions/create-release@v1 |
| 195 | + env: |
| 196 | + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' |
| 197 | + with: |
| 198 | + tag_name: 'nightly-${{ steps.version.outputs.nightly_version }}' |
| 199 | + release_name: 'Nightly Build ${{ steps.version.outputs.nightly_version }}' |
| 200 | + body: > |
| 201 | + Nightly build of Altair |
| 202 | +
|
| 203 | +
|
| 204 | + **Version:** ${{ steps.version.outputs.nightly_version }} |
| 205 | +
|
| 206 | + **Commit:** ${{ github.sha }} |
| 207 | +
|
| 208 | + **Date:** ${{ github.event.schedule }} |
| 209 | +
|
| 210 | + **Changes since last nightly:** ${{ |
| 211 | + needs.check-changes.outputs.last-nightly-commit }} |
| 212 | +
|
| 213 | +
|
| 214 | + This is a pre-release version for testing purposes. |
| 215 | +
|
| 216 | +
|
| 217 | + ## Installation |
| 218 | +
|
| 219 | +
|
| 220 | + ### From GitHub Release |
| 221 | +
|
| 222 | + Download the wheel file from the assets below and install: |
| 223 | +
|
| 224 | + ```bash |
| 225 | +
|
| 226 | + pip install altair-${{ steps.version.outputs.nightly_version |
| 227 | + }}-py3-none-any.whl |
| 228 | +
|
| 229 | + ``` |
| 230 | +
|
| 231 | +
|
| 232 | + ### From GitHub Repository |
| 233 | +
|
| 234 | + ```bash |
| 235 | +
|
| 236 | + pip install git+https://github.com/vega/altair.git@${{ github.sha }} |
| 237 | +
|
| 238 | + ``` |
| 239 | + draft: false |
| 240 | + prerelease: true |
| 241 | + - name: Upload wheel to release |
| 242 | + uses: actions/upload-release-asset@v1 |
| 243 | + env: |
| 244 | + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' |
| 245 | + with: |
| 246 | + upload_url: '${{ steps.create_release.outputs.upload_url }}' |
| 247 | + asset_path: >- |
| 248 | + ./dist/altair-${{ steps.version.outputs.nightly_version |
| 249 | + }}-py3-none-any.whl |
| 250 | + asset_name: 'altair-${{ steps.version.outputs.nightly_version }}-py3-none-any.whl' |
| 251 | + asset_content_type: application/octet-stream |
| 252 | + - name: Upload signatures to release |
| 253 | + uses: actions/upload-release-asset@v1 |
| 254 | + env: |
| 255 | + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' |
| 256 | + with: |
| 257 | + upload_url: '${{ steps.create_release.outputs.upload_url }}' |
| 258 | + asset_path: >- |
| 259 | + ./dist/altair-${{ steps.version.outputs.nightly_version |
| 260 | + }}-py3-none-any.whl.sig |
| 261 | + asset_name: >- |
| 262 | + altair-${{ steps.version.outputs.nightly_version |
| 263 | + }}-py3-none-any.whl.sig |
| 264 | + asset_content_type: application/octet-stream |
| 265 | + - name: Upload certificates to release |
| 266 | + uses: actions/upload-release-asset@v1 |
| 267 | + env: |
| 268 | + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' |
| 269 | + with: |
| 270 | + upload_url: '${{ steps.create_release.outputs.upload_url }}' |
| 271 | + asset_path: >- |
| 272 | + ./dist/altair-${{ steps.version.outputs.nightly_version |
| 273 | + }}-py3-none-any.whl.cert |
| 274 | + asset_name: >- |
| 275 | + altair-${{ steps.version.outputs.nightly_version |
| 276 | + }}-py3-none-any.whl.cert |
| 277 | + asset_content_type: application/octet-stream |
| 278 | + - name: Upload dependency diff to release |
| 279 | + if: steps.deps.outputs.dependency_changes == 'true' |
| 280 | + uses: actions/upload-release-asset@v1 |
| 281 | + env: |
| 282 | + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' |
| 283 | + with: |
| 284 | + upload_url: '${{ steps.create_release.outputs.upload_url }}' |
| 285 | + asset_path: ./dependency_diff.txt |
| 286 | + asset_name: dependency_diff.txt |
| 287 | + asset_content_type: text/plain |
| 288 | + - name: Upload binary changes to release |
| 289 | + if: steps.checksums.outputs.binary_changes == 'true' |
| 290 | + uses: actions/upload-release-asset@v1 |
| 291 | + env: |
| 292 | + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' |
| 293 | + with: |
| 294 | + upload_url: '${{ steps.create_release.outputs.upload_url }}' |
| 295 | + asset_path: ./binary_changes.txt |
| 296 | + asset_name: binary_changes.txt |
| 297 | + asset_content_type: text/plain |
| 298 | + - name: Upload full checksums to release |
| 299 | + uses: actions/upload-release-asset@v1 |
| 300 | + env: |
| 301 | + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' |
| 302 | + with: |
| 303 | + upload_url: '${{ steps.create_release.outputs.upload_url }}' |
| 304 | + asset_path: ./binary_checksums.txt |
| 305 | + asset_name: binary_checksums.txt |
| 306 | + asset_content_type: text/plain |
0 commit comments