BaekjoonHub customization (백준허브 커스터마이징)

    Inspired by BaekjoonHub, A Chrome extension that automatically pushes my code to GitHub when I pass all tests on a BOJ (Baekjoon Online Judge) problem. Instead of using BaekjoonHub as is, I made a few modifications of my own.

push automation (251201)

Release 버전 다운로드

Why Not Fork?

    Commits made in a fork will not count toward your contributions. To make them count, you must open a pull request to have your changes merged into the parent repository.

Troubleshooting missing contributions (GitHub Docs)

GitHub 공식 정책에 따르면 기여가 집계되지 않는다. 즉, 잔디밭이 채워지지 않는다.


Start from the v1.2.8

BaekjoonHub Release v1.2.8

본 프로젝트는 BaekjoonHub Release v1.2.8 소스 코드에서 시작한다.

커스터마이징 목표

{root}/scripts/baekjoon/parsing.js 파일에서 makeDetailMessageAndReadme 함수 내용 수정이 주 목적이다.

폴더 경로 및 파일 이름

parsing.js
const directory = await getDirNameByOrgOption(
  `백준/${level.replace(/ .*/, '')}/${problemId}. ${convertSingleCharToDoubleChar(title)}`,
  langVersionRemove(language, null)
);
parsing.js
const fileName = `${convertSingleCharToDoubleChar(title)}.${languages[language]}`;

기본적인 폴더 경로와 파일 이름을 확인할 수 있다. BOJ (Baekjoon Online Judge) 기준 단계별로 풀어보기 위주로 학습할 예정이다. 폴더 경로는 algorithm/{플랫폼}/{프로그래밍 언어}/{단계}로, 그리고 파일 이름은 {문제 번호}.{프로그래밍 언어 관련 파일 확장자}로 지정하려 한다.


커밋 메시지

parsing.js
const message = `[${level}] Title: ${title}, Time: ${runtime} ms, Memory: ${memory} KB`
  + ((isNaN(score)) ? ' ' : `, Score: ${score} point `) // 서브 태스크가 있는 문제로, 점수가 있는 경우 점수까지 커밋 메시지에 표기
  + `-BaekjoonHub`;
);

기본적인 커밋 메시지를 확인할 수 있다. 커밋 메시지는 "{문제 제목}" (Memory: {메모리} KB, Time: {실행 시간} ms)로 지정하려 한다. 서브 태스크가 있는 문제의 경우, 뒤에 Score: {획득 점수}를 추가한다.


NO README

parsing.js
// prettier-ignore-start
const readme = `# [${level}] ${title} - ${problemId} \n\n`
  + `[문제 링크](https://www.acmicpc.net/problem/${problemId}) \n\n`
  + `### 성능 요약\n\n`
  + `메모리: ${memory} KB, `
  + `시간: ${runtime} ms\n\n`
  + `### 분류\n\n`
  + `${category || "Empty"}\n\n` + (!!problem_description ? ''
  + `### 제출 일자\n\n`
  + `${dateInfo}\n\n`
    + `### 문제 설명\n\n${problem_description}\n\n`
    + `### 입력 \n\n ${problem_input}\n\n`
    + `### 출력 \n\n ${problem_output}\n\n` : '');
// prettier-ignore-end

기본적인 README를 확인할 수 있다. 소스 파일만 수집하고 필요한 내용은 Study 탭에 따로 정리할 예정이므로, README는 업로드하지 않으려 한다.

업로드 로직 수정

① parsing.js

parsing.js
async function makeDetailMessageAndReadme(data) {
  const { problemId, result, title, level, code, language, memory, runtime } = data;
  
  // 1. 폴더 경로 및 파일 이름
  const noBlankLanguage = language.replace(/\s/g, '_');
  const noBlankLevel = level.replace(/\s/g, '_');
  const directory = `algorithm/Baekjoon/${noBlankLanguage}/${noBlankLevel}`;
  
  const extension = languages[language];
  const fileName = `${problemId}.${extension}`;

  // 2. 커밋 메시지
  const score = parseNumberFromString(result);
  const message = `"${title}" (Memory: ${memory} KB, Time: ${runtime} ms`
                  + ((isNaN(score)) ? ')' : `, Score: ${score})`);
  
  // 3. README
  const readme = null;

  return {
    directory,
    fileName,
    message,
    readme,
    code
  };
}

폴더 경로에 공백은 취급하지 않는다. README는 그 값이 의도적으로 비어있기 때문에 명시적으로 표현한다.


② uploadfunctions.js

