# Obs Management

> Manage Open Build Service (OBS) projects. Use when creating, building, testing, debugging, publishing, or installing packages for home:aspiers.

- Skill: `aspiers/obs-management` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add aspiers/obs-management`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aspiers/obs-management/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: aspiers (https://skillmd.com/u/aspiers)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/aspiers/obs-management

---


# OBS Management

Manage packages in the Open Build Service project `home:aspiers`.

## Build from source only

**Never package pre-built binaries, AppImages, release RPMs, or other binary
artifacts.** Use upstream source archives or source-control checkouts and build
inside OBS. This preserves reproducibility, reviewability, distribution
integration, and licence compliance.

Release pages often present binaries more prominently than source archives.
Verify the selected artifact contains source before adding it to a package.

## Locate the project checkout

Do not hardcode the local path separator. The OBS project name is always
`home:aspiers`, but `osc`'s `checkout_no_colon` option controls its checkout
layout:

- default (`checkout_no_colon = 0`): `/home/adam/OBS/home:aspiers`
- enabled (`checkout_no_colon = 1`): `/home/adam/OBS/home/aspiers`

`checkout_no_colon` takes priority over `project_separator`. An unset option
means the default is in effect. Prefer an existing working copy and verify it
rather than inferring its path from the current configuration, which may have
changed since checkout:

```bash
for candidate in /home/adam/OBS/home:aspiers /home/adam/OBS/home/aspiers; do
    if test "$(cat "$candidate/.osc/_project" 2>/dev/null)" = home:aspiers; then
        PROJECT_DIR=$candidate
        break
    fi
done
test -n "${PROJECT_DIR:-}" && osc info "$PROJECT_DIR"
```

Inspect or change the option with:

```bash
osc config general checkout_no_colon
osc config general checkout_no_colon 1   # slash layout for future checkouts
osc config general checkout_no_colon 0   # colon layout for future checkouts
```

If no working copy exists, run `osc checkout home:aspiers` from the intended
checkout root and let the current configuration choose the layout.

## Create a package

1. **Prerequisite: search OBS for pre-existing packages before packaging
   anything.** Packaging software someone has already packaged wastes the
   effort and creates a competing package to maintain. Run all of these and
   read the results before continuing:

   ```bash
   osc search --package <package-name>   # package names only
   osc search --binary <package-name>    # published binaries and provides
   osc search <upstream-name>            # substring over titles/descriptions
   osc api /source/home:aspiers/<package-name>
   ```

   `osc search --package` matches package *names* only, so on its own it
   cannot answer "does this software already exist on OBS". Software is
   frequently packaged under a name that differs from its upstream project:
   a language prefix such as `rust-` or `python-`, a renamed fork, or
   bundled inside a larger package. Only the `--binary` and substring
   searches find those.

   Search *before* running `osc mkpac`, so that any hit is unambiguously
   someone else's package rather than your own new entry.

   If a package does exist, report it to the user and prefer branching it
   (`osc branch <project> <package>`) over starting from scratch, rather
   than silently creating a duplicate.

   Then identify the latest suitable source release.

2. Inspect the project and package working-copy status. The project checkout
   may contain unrelated work, so perform subsequent operations inside the new
   package directory and avoid broad cleanup commands.

3. Create the local package and add its source archive, spec, changes file, and
   optional `_service` definition:

   ```bash
   cd "$PROJECT_DIR"
   osc mkpac <package-name>
   cd <package-name>
   # Add verified source and packaging files.
   osc add <files>
   osc status
   ```

4. Build and validate locally:

   ```bash
   osc build --clean openSUSE_Tumbleweed x86_64
   ```

   A zero exit status is not enough: inspect the test output and final rpmlint
   report. Resolve rpmlint errors and unexpected warnings. Check the resulting
   RPM's metadata, file list, dependencies, and executable version where
   relevant.

5. Commit as soon as the package is plausibly correct. When all required
   generated sources are already present, prevent an unintended service run:

   ```bash
   osc commit --noservice -m "Initial package of <name> <version>"
   ```

   **Do not withhold a commit pending a perfect local build.** Committing is
   cheap and reversible, and the server builds every configured repository and
   architecture in parallel - coverage no single local build can reproduce.
   Deferring it forfeits that feedback for no gain.

   So commit once the package looks plausibly correct, and let the local build
   and the server builds proceed together. In particular, commit even when:

   - the local build is still running, or has not been started;
   - the local build failed for an environment-specific reason, such as a
     missing build dependency or a version skew between the local build root
     and the installed system;
   - rpmlint reported warnings that are understood and benign.

   Withhold a commit only when the package is known to be wrong - for example
   the source archive is unverified or came from an untrusted place, the spec
   references files that do not exist, or the sources are not the ones
   intended.

   After committing, check the server results and act on them (see
   [Monitor builds](#monitor-builds)). A repository that fails on the server
   but not locally is real feedback, not noise.

6. Set useful package metadata if the new package has an empty title,
   description, or URL:

   ```bash
   osc meta pkg home:aspiers <package-name> -F package-meta.xml
   ```

## Source services

Choose service mode deliberately and do not assume every `_service` can run
remotely:

- `osc service run` runs services with no mode, `trylocal`, or `localonly`.
- `osc service manualrun` runs services with `mode="manual"`.
- `osc service disabledrun` runs services with `mode="disabled"`.
- `osc service remoterun` only triggers server-side services eligible to run
  remotely; manual or disabled definitions are not remote source services.
- `osc service runall` ignores modes and runs everything locally.

Local service execution requires the corresponding `obs-service-*` packages.
Generated files must still have a clear, verifiable source.

## Monitor builds

Inside a package working copy:

```bash
osc results                                      # all configured targets
osc results -r openSUSE_Tumbleweed               # one repository
osc results -w -r openSUSE_Tumbleweed            # watch until complete
osc results -w -r openSUSE_Tumbleweed \
               -r openSUSE_Factory \
               -r openSUSE_Factory_ARM           # relevant current targets
