Skip to content

Commit 8b34155

Browse files
authored
Add environment.yml to unify conda versioning (Azure#18141)
* add an environment.yml. consume in both build_conda_artifacts as well as in the meta.yml package definitions. * enhance conda_build_artifacts.py to extend the `summary` attribute via setting an environment variable * update to get_tagged_code to use mkdir -p instead of testing any paths.
1 parent 7e74cb3 commit 8b34155

File tree

8 files changed

+114
-33
lines changed

8 files changed

+114
-33
lines changed

doc/dev/conda-builds.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,21 @@ A Conda Artifact defines:
1616

1717
## How to Build an Azure SDK Conda Package Locally
1818

19+
### Set up your conda environment
20+
21+
You will notice that all the azure-sdk conda distributions have the **same** version number and requirement set. This is due to the fact that the azure-sdk team pushes our conda packages out in waves. To support this, all versions are set via a common environment variable `AZURESDK_CONDA_VERSION`.
22+
23+
We keep this environment variable set properly across all our builds by using a common `conda_env.yml` when creating our build environment. This environment definition ensures that:
24+
25+
1. Our channel `https://azuresdkconda.blob.core.windows.net/channel1/` is added to the set to download packages
26+
2. The environment variable `AZURESDK_CONDA_VERSION` will be set exactly once.
27+
28+
29+
Reference the `conda_env.yml` in your local build by pass `-f <path to conda_env.yml>` when you create your conda environment.
30+
31+
```
32+
conda env create --yes --quiet --name ${{ artifact.name }} -f $(Build.SourcesDirectory)/eng/conda_env.yml
33+
```
1934

2035
### Create Your Build Directory
2136
Given how Conda packages are comprised of multiple source distributions _combined_, the buildable source does not exist directly within the azure-sdk-for-python repo. Currently, there is _some_ manual work that needs to be done.
@@ -84,6 +99,8 @@ python `build_conda_artifacts.py`
8499
-r "azure/storage"
85100
-n "azure-storage"
86101
-s "storage"
102+
-e "<resolvable path to repo root>/eng/conda_env.yml"
103+
-c "<resolvable path to sdk/storage/ci.yml>"
87104
```
88105

89106
### Generate the Conda Package

eng/conda_env.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
variables:
2+
AZURESDK_CONDA_VERSION: '2021.05.01'

eng/pipelines/templates/steps/build-conda-artifacts.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,28 @@ steps:
5353
displayName: 'Build Source Distribution for ${{ artifact.name }}'
5454
inputs:
5555
scriptPath: 'scripts/devops_tasks/build_conda_artifacts.py'
56-
arguments: '-d "$(conda.output)" -b "$(conda.build)" -m "$(Build.SourcesDirectory)/sdk/${{ parameters.ServiceDirectory }}/${{ artifact.meta_source }}" -r "${{ artifact.common_root }}" -n "${{ artifact.name }}" -s "${{ parameters.ServiceDirectory }}" -o "${{ upper(parameters.ServiceDirectory) }}_SOURCE_DISTRIBUTION"'
56+
arguments: >-
57+
-d "$(conda.output)"
58+
-b "$(conda.build)"
59+
-m "$(Build.SourcesDirectory)/sdk/${{ parameters.ServiceDirectory }}/${{ artifact.meta_source }}"
60+
-r "${{ artifact.common_root }}"
61+
-n "${{ artifact.name }}"
62+
-s "${{ parameters.ServiceDirectory }}"
63+
-e "$(Build.SourcesDirectory)/eng/conda_env.yml"
64+
-c "$(Build.SourcesDirectory)/sdk/${{ parameters.ServiceDirectory }}/ci.yml"
5765
5866
- bash: |
5967
echo "##vso[task.prependpath]$CONDA/bin"
6068
displayName: 'Prepend PATH with Conda and INIT'
6169
6270
- bash: |
63-
conda create --yes --quiet --name ${{ artifact.name }}
64-
source activate ${{ artifact.name }}
71+
conda env create --name ${{ artifact.name }} --file $(Build.SourcesDirectory)/eng/conda_env.yml
6572
conda install --yes --quiet --name ${{ artifact.name }} conda-build
6673
displayName: 'Prepare Conda Environment for building ${{ artifact.name }}'
6774
6875
- bash: |
6976
source activate ${{ artifact.name }}
70-
conda-build . --output-folder "$(Agent.BuildDirectory)/conda/output" -c https://azuresdkconda.blob.core.windows.net/channel1/
77+
conda-build . --output-folder "$(Agent.BuildDirectory)/conda/output" -c $(AzureSDKCondaChannel)
7178
displayName: 'Activate Conda Environment and Build ${{ artifact.name }}'
7279
workingDirectory: $(Build.SourcesDirectory)/sdk/${{ parameters.ServiceDirectory }}
7380

eng/pipelines/templates/steps/get-tagged-code.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ parameters:
1515
steps:
1616
- pwsh: |
1717
$targetPath = "$(Agent.TempDirectory)/${{ parameters.Package }}"
18-
if (!(Test-Path $targetPath)) {
19-
mkdir $targetPath
20-
}
18+
mkdir -p $targetPath
2119
2220
Write-Host "##vso[task.setvariable variable=Package.Clone]$targetPath"
2321
displayName: 'Prep for Sparse Checkout'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
variables:
22
PythonVersion: '3.6'
33
skipComponentGovernanceDetection: true
4+
AzureSDKCondaChannel: https://azuresdkconda.blob.core.windows.net/channel1/

scripts/devops_tasks/build_conda_artifacts.py

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@
2525
import os
2626
import shutil
2727
import re
28+
import yaml
2829

2930
from common_tasks import process_glob_string, run_check_call, str_to_bool, parse_setup
3031
from subprocess import check_call
3132
from distutils.dir_util import copy_tree
3233

33-
VERSION_REGEX = re.compile(r"\{\%\s*set\s*version\s*=\s*\"(.*)\"\s*\%\}")
34+
VERSION_REGEX = re.compile(r"\s*AZURESDK_CONDA_VERSION\s*:\s*[\'](.*)[\']\s*")
35+
36+
SUMMARY_TEMPLATE = "- Generated from {}."
3437

3538
NAMESPACE_EXTENSION_TEMPLATE = """__path__ = __import__('pkgutil').extend_path(__path__, __name__) # type: str
3639
"""
@@ -143,8 +146,8 @@ def create_sdist_skeleton(build_directory, artifact_name, common_root):
143146
shutil.copytree(src, dest)
144147

145148

146-
def get_version_from_meta(meta_yaml_location):
147-
with open(os.path.abspath((meta_yaml_location)), "r") as f:
149+
def get_version_from_config(environment_config):
150+
with open(os.path.abspath((environment_config)), "r") as f:
148151
lines = f.readlines()
149152
for line in lines:
150153
result = VERSION_REGEX.match(line)
@@ -165,15 +168,17 @@ def get_manifest_includes(common_root):
165168
return breadcrumbs
166169

167170

168-
def create_setup_files(build_directory, common_root, artifact_name, service, meta_yaml):
171+
def create_setup_files(
172+
build_directory, common_root, artifact_name, service, meta_yaml, environment_config
173+
):
169174
sdist_directory = os.path.join(build_directory, artifact_name)
170175
setup_location = os.path.join(sdist_directory, "setup.py")
171176
manifest_location = os.path.join(sdist_directory, "MANIFEST.in")
172177
cfg_location = os.path.join(sdist_directory, "setup.cfg")
173178

174179
setup_template = CONDA_PKG_SETUP_TEMPLATE.format(
175180
conda_package_name=artifact_name,
176-
version=get_version_from_meta(meta_yaml),
181+
version=get_version_from_config(environment_config),
177182
service=service,
178183
package_excludes="'azure', 'tests', '{}'".format(common_root.replace("/", ".")),
179184
)
@@ -182,7 +187,9 @@ def create_setup_files(build_directory, common_root, artifact_name, service, met
182187
f.write(setup_template)
183188

184189
manifest_template = MANIFEST_TEMPLATE.format(
185-
namespace_includes="\n".join(["include " + ns for ns in get_manifest_includes(common_root)])
190+
namespace_includes="\n".join(
191+
["include " + ns for ns in get_manifest_includes(common_root)]
192+
)
186193
)
187194

188195
with open(manifest_location, "w") as f:
@@ -193,7 +200,13 @@ def create_setup_files(build_directory, common_root, artifact_name, service, met
193200

194201

195202
def create_combined_sdist(
196-
output_directory, build_directory, artifact_name, common_root, service, meta_yaml
203+
output_directory,
204+
build_directory,
205+
artifact_name,
206+
common_root,
207+
service,
208+
meta_yaml,
209+
environment_config,
197210
):
198211
singular_dependency = (
199212
len(get_pkgs_from_build_directory(build_directory, artifact_name)) == 0
@@ -202,7 +215,12 @@ def create_combined_sdist(
202215
if not singular_dependency:
203216
create_sdist_skeleton(build_directory, artifact_name, common_root)
204217
create_setup_files(
205-
build_directory, common_root, artifact_name, service, meta_yaml
218+
build_directory,
219+
common_root,
220+
artifact_name,
221+
service,
222+
meta_yaml,
223+
environment_config,
206224
)
207225

208226
sdist_location = os.path.join(build_directory, artifact_name)
@@ -222,6 +240,28 @@ def create_combined_sdist(
222240
return output_location
223241

224242

243+
def get_summary(ci_yml, artifact_name):
244+
pkg_list = []
245+
with open(ci_yml, "r") as f:
246+
data = f.read()
247+
248+
config = yaml.safe_load(data)
249+
250+
conda_artifact = [
251+
conda_artifact
252+
for conda_artifact in config["extends"]["parameters"]["CondaArtifacts"]
253+
if conda_artifact["name"] == artifact_name
254+
]
255+
256+
if conda_artifact:
257+
dependencies = conda_artifact[0]["checkout"]
258+
259+
for dep in dependencies:
260+
pkg_list.append("{}=={}".format(dep["package"], dep["version"]))
261+
262+
return SUMMARY_TEMPLATE.format(", ".join(pkg_list))
263+
264+
225265
if __name__ == "__main__":
226266
parser = argparse.ArgumentParser(
227267
description="Build a Conda Package, given a properly formatted build directory, and input configuration. This script assumes that the build directory has been set up w/ the necessary sdists in each location."
@@ -276,11 +316,19 @@ def create_combined_sdist(
276316
)
277317

278318
parser.add_argument(
279-
"-o",
280-
"--output_var",
281-
dest="output_var",
282-
help="The name of the environment variable that will be set in azure devops. The contents will be the final location of the output artifact. Local users will need to grab this value and set their env manually.",
283-
required=False,
319+
"-e",
320+
"--environment_config",
321+
dest="environment_config",
322+
help="The location of the yml config file used to create the conda environments. This file has necessary common configuration information within.",
323+
required=True,
324+
)
325+
326+
parser.add_argument(
327+
"-c",
328+
"--ci_yml",
329+
dest="ci_yml",
330+
help="The location of the ci.yml that is used to define our conda artifacts. Used when to easily grab summary information.",
331+
required=True,
284332
)
285333

286334
args = parser.parse_args()
@@ -291,11 +339,21 @@ def create_combined_sdist(
291339
args.common_root,
292340
args.service,
293341
args.meta_yml,
342+
args.environment_config,
294343
)
295344

296-
if args.output_var:
345+
summary = get_summary(args.ci_yml, args.artifact_name)
346+
347+
if output_source_location:
348+
print(
349+
"##vso[task.setvariable variable={}]{}".format(
350+
args.service.upper() + "_SOURCE_DISTRIBUTION", output_source_location
351+
)
352+
)
353+
354+
if summary:
297355
print(
298356
"##vso[task.setvariable variable={}]{}".format(
299-
args.output_var, output_source_location
357+
args.service.upper() + "_SUMMARY", summary
300358
)
301359
)

sdk/core/meta.yaml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
{% set name = "azure-core" %}
2-
{% set version = "2021.05.01" %}
32

43
package:
54
name: "{{ name|lower }}"
6-
version: "{{ version }}"
5+
version: {{ environ.get('AZURESDK_CONDA_VERSION', '0.0.0') }}
76

87
source:
98
url: {{ environ.get('CORE_SOURCE_DISTRIBUTION', '') }}
@@ -39,10 +38,10 @@ about:
3938
license: MIT
4039
license_family: MIT
4140
license_file:
42-
summary: "Microsoft Azure Core Library for Python"
41+
summary: Microsoft Azure Core Library for Python{{ environ.get('CORE_SUMMARY', '') }}
4342
doc_url:
4443
dev_url:
4544

4645
extra:
4746
recipe-maintainers:
48-
- lmazuel,xiangyan99
47+
- lmazuel,xiangyan99,scbedd

sdk/storage/meta.yaml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
{% set name = "azure-storage" %}
2-
{% set version = "2021.05.01" %}
32

43
package:
54
name: "{{ name|lower }}"
6-
version: "{{ version }}"
5+
version: {{ environ.get('AZURESDK_CONDA_VERSION', '0.0.0') }}
76

87
source:
98
url: {{ environ.get('STORAGE_SOURCE_DISTRIBUTION', '') }}
@@ -17,14 +16,14 @@ build:
1716

1817
requirements:
1918
host:
20-
- azure-core >=2021.05.01
19+
- azure-core >={{ environ.get('AZURESDK_CONDA_VERSION', '0.0.0') }}
2120
- cryptography >=2.1.4
2221
- msrest >=2021.05.01
2322
- pip
2423
- python
2524
- aiohttp
2625
run:
27-
- azure-core >=2021.05.01
26+
- azure-core >={{ environ.get('AZURESDK_CONDA_VERSION', '0.0.0') }}
2827
- cryptography >=2.1.4
2928
- msrest >=2021.05.01
3029
- python
@@ -42,11 +41,11 @@ about:
4241
home: "https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/storage"
4342
license: MIT
4443
license_family: MIT
45-
license_file:
46-
summary: "Microsoft Azure Storage Client Library for Python"
44+
license_file:
45+
summary: Microsoft Azure Storage Client Library for Python{{ environ.get('STORAGE_SUMMARY', '') }}
4746
doc_url:
4847
dev_url:
4948

5049
extra:
5150
recipe-maintainers:
52-
- your-github-id-here
51+
- lmazuel,xiangyan99,scbedd

0 commit comments

Comments
 (0)