Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Commit

Permalink
Merge branch 'jaoafa:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
book000 authored Mar 15, 2022
2 parents 57d8d34 + d9880f6 commit c2f67e5
Show file tree
Hide file tree
Showing 35 changed files with 743 additions and 285 deletions.
3 changes: 1 addition & 2 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
@book000
@Hiratake
* @book000 @Hiratake
2 changes: 2 additions & 0 deletions .github/gen-page-preview/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
yarn.lock
129 changes: 129 additions & 0 deletions .github/gen-page-preview/main.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { Octokit } from "@octokit/rest";
import { Page, test } from "@playwright/test";
import { execSync } from "child_process";
import fs from "fs";
import ImgurAnonymousUploader from "imgur-anonymous-uploader";

// 環境変数
// GITHUB_REPOSITORY: 所有者およびリポジトリの名前
// ISSUE_NUMBER: プルリクの番号 (envでgithub.event.numberを渡す必要あり)
// GITHUB_TOKEN: GitHub API トークン (envでsecrets.GITHUB_TOKENを渡す必要あり)
// BASE_SHA: プルリクのベース SHA
// IMGUR_CLIENT_ID: Imgur クライアント ID (オプション)

const OWNER = process.env.GITHUB_REPOSITORY!.split("/")[0];
const REPO = process.env.GITHUB_REPOSITORY!.split("/")[1];
const ISSUE_NUMBER = parseInt(process.env.ISSUE_NUMBER);
const IMGUR_CLIENT_ID = process.env.IMGUR_CLIENT_ID;
const BASE_SHA = process.env.BASE_SHA;

async function scrollFullPage(page: Page) {
await page.evaluate(async () => {
await new Promise<void>((resolve) => {
let totalHeight = 0;
const distance = 100;
const timer = setInterval(() => {
const scrollHeight = document.body.scrollHeight;
window.scrollBy(0, distance);
totalHeight += distance;

if (totalHeight >= scrollHeight) {
clearInterval(timer);
resolve();
}
}, 100);
});
});
}

test("page screenshot", async ({ page }) => {
const files = execSync(
"git diff --diff-filter=ACMR --name-only " + BASE_SHA + " HEAD",
{
cwd: process.env.GITHUB_WORKSPACE + "/content/",
}
)
.toString()
.split("\n");

fs.mkdirSync("screenshots", { recursive: true });

const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
});

const screenshot_files = [];
for (const file of files.filter((s) => s.endsWith(".md"))) {
console.log("File: " + file);
const filename = file.endsWith("index.md")
? file.slice(0, -8)
: file.endsWith(".md")
? file.slice(0, -3)
: file;
const url = "http://localhost:3000/" + filename;
console.log("URL: " + url);
await page.goto(url, { waitUntil: "domcontentloaded" });
await scrollFullPage(page);
await page.evaluate(() => {
window.scrollBy(0, -document.body.scrollHeight);
});
await page.screenshot({
path: "screenshots/" + file + ".png",
fullPage: true,
});
screenshot_files.push(file);
}

const screenshot_urls = [];
if (IMGUR_CLIENT_ID) {
const uploader = new ImgurAnonymousUploader(IMGUR_CLIENT_ID);
for (const screenshot_file of screenshot_files) {
const result = await uploader.upload(
"screenshots/" + screenshot_file + ".png"
);
if (!result.success) {
console.error("Error: " + result);
continue;
}
console.log(
"Imgur uploaded: " + result.url + " (" + result.deleteHash + ")"
);
screenshot_urls.push({
url: result.url,
file: screenshot_file,
});
}
}

const images = screenshot_urls.map(
(o) =>
`<details>\n<summary>${o.file}</summary>\n\n## ${o.file} \n\n![${o.file}](${o.url})\n</details>\n`
);
const comments = await octokit.issues.listComments({
owner: OWNER,
repo: REPO,
issue_number: ISSUE_NUMBER,
per_page: 100,
});
for (const comment of comments.data) {
if (!comment.body.trim().startsWith("# Page Preview Images")) {
continue;
}
await octokit.issues.deleteComment({
owner: OWNER,
repo: REPO,
comment_id: comment.id,
});
console.log("Deleted comment: " + comment.id);
}
await octokit.issues.createComment({
owner: OWNER,
repo: REPO,
issue_number: ISSUE_NUMBER,
body: `
# Page Preview Images
${images.join("\n")}
`,
});
});
8 changes: 8 additions & 0 deletions .github/gen-page-preview/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"dependencies": {
"@octokit/rest": "^18.12.0",
"@playwright/test": "^1.16.3",
"playwright": "^1.16.3",
"imgur-anonymous-uploader": "^1.1.2"
}
}
10 changes: 10 additions & 0 deletions .github/gen-page-preview/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { PlaywrightTestConfig } from "@playwright/test";
const config: PlaywrightTestConfig = {
webServer: {
command: "yarn start",
cwd: process.env.GITHUB_WORKSPACE,
port: 3000,
},
timeout: 0
};
export default config;
3 changes: 3 additions & 0 deletions .github/markdown-header-check/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
yarn.lock
markdown-header.js
177 changes: 177 additions & 0 deletions .github/markdown-header-check/markdown-header.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import axios from "axios";
import * as crypto from "crypto";
import * as fs from "fs";
import * as matter from "gray-matter";
import * as path from "path";
import { exit } from "process";

