Compare commits
7 Commits
kicad9
...
1beee56f5d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1beee56f5d | ||
|
|
8d6de4fea7 | ||
|
|
0277bba20f | ||
|
|
f75ad80492 | ||
|
|
30e3a98066 | ||
|
|
6a411cdc3f | ||
|
|
bd7882f54f |
60
.gitea/workflows/README.md
Normal file
60
.gitea/workflows/README.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# Gitea Actions — KiCad CI
|
||||||
|
|
||||||
|
Sibling implementation of `kibot-ci.yml` (GitLab) for projects hosted
|
||||||
|
on Gitea. Same fab pipeline, same Astable upload contract; only the
|
||||||
|
orchestration layer differs.
|
||||||
|
|
||||||
|
## Per-project setup
|
||||||
|
|
||||||
|
In each PCB project's repo, create `.gitea/workflows/ci.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: KiCad CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [dev, main]
|
||||||
|
pull_request:
|
||||||
|
branches: [dev, main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
kicad:
|
||||||
|
uses: Micromelon/kicad-ci/.gitea/workflows/kibot.yml@main
|
||||||
|
with:
|
||||||
|
project_name: m100301-dengus_cab_tablet_mainboard
|
||||||
|
secrets:
|
||||||
|
ASTABLE_URL: ${{ secrets.ASTABLE_URL }}
|
||||||
|
ASTABLE_API_TOKEN: ${{ secrets.ASTABLE_API_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
Adjust `project_name` per project. The PCB project's `.gitlab` (sic)
|
||||||
|
submodule still points at the same `Micromelon/kicad-ci` repo —
|
||||||
|
keeping the submodule path identical to the GitLab side means
|
||||||
|
`scripts/upload-bom.sh`, `configs/*.kibot.yaml`, etc. are reachable
|
||||||
|
from both platforms without per-platform path branching.
|
||||||
|
|
||||||
|
## Secrets
|
||||||
|
|
||||||
|
Set at the org level (Org → Settings → Actions → Secrets) so every
|
||||||
|
PCB repo picks them up automatically. Same shape as the GitLab CI
|
||||||
|
variables on `Micromelon/`:
|
||||||
|
|
||||||
|
- `ASTABLE_URL` — e.g. `https://astable.timhadwen.com`
|
||||||
|
- `ASTABLE_API_TOKEN` — `ast_…` minted at `/manage/api-tokens`
|
||||||
|
|
||||||
|
## Differences vs the GitLab side
|
||||||
|
|
||||||
|
- Each stage's artifacts are separate (`Fabrication`, `Fabrication-bom`,
|
||||||
|
`Fabrication-3d`, …) and the astable jobs `download-artifact` them
|
||||||
|
all into one directory. GitLab pools them automatically; Gitea
|
||||||
|
Actions doesn't.
|
||||||
|
- Version extraction reads from the commit message (main branch) or
|
||||||
|
the PR title (MR-to-main). Same V-token rule.
|
||||||
|
- Panel job uses `continue-on-error: true` to match GitLab's
|
||||||
|
`optional: true` semantics.
|
||||||
|
|
||||||
|
## Reusable-workflow support
|
||||||
|
|
||||||
|
Requires Gitea ≥ 1.21 (with Forgejo Actions / Gitea Actions support).
|
||||||
|
If your runner is older, vendor this file into each PCB project's
|
||||||
|
`.gitea/workflows/` directly (drop the `workflow_call:` trigger and
|
||||||
|
add a `push:` / `pull_request:` trigger).
|
||||||
472
.gitea/workflows/kibot.yml
Normal file
472
.gitea/workflows/kibot.yml
Normal file
@@ -0,0 +1,472 @@
|
|||||||
|
# Reusable Gitea Actions workflow — the Gitea-flavoured equivalent
|
||||||
|
# of /kibot-ci.yml. Same fab outputs, same Astable upload contract.
|
||||||
|
#
|
||||||
|
# Both files live in the same kicad-ci repo on the same branch; they
|
||||||
|
# don't collide because GitLab CI looks at /kibot-ci.yml and Gitea
|
||||||
|
# Actions looks at /.gitea/workflows/*.yml. Maintain in lockstep —
|
||||||
|
# any change that affects the upload contract should land in both.
|
||||||
|
#
|
||||||
|
# How a PCB project on Gitea uses this:
|
||||||
|
#
|
||||||
|
# # In the PCB project's .gitea/workflows/ci.yml
|
||||||
|
# name: KiCad CI
|
||||||
|
# on:
|
||||||
|
# push:
|
||||||
|
# branches: [dev, main]
|
||||||
|
# pull_request:
|
||||||
|
# branches: [dev, main]
|
||||||
|
# jobs:
|
||||||
|
# kicad:
|
||||||
|
# uses: Micromelon/kicad-ci/.gitea/workflows/kibot.yml@main
|
||||||
|
# with:
|
||||||
|
# project_name: m100301-dengus_cab_tablet_mainboard
|
||||||
|
# secrets:
|
||||||
|
# ASTABLE_URL: ${{ secrets.ASTABLE_URL }}
|
||||||
|
# ASTABLE_API_TOKEN: ${{ secrets.ASTABLE_API_TOKEN }}
|
||||||
|
#
|
||||||
|
# Gitea Actions secrets are configured under Repo → Settings → Actions
|
||||||
|
# → Secrets (or at the org level for inheritance, same idea as
|
||||||
|
# GitLab's group-level CI variables).
|
||||||
|
#
|
||||||
|
# Required secrets:
|
||||||
|
# ASTABLE_URL e.g. https://astable.timhadwen.com
|
||||||
|
# ASTABLE_API_TOKEN minted at /manage/api-tokens (ast_…)
|
||||||
|
|
||||||
|
name: KiBot CI (reusable)
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
project_name:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: "Project directory name — also used as the partIpn for Astable"
|
||||||
|
kibot_image:
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: "ghcr.io/inti-cmnb/kicad9_auto:1.8.4"
|
||||||
|
secrets:
|
||||||
|
ASTABLE_URL:
|
||||||
|
required: true
|
||||||
|
ASTABLE_API_TOKEN:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
PROJECT_NAME: ${{ inputs.project_name }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ─── Preflight ──────────────────────────────────────────────────────
|
||||||
|
extract_version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.derive.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- id: derive
|
||||||
|
run: |
|
||||||
|
# Dev branch: VERSION=dev. Main branch: parse VX.Y from commit
|
||||||
|
# message (matches the GitLab-side rule). MR-to-main uses the
|
||||||
|
# MR title's V-token, falling back to dev.
|
||||||
|
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
|
||||||
|
MSG="${{ github.event.head_commit.message }}"
|
||||||
|
VERSION=$(echo "$MSG" | grep -oE 'V[0-9]+(\.[0-9]+)*' | head -1 || true)
|
||||||
|
[ -z "$VERSION" ] && VERSION="${{ github.sha }}" && VERSION="${VERSION:0:8}"
|
||||||
|
elif [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||||
|
VERSION=$(echo "${{ github.event.pull_request.title }}" | grep -oE 'V[0-9]+(\.[0-9]+)*' | head -1 || true)
|
||||||
|
[ -z "$VERSION" ] && VERSION="dev"
|
||||||
|
else
|
||||||
|
VERSION="dev"
|
||||||
|
fi
|
||||||
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Resolved VERSION=$VERSION"
|
||||||
|
|
||||||
|
run_erc:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: extract_version
|
||||||
|
container:
|
||||||
|
image: ${{ inputs.kibot_image }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- run: |
|
||||||
|
if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch" ]; then
|
||||||
|
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
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: Validation
|
||||||
|
path: Validation/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
run_drc:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [extract_version, run_erc]
|
||||||
|
container:
|
||||||
|
image: ${{ inputs.kibot_image }}
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.extract_version.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Validation
|
||||||
|
path: Validation/
|
||||||
|
- run: |
|
||||||
|
if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_pcb" ]; then
|
||||||
|
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
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: Validation
|
||||||
|
path: Validation/
|
||||||
|
overwrite: true
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
# ─── Fabrication ────────────────────────────────────────────────────
|
||||||
|
# Reusable step block via a small composite isn't worth it — each
|
||||||
|
# generate_* job is small enough to inline its own checkout +
|
||||||
|
# kibot invocation. Common KIBOT_VAR_* env is set per-job.
|
||||||
|
|
||||||
|
generate_schematic:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [extract_version, run_erc, run_drc]
|
||||||
|
container:
|
||||||
|
image: ${{ inputs.kibot_image }}
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.extract_version.outputs.version }}
|
||||||
|
KIBOT_VAR_rev: ${{ needs.extract_version.outputs.version }}
|
||||||
|
KIBOT_VAR_rev_pcb: ${{ needs.extract_version.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with: { submodules: recursive }
|
||||||
|
- run: |
|
||||||
|
export KIBOT_VAR_name=$(echo "${PROJECT_NAME}" | cut -d"-" -f1)
|
||||||
|
if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch" ]; then
|
||||||
|
kibot -e "${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch" \
|
||||||
|
-c "$GITHUB_WORKSPACE/.gitlab/configs/sch.kibot.yaml" \
|
||||||
|
-d "$GITHUB_WORKSPACE/Fabrication/${PROJECT_NAME}"
|
||||||
|
mv "Fabrication/${PROJECT_NAME}"/*.pdf Fabrication/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Fabrication
|
||||||
|
path: Fabrication/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
generate_bom:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [extract_version, run_erc, run_drc]
|
||||||
|
container:
|
||||||
|
image: ${{ inputs.kibot_image }}
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.extract_version.outputs.version }}
|
||||||
|
KIBOT_VAR_rev: ${{ needs.extract_version.outputs.version }}
|
||||||
|
KIBOT_VAR_rev_pcb: ${{ needs.extract_version.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with: { submodules: recursive }
|
||||||
|
- run: |
|
||||||
|
export KIBOT_VAR_name=$(echo "${PROJECT_NAME}" | cut -d"-" -f1)
|
||||||
|
if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch" ]; then
|
||||||
|
kibot -e "${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch" \
|
||||||
|
-c "$GITHUB_WORKSPACE/.gitlab/configs/sch.kibot.yaml" \
|
||||||
|
-d "$GITHUB_WORKSPACE/Fabrication/${PROJECT_NAME}"
|
||||||
|
mv "Fabrication/${PROJECT_NAME}"/*.csv Fabrication/ 2>/dev/null || true
|
||||||
|
mv "Fabrication/${PROJECT_NAME}"/*.xlsx Fabrication/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Fabrication-bom
|
||||||
|
path: Fabrication/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
generate_3d:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [extract_version, run_erc, run_drc]
|
||||||
|
container:
|
||||||
|
image: ${{ inputs.kibot_image }}
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.extract_version.outputs.version }}
|
||||||
|
KIBOT_VAR_rev: ${{ needs.extract_version.outputs.version }}
|
||||||
|
KIBOT_VAR_rev_pcb: ${{ needs.extract_version.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with: { submodules: recursive }
|
||||||
|
- run: |
|
||||||
|
export KIBOT_VAR_name=$(echo "${PROJECT_NAME}" | cut -d"-" -f1)
|
||||||
|
if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_pcb" ]; then
|
||||||
|
kibot -e "${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch" \
|
||||||
|
-c "$GITHUB_WORKSPACE/.gitlab/configs/mech.kibot.yaml" \
|
||||||
|
-d "$GITHUB_WORKSPACE/Fabrication/${PROJECT_NAME}"
|
||||||
|
mv "Fabrication/${PROJECT_NAME}"/*.step Fabrication/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Fabrication-3d
|
||||||
|
path: Fabrication/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
# Main-only jobs — gated by the `if:` so dev branches skip them.
|
||||||
|
generate_gerbers:
|
||||||
|
if: github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.base_ref == 'main')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [extract_version, run_erc, run_drc]
|
||||||
|
container:
|
||||||
|
image: ${{ inputs.kibot_image }}
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.extract_version.outputs.version }}
|
||||||
|
KIBOT_VAR_rev: ${{ needs.extract_version.outputs.version }}
|
||||||
|
KIBOT_VAR_rev_pcb: ${{ needs.extract_version.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with: { submodules: recursive }
|
||||||
|
- run: |
|
||||||
|
export KIBOT_VAR_name=$(echo "${PROJECT_NAME}" | cut -d"-" -f1)
|
||||||
|
if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_pcb" ]; then
|
||||||
|
kibot -e "${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch" \
|
||||||
|
-c "$GITHUB_WORKSPACE/.gitlab/configs/pcb_main.kibot.yaml" \
|
||||||
|
-d "$GITHUB_WORKSPACE/Fabrication/${PROJECT_NAME}"
|
||||||
|
mv "Fabrication/${PROJECT_NAME}"/*.zip Fabrication/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Fabrication-gerbers
|
||||||
|
path: Fabrication/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
generate_position:
|
||||||
|
if: github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.base_ref == 'main')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [extract_version, run_erc, run_drc]
|
||||||
|
container:
|
||||||
|
image: ${{ inputs.kibot_image }}
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.extract_version.outputs.version }}
|
||||||
|
KIBOT_VAR_rev: ${{ needs.extract_version.outputs.version }}
|
||||||
|
KIBOT_VAR_rev_pcb: ${{ needs.extract_version.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with: { submodules: recursive }
|
||||||
|
- run: |
|
||||||
|
export KIBOT_VAR_name=$(echo "${PROJECT_NAME}" | cut -d"-" -f1)
|
||||||
|
if [ -f "${PROJECT_NAME}/${PROJECT_NAME}.kicad_pcb" ]; then
|
||||||
|
kibot -e "${PROJECT_NAME}/${PROJECT_NAME}.kicad_sch" \
|
||||||
|
-c "$GITHUB_WORKSPACE/.gitlab/configs/pos.kibot.yaml" \
|
||||||
|
-d "$GITHUB_WORKSPACE/Fabrication/${PROJECT_NAME}"
|
||||||
|
mv "Fabrication/${PROJECT_NAME}"/*cpl*.csv Fabrication/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Fabrication-position
|
||||||
|
path: Fabrication/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
generate_panel:
|
||||||
|
if: github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.base_ref == 'main')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [extract_version, run_erc, run_drc]
|
||||||
|
container:
|
||||||
|
image: ${{ inputs.kibot_image }}
|
||||||
|
continue-on-error: true # optional per the GitLab side
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.extract_version.outputs.version }}
|
||||||
|
KIBOT_VAR_rev: ${{ needs.extract_version.outputs.version }}
|
||||||
|
KIBOT_VAR_rev_pcb: ${{ needs.extract_version.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with: { submodules: recursive }
|
||||||
|
- run: |
|
||||||
|
export KIBOT_VAR_name=$(echo "${PROJECT_NAME}" | cut -d"-" -f1)
|
||||||
|
PANEL_JSON=$(find "${PROJECT_NAME}" -name "*_panel.json" 2>/dev/null | head -1)
|
||||||
|
if [ -n "$PANEL_JSON" ] && [ -f "$PANEL_JSON" ]; then
|
||||||
|
PANEL_NAME=$(basename "${PANEL_JSON%.json}")
|
||||||
|
mkdir -p "panels/${PANEL_NAME}"
|
||||||
|
PCB_FILE="${PROJECT_NAME}/${PROJECT_NAME}.kicad_pcb"
|
||||||
|
[ -f ".gitlab/.scripts/pre_panel.py" ] && python3 .gitlab/.scripts/pre_panel.py "$PCB_FILE" "$PANEL_JSON"
|
||||||
|
kikit panelize -p "$PANEL_JSON" "$PCB_FILE" "panels/${PANEL_NAME}/${PANEL_NAME}.kicad_pcb"
|
||||||
|
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
|
||||||
|
cp -r "${PROJECT_NAME}/libs" "panels/${PANEL_NAME}/" 2>/dev/null || true
|
||||||
|
[ -f ".gitlab/.scripts/post_panel.py" ] && python3 .gitlab/.scripts/post_panel.py "panels/${PANEL_NAME}/${PANEL_NAME}.kicad_pro" "$PCB_FILE"
|
||||||
|
cd panels
|
||||||
|
kibot -e "${PANEL_NAME}/${PANEL_NAME}.kicad_sch" \
|
||||||
|
-c "$GITHUB_WORKSPACE/.gitlab/configs/panel.kibot.yaml" \
|
||||||
|
-d "$GITHUB_WORKSPACE/Fabrication/panels"
|
||||||
|
cd "$GITHUB_WORKSPACE"
|
||||||
|
mv Fabrication/panels/*.zip Fabrication/ 2>/dev/null || true
|
||||||
|
else
|
||||||
|
echo "No panel config — skipping"
|
||||||
|
fi
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Fabrication-panel
|
||||||
|
path: Fabrication/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
# ─── Astable upload ─────────────────────────────────────────────────
|
||||||
|
# Mirrors astable_dev / astable_main on the GitLab side. The fab
|
||||||
|
# artifacts above are downloaded into a shared Fabrication/ tree;
|
||||||
|
# upload-bom.sh handles the POST + polling.
|
||||||
|
|
||||||
|
astable_dev:
|
||||||
|
if: github.ref == 'refs/heads/dev' || (github.event_name == 'pull_request' && github.base_ref == 'dev')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [extract_version, generate_schematic, generate_bom, generate_3d]
|
||||||
|
container:
|
||||||
|
image: alpine:3.20
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.extract_version.outputs.version }}
|
||||||
|
ASTABLE_URL: ${{ secrets.ASTABLE_URL }}
|
||||||
|
ASTABLE_API_TOKEN: ${{ secrets.ASTABLE_API_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with: { submodules: recursive }
|
||||||
|
- run: apk add --no-cache bash curl jq
|
||||||
|
# Pull each fab job's artifact into a unified Fabrication/ tree.
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with: { name: Fabrication, path: Fabrication/ }
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with: { name: Fabrication-bom, path: Fabrication/ }
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with: { name: Fabrication-3d, path: Fabrication/ }
|
||||||
|
- name: Upload to Astable (dev rev)
|
||||||
|
run: |
|
||||||
|
install -m 0755 "$GITHUB_WORKSPACE/.gitlab/scripts/upload-bom.sh" /usr/local/bin/upload-bom.sh
|
||||||
|
bash -s <<'BASH_SCRIPT'
|
||||||
|
set -euo pipefail
|
||||||
|
FAB="$GITHUB_WORKSPACE/Fabrication"
|
||||||
|
SUB="$FAB/${PROJECT_NAME}"
|
||||||
|
VAL="$GITHUB_WORKSPACE/Validation"
|
||||||
|
ARGS=(
|
||||||
|
--file "$FAB/${PROJECT_NAME}_dev_bom.csv"
|
||||||
|
--part-ipn "$PROJECT_NAME"
|
||||||
|
--branch dev
|
||||||
|
--commit-sha "$GITHUB_SHA"
|
||||||
|
--commit-message "${COMMIT_MESSAGE:-}"
|
||||||
|
)
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_dev_cpl.csv" ] && ARGS+=(--cpl "$FAB/${PROJECT_NAME}_dev_cpl.csv")
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_dev.step" ] && ARGS+=(--step "$FAB/${PROJECT_NAME}_dev.step")
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_dev_schematic.pdf" ] && ARGS+=(--schematic-pdf "$FAB/${PROJECT_NAME}_dev_schematic.pdf")
|
||||||
|
[ -f "$SUB/${PROJECT_NAME}_dev_ibom.html" ] && ARGS+=(--ibom-html "$SUB/${PROJECT_NAME}_dev_ibom.html")
|
||||||
|
[ -f "$SUB/${PROJECT_NAME}_dev_bom.xlsx" ] && ARGS+=(--bom-xlsx "$SUB/${PROJECT_NAME}_dev_bom.xlsx")
|
||||||
|
[ -f "$SUB/PCBA_${PROJECT_NAME}_dev.png" ] && ARGS+=(--pcba-image "$SUB/PCBA_${PROJECT_NAME}_dev.png")
|
||||||
|
[ -f "$SUB/PCB_${PROJECT_NAME}_dev.png" ] && ARGS+=(--pcb-image "$SUB/PCB_${PROJECT_NAME}_dev.png")
|
||||||
|
[ -f "$VAL/${PROJECT_NAME}-erc.html" ] && ARGS+=(--erc-report "$VAL/${PROJECT_NAME}-erc.html")
|
||||||
|
[ -f "$VAL/${PROJECT_NAME}-drc.html" ] && ARGS+=(--drc-report "$VAL/${PROJECT_NAME}-drc.html")
|
||||||
|
upload-bom.sh "${ARGS[@]}"
|
||||||
|
BASH_SCRIPT
|
||||||
|
env:
|
||||||
|
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||||
|
|
||||||
|
astable_main:
|
||||||
|
if: github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.base_ref == 'main')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- extract_version
|
||||||
|
- generate_schematic
|
||||||
|
- generate_bom
|
||||||
|
- generate_3d
|
||||||
|
- generate_gerbers
|
||||||
|
- generate_position
|
||||||
|
- generate_panel
|
||||||
|
container:
|
||||||
|
image: alpine:3.20
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.extract_version.outputs.version }}
|
||||||
|
ASTABLE_URL: ${{ secrets.ASTABLE_URL }}
|
||||||
|
ASTABLE_API_TOKEN: ${{ secrets.ASTABLE_API_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with: { submodules: recursive }
|
||||||
|
- run: apk add --no-cache bash curl jq
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with: { name: Fabrication, path: Fabrication/ }
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with: { name: Fabrication-bom, path: Fabrication/ }
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with: { name: Fabrication-3d, path: Fabrication/ }
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with: { name: Fabrication-gerbers, path: Fabrication/ }
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with: { name: Fabrication-position, path: Fabrication/ }
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with: { name: Fabrication-panel, path: Fabrication/ }
|
||||||
|
continue-on-error: true
|
||||||
|
- name: Upload to Astable (main rev)
|
||||||
|
run: |
|
||||||
|
install -m 0755 "$GITHUB_WORKSPACE/.gitlab/scripts/upload-bom.sh" /usr/local/bin/upload-bom.sh
|
||||||
|
bash -s <<'BASH_SCRIPT'
|
||||||
|
set -euo pipefail
|
||||||
|
FAB="$GITHUB_WORKSPACE/Fabrication"
|
||||||
|
SUB="$FAB/${PROJECT_NAME}"
|
||||||
|
VAL="$GITHUB_WORKSPACE/Validation"
|
||||||
|
# MR-to-main → upload to dev rev (pre-ordering window).
|
||||||
|
# Actual main pushes → released rev.
|
||||||
|
if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then
|
||||||
|
BRANCH=dev
|
||||||
|
else
|
||||||
|
BRANCH=main
|
||||||
|
fi
|
||||||
|
ARGS=(
|
||||||
|
--file "$FAB/${PROJECT_NAME}_${VERSION}_bom.csv"
|
||||||
|
--part-ipn "$PROJECT_NAME"
|
||||||
|
--branch "$BRANCH"
|
||||||
|
--commit-sha "$GITHUB_SHA"
|
||||||
|
--commit-message "${COMMIT_MESSAGE:-}"
|
||||||
|
)
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_${VERSION}_cpl.csv" ] && ARGS+=(--cpl "$FAB/${PROJECT_NAME}_${VERSION}_cpl.csv")
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_${VERSION}_JLC.zip" ] && ARGS+=(--gerbers "$FAB/${PROJECT_NAME}_${VERSION}_JLC.zip")
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_panel_${VERSION}_JLC.zip" ] && ARGS+=(--panel-gerbers "$FAB/${PROJECT_NAME}_panel_${VERSION}_JLC.zip")
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_${VERSION}.step" ] && ARGS+=(--step "$FAB/${PROJECT_NAME}_${VERSION}.step")
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_${VERSION}_schematic.pdf" ] && ARGS+=(--schematic-pdf "$FAB/${PROJECT_NAME}_${VERSION}_schematic.pdf")
|
||||||
|
[ -f "$SUB/${PROJECT_NAME}_${VERSION}_PCB.pdf" ] && ARGS+=(--pcb-pdf "$SUB/${PROJECT_NAME}_${VERSION}_PCB.pdf")
|
||||||
|
[ -f "$SUB/${PROJECT_NAME}_${VERSION}_ibom.html" ] && ARGS+=(--ibom-html "$SUB/${PROJECT_NAME}_${VERSION}_ibom.html")
|
||||||
|
[ -f "$SUB/${PROJECT_NAME}_${VERSION}_bom.xlsx" ] && ARGS+=(--bom-xlsx "$SUB/${PROJECT_NAME}_${VERSION}_bom.xlsx")
|
||||||
|
[ -f "$SUB/PCBA_${PROJECT_NAME}_${VERSION}.png" ] && ARGS+=(--pcba-image "$SUB/PCBA_${PROJECT_NAME}_${VERSION}.png")
|
||||||
|
[ -f "$SUB/PCB_${PROJECT_NAME}_${VERSION}.png" ] && ARGS+=(--pcb-image "$SUB/PCB_${PROJECT_NAME}_${VERSION}.png")
|
||||||
|
[ -f "$VAL/${PROJECT_NAME}-erc.html" ] && ARGS+=(--erc-report "$VAL/${PROJECT_NAME}-erc.html")
|
||||||
|
[ -f "$VAL/${PROJECT_NAME}-drc.html" ] && ARGS+=(--drc-report "$VAL/${PROJECT_NAME}-drc.html")
|
||||||
|
upload-bom.sh "${ARGS[@]}"
|
||||||
|
BASH_SCRIPT
|
||||||
|
env:
|
||||||
|
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: Fabrication-release
|
||||||
|
path: Fabrication/
|
||||||
|
retention-days: 7
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
kimelon git unlock all
|
|
||||||
|
|
||||||
command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/pre-push'.\n"; exit 2; }
|
command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/pre-push'.\n"; exit 2; }
|
||||||
git lfs pre-push "$@"
|
git lfs pre-push "$@"
|
||||||
|
|||||||
146
kibot-ci.yml
146
kibot-ci.yml
@@ -310,19 +310,51 @@ generate_panel:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Stage: InvenTree
|
# Stage: Astable (replaces the old InvenTree/hfsntree upload path)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
.inventree_base:
|
#
|
||||||
stage: inventree
|
# Uploads the BOM + every fabrication output to Astable via
|
||||||
image: python:3.11-slim
|
# POST /api/v1/bom/upload. Astable owns the supplier-API
|
||||||
before_script:
|
# credentials, so the CI runner doesn't need Digi-Key / Mouser
|
||||||
- |
|
# tokens any more. The upload is strict: any MPN that can't be
|
||||||
cd hfsntree
|
# resolved against an enabled supplier integration aborts the
|
||||||
pip install -e . --quiet
|
# upload and writes nothing.
|
||||||
cd $CI_PROJECT_DIR
|
#
|
||||||
|
# Revision-targeting follows the existing dev/main convention:
|
||||||
|
# - dev branch: uploads to the part's DRAFT "dev" revision.
|
||||||
|
# - main branch: uploads to the rev named from the commit
|
||||||
|
# message's "VX.Y" token, promoted to ACTIVE.
|
||||||
|
# - MR-to-main: uploads to "dev" (matches old behaviour).
|
||||||
|
#
|
||||||
|
# Required CI variables (set group-level on
|
||||||
|
# Micromelon/education/hardware/, masked + protected):
|
||||||
|
#
|
||||||
|
# ASTABLE_URL e.g. https://astable.timhadwen.com
|
||||||
|
# ASTABLE_API_TOKEN minted at /manage/api-tokens (ast_…)
|
||||||
|
#
|
||||||
|
# The script vendored at scripts/upload-bom.sh in this repo handles
|
||||||
|
# the POST + polling loop. PROJECT_NAME flows through as the
|
||||||
|
# part-ipn — Astable splits on the first hyphen to derive the
|
||||||
|
# PCBA IPN ("<base>A") + bare-PCB IPN ("<base>") + display name.
|
||||||
|
|
||||||
inventree_dev:
|
.astable_base:
|
||||||
extends: .inventree_base
|
stage: inventree # keep stage name so existing `needs:`
|
||||||
|
# downstream (upload_packages) still match
|
||||||
|
# Plain alpine (not curlimages/curl) — we need to apk-add bash+jq
|
||||||
|
# and curlimages/curl runs as a non-root user that can't write the
|
||||||
|
# apk db. alpine:3.20 is ~5MB, identical pull cost.
|
||||||
|
image: alpine:3.20
|
||||||
|
before_script:
|
||||||
|
- apk add --no-cache bash curl jq
|
||||||
|
# Each PCB project checks out kicad-ci as a submodule under
|
||||||
|
# .gitlab/ (with GIT_SUBMODULE_STRATEGY: recursive at the top of
|
||||||
|
# this file), so scripts/upload-bom.sh is available locally —
|
||||||
|
# no raw-URL fetch needed, and the script's version stays in
|
||||||
|
# lockstep with the submodule pointer the project pins to.
|
||||||
|
- install -m 0755 "$CI_PROJECT_DIR/.gitlab/scripts/upload-bom.sh" /usr/local/bin/upload-bom.sh
|
||||||
|
|
||||||
|
astable_dev:
|
||||||
|
extends: .astable_base
|
||||||
rules:
|
rules:
|
||||||
- !reference [.dev_rules, rules]
|
- !reference [.dev_rules, rules]
|
||||||
needs:
|
needs:
|
||||||
@@ -334,21 +366,40 @@ inventree_dev:
|
|||||||
artifacts: true
|
artifacts: true
|
||||||
- job: generate_3d
|
- job: generate_3d
|
||||||
artifacts: true
|
artifacts: true
|
||||||
- job: generate_gerbers
|
# Script body wrapped in `bash -s` because alpine ships busybox ash
|
||||||
artifacts: true
|
# by default, which doesn't parse bash arrays (ARGS=(...)). The
|
||||||
- job: generate_position
|
# quoted heredoc keeps GitLab from substituting variables — bash
|
||||||
artifacts: true
|
# sees the raw $CI_PROJECT_DIR etc. and expands them itself.
|
||||||
- job: generate_panel
|
|
||||||
artifacts: true
|
|
||||||
optional: true
|
|
||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
echo "Running InvenTree upload for dev branch (revision: dev)"
|
bash -s <<'BASH_SCRIPT'
|
||||||
cd hfsntree
|
set -euo pipefail
|
||||||
python main.py batch $CI_PROJECT_DIR/Fabrication --version-override dev -y
|
FAB="$CI_PROJECT_DIR/Fabrication"
|
||||||
|
SUB="$FAB/${PROJECT_NAME}"
|
||||||
|
VAL="$CI_PROJECT_DIR/Validation"
|
||||||
|
|
||||||
inventree_main:
|
ARGS=(
|
||||||
extends: .inventree_base
|
--file "$FAB/${PROJECT_NAME}_dev_bom.csv"
|
||||||
|
--part-ipn "$PROJECT_NAME"
|
||||||
|
--branch dev
|
||||||
|
--commit-sha "$CI_COMMIT_SHA"
|
||||||
|
--commit-message "$CI_COMMIT_MESSAGE"
|
||||||
|
)
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_dev_cpl.csv" ] && ARGS+=(--cpl "$FAB/${PROJECT_NAME}_dev_cpl.csv")
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_dev.step" ] && ARGS+=(--step "$FAB/${PROJECT_NAME}_dev.step")
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_dev_schematic.pdf" ] && ARGS+=(--schematic-pdf "$FAB/${PROJECT_NAME}_dev_schematic.pdf")
|
||||||
|
[ -f "$SUB/${PROJECT_NAME}_dev_ibom.html" ] && ARGS+=(--ibom-html "$SUB/${PROJECT_NAME}_dev_ibom.html")
|
||||||
|
[ -f "$SUB/${PROJECT_NAME}_dev_bom.xlsx" ] && ARGS+=(--bom-xlsx "$SUB/${PROJECT_NAME}_dev_bom.xlsx")
|
||||||
|
[ -f "$SUB/PCBA_${PROJECT_NAME}_dev.png" ] && ARGS+=(--pcba-image "$SUB/PCBA_${PROJECT_NAME}_dev.png")
|
||||||
|
[ -f "$SUB/PCB_${PROJECT_NAME}_dev.png" ] && ARGS+=(--pcb-image "$SUB/PCB_${PROJECT_NAME}_dev.png")
|
||||||
|
[ -f "$VAL/${PROJECT_NAME}-erc.html" ] && ARGS+=(--erc-report "$VAL/${PROJECT_NAME}-erc.html")
|
||||||
|
[ -f "$VAL/${PROJECT_NAME}-drc.html" ] && ARGS+=(--drc-report "$VAL/${PROJECT_NAME}-drc.html")
|
||||||
|
|
||||||
|
upload-bom.sh "${ARGS[@]}"
|
||||||
|
BASH_SCRIPT
|
||||||
|
|
||||||
|
astable_main:
|
||||||
|
extends: .astable_base
|
||||||
rules:
|
rules:
|
||||||
- !reference [.main_rules, rules]
|
- !reference [.main_rules, rules]
|
||||||
needs:
|
needs:
|
||||||
@@ -372,28 +423,43 @@ inventree_main:
|
|||||||
paths:
|
paths:
|
||||||
- Fabrication/
|
- Fabrication/
|
||||||
expire_in: 1 week
|
expire_in: 1 week
|
||||||
|
# Same alpine + ash limitation as astable_dev — invoke bash.
|
||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
cd hfsntree
|
bash -s <<'BASH_SCRIPT'
|
||||||
|
set -euo pipefail
|
||||||
|
FAB="$CI_PROJECT_DIR/Fabrication"
|
||||||
|
SUB="$FAB/${PROJECT_NAME}"
|
||||||
|
VAL="$CI_PROJECT_DIR/Validation"
|
||||||
|
|
||||||
# MRs targeting main upload to "dev" revision for pre-ordering parts
|
|
||||||
# Actual commits to main upload to the versioned revision
|
|
||||||
if [[ "$CI_PIPELINE_SOURCE" == "merge_request_event" ]]; then
|
if [[ "$CI_PIPELINE_SOURCE" == "merge_request_event" ]]; then
|
||||||
echo "Running InvenTree upload for MR (revision: dev)"
|
BRANCH=dev
|
||||||
python main.py batch $CI_PROJECT_DIR/Fabrication --version-override dev -y
|
|
||||||
else
|
else
|
||||||
echo "Running InvenTree upload for main branch (revision: $VERSION)"
|
BRANCH=main
|
||||||
python main.py batch $CI_PROJECT_DIR/Fabrication -y
|
|
||||||
|
|
||||||
# Generate Samsung P&P files only on actual release
|
|
||||||
echo "Generating Samsung pick-and-place files..."
|
|
||||||
python main.py samsung $CI_PROJECT_DIR/Fabrication
|
|
||||||
|
|
||||||
# Deactivate old revisions (keep only current release + dev)
|
|
||||||
echo "Deactivating old InvenTree revisions..."
|
|
||||||
python main.py deactivate-old $CI_PROJECT_DIR/Fabrication
|
|
||||||
fi
|
fi
|
||||||
cd $CI_PROJECT_DIR
|
|
||||||
|
ARGS=(
|
||||||
|
--file "$FAB/${PROJECT_NAME}_${VERSION}_bom.csv"
|
||||||
|
--part-ipn "$PROJECT_NAME"
|
||||||
|
--branch "$BRANCH"
|
||||||
|
--commit-sha "$CI_COMMIT_SHA"
|
||||||
|
--commit-message "$CI_COMMIT_MESSAGE"
|
||||||
|
)
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_${VERSION}_cpl.csv" ] && ARGS+=(--cpl "$FAB/${PROJECT_NAME}_${VERSION}_cpl.csv")
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_${VERSION}_JLC.zip" ] && ARGS+=(--gerbers "$FAB/${PROJECT_NAME}_${VERSION}_JLC.zip")
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_panel_${VERSION}_JLC.zip" ] && ARGS+=(--panel-gerbers "$FAB/${PROJECT_NAME}_panel_${VERSION}_JLC.zip")
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_${VERSION}.step" ] && ARGS+=(--step "$FAB/${PROJECT_NAME}_${VERSION}.step")
|
||||||
|
[ -f "$FAB/${PROJECT_NAME}_${VERSION}_schematic.pdf" ] && ARGS+=(--schematic-pdf "$FAB/${PROJECT_NAME}_${VERSION}_schematic.pdf")
|
||||||
|
[ -f "$SUB/${PROJECT_NAME}_${VERSION}_PCB.pdf" ] && ARGS+=(--pcb-pdf "$SUB/${PROJECT_NAME}_${VERSION}_PCB.pdf")
|
||||||
|
[ -f "$SUB/${PROJECT_NAME}_${VERSION}_ibom.html" ] && ARGS+=(--ibom-html "$SUB/${PROJECT_NAME}_${VERSION}_ibom.html")
|
||||||
|
[ -f "$SUB/${PROJECT_NAME}_${VERSION}_bom.xlsx" ] && ARGS+=(--bom-xlsx "$SUB/${PROJECT_NAME}_${VERSION}_bom.xlsx")
|
||||||
|
[ -f "$SUB/PCBA_${PROJECT_NAME}_${VERSION}.png" ] && ARGS+=(--pcba-image "$SUB/PCBA_${PROJECT_NAME}_${VERSION}.png")
|
||||||
|
[ -f "$SUB/PCB_${PROJECT_NAME}_${VERSION}.png" ] && ARGS+=(--pcb-image "$SUB/PCB_${PROJECT_NAME}_${VERSION}.png")
|
||||||
|
[ -f "$VAL/${PROJECT_NAME}-erc.html" ] && ARGS+=(--erc-report "$VAL/${PROJECT_NAME}-erc.html")
|
||||||
|
[ -f "$VAL/${PROJECT_NAME}-drc.html" ] && ARGS+=(--drc-report "$VAL/${PROJECT_NAME}-drc.html")
|
||||||
|
|
||||||
|
upload-bom.sh "${ARGS[@]}"
|
||||||
|
BASH_SCRIPT
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Stage: Release
|
# Stage: Release
|
||||||
@@ -407,7 +473,7 @@ upload_packages:
|
|||||||
needs:
|
needs:
|
||||||
- job: extract_version
|
- job: extract_version
|
||||||
artifacts: true
|
artifacts: true
|
||||||
- job: inventree_main
|
- job: astable_main
|
||||||
artifacts: true
|
artifacts: true
|
||||||
script:
|
script:
|
||||||
- apt-get update && apt-get -y install zip
|
- apt-get update && apt-get -y install zip
|
||||||
|
|||||||
157
scripts/upload-bom.sh
Executable file
157
scripts/upload-bom.sh
Executable file
@@ -0,0 +1,157 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Upload a BOM to Astable from CI. POSTs the multipart form to
|
||||||
|
# /api/v1/bom/upload, then polls the returned jobId until the worker
|
||||||
|
# finishes (done/failed). Prints the result JSON on done; exits non-zero
|
||||||
|
# on any failure (timeout, parse error, worker failure, …) so the CI
|
||||||
|
# job fails visibly.
|
||||||
|
#
|
||||||
|
# Env vars (required):
|
||||||
|
# ASTABLE_URL — base URL (e.g. https://astable.timhadwen.com)
|
||||||
|
# ASTABLE_API_TOKEN — bearer token minted at /manage/api-tokens
|
||||||
|
#
|
||||||
|
# Args:
|
||||||
|
# --file <path> — BOM CSV to upload (required)
|
||||||
|
# --part-ipn <ipn> — InvPart.ipn of the assembly (required)
|
||||||
|
# --branch <dev|main> — drives revision targeting (required)
|
||||||
|
# --commit-sha <sha> — for audit (optional)
|
||||||
|
# --commit-message <msg> — drives "main" revision label (optional)
|
||||||
|
# --poll-interval <sec> — default 3
|
||||||
|
# --timeout <sec> — default 300 (5 min)
|
||||||
|
#
|
||||||
|
# Designed to be runnable under gitlab-ci-local — just set ASTABLE_URL
|
||||||
|
# to your dev URL (e.g. http://host.docker.internal:3000 if Astable is
|
||||||
|
# on the host) and gitlab-ci-local will pick it up from .env or the
|
||||||
|
# `--variable` flag.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
POLL_INTERVAL=3
|
||||||
|
TIMEOUT=300
|
||||||
|
FILE=""
|
||||||
|
CPL=""
|
||||||
|
GERBERS=""
|
||||||
|
PANEL_GERBERS=""
|
||||||
|
STEP=""
|
||||||
|
PCB_PDF=""
|
||||||
|
SCHEMATIC_PDF=""
|
||||||
|
IBOM_HTML=""
|
||||||
|
BOM_XLSX=""
|
||||||
|
PCBA_IMAGE=""
|
||||||
|
PCB_IMAGE=""
|
||||||
|
DRC_REPORT=""
|
||||||
|
ERC_REPORT=""
|
||||||
|
PART_IPN=""
|
||||||
|
BRANCH=""
|
||||||
|
COMMIT_SHA=""
|
||||||
|
COMMIT_MESSAGE=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--file) FILE="$2"; shift 2 ;;
|
||||||
|
--cpl) CPL="$2"; shift 2 ;;
|
||||||
|
--gerbers) GERBERS="$2"; shift 2 ;;
|
||||||
|
--panel-gerbers) PANEL_GERBERS="$2"; shift 2 ;;
|
||||||
|
--step) STEP="$2"; shift 2 ;;
|
||||||
|
--pcb-pdf) PCB_PDF="$2"; shift 2 ;;
|
||||||
|
--schematic-pdf) SCHEMATIC_PDF="$2"; shift 2 ;;
|
||||||
|
--ibom-html) IBOM_HTML="$2"; shift 2 ;;
|
||||||
|
--bom-xlsx) BOM_XLSX="$2"; shift 2 ;;
|
||||||
|
--pcba-image) PCBA_IMAGE="$2"; shift 2 ;;
|
||||||
|
--pcb-image) PCB_IMAGE="$2"; shift 2 ;;
|
||||||
|
--drc-report) DRC_REPORT="$2"; shift 2 ;;
|
||||||
|
--erc-report) ERC_REPORT="$2"; shift 2 ;;
|
||||||
|
--part-ipn) PART_IPN="$2"; shift 2 ;;
|
||||||
|
--branch) BRANCH="$2"; shift 2 ;;
|
||||||
|
--commit-sha) COMMIT_SHA="$2"; shift 2 ;;
|
||||||
|
--commit-message) COMMIT_MESSAGE="$2"; shift 2 ;;
|
||||||
|
--poll-interval) POLL_INTERVAL="$2"; shift 2 ;;
|
||||||
|
--timeout) TIMEOUT="$2"; shift 2 ;;
|
||||||
|
*) echo "Unknown arg: $1" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
: "${ASTABLE_URL:?ASTABLE_URL must be set}"
|
||||||
|
: "${ASTABLE_API_TOKEN:?ASTABLE_API_TOKEN must be set}"
|
||||||
|
: "${FILE:?--file is required}"
|
||||||
|
: "${PART_IPN:?--part-ipn is required}"
|
||||||
|
: "${BRANCH:?--branch is required}"
|
||||||
|
[[ -f "$FILE" ]] || { echo "BOM file not found: $FILE" >&2; exit 2; }
|
||||||
|
[[ -z "$CPL" || -f "$CPL" ]] || { echo "CPL file not found: $CPL" >&2; exit 2; }
|
||||||
|
[[ "$BRANCH" == "dev" || "$BRANCH" == "main" ]] || { echo "--branch must be dev or main" >&2; exit 2; }
|
||||||
|
|
||||||
|
echo "[bom-upload] POST $ASTABLE_URL/api/v1/bom/upload"
|
||||||
|
echo "[bom-upload] partIpn=$PART_IPN branch=$BRANCH file=$FILE${CPL:+ cpl=$CPL}"
|
||||||
|
|
||||||
|
CURL_ARGS=(
|
||||||
|
-fsS -X POST
|
||||||
|
-H "authorization: Bearer $ASTABLE_API_TOKEN"
|
||||||
|
-F "file=@$FILE"
|
||||||
|
-F "partIpn=$PART_IPN"
|
||||||
|
-F "branch=$BRANCH"
|
||||||
|
-F "commitSha=$COMMIT_SHA"
|
||||||
|
-F "commitMessage=$COMMIT_MESSAGE"
|
||||||
|
)
|
||||||
|
[[ -n "$CPL" ]] && CURL_ARGS+=(-F "cpl=@$CPL")
|
||||||
|
[[ -n "$GERBERS" ]] && CURL_ARGS+=(-F "gerbers=@$GERBERS")
|
||||||
|
[[ -n "$PANEL_GERBERS" ]] && CURL_ARGS+=(-F "panelGerbers=@$PANEL_GERBERS")
|
||||||
|
[[ -n "$STEP" ]] && CURL_ARGS+=(-F "step=@$STEP")
|
||||||
|
[[ -n "$PCB_PDF" ]] && CURL_ARGS+=(-F "pcbPdf=@$PCB_PDF")
|
||||||
|
[[ -n "$SCHEMATIC_PDF" ]] && CURL_ARGS+=(-F "schematicPdf=@$SCHEMATIC_PDF")
|
||||||
|
[[ -n "$IBOM_HTML" ]] && CURL_ARGS+=(-F "ibomHtml=@$IBOM_HTML")
|
||||||
|
[[ -n "$BOM_XLSX" ]] && CURL_ARGS+=(-F "bomXlsx=@$BOM_XLSX")
|
||||||
|
[[ -n "$PCBA_IMAGE" ]] && CURL_ARGS+=(-F "pcbaImage=@$PCBA_IMAGE")
|
||||||
|
[[ -n "$PCB_IMAGE" ]] && CURL_ARGS+=(-F "pcbImage=@$PCB_IMAGE")
|
||||||
|
[[ -n "$DRC_REPORT" ]] && CURL_ARGS+=(-F "drcReport=@$DRC_REPORT")
|
||||||
|
[[ -n "$ERC_REPORT" ]] && CURL_ARGS+=(-F "ercReport=@$ERC_REPORT")
|
||||||
|
RESPONSE=$(curl "${CURL_ARGS[@]}" "$ASTABLE_URL/api/v1/bom/upload")
|
||||||
|
|
||||||
|
JOB_ID=$(echo "$RESPONSE" | jq -r '.jobId // empty')
|
||||||
|
POLL_URL=$(echo "$RESPONSE" | jq -r '.pollUrl // empty')
|
||||||
|
LINE_COUNT=$(echo "$RESPONSE" | jq -r '.lineCount // 0')
|
||||||
|
|
||||||
|
if [[ -z "$JOB_ID" || -z "$POLL_URL" ]]; then
|
||||||
|
echo "[bom-upload] enqueue failed: $RESPONSE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[bom-upload] queued ($LINE_COUNT lines parsed) → job $JOB_ID"
|
||||||
|
|
||||||
|
START=$(date +%s)
|
||||||
|
while true; do
|
||||||
|
NOW=$(date +%s)
|
||||||
|
ELAPSED=$((NOW - START))
|
||||||
|
if (( ELAPSED > TIMEOUT )); then
|
||||||
|
echo "[bom-upload] timed out after ${TIMEOUT}s" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
STATE=$(curl -fsS \
|
||||||
|
-H "authorization: Bearer $ASTABLE_API_TOKEN" \
|
||||||
|
"$ASTABLE_URL$POLL_URL")
|
||||||
|
STATUS=$(echo "$STATE" | jq -r '.status')
|
||||||
|
STEP=$(echo "$STATE" | jq -r '.progress.message // empty')
|
||||||
|
|
||||||
|
case "$STATUS" in
|
||||||
|
done)
|
||||||
|
echo "[bom-upload] done"
|
||||||
|
echo "$STATE" | jq -r '.result | " added=\(.added) updated=\(.updated) removed=\(.removed) missingPartsCreated=\(.missingPartsCreated) unresolvedSuppliers=\(.unresolvedSuppliers)"'
|
||||||
|
REV=$(echo "$STATE" | jq -r '.result.revision.label // "(none)"')
|
||||||
|
echo " revision: $REV"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
failed)
|
||||||
|
ERROR=$(echo "$STATE" | jq -r '.error // "(no error message)"')
|
||||||
|
echo "[bom-upload] failed: $ERROR" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
pending|running)
|
||||||
|
[[ -n "$STEP" ]] && echo " · $STATUS — $STEP"
|
||||||
|
sleep "$POLL_INTERVAL"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "[bom-upload] unexpected status: $STATUS" >&2
|
||||||
|
echo "$STATE" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user