website: Add draft about packages as channels.
* website/drafts/package-channel.md: New file.
This commit is contained in:
parent
8139a10b65
commit
dbc448b94a
|
@ -0,0 +1,584 @@
|
|||
title: From development environments to continuous integration—the ultimate guide to software development with Guix
|
||||
alttitle: Continuous integration, continuous delivery, and development environments, all at once
|
||||
author: Ludovic Courtès
|
||||
tags: Software development, Continuous integration, Cuirass
|
||||
date: 2023-06-02 15:00:00
|
||||
---
|
||||
|
||||
Guix is a handy tool for developers; [`guix
|
||||
shell`](https://guix.gnu.org/manual/en/html_node/Invoking-guix-shell.html),
|
||||
in particular, gives a standalone development environment for your
|
||||
package, no matter what language(s) it’s written in. To benefit from
|
||||
it, you have to initially write a package definition and have it either
|
||||
in Guix proper, in a channel, or directly upstream as a `guix.scm` file.
|
||||
This last option is appealing: all developers have to do to get set up
|
||||
is clone the project's repository and run `guix shell`, with no
|
||||
arguments—we looked at the rationale for `guix shell` in [an earlier
|
||||
missive](https://guix.gnu.org/en/blog/2021/from-guix-environment-to-guix-shell/).
|
||||
|
||||
Development needs go beyond development environments though. How can
|
||||
developers perform continuous integration of their code in Guix build
|
||||
environments? How can they deliver their code straight to adventurous
|
||||
users? This post describes a set of files and conventions developers
|
||||
can follow in their repository to set up Guix-based development
|
||||
environments, continuous integration, and continuous delivery—all at
|
||||
once.
|
||||
|
||||
# Getting started
|
||||
|
||||
How do we go about “Guixifying” a repository? The first step, as we’ve
|
||||
seen, will be to add a `guix.scm` at the root of the repository we’re
|
||||
interested in. We’ll take [Guile](https://www.gnu.org/software/guile)
|
||||
as an example in this post: it’s written in Scheme (mostly) and C, and
|
||||
has a number of dependencies—a C compilation tool chain, C libraries,
|
||||
Autoconf and its friends, LaTeX, and so on. The resulting `guix.scm` is
|
||||
looks like the usual [package
|
||||
definition](https://guix.gnu.org/manual/en/html_node/Defining-Packages.html),
|
||||
just without the `define-public` bit:
|
||||
|
||||
```scheme
|
||||
;; The ‘guix.scm’ file for Guile, for use by ‘guix shell’.
|
||||
|
||||
(use-modules (guix)
|
||||
(guix build-system gnu)
|
||||
((guix licenses) #:prefix license:)
|
||||
(gnu packages autotools)
|
||||
(gnu packages bash)
|
||||
(gnu packages bdw-gc)
|
||||
(gnu packages compression)
|
||||
(gnu packages flex)
|
||||
(gnu packages gdb)
|
||||
(gnu packages gettext)
|
||||
(gnu packages gperf)
|
||||
(gnu packages libffi)
|
||||
(gnu packages libunistring)
|
||||
(gnu packages linux)
|
||||
(gnu packages pkg-config)
|
||||
(gnu packages readline)
|
||||
(gnu packages tex)
|
||||
(gnu packages texinfo)
|
||||
(gnu packages version-control))
|
||||
|
||||
(package
|
||||
(name "guile")
|
||||
(version "3.0.99-git") ;funky version number
|
||||
(source #f) ;no source
|
||||
(build-system gnu-build-system)
|
||||
(native-inputs
|
||||
(append (list autoconf
|
||||
automake
|
||||
libtool
|
||||
gnu-gettext
|
||||
flex
|
||||
texinfo
|
||||
texlive-base ;for "make pdf"
|
||||
texlive-epsf
|
||||
gperf
|
||||
git
|
||||
gdb
|
||||
strace
|
||||
readline
|
||||
lzip
|
||||
pkg-config)
|
||||
|
||||
;; When cross-compiling, a native version of Guile itself is
|
||||
;; needed.
|
||||
(if (%current-target-system)
|
||||
(list this-package)
|
||||
'())))
|
||||
(inputs
|
||||
(list libffi bash-minimal))
|
||||
(propagated-inputs
|
||||
(list libunistring libgc))
|
||||
|
||||
(native-search-paths
|
||||
(list (search-path-specification
|
||||
(variable "GUILE_LOAD_PATH")
|
||||
(files '("share/guile/site/3.0")))
|
||||
(search-path-specification
|
||||
(variable "GUILE_LOAD_COMPILED_PATH")
|
||||
(files '("lib/guile/3.0/site-ccache")))))
|
||||
(synopsis "Scheme implementation intended especially for extensions")
|
||||
(description
|
||||
"Guile is the GNU Ubiquitous Intelligent Language for Extensions,
|
||||
and it's actually a full-blown Scheme implementation!")
|
||||
(home-page "https://www.gnu.org/software/guile/")
|
||||
(license license:lgpl3+))
|
||||
```
|
||||
|
||||
Quite a bit of boilerplate, but now someone who’d like to hack on Guile
|
||||
just needs to run:
|
||||
|
||||
```
|
||||
guix shell
|
||||
```
|
||||
|
||||
That gives them a shell containing all the dependencies of Guile: those
|
||||
listed above, but also _implicit dependencies_ such as the GCC tool
|
||||
chain, GNU Make, sed, grep, and so on. The chef’s recommendation:
|
||||
|
||||
```
|
||||
guix shell -CP
|
||||
```
|
||||
|
||||
That gives a shell in an isolated container, and all the dependencies
|
||||
show up in `$HOME/.guix-profile`, which plays well with caches such as
|
||||
[`config.cache`](https://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.70/html_node/Cache-Files.html)
|
||||
and absolute file names recorded in generated `Makefile`s and the likes.
|
||||
The fact that the shell runs in a container brings peace of mind:
|
||||
nothing but the current directory and Guile’s dependencies is visible
|
||||
inside the container; nothing from the system can possibly interfere
|
||||
with your development.
|
||||
|
||||
# Level 1: Building with Guix
|
||||
|
||||
Now that we have a package definition, why not also take advantage of it
|
||||
so we can build Guile with Guix? We had left the `source` field empty,
|
||||
because `guix shell` above only cares about the *inputs* of our
|
||||
package—so it can set up the development environment—not about the
|
||||
package itself.
|
||||
|
||||
To build the package with Guix, we’ll need to fill out the `source`
|
||||
field, along these lines:
|
||||
|
||||
```scheme
|
||||
(use-modules (guix)
|
||||
(guix git-download) ;for ‘git-predicate’
|
||||
…)
|
||||
|
||||
(define vcs-file?
|
||||
;; Return true if the given file is under version control.
|
||||
(or (git-predicate (current-source-directory))
|
||||
(const #t))) ;not in a Git checkout
|
||||
|
||||
(package
|
||||
(name "guile")
|
||||
(version "3.0.99-git") ;funky version number
|
||||
(source (local-file "." "guile-checkout"
|
||||
#:recursive? #t
|
||||
#:select? vcs-file?))
|
||||
…)
|
||||
```
|
||||
|
||||
Here’s what we changed:
|
||||
|
||||
1. We add `(guix git-download)` to our set of imported modules, so we
|
||||
can use its `git-predicate` procedure.
|
||||
2. We defined `vcs-file?` as a procedure that returns true when passed
|
||||
a file that is under version control. For good measure, we add a
|
||||
fallback case for when we’re not in a Git checkout: always return
|
||||
true.
|
||||
3. We set `source` to a
|
||||
[`local-file`](https://guix.gnu.org/manual/devel/en/html_node/G_002dExpressions.html#index-local_002dfile)—a
|
||||
recursive copy of the current directory (`"."`), limited to files
|
||||
under version control (the `#:select?` bit).
|
||||
|
||||
From there on, our `guix.scm` file serves a second purpose: it lets us
|
||||
build the software with Guix. The whole point of building with Guix is
|
||||
that it’s a “clean” build—you can be sure nothing from your working tree
|
||||
or system interferes with the build result—and it lets you test a
|
||||
variety of things. First, you can do a plain native build:
|
||||
|
||||
```
|
||||
guix build -f guix.scm
|
||||
```
|
||||
|
||||
But you can also build for another system (possibly after setting up
|
||||
[offloading](https://guix.gnu.org/manual/devel/en/html_node/Daemon-Offload-Setup.html)
|
||||
or [transparent
|
||||
emulation](https://guix.gnu.org/manual/devel/en/html_node/Virtualization-Services.html#index-emulation)):
|
||||
|
||||
```
|
||||
guix build -f guix.scm -s aarch64-linux -s riscv64-linux
|
||||
```
|
||||
|
||||
… or cross-compile:
|
||||
|
||||
```
|
||||
guix build -f guix.scm --target=x86_64-w64-mingw32
|
||||
```
|
||||
|
||||
You can also use [package transformation
|
||||
options](https://guix.gnu.org/manual/en/html_node/Package-Transformation-Options.html)
|
||||
to test package variants:
|
||||
|
||||
```
|
||||
# What if we built with Clang instead of GCC?
|
||||
guix build -f guix.scm \
|
||||
--with-c-toolchain=guile@3.0.99-git=clang-toolchain
|
||||
|
||||
# What about that under-tested configure flag?
|
||||
guix build -f guix.scm \
|
||||
--with-configure-flag=guile@3.0.99-git=--disable-networking
|
||||
```
|
||||
|
||||
Handy!
|
||||
|
||||
# Level 2: The repository as a channel
|
||||
|
||||
We now have a Git repository containing (among other things) a package
|
||||
definition. Can’t we turn it into a
|
||||
[*channel*](https://guix.gnu.org/manual/devel/en/html_node/Channels.html)?
|
||||
After all, channels are designed to ship package definitions to users,
|
||||
and that’s exactly what we’re doing with our `guix.scm`. Granted, our
|
||||
Git repository is one package definition lost in a sea of code—in this
|
||||
case, Guile—, but still.
|
||||
|
||||
Turns out we can indeed turn into a channel, but with one caveat: we
|
||||
must create a separate directory for the `.scm` file(s) of our channel
|
||||
so that `guix pull` doesn’t end loading unrelated `.scm` files when
|
||||
someone pulls the channel—and in Guile, there are lots of them! So
|
||||
we’ll start like this, keeping a top-level `guix.scm` symlink for the
|
||||
sake of `guix shell`:
|
||||
|
||||
```
|
||||
mkdir .guix
|
||||
mv guix.scm .guix/guile-package.scm
|
||||
ln -s .guix/guile-package.scm guix.scm
|
||||
```
|
||||
|
||||
(In Guile we actually used `build-aux/guix` instead of `.guix`, but the
|
||||
latter is probably clearer.) To make it usable as part of a channel, we
|
||||
need to turn our `guix.scm` file into a
|
||||
[module](https://guix.gnu.org/manual/devel/en/html_node/Package-Modules.html):
|
||||
we do that by changing the `use-modules` form at the top to a
|
||||
`define-module` form. We also need to actually *export* a package
|
||||
variable, with `define-public`, while still returning the package value
|
||||
at the end of the file so we can still use `guix shell` and `guix build
|
||||
-f guix.scm`. The end result looks like this (not repeating things that
|
||||
haven’t changed):
|
||||
|
||||
```scheme
|
||||
(define-module (guile-package)
|
||||
#:use-module (guix)
|
||||
#:use-module (guix git-download) ;for ‘git-predicate’
|
||||
…)
|
||||
|
||||
(define-public guile
|
||||
(package
|
||||
(name "guile")
|
||||
(version "3.0.99-git") ;funky version number
|
||||
…))
|
||||
|
||||
;; Return the package object define above at the end of the module.
|
||||
guile
|
||||
```
|
||||
|
||||
We need one last thing: a [`.guix-channel`
|
||||
file](https://guix.gnu.org/manual/devel/en/html_node/Package-Modules-in-a-Sub_002ddirectory.html)
|
||||
so Guix knows where to look for package modules in our repository:
|
||||
|
||||
```scheme
|
||||
;; This file lets us present this repo as a Guix channel.
|
||||
|
||||
(channel
|
||||
(version 0)
|
||||
(directory ".guix")) ;look for package modules under .guix/
|
||||
```
|
||||
|
||||
To recap, we now have these files:
|
||||
|
||||
```
|
||||
.
|
||||
├── .guix-channel
|
||||
├── guix.scm → .guix/guile-package.scm
|
||||
└── .guix
|
||||
└── guile-package.scm
|
||||
```
|
||||
|
||||
And that’s it: we have a channel! (We could do better and support
|
||||
[*channel
|
||||
authentication*](https://guix.gnu.org/manual/en/html_node/Specifying-Channel-Authorizations.html)
|
||||
so users know they’re pulling genuine code. We’ll spare you the details
|
||||
here but it’s worth considering!) Users can pull from this channel by
|
||||
[adding it to
|
||||
`~/.config/guix/channels.scm`](https://guix.gnu.org/manual/devel/en/html_node/Specifying-Additional-Channels.html),
|
||||
along these lines:
|
||||
|
||||
```
|
||||
(append (list (channel
|
||||
(name 'guile)
|
||||
(url "https://git.savannah.gnu.org/git/guile.git")
|
||||
(branch "main")))
|
||||
%default-channels)
|
||||
```
|
||||
|
||||
After running `guix pull`, we can see the new package:
|
||||
|
||||
```
|
||||
$ guix describe
|
||||
Generation 264 May 26 2023 16:00:35 (current)
|
||||
guile 36fd2b4
|
||||
repository URL: https://git.savannah.gnu.org/git/guile.git
|
||||
branch: main
|
||||
commit: 36fd2b4920ae926c79b936c29e739e71a6dff2bc
|
||||
guix c5bc698
|
||||
repository URL: https://git.savannah.gnu.org/git/guix.git
|
||||
commit: c5bc698e8922d78ed85989985cc2ceb034de2f23
|
||||
$ guix package -A ^guile$
|
||||
guile 3.0.99-git out,debug guile-package.scm:51:4
|
||||
guile 3.0.9 out,debug gnu/packages/guile.scm:317:2
|
||||
guile 2.2.7 out,debug gnu/packages/guile.scm:258:2
|
||||
guile 2.2.4 out,debug gnu/packages/guile.scm:304:2
|
||||
guile 2.0.14 out,debug gnu/packages/guile.scm:148:2
|
||||
guile 1.8.8 out gnu/packages/guile.scm:77:2
|
||||
$ guix build guile@3.0.99-git
|
||||
[…]
|
||||
/gnu/store/axnzbl89yz7ld78bmx72vpqp802dwsar-guile-3.0.99-git-debug
|
||||
/gnu/store/r34gsij7f0glg2fbakcmmk0zn4v62s5w-guile-3.0.99-git
|
||||
```
|
||||
|
||||
That’s how, as a developer, you get your software delivered directly in
|
||||
the hands of users! No intermediaries, yet no loss of transparency and
|
||||
provenance tracking.
|
||||
|
||||
With that in place, it also becomes trivial for anyone to create Docker
|
||||
images, Deb/RPM packages, or plain tarball of the software with [`guix
|
||||
pack`](https://guix.gnu.org/manual/devel/en/html_node/Invoking-guix-pack.html):
|
||||
|
||||
```
|
||||
# How about a Docker image of our Guile snapshot?
|
||||
guix pack -f docker -S /bin=bin guile@3.0.99-git
|
||||
|
||||
# And a relocatable RPM?
|
||||
guix pack -f rpm -R -S /bin=bin guile@3.0.99-git
|
||||
```
|
||||
|
||||
# Bonus: Package variants
|
||||
|
||||
We now have an actual channel, but it contains only one package. While
|
||||
we’re at it, we can [define package
|
||||
variants](https://guix.gnu.org/manual/devel/en/html_node/Defining-Package-Variants.html)
|
||||
in our `guile-package.scm` file, variants that we want to be able to
|
||||
test as Guile developers—similar to what we did above with
|
||||
transformation options. We can add them like so:
|
||||
|
||||
```scheme
|
||||
;; This is the ‘.guix/guile-package.scm’ file.
|
||||
|
||||
(define-module (guile-package)
|
||||
…)
|
||||
|
||||
(define-public guile
|
||||
…)
|
||||
|
||||
(define (package-with-configure-flags p flags)
|
||||
"Return P with FLAGS as addition 'configure' flags."
|
||||
(package/inherit p
|
||||
(arguments
|
||||
(substitute-keyword-arguments (package-arguments p)
|
||||
((#:configure-flags original-flags #~'())
|
||||
#~(append #$original-flags #$flags))))))
|
||||
|
||||
(define-public guile-without-threads
|
||||
(package
|
||||
(inherit (package-with-configure-flags guile
|
||||
#~'("--without-threads")))
|
||||
(name "guile-without-threads")))
|
||||
|
||||
(define-public guile-without-networking
|
||||
(package
|
||||
(inherit (package-with-configure-flags guile
|
||||
#~'("--disable-networking")))
|
||||
(name "guile-without-networking")))
|
||||
|
||||
|
||||
;; Return the package object define above at the end of the module.
|
||||
guile
|
||||
```
|
||||
|
||||
We can build these variants as regular packages once we’ve pulled the
|
||||
channel, or, if we have a checkout of Guile, we can run a command like
|
||||
this one from the top level:
|
||||
|
||||
```
|
||||
guix build -L $PWD/.guix guile-without-threads
|
||||
```
|
||||
|
||||
|
||||
# Level 3: Setting up continuous integration
|
||||
|
||||
This channel becomes even more interesting once we set up [continuous
|
||||
integration](https://en.wikipedia.org/wiki/Continuous_integration) (CI).
|
||||
There are several ways to do that.
|
||||
|
||||
You can use one of the mainstream continuous integration tools, such as
|
||||
GitLab-CI. To do that, you need to make sure you run jobs into a Docker
|
||||
image or virtual machine that has Guix installed. If we were to do that
|
||||
in the case of Guile, we’d have a job that runs a shell command like
|
||||
this one:
|
||||
|
||||
```
|
||||
guix build -L $PWD/.guix guile@3.0.99-git
|
||||
```
|
||||
|
||||
Doing this works great and has the advantage of being easy to achieve on
|
||||
your favorite CI platform.
|
||||
|
||||
That said, you’ll really get the most of it by using
|
||||
[Cuirass](https://guix.gnu.org/en/cuirass), a CI tool designed for and
|
||||
tightly integrated with Guix. Using it is more work than using a hosted
|
||||
CI tool because you first need to set it up, but that setup phase is
|
||||
greatly simplified if you use its Guix System
|
||||
[service](https://guix.gnu.org/manual/devel/en/html_node/Continuous-Integration.html).
|
||||
Going back to our example, we give Cuirass a spec file that goes like
|
||||
this:
|
||||
|
||||
```scheme
|
||||
;; Cuirass spec file to build all the packages of the ‘guile’ channel.
|
||||
(list (specification
|
||||
(name "guile")
|
||||
(build '(channels guile))
|
||||
(channels
|
||||
(append (list (channel
|
||||
(name 'guile)
|
||||
(url "https://git.savannah.gnu.org/git/guile.git")
|
||||
(branch "main")))
|
||||
%default-channels))))
|
||||
```
|
||||
|
||||
It differs from what you’d do with other CI tools in two important ways:
|
||||
|
||||
- Cuirass knows it’s tracking *two* channels, `guile` and `guix`.
|
||||
Indeed, our own `guile` package depends on many packages provided by
|
||||
the `guix` channel—GCC, the GNU libc, libffi, and so on. Changes to
|
||||
packages from the `guix` channel can potentially influence our
|
||||
`guile` build and this is something we’d like to see as soon as
|
||||
possible as Guile developers.
|
||||
- Build results are not thrown away: they can be distributed as
|
||||
[*substitutes*](https://guix.gnu.org/manual/devel/en/html_node/Substitutes.html)
|
||||
so that users of our `guile` channel transparently get pre-built
|
||||
binaries!
|
||||
|
||||
From a developer’s viewpoint, the end result is this [status
|
||||
page](https://ci.guix.gnu.org/jobset/guile) listing *evaluations*: each
|
||||
evaluation is a combination of commits of the `guix` and `guile`
|
||||
channels providing a number of *jobs*—one job per package defined in
|
||||
`guile-package.scm` times the number of target architectures.
|
||||
|
||||
As for substitutes, they come for free! As an example, our `guile`
|
||||
jobset being built on ci.guix.gnu.org, one automatically gets
|
||||
substitutes for it from ci.guix.gnu.org. It’s as simple as this.
|
||||
|
||||
# Bonus: Build manifest
|
||||
|
||||
The Cuirass spec above is convenient: it builds every package in our
|
||||
channel, which includes a few variants. However, this might may be
|
||||
insufficiently expressive in some cases: one might want specific
|
||||
cross-compilation jobs, transformations, Docker images, RPM/Deb
|
||||
packages, or even system tests.
|
||||
|
||||
To achieve that, you can write a
|
||||
[*manifest*](https://guix.gnu.org/manual/devel/en/html_node/Writing-Manifests.html).
|
||||
The one we have for Guile has entries for the package variants we
|
||||
defined above, as well as additional variants and cross builds:
|
||||
|
||||
```scheme
|
||||
;; This is ‘.guix/manifest.scm’ FIXME: move modules to .guix/modules/.
|
||||
(use-modules (guix)
|
||||
(guix profiles)
|
||||
(guile-package)) ;import our own package module
|
||||
|
||||
(define* (package->manifest-entry* package system
|
||||
#:key target)
|
||||
"Return a manifest entry for PACKAGE on SYSTEM, optionally cross-compiled to
|
||||
TARGET."
|
||||
(manifest-entry
|
||||
(inherit (package->manifest-entry package))
|
||||
(name (string-append (package-name package) "." system
|
||||
(if target
|
||||
(string-append "." target)
|
||||
"")))
|
||||
(item (with-parameters ((%current-system system)
|
||||
(%current-target-system target))
|
||||
package))))
|
||||
|
||||
(define native-builds
|
||||
(manifest
|
||||
(append (map (lambda (system)
|
||||
(package->manifest-entry* guile system))
|
||||
|
||||
'("x86_64-linux" "i686-linux"
|
||||
"aarch64-linux" "armhf-linux"
|
||||
"powerpc64le-linux"))
|
||||
(map (lambda (guile)
|
||||
(package->manifest-entry* guile "x86_64-linux"))
|
||||
(cons (package
|
||||
(inherit (package-with-c-toolchain
|
||||
guile
|
||||
`(("clang-toolchain"
|
||||
,(specification->package
|
||||
"clang-toolchain")))))
|
||||
(name "guile-clang"))
|
||||
(list guile-without-threads
|
||||
guile-without-networking
|
||||
guile-debug
|
||||
guile-strict-typing))))))
|
||||
|
||||
(define cross-builds
|
||||
(manifest
|
||||
(map (lambda (target)
|
||||
(package->manifest-entry* guile "x86_64-linux"
|
||||
#:target target))
|
||||
'("i586-pc-gnu"
|
||||
"aarch64-linux-gnu"
|
||||
"riscv64-linux-gnu"
|
||||
"i686-w64-mingw32"
|
||||
"x86_64-linux-gnu"))))
|
||||
|
||||
(concatenate-manifests (list native-builds cross-builds))
|
||||
```
|
||||
|
||||
We won’t go into the details of this manifest; suffice to say that it
|
||||
provides additional flexibility. We now need to tell Cuirass to build
|
||||
this manifest, which is done with a spec slightly different from the
|
||||
previous one:
|
||||
|
||||
```scheme
|
||||
;; Cuirass spec file to build all the packages of the ‘guile’ channel.
|
||||
(list (specification
|
||||
(name "guile")
|
||||
(build '(manifest ".guix/manifest.scm"))
|
||||
(channels
|
||||
(append (list (channel
|
||||
(name 'guile)
|
||||
(url "https://git.savannah.gnu.org/git/guile.git")
|
||||
(branch "main")))
|
||||
%default-channels))))
|
||||
```
|
||||
|
||||
This is it!
|
||||
|
||||
# Wrapping up
|
||||
|
||||
We picked Guile as the running example in this post and you can see the
|
||||
result here:
|
||||
|
||||
- [`.guix-channel`](https://git.savannah.gnu.org/cgit/guile.git/tree/.guix-channel?id=36fd2b4920ae926c79b936c29e739e71a6dff2bc);
|
||||
- [`.guix/modules/guile-package.scm`](https://git.savannah.gnu.org/cgit/guile.git/tree/build-aux/guix/guile-package.scm?id=36fd2b4920ae926c79b936c29e739e71a6dff2bc)
|
||||
with the top-level `guix.scm` symlink;
|
||||
- [`.guix/manifest.scm`](https://git.savannah.gnu.org/cgit/guile.git/tree/build-aux/manifest.scm?id=36fd2b4920ae926c79b936c29e739e71a6dff2bc).
|
||||
|
||||
These days, repositories are commonly splattered with dot files for
|
||||
various tools: `.envrc`, `.gitlab-ci.yml`, `.github/workflows`,
|
||||
`Dockerfile`, `.buildpacks`, `Aptfile`, `requirements.txt`, and whatnot.
|
||||
It may sound like we’re proposing a bunch of *additional* files, but in
|
||||
fact those files are expressive enough to *supersede* most or all of
|
||||
those listed above.
|
||||
|
||||
With a couple of files, we get support for:
|
||||
|
||||
- development environments (`guix shell`);
|
||||
- pristine test builds, including for package variants and for
|
||||
cross-compilation (`guix build`);
|
||||
- continuous integration;
|
||||
- continuous delivery to users (*via* the channel and with pre-built
|
||||
binaries);
|
||||
- generation of derivative build artifacts such as Docker images or
|
||||
Deb/RPM packages (`guix pack`).
|
||||
|
||||
At the Guix headquarters, we’re quite happy about the result. We’ve
|
||||
been building a unified tool set for reproducible software deployment
|
||||
when all the rage is on specialized tools building on one another;
|
||||
hopefully this is an illustration of how you as a developer can benefit
|
||||
from it!
|
Loading…
Reference in New Issue