forked from microsoft/rushstack
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation of rush-http-build-cache-plugin
- Loading branch information
1 parent
ac4e41e
commit 87cfece
Showing
21 changed files
with
838 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
common/changes/@microsoft/rush/enelson-http-cache_2023-02-15-13-36.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"changes": [ | ||
{ | ||
"packageName": "@microsoft/rush", | ||
"comment": "Add built-in plugin rush-http-build-cache-plugin", | ||
"type": "none" | ||
} | ||
], | ||
"packageName": "@microsoft/rush" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# @rushstack/rush-http-build-cache-plugin | ||
|
||
A Rush plugin that uses HTTP/HTTPS to manage cache objects. | ||
|
||
Authentication is provided via standard `Authorization` HTTP headers, with the value (a Bearer or Basic token) configured using a custom node script. Your "tokenHandler" node script is automatically called when the user updates their cloud credentials. | ||
|
||
## Configuration | ||
|
||
To use the HTTP build cache plugin, enable it in `common/config/rush/build-cache.json`: | ||
|
||
```json | ||
{ | ||
"buildCacheEnabled": true, | ||
|
||
"cacheProvider": "http" | ||
} | ||
``` | ||
|
||
Once enabled, configure the HTTP build cache in config file `common/config/rush-plugins/rush-http-build-cache-plugin.json`: | ||
|
||
```json | ||
{ | ||
"url": "https://build-cache.example.com", | ||
"tokenHandler": "node common/scripts/custom-script-that-returns-an-authentication-header.js", | ||
"isCacheWriteAllowed": false | ||
} | ||
``` | ||
|
||
- url: The server to store cache objects. | ||
- tokenHandler: A script that can print the Authorization header expected by the server. The value printed to `stdout` by this command should be an exact header, for example, `Bearer ab98d8c878d937290d979a9097c90dfffff` or `Basic 098abc7dff==`. | ||
- isCacheWriteAllowed: A flag that determines if the plugin should write to the cache. | ||
|
||
## HTTP Cache Server Requirements | ||
|
||
The HTTP build cache plugin can use almost any HTTP/HTTPS backend for remote caching, as long as it honors the following rules: | ||
|
||
- Uses `Authorization: Bearer xxx` or `Authorization: Basic xxx` headers for authentication. | ||
- Accepts GET requests for cache reads. | ||
- Accepts PUT requests for cache writes (with a raw request body -- no `form/multipart`). | ||
- Cache hits return HTTP 200 with the file in the response body. | ||
- Successful cache writes return HTTP 2xx (200-299). | ||
- Cache misses return HTTP 404 or HTTP 403. | ||
- Invalid or missing authentication returns HTTP 401. | ||
|
||
## Examples | ||
|
||
### Gradle Build Cache Server | ||
|
||
The Gradle Build Cache Server (typically used to support Gradle Remote Build Cache) meets all of the requirements above, so if you don't have another server in mind, you can use it as your remote backend. | ||
|
||
First, start up and configure your build cache node locally: | ||
|
||
- Download latest JAR file from https://docs.gradle.com/build-cache-node/ | ||
- Start the service: `java -jar build-cache-node-14.0.jar start` | ||
- Copy the startup banner information, and navigate to the specified localhost port | ||
- Enter the temporary username/password credentials printed in the startup banner | ||
- Click Build Cache > Settings > Cache Access Control and grant "Read & write" access to Anonymous | ||
|
||
Second, create your `rush-http-build-cache-plugin.json` file as described in the Configuration section: | ||
|
||
- Note that your `url` should end with `/cache`, for example, `http://localhost:5071/cache` | ||
- To test reading and writing, set `isCacheWriteAllowed: true`. | ||
- Configure `tokenHandler` to point to a script that prints a Basic or Bearer Authorization value (this can be a dummy string if you granted Read and Write to Anonymous in your build cache node configuration). | ||
|
||
Note that the Gradle Build Cache Server has a stricter format for its cache keys (they should be a simple hexadecimal hash with no non-alphanumeric characters). Configure this setting in your `common/config/rush/build-cache.json` file: | ||
|
||
```json | ||
{ | ||
"cacheEntryNamePattern": "[hash]" | ||
} | ||
``` | ||
|
||
Last, initialize your cache credentials using Rush: | ||
|
||
```console | ||
rush update-cloud-credentials --interactive | ||
``` | ||
|
||
To test out your remote build cache with full debugging output (for spotting any errors reading or writing the cache), run with the `--debug` flag: | ||
|
||
```console | ||
rush --debug build --verbose | ||
``` | ||
|
||
> If you go on to deploy Rush remote build caching to your developers using the Gradle Build Cache, update your `tokenHandler` | ||
> script to reflect your use case -- for example, you could require each developer to have a designated username/token configured | ||
> via environment variables, and configure Cache Access Control with the corresponding entries. In this case the `tokenHandler` | ||
> script should read the environment variables and print out an Authorization header, for example: | ||
> | ||
> ```javascript | ||
> // common/scripts/build-cache-auth.js | ||
> const credentials = `${process.env.CACHE_USER}:${process.env.CACHE_TOKEN}`; | ||
> console.log('Basic ' + Buffer.from(credentials).toString('base64')); | ||
> ``` |
14 changes: 14 additions & 0 deletions
14
rush-plugins/rush-http-build-cache-plugin/config/jest.config.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json", | ||
"clearMocks": true, | ||
"restoreMocks": true, | ||
"collectCoverage": true, | ||
"coverageThreshold": { | ||
"global": { | ||
"branches": 4, | ||
"functions": 15, | ||
"lines": 4, | ||
"statements": 4 | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
// The "rig.json" file directs tools to look for their config files in an external package. | ||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package | ||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", | ||
|
||
/** | ||
* (Required) The name of the rig package to inherit from. | ||
* It should be an NPM package name with the "-rig" suffix. | ||
*/ | ||
"rigPackageName": "@rushstack/heft-node-rig", | ||
|
||
/** | ||
* (Optional) Selects a config profile from the rig package. The name must consist of | ||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile". | ||
* If omitted, then the "default" profile will be used." | ||
*/ | ||
"rigProfile": "default" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
"name": "@rushstack/rush-http-build-cache-plugin", | ||
"version": "5.92.0", | ||
"description": "Rush plugin for generic HTTP cloud build cache", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/microsoft/rushstack", | ||
"directory": "rush-plugins/rush-http-build-cache-plugin" | ||
}, | ||
"homepage": "https://rushjs.io", | ||
"main": "lib/index.js", | ||
"types": "lib/index.d.ts", | ||
"license": "MIT", | ||
"scripts": { | ||
"build": "heft build --clean", | ||
"start": "heft test --clean --watch", | ||
"test": "heft test", | ||
"_phase:build": "heft build --clean", | ||
"_phase:test": "heft test --no-build" | ||
}, | ||
"dependencies": { | ||
"@rushstack/node-core-library": "workspace:*", | ||
"@rushstack/rush-sdk": "workspace:*", | ||
"https-proxy-agent": "~5.0.0", | ||
"node-fetch": "2.6.7" | ||
}, | ||
"devDependencies": { | ||
"@microsoft/rush-lib": "workspace:*", | ||
"@rushstack/eslint-config": "workspace:*", | ||
"@rushstack/heft": "workspace:*", | ||
"@rushstack/heft-node-rig": "workspace:*", | ||
"@types/heft-jest": "1.0.1", | ||
"@types/node": "14.18.36", | ||
"@types/node-fetch": "2.6.2" | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
rush-plugins/rush-http-build-cache-plugin/rush-plugin-manifest.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-plugin-manifest.schema.json", | ||
"plugins": [ | ||
{ | ||
"pluginName": "rush-http-build-cache-plugin", | ||
"description": "Rush plugin for generic HTTP build cache", | ||
"entryPoint": "lib/index.js", | ||
"optionsSchema": "lib/schemas/plugin-config.schema.json" | ||
} | ||
] | ||
} |
55 changes: 55 additions & 0 deletions
55
rush-plugins/rush-http-build-cache-plugin/src/HttpBuildCacheProvider.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
jest.mock('node-fetch', function () { | ||
return Object.assign(jest.fn(), jest.requireActual('node-fetch')); | ||
}); | ||
|
||
import fetch, { Response } from 'node-fetch'; | ||
import { HttpBuildCacheProvider } from './HttpBuildCacheProvider'; | ||
import { RushSession, EnvironmentConfiguration } from '@rushstack/rush-sdk'; | ||
import { StringBufferTerminalProvider, Terminal } from '@rushstack/node-core-library'; | ||
|
||
const EXAMPLE_OPTIONS = { | ||
url: 'https://buildcache.example.acme.com', | ||
tokenHandler: 'node tokenHandler.js', | ||
uploadMethod: 'POST', | ||
isCacheWriteAllowed: false, | ||
pluginName: 'example-plugin', | ||
rushProjectRoot: '/repo' | ||
}; | ||
|
||
describe('HttpBuildCacheProvider', () => { | ||
let terminalBuffer: StringBufferTerminalProvider; | ||
let terminal!: Terminal; | ||
|
||
beforeEach(() => { | ||
terminalBuffer = new StringBufferTerminalProvider(); | ||
terminal = new Terminal(terminalBuffer); | ||
}); | ||
|
||
describe('tryGetCacheEntryBufferByIdAsync', () => { | ||
it('prints warning if read credentials are not available', async () => { | ||
jest.spyOn(EnvironmentConfiguration, 'buildCacheCredential', 'get').mockReturnValue(undefined); | ||
|
||
const session: RushSession = {} as RushSession; | ||
const provider = new HttpBuildCacheProvider(EXAMPLE_OPTIONS, session); | ||
|
||
mocked(fetch).mockResolvedValue( | ||
new Response('Unauthorized', { | ||
status: 401, | ||
statusText: 'Unauthorized' | ||
}) | ||
); | ||
|
||
const result = await provider.tryGetCacheEntryBufferByIdAsync(terminal, 'some-key'); | ||
expect(result).toBe(undefined); | ||
expect(fetch).toHaveBeenCalledWith('https://buildcache.example.acme.com/some-key', { | ||
body: undefined, | ||
headers: {}, | ||
method: 'GET', | ||
redirect: 'follow' | ||
}); | ||
expect(terminalBuffer.getWarningOutput()).toEqual( | ||
'Error getting cache entry: Error: Credentials for https://buildcache.example.acme.com/ have not been provided.[n]In CI, verify that RUSH_BUILD_CACHE_CREDENTIAL contains a valid Authorization header value.[n][n]For local developers, run:[n][n] rush update-cloud-credentials --interactive[n][n]' | ||
); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.