From 3701681161e1c411a027cd40ce48f92e26ed0402 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 14:58:48 +0700 Subject: [PATCH 01/25] changed workflos --- .gitea/workflows/kibot.yml | 241 +++++++++++++++++++++++ .gitea/workflows/kibot_quick_start.yml | 90 --------- kibot.yaml | 257 +++++++++++++++++++++++++ 3 files changed, 498 insertions(+), 90 deletions(-) create mode 100644 .gitea/workflows/kibot.yml delete mode 100644 .gitea/workflows/kibot_quick_start.yml create mode 100644 kibot.yaml diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml new file mode 100644 index 0000000..8242652 --- /dev/null +++ b/.gitea/workflows/kibot.yml @@ -0,0 +1,241 @@ +name: "KiBot PCB Generation with Multi-layer Support" + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + push: + paths: + - 'kicad/c64psu/*.kicad_sch' + - 'kicad/c64psu/*.kicad_pcb' + - 'kicad/c64psu/*.kicad_pro' + - '.gitea/workflows/kibot_quick_start.yml' + - 'kibot.yaml' + pull_request: + paths: + - 'kicad/c64psu/*.kicad_sch' + - 'kicad/c64psu/*.kicad_pcb' + - 'kicad/c64psu/*.kicad_pro' + - '.gitea/workflows/kibot_quick_start.yml' + - 'kibot.yaml' + repository_dispatch: + types: [run_qs] + workflow_dispatch: + inputs: + board_layers: + description: 'Number of PCB layers (auto-detect if not specified)' + required: false + default: 'auto' + type: choice + options: + - auto + - 2 + - 4 + - 6 + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + generate: + runs-on: kicad-kibot-runner + + steps: + - uses: actions/checkout@v3 + with: + # So we can run a diff between last 2 changes + fetch-depth: '0' + + - name: Detect layer count + id: layers + working-directory: kicad/c64psu + run: | + if [ "${{ github.event.inputs.board_layers }}" != "" ] && [ "${{ github.event.inputs.board_layers }}" != "auto" ]; then + echo "layers=${{ github.event.inputs.board_layers }}" >> $GITHUB_OUTPUT + echo "Using manually specified ${{ github.event.inputs.board_layers }} layer configuration" + else + # Auto-detect layers from PCB file + LAYERS=$(python3 -c " + import re + import sys + import glob + + try: + pcb_files = glob.glob('*.kicad_pcb') + if not pcb_files: + print(2) + sys.exit(0) + + with open(pcb_files[0], 'r') as f: + content = f.read() + # Find all copper layers + copper_layers = re.findall(r'\(layer\s+\"([^\"]+)\".*?\(type\s+\"?copper\"?\)', content, re.DOTALL) + # Count inner layers (In1.Cu, In2.Cu, etc) + inner_layers = [l for l in copper_layers if 'In' in l and '.Cu' in l] + total = 2 + len(inner_layers) # F.Cu + B.Cu + inner layers + print(total) + except Exception as e: + print(f'Error: {e}', file=sys.stderr) + print(2) # Default to 2 layers + ") + echo "layers=$LAYERS" >> $GITHUB_OUTPUT + echo "Auto-detected $LAYERS layer board" + fi + + - name: Check for KiBot config + id: config_check + run: | + if [ -f "kibot.yaml" ]; then + echo "found=true" >> $GITHUB_OUTPUT + echo "Using custom kibot.yaml configuration" + elif [ -f "kicad/c64psu/kibot.yaml" ]; then + echo "found=true" >> $GITHUB_OUTPUT + echo "config_path=kicad/c64psu/kibot.yaml" >> $GITHUB_OUTPUT + echo "Using project-specific kibot.yaml configuration" + else + echo "found=false" >> $GITHUB_OUTPUT + echo "Using --quick-start mode (no custom config found)" + fi + + - name: Run ERC + working-directory: kicad/c64psu + run: | + if [ "${{ steps.config_check.outputs.found }}" == "true" ]; then + CONFIG_PATH="${{ steps.config_check.outputs.config_path }}" + [ -z "$CONFIG_PATH" ] && CONFIG_PATH="../../kibot.yaml" + [ -f *.kicad_sch ] && kibot -c "$CONFIG_PATH" -s run_erc -v || true + fi + continue-on-error: true + + - name: Run DRC + working-directory: kicad/c64psu + run: | + if [ "${{ steps.config_check.outputs.found }}" == "true" ]; then + CONFIG_PATH="${{ steps.config_check.outputs.config_path }}" + [ -z "$CONFIG_PATH" ] && CONFIG_PATH="../../kibot.yaml" + [ -f *.kicad_pcb ] && kibot -c "$CONFIG_PATH" -s run_drc -v || true + fi + continue-on-error: true + + - name: Generate outputs with custom config + if: steps.config_check.outputs.found == 'true' + working-directory: kicad/c64psu + run: | + echo "Generating outputs for ${{ steps.layers.outputs.layers }} layer board" + CONFIG_PATH="${{ steps.config_check.outputs.config_path }}" + [ -z "$CONFIG_PATH" ] && CONFIG_PATH="../../kibot.yaml" + + # Run KiBot with the configuration + kibot -c "$CONFIG_PATH" -d Fabrication -v + + # Move outputs to match the original structure + mkdir -p ../../Generated + if [ -d "Fabrication" ]; then + cp -r Fabrication/* ../../Generated/ + fi + + - name: Quick Start fallback + if: steps.config_check.outputs.found == 'false' + working-directory: kicad/c64psu + run: | + echo "Running KiBot in quick-start mode" + kibot --quick-start + + # Move outputs to expected location + if [ -d "Generated" ]; then + mv Generated ../../ + fi + + - name: Add layer information to outputs + run: | + echo "Board configuration: ${{ steps.layers.outputs.layers }} layers" > Generated/board_info.txt + echo "Project: ${{ steps.find_project.outputs.project_name }}" >> Generated/board_info.txt + echo "Project path: ${{ steps.find_project.outputs.project_dir }}" >> Generated/board_info.txt + echo "Generated on: $(date)" >> Generated/board_info.txt + echo "Commit: ${{ github.sha }}" >> Generated/board_info.txt + + - name: Fix file permissions + run: | + chmod -R a+rw Generated || true + + - name: Retrieve results - All outputs + uses: actions/upload-artifact@v3 + with: + name: Automatic_outputs + path: Generated + + - name: Retrieve results - Gerbers + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: Gerbers-${{ steps.layers.outputs.layers }}layer + path: Generated/Gerbers + + - name: Retrieve results - Assembly + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: Assembly-${{ steps.layers.outputs.layers }}layer + path: Generated/Assembly + + - name: Retrieve results - 3D Model + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: 3D-Model + path: Generated/3D + + - name: Retrieve results - Fabrication Package + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: Fabrication-Package-${{ steps.layers.outputs.layers }}layer + path: Generated/*.zip + + # This step is to upload the results to another repo (web pages) + deploy: + runs-on: kicad-kibot-runner + needs: generate + + steps: + - uses: actions/checkout@v4 + + - name: Retrieve results + uses: actions/download-artifact@v3 + with: + name: Automatic_outputs + path: Generated + + - name: Disable Jekyll + # Jekyll will filter the KiRi folders + run: | + touch Generated/.nojekyll + + - name: Push to docu branch + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + run: | + # Set up git identity + git config --global user.email "${{ gitea.actor }}@noreply.localhost" + git config --global user.name "${{ gitea.actor }}" + + # Get the repository URL and add authentication + REPO_URL=$(git config --get remote.origin.url) + AUTHED_URL=${REPO_URL/https:\/\//https:\/\/${GITEA_TOKEN}@} + + # Clone the docu branch into a separate folder + git clone --depth 1 --branch docu "$AUTHED_URL" docu-branch || \ + git clone --depth 1 "$AUTHED_URL" docu-branch + + cd docu-branch + git checkout -B docu + + # Clean old contents and copy new files + rm -rf ./* + cp -r ../Generated/* ./ + + # Commit and push if there are changes + git add . + if ! git diff --cached --quiet; then + git commit -m "chore: update deployed documentation for ${{ needs.generate.outputs.layers }}-layer board" + git push --force "$AUTHED_URL" docu + else + echo "No changes to deploy" + fi \ No newline at end of file diff --git a/.gitea/workflows/kibot_quick_start.yml b/.gitea/workflows/kibot_quick_start.yml deleted file mode 100644 index 0e3a562..0000000 --- a/.gitea/workflows/kibot_quick_start.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: "Variants demo using --quick-start" - -# Controls when the action will run. Triggers the workflow on push or pull request -# events but only for the master branch -on: - push: - paths: - - 'kicad/c64psu/*.kicad_sch' - - 'kicad/c64psu/*.kicad_pcb' - - '.gitea/workflows/kibot_quick_start.yml' - pull_request: - paths: - - 'kicad/c64psu/*.kicad_sch' - - 'kicad/c64psu/*.kicad_pcb' - - '.gitea/workflows/kibot_quick_start.yml' - repository_dispatch: - types: [run_qs] - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - quick-start: - runs-on: kicad-kibot-runner - - steps: - - - uses: actions/checkout@v3 - with: - # So we can run a diff between last 2 changes - fetch-depth: '0' - - - name: Quick Start - run: | - kibot --quick-start - - - name: Retrieve results - uses: actions/upload-artifact@v3 - with: - name: Automatic_outputs - path: Generated - - # This step is to upload the results to another repo (web pages) - deploy: - runs-on: kicad-kibot-runner - needs: quick-start - - steps: - - uses: actions/checkout@v4 - - - name: Retrieve results - uses: actions/download-artifact@v3 - with: - name: Automatic_outputs - path: Generated - - - name: Disable Jekyll - # Jekyll will filter the KiRi folders - run: | - touch Generated/.nojekyll - - - name: Push to docu branch - env: - GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} - run: | - # Set up git identity - git config --global user.email "${{ gitea.actor }}@noreply.localhost" - git config --global user.name "${{ gitea.actor }}" - - # Get the repository URL and add authentication - REPO_URL=$(git config --get remote.origin.url) - AUTHED_URL=${REPO_URL/https:\/\//https:\/\/${GITEA_TOKEN}@} - - # Clone the docu branch into a separate folder - git clone --depth 1 --branch docu "$AUTHED_URL" docu-branch || \ - git clone --depth 1 "$AUTHED_URL" docu-branch - - cd docu-branch - git checkout -B docu - - # Clean old contents and copy new files - rm -rf ./* - cp -r ../Generated/* ./ - - # Commit and push if there are changes - git add . - if ! git diff --cached --quiet; then - git commit -m "chore: update deployed documentation" - git push --force "$AUTHED_URL" docu - else - echo "No changes to deploy" - fi \ No newline at end of file diff --git a/kibot.yaml b/kibot.yaml new file mode 100644 index 0000000..8931096 --- /dev/null +++ b/kibot.yaml @@ -0,0 +1,257 @@ +# KiBot configuration for 2/4/6 layer boards +# Works with Gitea CI/CD pipeline + +kibot: + version: 1 + +global: + # Filters for ERC/DRC warnings + filters: + - number: 1007 + - number: 1015 + - number: 58 + - number: 61 + + # Default output directory + output: 'Fabrication' + + # Variant for assembly if needed + variant: '' + + # Date format for filenames + date_format: '%Y-%m-%d_%H-%M-%S' + +preflight: + run_erc: true + run_drc: true + check_zone_fills: true + ignore_unconnected: false + update_xml: true + +outputs: + # Schematic outputs + - name: 'print_sch' + comment: "Schematic PDF" + type: pdf_sch_print + dir: Schematic + options: + output: '%f-schematic.%x' + + - name: 'print_sch_svg' + comment: "Schematic SVG" + type: svg_sch_print + dir: Schematic + options: + output: '%f-schematic.%x' + + # PCB 2D renders + - name: 'pcb_top' + comment: "PCB render top" + type: pcbdraw + dir: PCB/2D_render + options: + output: '%f-top.%x' + format: svg + show_components: all + dpi: 300 + + - name: 'pcb_bottom' + comment: "PCB render bottom" + type: pcbdraw + dir: PCB/2D_render + options: + output: '%f-bottom.%x' + format: svg + bottom: true + show_components: all + dpi: 300 + + # PCB PDF documentation + - name: 'print_pcb' + comment: "PCB PDF" + type: pdf_pcb_print + dir: PCB/PDF + options: + output: '%f-pcb.%x' + plot_sheet_reference: true + monochrome: false + pages: + - layers: + - F.Cu + - F.Paste + - F.Silkscreen + - Edge.Cuts + sheet: 'Front copper' + - layers: + - B.Cu + - B.Paste + - B.Silkscreen + - Edge.Cuts + mirror: true + sheet: 'Bottom copper' + - layers: + - In1.Cu + - Edge.Cuts + sheet: 'Inner layer 1' + skip_if_no_layer: true + - layers: + - In2.Cu + - Edge.Cuts + sheet: 'Inner layer 2' + skip_if_no_layer: true + - layers: + - In3.Cu + - Edge.Cuts + sheet: 'Inner layer 3' + skip_if_no_layer: true + - layers: + - In4.Cu + - Edge.Cuts + sheet: 'Inner layer 4' + skip_if_no_layer: true + + # Gerber files - automatically handles 2/4/6 layers + - name: 'gerbers' + comment: "Gerber files" + type: gerber + dir: Gerbers + options: + output: '%f%i.%x' + exclude_edge_layer: true + exclude_pads_from_silkscreen: true + plot_sheet_reference: false + plot_footprint_refs: true + plot_footprint_values: false + force_plot_invisible_refs_vals: false + tent_vias: true + use_protel_extensions: false + create_gerber_job_file: true + disable_aperture_macros: true + gerber_precision: 4.6 + use_gerber_x2_attributes: false + use_gerber_net_attributes: false + line_width: 0.1 + subtract_mask_from_silk: true + layers: + # Copper layers - automatically included if present + - F.Cu + - B.Cu + - In1.Cu + - In2.Cu + - In3.Cu + - In4.Cu + # Technical layers + - F.Paste + - B.Paste + - F.Silkscreen + - B.Silkscreen + - F.Mask + - B.Mask + - Edge.Cuts + - User.Comments + - F.Fab + - B.Fab + + # Excellon drill files + - name: 'drill' + comment: "Drill files" + type: excellon + dir: Gerbers + options: + output: '%f%i.%x' + metric_units: true + minimal_header: false + mirror_y_axis: false + report: + filename: '%f-drill_report.txt' + pth_and_npth_single_file: false + + # Drill map + - name: 'drill_map' + comment: "Drill map" + type: gerb_drill + dir: Gerbers + options: + output: '%f-drill_map.%x' + + # Pick and place files + - name: 'position' + comment: "Pick and place file" + type: position + dir: Assembly + options: + output: '%f-position.%x' + format: CSV + units: millimeters + separate_files_for_front_and_back: true + only_smd: false + + # Interactive BOM + - name: 'ibom' + comment: "Interactive BOM" + type: ibom + dir: Assembly + options: + output: '%f-ibom.%x' + dark_mode: false + hide_pads: false + show_fabrication: false + hide_silkscreen: false + highlight_pin1: true + no_redraw_on_drag: false + board_rotation: 0 + check_extra_fields: false + include_tracks: false + include_nets: false + + # 3D model + - name: 'step' + comment: "3D STEP model" + type: step + dir: 3D + options: + output: '%f-3D.%x' + metric_units: true + origin: grid + no_virtual: true + + # Board characteristics report + - name: 'report' + comment: "Board report" + type: report + dir: . + options: + output: '%f-report.%x' + do_convert: true + + # Fabrication package (ZIP) + - name: 'fabrication' + comment: "Fabrication package" + type: compress + dir: . + options: + output: '%f-fabrication.%x' + format: ZIP + files: + - source: Gerbers/* + dest: / + - source: PCB/PDF/* + dest: /Documentation + - source: Assembly/* + dest: /Assembly + - source: 3D/* + dest: /3D + +# Variants for different layer counts (optional) +variants: + - name: 2layer + comment: "2 layer board variant" + type: kibom + + - name: 4layer + comment: "4 layer board variant" + type: kibom + + - name: 6layer + comment: "6 layer board variant" + type: kibom \ No newline at end of file From 7c67fbfaaade47a71311c0dd55326b6032423a2b Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 15:04:41 +0700 Subject: [PATCH 02/25] '' --- .gitea/workflows/kibot.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index 8242652..d23c596 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -197,6 +197,7 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Retrieve results uses: actions/download-artifact@v3 with: From 71bc0683cad39e1c5fa09aaeadf0eb85e4b29503 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 15:07:38 +0700 Subject: [PATCH 03/25] 'workflow' --- .gitea/workflows/kibot.yml | 176 ++++++++++++++++++++++++------------- 1 file changed, 116 insertions(+), 60 deletions(-) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index d23c596..7e7acc5 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -5,18 +5,20 @@ name: "KiBot PCB Generation with Multi-layer Support" on: push: paths: - - 'kicad/c64psu/*.kicad_sch' - - 'kicad/c64psu/*.kicad_pcb' - - 'kicad/c64psu/*.kicad_pro' - - '.gitea/workflows/kibot_quick_start.yml' - - 'kibot.yaml' + - '**/*.kicad_sch' + - '**/*.kicad_pcb' + - '**/*.kicad_pro' + - '**/*.kibot.yaml' + - '**/kibot.yaml' + - '.gitea/workflows/*.yml' pull_request: paths: - - 'kicad/c64psu/*.kicad_sch' - - 'kicad/c64psu/*.kicad_pcb' - - 'kicad/c64psu/*.kicad_pro' - - '.gitea/workflows/kibot_quick_start.yml' - - 'kibot.yaml' + - '**/*.kicad_sch' + - '**/*.kicad_pcb' + - '**/*.kicad_pro' + - '**/*.kibot.yaml' + - '**/kibot.yaml' + - '.gitea/workflows/*.yml' repository_dispatch: types: [run_qs] workflow_dispatch: @@ -43,38 +45,68 @@ jobs: # So we can run a diff between last 2 changes fetch-depth: '0' + - name: Find KiCad project + id: find_project + run: | + # Find all .kicad_pcb files in the repository + PCB_FILES=$(find . -name "*.kicad_pcb" -type f | grep -v -E '/(backups?|backup|old|archive|deprecated)/' | head -5) + + if [ -z "$PCB_FILES" ]; then + echo "Error: No KiCad PCB files found" + exit 1 + fi + + # Use the first PCB file found + PCB_FILE=$(echo "$PCB_FILES" | head -1) + PROJECT_DIR=$(dirname "$PCB_FILE") + PROJECT_NAME=$(basename "$PCB_FILE" .kicad_pcb) + + echo "Found KiCad project: $PROJECT_NAME" + echo "Project directory: $PROJECT_DIR" + echo "PCB file: $PCB_FILE" + + # Find schematic file + SCH_FILE=$(find "$PROJECT_DIR" -name "*.kicad_sch" -o -name "*.sch" | head -1) + + echo "pcb_file=$PCB_FILE" >> $GITHUB_OUTPUT + echo "project_dir=$PROJECT_DIR" >> $GITHUB_OUTPUT + echo "project_name=$PROJECT_NAME" >> $GITHUB_OUTPUT + echo "sch_file=$SCH_FILE" >> $GITHUB_OUTPUT + + # List all found projects for debugging + echo "All PCB files found:" + echo "$PCB_FILES" + - name: Detect layer count id: layers - working-directory: kicad/c64psu run: | + PCB_FILE="${{ steps.find_project.outputs.pcb_file }}" if [ "${{ github.event.inputs.board_layers }}" != "" ] && [ "${{ github.event.inputs.board_layers }}" != "auto" ]; then echo "layers=${{ github.event.inputs.board_layers }}" >> $GITHUB_OUTPUT echo "Using manually specified ${{ github.event.inputs.board_layers }} layer configuration" else - # Auto-detect layers from PCB file - LAYERS=$(python3 -c " - import re - import sys - import glob + # Create Python script to detect layers + cat > /tmp/detect_layers.py << 'PYTHON_SCRIPT' +#!/usr/bin/env python3 +import re +import sys + +try: + pcb_file = sys.argv[1] + with open(pcb_file, 'r') as f: + content = f.read() + # Find all copper layers + copper_layers = re.findall(r'\(layer\s+"([^"]+)".*?\(type\s+"?copper"?\)', content, re.DOTALL) + # Count inner layers (In1.Cu, In2.Cu, etc) + inner_layers = [l for l in copper_layers if 'In' in l and '.Cu' in l] + total = 2 + len(inner_layers) # F.Cu + B.Cu + inner layers + print(total) +except Exception as e: + print(2) # Default to 2 layers +PYTHON_SCRIPT - try: - pcb_files = glob.glob('*.kicad_pcb') - if not pcb_files: - print(2) - sys.exit(0) - - with open(pcb_files[0], 'r') as f: - content = f.read() - # Find all copper layers - copper_layers = re.findall(r'\(layer\s+\"([^\"]+)\".*?\(type\s+\"?copper\"?\)', content, re.DOTALL) - # Count inner layers (In1.Cu, In2.Cu, etc) - inner_layers = [l for l in copper_layers if 'In' in l and '.Cu' in l] - total = 2 + len(inner_layers) # F.Cu + B.Cu + inner layers - print(total) - except Exception as e: - print(f'Error: {e}', file=sys.stderr) - print(2) # Default to 2 layers - ") + chmod +x /tmp/detect_layers.py + LAYERS=$(/tmp/detect_layers.py "$PCB_FILE") echo "layers=$LAYERS" >> $GITHUB_OUTPUT echo "Auto-detected $LAYERS layer board" fi @@ -82,66 +114,91 @@ jobs: - name: Check for KiBot config id: config_check run: | - if [ -f "kibot.yaml" ]; then + PROJECT_DIR="${{ steps.find_project.outputs.project_dir }}" + + # Search for kibot config in multiple locations + if [ -f "$PROJECT_DIR/kibot.yaml" ]; then echo "found=true" >> $GITHUB_OUTPUT - echo "Using custom kibot.yaml configuration" - elif [ -f "kicad/c64psu/kibot.yaml" ]; then + echo "config_path=$PROJECT_DIR/kibot.yaml" >> $GITHUB_OUTPUT + echo "Using project-specific kibot.yaml" + elif [ -f "$PROJECT_DIR/../kibot.yaml" ]; then echo "found=true" >> $GITHUB_OUTPUT - echo "config_path=kicad/c64psu/kibot.yaml" >> $GITHUB_OUTPUT - echo "Using project-specific kibot.yaml configuration" + echo "config_path=$PROJECT_DIR/../kibot.yaml" >> $GITHUB_OUTPUT + echo "Using parent directory kibot.yaml" + elif [ -f "kibot.yaml" ]; then + echo "found=true" >> $GITHUB_OUTPUT + echo "config_path=kibot.yaml" >> $GITHUB_OUTPUT + echo "Using root kibot.yaml" + elif [ -f ".kibot/config.yaml" ]; then + echo "found=true" >> $GITHUB_OUTPUT + echo "config_path=.kibot/config.yaml" >> $GITHUB_OUTPUT + echo "Using .kibot/config.yaml" else echo "found=false" >> $GITHUB_OUTPUT echo "Using --quick-start mode (no custom config found)" fi - name: Run ERC - working-directory: kicad/c64psu + if: steps.config_check.outputs.found == 'true' run: | - if [ "${{ steps.config_check.outputs.found }}" == "true" ]; then - CONFIG_PATH="${{ steps.config_check.outputs.config_path }}" - [ -z "$CONFIG_PATH" ] && CONFIG_PATH="../../kibot.yaml" - [ -f *.kicad_sch ] && kibot -c "$CONFIG_PATH" -s run_erc -v || true + cd "${{ steps.find_project.outputs.project_dir }}" + CONFIG_PATH="../${{ steps.config_check.outputs.config_path }}" + CONFIG_PATH=$(realpath "$CONFIG_PATH") + + if [ -n "${{ steps.find_project.outputs.sch_file }}" ]; then + echo "Running ERC on schematic files..." + kibot -c "$CONFIG_PATH" -s run_erc -v || true + else + echo "No schematic files found, skipping ERC" fi continue-on-error: true - name: Run DRC - working-directory: kicad/c64psu + if: steps.config_check.outputs.found == 'true' run: | - if [ "${{ steps.config_check.outputs.found }}" == "true" ]; then - CONFIG_PATH="${{ steps.config_check.outputs.config_path }}" - [ -z "$CONFIG_PATH" ] && CONFIG_PATH="../../kibot.yaml" - [ -f *.kicad_pcb ] && kibot -c "$CONFIG_PATH" -s run_drc -v || true - fi + cd "${{ steps.find_project.outputs.project_dir }}" + CONFIG_PATH="../${{ steps.config_check.outputs.config_path }}" + CONFIG_PATH=$(realpath "$CONFIG_PATH") + + echo "Running DRC on PCB..." + kibot -c "$CONFIG_PATH" -s run_drc -v || true continue-on-error: true - name: Generate outputs with custom config if: steps.config_check.outputs.found == 'true' - working-directory: kicad/c64psu run: | + cd "${{ steps.find_project.outputs.project_dir }}" + CONFIG_PATH="../${{ steps.config_check.outputs.config_path }}" + CONFIG_PATH=$(realpath "$CONFIG_PATH") + echo "Generating outputs for ${{ steps.layers.outputs.layers }} layer board" - CONFIG_PATH="${{ steps.config_check.outputs.config_path }}" - [ -z "$CONFIG_PATH" ] && CONFIG_PATH="../../kibot.yaml" + echo "Project: ${{ steps.find_project.outputs.project_name }}" # Run KiBot with the configuration kibot -c "$CONFIG_PATH" -d Fabrication -v - # Move outputs to match the original structure - mkdir -p ../../Generated + # Move outputs to root Generated folder + mkdir -p ../Generated if [ -d "Fabrication" ]; then - cp -r Fabrication/* ../../Generated/ + cp -r Fabrication/* ../Generated/ fi + cd .. - name: Quick Start fallback if: steps.config_check.outputs.found == 'false' - working-directory: kicad/c64psu run: | + cd "${{ steps.find_project.outputs.project_dir }}" echo "Running KiBot in quick-start mode" kibot --quick-start - # Move outputs to expected location + # Move outputs to root Generated folder if [ -d "Generated" ]; then - mv Generated ../../ + mv Generated ../ + else + mkdir -p ../Generated + echo "No Generated folder found after quick-start" fi + cd .. - name: Add layer information to outputs run: | @@ -197,7 +254,6 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Retrieve results uses: actions/download-artifact@v3 with: From aba4abc8917e24c76ff4101a8191328f2b2897ff Mon Sep 17 00:00:00 2001 From: Alexander Grabowski Date: Mon, 8 Sep 2025 15:09:10 +0700 Subject: [PATCH 04/25] Update .gitea/workflows/kibot.yml --- .gitea/workflows/kibot.yml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index 7e7acc5..8277ee8 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -87,26 +87,26 @@ jobs: else # Create Python script to detect layers cat > /tmp/detect_layers.py << 'PYTHON_SCRIPT' -#!/usr/bin/env python3 -import re -import sys - -try: - pcb_file = sys.argv[1] - with open(pcb_file, 'r') as f: - content = f.read() - # Find all copper layers - copper_layers = re.findall(r'\(layer\s+"([^"]+)".*?\(type\s+"?copper"?\)', content, re.DOTALL) - # Count inner layers (In1.Cu, In2.Cu, etc) - inner_layers = [l for l in copper_layers if 'In' in l and '.Cu' in l] - total = 2 + len(inner_layers) # F.Cu + B.Cu + inner layers - print(total) -except Exception as e: - print(2) # Default to 2 layers -PYTHON_SCRIPT + #!/usr/bin/env python3 + import re + import sys + + try: + pcb_file = sys.argv[1] + with open(pcb_file, 'r') as f: + content = f.read() + # Find all copper layers + copper_layers = re.findall(r'\(layer\s+"([^"]+)".*?\(type\s+"?copper"?\)', content, re.DOTALL) + # Count inner layers (In1.Cu, In2.Cu, etc) + inner_layers = [l for l in copper_layers if 'In' in l and '.Cu' in l] + total = 2 + len(inner_layers) # F.Cu + B.Cu + inner layers + print(total) + except Exception as e: + print(2) # Default to 2 layers + PYTHON_SCRIPT chmod +x /tmp/detect_layers.py - LAYERS=$(/tmp/detect_layers.py "$PCB_FILE") + LAYERS=$(python3 /tmp/detect_layers.py "$PCB_FILE") echo "layers=$LAYERS" >> $GITHUB_OUTPUT echo "Auto-detected $LAYERS layer board" fi From fbe8a5920444460e3fc64559271f9df141185811 Mon Sep 17 00:00:00 2001 From: Alexander Grabowski Date: Mon, 8 Sep 2025 15:12:12 +0700 Subject: [PATCH 05/25] Update .gitea/workflows/kibot.yml --- .gitea/workflows/kibot.yml | 47 ++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index 8277ee8..44dcb78 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -95,20 +95,53 @@ jobs: pcb_file = sys.argv[1] with open(pcb_file, 'r') as f: content = f.read() - # Find all copper layers - copper_layers = re.findall(r'\(layer\s+"([^"]+)".*?\(type\s+"?copper"?\)', content, re.DOTALL) - # Count inner layers (In1.Cu, In2.Cu, etc) - inner_layers = [l for l in copper_layers if 'In' in l and '.Cu' in l] - total = 2 + len(inner_layers) # F.Cu + B.Cu + inner layers - print(total) + + # Look for the layers section in the KiCad PCB file + # Format: (layers (0 "F.Cu" signal) (4 "In1.Cu" signal) ...) + layers_match = re.search(r'\(layers[^)]*\)', content, re.DOTALL) + + if layers_match: + layers_text = layers_match.group(0) + # Find all layer definitions + layer_matches = re.findall(r'\(\d+\s+"([^"]+)"\s+\w+\)', layers_text) + + # Count copper layers + copper_count = 0 + for layer in layer_matches: + if '.Cu' in layer: + copper_count += 1 + print(f"Found copper layer: {layer}", file=sys.stderr) + + if copper_count > 0: + print(copper_count) + else: + # Fallback: count F.Cu, B.Cu, and In*.Cu layers + copper_layers = [l for l in layer_matches if l in ['F.Cu', 'B.Cu'] or (l.startswith('In') and l.endswith('.Cu'))] + print(len(copper_layers) if copper_layers else 2) + else: + # Old format fallback + copper_layers = re.findall(r'layer\s+"([^"]+)".*?type\s+"?copper"?', content, re.DOTALL) + if not copper_layers: + copper_layers = re.findall(r'\(layer\s+([FB]\.Cu|In\d+\.Cu)\)', content) + + total = len(set(copper_layers)) if copper_layers else 2 + print(total) + except Exception as e: + print(f"Error detecting layers: {e}", file=sys.stderr) print(2) # Default to 2 layers PYTHON_SCRIPT chmod +x /tmp/detect_layers.py - LAYERS=$(python3 /tmp/detect_layers.py "$PCB_FILE") + LAYERS=$(python3 /tmp/detect_layers.py "$PCB_FILE" 2>/dev/null) + if [ -z "$LAYERS" ]; then + LAYERS=2 + fi echo "layers=$LAYERS" >> $GITHUB_OUTPUT echo "Auto-detected $LAYERS layer board" + + # Debug: show what was detected + python3 /tmp/detect_layers.py "$PCB_FILE" 2>&1 | grep "Found copper layer" || true fi - name: Check for KiBot config From 2cb715d2c55ddc71fbdabd37b6acd8b0ead82538 Mon Sep 17 00:00:00 2001 From: Alexander Grabowski Date: Mon, 8 Sep 2025 15:13:53 +0700 Subject: [PATCH 06/25] Update .gitea/workflows/kibot.yml --- .gitea/workflows/kibot.yml | 54 +++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index 44dcb78..b2f31b3 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -98,34 +98,35 @@ jobs: # Look for the layers section in the KiCad PCB file # Format: (layers (0 "F.Cu" signal) (4 "In1.Cu" signal) ...) - layers_match = re.search(r'\(layers[^)]*\)', content, re.DOTALL) + layers_section = re.search(r'\(layers\s*\n([^)]+)\)', content, re.MULTILINE | re.DOTALL) - if layers_match: - layers_text = layers_match.group(0) - # Find all layer definitions + if layers_section: + layers_text = layers_section.group(0) + # Find all layer definitions - match the pattern (number "name" type) layer_matches = re.findall(r'\(\d+\s+"([^"]+)"\s+\w+\)', layers_text) - # Count copper layers - copper_count = 0 + # Count copper layers - any layer with .Cu suffix + copper_layers = [] for layer in layer_matches: if '.Cu' in layer: - copper_count += 1 + copper_layers.append(layer) print(f"Found copper layer: {layer}", file=sys.stderr) - if copper_count > 0: - print(copper_count) - else: - # Fallback: count F.Cu, B.Cu, and In*.Cu layers - copper_layers = [l for l in layer_matches if l in ['F.Cu', 'B.Cu'] or (l.startswith('In') and l.endswith('.Cu'))] - print(len(copper_layers) if copper_layers else 2) + print(len(copper_layers) if copper_layers else 2) else: - # Old format fallback - copper_layers = re.findall(r'layer\s+"([^"]+)".*?type\s+"?copper"?', content, re.DOTALL) - if not copper_layers: - copper_layers = re.findall(r'\(layer\s+([FB]\.Cu|In\d+\.Cu)\)', content) + # Alternative: look for individual layer definitions + all_layers = re.findall(r'\(\d+\s+"([^"]+\.Cu)"\s+\w+\)', content) + if not all_layers: + # Try another pattern + all_layers = re.findall(r'"(F\.Cu|B\.Cu|In\d+\.Cu)"', content) - total = len(set(copper_layers)) if copper_layers else 2 - print(total) + # Remove duplicates + unique_layers = list(set(all_layers)) + + for layer in unique_layers: + print(f"Found copper layer: {layer}", file=sys.stderr) + + print(len(unique_layers) if unique_layers else 2) except Exception as e: print(f"Error detecting layers: {e}", file=sys.stderr) @@ -133,15 +134,20 @@ jobs: PYTHON_SCRIPT chmod +x /tmp/detect_layers.py - LAYERS=$(python3 /tmp/detect_layers.py "$PCB_FILE" 2>/dev/null) - if [ -z "$LAYERS" ]; then + + # Run the detection and capture both stdout and stderr + LAYERS=$(python3 /tmp/detect_layers.py "$PCB_FILE" 2>&1 | tee /tmp/layer_detection.log | grep -v "Found copper layer" | tail -1) + + # Show debug output + grep "Found copper layer" /tmp/layer_detection.log || true + + if [ -z "$LAYERS" ] || [ "$LAYERS" = "0" ]; then LAYERS=2 + echo "Failed to detect layers, defaulting to 2" fi + echo "layers=$LAYERS" >> $GITHUB_OUTPUT echo "Auto-detected $LAYERS layer board" - - # Debug: show what was detected - python3 /tmp/detect_layers.py "$PCB_FILE" 2>&1 | grep "Found copper layer" || true fi - name: Check for KiBot config From 63efbeab0069662d675315cb933cb15724a53496 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 15:15:49 +0700 Subject: [PATCH 07/25] moved kibot --- kibot.yaml => kicad/kibot.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename kibot.yaml => kicad/kibot.yaml (100%) diff --git a/kibot.yaml b/kicad/kibot.yaml similarity index 100% rename from kibot.yaml rename to kicad/kibot.yaml From 1eb02a7ffb5c605880d57ed333f0146f8958d387 Mon Sep 17 00:00:00 2001 From: Alexander Grabowski Date: Mon, 8 Sep 2025 15:18:15 +0700 Subject: [PATCH 08/25] Update .gitea/workflows/kibot.yml --- .gitea/workflows/kibot.yml | 53 ++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index b2f31b3..d6fd1ea 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -181,11 +181,23 @@ jobs: if: steps.config_check.outputs.found == 'true' run: | cd "${{ steps.find_project.outputs.project_dir }}" - CONFIG_PATH="../${{ steps.config_check.outputs.config_path }}" - CONFIG_PATH=$(realpath "$CONFIG_PATH") + CONFIG_PATH="${{ steps.config_check.outputs.config_path }}" + + # Convert to absolute path if needed + if [[ "$CONFIG_PATH" != /* ]]; then + CONFIG_PATH="$(pwd)/$CONFIG_PATH" + fi + + # Normalize the path + CONFIG_PATH=$(readlink -f "$CONFIG_PATH" 2>/dev/null || realpath "$CONFIG_PATH" 2>/dev/null || echo "$CONFIG_PATH") + + if [ ! -f "$CONFIG_PATH" ]; then + echo "Warning: Config file not found at $CONFIG_PATH" + exit 0 + fi if [ -n "${{ steps.find_project.outputs.sch_file }}" ]; then - echo "Running ERC on schematic files..." + echo "Running ERC with config: $CONFIG_PATH" kibot -c "$CONFIG_PATH" -s run_erc -v || true else echo "No schematic files found, skipping ERC" @@ -196,10 +208,22 @@ jobs: if: steps.config_check.outputs.found == 'true' run: | cd "${{ steps.find_project.outputs.project_dir }}" - CONFIG_PATH="../${{ steps.config_check.outputs.config_path }}" - CONFIG_PATH=$(realpath "$CONFIG_PATH") + CONFIG_PATH="${{ steps.config_check.outputs.config_path }}" - echo "Running DRC on PCB..." + # Convert to absolute path if needed + if [[ "$CONFIG_PATH" != /* ]]; then + CONFIG_PATH="$(pwd)/$CONFIG_PATH" + fi + + # Normalize the path + CONFIG_PATH=$(readlink -f "$CONFIG_PATH" 2>/dev/null || realpath "$CONFIG_PATH" 2>/dev/null || echo "$CONFIG_PATH") + + if [ ! -f "$CONFIG_PATH" ]; then + echo "Warning: Config file not found at $CONFIG_PATH" + exit 0 + fi + + echo "Running DRC with config: $CONFIG_PATH" kibot -c "$CONFIG_PATH" -s run_drc -v || true continue-on-error: true @@ -207,11 +231,24 @@ jobs: if: steps.config_check.outputs.found == 'true' run: | cd "${{ steps.find_project.outputs.project_dir }}" - CONFIG_PATH="../${{ steps.config_check.outputs.config_path }}" - CONFIG_PATH=$(realpath "$CONFIG_PATH") + CONFIG_PATH="${{ steps.config_check.outputs.config_path }}" + + # Convert to absolute path if needed + if [[ "$CONFIG_PATH" != /* ]]; then + CONFIG_PATH="$(pwd)/$CONFIG_PATH" + fi + + # Normalize the path + CONFIG_PATH=$(readlink -f "$CONFIG_PATH" 2>/dev/null || realpath "$CONFIG_PATH" 2>/dev/null || echo "$CONFIG_PATH") + + if [ ! -f "$CONFIG_PATH" ]; then + echo "Error: Config file not found at $CONFIG_PATH" + exit 1 + fi echo "Generating outputs for ${{ steps.layers.outputs.layers }} layer board" echo "Project: ${{ steps.find_project.outputs.project_name }}" + echo "Using config: $CONFIG_PATH" # Run KiBot with the configuration kibot -c "$CONFIG_PATH" -d Fabrication -v From c06e81ae0f50c800d837b505ff2d5991c405b7e0 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 15:21:51 +0700 Subject: [PATCH 09/25] simplified workflow --- .gitea/workflows/kibot.yml | 197 ++++++--------------------------- kicad/kibot.yaml => kibot.yaml | 0 2 files changed, 36 insertions(+), 161 deletions(-) rename kicad/kibot.yaml => kibot.yaml (100%) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index d6fd1ea..e47872f 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -1,38 +1,34 @@ -name: "KiBot PCB Generation with Multi-layer Support" +name: "KiBot PCB Generation" -# Controls when the action will run. Triggers the workflow on push or pull request -# events but only for the master branch +# Controls when the action will run on: push: paths: - '**/*.kicad_sch' - '**/*.kicad_pcb' - '**/*.kicad_pro' - - '**/*.kibot.yaml' - - '**/kibot.yaml' + - 'kibot.yaml' - '.gitea/workflows/*.yml' pull_request: paths: - '**/*.kicad_sch' - '**/*.kicad_pcb' - '**/*.kicad_pro' - - '**/*.kibot.yaml' - - '**/kibot.yaml' + - 'kibot.yaml' - '.gitea/workflows/*.yml' repository_dispatch: types: [run_qs] workflow_dispatch: inputs: board_layers: - description: 'Number of PCB layers (auto-detect if not specified)' + description: 'Number of PCB layers' required: false - default: 'auto' + default: '2' type: choice options: - - auto - - 2 - - 4 - - 6 + - '2' + - '4' + - '6' # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: @@ -77,128 +73,38 @@ jobs: echo "All PCB files found:" echo "$PCB_FILES" - - name: Detect layer count + - name: Set layer count id: layers run: | - PCB_FILE="${{ steps.find_project.outputs.pcb_file }}" - if [ "${{ github.event.inputs.board_layers }}" != "" ] && [ "${{ github.event.inputs.board_layers }}" != "auto" ]; then - echo "layers=${{ github.event.inputs.board_layers }}" >> $GITHUB_OUTPUT - echo "Using manually specified ${{ github.event.inputs.board_layers }} layer configuration" - else - # Create Python script to detect layers - cat > /tmp/detect_layers.py << 'PYTHON_SCRIPT' - #!/usr/bin/env python3 - import re - import sys - - try: - pcb_file = sys.argv[1] - with open(pcb_file, 'r') as f: - content = f.read() - - # Look for the layers section in the KiCad PCB file - # Format: (layers (0 "F.Cu" signal) (4 "In1.Cu" signal) ...) - layers_section = re.search(r'\(layers\s*\n([^)]+)\)', content, re.MULTILINE | re.DOTALL) - - if layers_section: - layers_text = layers_section.group(0) - # Find all layer definitions - match the pattern (number "name" type) - layer_matches = re.findall(r'\(\d+\s+"([^"]+)"\s+\w+\)', layers_text) - - # Count copper layers - any layer with .Cu suffix - copper_layers = [] - for layer in layer_matches: - if '.Cu' in layer: - copper_layers.append(layer) - print(f"Found copper layer: {layer}", file=sys.stderr) - - print(len(copper_layers) if copper_layers else 2) - else: - # Alternative: look for individual layer definitions - all_layers = re.findall(r'\(\d+\s+"([^"]+\.Cu)"\s+\w+\)', content) - if not all_layers: - # Try another pattern - all_layers = re.findall(r'"(F\.Cu|B\.Cu|In\d+\.Cu)"', content) - - # Remove duplicates - unique_layers = list(set(all_layers)) - - for layer in unique_layers: - print(f"Found copper layer: {layer}", file=sys.stderr) - - print(len(unique_layers) if unique_layers else 2) - - except Exception as e: - print(f"Error detecting layers: {e}", file=sys.stderr) - print(2) # Default to 2 layers - PYTHON_SCRIPT - - chmod +x /tmp/detect_layers.py - - # Run the detection and capture both stdout and stderr - LAYERS=$(python3 /tmp/detect_layers.py "$PCB_FILE" 2>&1 | tee /tmp/layer_detection.log | grep -v "Found copper layer" | tail -1) - - # Show debug output - grep "Found copper layer" /tmp/layer_detection.log || true - - if [ -z "$LAYERS" ] || [ "$LAYERS" = "0" ]; then - LAYERS=2 - echo "Failed to detect layers, defaulting to 2" - fi - - echo "layers=$LAYERS" >> $GITHUB_OUTPUT - echo "Auto-detected $LAYERS layer board" + # Use workflow input or default to 2 layers + LAYERS="${{ github.event.inputs.board_layers }}" + if [ -z "$LAYERS" ]; then + LAYERS="2" fi + echo "layers=$LAYERS" >> $GITHUB_OUTPUT + echo "Using $LAYERS layer configuration" - name: Check for KiBot config id: config_check run: | - PROJECT_DIR="${{ steps.find_project.outputs.project_dir }}" - - # Search for kibot config in multiple locations - if [ -f "$PROJECT_DIR/kibot.yaml" ]; then + # Check if kibot.yaml exists in repository root + if [ -f "kibot.yaml" ]; then echo "found=true" >> $GITHUB_OUTPUT - echo "config_path=$PROJECT_DIR/kibot.yaml" >> $GITHUB_OUTPUT - echo "Using project-specific kibot.yaml" - elif [ -f "$PROJECT_DIR/../kibot.yaml" ]; then - echo "found=true" >> $GITHUB_OUTPUT - echo "config_path=$PROJECT_DIR/../kibot.yaml" >> $GITHUB_OUTPUT - echo "Using parent directory kibot.yaml" - elif [ -f "kibot.yaml" ]; then - echo "found=true" >> $GITHUB_OUTPUT - echo "config_path=kibot.yaml" >> $GITHUB_OUTPUT - echo "Using root kibot.yaml" - elif [ -f ".kibot/config.yaml" ]; then - echo "found=true" >> $GITHUB_OUTPUT - echo "config_path=.kibot/config.yaml" >> $GITHUB_OUTPUT - echo "Using .kibot/config.yaml" + echo "Using kibot.yaml configuration from repository root" else echo "found=false" >> $GITHUB_OUTPUT - echo "Using --quick-start mode (no custom config found)" + echo "No kibot.yaml found in repository root - will use --quick-start mode" fi - name: Run ERC if: steps.config_check.outputs.found == 'true' run: | cd "${{ steps.find_project.outputs.project_dir }}" - CONFIG_PATH="${{ steps.config_check.outputs.config_path }}" - - # Convert to absolute path if needed - if [[ "$CONFIG_PATH" != /* ]]; then - CONFIG_PATH="$(pwd)/$CONFIG_PATH" - fi - - # Normalize the path - CONFIG_PATH=$(readlink -f "$CONFIG_PATH" 2>/dev/null || realpath "$CONFIG_PATH" 2>/dev/null || echo "$CONFIG_PATH") - - if [ ! -f "$CONFIG_PATH" ]; then - echo "Warning: Config file not found at $CONFIG_PATH" - exit 0 - fi if [ -n "${{ steps.find_project.outputs.sch_file }}" ]; then - echo "Running ERC with config: $CONFIG_PATH" - kibot -c "$CONFIG_PATH" -s run_erc -v || true + echo "Running ERC on schematic files..." + # Use absolute path to config file in repo root + kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_erc -v || true else echo "No schematic files found, skipping ERC" fi @@ -208,57 +114,29 @@ jobs: if: steps.config_check.outputs.found == 'true' run: | cd "${{ steps.find_project.outputs.project_dir }}" - CONFIG_PATH="${{ steps.config_check.outputs.config_path }}" - # Convert to absolute path if needed - if [[ "$CONFIG_PATH" != /* ]]; then - CONFIG_PATH="$(pwd)/$CONFIG_PATH" - fi - - # Normalize the path - CONFIG_PATH=$(readlink -f "$CONFIG_PATH" 2>/dev/null || realpath "$CONFIG_PATH" 2>/dev/null || echo "$CONFIG_PATH") - - if [ ! -f "$CONFIG_PATH" ]; then - echo "Warning: Config file not found at $CONFIG_PATH" - exit 0 - fi - - echo "Running DRC with config: $CONFIG_PATH" - kibot -c "$CONFIG_PATH" -s run_drc -v || true + echo "Running DRC on PCB..." + # Use absolute path to config file in repo root + kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_drc -v || true continue-on-error: true - name: Generate outputs with custom config if: steps.config_check.outputs.found == 'true' run: | cd "${{ steps.find_project.outputs.project_dir }}" - CONFIG_PATH="${{ steps.config_check.outputs.config_path }}" - - # Convert to absolute path if needed - if [[ "$CONFIG_PATH" != /* ]]; then - CONFIG_PATH="$(pwd)/$CONFIG_PATH" - fi - - # Normalize the path - CONFIG_PATH=$(readlink -f "$CONFIG_PATH" 2>/dev/null || realpath "$CONFIG_PATH" 2>/dev/null || echo "$CONFIG_PATH") - - if [ ! -f "$CONFIG_PATH" ]; then - echo "Error: Config file not found at $CONFIG_PATH" - exit 1 - fi echo "Generating outputs for ${{ steps.layers.outputs.layers }} layer board" echo "Project: ${{ steps.find_project.outputs.project_name }}" - echo "Using config: $CONFIG_PATH" + echo "Using config: ${GITHUB_WORKSPACE}/kibot.yaml" - # Run KiBot with the configuration - kibot -c "$CONFIG_PATH" -d Fabrication -v + # Run KiBot with the configuration from repo root + kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -d Fabrication -v # Move outputs to root Generated folder - mkdir -p ../Generated + mkdir -p "${GITHUB_WORKSPACE}/Generated" if [ -d "Fabrication" ]; then - cp -r Fabrication/* ../Generated/ + cp -r Fabrication/* "${GITHUB_WORKSPACE}/Generated/" fi - cd .. - name: Quick Start fallback if: steps.config_check.outputs.found == 'false' @@ -268,15 +146,12 @@ jobs: kibot --quick-start # Move outputs to root Generated folder + mkdir -p "${GITHUB_WORKSPACE}/Generated" if [ -d "Generated" ]; then - mv Generated ../ - else - mkdir -p ../Generated - echo "No Generated folder found after quick-start" + cp -r Generated/* "${GITHUB_WORKSPACE}/Generated/" fi - cd .. - - name: Add layer information to outputs + - name: Add board information to outputs run: | echo "Board configuration: ${{ steps.layers.outputs.layers }} layers" > Generated/board_info.txt echo "Project: ${{ steps.find_project.outputs.project_name }}" >> Generated/board_info.txt @@ -322,7 +197,7 @@ jobs: name: Fabrication-Package-${{ steps.layers.outputs.layers }}layer path: Generated/*.zip - # This step is to upload the results to another repo (web pages) + # Deploy to documentation branch deploy: runs-on: kicad-kibot-runner needs: generate @@ -367,7 +242,7 @@ jobs: # Commit and push if there are changes git add . if ! git diff --cached --quiet; then - git commit -m "chore: update deployed documentation for ${{ needs.generate.outputs.layers }}-layer board" + git commit -m "chore: update deployed documentation" git push --force "$AUTHED_URL" docu else echo "No changes to deploy" diff --git a/kicad/kibot.yaml b/kibot.yaml similarity index 100% rename from kicad/kibot.yaml rename to kibot.yaml From 5bf4763a75f68dbbfccb82b7bf8f5085e1835b1f Mon Sep 17 00:00:00 2001 From: Alexander Grabowski Date: Mon, 8 Sep 2025 15:26:49 +0700 Subject: [PATCH 10/25] Update .gitea/workflows/kibot.yml --- .gitea/workflows/kibot.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index e47872f..79d7cd4 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -23,7 +23,7 @@ on: board_layers: description: 'Number of PCB layers' required: false - default: '2' + default: '4' type: choice options: - '2' @@ -130,7 +130,8 @@ jobs: echo "Using config: ${GITHUB_WORKSPACE}/kibot.yaml" # Run KiBot with the configuration from repo root - kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -d Fabrication -v + # Skip ERC/DRC preflight checks to avoid stopping on errors + kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_erc,run_drc -d Fabrication -v # Move outputs to root Generated folder mkdir -p "${GITHUB_WORKSPACE}/Generated" From 96332cdc7c859494f2475e736f98e7849d14e86e Mon Sep 17 00:00:00 2001 From: Alexander Grabowski Date: Mon, 8 Sep 2025 15:34:54 +0700 Subject: [PATCH 11/25] Update .gitea/workflows/kibot.yml --- .gitea/workflows/kibot.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index 79d7cd4..b63c294 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -23,7 +23,7 @@ on: board_layers: description: 'Number of PCB layers' required: false - default: '4' + default: '2' type: choice options: - '2' @@ -76,10 +76,10 @@ jobs: - name: Set layer count id: layers run: | - # Use workflow input or default to 2 layers + # Use workflow input or default to 4 layers LAYERS="${{ github.event.inputs.board_layers }}" if [ -z "$LAYERS" ]; then - LAYERS="2" + LAYERS="4" # Change this default if needed fi echo "layers=$LAYERS" >> $GITHUB_OUTPUT echo "Using $LAYERS layer configuration" From 2676e888d36b83f997c782b56c4f9e8b0241fea6 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 15:38:11 +0700 Subject: [PATCH 12/25] '' --- kibot.yaml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kibot.yaml b/kibot.yaml index 8931096..e03b61c 100644 --- a/kibot.yaml +++ b/kibot.yaml @@ -12,8 +12,8 @@ global: - number: 58 - number: 61 - # Default output directory - output: 'Fabrication' + # Default output directory - will be overridden by command line + # output: 'Fabrication' # Commented out to use command line option # Variant for assembly if needed variant: '' @@ -27,6 +27,14 @@ preflight: check_zone_fills: true ignore_unconnected: false update_xml: true + erc: + warnings_as_errors: false + # Continue even if ERC fails + dont_stop: true + drc: + warnings_as_errors: false + # Continue even if DRC fails + dont_stop: true outputs: # Schematic outputs From c0721926fa705c6cd673fd933eaf1b2eb6f27d78 Mon Sep 17 00:00:00 2001 From: Alexander Grabowski Date: Mon, 8 Sep 2025 15:40:12 +0700 Subject: [PATCH 13/25] position file update --- kibot.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kibot.yaml b/kibot.yaml index e03b61c..e520cd5 100644 --- a/kibot.yaml +++ b/kibot.yaml @@ -188,7 +188,7 @@ outputs: type: position dir: Assembly options: - output: '%f-position.%x' + output: '%f-position-%i.%x' # Added %i to differentiate front/back files format: CSV units: millimeters separate_files_for_front_and_back: true From 997cc53c40c449467ea5a0abd2d22dc6b33ad947 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 15:50:35 +0700 Subject: [PATCH 14/25] added jlc zip creation --- .gitea/workflows/kibot.yml | 7 +++++ kibot.yaml | 61 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index b63c294..97c079f 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -198,6 +198,13 @@ jobs: name: Fabrication-Package-${{ steps.layers.outputs.layers }}layer path: Generated/*.zip + - name: Retrieve results - JLCPCB Package + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: JLCPCB-${{ steps.layers.outputs.layers }}layer + path: Generated/*_JLCPCB_compress.zip + # Deploy to documentation branch deploy: runs-on: kicad-kibot-runner diff --git a/kibot.yaml b/kibot.yaml index e520cd5..a41c40a 100644 --- a/kibot.yaml +++ b/kibot.yaml @@ -232,6 +232,67 @@ outputs: output: '%f-report.%x' do_convert: true + # JLCPCB fabrication package + - name: 'jlcpcb_gerbers' + comment: "Gerbers for JLCPCB" + type: gerber + dir: JLCPCB + options: + output: '%f%i.%x' + exclude_edge_layer: false + exclude_pads_from_silkscreen: true + plot_sheet_reference: false + plot_footprint_refs: true + plot_footprint_values: false + force_plot_invisible_refs_vals: false + tent_vias: true + use_protel_extensions: true # JLCPCB prefers Protel extensions + create_gerber_job_file: false # JLCPCB doesn't need this + disable_aperture_macros: true + gerber_precision: 4.6 + use_gerber_x2_attributes: false + use_gerber_net_attributes: false + line_width: 0.1 + subtract_mask_from_silk: true + layers: + - F.Cu + - B.Cu + - In1.Cu + - In2.Cu + - In3.Cu + - In4.Cu + - F.Paste + - B.Paste + - F.Silkscreen + - B.Silkscreen + - F.Mask + - B.Mask + - Edge.Cuts + + # JLCPCB drill files + - name: 'jlcpcb_drill' + comment: "Drill files for JLCPCB" + type: excellon + dir: JLCPCB + options: + output: '%f%i.%x' + metric_units: true + minimal_header: false + mirror_y_axis: false + pth_and_npth_single_file: false # JLCPCB prefers separate files + + # JLCPCB ZIP file + - name: 'jlcpcb_zip' + comment: "JLCPCB fabrication ZIP" + type: compress + dir: . + options: + output: '%f_JLCPCB_compress.%x' + format: ZIP + files: + - source: JLCPCB/* + dest: / + # Fabrication package (ZIP) - name: 'fabrication' comment: "Fabrication package" From 142566c8f4b0069d1a08d449b89ef2712b855285 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 16:06:06 +0700 Subject: [PATCH 15/25] add diff function --- .gitea/workflows/kibot.yml | 7 +++++++ kibot.yaml | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index 97c079f..65a4b20 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -205,6 +205,13 @@ jobs: name: JLCPCB-${{ steps.layers.outputs.layers }}layer path: Generated/*_JLCPCB_compress.zip + - name: Retrieve results - Diff Report + if: steps.generate_diff.outputs.has_diff == 'true' + uses: actions/upload-artifact@v3 + with: + name: Schematic-PCB-Diff + path: Generated/DIFF + # Deploy to documentation branch deploy: runs-on: kicad-kibot-runner diff --git a/kibot.yaml b/kibot.yaml index a41c40a..cda1c24 100644 --- a/kibot.yaml +++ b/kibot.yaml @@ -232,6 +232,38 @@ outputs: output: '%f-report.%x' do_convert: true + # Schematic diff + - name: 'sch_diff' + comment: "Schematic diff PDF" + type: diff + dir: DIFF + disabled_by_default: false + options: + output: '%f-schematic-diff.%x' + format: PDF + diff_mode: 'red_green' + old: 'HEAD~1' + new: 'HEAD' + cache_dir: '.cache' + add_link_id: true + only_different: true + + # PCB diff + - name: 'pcb_diff' + comment: "PCB diff PDF" + type: diff + dir: DIFF + disabled_by_default: false + options: + output: '%f-pcb-diff.%x' + format: PDF + diff_mode: 'red_green' + old: 'HEAD~1' + new: 'HEAD' + cache_dir: '.cache' + pcb: true + only_different: true + # JLCPCB fabrication package - name: 'jlcpcb_gerbers' comment: "Gerbers for JLCPCB" From 35fd4da002af48481498488b9de832a4d6eb944f Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 16:09:28 +0700 Subject: [PATCH 16/25] fixed DIFF and added BoM --- .gitea/workflows/kibot.yml | 7 +++ kibot.yaml | 117 ++++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index 65a4b20..02b17d3 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -184,6 +184,13 @@ jobs: name: Assembly-${{ steps.layers.outputs.layers }}layer path: Generated/Assembly + - name: Retrieve results - BOM + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: BOM-${{ steps.layers.outputs.layers }}layer + path: Generated/BoM + - name: Retrieve results - 3D Model if: steps.config_check.outputs.found == 'true' uses: actions/upload-artifact@v3 diff --git a/kibot.yaml b/kibot.yaml index cda1c24..149d493 100644 --- a/kibot.yaml +++ b/kibot.yaml @@ -212,6 +212,119 @@ outputs: include_tracks: false include_nets: false + # Generic BOM in CSV format + - name: 'bom_csv' + comment: "Bill of Materials in CSV format" + type: bom + dir: BoM/Generic + options: + output: '%f_bom.%x' + format: CSV + separator: ',' + ref_separator: ',' + group_fields: ['Value', 'Footprint', 'Tolerance', 'Voltage'] + columns: + - field: 'References' + name: 'Reference' + - field: 'Value' + name: 'Value' + - field: 'Quantity Per PCB' + name: 'Qty' + - field: 'Footprint' + name: 'Package' + - field: 'Description' + name: 'Description' + - field: 'Manufacturer' + name: 'Manufacturer' + - field: 'MPN' + name: 'Part Number' + - field: 'LCSC' + name: 'LCSC Part' + - field: 'Digikey' + name: 'Digikey Part' + - field: 'Mouser' + name: 'Mouser Part' + + # Generic BOM in HTML format + - name: 'bom_html' + comment: "Bill of Materials in HTML format" + type: bom + dir: BoM/Generic + options: + output: '%f_bom.%x' + format: HTML + html: + style: 'modern-blue' # Style for HTML output + datasheet_as_link: true + generate_dnf: true + logo: false + title: 'Bill of Materials - %f' + extra_info: 'Generated on %d' + group_fields: ['Value', 'Footprint', 'Tolerance', 'Voltage'] + columns: + - field: 'References' + name: 'Reference' + - field: 'Value' + name: 'Value' + - field: 'Quantity Per PCB' + name: 'Qty' + - field: 'Footprint' + name: 'Package' + - field: 'Description' + name: 'Description' + - field: 'Manufacturer' + name: 'Manufacturer' + - field: 'MPN' + name: 'Part Number' + - field: 'LCSC' + name: 'LCSC Part' + - field: 'Digikey' + name: 'Digikey Part' + - field: 'Mouser' + name: 'Mouser Part' + + # Generic BOM in XLSX format + - name: 'bom_xlsx' + comment: "Bill of Materials in Excel format" + type: bom + dir: BoM/Generic + options: + output: '%f_bom.%x' + format: XLSX + xlsx: + datasheet_as_link: true + generate_dnf: true + logo: false + title: 'Bill of Materials' + max_col_width: 50 + highlight_empty: true + group_fields: ['Value', 'Footprint', 'Tolerance', 'Voltage'] + columns: + - field: 'References' + name: 'Reference' + - field: 'Value' + name: 'Value' + - field: 'Quantity Per PCB' + name: 'Qty' + - field: 'Footprint' + name: 'Package' + - field: 'Description' + name: 'Description' + - field: 'Manufacturer' + name: 'Manufacturer' + - field: 'MPN' + name: 'Part Number' + - field: 'LCSC' + name: 'LCSC Part' + - field: 'Digikey' + name: 'Digikey Part' + - field: 'Mouser' + name: 'Mouser Part' + - field: 'Unit Price' + name: 'Unit Cost' + - field: 'Total Price' + name: 'Extended Cost' + # 3D model - name: 'step' comment: "3D STEP model" @@ -237,7 +350,7 @@ outputs: comment: "Schematic diff PDF" type: diff dir: DIFF - disabled_by_default: false + run_by_default: false # Only run when explicitly requested options: output: '%f-schematic-diff.%x' format: PDF @@ -253,7 +366,7 @@ outputs: comment: "PCB diff PDF" type: diff dir: DIFF - disabled_by_default: false + run_by_default: false # Only run when explicitly requested options: output: '%f-pcb-diff.%x' format: PDF From 138d54c50dc66eec0ef40b22394abd719d336d33 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 16:15:04 +0700 Subject: [PATCH 17/25] fixed bom, added datasheet dl --- kibot.yaml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/kibot.yaml b/kibot.yaml index 149d493..104536c 100644 --- a/kibot.yaml +++ b/kibot.yaml @@ -255,11 +255,10 @@ outputs: format: HTML html: style: 'modern-blue' # Style for HTML output - datasheet_as_link: true + datasheet_as_link: 'Datasheet' # Column name for datasheet links generate_dnf: true logo: false title: 'Bill of Materials - %f' - extra_info: 'Generated on %d' group_fields: ['Value', 'Footprint', 'Tolerance', 'Voltage'] columns: - field: 'References' @@ -292,7 +291,7 @@ outputs: output: '%f_bom.%x' format: XLSX xlsx: - datasheet_as_link: true + datasheet_as_link: 'Datasheet' # Column name for datasheet links generate_dnf: true logo: false title: 'Bill of Materials' @@ -320,10 +319,20 @@ outputs: name: 'Digikey Part' - field: 'Mouser' name: 'Mouser Part' - - field: 'Unit Price' - name: 'Unit Cost' - - field: 'Total Price' - name: 'Extended Cost' + - field: 'Datasheet' + name: 'Datasheet' + + # Download datasheets + - name: 'download_datasheets' + comment: "Download component datasheets" + type: download_datasheets + dir: BoM/Datasheets + run_by_default: false # Can be slow, so disabled by default + options: + output: '%f_datasheets.csv' # CSV list of downloaded files + field: 'Datasheet' # Field containing datasheet URLs + dnf: false # Don't download for DNF parts + repeated: false # Don't download the same datasheet twice # 3D model - name: 'step' From 055e9f47924ae8d6156c7e0b38b1e617ac2acbf7 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 16:23:09 +0700 Subject: [PATCH 18/25] enable diff,ds_dl,pcb_diff --- kibot.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kibot.yaml b/kibot.yaml index 104536c..09d298d 100644 --- a/kibot.yaml +++ b/kibot.yaml @@ -327,7 +327,7 @@ outputs: comment: "Download component datasheets" type: download_datasheets dir: BoM/Datasheets - run_by_default: false # Can be slow, so disabled by default + run_by_default: true # Changed to run by default options: output: '%f_datasheets.csv' # CSV list of downloaded files field: 'Datasheet' # Field containing datasheet URLs From 75f74bfe3044f56160e0abdca3ee3e7f1dce49eb Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 16:32:36 +0700 Subject: [PATCH 19/25] changed dl and diff --- kibot.yaml | 729 ++++++++++++++++++++--------------------------------- 1 file changed, 273 insertions(+), 456 deletions(-) diff --git a/kibot.yaml b/kibot.yaml index 09d298d..dc13e53 100644 --- a/kibot.yaml +++ b/kibot.yaml @@ -1,480 +1,297 @@ -# KiBot configuration for 2/4/6 layer boards -# Works with Gitea CI/CD pipeline +- name: Download component datasheets + if: steps.config_check.outputs.found == 'true' + continue-on-error: true + run: | + echo "Datasheets will be downloaded by KiBot's download_datasheets output" + echo "They will be saved in Generated/BoM/Datasheets/" + + # Create a README for the datasheets folder if KiBot creates it + if [ -d "${GITHUB_WORKSPACE}/Generated/BoM/Datasheets" ]; then + cat > "${GITHUB_WORKSPACE}/Generated/BoM/Datasheets/README.md" << EOF + # Component Datasheets + + This folder contains downloaded datasheets for components used in the project. + + - Datasheets are downloaded automatically from URLs in the schematic + - Files are named based on the component reference and original filename + - Only unique datasheets are downloaded (duplicates are linked) + + Generated on: $(date) + Project: ${{ steps.find_project.outputs.project_name }} + EOF + + echo "Datasheet folder contents:" + ls -la "${GITHUB_WORKSPACE}/Generated/BoM/Datasheets/" || true + else + echo "Note: Datasheet folder not created yet - will be created by KiBot" + finame: "KiBot PCB Generation" -kibot: - version: 1 +# Controls when the action will run +on: + push: + paths: + - '**/*.kicad_sch' + - '**/*.kicad_pcb' + - '**/*.kicad_pro' + - 'kibot.yaml' + - '.gitea/workflows/*.yml' + pull_request: + paths: + - '**/*.kicad_sch' + - '**/*.kicad_pcb' + - '**/*.kicad_pro' + - 'kibot.yaml' + - '.gitea/workflows/*.yml' + repository_dispatch: + types: [run_qs] + workflow_dispatch: + inputs: + board_layers: + description: 'Number of PCB layers' + required: false + default: '2' + type: choice + options: + - '2' + - '4' + - '6' -global: - # Filters for ERC/DRC warnings - filters: - - number: 1007 - - number: 1015 - - number: 58 - - number: 61 - - # Default output directory - will be overridden by command line - # output: 'Fabrication' # Commented out to use command line option - - # Variant for assembly if needed - variant: '' - - # Date format for filenames - date_format: '%Y-%m-%d_%H-%M-%S' +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + generate: + runs-on: kicad-kibot-runner -preflight: - run_erc: true - run_drc: true - check_zone_fills: true - ignore_unconnected: false - update_xml: true - erc: - warnings_as_errors: false - # Continue even if ERC fails - dont_stop: true - drc: - warnings_as_errors: false - # Continue even if DRC fails - dont_stop: true + steps: + - uses: actions/checkout@v3 + with: + # So we can run a diff between last 2 changes + fetch-depth: '0' -outputs: - # Schematic outputs - - name: 'print_sch' - comment: "Schematic PDF" - type: pdf_sch_print - dir: Schematic - options: - output: '%f-schematic.%x' + - name: Find KiCad project + id: find_project + run: | + # Find all .kicad_pcb files in the repository + PCB_FILES=$(find . -name "*.kicad_pcb" -type f | grep -v -E '/(backups?|backup|old|archive|deprecated)/' | head -5) + + if [ -z "$PCB_FILES" ]; then + echo "Error: No KiCad PCB files found" + exit 1 + fi + + # Use the first PCB file found + PCB_FILE=$(echo "$PCB_FILES" | head -1) + PROJECT_DIR=$(dirname "$PCB_FILE") + PROJECT_NAME=$(basename "$PCB_FILE" .kicad_pcb) + + echo "Found KiCad project: $PROJECT_NAME" + echo "Project directory: $PROJECT_DIR" + echo "PCB file: $PCB_FILE" + + # Find schematic file + SCH_FILE=$(find "$PROJECT_DIR" -name "*.kicad_sch" -o -name "*.sch" | head -1) + + echo "pcb_file=$PCB_FILE" >> $GITHUB_OUTPUT + echo "project_dir=$PROJECT_DIR" >> $GITHUB_OUTPUT + echo "project_name=$PROJECT_NAME" >> $GITHUB_OUTPUT + echo "sch_file=$SCH_FILE" >> $GITHUB_OUTPUT + + # List all found projects for debugging + echo "All PCB files found:" + echo "$PCB_FILES" - - name: 'print_sch_svg' - comment: "Schematic SVG" - type: svg_sch_print - dir: Schematic - options: - output: '%f-schematic.%x' + - name: Set layer count + id: layers + run: | + # Use workflow input or default to 4 layers + LAYERS="${{ github.event.inputs.board_layers }}" + if [ -z "$LAYERS" ]; then + LAYERS="4" # Change this default if needed + fi + echo "layers=$LAYERS" >> $GITHUB_OUTPUT + echo "Using $LAYERS layer configuration" - # PCB 2D renders - - name: 'pcb_top' - comment: "PCB render top" - type: pcbdraw - dir: PCB/2D_render - options: - output: '%f-top.%x' - format: svg - show_components: all - dpi: 300 + - name: Check for KiBot config + id: config_check + run: | + # Check if kibot.yaml exists in repository root + if [ -f "kibot.yaml" ]; then + echo "found=true" >> $GITHUB_OUTPUT + echo "Using kibot.yaml configuration from repository root" + else + echo "found=false" >> $GITHUB_OUTPUT + echo "No kibot.yaml found in repository root - will use --quick-start mode" + fi - - name: 'pcb_bottom' - comment: "PCB render bottom" - type: pcbdraw - dir: PCB/2D_render - options: - output: '%f-bottom.%x' - format: svg - bottom: true - show_components: all - dpi: 300 + - name: Run ERC + if: steps.config_check.outputs.found == 'true' + run: | + cd "${{ steps.find_project.outputs.project_dir }}" + + if [ -n "${{ steps.find_project.outputs.sch_file }}" ]; then + echo "Running ERC on schematic files..." + # Use absolute path to config file in repo root + kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_erc -v || true + else + echo "No schematic files found, skipping ERC" + fi + continue-on-error: true - # PCB PDF documentation - - name: 'print_pcb' - comment: "PCB PDF" - type: pdf_pcb_print - dir: PCB/PDF - options: - output: '%f-pcb.%x' - plot_sheet_reference: true - monochrome: false - pages: - - layers: - - F.Cu - - F.Paste - - F.Silkscreen - - Edge.Cuts - sheet: 'Front copper' - - layers: - - B.Cu - - B.Paste - - B.Silkscreen - - Edge.Cuts - mirror: true - sheet: 'Bottom copper' - - layers: - - In1.Cu - - Edge.Cuts - sheet: 'Inner layer 1' - skip_if_no_layer: true - - layers: - - In2.Cu - - Edge.Cuts - sheet: 'Inner layer 2' - skip_if_no_layer: true - - layers: - - In3.Cu - - Edge.Cuts - sheet: 'Inner layer 3' - skip_if_no_layer: true - - layers: - - In4.Cu - - Edge.Cuts - sheet: 'Inner layer 4' - skip_if_no_layer: true + - name: Run DRC + if: steps.config_check.outputs.found == 'true' + run: | + cd "${{ steps.find_project.outputs.project_dir }}" + + echo "Running DRC on PCB..." + # Use absolute path to config file in repo root + kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_drc -v || true + continue-on-error: true - # Gerber files - automatically handles 2/4/6 layers - - name: 'gerbers' - comment: "Gerber files" - type: gerber - dir: Gerbers - options: - output: '%f%i.%x' - exclude_edge_layer: true - exclude_pads_from_silkscreen: true - plot_sheet_reference: false - plot_footprint_refs: true - plot_footprint_values: false - force_plot_invisible_refs_vals: false - tent_vias: true - use_protel_extensions: false - create_gerber_job_file: true - disable_aperture_macros: true - gerber_precision: 4.6 - use_gerber_x2_attributes: false - use_gerber_net_attributes: false - line_width: 0.1 - subtract_mask_from_silk: true - layers: - # Copper layers - automatically included if present - - F.Cu - - B.Cu - - In1.Cu - - In2.Cu - - In3.Cu - - In4.Cu - # Technical layers - - F.Paste - - B.Paste - - F.Silkscreen - - B.Silkscreen - - F.Mask - - B.Mask - - Edge.Cuts - - User.Comments - - F.Fab - - B.Fab + - name: Generate outputs with custom config + if: steps.config_check.outputs.found == 'true' + run: | + cd "${{ steps.find_project.outputs.project_dir }}" + + echo "Generating outputs for ${{ steps.layers.outputs.layers }} layer board" + echo "Project: ${{ steps.find_project.outputs.project_name }}" + echo "Using config: ${GITHUB_WORKSPACE}/kibot.yaml" + + # Run KiBot with the configuration from repo root + # Skip ERC/DRC preflight checks to avoid stopping on errors + kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_erc,run_drc -d Fabrication -v + + # Move outputs to root Generated folder + mkdir -p "${GITHUB_WORKSPACE}/Generated" + if [ -d "Fabrication" ]; then + cp -r Fabrication/* "${GITHUB_WORKSPACE}/Generated/" + fi - # Excellon drill files - - name: 'drill' - comment: "Drill files" - type: excellon - dir: Gerbers - options: - output: '%f%i.%x' - metric_units: true - minimal_header: false - mirror_y_axis: false - report: - filename: '%f-drill_report.txt' - pth_and_npth_single_file: false + - name: Quick Start fallback + if: steps.config_check.outputs.found == 'false' + run: | + cd "${{ steps.find_project.outputs.project_dir }}" + echo "Running KiBot in quick-start mode" + kibot --quick-start + + # Move outputs to root Generated folder + mkdir -p "${GITHUB_WORKSPACE}/Generated" + if [ -d "Generated" ]; then + cp -r Generated/* "${GITHUB_WORKSPACE}/Generated/" + fi - # Drill map - - name: 'drill_map' - comment: "Drill map" - type: gerb_drill - dir: Gerbers - options: - output: '%f-drill_map.%x' + - name: Add board information to outputs + run: | + echo "Board configuration: ${{ steps.layers.outputs.layers }} layers" > Generated/board_info.txt + echo "Project: ${{ steps.find_project.outputs.project_name }}" >> Generated/board_info.txt + echo "Project path: ${{ steps.find_project.outputs.project_dir }}" >> Generated/board_info.txt + echo "Generated on: $(date)" >> Generated/board_info.txt + echo "Commit: ${{ github.sha }}" >> Generated/board_info.txt - # Pick and place files - - name: 'position' - comment: "Pick and place file" - type: position - dir: Assembly - options: - output: '%f-position-%i.%x' # Added %i to differentiate front/back files - format: CSV - units: millimeters - separate_files_for_front_and_back: true - only_smd: false + - name: Fix file permissions + run: | + chmod -R a+rw Generated || true - # Interactive BOM - - name: 'ibom' - comment: "Interactive BOM" - type: ibom - dir: Assembly - options: - output: '%f-ibom.%x' - dark_mode: false - hide_pads: false - show_fabrication: false - hide_silkscreen: false - highlight_pin1: true - no_redraw_on_drag: false - board_rotation: 0 - check_extra_fields: false - include_tracks: false - include_nets: false + - name: Retrieve results - All outputs + uses: actions/upload-artifact@v3 + with: + name: Automatic_outputs + path: Generated - # Generic BOM in CSV format - - name: 'bom_csv' - comment: "Bill of Materials in CSV format" - type: bom - dir: BoM/Generic - options: - output: '%f_bom.%x' - format: CSV - separator: ',' - ref_separator: ',' - group_fields: ['Value', 'Footprint', 'Tolerance', 'Voltage'] - columns: - - field: 'References' - name: 'Reference' - - field: 'Value' - name: 'Value' - - field: 'Quantity Per PCB' - name: 'Qty' - - field: 'Footprint' - name: 'Package' - - field: 'Description' - name: 'Description' - - field: 'Manufacturer' - name: 'Manufacturer' - - field: 'MPN' - name: 'Part Number' - - field: 'LCSC' - name: 'LCSC Part' - - field: 'Digikey' - name: 'Digikey Part' - - field: 'Mouser' - name: 'Mouser Part' + - name: Retrieve results - Gerbers + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: Gerbers-${{ steps.layers.outputs.layers }}layer + path: Generated/Gerbers - # Generic BOM in HTML format - - name: 'bom_html' - comment: "Bill of Materials in HTML format" - type: bom - dir: BoM/Generic - options: - output: '%f_bom.%x' - format: HTML - html: - style: 'modern-blue' # Style for HTML output - datasheet_as_link: 'Datasheet' # Column name for datasheet links - generate_dnf: true - logo: false - title: 'Bill of Materials - %f' - group_fields: ['Value', 'Footprint', 'Tolerance', 'Voltage'] - columns: - - field: 'References' - name: 'Reference' - - field: 'Value' - name: 'Value' - - field: 'Quantity Per PCB' - name: 'Qty' - - field: 'Footprint' - name: 'Package' - - field: 'Description' - name: 'Description' - - field: 'Manufacturer' - name: 'Manufacturer' - - field: 'MPN' - name: 'Part Number' - - field: 'LCSC' - name: 'LCSC Part' - - field: 'Digikey' - name: 'Digikey Part' - - field: 'Mouser' - name: 'Mouser Part' + - name: Retrieve results - Assembly + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: Assembly-${{ steps.layers.outputs.layers }}layer + path: Generated/Assembly - # Generic BOM in XLSX format - - name: 'bom_xlsx' - comment: "Bill of Materials in Excel format" - type: bom - dir: BoM/Generic - options: - output: '%f_bom.%x' - format: XLSX - xlsx: - datasheet_as_link: 'Datasheet' # Column name for datasheet links - generate_dnf: true - logo: false - title: 'Bill of Materials' - max_col_width: 50 - highlight_empty: true - group_fields: ['Value', 'Footprint', 'Tolerance', 'Voltage'] - columns: - - field: 'References' - name: 'Reference' - - field: 'Value' - name: 'Value' - - field: 'Quantity Per PCB' - name: 'Qty' - - field: 'Footprint' - name: 'Package' - - field: 'Description' - name: 'Description' - - field: 'Manufacturer' - name: 'Manufacturer' - - field: 'MPN' - name: 'Part Number' - - field: 'LCSC' - name: 'LCSC Part' - - field: 'Digikey' - name: 'Digikey Part' - - field: 'Mouser' - name: 'Mouser Part' - - field: 'Datasheet' - name: 'Datasheet' + - name: Retrieve results - BOM + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: BOM-${{ steps.layers.outputs.layers }}layer + path: Generated/BoM - # Download datasheets - - name: 'download_datasheets' - comment: "Download component datasheets" - type: download_datasheets - dir: BoM/Datasheets - run_by_default: true # Changed to run by default - options: - output: '%f_datasheets.csv' # CSV list of downloaded files - field: 'Datasheet' # Field containing datasheet URLs - dnf: false # Don't download for DNF parts - repeated: false # Don't download the same datasheet twice + - name: Retrieve results - 3D Model + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: 3D-Model + path: Generated/3D - # 3D model - - name: 'step' - comment: "3D STEP model" - type: step - dir: 3D - options: - output: '%f-3D.%x' - metric_units: true - origin: grid - no_virtual: true + - name: Retrieve results - Fabrication Package + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: Fabrication-Package-${{ steps.layers.outputs.layers }}layer + path: Generated/*.zip - # Board characteristics report - - name: 'report' - comment: "Board report" - type: report - dir: . - options: - output: '%f-report.%x' - do_convert: true + - name: Retrieve results - JLCPCB Package + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: JLCPCB-${{ steps.layers.outputs.layers }}layer + path: Generated/*_JLCPCB_compress.zip - # Schematic diff - - name: 'sch_diff' - comment: "Schematic diff PDF" - type: diff - dir: DIFF - run_by_default: false # Only run when explicitly requested - options: - output: '%f-schematic-diff.%x' - format: PDF - diff_mode: 'red_green' - old: 'HEAD~1' - new: 'HEAD' - cache_dir: '.cache' - add_link_id: true - only_different: true - - # PCB diff - - name: 'pcb_diff' - comment: "PCB diff PDF" - type: diff - dir: DIFF - run_by_default: false # Only run when explicitly requested - options: - output: '%f-pcb-diff.%x' - format: PDF - diff_mode: 'red_green' - old: 'HEAD~1' - new: 'HEAD' - cache_dir: '.cache' - pcb: true - only_different: true + - name: Retrieve results - Diff Report + if: steps.generate_diff.outputs.has_diff == 'true' + uses: actions/upload-artifact@v3 + with: + name: Schematic-PCB-Diff + path: Generated/DIFF - # JLCPCB fabrication package - - name: 'jlcpcb_gerbers' - comment: "Gerbers for JLCPCB" - type: gerber - dir: JLCPCB - options: - output: '%f%i.%x' - exclude_edge_layer: false - exclude_pads_from_silkscreen: true - plot_sheet_reference: false - plot_footprint_refs: true - plot_footprint_values: false - force_plot_invisible_refs_vals: false - tent_vias: true - use_protel_extensions: true # JLCPCB prefers Protel extensions - create_gerber_job_file: false # JLCPCB doesn't need this - disable_aperture_macros: true - gerber_precision: 4.6 - use_gerber_x2_attributes: false - use_gerber_net_attributes: false - line_width: 0.1 - subtract_mask_from_silk: true - layers: - - F.Cu - - B.Cu - - In1.Cu - - In2.Cu - - In3.Cu - - In4.Cu - - F.Paste - - B.Paste - - F.Silkscreen - - B.Silkscreen - - F.Mask - - B.Mask - - Edge.Cuts + # Deploy to documentation branch + deploy: + runs-on: kicad-kibot-runner + needs: generate - # JLCPCB drill files - - name: 'jlcpcb_drill' - comment: "Drill files for JLCPCB" - type: excellon - dir: JLCPCB - options: - output: '%f%i.%x' - metric_units: true - minimal_header: false - mirror_y_axis: false - pth_and_npth_single_file: false # JLCPCB prefers separate files - - # JLCPCB ZIP file - - name: 'jlcpcb_zip' - comment: "JLCPCB fabrication ZIP" - type: compress - dir: . - options: - output: '%f_JLCPCB_compress.%x' - format: ZIP - files: - - source: JLCPCB/* - dest: / + steps: + - uses: actions/checkout@v4 - # Fabrication package (ZIP) - - name: 'fabrication' - comment: "Fabrication package" - type: compress - dir: . - options: - output: '%f-fabrication.%x' - format: ZIP - files: - - source: Gerbers/* - dest: / - - source: PCB/PDF/* - dest: /Documentation - - source: Assembly/* - dest: /Assembly - - source: 3D/* - dest: /3D + - name: Retrieve results + uses: actions/download-artifact@v3 + with: + name: Automatic_outputs + path: Generated -# Variants for different layer counts (optional) -variants: - - name: 2layer - comment: "2 layer board variant" - type: kibom - - - name: 4layer - comment: "4 layer board variant" - type: kibom - - - name: 6layer - comment: "6 layer board variant" - type: kibom \ No newline at end of file + - name: Disable Jekyll + # Jekyll will filter the KiRi folders + run: | + touch Generated/.nojekyll + + - name: Push to docu branch + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + run: | + # Set up git identity + git config --global user.email "${{ gitea.actor }}@noreply.localhost" + git config --global user.name "${{ gitea.actor }}" + + # Get the repository URL and add authentication + REPO_URL=$(git config --get remote.origin.url) + AUTHED_URL=${REPO_URL/https:\/\//https:\/\/${GITEA_TOKEN}@} + + # Clone the docu branch into a separate folder + git clone --depth 1 --branch docu "$AUTHED_URL" docu-branch || \ + git clone --depth 1 "$AUTHED_URL" docu-branch + + cd docu-branch + git checkout -B docu + + # Clean old contents and copy new files + rm -rf ./* + cp -r ../Generated/* ./ + + # Commit and push if there are changes + git add . + if ! git diff --cached --quiet; then + git commit -m "chore: update deployed documentation" + git push --force "$AUTHED_URL" docu + else + echo "No changes to deploy" + fi \ No newline at end of file From 0d57021e087fbf99ac02cf2436a6c8070d3c4bf0 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 16:35:41 +0700 Subject: [PATCH 20/25] '' --- kibot.yaml | 791 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 519 insertions(+), 272 deletions(-) diff --git a/kibot.yaml b/kibot.yaml index dc13e53..9050833 100644 --- a/kibot.yaml +++ b/kibot.yaml @@ -1,297 +1,544 @@ -- name: Download component datasheets - if: steps.config_check.outputs.found == 'true' - continue-on-error: true - run: | - echo "Datasheets will be downloaded by KiBot's download_datasheets output" - echo "They will be saved in Generated/BoM/Datasheets/" - - # Create a README for the datasheets folder if KiBot creates it - if [ -d "${GITHUB_WORKSPACE}/Generated/BoM/Datasheets" ]; then - cat > "${GITHUB_WORKSPACE}/Generated/BoM/Datasheets/README.md" << EOF - # Component Datasheets - - This folder contains downloaded datasheets for components used in the project. - - - Datasheets are downloaded automatically from URLs in the schematic - - Files are named based on the component reference and original filename - - Only unique datasheets are downloaded (duplicates are linked) - - Generated on: $(date) - Project: ${{ steps.find_project.outputs.project_name }} - EOF - - echo "Datasheet folder contents:" - ls -la "${GITHUB_WORKSPACE}/Generated/BoM/Datasheets/" || true - else - echo "Note: Datasheet folder not created yet - will be created by KiBot" - finame: "KiBot PCB Generation" +# KiBot configuration for 2/4/6 layer boards +# Works with Gitea CI/CD pipeline -# Controls when the action will run -on: - push: - paths: - - '**/*.kicad_sch' - - '**/*.kicad_pcb' - - '**/*.kicad_pro' - - 'kibot.yaml' - - '.gitea/workflows/*.yml' - pull_request: - paths: - - '**/*.kicad_sch' - - '**/*.kicad_pcb' - - '**/*.kicad_pro' - - 'kibot.yaml' - - '.gitea/workflows/*.yml' - repository_dispatch: - types: [run_qs] - workflow_dispatch: - inputs: - board_layers: - description: 'Number of PCB layers' - required: false - default: '2' - type: choice - options: - - '2' - - '4' - - '6' +kibot: + version: 1 -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - generate: - runs-on: kicad-kibot-runner +global: + # Filters for ERC/DRC warnings + filters: + - number: 1007 + - number: 1015 + - number: 58 + - number: 61 + + # Default output directory - will be overridden by command line + # output: 'Fabrication' # Commented out to use command line option + + # Variant for assembly if needed + variant: '' + + # Date format for filenames + date_format: '%Y-%m-%d_%H-%M-%S' - steps: - - uses: actions/checkout@v3 - with: - # So we can run a diff between last 2 changes - fetch-depth: '0' +preflight: + run_erc: true + run_drc: true + check_zone_fills: true + ignore_unconnected: false + update_xml: true + erc: + warnings_as_errors: false + # Continue even if ERC fails + dont_stop: true + drc: + warnings_as_errors: false + # Continue even if DRC fails + dont_stop: true - - name: Find KiCad project - id: find_project - run: | - # Find all .kicad_pcb files in the repository - PCB_FILES=$(find . -name "*.kicad_pcb" -type f | grep -v -E '/(backups?|backup|old|archive|deprecated)/' | head -5) - - if [ -z "$PCB_FILES" ]; then - echo "Error: No KiCad PCB files found" - exit 1 - fi - - # Use the first PCB file found - PCB_FILE=$(echo "$PCB_FILES" | head -1) - PROJECT_DIR=$(dirname "$PCB_FILE") - PROJECT_NAME=$(basename "$PCB_FILE" .kicad_pcb) - - echo "Found KiCad project: $PROJECT_NAME" - echo "Project directory: $PROJECT_DIR" - echo "PCB file: $PCB_FILE" - - # Find schematic file - SCH_FILE=$(find "$PROJECT_DIR" -name "*.kicad_sch" -o -name "*.sch" | head -1) - - echo "pcb_file=$PCB_FILE" >> $GITHUB_OUTPUT - echo "project_dir=$PROJECT_DIR" >> $GITHUB_OUTPUT - echo "project_name=$PROJECT_NAME" >> $GITHUB_OUTPUT - echo "sch_file=$SCH_FILE" >> $GITHUB_OUTPUT - - # List all found projects for debugging - echo "All PCB files found:" - echo "$PCB_FILES" +outputs: + # Schematic outputs + - name: 'print_sch' + comment: "Schematic PDF" + type: pdf_sch_print + dir: Schematic + options: + output: '%f-schematic.%x' - - name: Set layer count - id: layers - run: | - # Use workflow input or default to 4 layers - LAYERS="${{ github.event.inputs.board_layers }}" - if [ -z "$LAYERS" ]; then - LAYERS="4" # Change this default if needed - fi - echo "layers=$LAYERS" >> $GITHUB_OUTPUT - echo "Using $LAYERS layer configuration" + - name: 'print_sch_svg' + comment: "Schematic SVG" + type: svg_sch_print + dir: Schematic + options: + output: '%f-schematic.%x' - - name: Check for KiBot config - id: config_check - run: | - # Check if kibot.yaml exists in repository root - if [ -f "kibot.yaml" ]; then - echo "found=true" >> $GITHUB_OUTPUT - echo "Using kibot.yaml configuration from repository root" - else - echo "found=false" >> $GITHUB_OUTPUT - echo "No kibot.yaml found in repository root - will use --quick-start mode" - fi + # PCB 2D renders + - name: 'pcb_top' + comment: "PCB render top" + type: pcbdraw + dir: PCB/2D_render + options: + output: '%f-top.%x' + format: svg + show_components: all + dpi: 300 - - name: Run ERC - if: steps.config_check.outputs.found == 'true' - run: | - cd "${{ steps.find_project.outputs.project_dir }}" - - if [ -n "${{ steps.find_project.outputs.sch_file }}" ]; then - echo "Running ERC on schematic files..." - # Use absolute path to config file in repo root - kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_erc -v || true - else - echo "No schematic files found, skipping ERC" - fi - continue-on-error: true + - name: 'pcb_bottom' + comment: "PCB render bottom" + type: pcbdraw + dir: PCB/2D_render + options: + output: '%f-bottom.%x' + format: svg + bottom: true + show_components: all + dpi: 300 - - name: Run DRC - if: steps.config_check.outputs.found == 'true' - run: | - cd "${{ steps.find_project.outputs.project_dir }}" - - echo "Running DRC on PCB..." - # Use absolute path to config file in repo root - kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_drc -v || true - continue-on-error: true + # PCB PDF documentation + - name: 'print_pcb' + comment: "PCB PDF" + type: pdf_pcb_print + dir: PCB/PDF + options: + output: '%f-pcb.%x' + plot_sheet_reference: true + monochrome: false + pages: + - layers: + - F.Cu + - F.Paste + - F.Silkscreen + - Edge.Cuts + sheet: 'Front copper' + - layers: + - B.Cu + - B.Paste + - B.Silkscreen + - Edge.Cuts + mirror: true + sheet: 'Bottom copper' + - layers: + - In1.Cu + - Edge.Cuts + sheet: 'Inner layer 1' + skip_if_no_layer: true + - layers: + - In2.Cu + - Edge.Cuts + sheet: 'Inner layer 2' + skip_if_no_layer: true + - layers: + - In3.Cu + - Edge.Cuts + sheet: 'Inner layer 3' + skip_if_no_layer: true + - layers: + - In4.Cu + - Edge.Cuts + sheet: 'Inner layer 4' + skip_if_no_layer: true - - name: Generate outputs with custom config - if: steps.config_check.outputs.found == 'true' - run: | - cd "${{ steps.find_project.outputs.project_dir }}" - - echo "Generating outputs for ${{ steps.layers.outputs.layers }} layer board" - echo "Project: ${{ steps.find_project.outputs.project_name }}" - echo "Using config: ${GITHUB_WORKSPACE}/kibot.yaml" - - # Run KiBot with the configuration from repo root - # Skip ERC/DRC preflight checks to avoid stopping on errors - kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_erc,run_drc -d Fabrication -v - - # Move outputs to root Generated folder - mkdir -p "${GITHUB_WORKSPACE}/Generated" - if [ -d "Fabrication" ]; then - cp -r Fabrication/* "${GITHUB_WORKSPACE}/Generated/" - fi + # Gerber files - automatically handles 2/4/6 layers + - name: 'gerbers' + comment: "Gerber files" + type: gerber + dir: Gerbers + options: + output: '%f%i.%x' + exclude_edge_layer: true + exclude_pads_from_silkscreen: true + plot_sheet_reference: false + plot_footprint_refs: true + plot_footprint_values: false + force_plot_invisible_refs_vals: false + tent_vias: true + use_protel_extensions: false + create_gerber_job_file: true + disable_aperture_macros: true + gerber_precision: 4.6 + use_gerber_x2_attributes: false + use_gerber_net_attributes: false + line_width: 0.1 + subtract_mask_from_silk: true + layers: + # Copper layers - automatically included if present + - F.Cu + - B.Cu + - In1.Cu + - In2.Cu + - In3.Cu + - In4.Cu + # Technical layers + - F.Paste + - B.Paste + - F.Silkscreen + - B.Silkscreen + - F.Mask + - B.Mask + - Edge.Cuts + - User.Comments + - F.Fab + - B.Fab - - name: Quick Start fallback - if: steps.config_check.outputs.found == 'false' - run: | - cd "${{ steps.find_project.outputs.project_dir }}" - echo "Running KiBot in quick-start mode" - kibot --quick-start - - # Move outputs to root Generated folder - mkdir -p "${GITHUB_WORKSPACE}/Generated" - if [ -d "Generated" ]; then - cp -r Generated/* "${GITHUB_WORKSPACE}/Generated/" - fi + # Excellon drill files + - name: 'drill' + comment: "Drill files" + type: excellon + dir: Gerbers + options: + output: '%f%i.%x' + metric_units: true + minimal_header: false + mirror_y_axis: false + report: + filename: '%f-drill_report.txt' + pth_and_npth_single_file: false - - name: Add board information to outputs - run: | - echo "Board configuration: ${{ steps.layers.outputs.layers }} layers" > Generated/board_info.txt - echo "Project: ${{ steps.find_project.outputs.project_name }}" >> Generated/board_info.txt - echo "Project path: ${{ steps.find_project.outputs.project_dir }}" >> Generated/board_info.txt - echo "Generated on: $(date)" >> Generated/board_info.txt - echo "Commit: ${{ github.sha }}" >> Generated/board_info.txt + # Drill map + - name: 'drill_map' + comment: "Drill map" + type: gerb_drill + dir: Gerbers + options: + output: '%f-drill_map.%x' - - name: Fix file permissions - run: | - chmod -R a+rw Generated || true + # Pick and place files + - name: 'position' + comment: "Pick and place file" + type: position + dir: Assembly + options: + output: '%f-position-%i.%x' # Added %i to differentiate front/back files + format: CSV + units: millimeters + separate_files_for_front_and_back: true + only_smd: false - - name: Retrieve results - All outputs - uses: actions/upload-artifact@v3 - with: - name: Automatic_outputs - path: Generated + # Interactive BOM + - name: 'ibom' + comment: "Interactive BOM" + type: ibom + dir: Assembly + options: + output: '%f-ibom.%x' + dark_mode: false + hide_pads: false + show_fabrication: false + hide_silkscreen: false + highlight_pin1: true + no_redraw_on_drag: false + board_rotation: 0 + check_extra_fields: false + include_tracks: false + include_nets: false - - name: Retrieve results - Gerbers - if: steps.config_check.outputs.found == 'true' - uses: actions/upload-artifact@v3 - with: - name: Gerbers-${{ steps.layers.outputs.layers }}layer - path: Generated/Gerbers + # Generic BOM in CSV format + - name: 'bom_csv' + comment: "Bill of Materials in CSV format" + type: bom + dir: BoM/Generic + options: + output: '%f_bom.%x' + format: CSV + separator: ',' + ref_separator: ',' + group_fields: ['Value', 'Footprint', 'Tolerance', 'Voltage'] + columns: + - field: 'References' + name: 'Reference' + - field: 'Value' + name: 'Value' + - field: 'Quantity Per PCB' + name: 'Qty' + - field: 'Footprint' + name: 'Package' + - field: 'Description' + name: 'Description' + - field: 'Manufacturer' + name: 'Manufacturer' + - field: 'MPN' + name: 'Part Number' + - field: 'LCSC' + name: 'LCSC Part' + - field: 'Digikey' + name: 'Digikey Part' + - field: 'Mouser' + name: 'Mouser Part' - - name: Retrieve results - Assembly - if: steps.config_check.outputs.found == 'true' - uses: actions/upload-artifact@v3 - with: - name: Assembly-${{ steps.layers.outputs.layers }}layer - path: Generated/Assembly + # Generic BOM in HTML format + - name: 'bom_html' + comment: "Bill of Materials in HTML format" + type: bom + dir: BoM/Generic + options: + output: '%f_bom.%x' + format: HTML + html: + style: 'modern-blue' # Style for HTML output + datasheet_as_link: 'Datasheet' # Column name for datasheet links + generate_dnf: true + logo: false + title: 'Bill of Materials - %f' + group_fields: ['Value', 'Footprint', 'Tolerance', 'Voltage'] + columns: + - field: 'References' + name: 'Reference' + - field: 'Value' + name: 'Value' + - field: 'Quantity Per PCB' + name: 'Qty' + - field: 'Footprint' + name: 'Package' + - field: 'Description' + name: 'Description' + - field: 'Manufacturer' + name: 'Manufacturer' + - field: 'MPN' + name: 'Part Number' + - field: 'LCSC' + name: 'LCSC Part' + - field: 'Digikey' + name: 'Digikey Part' + - field: 'Mouser' + name: 'Mouser Part' - - name: Retrieve results - BOM - if: steps.config_check.outputs.found == 'true' - uses: actions/upload-artifact@v3 - with: - name: BOM-${{ steps.layers.outputs.layers }}layer - path: Generated/BoM + # Generic BOM in XLSX format + - name: 'bom_xlsx' + comment: "Bill of Materials in Excel format" + type: bom + dir: BoM/Generic + options: + output: '%f_bom.%x' + format: XLSX + xlsx: + datasheet_as_link: 'Datasheet' # Column name for datasheet links + generate_dnf: true + logo: false + title: 'Bill of Materials' + max_col_width: 50 + highlight_empty: true + group_fields: ['Value', 'Footprint', 'Tolerance', 'Voltage'] + columns: + - field: 'References' + name: 'Reference' + - field: 'Value' + name: 'Value' + - field: 'Quantity Per PCB' + name: 'Qty' + - field: 'Footprint' + name: 'Package' + - field: 'Description' + name: 'Description' + - field: 'Manufacturer' + name: 'Manufacturer' + - field: 'MPN' + name: 'Part Number' + - field: 'LCSC' + name: 'LCSC Part' + - field: 'Digikey' + name: 'Digikey Part' + - field: 'Mouser' + name: 'Mouser Part' + - field: 'Datasheet' + name: 'Datasheet' - - name: Retrieve results - 3D Model - if: steps.config_check.outputs.found == 'true' - uses: actions/upload-artifact@v3 - with: - name: 3D-Model - path: Generated/3D + # Download datasheets + - name: 'download_datasheets' + comment: "Download component datasheets" + type: download_datasheets + dir: BoM/Datasheets + run_by_default: true + options: + # Don't specify output - let KiBot handle the downloaded files + # output: '%f_datasheets.csv' # Removed - this was causing the issue + field: 'Datasheet' # Field containing datasheet URLs + dnf_filter: '_none' # Don't filter DNF parts + download: true # Actually download the files (not just list them) + link_repeated: true # Create links for repeated datasheets + repeated: false # Don't download the same datasheet twice - - name: Retrieve results - Fabrication Package - if: steps.config_check.outputs.found == 'true' - uses: actions/upload-artifact@v3 - with: - name: Fabrication-Package-${{ steps.layers.outputs.layers }}layer - path: Generated/*.zip + # 3D model + - name: 'step' + comment: "3D STEP model" + type: step + dir: 3D + options: + output: '%f-3D.%x' + metric_units: true + origin: grid + no_virtual: true - - name: Retrieve results - JLCPCB Package - if: steps.config_check.outputs.found == 'true' - uses: actions/upload-artifact@v3 - with: - name: JLCPCB-${{ steps.layers.outputs.layers }}layer - path: Generated/*_JLCPCB_compress.zip + # Board characteristics report + - name: 'report' + comment: "Board report" + type: report + dir: . + options: + output: '%f-report.%x' + do_convert: true - - name: Retrieve results - Diff Report - if: steps.generate_diff.outputs.has_diff == 'true' - uses: actions/upload-artifact@v3 - with: - name: Schematic-PCB-Diff - path: Generated/DIFF + # Schematic diff + - name: 'sch_diff' + comment: "Schematic diff PDF" + type: diff + dir: DIFF + run_by_default: true + options: + output: '%f-schematic-diff.%x' + format: 'pdf' # Output format: pdf, svg, ps, or png + pcb: false # This is for schematic + old: 'HEAD~1' # Old version to compare (previous commit) + new: 'HEAD' # New version (current) + cache_dir: '.cache' + add_link_id: true # Add link IDs to the generated diff + only_different: true # Only include pages that changed + threshold: 0 # Difference threshold (0 = any difference) + fuzz: 5 # Fuzzy matching tolerance in pixels + diff_mode: 'red_green' # How to show differences + old_color: '#FF0000' # Color for old/removed items + new_color: '#00FF00' # Color for new/added items + + # PCB diff + - name: 'pcb_diff' + comment: "PCB diff PDF" + type: diff + dir: DIFF + run_by_default: true + options: + output: '%f-pcb-diff.%x' + format: 'pdf' # Output format + pcb: true # This is for PCB + old: 'HEAD~1' # Old version to compare + new: 'HEAD' # New version + cache_dir: '.cache' + add_link_id: false # Not used for PCB + only_different: true # Only include layers that changed + threshold: 0 # Difference threshold + fuzz: 5 # Fuzzy matching tolerance + diff_mode: 'red_green' # Visual diff mode + old_color: '#FF0000' # Color for removed items + new_color: '#00FF00' # Color for added items + layers: # Which PCB layers to include in diff + - 'F.Cu' + - 'B.Cu' + - 'In1.Cu' + - 'In2.Cu' + - 'In3.Cu' + - 'In4.Cu' + - 'F.Silkscreen' + - 'B.Silkscreen' + - 'F.Mask' + - 'B.Mask' + - 'Edge.Cuts' + - 'F.Courtyard' + - 'B.Courtyard' + - 'F.Fab' + - 'B.Fab' - # Deploy to documentation branch - deploy: - runs-on: kicad-kibot-runner - needs: generate + # Interactive diff using KiRi (HTML output) + - name: 'interactive_diff' + comment: "Interactive HTML diff" + type: diff + dir: DIFF + run_by_default: false # Optional - can be slow + options: + output: '%f-interactive-diff.%x' + format: 'html' # HTML format for interactive viewing + pcb: true + old: 'HEAD~1' + new: 'HEAD' + cache_dir: '.cache' + only_different: true + diff_mode: 'stats' # Show statistics in HTML + + # SVG diff for web viewing + - name: 'svg_diff' + comment: "SVG diff for web" + type: diff + dir: DIFF + run_by_default: false + options: + output: '%f-diff.%x' + format: 'svg' # SVG format for web embedding + pcb: true + old: 'HEAD~1' + new: 'HEAD' + cache_dir: '.cache' + only_different: true + diff_mode: 'red_green' + old_color: '#FF0000' + new_color: '#00FF00' + zones: 'fill' # How to handle zones: fill, outline, or none - steps: - - uses: actions/checkout@v4 + # JLCPCB fabrication package + - name: 'jlcpcb_gerbers' + comment: "Gerbers for JLCPCB" + type: gerber + dir: JLCPCB + options: + output: '%f%i.%x' + exclude_edge_layer: false + exclude_pads_from_silkscreen: true + plot_sheet_reference: false + plot_footprint_refs: true + plot_footprint_values: false + force_plot_invisible_refs_vals: false + tent_vias: true + use_protel_extensions: true # JLCPCB prefers Protel extensions + create_gerber_job_file: false # JLCPCB doesn't need this + disable_aperture_macros: true + gerber_precision: 4.6 + use_gerber_x2_attributes: false + use_gerber_net_attributes: false + line_width: 0.1 + subtract_mask_from_silk: true + layers: + - F.Cu + - B.Cu + - In1.Cu + - In2.Cu + - In3.Cu + - In4.Cu + - F.Paste + - B.Paste + - F.Silkscreen + - B.Silkscreen + - F.Mask + - B.Mask + - Edge.Cuts - - name: Retrieve results - uses: actions/download-artifact@v3 - with: - name: Automatic_outputs - path: Generated + # JLCPCB drill files + - name: 'jlcpcb_drill' + comment: "Drill files for JLCPCB" + type: excellon + dir: JLCPCB + options: + output: '%f%i.%x' + metric_units: true + minimal_header: false + mirror_y_axis: false + pth_and_npth_single_file: false # JLCPCB prefers separate files + + # JLCPCB ZIP file + - name: 'jlcpcb_zip' + comment: "JLCPCB fabrication ZIP" + type: compress + dir: . + options: + output: '%f_JLCPCB_compress.%x' + format: ZIP + files: + - source: JLCPCB/* + dest: / - - name: Disable Jekyll - # Jekyll will filter the KiRi folders - run: | - touch Generated/.nojekyll + # Fabrication package (ZIP) + - name: 'fabrication' + comment: "Fabrication package" + type: compress + dir: . + options: + output: '%f-fabrication.%x' + format: ZIP + files: + - source: Gerbers/* + dest: / + - source: PCB/PDF/* + dest: /Documentation + - source: Assembly/* + dest: /Assembly + - source: 3D/* + dest: /3D - - name: Push to docu branch - env: - GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} - run: | - # Set up git identity - git config --global user.email "${{ gitea.actor }}@noreply.localhost" - git config --global user.name "${{ gitea.actor }}" - - # Get the repository URL and add authentication - REPO_URL=$(git config --get remote.origin.url) - AUTHED_URL=${REPO_URL/https:\/\//https:\/\/${GITEA_TOKEN}@} - - # Clone the docu branch into a separate folder - git clone --depth 1 --branch docu "$AUTHED_URL" docu-branch || \ - git clone --depth 1 "$AUTHED_URL" docu-branch - - cd docu-branch - git checkout -B docu - - # Clean old contents and copy new files - rm -rf ./* - cp -r ../Generated/* ./ - - # Commit and push if there are changes - git add . - if ! git diff --cached --quiet; then - git commit -m "chore: update deployed documentation" - git push --force "$AUTHED_URL" docu - else - echo "No changes to deploy" - fi \ No newline at end of file +# Variants for different layer counts (optional) +variants: + - name: 2layer + comment: "2 layer board variant" + type: kibom + + - name: 4layer + comment: "4 layer board variant" + type: kibom + + - name: 6layer + comment: "6 layer board variant" + type: kibom \ No newline at end of file From c8e56447bf4845cafb7d10e53e32b9404f404f9c Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 16:40:36 +0700 Subject: [PATCH 21/25] dl and diff --- kibot.yaml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/kibot.yaml b/kibot.yaml index 9050833..1a7842a 100644 --- a/kibot.yaml +++ b/kibot.yaml @@ -328,14 +328,12 @@ outputs: type: download_datasheets dir: BoM/Datasheets run_by_default: true + priority: 60 + category: 'Schematic/docs' options: - # Don't specify output - let KiBot handle the downloaded files - # output: '%f_datasheets.csv' # Removed - this was causing the issue field: 'Datasheet' # Field containing datasheet URLs + output: 'datasheet_index.csv' # CSV file listing downloads dnf_filter: '_none' # Don't filter DNF parts - download: true # Actually download the files (not just list them) - link_repeated: true # Create links for repeated datasheets - repeated: false # Don't download the same datasheet twice # 3D model - name: 'step' @@ -362,7 +360,10 @@ outputs: comment: "Schematic diff PDF" type: diff dir: DIFF + category: 'Schematic/docs' run_by_default: true + priority: 50 + layers: [] # Empty for schematic (not PCB) options: output: '%f-schematic-diff.%x' format: 'pdf' # Output format: pdf, svg, ps, or png @@ -383,7 +384,10 @@ outputs: comment: "PCB diff PDF" type: diff dir: DIFF + category: 'PCB/docs' run_by_default: true + priority: 50 + layers: 'selected' # Use selected layers below options: output: '%f-pcb-diff.%x' format: 'pdf' # Output format @@ -420,7 +424,10 @@ outputs: comment: "Interactive HTML diff" type: diff dir: DIFF + category: 'PCB/docs' run_by_default: false # Optional - can be slow + priority: 40 + layers: 'all' # Include all layers for interactive view options: output: '%f-interactive-diff.%x' format: 'html' # HTML format for interactive viewing @@ -436,7 +443,10 @@ outputs: comment: "SVG diff for web" type: diff dir: DIFF + category: 'PCB/docs' run_by_default: false + priority: 40 + layers: 'copper' # Only copper layers for web view options: output: '%f-diff.%x' format: 'svg' # SVG format for web embedding From a33729aeed6b7e282d6eb40dcd401d0bdeb814a5 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 16:48:47 +0700 Subject: [PATCH 22/25] ds dl --- .gitea/workflows/kibot.yml | 35 ++++++++++++++++++++++++++++++++++- kibot.yaml | 27 ++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index 02b17d3..b1fd327 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -1,4 +1,30 @@ -name: "KiBot PCB Generation" +- name: Download component datasheets + if: steps.config_check.outputs.found == 'true' + continue-on-error: true + run: | + echo "Datasheets will be downloaded by KiBot's download_datasheets output" + echo "They will be saved in Generated/BoM/Datasheets/" + + # Create a README for the datasheets folder if KiBot creates it + if [ -d "${GITHUB_WORKSPACE}/Generated/BoM/Datasheets" ]; then + cat > "${GITHUB_WORKSPACE}/Generated/BoM/Datasheets/README.md" << EOF + # Component Datasheets + + This folder contains downloaded datasheets for components used in the project. + + - Datasheets are downloaded automatically from URLs in the schematic + - Files are named based on the component reference and original filename + - Only unique datasheets are downloaded (duplicates are linked) + + Generated on: $(date) + Project: ${{ steps.find_project.outputs.project_name }} + EOF + + echo "Datasheet folder contents:" + ls -la "${GITHUB_WORKSPACE}/Generated/BoM/Datasheets/" || true + else + echo "Note: Datasheet folder not created yet - will be created by KiBot" + finame: "KiBot PCB Generation" # Controls when the action will run on: @@ -191,6 +217,13 @@ jobs: name: BOM-${{ steps.layers.outputs.layers }}layer path: Generated/BoM + - name: Retrieve results - Datasheets + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: Datasheets + path: Generated/Datasheets + - name: Retrieve results - 3D Model if: steps.config_check.outputs.found == 'true' uses: actions/upload-artifact@v3 diff --git a/kibot.yaml b/kibot.yaml index 1a7842a..913229c 100644 --- a/kibot.yaml +++ b/kibot.yaml @@ -324,16 +324,33 @@ outputs: # Download datasheets - name: 'download_datasheets' - comment: "Download component datasheets" + comment: "Download component datasheets as PDFs" type: download_datasheets - dir: BoM/Datasheets + dir: Datasheets # Put in root Datasheets folder run_by_default: true priority: 60 category: 'Schematic/docs' options: - field: 'Datasheet' # Field containing datasheet URLs - output: 'datasheet_index.csv' # CSV file listing downloads - dnf_filter: '_none' # Don't filter DNF parts + field: 'Datasheet' # Field containing the datasheet URLs + output: '${REFERENCE}_${VALUE}.pdf' # Name pattern for downloaded files + dnf: false # Don't include DNF components + dnf_filter: '_none' # No DNF filtering + link_repeated: true # Use symlinks for repeated datasheets + repeated: false # Don't re-download same URL + classify: true # Organize by component type (Capacitors/, Resistors/, etc.) + classify_extra: # Additional classification rules + 'U': 'ICs' + 'Q': 'Transistors' + 'D': 'Diodes' + 'L': 'Inductors' + 'F': 'Fuses' + 'J': 'Connectors' + 'P': 'Connectors' + 'SW': 'Switches' + 'Y': 'Crystals' + 'T': 'Transformers' + 'RV': 'Varistors' + 'FB': 'Ferrite_Beads' # 3D model - name: 'step' From 7c5035d0914e3a1d45b45d959142d6d757d5ebf3 Mon Sep 17 00:00:00 2001 From: Alexander Grabowski Date: Mon, 8 Sep 2025 16:51:58 +0700 Subject: [PATCH 23/25] Update .gitea/workflows/kibot.yml --- .gitea/workflows/kibot.yml | 294 ++++++++++++++++++++++++++++++++----- 1 file changed, 259 insertions(+), 35 deletions(-) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index b1fd327..60f24f4 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -1,30 +1,4 @@ -- name: Download component datasheets - if: steps.config_check.outputs.found == 'true' - continue-on-error: true - run: | - echo "Datasheets will be downloaded by KiBot's download_datasheets output" - echo "They will be saved in Generated/BoM/Datasheets/" - - # Create a README for the datasheets folder if KiBot creates it - if [ -d "${GITHUB_WORKSPACE}/Generated/BoM/Datasheets" ]; then - cat > "${GITHUB_WORKSPACE}/Generated/BoM/Datasheets/README.md" << EOF - # Component Datasheets - - This folder contains downloaded datasheets for components used in the project. - - - Datasheets are downloaded automatically from URLs in the schematic - - Files are named based on the component reference and original filename - - Only unique datasheets are downloaded (duplicates are linked) - - Generated on: $(date) - Project: ${{ steps.find_project.outputs.project_name }} - EOF - - echo "Datasheet folder contents:" - ls -la "${GITHUB_WORKSPACE}/Generated/BoM/Datasheets/" || true - else - echo "Note: Datasheet folder not created yet - will be created by KiBot" - finame: "KiBot PCB Generation" +name: "KiBot PCB Generation" # Controls when the action will run on: @@ -49,12 +23,27 @@ on: board_layers: description: 'Number of PCB layers' required: false - default: '2' + default: '4' type: choice options: - '2' - '4' - '6' + diff_old: + description: 'Old revision for diff (e.g., HEAD~1, commit hash, tag)' + required: false + default: 'HEAD~1' + type: string + diff_new: + description: 'New revision for diff (e.g., HEAD, commit hash, tag)' + required: false + default: 'HEAD' + type: string + enable_interactive_diff: + description: 'Generate interactive HTML diff' + required: false + default: false + type: boolean # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: @@ -64,7 +53,7 @@ jobs: steps: - uses: actions/checkout@v3 with: - # So we can run a diff between last 2 changes + # Fetch full history for diff generation fetch-depth: '0' - name: Find KiCad project @@ -105,7 +94,7 @@ jobs: # Use workflow input or default to 4 layers LAYERS="${{ github.event.inputs.board_layers }}" if [ -z "$LAYERS" ]; then - LAYERS="4" # Change this default if needed + LAYERS="4" fi echo "layers=$LAYERS" >> $GITHUB_OUTPUT echo "Using $LAYERS layer configuration" @@ -122,15 +111,158 @@ jobs: echo "No kibot.yaml found in repository root - will use --quick-start mode" fi + - name: Generate schematic diff + id: generate_diff + if: github.event_name == 'push' || github.event_name == 'pull_request' + run: | + PROJECT_DIR="${{ steps.find_project.outputs.project_dir }}" + PROJECT_NAME="${{ steps.find_project.outputs.project_name }}" + + # Create DIFF output directory + mkdir -p "${GITHUB_WORKSPACE}/Generated/DIFF" + cd "$PROJECT_DIR" + + # Get the previous commit hash + if [ "${{ github.event_name }}" = "pull_request" ]; then + BASE_SHA="${{ github.event.pull_request.base.sha }}" + HEAD_SHA="${{ github.event.pull_request.head.sha }}" + else + # For push events, compare with previous commit + HEAD_SHA="${{ github.sha }}" + BASE_SHA=$(git rev-parse HEAD~1 2>/dev/null || echo "") + fi + + if [ -z "$BASE_SHA" ]; then + echo "No previous commit found for comparison" + echo "has_diff=false" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "Comparing commits: $BASE_SHA...$HEAD_SHA" + + # Find all schematic files + SCH_FILES=$(find . -name "*.kicad_sch" -o -name "*.sch" | head -10) + + if [ -z "$SCH_FILES" ]; then + echo "No schematic files found" + echo "has_diff=false" >> $GITHUB_OUTPUT + exit 0 + fi + + # Generate diff for each schematic + DIFF_GENERATED=false + for SCH_FILE in $SCH_FILES; do + SCH_NAME=$(basename "$SCH_FILE" | sed 's/\.[^.]*$//') + + # Check if file changed between commits + if git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep -q "$SCH_FILE"; then + echo "Generating diff for $SCH_FILE" + + # Create temp directory for processing + TEMP_DIR=$(mktemp -d) + + # Export old version + git show "$BASE_SHA:$SCH_FILE" > "$TEMP_DIR/${SCH_NAME}_old.kicad_sch" 2>/dev/null || continue + + # Copy current version + cp "$SCH_FILE" "$TEMP_DIR/${SCH_NAME}_new.kicad_sch" + + # Try to use kidiff if available (better visual diff tool for KiCad) + if command -v kidiff &> /dev/null; then + kidiff -o "${GITHUB_WORKSPACE}/Generated/DIFF/${SCH_NAME}_diff.pdf" \ + "$TEMP_DIR/${SCH_NAME}_old.kicad_sch" \ + "$TEMP_DIR/${SCH_NAME}_new.kicad_sch" || true + fi + + # Also generate text diff + git diff "$BASE_SHA" "$HEAD_SHA" -- "$SCH_FILE" > "${GITHUB_WORKSPACE}/Generated/DIFF/${SCH_NAME}_changes.txt" + + # Generate a simple HTML diff for easy viewing + cat > "${GITHUB_WORKSPACE}/Generated/DIFF/${SCH_NAME}_diff.html" << EOF + + + + Schematic Diff: ${SCH_NAME} + + + +

