Vue d'ensemble
Vue d'ensemble
URL du projet : https://github.com/milvus-io/milvus_cli
Préparation : Python3.8
, Click 8.0.x
Grouper les commandes
Créer une commande
import click
from utils import PyOrm
@click.group(no_args_is_help=False, add_help_option=False, invoke_without_command=True)
@click.pass_context
def cli(ctx):
"""Milvus CLI"""
ctx.obj = PyOrm() # PyOrm is a util class which wraps the milvus python SDK. You can pass any class instance here. Any command function passed by @click.obj can call it.
if __name__ == '__main__':
cli()
Comme dans le code ci-dessus, nous utilisons @click.group()
pour créer un groupe de commandes cli
comme point d'entrée. Pour implémenter une CLI prompte, nous devons désactiver les messages d'aide pour l'entrée, donc nous ajoutons no_args_is_help=False
, add_help_option=False
et invoke_without_command=True
. Rien ne sera imprimé si nous saisissons cli
dans le terminal uniquement.
En outre, nous utilisons @click.pass_context
pour transmettre un contexte à ce groupe en vue d'une utilisation ultérieure.
Créer une sous-commande du groupe de commandes
Nous ajoutons ensuite la première sous-commande help
sous cli
:
# Print the help message of specified command.
def print_help_msg(command):
with click.Context(command) as ctx:
click.echo(command.get_help(ctx))
# Use @cli.command() to create a sub command of cli.
@cli.command()
def help():
"""Show help messages."""
# Print help message of cli.
click.echo(print_help_msg(cli))
Nous pouvons maintenant utiliser cli help
dans le terminal :
$ python milvus_cli/scripts/milvus_cli.py help
Créer un sous-groupe d'un groupe de commandes
Non seulement nous voulons avoir une sous-commande comme cli help
, mais nous avons aussi besoin de sous-groupes de commandes comme cli list collection
, cli list partition
et cli list indexes
.
Tout d'abord, nous créons un sous-groupe de commandes list
, ici nous pouvons passer le premier paramètre à @cli.group
comme nom de commande au lieu d'utiliser le nom de la fonction par défaut, de sorte que nous pouvons réduire les noms de fonctions dupliqués.
Attention, nous utilisons ici @cli.group()
au lieu de @click.group
afin de créer un sous-groupe du groupe d'origine.
Nous utilisons ensuite @click.pass_obj
pour transmettre context.obj
aux sous-commandes de ce sous-groupe.
@cli.group('list', no_args_is_help=False)
@click.pass_obj
def listDetails(obj):
"""List collections, partitions and indexes."""
pass
Ensuite, nous ajoutons des sous-commandes à ce sous-groupe par @listDetails.command()
(et non @cli.command()
). Ce n'est qu'un exemple, vous pouvez ignorer l'implémentation et nous en discuterons plus tard.
@listDetails.command()
@click.option('--timeout', 'timeout', help="[Optional] - An optional duration of time in seconds to allow for the RPC. When timeout is set to None, client waits until server response or error occur.", default=None)
@click.option('--show-loaded', 'showLoaded', help="[Optional] - Only show loaded collections.", default=False)
@click.pass_obj
def collections(obj, timeout, showLoaded):
"""List all collections."""
try:
obj.checkConnection()
click.echo(obj.listCollections(timeout, showLoaded))
except Exception as e:
click.echo(message=e, err=True)
@listDetails.command()
@click.option('-c', '--collection', 'collection', help='The name of collection.', default='')
@click.pass_obj
def partitions(obj, collection):
"""List all partitions of the specified collection."""
try:
obj.checkConnection()
validateParamsByCustomFunc(
obj.getTargetCollection, 'Collection Name Error!', collection)
click.echo(obj.listPartitions(collection))
except Exception as e:
click.echo(message=e, err=True)
Une fois toutes ces opérations terminées, nous avons une commande miltigroup qui ressemble à ceci :
image
Personnaliser une commande
Ajouter des options
Vous pouvez ajouter des options à une commande qui sera utilisée comme cli --test-option value
.
Voici un exemple, nous ajoutons trois options alias
, host
et port
pour spécifier une adresse pour se connecter à Milvus.
Les deux premiers paramètres définissent le nom court et le nom complet de l'option, le troisième paramètre définit le nom de la variable, le paramètre help
spécifie le message d'aide court, le paramètre default
spécifie la valeur par défaut et le paramètre type
spécifie le type de valeur.
Toutes les valeurs des options seront transmises à la fonction dans l'ordre de leur définition.
@cli.command(no_args_is_help=False)
@click.option('-a', '--alias', 'alias', help="Milvus link alias name, default is `default`.", default='default', type=str)
@click.option('-h', '--host', 'host', help="Host name, default is `127.0.0.1`.", default='127.0.0.1', type=str)
@click.option('-p', '--port', 'port', help="Port, default is `19530`.", default=19530, type=int)
@click.pass_obj
def connect(obj, alias, host, port):
pass
Ajouter des options de drapeau
Nous avons utilisé les options ci-dessus pour transmettre une valeur, mais il arrive que nous ayons simplement besoin d'un drapeau comme valeur booléenne.
Dans l'exemple ci-dessous, l'option autoId
est une option de drapeau et ne transmet aucune donnée à la fonction, nous pouvons donc l'utiliser comme cli create collection -c c_name -p p_name -a
.
@createDetails.command('collection')
@click.option('-c', '--collection-name', 'collectionName', help='Collection name to be created.', default='')
@click.option('-p', '--schema-primary-field', 'primaryField', help='Primary field name.', default='')
@click.option('-a', '--schema-auto-id', 'autoId', help='Enable auto id.', default=False, is_flag=True)
@click.pass_obj
def createCollection(obj, collectionName, primaryField, autoId, description, fields):
pass
Ajouter des arguments
Dans ce projet, nous remplaçons tous les arguments par des options. Mais nous introduisons toujours l'utilisation des arguments ici. Contrairement aux options, les arguments sont utilisés comme cli COMMAND [OPTIONS] ARGUEMENTS
. Si nous convertissons l'exemple ci-dessus en utilisation d'arguments, ce sera comme ceci :
@createDetails.command('collection')
@click.argument('collectionName')
@click.option('-p', '--schema-primary-field', 'primaryField', help='Primary field name.', default='')
@click.option('-a', '--schema-auto-id', 'autoId', help='Enable auto id.', default=False, is_flag=True)
@click.pass_obj
def createCollection(obj, collectionName, primaryField, autoId, description, fields):
pass
L'utilisation devrait alors être cli create collection c_name -p p_name -a
.
Ajouter un message d'aide complet
Comme nous avons défini le message d'aide court ci-dessus, nous pouvons définir le message d'aide complet dans la fonction :
@cli.command(no_args_is_help=False)
@click.option('-a', '--alias', 'alias', help="Milvus link alias name, default is `default`.", default='default', type=str)
@click.option('-h', '--host', 'host', help="Host name, default is `127.0.0.1`.", default='127.0.0.1', type=str)
@click.option('-p', '--port', 'port', help="Port, default is `19530`.", default=19530, type=int)
@click.pass_obj
def connect(obj, alias, host, port):
"""
Connect to Milvus.
Example:
milvus_cli > connect -h 127.0.0.1 -p 19530 -a default
"""
try:
obj.connect(alias, host, port)
except Exception as e:
click.echo(message=e, err=True)
else:
click.echo("Connect Milvus successfully!")
click.echo(obj.showConnection(alias))
Le premier bloc à l'intérieur de la fonction est le message d'aide qui sera imprimé après la saisie de cli connect --help
.
milvus_cli > connect --help
Usage: milvus_cli.py connect [OPTIONS]
Connect to Milvus.
Example:
milvus_cli > connect -h 127.0.0.1 -p 19530 -a default
Options:
-a, --alias TEXT Milvus link alias name, default is `default`.
-h, --host TEXT Host name, default is `127.0.0.1`.
-p, --port INTEGER Port, default is `19530`.
--help Show this message and exit.
Ajouter une confirmation
Il arrive que l'utilisateur doive confirmer une action, en particulier la suppression d'un élément. Nous pouvons ajouter click.confirm
pour faire une pause et demander à l'utilisateur de confirmer :
@deleteSth.command('collection')
@click.option('-c', '--collection', 'collectionName', help='The name of collection to be deleted.', default='')
@click.option('-t', '--timeout', 'timeout', help='An optional duration of time in seconds to allow for the RPC. If timeout is set to None, the client keeps waiting until the server responds or an error occurs.', default=None, type=int)
@click.pass_obj
def deleteCollection(obj, collectionName, timeout):
"""
Drops the collection together with its index files.
Example:
milvus_cli > delete collection -c car
"""
click.echo(
"Warning!\nYou are trying to delete the collection with data. This action cannot be undone!\n")
if not click.confirm('Do you want to continue?'):
return
pass
Comme dans l'exemple ci-dessus, une conversation de confirmation s'affichera sous la forme suivante : Aborted!ant to continue? [y/N]:
.
Ajouter des invites
Pour mettre en place des invites, il suffit d'ajouter click.prompt
.
@cli.command()
@click.pass_obj
def query(obj):
"""
Query with a set of criteria, and results in a list of records that match the query exactly.
"""
collectionName = click.prompt(
'Collection name', type=click.Choice(obj._list_collection_names()))
expr = click.prompt('The query expression(field_name in [x,y])')
partitionNames = click.prompt(
f'The names of partitions to search(split by "," if multiple) {obj._list_partition_names(collectionName)}', default='')
outputFields = click.prompt(
f'Fields to return(split by "," if multiple) {obj._list_field_names(collectionName)}', default='')
timeout = click.prompt('timeout', default='')
pass
L'invite s'affichera à chaque fois que click.prompt
sera utilisé. Nous utilisons quelques invites en série pour que cela ressemble à une conversation continue. Cela permet de s'assurer que l'utilisateur saisira les données dans l'ordre souhaité. Dans ce cas, l'utilisateur doit d'abord choisir une collection, puis récupérer toutes les partitions de cette collection, avant de les montrer à l'utilisateur pour qu'il les choisisse.
Ajouter des choix
Parfois, vous souhaitez que l'utilisateur ne saisisse qu'une plage/un type de valeur limité, vous pouvez ajouter type=click.Choice([<any>])
à click.prompt
, click.options
, etc...
Par exemple,
collectionName = click.prompt(
'Collection name', type=click.Choice(['collection_1', 'collection_2']))
L'utilisateur ne peut alors saisir que collection_1
ou collection_2
, une erreur se produira si d'autres valeurs sont saisies.
Ajouter un écran clair
Vous pouvez utiliser click.clear()
pour l'implémenter.
@cli.command()
def clear():
"""Clear screen."""
click.clear()
Conseils supplémentaires
- La valeur par défaut est
None
, il est donc inutile de spécifier la valeur par défautNone
. De plus, la valeur par défautNone
entraînera l'affichage continu declick.prompt
si vous souhaitez laisser une valeur vide pour passer par-dessus.
Mise en œuvre d'une invite CLI pour la saisie par l'utilisateur
Pourquoi l'invite CLI ?
Pour le fonctionnement de la base de données, nous avons besoin d'une connexion continue à une instance. Si nous utilisons le mode de ligne de commande d'origine, la connexion sera interrompue après chaque commande exécutée. Nous voulons également stocker certaines données lors de l'utilisation du CLI, et les nettoyer après la sortie.
Mise en œuvre
- Utilisez
while True
pour écouter continuellement les entrées de l'utilisateur.
def runCliPrompt():
while True:
astr = input('milvus_cli > ')
try:
cli(astr.split())
except SystemExit:
# trap argparse error message
# print('error', SystemExit)
continue
if __name__ == '__main__':
runCliPrompt()
- L'utilisation de
input
uniquement entraînera la conversion automatique des touches fléchéesup
,down
,left
,right
, de la touchetab
et d'autres touches en chaînes Acsii. En outre, les commandes de l'historique ne peuvent pas être lues à partir de la session. Nous ajoutons doncreadline
à la fonctionrunCliPrompt
.
def runCliPrompt():
while True:
import readline
readline.set_completer_delims(' \t\n;')
astr = input('milvus_cli > ')
try:
cli(astr.split())
except SystemExit:
# trap argparse error message
# print('error', SystemExit)
continue
- Ajouter
quit
CLI.
@cli.command('exit')
def quitapp():
"""Exit the CLI."""
global quitapp
quitapp = True
quitapp = False # global flag
def runCliPrompt():
while not quitapp:
import readline
readline.set_completer_delims(' \t\n;')
astr = input('milvus_cli > ')
try:
cli(astr.split())
except SystemExit:
# trap argparse error message
# print('error', SystemExit)
continue
- Correction de l'erreur
KeyboardInterrupt
lors de l'utilisation dectrl C
pour quitter.
def runCliPrompt():
try:
while not quitapp:
import readline
readline.set_completer_delims(' \t\n;')
astr = input('milvus_cli > ')
try:
cli(astr.split())
except SystemExit:
# trap argparse error message
# print('error', SystemExit)
continue
except KeyboardInterrupt:
sys.exit(0)
- Après tous ces réglages, le CLI ressemble maintenant à ce qui suit :
milvus_cli >
milvus_cli > connect
+-------+-----------+
| Host | 127.0.0.1 |
| Port | 19530 |
| Alias | default |
+-------+-----------+
milvus_cli > help
Usage: [OPTIONS] COMMAND [ARGS]...
Milvus CLI
Commands:
clear Clear screen.
connect Connect to Milvus.
create Create collection, partition and index.
delete Delete specified collection, partition and index.
describe Describe collection or partition.
exit Exit the CLI.
help Show help messages.
import Import data from csv file with headers and insert into target...
list List collections, partitions and indexes.
load Load specified collection.
query Query with a set of criteria, and results in a list of...
release Release specified collection.
search Conducts a vector similarity search with an optional boolean...
show Show connection, loading_progress and index_progress.
version Get Milvus CLI version.
milvus_cli > exit
Implémentation manuelle de l'autocomplétion
Contrairement à l'autocomplétion du shell de click, notre projet reprend la ligne de commande et utilise une boucle pour récupérer les données de l'utilisateur afin d'implémenter une ligne de commande rapide. Nous devons donc lier un compléteur à readline
.
class Completer(object):
RE_SPACE = re.compile('.*\s+$', re.M)
CMDS_DICT = {
'clear': [],
'connect': [],
'create': ['collection', 'partition', 'index'],
'delete': ['collection', 'partition', 'index'],
'describe': ['collection', 'partition'],
'exit': [],
'help': [],
'import': [],
'list': ['collections', 'partitions', 'indexes'],
'load': [],
'query': [],
'release': [],
'search': [],
'show': ['connection', 'index_progress', 'loading_progress'],
'version': [],
}
def __init__(self) -> None:
super().__init__()
self.COMMANDS = list(self.CMDS_DICT.keys())
self.createCompleteFuncs(self.CMDS_DICT)
def createCompleteFuncs(self, cmdDict):
for cmd in cmdDict:
sub_cmds = cmdDict[cmd]
complete_example = self.makeComplete(cmd, sub_cmds)
setattr(self, 'complete_%s' % cmd, complete_example)
def makeComplete(self, cmd, sub_cmds):
def f_complete(args):
f"Completions for the {cmd} command."
if not args:
return self._complete_path('.')
if len(args) <= 1 and not cmd == 'import':
return self._complete_2nd_level(sub_cmds, args[-1])
return self._complete_path(args[-1])
return f_complete
def _listdir(self, root):
"List directory 'root' appending the path separator to subdirs."
res = []
for name in os.listdir(root):
path = os.path.join(root, name)
if os.path.isdir(path):
name += os.sep
res.append(name)
return res
def _complete_path(self, path=None):
"Perform completion of filesystem path."
if not path:
return self._listdir('.')
dirname, rest = os.path.split(path)
tmp = dirname if dirname else '.'
res = [os.path.join(dirname, p)
for p in self._listdir(tmp) if p.startswith(rest)]
# more than one match, or single match which does not exist (typo)
if len(res) > 1 or not os.path.exists(path):
return res
# resolved to a single directory, so return list of files below it
if os.path.isdir(path):
return [os.path.join(path, p) for p in self._listdir(path)]
# exact file match terminates this completion
return [path + ' ']
def _complete_2nd_level(self, SUB_COMMANDS=[], cmd=None):
if not cmd:
return [c + ' ' for c in SUB_COMMANDS]
res = [c for c in SUB_COMMANDS if c.startswith(cmd)]
if len(res) > 1 or not (cmd in SUB_COMMANDS):
return res
return [cmd + ' ']
def complete(self, text, state):
"Generic readline completion entry point."
buffer = readline.get_line_buffer()
line = readline.get_line_buffer().split()
# show all commands
if not line:
return [c + ' ' for c in self.COMMANDS][state]
# account for last argument ending in a space
if self.RE_SPACE.match(buffer):
line.append('')
# resolve command to the implementation function
cmd = line[0].strip()
if cmd in self.COMMANDS:
impl = getattr(self, 'complete_%s' % cmd)
args = line[1:]
if args:
return (impl(args) + [None])[state]
return [cmd + ' '][state]
results = [
c + ' ' for c in self.COMMANDS if c.startswith(cmd)] + [None]
return results[state]
Après avoir défini Completer
, nous pouvons le lier à readline :
comp = Completer()
def runCliPrompt():
try:
while not quitapp:
import readline
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")
readline.set_completer(comp.complete)
astr = input('milvus_cli > ')
try:
cli(astr.split())
except SystemExit:
# trap argparse error message
# print('error', SystemExit)
continue
except KeyboardInterrupt:
sys.exit(0)
Ajouter une option unique
Pour la ligne de commande rapide, parfois nous ne voulons pas exécuter complètement les scripts pour obtenir certaines informations telles que la version. Un bon exemple est Python
, lorsque vous tapez python
dans le terminal, la ligne de commande promtp s'affichera, mais elle ne renverra qu'un message de version et n'entrera pas dans les scripts d'invite si vous tapez python -V
. Nous pouvons donc utiliser sys.args
dans notre code pour l'implémenter.
def runCliPrompt():
args = sys.argv
if args and (args[-1] == '--version'):
print(f"Milvus Cli v{getPackageVersion()}")
return
try:
while not quitapp:
import readline
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")
readline.set_completer(comp.complete)
astr = input('milvus_cli > ')
try:
cli(astr.split())
except SystemExit:
# trap argparse error message
# print('error', SystemExit)
continue
except KeyboardInterrupt:
sys.exit(0)
if __name__ == '__main__':
runCliPrompt()
Nous obtenons sys.args
avant la boucle lors de la première exécution des scripts CLI. Si le dernier argument est --version
, le code renverra la version du paquet sans entrer dans la boucle.
Cela sera utile une fois que nous aurons créé les codes en tant que paquet. L'utilisateur peut taper milvus_cli
pour accéder à un prompt CLI, ou taper milvus_cli --version
pour obtenir uniquement la version.
Construction et publication
Enfin, nous voulons créer un paquet et le publier par PYPI. Ainsi, l'utilisateur peut simplement utiliser pip install <package name>
pour l'installer.
Installer localement pour tester
Avant de publier le paquetage sur PYPI, vous pouvez l'installer localement pour effectuer des tests.
Dans ce cas, vous pouvez simplement placer cd
dans le répertoire du paquet et lancer pip install -e .
(n'oubliez pas .
).
Créer les fichiers du paquet
Voir : https://packaging.python.org/tutorials/packaging-projects/
La structure d'un paquetage doit ressembler à ce qui suit :
package_example/
├── LICENSE
├── README.md
├── setup.py
├── src/
│ ├── __init__.py
│ ├── main.py
│ └── scripts/
│ ├── __init__.py
│ └── example.py
└── tests/
Créer le répertoire du paquet
Créez le répertoire Milvus_cli
avec la structure ci-dessous :
Milvus_cli/
├── LICENSE
├── README.md
├── setup.py
├── milvus_cli/
│ ├── __init__.py
│ ├── main.py
│ ├── utils.py
│ └── scripts/
│ ├── __init__.py
│ └── milvus_cli.py
└── dist/
Écrire le code d'entrée
L'entrée du script doit être dans Milvus_cli/milvus_cli/scripts
, et le Milvus_cli/milvus_cli/scripts/milvus_cli.py
doit être comme :
import sys
import os
import click
from utils import PyOrm, Completer
pass_context = click.make_pass_decorator(PyOrm, ensure=True)
@click.group(no_args_is_help=False, add_help_option=False, invoke_without_command=True)
@click.pass_context
def cli(ctx):
"""Milvus CLI"""
ctx.obj = PyOrm()
"""
...
Here your code.
...
"""
@cli.command('exit')
def quitapp():
"""Exit the CLI."""
global quitapp
quitapp = True
quitapp = False # global flag
comp = Completer()
def runCliPrompt():
args = sys.argv
if args and (args[-1] == '--version'):
print(f"Milvus Cli v{getPackageVersion()}")
return
try:
while not quitapp:
import readline
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")
readline.set_completer(comp.complete)
astr = input('milvus_cli > ')
try:
cli(astr.split())
except SystemExit:
# trap argparse error message
# print('error', SystemExit)
continue
except Exception as e:
click.echo(
message=f"Error occurred!\n{str(e)}", err=True)
except KeyboardInterrupt:
sys.exit(0)
if __name__ == '__main__':
runCliPrompt()
Modifier le code d'entrée setup.py
from setuptools import setup, find_packages
with open("README.md", "r", encoding="utf-8") as fh:
long_description = fh.read()
setup(
name='milvus_cli',
version='0.1.6',
author='Milvus Team',
author_email='milvus-team@zilliz.com',
url='https://github.com/milvus-io/milvus_cli',
description='CLI for Milvus',
long_description=long_description,
long_description_content_type='text/markdown',
license='Apache-2.0',
packages=find_packages(),
include_package_data=True,
install_requires=[
'Click==8.0.1',
'pymilvus==2.0.0rc5',
'tabulate==0.8.9'
],
entry_points={
'console_scripts': [
'milvus_cli = milvus_cli.scripts.milvus_cli:runCliPrompt',
],
},
python_requires='>=3.8'
)
Quelques conseils ici :
- Nous utilisons le contenu de
README.md
comme description longue du paquet. - Ajoutez toutes les dépendances à
install_requires
. - Spécifiez le
entry_points
. Dans ce cas, nous définissonsmilvus_cli
comme un enfant deconsole_scripts
, de sorte que nous puissions tapermilvus_cli
comme commande directement après l'installation de ce paquet. Le point d'entrée demilvus_cli
est la fonctionrunCliPrompt
dansmilvus_cli/scripts/milvus_cli.py
.
Construction
Mettez à jour le paquet
build
:python3 -m pip install --upgrade build
Exécutez la commande build :
python -m build --sdist --wheel --outdir dist/ .
Deux fichiers seront générés dans le répertoire
dist/
:
dist/
example_package_YOUR_USERNAME_HERE-0.0.1-py3-none-any.whl
example_package_YOUR_USERNAME_HERE-0.0.1.tar.gz
Publier la version
Se référer à : https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives
- Mettre à jour le paquet
twine
:python3 -m pip install --upgrade twine
- Télécharger sur
PYPI
test env :python3 -m twine upload --repository testpypi dist/*
- Télécharger sur
PYPI
:python3 -m twine upload dist/*
Flux de travail CI/CD par Github
Se référer à : https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/
Nous voulons un moyen d'uploader les assets automatiquement, il peut construire les paquets et les uploader vers les releases github et PYPI.
(Pour une raison ou une autre, nous voulons que le workflow ne publie que la version pour tester PYPI).
# This is a basic workflow to help you get started with Actions
name: Update the release's assets after it published
# Controls when the workflow will run
on:
release:
# The workflow will run after release published
types: [published]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.8'
architecture: 'x64'
- name: Install pypa/build
run: >-
python -m
pip install
build
--user
- name: Clean dist/
run: |
sudo rm -fr dist/*
- name: Build a binary wheel and a source tarball
run: >-
python -m
build
--sdist
--wheel
--outdir dist/
.
# Update target github release's assets
- name: Update assets
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: ./dist/*
- name: Publish distribution 📦 to Test PyPI
if: contains(github.ref, 'beta') && startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
repository_url: https://test.pypi.org/legacy/
packages_dir: dist/
verify_metadata: false
En savoir plus sur Milvus
Milvus est un outil puissant capable d'alimenter une vaste gamme d'applications d'intelligence artificielle et de recherche de similarités vectorielles. Pour en savoir plus sur le projet, consultez les ressources suivantes :
- Grouper les commandes
- Personnaliser une commande
- Mise en œuvre d'une invite CLI pour la saisie par l'utilisateur
- Implémentation manuelle de l'autocomplétion
- Ajouter une option unique
- Construction et publication
- En savoir plus sur Milvus
On This Page
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word