Skip to content

Commit 06f7930

Browse files
authored
steps/RustSetupSteps.yml: Use step template to download artifacts (#233)
Downloads the Cargo tools from an Azure pipeline using a YAML step template. This resolves a problem in commit 69f6e96 that was not found until after check-in due to the nature of the issue. It is summarized below. In the original check-in (69f6e96), the following succeeds and fails: - Downloading and Caching as an Artifact: - ✓ Use GitHub REST API to download release and extract the binaries - ✓ Publish the binaries as a pipeline artifact in the projectmu org - Retrieving the Artifact - Note: In all cases, try the `DownloadPipelineArtifact@2` and `DownloadBuildArtifacts@1` tasks - ✓ Access in a manually triggered pipeline - ✓ Access in a PR triggered pipeline from a branch in the microsoft org repo (i.e. not a fork) - ✗ Access in a PR triggered pipeline from a branch outside the microsoft org repo (i.e. a fork) To allow testing using pre-existing PR check pipelines (which have access to branches on the microsoft org, not forks), the last case was unexpected and not encountered until the PRs completed. Since the information is readily available using the Azure Pipelines REST API without authentication, this change replaces the built-in tasks with calls to download the binaries from the REST API. There are a few quirks with pipelines that are accounted for: 1. The Python code is inline so it can directly be used as a template in other YAML files that are used as a repository resource. If in a separate Python file, the mu_devops repo would need to be checked out to use access the file. Checking out the repo would increase build times and complicate pre-existing logic. 2. The Azure Pipeline `InvokeRESTAPI@1` task is not used as it is limited to agentless jobs. 3. Conditions are not allowed on templates. Therefore the OS condition is moved to a string parameter that is compared in the `condition` on the task in the template. By default, the task will run on all operating systems if not specified. As a follow up, the `Cache@2` task will be explored which might simplify some logic but require changes elsewhere as an alternative to cache the binaries in the pipelines. Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com>
1 parent 99e40ad commit 06f7930

2 files changed

Lines changed: 159 additions & 40 deletions

File tree

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
## @file
2+
# Azure Pipelines step template to download Azure Pipeline artifacts.
3+
#
4+
# Copyright (c) Microsoft Corporation. All rights reserved.
5+
# SPDX-License-Identifier: BSD-2-Clause-Patent
6+
##
7+
8+
parameters:
9+
- name: artifact_name
10+
displayName: Artifact Name
11+
type: string
12+
default: 'Binaries'
13+
- name: azure_org_name
14+
displayName: Azure Org Name
15+
type: string
16+
default: 'projectmu'
17+
- name: azure_proj_name
18+
displayName: Azure Project Name
19+
type: string
20+
default: 'mu'
21+
- name: azure_pipeline_def_id
22+
displayName: Azure Pipeline Definition ID
23+
type: string
24+
default: '0'
25+
- name: file_pattern
26+
displayName: File Pattern
27+
type: string
28+
default: '*'
29+
- name: target_dir
30+
displayName: Target Directory
31+
type: string
32+
default: ''
33+
- name: target_os
34+
displayName: Target OS For Task to Run
35+
type: string
36+
default: 'Windows_NT,Darwin,Linux'
37+
- name: task_display_name
38+
displayName: Task Display Name
39+
type: string
40+
default: 'Download Pipeline Artifact'
41+
- name: work_dir
42+
displayName: Work Directory
43+
type: string
44+
default: ''
45+
46+
steps:
47+
48+
- task: PythonScript@0
49+
displayName: ${{ parameters.task_display_name }}
50+
env:
51+
ARTIFACT_NAME: ${{ parameters.artifact_name }}
52+
AZURE_ORG_NAME: ${{ parameters.azure_org_name }}
53+
AZURE_PROJ_NAME: ${{ parameters.azure_proj_name }}
54+
AZURE_PIPELINE_DEF_ID: ${{ parameters.azure_pipeline_def_id }}
55+
FILE_PATTERN: ${{ parameters.file_pattern }}
56+
TARGET_DIR: ${{ parameters.target_dir }}
57+
WORK_DIR: ${{ parameters.work_dir }}
58+
inputs:
59+
scriptSource: inline
60+
workingDirectory: $(Agent.BuildDirectory)
61+
script: |
62+
import os
63+
import requests
64+
import shutil
65+
import zipfile
66+
from pathlib import Path
67+
68+
ARTIFACT_NAME = os.environ["ARTIFACT_NAME"]
69+
AZURE_ORG_NAME = os.environ["AZURE_ORG_NAME"]
70+
AZURE_PROJ_NAME = os.environ["AZURE_PROJ_NAME"]
71+
AZURE_PIPELINE_DEF_ID = os.environ["AZURE_PIPELINE_DEF_ID"]
72+
FILE_PATTERN = os.environ["FILE_PATTERN"]
73+
TARGET_DIR = Path(os.environ["TARGET_DIR"])
74+
WORK_DIR = os.environ["WORK_DIR"]
75+
76+
build_id_url = f"https://dev.azure.com/{AZURE_ORG_NAME}/{AZURE_PROJ_NAME}/_apis/build/builds?definitions={AZURE_PIPELINE_DEF_ID}&$top=1&api-version=6.0"
77+
78+
# Fetch the list of assets from the GitHub releases
79+
response = requests.get(build_id_url)
80+
response.raise_for_status()
81+
latest_build_id = response.json()["value"][0]["id"]
82+
83+
artifact_url = f"https://dev.azure.com/{AZURE_ORG_NAME}/{AZURE_PROJ_NAME}/_apis/build/builds/{latest_build_id}/artifacts?artifactName={ARTIFACT_NAME}&api-version=6.0"
84+
response = requests.get(artifact_url)
85+
response.raise_for_status()
86+
download_url = response.json()["resource"]["downloadUrl"]
87+
88+
print(f"Latest Build ID: {latest_build_id}")
89+
print(f"Artifact Download URL: {download_url}")
90+
91+
download_path = Path(WORK_DIR, "artifact_download", ARTIFACT_NAME).with_suffix(".zip")
92+
download_path.parent.mkdir(parents=True)
93+
with requests.get(download_url, stream=True) as r:
94+
with download_path.open('wb') as f:
95+
for chunk in r.iter_content(chunk_size=8192):
96+
f.write(chunk)
97+
98+
with zipfile.ZipFile(download_path, 'r') as zip_ref:
99+
zip_ref.extractall(download_path.parent)
100+
101+
unzip_path = download_path.parent / ARTIFACT_NAME
102+
103+
104+
def flatten_copy(src: Path, dst: Path, pattern: str):
105+
if not dst.exists():
106+
dst.mkdir(parents=True)
107+
108+
for item in src.rglob(pattern):
109+
print(f"Current item is {item}")
110+
if item.is_dir():
111+
flatten_copy(item, dst, pattern)
112+
else:
113+
shutil.copy2(item, dst)
114+
115+
116+
TARGET_DIR.mkdir(parents=True, exist_ok=True)
117+
flatten_copy(unzip_path, TARGET_DIR, FILE_PATTERN)
118+
shutil.rmtree(download_path.parent)
119+
condition: and(succeeded(), contains('${{ parameters.target_os}}', variables['Agent.OS']))

Steps/RustSetupSteps.yml

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -48,53 +48,53 @@ steps:
4848
displayName: Install Rust 1.71.1 (Windows)
4949
condition: eq(variables['Agent.OS'], 'Windows_NT')
5050

51-
- task: DownloadPipelineArtifact@2
52-
displayName: Download Cargo Make (Windows)
53-
inputs:
54-
buildType: specific
55-
project: mu
56-
definition: 166
57-
targetPath: '$(cargoBinPath)\'
58-
itemPattern: '**/cargo-make.exe'
59-
artifactName: Binaries
60-
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
51+
- script: pip install requests --upgrade
52+
displayName: Install and Upgrade requests PIP Module
53+
condition: succeeded()
6154

62-
- task: DownloadPipelineArtifact@2
63-
displayName: Download Cargo Make (Linux)
64-
inputs:
65-
buildType: specific
66-
project: mu
67-
definition: 166
68-
targetPath: '$(Agent.TempDirectory)'
69-
itemPattern: '**/cargo-make'
70-
artifactName: Binaries
71-
condition: eq(variables['Agent.OS'], 'Linux')
55+
- template: DownloadAzurePipelineArtifact.yml
56+
parameters:
57+
task_display_name: Download Cargo Make (Windows)
58+
artifact_name: Binaries
59+
azure_pipeline_def_id: 166
60+
file_pattern: "**/cargo-make.exe"
61+
target_dir: "$(cargoBinPath)"
62+
target_os: "Windows_NT"
63+
work_dir: "$(Agent.TempDirectory)"
64+
65+
- template: DownloadAzurePipelineArtifact.yml
66+
parameters:
67+
task_display_name: Download Cargo Make (Linux)
68+
artifact_name: Binaries
69+
azure_pipeline_def_id: 166
70+
file_pattern: "**/cargo-make"
71+
target_dir: "$(Agent.TempDirectory)"
72+
target_os: "Linux"
73+
work_dir: "$(Agent.TempDirectory)"
7274
- script: |
7375
cp $AGENT_TEMPDIRECTORY/cargo-make /.cargo/bin
7476
displayName: Copy cargo-make
7577
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
7678

77-
- task: DownloadPipelineArtifact@2
78-
displayName: Download Cargo Tarpaulin (Windows)
79-
inputs:
80-
buildType: specific
81-
project: mu
82-
definition: 167
83-
targetPath: '$(cargoBinPath)\'
84-
itemPattern: '**/cargo-tarpaulin.exe'
85-
artifactName: Binaries
86-
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
79+
- template: DownloadAzurePipelineArtifact.yml
80+
parameters:
81+
task_display_name: Download Cargo Tarpaulin (Windows)
82+
artifact_name: Binaries
83+
azure_pipeline_def_id: 167
84+
file_pattern: "**/cargo-tarpaulin.exe"
85+
target_dir: "$(cargoBinPath)"
86+
target_os: "Windows_NT"
87+
work_dir: "$(Agent.TempDirectory)"
8788

88-
- task: DownloadPipelineArtifact@2
89-
displayName: Download Cargo Tarpaulin (Linux)
90-
inputs:
91-
buildType: specific
92-
project: mu
93-
definition: 167
94-
targetPath: '$(Agent.TempDirectory)'
95-
itemPattern: '**/cargo-tarpaulin'
96-
artifactName: Binaries
97-
condition: eq(variables['Agent.OS'], 'Linux')
89+
- template: DownloadAzurePipelineArtifact.yml
90+
parameters:
91+
task_display_name: Download Cargo Tarpaulin (Linux)
92+
artifact_name: Binaries
93+
azure_pipeline_def_id: 167
94+
file_pattern: "**/cargo-tarpaulin"
95+
target_dir: "$(Agent.TempDirectory)"
96+
target_os: "Linux"
97+
work_dir: "$(Agent.TempDirectory)"
9898
- script: |
9999
cp $AGENT_TEMPDIRECTORY/cargo-tarpaulin /.cargo/bin
100100
displayName: Copy cargo-tarpaulin

0 commit comments

Comments
 (0)