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.

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 소스 코드에서 시작한다.
커스터마이징 목표
{root}/scripts/baekjoon/parsing.js 파일에서 makeDetailMessageAndReadme 함수 내용 수정이 주 목적이다.
폴더 경로 및 파일 이름
const directory = await getDirNameByOrgOption(
`백준/${level.replace(/ .*/, '')}/${problemId}. ${convertSingleCharToDoubleChar(title)}`,
langVersionRemove(language, null)
);const fileName = `${convertSingleCharToDoubleChar(title)}.${languages[language]}`;기본적인 폴더 경로와 파일 이름을 확인할 수 있다. BOJ (Baekjoon Online Judge) 기준 단계별로 풀어보기 위주로 학습할 예정이다. 폴더 경로는 algorithm/{플랫폼}/{프로그래밍 언어}/{단계}로, 그리고 파일 이름은 {문제 번호}.{프로그래밍 언어 관련 파일 확장자}로 지정하려 한다.
커밋 메시지
const message = `[${level}] Title: ${title}, Time: ${runtime} ms, Memory: ${memory} KB`
+ ((isNaN(score)) ? ' ' : `, Score: ${score} point `) // 서브 태스크가 있는 문제로, 점수가 있는 경우 점수까지 커밋 메시지에 표기
+ `-BaekjoonHub`;
);기본적인 커밋 메시지를 확인할 수 있다. 커밋 메시지는 "{문제 제목}" (Memory: {메모리} KB, Time: {실행 시간} ms)로 지정하려 한다. 서브 태스크가 있는 문제의 경우, 뒤에 Score: {획득 점수}를 추가한다.
NO README
// 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
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
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
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와는 다르다.
확장 프로그램 적용 방법
- 구글 크롬에서 우측 상단 케밥 메뉴 아이콘(⋮) 클릭 > 확장 프로그램 > 확장 프로그램 관리
- 우측 상단 개발자 모드 토글 On >
압축해제된 확장 프로그램 로드클릭 > 커스터마이징 완료 루트 폴더 선택 - 우측 상단 확장 프로그램 아이콘(퍼즐 모양) 클릭 > 커스터마이징 완료 프로그램 선택 > GitHub 권한 부여 > GitHub 리포지토리 연동

- Let’s get started.