# CI/CD Pipeline Debugging Session - 2026-01-17

## The Journey from "yaml invalid" to Green Pipeline

This document chronicles a debugging session that took a broken GitLab CI pipeline from "Unable to create pipeline" to fully passing in approximately 90 minutes and 12 commits.

## Initial State

```
Unable to create pipeline
Local file `test-matrix.yml` does not exist!
```

## Issue #1: Missing `test-matrix.yml`

**Problem**: GitLab validates `include` directives at pipeline creation time, before any jobs run. The pipeline config referenced a file that would be generated by a job, but GitLab needed it to exist in the repo.

**Fix**: Created `test-matrix.yml` as a placeholder file.

**Commit**: `fix(ci): Add test-matrix.yml placeholder for GitLab CI`

---

## Issue #2: Invalid YAML Syntax

**Problem**: The placeholder file had comments but wasn't valid GitLab CI YAML.

```
Included file `test-matrix.yml` does not have valid YAML syntax!
```

**Fix**: Created a valid but inactive job definition:

```yaml
.test-matrix-placeholder:
  script:
    - echo "Placeholder"
  rules:
    - when: never
```

**Commit**: `fix(ci): Use valid GitLab CI YAML syntax in placeholder`

---

## Issue #3: No Runner Available

**Problem**: Jobs were stuck in "pending" - no runners matched.

```
This job is stuck because the project doesn't have any runners online assigned to it.
```

**Fix**: Added `default: tags: [build]` to `.gitlab-ci.yml` to route jobs to the `build` runner.

**Commit**: `fix(ci): Add build runner tag to all jobs`

---

## Issue #4: Shell Executor vs Docker

**Problem**: The `build` runner uses a shell executor, not Docker. Alpine commands failed.

```
bash: line 159: apk: command not found
```

**Fix**: Removed all `image: alpine:latest` and `apk add` commands from `.gitlab-ci.yml`. The shell executor runs directly on the host.

**Commit**: `fix(ci): Remove Docker/Alpine config for shell executor`

---

## Issue #5: Output File Conflict

**Problem**: The `generate-matrix.sh` script wrote to `test-matrix.yml` internally, then the CI config also redirected to the same file.

```
cat: test-matrix.yml: input file is output file
```

**Fix**: Removed the `> test-matrix.yml` redirect from `.gitlab-ci.yml` since the script handles file creation internally.

**Commit**: `fix(ci): Fix generate-matrix output conflict and remove Alpine deps`

---

## Issue #6: Missing C Development Libraries

**Problem**: The build server lacked headers for compiling the C SDK.

```
src/un.c:27:10: fatal error: curl/curl.h: No such file or directory
```

**Fix**: Updated Salt states (`foxhop-states/gitlab/build-host/ubuntu.sls`) to install:
- `libcurl4-openssl-dev`
- `libwebsockets-dev`
- `libjson-c-dev`

Then ran `salt 'build.unturf.com' state.highstate` to apply.

**Commit**: (in foxhop-states) `feat(build): Add C SDK dependencies for un-inception`

---

## Issue #7: Type Definitions Not Found

**Problem**: Library types like `unsandbox_result_t` weren't defined when building CLI.

```
src/un.c:5714:28: error: unknown type name 'unsandbox_result_t'
```

**Root Cause**: The types are defined in `un.h`, which is only included when `UNSANDBOX_LIBRARY` is defined. But we were building CLI without that flag.

**Attempted Fix**: Build with `-DUNSANDBOX_LIBRARY`

**Commit**: `fix(c): Build CLI with library flag to include type definitions`

---

## Issue #8: No main() Function

**Problem**: When `UNSANDBOX_LIBRARY` is defined, `main()` is excluded via `#ifndef`.

```
undefined reference to `main'
```

**Code Structure**:
```c
#ifdef UNSANDBOX_LIBRARY
#include "un.h"  // Types defined here
#endif

// Library API functions (used types but weren't wrapped)
void unsandbox_free_result(unsandbox_result_t *result) { ... }

#ifndef UNSANDBOX_LIBRARY
int main(int argc, char *argv[]) { ... }  // CLI entry point
#endif
```

**The Dilemma**:
- Without `UNSANDBOX_LIBRARY`: main() included, but types undefined → compile error
- With `UNSANDBOX_LIBRARY`: types defined, but main() excluded → link error

**Fix**: Wrapped library API functions (lines 5714-8179) in `#ifdef UNSANDBOX_LIBRARY`:

```c
#ifdef UNSANDBOX_LIBRARY
/* Library API - Only compiled when UNSANDBOX_LIBRARY is defined */
void unsandbox_free_result(unsandbox_result_t *result) { ... }
// ... all library functions ...
#endif /* UNSANDBOX_LIBRARY */

#ifndef UNSANDBOX_LIBRARY
int main(int argc, char *argv[]) { ... }
#endif
```

Now:
- **CLI build** (no flag): Compiles main(), skips library API
- **Library build** (with flag): Compiles library API, skips main()

**Commit**: `fix(c): Wrap library API functions in ifdef UNSANDBOX_LIBRARY`

---

## Final Result

```
Pipeline #10647: SUCCESS
All 9 jobs passed:
  - detect-changes
  - generate-matrix
  - build
  - science-validate-examples
  - science-lint-sdks
  - science-benchmark-clients
  - validate-examples
  - generate-documentation
  - report
```

## Lessons Learned

1. **GitLab validates includes at parse time** - Dynamic includes via job artifacts don't work as expected. Use placeholder files.

2. **Know your executor** - Shell executors run on the host; Docker executors run in containers. Don't mix commands.

3. **Salt state changes need explicit application** - Pushing to the states repo doesn't auto-apply. Run `state.highstate`.

4. **C preprocessor conditionals are tricky** - When code has `#ifdef` for library vs CLI modes, ensure all dependent code is properly wrapped.

5. **Incremental debugging works** - Each fix revealed the next issue. 12 commits, 8 distinct problems, one green pipeline.

## Commit History

```
b6d6f16 fix(c): Wrap library API functions in ifdef UNSANDBOX_LIBRARY
f72b6b4 fix(c): Build CLI with library flag to include type definitions
4594a0f ci: Retry after curl deps installed
3c38598 ci: Retry after build server deps installed
83f2691 feat(ci): Implement inception testing pattern
c769427 fix(ci): Fix generate-matrix output conflict and remove Alpine deps
6ac4f53 fix(ci): Remove Docker/Alpine config for shell executor
22df152 fix(ci): Add build runner tag to all jobs
164ebcc fix(ci): Use valid GitLab CI YAML syntax in placeholder
512840c fix(ci): Add test-matrix.yml placeholder for GitLab CI
```

## Architecture Note: Inception Testing

The CI now implements "inception testing" - using the C CLI (`build/un`) to test all other SDK implementations through the unsandbox API:

```
build server → un (C binary) → unsandbox API → un.py/un.js/etc → unsandbox API → test code
```

This means the build server only needs: `gcc`, `libcurl`, `libwebsockets`, `libssl`. No Python, Ruby, PHP, etc. required locally - everything runs through unsandbox.