Schematic Changes: ${SCH_NAME}

+
Commit: ${BASE_SHA:0:8} → ${HEAD_SHA:0:8}
+
+        EOF
+            
+            # Add the diff content with HTML escaping
+            git diff "$BASE_SHA" "$HEAD_SHA" -- "$SCH_FILE" | \
+              sed 's/&/\&/g; s//\>/g' | \
+              sed 's/^+[^+].*/&<\/span>/g' | \
+              sed 's/^-[^-].*/&<\/span>/g' >> "${GITHUB_WORKSPACE}/Generated/DIFF/${SCH_NAME}_diff.html"
+            
+            echo "
" >> "${GITHUB_WORKSPACE}/Generated/DIFF/${SCH_NAME}_diff.html" + + rm -rf "$TEMP_DIR" + DIFF_GENERATED=true + fi + done + + # Generate PCB diff if PCB file changed + PCB_FILE="${{ steps.find_project.outputs.pcb_file }}" + if git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep -q "$PCB_FILE"; then + echo "Generating PCB diff" + PCB_NAME=$(basename "$PCB_FILE" | sed 's/\.[^.]*$//') + + # Generate text diff for PCB + git diff "$BASE_SHA" "$HEAD_SHA" -- "$PCB_FILE" > "${GITHUB_WORKSPACE}/Generated/DIFF/${PCB_NAME}_pcb_changes.txt" + + # Summary of changes + git diff --stat "$BASE_SHA" "$HEAD_SHA" -- "$PCB_FILE" > "${GITHUB_WORKSPACE}/Generated/DIFF/${PCB_NAME}_pcb_summary.txt" + + DIFF_GENERATED=true + fi + + # Create a summary file + cat > "${GITHUB_WORKSPACE}/Generated/DIFF/README.md" << EOF + # KiCad Project Diff Report + + **Base Commit:** ${BASE_SHA:0:8} + **Head Commit:** ${HEAD_SHA:0:8} + **Date:** $(date) + + ## Changed Files: + $(git diff --name-status "$BASE_SHA" "$HEAD_SHA" -- "*.kicad_sch" "*.sch" "*.kicad_pcb" "*.pcb") + + ## Summary: + $(git diff --stat "$BASE_SHA" "$HEAD_SHA" -- "*.kicad_sch" "*.sch" "*.kicad_pcb" "*.pcb") + + ## Commit Messages: + $(git log --oneline "$BASE_SHA".."$HEAD_SHA") + EOF + + if [ "$DIFF_GENERATED" = true ]; then + echo "has_diff=true" >> $GITHUB_OUTPUT + else + echo "No schematic or PCB changes detected" + echo "has_diff=false" >> $GITHUB_OUTPUT + fi + - name: Run ERC if: steps.config_check.outputs.found == 'true' run: | cd "${{ steps.find_project.outputs.project_dir }}" + # Clean up any existing output directories + rm -rf Fabrication Generated Output + if [ -n "${{ steps.find_project.outputs.sch_file }}" ]; then echo "Running ERC on schematic files..." # Use absolute path to config file in repo root - kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_erc -v || true + # Skip ERC step in preflight to avoid stopping on errors + kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_erc,run_drc -v || true else echo "No schematic files found, skipping ERC" fi @@ -143,7 +275,8 @@ jobs: echo "Running DRC on PCB..." # Use absolute path to config file in repo root - kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_drc -v || true + # Skip both ERC and DRC in preflight to avoid stopping on errors + kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_erc,run_drc -v || true continue-on-error: true - name: Generate outputs with custom config @@ -155,14 +288,105 @@ jobs: echo "Project: ${{ steps.find_project.outputs.project_name }}" echo "Using config: ${GITHUB_WORKSPACE}/kibot.yaml" - # Run KiBot with the configuration from repo root + # First, let's check what's in the current directory + echo "Current directory contents:" + ls -la + + # Check if Fabrication exists and what it is + if [ -e "Fabrication" ]; then + echo "Fabrication exists as:" + file Fabrication + echo "Removing it..." + rm -rf Fabrication + fi + + # Create a temporary modified config without the global output setting + cp "${GITHUB_WORKSPACE}/kibot.yaml" /tmp/kibot_temp.yaml + + # Remove or comment out the global output line if it exists + sed -i 's/^ output:.*$/ # output: commented out for workflow/' /tmp/kibot_temp.yaml + + # Update diff revisions if specified + if [ -n "${{ github.event.inputs.diff_old }}" ]; then + sed -i "s/old: 'HEAD~1'/old: '${{ github.event.inputs.diff_old }}'/" /tmp/kibot_temp.yaml + fi + if [ -n "${{ github.event.inputs.diff_new }}" ]; then + sed -i "s/new: 'HEAD'/new: '${{ github.event.inputs.diff_new }}'/" /tmp/kibot_temp.yaml + fi + + # Use a timestamp-based directory to ensure uniqueness + OUTPUT_DIR="kibot_output_$(date +%s)" + + # Determine which outputs to run + EXTRA_OUTPUTS="" + if [ "${{ github.event.inputs.enable_interactive_diff }}" = "true" ]; then + EXTRA_OUTPUTS="interactive_diff svg_diff" + fi + + # Run KiBot with the modified configuration # Skip ERC/DRC preflight checks to avoid stopping on errors - kibot -c "${GITHUB_WORKSPACE}/kibot.yaml" -s run_erc,run_drc -d Fabrication -v + # Explicitly run diff and datasheet download outputs + echo "Running KiBot with output directory: $OUTPUT_DIR" + kibot -c /tmp/kibot_temp.yaml -s run_erc,run_drc -d "$OUTPUT_DIR" -v \ + sch_diff pcb_diff download_datasheets $EXTRA_OUTPUTS || \ + kibot -c /tmp/kibot_temp.yaml -s run_erc,run_drc -d "$OUTPUT_DIR" -v # Move outputs to root Generated folder mkdir -p "${GITHUB_WORKSPACE}/Generated" - if [ -d "Fabrication" ]; then + if [ -d "$OUTPUT_DIR" ]; then + echo "Copying outputs from $OUTPUT_DIR to Generated/" + cp -r "$OUTPUT_DIR"/* "${GITHUB_WORKSPACE}/Generated/" + rm -rf "$OUTPUT_DIR" + elif [ -d "Fabrication" ]; then + echo "KiBot used default Fabrication directory, copying from there" cp -r Fabrication/* "${GITHUB_WORKSPACE}/Generated/" + rm -rf Fabrication + else + echo "Warning: No output directory was created" + fi + + - name: Check downloaded datasheets + if: steps.config_check.outputs.found == 'true' + continue-on-error: true + run: | + echo "Checking for downloaded datasheets..." + + # KiBot should download datasheets to the configured directory + # Check if datasheets were downloaded + if [ -d "${GITHUB_WORKSPACE}/Generated/Datasheets" ]; then + echo "Datasheets folder found in Generated/" + + # Create README + cat > "${GITHUB_WORKSPACE}/Generated/Datasheets/README.md" << EOF + # Component Datasheets + + This folder contains downloaded datasheets for all components in the project. + + Organized by component type: + - **ICs/** - Integrated circuits (U references) + - **Capacitors/** - Capacitors (C references) + - **Resistors/** - Resistors (R references) + - **Transistors/** - Transistors (Q references) + - **Diodes/** - Diodes (D references) + - **Connectors/** - Connectors (J, P references) + - **Inductors/** - Inductors (L references) + - And more... + + Files are named: REFERENCE_VALUE.pdf (e.g., U1_LM7805.pdf) + + Generated on: $(date) + Project: ${{ steps.find_project.outputs.project_name }} + EOF + + echo "Datasheet folder structure:" + find "${GITHUB_WORKSPACE}/Generated/Datasheets" -type f -name "*.pdf" | head -20 + + # Count PDFs + PDF_COUNT=$(find "${GITHUB_WORKSPACE}/Generated/Datasheets" -type f -name "*.pdf" | wc -l) + echo "Total PDFs downloaded: $PDF_COUNT" + else + echo "No Datasheets folder found - KiBot may not have downloaded any datasheets" + echo "Check that components have valid Datasheet URLs in the schematic" fi - name: Quick Start fallback From ce1350ff372e2411cbca48ddafc684e153ac741d Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 17:02:43 +0700 Subject: [PATCH 24/25] activate all tasks --- .gitea/workflows/kibot.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index 60f24f4..b1453d6 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -325,11 +325,17 @@ jobs: # Run KiBot with the modified configuration # Skip ERC/DRC preflight checks to avoid stopping on errors - # Explicitly run diff and datasheet download outputs echo "Running KiBot with output directory: $OUTPUT_DIR" - kibot -c /tmp/kibot_temp.yaml -s run_erc,run_drc -d "$OUTPUT_DIR" -v \ - sch_diff pcb_diff download_datasheets $EXTRA_OUTPUTS || \ - kibot -c /tmp/kibot_temp.yaml -s run_erc,run_drc -d "$OUTPUT_DIR" -v + + # Run all outputs (KiBot will run all by default when no specific outputs are listed) + # Only add extra outputs if interactive diff is enabled + if [ "${{ github.event.inputs.enable_interactive_diff }}" = "true" ]; then + # Run with interactive diff outputs explicitly enabled + kibot -c /tmp/kibot_temp.yaml -s run_erc,run_drc -d "$OUTPUT_DIR" -v interactive_diff svg_diff + else + # Run all default outputs (everything with run_by_default: true) + kibot -c /tmp/kibot_temp.yaml -s run_erc,run_drc -d "$OUTPUT_DIR" -v + fi # Move outputs to root Generated folder mkdir -p "${GITHUB_WORKSPACE}/Generated" From b4788f8e47db1ee7f24108d46a016bbb98fe755a Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 8 Sep 2025 17:26:16 +0700 Subject: [PATCH 25/25] enable kicanvas --- .gitea/workflows/kibot.yml | 7 ++++++ kibot.yaml | 51 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/.gitea/workflows/kibot.yml b/.gitea/workflows/kibot.yml index b1453d6..84ddadc 100644 --- a/.gitea/workflows/kibot.yml +++ b/.gitea/workflows/kibot.yml @@ -475,6 +475,13 @@ jobs: name: JLCPCB-${{ steps.layers.outputs.layers }}layer path: Generated/*_JLCPCB_compress.zip + - name: Retrieve results - Interactive Viewers + if: steps.config_check.outputs.found == 'true' + uses: actions/upload-artifact@v3 + with: + name: Interactive-Viewers + path: Generated/Interactive + - name: Retrieve results - Diff Report if: steps.generate_diff.outputs.has_diff == 'true' uses: actions/upload-artifact@v3 diff --git a/kibot.yaml b/kibot.yaml index 913229c..3997525 100644 --- a/kibot.yaml +++ b/kibot.yaml @@ -556,6 +556,57 @@ outputs: - source: 3D/* dest: /3D + # KiCanvas interactive viewer - Schematic + - name: 'kicanvas_schematic' + comment: "Interactive schematic viewer" + type: kicanvas + dir: Interactive + output: '%f-schematic-viewer.%x' + category: 'Schematic/docs' + run_by_default: true + priority: 45 + options: + source: 'schematic' + local_script: true # Download and use local copy of script + controls: 'full' # Full controls for navigation + download: true # Show download button + overlay: true # Show click overlay + title: '%f Schematic - Interactive Viewer' + + # KiCanvas interactive viewer - PCB + - name: 'kicanvas_pcb' + comment: "Interactive PCB viewer" + type: kicanvas + dir: Interactive + output: '%f-pcb-viewer.%x' + category: 'PCB/docs' + run_by_default: true + priority: 45 + options: + source: 'pcb' + local_script: true # Download and use local copy of script + controls: 'full' # Full controls for navigation + download: true # Show download button + overlay: true # Show click overlay + title: '%f PCB - Interactive Viewer' + + # KiCanvas interactive viewer - Full Project + - name: 'kicanvas_project' + comment: "Interactive project viewer (both schematic and PCB)" + type: kicanvas + dir: Interactive + output: '%f-project-viewer.%x' + category: ['Schematic/docs', 'PCB/docs'] + run_by_default: true + priority: 45 + options: + source: 'project' # Show both schematic and PCB + local_script: true # Download and use local copy of script + controls: 'full' # Full controls for navigation + download: true # Show download button + overlay: true # Show click overlay + title: '%f Project - Interactive Viewer' + # Variants for different layer counts (optional) variants: - name: 2layer