다음을 통해 공유


Node.js 사용하여 끌어오기 요청 상태 서버 만들기

Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022 | Azure DevOps Server 2020

PR(끌어오기 요청) 워크플로는 개발자에게 피어 및 자동화된 도구에서 코드에 대한 피드백을 얻을 수 있는 기회를 제공합니다. Microsoft 이외의 도구 및 서비스는 PR 상태 API사용하여 PR 워크플로에 참여할 수 있습니다. 이 문서에서는 Azure DevOps Services Git 리포지토리에서 PR의 유효성을 검사하는 상태 서버를 만드는 프로세스를 안내합니다. PR 상태에 대한 더 많은 정보는 끌어오기 요청 워크플로 사용자 지정 및 확장 및 끌어오기 요청 상태을 참조하세요.

필수 구성 요소

카테고리 요구 사항
조직 Azure DevOps의 조직에는 Git 리포지토리가 있습니다.
도구 - Visual Studio Code 또는 선택한 다른 코드 편집기입니다.
- Node.js. 다운로드에는 로컬 컴퓨터에 Node.js 런타임을 설치하기 위해 실행할 수 있는 설치 관리자가 포함되어 있습니다. Node.js설치할 때는 기본적으로 선택된 npm 패키지 관리자 설치 부분을 유지해야 합니다.
인증 Microsoft Entra ID 토큰은 PR 상태를 변경할 수 있는 권한을 가지기 위해 코드(상태) 범위가 있어야 합니다. 자세한 내용은 Microsoft Entra 인증을 참조하세요.

Express를 사용하여 기본 웹 서버 만들기

이 섹션의 단계에서는 웹 서버 만들기를 간소화하는 많은 HTTP 유틸리티 메서드를 제공하는 Node.js 간단한 웹 프레임워크인 Express사용합니다. 이 프레임워크는 PR 이벤트를 수신 대기하는 데 필요한 기본 함수를 제공합니다.

  1. 명령줄에서 웹 서버에 대한 새 프로젝트 폴더를 만듭니다.

    mkdir pr-server
    cd pr-server
    
  2. npm init 명령을 사용하여 프로젝트에 대한 새 package.json 파일을 만듭니다.

    npm init
    

    Enter 선택하여 진입점을 제외한 모든 옵션의 기본값을 적용합니다. app.js으로 변경하세요.

    entry point: (index.js) app.js
    
  3. 다음 명령을 사용하여 pr-server 디렉터리에 Express를 설치합니다. 그러면 Express가 설치되고 종속성 목록에 저장됩니다.

    npm install express
    
  4. PR 상태 서버에 대해 빌드할 Express 앱을 만듭니다. 다음 단계는 Express Hello 월드 예제기반으로 합니다.

    a. pr-server 폴더에서 다음 명령을 실행하여 Visual Studio Code에서 프로젝트 폴더를 엽니다.

    code .
    

    b. 새 파일을 (Ctrl + N) 만들고 다음 샘플 코드에 붙여넣어 기본 Express 서버를 만듭니다.

    const express = require('express')
    const app = express()
    
    app.get('/', function (req, res) {
    res.send('Hello World!')
    })
    
    app.listen(3000, function () {
    console.log('Example app listening on port 3000!')
    })
    

    다. app.js로 파일을 저장합니다.

  5. 다음 명령을 사용하여 기본 웹 서버를 실행합니다.

    node app.js
    

    http://localhost:3000/검색하여 서버가 실행 중인지 확인합니다.

HTTP POST 요청 수신 대기

웹 서버는 Azure DevOps Services에서 POST 요청을 수신하므로 서버에서 해당 요청을 처리해야 합니다.

  1. app.js 파일의 끝에 다음 코드를 추가하고 파일을 저장합니다.

    app.post('/', function (req, res) {
        res.send('Received the POST')
    })
    
  2. 다음 명령을 사용하여 웹 서버를 다시 실행합니다.

    node app.js
    

PR 이벤트에 대한 서비스 후크 구성

서비스 후크는 특정 이벤트가 발생할 때 외부 서비스에 경고할 수 있는 Azure DevOps Services 기능입니다. 이 샘플의 경우 PR 이벤트에 대해 두 개의 서비스 후크를 설정하여 상태 서버에 알림을 받을 수 있습니다. 첫 번째는 이벤트를 만든 끌어오기 요청에 대한 것이고, 두 번째는 업데이트된 이벤트에 대한 끌어오기 요청입니다.