interface FileDetails {
filePath: string;
fields: {
[key: string]: any;
};
oldFile: string | null;
isChanged: boolean;
}

function getGrayMatter(content: string): {
data: {
[key: string]: any;
};
} {
// @ts-ignore
return matter(content);
}

(async () => {
function getAllFiles(searchPath: string) {
const files = fs.readdirSync(searchPath);
const results: string[] = [];

files.forEach((file) => {
const fullPath = path.normalize(searchPath + "/" + file);
const stat = fs.statSync(fullPath);

if (stat.isDirectory()) {
results.push(...getAllFiles(fullPath));
} else {
results.push(fullPath);
}
});
return results;
}

function getHeaderFields(filePath: string): {
[key: string]: any;
} {
const content = fs.readFileSync(filePath, "utf8");
const { data } = getGrayMatter(content);
return data;
}

async function getHTTPContent(url: string): Promise<any | null> {
try {
const response = await axios.get(url);
if (response.status !== 200) {
return null;
}
return response.data;
} catch (error) {
console.warn(error);
return null;
}
}

const files = getAllFiles(".")
.filter((x) => x.endsWith(".md"))
.filter((x) => !x.includes("node_modules"))
.filter((x) => !x.includes("README.md"));

const requiredFields = {
blog: ["title", "category", "author", "createdAt", "updatedAt"],
other: ["title", "description", "createdAt", "updatedAt"],
};

async function forBlog(
fileDetails: FileDetails,
authors: [{ [key: string]: any }],
categories: [{ [key: string]: any }]
) {
const file = fileDetails.filePath;
const oldFile = fileDetails.oldFile;
const fields = fileDetails.fields;
const isChanged = fileDetails.isChanged;

// 執筆者の確認
const author = fields["author"];
if (authors.findIndex((x) => x.slug === author) === -1) {
console.warn(`${file}: 執筆者が正しくありません (${author})`);
return false;
}

// カテゴリーの確認
const category = fields["category"];
if (categories.findIndex((x) => x.slug === category) === -1) {
console.warn(`${file}: カテゴリが正しくありません (${category})`);
return false;
}

if (oldFile !== null && isChanged) {
const oldFields = getGrayMatter(oldFile).data;
if (oldFields["updatedAt"] == fields["updatedAt"]) {
console.warn(
`${file}: 更新日時が変更されていません (${fields["updatedAt"]})`
);
return false;
}
}

return true;
}

async function forOther(fileDetails: FileDetails) {
const file = fileDetails.filePath;
const oldFile = fileDetails.oldFile;
const fields = fileDetails.fields;
const isChanged = fileDetails.isChanged;

if (oldFile !== null && isChanged) {
const oldFields = getGrayMatter(oldFile).data;
if (oldFields["updatedAt"].toString() == fields["updatedAt"].toString()) {
console.warn(
`${file}: 更新日時が変更されていません (${fields["updatedAt"]})`
);
return false;
}
}
return true;
}

const authors = await getHTTPContent(
"https://raw.githubusercontent.com/jaoafa/jaoweb/master/content/blog/authors.json"
);
const categories = await getHTTPContent(
"https://raw.githubusercontent.com/jaoafa/jaoweb/master/content/blog/categories.json"
);

let isValid = true;
// %f: %m
for (const file of files) {
const fields = getHeaderFields(file);
const fileType = file.includes("blog") ? "blog" : "other";
const requiredFieldsForFile = requiredFields[fileType];
for (const field of requiredFieldsForFile) {
if (!fields[field]) {
console.warn(`${file}: 必要なフィールド ${field} がありません。`);
isValid = false;
}
}

const oldFile = await getHTTPContent(
`https://raw.githubusercontent.com/jaoafa/jaoweb-docs/main/${file.replace(
/\\/g,
"/"
)}`
);
const content = fs.readFileSync(file, "utf8");

const fileDetails: FileDetails = {
filePath: file,
fields: fields,
oldFile: oldFile,
isChanged:
crypto.createHash("sha256").update(oldFile).digest("hex") !=
crypto.createHash("sha256").update(content).digest("hex"),
};

if (fileType == "blog") {
const vaild = await forBlog(fileDetails, authors, categories);
if (!vaild) isValid = false;
} else {
const vaild = await forOther(fileDetails);
if (!vaild) isValid = false;
}
}

process.exitCode = isValid ? 0 : 1;
})();
8 changes: 8 additions & 0 deletions .github/markdown-header-check/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"devDependencies": {
"axios": "^0.24.0",
"gray-matter": "^4.0.3",
"typescript": "^4.4.4",
"@types/node": "^16.11.6"
}
}
16 changes: 16 additions & 0 deletions .github/markdown-header-check/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "ES2018",
"module": "ESNext",
"moduleResolution": "Node",
"lib": ["ESNext", "ESNext.AsyncIterable", "DOM"],
"esModuleInterop": true,
"allowJs": true,
"sourceMap": true,
"strict": true,
"noEmit": true,
"experimentalDecorators": true,
"baseUrl": "."
},
"exclude": ["node_modules"]
}
Loading

0 comments on commit c2f67e5

Please sign in to comment.