name: Pytest CI Workflow description: Run pytest with coverage and report upload to Gitea inputs: ci-branch: description: 'Target branch where test reports will be committed' required: true module-dir: description: 'Directory (folder) containing the Python module to test' required: true gitea-domain: description: 'Domain name of your Gitea instance without protocol (e.g., gitea.psi.ch)' required: true repo-path: description: 'Relative path to the root of your repository' required: true ci-token: description: ' CI authentication token (used for pushing to Gitea)' required: true enable_pixi_post_link_scripts: description: 'Set to true to enable post-link scripts for Pixi' required: false default: 'false' use_tmux_test: description: "Set to true to enable the tmux-based terminal title test" required: false default: "false" apt_packages: description: "List of system packages to install via apt-get" required: false default: "" custom_shell_commands: description: "Additional shell commands to run" required: false default: "" runs: using: "composite" steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 1 - name: Install optional apt packages if: inputs.apt_packages != '' run: | git clone -q https://gitea.psi.ch/tligui_y/test-ci.git ./temp-ci echo "🛠 Installing packages: ${{ inputs.apt_packages }}" sudo DEBIAN_FRONTEND=noninteractive apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -y ${{ inputs.apt_packages }} shell: bash - name: Install tmux if enabled shell: bash if: inputs.use_tmux_test == 'true' run: | sudo apt-get update sudo apt-get install -y tmux - name: Install Pixi & project dependencies shell: bash run: | OUTPUT_DIR="outputs" LOG_FILE="${OUTPUT_DIR}/pixi.log" mkdir -p "$OUTPUT_DIR" if [ "${{ inputs.enable_pixi_post_link_scripts }}" == "true" ]; then pixi config set --local run-post-link-scripts insecure fi echo "🔧 Installing Pixi..." echo "🔧 Installing Pixi..." >> $LOG_FILE curl -fsSL https://pixi.sh/install.sh | bash >> $LOG_FILE 2>&1 || { echo "❌ Pixi download failed" >> $LOG_FILE echo "Exit Code: 1" >> $LOG_FILE exit 1 } echo "$HOME/.pixi/bin" >> $GITHUB_PATH export PATH="$HOME/.pixi/bin:$PATH" if [ ! -f "pixi.toml" ]; then echo "❌ Missing pixi.toml at repo root: please add one to use Pixi" >> $LOG_FILE echo "Exit Code: 1" >> $LOG_FILE exit 1 fi echo "📦 Installing dependencies from pixi.toml..." >> $LOG_FILE pixi install >> $LOG_FILE 2>&1 || { echo "❌ Pixi project dependencies failed" >> $LOG_FILE echo "Exit Code: 1" >> $LOG_FILE exit 1 } echo "✅ Pixi installed and environment created" echo "✅ Pixi installed and environment created" >> $LOG_FILE pixi list >> $LOG_FILE echo "Exit Code: 0" >> $LOG_FILE - name: Run custom shell commands if: inputs.custom_shell_commands != '' shell: bash run: | OUTPUT_DIR="outputs" LOG_FILE="${OUTPUT_DIR}/custom-shell.log" mkdir -p "$OUTPUT_DIR" echo "📜 Executing custom shell commands..." echo "${{ inputs.custom_shell_commands }}" >> custom_commands.sh chmod +x custom_commands.sh # bash -e ./custom_commands.sh >> $LOG_FILE 2>&1 ./custom_commands.sh >> $LOG_FILE 2>&1 cat $LOG_FILE echo "✅ Custom shell executed" >> $LOG_FILE echo "Exit Code: 0" >> $LOG_FILE # - name: Show custom shell log if failed # if: failure() # cat outputs/custom-shell.log - name: Run tests and generate reports shell: bash run: | OUTPUT_DIR="outputs" LOG_FILE="${OUTPUT_DIR}/test-ci.log" echo "🚀 Running tests and generating reports..." echo "🚀 Running tests and generating reports..." >> $LOG_FILE cp temp-ci/conftest.py ./ PYTEST_ARGS="--continue-on-collection-errors \ --cov=${{ inputs.module-dir }} \ --json-report \ --json-report-file=markdown/pytest-report.json \ --capture=no" set +e if [ "${{ inputs.use_tmux_test }}" = "true" ]; then echo "▶️ Running tests inside tmux session" echo "▶️ Running tests inside tmux session" >> $LOG_FILE tmux new-session -d -s test-session " TMUX_TTY=\$(tmux list-panes -t test-session -F '#{pane_tty}'); export TMUX_TTY; pixi run pytest -s . $PYTEST_ARGS >> outputs/full-output.log 2>&1; tmux wait-for -S done " sleep 1 tmux wait-for done else echo "▶️ Running tests" echo "▶️ Running tests" >> $LOG_FILE pixi run pytest -s . $PYTEST_ARGS >> outputs/full-output.log 2>&1 fi echo "▶️ END" cat outputs/full-output.log awk '/=+ test session starts =+/{flag=1} /-+ JSON report -+/{flag=0} flag' outputs/full-output.log > outputs/raw-test-output.log awk '/=+ short test summary info =+/,/=+.* in .*s =+/' outputs/full-output.log > outputs/short-test-output.log exit_code=$? set -e echo "" >> $LOG_FILE echo "📋 Short test summary:" >> $LOG_FILE cat outputs/short-test-output.log >> $LOG_FILE || true mkdir -p markdown echo "" >> $LOG_FILE echo "📈 Generating coverage summary..." >> $LOG_FILE pixi run coverage report --format=markdown >> markdown/coverage-summary.md 2>> outputs/coverage-error.log && { echo "✅ Coverage summary generated" >> $LOG_FILE } || { echo "❌ Coverage summary generation failed" >> $LOG_FILE echo "🪵 Extracting coverage error:" >> $LOG_FILE cat outputs/coverage-error.log >> $LOG_FILE echo "Exit Code: 1" >> $LOG_FILE exit 1 } if [ -f markdown/runtime_params.log ]; then echo "✅ [INFO] File 'runtime_params.log' found in project root." ls -lh markdown/runtime_params.log echo "Content of runtime_params.log:" cat runtime_params.log else echo "❌ [WARNING] File 'runtime_params.log' NOT FOUND in project root: $(pwd)" echo "Files in current directory:" ls -lh fi echo "" >> $LOG_FILE echo "📊 Generating tests markdown summary..." >> $LOG_FILE pixi run python temp-ci/json_to_md.py \ --input "markdown/pytest-report.json" \ --output "markdown/TEST-REPORT.md" \ --log_long "outputs/raw-test-output.log" \ --log_short "outputs/short-test-output.log" \ --params "markdown/runtime_params.json" \ --exit-code $exit_code >> "outputs/md-report.log" 2>&1 && { echo "✅ Tests markdown generated" >> $LOG_FILE } || { echo "❌ Tests markdown generation failed" >> $LOG_FILE echo "🪵 Extracting markdown generation error:" >> $LOG_FILE cat outputs/md-report.log >> $LOG_FILE echo "Exit Code: 1" >> $LOG_FILE exit 1 } echo "" >> $LOG_FILE echo "🌳 Generating JSON tree view..." >> $LOG_FILE pixi run python temp-ci/json_to_tree.py \ "markdown/pytest-report.json" \ >> "outputs/json-tree-gen.log" 2>&1 && { echo "✅ JSON tree view generated" >> $LOG_FILE } || { echo "❌ JSON tree generation failed" >> $LOG_FILE echo "🪵 Extracting json file error:" >> $LOG_FILE cat outputs/json-tree-gen.log >> $LOG_FILE echo "Exit Code: 1" >> $LOG_FILE exit 1 } rm -rf ./temp-test-ci echo "Exit Code: 0" >> $LOG_FILE - name: Clone Gitea Wiki shell: bash env: CI_TOKEN: ${{ inputs.ci-token }} run: | REPO_NAME="${{ github.repository }}" DOMAIN="${{ inputs.gitea-domain }}" WIKI_REPO="https://ci-token:${CI_TOKEN}@${DOMAIN}/${REPO_NAME}.wiki.git" OUTPUT_DIR="outputs" LOG_FILE="${OUTPUT_DIR}/wiki-clone.log" mkdir -p "$OUTPUT_DIR" echo "📚 Cloning wiki repo: $WIKI_REPO" >> $LOG_FILE git clone "$WIKI_REPO" wiki >> wiki_clone.log 2>&1 && { echo "✅ Wiki cloned to ./wiki" >> $LOG_FILE } || { echo "❌ Wiki clone failed" >> $LOG_FILE cat wiki_clone.log >> $LOG_FILE echo "Exit Code: 1" >> $LOG_FILE exit 1 } SHORT_SHA=$(git rev-parse --short HEAD) REPO_URL="https://${{ inputs.gitea-domain }}/${{ github.repository }}" RUN_LINK="${REPO_URL}/actions/runs/${GITHUB_RUN_NUMBER}" echo "🌳 Generating wiki reports..." >> $LOG_FILE TEST_FILE="run-${GITHUB_RUN_NUMBER}-TEST-commit-${SHORT_SHA}.md" COVERAGE_FILE="run-${GITHUB_RUN_NUMBER}-COVERAGE-commit-${SHORT_SHA}.md" echo "## Test Report" > "wiki/${TEST_FILE}" echo "[View CI Run ${GITHUB_RUN_NUMBER}](${RUN_LINK}) | [Commit ${SHORT_SHA}](${REPO_URL}/commit/${SHORT_SHA})" >> "wiki/${TEST_FILE}" cat markdown/TEST-REPORT.md >> "wiki/${TEST_FILE}" echo "## Coverage Report" > "wiki/${COVERAGE_FILE}" echo "[View CI Run ${GITHUB_RUN_NUMBER}](${RUN_LINK}) | [Commit ${SHORT_SHA}](${REPO_URL}/commit/${SHORT_SHA})" >> "wiki/${COVERAGE_FILE}" cat markdown/coverage-summary.md >> "wiki/${COVERAGE_FILE}" HOME_FILE="wiki/Home.md" TEMP_CONTENT=$(mktemp) { echo "# Test Reports History" echo "" echo "## Latest Run" echo "- [run ${GITHUB_RUN_NUMBER} TEST commit ${SHORT_SHA}](${TEST_FILE})" echo "- [run ${GITHUB_RUN_NUMBER} COVERAGE commit ${SHORT_SHA}](${COVERAGE_FILE})" echo "" echo "## Previous Runs" if [[ -f "${HOME_FILE}" && -s "${HOME_FILE}" ]]; then sed -n '/## Latest Run/{n;p;n;p}' "${HOME_FILE}" sed -n '/## Previous Runs/,$p' "${HOME_FILE}" | tail -n +2 | grep -v "^#" || true fi } > "${TEMP_CONTENT}" 2> reports.log 2>&1 && { echo "✅ Wiki report preparation succeeded" >> $LOG_FILE } || { echo "❌ Wiki report preparation failed" >> $LOG_FILE cat reports.log >> $LOG_FILE echo "Exit Code: 1" >> $LOG_FILE exit 1 } mv "${TEMP_CONTENT}" "${HOME_FILE}" echo "Exit Code: 0" >> $LOG_FILE - name: Commit and push wiki shell: bash env: CI_TOKEN: ${{ inputs.ci-token }} run: | OUTPUT_DIR="$(pwd)/outputs" LOG_FILE="${OUTPUT_DIR}/commit-push-wiki.log" mkdir -p "$OUTPUT_DIR" echo "🚀 Pushing wiki changes..." >> $LOG_FILE cd wiki git config user.name "ci-bot" git config user.email "ci-bot@example.com" git add . if git commit -m "Update wiki with run ${GITHUB_RUN_NUMBER}"; then REPO_NAME="${{ github.repository }}" DOMAIN="${{ inputs.gitea-domain }}" WIKI_REPO="https://ci-token:${CI_TOKEN}@${DOMAIN}/${REPO_NAME}.wiki.git" git push "$WIKI_REPO" HEAD:main >> git.log 2>&1 || { echo "❌ Push failed: Unable to push wiki changes" >> $LOG_FILE cat git.log >> $LOG_FILE echo "Exit Code: 1" >> $LOG_FILE exit 1 } echo "✅ Wiki updated successfully" >> $LOG_FILE else echo "⚠️ No changes to commit" >> $LOG_FILE fi echo "Exit Code: 0" >> $LOG_FILE - name: Log wiki report URLs shell: bash run: | OUTPUT_DIR="outputs" LOG_FILE=""$OUTPUT_DIR"/wiki-report-urls.log" mkdir -p "$OUTPUT_DIR" REPO_URL="https://${{ inputs.gitea-domain }}/${{ github.repository }}" SHORT_SHA=$(git rev-parse --short HEAD) echo "🔗 Wiki Test Report URL:" >> $LOG_FILE echo " ${REPO_URL}/wiki/run-${GITHUB_RUN_NUMBER}-TEST-commit-${SHORT_SHA}.md" >> $LOG_FILE echo "🔗 Wiki Coverage Report URL:" >> $LOG_FILE echo " ${REPO_URL}/wiki/run-${GITHUB_RUN_NUMBER}-COVERAGE-commit-${SHORT_SHA}.md" >> $LOG_FILE echo "Exit Code: 0" >> $LOG_FILE