diff --git a/azure-cli2017.pyproj b/azure-cli2017.pyproj
index 0c47a614cda..680560b71d8 100644
--- a/azure-cli2017.pyproj
+++ b/azure-cli2017.pyproj
@@ -83,6 +83,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -406,6 +438,17 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -607,6 +650,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -668,6 +724,17 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -794,6 +861,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -974,6 +1059,13 @@
+
+
+
+
+
+
+
@@ -1069,6 +1161,13 @@
+
+
+
+
+
+
+
@@ -1097,6 +1196,15 @@
+
+
+
+
+
+
+
+
+
@@ -1156,6 +1264,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1289,6 +1489,8 @@
+
+
@@ -1343,6 +1545,8 @@
+
+
@@ -1378,6 +1582,11 @@
+
+
+
+
+
diff --git a/build_scripts/debian/README.md b/build_scripts/debian/README.md
new file mode 100644
index 00000000000..f1c92138bdb
--- /dev/null
+++ b/build_scripts/debian/README.md
@@ -0,0 +1,30 @@
+Debian Packaging
+================
+
+Updating the Debian package
+---------------------------
+
+On a build machine (e.g. new Ubuntu 14.04 VM), run the build script.
+
+For example:
+```
+git clone https://github.com/azure/azure-cli
+cd azure-cli
+export CLI_VERSION=2.0.9 \
+ && export BUILD_ARTIFACT_DIR=$(mktemp -d)\
+ && build_scripts/debian/build.sh $(pwd)
+```
+
+Note: The paths above have to be full paths, not relative otherwise the build will fail.
+
+Now you have built the package, upload the package to the apt repository.
+
+
+Verification
+------------
+
+```
+sudo dpkg -i azure-cli_${CLI_VERSION}-1_all.deb
+az
+az --version
+```
diff --git a/build_scripts/debian/build.sh b/build_scripts/debian/build.sh
new file mode 100644
index 00000000000..9e7ea6200e2
--- /dev/null
+++ b/build_scripts/debian/build.sh
@@ -0,0 +1,79 @@
+#!/usr/bin/env bash
+#---------------------------------------------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License. See License.txt in the project root for license information.
+#---------------------------------------------------------------------------------------------
+
+set -ex
+
+: "${CLI_VERSION:?CLI_VERSION environment variable not set.}"
+: "${BUILD_ARTIFACT_DIR:?BUILD_ARTIFACT_DIR environment variable not set.}"
+
+if [ -z "$1" ]
+ then
+ echo "Argument should be path to local repo."
+ exit 1
+fi
+
+local_repo=$1
+
+sudo apt-get update
+
+script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+debian_directory_creator=$script_dir/dir_creator.sh
+
+# Install dependencies for the build
+sudo apt-get install -y libssl-dev libffi-dev python3-dev debhelper
+# Download, Extract, Patch, Build CLI
+tmp_pkg_dir=$(mktemp -d)
+working_dir=$(mktemp -d)
+cd $working_dir
+source_dir=$local_repo
+deb_file=$local_repo/../azure-cli_${CLI_VERSION}-${CLI_VERSION_REVISION:=1}_all.deb
+az_completion_file=$source_dir/az.completion
+# clean up old build output
+if [ -d "$source_dir/debian" ]
+ then
+ rm -rf $source_dir/debian
+fi
+[ -d $local_repo/privates ] && cp $local_repo/privates/*.whl $tmp_pkg_dir
+
+# Build Python from source and include
+python_dir=$(mktemp -d)
+python_archive=$(mktemp)
+wget https://www.python.org/ftp/python/3.6.1/Python-3.6.1.tgz -qO $python_archive
+tar -xvzf $python_archive -C $python_dir
+echo "Python dir is $python_dir"
+# clean any previous make files
+make clean || echo "Nothing to clean"
+$python_dir/*/configure --srcdir $python_dir/* --prefix $source_dir/python_env
+make
+# required to run the 'make install'
+sudo apt-get install -y zlib1g-dev
+make install
+
+# note: This installation step could happen in debian/rules but was unable to escape $ char.
+# It does not affect the built .deb file though.
+$source_dir/python_env/bin/pip3 install wheel
+for d in $source_dir/src/azure-cli $source_dir/src/azure-cli-core $source_dir/src/azure-cli-nspkg \
+ $source_dir/src/azure-cli-command_modules-nspkg $source_dir/src/command_modules/azure-cli-*/;
+ do cd $d;
+ $source_dir/python_env/bin/python3 setup.py bdist_wheel -d $tmp_pkg_dir;
+ cd -;
+done;
+all_modules=`find $tmp_pkg_dir -name "*.whl"`
+$source_dir/python_env/bin/pip3 install $all_modules
+$source_dir/python_env/bin/pip3 install --force-reinstall --upgrade azure-nspkg azure-mgmt-nspkg
+# Add the debian files
+mkdir $source_dir/debian
+# Create temp dir for the debian/ directory used for CLI build.
+cli_debian_dir_tmp=$(mktemp -d)
+
+$debian_directory_creator $cli_debian_dir_tmp $az_completion_file $source_dir
+cp -r $cli_debian_dir_tmp/* $source_dir/debian
+cd $source_dir
+dpkg-buildpackage -us -uc
+echo "The archive is available at $working_dir/azure-cli_${CLI_VERSION}-${CLI_VERSION_REVISION:=1}_all.deb"
+cp $deb_file ${BUILD_ARTIFACT_DIR}
+echo "The archive has also been copied to ${BUILD_ARTIFACT_DIR}"
+echo "Done."
diff --git a/build_scripts/debian/dir_creator.sh b/build_scripts/debian/dir_creator.sh
new file mode 100644
index 00000000000..f14331b6cb7
--- /dev/null
+++ b/build_scripts/debian/dir_creator.sh
@@ -0,0 +1,124 @@
+#!/usr/bin/env bash
+#---------------------------------------------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License. See License.txt in the project root for license information.
+#---------------------------------------------------------------------------------------------
+
+set -ex
+
+# Create the debian/ directory for building the azure-cli Debian package
+
+# This script takes an argument of the empty directory where the files will be placed.
+
+if [ -z "$1" ]
+ then
+ echo "No argument supplied for debian directory."
+ exit 1
+fi
+
+if [ -z "$2" ]
+ then
+ echo "No argument supplied for completion script."
+ exit 1
+fi
+
+if [ -z "$3" ]
+ then
+ echo "No argument supplied for source directory."
+ exit 1
+fi
+
+TAB=$'\t'
+
+debian_dir=$1
+completion_script=$2
+source_dir=$3
+mkdir $debian_dir/source
+
+echo '1.0' > $debian_dir/source/format
+echo '9' > $debian_dir/compat
+
+cat > $debian_dir/changelog <<- EOM
+azure-cli (${CLI_VERSION}-${CLI_VERSION_REVISION:=1}) unstable; urgency=low
+
+ * Debian package release.
+
+ -- Azure Python CLI Team $(date -R)
+
+EOM
+
+cat > $debian_dir/control <<- EOM
+Source: azure-cli
+Section: python
+Priority: extra
+Maintainer: Azure Python CLI Team
+Build-Depends: debhelper (>= 9), libssl-dev, libffi-dev, python3-dev
+Standards-Version: 3.9.5
+Homepage: https://github.com/azure/azure-cli
+
+Package: azure-cli
+Architecture: all
+Depends: \${shlibs:Depends}, \${misc:Depends}
+Description: Azure CLI
+ A great cloud needs great tools; we're excited to introduce Azure CLI,
+ our next generation multi-platform command line experience for Azure.
+
+EOM
+
+cat > $debian_dir/copyright <<- EOM
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: azure-cli
+Upstream-Contact: Azure Python CLI Team
+Source: https://github.com/azure/azure-cli
+
+Files: *
+Copyright: Copyright (c) Microsoft Corporation
+License: MIT
+Azure CLI
+
+Copyright (c) Microsoft Corporation
+
+All rights reserved.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+EOM
+
+# TODO: Instead of "_ssl.cpython-36m-x86_64-linux-gnu.so" only, find all .so files with "find debian/azure-cli/opt/az -type f -name '*.so'"
+
+cat > $debian_dir/rules << EOM
+#!/usr/bin/make -f
+
+# Uncomment this to turn on verbose mode.
+export DH_VERBOSE=1
+export DH_OPTIONS=-v
+
+%:
+${TAB}dh \$@ --sourcedirectory $source_dir
+
+override_dh_install:
+${TAB}mkdir -p debian/azure-cli/opt/az
+${TAB}cp -a python_env/* debian/azure-cli/opt/az
+${TAB}mkdir -p debian/azure-cli/usr/bin/
+${TAB}echo "\043!/usr/bin/env bash\n/opt/az/bin/python3 -Im azure.cli \"\044\100\"" > debian/azure-cli/usr/bin/az
+${TAB}chmod 0755 debian/azure-cli/usr/bin/az
+${TAB}mkdir -p debian/azure-cli/etc/bash_completion.d/
+${TAB}cat ${completion_script} > debian/azure-cli/etc/bash_completion.d/azure-cli
+${TAB}dpkg-shlibdeps -v --warnings=7 -Tdebian/azure-cli.substvars -dDepends -edebian/azure-cli/opt/az/bin/python3 debian/azure-cli/opt/az/lib/python3.6/lib-dynload/_ssl.cpython-36m-x86_64-linux-gnu.so
+
+
+override_dh_strip:
+${TAB}dh_strip --exclude=_cffi_backend
+
+EOM
+
+cat $debian_dir/rules
+
+# debian/rules should be executable
+chmod 0755 $debian_dir/rules
diff --git a/build_scripts/docker/README.md b/build_scripts/docker/README.md
new file mode 100644
index 00000000000..b1740a090fc
--- /dev/null
+++ b/build_scripts/docker/README.md
@@ -0,0 +1,35 @@
+Docker Packaging
+================
+
+The Docker image is available at https://hub.docker.com/r/microsoft/azure-cli/tags/.
+
+Updating the Docker image
+-------------------------
+1. Run `docker build` with the Dockerfile.
+ When tagging this Docker image, choose an appropriate version number.
+ e.g.: ``sudo docker build --no-cache --build-arg BUILD_DATE="`date -u +"%Y-%m-%dT%H:%M:%SZ"`" --build-arg CLI_VERSION=${CLI_VERSION} -f Dockerfile -t microsoft/azure-cli:${CLI_VERSION} .``
+2. Push the image to the registry,
+ e.g.: `sudo docker push microsoft/azure-cli:${CLI_VERSION}`
+3. Create a PR to commit the Dockerfile changes back to the repository.
+
+
+Verification
+------------
+
+Run the image.
+
+```
+$ docker run -it microsoft/azure-cli:${CLI_VERSION}
+$ az
+$ az --version
+```
+
+Save the image
+```
+docker save -o docker-microsoft-azure-cli-VERSION.tar microsoft/azure-cli:${CLI_VERSION}
+```
+
+Load the saved image
+```
+docker load -i docker-microsoft-azure-cli-VERSION.tar
+```
diff --git a/build_scripts/homebrew/README.md b/build_scripts/homebrew/README.md
new file mode 100644
index 00000000000..a14e5a373d0
--- /dev/null
+++ b/build_scripts/homebrew/README.md
@@ -0,0 +1,35 @@
+Homebrew Packaging
+==================
+
+
+Updating the formula
+--------------------
+1. Change the `url` in the formula to point to the new release and change the `sha256` value also.
+2. Update the resources list in the formula (see below).
+3. Run the formula verification commands (see below).
+4. Submit a PR to https://github.com/Homebrew/homebrew-core.
+
+
+Updating the resources list
+---------------------------
+```
+# Create a new virtual environment first
+pip install azure-cli homebrew-pypi-poet; poet -r azure-cli
+```
+
+Verification
+------------
+
+```
+brew install --build-from-source azure-cli.rb
+brew test azure-cli.rb
+brew audit --strict --online azure-cli.rb
+```
+
+More Information
+----------------
+https://github.com/Homebrew/homebrew-core/blob/master/CONTRIBUTING.md
+
+https://github.com/Homebrew/brew/blob/master/docs/Formula-Cookbook.md
+
+https://github.com/Homebrew/brew/blob/master/docs/Acceptable-Formulae.md
diff --git a/build_scripts/homebrew/formula-generate.py b/build_scripts/homebrew/formula-generate.py
new file mode 100644
index 00000000000..5080943b83d
--- /dev/null
+++ b/build_scripts/homebrew/formula-generate.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+# --------------------------------------------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License. See License.txt in the project root for license information.
+# --------------------------------------------------------------------------------------------
+import os
+import sys
+import tempfile
+import hashlib
+import shutil
+from collections import OrderedDict
+from sh import pip, git
+
+try:
+ # Attempt to load python 3 module
+ from urllib.request import urlopen
+except ImportError:
+ # Import python 2 version
+ from urllib2 import urlopen
+
+HOMEBREW_REPO_URL = 'https://github.com/Homebrew/homebrew-core'
+AZURE_CLI_FORMULA_PATH = os.path.join('Formula', 'azure-cli.rb')
+
+CLI_VERSION = os.environ['CLI_VERSION']
+UPSTREAM_URL = os.environ['UPSTREAM_URL']
+BUILD_ARTIFACTS_DIR = os.environ.get('BUILD_ARTIFACT_DIR')
+
+def get_homebrew_formula():
+ tmp_dir = tempfile.mkdtemp()
+ git(['clone', '--depth', '1', HOMEBREW_REPO_URL, tmp_dir], _out=sys.stdout, _err=sys.stdout)
+ formula_path = os.path.join(tmp_dir, AZURE_CLI_FORMULA_PATH)
+ formula_content = []
+ with open(formula_path) as f:
+ formula_content = f.readlines()
+ formula_content = [x.rstrip() for x in formula_content]
+ return formula_path, formula_content
+
+def modify_url(formula_content):
+ for line_no, line_contents in enumerate(formula_content):
+ # Find/replace first url
+ if 'url' in line_contents:
+ formula_content[line_no] = ' url "' + UPSTREAM_URL + '"'
+ break
+
+def modify_sha256(formula_content):
+ tmp_file = tempfile.mkstemp()[1]
+ response = urlopen(UPSTREAM_URL)
+ with open(tmp_file, 'wb') as f:
+ f.write(response.read())
+ sha256 = hashlib.sha256()
+ with open(tmp_file, 'rb') as f:
+ sha256.update(f.read())
+ computed_hash = sha256.hexdigest()
+ for line_no, line_contents in enumerate(formula_content):
+ # Find/replace first sha256
+ if 'sha256' in line_contents:
+ formula_content[line_no] = ' sha256 "' + computed_hash + '"'
+ break
+
+def _should_include_resource(r):
+ return not r.startswith('azure-cli') and r not in ['futures']
+
+def modify_resources(formula_content):
+ start_resources_line_no = None
+ end_resources_line_no = None
+ for line_no, line_contents in enumerate(formula_content):
+ if 'resource' in line_contents and start_resources_line_no is None:
+ start_resources_line_no = line_no
+ if 'def install' in line_contents and end_resources_line_no is None:
+ end_resources_line_no = line_no - 1
+ break
+ # Delete resources block
+ del formula_content[start_resources_line_no : end_resources_line_no]
+ # The script will have installed homebrew-pypi-poet by this point so we can import
+ from poet.poet import make_graph, RESOURCE_TEMPLATE
+ nodes = make_graph('azure-cli')
+ filtered_nodes = OrderedDict([(n, nodes[n]) for n in nodes if _should_include_resource(n)])
+ resources_stanza = '\n\n'.join([RESOURCE_TEMPLATE.render(resource=node) for node in filtered_nodes.values()])
+ formula_content[start_resources_line_no:start_resources_line_no] = resources_stanza.split('\n')
+
+
+def write_file(formula_path, formula_content):
+ with open(formula_path, 'w') as f:
+ for line in formula_content:
+ f.write("%s\n" % line)
+
+def setup_pip_deps():
+ pip(['install', '--ignore-installed', 'azure-cli', 'homebrew-pypi-poet'], _out=sys.stdout, _err=sys.stdout)
+
+def main():
+ formula_path, formula_content = get_homebrew_formula()
+ setup_pip_deps()
+ modify_url(formula_content)
+ modify_sha256(formula_content)
+ modify_resources(formula_content)
+ write_file(formula_path, formula_content)
+ print('Done')
+ new_formula_path = formula_path
+ if BUILD_ARTIFACTS_DIR:
+ new_formula_path = os.path.join(BUILD_ARTIFACTS_DIR, 'azure-cli.rb')
+ shutil.copyfile(formula_path, new_formula_path)
+ print('The new Homebrew formula is available at {}'.format(new_formula_path))
+ print('Create a PR to {} for {}'.format(HOMEBREW_REPO_URL, AZURE_CLI_FORMULA_PATH))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/build_scripts/pypi/README.md b/build_scripts/pypi/README.md
new file mode 100644
index 00000000000..e41a2fbe69b
--- /dev/null
+++ b/build_scripts/pypi/README.md
@@ -0,0 +1,9 @@
+Python Package Builds (.whl & .tar.gz)
+======================================
+
+For example:
+```
+artifacts_dir=$(mktemp -d)
+python build_scripts/pypi/build.py $artifacts_dir .
+ls -la $artifacts_dir
+```
diff --git a/build_scripts/pypi/build.py b/build_scripts/pypi/build.py
new file mode 100644
index 00000000000..39800b2172b
--- /dev/null
+++ b/build_scripts/pypi/build.py
@@ -0,0 +1,67 @@
+# --------------------------------------------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License. See License.txt in the project root for license information.
+# --------------------------------------------------------------------------------------------
+
+"""
+Script to build all command modules that can be used to install a fully self-contained instance of the CLI.
+"""
+
+from __future__ import print_function
+
+import glob
+import os
+import sys
+import tempfile
+import subprocess
+
+def _error_exit(msg):
+ print('ERROR: '+msg, file=sys.stderr)
+ sys.exit(1)
+
+def _print_status(msg=''):
+ print('-- '+msg)
+
+def _get_tmp_dir():
+ return tempfile.mkdtemp()
+
+def _get_tmp_file():
+ return tempfile.mkstemp()[1]
+
+def _exec_command(command_list, cwd=None, stdout=None):
+ """Returns True in the command was executed successfully"""
+ try:
+ _print_status('Executing {}'.format(command_list))
+ subprocess.check_call(command_list, stdout=stdout, cwd=cwd)
+ return True
+ except subprocess.CalledProcessError as err:
+ print(err, file=sys.stderr)
+ return False
+
+def _build_package(path_to_package, dist_dir):
+ cmd_success = _exec_command(['python', 'setup.py', 'bdist_wheel', '-d', dist_dir], cwd=path_to_package)
+ cmd_success = _exec_command(['python', 'setup.py', 'sdist', '-d', dist_dir], cwd=path_to_package)
+ if not cmd_success:
+ _error_exit('Error building {}.'.format(path_to_package))
+
+def build_packages(clone_root, dist_dir):
+ packages_to_build = [
+ os.path.join(clone_root, 'src', 'azure-cli'),
+ os.path.join(clone_root, 'src', 'azure-cli-core'),
+ os.path.join(clone_root, 'src', 'azure-cli-nspkg'),
+ os.path.join(clone_root, 'src', 'azure-cli-command_modules-nspkg'),
+ ]
+
+ packages_to_build.extend(glob.glob(os.path.join(clone_root, 'src', 'command_modules', 'azure-cli-*')))
+ for p in packages_to_build:
+ if os.path.isfile(os.path.join(p, 'setup.py')):
+ _build_package(p, dist_dir)
+
+if __name__ == '__main__':
+ if len(sys.argv) == 1:
+ raise ValueError('Please provide temporary path for local built packages')
+ dist_dir = sys.argv[1]
+ clone_root = sys.argv[2]
+ build_packages(clone_root, dist_dir)
+ print("package were built to {}".format(dist_dir))
+ print("Done.")
diff --git a/build_scripts/rpm/README.md b/build_scripts/rpm/README.md
new file mode 100644
index 00000000000..a64dfe7c681
--- /dev/null
+++ b/build_scripts/rpm/README.md
@@ -0,0 +1,53 @@
+RPM Packaging
+================
+
+Building the RPM package
+------------------------
+
+On a build machine (e.g. new CentOS 7 VM) run the following.
+
+Install dependencies required to build:
+Required for rpm build tools & required to build the CLI.
+```
+sudo yum install -y gcc git rpm-build rpm-devel rpmlint make bash coreutils diffutils patch rpmdevtools python libffi-devel python-devel openssl-devel
+```
+
+Build example:
+Note: use the full path to the repo path, not a relative path.
+```
+git clone https://github.com/azure/azure-cli
+cd azure-cli
+export CLI_VERSION=2.0.16
+export REPO_PATH=$(pwd)
+rpmbuild -v -bb --clean build_scripts/rpm/azure-cli.spec
+```
+
+Verification
+------------
+
+```
+sudo rpm -i RPMS/*/azure-cli-2.0.16-1.noarch.rpm
+az --version
+```
+
+Check the file permissions of the package:
+```
+rpmlint RPMS/*/azure-cli-2.0.16-1.x86_64.rpm
+```
+
+Check the file permissions of the package:
+```
+rpm -qlvp RPMS/*/azure-cli-2.0.16-1.x86_64.rpm
+```
+
+To remove:
+```
+sudo rpm -e azure-cli
+```
+
+Links
+-----
+
+https://fedoraproject.org/wiki/How_to_create_an_RPM_package
+
+https://fedoraproject.org/wiki/Packaging:RPMMacros?rd=Packaging/RPMMacros
diff --git a/build_scripts/rpm/azure-cli.spec b/build_scripts/rpm/azure-cli.spec
new file mode 100644
index 00000000000..4f034e11fa6
--- /dev/null
+++ b/build_scripts/rpm/azure-cli.spec
@@ -0,0 +1,78 @@
+# RPM spec file for Azure CLI
+# Definition of macros used - https://fedoraproject.org/wiki/Packaging:RPMMacros?rd=Packaging/RPMMacros
+
+# .el7.centos -> .el7
+%if 0%{?rhel} == 7
+ %define dist .el7
+%endif
+
+%define name azure-cli
+%define release 1%{?dist}
+%define version %{getenv:CLI_VERSION}
+%define repo_path %{getenv:REPO_PATH}
+%define venv_url https://pypi.python.org/packages/source/v/virtualenv/virtualenv-15.0.0.tar.gz
+%define venv_sha256 70d63fb7e949d07aeb37f6ecc94e8b60671edb15b890aa86dba5dfaf2225dc19
+%define cli_lib_dir %{_libdir}/az
+
+Summary: Azure CLI
+License: MIT
+Name: %{name}
+Version: %{version}
+Release: %{release}
+Url: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli
+BuildArch: x86_64
+Requires: python
+
+BuildRequires: gcc
+BuildRequires: python
+BuildRequires: libffi-devel
+BuildRequires: python-devel
+BuildRequires: openssl-devel
+
+%global _python_bytecompile_errors_terminate_build 0
+
+%description
+A great cloud needs great tools; we're excited to introduce Azure CLI,
+ our next generation multi-platform command line experience for Azure.
+
+%prep
+# Create some tmp files
+tmp_venv_archive=$(mktemp)
+
+# Download, Extract Virtualenv
+wget %{venv_url} -qO $tmp_venv_archive
+echo "%{venv_sha256} $tmp_venv_archive" | sha256sum -c -
+tar -xvzf $tmp_venv_archive -C %{_builddir}
+
+%install
+# Create the venv
+python %{_builddir}/virtualenv-15.0.0/virtualenv.py --python python %{buildroot}%{cli_lib_dir}
+
+# Build the wheels from the source
+source_dir=%{repo_path}
+dist_dir=$(mktemp -d)
+for d in $source_dir/src/azure-cli $source_dir/src/azure-cli-core $source_dir/src/azure-cli-nspkg $source_dir/src/azure-cli-command_modules-nspkg $source_dir/src/command_modules/azure-cli-*/; \
+do cd $d; %{buildroot}%{cli_lib_dir}/bin/python setup.py bdist_wheel -d $dist_dir; cd -; done;
+
+[ -d $source_dir/privates ] && cp $source_dir/privates/*.whl $dist_dir
+
+# Install the CLI
+all_modules=`find $dist_dir -name "*.whl"`
+%{buildroot}%{cli_lib_dir}/bin/pip install --no-compile $all_modules
+%{buildroot}%{cli_lib_dir}/bin/pip install --no-compile --force-reinstall --upgrade azure-nspkg azure-mgmt-nspkg
+
+# Fix up %{buildroot} appearing in some files...
+for d in %{buildroot}%{cli_lib_dir}/bin/*; do perl -p -i -e "s#%{buildroot}##g" $d; done;
+
+# Create executable
+mkdir -p %{buildroot}%{_bindir}
+printf '#!/usr/bin/env bash\n%{cli_lib_dir}/bin/python -Esm azure.cli "$@"' > %{buildroot}%{_bindir}/az
+
+# Set up tab completion
+mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d/
+cat $source_dir/az.completion > %{buildroot}%{_sysconfdir}/bash_completion.d/azure-cli
+
+%files
+%attr(-,root,root) %{cli_lib_dir}
+%config(noreplace) %{_sysconfdir}/bash_completion.d/azure-cli
+%attr(0755,root,root) %{_bindir}/az
diff --git a/scripts/smart_create_gen/_common.py b/scripts/smart_create_gen/_common.py
deleted file mode 100644
index 6afcfe11b36..00000000000
--- a/scripts/smart_create_gen/_common.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# --------------------------------------------------------------------------------------------
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License. See License.txt in the project root for license information.
-# --------------------------------------------------------------------------------------------
-
-import configparser
-import os
-import re
-
-def get_name_from_path(path):
- """ Parses a directory in 'mgmt_ ' format into the camel case name used by ARM. The
- directory name should be in snake case. """
- while path:
- path, item = os.path.split(path)
- if 'mgmt_' in item:
- return snake_to_camel(item.replace('mgmt_', '', 1))
- raise RuntimeError('You must specify a path with --src that includes a directory in the format \'mgmt_\'')
-
-def get_config():
- config = configparser.ConfigParser()
- CONFIG_PATH = os.path.join(os.getcwd(), 'config.ini')
- if os.path.isfile('config.ini'):
- config.read('config.ini')
- return config
- else:
- print("'config.ini' file not found. Creating an empty one. Please update it and re-run the script.")
- with open(CONFIG_PATH, 'w') as f:
- f.write("""[autorest]
-path = AutoRest.exe
-
-[storage]
-account = FILL_IN
-key = FILL_IN
-container = FILL_IN
-subscription = FILL_IN
-""")
- return None
-
-def to_snake_case(s):
- s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', s)
- return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
-
-def to_dash_case(s):
- return _to_snake_case(s).replace('_', '-')
-
-def snake_to_camel(s):
- return ''.join([x.capitalize() for x in s.split('_')])
diff --git a/scripts/smart_create_gen/generate_smart_create.py b/scripts/smart_create_gen/generate_smart_create.py
deleted file mode 100644
index 33a121d789d..00000000000
--- a/scripts/smart_create_gen/generate_smart_create.py
+++ /dev/null
@@ -1,123 +0,0 @@
-# --------------------------------------------------------------------------------------------
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License. See License.txt in the project root for license information.
-# --------------------------------------------------------------------------------------------
-
-from __future__ import print_function
-
-from distutils import dir_util
-import os
-import re
-import shutil
-import sys
-
-import argparse
-
-from template_to_swagger import convert_template_to_swagger
-from upload_arm_templates import upload_template_files
-from _common import get_name_from_path, get_config, to_snake_case
-
-def _autorest_client_name(name):
- return '{}creationclient'.format(str.lower(name))
-
-HEADER = """# --------------------------------------------------------------------------------------------
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License. See License.txt in the project root for license information.
-# --------------------------------------------------------------------------------------------
-# pylint: skip-file
-"""
-
-INIT_FILE_CONTENTS = HEADER + """import pkg_resources
-pkg_resources.declare_namespace(__name__)
-
-"""
-
-def _args_to_list(**kwargs):
- arg_list = []
- for k, v in kwargs.items():
- k = k.replace('_','-')
- arg_list.append('--{}'.format(k))
- arg_list.append(v)
- return arg_list
-
-def generate_smart_create(*args):
-
- print('\n== GENERATE SMART CREATE ==')
-
- parser = argparse.ArgumentParser(description='Smart Create Generation')
- parser.add_argument('--src', metavar='PATH', required=True, help='Path to the directory containing the main ARM template. Must contain a directory in the format \'mgmt_\'. Default name of main template is \'azuredeploy.json\' but a different name can be specified.')
- parser.add_argument('--api-version', metavar='VERSION', required=True, help='API version for the template being generated in yyyy-MM-dd format. (ex: 2016-07-01)')
- parser.add_argument('--no-upload', action='store_true', help='Turn off upload to only regenerate the client-side code.')
- args = parser.parse_args(args)
-
- api_version = args.api_version
- src = args.src or os.path.join(os.getcwd())
- if not os.path.isfile(src):
- root = src
- src = os.path.join(root, 'azuredeploy.json')
- else:
- root = os.path.split(src)[0]
- name = get_name_from_path(root) # name in CamelCase
- dest = root
- if not os.path.isfile(src):
- raise RuntimeError('Main template not found at path: {}'.format(src))
- if not name:
- raise RuntimeError('Unable to parse name from path: {}. Path must contain a directory in the format \'mgmt_\'.'.format(src))
-
- # Convert the supplied template into Swagger
- swagger_path = convert_template_to_swagger(*_args_to_list(api_version=api_version, src=src, name=to_snake_case(name)))
-
- # Use AutoRest to generate the Python files from the Swagger
- config = get_config()
- if not config:
- sys.exit(-1)
-
- AUTO_REST_PATH = config['autorest']['path'] or 'AutoRest.exe'
- cmd = '{} -Namespace Default -CodeGenerator Azure.Python -addcredentials -input {} -OutputDirectory {}'.format(AUTO_REST_PATH, swagger_path, root)
- os.system(cmd)
-
- # Insert Pylint ignore statements into the generated files
- autorest_generated_path = os.path.join(root, _autorest_client_name(name))
- for root, dirs, files in os.walk(autorest_generated_path):
- path = root.split('/')
- for file in files:
- if file.endswith('.py'):
- with open(os.path.join(root, file), 'r') as original: data = original.read()
- with open(os.path.join(root, file), 'w') as modified: modified.write(HEADER + '\n' + data)
-
- # Rename the generated file directory to lib
- dir_util.copy_tree(autorest_generated_path, os.path.join(dest, 'lib'))
-
- # Create the cheesy __init__.py file in
- with open(os.path.join(dest, '__init__.py'), 'w') as f:
- f.write(INIT_FILE_CONTENTS)
-
- if not args.no_upload:
- # Publish template files into blob storage
- upload_template_files(*_args_to_list(src=dest, api_version=api_version, name=name))
-
- # Delete the Generated folder
- shutil.rmtree(autorest_generated_path)
- os.remove(os.path.join(dest, 'setup.py'))
-
- print("""SMART GENERATION COMPLETE!
-
-Your template is almost ready to be integrated into the CLI. You need to do the following:
-
-(1) If you did not generate your files in-place, move the directory into the project structure.
- Import the newly added directory in Visual Studio so it appears in the Solution Explorer.
-
-(2) Add your folders to the 'packages' list in the parent module's 'setup.py' file with the
- following pattern:
- 'azure.cli.command_modules..mgmt__create'
- 'azure.cli.command_modules..mgmt__create.lib'
- 'azure.cli.command_modules..mgmt__create.lib.models'
- 'azure.cli.command_modules..mgmt__create.lib.operations'
-
-(3) Expose your SmartCreate command in the module's 'generated.py' file and tweak any necessary
- parameter aliasing in '_params.py'.
-"""
- )
-
-if __name__ == '__main__':
- generate_smart_create(*sys.argv[1:])
diff --git a/scripts/smart_create_gen/template_to_swagger.py b/scripts/smart_create_gen/template_to_swagger.py
deleted file mode 100644
index 412f19cc438..00000000000
--- a/scripts/smart_create_gen/template_to_swagger.py
+++ /dev/null
@@ -1,569 +0,0 @@
-# --------------------------------------------------------------------------------------------
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License. See License.txt in the project root for license information.
-# --------------------------------------------------------------------------------------------
-
-from __future__ import print_function
-
-from collections import OrderedDict
-import json
-import os
-import re
-import sys
-
-import argparse
-
-from _common import snake_to_camel
-
-# HELPER FUNCTIONS
-
-def get_required(value):
- default_value = value.get('defaultValue')
- if not default_value and default_value != '' \
- and not isinstance(default_value, list) and not isinstance(default_value, dict):
- return swagger_template_required
- return ''
-
-def get_enum(value, name):
- if value.get('allowedValues'):
- enum_text = ',\n'.join(['"{0}"'.format(v) for v in value['allowedValues']])
- return swagger_template_enum.format(enum_text, name)
- return ''
-
-def get_default(value):
- default_value = value.get('defaultValue')
- swagger = ''
- if default_value and (isinstance(default_value, int) or '[' not in default_value) \
- and not isinstance(default_value, list) and not isinstance(default_value, dict):
- swagger = swagger_template_default.format(default_value)
- elif (isinstance(default_value, bool)):
- swagger = swagger_template_default.format(default_value)
- return swagger
-
-def get_type_string(value):
- type = value.get('type')
- type_conversion = {
- 'bool': 'boolean',
- 'int': 'integer',
- 'securestring': 'string'
- }
- type = type_conversion.get(type, type)
-
- type_string = '"type": "{}"'.format(type)
- if type == 'array':
- type_string = '''
- "type": "{0}",
- "items": {{
- "type": "{1}"
- }}
- '''.format(type, 'object') # Currently only support arrays of objects
-
- return type_string
-
-
-def get_required_list(items):
- list = ',\n '.join(['"{0}"'.format(name) for name, value in items if get_required(value)])
- return swagger_template_required_list.format(list)
-
-# SWAGGER TEMPLATE SNIPPETS
-
-swagger_template_master = '''{{
- "swagger": "2.0",
- "info": {{
- "title": "{3}CreationClient",
- "version": "2015-11-01"
- }},
- "host": "management.azure.com",
- "schemes": [
- "https"
- ],
- "consumes": [
- "application/json"
- ],
- "produces": [
- "application/json"
- ],
- "security": [
- {{
- "azure_auth": [
- "user_impersonation"
- ]
- }}
- ],
- "securityDefinitions": {{
- "azure_auth": {{
- "type": "oauth2",
- "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize",
- "flow": "implicit",
- "description": "Azure Active Directory OAuth2 Flow",
- "scopes": {{
- "user_impersonation": "impersonate your user account"
- }}
- }}
- }},
- "paths": {{
- "/subscriptions/{{subscriptionId}}/resourcegroups/{{resourceGroupName}}/providers/Microsoft.Resources/deployments/{{deploymentName}}": {{
- "put": {{
- "tags": [
- "{3}"
- ],
- "operationId": "{3}_CreateOrUpdate",
- "description": "Create a new {3}.",
- "parameters": [
- {{
- "name": "resourceGroupName",
- "in": "path",
- "required": true,
- "type": "string",
- "description": "The name of the resource group. The name is case insensitive.",
- "pattern": "^[-\\\\w\\\\._]+$",
- "minLength": 1,
- "maxLength": 64
- }},
- {{
- "name": "deploymentName",
- "in": "path",
- "required": true,
- "type": "string",
- "description": "The name of the deployment.",
- "pattern": "^[-\\\\w\\\\._]+$",
- "minLength": 1,
- "maxLength": 64
- }},
- {{
- "name": "parameters",
- "x-ms-client-flatten": true,
- "in": "body",
- "required": true,
- "schema": {{
- "$ref": "#/definitions/Deployment_{3}"
- }},
- "description": "Additional parameters supplied to the operation."
- }},
- {{
- "$ref": "#/parameters/ApiVersionParameter"
- }},
- {{
- "$ref": "#/parameters/SubscriptionIdParameter"
- }}
- ],
- "responses": {{
- "200": {{
- "description": "",
- "schema": {{
- "$ref": "#/definitions/DeploymentExtended"
- }}
- }},
- "201": {{
- "description": "",
- "schema": {{
- "$ref": "#/definitions/DeploymentExtended"
- }}
- }}
- }},
- "x-ms-long-running-operation": true
- }}
- }}
- }},
- "definitions": {{
- "Deployment_{3}": {{
- "properties": {{
- "properties": {{
- "$ref": "#/definitions/DeploymentProperties_{3}",
- "description": "Gets or sets the deployment properties.",
- "x-ms-client-flatten": true
- }}
- }},
- "description": "Deployment operation parameters."
- }},
- "DeploymentProperties_{3}": {{
- "properties": {{
- "templateLink": {{
- "$ref": "#/definitions/TemplateLink",
- "description": "Gets or sets the URI referencing the template. Use only one of Template or TemplateLink.",
- "x-ms-client-flatten": true
- }},
- "parameters": {{
- "$ref": "#/definitions/{3}Parameters",
- "type": "object",
- "description": "Deployment parameters. Use only one of Parameters or ParametersLink.",
- "x-ms-client-flatten": true
- }},
- "mode": {{
- "type": "string",
- "description": "Gets or sets the deployment mode.",
- "enum": [
- "Incremental"
- ],
- "x-ms-enum": {{
- "name": "DeploymentMode",
- "modelAsString": false
- }}
- }}
- }},
- "required": [
- "templateLink",
- "parameters",
- "mode"
- ],
- "description": "Deployment properties."
- }},
- "TemplateLink": {{
- "properties": {{
- "uri": {{
- "type": "string",
- "description": "URI referencing the template.",
- "enum": [
- "https://azuresdkci.blob.core.windows.net/templatehost/Create{3}_{4}/azuredeploy.json"
- ]
- }},
- "contentVersion": {{
- "type": "string",
- "description": "If included it must match the ContentVersion in the template."
- }}
- }},
- "required": [
- "uri"
- ],
- "description": "Entity representing the reference to the template."
- }},
- "{3}Parameters": {{
- {0}{1}
- }},
- {2},
- "ParametersLink": {{
- "properties": {{
- "uri": {{
- "type": "string",
- "description": "URI referencing the template."
- }},
- "contentVersion": {{
- "type": "string",
- "description": "If included it must match the ContentVersion in the template."
- }}
- }},
- "required": [
- "uri"
- ],
- "description": "Entity representing the reference to the deployment parameters."
- }},
- "ProviderResourceType": {{
- "properties": {{
- "resourceType": {{
- "type": "string",
- "description": "Gets or sets the resource type."
- }},
- "locations": {{
- "type": "array",
- "items": {{
- "type": "string"
- }},
- "description": "Gets or sets the collection of locations where this resource type can be created in."
- }},
- "apiVersions": {{
- "type": "array",
- "items": {{
- "type": "string"
- }},
- "description": "Gets or sets the api version."
- }},
- "properties": {{
- "type": "object",
- "additionalProperties": {{
- "type": "string"
- }},
- "description": "Gets or sets the properties."
- }}
- }},
- "description": "Resource type managed by the resource provider."
- }},
- "Provider": {{
- "properties": {{
- "id": {{
- "type": "string",
- "description": "Gets or sets the provider id."
- }},
- "namespace": {{
- "type": "string",
- "description": "Gets or sets the namespace of the provider."
- }},
- "registrationState": {{
- "type": "string",
- "description": "Gets or sets the registration state of the provider."
- }},
- "resourceTypes": {{
- "type": "array",
- "items": {{
- "$ref": "#/definitions/ProviderResourceType"
- }},
- "description": "Gets or sets the collection of provider resource types."
- }}
- }},
- "description": "Resource provider information."
- }},
- "BasicDependency": {{
- "properties": {{
- "id": {{
- "type": "string",
- "description": "Gets or sets the ID of the dependency."
- }},
- "resourceType": {{
- "type": "string",
- "description": "Gets or sets the dependency resource type."
- }},
- "resourceName": {{
- "type": "string",
- "description": "Gets or sets the dependency resource name."
- }}
- }},
- "description": "Deployment dependency information."
- }},
- "Dependency": {{
- "properties": {{
- "dependsOn": {{
- "type": "array",
- "items": {{
- "$ref": "#/definitions/BasicDependency"
- }},
- "description": "Gets the list of dependencies."
- }},
- "id": {{
- "type": "string",
- "description": "Gets or sets the ID of the dependency."
- }},
- "resourceType": {{
- "type": "string",
- "description": "Gets or sets the dependency resource type."
- }},
- "resourceName": {{
- "type": "string",
- "description": "Gets or sets the dependency resource name."
- }}
- }},
- "description": "Deployment dependency information."
- }},
- "DeploymentPropertiesExtended": {{
- "properties": {{
- "provisioningState": {{
- "type": "string",
- "description": "Gets or sets the state of the provisioning."
- }},
- "correlationId": {{
- "type": "string",
- "description": "Gets or sets the correlation ID of the deployment."
- }},
- "timestamp": {{
- "type": "string",
- "format": "date-time",
- "description": "Gets or sets the timestamp of the template deployment."
- }},
- "outputs": {{
- "type": "object",
- "description": "Gets or sets key/value pairs that represent deploymentoutput."
- }},
- "providers": {{
- "type": "array",
- "items": {{
- "$ref": "#/definitions/Provider"
- }},
- "description": "Gets the list of resource providers needed for the deployment."
- }},
- "dependencies": {{
- "type": "array",
- "items": {{
- "$ref": "#/definitions/Dependency"
- }},
- "description": "Gets the list of deployment dependencies."
- }},
- "template": {{
- "type": "object",
- "description": "Gets or sets the template content. Use only one of Template or TemplateLink."
- }},
- "TemplateLink": {{
- "$ref": "#/definitions/TemplateLink",
- "description": "Gets or sets the URI referencing the template. Use only one of Template or TemplateLink."
- }},
- "parameters": {{
- "type": "object",
- "description": "Deployment parameters. Use only one of Parameters or ParametersLink."
- }},
- "parametersLink": {{
- "$ref": "#/definitions/ParametersLink",
- "description": "Gets or sets the URI referencing the parameters. Use only one of Parameters or ParametersLink."
- }},
- "mode": {{
- "type": "string",
- "description": "Gets or sets the deployment mode.",
- "enum": [
- "Incremental",
- "Complete"
- ],
- "x-ms-enum": {{
- "name": "DeploymentMode",
- "modelAsString": false
- }}
- }}
- }},
- "description": "Deployment properties with additional details."
- }},
- "DeploymentExtended": {{
- "properties": {{
- "id": {{
- "type": "string",
- "description": "Gets or sets the ID of the deployment."
- }},
- "name": {{
- "type": "string",
- "description": "Gets or sets the name of the deployment."
- }},
- "properties": {{
- "$ref": "#/definitions/DeploymentPropertiesExtended",
- "description": "Gets or sets deployment properties."
- }}
- }},
- "required": [
- "name"
- ],
- "description": "Deployment information."
- }}
- }},
- "parameters": {{
- "SubscriptionIdParameter": {{
- "name": "subscriptionId",
- "in": "path",
- "required": true,
- "type": "string",
- "description": "Gets subscription credentials which uniquely identify Microsoft Azure subscription. The subscription ID forms part of the URI for every service call."
- }},
- "ApiVersionParameter": {{
- "name": "api-version",
- "in": "query",
- "required": true,
- "type": "string",
- "description": "Client Api Version."
- }}
- }}
-}}
-'''
-
-swagger_props_template = '''
-"properties": {{
-{0}
-}}
-'''
-
-swagger_template_prop = ''' "{0}": {{
- "type": "object",
- "$ref": "#/definitions/DeploymentParameter_{0}",
- "x-ms-client-flatten": true
- }}'''
-
-swagger_template_param = '''"DeploymentParameter_{0}": {{
- "properties": {{
- "value": {{
- {5},
- "description": "{1}",
- "x-ms-client-name": "{0}"{3}{4}
- }}
- }}{2}
-}}'''
-
-swagger_template_artifacts_location = '''
-"DeploymentParameter__artifactsLocation": {{
- "properties": {{
- "value": {{
- "type": "string",
- "description": "Container URI of of the template.",
- "x-ms-client-name": "_artifactsLocation",
- "enum": [
- "https://azuresdkci.blob.core.windows.net/templatehost/Create{0}_{1}"
- ]
- }}
- }},
- "required": [
- "value"
- ]
- }}'''
-
-swagger_template_required = ''',
- "required": [
- "value"
- ]
-'''
-
-swagger_template_enum = ''',
- "enum": [
- {0}
- ],
- "x-ms-enum": {{
- "name": "{1}",
- "modelAsString": false
- }}'''
-
-swagger_template_default = ''',
- "default": "{0}"'''
-
-swagger_template_required_list = ''',
- "required": [
- {0}
- ]
-'''
-
-def convert_template_to_swagger(*args):
-
- # Create script parser
-
- print('\n== CONVERT TEMPLATE TO SWAGGER ==')
-
- parser = argparse.ArgumentParser(description='ARM Template to Swagger')
- parser.add_argument('--name', metavar='NAME', required=True, help='Name of the thing being created (in snake_case)')
- parser.add_argument('--src', metavar='PATH', required=True, help='Path to the ARM template file to convert.')
- parser.add_argument('--api-version', metavar='VERSION', required=True, help='API version for the template being generated in yyyy-MM-dd format. (ex: 2016-07-01)')
- args = parser.parse_args(args)
-
- api_version = args.api_version
- src = args.src
- name = args.name
-
- if not os.path.isfile(src):
- raise RuntimeError('File {} not found.'.format(src))
-
- root, src_file = os.path.split(src)
- dest = os.path.join(root, 'swagger_create_{}.json'.format(name))
-
- # Convert template to swagger
- with open(src) as f:
- j = json.load(f)
-
- print('Converting ARM template {} to swagger.'.format(src_file))
- params = j['parameters']
- prop_strings = [swagger_template_prop.format(key)
- for key, value in sorted(params.items(), key=lambda item: item[0])]
- props_section = swagger_props_template.format(',\n '.join(prop_strings))
-
- param_strs = []
- for key, value in sorted(params.items(), key=lambda item: item[0]):
- comment = value['metadata'].get('description') if value['metadata'] else None
- if not comment:
- print('FAILURE: Description metadata is required for all parameters. Not found for {}'.format(value))
- sys.exit(-1)
- param_strs.append(swagger_template_param.format(
- key, comment or '', get_required(value), get_enum(value, key), get_default(value), get_type_string(value)))
-
- # artifacts special case
- artifacts_paramstr = next((p for p in param_strs if '_artifacts' in p), None)
- if artifacts_paramstr:
- param_strs.remove(artifacts_paramstr)
- param_strs.append(swagger_template_artifacts_location.format(snake_to_camel(name), api_version))
-
- params_section = ',\n'.join(param_strs)
-
- with open(dest, 'w') as output_file:
- raw_swagger = swagger_template_master.format(props_section, get_required_list(params.items()), params_section, snake_to_camel(name), api_version)
- output_file.write(json.dumps(json.loads(raw_swagger, object_pairs_hook=OrderedDict), indent=2))
- print('{} generated successfully.'.format(dest))
-
- return dest
-
-if __name__ == '__main__':
- convert_template_to_swagger(*sys.argv[1:])
diff --git a/scripts/smart_create_gen/upload_arm_templates.py b/scripts/smart_create_gen/upload_arm_templates.py
deleted file mode 100644
index 5897c893aa0..00000000000
--- a/scripts/smart_create_gen/upload_arm_templates.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# --------------------------------------------------------------------------------------------
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License. See License.txt in the project root for license information.
-# --------------------------------------------------------------------------------------------
-
-from __future__ import print_function
-
-import json
-import os
-import re
-import sys
-
-import argparse
-
-from _common import get_config
-
-config = get_config()
-if not config:
- sys.exit(-1)
-
-STORAGE_ACCOUNT_NAME = config['storage']['account']
-TEMPLATE_CONTAINER_NAME = config['storage']['container']
-STORAGE_ACCOUNT_KEY = config['storage']['key']
-SUBSCRIPTION_ID = config['storage']['subscription']
-
-uploads = []
-
-def _upload_templates(name, api_version, path, dir=None):
- for item in os.listdir(path):
- item_path = os.path.join(path, item)
- if os.path.isdir(item_path):
- temp_dir = '{}/{}'.format(dir, item) if dir else item
- _upload_templates(name, api_version, item_path, temp_dir)
- elif item.endswith('.json') and 'swagger' not in item:
- blob_src = os.path.join(path, item)
- if dir:
- blob_dest = 'Create{}_{}/{}/{}'.format(name, api_version, dir, item)
- else:
- blob_dest = 'Create{}_{}/{}'.format(name, api_version, item)
- cmd = 'az storage blob upload -n {0} --account-name {1} --type block --account-key {2} --container-name {3} --file {4} --subscription {5}'.format(
- blob_dest, STORAGE_ACCOUNT_NAME, STORAGE_ACCOUNT_KEY, TEMPLATE_CONTAINER_NAME, blob_src, SUBSCRIPTION_ID
- )
- print('\nUPLOADING {}...'.format(blob_dest))
- uploads.append(cmd)
-
-def upload_template_files(*args):
-
- print('\n== UPLOAD ARM TEMPLATES ==')
-
- parser = argparse.ArgumentParser(description='Upload ARM Templates')
- parser.add_argument('--name', metavar='NAME', required=True, help='Name of the thing being uploaded (in CamelCase)')
- parser.add_argument('--src', metavar='PATH', required=True, help='Path to the directory containing ARM templates to upload. Subdirectories will automatically be crawled.')
- parser.add_argument('--api-version', metavar='VERSION', required=True, help='API version for the templates being uploaded in yyyy-MM-dd format. (ex: 2016-07-01)')
- args = parser.parse_args(args)
-
- name = args.name
- api_version = args.api_version
- src = args.src
-
- _upload_templates(name, api_version, src)
-
- from concurrent.futures import ThreadPoolExecutor, as_completed
- with ThreadPoolExecutor(max_workers=40) as executor:
- tasks = [executor.submit(lambda cmd: os.system(cmd), u) for u in uploads]
- for t in as_completed(tasks):
- t.result() # don't use the result but expose exceptions from the threads
-
-
-if __name__ == '__main__':
- upload_template_files(*sys.argv[1:])
diff --git a/src/azure-cli-core/HISTORY.rst b/src/azure-cli-core/HISTORY.rst
index d9a04d57d5e..699d7471126 100644
--- a/src/azure-cli-core/HISTORY.rst
+++ b/src/azure-cli-core/HISTORY.rst
@@ -2,11 +2,16 @@
Release History
===============
-2.0.48
+
+2.0.49
++++++
* Fix issue with `--ids` where `--subscription` would take precedence over the subscription in `--ids`.
Adding explicit warnings when name parameters would be ignored by use of `--ids`.
+2.0.48
+++++++
+* Fix Homebrew.
+
2.0.47
++++++
* Introduces generic behavior to handle "Bad Request" errors.
diff --git a/src/azure-cli-core/azure/cli/core/__init__.py b/src/azure-cli-core/azure/cli/core/__init__.py
index 4b4c3ed7d8b..ae009981272 100644
--- a/src/azure-cli-core/azure/cli/core/__init__.py
+++ b/src/azure-cli-core/azure/cli/core/__init__.py
@@ -4,7 +4,7 @@
# --------------------------------------------------------------------------------------------
from __future__ import print_function
-__version__ = "2.0.48"
+__version__ = "2.0.49"
import os
import sys
diff --git a/src/azure-cli-core/setup.py b/src/azure-cli-core/setup.py
index a4d6f8b7cee..10399af9f35 100644
--- a/src/azure-cli-core/setup.py
+++ b/src/azure-cli-core/setup.py
@@ -17,7 +17,7 @@
logger.warn("Wheel is not available, disabling bdist_wheel hook")
cmdclass = {}
-VERSION = "2.0.48"
+VERSION = "2.0.49"
# If we have source, validate that our version numbers match
# This should prevent uploading releases with mismatched versions.
try:
diff --git a/src/azure-cli/HISTORY.rst b/src/azure-cli/HISTORY.rst
index 147aadf05ab..4aff23042ba 100644
--- a/src/azure-cli/HISTORY.rst
+++ b/src/azure-cli/HISTORY.rst
@@ -2,10 +2,15 @@
Release History
===============
-2.0.48
+
+2.0.49
++++++
* Minor fixes
+2.0.48
+++++++
+* Fix Homebrew
+
2.0.47
++++++
* Minor fixes
diff --git a/src/azure-cli/azure/cli/__init__.py b/src/azure-cli/azure/cli/__init__.py
index 5d17eec4dda..c0ad5d4c06c 100644
--- a/src/azure-cli/azure/cli/__init__.py
+++ b/src/azure-cli/azure/cli/__init__.py
@@ -11,4 +11,4 @@
pkg_resources.declare_namespace(__name__)
__author__ = "Microsoft Corporation "
-__version__ = "2.0.48"
+__version__ = "2.0.49"
diff --git a/src/azure-cli/setup.py b/src/azure-cli/setup.py
index aa9f739030f..90f01b7b1aa 100644
--- a/src/azure-cli/setup.py
+++ b/src/azure-cli/setup.py
@@ -15,7 +15,7 @@
logger.warn("Wheel is not available, disabling bdist_wheel hook")
cmdclass = {}
-VERSION = "2.0.48"
+VERSION = "2.0.49"
# If we have source, validate that our version numbers match
# This should prevent uploading releases with mismatched versions.
try:
@@ -54,26 +54,28 @@
'azure-cli-advisor',
'azure-cli-ams',
'azure-cli-appservice',
+ 'azure-cli-backup',
'azure-cli-batch',
'azure-cli-batchai',
- 'azure-cli-backup',
'azure-cli-billing',
'azure-cli-botservice',
'azure-cli-cdn',
'azure-cli-cloud',
'azure-cli-cognitiveservices',
- 'azure-cli-container',
'azure-cli-configure',
'azure-cli-consumption',
+ 'azure-cli-container',
'azure-cli-core',
'azure-cli-cosmosdb',
'azure-cli-dla',
'azure-cli-dls',
'azure-cli-dms',
'azure-cli-eventgrid',
+ 'azure-cli-eventhubs',
'azure-cli-extension',
'azure-cli-feedback',
'azure-cli-find',
+ 'azure-cli-hdinsight',
'azure-cli-interactive',
'azure-cli-iot',
'azure-cli-iotcentral',
@@ -83,20 +85,21 @@
'azure-cli-monitor',
'azure-cli-network',
'azure-cli-nspkg',
+ 'azure-cli-policyinsights',
'azure-cli-profile',
'azure-cli-rdbms',
'azure-cli-redis',
+ 'azure-cli-relay',
'azure-cli-reservations',
'azure-cli-resource',
'azure-cli-role',
+ 'azure-cli-search',
+ 'azure-cli-servicebus',
+ 'azure-cli-servicefabric',
+ 'azure-cli-signalr',
'azure-cli-sql',
'azure-cli-storage',
- 'azure-cli-vm',
- 'azure-cli-servicefabric',
- 'azure-cli-servicebus',
- 'azure-cli-eventhubs',
- 'azure-cli-search',
- 'azure-cli-signalr'
+ 'azure-cli-vm'
]
with open('README.rst', 'r', encoding='utf-8') as f:
diff --git a/src/command_modules/azure-cli-acs/HISTORY.rst b/src/command_modules/azure-cli-acs/HISTORY.rst
index 73635ba3a2e..1ce3616d599 100644
--- a/src/command_modules/azure-cli-acs/HISTORY.rst
+++ b/src/command_modules/azure-cli-acs/HISTORY.rst
@@ -3,6 +3,14 @@
Release History
===============
+2.3.8
++++++
+* Minor fixes.
+
+2.3.7
++++++
+* Minor fixes.
+
2.3.6
+++++
* `az aks create/scale --nodepool-name` configures nodepool name, truncated to 12 characters, default - nodepool1
diff --git a/src/command_modules/azure-cli-acs/setup.py b/src/command_modules/azure-cli-acs/setup.py
index 2804ff2eafe..42cd3dc8e13 100644
--- a/src/command_modules/azure-cli-acs/setup.py
+++ b/src/command_modules/azure-cli-acs/setup.py
@@ -14,7 +14,7 @@
logger.warn("Wheel is not available, disabling bdist_wheel hook")
cmdclass = {}
-VERSION = "2.3.6"
+VERSION = "2.3.8"
CLASSIFIERS = [
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
@@ -31,7 +31,7 @@
DEPENDENCIES = [
'azure-mgmt-authorization==0.50.0',
- 'azure-mgmt-compute==4.3.0',
+ 'azure-mgmt-compute==4.3.1',
'azure-mgmt-containerservice==4.2.2',
'azure-graphrbac==0.40.0',
'azure-cli-core',
diff --git a/src/command_modules/azure-cli-servicefabric/HISTORY.rst b/src/command_modules/azure-cli-servicefabric/HISTORY.rst
index 36c02ebb073..d9f67b39669 100644
--- a/src/command_modules/azure-cli-servicefabric/HISTORY.rst
+++ b/src/command_modules/azure-cli-servicefabric/HISTORY.rst
@@ -2,6 +2,15 @@
Release History
===============
+
+0.1.6
++++++
+* Minor fixes
+
+0.1.5
++++++
+* Minor fixes
+
0.1.4
+++++
* Minor fixes
diff --git a/src/command_modules/azure-cli-servicefabric/setup.py b/src/command_modules/azure-cli-servicefabric/setup.py
index 1e1425397bd..4c12ee2e347 100644
--- a/src/command_modules/azure-cli-servicefabric/setup.py
+++ b/src/command_modules/azure-cli-servicefabric/setup.py
@@ -16,7 +16,7 @@
logger.warn("Wheel is not available, disabling bdist_wheel hook")
cmdclass = {}
-VERSION = "0.1.4"
+VERSION = "0.1.6"
# The full list of classifiers is available at
# https://pypi.python.org/pypi?%3Aaction=list_classifiers
@@ -38,7 +38,7 @@
'azure-graphrbac==0.40.0',
'azure-keyvault==1.1.0',
'azure-mgmt-network==2.2.1',
- 'azure-mgmt-compute==4.3.0',
+ 'azure-mgmt-compute==4.3.1',
'azure-mgmt-storage==2.0.0rc4',
'azure-mgmt-servicefabric==0.2.0',
'azure-mgmt-keyvault==1.1.0',
diff --git a/src/command_modules/azure-cli-vm/HISTORY.rst b/src/command_modules/azure-cli-vm/HISTORY.rst
index dcdc58cd015..51f03fdf587 100644
--- a/src/command_modules/azure-cli-vm/HISTORY.rst
+++ b/src/command_modules/azure-cli-vm/HISTORY.rst
@@ -2,12 +2,17 @@
Release History
===============
-2.2.5
+
+2.2.6
++++++
* `vm/vmss create`: enforce disk caching mode be `None` for Lv/Lv2 series of machines
* `vm create`: update supported size list supporting networking accelerator
* `disk update`: expose strong typed arguments for ultrassd iops and mbps configs
+2.2.5
+++++++
+* Fix SDK issue that caused Homebrew instllation to fail.
+
2.2.4
++++++
* `az disk grant-access`: fix the empty "accessSas" field
diff --git a/src/command_modules/azure-cli-vm/setup.py b/src/command_modules/azure-cli-vm/setup.py
index d3ee0b941f0..575bff4343a 100644
--- a/src/command_modules/azure-cli-vm/setup.py
+++ b/src/command_modules/azure-cli-vm/setup.py
@@ -15,7 +15,7 @@
cmdclass = {}
-VERSION = "2.2.5"
+VERSION = "2.2.6"
CLASSIFIERS = [
'Development Status :: 5 - Production/Stable',
@@ -34,7 +34,7 @@
DEPENDENCIES = [
'azure-mgmt-msi==0.2.0',
'azure-mgmt-authorization==0.50.0',
- 'azure-mgmt-compute==4.3.0',
+ 'azure-mgmt-compute==4.3.1',
'azure-mgmt-keyvault==1.1.0',
'azure-keyvault==1.1.0',
'azure-mgmt-network==2.2.1',
diff --git a/tools/automation/__main__.py b/tools/automation/__main__.py
index 44e6463cb48..95ccb7baee2 100644
--- a/tools/automation/__main__.py
+++ b/tools/automation/__main__.py
@@ -6,6 +6,8 @@
import argparse
import sys
import automation.verify
+import automation.clibuild
+import automation.clipublish
import automation.style
import automation.tests
import automation.cli_linter
@@ -16,6 +18,8 @@ def main():
sub_parser = parser.add_subparsers(title='sub commands')
automation.verify.init_args(sub_parser)
+ automation.clibuild.init_args(sub_parser)
+ automation.clipublish.init_args(sub_parser)
automation.style.init_args(sub_parser)
automation.tests.init_args(sub_parser)
automation.cli_linter.init_args(sub_parser)
diff --git a/tools/automation/clibuild/__init__.py b/tools/automation/clibuild/__init__.py
new file mode 100644
index 00000000000..fe588adccd5
--- /dev/null
+++ b/tools/automation/clibuild/__init__.py
@@ -0,0 +1,172 @@
+# --------------------------------------------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License. See License.txt in the project root for license information.
+# --------------------------------------------------------------------------------------------
+import os
+import tempfile
+import datetime
+from subprocess import check_output, CalledProcessError
+from concurrent.futures import ThreadPoolExecutor, as_completed
+
+try:
+ import xmlrpclib
+except ImportError:
+ import xmlrpc.client as xmlrpclib # pylint: disable=import-error
+
+from ..utilities.display import print_heading
+from ..utilities.path import get_repo_root
+from ..utilities.pypi import is_available_on_pypi
+
+# TODO Add 'msi' once we support it
+BUILD_TYPES = ['debian', 'docker', 'rpm', 'pypi', 'homebrew']
+
+def build_all_debian(git_url, git_branch, cli_version, artifact_dir, arg_ns=None):
+ # To only build a certain debian package, comment out the ones you don't want from the list below
+ dists = [
+ ('wheezy', 'debian:wheezy'),
+ ('jessie', 'debian:jessie'),
+ ('stretch', 'debian:stretch'),
+ ('artful', 'ubuntu:artful'),
+ ('xenial', 'ubuntu:xenial'),
+ ('trusty', 'ubuntu:trusty'),
+ ('bionic', 'ubuntu:bionic'),
+ ]
+ with ThreadPoolExecutor(max_workers=len(dists)) as executor:
+ tasks = {executor.submit(build_debian, dist_info, git_url, git_branch, cli_version, artifact_dir, arg_ns=arg_ns) for dist_info
+ in dists}
+ for t in as_completed(tasks):
+ t.result()
+ print('Finished debian builds for {}. Check each build message above for completion status.'.format(', '.join([d[0] for d in dists])))
+
+def build_debian(dist_info, git_url, git_branch, cli_version, artifact_dir, arg_ns=None):
+ # If you need to release a revision to the package, change this number then reset back to 1 for the next release
+ revision = 1
+ dist_codename = dist_info[0]
+ docker_image = dist_info[1]
+ cmd = ['docker', 'run', '-d', '-e', 'CLI_VERSION=' + cli_version,
+ '-e', 'CLI_VERSION_REVISION={}~{}'.format(revision, dist_codename), '-e', 'BUILD_ARTIFACT_DIR=/artifacts',
+ '-v', artifact_dir + ':/artifacts', docker_image, '/bin/bash', '-cx',
+ 'apt-get update && apt-get install -y git wget sudo && git clone --progress --verbose {} --branch {} /repo_clone '
+ '&& cd /repo_clone && build_scripts/debian/build.sh /repo_clone'.format(git_url, git_branch)]
+ container_id = check_output(cmd, universal_newlines=True).strip()
+ print('Debian {} build running. Use `docker logs -f {}`'.format(dist_info[0], container_id))
+ exit_code = check_output(['docker', 'wait', container_id], universal_newlines=True).strip()
+ print('FINISHED Debian {} build. Exit code {} (0 for success)'.format(dist_info[0], exit_code))
+
+
+def build_docker(git_url, git_branch, cli_version, artifact_dir, arg_ns=None):
+ cmd = ['docker', 'build', '--no-cache', '--quiet', '--build-arg', 'BUILD_DATE="`date -u +"%Y-%m-%dT%H:%M:%SZ"`"',
+ '--build-arg', 'CLI_VERSION=' + cli_version, get_repo_root()]
+ print('Docker build started. The git url and branch parameters are ignored. We use the current repository.')
+ image_id = check_output(cmd, universal_newlines=True).strip()
+ image_id = image_id.split(':')[1]
+ image_file_location = os.path.join(artifact_dir, 'docker-azure-cli-{}.tar'.format(cli_version))
+ cmd = ['docker', 'save', '-o', image_file_location, image_id]
+ image_id = check_output(cmd, universal_newlines=True).strip()
+ print('COMPLETED Docker build. image id: {}, saved to {}'.format(image_id, image_file_location))
+
+
+def build_rpm(git_url, git_branch, cli_version, artifact_dir, arg_ns=None):
+ cmd = ['docker', 'run', '-d', '-e', 'CLI_VERSION=' + cli_version, '-e', 'REPO_PATH=/repo_clone',
+ '-v', artifact_dir + ':/artifacts', 'centos:7', '/bin/bash', '-cx',
+ 'yum check-update; yum install -y gcc git rpm-build rpm-devel rpmlint make bash coreutils diffutils patch '
+ 'rpmdevtools python libffi-devel python-devel openssl-devel wget && git clone --progress --verbose {} '
+ '--branch {} /repo_clone && cd /repo_clone && rpmbuild -v -bb --clean build_scripts/rpm/azure-cli.spec && '
+ 'cp /root/rpmbuild/RPMS/x86_64/* /artifacts/'.format(git_url, git_branch)]
+ container_id = check_output(cmd, universal_newlines=True).strip()
+ print('RPM build running. Use `docker logs -f {}` to view logs'.format(container_id))
+ exit_code = check_output(['docker', 'wait', container_id], universal_newlines=True).strip()
+ print('FINISHED RPM build. Exit code {} (0 for success)'.format(exit_code))
+
+
+def build_pypi(git_url, git_branch, _, artifact_dir, arg_ns=None):
+ cmd = ['docker', 'run', '-d', '-v', artifact_dir + ':/artifacts', 'python:3.6', '/bin/bash', '-cx',
+ 'mkdir /artifacts/pypi && git clone --progress --verbose {} --branch {} /repo_clone && cd /repo_clone && '
+ 'python build_scripts/pypi/build.py /artifacts/pypi /repo_clone'.format(git_url, git_branch)]
+ container_id = check_output(cmd, universal_newlines=True).strip()
+ print('Python pypi build message: The version numbers of packages will be as defined in source code.')
+ print('Python pypi build running. Use `docker logs -f {}`'.format(container_id))
+ exit_code = check_output(['docker', 'wait', container_id], universal_newlines=True).strip()
+ print('COMPLETED Python pypi build. Exit code {}'.format(exit_code))
+
+
+def build_msi(git_url, git_branch, cli_version, artifact_dir, arg_ns=None):
+ # TODO
+ print('SKIPPED MSI build. Not Yet Implemented. Please build manually.')
+
+
+def build_homebrew(git_url, git_branch, cli_version, artifact_dir, arg_ns=None):
+ if not is_available_on_pypi('azure-cli', cli_version):
+ print('Homebrew message : The Homebrew formula requires CLI packages to be available on public PyPI. '
+ 'Version {} of the CLI does not appear to be on PyPI. '
+ 'If it was just updated, this message can be safely ignored.'.format(cli_version))
+
+ upstream_url = arg_ns.homebrew_upstream_url or 'https://github.com/Azure/azure-cli/archive/azure-cli-{cli_version}.tar.gz'.format(
+ cli_version=cli_version)
+ print('Homebrew message: The generated formula uses the latest public packages that are available on PyPI, '
+ 'not the code in your Git repo.')
+ cmd = ['docker', 'run', '-d', '-e', 'CLI_VERSION=' + cli_version, '-e', 'BUILD_ARTIFACT_DIR=/artifacts',
+ '-e', 'UPSTREAM_URL=' + upstream_url,
+ '-v', artifact_dir + ':/artifacts', 'python:3.6', '/bin/bash', '-cx',
+ 'pip install sh && git clone --progress --verbose {} --branch {} /repo_clone && cd /repo_clone && '
+ 'python build_scripts/homebrew/formula-generate.py'.format(git_url, git_branch)]
+ container_id = check_output(cmd, universal_newlines=True).strip()
+ print('Homebrew formula generation running. Use `docker logs -f {}`'.format(container_id))
+ exit_code = check_output(['docker', 'wait', container_id], universal_newlines=True).strip()
+ print('COMPLETED Homebrew formula generation. Exit code {}'.format(exit_code))
+
+
+def build_dispatch(build_type, git_url, git_branch, cli_version, artifact_dir, arg_ns=None):
+ if build_type == 'debian':
+ build_all_debian(git_url, git_branch, cli_version, artifact_dir, arg_ns=arg_ns)
+ elif build_type == 'docker':
+ build_docker(git_url, git_branch, cli_version, artifact_dir, arg_ns=arg_ns)
+ elif build_type == 'rpm':
+ build_rpm(git_url, git_branch, cli_version, artifact_dir, arg_ns=arg_ns)
+ elif build_type == 'pypi':
+ build_pypi(git_url, git_branch, cli_version, artifact_dir, arg_ns=arg_ns)
+ elif build_type == 'msi':
+ build_msi(git_url, git_branch, cli_version, artifact_dir, arg_ns=arg_ns)
+ elif build_type == 'homebrew':
+ build_homebrew(git_url, git_branch, cli_version, artifact_dir, arg_ns=arg_ns)
+
+
+def cli_build(args):
+ assert check_output(['docker', 'ps']), "Docker required."
+ build_types = args.build_types
+ git_url = args.git_clone_url
+ git_branch = args.git_clone_branch
+ cli_version = args.cli_version
+ artifact_dir = tempfile.mkdtemp(
+ prefix='cli-build-{}-'.format(datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')), dir=os.getcwd())
+ if len(build_types) == 1 and build_types[0] == '*':
+ build_types = BUILD_TYPES
+ print_heading('Building for {} from branch {} of {} '
+ 'and version number will be {}\n'
+ 'Build artifacts will be in {}'.format(', '.join(build_types), git_branch, git_url, cli_version,
+ artifact_dir))
+ with ThreadPoolExecutor(max_workers=len(build_types)) as executor:
+ tasks = {executor.submit(build_dispatch, bt, git_url, git_branch, cli_version, artifact_dir, arg_ns=args) for bt
+ in build_types}
+ for t in as_completed(tasks):
+ t.result()
+ print('Done.')
+
+
+def init_args(root):
+ cli_build_parser = root.add_parser('build', help='Build the CLI. Docker is required.')
+ cli_build_parser.set_defaults(func=cli_build)
+ git_args = cli_build_parser.add_argument_group('Git Clone Arguments')
+ git_args.add_argument('-b', '--git-clone-branch', dest='git_clone_branch',
+ help='Branch name that should be checked out. (default: %(default)s)', default='master')
+ git_args.add_argument('-u', '--git-clone-url', dest='git_clone_url',
+ help='The url to clone. This will be passed to `git clone`. (default: %(default)s)',
+ default='https://github.com/Azure/azure-cli.git')
+ cli_build_parser.add_argument('-t', '--type', dest='build_types', required=True, nargs='+',
+ choices=BUILD_TYPES + ['*'],
+ help="Space-separated list of the artifacts to build. Use '*' for all.")
+ cli_build_parser.add_argument('-c', '--cli-version', dest='cli_version', required=True,
+ help="The version of the build. (ignored for 'pypi' type)")
+ homebrew_args = cli_build_parser.add_argument_group('Homebrew Specific Arguments')
+ homebrew_args.add_argument('--homebrew-upstream-url', dest='homebrew_upstream_url',
+ help='The upstream URL to specify in the formula.')
diff --git a/tools/automation/clipublish/__init__.py b/tools/automation/clipublish/__init__.py
new file mode 100644
index 00000000000..320381f6efd
--- /dev/null
+++ b/tools/automation/clipublish/__init__.py
@@ -0,0 +1,83 @@
+# --------------------------------------------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License. See License.txt in the project root for license information.
+# --------------------------------------------------------------------------------------------
+
+import os
+import sys
+import requests
+
+PUBLISH_TYPES = ['debian', 'rpm']
+
+
+def get_debian_payload(cli_version, repo_id, distro, source_url):
+ return {'name': 'azure-cli', 'version': cli_version + '-1~' + distro, 'repositoryId': repo_id, 'sourceUrl': source_url}
+
+
+def get_yum_payload(cli_version, repo_id, source_url):
+ return {'name': 'azure-cli', 'version': cli_version, 'repositoryId': repo_id, 'sourceUrl': source_url}
+
+
+def publish_payload(endpoint, payload):
+ assert 'REPO_PASSWORD' in os.environ, "Set REPO_PASSWORD environment variable"
+ repo_password = os.environ['REPO_PASSWORD']
+ repo_username = 'azure-cli'
+ print('Publishing - {}'.format(payload))
+ r = requests.post(endpoint + '/v1/packages', verify=False, auth=(repo_username, repo_password), json=payload)
+ print('Status Code {}'.format(r.status_code))
+ # Query with a GET to the following (creds. required)
+ if r.status_code == 202:
+ print(endpoint + r.headers['Location'])
+ else:
+ print('Possible error. Server didn\'t return 202 Accepted.')
+
+
+def cli_publish(args):
+ publish_type = args.publish_type
+ cli_version = args.cli_version
+ endpoint = args.endpoint
+ if publish_type == 'debian':
+ debs = args.debs
+ assert debs, "No debs provided. Nothing to do."
+ payloads = [get_debian_payload(cli_version, repo_id, distro, source_url) for repo_id, distro, source_url in debs]
+ print('Payloads')
+ print('\n'.join(str(p) for p in payloads))
+ input('Please enter to confirm the payloads to make requests to publish the DEBIAN packages: ')
+ for p in payloads:
+ publish_payload(endpoint, p)
+ elif publish_type == 'rpm':
+ repo_id = args.rpm_repo_id
+ source_url = args.rpm_source_url
+ assert repo_id, "Missing --repo-id"
+ assert source_url, "Missing --source-url"
+ payload = get_yum_payload(cli_version, repo_id, source_url)
+ print('Payload')
+ print(payload)
+ input('Please enter to confirm the payload to make requests to publish the RPM package: ')
+ publish_payload(endpoint, payload)
+ else:
+ raise ValueError("Unknown publish type {}".format(publish_type))
+
+
+def type_debs(val):
+ repo_id, distro, source_url = val.split('/', 2)
+ return repo_id, distro, source_url
+
+
+def init_args(root):
+ parser = root.add_parser('publish', help='Publish the CLI.')
+ parser.set_defaults(func=cli_publish)
+ git_args = parser.add_argument_group('Git Clone Arguments')
+ parser.add_argument('-t', '--type', dest='publish_type', required=True,
+ choices=PUBLISH_TYPES,
+ help="Space separated list of the artifacts to build. Use '*' for all.")
+ parser.add_argument('-c', '--cli-version', dest='cli_version', required=True,
+ help="The version of the publish.")
+ parser.add_argument('-e', '--repo-endpoint', dest='endpoint', required=True,
+ help="The endpoint to publish the debian or yum package.")
+ deb_args = parser.add_argument_group('Debian Publish Arguments')
+ deb_args.add_argument('--debs', dest='debs', nargs='+', type=type_debs, default=[],
+ help='A space separated list of repoid/distro/source_url for each package to publish.')
+ rpm_args = parser.add_argument_group('RPM Publish Arguments')
+ rpm_args.add_argument('-r', '--rpm-repo-id', dest='rpm_repo_id', help='Repo ID for RPM Repo')
+ rpm_args.add_argument('-s', '--rpm-source-url', dest='rpm_source_url', help='URL to the RPM package')