Artifacts: dsci@007aefe.1789628812. File: answer.md
Below is a complete **DSCI** pipeline that builds, tests and (optionally) deploys a generic C++ project that uses a **Makefile**.
All heavy‑lifting is done in normal Bash scripts – the only DSCI‑specific parts are the tiny `jobs.yaml` file and the helper calls (`run_task`, `config`, `update_state`, …).
---
## 1️⃣ Repository layout
```
.
├── .dsci/
│ ├── jobs.yaml # pipeline definition (list of jobs)
│ ├── build_job/ # ── Job 1 – compile the project
│ │ ├── job.bash
│ │ ├── config.yaml # default job parameters
│ │ └── tasks/
│ │ └── build/
│ │ └── task.bash
│ ├── test_job/ # ── Job 2 – run unit‑tests
│ │ ├── job.bash
│ │ └── tasks/
│ │ └── test/
│ │ └── task.bash
│ └── deploy_job/ # ── Job 3 – optional deployment
│ ├── job.bash
│ └── tasks/
│ └── deploy/
│ └── task.bash
├── Makefile # (your regular C++ makefile – not shown)
└── src/ … # C++ source tree
```
All files that contain DSCI logic are shown below inside **markdown code blocks** labelled with their filenames.
---
## 2️⃣ `jobs.yaml` – only the job list
```yaml
# .dsci/jobs.yaml
# ----------------------------------------------------------------------
# The pipeline consists of three sequential jobs:
# 1️⃣ build_job – compiles the code with `make`
# 2️⃣ test_job – runs the test suite
# 3️⃣ deploy_job – copies the binary somewhere if tests passed
# ----------------------------------------------------------------------
jobs:
- id: build_job
path: build_job/
# optional override of the default build type (Debug|Release)
# params:
# BUILD_TYPE: Debug
- id: test_job
path: test_job/
- id: deploy_job
path: deploy_job/
# you could add a `skip: $(config SKIP_DEPLOY) == "true"` flag here
```
*Only the list of jobs (and optional per‑job `params`) is declared – all real logic lives in the script files.*
---
## 3️⃣ Job 1 – **build_job**
### 3.1 Default parameters (`config.yaml`)
```yaml
# .dsci/build_job/config.yaml
# ----------------------------------------------------------------------
# Default values for this job. They can be overridden in `jobs.yaml`
# ----------------------------------------------------------------------
BUILD_TYPE: Release # can be “Debug” or “Release”
BUILD_TARGET: all # the target you want `make` to run
```
### 3.2 Job entry point (`job.bash`)
```bash
# .dsci/build_job/job.bash
#!/usr/bin/env bash
# ----------------------------------------------------------------------
# Job “build_job” – simply runs the single “build” task.
# ----------------------------------------------------------------------
set -euo pipefail
# Run the only task of this job
run_task "build"
```
### 3.3 Build task (`task.bash`)
```bash
# .dsci/build_job/tasks/build/task.bash
#!/usr/bin/env bash
# ----------------------------------------------------------------------
# Task “build” – compiles the C++ sources with `make`.
# It reads the job parameters via the `config` helper and stores the
# produced binary in the shared `~/artifacts` directory.
# ----------------------------------------------------------------------
set -euo pipefail
# ----------------------------------------------------------------------
# 1️⃣ Read job parameters (they come from config.yaml or from the pipeline)
# ----------------------------------------------------------------------
BUILD_TYPE=$(config BUILD_TYPE) # e.g. Release or Debug
BUILD_TARGET=$(config BUILD_TARGET) # default is “all”
echo "=== Build job: BUILD_TYPE=${BUILD_TYPE}, BUILD_TARGET=${BUILD_TARGET} ==="
# ----------------------------------------------------------------------
# 2️⃣ Run make. Pass the build type as a variable – adapt to your Makefile.
# ----------------------------------------------------------------------
make ${BUILD_TARGET} BUILD_TYPE=${BUILD_TYPE}
# ----------------------------------------------------------------------
# 3️⃣ Export the binary as an artifact for downstream jobs.
# (Assume the binary is ./bin/app)
# ----------------------------------------------------------------------
ARTIFACT_DIR="${HOME}/artifacts"
mkdir -p "${ARTIFACT_DIR}"
cp ./bin/app "${ARTIFACT_DIR}/app_${BUILD_TYPE}"
echo "✅ Built binary copied to ${ARTIFACT_DIR}/app_${BUILD_TYPE}"
```
---
## 4️⃣ Job 2 – **test_job**
### 4.1 Job entry point (`job.bash`)
```bash
# .dsci/test_job/job.bash
#!/usr/bin/env bash
# ----------------------------------------------------------------------
# Job “test_job” – executes the unit‑test suite.
# ----------------------------------------------------------------------
set -euo pipefail
run_task "test"
```
### 4.2 Test task (`task.bash`)
```bash
# .dsci/test_job/tasks/test/task.bash
#!/usr/bin/env bash
# ----------------------------------------------------------------------
# Task “test” – runs `make test` (or any command you prefer) and
# records the result in the job state so that later jobs can read it.
# ----------------------------------------------------------------------
set -euo pipefail
echo "=== Running test suite ==="
# Run the tests – adapt the target name to your Makefile.
# The command must return a non‑zero exit code on failure,
# otherwise the whole job would abort (set -e).
if make test; then
TEST_STATUS="passed"
echo "✅ Tests passed"
else
TEST_STATUS="failed"
echo "❌ Tests failed"
fi
# ----------------------------------------------------------------------
# 5️⃣ Export the test status as state (shared with later jobs)
# ----------------------------------------------------------------------
# IMPORTANT: update_state must receive ONE JSON object.
update_state "{ \"test_status\": \"${TEST_STATUS}\" }"
```
*The downstream **deploy_job** can read `test_status` from the pipeline state.*
---
## 5️⃣ Job 3 – **deploy_job**
> **Optional** – you can skip this job (or add a `skip:` clause in `jobs.yaml`) if you only want to build & test.
### 5️⃣1 Job entry point (`job.bash`)
```bash
# .dsci/deploy_job/job.bash
#!/usr/bin/env bash
# ----------------------------------------------------------------------
# Job “deploy_job” – copies the built binary to a deployment folder
# only when the previous test job succeeded.
# ----------------------------------------------------------------------
set -euo pipefail
run_task "deploy"
```
### 5️⃣2 Deploy task (`task.bash`)
```bash
# .dsci/deploy_job/tasks/deploy/task.bash
#!/usr/bin/env bash
# ----------------------------------------------------------------------
# Task “deploy” – reads the test result from the pipeline state,
# selects the appropriate artifact, and copies it to a target location.
# ----------------------------------------------------------------------
set -euo pipefail
# ----------------------------------------------------------------------
# 1️⃣ Read the whole pipeline state (JSON) and pull the test status.
# The state is automatically placed under the reserved key "_dsci_".
# ----------------------------------------------------------------------
PIPELINE_STATE=$(config) # returns the full JSON
TEST_STATUS=$(echo "$PIPELINE_STATE" | jq -r '._dsci_.test_job.test_status')
echo "=== Deploy job – test status reported as: ${TEST_STATUS} ==="
if [[ "${TEST_STATUS}" != "passed" ]]; then
echo "⚠️ Tests did not pass – aborting deployment."
exit 0 # not an error, just skip the deployment step
fi
# ----------------------------------------------------------------------
# 2️⃣ Locate the built artifact. We stored it as:
# ~/artifacts/app_