GitHub: Add Repository Collaborator

GitHub: Add Repository Collaborator

GitHub: リポジトリコラボレーター追加

This item adds a collaborator to a specified repository on GitHub.

Basic Configs
Step Name
Note
Auto Step icon
Configs for this Auto Step
conf_Auth
C1: Personal Access Token (fine-grained) *
conf_RepositoryUrl
C2: Repository’s URL (https://github.com/{owner}/{repository}) *
conf_UserName
C3: Handle of the GitHub user to invite *
conf_Permission
C4: Permission (valid only for an organization’s repository)

Notes

  • To learn about how to create your Personal Access Token (fine-grained), see the GitHub documentation
    • To add a repository collaborator, it is required that the token is granted the following permission: Repository permissions > Administration > Read and Write

Capture

See Also

Script (click to open)
  • An XML file that contains the code below is available to download


const API_VERSION = '2022-11-28';
const API_VERSION_HEADER = 'X-GitHub-Api-Version';

const main = () => {
    ////// == 工程コンフィグ・ワークフローデータの参照 / Config & Data Retrieving ==
    const auth = configs.getObject('conf_Auth');
    const ownerAndRepo = retrieveOwnerAndRepo();
    const handle = retrieveHandle();
    const permission = configs.get("conf_Permission");

    ////// == 演算 / Calculating ==
    checkInvitee(auth, ownerAndRepo, handle);
    checkCollaborator(auth, ownerAndRepo, handle);
    addCollaborator(auth, ownerAndRepo, handle, permission);
}

/**
  * config からリポジトリの URL を読み出し、API に必要な文字列を取り出す
  * @return {String} ownerAndRepo
  */
const retrieveOwnerAndRepo = () => {
    const repositoryUrlDef = configs.getObject('conf_RepositoryUrl');
    let repositoryUrl;
    if (repositoryUrlDef === null) { // 固定値で指定
        repositoryUrl = configs.get('conf_RepositoryUrl');
    } else {
        repositoryUrl = engine.findData(repositoryUrlDef);
    }
    if (repositoryUrl === null) {
        throw new Error("Repository's URL is blank.");
    }

    const regExp = /^https:\/\/github\.com\/([a-zA-Z0-9-_]+\/[a-zA-Z0-9-_]+)$/;
    const found = repositoryUrl.match(regExp);
    if (found === null) {
        throw new Error("Invalid repository URL.");
    }
    const ownerAndRepo = found[1];
    return ownerAndRepo;
}

/**
  * config から招待する GitHub ユーザのハンドル名 を読み出す
  * @return {String} handle
  */
const retrieveHandle = () => {
    const handleDef = configs.getObject('conf_UserName');
    let handle;
    if (handleDef === null) { // 固定値で指定
        handle = configs.get('conf_UserName');
    } else {
        handle = engine.findData(handleDef);
    }
    if (handle === null) {
        throw new Error("handle is blank.");
    }
    return handle;
}

/**
 * ユーザーがリポジトリ招待中であるかどうかを確認する
 * @param {AuthSettingWrapper} auth HTTP 認証設定
 * @param {String} ownerAndRepo {owner}/{repo}
 * @param {String} handle 招待する GitHub ユーザのハンドル名
 */
const checkInvitee = (auth, ownerAndRepo, handle) => {
    const url = `https://api.github.com/repos/${ownerAndRepo}/invitations`;

    const MAX_PER_PAGE = 100;
    const response = httpClient.begin()
        .header(API_VERSION_HEADER, API_VERSION)
        .authSetting(auth)
        .queryParam("per_page", MAX_PER_PAGE.toString())
        .get(url);

    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();

    if (status !== 200) {
        engine.log(responseStr);
        throw new Error(`Failed to get invitee information. status: ${status}`);
    }

    const invitations = JSON.parse(responseStr);

    if (invitations.length === MAX_PER_PAGE) {
       throw new Error("Too many invitations found.");
    }

    if (invitations.some(invitation => invitation.invitee.login === handle)) {
        throw new Error(`${handle} is already invited.`);
    }
};

/**
 * ユーザーがリポジトリコラボレータであるかどうかを確認する
 * @param {AuthSettingWrapper} auth HTTP 認証設定
 * @param {String} ownerAndRepo {owner}/{repo}
 * @param {String} handle 招待する GitHub ユーザのハンドル名
 */
const checkCollaborator = (auth, ownerAndRepo, handle) => {
    const url = `https://api.github.com/repos/${ownerAndRepo}/collaborators/${encodeURIComponent(handle)}`;

    const response = httpClient.begin()
        .header(API_VERSION_HEADER, API_VERSION)
        .authSetting(auth)
        .get(url);

    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status === 404) {
        // success: user is not a collaborator yet
        return;
    }
    if (status === 204) {
        throw new Error(`${handle} is already a collaborator.`);
    }
    engine.log(responseStr);
    throw new Error(`Failed to get a ${handle} information. status: ${status}`);
};

/**
 * リポジトリにコラボレータを追加
 * @param {AuthSettingWrapper} auth HTTP 認証設定
 * @param {String} ownerAndRepo {owner}/{repo}
 * @param {String} handle 招待する GitHub ユーザのハンドル名
 * @param {String} permission 付与する権限
 */
const addCollaborator = (auth, ownerAndRepo, handle, permission) => {
    const url = `https://api.github.com/repos/${ownerAndRepo}/collaborators/${encodeURIComponent(handle)}`;

    const jsonReq = {};
    if (permission !== "") {
        jsonReq["permission"] = permission;
    }

    const response = httpClient.begin()
        .header(API_VERSION_HEADER, API_VERSION)
        .authSetting(auth)
        .body(JSON.stringify(jsonReq), "application/json; charset=UTF-8")
        .put(url);

    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();

    if (status !== 201) {
        // including 204: already a collaborator etc. (permission might have been updated)
        engine.log(responseStr);
        throw new Error(`Failed to add a collaborator. status: ${status}`);
    }
    const jsonRes = JSON.parse(responseStr);
    engine.log(`Succeeded to add a collaborator.`);
    engine.log(`repository.full_name: ${jsonRes.repository.full_name}`);
    engine.log(`invitee.login: ${jsonRes.invitee.login}`);
    engine.log(`permissions: ${jsonRes.permissions}`);
};

Scroll to Top

Discover more from Questetra Support

Subscribe now to keep reading and get access to the full archive.

Continue reading