@apollo/rover 0.39.0-prerelease-test.0 → 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 +56 -9
  2. package/binary.js +86 -36
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -81,6 +81,8 @@ Commands:
81
81
  Explain error codes
82
82
  license
83
83
  Commands for fetching offline licenses
84
+ client
85
+ Client workflow commands
84
86
  help
85
87
  Print this message or the help of the given subcommand(s)
86
88
 
@@ -169,6 +171,11 @@ This repo is organized as a [`cargo` workspace], containing several related proj
169
171
 
170
172
  ## Installation Methods
171
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
+
172
179
  #### Linux and MacOS `curl | sh` installer
173
180
 
174
181
  To install the latest release of Rover:
@@ -179,7 +186,7 @@ curl -sSL https://rover.apollo.dev/nix/latest | sh
179
186
 
180
187
  To install a specific version of Rover (note the `v` prefixing the version number):
181
188
 
182
- > 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.
183
190
 
184
191
  ```bash
185
192
  curl -sSL https://rover.apollo.dev/nix/v0.10.0 | sh
@@ -197,27 +204,64 @@ iwr 'https://rover.apollo.dev/win/latest' | iex
197
204
 
198
205
  To install a specific version of Rover (note the `v` prefixing the version number):
199
206
 
200
- > 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
+
201
209
 
202
210
  ```bash
203
211
  iwr 'https://rover.apollo.dev/win/v0.10.0' | iex
204
212
  ```
205
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
+
206
251
  #### npm installer
207
252
 
208
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.
209
256
 
210
257
  ##### devDependency install
211
258
 
212
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.
213
260
 
214
- ##### Manual download and install
215
-
216
- If you'd like to call `rover` from any directory on your machine, you can run `npm i -g @apollo/rover`.
217
-
218
- 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.
219
262
 
220
263
  #### Homebrew
264
+
221
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).
222
266
 
223
267
  #### Manual binary download
@@ -226,11 +270,14 @@ You can also [download the binary for your operating system](https://github.com/
226
270
 
227
271
  ##### Unsupported architectures
228
272
 
229
- 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.
230
277
 
231
278
  ```
232
279
  git clone https://github.com/apollographql/rover
233
- cargo xtask dist --version v0.1.3
280
+ cargo xtask dist
234
281
  ```
235
282
 
236
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.39.0-prerelease-test.0",
3
+ "version": "0.39.1-rc.1",
4
4
  "description": "The new Apollo CLI",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -41,7 +41,7 @@
41
41
  "undici": "^7.0.0"
42
42
  },
43
43
  "devDependencies": {
44
- "jest": "30.3.0",
44
+ "jest": "30.4.2",
45
45
  "jest-junit": "17.0.0",
46
46
  "prettier": "3.8.3"
47
47
  }