From 614b566d31a42c9d915eb9e0652a9e209da94325 Mon Sep 17 00:00:00 2001 From: timmyhadwen Date: Sat, 17 Jan 2026 10:23:05 +1000 Subject: [PATCH] Add complete KiCad CI pipeline with hfsntree integration Pipeline stages: - preflight: extract_version, run_erc, run_drc - fabrication: schematic, BOM, 3D, gerbers, position, panel - inventree: dry-run on dev, full upload on main - release: package upload and GitLab release Features: - Version extraction from MR title (V1.0, V1.1, etc.) - Version injection into KiBot outputs - Samsung P&P file generation - Panel gerber generation Usage: Include this file in project CI and set PROJECT_NAME variable. Co-Authored-By: Claude Opus 4.5 --- kibot-ci.yml | 692 ++++++++++++++++++++++++++------------------------- 1 file changed, 358 insertions(+), 334 deletions(-) diff --git a/kibot-ci.yml b/kibot-ci.yml index 8998abd..c2b9b63 100644 --- a/kibot-ci.yml +++ b/kibot-ci.yml @@ -1,3 +1,12 @@ +# KiCad CI Pipeline with hfsntree Integration +# Include this file in your project's .gitlab-ci.yml and set PROJECT_NAME variable +# +# Example project .gitlab-ci.yml: +# include: +# - local: '.gitlab/kibot-ci.yml' +# variables: +# PROJECT_NAME: "my_project_name" + workflow: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" @@ -9,19 +18,20 @@ variables: GIT_STRATEGY: clone GIT_SUBMODULE_STRATEGY: recursive GIT_SUBMODULE_FORCE_HTTPS: "true" - # GIT_SUBMODULE_UPDATE_FLAGS: --remote --merge PACKAGE_REGISTRY_URL: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/kicad" stages: - - gen_mech - - gen_fab + - preflight + - fabrication - inventree - - upload - release image: name: ghcr.io/inti-cmnb/kicad9_auto:1.8.4 +# ============================================================================= +# Rule Templates +# ============================================================================= .main_rules: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"' @@ -32,387 +42,401 @@ image: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "dev"' - if: $CI_COMMIT_BRANCH == "dev" + - if: $GITLAB_CI == 'false' -.commands: - get_files: - - FILES=$(find . -name *$SEARCH -not -path "./.gitlab/*") - get_dirs: - - !reference [.commands, get_files] - - | - if [[ $FILES == "" ]]; then - DIRS="" - else - DIRS=$(dirname $FILES) - fi - sch_from_pro: - - 'SCHEMS=$(for f in $FILES ; do echo "${f%.*}.kicad_sch"; done)' - - 'SCHEMS=$(for f in $SCHEMS ; do echo "${f##**/}"; done)' - get_dirs_filt: - - !reference [.commands, get_files] - - | - FILT_FILES="" - FILT_DIRS="" - for f in $FILES - do - dir=$(dirname $f) - echo $dir - if [[ "$dir" =~ ^.?\/?[a-z]?[0-9]{6}-.*$ || "$dir" =~ .+_panel ]] - then - FILT_FILES=$(echo "$FILT_FILES $f") - FILT_DIRS=$(echo "$FILT_DIRS $dir") - fi - done - FILES=$FILT_FILES - DIRS=$FILT_DIRS - dir_arr: - - !reference [.commands, get_dirs_filt] +# ============================================================================= +# Stage: Preflight +# ============================================================================= +extract_version: + stage: preflight + script: - | - END=$(echo $DIRS | wc -w) - dir_arr=($DIRS) - kibot: - - 'SEARCH=".kicad_pro"' - - !reference [.commands, dir_arr] - - !reference [.commands, sch_from_pro] - - sch_arr=($SCHEMS) - - | - for i in $(seq 1 $END) - do - if [[ ${dir_arr[i-1]} == "./Frame" ]]; then - continue - fi - echo ${dir_arr[i-1]} - echo ${sch_arr[i-1]} - USER_FILE="$CI_PROJECT_DIR/.gitlab/configs/blank.kibot.yaml" - if [ -f "${dir_arr[i-1]}/kibot.yaml" ]; then - USER_FILE="${dir_arr[i-1]}/kibot.yaml" - fi - cp $USER_FILE $CI_PROJECT_DIR/.gitlab/configs/user.kibot.yaml - python3 $CI_PROJECT_DIR/.gitlab/.scripts/orig.py ${dir_arr[i-1]} - for CONF in $KIBOT_CONF - do - kibot -e ${dir_arr[i-1]}/${sch_arr[i-1]} -c $CI_PROJECT_DIR/.gitlab/configs/$CONF.kibot.yaml -d $CI_PROJECT_DIR/Fabrication/${dir_arr[i-1]} $VARIANTS - done - mv $CI_PROJECT_DIR/Fabrication/${dir_arr[i-1]}/*.zip Fabrication/ 2> /dev/null || true - done - - cd $CI_PROJECT_DIR - - pre_panel: - - cd $CI_PROJECT_DIR - - mkdir panels - - 'SEARCH="_panel.json"' - - !reference [.commands, get_dirs] - - | - for d in $DIRS - do - echo "found panel: $d" - JSON=$(find $d/*_panel.json) - FILE=$(basename "${JSON}") - NAME=$(echo "${FILE%.json}") - PCB=$(find $d/*.kicad_pcb) - mkdir -p panels/$NAME - echo "panelising" - python3 .gitlab/.scripts/pre_panel.py $PCB $JSON - kikit panelize -p $JSON $PCB panels/$NAME/$NAME.kicad_pcb - cp .gitlab/templates/micromelon_default/micromelon_default.kicad_sch panels/$NAME/$NAME.kicad_sch - cp $d/fp-lib-table panels/$NAME/ - python3 .gitlab/.scripts/post_panel.py panels/$NAME/$NAME.kicad_pro $PCB - done - - cd panels - - post_panel: - - mv panels/* . || true - - neo: - - 'SEARCH=".kicad_pro"' - - !reference [.commands, get_dirs_filt] - - | - for d in $DIRS - do - if [[ $d == "./Frame" ]]; then - continue - fi - echo $d - python3 $CI_PROJECT_DIR/.gitlab/.scripts/neo.py $d - done - - boms: - - client=$(echo $CI_PROJECT_PATH | cut -d'/' -f3 | sed -r 's/\<./\U&/g') - - proj=$(echo $CI_PROJECT_NAME | tr -d '0123456789' | tr '-' ' ' | cut -d ' ' -f 2) - - desc_suffix=$(echo $client $proj) - - url="https://gitlab.com/api/v4/projects/${DIGI_API_PRJ_ID}/packages/generic/digikey_api/0/token_storage.json" - - | - if [[ -z "$KINTREE_DIGI_TOKEN" ]]; then - curl --header "JOB-TOKEN: $CI_JOB_TOKEN" $url >> token_storage.json - else - cp $KINTREE_DIGI_TOKEN token_storage.json - fi - - cat token_storage.json - - cd $CI_PROJECT_DIR/Fabrication - - 'SEARCH="bom.csv"' - - !reference [.commands, parse_commit] - - !reference [.commands, get_files] - - cd $CI_PROJECT_DIR - - FAIL=0 - - | - for f in $FILES - do - echo $f - fab_path=$CI_PROJECT_DIR/Fabrication/$(dirname $f | cut -c3-) - full_name=$(echo $f | cut -c3- | rev | cut -d'/' -f1 | cut -c9- | rev) - echo "name: $full_name" - name_n_rev=$(echo $full_name | cut -d'_' -f1,2) - assembly_rev=$(echo $full_name | cut -d'_' -f2) - name=$(echo $full_name | cut -d'-' -f1)A$(echo ${full_name//$assembly_rev/.} | cut -d'.' -f2) - pcb_rev=$(echo ${assembly_rev} | cut -d'_' -f1,2 | cut -d'.' -f1,2) - pcb_im=${fab_path}/PCB_${name_n_rev}.png - pcba_im=${fab_path}/PCBA_${name_n_rev}.png - pcb_attach=$(echo "['${fab_path}/${name_n_rev}_PCB.pdf', '$CI_PROJECT_DIR/Fabrication/${name_n_rev}_JLC.zip']") - pcba_attach=$(echo "['${fab_path}/${full_name}_schematic.pdf', '${fab_path}/${full_name}_bom.csv', '${fab_path}/${full_name}-neo-pos_top.csv', '${fab_path}/${full_name}-neo-pos_bot.csv', '${fab_path}/${full_name}_ibom.html']") - - DRY="--dry assemblies" - if [[ $CI_COMMIT_BRANCH == "main" ]]; then - DRY="" - fi - echo "Revs: $pcb_rev, $assembly_rev" - python -m kintree.kintree_cli $DRY -b $CI_PROJECT_DIR/Fabrication/$f -a "{'ipn': '$name', 'rev': ['$pcb_rev', '$assembly_rev'], 'image': ['$pcb_im', '$pcba_im'], 'desc': '$desc_suffix', 'attachments': [$pcb_attach, $pcba_attach]}" --settings $KINTREE_SETT --digi_token token_storage.json || FAIL=1 - done - # - cp token_storage.json /tmp - - | - cd $CI_PROJECT_DIR - cat token_storage.json - curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file token_storage.json $url - - exit $FAIL - # USE PRIVATE-TOKEN to upload from gitlab-ci - - git_tag: - - !reference [.commands, parse_commit] - - | - if [[ $GITLAB_CI == 'true' && $CI_COMMIT_BRANCH == "main" ]]; then - echo "running git tag" - git tag $TAG - elif [[ $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main" && "$CI_MERGE_REQUEST_TITLE" =~ [^a-zA-Z0-9.-_] ]]; then - exit 1 + # Extract version from MR title (pattern: V followed by numbers and dots) + if [[ -n "$CI_MERGE_REQUEST_TITLE" ]]; then + VERSION=$(echo "$CI_MERGE_REQUEST_TITLE" | grep -oE 'V[0-9]+(\.[0-9]+)*' | head -1) fi - merge_libs: - - | - if [[ $GITLAB_CI == 'true' && $CI_COMMIT_BRANCH == "main" ]]; then - git config --global user.name "KicadCi" - git config --global user.email "andrew@micromelon.com.au" - cd $CI_PROJECT_DIR - for d in "libs/melonlib" "libs/melon3d" - do - cd $d - if [ $d == "libs/melonlib" ] - then - TOKEN=$KI_LIB_TOKEN - else - TOKEN=$KI_LIB_3D_TOKEN - fi - REPO=$(basename $(git remote get-url origin)) - cd $CI_PROJECT_DIR - rm -rf $d - rm -rf .git/modules/$d - git submodule set-url $d https://ci_push:$TOKEN@gitlab.com/Micromelon/education/hardware/$REPO - git submodule update --init --remote --merge $d - cd $d - git remote set-url origin https://ci_push:$TOKEN@gitlab.com/Micromelon/education/hardware/$REPO - cd $CI_PROJECT_DIR - done - git submodule status - for d in "libs/melonlib" "libs/melon3d" - do - cd $d - if [ $(git rev-parse HEAD) != $(git rev-parse main) ] - then - CURR_B=$(git config -f $CI_PROJECT_DIR/.gitmodules submodule.$d.branch) - echo CURR_B - echo $CURR_B - git checkout $CURR_B - git commit --allow-empty -m "Merge" - git remote get-url origin - git push -u origin HEAD:$CURR_B -o ci.skip -o merge_request.create -o merge_request.remove_source_branch=false -o merge_request.merge_when_pipeline_succeeds -o merge_request.target=main - fi - cd $CI_PROJECT_DIR - done + # Fallback to git tag or commit SHA + if [[ -z "$VERSION" ]]; then + VERSION=$(git describe --tags 2>/dev/null || echo "$CI_COMMIT_SHORT_SHA") fi - parse_commit: - - | - initials () { - echo "$(echo $1 | cut -d' ' -f1 | cut -c1-1)$(echo $1 | cut -d' ' -f2 | cut -c1-1)" - } + echo "VERSION=$VERSION" >> build.env + echo "Extracted version: $VERSION" + artifacts: + reports: + dotenv: build.env - str=$CI_COMMIT_MESSAGE - REV=$(echo "$str" | sed -n '1 p') - DWN=$(initials "$(echo "$str" | sed -n '3 p' | cut -d' ' -f1,2)") - CHK='' - APP='' - - while IFS= read -r line; do - if [[ $line == Rev* ]]; then - CHK=$(initials "$(echo $line | cut -d' ' -f2,3)") - fi - if [[ $line == App* ]]; then - APP=$(initials "$(echo $line | cut -d' ' -f2,3)") - fi - done <<<"$str" - export DWN - export CHK - export APP - - !reference [.commands, parse_tag] - parse_tag: - - TAG=$(echo "$CI_COMMIT_MESSAGE" | sed -n '1 p') - - echo $TAG - -outputs_mech: - stage: gen_mech - when: always +run_erc: + stage: preflight + needs: + - job: extract_version + artifacts: true + script: + - | + if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch" ]; then + echo "Running ERC..." + cat > /tmp/erc.kibot.yaml << 'EOF' + kibot: + version: 1 + preflight: + erc: + enabled: true + warnings_as_errors: false + EOF + kibot -e ${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch \ + -c /tmp/erc.kibot.yaml \ + -d Validation/ + fi artifacts: when: always paths: - - Fabrication/**/* + - Validation/ expire_in: 1 week + +run_drc: + stage: preflight + needs: + - job: extract_version + artifacts: true + script: + - | + if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_pcb" ]; then + echo "Running DRC..." + # Extract part number from project name for text variables + PART_NUM=$(echo "${PROJECT_NAME}" | cut -d"-" -f1) + cat > /tmp/drc.kibot.yaml << EOF + kibot: + version: 1 + preflight: + set_text_variables: + - name: 'name' + text: '${PART_NUM}' + - name: 'rev' + text: '${VERSION}' + - name: 'rev_pcb' + text: '${VERSION}' + drc: + enabled: true + warnings_as_errors: false + EOF + kibot -e ${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch \ + -b ${PROJECT_NAME}/${PROJECT_NAME}.kicad_pcb \ + -c /tmp/drc.kibot.yaml \ + -d Validation/ + fi + artifacts: + when: always + paths: + - Validation/ + expire_in: 1 week + +# ============================================================================= +# Stage: Fabrication +# ============================================================================= +.fabrication_base: + stage: fabrication + needs: + - job: extract_version + artifacts: true + - job: run_erc + artifacts: false + - job: run_drc + artifacts: false before_script: - - !reference [.commands, git_tag] + - | + # Ensure submodules are initialized (needed for gitlab-ci-local) + git submodule update --init --recursive 2>/dev/null || true + + # Export version for KiBot text variable injection + export KIBOT_VAR_rev="${VERSION}" + export KIBOT_VAR_rev_pcb="${VERSION}" + echo "KiBot version variables set to: $VERSION" + + # Setup user config + USER_FILE="$CI_PROJECT_DIR/.gitlab/configs/blank.kibot.yaml" + if [ -f "${PROJECT_NAME}/kibot.yaml" ]; then + USER_FILE="${PROJECT_NAME}/kibot.yaml" + fi + cp $USER_FILE $CI_PROJECT_DIR/.gitlab/configs/user.kibot.yaml + artifacts: + when: always + paths: + - Fabrication/ + expire_in: 1 week + +generate_schematic: + extends: .fabrication_base script: - - VARIANTS="$VARIANTS_MECH" - - KIBOT_CONF="mech" - - !reference [.commands, kibot] + - | + export KIBOT_VAR_rev="${VERSION}" + export KIBOT_VAR_rev_pcb="${VERSION}" + echo "Using version: $VERSION" + if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch" ]; then + kibot -e ${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch \ + -c $CI_PROJECT_DIR/.gitlab/configs/sch.kibot.yaml \ + -d $CI_PROJECT_DIR/Fabrication/${PROJECT_NAME} + mv Fabrication/${PROJECT_NAME}/*.pdf Fabrication/ 2>/dev/null || true + fi -outputs_dev: - extends: outputs_mech - rules: - - !reference [.dev_rules, rules] - stage: gen_fab +generate_bom: + extends: .fabrication_base script: - # SCH - - VARIANTS="$VARIANTS_SCH" - - KIBOT_CONF="sch" - - !reference [.commands, kibot] - # PCB - - VARIANTS="$VARIANTS_PCB" - - KIBOT_CONF="pcb_dev" - - !reference [.commands, kibot] + - | + export KIBOT_VAR_rev="${VERSION}" + export KIBOT_VAR_rev_pcb="${VERSION}" + echo "Using version: $VERSION" -outputs_main: - extends: outputs_dev + if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch" ]; then + kibot -e ${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch \ + -c $CI_PROJECT_DIR/.gitlab/configs/sch.kibot.yaml \ + -d $CI_PROJECT_DIR/Fabrication/${PROJECT_NAME} + mv Fabrication/${PROJECT_NAME}/*.csv Fabrication/ 2>/dev/null || true + mv Fabrication/${PROJECT_NAME}/*.xlsx Fabrication/ 2>/dev/null || true + fi + +generate_3d: + extends: .fabrication_base + script: + - | + export KIBOT_VAR_rev="${VERSION}" + export KIBOT_VAR_rev_pcb="${VERSION}" + echo "Using version: $VERSION" + + if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_pcb" ]; then + kibot -e ${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch \ + -c $CI_PROJECT_DIR/.gitlab/configs/mech.kibot.yaml \ + -d $CI_PROJECT_DIR/Fabrication/${PROJECT_NAME} + mv Fabrication/${PROJECT_NAME}/*.step Fabrication/ 2>/dev/null || true + fi + +generate_gerbers: + extends: .fabrication_base rules: - !reference [.main_rules, rules] script: - # SCH - - VARIANTS="$VARIANTS_SCH" - - KIBOT_CONF="sch" - - !reference [.commands, kibot] - # PCB - - VARIANTS="$VARIANTS_PCB" - - KIBOT_CONF="pcb_main" - - !reference [.commands, kibot] - # Panels - - !reference [.commands, pre_panel] - - KIBOT_CONF="panel" - - !reference [.commands, kibot] - - !reference [.commands, post_panel] - # Pos - - VARIANTS="$VARIANTS_SCH" - - KIBOT_CONF="pos" - - !reference [.commands, kibot] - - !reference [.commands, neo] + - | + export KIBOT_VAR_rev="${VERSION}" + export KIBOT_VAR_rev_pcb="${VERSION}" + echo "Using version: $VERSION" -inventree_job_dev: - image: - name: ghcr.io/andrew-collins/ki-ntree:cli - entrypoint: [""] + if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_pcb" ]; then + kibot -e ${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch \ + -c $CI_PROJECT_DIR/.gitlab/configs/pcb_main.kibot.yaml \ + -d $CI_PROJECT_DIR/Fabrication/${PROJECT_NAME} + mv Fabrication/${PROJECT_NAME}/*.zip Fabrication/ 2>/dev/null || true + fi + +generate_position: + extends: .fabrication_base + rules: + - !reference [.main_rules, rules] + script: + - | + export KIBOT_VAR_rev="${VERSION}" + export KIBOT_VAR_rev_pcb="${VERSION}" + echo "Using version: $VERSION" + + if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_pcb" ]; then + kibot -e ${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch \ + -c $CI_PROJECT_DIR/.gitlab/configs/pos.kibot.yaml \ + -d $CI_PROJECT_DIR/Fabrication/${PROJECT_NAME} + mv Fabrication/${PROJECT_NAME}/*cpl*.csv Fabrication/ 2>/dev/null || true + fi + +generate_panel: + extends: .fabrication_base + rules: + - !reference [.main_rules, rules] + script: + - | + export KIBOT_VAR_rev="${VERSION}" + export KIBOT_VAR_rev_pcb="${VERSION}" + echo "Using version: $VERSION" + + # Find panel configuration + PANEL_JSON=$(find ${PROJECT_NAME} -name "*_panel.json" 2>/dev/null | head -1) + + if [ -n "$PANEL_JSON" ] && [ -f "$PANEL_JSON" ]; then + echo "Found panel config: $PANEL_JSON" + + # Create panel directory + PANEL_NAME=$(basename "${PANEL_JSON%.json}") + mkdir -p panels/${PANEL_NAME} + PCB_FILE="${PROJECT_NAME}/${PROJECT_NAME}.kicad_pcb" + + # Run pre-panel script if exists + if [ -f ".gitlab/.scripts/pre_panel.py" ]; then + python3 .gitlab/.scripts/pre_panel.py $PCB_FILE $PANEL_JSON + fi + + # Generate panel + kikit panelize -p $PANEL_JSON $PCB_FILE panels/${PANEL_NAME}/${PANEL_NAME}.kicad_pcb + + # Copy required files for panel + cp .gitlab/templates/micromelon_default/micromelon_default.kicad_sch panels/${PANEL_NAME}/${PANEL_NAME}.kicad_sch 2>/dev/null || true + cp ${PROJECT_NAME}/fp-lib-table panels/${PANEL_NAME}/ 2>/dev/null || true + # Copy libs folder for 3D models + cp -r ${PROJECT_NAME}/libs panels/${PANEL_NAME}/ 2>/dev/null || true + + # Run post-panel script if exists + if [ -f ".gitlab/.scripts/post_panel.py" ]; then + python3 .gitlab/.scripts/post_panel.py panels/${PANEL_NAME}/${PANEL_NAME}.kicad_pro $PCB_FILE + fi + + # Generate panel gerbers + # Set name variable explicitly for panel (extract part number from project name) + export KIBOT_VAR_name=$(echo "${PROJECT_NAME}" | cut -d"-" -f1) + cd panels + kibot -e ${PANEL_NAME}/${PANEL_NAME}.kicad_sch \ + -c $CI_PROJECT_DIR/.gitlab/configs/panel.kibot.yaml \ + -d $CI_PROJECT_DIR/Fabrication/panels + cd $CI_PROJECT_DIR + + mv Fabrication/panels/*.zip Fabrication/ 2>/dev/null || true + else + echo "No panel configuration found, skipping panel generation" + fi + +# ============================================================================= +# Stage: InvenTree +# ============================================================================= +.inventree_base: stage: inventree + image: python:3.11-slim + before_script: + - | + cd hfsntree + pip install -e . --quiet + cd $CI_PROJECT_DIR + +inventree_dev: + extends: .inventree_base rules: - !reference [.dev_rules, rules] needs: - - job: outputs_mech + - job: extract_version artifacts: true - - job: outputs_dev + - job: generate_schematic + artifacts: true + - job: generate_bom + artifacts: true + - job: generate_3d artifacts: true script: - - !reference [.commands, boms] + - | + echo "Running InvenTree upload in dry-run mode for dev branch" + cd hfsntree + python main.py batch $CI_PROJECT_DIR/Fabrication --dry-run -y -inventree_job_main: - image: - name: ghcr.io/andrew-collins/ki-ntree:cli - entrypoint: [""] - stage: inventree +inventree_main: + extends: .inventree_base rules: - !reference [.main_rules, rules] needs: - - job: outputs_mech + - job: extract_version artifacts: true - - job: outputs_main + - job: generate_schematic artifacts: true + - job: generate_bom + artifacts: true + - job: generate_3d + artifacts: true + - job: generate_gerbers + artifacts: true + - job: generate_position + artifacts: true + - job: generate_panel + artifacts: true + optional: true + artifacts: + when: always + paths: + - Fabrication/ + expire_in: 1 week script: - - !reference [.commands, boms] + - | + echo "Running InvenTree upload for main branch" + cd hfsntree + python main.py batch $CI_PROJECT_DIR/Fabrication -y -upload_job: - stage: upload - needs: - - job: inventree_job_main - - job: outputs_main - artifacts: true + # Generate Samsung P&P files (requires parts to exist in InvenTree) + echo "Generating Samsung pick-and-place files..." + python main.py samsung $CI_PROJECT_DIR/Fabrication + cd $CI_PROJECT_DIR + +# ============================================================================= +# Stage: Release +# ============================================================================= +upload_packages: + stage: release rules: - if: $CI_COMMIT_TAG - when: never # Do not run this job when a tag is created manually - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when commits are pushed or merged to the default branch + when: never + - if: $CI_COMMIT_BRANCH == "main" + needs: + - job: extract_version + artifacts: true + - job: inventree_main + artifacts: true script: - - !reference [.commands, parse_commit] - - apt remove libcurl4 -y - - apt-get update && apt-get -y install zip curl - | + apt-get update && apt-get -y install zip curl + + # Create combined zip zip -r Fabrication/All.zip Fabrication/ - for d in $(find Fabrication/* -maxdepth 0 -type d) - do - zip=$d.zip - zip -r $zip $d - done - for d in $(find Fabrication/ -maxdepth 1 -name '*.zip') - do - b=$(basename $d) - f=$(echo "${b%.*}") - url=${PACKAGE_REGISTRY_URL}/$TAG/$f-$TAG.zip - echo "uploading: $d to $url" - curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file $d $url + + # Upload each zip to package registry + for zipfile in $(find Fabrication/ -maxdepth 1 -name '*.zip'); do + basename=$(basename "$zipfile" .zip) + url="${PACKAGE_REGISTRY_URL}/${VERSION}/${basename}-${VERSION}.zip" + echo "Uploading: $zipfile to $url" + curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file "$zipfile" "$url" done artifacts: when: always paths: - - Fabrication/**/* + - Fabrication/ expire_in: 1 week -release_job: +create_release: stage: release image: registry.gitlab.com/gitlab-org/release-cli:latest - needs: - - job: upload_job - artifacts: true rules: - if: $CI_COMMIT_TAG - when: never # Do not run this job when a tag is created manually - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when commits are pushed or merged to the default branch + when: never + - if: $CI_COMMIT_BRANCH == "main" + needs: + - job: extract_version + artifacts: true + - job: upload_packages + artifacts: true script: - - !reference [.commands, parse_tag] - - apk add jq curl - | - echo "running release_job for $TAG" - echo "#!/bin/sh" >> fab.sh - packid=$(curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages | jq .[-1].id) - echo "release-cli create --name \"Release $TAG\" --tag-name \"$TAG\" \\" >> fab.sh - cnt=$(curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/$packid/package_files | jq length) - ids=$(curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/$packid/package_files | jq .[].id) - names=$(curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/$packid/package_files | jq .[].file_name) - i=1 - while [ "$i" -le $cnt ] - do - id=$(echo $ids | cut -d' ' -f $i) - name=$(echo $names | cut -d' ' -f $i) - echo " --assets-link \"{\\\"name\\\":\\\"$name\\\",\\\"url\\\":\\\"${CI_PROJECT_URL}/-/package_files/$id/download\\\"}\" \\" >> fab.sh - i=$(( i + 1 )) + apk add jq curl + + echo "Creating release for version: $VERSION" + + # Get package ID + packid=$(curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \ + "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages" | jq '.[-1].id') + + # Build release command with asset links + echo "#!/bin/sh" > release.sh + echo "release-cli create --name \"Release $VERSION\" --tag-name \"$VERSION\" \\" >> release.sh + + # Get package files + files_json=$(curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \ + "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/$packid/package_files") + + cnt=$(echo "$files_json" | jq 'length') + + for i in $(seq 0 $((cnt - 1))); do + id=$(echo "$files_json" | jq -r ".[$i].id") + name=$(echo "$files_json" | jq -r ".[$i].file_name") + echo " --assets-link \"{\\\"name\\\":\\\"$name\\\",\\\"url\\\":\\\"${CI_PROJECT_URL}/-/package_files/$id/download\\\"}\" \\" >> release.sh done - chmod +x fab.sh - ./fab.sh + + chmod +x release.sh + ./release.sh