diff --git a/.travis.yml b/.travis.yml index ff9b70cbc2..1bea11af3d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: c script: - - make -s check + - sh ./scripts/check.sh diff --git a/Makefile b/Makefile deleted file mode 100644 index f33b3e3f7d..0000000000 --- a/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -DIR_PATTERN := (news|talk|tech) -NAME_PATTERN := [0-9]{8} [a-zA-Z0-9_.,() -]*\.md - -RULES := rule-source-added \ - rule-translation-requested \ - rule-translation-completed \ - rule-translation-revised \ - rule-translation-published -.PHONY: check match $(RULES) - -CHANGE_FILE := /tmp/changes - -check: $(CHANGE_FILE) - echo 'PR #$(TRAVIS_PULL_REQUEST) Changes:' - cat $(CHANGE_FILE) - echo - echo 'Check for rules...' - make -k $(RULES) 2>/dev/null | grep '^Rule Matched: ' - -$(CHANGE_FILE): - git --no-pager diff $(TRAVIS_BRANCH) origin/master --no-renames --name-status > $@ - -rule-source-added: - echo 'Unmatched Files:' - egrep -v '^A\s*"?sources/$(DIR_PATTERN)/$(NAME_PATTERN)"?' $(CHANGE_FILE) || true - echo '[End of Unmatched Files]' - [ $(shell egrep '^A\s*"?sources/$(DIR_PATTERN)/$(NAME_PATTERN)"?' $(CHANGE_FILE) | wc -l) -ge 1 ] - [ $(shell egrep -v '^A\s*"?sources/$(DIR_PATTERN)/$(NAME_PATTERN)"?' $(CHANGE_FILE) | wc -l) = 0 ] - echo 'Rule Matched: $(@)' - -rule-translation-requested: - [ $(shell egrep '^M\s*"?sources/$(DIR_PATTERN)/$(NAME_PATTERN)"?' $(CHANGE_FILE) | wc -l) = 1 ] - [ $(shell cat $(CHANGE_FILE) | wc -l) = 1 ] - echo 'Rule Matched: $(@)' - -rule-translation-completed: - [ $(shell egrep '^D\s*"?sources/$(DIR_PATTERN)/$(NAME_PATTERN)"?' $(CHANGE_FILE) | wc -l) = 1 ] - [ $(shell egrep '^A\s*"?translated/$(DIR_PATTERN)/$(NAME_PATTERN)"?' $(CHANGE_FILE) | wc -l) = 1 ] - [ $(shell cat $(CHANGE_FILE) | wc -l) = 2 ] - echo 'Rule Matched: $(@)' - -rule-translation-revised: - [ $(shell egrep '^M\s*"?translated/$(DIR_PATTERN)/$(NAME_PATTERN)"?' $(CHANGE_FILE) | wc -l) = 1 ] - [ $(shell cat $(CHANGE_FILE) | wc -l) = 1 ] - echo 'Rule Matched: $(@)' - -rule-translation-published: - [ $(shell egrep '^D\s*"?translated/$(DIR_PATTERN)/$(NAME_PATTERN)"?' $(CHANGE_FILE) | wc -l) = 1 ] - [ $(shell egrep '^A\s*"?published/$(NAME_PATTERN)' $(CHANGE_FILE) | wc -l) = 1 ] - [ $(shell cat $(CHANGE_FILE) | wc -l) = 2 ] - echo 'Rule Matched: $(@)' - -badge: - mkdir -p build/badge - ./lctt-scripts/show_status.sh -s published >build/badge/published.svg - ./lctt-scripts/show_status.sh -s translated >build/badge/translated.svg - ./lctt-scripts/show_status.sh -s translating >build/badge/translating.svg - ./lctt-scripts/show_status.sh -s sources >build/badge/sources.svg diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100644 index 0000000000..539f2426e7 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# PR 检查脚本 +set -e + +CHECK_DIR="$(dirname "$0")/check" +# sh "${CHECK_DIR}/check.sh" # 需要依赖,暂时禁用 +sh "${CHECK_DIR}/info.sh" +sh "${CHECK_DIR}/collect.sh" +sh "${CHECK_DIR}/analyze.sh" +sh "${CHECK_DIR}/identify.sh" diff --git a/scripts/check/analyze.sh b/scripts/check/analyze.sh new file mode 100644 index 0000000000..9aa47d5664 --- /dev/null +++ b/scripts/check/analyze.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# PR 文件变更分析 +set -e + +# 加载公用常量和函数 +# shellcheck source=common.inc.sh +. "$(dirname "$0")/common.inc.sh" + +################################################################################ +# 读入: +# - /tmp/changes # 文件变更列表 +# 写出: +# - /tmp/stats # 文件变更统计 +################################################################################ + +# 执行分析并将统计输出到标准输出 +do_analyze() { + cat /dev/null > /tmp/stats + OTHER_REGEX='^$' + for TYPE in 'SRC' 'TSL' 'PUB'; do + for STAT in 'A' 'M' 'D'; do + # 统计每个类别的每个操作 + REGEX="$(get_operation_regex "$STAT" "$TYPE")" + OTHER_REGEX="${OTHER_REGEX}|${REGEX}" + eval "${TYPE}_${STAT}=\"\$(grep -Ec '$REGEX' /tmp/changes)\"" || true + eval echo "${TYPE}_${STAT}=\$${TYPE}_${STAT}" + done + done + + # 统计其他操作 + OTHER="$(grep -Evc "$OTHER_REGEX" /tmp/changes)" || true + echo "OTHER=$OTHER" + + # 统计变更总数 + TOTAL="$(wc -l < /tmp/changes )" + echo "TOTAL=$TOTAL" +} + + +echo "[分析] 统计文件变更……" +do_analyze > /tmp/stats +echo "[分析] 已写入统计结果:" +cat /tmp/stats diff --git a/scripts/check/check.sh b/scripts/check/check.sh new file mode 100644 index 0000000000..a527c225ab --- /dev/null +++ b/scripts/check/check.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# 检查脚本状态 +set -e + +################################################################################ +# 暂时仅供开发使用 +################################################################################ + +shellcheck -e SC2034 -x mock/stats.sh "$(dirname "$0")"/*.sh \ + && echo '[检查] ShellCheck 通过' diff --git a/scripts/check/collect.sh b/scripts/check/collect.sh new file mode 100644 index 0000000000..db49dacaa6 --- /dev/null +++ b/scripts/check/collect.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# PR 文件变更收集 +set -e + +################################################################################ +# 读入:(无) +# 写出: +# - /tmp/changes # 文件变更列表 +################################################################################ + + +echo "[收集] 计算 PR 分支与目标分支的分叉点……" + +TARGET_BRANCH="${TRAVIS_BRANCH:-master}" +echo "[收集] 目标分支设定为:${TARGET_BRANCH}" + +MERGE_BASE="$(git merge-base "$TARGET_BRANCH" FETCH_HEAD)" +echo "[收集] 找到分叉节点:${MERGE_BASE}" + +{ + git log --oneline "${MERGE_BASE}..FETCH_HEAD" | grep -Eq '^绕过检查:' && { + touch /tmp/bypass + echo "[收集] 已标记为绕过检查项" + } +} || true + +echo "[收集] 写出文件变更列表……" + +git diff "$MERGE_BASE" FETCH_HEAD --no-renames --name-status > /tmp/changes +echo "[收集] 已写出文件变更列表:" +cat /tmp/changes +{ [ -z "$(cat /tmp/changes)" ] && echo "(无变更)"; } || true diff --git a/scripts/check/common.inc.sh b/scripts/check/common.inc.sh new file mode 100644 index 0000000000..6012bc2fe5 --- /dev/null +++ b/scripts/check/common.inc.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +################################################################################ +# 公用常量和函数 +################################################################################ + +# 定义类别目录 +export SRC_DIR='sources' # 未翻译 +export TSL_DIR='translated' # 已翻译 +export PUB_DIR='published' # 已发布 + +# 定义匹配规则 +export CATE_PATTERN='(news|talk|tech)' # 类别 +export FILE_PATTERN='[0-9]{8} [a-zA-Z0-9_.,() -]*\.md' # 文件名 + +# 用法:get_operation_regex 状态 类型 +# +# 状态为: +# - A:添加 +# - M:修改 +# - D:删除 +# 类型为: +# - SRC:未翻译 +# - TSL:已翻译 +# - PUB:已发布 +get_operation_regex() { + STAT="$1" + TYPE="$2" + echo "^${STAT}\\s+\"?$(eval echo "\$${TYPE}_DIR")/" +} diff --git a/scripts/check/identify.sh b/scripts/check/identify.sh new file mode 100644 index 0000000000..195ca4b669 --- /dev/null +++ b/scripts/check/identify.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# 匹配 PR 规则 +set -e + +################################################################################ +# 读入: +# - /tmp/stats +# 写出:(无) +################################################################################ + +# 加载公用常量和函数 +# shellcheck source=common.inc.sh +. "$(dirname "$0")/common.inc.sh" + +echo "[匹配] 加载统计结果……" +# 加载统计结果 +# shellcheck source=mock/stats.sh +. /tmp/stats + +# 定义 PR 规则 + +# 绕过检查:绕过 PR 检查 +rule_bypass_check() { + [ -f /tmp/bypass ] && echo "匹配规则:绕过检查" +} + +# 添加原文:添加至少一篇原文 +rule_source_added() { + [ "$SRC_A" -ge 1 ] \ + && [ "$TOTAL" -eq "$SRC_A" ] && echo "匹配规则:添加原文 ${SRC_A} 篇" +} + +# 申领翻译:只能申领一篇原文 +rule_translation_requested() { + [ "$SRC_M" -eq 1 ] \ + && [ "$TOTAL" -eq 1 ] && echo "匹配规则:申领翻译" +} + +# 提交译文:只能提交一篇译文 +rule_translation_completed() { + [ "$SRC_D" -eq 1 ] && [ "$TSL_A" -eq 1 ] \ + && [ "$TOTAL" -eq 2 ] && echo "匹配规则:提交译文" +} + +# 校对译文:只能校对一篇 +rule_translation_revised() { + [ "$TSL_M" -eq 1 ] \ + && [ "$TOTAL" -eq 1 ] && echo "匹配规则:校对译文" +} + +# 发布译文:发布多篇译文 +rule_translation_published() { + [ "$TSL_D" -ge 1 ] && [ "$PUB_A" -ge 1 ] && [ "$TSL_D" -eq "$PUB_A" ] \ + && [ "$TOTAL" -eq $(("$TSL_D" + "$PUB_A")) ] \ + && echo "匹配规则:发布译文 ${PUB_A} 篇" +} + +# 定义常见错误 + +# 未知错误 +error_undefined() { + echo "未知错误:无匹配规则,请尝试只对一篇文章进行操作" +} + +# 申领多篇 +error_translation_requested_multiple() { + [ "$SRC_M" -gt 1 ] \ + && echo "匹配错误:申领多篇,请一次仅申领一篇" +} + +# 执行检查并输出匹配项目 +do_check() { + rule_bypass_check \ + || rule_source_added \ + || rule_translation_requested \ + || rule_translation_completed \ + || rule_translation_revised \ + || rule_translation_published \ + || { + error_translation_requested_multiple \ + || error_undefined + exit 1 + } +} + +do_check diff --git a/scripts/check/info.sh b/scripts/check/info.sh new file mode 100644 index 0000000000..2e8159fe0c --- /dev/null +++ b/scripts/check/info.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +# 打印 PR 信息 +echo "" diff --git a/scripts/check/mock/stats.sh b/scripts/check/mock/stats.sh new file mode 100644 index 0000000000..966f1b04fd --- /dev/null +++ b/scripts/check/mock/stats.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# 给 ShellCheck 用的 Mock 统计 +SRC_A=0 +SRC_M=0 +SRC_D=0 +TSL_A=0 +TSL_M=0 +TSL_D=0 +PUB_A=0 +PUB_M=0 +PUB_D=0 +OTHER=0 +TOTAL=0