Firebase 클라우드 함수: Git 명령 및 GitHub GraphQL API

우리는 최근 DeckDeckGo에 사람을 흥분시키는 새로운 특성을 발표했다.
향상된 웹 응용 프로그램으로 프레젠테이션을 온라인으로 배포할 수 있을 뿐 아니라, Dell 웹 소스 편집기는 현재 소스 코드를 GitHub으로 푸시할 수 있습니다.🎉.
이 새 함수는 Firebase Cloud Functions에서 실행됩니다.우리는 우리의 발견을 공유하는 것을 좋아하기 때문에 다음은 우리가 이러한 통합을 개발할 때 배운 관건적인 요소이다.

액세스 토큰
GitHub과 상호작용을 하려면 영패가 필요합니다.

개인 토큰
계정을 통해 GitHub과 상호 작용하려면 personal access token을 사용하십시오.Firebase 함수를 만든 후 구성을 설정할 수 있습니다.이렇게 하면 코드에서 혼동될 것입니다.
#!/bin/sh
firebase functions:config:set github.token="4a686......."

Firebase 인증 및 GitHub 토큰
사용자의 행동으로 GitHub과 상호작용하는 것에 관심이 있다면 Firebase UIFirebase Authentication을 사용할 수 있습니다.
제가 알기로는 이런 조합이 생겨서 불행히도 Firebase 클라우드에서 사용자의 GitHub 영패를 얻을 수 없습니다.인증 events에 연결하려고 했지만 트리거 대상에서 관련 정보를 찾지 못했습니다.
제가 뭘 놓쳤는지 이 상황에서 빨리 알려주세요(!),그러나 이러한 정보가 없으면 Firebase UI 구성의 signInSuccessWithAuthResult 콜백을 통해 찾아야 합니다.
callbacks: {
  signInSuccessWithAuthResult: 
    (authResult: firebase.auth.UserCredential, _redirectUrl) => {

    const token: string =
      (userCred.credential as 
               firebase.auth.OAuthCredential).accessToken;

    return true;
  },
},
TypeScript 액세스 토큰을 사용하는 방법에 대한 질문을 열었습니다. *OAuthCredential의 변환은 answer으로 제공됩니다.*

파일 시스템
진일보한 토론을 하기 전에, 당신은 우리가 클라우드에서 Git 명령을 어떻게 집행할 것인지를 스스로에게 물어볼 수 있습니다.나도 실제로 같은 질문을 했는데 Firebase 함수가 file system의 임시 폴더에 접근할 수 있다는 것이 증명되었다.

The only writeable part of the filesystem is the /tmp directory, which you can use to store temporary files in a function instance. This is a local disk mount point known as a "tmpfs" volume in which data written to the volume is stored in memory. Note that it will consume memory resources provisioned for the function.


또한 임시 디렉터리는 기능 간에 공유할 수 없다.예를 들어, 이것은 이러한 폴더를 사용하여 데이터를 공유할 수 없다는 것을 의미한다.tmp 주문서는 하드코딩이 필요하지 않습니다.대신 Node.js OS module을 사용하여 임시 폴더를 검색할 수 있습니다.만약 어떤 원인 때문 에 그것 이 미래 에 변화 를 일으킬 것 이라면, 너 는 영원히 모를 것 이다. 그것 은 더욱 편리할 것 이다😉.
import * as os from 'os';

console.log(os.tmpdir()); // -> /tmp
Path module과 결합하여 사용하면 로컬 파일의 경로를 분석하는 간단한 실용 프로그램 함수를 만들 수 있습니다.
import * as path from 'path';
import * as os from 'os';

export function getFilePath(...files: string[]): string {
  return path.join(os.tmpdir(), ...files);
}