서비스 후크 알림을 받으려면 공용 인터넷에 포트를 노출합니다. ngrok 유틸리티는 개발 환경에서 이 작업을 수행하는 데 유용합니다.

  1. 플랫폼에 적합한 ngrok 릴리스를 다운로드하고 압축을 풉니다.

  2. ngrok를 사용하여 샘플 서버와 동일한 포트인 포트 3000에서 수신 대기를 시작합니다. 새 명령 창에서 다음 명령을 실행합니다.

    ngrok http 3000
    

    Ngrok는 localhost:3000에 연결되는 공용 URL을 만듭니다. 다음 단계에서 필요하므로 URL을 기록해 둡다. 다음 예제와 같습니다.

    http://c3c1bffa.ngrok.io
    
  3. Azure DevOps에서 프로젝트로 이동합니다(예: https://dev.azure.com/<your account>/<your project name>

  4. 탐색 메뉴에서 기어에 마우스를 올려놓고 서비스 후크를 선택합니다.

    스크린샷은 관리 메뉴에서 서비스 후크 선택을 보여 줍니다.

  5. 첫 번째 서비스 후크인 경우 + 구독 만들기를 선택합니다.

    스크린샷은 도구 모음에서 선택한 새 구독 만들기를 보여줍니다.

    다른 서비스 후크가 이미 구성된 경우 더하기 (+) 선택하여 새 서비스 후크 구독을 만듭니다.

    스크린샷이 새로운 서비스 후크 구독을 만들기 위해 선택한 플러스 버튼을 보여줍니다.

  6. 새 서비스 후크 구독 대화 상자의 서비스 목록에서 웹 후크를 선택한 다음, 다음을 선택합니다.

    스크린샷은 서비스 목록에서 선택한 웹 후크를 보여줍니다.

  7. 이벤트 트리거 목록에서 에서 생성된 끌어오기 요청을 선택한 다음, 다음을 선택합니다.

    스크린샷은 이벤트 트리거 목록에서 만든 선택한 끌어오기 요청을 보여줍니다.

  8. 작업 페이지의 URL 상자에 ngrok의 URL을 입력합니다. 테스트 선택하여 테스트 이벤트를 서버에 보냅니다.

    스크린샷은 서비스 후크를 테스트하기 위해 입력한 URL 및 선택한 테스트를 보여줍니다.

    ngrok 콘솔 창에서 들어오는 POST는 서버가 서비스 후크 이벤트를 수신했음을 나타내며 200 OK을 반환합니다.

    HTTP Requests
    -------------
    
    POST /                         200 OK
    

    테스트 알림 창에서 응답 탭을 선택하여 서버의 응답 세부 정보를 확인합니다. POST 처리기의 문자열 길이와 일치하는 17의 콘텐츠 길이가 표시됩니다(예: "POST 수신됨").

    스크린샷은 테스트 결과에 대해 선택한 응답 탭을 보여줍니다.

  9. 테스트 알림 창을 닫고 마침 선택하여 서비스 후크를 만듭니다.

3-9단계를 다시 진행하지만 이번에는 끌어오기 요청 업데이트 이벤트를 구성합니다.

중요하다

이전 단계를 두 번 검토하고 끌어오기 요청 생성끌어오기 요청 업데이트 이벤트에 대한 서비스 후크를 각각 만들어야 합니다.

PR에 상태 게시

이제 서버가 새 PR을 만들 때 서비스 후크 이벤트를 받을 수 있으므로 PR에 상태를 게시할 수 있도록 업데이트하세요.

  1. 서비스 후크 요청에는 이벤트를 설명하는 JSON 페이로드가 포함됩니다. 서비스 후크에서 반환된 JSON을 구문 분석하는 데 도움이 되도록 본문 파서 패키지를 설치합니다.

    npm install body-parser
    
  2. app.js구문 분석하기 위해 본문 파서가 사용되도록 application/json 업데이트합니다.

    var bodyParser = require('body-parser')
    
    app.use(bodyParser.json())
    
  3. Azure Repos에 대한 REST API 호출을 간소화하려면 azure-devops-node-api 패키지를 설치합니다.

    npm install azure-devops-node-api 
    
  4. azure-devops-node-api 패키지를 사용하도록 app.js을 업데이트하고, 계정 연결 세부 정보를 설정한 후 Git API 인스턴스를 가져옵니다.

    const vsts = require("azure-devops-node-api")
    
    const collectionURL = process.env.COLLECTIONURL    
    const token = process.env.TOKEN
    
    var authHandler = vsts.getBearerHandler(token)
    var connection = new vsts.WebApi(collectionURL, authHandler)
    var vstsGit = connection.getGitApi()
    
  5. 컬렉션 URL에 대한 환경 변수를 만들어 <your account> Azure DevOps 조직의 이름으로 바꿉니다.

    setx COLLECTIONURL "https://dev.azure.com/<your account>"
    
  6. 앱에서 사용할 Microsoft Entra ID 토큰을 가져옵니다. Microsoft Entra ID 토큰은 Azure DevOps REST API에 권장되는 인증 방법입니다. 다음 방법을 통해 이러한 토큰을 가져올 수 있습니다.

    • 옵션 1: Azure CLI(개발/테스트용)
      az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query "accessToken" --output tsv
      
    • 옵션 2: 서비스 원칙(프로덕션용)
      1. Microsoft Entra ID에 애플리케이션 등록
      2. 애플리케이션에 대한 클라이언트 암호 만들기
      3. Azure DevOps에서 애플리케이션에 적절한 권한 부여
      4. 서비스 주체 자격 증명을 사용하여 프로그래밍 방식으로 토큰 가져오기

    자세한 내용은 Microsoft Entra 인증을 참조하세요.

  7. Microsoft Entra ID 토큰에 대한 환경 변수를 만듭니다.

    setx TOKEN "your-entra-id-token-here"
    

프로덕션 애플리케이션의 경우 정적 토큰을 사용하는 대신 프로그래밍 방식으로 Microsoft Entra ID 토큰을 가져와야 합니다. Node.js위해 MSAL(Microsoft 인증 라이브러리)을 사용하여 이를 구현하는 방법은 다음과 같습니다.

  1. MSAL 노드 패키지를 설치합니다.

    npm install @azure/msal-node
    
  2. 토큰 공급자 모듈 만들기(tokenProvider.js):

    const { ConfidentialClientApplication } = require('@azure/msal-node');
    
    const clientConfig = {
        auth: {
            clientId: process.env.CLIENT_ID,
            clientSecret: process.env.CLIENT_SECRET,
            authority: `https://login.microsoftonline.com/${process.env.TENANT_ID}`
        }
    };
    
    const cca = new ConfidentialClientApplication(clientConfig);
    
    async function getAccessToken() {
        const clientCredentialRequest = {
            scopes: ['499b84ac-1321-427f-aa17-267ca6975798/.default']
        };
    
        try {
            const response = await cca.acquireTokenByClientCredential(clientCredentialRequest);
            return response.accessToken;
        } catch (error) {
            console.error('Error acquiring token:', error);
            throw error;
        }
    }
    
    module.exports = { getAccessToken };
    
  3. app.js을(를) 토큰 공급자를 사용하도록 업데이트합니다.

    const { getAccessToken } = require('./tokenProvider');
    
    // Instead of using a static token, get a fresh token
    app.post("/", async function (req, res) {
        try {
            const token = await getAccessToken();
            var authHandler = vsts.getBearerHandler(token);
            var connection = new vsts.WebApi(collectionURL, authHandler);
    
            // ... rest of your POST handler code
        } catch (error) {
            console.error('Authentication error:', error);
            res.status(500).send('Authentication failed');
        }
    });
    
  4. post() 함수를 업데이트하여 서비스 후크 페이로드에서 PR 세부 정보를 읽습니다. 상태를 다시 게시하려면 이러한 값이 필요합니다.

    var repoId = req.body.resource.repository.id
    var pullRequestId = req.body.resource.pullRequestId
    var title = req.body.resource.title
    
  5. PR에 게시할 상태 개체를 빌드합니다.

    State GitStatusState 형식의 열거형입니다. succeeded 사용하여 PR이 상태 검사를 통과하고 병합할 준비가 되었음을 나타냅니다.

    description 상태 섹션의 사용자에게 표시되고 PR 세부 정보 보기의 활동 피드에 표시되는 문자열 값입니다.

    targetUrl 상태 섹션 및 활동 피드에서 설명 텍스트에 대한 링크를 만드는 데 사용되는 URL로, 사용자가 빌드 보고서 또는 테스트 실행과 같은 상태에 대한 자세한 정보를 얻을 수 있습니다. URL을 지정하지 않으면 설명이 링크가 없는 텍스트로 표시됩니다.

    컨텍스트 namegenre 상태를 분류하고 다른 서비스 게시 상태와 구분하는 데 사용됩니다.

        var prStatus = {
            "state": "succeeded",
            "description": "Ready for review",
            "targetUrl": "https://visualstudio.microsoft.com",
            "context": {
                "name": "wip-checker",
                "genre": "continuous-integration"
            }
        }
    
  6. succeeded 상태를 즉시 게시하는 대신 PR 제목을 검사하여 사용자가 타이틀에 WIP 추가하여 PR이 진행 중인 작업인지 여부를 확인합니다. 그렇다면 PR에 게시된 상태를 원래대로 되돌립니다.

        if (title.includes("WIP")) {
            prStatus.state = "pending"
            prStatus.description = "Work in progress"
        }
    
  7. 마지막으로 createPullRequestStatus() 메서드를 사용하여 상태를 게시합니다. 상태 개체, 리포지토리 ID 및 끌어오기 요청 ID가 필요합니다. 게시 결과를 볼 수 있도록 노드 콘솔에 응답을 출력합니다.

    vstsGit.createPullRequestStatus(prStatus, repoId, pullRequestId).then( result => {
        console.log(result)
    })
    
  8. 결과 메서드는 다음과 같이 표시됩니다.

    app.post("/", async function (req, res) {
        try {
            // Get the details about the PR from the service hook payload
            var repoId = req.body.resource.repository.id
            var pullRequestId = req.body.resource.pullRequestId
            var title = req.body.resource.title
    
            // Build the status object that we want to post.
            // Assume that the PR is ready for review...
            var prStatus = {
                "state": "succeeded",
                "description": "Ready for review",
                "targetUrl": "https://visualstudio.microsoft.com",
                "context": {
                    "name": "wip-checker",
                    "genre": "continuous-integration"
                }
            }
    
            // Check the title to see if there is "WIP" in the title.
            if (title.includes("WIP")) {
                // If so, change the status to pending and change the description.
                prStatus.state = "pending"
                prStatus.description = "Work in progress"
            }
    
            // Get the Git API instance and post the status to the PR
            const gitApi = await vstsGit
            const result = await gitApi.createPullRequestStatus(prStatus, repoId, pullRequestId)
            console.log(result)
    
            res.send("Received the POST")
        } catch (error) {
            console.error('Error processing PR status:', error)
            res.status(500).send('Error processing request')
        }
    })
    
  9. app.js 저장하고 노드 앱을 다시 시작합니다.

    node app.js
    

상태 서버를 테스트하기 위한 새 PR 만들기

서버가 실행 중이고 서비스 후크 알림을 수신 대기했으므로 끌어오기 요청을 만들어 테스트합니다.

  1. 파일 보기에서 시작합니다. 리포지토리에서 readme.md 파일을 편집합니다(또는 readme.md 없는 경우 다른 파일).

    스크린샷은 상황에 맞는 메뉴에서 선택한 편집 단추를 보여줍니다.

  2. 편집하고 변경 내용을 리포지토리에 커밋합니다.

    스크린샷은 도구 모음에서 파일 편집 및 선택한 커밋 단추를 보여 줍니다.

  3. 다음 단계에서 PR을 만들 수 있도록 변경 내용을 새 분기에 커밋해야 합니다.

    스크린샷은 입력한 새 분기 이름과 선택한 커밋 단추를 보여줍니다.

  4. 끌어오기 요청 만들기 링크를 선택합니다.

    스크린샷은 선택한 제안 표시줄에서 끌어오기 요청 만들기를 보여줍니다.

  5. 타이틀에 WIP 추가하여 앱의 기능을 테스트합니다. 만들기 선택하여 PR을 만듭니다.

    스크린샷은 기본 PR 제목에 추가된 WIP를 보여줍니다.

  6. PR이 생성되면, 상태 섹션에는 페이로드에 지정된 URL에 연결되는 항목이 포함된 작업 진행 중이 표시됩니다.

    스크린샷은 진행 중인 작업 항목이 있는 상태 섹션을 보여줍니다.

  7. PR 제목을 업데이트하고 WIP 텍스트를 제거한 다음, 상태가 진행 중에서 검토 준비로 변경되었음을 명심하세요.