OBS(Source)

πŸ“¦
version 0.6.9 Β· 2 versions
, in fedora 43, fedora 44

πŸ’– OBS(Source) β€” the obs-service-go_modules glow-up πŸ’–

okay bestie, sit down, hydrate, because this is the git repo page for obs-service-go_modules and it is lowkey a whole vibe. This is an Open Build Service (OBS) Source Service that downloads, verifies, and vendors Go module dependency sources. πŸ‘‘

the links, bestie, always the links:


πŸ“š Contents πŸ“š

  • Overview β€” #overview
  • Usage for packagers β€” #usage-for-packagers
  • Compression format support β€” #compression-format-support
  • OBS Source Service Build Mode support β€” #obs-source-service-build-mode-support
  • Building Go applications with vendored dependency modules β€” #building-go-applications-with-vendored-dependency-modules
  • Example β€” #example
  • Example β€” #example-_service-configuration
  • Transition note β€” #transition-note
  • openSUSE RPM packages built using β€” #opensuse-rpm-packages-built-using-obs-service-go_modules
  • Dependencies β€” #dependencies
  • FAQ β€” #faq
  • Support β€” #support
  • Contributing β€” #contributing
  • License β€” #license

✨ Overview ✨

so. this is the git repository for obs-service-go_modules, an Open Build Service (OBS) Source Service that does the download + verify + vendor thing for Go module dependency sources. The authoritative source is <https://github.com/openSUSE/obs-service-go_modules>. no cap.

if you've got <https://github.com/golang/go/wiki/Modules> present in a Go application, obs-service-go_modules will call the Go tools in sequence, one after the other, slay by slay:

go mod download
go mod verify
go mod vendor

then obs-service-go_modules creates a vendor.tar.gz archive β€” or another supported compression type, we don't gatekeep β€” containing the vendor/ directory that go mod vendor populated. The archive gets generated in the rpm package directory, and you can commit it to OBS to make offline Go application package builds work for openSUSE, SUSE, and numerous other distributions. πŸ’…


πŸŽ€ Usage for packagers πŸŽ€

rght now the assumption is that your Go application source is distributed as a compressed tarball named app-0.1.0.tar.gz β€” or another supported compression type, again, options β€” unpacking to app-0.1.0/. The compression type can be specified using the compression parameter, and it defaults to gz (gzip). obs-service-go_modules will autodetect tarball archives of the form app-0.1.0.tar.gz, where the RPM packaging uses spec file app.spec sharing the base name app. it just knows. πŸ¦‹

Create a _service file containing:

<services>
  <service name="go_modules" mode="manual">
  </service>
</services>

the archive name can alternatively be specified using service parameter archive:

<services>
  <service name="go_modules" mode="manual">
  <param name="archive">app-0.1.0.tar.xz</param>
  </service>
</services>

then run the osc command locally:

osc service manualrun
⚠️ heads up bestie: if you previously ran the command, there might be a git clone of the app repository just sitting there. To avoid errors, remove this directory before calling the osc service manualrun command:
rm -rf app; osc service manualrun

See Example below for typical output with a complete _service file. πŸ’–


🌈 Compression format support 🌈

obs-service-go_modules reads and writes compressed tar archives using <https://libarchive.org/> via the Python3 ctypes wrapper <https://github.com/Changaco/python-libarchive-c>.

libarchive supports numerous compression formats, but the usage here recommends limiting selections to gz (default), xz and zstd. Tables of representative compression method relative sizes and timings with Go vendored dependency sources (vendor/) are shown below. ✨

πŸ“¦ Compression sizes with Go dependency sources

  • hugo β€” version v0.100.2 β€” uncompressed 75M, gz 11M, xz 7.5M, zstd 8.3M
  • kubernetes β€” version v1.24.1 β€” uncompressed 129M, gz 17M, xz 12M, zstd 14M

⏱️ Compression timing with Go dependency sources

  • hugo β€” version v0.100.2 β€” gz 1.8s, xz 6.3s, zstd 0.3s
  • kubernetes β€” version v1.24.1 β€” gz 2.9s, xz 10.9s, zstd 0.4s

