Use CloudRepo’s PyPI index from Pixi
This section provides comprehensive documentation for using CloudRepo with Pixi, the modern cross-platform package and environment management tool.
See also
Authenticate with a repository token. Repository Tokens: Create One and Authenticate shows how to create one, with a ready-to-paste example. For pixi: the username is the email address of the account that created the token, in lower case, and the password is the token.
Introduction to Pixi
What is Pixi?
Pixi is a revolutionary cross-platform package management tool that bridges the conda and PyPI ecosystems, offering developers a unified solution for managing dependencies, environments, and workflows across Windows, macOS, and Linux. Built in Rust for exceptional performance, Pixi represents a significant evolution in Python package management.
Key advantages of Pixi include:
Cross-platform by design: Single configuration works seamlessly across Windows, macOS, and Linux
Dual ecosystem support: Native integration with both conda-forge and PyPI packages
Blazing fast performance: Up to 10x faster than conda, 4x faster than micromamba
Deterministic environments: Lock files ensure reproducible builds across all platforms
Language agnostic: Manage Python, R, C++, Rust, and other language dependencies
Built-in task runner: Define and execute project tasks without additional tools
Modern configuration: Single
pixi.tomlfile replaces multiple config files
Why Use Pixi with CloudRepo?
Combining Pixi’s modern workflow with CloudRepo’s private repository hosting provides:
Private package distribution: Securely share internal packages across your organization
Cross-platform consistency: Ensure teams on different OS platforms get identical environments
Predictable pricing: Flat plan pricing with no per-seat licensing
Simplified CI/CD: One configuration file works across all build environments
Mixed dependencies: Seamlessly combine public PyPI/conda packages with private CloudRepo packages
Prerequisites
Before configuring Pixi to work with CloudRepo, ensure you have:
CloudRepo Account and Repository
An active CloudRepo organization account
A Python repository created in the CloudRepo Admin Portal
A repository user with appropriate permissions (upload/download)
Note
Your admin user cannot directly access repositories for security reasons. Always create a dedicated repository user in the CloudRepo Admin Portal.
If you haven’t completed these steps, please refer to:
Pixi Installation
Install Pixi using the official installer:
Installing Pixi (Recommended Method)# macOS and Linux curl -fsSL https://pixi.sh/install.sh | bash # Windows (PowerShell) iwr -useb https://pixi.sh/install.ps1 | iex
Or install via conda/mamba:
Installing Pixi via Condaconda install -c conda-forge pixi # or mamba install -c conda-forge pixi
Python Environment
Python 3.8 or higher (managed by Pixi)
A project directory for your Pixi configuration
Configuration
Basic Pixi Configuration for CloudRepo
Initialize a new Pixi project or add CloudRepo to an existing project:
# Create new project
pixi init my-project
cd my-project
# Or initialize in existing directory
pixi init
This creates a pixi.toml file. Give each private package the CloudRepo index URL, and pixi
fetches that package from CloudRepo alone:
1[project]
2name = "my-project"
3version = "0.1.0"
4description = "Project using CloudRepo for private packages"
5authors = ["Your Team <team@example.com>"]
6channels = ["conda-forge"]
7platforms = ["win-64", "linux-64", "osx-64", "osx-arm64"]
8
9[dependencies]
10python = ">=3.8,<3.12"
11
12[pypi-dependencies]
13# Public packages from PyPI
14requests = ">=2.28.0"
15pandas = "*"
16
17# Private packages: each names the CloudRepo index, so pixi fetches it from there alone
18acme-private-package = { version = ">=1.0.0", index = "https://[organization-id].mycloudrepo.io/repositories/[repository-id]/simple" }
19acme-utils = { version = "*", index = "https://[organization-id].mycloudrepo.io/repositories/[repository-id]/simple" }
Do not list CloudRepo under [pypi-options] beside PyPI: an index there serves every package, and
a package someone publishes on PyPI under your private package’s name can be installed instead of
yours. Give every private package its own index, including the private packages that your
private packages depend on, because pixi looks up any package you have not pinned on PyPI. See
Installing Private and Public Packages Together.
Authentication Setup
Pixi supports multiple authentication methods for private repositories:
Method 1: A .netrc File (Recommended, including CI/CD)
pixi reads PyPI credentials from a .netrc file: ~/.netrc on Linux and macOS,
%HOME%\_netrc on Windows, or the file the NETRC environment variable names. The login is
the email address of the user that created the token, in lower case; the password is the token.
machine [organization-id].mycloudrepo.io
login you@example.com
password YOUR_REPOSITORY_TOKEN
chmod 600 ~/.netrc
In CI, write the file from your secrets and point NETRC at it; the CI/CD examples below do
this. pixi has no per-index username or password environment variables.
Method 2: Keyring Integration (Most Secure for Development)
# Install keyring where pixi can run it
pixi global install keyring
# Store the token in the system keyring under your email address
keyring set https://[organization-id].mycloudrepo.io/repositories/[repository-id]/simple you@example.com
# Paste your repository token when prompted
# Ask pixi to use keyring when it installs
pixi install --pypi-keyring-provider subprocess
For keyring, the index URL in your manifest must carry the username (https://you%40example.com@...)
and no password, so pixi knows which keyring entry to read.
Multiple Index Configuration
Packages from more than one CloudRepo repository each name their own index URL. Everything else comes from PyPI:
1[project]
2name = "multi-index-project"
3channels = ["conda-forge"]
4platforms = ["win-64", "linux-64", "osx-64", "osx-arm64"]
5
6[pypi-dependencies]
7# From PyPI, the default index
8numpy = ">=1.24.0"
9scipy = "*"
10
11# From your main CloudRepo repository
12acme-core = { version = ">=2.0.0", index = "https://[organization-id].mycloudrepo.io/repositories/[repository-id]/simple" }
13
14# From a second CloudRepo repository
15acme-ml-toolkit = { version = ">=1.0.0", index = "https://[organization-id].mycloudrepo.io/repositories/[ml-repository-id]/simple" }
Publishing Packages for Pixi
Building Python Packages
Create a standard Python package structure for publishing:
my-package/
├── pyproject.toml # Package configuration
├── pixi.toml # Pixi environment configuration
├── src/
│ └── my_package/
│ ├── __init__.py
│ └── module.py
├── tests/
│ └── test_module.py
└── README.md
Configure your package’s pyproject.toml:
1[build-system]
2requires = ["hatchling"]
3build-backend = "hatchling.build"
4
5[project]
6name = "my-package"
7version = "1.0.0"
8description = "Private package for our team"
9authors = [{name = "Your Team", email = "team@example.com"}]
10requires-python = ">=3.8"
11dependencies = [
12 "requests>=2.28.0",
13]
14
15[project.optional-dependencies]
16dev = [
17 "pytest>=7.0.0",
18 "black>=22.0.0",
19]
Build the package:
# Add build dependencies
pixi add --pypi build twine
# Build the package
pixi run python -m build
# This creates:
# dist/my_package-1.0.0-py3-none-any.whl
# dist/my_package-1.0.0.tar.gz
Publishing to CloudRepo
Upload your package to CloudRepo using twine:
# Configure .pypirc for CloudRepo
cat > ~/.pypirc << EOF
[distutils]
index-servers = cloudrepo
[cloudrepo]
repository = https://[organization-id].mycloudrepo.io/repositories/[repository-id]
username = [repository-user-email]
password = YOUR_REPOSITORY_TOKEN
EOF
# Upload the package
pixi run twine upload -r cloudrepo dist/*
# Or upload with the credential in the environment
export TWINE_USERNAME=[repository-user-email]
export TWINE_PASSWORD=YOUR_REPOSITORY_TOKEN
pixi run twine upload --non-interactive \
--repository-url https://[organization-id].mycloudrepo.io/repositories/[repository-id] \
dist/*
Platform-Specific Packages
Build and publish platform-specific packages with binary extensions:
1[project]
2name = "native-extension"
3platforms = ["win-64", "linux-64", "osx-64", "osx-arm64"]
4
5[dependencies]
6python = "3.11.*"
7cython = ">=3.0.0"
8numpy = ">=1.24.0"
9
10[tasks]
11build-extension = "python setup.py build_ext --inplace"
12build-wheel = { cmd = "python -m build --wheel", depends_on = ["build-extension"] }
13
14# Platform-specific build tasks
15[target.win-64.tasks]
16build-wheel = "python -m build --wheel --plat-name win_amd64"
17
18[target.linux-64.tasks]
19build-wheel = "python -m build --wheel --plat-name manylinux2014_x86_64"
20
21[target.osx-arm64.tasks]
22build-wheel = "python -m build --wheel --plat-name macosx_11_0_arm64"
Build for all platforms:
# Build for current platform
pixi run build-wheel
# Or use cibuildwheel for comprehensive platform support
pixi add --pypi cibuildwheel
pixi run cibuildwheel --platform linux
# Upload all platform wheels
pixi run twine upload -r cloudrepo dist/*.whl
Version Management
Manage package versions effectively:
# Install bump2version for version management
pixi add --pypi bump2version
# Configure version bumping in setup.cfg
cat > setup.cfg << EOF
[bumpversion]
current_version = 1.0.0
commit = True
tag = True
[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"
EOF
# Bump versions
pixi run bump2version patch # 1.0.0 -> 1.0.1
pixi run bump2version minor # 1.0.1 -> 1.1.0
pixi run bump2version major # 1.1.0 -> 2.0.0
# Pre-release versions
pixi run bump2version --new-version 2.0.0-alpha.1 prerelease
pixi run bump2version --new-version 2.0.0-beta.1 prerelease
pixi run bump2version --new-version 2.0.0-rc.1 prerelease
Consuming Packages from CloudRepo
Adding Private Dependencies
Add CloudRepo packages to your Pixi project:
# Add a private package from CloudRepo, with its private dependencies in the same
# command: pixi installs at once, and a private dependency it meets unpinned comes
# from PyPI. --index takes the index URL.
pixi add --pypi acme-private-package acme-utils --index https://[organization-id].mycloudrepo.io/repositories/[repository-id]/simple
# Add with version constraints
pixi add --pypi "acme-private-package>=2.0.0,<3.0.0" --index https://[organization-id].mycloudrepo.io/repositories/[repository-id]/simple
# Add to specific feature/environment
pixi add --pypi acme-dev-tools --index https://[organization-id].mycloudrepo.io/repositories/[repository-id]/simple --feature dev
Alternatively, manually edit pixi.toml:
1[pypi-dependencies]
2# Simple private dependency
3my-private-lib = { version = "*", index = "cloudrepo" }
4
5# With version constraints
6company-framework = { version = ">=2.0,<3.0", index = "cloudrepo" }
7
8# With extras
9ml-toolkit = { version = ">=1.0.0", extras = ["gpu", "viz"], index = "cloudrepo" }
10
11# Pre-release versions
12experimental-lib = { version = ">=1.0.0a1", index = "cloudrepo" }
Mixing Public and Private Indices
Efficiently combine packages from multiple sources:
1[project]
2name = "mixed-dependencies"
3channels = ["conda-forge", "pytorch"]
4platforms = ["linux-64", "win-64", "osx-64", "osx-arm64"]
5
6# Conda dependencies (from conda-forge/pytorch)
7[dependencies]
8python = "3.11.*"
9pytorch = ">=2.0.0"
10cudatoolkit = "11.8.*" # Platform-specific, managed by conda
11
12# PyPI dependencies
13[pypi-dependencies]
14# Public packages (from PyPI)
15transformers = ">=4.30.0"
16datasets = "*"
17
18# Private packages (from CloudRepo)
19company-ml-utils = { version = ">=2.0.0", index = "cloudrepo" }
20internal-datasets = { version = "*", index = "cloudrepo" }
21
22# Mixed - public package with private plugin
23fastapi = ">=0.100.0" # From PyPI
24fastapi-company-auth = { version = ">=1.0.0", index = "cloudrepo" } # Private auth plugin
Lock File Management
Pixi automatically manages lock files for reproducible environments:
# Install dependencies and create/update lock file
pixi install
# Update all dependencies to latest compatible versions
pixi update
# Update specific packages only
pixi update numpy pandas my-private-package
# Show current lock file status
pixi list
# Clean and regenerate lock file
pixi clean
pixi install
The pixi.lock file ensures all team members and CI/CD systems get identical dependencies:
1version: 4
2environments:
3 default:
4 channels:
5 - conda-forge
6 packages:
7 linux-64:
8 - python: 3.11.0
9 - numpy: 1.24.0
10 pypi:
11 - my-private-package:
12 version: 2.1.0
13 index: cloudrepo
14 hash: sha256:abc123...
Note
Always commit pixi.lock to version control for reproducible builds.
Environment Specifications
Define multiple environments for different use cases:
1[project]
2name = "multi-env-project"
3channels = ["conda-forge"]
4platforms = ["linux-64", "win-64", "osx-64"]
5
6# Default environment dependencies
7[dependencies]
8python = "3.11.*"
9
10[pypi-dependencies]
11requests = "*"
12my-core-lib = { version = ">=1.0.0", index = "cloudrepo" }
13
14# Development environment
15[feature.dev.dependencies]
16pytest = ">=7.0.0"
17black = ">=22.0.0"
18
19[feature.dev.pypi-dependencies]
20my-test-utils = { version = "*", index = "cloudrepo" }
21pytest-cov = "*"
22
23# Production environment
24[feature.prod.dependencies]
25gunicorn = "*"
26
27[feature.prod.pypi-dependencies]
28my-prod-monitor = { version = ">=2.0.0", index = "cloudrepo" }
29
30# Data science environment
31[feature.datascience.dependencies]
32jupyter = "*"
33scikit-learn = "*"
34
35[feature.datascience.pypi-dependencies]
36company-ml-lib = { version = ">=3.0.0", index = "cloudrepo" }
37
38# Define environment combinations
39[environments]
40dev = ["dev"]
41prod = ["prod"]
42datascience = ["dev", "datascience"]
Activate and use environments:
# Install default environment
pixi install
# Install specific environment
pixi install -e dev
pixi install -e prod
pixi install -e datascience
# Run commands in specific environment
pixi run -e dev pytest
pixi run -e prod gunicorn app:main
pixi run -e datascience jupyter notebook
# Shell into environment
pixi shell -e dev
Advanced Topics
Multi-Project Workspaces
Manage multiple related projects in a workspace:
my-workspace/
├── pixi.toml # Workspace configuration
├── pixi.lock
├── packages/
│ ├── core-lib/
│ │ ├── pyproject.toml
│ │ └── src/
│ ├── web-api/
│ │ ├── pyproject.toml
│ │ └── src/
│ └── cli-tool/
│ ├── pyproject.toml
│ └── src/
└── apps/
├── frontend/
└── backend/
Workspace pixi.toml configuration:
1[workspace]
2name = "my-workspace"
3channels = ["conda-forge"]
4platforms = ["linux-64", "win-64", "osx-64"]
5
6# Workspace members
7members = [
8 "packages/core-lib",
9 "packages/web-api",
10 "packages/cli-tool",
11]
12
13# Shared dependencies across workspace
14[dependencies]
15python = "3.11.*"
16
17[[pypi-indices]]
18name = "cloudrepo"
19url = "https://[organization-id].mycloudrepo.io/repositories/[repository-id]/simple"
20
21# Workspace-wide tasks
22[tasks]
23test-all = "pytest packages/*/tests"
24build-all = "python -m build packages/*"
25publish-all = """
26for pkg in packages/*; do
27 cd $pkg && pixi run twine upload -r cloudrepo dist/*
28 cd ../..
29done
30"""
Cross-Platform Builds
Configure platform-specific dependencies and builds:
1[project]
2name = "cross-platform-app"
3platforms = ["linux-64", "win-64", "osx-64", "osx-arm64"]
4
5# Shared dependencies
6[dependencies]
7python = "3.11.*"
8
9# Platform-specific dependencies
10[target.linux-64.dependencies]
11gtk = "*" # Linux-only GUI toolkit
12
13[target.win-64.dependencies]
14pywin32 = "*" # Windows-specific APIs
15
16[target.osx-64.dependencies]
17pyobjc = "*" # macOS framework bindings
18
19[target.osx-arm64.dependencies]
20pyobjc = "*"
21tensorflow-metal = "*" # Apple Silicon acceleration
22
23# Platform-specific PyPI packages from CloudRepo
24[target.linux-64.pypi-dependencies]
25company-linux-drivers = { version = "*", index = "cloudrepo" }
26
27[target.win-64.pypi-dependencies]
28company-windows-utils = { version = "*", index = "cloudrepo" }
29
30# Platform-specific build tasks
31[target.linux-64.tasks]
32build = "gcc -shared -fPIC -o lib.so src/native.c"
33
34[target.win-64.tasks]
35build = "cl /LD src\\native.c /Fe:lib.dll"
36
37[target.osx-64.tasks]
38build = "clang -shared -o lib.dylib src/native.c"
Build for all platforms:
# Install for current platform
pixi install
# Build for all platforms using CI
for platform in linux-64 win-64 osx-64 osx-arm64; do
pixi install --platform $platform
pixi run --platform $platform build
done
Conda Integration Alongside PyPI
Leverage both conda and PyPI packages effectively:
1[project]
2name = "scientific-computing"
3channels = ["conda-forge", "bioconda", "pytorch"]
4platforms = ["linux-64", "osx-64"]
5
6# Complex binary dependencies from conda
7[dependencies]
8python = "3.11.*"
9numpy = ">=1.24.0" # Optimized builds from conda
10scipy = "*"
11pytorch = ">=2.0.0"
12cudatoolkit = "11.8.*"
13mkl = "*" # Intel Math Kernel Library
14gdal = "*" # Geospatial libraries
15r-base = ">=4.0" # R language support
16
17# Python-only packages from PyPI/CloudRepo
18[pypi-dependencies]
19# Public PyPI packages
20pandas = ">=2.0.0"
21scikit-learn = "*"
22
23# Private CloudRepo packages
24company-ml-models = { version = ">=3.0.0", index = "cloudrepo" }
25internal-bio-tools = { version = "*", index = "cloudrepo" }
26
27# Use conda for system dependencies, PyPI for Python packages
28[feature.bioinfo.dependencies]
29blast = "*" # NCBI BLAST from bioconda
30samtools = "*"
31bedtools = "*"
32
33[feature.bioinfo.pypi-dependencies]
34biopython = "*"
35company-genomics = { version = ">=1.0.0", index = "cloudrepo" }
Task Runners and Scripts
Define reusable tasks in your Pixi configuration:
1[project]
2name = "task-runner-example"
3
4[tasks]
5# Simple tasks
6test = "pytest tests/"
7lint = "ruff check src/"
8format = "black src/ tests/"
9
10# Complex multi-step tasks
11ci = { depends_on = ["lint", "test", "build"] }
12
13# Tasks with arguments
14test-verbose = "pytest -vvs tests/"
15test-coverage = "pytest --cov=src --cov-report=html"
16
17# Shell scripts
18deploy = """
19echo "Building package..."
20python -m build
21echo "Uploading to CloudRepo..."
22twine upload -r cloudrepo dist/*
23echo "Deployment complete!"
24"""
25
26# Python scripts
27process-data = { cmd = "python scripts/process.py", inputs = ["data/*.csv"], outputs = ["processed/"] }
28
29# Platform-specific tasks
30[target.linux-64.tasks]
31system-info = "uname -a && lscpu"
32
33[target.win-64.tasks]
34system-info = "systeminfo"
35
36# Environment-specific tasks
37[feature.dev.tasks]
38watch = "watchdog src/ --command='pixi run test'"
39debug = "python -m pdb src/main.py"
40
41[feature.prod.tasks]
42start-server = "gunicorn app:main --bind 0.0.0.0:8000"
43health-check = "curl http://localhost:8000/health"
Execute tasks:
# Run single task
pixi run test
pixi run lint
# Run task with dependencies
pixi run ci # Runs lint, test, and build in order
# Run environment-specific task
pixi run -e dev watch
pixi run -e prod start-server
# Pass arguments to tasks
pixi run test -- --maxfail=1
pixi run python -- script.py --arg value
Features and Environments
Create flexible feature combinations:
1[project]
2name = "feature-example"
3
4# Base dependencies
5[dependencies]
6python = "3.11.*"
7
8[pypi-dependencies]
9core-lib = { version = ">=1.0.0", index = "cloudrepo" }
10
11# Testing feature
12[feature.test.dependencies]
13pytest = ">=7.0.0"
14
15[feature.test.pypi-dependencies]
16pytest-cov = "*"
17test-fixtures = { version = "*", index = "cloudrepo" }
18
19[feature.test.tasks]
20test = "pytest"
21test-ci = "pytest --junitxml=report.xml"
22
23# Documentation feature
24[feature.docs.dependencies]
25sphinx = ">=5.0.0"
26
27[feature.docs.pypi-dependencies]
28company-sphinx-theme = { version = ">=1.0.0", index = "cloudrepo" }
29
30[feature.docs.tasks]
31build-docs = "sphinx-build docs/ docs/_build"
32serve-docs = "python -m http.server --directory docs/_build"
33
34# GPU feature
35[feature.gpu.dependencies]
36cuda = "11.8.*"
37cudnn = "8.9.*"
38
39[feature.gpu.pypi-dependencies]
40torch = { version = ">=2.0.0", index = "pytorch" }
41company-gpu-utils = { version = "*", index = "cloudrepo" }
42
43# CPU-only feature (mutual exclusive with gpu)
44[feature.cpu.pypi-dependencies]
45torch = { version = ">=2.0.0+cpu", index = "pytorch-cpu" }
46
47# Define environment combinations
48[environments]
49default = []
50test = ["test"]
51docs = ["docs"]
52dev = ["test", "docs"]
53gpu-dev = ["test", "gpu"]
54cpu-dev = ["test", "cpu"]
55prod-gpu = ["gpu"]
56prod-cpu = ["cpu"]
Use different feature combinations:
# Install specific environment
pixi install -e gpu-dev
pixi install -e cpu-dev
# Run in specific environment
pixi run -e gpu-dev python train_model.py
pixi run -e docs build-docs
# List available environments
pixi info
# Clean specific environment
pixi clean -e test
CI/CD Integration
GitHub Actions
1name: Pixi Package CI/CD
2
3on:
4 push:
5 branches: [main, develop]
6 pull_request:
7 branches: [main]
8 release:
9 types: [published]
10
11jobs:
12 test:
13 strategy:
14 matrix:
15 os: [ubuntu-latest, windows-latest, macos-latest]
16 python: ["3.9", "3.10", "3.11"]
17
18 runs-on: ${{ matrix.os }}
19
20 steps:
21 - uses: actions/checkout@v4
22
23 - name: Setup Pixi
24 uses: prefix-dev/setup-pixi@v0.4.0
25 with:
26 pixi-version: latest
27 cache: true
28 cache-dependency-path: pixi.lock
29
30 - name: Configure CloudRepo Authentication
31 shell: bash
32 env:
33 CLOUDREPO_USERNAME: ${{ secrets.CLOUDREPO_USERNAME }}
34 CLOUDREPO_API_TOKEN: ${{ secrets.CLOUDREPO_API_TOKEN }}
35 run: |
36 (umask 077 && printf 'machine %s\nlogin %s\npassword %s\n' "[organization-id].mycloudrepo.io" \
37 "$CLOUDREPO_USERNAME" "$CLOUDREPO_API_TOKEN" > "$RUNNER_TEMP/.netrc")
38 echo "NETRC=$RUNNER_TEMP/.netrc" >> "$GITHUB_ENV"
39
40 - name: Install Dependencies
41 run: pixi install -e test
42
43 - name: Run Tests
44 run: pixi run -e test test-ci
45
46 - name: Upload Test Results
47 uses: actions/upload-artifact@v4
48 if: always()
49 with:
50 name: test-results-${{ matrix.os }}-${{ matrix.python }}
51 path: report.xml
52
53 build:
54 needs: test
55 runs-on: ubuntu-latest
56
57 steps:
58 - uses: actions/checkout@v4
59
60 - name: Setup Pixi
61 uses: prefix-dev/setup-pixi@v0.4.0
62 with:
63 pixi-version: latest
64
65 - name: Configure CloudRepo Authentication
66 shell: bash
67 env:
68 CLOUDREPO_USERNAME: ${{ secrets.CLOUDREPO_USERNAME }}
69 CLOUDREPO_API_TOKEN: ${{ secrets.CLOUDREPO_API_TOKEN }}
70 run: |
71 (umask 077 && printf 'machine %s\nlogin %s\npassword %s\n' "[organization-id].mycloudrepo.io" \
72 "$CLOUDREPO_USERNAME" "$CLOUDREPO_API_TOKEN" > "$RUNNER_TEMP/.netrc")
73 echo "NETRC=$RUNNER_TEMP/.netrc" >> "$GITHUB_ENV"
74
75 - name: Build Package
76 run: |
77 pixi run python -m build
78
79 - name: Upload Build Artifacts
80 uses: actions/upload-artifact@v4
81 with:
82 name: dist
83 path: dist/
84
85 publish:
86 needs: build
87 runs-on: ubuntu-latest
88 if: github.event_name == 'release'
89
90 steps:
91 - uses: actions/checkout@v4
92
93 - name: Download Build Artifacts
94 uses: actions/download-artifact@v4
95 with:
96 name: dist
97 path: dist/
98
99 - name: Setup Pixi
100 uses: prefix-dev/setup-pixi@v0.4.0
101
102 - name: Publish to CloudRepo
103 # twine reads its login from these three variables, so no .pypirc holds it
104 env:
105 TWINE_REPOSITORY_URL: https://${{ secrets.CLOUDREPO_ORG }}.mycloudrepo.io/repositories/${{ secrets.CLOUDREPO_REPO }}
106 TWINE_USERNAME: ${{ secrets.CLOUDREPO_USERNAME }}
107 TWINE_PASSWORD: ${{ secrets.CLOUDREPO_API_TOKEN }}
108 run: |
109 pixi add --pypi twine
110 pixi run twine upload --non-interactive dist/*
GitLab CI
1stages:
2 - test
3 - build
4 - publish
5
6variables:
7 PIXI_CACHE_DIR: ${CI_PROJECT_DIR}/.pixi_cache
8
9default:
10 image: ghcr.io/prefix-dev/pixi:latest
11 cache:
12 key: ${CI_COMMIT_REF_SLUG}
13 paths:
14 - .pixi_cache/
15 - .pixi/
16 # CLOUDREPO_USERNAME and CLOUDREPO_API_TOKEN are masked CI/CD variables
17 before_script:
18 - export NETRC="$HOME/.netrc"
19 - (umask 077 && printf 'machine %s\nlogin %s\npassword %s\n' "[organization-id].mycloudrepo.io" "$CLOUDREPO_USERNAME" "$CLOUDREPO_API_TOKEN" > "$NETRC")
20
21test:linux:
22 stage: test
23 script:
24 - pixi install -e test
25 - pixi run -e test test-ci
26 artifacts:
27 when: always
28 reports:
29 junit: report.xml
30
31test:windows:
32 stage: test
33 tags:
34 - windows
35 before_script:
36 - $env:NETRC = "$env:USERPROFILE\_netrc"
37 - Set-Content -Path $env:NETRC -Value "machine [organization-id].mycloudrepo.io`nlogin $env:CLOUDREPO_USERNAME`npassword $env:CLOUDREPO_API_TOKEN"
38 script:
39 - pixi install -e test
40 - pixi run -e test test-ci
41
42build:
43 stage: build
44 script:
45 - pixi install
46 - pixi run python -m build
47 artifacts:
48 paths:
49 - dist/
50 expire_in: 1 week
51
52publish:
53 stage: publish
54 only:
55 - tags
56 dependencies:
57 - build
58 script:
59 - pixi add --pypi twine
60 - |
61 # twine reads its login from these three variables, so no .pypirc holds it
62 export TWINE_REPOSITORY_URL="$CLOUDREPO_URL"
63 export TWINE_USERNAME="$CLOUDREPO_USERNAME"
64 export TWINE_PASSWORD="$CLOUDREPO_API_TOKEN"
65 pixi run twine upload --non-interactive dist/*
Azure DevOps
1trigger:
2 branches:
3 include:
4 - main
5 - develop
6 tags:
7 include:
8 - v*
9
10pool:
11 vmImage: 'ubuntu-latest'
12
13stages:
14- stage: Test
15 jobs:
16 - job: TestMatrix
17 strategy:
18 matrix:
19 Linux_Python39:
20 imageName: 'ubuntu-latest'
21 pythonVersion: '3.9'
22 Windows_Python311:
23 imageName: 'windows-latest'
24 pythonVersion: '3.11'
25 macOS_Python310:
26 imageName: 'macOS-latest'
27 pythonVersion: '3.10'
28 pool:
29 vmImage: $(imageName)
30 steps:
31 - task: UsePythonVersion@0
32 inputs:
33 versionSpec: '$(pythonVersion)'
34
35 - script: |
36 curl -fsSL https://pixi.sh/install.sh | bash
37 echo "##vso[task.prependpath]$HOME/.pixi/bin"
38 displayName: 'Install Pixi'
39 condition: ne(variables['Agent.OS'], 'Windows_NT')
40
41 - pwsh: |
42 iwr -useb https://pixi.sh/install.ps1 | iex
43 echo "##vso[task.prependpath]$env:USERPROFILE\.pixi\bin"
44 displayName: 'Install Pixi (Windows)'
45 condition: eq(variables['Agent.OS'], 'Windows_NT')
46
47 - bash: |
48 (umask 077 && printf 'machine %s\nlogin %s\npassword %s\n' "[organization-id].mycloudrepo.io" \
49 "$CLOUDREPO_USERNAME" "$CLOUDREPO_API_TOKEN" > "$(Agent.TempDirectory)/.netrc")
50 echo "##vso[task.setvariable variable=NETRC]$(Agent.TempDirectory)/.netrc"
51 env:
52 CLOUDREPO_USERNAME: $(CloudRepoUsername)
53 CLOUDREPO_API_TOKEN: $(CloudRepoApiToken)
54 displayName: 'Configure CloudRepo Authentication'
55
56 - script: |
57 pixi install -e test
58 pixi run -e test test-ci
59 displayName: 'Run Tests'
60
61 - task: PublishTestResults@2
62 inputs:
63 testResultsFiles: 'report.xml'
64 testRunTitle: 'Python $(pythonVersion) - $(Agent.OS)'
65 condition: always()
66
67- stage: Build
68 dependsOn: Test
69 jobs:
70 - job: BuildPackage
71 steps:
72 - bash: |
73 (umask 077 && printf 'machine %s\nlogin %s\npassword %s\n' "[organization-id].mycloudrepo.io" \
74 "$CLOUDREPO_USERNAME" "$CLOUDREPO_API_TOKEN" > "$(Agent.TempDirectory)/.netrc")
75 echo "##vso[task.setvariable variable=NETRC]$(Agent.TempDirectory)/.netrc"
76 env:
77 CLOUDREPO_USERNAME: $(CloudRepoUsername)
78 CLOUDREPO_API_TOKEN: $(CloudRepoApiToken)
79 displayName: 'Configure CloudRepo Authentication'
80
81 - script: |
82 curl -fsSL https://pixi.sh/install.sh | bash
83 export PATH="$HOME/.pixi/bin:$PATH"
84 pixi install
85 pixi run python -m build
86 displayName: 'Build Package'
87
88 - task: PublishPipelineArtifact@1
89 inputs:
90 targetPath: 'dist'
91 artifactName: 'packages'
92
93- stage: Publish
94 dependsOn: Build
95 condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
96 jobs:
97 - job: PublishToCloudRepo
98 steps:
99 - task: DownloadPipelineArtifact@2
100 inputs:
101 artifactName: 'packages'
102 targetPath: 'dist'
103
104 - script: |
105 curl -fsSL https://pixi.sh/install.sh | bash
106 export PATH="$HOME/.pixi/bin:$PATH"
107 pixi add --pypi twine
108
109 pixi run twine upload --non-interactive dist/*
110 displayName: 'Publish to CloudRepo'
111 # twine reads its login from these three variables, so no .pypirc holds it
112 # (a secret variable reaches a script only through env:)
113 env:
114 TWINE_REPOSITORY_URL: $(CloudRepoUrl)
115 TWINE_USERNAME: $(CloudRepoUsername)
116 TWINE_PASSWORD: $(CloudRepoApiToken)
Troubleshooting
Common Issues and Solutions
Authentication Failures
# Check the .netrc entry without printing the token
sed -E 's/(password)[[:space:]]+[^[:space:]]+/\1 ***/g' "${NETRC:-$HOME/.netrc}"
# Test authentication directly (curl prompts for the token)
curl -u you@example.com \
https://[organization-id].mycloudrepo.io/repositories/[repository-id]/simple/
# Clear Pixi cache and retry
pixi clean
pixi install
# Use verbose mode for debugging
PIXI_VERBOSITY=3 pixi install
SSL/TLS Certificate Issues
# Point pixi at a CA bundle containing the public roots AND your corporate CA.
#
# SET BOTH VARIABLES -- they reach different consumers:
# SSL_CERT_FILE pixi itself (a Rust binary); ignored by requests
# REQUESTS_CA_BUNDLE pip / requests running INSIDE the environment. requests
# never reads SSL_CERT_FILE, and on macOS and Windows nothing
# else supplies it either -- so dropping this one silently
# strips the corporate CA from `pixi run pip install`.
pixi add certifi
CERTIFI_PEM="$(pixi run python -m certifi)" && \
cat "$CERTIFI_PEM" /path/to/corporate-ca.crt > /path/to/ca-bundle.pem
export SSL_CERT_FILE=/path/to/ca-bundle.pem
export REQUESTS_CA_BUNDLE=/path/to/ca-bundle.pem
# Configure git for corporate certificates
git config --global http.sslCAInfo /path/to/ca-bundle.pem
Package Resolution Conflicts
# Show detailed solver information
PIXI_VERBOSITY=3 pixi install
# Clean all caches and reinstall
pixi clean --all
rm pixi.lock
pixi install
# Show dependency tree
pixi list --explicit
# Update specific packages only
pixi update numpy pandas
# Force reinstall
pixi install --force-reinstall
Platform-Specific Issues
# Check current platform
pixi info
# Install for specific platform
pixi install --platform linux-64
# List platform-specific packages
pixi list --platform win-64
# Clear platform-specific cache
pixi clean --platform osx-arm64
A Private Package Installs from PyPI
pixi takes each package from the first index that has it, and ranks extra-index-urls above
index-url. Reordering the indexes protects only the names CloudRepo already has, so pin the
package to CloudRepo instead:
[pypi-dependencies]
acme-private-package = { version = "*", index = "https://[organization-id].mycloudrepo.io/repositories/[repository-id]/simple" }
Lock File Corruption
# Backup current lock file
cp pixi.lock pixi.lock.backup
# Remove and regenerate
rm pixi.lock
pixi install
# Compare with backup if needed
diff pixi.lock pixi.lock.backup
# Update lock without installing
pixi lock
Slow Package Downloads
# Use parallel downloads
export PIXI_MAX_WORKERS=10
# Configure timeout
export PIXI_REQUEST_TIMEOUT=60
# Use a local cache server (if available). Serve it over TLS with a certificate your
# machines already trust -- your internal CA is fine, import it once per machine.
export PIP_INDEX_URL=https://local-cache:8443/simple
# Pre-download packages for offline use
pixi install --frozen
pixi list --json > packages.json
If the cache can only speak plaintext today, keep it bound to the loopback interface so nothing off-host can reach it, and put TLS in front of it before it serves anything on a shared network. Do not reach for a flag that turns certificate verification off – that trades a local problem for one that applies to every host the tool talks to.
Best Practices
Version Control: Always commit both
pixi.tomlandpixi.lockfilesEnvironment Isolation: Use features and environments for different contexts
Security: Never commit credentials; use a
.netrcfile or keyringCross-Platform: Test on all target platforms before releasing
Caching: Leverage Pixi’s caching in CI/CD for faster builds
Documentation: Document custom indices and authentication in README
Updates: Regularly update dependencies with
pixi updateReproducibility: Use
pixi install --frozenin production
Performance Tips
Index Configuration: Place CloudRepo as primary index if most packages are private
Parallel Installation: Enable with
PIXI_MAX_WORKERSenvironment variableCache Management: Use
pixi cleanperiodically to manage disk spaceMinimal Dependencies: Use features to avoid installing unnecessary packages
Platform-Specific Builds: Build native extensions per platform for best performance
Getting Help
If you encounter issues not covered in this documentation:
Check the Pixi documentation
Review CloudRepo’s Knowledge Base
Visit the Pixi Discord community
Contact CloudRepo support at support@cloudrepo.io
CloudRepo support is included with all plans - we respond within one business day and are here to help you succeed with your cross-platform package management.
See also
Python Repositories - Traditional Python repository setup with pip
Poetry Repositories - Poetry-specific configuration for CloudRepo
Continuous Integration and Deployment - Detailed CI/CD integration examples
Repository Management - Managing CloudRepo repositories
Pixi Documentation - Official Pixi documentation
Prefix.dev Blog - Latest updates and tutorials