import gitlab
import os
import requests
from typing import Optional, List
from datetime import datetime
class GitlabClient:
def __init__(self, url: str, private_token: str):
"""
初始化GitLab客户端
:param url: GitLab服务器URL
:param private_token: GitLab访问令牌
"""
self.gl = gitlab.Gitlab(url=url, private_token=private_token)
self.token = private_token
def get_project(self, project_id: int):
"""
获取指定项目
:param project_id: 项目ID
:return: 项目对象
"""
try:
return self.gl.projects.get(project_id)
except gitlab.exceptions.GitlabGetError:
print(f"错误: 未找到ID为 {project_id} 的项目")
return None
def get_job_artifacts(self, project_id: int, job_id: Optional[int] = None,
pipeline_id: Optional[int] = None, ref: str = 'main',
download_path: str = 'artifacts') -> List[str]:
"""
下载项目制品
:param project_id: 项目ID
:param job_id: 指定的任务ID(可 选)
:param pipeline_id: 指定的流水线ID(可选)
:param ref: 分支名称,默认为main
:param download_path: 下载保存路径
:return: 下载的文件路径列表
"""
project = self.get_project(project_id)
if not project:
return []
downloaded_files = []
try:
if job_id:
job = project.jobs.get(job_id)
file_path = self._download_artifact(project, job, download_path)
if file_path:
downloaded_files.append(file_path)
elif pipeline_id:
pipeline = project.pipelines.get(pipeline_id)
jobs = pipeline.jobs.list(all=True)
for job in jobs:
if job.artifacts:
file_path = self._download_artifact(project, job, download_path)
if file_path:
downloaded_files.append(file_path)
else:
pipelines = project.pipelines.list(ref=ref, status='success')
if pipelines:
latest_pipeline = pipelines[0]
jobs = latest_pipeline.jobs.list(all=True)
for job in jobs:
if job.artifacts:
file_path = self._download_artifact(project, job, download_path)
if file_path:
downloaded_files.append(file_path)
except Exception as e:
print(f"下载制品时发生错误: {str(e)}")
return downloaded_files
def _download_artifact(self, project, job, download_path: str) -> Optional[str]:
"""
下载单个job的制品
:param project: 项目对象
:param job: 任务对象
:param download_path: 下载保存路径
:return: 下载的文件路径
"""
try:
os.makedirs(download_path, exist_ok=True)
try:
if isinstance(job.finished_at, str):
finished_time = datetime.strptime(job.finished_at, '%Y-%m-%dT%H:%M:%S.%fZ')
else:
finished_time = datetime.fromtimestamp(job.finished_at)
timestamp = finished_time.strftime('%Y%m%d_%H%M%S')
except (TypeError, ValueError):
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
filename = f"{project.path}_{job.name}_{timestamp}.zip"
file_path = os.path.join(download_path, filename)
with open(file_path, 'wb') as f:
job.artifacts(streamed=True, action=f.write)
print(f"成功下载制品: {file_path}")
return file_path
except Exception as e:
print(f"下载制品 {job.name} 时发生错误: {str(e)}")
return None
def get_latest_job_artifacts(self, project_id: int, download_path: str = 'artifacts') -> List[str]:
"""
获取项目最近一次运行的job制品
:param project_id: 项目ID
:param download_path: 下载保存路径
:return: 下载的文件路径列表
"""
project = self.get_project(project_id)
if not project:
return []
downloaded_files = []
try:
jobs = project.jobs.list(all=False, order_by='id', sort='desc')
if not jobs:
print(f"项目 {project_id} 没有找到任何job")
return []
latest_job_with_artifacts = None
for job in jobs:
if job.artifacts:
latest_job_with_artifacts = job
break
if latest_job_with_artifacts:
print(f"找到最近的job: {latest_job_with_artifacts.name} (ID: {latest_job_with_artifacts.id})")
file_path = self._download_artifact(project, latest_job_with_artifacts, download_path)
if file_path:
downloaded_files.append(file_path)
else:
print(f"项目 {project_id} 没有找到任何制品")
except Exception as e:
print(f"获取最近制品时发生错误: {str(e)}")
return downloaded_files
def main():
import argparse
parser = argparse.ArgumentParser(description='GitLab制品下载工具')
parser.add_argument('--project-id', type=int, required=True, help='项目ID')
parser.add_argument('--job-id', type=int, help='指定的任务ID')
parser.add_argument('--pipeline-id', type=int, help='指定的流水线ID')
parser.add_argument('--ref', default='main', help='分支名称,默认为main')
parser.add_argument('--path', default='artifacts', help='下载保存路径,默认为artifacts')
parser.add_argument('--latest', action='store_true', help='获取最近一次运行的job制品')
args = parser.parse_args()
gitlab_url = os.getenv('GITLAB_URL', 'https://gitlab.example.com')
gitlab_token = os.getenv('GITLAB_TOKEN')
if not gitlab_token:
raise ValueError("请设置GITLAB_TOKEN环境变量")
client = GitlabClient(gitlab_url, gitlab_token)
if args.latest:
downloaded_files = client.get_latest_job_artifacts(
project_id=args.project_id,
download_path=args.path
)
else:
downloaded_files = client.get_job_artifacts(
project_id=args.project_id,
job_id=args.job_id,
pipeline_id=args.pipeline_id,
ref=args.ref,
download_path=args.path
)
if downloaded_files:
print(f"\n成功下载 {len(downloaded_files)} 个制品:")
for file_path in downloaded_files:
print(f"- {file_path}")
else:
print("未找到可下载的制品")
if __name__ == '__main__':
main()