console.log(getFilePath('yo', 'david.txt'); // -> /tmp/yo/david.txt

Git 명령
리포를 복제하거나 일반적으로commit,pull,push 등 Git 명령을 실행하기 위해서, Node에 simple-git 인터페이스를 사용하는 것을 권장합니다.js는 Steve King에서 개발(npm에 매주 150만 회 다운로드).이것은 정말 모든 일을 경감시켰다.
npm i simple-git --save

클론 복제
구체적으로 클론 기능은 다음과 같습니다.
import * as path from 'path';
import * as os from 'os';

import simpleGit, {SimpleGit} from 'simple-git';

export async function clone(repoUrl: string, repoName: string) {
  const localPath: string = path.join(os.tmpdir(), repoName);

  await deleteDir(localPath);

  const git: SimpleGit = simpleGit();
  await git.clone(repoUrl, localPath);
}

// Demo:

(async () => {
 await clone('https://github.com/deckgo/deckdeckgo/', 'deckdeckgo'); 
})();
임시 폴더가 비어 있을 수도 있지만, 우선 작업 하위 디렉터리를 삭제하는 것이 안전할 수도 있습니다.이것이 바로 내가 위의 함수에서 deleteDir을 호출한 이유다.
import * as rimraf from 'rimraf';

export function deleteDir(localPath: string): Promise<void> {
  return new Promise<void>((resolve) => {
    rimraf(localPath, () => {
      resolve();
    });
  });
}
보시다시피 저는 rimraf(주당 npmjs에서 3700만 번 다운로드)을 사용합니다.
npm i rimraf --save && npm i @types/rimraf --save-dev

밀다
Git 명령의 또 다른 흥미로운 예는 Push 요청입니다. 왜냐하면 우리는 영패를 사용하여 요청을 검증해야 하기 때문입니다.
토큰을 사용하는 해결 방안을 찾은 후에 나는 Stackoverflow 문답을 읽는 데 시간이 좀 걸렸고 토큰이 노출되지 않도록 함수에서 상호작용을 하더라도 가장 좋은 결과를 주는 해결 방안은 Git URI에서 사용하는 것이라고 결론을 내렸다.
영패는 잠재적인 오류 메시지에 노출될 수 있으니 주의하십시오. 이것이 바로 내가 이 소식을 정확하게 포착하는 것도 좋다고 생각하는 이유입니다.
기호화폐를 제외하고 GitHub 계정의 username(예를 들어 peterpeterparker)과 email을 제공해야 할 수도 있습니다.이러한 정보는 우리의 기능 설정을 통해 관리할 수 있다.
import * as functions from 'firebase-functions';

import * as path from 'path';
import * as os from 'os';

import simpleGit, {SimpleGit} from 'simple-git';

export async function push(project: string,
                           branch: string) {
  try {
    const localPath: string = path.join(os.tmpdir(), repoName);

    // Git needs to know where is has to run, that's why we pass
    // the pass to the constructor of simple-git

    const git: SimpleGit = getSimpleGit(localPath);

    // Configure Git with the username and email

    const username: string = functions.config().github.username;
    const email: string = functions.config().github.email;

    await git.addConfig('user.name', username);
    await git.addConfig('user.email', email);

    // Finally Git push

    const token: string = functions.config().github.token;

    await git.push(`https://${username}:${token}@github.com/${username}/${project}.git`, branch);
  } catch (err) {
    throw new Error(`Error pushing.`);
  }
}

// Demo:

(async () => {
 await push('deckdeckgo', 'my-branch'); 
})();

GitHub GraphQL API
마지막 버전이나 새로운 버전은 관점에 따라 GitHub API 버전(v4)은GraphQL 조회에 사용할 수 있습니다.그것의 documentation은 검색 정보를 상대적으로 쉽게 하지만 explorer과 그의 자동 완성 기능은 조회를 신속하고 유연하게 조합하는 데 더욱 편리할 것이다.

묻다
나는 조회를 실행하기 위해 GraphQL 클라이언트 (예: Apollo) 를 사용하지 않았다.대신 HTTPS 요청을 수행하기 위해 함수를 개발했습니다.
import fetch, {Response} from 'node-fetch';

async function queryGitHub(githubToken: string, 
                           query: string): Promise<Response> {
  const githubApiV4: string = 'https://api.github.com/graphql';

  const rawResponse: Response = await fetch(`${githubApiV4}`, {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
      Authorization: `token ${githubToken}`,
    },
    body: JSON.stringify({query}),
  });

  if (!rawResponse || !rawResponse.ok) {
    throw new Error('Cannot perform GitHub query.');
  }

  return rawResponse;
}
Node.jsfetch을 제공할 수 없기 때문에 저는 node-fetch(npm을 매주 1600만 번 다운로드)을 사용했습니다.
npm i node-fetch --save && npm i @types/node-fetch --save-dev

