commit
ab3456207b
5 changed files with 598 additions and 61 deletions
463
.github/workflows/release.yaml
vendored
Normal file
463
.github/workflows/release.yaml
vendored
Normal file
|
@ -0,0 +1,463 @@
|
||||||
|
name: release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Full build of the Mac assets
|
||||||
|
build-darwin:
|
||||||
|
runs-on: macos-latest
|
||||||
|
environment: release
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set Version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||||
|
echo "RELEASE_VERSION=$(echo ${GITHUB_REF_NAME} | cut -f1 -d-)" >> $GITHUB_ENV
|
||||||
|
- name: key
|
||||||
|
env:
|
||||||
|
MACOS_SIGNING_KEY: ${{ secrets.MACOS_SIGNING_KEY }}
|
||||||
|
MACOS_SIGNING_KEY_PASSWORD: ${{ secrets.MACOS_SIGNING_KEY_PASSWORD }}
|
||||||
|
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
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.22'
|
||||||
|
cache: true
|
||||||
|
- name: Build Darwin
|
||||||
|
env:
|
||||||
|
APPLE_IDENTITY: ${{ secrets.APPLE_IDENTITY }}
|
||||||
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
|
APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }}
|
||||||
|
APPLE_ID: ${{ vars.APPLE_ID }}
|
||||||
|
run: |
|
||||||
|
./scripts/build_darwin.sh
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dist-darwin
|
||||||
|
path: |
|
||||||
|
dist/*arwin*
|
||||||
|
!dist/*-cov
|
||||||
|
|
||||||
|
|
||||||
|
# Windows builds take a long time to both install the dependencies and build, so parallelize
|
||||||
|
# CPU generation step
|
||||||
|
generate-windows-cpu:
|
||||||
|
environment: release
|
||||||
|
runs-on: windows
|
||||||
|
env:
|
||||||
|
KEY_CONTAINER: ${{ vars.KEY_CONTAINER }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set Version
|
||||||
|
shell: bash
|
||||||
|
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||||
|
- uses: 'google-github-actions/auth@v2'
|
||||||
|
with:
|
||||||
|
project_id: 'ollama'
|
||||||
|
credentials_json: '${{ secrets.GOOGLE_SIGNING_CREDENTIALS }}'
|
||||||
|
- run: echo "${{ vars.OLLAMA_CERT }}" > ollama_inc.crt
|
||||||
|
- name: install Windows SDK 8.1 to get signtool
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
write-host "downloading SDK"
|
||||||
|
Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=323507" -OutFile "${env:RUNNER_TEMP}\sdksetup.exe"
|
||||||
|
Start-Process "${env:RUNNER_TEMP}\sdksetup.exe" -ArgumentList @("/q") -NoNewWindow -Wait
|
||||||
|
write-host "Win SDK 8.1 installed"
|
||||||
|
gci -path 'C:\Program Files (x86)\Windows Kits\' -r -fi 'signtool.exe'
|
||||||
|
- name: install signing plugin
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
write-host "downloading plugin"
|
||||||
|
Invoke-WebRequest -Uri "https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/cng-v1.0/kmscng-1.0-windows-amd64.zip" -OutFile "${env:RUNNER_TEMP}\plugin.zip"
|
||||||
|
Expand-Archive -Path "${env:RUNNER_TEMP}\plugin.zip" -DestinationPath ${env:RUNNER_TEMP}\plugin\
|
||||||
|
write-host "Installing plugin"
|
||||||
|
& "${env:RUNNER_TEMP}\plugin\*\kmscng.msi" /quiet
|
||||||
|
write-host "plugin installed"
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.22'
|
||||||
|
cache: true
|
||||||
|
- run: go get ./...
|
||||||
|
- run: |
|
||||||
|
$gopath=(get-command go).source | split-path -parent
|
||||||
|
& "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\Launch-VsDevShell.ps1"
|
||||||
|
cd $env:GITHUB_WORKSPACE
|
||||||
|
$env:CMAKE_SYSTEM_VERSION="10.0.22621.0"
|
||||||
|
$env:PATH="$gopath;$env:PATH"
|
||||||
|
go generate -x ./...
|
||||||
|
name: go generate
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: generate-windows-cpu
|
||||||
|
path: llm/llama.cpp/build/**/lib/*
|
||||||
|
|
||||||
|
# ROCm generation step
|
||||||
|
generate-windows-rocm:
|
||||||
|
environment: release
|
||||||
|
runs-on: windows
|
||||||
|
env:
|
||||||
|
KEY_CONTAINER: ${{ vars.KEY_CONTAINER }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set Version
|
||||||
|
shell: bash
|
||||||
|
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||||
|
- uses: 'google-github-actions/auth@v2'
|
||||||
|
with:
|
||||||
|
project_id: 'ollama'
|
||||||
|
credentials_json: '${{ secrets.GOOGLE_SIGNING_CREDENTIALS }}'
|
||||||
|
- run: echo "${{ vars.OLLAMA_CERT }}" > ollama_inc.crt
|
||||||
|
- name: install Windows SDK 8.1 to get signtool
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
write-host "downloading SDK"
|
||||||
|
Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=323507" -OutFile "${env:RUNNER_TEMP}\sdksetup.exe"
|
||||||
|
Start-Process "${env:RUNNER_TEMP}\sdksetup.exe" -ArgumentList @("/q") -NoNewWindow -Wait
|
||||||
|
write-host "Win SDK 8.1 installed"
|
||||||
|
gci -path 'C:\Program Files (x86)\Windows Kits\' -r -fi 'signtool.exe'
|
||||||
|
- name: install signing plugin
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
write-host "downloading plugin"
|
||||||
|
Invoke-WebRequest -Uri "https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/cng-v1.0/kmscng-1.0-windows-amd64.zip" -OutFile "${env:RUNNER_TEMP}\plugin.zip"
|
||||||
|
Expand-Archive -Path "${env:RUNNER_TEMP}\plugin.zip" -DestinationPath ${env:RUNNER_TEMP}\plugin\
|
||||||
|
write-host "Installing plugin"
|
||||||
|
& "${env:RUNNER_TEMP}\plugin\*\kmscng.msi" /quiet
|
||||||
|
write-host "plugin installed"
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.22'
|
||||||
|
cache: true
|
||||||
|
- name: "Install ROCm"
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
write-host "downloading AMD HIP Installer"
|
||||||
|
Invoke-WebRequest -Uri "https://download.amd.com/developer/eula/rocm-hub/AMD-Software-PRO-Edition-23.Q4-WinSvr2022-For-HIP.exe" -OutFile "${env:RUNNER_TEMP}\rocm-install.exe"
|
||||||
|
write-host "Installing AMD HIP"
|
||||||
|
Start-Process "${env:RUNNER_TEMP}\rocm-install.exe" -ArgumentList '-install' -NoNewWindow -Wait
|
||||||
|
write-host "Completed AMD HIP"
|
||||||
|
- name: "Verify ROCm"
|
||||||
|
run: |
|
||||||
|
& 'C:\Program Files\AMD\ROCm\*\bin\clang.exe' --version
|
||||||
|
- run: go get ./...
|
||||||
|
- run: |
|
||||||
|
$gopath=(get-command go).source | split-path -parent
|
||||||
|
& "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\Launch-VsDevShell.ps1"
|
||||||
|
cd $env:GITHUB_WORKSPACE
|
||||||
|
$env:CMAKE_SYSTEM_VERSION="10.0.22621.0"
|
||||||
|
$env:PATH="$gopath;$env:PATH"
|
||||||
|
$env:OLLAMA_SKIP_CPU_GENERATE="1"
|
||||||
|
$env:HIP_PATH=$(Resolve-Path 'C:\Program Files\AMD\ROCm\*\bin\clang.exe' | split-path | split-path)
|
||||||
|
go generate -x ./...
|
||||||
|
name: go generate
|
||||||
|
- name: "gather rocm dependencies"
|
||||||
|
run: |
|
||||||
|
$HIP_PATH=$(Resolve-Path 'C:\Program Files\AMD\ROCm\*\bin\clang.exe' | split-path | split-path)
|
||||||
|
md "dist\deps\bin\rocblas\library"
|
||||||
|
cp "${HIP_PATH}\bin\hipblas.dll" "dist\deps\bin\"
|
||||||
|
cp "${HIP_PATH}\bin\rocblas.dll" "dist\deps\bin\"
|
||||||
|
cp "${HIP_PATH}\bin\rocblas\library\*" "dist\deps\bin\rocblas\library\"
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: generate-windows-rocm
|
||||||
|
path: llm/llama.cpp/build/**/lib/*
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: windows-rocm-deps
|
||||||
|
path: dist/deps/*
|
||||||
|
|
||||||
|
# CUDA generation step
|
||||||
|
generate-windows-cuda:
|
||||||
|
environment: release
|
||||||
|
runs-on: windows
|
||||||
|
env:
|
||||||
|
KEY_CONTAINER: ${{ vars.KEY_CONTAINER }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set Version
|
||||||
|
shell: bash
|
||||||
|
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||||
|
- uses: 'google-github-actions/auth@v2'
|
||||||
|
with:
|
||||||
|
project_id: 'ollama'
|
||||||
|
credentials_json: '${{ secrets.GOOGLE_SIGNING_CREDENTIALS }}'
|
||||||
|
- run: echo "${{ vars.OLLAMA_CERT }}" > ollama_inc.crt
|
||||||
|
- name: install Windows SDK 8.1 to get signtool
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
write-host "downloading SDK"
|
||||||
|
Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=323507" -OutFile "${env:RUNNER_TEMP}\sdksetup.exe"
|
||||||
|
Start-Process "${env:RUNNER_TEMP}\sdksetup.exe" -ArgumentList @("/q") -NoNewWindow -Wait
|
||||||
|
write-host "Win SDK 8.1 installed"
|
||||||
|
gci -path 'C:\Program Files (x86)\Windows Kits\' -r -fi 'signtool.exe'
|
||||||
|
- name: install signing plugin
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
write-host "downloading plugin"
|
||||||
|
Invoke-WebRequest -Uri "https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/cng-v1.0/kmscng-1.0-windows-amd64.zip" -OutFile "${env:RUNNER_TEMP}\plugin.zip"
|
||||||
|
Expand-Archive -Path "${env:RUNNER_TEMP}\plugin.zip" -DestinationPath ${env:RUNNER_TEMP}\plugin\
|
||||||
|
write-host "Installing plugin"
|
||||||
|
& "${env:RUNNER_TEMP}\plugin\*\kmscng.msi" /quiet
|
||||||
|
write-host "plugin installed"
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.22'
|
||||||
|
cache: true
|
||||||
|
# TODO - consider replacing this action with a ps1 snippet to install
|
||||||
|
# This actions seems to fail sometimes with "no tools in cache" but a re-run of the failed job clears it
|
||||||
|
# https://developer.download.nvidia.com/compute/cuda/11.3.1/local_installers/cuda_11.3.1_465.89_win10.exe
|
||||||
|
- name: "Install CUDA"
|
||||||
|
uses: Jimver/cuda-toolkit@v0.2.14
|
||||||
|
id: cuda-toolkit
|
||||||
|
with:
|
||||||
|
cuda: '11.3.1'
|
||||||
|
- name: "Verify CUDA"
|
||||||
|
run: nvcc -V
|
||||||
|
- run: go get ./...
|
||||||
|
- name: go generate
|
||||||
|
run: |
|
||||||
|
$gopath=(get-command go).source | split-path -parent
|
||||||
|
& "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\Launch-VsDevShell.ps1"
|
||||||
|
cd $env:GITHUB_WORKSPACE
|
||||||
|
$env:CMAKE_SYSTEM_VERSION="10.0.22621.0"
|
||||||
|
$env:PATH="$gopath;$env:PATH"
|
||||||
|
$env:OLLAMA_SKIP_CPU_GENERATE="1"
|
||||||
|
go generate -x ./...
|
||||||
|
- name: "gather cuda dependencies"
|
||||||
|
run: |
|
||||||
|
$NVIDIA_DIR=(resolve-path 'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\*\bin\')[0]
|
||||||
|
md "dist\deps"
|
||||||
|
cp "${NVIDIA_DIR}\cudart64_*.dll" "dist\deps\"
|
||||||
|
cp "${NVIDIA_DIR}\cublas64_*.dll" "dist\deps\"
|
||||||
|
cp "${NVIDIA_DIR}\cublasLt64_*.dll" "dist\deps\"
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: generate-windows-cuda
|
||||||
|
path: llm/llama.cpp/build/**/lib/*
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: windows-cuda-deps
|
||||||
|
path: dist/deps/*
|
||||||
|
|
||||||
|
# Import the prior generation steps and build the final windows assets
|
||||||
|
build-windows:
|
||||||
|
environment: release
|
||||||
|
runs-on: windows
|
||||||
|
needs:
|
||||||
|
- generate-windows-cuda
|
||||||
|
- generate-windows-rocm
|
||||||
|
- generate-windows-cpu
|
||||||
|
env:
|
||||||
|
KEY_CONTAINER: ${{ vars.KEY_CONTAINER }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: Set Version
|
||||||
|
shell: bash
|
||||||
|
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||||
|
- uses: 'google-github-actions/auth@v2'
|
||||||
|
with:
|
||||||
|
project_id: 'ollama'
|
||||||
|
credentials_json: '${{ secrets.GOOGLE_SIGNING_CREDENTIALS }}'
|
||||||
|
- run: echo "${{ vars.OLLAMA_CERT }}" > ollama_inc.crt
|
||||||
|
- name: install Windows SDK 8.1 to get signtool
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
write-host "downloading SDK"
|
||||||
|
Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=323507" -OutFile "${env:RUNNER_TEMP}\sdksetup.exe"
|
||||||
|
Start-Process "${env:RUNNER_TEMP}\sdksetup.exe" -ArgumentList @("/q") -NoNewWindow -Wait
|
||||||
|
write-host "Win SDK 8.1 installed"
|
||||||
|
gci -path 'C:\Program Files (x86)\Windows Kits\' -r -fi 'signtool.exe'
|
||||||
|
- name: install signing plugin
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
write-host "downloading plugin"
|
||||||
|
Invoke-WebRequest -Uri "https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/cng-v1.0/kmscng-1.0-windows-amd64.zip" -OutFile "${env:RUNNER_TEMP}\plugin.zip"
|
||||||
|
Expand-Archive -Path "${env:RUNNER_TEMP}\plugin.zip" -DestinationPath ${env:RUNNER_TEMP}\plugin\
|
||||||
|
write-host "Installing plugin"
|
||||||
|
& "${env:RUNNER_TEMP}\plugin\*\kmscng.msi" /quiet
|
||||||
|
write-host "plugin installed"
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.22'
|
||||||
|
cache: true
|
||||||
|
- run: go get
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: generate-windows-cpu
|
||||||
|
path: llm/llama.cpp/build
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: generate-windows-cuda
|
||||||
|
path: llm/llama.cpp/build
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: windows-cuda-deps
|
||||||
|
path: dist/deps
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: windows-rocm-deps
|
||||||
|
path: dist/deps
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: generate-windows-rocm
|
||||||
|
path: llm/llama.cpp/build
|
||||||
|
- run: dir llm/llama.cpp/build
|
||||||
|
- run: |
|
||||||
|
$gopath=(get-command go).source | split-path -parent
|
||||||
|
& "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\Launch-VsDevShell.ps1"
|
||||||
|
cd $env:GITHUB_WORKSPACE
|
||||||
|
$env:CMAKE_SYSTEM_VERSION="10.0.22621.0"
|
||||||
|
$env:PATH="$gopath;$env:PATH"
|
||||||
|
$env:OLLAMA_SKIP_GENERATE="1"
|
||||||
|
$env:NVIDIA_DIR=$(resolve-path ".\dist\deps")
|
||||||
|
$env:HIP_PATH=$(resolve-path ".\dist\deps")
|
||||||
|
& .\scripts\build_windows.ps1
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dist-windows
|
||||||
|
path: dist/*.exe
|
||||||
|
|
||||||
|
# Linux x86 assets built using the container based build
|
||||||
|
build-linux-amd64:
|
||||||
|
environment: release
|
||||||
|
runs-on: linux
|
||||||
|
env:
|
||||||
|
OLLAMA_SKIP_MANIFEST_CREATE: "1"
|
||||||
|
BUILD_ARCH: amd64
|
||||||
|
PUSH: "1"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: Set Version
|
||||||
|
shell: bash
|
||||||
|
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ vars.DOCKER_USER }}
|
||||||
|
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
|
- run: |
|
||||||
|
./scripts/build_linux.sh
|
||||||
|
./scripts/build_docker.sh
|
||||||
|
mv dist/deps/* dist/
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dist-linux-amd64
|
||||||
|
path: |
|
||||||
|
dist/*linux*
|
||||||
|
!dist/*-cov
|
||||||
|
|
||||||
|
# Linux ARM assets built using the container based build
|
||||||
|
# (at present, docker isn't pre-installed on arm ubunutu images)
|
||||||
|
build-linux-arm64:
|
||||||
|
environment: release
|
||||||
|
runs-on: linux-arm64
|
||||||
|
env:
|
||||||
|
OLLAMA_SKIP_MANIFEST_CREATE: "1"
|
||||||
|
BUILD_ARCH: arm64
|
||||||
|
PUSH: "1"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: Set Version
|
||||||
|
shell: bash
|
||||||
|
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||||
|
- name: "Install Docker"
|
||||||
|
run: |
|
||||||
|
# Add Docker's official GPG key:
|
||||||
|
env
|
||||||
|
uname -a
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y ca-certificates curl
|
||||||
|
sudo install -m 0755 -d /etc/apt/keyrings
|
||||||
|
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
|
||||||
|
sudo chmod a+r /etc/apt/keyrings/docker.asc
|
||||||
|
|
||||||
|
# Add the repository to Apt sources:
|
||||||
|
echo \
|
||||||
|
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
|
||||||
|
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
sudo apt-get install acl
|
||||||
|
sudo setfacl --modify user:$USER:rw /var/run/docker.sock
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ vars.DOCKER_USER }}
|
||||||
|
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
|
- run: |
|
||||||
|
./scripts/build_linux.sh
|
||||||
|
./scripts/build_docker.sh
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dist-linux-arm64
|
||||||
|
path: |
|
||||||
|
dist/*linux*
|
||||||
|
!dist/*-cov
|
||||||
|
|
||||||
|
# Aggregate all the assets and ship a release
|
||||||
|
release:
|
||||||
|
needs:
|
||||||
|
- build-darwin
|
||||||
|
- build-windows
|
||||||
|
- build-linux-amd64
|
||||||
|
- build-linux-arm64
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: release
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
env:
|
||||||
|
OLLAMA_SKIP_IMAGE_BUILD: "1"
|
||||||
|
PUSH: "1"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set Version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||||
|
echo "RELEASE_VERSION=$(echo ${GITHUB_REF_NAME} | cut -f1 -d-)" >> $GITHUB_ENV
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ vars.DOCKER_USER }}
|
||||||
|
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
|
- run: ./scripts/build_docker.sh
|
||||||
|
- name: Retrieve built artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: dist
|
||||||
|
pattern: dist-*
|
||||||
|
merge-multiple: true
|
||||||
|
- run: |
|
||||||
|
ls -lh dist/
|
||||||
|
(cd dist; sha256sum * > sha256sum.txt)
|
||||||
|
cat dist/sha256sum.txt
|
||||||
|
- uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
name: ${{ env.RELEASE_VERSION }}
|
||||||
|
allowUpdates: true
|
||||||
|
artifacts: "dist/*"
|
||||||
|
draft: true
|
||||||
|
prerelease: true
|
||||||
|
omitBodyDuringUpdate: true
|
||||||
|
generateReleaseNotes: true
|
||||||
|
omitDraftDuringUpdate: true
|
||||||
|
omitPrereleaseDuringUpdate: true
|
||||||
|
replacesArtifacts: true
|
|
@ -101,6 +101,7 @@ ARG GOLANG_VERSION
|
||||||
WORKDIR /go/src/github.com/jmorganca/ollama
|
WORKDIR /go/src/github.com/jmorganca/ollama
|
||||||
COPY . .
|
COPY . .
|
||||||
COPY --from=cuda-build-arm64 /go/src/github.com/jmorganca/ollama/llm/llama.cpp/build/linux/ llm/llama.cpp/build/linux/
|
COPY --from=cuda-build-arm64 /go/src/github.com/jmorganca/ollama/llm/llama.cpp/build/linux/ llm/llama.cpp/build/linux/
|
||||||
|
RUN mkdir -p /go/src/github.com/jmorganca/ollama/dist/deps/
|
||||||
ARG GOFLAGS
|
ARG GOFLAGS
|
||||||
ARG CGO_CFLAGS
|
ARG CGO_CFLAGS
|
||||||
RUN go build -trimpath .
|
RUN go build -trimpath .
|
||||||
|
|
|
@ -68,8 +68,12 @@ function init_vars {
|
||||||
} else {
|
} else {
|
||||||
$script:CMAKE_CUDA_ARCHITECTURES=$env:CMAKE_CUDA_ARCHITECTURES
|
$script:CMAKE_CUDA_ARCHITECTURES=$env:CMAKE_CUDA_ARCHITECTURES
|
||||||
}
|
}
|
||||||
# Note: 10 Windows Kit signtool crashes with GCP's plugin
|
# Note: Windows Kits 10 signtool crashes with GCP's plugin
|
||||||
${script:SignTool}="C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe"
|
if ($null -eq $env:SIGN_TOOL) {
|
||||||
|
${script:SignTool}="C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe"
|
||||||
|
} else {
|
||||||
|
${script:SignTool}=${env:SIGN_TOOL}
|
||||||
|
}
|
||||||
if ("${env:KEY_CONTAINER}") {
|
if ("${env:KEY_CONTAINER}") {
|
||||||
${script:OLLAMA_CERT}=$(resolve-path "${script:SRC_DIR}\ollama_inc.crt")
|
${script:OLLAMA_CERT}=$(resolve-path "${script:SRC_DIR}\ollama_inc.crt")
|
||||||
}
|
}
|
||||||
|
@ -137,7 +141,7 @@ function sign {
|
||||||
if ("${env:KEY_CONTAINER}") {
|
if ("${env:KEY_CONTAINER}") {
|
||||||
write-host "Signing ${script:buildDir}/lib/*.dll"
|
write-host "Signing ${script:buildDir}/lib/*.dll"
|
||||||
foreach ($file in (get-childitem "${script:buildDir}/lib/*.dll")){
|
foreach ($file in (get-childitem "${script:buildDir}/lib/*.dll")){
|
||||||
& "${script:SignTool}" sign /v /fd sha256 /t http://timestamp.digicert.com /f "${script:OLLAMA_CERT}" `
|
& "${script:SignTool}" sign /v /debug /fd sha256 /t http://timestamp.digicert.com /f "${script:OLLAMA_CERT}" `
|
||||||
/csp "Google Cloud KMS Provider" /kc "${env:KEY_CONTAINER}" $file
|
/csp "Google Cloud KMS Provider" /kc "${env:KEY_CONTAINER}" $file
|
||||||
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
|
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
|
||||||
}
|
}
|
||||||
|
@ -187,32 +191,37 @@ apply_patches
|
||||||
|
|
||||||
$script:commonCpuDefs = @("-DCMAKE_POSITION_INDEPENDENT_CODE=on")
|
$script:commonCpuDefs = @("-DCMAKE_POSITION_INDEPENDENT_CODE=on")
|
||||||
|
|
||||||
init_vars
|
if ($null -eq ${env:OLLAMA_SKIP_CPU_GENERATE}) {
|
||||||
$script:cmakeDefs = $script:commonCpuDefs + @("-A", "x64", "-DLLAMA_AVX=off", "-DLLAMA_AVX2=off", "-DLLAMA_AVX512=off", "-DLLAMA_FMA=off", "-DLLAMA_F16C=off") + $script:cmakeDefs
|
|
||||||
$script:buildDir="${script:llamacppDir}/build/windows/${script:ARCH}/cpu"
|
|
||||||
write-host "Building LCD CPU"
|
|
||||||
build
|
|
||||||
install
|
|
||||||
sign
|
|
||||||
compress_libs
|
|
||||||
|
|
||||||
init_vars
|
init_vars
|
||||||
$script:cmakeDefs = $script:commonCpuDefs + @("-A", "x64", "-DLLAMA_AVX=on", "-DLLAMA_AVX2=off", "-DLLAMA_AVX512=off", "-DLLAMA_FMA=off", "-DLLAMA_F16C=off") + $script:cmakeDefs
|
$script:cmakeDefs = $script:commonCpuDefs + @("-A", "x64", "-DLLAMA_AVX=off", "-DLLAMA_AVX2=off", "-DLLAMA_AVX512=off", "-DLLAMA_FMA=off", "-DLLAMA_F16C=off") + $script:cmakeDefs
|
||||||
$script:buildDir="${script:llamacppDir}/build/windows/${script:ARCH}/cpu_avx"
|
$script:buildDir="${script:llamacppDir}/build/windows/${script:ARCH}/cpu"
|
||||||
write-host "Building AVX CPU"
|
write-host "Building LCD CPU"
|
||||||
build
|
build
|
||||||
install
|
install
|
||||||
sign
|
sign
|
||||||
compress_libs
|
compress_libs
|
||||||
|
|
||||||
init_vars
|
init_vars
|
||||||
$script:cmakeDefs = $script:commonCpuDefs + @("-A", "x64", "-DLLAMA_AVX=on", "-DLLAMA_AVX2=on", "-DLLAMA_AVX512=off", "-DLLAMA_FMA=on", "-DLLAMA_F16C=on") + $script:cmakeDefs
|
$script:cmakeDefs = $script:commonCpuDefs + @("-A", "x64", "-DLLAMA_AVX=on", "-DLLAMA_AVX2=off", "-DLLAMA_AVX512=off", "-DLLAMA_FMA=off", "-DLLAMA_F16C=off") + $script:cmakeDefs
|
||||||
$script:buildDir="${script:llamacppDir}/build/windows/${script:ARCH}/cpu_avx2"
|
$script:buildDir="${script:llamacppDir}/build/windows/${script:ARCH}/cpu_avx"
|
||||||
write-host "Building AVX2 CPU"
|
write-host "Building AVX CPU"
|
||||||
build
|
build
|
||||||
install
|
install
|
||||||
sign
|
sign
|
||||||
compress_libs
|
compress_libs
|
||||||
|
|
||||||
|
init_vars
|
||||||
|
$script:cmakeDefs = $script:commonCpuDefs + @("-A", "x64", "-DLLAMA_AVX=on", "-DLLAMA_AVX2=on", "-DLLAMA_AVX512=off", "-DLLAMA_FMA=on", "-DLLAMA_F16C=on") + $script:cmakeDefs
|
||||||
|
$script:buildDir="${script:llamacppDir}/build/windows/${script:ARCH}/cpu_avx2"
|
||||||
|
write-host "Building AVX2 CPU"
|
||||||
|
build
|
||||||
|
install
|
||||||
|
sign
|
||||||
|
compress_libs
|
||||||
|
} else {
|
||||||
|
write-host "Skipping CPU generation step as requested"
|
||||||
|
}
|
||||||
|
|
||||||
if ($null -ne $script:CUDA_LIB_DIR) {
|
if ($null -ne $script:CUDA_LIB_DIR) {
|
||||||
# Then build cuda as a dynamically loaded library
|
# Then build cuda as a dynamically loaded library
|
||||||
|
@ -270,4 +279,4 @@ if ($null -ne $env:HIP_PATH) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup
|
cleanup
|
||||||
write-host "`ngo generate completed"
|
write-host "`ngo generate completed. LLM runners: $(get-childitem -path ${script:SRC_DIR}\llm\llama.cpp\build\windows\${script:ARCH})"
|
||||||
|
|
|
@ -5,30 +5,73 @@ set -eu
|
||||||
export VERSION=${VERSION:-$(git describe --tags --first-parent --abbrev=7 --long --dirty --always | sed -e "s/^v//g")}
|
export VERSION=${VERSION:-$(git describe --tags --first-parent --abbrev=7 --long --dirty --always | sed -e "s/^v//g")}
|
||||||
export GOFLAGS="'-ldflags=-w -s \"-X=github.com/jmorganca/ollama/version.Version=$VERSION\" \"-X=github.com/jmorganca/ollama/server.mode=release\"'"
|
export GOFLAGS="'-ldflags=-w -s \"-X=github.com/jmorganca/ollama/version.Version=$VERSION\" \"-X=github.com/jmorganca/ollama/server.mode=release\"'"
|
||||||
|
|
||||||
IMAGE_NAME=${IMAGE_NAME:-"ollama/ollama"}
|
# We use 2 different image repositories to handle combining architecture images into multiarch manifest
|
||||||
BUILD_PLATFORM=${BUILD_PLATFORM:-"linux/arm64,linux/amd64"}
|
# (The ROCm image is x86 only and is not a multiarch manifest)
|
||||||
docker build \
|
# For developers, you can override the DOCKER_ORG to generate multiarch manifests
|
||||||
--load \
|
# DOCKER_ORG=jdoe PUSH=1 ./scripts/build_docker.sh
|
||||||
--platform=${BUILD_PLATFORM} \
|
DOCKER_ORG=${DOCKER_ORG:-"ollama"}
|
||||||
--build-arg=VERSION \
|
ARCH_IMAGE_REPO=${ARCH_IMAGE_REPO:-"${DOCKER_ORG}/release"}
|
||||||
--build-arg=GOFLAGS \
|
FINAL_IMAGE_REPO=${FINAL_IMAGE_REPO:-"${DOCKER_ORG}/ollama"}
|
||||||
-f Dockerfile \
|
|
||||||
-t ${IMAGE_NAME}:$VERSION \
|
|
||||||
.
|
|
||||||
|
|
||||||
docker build \
|
BUILD_ARCH=${BUILD_ARCH:-"amd64 arm64"}
|
||||||
--load \
|
|
||||||
--platform=linux/amd64 \
|
|
||||||
--build-arg=VERSION \
|
|
||||||
--build-arg=GOFLAGS \
|
|
||||||
--target runtime-rocm \
|
|
||||||
-f Dockerfile \
|
|
||||||
-t ${IMAGE_NAME}:$VERSION-rocm \
|
|
||||||
.
|
|
||||||
|
|
||||||
docker tag ${IMAGE_NAME}:$VERSION ${IMAGE_NAME}:latest
|
# Set PUSH to a non-empty string to trigger push instead of load
|
||||||
docker tag ${IMAGE_NAME}:$VERSION-rocm ${IMAGE_NAME}:rocm
|
PUSH=${PUSH:-""}
|
||||||
|
|
||||||
echo "To release, run:"
|
# In CI mode, we break things down
|
||||||
echo " docker push ${IMAGE_NAME}:$VERSION && docker push ${IMAGE_NAME}:latest"
|
OLLAMA_SKIP_MANIFEST_CREATE=${OLLAMA_SKIP_MANIFEST_CREATE:-""}
|
||||||
echo " docker push ${IMAGE_NAME}:$VERSION-rocm && docker push ${IMAGE_NAME}:rocm"
|
OLLAMA_SKIP_IMAGE_BUILD=${OLLAMA_SKIP_IMAGE_BUILD:-""}
|
||||||
|
|
||||||
|
if [ -z "${PUSH}" ] ; then
|
||||||
|
LOAD_OR_PUSH="--load"
|
||||||
|
else
|
||||||
|
echo "Will be pushing ${ARCH_IMAGE_REPO}:$VERSION for ${BUILD_ARCH}"
|
||||||
|
LOAD_OR_PUSH="--push"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${OLLAMA_SKIP_IMAGE_BUILD}" ]; then
|
||||||
|
for TARGETARCH in ${BUILD_ARCH}; do
|
||||||
|
docker build \
|
||||||
|
${LOAD_OR_PUSH} \
|
||||||
|
--platform=linux/${TARGETARCH} \
|
||||||
|
--build-arg=VERSION \
|
||||||
|
--build-arg=GOFLAGS \
|
||||||
|
-f Dockerfile \
|
||||||
|
-t ${ARCH_IMAGE_REPO}:$VERSION-${TARGETARCH} \
|
||||||
|
.
|
||||||
|
done
|
||||||
|
|
||||||
|
if echo ${BUILD_ARCH} | grep "amd64" > /dev/null; then
|
||||||
|
docker build \
|
||||||
|
${LOAD_OR_PUSH} \
|
||||||
|
--platform=linux/amd64 \
|
||||||
|
--build-arg=VERSION \
|
||||||
|
--build-arg=GOFLAGS \
|
||||||
|
--target runtime-rocm \
|
||||||
|
-f Dockerfile \
|
||||||
|
-t ${ARCH_IMAGE_REPO}:$VERSION-rocm \
|
||||||
|
.
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${OLLAMA_SKIP_MANIFEST_CREATE}" ]; then
|
||||||
|
if [ -n "${PUSH}" ]; then
|
||||||
|
docker manifest create ${FINAL_IMAGE_REPO}:$VERSION \
|
||||||
|
${ARCH_IMAGE_REPO}:$VERSION-amd64 \
|
||||||
|
${ARCH_IMAGE_REPO}:$VERSION-arm64
|
||||||
|
docker manifest push ${FINAL_IMAGE_REPO}:$VERSION
|
||||||
|
|
||||||
|
# For symmetry, tag/push the rocm image
|
||||||
|
if [ "${ARCH_IMAGE_REPO}" != "${FINAL_IMAGE_REPO}" ]; then
|
||||||
|
echo "Tagging and pushing rocm image"
|
||||||
|
docker pull ${ARCH_IMAGE_REPO}:$VERSION-rocm
|
||||||
|
docker tag ${ARCH_IMAGE_REPO}:$VERSION-rocm ${FINAL_IMAGE_REPO}:$VERSION-rocm
|
||||||
|
docker push ${FINAL_IMAGE_REPO}:$VERSION-rocm
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Skipping manifest generation when not pushing images are available locally as "
|
||||||
|
echo " ${ARCH_IMAGE_REPO}:$VERSION-amd64"
|
||||||
|
echo " ${ARCH_IMAGE_REPO}:$VERSION-arm64"
|
||||||
|
echo " ${ARCH_IMAGE_REPO}:$VERSION-rocm"
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -13,7 +13,21 @@ function checkEnv() {
|
||||||
$MSVC_INSTALL=(Get-CimInstance MSFT_VSInstance -Namespace root/cimv2/vs)[0].InstallLocation
|
$MSVC_INSTALL=(Get-CimInstance MSFT_VSInstance -Namespace root/cimv2/vs)[0].InstallLocation
|
||||||
$env:VCToolsRedistDir=(get-item "${MSVC_INSTALL}\VC\Redist\MSVC\*")[0]
|
$env:VCToolsRedistDir=(get-item "${MSVC_INSTALL}\VC\Redist\MSVC\*")[0]
|
||||||
}
|
}
|
||||||
$script:NVIDIA_DIR=(get-item "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v*\bin\")[0]
|
# Try to find the CUDA dir
|
||||||
|
if ($null -eq $env:NVIDIA_DIR) {
|
||||||
|
$d=(get-command -ea 'silentlycontinue' nvcc).path
|
||||||
|
if ($d -ne $null) {
|
||||||
|
$script:NVIDIA_DIR=($d| split-path -parent)
|
||||||
|
} else {
|
||||||
|
$cudaList=(get-item "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v*\bin\" -ea 'silentlycontinue')
|
||||||
|
if ($cudaList.length > 0) {
|
||||||
|
$script:NVIDIA_DIR=$cudaList[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$script:NVIDIA_DIR=$env:NVIDIA_DIR
|
||||||
|
}
|
||||||
|
|
||||||
$script:INNO_SETUP_DIR=(get-item "C:\Program Files*\Inno Setup*\")[0]
|
$script:INNO_SETUP_DIR=(get-item "C:\Program Files*\Inno Setup*\")[0]
|
||||||
|
|
||||||
$script:DEPS_DIR="${script:SRC_DIR}\dist\windeps"
|
$script:DEPS_DIR="${script:SRC_DIR}\dist\windeps"
|
||||||
|
@ -28,20 +42,23 @@ function checkEnv() {
|
||||||
} else {
|
} else {
|
||||||
$script:VERSION=$env:VERSION
|
$script:VERSION=$env:VERSION
|
||||||
}
|
}
|
||||||
$pattern = "(\d+[.]\d+[.]\d+)-(\d+)-"
|
$pattern = "(\d+[.]\d+[.]\d+).*"
|
||||||
if ($script:VERSION -match $pattern) {
|
if ($script:VERSION -match $pattern) {
|
||||||
$script:PKG_VERSION=$matches[1] + "." + $matches[2]
|
$script:PKG_VERSION=$matches[1]
|
||||||
} else {
|
} else {
|
||||||
$script:PKG_VERSION=$script:VERSION
|
$script:PKG_VERSION="0.0.0"
|
||||||
}
|
}
|
||||||
write-host "Building Ollama $script:VERSION with package version $script:PKG_VERSION"
|
write-host "Building Ollama $script:VERSION with package version $script:PKG_VERSION"
|
||||||
|
|
||||||
# Check for signing key
|
# Note: Windows Kits 10 signtool crashes with GCP's plugin
|
||||||
|
if ($null -eq $env:SIGN_TOOL) {
|
||||||
|
${script:SignTool}="C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe"
|
||||||
|
} else {
|
||||||
|
${script:SignTool}=${env:SIGN_TOOL}
|
||||||
|
}
|
||||||
if ("${env:KEY_CONTAINER}") {
|
if ("${env:KEY_CONTAINER}") {
|
||||||
${script:OLLAMA_CERT}=$(resolve-path "${script:SRC_DIR}\ollama_inc.crt")
|
${script:OLLAMA_CERT}=$(resolve-path "${script:SRC_DIR}\ollama_inc.crt")
|
||||||
Write-host "Code signing enabled"
|
Write-host "Code signing enabled"
|
||||||
# Note: 10 Windows Kit signtool crashes with GCP's plugin
|
|
||||||
${script:SignTool}="C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe"
|
|
||||||
} else {
|
} else {
|
||||||
write-host "Code signing disabled - please set KEY_CONTAINERS to sign and copy ollama_inc.crt to the top of the source tree"
|
write-host "Code signing disabled - please set KEY_CONTAINERS to sign and copy ollama_inc.crt to the top of the source tree"
|
||||||
}
|
}
|
||||||
|
@ -51,8 +68,12 @@ function checkEnv() {
|
||||||
|
|
||||||
function buildOllama() {
|
function buildOllama() {
|
||||||
write-host "Building ollama CLI"
|
write-host "Building ollama CLI"
|
||||||
& go generate ./...
|
if ($null -eq ${env:OLLAMA_SKIP_GENERATE}) {
|
||||||
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
|
& go generate ./...
|
||||||
|
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
|
||||||
|
} else {
|
||||||
|
write-host "Skipping generate step with OLLAMA_SKIP_GENERATE set"
|
||||||
|
}
|
||||||
& go build -trimpath -ldflags "-s -w -X=github.com/jmorganca/ollama/version.Version=$script:VERSION -X=github.com/jmorganca/ollama/server.mode=release" .
|
& go build -trimpath -ldflags "-s -w -X=github.com/jmorganca/ollama/version.Version=$script:VERSION -X=github.com/jmorganca/ollama/server.mode=release" .
|
||||||
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
|
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
|
||||||
if ("${env:KEY_CONTAINER}") {
|
if ("${env:KEY_CONTAINER}") {
|
||||||
|
|
Loading…
Reference in a new issue