uploadfunctions.js
async function upload(token, hook, sourceText, readmeText, directory, filename, commitMessage, cb) {
  /* 업로드 후 커밋 */
  const git = new GitHub(hook, token);
  const stats = await getStats();
  let default_branch = stats.branches[hook];
  if (isNull(default_branch)) {
    default_branch = await git.getDefaultBranchOnRepo();
    stats.branches[hook] = default_branch;
  }
  const { refSHA, ref } = await git.getReference(default_branch);
  const source = await git.createBlob(sourceText, `${directory}/${filename}`); // 소스코드 파일
  const treeItems = [source];
  let readme = null;
  if (!isNull(readmeText)) {
    readme = await git.createBlob(readmeText, `${directory}/README.md`); // readme 파일
    treeItems.push(readme);
  }
  const treeSHA = await git.createTree(refSHA, treeItems);
  const commitSHA = await git.createCommit(commitMessage, treeSHA, refSHA);
  await git.updateHead(ref, commitSHA);

  /* stats의 값을 갱신합니다. */
  updateObjectDatafromPath(stats.submission, `${hook}/${source.path}`, source.sha);
  if (readme !== null) {
    updateObjectDatafromPath(stats.submission, `${hook}/${readme.path}`, readme.sha);
  }
  await saveStats(stats);
  // 콜백 함수 실행
  if (typeof cb === 'function') {
    cb(stats.branches, directory);
  }
}

소스 코드 및 README가 항상 함께 업로드되는 구조에서 필요시에만 업로드되도록 바꾼다. if (!isNull(readmeText)) {...} 조건문과 if (readme !== null) {...} 조건문 위주로 살펴보면 충분하다.


③ baekjoon.js

baekjoon.js
async function beginUpload(bojData) {
  bojData = preProcessEmptyObj(bojData);
  log('bojData', bojData);
  if (bojData && bojData.code) {
    console.log('Enter the "if statement" of beginUpload function.');
    const stats = await getStats();
    const hook = await getHook();

    const currentVersion = stats.version;
    /* 버전 차이가 발생하거나, 해당 hook에 대한 데이터가 없는 경우 localstorage의 Stats 값을 업데이트하고, version을 최신으로 변경한다 */
    if (isNull(currentVersion) || currentVersion !== getVersion() || isNull(await getStatsSHAfromPath(hook))) {
      await versionUpdate();
    }

    /* 현재 제출하려는 소스코드가 기존 업로드한 내용과 같다면 중지 */
    cachedSHA = await getStatsSHAfromPath(`${hook}/${bojData.directory}/${bojData.fileName}`)
    calcSHA = calculateBlobSHA(bojData.code)
    log('cachedSHA', cachedSHA, 'calcSHA', calcSHA)

    if (cachedSHA == calcSHA) {
      markUploadedCSS(stats.branches, bojData.directory);
      console.log(`현재 제출번호를 업로드한 기록이 있습니다.` /* submissionID ${bojData.submissionId}` */);
      return;
    }
    /* 신규 제출 번호라면 새롭게 커밋  */
    await uploadOneSolveProblemOnGit(bojData, markUploadedCSS);
  }
}

if (bojData && bojData.code) {...} 조건문이 기존에는 if (isNotEmpty(bojData)) {...} 이렇게 되어 있다. ②번까지만 바꾸면 첫 번째 조건문에 진입할 수 없다. 이는 커밋할 수 없다는 뜻이고 정상 작동이 아님에 주의한다.

마이너 이슈

확장 프로그램 세부 정보

{root}/manifest.json 파일에서 해당 확장 프로그램의 몇 가지 세부 정보 내용을 변경할 수 있다.


리포지토리 생성 시 설명

{root}/welcome.js 파일에서 새로운 리포지토리 생성 시 README 설명을 변경할 수 있다.

Warning

해당 README는 리포지토리 관련 설명으로써, 업로드하지 않도록 수정한 문제별 README와는 다르다.

확장 프로그램 적용 방법

  1. 구글 크롬에서 우측 상단 케밥 메뉴 아이콘(⋮) 클릭 > 확장 프로그램 > 확장 프로그램 관리
  2. 우측 상단 개발자 모드 토글 On > 압축해제된 확장 프로그램 로드 클릭 > 커스터마이징 완료 루트 폴더 선택
  3. 우측 상단 확장 프로그램 아이콘(퍼즐 모양) 클릭 > 커스터마이징 완료 프로그램 선택 > GitHub 권한 부여 > GitHub 리포지토리 연동
  4. Let’s get started.