Forum Discussion
elik1001
Jan 03, 2025Copper Contributor
Azure DevOps - How to Publish Artifacts Using REST API in Python?
I'm struggling to implement this functionality and could use some help. Here's my setup:
- I'm using YAML pipelines in Azure DevOps.
- Part of the pipeline includes a Python task with a long and complex processing script that generates output reports.
- For better control, I want to add the Publish Artifacts functionality (based on some logic) directly within the Python script.
So far, I have tried the following REST API calls without success.
Part 1 - Works
url = f"https://dev.azure.com/{organization}/{project}/_apis/build/builds/{build_id}/artifacts?artifactName={artifact_name}&api-version=7.1-preview.5"
headers = {
"Authorization": f"Basic {ACCESS_TOKEN}",
"Accept": "application/json",
"Content-Type": "application/json",
}
payload = {
"name": artifact_name,
"resource": {
"type": "Container",
"data": artifact_name,
"properties": {
"RootId": artifact_name
}
},
}
logger.info("Creating artifact metadata...")
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 200:
logger.info("Artifact metadata created successfully.")
response_json = response.json()
logger.info(f"Create Pre-Payload Response: {response_json}")
Part 2 - FAILS
headers = {
"Authorization": f"Basic {ACCESS_TOKEN}",
"Content-Type": "application/octet-stream",
}
for artifact_file in artifact_files:
if not os.path.exists(artifact_file):
logger.warning(f"File {artifact_file} does not exist. Skipping.")
continue
# Construct the full upload URL
item_path = f"{artifact_name}/{os.path.basename(artifact_file)}"
upload_url = f"{container_url}?itemPath={item_path}"
logger.info(f"Uploading: {artifact_file} to {upload_url}")
with open(artifact_file, "rb") as f:
response = requests.put(upload_url, headers=headers, data=f)
if response.status_code == 201:
logger.info(f"File {artifact_file} uploaded successfully.")
else:
logger.error(f"Failed to upload {artifact_file}: {response.status_code}, {response.text}")
Part 2 returns a 404 - like the one below..
INFO:__main__:Uploading: reports/test.json to https://dev.azure.com/OrgDevOps/_apis/resources/Containers/c82916f3-4665-43bf-8927-e05a3b6492a9?itemPath=drop_v3/test.json
ERROR:__main__:Failed to upload reports/test.json: 404, <!DOCTYPE html >
<html>
<head>
<title>The controller for path '/_apis/resources/Containers/c82916f3-4665-43bf-8927-e05a3b6492a9' was not found or does not implement IController.</title>
<style type="text/css">html {
height: 100%;
}
…
Any guidance or working examples would be greatly appreciated.
Thanks in advance!
2 Replies
Sort By
- elik1001Copper Contributor
Hi,
Thanks for your response.
Unfortunately I get the same results. the second part comes back with a 404 page.INFO:__main__:Creating artifact metadata... INFO:__main__:Artifact metadata created successfully. INFO:__main__:Create Pre-Payload Response: {'id': 47056, 'name': 'drop_v4', 'source': None, 'resource': {'type': 'Container', 'data': 'drop_v4', 'properties': {'RootId': 'drop_v4'}, 'url': 'https://dev.azure.com/my_org/c82916f3-4665-43bf-8927-e05a3b6492a9/_apis/build/builds/72710/artifacts?artifactName=drop_v4&api-version=7.1-preview.5', 'downloadUrl': 'https://dev.azure.com/my_org/c82916f3-4665-43bf-8927-e05a3b6492a9/_apis/build/builds/72710/artifacts?artifactName=drop_v4&api-version=7.1-preview.5&%24format=zip'}} INFO:__main__:Uploading: reports/test-1.json to https://dev.azure.com/my_org/my_project/_apis/resources/Containers/c82916f3-4665-43bf-8927-e05a3b6492a9?itemPath=drop_v4/test-1.json ERROR:__main__:Failed to upload reports/test-1.json: 404, <!DOCTYPE html > ... </div> <div class="error-info"> <div class="title">404 - Page not found</div> <div class="details">Looks like this page doesn't exist or can't be found. Make sure the URL is correct.</div> <div class="reference-data"> <div>1/6/2025 1:31:51 PM (UTC)</div> <div>adcbe8d1-8e32-4c9b-92c0-2586dc535880</div> </div> <div class="action-container"> <a class="action" href="https://dev.azure.com/my_org/"> <span>Go back home</span> </a> </div> </div> </div> </div> </div> </body> </html>
Thanks in advance. Try below with logging which help to diagnose the issue:
import os import requests import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) organization = 'your_organization' project = 'your_project' build_id = 'your_build_id' artifact_name = 'your_artifact_name' artifact_files = ['path/to/your/artifact1', 'path/to/your/artifact2'] container_url = f"https://dev.azure.com/{organization}/{project}/_apis/resources/Containers/c82916f3-4665-43bf-8927-e05a3b6492a9" # Part 1 - Create artifact metadata url = f"https://dev.azure.com/{organization}/{project}/_apis/build/builds/{build_id}/artifacts?artifactName={artifact_name}&api-version=7.1-preview.5" headers = { "Authorization": f"Basic {ACCESS_TOKEN}", "Accept": "application/json", "Content-Type": "application/json", } payload = { "name": artifact_name, "resource": { "type": "Container", "data": artifact_name, "properties": { "RootId": artifact_name } }, } logger.info("Creating artifact metadata...") response = requests.post(url, headers=headers, json=payload) if response.status_code == 200: logger.info("Artifact metadata created successfully.") response_json = response.json() logger.info(f"Create Pre-Payload Response: {response_json}") else: logger.error(f"Failed to create artifact metadata: {response.status_code}, {response.text}") # Part 2 - Upload artifacts headers = { "Authorization": f"Basic {ACCESS_TOKEN}", "Content-Type": "application/octet-stream", } for artifact_file in artifact_files: if not os.path.exists(artifact_file): logger.warning(f"File {artifact_file} does not exist. Skipping.") continue item_path = f"{artifact_name}/{os.path.basename(artifact_file)}" upload_url = f"{container_url}?itemPath={item_path}" logger.info(f"Uploading: {artifact_file} to {upload_url}") with open(artifact_file, "rb") as f: response = requests.put(upload_url, headers=headers, data=f) if response.status_code == 201: logger.info(f"File {artifact_file} uploaded successfully.") else: logger.error(f"Failed to upload {artifact_file}: {response.status_code}, {response.text}")