azure-devops/azext_devops/dev/pipelines/pipeline.py (128 lines of code) (raw):
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from webbrowser import open_new
from knack.log import get_logger
from knack.util import CLIError
from azext_devops.dev.common.services import (get_build_client,
get_git_client,
resolve_instance_and_project,
get_new_pipeline_client_v60)
from azext_devops.dev.common.uri import uri_quote
from azext_devops.dev.common.uuid import is_uuid
from azext_devops.dev.common.git import resolve_git_ref_heads
from azext_devops.devops_sdk.v5_0.build.models import Build, DefinitionReference
from azext_devops.devops_sdk.v6_0.pipelines.models import (RunPipelineParameters,
RunResourcesParameters,
RepositoryResourceParameters)
from .build_definition import get_definition_id_from_name, fix_path_for_api
from .pipeline_run import _open_pipeline_run, _open_pipeline_run6_0
logger = get_logger(__name__)
def pipeline_list(name=None, top=None, organization=None, project=None, repository=None, query_order=None,
folder_path=None, repository_type=None, detect=None):
""" List pipelines.
:param name: Limit results to pipelines with this name or starting with this name. Examples: "FabCI" or "Fab*"
:type name: str
:param top: Maximum number of pipelines to list.
:type top: int
:param query_order: Order of the results.
:type query_order: str
:param repository: Limit results to pipelines associated with this repository.
:type repository: str
:param detect: Automatically detect values for organization and project. Default is "on".
:type detect: str
:param folder_path: If specified, filters to definitions under this folder.
:type folder_path: str
:param repository_type: Limit results to pipelines associated with this repository type.
It is mandatory to pass 'repository' argument along with this argument.
:type repository_type: str
"""
organization, project = resolve_instance_and_project(
detect=detect, organization=organization, project=project)
client = get_build_client(organization)
query_order = _resolve_query_order(query_order)
if repository is not None:
if repository_type is None:
repository_type = 'TfsGit'
if repository_type.lower() == 'tfsgit':
repository = _resolve_repository_as_id(repository, organization, project)
if repository is None:
raise ValueError("Could not find a repository with name '{}', in project '{}'."
.format(repository, project))
folder_path = fix_path_for_api(folder_path)
definition_references = client.get_definitions(project=project, name=name, repository_id=repository,
repository_type=repository_type, top=top, path=folder_path,
query_order=query_order)
return definition_references
def _resolve_query_order(query_order):
if query_order:
query_order_vals = ['definitionNameAscending', 'definitionNameDescending', 'lastModifiedAscending',
'lastModifiedDescending', 'none']
for val in query_order_vals:
if query_order.lower() in val.lower():
return val
logger.warning("Cannot resolve --query-order, continuing with 'none'")
return 'none'
def pipeline_show(id=None, name=None, open=False, organization=None, project=None, # pylint: disable=redefined-builtin
folder_path=None, detect=None):
""" Get the details of a pipeline.
:param id: ID of the pipeline.
:type id: int
:param name: Name of the pipeline. Ignored if --id is supplied.
:type name: str
:param folder_path: Folder path of pipeline. Default is root level folder.
:type folder_path: str
:param open: Open the pipeline summary page in your web browser.
:type open: bool
:param detect: Automatically detect values for instance and project. Default is "on".
:type detect: str
"""
organization, project = resolve_instance_and_project(
detect=detect, organization=organization, project=project)
client = get_build_client(organization)
if id is None:
if name is not None:
id = get_definition_id_from_name(name, client, project, path=folder_path)
else:
raise CLIError("Either the --id argument or the --name argument must be supplied for this command.")
build_definition = client.get_definition(definition_id=id, project=project)
if open:
_open_pipeline(build_definition, organization)
return build_definition
def set_param_variable(variables, as_dict=False, argument='variables'):
param_variables = None
if variables is not None and variables:
param_variables = {}
for variable in variables:
separator_pos = variable.find('=')
if separator_pos >= 0:
if as_dict:
param_variables[variable[:separator_pos]] = {"value": variable[separator_pos + 1:]}
else:
param_variables[variable[:separator_pos]] = variable[separator_pos + 1:]
else:
raise ValueError(
f'The --{argument} argument should consist of space separated "name=value" pairs.')
return param_variables
def pipeline_run(id=None, branch=None, commit_id=None, name=None, open=False, variables=None, # pylint: disable=redefined-builtin
folder_path=None, organization=None, project=None, detect=None, parameters=None):
""" Queue (run) a pipeline.
:param id: ID of the pipeline to queue. Required if --name is not supplied.
:type id: int
:param name: Name of the pipeline to queue. Ignored if --id is supplied.
:type name: str
:param branch: Name of the branch on which the pipeline run is to be queued. Example: refs/heads/master or master
or refs/pull/1/merge or refs/tags/tag
:type branch: str
:param folder_path: Folder path of pipeline. Default is root level folder.
:type folder_path: str
:param variables: Space separated "name=value" pairs for the variables you would like to set.
:type variables: [str]
:param parameters: Space separated "name=value" pairs for the parameters you would like to set.
:type parameters: [str]
:param commit_id: Commit-id on which the pipeline run is to be queued.
:type commit_id: str
:param open: Open the pipeline results page in your web browser.
:type open: bool
:param detect: Automatically detect organization and project. Default is "on".
:type detect: str
"""
organization, project = resolve_instance_and_project(
detect=detect, organization=organization, project=project)
if id is None and name is None:
raise ValueError('Either the --id argument or the --name argument ' +
'must be supplied for this command.')
client = get_build_client(organization)
if id is None:
id = get_definition_id_from_name(name, client, project, folder_path)
if parameters:
logger.debug('Using "pipelines client v6_0" to include parameters in run')
client = get_new_pipeline_client_v60(organization)
repositories = {"self": RepositoryResourceParameters(ref_name=branch, version=commit_id)}
resources = RunResourcesParameters(repositories=repositories)
template_parameters = set_param_variable(parameters, argument='parameters')
param_variables = set_param_variable(variables, as_dict=True)
run_parameters = RunPipelineParameters(
resources=resources, variables=param_variables, template_parameters=template_parameters)
queued_pipeline = client.run_pipeline(run_parameters=run_parameters, project=project, pipeline_id=id)
if open:
_open_pipeline_run6_0(queued_pipeline, project, organization)
return queued_pipeline
definition_reference = DefinitionReference(id=id)
branch = resolve_git_ref_heads(branch)
build = Build(definition=definition_reference, source_branch=branch, source_version=commit_id)
param_variables = set_param_variable(variables)
build.parameters = param_variables
queued_build = client.queue_build(build=build, project=project)
if open:
_open_pipeline_run(queued_build, organization)
return queued_build
def pipeline_delete(id, organization=None, project=None, detect=None): # pylint: disable=redefined-builtin
""" Delete a pipeline.
:param id: ID of the pipeline.
:type id: int
:param detect: Automatically detect instance and project. Default is "on".
:type detect: str
"""
organization, project = resolve_instance_and_project(
detect=detect, organization=organization, project=project)
client = get_build_client(organization)
build = client.delete_definition(definition_id=id, project=project)
print('Pipeline {id} was deleted successfully.'.format(id=id))
return build
def _open_pipeline(definition, organization):
"""Opens the build definition in the default browser.
"""
# https://dev.azure.com/OrgName/ProjectName/_build/index?definitionId=1234
project = definition.project.name
url = organization.rstrip('/') + '/' + uri_quote(project) + '/_build?definitionId='\
+ uri_quote(str(definition.id))
logger.debug('Opening web page: %s', url)
open_new(url=url)
def _resolve_repository_as_id(repository, organization, project):
if is_uuid(repository):
return repository
git_client = get_git_client(organization)
repositories = git_client.get_repositories(project=project, include_links=False, include_all_urls=False)
for found_repository in repositories:
if found_repository.name.lower() == repository.lower():
return found_repository.id
return None