g1t/apps/docs/src/content/docs/guides/rubygems.md

191 lines7,168 bytesCodeBlame
1---
2title: RubyGems
3description: Push a workspace's Ruby gems with gem push and install them with Bundler, from a gem registry of its own on g1t.sh, from your machine and from workflows.
4---
5
6Every workspace has a gem registry of its own. `gem push` publishes to it,
7and Bundler and `gem install` install from it, private gems included.
8It serves both indexes RubyGems reads: the compact index Bundler uses
9(`versions`, `info/<gem>`), and the full index (`specs.4.8.gz` and each
10version's specification) that `gem install --source` and `gem search`
11read.
12
13```text
14https://g1t.sh/-/rubygems/<workspace>/
15```
16
17Gems from rubygems.org still come from rubygems.org; only the gems you
18name with the workspace's source come from g1t.
19
20## Push
21
22Say in the gemspec which repository the gem comes from:
23
24```ruby
25Gem::Specification.new do |spec|
26 spec.name = "http-client"
27 spec.version = "0.3.1"
28 spec.summary = "Our HTTP client"
29 spec.authors = ["Ada"]
30 spec.files = Dir["lib/**/*.rb"]
31 spec.homepage = "https://g1t.sh/acme/http-client"
32 spec.metadata["source_code_uri"] = "https://g1t.sh/acme/http-client"
33 spec.metadata["allowed_push_host"] = "https://g1t.sh/-/rubygems/acme"
34end
35```
36
37Build it and push it with an [access token](https://g1t.sh/settings/tokens)
38as the API key:
39
40```sh
41gem build http-client.gemspec
42GEM_HOST_API_KEY=<token> gem push http-client-0.3.1.gem --host https://g1t.sh/-/rubygems/acme
43```
44
45To keep the key instead of passing it each time, add it to
46`~/.gem/credentials`, under the registry's address:
47
48```yaml
49---
50:https://g1t.sh/-/rubygems/acme: g1t_...
51```
52
53`allowed_push_host` keeps the gem from being pushed to rubygems.org by
54mistake. A token with full access works; one with scopes needs
55`packages:write` to push and yank, and `packages:read` to install private
56gems.
57
58The first push makes the gem. When its `source_code_uri` (or `homepage`)
59is a g1t.sh repository of the same workspace, or a repository is named
60like the gem (a gem `http_client` is also matched to a repository
61`http-client`), it is linked to that repository and has its visibility and
62roles: pushing needs Write on it. Otherwise it is the workspace's,
63private, and needs the workspace's Write base permission. See
64[who can see and publish a package](/guides/packages/#who-can-see-and-publish-a-package).
65
66The gem's page on g1t.sh shows the summary of its highest stable version.
67
68## Install with Bundler
69
70Name the workspace's source for the gems that come from it:
71
72```ruby
73source "https://rubygems.org"
74
75source "https://g1t.sh/-/rubygems/acme/" do
76 gem "http-client", "~> 0.3"
77end
78```
79
80or let Bundler write that for you:
81
82```sh
83bundle add http-client --source https://g1t.sh/-/rubygems/acme/
84```
85
86For private gems, give Bundler a username (any works) and a token, for
87the source's address:
88
89```sh
90bundle config set --global https://g1t.sh/-/rubygems/acme/ ada:<token>
91```
92
93Or set them once for every workspace on g1t.sh, by host:
94`bundle config set --global g1t.sh ada:<token>`, which can also come from
95the environment as `BUNDLE_G1T__SH`, as [workflows](#in-workflows) give it.
96
97`bundle install` then reads the compact index and downloads each `.gem`,
98which it checks against the SHA-256 the index names.
99
100## Install with gem
101
102To install a gem and what it depends on without a `Gemfile`, name the
103workspace's source. For private gems, put a username (any works) and a
104token in its address:
105
106```sh
107gem install http-client --source https://ada:<token>@g1t.sh/-/rubygems/acme/
108```
109
110Gems it depends on from rubygems.org need that source too: add
111`--source https://rubygems.org/` after the workspace's. To keep the
112source, add it once with `gem sources --add <address>`.
113
114`gem search http --remote --source <address>` lists the workspace's gems,
115and `gem specification http-client --remote --source <address>` shows one.
116`--prerelease` includes pre-releases. Yanked versions are in neither.
117
118## Names and versions
119
120A gem's name is letters, digits, `.`, `-` and `_`, with at least one
121letter. In a workspace, names that differ only in case are one name: once
122`http-client` is pushed, `HTTP-Client` is refused.
123
124A version is pushed once, for each platform: pushing `0.3.1` again is
125refused with `409`, even after it is yanked, so bump `version` first. A
126gem built for a platform (`0.3.1-x86_64-linux`) is its own version beside
127the `ruby` one. A version with a letter in it (`0.4.0.rc1`) is a
128pre-release, which Bundler picks only when asked for.
129
130## Yank
131
132```sh
133GEM_HOST_API_KEY=<token> gem yank http-client --version 0.3.1 --host https://g1t.sh/-/rubygems/acme
134```
135
136A yanked version leaves both indexes, so Bundler and `gem install` no
137longer resolve to it,
138but its `.gem` is still downloaded for a `Gemfile.lock` that names it.
139Yanking needs what pushing does. The gem's page marks yanked versions, and
140someone with Admin on the linked repository (an owner, for the
141workspace's own gems) can delete a version there for good.
142
143## Private and public gems
144
145A gem linked to a repository has the repository's visibility; one of the
146workspace's own is private until an owner makes it public on its page.
147
148| The workspace's gems | Without credentials | With credentials |
149| --- | --- | --- |
150| All public | Bundler and `gem` read the index and download them. | The same; the key is sent to push and yank. |
151| Some private | The registry answers `401`: Bundler asks for credentials for the source, and `gem` needs them in the source's address. | Each gem the credentials' owner may see. |
152
153A private gem you cannot see looks exactly like one that does not exist.
154
155## In workflows
156
157A workflow's `G1T_TOKEN` is the workspace's own token for the run, and can
158install and push the workspace's gems:
159
160```yaml
161jobs:
162 publish:
163 runs-on: ubuntu-latest
164 env:
165 GEM_HOST_API_KEY: ${{ secrets.G1T_TOKEN }}
166 BUNDLE_G1T__SH: g1t:${{ secrets.G1T_TOKEN }}
167 steps:
168 - uses: actions/checkout@v4
169 - run: bundle install && bundle exec rake test
170 - run: gem build http-client.gemspec
171 - run: gem push http-client-*.gem --host https://g1t.sh/-/rubygems/acme
172```
173
174## Size
175
176A push is one request with the `.gem` as its body, and may hold at most
177100 MB. Without the [g1t plan](/guides/usage-and-billing/#the-g1t-plan), a
178workspace's private packages may hold 500 MB and its public ones 10 GB, as
179for [container images](/guides/containers/#storage-and-pull-limits). A
180`.gem` is stored once, by its content.
181
182## Errors
183
184| Error | Means |
185| --- | --- |
186| `401` | No credentials or key, or a wrong or expired token. For Bundler, set the source's credentials with `bundle config set`; for `gem install`, put them in the source's address; for `gem push`, give `GEM_HOST_API_KEY`. |
187| `403` | Signed in, but your role or your token's scopes do not allow it, or the workspace is out of free package storage. The response says which. |
188| `404` | No such gem or version, or a private one you cannot see. |
189| `409` | That version is already pushed, or its name is taken by a gem named in another case. |
190| `422` | The push was refused: not a `.gem`, or a name or version RubyGems would not take. The response says which. |
191| `413` | The `.gem` is over 100 MB. |