@apollo/rover 0.38.1 → 0.39.1-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +59 -10
  2. package/binary.js +86 -36
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # Rover
2
2
 
3
- [![CircleCI Tests](https://circleci.com/gh/apollographql/rover.svg?style=svg)](https://app.circleci.com/pipelines/github/apollographql/rover?branch=main)
4
3
  [![GitHub Release Downloads](https://shields.io/github/downloads/apollographql/rover/total.svg)](https://github.com/apollographql/rover/releases/latest)
4
+ [![Unit/Integration Tests](https://github.com/apollographql/rover/actions/workflows/checks.yml/badge.svg)](https://github.com/apollographql/rover/actions/workflows/checks.yml)
5
+ [![E2E Tests](https://github.com/apollographql/rover/actions/workflows/run-smokes-on-pr.yml/badge.svg)](https://github.com/apollographql/rover/actions/workflows/run-smokes-on-pr.yml)
6
+ [![Command E2E Tests](https://github.com/apollographql/rover/actions/workflows/run-commands-e2e.yml/badge.svg)](https://github.com/apollographql/rover/actions/workflows/run-commands-e2e.yml)
5
7
 
6
8
  This is the home of Rover, the new CLI for Apollo's suite of GraphQL developer productivity tools.
7
9
 
@@ -79,6 +81,8 @@ Commands:
79
81
  Explain error codes
80
82
  license
81
83
  Commands for fetching offline licenses
84
+ client
85
+ Client workflow commands
82
86
  help
83
87
  Print this message or the help of the given subcommand(s)
84
88
 
@@ -167,6 +171,11 @@ This repo is organized as a [`cargo` workspace], containing several related proj
167
171
 
168
172
  ## Installation Methods
169
173
 
174
+ As of Rover 0.39.0, all the platforms listed below enforce immutable release tags. This means that you can reference a GitHub release, Docker image, or NPM release version directly by SemVer and be
175
+ guaranteed that the artifact will not change. Note that the `curl | sh` method, while ultimately referencing immutable GitHub release binaries, still first downloads a shell script from a webservice
176
+ that does not provide that same guarantee of immutability. Security conscious installers should verify the downloaded shell script matches
177
+ [the pinned artifact for its respective Rover version](https://github.com/apollographql-gh-actions/install-rover) or use one of the immutable installation methods described below.
178
+
170
179
  #### Linux and MacOS `curl | sh` installer
171
180
 
172
181
  To install the latest release of Rover:
@@ -177,7 +186,7 @@ curl -sSL https://rover.apollo.dev/nix/latest | sh
177
186
 
178
187
  To install a specific version of Rover (note the `v` prefixing the version number):
179
188
 
180
- > Note: If you're installing Rover in a CI environment, it's best to target a specific version rather than using the latest URL, since future major breaking changes could affect CI workflows otherwise.
189
+ > Note: If you're installing Rover in a CI environment, we highly recommend using an [immutable Docker image of Rover](#docker-images)). As an alternative for GitHub Actions users, we vend a [GitHub Action](https://github.com/marketplace/actions/install-apollo-rover-cli) which pins an immutable instance of the download script and installs the native binary.
181
190
 
182
191
  ```bash
183
192
  curl -sSL https://rover.apollo.dev/nix/v0.10.0 | sh
@@ -195,27 +204,64 @@ iwr 'https://rover.apollo.dev/win/latest' | iex
195
204
 
196
205
  To install a specific version of Rover (note the `v` prefixing the version number):
197
206
 
198
- > Note: If you are installing Rover in a CI environment, it's best to target a specific version rather than using the latest URL, since future major breaking changes could affect CI workflows otherwise.
207
+ > Note: If you are installing Rover in a Windows CI environment, you need to put Docker into Linux mode to use the [recommended immutable Docker images](#docker-images)). As an alternative for GitHub Actions users, we vend a [GitHub Action](https://github.com/marketplace/actions/install-apollo-rover-cli) to do so which pins an immutable instance of the download script and installs the native binary.
208
+
199
209
 
200
210
  ```bash
201
211
  iwr 'https://rover.apollo.dev/win/v0.10.0' | iex
202
212
  ```
203
213
 
214
+ #### Docker images
215
+
216
+ Starting with version 0.39.0, Rover vends immutable Linux Docker images that pre-build Rover as an entry point for consumption in CI environments
217
+ or to run Rover on platforms that Rover does not build natively for. Each release verison tag is enforced as immutable at the platform level for
218
+ your convenience so that you can pin to the Rover version you want without needing to deal with the indirection of SHA pinning.
219
+
220
+ Install directly from Dockerhub:
221
+
222
+ ```bash
223
+ docker pull apollograph/rover:0.39.0
224
+ docker run apollograph/rover:0.39.0 <<args>>
225
+ ```
226
+
227
+ or via ghcr.io:
228
+
229
+ ```bash
230
+ docker pull ghcr.io/apollographql/rover:0.39.0
231
+ docker run ghcr.io/apollographql/rover:0.39.0 <<args>>
232
+ ```
233
+
234
+ All CI platforms that support referencing images from those respective image repositories can do so directly as well.
235
+
236
+ #### GitHub Actions
237
+
238
+ Rover vends a number of GitHub actions for convenient invocation of common Rover commands in your CI pipeline. They can be found on
239
+ [GitHub's actions marketplace](https://github.com/marketplace?query=apollographql-gh-actions+Rover&type=actions).
240
+
241
+ As of Rover 0.39.0, each Rover release corresponds to an immutable action tag of `<action>@rover-<version>`. This allows you to specify
242
+ the exact version of Rover for your CI actions without needing to rely on SHA pinning to guarantee action immutability. These actions
243
+ leverage Rover's Docker image under the hood to sandbox the Rover invocation and only expose it to the `APOLLO_*` environment variable
244
+ surface.
245
+
246
+ For use cases that need to invoke older versions of Rover or that cannot use Docker (such as Windows runners that aren't in Linux Docker mode),
247
+ the legacy `<action>@v1` tags have been left in place which accept a Rover version as an input.
248
+
249
+ Their source code is mastered in this repository under the `actions` directory.
250
+
204
251
  #### npm installer
205
252
 
206
253
  Rover is distributed on npm for easy integration with your JavaScript projects. Rover's Node dependency will follow LTS versions where possible unless security concerns justify an earlier upgrade.
254
+ While this installation method is provided for convenience in projects that are already in the Node ecosystem, we do not recommend it as an installation method otherwise as it exposes your
255
+ installation to NPM's surface area of potential supply-chain attacks. We have attempted to minimize the dependency surface of Rover's NPM installation script, but it still represents nonzero risk.
207
256
 
208
257
  ##### devDependency install
209
258
 
210
259
  If you'd like to install `rover` as a `devDependency` in your JavaScript project, you can run `npm i --save-dev @apollo/rover`. You can then call `rover` directly in your `package.json` [scripts](https://docs.npmjs.com/cli/v6/using-npm/scripts), or you can run `npx rover` in your project directory to execute commands.
211
260
 
212
- ##### Manual download and install
213
-
214
- If you'd like to call `rover` from any directory on your machine, you can run `npm i -g @apollo/rover`.
215
-
216
- Note: Unfortunately if you've installed `npm` without a version manager such as `nvm`, you may have trouble with global installs. If you encounter an `EACCES` permission-related error while trying to install globally, DO NOT run the install command with `sudo`. [This support page](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally) has information that should help to resolve this issue.
261
+ Note that installing rover directly via `npx install` bypasses lockfiles (including Rover's own) and is the highest-risk installation method in terms of potential supply-chain risk as a result.
217
262
 
218
263
  #### Homebrew
264
+
219
265
  While we recommend using one of the other installation methods above, we do have a homebrew recipe `brew install rover`. The code for this recipe is in the [homebrew-core repo](https://github.com/Homebrew/homebrew-core/blob/master/Formula/r/rover.rb).
220
266
 
221
267
  #### Manual binary download
@@ -224,11 +270,14 @@ You can also [download the binary for your operating system](https://github.com/
224
270
 
225
271
  ##### Unsupported architectures
226
272
 
227
- If you don't see your CPU architecture supported as part of our release pipeline, you can build from source with [`cargo`](https://github.com/rust-lang/cargo). Clone this repo, and run `cargo xtask dist --version v0.1.3`. This will compile a released version of Rover for you, and place the binary in your `target` directory.
273
+ If you don't see your CPU architecture supported as part of our release pipeline and cannot utilize the Docker images, you can build from source with [`cargo`](https://github.com/rust-lang/cargo).
274
+ Clone this repo, and run `cargo xtask dist`. This will compile a released version of Rover for you, and place the binary in your `target` directory.
275
+
276
+ If you want a specific Rover version, check out its respective Git tag before building.
228
277
 
229
278
  ```
230
279
  git clone https://github.com/apollographql/rover
231
- cargo xtask dist --version v0.1.3
280
+ cargo xtask dist
232
281
  ```
233
282
 
234
283
  From here you can either place the binary in your `PATH` manually, or run `./target/release/{optional_target}/rover install`.
package/binary.js CHANGED
@@ -103,55 +103,103 @@ const getPlatform = (type = os.type(), architecture = os.arch()) => {
103
103
  process.exit(1);
104
104
  };
105
105
 
106
- /*! Copyright (c) 2022 Mitchell Alderson - MIT License */
107
- const getProxyEnv = (requestURL) => {
108
- const noProxy = process.env.NO_PROXY || process.env.no_proxy || "";
109
-
110
- // if the noProxy is a wildcard then return null
111
- if (noProxy === "*") {
112
- return null;
106
+ const DEFAULT_PORTS = { "http:": 80, "https:": 443 };
107
+
108
+ /*! Copyright (c) 2014-present Matt Zabriskie & Collaborators - MIT License */
109
+ const parseNoProxyEntry = (entry) => {
110
+ let entryHost = entry;
111
+ let entryPort = 0;
112
+
113
+ if (entryHost.charAt(0) === "[") {
114
+ const bracketIndex = entryHost.indexOf("]");
115
+ if (bracketIndex !== -1) {
116
+ const host = entryHost.slice(1, bracketIndex);
117
+ const rest = entryHost.slice(bracketIndex + 1);
118
+ if (rest.charAt(0) === ":" && /^\d+$/.test(rest.slice(1))) {
119
+ entryPort = Number.parseInt(rest.slice(1), 10);
120
+ }
121
+ return [host, entryPort];
122
+ }
113
123
  }
114
124
 
115
- // if the noProxy is not empty and the uri is found, return null
116
- if (noProxy !== "" && urlInNoProxy(requestURL, noProxy)) {
117
- return null;
125
+ const firstColon = entryHost.indexOf(":");
126
+ const lastColon = entryHost.lastIndexOf(":");
127
+ if (
128
+ firstColon !== -1 &&
129
+ firstColon === lastColon &&
130
+ /^\d+$/.test(entryHost.slice(lastColon + 1))
131
+ ) {
132
+ entryPort = Number.parseInt(entryHost.slice(lastColon + 1), 10);
133
+ entryHost = entryHost.slice(0, lastColon);
118
134
  }
119
135
 
120
- // get proxy based on request url's protocol
121
- if (requestURL.protocol == "http:") {
122
- return process.env.HTTP_PROXY || process.env.http_proxy || null;
123
- }
136
+ return [entryHost, entryPort];
137
+ };
124
138
 
125
- if (requestURL.protocol == "https:") {
126
- return process.env.HTTPS_PROXY || process.env.https_proxy || null;
139
+ /*! Copyright (c) 2014-present Matt Zabriskie & Collaborators - MIT License */
140
+ const normalizeNoProxyHost = (hostname) => {
141
+ if (!hostname) return hostname;
142
+ if (
143
+ hostname.charAt(0) === "[" &&
144
+ hostname.charAt(hostname.length - 1) === "]"
145
+ ) {
146
+ hostname = hostname.slice(1, -1);
127
147
  }
128
-
129
- // not a supported protocol...
130
- return null;
148
+ return hostname.replace(/\.+$/, "");
131
149
  };
132
150
 
133
- /*! Copyright (c) 2022 Mitchell Alderson - MIT License */
134
- const urlInNoProxy = (requestURL, noProxy) => {
151
+ /*! Copyright (c) 2014-present Matt Zabriskie & Collaborators - MIT License */
152
+ const shouldBypassProxy = (requestURL) => {
153
+ const noProxy = (
154
+ process.env.no_proxy ||
155
+ process.env.NO_PROXY ||
156
+ ""
157
+ ).toLowerCase();
158
+
159
+ if (!noProxy) return false;
160
+ if (noProxy === "*") return true;
161
+
135
162
  const port =
136
- requestURL.port || (requestURL.protocol === "https:" ? "443" : "80");
137
- const hostname = formatHostName(requestURL.hostname);
138
- //testing: internal.example.com,internal2.example.com
139
- const noProxyList = noProxy.split(",");
140
-
141
- return noProxyList.map(parseNoProxyZone).some((noProxyZone) => {
142
- const isMatchedAt = hostname.indexOf(noProxyZone.hostname);
143
- const hostnameMatched =
144
- isMatchedAt > -1 &&
145
- isMatchedAt === hostname.length - noProxyZone.hostname.length;
146
-
147
- if (noProxyZone.hasPort) {
148
- return port === noProxyZone.port && hostnameMatched;
163
+ Number.parseInt(requestURL.port, 10) ||
164
+ DEFAULT_PORTS[requestURL.protocol] ||
165
+ 0;
166
+ const hostname = normalizeNoProxyHost(requestURL.hostname.toLowerCase());
167
+
168
+ return noProxy.split(/[\s,]+/).some((entry) => {
169
+ if (!entry) return false;
170
+
171
+ let [entryHost, entryPort] = parseNoProxyEntry(entry);
172
+ entryHost = normalizeNoProxyHost(entryHost);
173
+ if (!entryHost) return false;
174
+
175
+ if (entryPort && entryPort !== port) return false;
176
+
177
+ if (entryHost.charAt(0) === "*") {
178
+ entryHost = entryHost.slice(1);
179
+ }
180
+
181
+ if (entryHost.charAt(0) === ".") {
182
+ return hostname.endsWith(entryHost);
149
183
  }
150
184
 
151
- return hostnameMatched;
185
+ return hostname === entryHost;
152
186
  });
153
187
  };
154
188
 
189
+ const getProxyEnv = (requestURL) => {
190
+ if (shouldBypassProxy(requestURL)) return null;
191
+
192
+ if (requestURL.protocol === "http:") {
193
+ return process.env.HTTP_PROXY || process.env.http_proxy || null;
194
+ }
195
+
196
+ if (requestURL.protocol === "https:") {
197
+ return process.env.HTTPS_PROXY || process.env.https_proxy || null;
198
+ }
199
+
200
+ return null;
201
+ };
202
+
155
203
  /*! Copyright (c) 2019 Avery Harnish - MIT License */
156
204
  class Binary {
157
205
  constructor(name, raw_name, url, installDirectory) {
@@ -218,7 +266,7 @@ class Binary {
218
266
  console.error(`Downloading release from ${this.url}`);
219
267
  }
220
268
 
221
- const proxyUrl = getProxyEnv(this.url);
269
+ const proxyUrl = getProxyEnv(new URL(this.url));
222
270
 
223
271
  const agent = proxyUrl ? new ProxyAgent(proxyUrl) : null;
224
272
  const fetchPromise = fetch(this.url, agent ? { dispatcher: agent } : {});
@@ -330,4 +378,6 @@ module.exports = {
330
378
  run,
331
379
  getBinary,
332
380
  getPlatform,
381
+ getProxyEnv,
382
+ shouldBypassProxy,
333
383
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apollo/rover",
3
- "version": "0.38.1",
3
+ "version": "0.39.1-rc.1",
4
4
  "description": "The new Apollo CLI",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -41,8 +41,8 @@
41
41
  "undici": "^7.0.0"
42
42
  },
43
43
  "devDependencies": {
44
- "jest": "30.3.0",
45
- "jest-junit": "16.0.0",
44
+ "jest": "30.4.2",
45
+ "jest-junit": "17.0.0",
46
46
  "prettier": "3.8.3"
47
47
  }
48
48
  }