the above are an average of five runs on Intel i7-6820HQ CPU. The zstd format has a clear advantage in speed with reasonable compression ratio, and is likely to become the default compression method in a future release. Decompression timings are closely matched among gz, xz, and zstd compression methods. not one of them is out here losing. πŸ’€


πŸ‘‘ OBS Source Service Build Mode support πŸ‘‘

OBS Source Services can run in one of several modes, as shown in OBS Documentation: Using Source Services: Modes of Services β€” <https://openbuildservice.org/help/manuals/obs-user-guide/cha.obs.source_service.html#sec.obs.sserv.mode>.

Currently the recommended mode is manual, which implies:

  • The service is run locally via explicit CLI call osc service manualrun
  • obs-service-go_modules and its dependencies python3, libarchive and python3-libarchive-c are installed on the local machine. In particular, python3-libarchive-c packages are not available in all SUSE repositories at this time. 😭
  • The resulting vendor.tar.gz or other supported compression type should be committed and referenced as an RPM Source:.
  • Source: file names are as given in the RPM .spec, no _service: prefix is applied.

If and when obs-service-go_modules is available on OBS, additional modes including Default will be supported. Default runs server side after each commit and locally before every local build. This will enable tighter integration with the tar_scm / obs_scm source service (<https://github.com/openSUSE/obs-service-tar_scm>), including uncommitted server-managed .obscpio source and vendor archives. big things coming, bestie. πŸ”₯


πŸ¦‹ Building Go applications with vendored dependency modules πŸ¦‹

Go commands support building with vendored dependencies, but it is no longer on by default. Upstream has stated vendoring is not going away. To ensure the top-level vendor/ directory is used by go build, either:

  • pass the argument go build -mod=vendor to each invocation
  • set environment variable GOFLAGS=-mod=vendor to apply the setting to all invocations

more info about additional controls is available at: Go Module Knobs β€” <https://github.com/thepudds/go-module-knobs/blob/master/README.md>, Go Wiki: Modules: How do I use vendoring β€” <https://github.com/golang/go/wiki/Modules#how-do-i-use-vendoring-with-modules-is-vendoring-going-away> and Go Wiki: Modules: Old vs. new behavior β€” <https://github.com/golang/go/wiki/Modules#when-do-i-get-old-behavior-vs-new-module-based-behavior>


🌸 Example 🌸

using the hugo static site generator as an example of a Go application with a large number of Go module dependencies, obs-service-go_modules produces vendor.tar.gz:

$ ls -1
hugo-0.57.2.tar.gz
hugo.changes
hugo.spec
_service
_servicedata

$ osc service manualrun
INFO:obs-service-go_modules:Autodetecting archive since no archive param provided in _service
INFO:obs-service-go_modules:Archive autodetected at /path/to/prj/pkg/hugo-0.57.2.tar.gz
INFO:obs-service-go_modules:Using archive hugo-0.57.2.tar.gz
INFO:obs-service-go_modules:Extracting hugo-0.57.2.tar.gz to /path/to/tmpdir
INFO:obs-service-go_modules:Using go.mod found at /path/to/tmpdir/hugo-0.57.2/go.mod
INFO:obs-service-go_modules:go mod download
go: finding cloud.google.com/go v0.39.0
go: finding contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0
go: finding contrib.go.opencensus.io/exporter/ocagent v0.4.12
go: finding contrib.go.opencensus.io/exporter/stackdriver v0.11.0
go: finding contrib.go.opencensus.io/integrations/ocsql v0.1.4
go: finding contrib.go.opencensus.io/resource v0.0.0-20190131005048-21591786a5e0
go: finding github.com/Azure/azure-amqp-common-go v1.1.4
go: finding github.com/Azure/azure-pipeline-go v0.1.9
go: finding github.com/Azure/azure-sdk-for-go v27.3.0+incompatible
(elided: 193 additional entries)
go: finding gopkg.in/fsnotify.v1 v1.4.7
go: finding gopkg.in/resty.v1 v1.12.0
go: finding gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
go: finding gopkg.in/yaml.v2 v2.2.2
go: finding honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a
go: finding pack.ag/amqp v0.11.0
INFO:obs-service-go_modules:go mod verify
INFO:obs-service-go_modules:all modules verified
INFO:obs-service-go_modules:go mod vendor
INFO:obs-service-go_modules:Vendor go.mod dependencies to vendor.tar.gz

$ ls -1
hugo-0.57.2.tar.gz
hugo.changes
hugo.spec
_service
_servicedata
vendor.tar.gz

and the hugo link, since the original gives it: <https://github.com/gohugoio>. 🫢

...to be continued, bestie, there's more page coming πŸ’œ

✨ Example _service configuration ✨

Okay bestie, gather round, because these OBS Source Services are about to eat πŸ’… β€” tar_scm (https://github.com/openSUSE/obs-service-tar_scm), set_version (https://github.com/openSUSE/obs-service-set_version) and obs-service-recompress can all link up to automate the whole Go application source-archive situation and support your local dev workflow. Obsessed. Here's what it looks like:

<services>
  <service name="tar_scm" mode="manual">
    <param name="url">git://github.com/gohugoio/hugo.git</param>
    <param name="scm">git</param>
    <param name="exclude">.git</param>
    <param name="revision">v0.57.2</param>
    <param name="versionformat">@PARENT_TAG@</param>
    <param name="changesgenerate">enable</param>
    <param name="versionrewrite-pattern">v(.*)</param>
  </service>
  <service name="set_version" mode="manual">
    <param name="basename">hugo</param>
  </service>
  <service name="recompress" mode="manual">
    <param name="file">*.tar</param>
    <param name="compression">gz</param>
  </service>
  <service name="go_modules" mode="manual">
  </service>
</services>

And FYI, the persistent state for changelog generation lives in _servicedata. Keep that in mind, iykyk πŸ’–


πŸ”₯ Updating dependencies during vendoring

So when a CVE or a bug gets reported against a dependency of a Go application, the best move is a pull request upstream asking the project to use a newer, fixed version of that dependency, plus a tagged release to go with it. The govulncheck output literally tells you which fixed version of the vulnerable dependency to reach for. Chef's kiss πŸ‘‘

That said, sometimes upstream is slow to accept that update PR or tag a release β€” tragic, we know 😭. If the vulnerability is severe and it actually applies to the packaged Go application, package maintainers can, at their option, temporarily vendor a newer fixed version of the dependency.

And in Long Term Support (LTS) scenarios? A dependency of an upstream project might be inactive, archived, removed, or straight-up marked EOL. In those cases it's totally appropriate to vendor a newer fixed version of the dependency from a different repository URL or local file path. No cap 🧚


πŸ’… Update a Go module with go mod edit -replace

To swap a specific module out for a newer fixed version, obs-service-go_modules supports the command go mod edit -replace module=replacement. This method is recommended, because the replace statement gets explicitly written into go.mod and recorded in the debug/buildinfo metadata. Other ways of updating, like go get, don't highlight the changed versions or the divergence from upstream pinned versions β€” not iconic of them 🫠. And yes, upstream projects do occasionally use replace statements in their pristine go.mod files themselves.

🌈 Example replace to fix CVEs in dependencies 🌈

Scenario: a packaged Go application has gone a long, long time since a tagged release. GitHub dependabot keeps making regular dependency-update PRs to the upstream main branch, but nobody has tagged a release. Two CVEs are detected against the most recent tagged release, according to govulncheck run in the local git clone:

govulncheck .
=== Symbol Results ===

Vulnerability #1: GO-2025-3485
    DoS in go-jose Parsing in github.com/go-jose/go-jose
  More info: https://pkg.go.dev/vuln/GO-2025-3485
  Module: github.com/go-jose/go-jose/v3
    Found in: github.com/go-jose/go-jose/v3@v3.0.3
    Fixed in: github.com/go-jose/go-jose/v3@v3.0.4
    Example traces found:
      #1: pkg/attestation/attestation.go:145:35: attestation.signAttestation calls sign.SignerFromKeyOpts, which eventually calls jose.ParseSigned

  Module: github.com/go-jose/go-jose/v4
    Found in: github.com/go-jose/go-jose/v4@v4.0.2
    Fixed in: github.com/go-jose/go-jose/v4@v4.0.5
    Example traces found:
      #1: pkg/attestation/attestation.go:145:35: attestation.signAttestation calls sign.SignerFromKeyOpts, which eventually calls jose.ParseSignedCompact

Your code is affected by 1 vulnerability from 1 module.
This scan also found 6 vulnerabilities in packages you import and 2
vulnerabilities in modules you require, but your code doesn't appear to call
these vulnerabilities.
Use '-show verbose' for more details.

Mitigation: drop two replace entries into _service, using exactly what the CVE report's fixed in field says:

<service name="go_modules" mode="manual">
  <param name="replace">github.com/go-jose/go-jose/v3=github.com/go-jose/go-jose/v3@v3.0.4</param>
  <param name="replace">github.com/go-jose/go-jose/v4=github.com/go-jose/go-jose/v4@v4.0.5</param>
</service>

Then run the source service to vendor dependencies like you always do. Easy πŸ’–

vendor.tar.gz now holds the original dependencies pinned by upstream go.mod, plus the updates to those two dependencies shown above. And to keep the vendoring consistent, vendor.tar.gz also contains modified go.mod and go.sum lock files. Those two files are the only diff against pristine upstream sources you'll need in most uses of replace.

Run govulncheck on the updated vendored dependencies and boom β€” both CVEs are cleared:

govulncheck .
=== Symbol Results ===

No vulnerabilities found.

Your code is affected by 0 vulnerabilities.
This scan also found 5 vulnerabilities in packages you import and 2
vulnerabilities in modules you require, but your code doesn't appear to call
these vulnerabilities.
Use '-show verbose' for more details.

You can spot the replace in action either in the modified go.mod or in the built binary:

go version -m <BINARYNAME> |grep jose
        dep     github.com/go-jose/go-jose/v3   v3.0.3
        =>      github.com/go-jose/go-jose/v3   v3.0.4
        dep     github.com/go-jose/go-jose/v4   v4.0.2
        =>      github.com/go-jose/go-jose/v4   v4.0.5

⭐ Pin a specific module version with go mod edit -require

Sometimes you just want to explicitly require a module version without replacing anything. The go mod edit -require command makes sure a specific version of a module gets recorded in go.mod, even if it's only indirectly required by other dependencies. Main character energy, honestly πŸ‘‘

This is especially useful when:

  • You're pinning a minimum secure version of a module to address a known vulnerability, as identified by govulncheck
  • You want consistent dependency resolution across vendoring and build environments
  • You're promoting a fixed version of an indirectly required dependency up to a first-class requirement in go.mod

πŸ“ Example

You can add a require param:

<service name="go_modules" mode="manual">
  <param name="require">github.com/go-jose/go-jose/v4@v4.0.5</param>
</service>

The go.mod will contain:

require github.com/go-jose/go-jose/v4 v4.0.5

and the binary's module info will show the pinned version:

go version -m <binary> | grep jose
        dep     github.com/go-jose/go-jose/v4    v4.0.5

As soon as the Go application upstream tags a newer release, remove the replace and require parameters from _service so you go back to pristine upstream sources and start receiving further updates to that dependency again. Cleanup era ✨


πŸ¦‹ Transition note

Until such time as obs-service-go_modules is available on OBS (https://build.opensuse.org), vendor.tar.gz should be committed along with the Go application release tarball. That's just how it is rn 🫢


πŸ‘‘ openSUSE RPM packages built using obs-service-go_modules


🌸 Dependencies

obs-service-go_modules requires:

  • python3
  • python-libarchive-c β€” the ctypes wrapper for the libarchive C library (added in v0.5.0)

The Python standard library only supports gzipped tar archives, which is giving limited 😭. The libarchive dependency was chosen so we can support additional compression types, including xz and the cpio_newc used by .obscpio archives. Supported compression types are intentionally limited to gz, xz, zstd and cpio_newc, to preserve future flexibility in case eliminating the python-libarchive-c dependency ever becomes desirable.


πŸ’œ FAQ

Q: Does vendor.tar.gz need to be committed to OBS package?

A: Currently, yes. As long as obs-service-go_modules is run locally via osc service manualrun, then vendor.tar.gz should be committed and referenced as an additional Source:. If and when obs-service-go_modules is available on OBS (https://build.opensuse.org), additional strategies should be possible β€” like a vendor.cpio where the vendored dependencies are managed on the fly and don't need to be committed to OBS package revisions. The single source of truth, go.mod and go.sum, always remain with the application source code. Periodt πŸ’…

Q: Does obs-service-go_modules update dependencies to newer versions?

A: No. Go modules use Minimum Version Selection (https://github.com/golang/go/wiki/Modules#faqs--minimal-version-selection), selecting the minimum (oldest) version of a Go module that satisfies all go.mod entries in the transitive dependency set. Go modules are relatively new and real-world use remains to be seen, but the expectation is that dependency versions will increment at a measured pace, driven by upstream projects making releases with a well-tested dependency set. It's a design goal that there should be no surprise updates pulled in, and that the dependency set selected stays repeatable over time. These characteristics should be quite favorable for distribution packagers. Slay πŸŽ€

Q: Does obs-service-go_modules cache Go module downloads to save time and bandwidth?

A: Yes. For local use with osc service manualrun, obs-service-go_modules uses the standard module cache ~/go/pkg/mod. Subsequent runs of obs-service-go_modules with a populated cache will finish in less time. Fast queen ⭐

Q: Would obs-service-go_modules installed on OBS cache Go module downloads to conserve server resources?

A: Not directly. The Go module cache ~/go/pkg/mod would not persist between OBS build runs. Running a private Go proxy (https://proxy.golang.org) inside OBS could accomplish this, as well as provide protections against third-party service outages and upstream Go modules being removed by the author. Protect the vibes πŸ›‘οΈβœ¨

πŸ’– Support πŸ’–

okay so bestie, here's the tea: obs-service-go_modules is trying its absolute best to play nice with basically every upstream Go project out there that follows best-practice source layouts and normal module conventions. she's flexible like that ✨

but if you're packaging a Go app with an uncommon project layout, or some nonstandard go.mod usage pattern that's giving compatibility problems? please file an issue, okay? 🫢

  • the issue tracker lives at https://github.com/openSUSE/obs-service-go_modules/issues
  • drop a description in there and reference the specific upstream tag or commit β€” receipts matter, iykyk
  • and yeah, it may genuinely not be possible to support every single unique upstream layout 😭 a maintainer will still evaluate the feasibility of adding support for that special case, or at least improve the output messages so the error is clearly indicated. clarity is the vibe.

🌈 Contributing 🌈

in keeping with those Support objectives up there ⬆️ β€” feature ideas are welcome, bestie, especially the ones that:

  • improve idiomatic OBS and RPM usage πŸ‘‘
  • level up packaging automation πŸŽ€
  • build commonality among Go application package sources πŸ¦‹

also, a whole goal of this project is keeping manual configuration parameters in _service to a minimum for maintainability β€” like, the config should not be doing the most, periodt πŸ’…

so how do you slide into the contributions? file proposals as an issue at https://github.com/openSUSE/obs-service-go_modules/issues so y'all can discuss feasibility and feature design, and then follow it up with an implementation via pull request. that's the flow ✨

🚨 PR bestie PSA: when you create a pull request, please enable the permission: "Maintainers are allowed to edit this pull request" β€” if it's not already on by default. the docs for that live at https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork#enabling-repository-maintainer-permissions-on-existing-pull-requests πŸ”—

πŸ“œ License πŸ“œ

GNU General Public License v2.0 or later β€” that's the one, no cap πŸ’–


and that's a wrap on this whole manpage, bestie!! πŸŽ€ the footer on this iconic document reads obs-service-go_modules on the left, Service in the middle, and OBS(Source) on the right β€” that's the source/version tag and the date stamp sitting pretty at the bottom of the page 🌸 go forth and package those Go apps, you absolute main character πŸ‘‘βœ¨

πŸ“¦ where this page comes from