> ## Documentation Index
> Fetch the complete documentation index at: https://anaconda.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Anaconda GitHub Actions

export const Comments = ({children}) => {
  return <div class="my-4 px-5 py-4 overflow-hidden rounded-2xl flex gap-3 border border-zinc-500/20 bg-zinc-50/50 dark:border-zinc-500/30 dark:bg-zinc-500/10" data-callout-type="comments">
      <div class="w-4">
        <svg width="14" height="14" viewBox="0 0 640 640" fill="currentColor" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" aria-label="Comments">
            <path d="M320 112C434.9 112 528 205.1 528 320C528 434.9 434.9 528 320 528C205.1 528 112 434.9 112 320C112 205.1 205.1 112 320 112zM320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM280 400C266.7 400 256 410.7 256 424C256 437.3 266.7 448 280 448L360 448C373.3 448 384 437.3 384 424C384 410.7 373.3 400 360 400L352 400L352 312C352 298.7 341.3 288 328 288L280 288C266.7 288 256 298.7 256 312C256 325.3 266.7 336 280 336L304 336L304 400L280 400zM320 256C337.7 256 352 241.7 352 224C352 206.3 337.7 192 320 192C302.3 192 288 206.3 288 224C288 241.7 302.3 256 320 256z" />
        </svg>
      </div>
      <div class="text-sm prose min-w-0 w-full">
        {children}
      </div>
    </div>;
};

export const GCell = ({children, className}) => <div className={`grid-table-cell ${className || ""}`} role="cell">
    {children}
  </div>;

export const GTH = ({children, className}) => <div className={`grid-table-th ${className || ""}`} role="columnheader">
    {children}
  </div>;

export const GRow = ({children}) => <div className="grid-table-row" role="row">{children}</div>;

export const GBody = ({children}) => <div className="grid-table-body" role="rowgroup">{children}</div>;

export const GHead = ({children}) => <div className="grid-table-head" role="rowgroup">{children}</div>;

export const GTable = ({children, className, cols}) => <div className={`grid-table not-prose overflow-hidden rounded-2xl ${className || ""}`} style={{
  "--grid-table-cols": cols
}} role="table">
    {children}
  </div>;

Use Anaconda's [GitHub Actions](https://github.com/anaconda/actions) to install tools and publish packages in CI/CD workflows.

The examples install the latest Anaconda CLI. The GitHub Actions themselves are pinned to `v0.3.1` for reproducibility; action versions are independent of CLI versions.

## setup-anaconda-cli

Install the [Anaconda CLI](/docs/cli-reference/anaconda-cli/getting-started) on your runner for use in subsequent workflow steps. The action skips bootstrap. On a fresh runner, install ana first, authenticate, and then install any companion tools you need.

### Usage example

```yaml theme={null}
- uses: anaconda/actions/setup-anaconda-cli@v0.3.1
  with:
    version: latest
- name: Authenticate Anaconda CLI
  shell: bash
  env:
    ANACONDA_API_KEY: ${{ secrets.ANACONDA_API_KEY }}
  run: printf '%s\n' "$ANACONDA_API_KEY" | ana login --force -
- name: Install companion tools
  shell: bash
  run: |
    ana tool install anaconda-cli
    ana tool install pixi
```

Store an anaconda.com API key as the `ANACONDA_API_KEY` secret. This logs ana in so it can install tools. Use a separate registry token to upload packages. If you only need to install ana, omit the login and companion-tool steps until you need them.

### Inputs