```

`home:aspiers` contains obsolete repositories which may not provide modern
build dependencies. Focus validation on requested/current targets and report
meaningful unresolvable or failed results rather than treating repository-wide
legacy failures as package regressions.

A result showing `_repository` with `published*` or `dirty="true"` can mean the
scheduler has not yet produced the package result. Use `osc results -w` rather
than assuming the package was skipped.

Results carrying a trailing `*` are from an older revision than the working
copy. After a commit, expect the new revision to appear as `scheduled` before
it builds; do not read a stale `succeeded*` as validating what was just
committed.

When waiting on a build, never poll with a `pgrep` pattern that can match the
waiting command itself. `pgrep -f 'osc build'` matches the shell running the
poll loop, so the loop never terminates. Match on the recorded PID, or use a
bracketed pattern such as `pgrep -f '[o]sc build'`.

## Installing built packages

A local build root resolves dependencies from the configured repositories,
which may be newer than the installed system. The resulting RPM can therefore
be uninstallable on the very machine that built it.

Check before installing, and never force past a dependency conflict:

```bash
rpm -qpR <built>.rpm | grep -i private   # exact-version private API deps
```

Libraries exposing private API - Qt in particular - encode an exact version,
for example `libQt6Gui.so.6(Qt_6.11.2_PRIVATE_API)`. A package built against
6.11.2 will not load against an installed 6.11.1, even though the public
`so` version matches.

When `zypper` offers to resolve such a conflict by removing unrelated
packages, or by breaking the dependency, take neither. Both leave the system
worse off, and a broken dependency usually surfaces later as a runtime crash.
Report the mismatch and let the user decide between upgrading the system
library, waiting for the repositories to converge, or rebuilding the package
without the private-API linkage.

## Inspect failures

```bash
osc bl openSUSE_Tumbleweed x86_64       # current build log
osc blt openSUSE_Tumbleweed x86_64      # tail of the build log
osc bl --last openSUSE_Tumbleweed x86_64
osc buildhist openSUSE_Tumbleweed x86_64
osc chroot openSUSE_Tumbleweed x86_64
osc shell openSUSE_Tumbleweed x86_64
```

Use `osc version` to inspect the installed client version.

## Language-specific guidance

For Go modules, vendoring, reproducible version metadata, and avoiding
statically linked distro packages, read
[Go packaging](references/go-packaging.md).

Authoritative references:

- [openSUSE Packaging Guidelines](https://en.opensuse.org/openSUSE:Packaging_guidelines)
- [OBS User Guide](https://openbuildservice.org/help/manuals/obs-user-guide/)
- [Spec file guidelines](https://en.opensuse.org/openSUSE:Specfile_guidelines)
- [Packaging patches](https://en.opensuse.org/openSUSE:Packaging_Patches_guidelines)