질의:사용자 정보
다음은 상대적으로 기본적인 조회 예이다.이러한 함수에서 GitHub login ("사용자 이름") 과 id (각각 currently authenticated user) 의 정보를 검색하려고 합니다.
export interface GitHubUser {
  id: string;
  login: string;
}

export function getUser(githubToken: string): Promise<GitHubUser> {
  return new Promise<GitHubUser>(async (resolve, reject) => {
    try {
      const query = `
        query {
          viewer {
            id,
            login
          }
        }
      `;

      const response: Response = 
            await queryGitHub(githubToken, query);

      const result = await response.json();

      resolve(result.data.viewer);
    } catch (err) {
      reject(err);
    }
  });
}

// Demo:

(async () => {
 const token: string = functions.config().github.token;

 const user = await getUser(token); 

 console.log(user); // -> {login: 'peterpeterparker', id: '123'}
})();

변이: 당김 요청
Pull 요청은 GraphQL 쿼리가 아니라 mutation 쿼리입니다.이전의 검색에 비해 더 많은 정보를 필요로 하지만, 배후의 논리는 같다. GraphQL 검색/변이를 작성하여 HTTPS를 통해 요청을 보내고 결과를 얻는 것이다😁.
주목할 점은 PR을 만들기 위해서는 돌연변이에 repositoryId이 필요하다는 점이다.repository 정보를 요청할 때 제공하는 쿼리와 같은 다른 GraphQL 쿼리를 통해 이 정보를 찾을 수 있습니다.
export function createPR(githubToken: string,
                         repositoryId: string,
                         branch: string): Promise<void> {
  return new Promise<void>(async (resolve, reject) => {
    try {
      const title: string = 'feat: my title';
            const body: string = `# The Pull Request body.

      It supports *Markdown*.`;

     // We want to provide a PR from a branch to master

     const query = `
             mutation CreatePullRequest {
               createPullRequest(input:{baseRefName:"master",body:"${body}",headRefName:"${branch}",repositoryId:"${repositoryId}",title:"${title}"}) {
                 pullRequest {
                   id
                 }
               }
             }
           `;

      const response: Response = 
            await queryGitHub(githubToken, query);

      const result = await response.json();

      if (!result || !result.data || 
          !result.data.createPullRequest || result.errors) {
        resolve(undefined);
        return;
      }

      resolve();
    } catch (err) {
      reject(err);
    }
  });
}

// Demo:

(async () => {
 const token: string = functions.config().github.token;

 await createPR(token, '6789', 'my-branch');
})();

요약
이 기능을 개발하는 과정에서 저는 새로운 것을 많이 배웠습니다. 저는 이 블로그의 도움으로 제 주요 경험을 나눌 수 있기를 바랍니다.
그 밖에 우리는 원본을 만들었습니다. 당신은 언제든지 우리 repo의 원본 코드를 보거나 우리 프로젝트에 공헌할 수 있습니다.
다음 강연에서 DeckDeckGo을 사용해 보시는 것도 환영합니다.
저도 계산을 기대하고 슬라이드 소스 코드가 포함된 GitHub repos를 시도해 보겠습니다.😉.
무한원까지!
다윗
Lukas Blazek의 표지 배경 사진

좋은 웹페이지 즐겨찾기