Skip to content
This repository has been archived by the owner on Aug 1, 2023. It is now read-only.
/ gyro Public archive

A Zig package manager with an index, build runner, and build dependencies.

License

Notifications You must be signed in to change notification settings

mattnite/gyro

Repository files navigation

gyro: a zig package manager

A gyroscope is a device used for measuring or maintaining orientation

Linux windows macOS



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

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.