mirror of
https://github.com/open-webui/open-webui.git
synced 2026-06-14 03:30:25 +00:00
refac: reorganize scripts and ci workflows
This commit is contained in:
@@ -1,82 +1,76 @@
|
|||||||
name: Feature Request
|
name: Feature Request
|
||||||
description: Suggest an idea for this project
|
description: Suggest a new feature or improvement
|
||||||
title: 'feat: '
|
title: 'feat: '
|
||||||
labels: ['triage']
|
labels: ['triage']
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
## Important Notes
|
## Before Submitting
|
||||||
### Before submitting
|
|
||||||
|
|
||||||
Please check the **open AND closed** [Issues](https://github.com/open-webui/open-webui/issues) AND [Discussions](https://github.com/open-webui/open-webui/discussions) to see if a similar request has been posted.
|
Please check **open AND closed** [Issues](https://github.com/open-webui/open-webui/issues) and [Discussions](https://github.com/open-webui/open-webui/discussions) for similar requests. If you find one, add your input there instead.
|
||||||
It's likely we're already tracking it! If you’re unsure, start a discussion post first.
|
|
||||||
|
|
||||||
#### Scope
|
### Scope Guidelines
|
||||||
|
|
||||||
If your feature request is likely to take more than a quick coding session to implement, test and verify, then open it in the **Ideas** section of the [Discussions](https://github.com/open-webui/open-webui/discussions) instead.
|
Feature requests that require significant implementation effort should be posted in the **Ideas** section of [Discussions](https://github.com/open-webui/open-webui/discussions) instead. We will move oversized feature requests to Discussions to keep the Issues tab focused on actionable items.
|
||||||
**We will close and force move your feature request to the Ideas section, if we believe your feature request is not trivial/quick to implement.**
|
|
||||||
This is to ensure the issues tab is used only for issues, quickly addressable feature requests and tracking tickets by the maintainers.
|
|
||||||
Other feature requests belong in the **Ideas** section of the [Discussions](https://github.com/open-webui/open-webui/discussions).
|
|
||||||
|
|
||||||
If your feature request might impact others in the community, definitely open a discussion instead and evaluate whether and how to implement it.
|
If your request might impact the broader community, please open a Discussion first so others can weigh in on the design.
|
||||||
|
|
||||||
This will help us efficiently focus on improving the project.
|
### Be Respectful
|
||||||
|
|
||||||
### Collaborate respectfully
|
Open WebUI is a volunteer-driven project maintained by a small team. We value constructive, positive communication. Please be mindful of maintainers' time and energy.
|
||||||
We value a **constructive attitude**, so please be mindful of your communication. If negativity is part of your approach, our capacity to engage may be limited. We're here to help if you're **open to learning** and **communicating positively**.
|
|
||||||
|
|
||||||
Remember:
|
|
||||||
- Open WebUI is a **volunteer-driven project**
|
|
||||||
- It's managed by a **single maintainer**
|
|
||||||
- It's supported by contributors who also have **full-time jobs**
|
|
||||||
|
|
||||||
We appreciate your time and ask that you **respect ours**.
|
|
||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
If you encounter an issue, we highly encourage you to submit a pull request or fork the project. We actively work to prevent contributor burnout to maintain the quality and continuity of Open WebUI.
|
|
||||||
|
|
||||||
### Bug reproducibility
|
If you encounter an issue, we encourage you to submit a pull request or fork the project. We actively work to prevent contributor burnout and maintain project quality.
|
||||||
If a bug cannot be reproduced with a `:main` or `:dev` Docker setup, or a `pip install` with Python 3.11, it may require additional help from the community. In such cases, we will move it to the "[issues](https://github.com/open-webui/open-webui/discussions/categories/issues)" Discussions section due to our limited resources. We encourage the community to assist with these issues. Remember, it’s not that the issue doesn’t exist; we need your help!
|
|
||||||
|
### Reproducibility
|
||||||
|
|
||||||
|
If a bug cannot be reproduced with a `:main` or `:dev` Docker setup, or a `pip install` with Python 3.11, it may be moved to the "Issues" section in Discussions for community assistance.
|
||||||
|
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: existing-issue
|
id: existing-issue
|
||||||
attributes:
|
attributes:
|
||||||
label: Check Existing Issues
|
label: Check Existing Issues
|
||||||
description: Please confirm that you've checked for existing similar requests
|
description: Confirm you have searched for similar requests.
|
||||||
options:
|
options:
|
||||||
- label: I have searched for all existing **open AND closed** issues and discussions for similar requests. I have found none that is comparable to my request.
|
- label: I have searched all existing **open AND closed** issues and discussions and found none comparable to my request.
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: feature-scope
|
id: feature-scope
|
||||||
attributes:
|
attributes:
|
||||||
label: Verify Feature Scope
|
label: Verify Feature Scope
|
||||||
description: Please confirm the feature's scope is within the described scope
|
description: Confirm this request belongs in Issues rather than Discussions.
|
||||||
options:
|
options:
|
||||||
- label: I have read through and understood the scope definition for feature requests in the Issues section. I believe my feature request meets the definition and belongs in the Issues section instead of the Discussions.
|
- label: I believe this feature request is appropriately scoped for the Issues section as described above.
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: problem-description
|
id: problem-description
|
||||||
attributes:
|
attributes:
|
||||||
label: Problem Description
|
label: Problem Description
|
||||||
description: Is your feature request related to a problem? Please provide a clear and concise description of what the problem is.
|
description: Is this related to a problem? Describe the pain point clearly.
|
||||||
placeholder: "Ex. I'm always frustrated when... / Not related to a problem"
|
placeholder: "e.g., I'm frustrated when..."
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: solution-description
|
id: solution-description
|
||||||
attributes:
|
attributes:
|
||||||
label: Desired Solution you'd like
|
label: Proposed Solution
|
||||||
description: Clearly describe what you want to happen.
|
description: Describe what you would like to happen.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: alternatives-considered
|
id: alternatives-considered
|
||||||
attributes:
|
attributes:
|
||||||
label: Alternatives Considered
|
label: Alternatives Considered
|
||||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
description: Describe any alternative solutions or workarounds you have considered.
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: additional-context
|
id: additional-context
|
||||||
attributes:
|
attributes:
|
||||||
label: Additional Context
|
label: Additional Context
|
||||||
description: Add any other context or screenshots about the feature request here.
|
description: Add any other context, mockups, or screenshots about the feature request.
|
||||||
|
|||||||
+10
-5
@@ -4,17 +4,22 @@ updates:
|
|||||||
directory: '/'
|
directory: '/'
|
||||||
schedule:
|
schedule:
|
||||||
interval: monthly
|
interval: monthly
|
||||||
target-branch: 'dev'
|
target-branch: dev
|
||||||
|
|
||||||
- package-ecosystem: pip
|
- package-ecosystem: pip
|
||||||
directory: '/backend'
|
directory: '/backend'
|
||||||
schedule:
|
schedule:
|
||||||
interval: monthly
|
interval: monthly
|
||||||
target-branch: 'dev'
|
target-branch: dev
|
||||||
|
|
||||||
- package-ecosystem: 'github-actions'
|
- package-ecosystem: github-actions
|
||||||
directory: '/'
|
directory: '/'
|
||||||
schedule:
|
schedule:
|
||||||
# Check for updates to GitHub Actions every week
|
|
||||||
interval: monthly
|
interval: monthly
|
||||||
target-branch: 'dev'
|
target-branch: dev
|
||||||
|
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: '/'
|
||||||
|
schedule:
|
||||||
|
interval: monthly
|
||||||
|
target-branch: dev
|
||||||
|
|||||||
@@ -19,75 +19,74 @@ The most impactful way to contribute to Open WebUI is through well-written bug r
|
|||||||
**Before submitting, make sure you've checked the following:**
|
**Before submitting, make sure you've checked the following:**
|
||||||
|
|
||||||
- [ ] **Linked Issue/Discussion:** This PR references an existing [Issue](https://github.com/open-webui/open-webui/issues) or [Discussion](https://github.com/open-webui/open-webui/discussions) — `Closes #___` / `Relates to #___`. If one does not exist, create one first. PRs without a linked issue or discussion may be closed without review.
|
- [ ] **Linked Issue/Discussion:** This PR references an existing [Issue](https://github.com/open-webui/open-webui/issues) or [Discussion](https://github.com/open-webui/open-webui/discussions) — `Closes #___` / `Relates to #___`. If one does not exist, create one first. PRs without a linked issue or discussion may be closed without review.
|
||||||
- [ ] **Target branch:** Verify that the pull request targets the `dev` branch. **PRs targeting `main` will be immediately closed.**
|
- [ ] **Target branch:** The pull request targets the `dev` branch. **PRs targeting `main` will be immediately closed.**
|
||||||
- [ ] **Description:** Provide a concise description of the changes made in this pull request down below.
|
- [ ] **Description:** A concise description of the changes is provided below.
|
||||||
- [ ] **Changelog:** Ensure a changelog entry following the format of [Keep a Changelog](https://keepachangelog.com/) is added at the bottom of the PR description.
|
- [ ] **Changelog:** A changelog entry following [Keep a Changelog](https://keepachangelog.com/) format is included at the bottom.
|
||||||
- [ ] **Documentation:** Add docs in [Open WebUI Docs Repository](https://github.com/open-webui/docs). Document user-facing behavior, environment variables, public APIs/interfaces, or deployment steps.
|
- [ ] **Documentation:** Relevant documentation has been added or updated in the [Open WebUI Docs Repository](https://github.com/open-webui/docs).
|
||||||
- [ ] **Dependencies:** Are there any new or upgraded dependencies? If so, explain why, update the changelog/docs, and include any compatibility notes. Actually run the code/function that uses updated library to ensure it doesn't crash.
|
- [ ] **Dependencies:** Any new or updated dependencies are explained, tested, and documented.
|
||||||
- [ ] **Testing:** Perform manual tests to **verify the implemented fix/feature works as intended AND does not break any other functionality**. Include reproducible steps to demonstrate the issue before the fix. Test edge cases (URL encoding, HTML entities, types). Take this as an opportunity to **make screenshots of the feature/fix and include them in the PR description**.
|
- [ ] **Testing:** Manual tests have been performed to verify the fix/feature works correctly and does not introduce regressions. Screenshots or recordings are included where applicable.
|
||||||
- [ ] **Agentic AI Code:** Confirm this Pull Request is **not written by any AI Agent** or has at least **gone through additional human review AND manual testing**. If any AI Agent is the co-author of this PR, it may lead to immediate closure of the PR.
|
- [ ] **No Unchecked AI Code:** This PR is either human-written or has undergone thorough human review AND manual testing. Unreviewed AI-generated PRs may be closed immediately.
|
||||||
- [ ] **Code review:** Have you performed a self-review of your code, addressing any coding standard issues and ensuring adherence to the project's coding standards?
|
- [ ] **Self-Review:** A self-review of the code has been performed, ensuring adherence to project coding standards.
|
||||||
- [ ] **Design & Architecture:** Prefer smart defaults over adding new settings; use local state for ephemeral UI logic. Open a Discussion for major architectural or UX changes.
|
- [ ] **Architecture:** Smart defaults are preferred over new settings. Local state is used for ephemeral UI logic. Major architectural or UX changes have been discussed first.
|
||||||
- [ ] **Git Hygiene:** Keep PRs atomic (one logical change). Clean up commits and rebase on `dev` to ensure no unrelated commits (e.g. from `main`) are included. Push updates to the existing PR branch instead of closing and reopening.
|
- [ ] **Git Hygiene:** The PR is atomic (one logical change), rebased on `dev`, and contains no unrelated commits.
|
||||||
- [ ] **Title Prefix:** To clearly categorize this pull request, prefix the pull request title using one of the following:
|
- [ ] **Title Prefix:** The PR title uses one of the following prefixes:
|
||||||
- **BREAKING CHANGE**: Significant changes that may affect compatibility
|
- **BREAKING CHANGE**: Changes affecting backward compatibility
|
||||||
- **build**: Changes that affect the build system or external dependencies
|
- **build**: Build system or dependency changes
|
||||||
- **ci**: Changes to our continuous integration processes or workflows
|
- **ci**: CI/CD workflow changes
|
||||||
- **chore**: Refactor, cleanup, or other non-functional code changes
|
- **chore**: Refactoring, cleanup, or non-functional changes
|
||||||
- **docs**: Documentation update or addition
|
- **docs**: Documentation additions or updates
|
||||||
- **feat**: Introduces a new feature or enhancement to the codebase
|
- **feat**: New features or enhancements
|
||||||
- **fix**: Bug fix or error correction
|
- **fix**: Bug fixes or corrections
|
||||||
- **i18n**: Internationalization or localization changes
|
- **i18n**: Internationalization or localization changes
|
||||||
- **perf**: Performance improvement
|
- **perf**: Performance improvements
|
||||||
- **refactor**: Code restructuring for better maintainability, readability, or scalability
|
- **refactor**: Code restructuring
|
||||||
- **style**: Changes that do not affect the meaning of the code (white space, formatting, missing semi-colons, etc.)
|
- **style**: Formatting changes (whitespace, semicolons, etc.)
|
||||||
- **test**: Adding missing tests or correcting existing tests
|
- **test**: Test additions or corrections
|
||||||
- **WIP**: Work in progress, a temporary label for incomplete or ongoing work
|
- **WIP**: Work in progress
|
||||||
|
|
||||||
# Changelog Entry
|
# Changelog Entry
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
|
|
||||||
- [Concisely describe the changes made in this pull request, including any relevant motivation and impact (e.g., fixing a bug, adding a feature, or improving performance)]
|
- [Describe the changes, including motivation and impact]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- [List any new features, functionalities, or additions]
|
- [New features, functionalities, or additions]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- [List any changes, updates, refactorings, or optimizations]
|
- [Changes, updates, refactorings, or optimizations]
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
- [List any deprecated functionality or features that have been removed]
|
- [Deprecated functionality or features]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- [List any removed features, files, or functionalities]
|
- [Removed features, files, or functionalities]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- [List any fixes, corrections, or bug fixes]
|
- [Bug fixes or corrections]
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
- [List any new or updated security-related changes, including vulnerability fixes]
|
- [Security-related changes or vulnerability fixes]
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|
||||||
- **BREAKING CHANGE**: [List any breaking changes affecting compatibility or functionality]
|
- **BREAKING CHANGE**: [Changes affecting compatibility or functionality]
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Additional Information
|
### Additional Information
|
||||||
|
|
||||||
- [Insert any additional context, notes, or explanations for the changes]
|
- [Any additional context, notes, or references to related issues/commits]
|
||||||
- [Reference any related issues, commits, or other relevant information]
|
|
||||||
|
|
||||||
### Screenshots or Videos
|
### Screenshots or Videos
|
||||||
|
|
||||||
- [Attach any relevant screenshots or videos demonstrating the changes]
|
- [Attach relevant screenshots or videos demonstrating the changes]
|
||||||
|
|
||||||
### Contributor License Agreement
|
### Contributor License Agreement
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Backend CI — Python formatting checks via Ruff
|
||||||
|
# Runs on pushes and PRs to main/dev when backend files change
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
name: Python CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, dev]
|
||||||
|
paths: ['backend/**', 'pyproject.toml', 'uv.lock']
|
||||||
|
pull_request:
|
||||||
|
branches: [main, dev]
|
||||||
|
paths: ['backend/**', 'pyproject.toml', 'uv.lock']
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: backend-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ── Ruff format check across supported Python versions ───────────────────
|
||||||
|
format-check:
|
||||||
|
name: Ruff Format (${{ matrix.python-version }})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
python-version: ['3.11', '3.12']
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
- name: Install formatter
|
||||||
|
run: pip install "ruff>=0.15.5"
|
||||||
|
|
||||||
|
- name: Verify formatting
|
||||||
|
run: ruff format --check . --exclude .venv --exclude venv
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main # or whatever branch you want to use
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
|
|
||||||
- name: Check for changes in package.json
|
|
||||||
run: |
|
|
||||||
git diff --cached --diff-filter=d package.json || {
|
|
||||||
echo "No changes to package.json"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Get version number from package.json
|
|
||||||
id: get_version
|
|
||||||
run: |
|
|
||||||
VERSION=$(jq -r '.version' package.json)
|
|
||||||
echo "::set-output name=version::$VERSION"
|
|
||||||
|
|
||||||
- name: Extract latest CHANGELOG entry
|
|
||||||
run: |
|
|
||||||
VERSION="${{ steps.get_version.outputs.version }}"
|
|
||||||
awk "/^## \[${VERSION}\]/{found=1; next} /^## \[/{if(found) exit} found{print}" CHANGELOG.md > /tmp/release-notes.md
|
|
||||||
|
|
||||||
- name: Create GitHub release
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
gh release create "v${{ steps.get_version.outputs.version }}" \
|
|
||||||
--title "v${{ steps.get_version.outputs.version }}" \
|
|
||||||
--notes-file /tmp/release-notes.md
|
|
||||||
|
|
||||||
- name: Upload package to GitHub release
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: package
|
|
||||||
path: |
|
|
||||||
.
|
|
||||||
!.git
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Trigger Docker build workflow
|
|
||||||
uses: actions/github-script@v8
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
github.rest.actions.createWorkflowDispatch({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
workflow_id: 'docker-build.yaml',
|
|
||||||
ref: 'v${{ steps.get_version.outputs.version }}',
|
|
||||||
})
|
|
||||||
@@ -1,917 +0,0 @@
|
|||||||
name: Create and publish Docker images with specific build args
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev
|
|
||||||
tags:
|
|
||||||
- v*
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: ghcr.io
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-main-image:
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
|
||||||
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
|
|
||||||
- name: Set repository and image name to lowercase
|
|
||||||
run: |
|
|
||||||
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: '${{ github.repository }}'
|
|
||||||
|
|
||||||
- name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker images (default latest tag)
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=tag
|
|
||||||
type=sha,prefix=git-
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
flavor: |
|
|
||||||
latest=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker cache
|
|
||||||
id: cache-meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }}
|
|
||||||
flavor: |
|
|
||||||
prefix=cache-${{ matrix.platform }}-
|
|
||||||
latest=false
|
|
||||||
|
|
||||||
- name: Build Docker image (latest)
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
id: build
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
platforms: ${{ matrix.platform }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
|
||||||
cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }}
|
|
||||||
cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max
|
|
||||||
sbom: true
|
|
||||||
build-args: |
|
|
||||||
BUILD_HASH=${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Export digest
|
|
||||||
run: |
|
|
||||||
mkdir -p /tmp/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "/tmp/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: digests-main-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: /tmp/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
build-cuda-image:
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
|
||||||
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
|
|
||||||
- name: Set repository and image name to lowercase
|
|
||||||
run: |
|
|
||||||
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: '${{ github.repository }}'
|
|
||||||
|
|
||||||
- name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Delete huge unnecessary tools folder
|
|
||||||
run: rm -rf /opt/hostedtoolcache
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker images (cuda tag)
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=tag
|
|
||||||
type=sha,prefix=git-
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=cuda
|
|
||||||
flavor: |
|
|
||||||
latest=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
suffix=-cuda,onlatest=true
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker cache
|
|
||||||
id: cache-meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }}
|
|
||||||
flavor: |
|
|
||||||
prefix=cache-cuda-${{ matrix.platform }}-
|
|
||||||
latest=false
|
|
||||||
|
|
||||||
- name: Build Docker image (cuda)
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
id: build
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
platforms: ${{ matrix.platform }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
|
||||||
cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }}
|
|
||||||
cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max
|
|
||||||
sbom: true
|
|
||||||
build-args: |
|
|
||||||
BUILD_HASH=${{ github.sha }}
|
|
||||||
USE_CUDA=true
|
|
||||||
|
|
||||||
- name: Export digest
|
|
||||||
run: |
|
|
||||||
mkdir -p /tmp/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "/tmp/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: digests-cuda-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: /tmp/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
build-cuda126-image:
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
|
||||||
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
|
|
||||||
- name: Set repository and image name to lowercase
|
|
||||||
run: |
|
|
||||||
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: '${{ github.repository }}'
|
|
||||||
|
|
||||||
- name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Delete huge unnecessary tools folder
|
|
||||||
run: rm -rf /opt/hostedtoolcache
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker images (cuda126 tag)
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=tag
|
|
||||||
type=sha,prefix=git-
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=cuda126
|
|
||||||
flavor: |
|
|
||||||
latest=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
suffix=-cuda126,onlatest=true
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker cache
|
|
||||||
id: cache-meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }}
|
|
||||||
flavor: |
|
|
||||||
prefix=cache-cuda126-${{ matrix.platform }}-
|
|
||||||
latest=false
|
|
||||||
|
|
||||||
- name: Build Docker image (cuda126)
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
id: build
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
platforms: ${{ matrix.platform }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
|
||||||
cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }}
|
|
||||||
cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max
|
|
||||||
sbom: true
|
|
||||||
build-args: |
|
|
||||||
BUILD_HASH=${{ github.sha }}
|
|
||||||
USE_CUDA=true
|
|
||||||
USE_CUDA_VER=cu126
|
|
||||||
|
|
||||||
- name: Export digest
|
|
||||||
run: |
|
|
||||||
mkdir -p /tmp/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "/tmp/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: digests-cuda126-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: /tmp/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
build-ollama-image:
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
|
||||||
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
|
|
||||||
- name: Set repository and image name to lowercase
|
|
||||||
run: |
|
|
||||||
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: '${{ github.repository }}'
|
|
||||||
|
|
||||||
- name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker images (ollama tag)
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=tag
|
|
||||||
type=sha,prefix=git-
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=ollama
|
|
||||||
flavor: |
|
|
||||||
latest=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
suffix=-ollama,onlatest=true
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker cache
|
|
||||||
id: cache-meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }}
|
|
||||||
flavor: |
|
|
||||||
prefix=cache-ollama-${{ matrix.platform }}-
|
|
||||||
latest=false
|
|
||||||
|
|
||||||
- name: Build Docker image (ollama)
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
id: build
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
platforms: ${{ matrix.platform }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
|
||||||
cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }}
|
|
||||||
cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max
|
|
||||||
sbom: true
|
|
||||||
build-args: |
|
|
||||||
BUILD_HASH=${{ github.sha }}
|
|
||||||
USE_OLLAMA=true
|
|
||||||
|
|
||||||
- name: Export digest
|
|
||||||
run: |
|
|
||||||
mkdir -p /tmp/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "/tmp/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: digests-ollama-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: /tmp/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
build-slim-image:
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
|
||||||
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
|
|
||||||
- name: Set repository and image name to lowercase
|
|
||||||
run: |
|
|
||||||
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: '${{ github.repository }}'
|
|
||||||
|
|
||||||
- name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker images (slim tag)
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=tag
|
|
||||||
type=sha,prefix=git-
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=slim
|
|
||||||
flavor: |
|
|
||||||
latest=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
suffix=-slim,onlatest=true
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker cache
|
|
||||||
id: cache-meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }}
|
|
||||||
flavor: |
|
|
||||||
prefix=cache-slim-${{ matrix.platform }}-
|
|
||||||
latest=false
|
|
||||||
|
|
||||||
- name: Build Docker image (slim)
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
id: build
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
platforms: ${{ matrix.platform }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
|
||||||
cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }}
|
|
||||||
cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max
|
|
||||||
sbom: true
|
|
||||||
build-args: |
|
|
||||||
BUILD_HASH=${{ github.sha }}
|
|
||||||
USE_SLIM=true
|
|
||||||
|
|
||||||
- name: Export digest
|
|
||||||
run: |
|
|
||||||
mkdir -p /tmp/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "/tmp/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: digests-slim-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: /tmp/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
merge-main-images:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build-main-image]
|
|
||||||
steps:
|
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
|
||||||
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
|
|
||||||
- name: Set repository and image name to lowercase
|
|
||||||
run: |
|
|
||||||
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: '${{ github.repository }}'
|
|
||||||
|
|
||||||
- name: Download digests
|
|
||||||
uses: actions/download-artifact@v5
|
|
||||||
with:
|
|
||||||
pattern: digests-main-*
|
|
||||||
path: /tmp/digests
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker images (default latest tag)
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=tag
|
|
||||||
type=sha,prefix=git-
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
flavor: |
|
|
||||||
latest=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: /tmp/digests
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
$(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *)
|
|
||||||
|
|
||||||
- name: Inspect image
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
|
||||||
|
|
||||||
merge-cuda-images:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build-cuda-image]
|
|
||||||
steps:
|
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
|
||||||
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
|
|
||||||
- name: Set repository and image name to lowercase
|
|
||||||
run: |
|
|
||||||
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: '${{ github.repository }}'
|
|
||||||
|
|
||||||
- name: Download digests
|
|
||||||
uses: actions/download-artifact@v5
|
|
||||||
with:
|
|
||||||
pattern: digests-cuda-*
|
|
||||||
path: /tmp/digests
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker images (default latest tag)
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=tag
|
|
||||||
type=sha,prefix=git-
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=cuda
|
|
||||||
flavor: |
|
|
||||||
latest=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
suffix=-cuda,onlatest=true
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: /tmp/digests
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
$(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *)
|
|
||||||
|
|
||||||
- name: Inspect image
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
|
||||||
|
|
||||||
merge-cuda126-images:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build-cuda126-image]
|
|
||||||
steps:
|
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
|
||||||
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
|
|
||||||
- name: Set repository and image name to lowercase
|
|
||||||
run: |
|
|
||||||
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: '${{ github.repository }}'
|
|
||||||
|
|
||||||
- name: Download digests
|
|
||||||
uses: actions/download-artifact@v5
|
|
||||||
with:
|
|
||||||
pattern: digests-cuda126-*
|
|
||||||
path: /tmp/digests
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker images (default latest tag)
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=tag
|
|
||||||
type=sha,prefix=git-
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=cuda126
|
|
||||||
flavor: |
|
|
||||||
latest=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
suffix=-cuda126,onlatest=true
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: /tmp/digests
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
$(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *)
|
|
||||||
|
|
||||||
- name: Inspect image
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
|
||||||
|
|
||||||
merge-ollama-images:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build-ollama-image]
|
|
||||||
steps:
|
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
|
||||||
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
|
|
||||||
- name: Set repository and image name to lowercase
|
|
||||||
run: |
|
|
||||||
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: '${{ github.repository }}'
|
|
||||||
|
|
||||||
- name: Download digests
|
|
||||||
uses: actions/download-artifact@v5
|
|
||||||
with:
|
|
||||||
pattern: digests-ollama-*
|
|
||||||
path: /tmp/digests
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker images (default ollama tag)
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=tag
|
|
||||||
type=sha,prefix=git-
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=ollama
|
|
||||||
flavor: |
|
|
||||||
latest=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
suffix=-ollama,onlatest=true
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: /tmp/digests
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
$(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *)
|
|
||||||
|
|
||||||
- name: Inspect image
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
|
||||||
|
|
||||||
merge-slim-images:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build-slim-image]
|
|
||||||
steps:
|
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
|
||||||
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
|
|
||||||
- name: Set repository and image name to lowercase
|
|
||||||
run: |
|
|
||||||
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: '${{ github.repository }}'
|
|
||||||
|
|
||||||
- name: Download digests
|
|
||||||
uses: actions/download-artifact@v5
|
|
||||||
with:
|
|
||||||
pattern: digests-slim-*
|
|
||||||
path: /tmp/digests
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata for Docker images (default slim tag)
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.FULL_IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=tag
|
|
||||||
type=sha,prefix=git-
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=slim
|
|
||||||
flavor: |
|
|
||||||
latest=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
suffix=-slim,onlatest=true
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: /tmp/digests
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
$(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *)
|
|
||||||
|
|
||||||
- name: Inspect image
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
|
||||||
|
|
||||||
# Copy images from GHCR to Docker Hub (best-effort, won't block GHCR)
|
|
||||||
copy-to-dockerhub:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
|
|
||||||
needs: [merge-main-images, merge-cuda-images, merge-cuda126-images, merge-ollama-images, merge-slim-images]
|
|
||||||
continue-on-error: true
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- variant: main
|
|
||||||
suffix: ""
|
|
||||||
- variant: cuda
|
|
||||||
suffix: "-cuda"
|
|
||||||
- variant: cuda126
|
|
||||||
suffix: "-cuda126"
|
|
||||||
- variant: ollama
|
|
||||||
suffix: "-ollama"
|
|
||||||
- variant: slim
|
|
||||||
suffix: "-slim"
|
|
||||||
steps:
|
|
||||||
- name: Set repository and image name to lowercase
|
|
||||||
run: |
|
|
||||||
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: '${{ github.repository }}'
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Determine source and destination tags
|
|
||||||
id: tags
|
|
||||||
run: |
|
|
||||||
DOCKERHUB_IMAGE="openwebui/open-webui"
|
|
||||||
SUFFIX="${{ matrix.suffix }}"
|
|
||||||
|
|
||||||
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
|
||||||
# For version tags: copy version tag and major.minor tag
|
|
||||||
VERSION="${{ github.ref_name }}"
|
|
||||||
VERSION="${VERSION#v}"
|
|
||||||
MAJOR_MINOR="${VERSION%.*}"
|
|
||||||
|
|
||||||
echo "tags<<EOF" >> $GITHUB_OUTPUT
|
|
||||||
echo "${VERSION}${SUFFIX}" >> $GITHUB_OUTPUT
|
|
||||||
echo "${MAJOR_MINOR}${SUFFIX}" >> $GITHUB_OUTPUT
|
|
||||||
echo "EOF" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
# For main branch
|
|
||||||
if [ -z "$SUFFIX" ]; then
|
|
||||||
echo "tags=latest" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
# e.g. latest-cuda -> also tag as just "cuda"
|
|
||||||
VARIANT_NAME="${SUFFIX#-}"
|
|
||||||
echo "tags<<EOF" >> $GITHUB_OUTPUT
|
|
||||||
echo "latest${SUFFIX}" >> $GITHUB_OUTPUT
|
|
||||||
echo "${VARIANT_NAME}" >> $GITHUB_OUTPUT
|
|
||||||
echo "EOF" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "dockerhub_image=${DOCKERHUB_IMAGE}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Copy images from GHCR to Docker Hub
|
|
||||||
run: |
|
|
||||||
DOCKERHUB_IMAGE="${{ steps.tags.outputs.dockerhub_image }}"
|
|
||||||
SUFFIX="${{ matrix.suffix }}"
|
|
||||||
|
|
||||||
# Determine the source tag on GHCR
|
|
||||||
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
|
||||||
VERSION="${{ github.ref_name }}"
|
|
||||||
VERSION="${VERSION#v}"
|
|
||||||
SOURCE_TAG="${VERSION}${SUFFIX}"
|
|
||||||
else
|
|
||||||
if [ -z "$SUFFIX" ]; then
|
|
||||||
SOURCE_TAG="latest"
|
|
||||||
else
|
|
||||||
SOURCE_TAG="latest${SUFFIX}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
SOURCE="${{ env.FULL_IMAGE_NAME }}:${SOURCE_TAG}"
|
|
||||||
|
|
||||||
echo "Copying from ${SOURCE} to Docker Hub..."
|
|
||||||
|
|
||||||
# Copy each destination tag
|
|
||||||
while IFS= read -r TAG; do
|
|
||||||
[ -z "$TAG" ] && continue
|
|
||||||
DEST="${DOCKERHUB_IMAGE}:${TAG}"
|
|
||||||
echo " -> ${DEST}"
|
|
||||||
docker buildx imagetools create -t "${DEST}" "${SOURCE}"
|
|
||||||
done <<< "${{ steps.tags.outputs.tags }}"
|
|
||||||
@@ -0,0 +1,311 @@
|
|||||||
|
name: Create and publish Docker images with specific build args
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- dev
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: docker-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
platform:
|
||||||
|
- arch: linux/amd64
|
||||||
|
runner: ubuntu-latest
|
||||||
|
- arch: linux/arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
variant:
|
||||||
|
- name: main
|
||||||
|
suffix: ""
|
||||||
|
build_args: ""
|
||||||
|
free_disk: false
|
||||||
|
- name: cuda
|
||||||
|
suffix: "-cuda"
|
||||||
|
build_args: "USE_CUDA=true"
|
||||||
|
free_disk: true
|
||||||
|
- name: cuda126
|
||||||
|
suffix: "-cuda126"
|
||||||
|
build_args: |
|
||||||
|
USE_CUDA=true
|
||||||
|
USE_CUDA_VER=cu126
|
||||||
|
free_disk: true
|
||||||
|
- name: ollama
|
||||||
|
suffix: "-ollama"
|
||||||
|
build_args: "USE_OLLAMA=true"
|
||||||
|
free_disk: false
|
||||||
|
- name: slim
|
||||||
|
suffix: "-slim"
|
||||||
|
build_args: "USE_SLIM=true"
|
||||||
|
free_disk: false
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Prepare environment
|
||||||
|
run: |
|
||||||
|
echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV}
|
||||||
|
echo "FULL_IMAGE_NAME=${REGISTRY}/${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV}
|
||||||
|
platform=${{ matrix.platform.arch }}
|
||||||
|
echo "PLATFORM_PAIR=${platform//\//-}" >> ${GITHUB_ENV}
|
||||||
|
|
||||||
|
- name: Free disk space
|
||||||
|
if: matrix.variant.free_disk
|
||||||
|
run: rm -rf /opt/hostedtoolcache
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata for Docker images
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.FULL_IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=tag
|
||||||
|
type=sha,prefix=git-
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
${{ matrix.variant.suffix != '' && format('type=raw,enable={0},prefix=,suffix=,value={1}', github.ref == 'refs/heads/main', matrix.variant.name) || '' }}
|
||||||
|
flavor: |
|
||||||
|
latest=${{ github.ref == 'refs/heads/main' }}
|
||||||
|
${{ matrix.variant.suffix != '' && format('suffix={0},onlatest=true', matrix.variant.suffix) || '' }}
|
||||||
|
|
||||||
|
- name: Extract metadata for Docker cache
|
||||||
|
id: cache-meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.FULL_IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }}
|
||||||
|
flavor: |
|
||||||
|
prefix=cache-${{ matrix.variant.name }}-${{ matrix.platform.arch }}-
|
||||||
|
latest=false
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
id: build
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
platforms: ${{ matrix.platform.arch }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
||||||
|
cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }}
|
||||||
|
cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max
|
||||||
|
sbom: true
|
||||||
|
build-args: |
|
||||||
|
BUILD_HASH=${{ github.sha }}
|
||||||
|
${{ matrix.variant.build_args }}
|
||||||
|
|
||||||
|
- name: Export digest
|
||||||
|
run: |
|
||||||
|
mkdir -p /tmp/digests
|
||||||
|
digest="${{ steps.build.outputs.digest }}"
|
||||||
|
touch "/tmp/digests/${digest#sha256:}"
|
||||||
|
|
||||||
|
- name: Upload digest
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: digests-${{ matrix.variant.name }}-${{ env.PLATFORM_PAIR }}
|
||||||
|
path: /tmp/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
merge:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
variant:
|
||||||
|
- name: main
|
||||||
|
suffix: ""
|
||||||
|
- name: cuda
|
||||||
|
suffix: "-cuda"
|
||||||
|
- name: cuda126
|
||||||
|
suffix: "-cuda126"
|
||||||
|
- name: ollama
|
||||||
|
suffix: "-ollama"
|
||||||
|
- name: slim
|
||||||
|
suffix: "-slim"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Prepare environment
|
||||||
|
run: |
|
||||||
|
echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV}
|
||||||
|
echo "FULL_IMAGE_NAME=${REGISTRY}/${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV}
|
||||||
|
|
||||||
|
- name: Download digests
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: digests-${{ matrix.variant.name }}-*
|
||||||
|
path: /tmp/digests
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata for Docker images
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.FULL_IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=tag
|
||||||
|
type=sha,prefix=git-
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
${{ matrix.variant.suffix != '' && format('type=raw,enable={0},prefix=,suffix=,value={1}', github.ref == 'refs/heads/main', matrix.variant.name) || '' }}
|
||||||
|
flavor: |
|
||||||
|
latest=${{ github.ref == 'refs/heads/main' }}
|
||||||
|
${{ matrix.variant.suffix != '' && format('suffix={0},onlatest=true', matrix.variant.suffix) || '' }}
|
||||||
|
|
||||||
|
- name: Create manifest list and push
|
||||||
|
working-directory: /tmp/digests
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools create \
|
||||||
|
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||||
|
$(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *)
|
||||||
|
|
||||||
|
- name: Inspect image
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
||||||
|
|
||||||
|
copy-to-dockerhub:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
|
||||||
|
needs: [merge]
|
||||||
|
continue-on-error: true
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- variant: main
|
||||||
|
suffix: ""
|
||||||
|
- variant: cuda
|
||||||
|
suffix: "-cuda"
|
||||||
|
- variant: cuda126
|
||||||
|
suffix: "-cuda126"
|
||||||
|
- variant: ollama
|
||||||
|
suffix: "-ollama"
|
||||||
|
- variant: slim
|
||||||
|
suffix: "-slim"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Prepare environment
|
||||||
|
run: |
|
||||||
|
echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV}
|
||||||
|
echo "FULL_IMAGE_NAME=${REGISTRY}/${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Determine source and destination tags
|
||||||
|
id: tags
|
||||||
|
run: |
|
||||||
|
DOCKERHUB_IMAGE="openwebui/open-webui"
|
||||||
|
SUFFIX="${{ matrix.suffix }}"
|
||||||
|
|
||||||
|
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
VERSION="${VERSION#v}"
|
||||||
|
MAJOR_MINOR="${VERSION%.*}"
|
||||||
|
|
||||||
|
echo "tags<<EOF" >> $GITHUB_OUTPUT
|
||||||
|
echo "${VERSION}${SUFFIX}" >> $GITHUB_OUTPUT
|
||||||
|
echo "${MAJOR_MINOR}${SUFFIX}" >> $GITHUB_OUTPUT
|
||||||
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
if [ -z "$SUFFIX" ]; then
|
||||||
|
echo "tags=latest" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
VARIANT_NAME="${SUFFIX#-}"
|
||||||
|
echo "tags<<EOF" >> $GITHUB_OUTPUT
|
||||||
|
echo "latest${SUFFIX}" >> $GITHUB_OUTPUT
|
||||||
|
echo "${VARIANT_NAME}" >> $GITHUB_OUTPUT
|
||||||
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "dockerhub_image=${DOCKERHUB_IMAGE}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Copy images from GHCR to Docker Hub
|
||||||
|
run: |
|
||||||
|
DOCKERHUB_IMAGE="${{ steps.tags.outputs.dockerhub_image }}"
|
||||||
|
SUFFIX="${{ matrix.suffix }}"
|
||||||
|
|
||||||
|
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||||||
|
VERSION="${{ github.ref_name }}"
|
||||||
|
VERSION="${VERSION#v}"
|
||||||
|
SOURCE_TAG="${VERSION}${SUFFIX}"
|
||||||
|
else
|
||||||
|
if [ -z "$SUFFIX" ]; then
|
||||||
|
SOURCE_TAG="latest"
|
||||||
|
else
|
||||||
|
SOURCE_TAG="latest${SUFFIX}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
SOURCE="${{ env.FULL_IMAGE_NAME }}:${SOURCE_TAG}"
|
||||||
|
|
||||||
|
echo "Copying from ${SOURCE} to Docker Hub..."
|
||||||
|
|
||||||
|
while IFS= read -r TAG; do
|
||||||
|
[ -z "$TAG" ] && continue
|
||||||
|
DEST="${DOCKERHUB_IMAGE}:${TAG}"
|
||||||
|
echo " -> ${DEST}"
|
||||||
|
docker buildx imagetools create -t "${DEST}" "${SOURCE}"
|
||||||
|
done <<< "${{ steps.tags.outputs.tags }}"
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
name: Python CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev
|
|
||||||
paths:
|
|
||||||
- 'backend/**'
|
|
||||||
- 'pyproject.toml'
|
|
||||||
- 'uv.lock'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev
|
|
||||||
paths:
|
|
||||||
- 'backend/**'
|
|
||||||
- 'pyproject.toml'
|
|
||||||
- 'uv.lock'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: 'Format Backend'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
python-version:
|
|
||||||
- 3.11.x
|
|
||||||
- 3.12.x
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v5
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: '${{ matrix.python-version }}'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install "ruff>=0.15.5"
|
|
||||||
|
|
||||||
- name: Ruff format check
|
|
||||||
run: ruff format --check . --exclude .venv --exclude venv
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
name: Frontend Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev
|
|
||||||
paths-ignore:
|
|
||||||
- 'backend/**'
|
|
||||||
- 'pyproject.toml'
|
|
||||||
- 'uv.lock'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev
|
|
||||||
paths-ignore:
|
|
||||||
- 'backend/**'
|
|
||||||
- 'pyproject.toml'
|
|
||||||
- 'uv.lock'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: 'Format & Build Frontend'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repository
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v5
|
|
||||||
with:
|
|
||||||
node-version: '22'
|
|
||||||
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: npm install --force
|
|
||||||
|
|
||||||
- name: Format Frontend
|
|
||||||
run: npm run format
|
|
||||||
|
|
||||||
- name: Run i18next
|
|
||||||
run: npm run i18n:parse
|
|
||||||
|
|
||||||
- name: Check for Changes After Format
|
|
||||||
run: git diff --exit-code
|
|
||||||
|
|
||||||
- name: Build Frontend
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
test-frontend:
|
|
||||||
name: 'Frontend Unit Tests'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repository
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v5
|
|
||||||
with:
|
|
||||||
node-version: '22'
|
|
||||||
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: npm ci --force
|
|
||||||
|
|
||||||
- name: Run vitest
|
|
||||||
run: npm run test:frontend
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Frontend CI — Lint, format check, build, and unit tests
|
||||||
|
# Runs on pushes and PRs to main/dev, skipping backend-only changes
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
name: Frontend Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, dev]
|
||||||
|
paths-ignore: ['backend/**', 'pyproject.toml', 'uv.lock']
|
||||||
|
pull_request:
|
||||||
|
branches: [main, dev]
|
||||||
|
paths-ignore: ['backend/**', 'pyproject.toml', 'uv.lock']
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: frontend-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ── Format, i18n, and production build ────────────────────────────────────
|
||||||
|
format-and-build:
|
||||||
|
name: Format & Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v5
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm install --force
|
||||||
|
|
||||||
|
- name: Verify code formatting
|
||||||
|
run: npm run format
|
||||||
|
|
||||||
|
- name: Verify i18n strings
|
||||||
|
run: npm run i18n:parse
|
||||||
|
|
||||||
|
- name: Ensure working tree is clean
|
||||||
|
run: git diff --exit-code
|
||||||
|
|
||||||
|
- name: Production build
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
# ── Vitest unit tests ────────────────────────────────────────────────────
|
||||||
|
unit-tests:
|
||||||
|
name: Unit Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v5
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
|
||||||
|
- name: Install dependencies (frozen lockfile)
|
||||||
|
run: npm ci --force
|
||||||
|
|
||||||
|
- name: Execute test suite
|
||||||
|
run: npm run test:frontend
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Release — Create GitHub release from CHANGELOG, trigger Docker builds
|
||||||
|
# Runs on pushes to main when package.json version changes
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: release-${{ github.ref }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ── Create release and trigger downstream workflows ──────────────────────
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Abort if package.json unchanged
|
||||||
|
run: |
|
||||||
|
git diff --cached --diff-filter=d package.json || {
|
||||||
|
echo "package.json not modified — skipping release"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Read version
|
||||||
|
id: pkg
|
||||||
|
run: echo "version=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Extract release notes from CHANGELOG
|
||||||
|
run: |
|
||||||
|
VER="${{ steps.pkg.outputs.version }}"
|
||||||
|
awk "/^## \[${VER}\]/{found=1; next} /^## \[/{if(found) exit} found{print}" \
|
||||||
|
CHANGELOG.md > /tmp/release-notes.md
|
||||||
|
|
||||||
|
- name: Publish GitHub release
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh release create "v${{ steps.pkg.outputs.version }}" \
|
||||||
|
--title "v${{ steps.pkg.outputs.version }}" \
|
||||||
|
--notes-file /tmp/release-notes.md
|
||||||
|
|
||||||
|
- name: Archive source
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-archive
|
||||||
|
path: |
|
||||||
|
.
|
||||||
|
!.git
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Dispatch Docker build
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
github.rest.actions.createWorkflowDispatch({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
workflow_id: 'docker.yaml',
|
||||||
|
ref: 'v${{ steps.pkg.outputs.version }}',
|
||||||
|
})
|
||||||
+54
-39
@@ -1,86 +1,101 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Container entry point for Open WebUI.
|
||||||
|
# Handles secret key generation, optional Ollama/CUDA/Playwright setup,
|
||||||
|
# HuggingFace Space deployment, and launches the uvicorn server.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
|
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
|
||||||
cd "$SCRIPT_DIR" || exit
|
cd "$SCRIPT_DIR" || exit 1
|
||||||
|
|
||||||
|
# ── Playwright browser installation (if configured) ──────────────────────────
|
||||||
|
|
||||||
# Add conditional Playwright browser installation
|
|
||||||
if [[ "${WEB_LOADER_ENGINE,,}" == "playwright" ]]; then
|
if [[ "${WEB_LOADER_ENGINE,,}" == "playwright" ]]; then
|
||||||
if [[ -z "${PLAYWRIGHT_WS_URL}" ]]; then
|
if [[ -z "${PLAYWRIGHT_WS_URL:-}" ]]; then
|
||||||
echo "Installing Playwright browsers..."
|
echo "Installing Playwright Chromium browser..."
|
||||||
playwright install chromium
|
playwright install chromium
|
||||||
playwright install-deps chromium
|
playwright install-deps chromium
|
||||||
fi
|
fi
|
||||||
|
|
||||||
python -c "import nltk; nltk.download('punkt_tab')"
|
python -c "import nltk; nltk.download('punkt_tab')"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "${WEBUI_SECRET_KEY_FILE}" ]; then
|
# ── Secret key setup ─────────────────────────────────────────────────────────
|
||||||
KEY_FILE="${WEBUI_SECRET_KEY_FILE}"
|
|
||||||
else
|
|
||||||
KEY_FILE=".webui_secret_key"
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
KEY_FILE="${WEBUI_SECRET_KEY_FILE:-.webui_secret_key}"
|
||||||
PORT="${PORT:-8080}"
|
PORT="${PORT:-8080}"
|
||||||
HOST="${HOST:-0.0.0.0}"
|
HOST="${HOST:-0.0.0.0}"
|
||||||
if test "$WEBUI_SECRET_KEY $WEBUI_JWT_SECRET_KEY" = " "; then
|
|
||||||
echo "Loading WEBUI_SECRET_KEY from file, not provided as an environment variable."
|
|
||||||
|
|
||||||
if ! [ -e "$KEY_FILE" ]; then
|
if [[ -z "${WEBUI_SECRET_KEY:-}" && -z "${WEBUI_JWT_SECRET_KEY:-}" ]]; then
|
||||||
echo "Generating WEBUI_SECRET_KEY"
|
echo "No WEBUI_SECRET_KEY environment variable set, loading from file."
|
||||||
# Generate a random value to use as a WEBUI_SECRET_KEY in case the user didn't provide one.
|
|
||||||
echo $(head -c 12 /dev/random | base64) > "$KEY_FILE"
|
if [[ ! -f "$KEY_FILE" ]]; then
|
||||||
|
echo "Generating new WEBUI_SECRET_KEY..."
|
||||||
|
head -c 12 /dev/random | base64 > "$KEY_FILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Loading WEBUI_SECRET_KEY from $KEY_FILE"
|
echo "Loading WEBUI_SECRET_KEY from ${KEY_FILE}"
|
||||||
WEBUI_SECRET_KEY=$(cat "$KEY_FILE")
|
WEBUI_SECRET_KEY=$(cat "$KEY_FILE")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Ollama (bundled Docker image) ────────────────────────────────────────────
|
||||||
|
|
||||||
if [[ "${USE_OLLAMA_DOCKER,,}" == "true" ]]; then
|
if [[ "${USE_OLLAMA_DOCKER,,}" == "true" ]]; then
|
||||||
echo "USE_OLLAMA is set to true, starting ollama serve."
|
echo "Starting bundled ollama serve..."
|
||||||
ollama serve &
|
ollama serve &
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── CUDA library paths ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
if [[ "${USE_CUDA_DOCKER,,}" == "true" ]]; then
|
if [[ "${USE_CUDA_DOCKER,,}" == "true" ]]; then
|
||||||
echo "CUDA is enabled, appending LD_LIBRARY_PATH to include torch/cudnn & cublas libraries."
|
echo "CUDA enabled — extending LD_LIBRARY_PATH for torch/cudnn libraries."
|
||||||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib/python3.11/site-packages/torch/lib:/usr/local/lib/python3.11/site-packages/nvidia/cudnn/lib"
|
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}:/usr/local/lib/python3.11/site-packages/torch/lib:/usr/local/lib/python3.11/site-packages/nvidia/cudnn/lib"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if SPACE_ID is set, if so, configure for space
|
# ── HuggingFace Space deployment ─────────────────────────────────────────────
|
||||||
if [ -n "$SPACE_ID" ]; then
|
|
||||||
echo "Configuring for HuggingFace Space deployment"
|
if [[ -n "${SPACE_ID:-}" ]]; then
|
||||||
if [ -n "$ADMIN_USER_EMAIL" ] && [ -n "$ADMIN_USER_PASSWORD" ]; then
|
echo "Configuring for HuggingFace Space deployment..."
|
||||||
echo "Admin user configured, creating"
|
|
||||||
WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips "${FORWARDED_ALLOW_IPS:-*}" &
|
if [[ -n "${ADMIN_USER_EMAIL:-}" && -n "${ADMIN_USER_PASSWORD:-}" ]]; then
|
||||||
|
echo "Creating admin user for Space..."
|
||||||
|
WEBUI_SECRET_KEY="${WEBUI_SECRET_KEY:-}" \
|
||||||
|
uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips "${FORWARDED_ALLOW_IPS:-*}" &
|
||||||
webui_pid=$!
|
webui_pid=$!
|
||||||
echo "Waiting for webui to start..."
|
|
||||||
while ! curl -s "http://localhost:${PORT}/health" > /dev/null; do
|
echo "Waiting for server to become healthy..."
|
||||||
|
until curl -sf "http://localhost:${PORT}/health" > /dev/null 2>&1; do
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
echo "Creating admin user..."
|
|
||||||
curl \
|
echo "Registering admin user..."
|
||||||
-X POST "http://localhost:${PORT}/api/v1/auths/signup" \
|
curl -sS -X POST "http://localhost:${PORT}/api/v1/auths/signup" \
|
||||||
-H "accept: application/json" \
|
-H "Accept: application/json" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"email\": \"${ADMIN_USER_EMAIL}\", \"password\": \"${ADMIN_USER_PASSWORD}\", \"name\": \"Admin\"}"
|
-d "{\"email\": \"${ADMIN_USER_EMAIL}\", \"password\": \"${ADMIN_USER_PASSWORD}\", \"name\": \"Admin\"}"
|
||||||
echo "Shutting down webui..."
|
|
||||||
kill $webui_pid
|
echo "Restarting server..."
|
||||||
|
kill "$webui_pid"
|
||||||
|
wait "$webui_pid" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export WEBUI_URL=${SPACE_HOST}
|
export WEBUI_URL="${SPACE_HOST}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Launch uvicorn ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
PYTHON_CMD=$(command -v python3 || command -v python)
|
PYTHON_CMD=$(command -v python3 || command -v python)
|
||||||
UVICORN_WORKERS="${UVICORN_WORKERS:-1}"
|
UVICORN_WORKERS="${UVICORN_WORKERS:-1}"
|
||||||
|
|
||||||
# If script is called with arguments, use them; otherwise use default workers
|
if [[ "$#" -gt 0 ]]; then
|
||||||
if [ "$#" -gt 0 ]; then
|
|
||||||
ARGS=("$@")
|
ARGS=("$@")
|
||||||
else
|
else
|
||||||
ARGS=(--workers "$UVICORN_WORKERS")
|
ARGS=(--workers "$UVICORN_WORKERS")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run uvicorn
|
exec env WEBUI_SECRET_KEY="${WEBUI_SECRET_KEY:-}" \
|
||||||
WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" exec "$PYTHON_CMD" -m uvicorn open_webui.main:app \
|
"$PYTHON_CMD" -m uvicorn open_webui.main:app \
|
||||||
--host "$HOST" \
|
--host "$HOST" \
|
||||||
--port "$PORT" \
|
--port "$PORT" \
|
||||||
--forwarded-allow-ips "${FORWARDED_ALLOW_IPS:-*}" \
|
--forwarded-allow-ips "${FORWARDED_ALLOW_IPS:-*}" \
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
echo "Warning: This will remove all containers and volumes, including persistent data. Do you want to continue? [Y/N]"
|
|
||||||
read ans
|
|
||||||
if [ "$ans" == "Y" ] || [ "$ans" == "y" ]; then
|
|
||||||
command docker-compose 2>/dev/null
|
|
||||||
if [ "$?" == "0" ]; then
|
|
||||||
docker-compose down -v
|
|
||||||
else
|
|
||||||
docker compose down -v
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Operation cancelled."
|
|
||||||
fi
|
|
||||||
Executable
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Tear down the compose project and remove all volumes (including data).
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
echo "WARNING: This will stop all containers and delete all volumes (including persistent data)."
|
||||||
|
read -rp "Are you sure you want to continue? [y/N]: " answer
|
||||||
|
|
||||||
|
if [[ "${answer,,}" =~ ^y(es)?$ ]]; then
|
||||||
|
docker compose down -v
|
||||||
|
echo "All containers and volumes have been removed."
|
||||||
|
else
|
||||||
|
echo "Operation cancelled."
|
||||||
|
fi
|
||||||
Executable
+175
@@ -0,0 +1,175 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Interactive docker compose launcher for Open WebUI.
|
||||||
|
# Supports GPU auto-detection, configurable ports, data mounts, and Playwright.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
readonly BOLD='\033[1m'
|
||||||
|
readonly GREEN='\033[1;32m'
|
||||||
|
readonly WHITE='\033[1;37m'
|
||||||
|
readonly RED='\033[0;31m'
|
||||||
|
readonly RESET='\033[0m'
|
||||||
|
readonly CHECK_MARK='\xE2\x9C\x93'
|
||||||
|
|
||||||
|
# ── GPU detection ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
detect_gpu_driver() {
|
||||||
|
if command -v nvidia-smi &>/dev/null && nvidia-smi &>/dev/null; then
|
||||||
|
echo "nvidia"
|
||||||
|
elif lspci 2>/dev/null | grep -qi 'vga.*amd\|display.*amd'; then
|
||||||
|
if lspci | grep -qiE 'Radeon (RX|R[579]|HD [78])'; then
|
||||||
|
echo "amdgpu"
|
||||||
|
else
|
||||||
|
echo "radeon"
|
||||||
|
fi
|
||||||
|
elif lspci 2>/dev/null | grep -qi 'vga.*intel\|display.*intel'; then
|
||||||
|
echo "i915"
|
||||||
|
else
|
||||||
|
echo >&2 "Error: No supported GPU detected."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Argument helpers ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
extract_bracket_value() {
|
||||||
|
local input="$1" fallback="${2:-}"
|
||||||
|
if [[ "$input" =~ \[.*=(.*)\] ]]; then
|
||||||
|
echo "${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
echo "$fallback"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $(basename "$0") [OPTIONS]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--enable-gpu[count=N] Enable GPU passthrough (N = number or "all")
|
||||||
|
--enable-api[port=PORT] Expose the Ollama API on PORT (default: 11435)
|
||||||
|
--webui[port=PORT] Set the WebUI port (default: 3000)
|
||||||
|
--data[folder=PATH] Bind-mount a host path for Ollama data
|
||||||
|
--playwright Enable Playwright for web scraping
|
||||||
|
--build Build images before starting
|
||||||
|
--drop Tear down the compose project
|
||||||
|
-q, --quiet Skip the confirmation prompt
|
||||||
|
-h, --help Show this help message
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$(basename "$0") --drop
|
||||||
|
$(basename "$0") --enable-gpu[count=1]
|
||||||
|
$(basename "$0") --enable-gpu[count=all] --webui[port=8080]
|
||||||
|
$(basename "$0") --enable-gpu[count=1] --enable-api[port=12345] --data[folder=./data] --build
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Defaults ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
enable_gpu=false
|
||||||
|
enable_api=false
|
||||||
|
enable_playwright=false
|
||||||
|
build_image=false
|
||||||
|
headless=false
|
||||||
|
drop_project=false
|
||||||
|
gpu_count=1
|
||||||
|
api_port=11435
|
||||||
|
webui_port=3000
|
||||||
|
data_dir=""
|
||||||
|
|
||||||
|
# ── Parse arguments ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--enable-gpu*) enable_gpu=true; gpu_count=$(extract_bracket_value "$1" "1") ;;
|
||||||
|
--enable-api*) enable_api=true; api_port=$(extract_bracket_value "$1" "11435") ;;
|
||||||
|
--webui*) webui_port=$(extract_bracket_value "$1" "3000") ;;
|
||||||
|
--data*) data_dir=$(extract_bracket_value "$1" "./ollama-data") ;;
|
||||||
|
--playwright) enable_playwright=true ;;
|
||||||
|
--build) build_image=true ;;
|
||||||
|
--drop) drop_project=true ;;
|
||||||
|
-q|--quiet) headless=true ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) echo "Unknown option: $1"; usage; exit 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Drop mode ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if [[ "$drop_project" == true ]]; then
|
||||||
|
docker compose down --remove-orphans
|
||||||
|
echo -e "${GREEN}${BOLD}Compose project stopped and cleaned up.${RESET}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Build compose command ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
compose_files=("-f" "docker-compose.yaml")
|
||||||
|
|
||||||
|
if [[ "$enable_gpu" == true ]]; then
|
||||||
|
if ! [[ "$gpu_count" =~ ^([0-9]+|all)$ ]]; then
|
||||||
|
echo >&2 "Error: Invalid GPU count '${gpu_count}'. Must be a number or 'all'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
export OLLAMA_GPU_DRIVER
|
||||||
|
OLLAMA_GPU_DRIVER=$(detect_gpu_driver)
|
||||||
|
export OLLAMA_GPU_COUNT="$gpu_count"
|
||||||
|
compose_files+=("-f" "docker-compose.gpu.yaml")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$enable_api" == true ]]; then
|
||||||
|
export OLLAMA_WEBAPI_PORT="$api_port"
|
||||||
|
compose_files+=("-f" "docker-compose.api.yaml")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$data_dir" ]]; then
|
||||||
|
export OLLAMA_DATA_DIR="$data_dir"
|
||||||
|
compose_files+=("-f" "docker-compose.data.yaml")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$enable_playwright" == true ]]; then
|
||||||
|
compose_files+=("-f" "docker-compose.playwright.yaml")
|
||||||
|
fi
|
||||||
|
|
||||||
|
export OPEN_WEBUI_PORT="$webui_port"
|
||||||
|
|
||||||
|
up_args=("up" "-d" "--remove-orphans" "--force-recreate")
|
||||||
|
if [[ "$build_image" == true ]]; then
|
||||||
|
up_args+=("--build")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Confirmation ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo -e "${WHITE}${BOLD}Current Setup:${RESET}"
|
||||||
|
echo -e " ${GREEN}${BOLD}GPU Driver:${RESET} ${OLLAMA_GPU_DRIVER:-Disabled}"
|
||||||
|
echo -e " ${GREEN}${BOLD}GPU Count:${RESET} ${OLLAMA_GPU_COUNT:-Disabled}"
|
||||||
|
echo -e " ${GREEN}${BOLD}API Port:${RESET} ${OLLAMA_WEBAPI_PORT:-Disabled}"
|
||||||
|
echo -e " ${GREEN}${BOLD}Data Dir:${RESET} ${data_dir:-Docker volume}"
|
||||||
|
echo -e " ${GREEN}${BOLD}WebUI Port:${RESET} ${webui_port}"
|
||||||
|
echo -e " ${GREEN}${BOLD}Playwright:${RESET} ${enable_playwright}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ "$headless" != true ]]; then
|
||||||
|
read -rp "$(echo -e "${WHITE}${BOLD}Proceed with this setup? [Y/n]: ${RESET}")" confirm
|
||||||
|
if [[ "${confirm,,}" == "n" ]]; then
|
||||||
|
echo "Aborted."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Launch ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if docker compose "${compose_files[@]}" "${up_args[@]}"; then
|
||||||
|
echo
|
||||||
|
echo -e "${GREEN}${BOLD}${CHECK_MARK} Compose project started successfully.${RESET}"
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
echo -e "${RED}${BOLD}Failed to start compose project.${RESET}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
Executable
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Pull and run the official Ollama container with optional GPU support.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
readonly CONTAINER="ollama"
|
||||||
|
readonly HOST_PORT="${OLLAMA_PORT:-11434}"
|
||||||
|
readonly CONTAINER_PORT=11434
|
||||||
|
|
||||||
|
read -rp "Enable GPU passthrough? [y/N]: " use_gpu
|
||||||
|
|
||||||
|
echo "Pulling latest Ollama image..."
|
||||||
|
docker pull ollama/ollama:latest
|
||||||
|
|
||||||
|
echo "Stopping any existing ${CONTAINER} container..."
|
||||||
|
docker rm -f "$CONTAINER" 2>/dev/null || true
|
||||||
|
|
||||||
|
gpu_flags=()
|
||||||
|
if [[ "${use_gpu,,}" =~ ^y(es)?$ ]]; then
|
||||||
|
gpu_flags=("--gpus=all")
|
||||||
|
echo "GPU passthrough enabled."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Starting ${CONTAINER}..."
|
||||||
|
docker run -d \
|
||||||
|
"${gpu_flags[@]}" \
|
||||||
|
-v "ollama:/root/.ollama" \
|
||||||
|
-p "${HOST_PORT}:${CONTAINER_PORT}" \
|
||||||
|
--name "$CONTAINER" \
|
||||||
|
ollama/ollama
|
||||||
|
|
||||||
|
echo "Cleaning up dangling images..."
|
||||||
|
docker image prune -f
|
||||||
|
|
||||||
|
echo "Ollama is running at http://localhost:${HOST_PORT}"
|
||||||
Executable
+32
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Build and run the Open WebUI Docker container locally.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
readonly IMAGE="open-webui"
|
||||||
|
readonly CONTAINER="open-webui"
|
||||||
|
readonly HOST_PORT="${OPEN_WEBUI_PORT:-3000}"
|
||||||
|
readonly CONTAINER_PORT=8080
|
||||||
|
|
||||||
|
echo "Building ${IMAGE} image..."
|
||||||
|
docker build -t "$IMAGE" .
|
||||||
|
|
||||||
|
echo "Stopping any existing ${CONTAINER} container..."
|
||||||
|
docker stop "$CONTAINER" 2>/dev/null || true
|
||||||
|
docker rm "$CONTAINER" 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Starting ${CONTAINER}..."
|
||||||
|
docker run -d \
|
||||||
|
-p "${HOST_PORT}:${CONTAINER_PORT}" \
|
||||||
|
--add-host=host.docker.internal:host-gateway \
|
||||||
|
-v "${IMAGE}:/app/backend/data" \
|
||||||
|
--name "$CONTAINER" \
|
||||||
|
--restart always \
|
||||||
|
"$IMAGE"
|
||||||
|
|
||||||
|
echo "Cleaning up dangling images..."
|
||||||
|
docker image prune -f
|
||||||
|
|
||||||
|
echo "Open WebUI is running at http://localhost:${HOST_PORT}"
|
||||||
Executable
+24
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Pull the latest version of every model installed in the Ollama container.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
readonly CONTAINER="${OLLAMA_CONTAINER:-ollama}"
|
||||||
|
|
||||||
|
echo "Fetching installed models from '${CONTAINER}' container..."
|
||||||
|
models=$(docker exec "$CONTAINER" ollama list | tail -n +2 | awk '{print $1}')
|
||||||
|
|
||||||
|
if [[ -z "$models" ]]; then
|
||||||
|
echo "No models found."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Updating models..."
|
||||||
|
while IFS= read -r model; do
|
||||||
|
echo " Pulling ${model}..."
|
||||||
|
docker exec "$CONTAINER" ollama pull "$model"
|
||||||
|
done <<< "$models"
|
||||||
|
|
||||||
|
echo "All models updated."
|
||||||
-250
@@ -1,250 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Define color and formatting codes
|
|
||||||
BOLD='\033[1m'
|
|
||||||
GREEN='\033[1;32m'
|
|
||||||
WHITE='\033[1;37m'
|
|
||||||
RED='\033[0;31m'
|
|
||||||
NC='\033[0m' # No Color
|
|
||||||
# Unicode character for tick mark
|
|
||||||
TICK='\u2713'
|
|
||||||
|
|
||||||
# Detect GPU driver
|
|
||||||
get_gpu_driver() {
|
|
||||||
# Detect NVIDIA GPUs using lspci or nvidia-smi
|
|
||||||
if lspci | grep -i nvidia >/dev/null || nvidia-smi >/dev/null 2>&1; then
|
|
||||||
echo "nvidia"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Detect AMD GPUs (including GCN architecture check for amdgpu vs radeon)
|
|
||||||
if lspci | grep -i amd >/dev/null; then
|
|
||||||
# List of known GCN and later architecture cards
|
|
||||||
# This is a simplified list, and in a real-world scenario, you'd want a more comprehensive one
|
|
||||||
local gcn_and_later=("Radeon HD 7000" "Radeon HD 8000" "Radeon R5" "Radeon R7" "Radeon R9" "Radeon RX")
|
|
||||||
|
|
||||||
# Get GPU information
|
|
||||||
local gpu_info=$(lspci | grep -i 'vga.*amd')
|
|
||||||
|
|
||||||
for model in "${gcn_and_later[@]}"; do
|
|
||||||
if echo "$gpu_info" | grep -iq "$model"; then
|
|
||||||
echo "amdgpu"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Default to radeon if no GCN or later architecture is detected
|
|
||||||
echo "radeon"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Detect Intel GPUs
|
|
||||||
if lspci | grep -i intel >/dev/null; then
|
|
||||||
echo "i915"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If no known GPU is detected
|
|
||||||
echo "Unknown or unsupported GPU driver"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function for rolling animation
|
|
||||||
show_loading() {
|
|
||||||
local spin='-\|/'
|
|
||||||
local i=0
|
|
||||||
|
|
||||||
printf " "
|
|
||||||
|
|
||||||
while kill -0 $1 2>/dev/null; do
|
|
||||||
i=$(( (i+1) %4 ))
|
|
||||||
printf "\b${spin:$i:1}"
|
|
||||||
sleep .1
|
|
||||||
done
|
|
||||||
|
|
||||||
# Replace the spinner with a tick
|
|
||||||
printf "\b${GREEN}${TICK}${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Usage information
|
|
||||||
usage() {
|
|
||||||
echo "Usage: $0 [OPTIONS]"
|
|
||||||
echo "Options:"
|
|
||||||
echo " --enable-gpu[count=COUNT] Enable GPU support with the specified count."
|
|
||||||
echo " --enable-api[port=PORT] Enable API and expose it on the specified port."
|
|
||||||
echo " --webui[port=PORT] Set the port for the web user interface."
|
|
||||||
echo " --data[folder=PATH] Bind mount for ollama data folder (by default will create the 'ollama' volume)."
|
|
||||||
echo " --playwright Enable Playwright support for web scraping."
|
|
||||||
echo " --build Build the docker image before running the compose project."
|
|
||||||
echo " --drop Drop the compose project."
|
|
||||||
echo " -q, --quiet Run script in headless mode."
|
|
||||||
echo " -h, --help Show this help message."
|
|
||||||
echo ""
|
|
||||||
echo "Examples:"
|
|
||||||
echo " $0 --drop"
|
|
||||||
echo " $0 --enable-gpu[count=1]"
|
|
||||||
echo " $0 --enable-gpu[count=all]"
|
|
||||||
echo " $0 --enable-api[port=11435]"
|
|
||||||
echo " $0 --enable-gpu[count=1] --enable-api[port=12345] --webui[port=3000]"
|
|
||||||
echo " $0 --enable-gpu[count=1] --enable-api[port=12345] --webui[port=3000] --data[folder=./ollama-data]"
|
|
||||||
echo " $0 --enable-gpu[count=1] --enable-api[port=12345] --webui[port=3000] --data[folder=./ollama-data] --build"
|
|
||||||
echo ""
|
|
||||||
echo "This script configures and runs a docker-compose setup with optional GPU support, API exposure, and web UI configuration."
|
|
||||||
echo "About the gpu to use, the script automatically detects it using the "lspci" command."
|
|
||||||
echo "In this case the gpu detected is: $(get_gpu_driver)"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Default values
|
|
||||||
gpu_count=1
|
|
||||||
api_port=11435
|
|
||||||
webui_port=3000
|
|
||||||
headless=false
|
|
||||||
build_image=false
|
|
||||||
kill_compose=false
|
|
||||||
enable_playwright=false
|
|
||||||
|
|
||||||
# Function to extract value from the parameter
|
|
||||||
extract_value() {
|
|
||||||
echo "$1" | sed -E 's/.*\[.*=(.*)\].*/\1/; t; s/.*//'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Parse arguments
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
key="$1"
|
|
||||||
|
|
||||||
case $key in
|
|
||||||
--enable-gpu*)
|
|
||||||
enable_gpu=true
|
|
||||||
value=$(extract_value "$key")
|
|
||||||
gpu_count=${value:-1}
|
|
||||||
;;
|
|
||||||
--enable-api*)
|
|
||||||
enable_api=true
|
|
||||||
value=$(extract_value "$key")
|
|
||||||
api_port=${value:-11435}
|
|
||||||
;;
|
|
||||||
--webui*)
|
|
||||||
value=$(extract_value "$key")
|
|
||||||
webui_port=${value:-3000}
|
|
||||||
;;
|
|
||||||
--data*)
|
|
||||||
value=$(extract_value "$key")
|
|
||||||
data_dir=${value:-"./ollama-data"}
|
|
||||||
;;
|
|
||||||
--playwright)
|
|
||||||
enable_playwright=true
|
|
||||||
;;
|
|
||||||
--drop)
|
|
||||||
kill_compose=true
|
|
||||||
;;
|
|
||||||
--build)
|
|
||||||
build_image=true
|
|
||||||
;;
|
|
||||||
-q|--quiet)
|
|
||||||
headless=true
|
|
||||||
;;
|
|
||||||
-h|--help)
|
|
||||||
usage
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Unknown option
|
|
||||||
echo "Unknown option: $key"
|
|
||||||
usage
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
shift # past argument or value
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ $kill_compose == true ]]; then
|
|
||||||
docker compose down --remove-orphans
|
|
||||||
echo -e "${GREEN}${BOLD}Compose project dropped successfully.${NC}"
|
|
||||||
exit
|
|
||||||
else
|
|
||||||
DEFAULT_COMPOSE_COMMAND="docker compose -f docker-compose.yaml"
|
|
||||||
if [[ $enable_gpu == true ]]; then
|
|
||||||
# Validate and process command-line arguments
|
|
||||||
if [[ -n $gpu_count ]]; then
|
|
||||||
if ! [[ $gpu_count =~ ^([0-9]+|all)$ ]]; then
|
|
||||||
echo "Invalid GPU count: $gpu_count"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Enabling GPU with $gpu_count GPUs"
|
|
||||||
# Add your GPU allocation logic here
|
|
||||||
export OLLAMA_GPU_DRIVER=$(get_gpu_driver)
|
|
||||||
export OLLAMA_GPU_COUNT=$gpu_count # Set OLLAMA_GPU_COUNT environment variable
|
|
||||||
fi
|
|
||||||
DEFAULT_COMPOSE_COMMAND+=" -f docker-compose.gpu.yaml"
|
|
||||||
fi
|
|
||||||
if [[ $enable_api == true ]]; then
|
|
||||||
DEFAULT_COMPOSE_COMMAND+=" -f docker-compose.api.yaml"
|
|
||||||
if [[ -n $api_port ]]; then
|
|
||||||
export OLLAMA_WEBAPI_PORT=$api_port # Set OLLAMA_WEBAPI_PORT environment variable
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [[ -n $data_dir ]]; then
|
|
||||||
DEFAULT_COMPOSE_COMMAND+=" -f docker-compose.data.yaml"
|
|
||||||
export OLLAMA_DATA_DIR=$data_dir # Set OLLAMA_DATA_DIR environment variable
|
|
||||||
fi
|
|
||||||
if [[ $enable_playwright == true ]]; then
|
|
||||||
DEFAULT_COMPOSE_COMMAND+=" -f docker-compose.playwright.yaml"
|
|
||||||
fi
|
|
||||||
if [[ -n $webui_port ]]; then
|
|
||||||
export OPEN_WEBUI_PORT=$webui_port # Set OPEN_WEBUI_PORT environment variable
|
|
||||||
fi
|
|
||||||
DEFAULT_COMPOSE_COMMAND+=" up -d"
|
|
||||||
DEFAULT_COMPOSE_COMMAND+=" --remove-orphans"
|
|
||||||
DEFAULT_COMPOSE_COMMAND+=" --force-recreate"
|
|
||||||
if [[ $build_image == true ]]; then
|
|
||||||
DEFAULT_COMPOSE_COMMAND+=" --build"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Recap of environment variables
|
|
||||||
echo
|
|
||||||
echo -e "${WHITE}${BOLD}Current Setup:${NC}"
|
|
||||||
echo -e " ${GREEN}${BOLD}GPU Driver:${NC} ${OLLAMA_GPU_DRIVER:-Not Enabled}"
|
|
||||||
echo -e " ${GREEN}${BOLD}GPU Count:${NC} ${OLLAMA_GPU_COUNT:-Not Enabled}"
|
|
||||||
echo -e " ${GREEN}${BOLD}WebAPI Port:${NC} ${OLLAMA_WEBAPI_PORT:-Not Enabled}"
|
|
||||||
echo -e " ${GREEN}${BOLD}Data Folder:${NC} ${data_dir:-Using ollama volume}"
|
|
||||||
echo -e " ${GREEN}${BOLD}WebUI Port:${NC} $webui_port"
|
|
||||||
echo -e " ${GREEN}${BOLD}Playwright:${NC} ${enable_playwright:-false}"
|
|
||||||
echo
|
|
||||||
|
|
||||||
if [[ $headless == true ]]; then
|
|
||||||
echo -ne "${WHITE}${BOLD}Running in headless mode... ${NC}"
|
|
||||||
choice="y"
|
|
||||||
else
|
|
||||||
# Ask for user acceptance
|
|
||||||
echo -ne "${WHITE}${BOLD}Do you want to proceed with current setup? (Y/n): ${NC}"
|
|
||||||
read -n1 -s choice
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo
|
|
||||||
|
|
||||||
if [[ $choice == "" || $choice == "y" ]]; then
|
|
||||||
# Execute the command with the current user
|
|
||||||
eval "$DEFAULT_COMPOSE_COMMAND" &
|
|
||||||
|
|
||||||
# Capture the background process PID
|
|
||||||
PID=$!
|
|
||||||
|
|
||||||
# Display the loading animation
|
|
||||||
#show_loading $PID
|
|
||||||
|
|
||||||
# Wait for the command to finish
|
|
||||||
wait $PID
|
|
||||||
|
|
||||||
echo
|
|
||||||
# Check exit status
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo -e "${GREEN}${BOLD}Compose project started successfully.${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${RED}${BOLD}There was an error starting the compose project.${NC}"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Aborted."
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
host_port=11434
|
|
||||||
container_port=11434
|
|
||||||
|
|
||||||
read -r -p "Do you want ollama in Docker with GPU support? (y/n): " use_gpu
|
|
||||||
|
|
||||||
docker rm -f ollama || true
|
|
||||||
docker pull ollama/ollama:latest
|
|
||||||
|
|
||||||
docker_args="-d -v ollama:/root/.ollama -p $host_port:$container_port --name ollama ollama/ollama"
|
|
||||||
|
|
||||||
if [ "$use_gpu" = "y" ]; then
|
|
||||||
docker_args="--gpus=all $docker_args"
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker run $docker_args
|
|
||||||
|
|
||||||
docker image prune -f
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
image_name="open-webui"
|
|
||||||
container_name="open-webui"
|
|
||||||
host_port=3000
|
|
||||||
container_port=8080
|
|
||||||
|
|
||||||
docker build -t "$image_name" .
|
|
||||||
docker stop "$container_name" &>/dev/null || true
|
|
||||||
docker rm "$container_name" &>/dev/null || true
|
|
||||||
|
|
||||||
docker run -d -p "$host_port":"$container_port" \
|
|
||||||
--add-host=host.docker.internal:host-gateway \
|
|
||||||
-v "${image_name}:/app/backend/data" \
|
|
||||||
--name "$container_name" \
|
|
||||||
--restart always \
|
|
||||||
"$image_name"
|
|
||||||
|
|
||||||
docker image prune -f
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# update_llm.sh
|
|
||||||
|
|
||||||
# Retrieves the list of LLMs installed in the Docker container
|
|
||||||
llm_list=$(docker exec ollama ollama list | tail -n +2 | awk '{print $1}')
|
|
||||||
|
|
||||||
# Loop over each LLM to update it
|
|
||||||
for llm in $llm_list; do
|
|
||||||
docker exec ollama ollama pull $llm
|
|
||||||
done
|
|
||||||
Reference in New Issue
Block a user