今からでも間に合う

技術を学ぶのは今からでも遅くない

特定コミット間のコミットに紐づいた作業項目を取得する

(2023/1/17追記:全然ちゃんとした環境で動いてなかったので大幅に書き直しました)

azure devopsの特定コミット間のコミット一覧の中から、それに紐づいた作業項目を取得するpythonコードです。
たぶんリリース情報を整理するのに活用できるはずです。

近しい提供されているAPIとして、 Build - Get Work Items Between Builds というのはあるのですが、こちらはBuild IDがないとだめなのと、「同リポジトリだけど、パイプラインIDが違うと動かない」という片手落ちだったので今回のものを作りました。

やりたいこと

  • 特定の2コミット間の間にあるコミットから
  • 含まれる作業項目を取り出し
  • いい感じに整形して出力したい

できるとうれしいこと

リリースが捗ります。
個人的には2~3割は工数の削減につながります。

環境

  • VSCode
  • python 3.11.0
  • azure devops

使用するライブラリ

  • azure-devops
    • rest apiはv6.0を使用

コード

概要

azure devops rest apiのうち、今回は

を使用しています。

前者で特定コミット間のコミット一覧を取得し、その中に含まれるWork Item IDの詳細を、後者で取得しています。

一番わかりにくかったのが、コミットIDを示す「to」と「from」の意味合いが思っていたのと逆だったこと。
「from」が新しいほうのコミットIDを指すようです。

from_commit_idto_commit_idではまともに情報取れなかったので、from_dateto_dateを使うように書き換えてます。
そのためコミットIDからコミット時刻を特定する処理を追加しています。

コード全文(全面書き直し)
import datetime
from azure.devops.connection import Connection
from msrest.authentication import BasicAuthentication
import azure.devops.v6_0.git as git
import azure.devops.v6_0.pipelines as pipe
import azure.devops.v6_0.work_item_tracking as wit
  
class Setup :
    def __init__(self, org_url, project, repository_id, pat, target_branch=None) :
        self.org_url = org_url
        self.project = project
        self.repos_id = repository_id
        self.pat = pat
        self.branch = target_branch
  
def __get_date_from_commit(setup: Setup, gc : git.git_client.GitClient, commit_id) -> datetime.datetime :
    """__get_date_from_commit
    get date from commit.
    utc offset :9h
    """
    commit : git.models.GitCommit = gc.get_commit(
        commit_id=commit_id,
        repository_id=setup.repos_id,
        project=setup.project)
    
    # コミットした人を特定
    committer : git.models.GitUserDate = commit.committer
    # コミット時刻を取得(タイムゾーン合わせる)
    d : datetime.datetime = committer.date + datetime.timedelta(hours=9)
    # タイムゾーンの設定を削除
    d = d.replace(tzinfo=None)
    return d
  
def __find_pipeline_id_from_build_id(setup: Setup, pc: pipe.PipelinesClient, build_id) :
    """__find_pipeline_id_from_build_id
    """
    # パイプライン一覧を取得
    pipelines = pc.list_pipelines(setup.project)
    
    for pl in pipelines :
        pl : pipe.models.Pipeline
        
        # ビルド結果を取得
        runs = pc.list_runs(setup.project, pipeline_id=pl.id)
        # build_idを探す
        for r in runs :
            r : pipe.models.Run
            if r.id == build_id :
                return pl.id
    return None
  
def get_commit_id_from_build_id(setup: Setup, build_id) :
    credentials = BasicAuthentication('', setup.pat)
    connection = Connection(base_url=setup.org_url, creds=credentials)
    
    pc : pipe.PipelinesClient = connection.clients_v6_0.get_pipelines_client()
    
    pipeline_id = __find_pipeline_id_from_build_id(setup, pc, build_id)
    run: pipe.models.Run = pc.get_run(project=setup.project, pipeline_id=pipeline_id, run_id=build_id)
    return run.as_dict()["resources"]["repositories"]["__designer_repo"]["versiom"]
   
def get_work_items_between_commits(setup : Setup, from_commit_id : str, to_commit_id : str) :
    credentials = BasicAuthentication('', setup.pat)
    connection = Connection(base_url=setup.org_url, creds=credentials)
    
    # GitClient
    gc : git.git_client.GitClient = connection.clients_v6_0.get_git_client()
    # Work Item Tracking
    witc : wit.work_item_tracking_client.WorkItemTrackingClient = connection.clients_v6_0.get_work_item_tracking_client()
   
    # get date from commit id
    from_date = __get_date_from_commit(setup, gc, from_commit_id)
    to_date = __get_date_from_commit(setup, gc, to_commit_id)
    
    # Git Get Commits
    criteria = git.models.GitQueryCommitsCriteria()
    criteria.from_date = from_date
    criteria.to_date = to_date
    criteria.top = 10000
    # 取得対象のブランチを指定
    if setup.branch != None :
        criteria.item_version = git.models.GitVersionDescriptor(setup.branch)
    # 紐づいている作業項目を取得
    criteria.include_work_items = True
    criteria.history_mode = "firstParent"
  
    commits = gc.get_commits(
        repository_id=setup.repos_id, 
        search_criteria=criteria, 
        project=setup.project
        )
    
    result = []
    for commit in commits :
        commit : git.models.GitCommitRef
        dic_commit = commit.as_dict()
  
        if dic_commit["work_items"] == None :
            continue
        
        commit_id = dic_commit["commit_id"]
        for wi in dic_commit["work_items"] :
            # コミットに紐づいた作業項目の内容を出力
            id = wi["id"]
            work_item : wit.models.WorkItem = witc.get_work_item(id, project=setup.project)
            wi_dict = work_item.as_dict()
            result.append(wi_dict)
  
    return result
使い方

シンプルです。必要な情報を渡すだけです。

setup =  Setup(
    org_url="https://dev.azure.com/<your organization>",
    project="<your project>",
    repository_id="<your repository>",
    target_branch="<target branch>(option)",
    pat="<your pat>")

ret =  azure_test.get_work_items_between_commits(
    setup=setup,
    from_commit_id="from commit id",
    to_commit_id="to commit id",
)
おまけ(取得した作業項目から解決済みバグ一覧を取得)
for r in ret :
    id = r["id"]
    title = r["fields"]["System.Title"]
    url = r["url"]
    
    print(f"{id}\t{title}\t{url}")

おしまい

あるものを組み合わせて作業を効率化するのって楽しいですね。

python入門Top

プライバシーポリシー


d払いポイントGETモール