name: release on: push: tags: - 'v*' env: CGO_CFLAGS: '-O3' CGO_CXXFLAGS: '-O3' jobs: setup-environment: runs-on: ubuntu-latest environment: release outputs: GOFLAGS: ${{ steps.goflags.outputs.GOFLAGS }} VERSION: ${{ steps.goflags.outputs.VERSION }} steps: - uses: actions/checkout@v4 - name: Set environment id: goflags run: | echo GOFLAGS="'-ldflags=-w -s \"-X=github.com/ollama/ollama/version.Version=${GITHUB_REF_NAME#v}\" \"-X=github.com/ollama/ollama/server.mode=release\"'" >>$GITHUB_OUTPUT echo VERSION="${GITHUB_REF_NAME#v}" >>$GITHUB_OUTPUT darwin-build: runs-on: macos-14-xlarge environment: release needs: setup-environment env: GOFLAGS: ${{ needs.setup-environment.outputs.GOFLAGS }} VERSION: ${{ needs.setup-environment.outputs.VERSION }} APPLE_IDENTITY: ${{ secrets.APPLE_IDENTITY }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }} APPLE_ID: ${{ vars.APPLE_ID }} MACOS_SIGNING_KEY: ${{ secrets.MACOS_SIGNING_KEY }} MACOS_SIGNING_KEY_PASSWORD: ${{ secrets.MACOS_SIGNING_KEY_PASSWORD }} CGO_CFLAGS: '-mmacosx-version-min=14.0 -O3' CGO_CXXFLAGS: '-mmacosx-version-min=14.0 -O3' CGO_LDFLAGS: '-mmacosx-version-min=14.0 -O3' steps: - uses: actions/checkout@v4 - run: | echo $MACOS_SIGNING_KEY | base64 --decode > certificate.p12 security create-keychain -p password build.keychain security default-keychain -s build.keychain security unlock-keychain -p password build.keychain security import certificate.p12 -k build.keychain -P $MACOS_SIGNING_KEY_PASSWORD -T /usr/bin/codesign security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k password build.keychain security set-keychain-settings -lut 3600 build.keychain - uses: actions/setup-go@v5 with: go-version-file: go.mod - run: | ./scripts/build_darwin.sh - name: Log build results run: | ls -l dist/ - uses: actions/upload-artifact@v4 with: name: bundles-darwin path: | dist/*.tgz dist/*.zip dist/*.dmg windows-depends: strategy: matrix: os: [windows] arch: [amd64] preset: ['CPU'] include: - os: windows arch: amd64 preset: 'CUDA 12' install: https://developer.download.nvidia.com/compute/cuda/12.8.0/local_installers/cuda_12.8.0_571.96_windows.exe cuda-components: - '"cudart"' - '"nvcc"' - '"cublas"' - '"cublas_dev"' cuda-version: '12.8' flags: '' - os: windows arch: amd64 preset: 'CUDA 13' install: https://developer.download.nvidia.com/compute/cuda/13.0.0/local_installers/cuda_13.0.0_windows.exe cuda-components: - '"cudart"' - '"nvcc"' - '"cublas"' - '"cublas_dev"' - '"crt"' - '"nvvm"' - '"nvptxcompiler"' cuda-version: '13.0' flags: '' - os: windows arch: amd64 preset: 'ROCm 6' install: https://download.amd.com/developer/eula/rocm-hub/AMD-Software-PRO-Edition-24.Q4-WinSvr2022-For-HIP.exe rocm-version: '6.2' flags: '-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_FLAGS="-parallel-jobs=4 -Wno-ignored-attributes -Wno-deprecated-pragma" -DCMAKE_CXX_FLAGS="-parallel-jobs=4 -Wno-ignored-attributes -Wno-deprecated-pragma"' runner_dir: 'rocm' - os: windows arch: amd64 preset: Vulkan install: https://sdk.lunarg.com/sdk/download/1.4.321.1/windows/vulkansdk-windows-X64-1.4.321.1.exe flags: '' runner_dir: 'vulkan' runs-on: ${{ matrix.arch == 'arm64' && format('{0}-{1}', matrix.os, matrix.arch) || matrix.os }} environment: release env: GOFLAGS: ${{ needs.setup-environment.outputs.GOFLAGS }} steps: - name: Install system dependencies run: | choco install -y --no-progress ccache ninja ccache -o cache_dir=${{ github.workspace }}\.ccache - if: startsWith(matrix.preset, 'CUDA ') || startsWith(matrix.preset, 'ROCm ') || startsWith(matrix.preset, 'Vulkan') id: cache-install uses: actions/cache/restore@v4 with: path: | C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA C:\Program Files\AMD\ROCm C:\VulkanSDK key: ${{ matrix.install }} - if: startsWith(matrix.preset, 'CUDA ') name: Install CUDA ${{ matrix.cuda-version }} run: | $ErrorActionPreference = "Stop" if ("${{ steps.cache-install.outputs.cache-hit }}" -ne 'true') { Invoke-WebRequest -Uri "${{ matrix.install }}" -OutFile "install.exe" $subpackages = @(${{ join(matrix.cuda-components, ', ') }}) | Foreach-Object {"${_}_${{ matrix.cuda-version }}"} Start-Process -FilePath .\install.exe -ArgumentList (@("-s") + $subpackages) -NoNewWindow -Wait } $cudaPath = (Resolve-Path "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\*").path echo "$cudaPath\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - if: startsWith(matrix.preset, 'ROCm') name: Install ROCm ${{ matrix.rocm-version }} run: | $ErrorActionPreference = "Stop" if ("${{ steps.cache-install.outputs.cache-hit }}" -ne 'true') { Invoke-WebRequest -Uri "${{ matrix.install }}" -OutFile "install.exe" Start-Process -FilePath .\install.exe -ArgumentList '-install' -NoNewWindow -Wait } $hipPath = (Resolve-Path "C:\Program Files\AMD\ROCm\*").path echo "$hipPath\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append echo "CC=$hipPath\bin\clang.exe" | Out-File -FilePath $env:GITHUB_ENV -Append echo "CXX=$hipPath\bin\clang++.exe" | Out-File -FilePath $env:GITHUB_ENV -Append echo "HIPCXX=$hipPath\bin\clang++.exe" | Out-File -FilePath $env:GITHUB_ENV -Append echo "HIP_PLATFORM=amd" | Out-File -FilePath $env:GITHUB_ENV -Append echo "CMAKE_PREFIX_PATH=$hipPath" | Out-File -FilePath $env:GITHUB_ENV -Append - if: matrix.preset == 'Vulkan' name: Install Vulkan ${{ matrix.rocm-version }} run: | $ErrorActionPreference = "Stop" if ("${{ steps.cache-install.outputs.cache-hit }}" -ne 'true') { Invoke-WebRequest -Uri "${{ matrix.install }}" -OutFile "install.exe" Start-Process -FilePath .\install.exe -ArgumentList "-c","--am","--al","in" -NoNewWindow -Wait } $vulkanPath = (Resolve-Path "C:\VulkanSDK\*").path echo "$vulkanPath\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append echo "VULKAN_SDK=$vulkanPath" >> $env:GITHUB_ENV - if: matrix.preset == 'CPU' run: | echo "CC=clang.exe" | Out-File -FilePath $env:GITHUB_ENV -Append echo "CXX=clang++.exe" | Out-File -FilePath $env:GITHUB_ENV -Append - if: ${{ !cancelled() && steps.cache-install.outputs.cache-hit != 'true' }} uses: actions/cache/save@v4 with: path: | C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA C:\Program Files\AMD\ROCm C:\VulkanSDK key: ${{ matrix.install }} - uses: actions/checkout@v4 - uses: actions/cache@v4 with: path: ${{ github.workspace }}\.ccache key: ccache-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.preset }} - name: Build target "${{ matrix.preset }}" run: | Import-Module 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\Microsoft.VisualStudio.DevShell.dll' Enter-VsDevShell -VsInstallPath 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise' -SkipAutomaticLocation -DevCmdArguments '-arch=x64 -no_logo' cmake --preset "${{ matrix.preset }}" ${{ matrix.flags }} --install-prefix "$((pwd).Path)\dist\${{ matrix.os }}-${{ matrix.arch }}" cmake --build --parallel ([Environment]::ProcessorCount) --preset "${{ matrix.preset }}" cmake --install build --component "${{ startsWith(matrix.preset, 'CUDA ') && 'CUDA' || startsWith(matrix.preset, 'ROCm ') && 'HIP' || 'CPU' }}" --strip Remove-Item -Path dist\lib\ollama\rocm\rocblas\library\*gfx906* -ErrorAction SilentlyContinue env: CMAKE_GENERATOR: Ninja - name: Log build results run: | gci -path .\dist -Recurse -File | ForEach-Object { get-filehash -path $_.FullName -Algorithm SHA256 } | format-list - uses: actions/upload-artifact@v4 with: name: depends-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.preset }} path: dist\* windows-build: strategy: matrix: os: [windows] arch: [amd64, arm64] include: - os: windows arch: amd64 llvmarch: x86_64 - os: windows arch: arm64 llvmarch: aarch64 runs-on: ${{ matrix.arch == 'arm64' && format('{0}-{1}', matrix.os, matrix.arch) || matrix.os }} environment: release needs: [setup-environment] env: GOFLAGS: ${{ needs.setup-environment.outputs.GOFLAGS }} VERSION: ${{ needs.setup-environment.outputs.VERSION }} steps: - name: Install ARM64 system dependencies if: matrix.arch == 'arm64' run: | $ErrorActionPreference = "Stop" Set-ExecutionPolicy Bypass -Scope Process -Force [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) echo "C:\ProgramData\chocolatey\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append Invoke-WebRequest -Uri https://aka.ms/vs/17/release/vc_redist.arm64.exe -OutFile "${{ runner.temp }}\vc_redist.arm64.exe" Start-Process -FilePath "${{ runner.temp }}\vc_redist.arm64.exe" -ArgumentList @("/install", "/quiet", "/norestart") -NoNewWindow -Wait choco install -y --no-progress git gzip echo "C:\Program Files\Git\cmd" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Install clang and gcc-compat run: | $ErrorActionPreference = "Stop" Set-ExecutionPolicy Bypass -Scope Process -Force Invoke-WebRequest -Uri "https://github.com/mstorsjo/llvm-mingw/releases/download/20240619/llvm-mingw-20240619-ucrt-${{ matrix.llvmarch }}.zip" -OutFile "${{ runner.temp }}\llvm-mingw-ucrt.zip" Expand-Archive -Path ${{ runner.temp }}\llvm-mingw-ucrt.zip -DestinationPath "C:\Program Files\" $installPath=(Resolve-Path -Path "C:\Program Files\llvm-mingw-*-ucrt*").path echo "$installPath\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: go.mod - name: Verify gcc is actually clang run: | $ErrorActionPreference='Continue' $version=& gcc -v 2>&1 $version=$version -join "`n" echo "gcc is $version" if ($version -notmatch 'clang') { echo "ERROR: GCC must be clang for proper utf16 handling" exit 1 } $ErrorActionPreference='Stop' - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "20" - run: | ./scripts/build_windows ollama app - name: Log build results run: | gci -path .\dist -Recurse -File | ForEach-Object { get-filehash -path $_.FullName -Algorithm SHA256 } | format-list - uses: actions/upload-artifact@v4 with: name: build-${{ matrix.os }}-${{ matrix.arch }} path: | dist\* windows-app: runs-on: windows environment: release needs: [windows-build, windows-depends] env: GOFLAGS: ${{ needs.setup-environment.outputs.GOFLAGS }} VERSION: ${{ needs.setup-environment.outputs.VERSION }} KEY_CONTAINER: ${{ vars.KEY_CONTAINER }} steps: - uses: actions/checkout@v4 - uses: google-github-actions/auth@v2 with: project_id: ollama credentials_json: ${{ secrets.GOOGLE_SIGNING_CREDENTIALS }} - run: | $ErrorActionPreference = "Stop" Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=323507" -OutFile "${{ runner.temp }}\sdksetup.exe" Start-Process "${{ runner.temp }}\sdksetup.exe" -ArgumentList @("/q") -NoNewWindow -Wait Invoke-WebRequest -Uri "https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/cng-v1.0/kmscng-1.0-windows-amd64.zip" -OutFile "${{ runner.temp }}\plugin.zip" Expand-Archive -Path "${{ runner.temp }}\plugin.zip" -DestinationPath "${{ runner.temp }}\plugin\" & "${{ runner.temp }}\plugin\*\kmscng.msi" /quiet echo "${{ vars.OLLAMA_CERT }}" >ollama_inc.crt - uses: actions/setup-go@v5 with: go-version-file: go.mod - uses: actions/download-artifact@v4 with: pattern: depends-windows* path: dist merge-multiple: true - uses: actions/download-artifact@v4 with: pattern: build-windows* path: dist merge-multiple: true - name: Log dist contents after download run: | gci -path .\dist -recurse - run: | ./scripts/build_windows.ps1 deps sign installer zip - name: Log contents after build run: | gci -path .\dist -Recurse -File | ForEach-Object { get-filehash -path $_.FullName -Algorithm SHA256 } | format-list - uses: actions/upload-artifact@v4 with: name: bundles-windows path: | dist/*.zip dist/OllamaSetup.exe linux-build: strategy: matrix: include: - os: linux arch: amd64 target: archive - os: linux arch: amd64 target: rocm - os: linux arch: arm64 target: archive runs-on: ${{ matrix.arch == 'arm64' && format('{0}-{1}', matrix.os, matrix.arch) || matrix.os }} environment: release needs: setup-environment env: GOFLAGS: ${{ needs.setup-environment.outputs.GOFLAGS }} steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v6 with: context: . platforms: ${{ matrix.os }}/${{ matrix.arch }} target: ${{ matrix.target }} build-args: | GOFLAGS=${{ env.GOFLAGS }} CGO_CFLAGS=${{ env.CGO_CFLAGS }} CGO_CXXFLAGS=${{ env.CGO_CXXFLAGS }} outputs: type=local,dest=dist/${{ matrix.os }}-${{ matrix.arch }} cache-from: type=registry,ref=${{ vars.DOCKER_REPO }}:latest cache-to: type=inline - run: | for COMPONENT in bin/* lib/ollama/*; do case "$COMPONENT" in bin/ollama) echo $COMPONENT >>ollama-${{ matrix.os }}-${{ matrix.arch }}.tar.in ;; lib/ollama/*.so*) echo $COMPONENT >>ollama-${{ matrix.os }}-${{ matrix.arch }}.tar.in ;; lib/ollama/cuda_v*) echo $COMPONENT >>ollama-${{ matrix.os }}-${{ matrix.arch }}.tar.in ;; lib/ollama/cuda_jetpack5) echo $COMPONENT >>ollama-${{ matrix.os }}-${{ matrix.arch }}-jetpack5.tar.in ;; lib/ollama/cuda_jetpack6) echo $COMPONENT >>ollama-${{ matrix.os }}-${{ matrix.arch }}-jetpack6.tar.in ;; lib/ollama/rocm) echo $COMPONENT >>ollama-${{ matrix.os }}-${{ matrix.arch }}-rocm.tar.in ;; esac done working-directory: dist/${{ matrix.os }}-${{ matrix.arch }} - run: | echo "Manifests" for ARCHIVE in dist/${{ matrix.os }}-${{ matrix.arch }}/*.tar.in ; do echo $ARCHIVE cat $ARCHIVE done - run: | for ARCHIVE in dist/${{ matrix.os }}-${{ matrix.arch }}/*.tar.in; do tar c -C dist/${{ matrix.os }}-${{ matrix.arch }} -T $ARCHIVE --owner 0 --group 0 | pigz -9vc >$(basename ${ARCHIVE//.*/}.tgz); done - uses: actions/upload-artifact@v4 with: name: bundles-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.target }} path: | *.tgz # Build each Docker variant (OS, arch, and flavor) separately. Using QEMU is unreliable and slower. docker-build-push: strategy: matrix: include: - os: linux arch: arm64 build-args: | CGO_CFLAGS CGO_CXXFLAGS GOFLAGS - os: linux arch: amd64 build-args: | CGO_CFLAGS CGO_CXXFLAGS GOFLAGS - os: linux arch: amd64 suffix: '-rocm' build-args: | CGO_CFLAGS CGO_CXXFLAGS GOFLAGS FLAVOR=rocm runs-on: ${{ matrix.arch == 'arm64' && format('{0}-{1}', matrix.os, matrix.arch) || matrix.os }} environment: release needs: setup-environment env: GOFLAGS: ${{ needs.setup-environment.outputs.GOFLAGS }} steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: username: ${{ vars.DOCKER_USER }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} - id: build-push uses: docker/build-push-action@v6 with: context: . platforms: ${{ matrix.os }}/${{ matrix.arch }} build-args: ${{ matrix.build-args }} outputs: type=image,name=${{ vars.DOCKER_REPO }},push-by-digest=true,name-canonical=true,push=true cache-from: type=registry,ref=${{ vars.DOCKER_REPO }}:latest cache-to: type=inline - run: | mkdir -p ${{ matrix.os }}-${{ matrix.arch }} echo "${{ steps.build-push.outputs.digest }}" >${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.suffix }}.txt working-directory: ${{ runner.temp }} - uses: actions/upload-artifact@v4 with: name: digest-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.suffix }} path: | ${{ runner.temp }}/${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.suffix }}.txt # Merge Docker images for the same flavor into a single multi-arch manifest docker-merge-push: strategy: matrix: suffix: ['', '-rocm'] runs-on: linux environment: release needs: [docker-build-push] steps: - uses: docker/login-action@v3 with: username: ${{ vars.DOCKER_USER }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} - id: metadata uses: docker/metadata-action@v4 with: flavor: | latest=false suffix=${{ matrix.suffix }} images: | ${{ vars.DOCKER_REPO }} tags: | type=ref,enable=true,priority=600,prefix=pr-,event=pr type=semver,pattern={{version}} - uses: actions/download-artifact@v4 with: pattern: digest-* path: ${{ runner.temp }} merge-multiple: true - run: | docker buildx imagetools create $(echo '${{ steps.metadata.outputs.json }}' | jq -cr '.tags | map("-t", .) | join(" ")') $(cat *-${{ matrix.suffix }}.txt | xargs printf '${{ vars.DOCKER_REPO }}@%s ') docker buildx imagetools inspect ${{ vars.DOCKER_REPO }}:${{ steps.metadata.outputs.version }} working-directory: ${{ runner.temp }} # Final release process release: runs-on: ubuntu-latest environment: release needs: [darwin-build, windows-app, linux-build] permissions: contents: write env: GH_TOKEN: ${{ github.token }} steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: pattern: bundles-* path: dist merge-multiple: true - name: Log dist contents run: | ls -l dist/ - name: Generate checksum file run: find . -type f -not -name 'sha256sum.txt' | xargs sha256sum | tee sha256sum.txt working-directory: dist - name: Create or update Release for tag run: | RELEASE_VERSION="$(echo ${GITHUB_REF_NAME} | cut -f1 -d-)" echo "Looking for existing release for ${RELEASE_VERSION}" OLD_TAG=$(gh release ls --json name,tagName | jq -r ".[] | select(.name == \"${RELEASE_VERSION}\") | .tagName") if [ -n "$OLD_TAG" ]; then echo "Updating release ${RELEASE_VERSION} to point to new tag ${GITHUB_REF_NAME}" gh release edit ${OLD_TAG} --tag ${GITHUB_REF_NAME} else echo "Creating new release ${RELEASE_VERSION} pointing to tag ${GITHUB_REF_NAME}" gh release create ${GITHUB_REF_NAME} \ --title ${RELEASE_VERSION} \ --draft \ --generate-notes \ --prerelease fi - name: Upload release artifacts run: | pids=() for payload in dist/*.txt dist/*.zip dist/*.tgz dist/*.exe dist/*.dmg ; do echo "Uploading $payload" gh release upload ${GITHUB_REF_NAME} $payload --clobber & pids[$!]=$! sleep 1 done echo "Waiting for uploads to complete" for pid in "${pids[*]}"; do wait $pid done echo "done"