Awesome
<img align="left" width="200" height="200" src="img/logo.gif">gyro: a zig package manager
<br />A gyroscope is a device used for measuring or maintaining orientation
GYRO IS NOW ARCHIVED, THANK YOU EVERYONE FOR YOUR FEEDBACK AND TIME. ZIG NOW HAS AN OFFICIAL PACKAGE MANAGER AND DAMN IT'S GOOD.
Table of contents
- Introduction
- Installation
- How tos
- Design philosophy
- Generated files
Introduction
Gyro is an unofficial package manager for the Zig programming language. It
improves a developer's life by giving them a package experience similar to
cargo. Dependencies are declared in a gyro.zzz file in the root of your
project, and are exposed to you programmatically in the build.zig
file by
importing @import("deps.zig").pkgs
. In short, all that's needed on your part
is how you want to add packages to different objects you're building:
const Builder = @import("std").build.Builder;
const pkgs = @import("deps.zig").pkgs;
pub fn build(b: *Builder) void {
const exe = b.addExecutable("main", "src/main.zig");
pkgs.addAllTo(exe);
exe.install();
}
To make the job of finding suitable packages to use in your project easier,
gyro is paired with a package index located at
astrolabe.pm. A simple gyro add alexnask/iguanaTLS
will add the latest version of iguanaTLS
(pure Zig TLS library) as a
dependency. To build your project all that's needed is gyro build
which
works exactly like zig build
, you can append the same arguments, except it
automatically downloads any missing dependencies. To learn about other If you
want to use a dependency from github, you can add it by explicitly with github add -s github <user>/<repo> [<ref>]
. <ref>
is an optional arg which can be
a branch, tag, or commit hash, if not specified, gyro uses the default branch.
Installation
In order to install gyro, all you need to do is extract one of the release tarballs for your system and add the single static binary to your PATH. Gyro requires the latest stable version of the Zig compiler (0.10.1 at time of writing)
Building
If you'd like to build from source, the only thing you need is the Zig compiler:
git clone https://github.com/mattnite/gyro.git
cd gyro
zig build -Drelease-safe
How tos
Instead of just documenting all the different subcommands, this documentation
just lists out all the different scenarios that Gyro was built for. And if you
wanted to learn more about the cli you can simply gyro <subcommand> --help
.
Initialize project
If you have an existing project on Github that's a library then you can populate gyro.zzz file with metadata:
gyro init <user>/<repo>
For both new and existing libraries you'd want to check out export a package, if you don't plan on publishing to astrolabe then ensuring the root file is declared is all that's needed. For projects that are an executable or considered the 'root' of the dependency tree, all you need to do is add dependencies.
Setting up build.zig
In build.zig, the dependency tree can be imported with
const pkgs = @import("deps.zig").pkgs;
then in the build function all the packages can be added to an artifact with:
pkgs.addAllTo(lib);
individual packages exist in the pkgs namespace, so a package named mecha
can
be individually added:
lib.addPackage(pkgs.mecha)
Ignoring gyro.lock
gyro.lock is intended for reproducible builds. It is advised to add it to
.gitignore
if your project is a library.
Export a package
This operation doesn't have a cli equivalent so editing of gyro.zzz is required, if you followed initializing a project and grabbed metadata from Github, then a lot of this work is done for you -- but still probably needs some attention:
- the root file, it is
src/main.zig
by default - the version
- file globs describing which files are actually part of the package. It is encouraged to include the license and readme.
- metadata: description, tags, source_url, etc.
- dependencies
Publishing a package to astrolabe.pm
In order to publish to astrolabe you need a Github account and a browser. If your project exports multiple packages you'll need to append the name, otherwise you can simply:
gyro publish
This should open your browser to a page asking for a alphanumeric code which you can find printed on the command line. Enter this code, this will open another page to confirm read access to your user and your email from your Github account. Once that is complete, Gyro will publish your package and if successful a link to it will be printed to stdout.
An access token is cached so that this browser sign-on process only needs to be done once for a given dev machine.
If you'd like to add publishing from a CI system see Publishing from an action.
Adding dependencies
From package index
gyro add <user>/<pkg>
From a git repo
Right now if the repo isn't from Github then you can add an entry to your deps
like so:
deps:
pkgname:
git:
url: "https://some.repo/user/repo.git"
ref: main
root: my/root.zig
From Github
If you add a package from Github, gyro will get the default branch, and try to figure out the root path for you:
gyro add --src github <user>/<repo>
From url
Note that at this time it is not possible to add a dependency from a url using
the command line. However, it is possible to give a url to a .tar.gz file by
adding it to your gyro.zzz
file.
deps:
pkgname:
url: "https://path/to/my/library.tar.gz"
root: libname/rootfile.zig
In this example, when library.tar.gz
is extracted the top level directory is
libname
.
Build dependencies
It's also possible to use packaged code in your build.zig
, since this would
only run at build time and not required in your application or library these are
kept separate from your regular dependencies in your project file.
When you want to add a dependency as a build dep, all you need to do is add
--build-dep
to the gyro invocation. For example, let's assume I need to do
some parsing with a package called mecha
:
gyro add --build-dep mattnite/zzz
and in my build.zig
:
const Builder = @import("std").build.Builder;
const pkgs = @import("gyro").pkgs;
const zzz = @import("zzz");
pub fn build(b: *Builder) void {
const exe = b.addExecutable("main", "src/main.zig");
pkgs.addAllTo(exe);
exe.install();
// maybe do some workflow based on gyro.zzz
var tree = zzz.ZTree(1, 100){};
...
}
Removing dependencies
Removing a dependency only requires the alias (string used to import):
gyro rm iguanaTLS
Local development
One can switch out a dependency for a local copy using the redirect
subcommand. Let's say we're using mattnite/tar
from astrolabe and we come
across a bug, we can debug with a local version of the package by running:
gyro redirect -a tar -p ../tar
This will point gyro at your local copy, and when you're done you can revert the redirect(s) with:
gyro redirect --clean
Multiple dependencies can be redirected. Build dependencies are redirected by
passing -b
. You can even redirect the dependencies of a local package.
HOT tip: add this to your git pre-commit
hook to catch you before accidentally
commiting redirects:
gyro redirect --check
Update dependencies -- for package consumers
Updating dependencies only works for package consumers as it modifies gyro.lock. It does not change dependency requirements, merely resolves the dependencies to their latest versions. Simply:
gyro update
Updating single dependencies will come soon, right now everything is updated.
Use gyro in Github Actions
You can get your hands on Gyro for github actions here, it does not install the zig compiler so remember to include that as well!
Publishing from an action
It's possible to publish your package in an action or other CI system. If the
environment variable GYRO_ACCESS_TOKEN
exists, Gyro will use that for the
authenticated publish request instead of using Github's device flow. For Github
actions this requires creating a personal access
token
with scope read:user
and user:email
and adding it to an
Environment,
and adding that Environment to your job. This allows you to access your token in
the secrets
namespace. Here's an example publish job:
name: Publish
on: workflow_dispatch
jobs:
publish:
runs-on: ubuntu-latest
environment: publish
steps:
- uses: mattnite/setup-gyro@v1
- uses: actions/checkout@v2
- run: gyro publish
env:
GYRO_ACCESS_TOKEN: ${{ secrets.GYRO_ACCESS_TOKEN }}
Completion Scripts
Completion scripts can be generated by the completion
subcommand, it is run
like so:
gyro completion -s <shell> <install path>
Right now only zsh
is the only supported shell, and this will create a _gyro
file in the install path. If you are using oh-my-zsh
then this path should be
$HOME/.oh-my-zsh/completions
.
Design philosophy
The two main obectives for gyro are providing a great user experience and creating a platform for members of the community to get their hands dirty with Zig package management. The hope here is that this experience will better inform the development of the official package manager.
To create a great user experience, gyro is inspired by Rust's package manager,
Cargo. It does this by taking over the build runner so that zig build
is
effectively replaced with gyro build
, and this automatically downloads missing
dependencies and allows for build dependencies. Other
features include easy addition of dependencies through
the cli, publishing packages on
astrolabe.pm, as well as local
development.
The official Zig package manager is going to be decentralized, meaning that there will be no official package index. Gyro has a centralized feel in that the best UX is to use Astrolabe, but you can use it without interacting with the package index. It again comes down to not spending effort on supporting everything imaginable, and instead focus on experimenting with big design decisions around package management.
Generated files
gyro.zzz
This is your project file, it contains the packages you export (if any),
dependencies, and build dependencies. zzz
is a file format similar to yaml but
has a stricter spec and is implemented in zig.
A map of a gyro.zzz file looks something like this:
pkgs:
pkg_a:
version: 0.0.0
root: path/to/root.zig
description: the description field
license: spdix-id
homepage_url: https://straight.forward
source_url: https://straight.forward
# these are shown on astrolabe
tags:
http
cli
# allows for globbing, doesn't do recursive globbing yet
files:
LICENSE
README.md # this is displayed on the astrolabe
build.zig
src/*.zig
# exporting a second package
pkg_b:
version: 0.0.0
...
# like 'deps' but these can be directly imported in build.zig
build_deps:
...
# most 'keys' are the string used to import in zig code, the exception being
# packages from the default package index which have a shortend version
deps:
# a package from the default package index, user is 'bruh', its name is 'blarg'
# and is imported with the same string
bruh/blarg: ^0.1.0
# importing blarg from a different package index, have to use a different
# import string, I'll use 'flarp'
flarp:
pkg:
name: blarg
user: arst
version: ^0.3.0
repository: something.gg
# a git package, imported with string 'mecha'
mecha:
git:
url: "https://github.com/Hejsil/mecha.git"
ref: zig-master
root: mecha.zig
# a raw url, imported with string 'raw' (remember its gotta be a tar.gz)
raw:
url: "https://example.com/foo.tar.gz"
root: bar.zig
# a local path, usually used for local development using the redirect
# subcommand, but could be used for vendoring or monorepos
blarg:
local: deps/blarg
root: src/main.zig
gyro.lock
This contains a lockfile for reproducible builds, it is only useful in compiled
projects, not libraries. Adding gyro.lock to your package will not affect
resolved versions of dependencies for your users -- it is suggested to add this
file to your .gitignore
for libraries.
deps.zig
This is the generated file that's imported by build.zig
, it can be imported
with @import("deps.zig")
or @import("gyro")
. Unless you are vendoring your
dependencies, this should be added to .gitignore
.
.gyro/
This directory holds the source code of all your dependencies. Path names are
human readable and look something like <package>-<user>-<version>
so in many
cases it is possible to navigate to dependencies and make small edits if you run
into bugs. For a more robust way to edit dependencies see local
development
It is suggested to add this to .gitignore
as well.