<GTable cols="20% 55% 25%">
  <GHead>
    <GRow>
      <GTH>Input</GTH>
      <GTH>Description</GTH>
      <GTH>Default</GTH>
    </GRow>
  </GHead>

  <GBody>
    <GRow>
      <GCell>`version`</GCell>
      <GCell>CLI release tag to install, or `latest` to use the installer's default version</GCell>
      <GCell>`latest`</GCell>
    </GRow>

    <GRow>
      <GCell>`tools`</GCell>
      <GCell>Space- or comma-separated list of [available managed tools](/docs/cli-reference/anaconda-cli/tools#available-managed-tools) to install; requires existing ana credentials on the runner. On a fresh runner, leave this empty and install tools after login as shown above.</GCell>
      <GCell>None</GCell>
    </GRow>
  </GBody>
</GTable>

### Outputs

<GTable cols="30% 70%">
  <GHead>
    <GRow>
      <GTH>Output</GTH>
      <GTH>Description</GTH>
    </GRow>
  </GHead>

  <GBody>
    <GRow>
      <GCell>`version`</GCell>
      <GCell>Installed CLI version (see note below)</GCell>
    </GRow>
  </GBody>
</GTable>

<Note>
  The current action can return an empty `version` output. To check the installed version, run `ana --version` in a later step instead.
</Note>

### Platform support

The setup action supports Linux, macOS, and Windows runners with Bash and curl available. Choose a runner architecture supported by the [CLI release](/docs/cli-reference/anaconda-cli/getting-started).

## upload-package

Upload conda or Python packages to Anaconda.org or Package Security Manager (PSM). The `target-type` input selects the registry.

Complete the [setup sequence](#usage-example) first to use the latest CLI and install the companion tools. The upload action skips tool installation when `ana` is already on `PATH`; without a prior setup step, it uses its own bundled CLI version instead of selecting the latest release.

Use a Linux runner for the upload examples. The upload action's Anaconda.org support is not Windows-compatible.

### Usage examples

Add one of these upload steps after the setup, login, and companion-tool installation steps above.

<Tabs>
  <Tab title="Anaconda.org">
    Upload a conda package to your Anaconda.org organization:

    ```yaml theme={null}
    - uses: anaconda/actions/upload-package@v0.3.1
      with:
        token: ${{ secrets.ANACONDA_ORG_TOKEN }}
        channel: <ORG_NAME>
        packages: ./build/noarch/*.conda
    ```

    <Comments>
      Replace \<ORG\_NAME> with your Anaconda.org username or organization name.
    </Comments>

    Upload a Python wheel as a private package with labels:

    ```yaml theme={null}
    - uses: anaconda/actions/upload-package@v0.3.1
      with:
        token: ${{ secrets.ANACONDA_ORG_TOKEN }}
        channel: <ORG_NAME>
        packages: ./dist/*.whl
        private: true
        labels: main,dev
    ```

    <Comments>
      Replace \<ORG\_NAME> with your Anaconda.org username or organization name.
    </Comments>
  </Tab>

  <Tab title="Package Security Manager">
    Upload a conda package to Package Security Manager:

    ```yaml theme={null}
    - uses: anaconda/actions/upload-package@v0.3.1
      with:
        token: ${{ secrets.PSM_TOKEN }}
        channel: <CHANNEL_NAME>
        packages: ./build/noarch/*.conda
        target-type: psm
        repository-url: https://<PSM_HOST>/api/repo
    ```

    <Comments>
      Replace \<CHANNEL\_NAME> with your PSM channel name.<br />
      Replace \<PSM\_HOST> with the hostname of your PSM instance.
    </Comments>

    <Note>Your GitHub Actions runner must have network access to your PSM instance.</Note>
  </Tab>
</Tabs>

### Authentication

The `upload-package` action requires an API token for the registry you're publishing to. Generate a token for your target registry, then store it as a [GitHub Actions secret](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions) in your repository or organization settings.

* **Anaconda.org**: [Generate a token](/docs/anaconda-org/admin-guide/tokens) with the `api:write` scope.
* **Package Security Manager**: [Generate a token](/docs/psm-on-prem/6.8.0/user/auth_token) with **Write** permissions for your target channel.

### Inputs

<GTable cols="22% 55% 23%">
  <GHead>
    <GRow>
      <GTH>Input</GTH>
      <GTH>Description</GTH>
      <GTH>Default</GTH>
    </GRow>
  </GHead>

  <GBody>
    <GRow>
      <GCell>`token`</GCell>
      <GCell><Badge shape="pill" stroke color="blue" size="xs">Required</Badge> API token for authentication</GCell>
      <GCell>None</GCell>
    </GRow>

    <GRow>
      <GCell>`packages`</GCell>
      <GCell><Badge shape="pill" stroke color="blue" size="xs">Required</Badge> Path or glob pattern to package files (for example, `./build/noarch/*.conda`)</GCell>
      <GCell>None</GCell>
    </GRow>

    <GRow>
      <GCell>`channel`</GCell>
      <GCell><Badge shape="pill" stroke color="blue" size="xs">Required</Badge> Channel to publish your package to, unless the deprecated `owner` input is set <br />
      <Tip>For Anaconda.org, this is your username or organization name.</Tip></GCell>
      <GCell>None</GCell>
    </GRow>

    <GRow>
      <GCell>`target-type`</GCell>
      <GCell>`anaconda.org` or `psm`</GCell>
      <GCell>`anaconda.org`</GCell>
    </GRow>

    <GRow>
      <GCell>`repository-url`</GCell>
      <GCell>Repository URL (required for `psm` target)</GCell>
      <GCell>None</GCell>
    </GRow>

    <GRow>
      <GCell>`package-type`</GCell>
      <GCell>`conda` or `pypi` (auto-detected from file extension if omitted)</GCell>
      <GCell>None</GCell>
    </GRow>

    <GRow>
      <GCell>`verbose`</GCell>
      <GCell>Enable verbose output</GCell>
      <GCell>`false`</GCell>
    </GRow>
  </GBody>
</GTable>

The following inputs apply only when `target-type` is `anaconda.org`:

<GTable cols="20% 55% 25%">
  <GHead>
    <GRow>
      <GTH>Input</GTH>
      <GTH>Description</GTH>
      <GTH>Default</GTH>
    </GRow>
  </GHead>

  <GBody>
    <GRow>
      <GCell>`summary`</GCell>
      <GCell>Package summary</GCell>
      <GCell>None</GCell>
    </GRow>

    <GRow>
      <GCell>`private`</GCell>
      <GCell>Make the package private</GCell>
      <GCell>`false`</GCell>
    </GRow>

    <GRow>
      <GCell>`labels`</GCell>
      <GCell>Comma-separated labels (for example, `main,dev`)</GCell>
      <GCell>None</GCell>
    </GRow>

    <GRow>
      <GCell>`force`</GCell>
      <GCell>Overwrite existing packages (not forwarded to PSM)</GCell>
      <GCell>`false`</GCell>
    </GRow>

    <GRow>
      <GCell>`disable-new-cli`</GCell>
      <GCell>Stop forcing the new `anaconda-client` parser</GCell>
      <GCell>`false`</GCell>
    </GRow>
  </GBody>
</GTable>

For existing workflows, `owner` is a deprecated alias for `channel`, and `target-type: repository` is a deprecated alias for `psm`. Use `channel` and `psm` in new workflows. If both `channel` and `owner` are set, `channel` takes precedence.

<Note>
  Summaries containing spaces do not work correctly with action v0.3.1. Omit the `summary` input and set the summary in your package metadata instead.
</Note>

## Full workflow example

This workflow builds a conda package on push to a version tag and publishes it to Anaconda.org. It assumes your repository contains a noarch recipe in `recipe/` that produces a `.conda` package:

```yaml expandable theme={null}
name: Build and publish

on:
  push:
    tags:
      - 'v*'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Miniconda
        uses: conda-incubator/setup-miniconda@v3
        with:
          channels: defaults

      - name: Build conda package
        shell: bash -el {0}
        run: |
          conda install --yes conda-build
          conda build recipe --output-folder ./build

      - name: Upload build artifact
        uses: actions/upload-artifact@v4
        with:
          name: conda-package
          path: ./build/noarch/*.conda

  publish:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: conda-package
          path: ./dist

      - name: Set up Anaconda CLI
        uses: anaconda/actions/setup-anaconda-cli@v0.3.1
        with:
          version: latest

      - name: Authenticate Anaconda CLI
        shell: bash
        env:
          ANACONDA_API_KEY: ${{ secrets.ANACONDA_API_KEY }}
        run: printf '%s\n' "$ANACONDA_API_KEY" | ana login --force -

      - name: Install companion tools
        shell: bash
        run: |
          ana tool install anaconda-cli
          ana tool install pixi

      - name: Publish to Anaconda.org
        uses: anaconda/actions/upload-package@v0.3.1
        with:
          token: ${{ secrets.ANACONDA_ORG_TOKEN }}
          channel: <ORG_NAME>
          packages: ./dist/*.conda
```

<Comments>
  Replace \<ORG\_NAME> with your Anaconda.org username or organization name.
</Comments>

## Migrating from existing workflows

If you're currently using CLI commands directly in your workflows, you can replace them with the `upload-package` action. Run the [setup sequence](#usage-example) before the replacement upload step so it uses the latest CLI:

<Tabs>
  <Tab title="Anaconda.org">
    **Before:**

    ```yaml theme={null}
    - name: Install anaconda-client
      run: conda install --yes anaconda-client
    - name: Upload
      env:
        ANACONDA_API_TOKEN: ${{ secrets.TOKEN }}
      run: |
        anaconda --token "$ANACONDA_API_TOKEN" upload \
          --user <ORG_NAME> \
          --private \
          ./build/*.conda
    ```

    <Comments>
      Replace \<ORG\_NAME> with your Anaconda.org username or organization name.
    </Comments>

    **After:**

    ```yaml theme={null}
    - uses: anaconda/actions/upload-package@v0.3.1
      with:
        token: ${{ secrets.TOKEN }}
        channel: <ORG_NAME>
        packages: ./build/*.conda
        private: true
    ```

    <Comments>
      Replace \<ORG\_NAME> with your Anaconda.org username or organization name.
    </Comments>
  </Tab>

  <Tab title="Package Security Manager">
    **Before:**

    ```yaml theme={null}
    - name: Install conda-repo-cli
      run: conda install --yes conda-repo-cli
    - name: Upload
      env:
        PSM_TOKEN: ${{ secrets.TOKEN }}
      run: |
        conda repo config --set sites.mysite.url https://<PSM_HOST>/api/repo
        conda repo --token "$PSM_TOKEN" --site mysite upload \
          --channel <CHANNEL_NAME> \
          --package-type conda \
          ./build/*.conda
    ```

    <Comments>
      Replace \<PSM\_HOST> with the hostname of your PSM instance.<br />
      Replace \<CHANNEL\_NAME> with your PSM channel name.
    </Comments>

    **After:**

    ```yaml theme={null}
    - uses: anaconda/actions/upload-package@v0.3.1
      with:
        token: ${{ secrets.TOKEN }}
        channel: <CHANNEL_NAME>
        packages: ./build/*.conda
        target-type: psm
        repository-url: https://<PSM_HOST>/api/repo
    ```

    <Comments>
      Replace \<CHANNEL\_NAME> with your PSM channel name.<br />
      Replace \<PSM\_HOST> with the hostname of your PSM instance.
    </Comments>
  </Tab>
</Tabs>

## Release history

For action versions and changes, see the [GitHub Actions releases](https://github.com/anaconda/actions/releases). For the implementation documented here, see the [setup action source](https://github.com/anaconda/actions/blob/v0.3.1/setup-anaconda-cli/action.yml) and [upload action source](https://github.com/anaconda/actions/blob/v0.3.1/upload-package/action.yml).
