Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create an ADO pipeline for doing releases to PyPI, in the pattern of other projects in RAI. Signed-off-by: Richard Edgar <riedgar@microsoft.com>
- Loading branch information
1 parent
e82fc09
commit 5da88c6
Showing
11 changed files
with
322 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
# Simplified PyPI release pipeline | ||
|
||
# At queue time, the user selects a Test or Production deployment. The following stages | ||
# then run: | ||
# - Predeployment validation (run a set of tests against the repository) | ||
# - Creates a wheel and stores in Pipeline Artifact | ||
# - Download wheel file from Artifact, pip install, and run tests | ||
# - Upload the wheel to PyPI (Test or Production as specified at queue time) | ||
# - Install from PyPI and run tests | ||
|
||
parameters: | ||
- name: releaseType | ||
displayName: Release Type | ||
type: string | ||
default: Test | ||
values: | ||
- Test | ||
- Production | ||
|
||
variables: | ||
poolImage: "ubuntu-latest" | ||
poolPythonVersion: 3.6 | ||
packageArtifactName: Wheels | ||
versionArtifactName: Version | ||
versionFileName: versionInfo.txt | ||
|
||
trigger: none # No CI build | ||
|
||
pr: none # Not for pull requests | ||
|
||
# ================================================================================================== | ||
|
||
stages: | ||
- stage: PredeploymentValidation | ||
displayName: Predeployment Validation | ||
pool: | ||
vmImage: $(poolImage) | ||
|
||
jobs: | ||
- template: templates/all-tests-job-template.yml | ||
parameters: | ||
platforms: { Linux: ubuntu-latest, MacOS: macos-latest, Windows: windows-latest } | ||
pyVersions: [3.6, 3.7] | ||
installationType: PipLocal | ||
envArtifactStem: PredeployFreeze | ||
envFileStem: redeploy-requirements | ||
|
||
# ================================================================================================== | ||
|
||
- stage: CreateWheel | ||
displayName: Create Wheel Artifact | ||
dependsOn: PredeploymentValidation | ||
pool: | ||
vmImage: $(poolImage) | ||
|
||
variables: | ||
wheelEnvName: WheelEnvironment | ||
|
||
jobs: | ||
- job: CreateWheel | ||
displayName: Build and publish wheel | ||
pool: | ||
vmImage: $(poolImage) | ||
|
||
steps: | ||
- task: UsePythonVersion@0 | ||
displayName: 'Use Python $(poolPythonVersion)' | ||
inputs: | ||
versionSpec: $(poolPythonVersion) | ||
addToPath: true | ||
|
||
- template: templates/create-env-step-template.yml | ||
parameters: | ||
pythonVersion: $(poolPythonVersion) | ||
envInfoArtifact: CreateWheelFreeze | ||
envInfoFileBase: createwheel-freeze | ||
condaEnv: $(wheelEnvName) | ||
|
||
- bash: | | ||
source activate $(wheelEnvName) | ||
pip install --upgrade wheel | ||
displayName: 'Install wheel' | ||
- bash: | | ||
source activate $(wheelEnvName) | ||
python ./tools/build_wheels.py --version-filename $(versionFilename) | ||
displayName: 'Build wheels' | ||
- task: PublishPipelineArtifact@1 | ||
displayName: "Publish wheels" | ||
inputs: | ||
path: $(System.DefaultWorkingDirectory)/python/dist | ||
artifact: $(packageArtifactName) | ||
|
||
- task: PublishPipelineArtifact@1 | ||
displayName: "Publish version information file" | ||
inputs: | ||
path: '$(System.DefaultWorkingDirectory)/$(versionFilename)' | ||
artifact: $(versionArtifactName) | ||
|
||
# ================================================================================================== | ||
|
||
- stage: TestWheel | ||
displayName: Test Wheel from Artifact | ||
dependsOn: CreateWheel | ||
pool: | ||
vmImage: $(poolImage) | ||
|
||
jobs: | ||
- template: templates/all-tests-job-template.yml | ||
parameters: | ||
platforms: { Linux: ubuntu-latest, MacOS: macos-latest, Windows: windows-latest } | ||
pyVersions: [3.6, 3.7] | ||
installationType: 'WheelArtifact' | ||
envArtifactStem: TestWheelFreeze | ||
envFileStem: requirements-wheel-test | ||
wheelArtifactName: $(packageArtifactName) | ||
|
||
# ================================================================================================== | ||
|
||
- stage: UploadWheel | ||
displayName: Upload Wheel to PyPI (${{parameters.releaseType}}) | ||
dependsOn: TestWheel | ||
pool: | ||
vmImage: $(poolImage) | ||
|
||
variables: | ||
${{ if eq(parameters.releaseType, 'Test')}}: | ||
twineConnection: PyPI-Test | ||
twineEndpoint: PyPITest | ||
${{ if eq(parameters.releaseType, 'Production')}}: | ||
twineConnection: PyPI-Prod | ||
twineEndpoint: PyPIProd | ||
|
||
jobs: | ||
- deployment: 'PyPI_${{parameters.releaseType}}_Upload' | ||
displayName: PyPI ${{parameters.releaseType}} Upload | ||
${{ if eq(parameters.releaseType, 'Test')}}: | ||
environment: 'PyPI-Test Deployment' | ||
${{ if eq(parameters.releaseType, 'Production')}}: | ||
environment: 'PyPI Deployment' | ||
pool: | ||
vmImage: $(poolImage) | ||
|
||
strategy: | ||
runOnce: | ||
deploy: | ||
steps: | ||
- task: UsePythonVersion@0 | ||
displayName: 'Use Python $(poolPythonVersion)' | ||
inputs: | ||
versionSpec: $(poolPythonVersion) | ||
addToPath: true | ||
|
||
- script: pip install twine | ||
displayName: 'Install twine' | ||
|
||
- task: TwineAuthenticate@0 | ||
inputs: | ||
externalFeeds: ${{variables.twineConnection}} | ||
|
||
- script: 'twine upload --verbose -r $(twineEndpoint) --config-file $(PYPIRC_PATH) $(Pipeline.Workspace)/$(packageArtifactName)/*' | ||
displayName: Upload to ${{parameters.releaseType}} PyPI | ||
|
||
# TODO: Add GitHub Release task, so links in PyPI ReadMe will work without manual intervention (Prod only) | ||
|
||
- job: PyPI_Pause | ||
pool: server | ||
dependsOn: 'PyPI_${{parameters.releaseType}}_Upload' | ||
displayName: PyPI Pause | ||
|
||
steps: | ||
- task: Delay@1 | ||
displayName: "Pause to allow PyPI updates to complete" | ||
inputs: | ||
delayForMinutes: "5" | ||
|
||
# # ================================================================================================== | ||
|
||
- stage: TestFromPyPI | ||
displayName: Test package from ${{parameters.releaseType}} PyPI | ||
dependsOn: UploadWheel | ||
pool: | ||
vmImage: $(poolImage) | ||
|
||
variables: | ||
envInfoArtifact: TestPyPIFreeze | ||
envInfoFileBase: requirements-pypi-test | ||
|
||
jobs: | ||
- template: templates/all-tests-job-template.yml | ||
parameters: | ||
platforms: { Linux: ubuntu-latest, MacOS: macos-latest, Windows: windows-latest } | ||
pyVersions: [3.6, 3.7] | ||
envArtifactStem: TestPyPIFreeze | ||
envFileStem: requirements-pypi-test | ||
installationType: 'PyPI' | ||
targetType: ${{parameters.releaseType}} | ||
versionArtifactName: $(versionArtifactName) | ||
versionArtifactFile: $(versionFileName) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import logging | ||
|
||
_logger = logging.getLogger(__file__) | ||
logging.basicConfig(level=logging.INFO) | ||
|
||
|
||
class _LogWrapper: | ||
def __init__(self, description): | ||
self._description = description | ||
|
||
def __enter__(self): | ||
_logger.info("Starting %s", self._description) | ||
|
||
def __exit__(self, type, value, traceback): # noqa: A002 | ||
# raise exceptions if any occurred | ||
if value is not None: | ||
raise value | ||
_logger.info("Completed %s", self._description) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import argparse | ||
import logging | ||
import subprocess | ||
import sys | ||
|
||
from _utils import _LogWrapper | ||
|
||
_logger = logging.getLogger(__file__) | ||
logging.basicConfig(level=logging.INFO) | ||
|
||
|
||
def build_argument_parser(): | ||
desc = "Build wheels for interpret-community" | ||
|
||
parser = argparse.ArgumentParser(description=desc) | ||
parser.add_argument("--version-filename", | ||
help="The file where the version will be stored.", | ||
required=True) | ||
|
||
return parser | ||
|
||
|
||
def main(argv): | ||
parser = build_argument_parser() | ||
args = parser.parse_args(argv) | ||
|
||
with _LogWrapper("installation of interpret-community"): | ||
subprocess.check_call(["pip", "install", "./python"]) | ||
|
||
with _LogWrapper("Check pip"): | ||
subprocess.check_call(["pip", "freeze"]) | ||
|
||
with _LogWrapper("storing interpret-community version in {}".format(args.version_filename)): | ||
import interpret_community | ||
with open(args.version_filename, 'w') as version_file: | ||
version_file.write(interpret_community.__version__) | ||
|
||
with _LogWrapper("creation of packages"): | ||
subprocess.check_call( | ||
["python", "./setup.py", "sdist", "bdist_wheel"], cwd="./python/") | ||
|
||
|
||
if __name__ == "__main__": | ||
main(sys.argv[1:]) |
Oops, something went wrong.