This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: GitHub Classroom Workflow | |
on: | |
push: | |
branches: [ main ] | |
paths-ignore: | |
- 'README.md' | |
pull_request: | |
branches: [ main ] | |
workflow_dispatch: | |
env: | |
CARGO_TERM_COLOR: always | |
TZ: Asia/Shanghai # 设置时区 | |
ENCODED_API_URL: "aHR0cHM6Ly9hcGkubGVhcm5pbmdjYW1wLmNuL3dlYi9hcGkvY291cnNlUmFuay9jcmVhdGVCeVRoaXJk" | |
jobs: | |
initialize: | |
name: Initialize Submodules and Check for Experiments | |
runs-on: ubuntu-latest | |
outputs: | |
run_rustlings: ${{ steps.check.outputs.run_rustlings }} | |
run_cxx: ${{ steps.check.outputs.run_cxx }} | |
run_test: ${{ steps.check.outputs.run_test }} | |
api_url: ${{ steps.decode_api_url.outputs.url }} | |
run_project1: ${{ steps.check.outputs.run_project1 }} | |
run_project2: ${{ steps.check.outputs.run_project2 }} | |
steps: | |
- uses: actions/checkout@v3 | |
with: | |
submodules: recursive # 初始化和更新子模块 | |
- name: Decode API URL | |
id: decode_api_url | |
run: | | |
echo "url=$(echo "$ENCODED_API_URL" | base64 --decode)" >> "$GITHUB_OUTPUT" | |
- name: Check for Rustlings, C++ Experiments and Other Submodules | |
id: check | |
run: | | |
if [ -d "rustlings" ]; then | |
echo "::set-output name=run_rustlings::true" | |
else | |
echo "::set-output name=run_rustlings::false" | |
fi | |
if [ -d "learning-cxx" ]; then | |
echo "::set-output name=run_cxx::true" | |
else | |
echo "::set-output name=run_cxx::false" | |
fi | |
if [ -d "exams" ]; then | |
echo "::set-output name=run_test::true" | |
else | |
echo "::set-output name=run_test::false" | |
fi | |
if [ -d "TinyInfiniTensor" ]; then | |
echo "::set-output name=run_project1::true" | |
else | |
echo "::set-output name=run_project1::false" | |
fi | |
if [ -d "learning-lm-rs" ]; then | |
echo "::set-output name=run_project2::true" | |
else | |
echo "::set-output name=run_project2::false" | |
fi | |
rustlings: | |
name: Rustlings Autograding | |
needs: initialize | |
if: ${{ needs.initialize.outputs.run_rustlings == 'true' }} | |
runs-on: ubuntu-latest | |
outputs: | |
details: ${{ steps.autograding.outputs.details }} | |
points: ${{ steps.autograding.outputs.points}} | |
env: | |
API_URL: ${{ needs.initialize.outputs.api_url }} | |
steps: | |
- uses: actions/checkout@v3 | |
with: | |
submodules: 'true' | |
- name: Set up Rust environment | |
uses: actions-rs/toolchain@v1 | |
with: | |
toolchain: stable | |
profile: minimal | |
override: true | |
- name: Install rustlings | |
run: | | |
cargo install --force rustlings | |
rustlings --version | |
- name: Copy exercises directory to project root | |
run: cp -r rustlings/exercises . | |
- name: Run tests for rustlings | |
run: cargo test --test cicv --verbose | |
continue-on-error: true | |
- uses: yfblock/os-autograding@master | |
id: autograding | |
with: | |
outputFile: .github/result/rust_result.json | |
- name: Generate summary JSON for Rustlings | |
run: | | |
outfile=".github/result/rust_result.json" | |
summary_file=".github/result/rustlings_summary.json" | |
# 提取需要的值 | |
total_exercations=$(jq '.statistics.total_exercations' $outfile) | |
total_succeeds=$(jq '.statistics.total_succeeds' $outfile) | |
github_user="${{ github.actor }}" | |
# 生成新的 JSON 内容 | |
new_json=$(jq -n \ | |
--arg channel "github" \ | |
--argjson courseId 1700 \ | |
--arg ext "aaa" \ | |
--arg name "$github_user" \ | |
--argjson score "$total_succeeds" \ | |
--argjson totalScore "$total_exercations" \ | |
'{channel: $channel, courseId: $courseId, ext: $ext, name: $name, score: $score, totalScore: $totalScore}') | |
# 保存新的 JSON 文件 | |
echo "$new_json" > $summary_file | |
# 打印新的 JSON 文件到终端 | |
cat $summary_file | |
- name: Post summary JSON to remote API | |
run: | | |
summary_file=".github/result/rustlings_summary.json" | |
# 发送 POST 请求 | |
curl -X POST "$API_URL" \ | |
-H "accept: application/json;charset=utf-8" \ | |
-H "Content-Type: application/json" \ | |
-d "$(cat $summary_file)" \ | |
-v | |
cxx: | |
name: C++ Autograding | |
needs: initialize | |
if: ${{ needs.initialize.outputs.run_cxx == 'true' }} | |
runs-on: ubuntu-latest | |
outputs: | |
details: ${{ steps.autograding.outputs.details }} | |
points: ${{ steps.autograding.outputs.points}} | |
env: | |
API_URL: ${{ needs.initialize.outputs.api_url }} | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
with: | |
submodules: 'true' | |
- name: Install xmake | |
uses: xmake-io/github-action-setup-xmake@v1 | |
with: | |
xmake-version: latest | |
- name: Run tests and save output | |
run: | | |
(xmake && xmake run summary) | tee test_output.txt | |
working-directory: learning-cxx | |
continue-on-error: true | |
- name: Parse test results and generate JSON for C++ | |
run: | | |
outfile="test_output.txt" | |
summary_file="./../.github/result/cxx_result.json" | |
# 获取最后一行信息并进行处理 | |
info_line=$(tail -n 1 $outfile | sed 's/\x1b\[[0-9;]*m//g') | |
# 提取通过题数和总题目数量 | |
total_succeeds=$(echo $info_line | cut -d' ' -f1 | cut -d'/' -f1) | |
total_exercises=$(echo $info_line | cut -d' ' -f1 | cut -d'/' -f2) | |
# 提取题目状态字符串 | |
status_string=$(echo $info_line | cut -d' ' -f2 | tr -d '[]') | |
# 初始化总失败数 | |
total_failures=$((total_exercises - total_succeeds)) | |
# 遍历状态字符串并更新 JSON 中的 result 字段 | |
for (( i=0; i<${#status_string}; i++ )); do | |
exercise_name=$(printf "exercise%02d" $i) | |
current_char=${status_string:$i:1} | |
if [[ $current_char == "#" ]]; then | |
result="true" | |
else | |
result="false" | |
fi | |
# 直接修改 JSON 文件中的相应字段 | |
jq --arg name "$exercise_name" --arg result "$result" '.exercises |= map(if .name == $name then .result = ($result | test("true")) else . end)' $summary_file > tmp.json && mv tmp.json $summary_file | |
done | |
# 更新统计信息 | |
jq --argjson succeeds "$total_succeeds" --argjson failures "$total_failures" '.statistics.total_succeeds = $succeeds | .statistics.total_failures = $failures' $summary_file > tmp.json && mv tmp.json $summary_file | |
# 打印新的 JSON 文件到终端 | |
cat $summary_file | |
working-directory: learning-cxx | |
continue-on-error: true | |
- uses: yfblock/os-autograding@master | |
id: autograding | |
with: | |
outputFile: .github/result/cxx_result.json | |
- name: Generate summary JSON for CXX | |
run: | | |
outfile=".github/result/cxx_result.json" | |
summary_file=".github/result/cxx_summary.json" | |
# 提取需要的值 | |
total_exercations=$(jq '.statistics.total_exercations' $outfile) | |
total_succeeds=$(jq '.statistics.total_succeeds' $outfile) | |
cxx_user="${{ github.actor }}" | |
# 生成新的 JSON 内容 | |
new_json=$(jq -n \ | |
--arg channel "github" \ | |
--argjson courseId 1699 \ | |
--arg ext "aaa" \ | |
--arg name "$cxx_user" \ | |
--argjson score "$total_succeeds" \ | |
--argjson totalScore "$total_exercations" \ | |
'{channel: $channel, courseId: $courseId, ext: $ext, name: $name, score: $score, totalScore: $totalScore}') | |
# 保存新的 JSON 文件 | |
echo "$new_json" > $summary_file | |
# 打印新的 JSON 文件到终端 | |
cat $summary_file | |
- name: Post summary JSON to remote API | |
run: | | |
summary_file=".github/result/cxx_summary.json" | |
# 发送 POST 请求 | |
curl -X POST "$API_URL" \ | |
-H "accept: application/json;charset=utf-8" \ | |
-H "Content-Type: application/json" \ | |
-d "$(cat $summary_file)" \ | |
-v | |
test: | |
name: Test Job for Other Submodules | |
needs: initialize | |
if: ${{ needs.initialize.outputs.run_test == 'true' }} | |
runs-on: ubuntu-latest | |
env: | |
API_URL: ${{ needs.initialize.outputs.api_url }} | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v3 | |
with: | |
submodules: 'true' | |
- name: Post summary JSON to remote API | |
run: | | |
curl -X POST "$API_URL" \ | |
-H "accept: application/json;charset=utf-8" \ | |
-H "Content-Type: application/json" \ | |
-d '{ | |
"channel": "github", | |
"courseId": 1698, | |
"ext": "aaa", | |
"name": "${{ github.actor }}", | |
"score": 1, | |
"totalScore": 23 | |
}' \ | |
-v | |
TinyInfiniTensor: | |
name: TinyInfiniTensor Autograding | |
needs: initialize | |
if: ${{ needs.initialize.outputs.run_project1 == 'true' }} | |
runs-on: ubuntu-latest | |
outputs: | |
details: ${{ steps.autograding.outputs.details }} | |
points: ${{ steps.autograding.outputs.points}} | |
env: | |
API_URL: ${{ needs.initialize.outputs.api_url }} | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v3 | |
with: | |
submodules: recursive | |
- name: Build TinyInfiniTensor | |
run: | | |
make | |
working-directory: TinyInfiniTensor | |
continue-on-error: false | |
- name: Run tests for TinyInfiniTensor | |
id: run_tests | |
run: | | |
make test-cpp | tee test_output.txt | |
working-directory: TinyInfiniTensor | |
continue-on-error: true | |
- name: Parse test results and generate JSON for TinyInfiniTensor | |
run: | | |
test_output="test_output.txt" | |
json_file="./../.github/result/project1_result.json" | |
# 初始化计数器 | |
total_exercations=0 | |
total_succeeds=0 | |
total_failures=0 | |
exercises_json="[]" | |
# 解析输出 | |
while read -r line; do | |
if [[ "$line" =~ ^[0-9]+/[0-9]+[[:space:]]+Test[[:space:]]+#[0-9]+:[[:space:]]+([a-zA-Z_]+).+(Passed|Failed).*$ ]]; then | |
test_name="${BASH_REMATCH[1]}" | |
test_result="${BASH_REMATCH[2]}" | |
# 计数 | |
total_exercations=$((total_exercations + 1)) | |
if [[ $test_result == "Failed" ]]; then | |
exercises_json=$(echo "$exercises_json" | jq --arg name "$test_name" --argjson result false '. + [{name: $name, result: $result}]') | |
total_failures=$((total_failures + 1)) | |
else | |
exercises_json=$(echo "$exercises_json" | jq --arg name "$test_name" --argjson result true '. + [{name: $name, result: $result}]') | |
total_succeeds=$((total_succeeds + 1)) | |
fi | |
fi | |
done < "$test_output" | |
# 生成最终 JSON | |
final_json=$(jq -n \ | |
--argjson exercises "$exercises_json" \ | |
--arg user_name "null" \ | |
--argjson total_exercations "$total_exercations" \ | |
--argjson total_succeeds "$total_succeeds" \ | |
--argjson total_failures "$total_failures" \ | |
--argjson total_time 3 \ | |
'{exercises: $exercises, user_name: $user_name, statistics: {total_exercations: $total_exercations, total_succeeds: $total_succeeds, total_failures: $total_failures, total_time: $total_time}}') | |
# 保存到 JSON 文件 | |
echo "$final_json" > "$json_file" | |
# 打印新的 JSON 文件到终端 | |
cat $json_file | |
working-directory: TinyInfiniTensor | |
continue-on-error: true | |
- uses: yfblock/os-autograding@master | |
id: autograding | |
with: | |
outputFile: .github/result/project1_result.json | |
- name: Generate summary JSON for TinyInfiniTensor | |
run: | | |
outfile=".github/result/project1_result.json" | |
summary_file=".github/result/project1_summary.json" | |
# 提取需要的值 | |
total_exercations=$(jq '.statistics.total_exercations' $outfile) | |
total_succeeds=$(jq '.statistics.total_succeeds' $outfile) | |
pr1_user="${{ github.actor }}" | |
new_json=$(jq -n \ | |
--arg channel "github" \ | |
--argjson courseId 1701 \ | |
--arg ext "aaa" \ | |
--arg name "$pr1_user" \ | |
--argjson score "$total_succeeds" \ | |
--argjson totalScore "$total_exercations" \ | |
'{channel: $channel, courseId: $courseId, ext: $ext, name: $name, score: $score, totalScore: $totalScore}') | |
echo "$new_json" > $summary_file | |
cat $summary_file | |
- name: Post summary JSON for TinyInfiniTensor | |
run: | | |
summary_file=".github/result/project1_summary.json" | |
curl -X POST "$API_URL" \ | |
-H "accept: application/json;charset=utf-8" \ | |
-H "Content-Type: application/json" \ | |
-d "$(cat $summary_file)" \ | |
-v | |
learning-lm-rs: | |
name: learning-lm-rs Autograding | |
needs: initialize | |
if: ${{ needs.initialize.outputs.run_project2 == 'true' }} | |
runs-on: ubuntu-latest | |
outputs: | |
details: ${{ steps.autograding.outputs.details }} | |
points: ${{ steps.autograding.outputs.points}} | |
env: | |
API_URL: ${{ needs.initialize.outputs.api_url }} | |
steps: | |
- uses: actions/checkout@v3 | |
with: | |
submodules: 'true' | |
- name: Set up Rust environment for learning-lm-rs | |
uses: actions-rs/toolchain@v1 | |
with: | |
toolchain: stable | |
profile: minimal | |
override: true | |
- name: Build learning-lm-rs | |
run: | | |
cargo build --verbose | |
working-directory: learning-lm-rs | |
continue-on-error: false | |
- name: Run tests for learning-lm-rs | |
id: run_tests | |
run: | | |
cargo test --verbose | tee test_output.txt | |
working-directory: learning-lm-rs | |
continue-on-error: true | |
- name: Parse test results and generate JSON for learning-lm-rs | |
run: | | |
test_output="test_output.txt" | |
json_file="./../.github/result/learning-lm-rs_result.json" | |
# 初始化计数器 | |
total_exercations=0 | |
total_succeeds=0 | |
total_failures=0 | |
exercises_json="[" | |
# 从测试输出中提取测试结果 | |
while IFS= read -r line; do | |
if [[ $line =~ test\ (.+)\ \.\.\.\ (FAILED|ok) ]]; then | |
test_name="${BASH_REMATCH[1]}" | |
test_result="${BASH_REMATCH[2]}" | |
# 计数 | |
total_exercations=$((total_exercations + 1)) | |
if [[ $test_result == "FAILED" ]]; then | |
exercises_json+="$(jq -n --arg name "$test_name" --argjson result false '{name: $name, result: $result}')," | |
total_failures=$((total_failures + 1)) | |
else | |
exercises_json+="$(jq -n --arg name "$test_name" --argjson result true '{name: $name, result: $result}')," | |
total_succeeds=$((total_succeeds + 1)) | |
fi | |
fi | |
done < "$test_output" | |
# 去掉最后一个逗号并闭合 JSON 数组 | |
exercises_json="${exercises_json%,}]" | |
# 生成最终 JSON | |
final_json=$(jq -n \ | |
--argjson exercises "$exercises_json" \ | |
--arg user_name "null" \ | |
--argjson total_exercations "$total_exercations" \ | |
--argjson total_succeeds "$total_succeeds" \ | |
--argjson total_failures "$total_failures" \ | |
--argjson total_time 3 \ | |
'{exercises: $exercises, user_name: $user_name, statistics: {total_exercations: $total_exercations, total_succeeds: $total_succeeds, total_failures: $total_failures, total_time: $total_time}}') | |
# 保存到 JSON 文件 | |
echo "$final_json" > "$json_file" | |
# 打印新的 JSON 文件到终端 | |
cat $json_file | |
working-directory: learning-lm-rs | |
continue-on-error: true | |
- uses: yfblock/os-autograding@master | |
id: autograding | |
with: | |
outputFile: .github/result/learning-lm-rs_result.json | |
- name: Generate summary JSON for Project 2 | |
run: | | |
outfile=".github/result/learning-lm-rs_result.json" | |
summary_file=".github/result/learning-lm-rs_summary.json" | |
# 提取需要的值 | |
total_exercations=$(jq '.statistics.total_exercations' $outfile) | |
total_succeeds=$(jq '.statistics.total_succeeds' $outfile) | |
pr2_user="${{ github.actor }}" | |
# 生成新的 JSON 内容 | |
new_json=$(jq -n \ | |
--arg channel "github" \ | |
--argjson courseId 1699 \ | |
--arg ext "aaa" \ | |
--arg name "$pr2_user" \ | |
--argjson score "$total_succeeds" \ | |
--argjson totalScore "$total_exercations" \ | |
'{channel: $channel, courseId: $courseId, ext: $ext, name: $name, score: $score, totalScore: $totalScore}') | |
# 保存新的 JSON 文件 | |
echo "$new_json" > $summary_file | |
# 打印新的 JSON 文件到终端 | |
cat $summary_file | |
- name: Post summary JSON for Project 2 | |
run: | | |
summary_file=".github/result/learning-lm-rs_summary.json" | |
# 发送 POST 请求 | |
curl -X POST "$API_URL" \ | |
-H "accept: application/json;charset=utf-8" \ | |
-H "Content-Type: application/json" \ | |
-d "$(cat $summary_file)" \ | |
-v |