@rushstack/rush-amazon-s3-build-cache-plugin 5.62.0-pr3059 → 5.62.0

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.
@@ -1,7 +1,108 @@
1
+ /// <reference types="node" />
2
+
3
+ import * as fetch from 'node-fetch';
1
4
  import type { IRushPlugin } from '@rushstack/rush-sdk';
5
+ import { ITerminal } from '@rushstack/node-core-library';
2
6
  import type { RushConfiguration } from '@rushstack/rush-sdk';
3
7
  import type { RushSession } from '@rushstack/rush-sdk';
4
8
 
9
+ /**
10
+ * A helper for reading and updating objects on Amazon S3
11
+ *
12
+ * @public
13
+ */
14
+ export declare class AmazonS3Client {
15
+ private readonly _credentials;
16
+ private readonly _s3Endpoint;
17
+ private readonly _s3Region;
18
+ private readonly _webClient;
19
+ private readonly _terminal;
20
+ constructor(credentials: IAmazonS3Credentials | undefined, options: IAmazonS3BuildCacheProviderOptionsAdvanced, webClient: WebClient, terminal: ITerminal);
21
+ static UriEncode(input: string): string;
22
+ static tryDeserializeCredentials(credentialString: string | undefined): IAmazonS3Credentials | undefined;
23
+ getObjectAsync(objectName: string): Promise<Buffer | undefined>;
24
+ uploadObjectAsync(objectName: string, objectBuffer: Buffer): Promise<void>;
25
+ private _writeDebugLine;
26
+ private _makeRequestAsync;
27
+ _getSha256Hmac(key: string | Buffer, data: string): Buffer;
28
+ _getSha256Hmac(key: string | Buffer, data: string, encoding: 'hex'): string;
29
+ private _getSha256;
30
+ private _getIsoDateString;
31
+ private _safeReadResponseText;
32
+ private _throwS3Error;
33
+ /**
34
+ * Validates a S3 endpoint which is http(s):// + hostname + port. Hostname validated according to RFC 1123
35
+ * {@link https://docs.aws.amazon.com/general/latest/gr/s3.html}
36
+ */
37
+ private _validateEndpoint;
38
+ }
39
+
40
+ /**
41
+ * Advanced options where user has the specify the full http endpoint
42
+ * @public
43
+ */
44
+ export declare interface IAmazonS3BuildCacheProviderOptionsAdvanced extends IAmazonS3BuildCacheProviderOptionsBase {
45
+ s3Endpoint: string;
46
+ }
47
+
48
+ /**
49
+ * @public
50
+ */
51
+ export declare interface IAmazonS3BuildCacheProviderOptionsBase {
52
+ s3Region: string;
53
+ s3Prefix: string | undefined;
54
+ isCacheWriteAllowed: boolean;
55
+ }
56
+
57
+ /**
58
+ * Simple options where user only provides the bucket and the endpoint is automatically built
59
+ * @public
60
+ */
61
+ export declare interface IAmazonS3BuildCacheProviderOptionsSimple extends IAmazonS3BuildCacheProviderOptionsBase {
62
+ s3Bucket: string;
63
+ }
64
+
65
+ /**
66
+ * Credentials for authorizing and signing requests to an Amazon S3 endpoint.
67
+ *
68
+ * @public
69
+ */
70
+ export declare interface IAmazonS3Credentials {
71
+ accessKeyId: string;
72
+ secretAccessKey: string;
73
+ sessionToken: string | undefined;
74
+ }
75
+
76
+ /**
77
+ * For use with {@link WebClient}.
78
+ *
79
+ * @public
80
+ */
81
+ export declare interface IGetFetchOptions extends IWebFetchOptionsBase {
82
+ verb: 'GET' | never;
83
+ }
84
+
85
+ /**
86
+ * For use with {@link WebClient}.
87
+ *
88
+ * @public
89
+ */
90
+ export declare interface IPutFetchOptions extends IWebFetchOptionsBase {
91
+ verb: 'PUT';
92
+ body?: Buffer;
93
+ }
94
+
95
+ /**
96
+ * For use with {@link WebClient}.
97
+ *
98
+ * @public
99
+ */
100
+ declare interface IWebFetchOptionsBase {
101
+ timeoutMs?: number;
102
+ verb?: 'GET' | 'PUT';
103
+ headers?: fetch.Headers;
104
+ }
105
+
5
106
  /**
6
107
  * @public
7
108
  */
@@ -11,4 +112,37 @@ declare class RushAmazonS3BuildCachePlugin implements IRushPlugin {
11
112
  }
12
113
  export default RushAmazonS3BuildCachePlugin;
13
114
 
115
+ /**
116
+ * A helper for issuing HTTP requests.
117
+ *
118
+ * @public
119
+ */
120
+ export declare class WebClient {
121
+ readonly standardHeaders: fetch.Headers;
122
+ accept: string | undefined;
123
+ userAgent: string | undefined;
124
+ proxy: WebClientProxy;
125
+ constructor();
126
+ static mergeHeaders(target: fetch.Headers, source: fetch.Headers): void;
127
+ addBasicAuthHeader(userName: string, password: string): void;
128
+ fetchAsync(url: string, options?: IGetFetchOptions | IPutFetchOptions): Promise<WebClientResponse>;
129
+ }
130
+
131
+ /**
132
+ * For use with {@link WebClient}.
133
+ * @public
134
+ */
135
+ declare enum WebClientProxy {
136
+ None = 0,
137
+ Detect = 1,
138
+ Fiddler = 2
139
+ }
140
+
141
+ /**
142
+ * For use with {@link WebClient}.
143
+ *
144
+ * @public
145
+ */
146
+ export declare type WebClientResponse = fetch.Response;
147
+
14
148
  export { }
@@ -1,12 +1,28 @@
1
1
  /// <reference types="node" />
2
2
  import { ITerminal } from '@rushstack/node-core-library';
3
3
  import { ICloudBuildCacheProvider, RushSession } from '@rushstack/rush-sdk';
4
- export interface IAmazonS3BuildCacheProviderOptions {
5
- s3Bucket: string;
4
+ /**
5
+ * @public
6
+ */
7
+ export interface IAmazonS3BuildCacheProviderOptionsBase {
6
8
  s3Region: string;
7
- s3Prefix?: string;
9
+ s3Prefix: string | undefined;
8
10
  isCacheWriteAllowed: boolean;
9
11
  }
12
+ /**
13
+ * Advanced options where user has the specify the full http endpoint
14
+ * @public
15
+ */
16
+ export interface IAmazonS3BuildCacheProviderOptionsAdvanced extends IAmazonS3BuildCacheProviderOptionsBase {
17
+ s3Endpoint: string;
18
+ }
19
+ /**
20
+ * Simple options where user only provides the bucket and the endpoint is automatically built
21
+ * @public
22
+ */
23
+ export interface IAmazonS3BuildCacheProviderOptionsSimple extends IAmazonS3BuildCacheProviderOptionsBase {
24
+ s3Bucket: string;
25
+ }
10
26
  export declare class AmazonS3BuildCacheProvider implements ICloudBuildCacheProvider {
11
27
  private readonly _options;
12
28
  private readonly _s3Prefix;
@@ -16,7 +32,8 @@ export declare class AmazonS3BuildCacheProvider implements ICloudBuildCacheProvi
16
32
  private _rushSession;
17
33
  get isCacheWriteAllowed(): boolean;
18
34
  private __s3Client;
19
- constructor(options: IAmazonS3BuildCacheProviderOptions, rushSession: RushSession);
35
+ constructor(options: IAmazonS3BuildCacheProviderOptionsSimple | IAmazonS3BuildCacheProviderOptionsAdvanced, rushSession: RushSession);
36
+ private get _s3Endpoint();
20
37
  private get _credentialCacheId();
21
38
  private _getS3ClientAsync;
22
39
  tryGetCacheEntryBufferByIdAsync(terminal: ITerminal, cacheId: string): Promise<Buffer | undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"AmazonS3BuildCacheProvider.d.ts","sourceRoot":"","sources":["../src/AmazonS3BuildCacheProvider.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EACL,wBAAwB,EAGxB,WAAW,EAIZ,MAAM,qBAAqB,CAAC;AAK7B,MAAM,WAAW,kCAAkC;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED,qBAAa,0BAA2B,YAAW,wBAAwB;IACzE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAqB;IAC5D,OAAO,CAAC,QAAQ,CAAC,mCAAmC,CAAU;IAC9D,OAAO,CAAC,mBAAmB,CAAqB;IAChD,OAAO,CAAC,YAAY,CAAc;IAElC,IAAW,mBAAmB,IAAI,OAAO,CAExC;IAED,OAAO,CAAC,UAAU,CAA6B;gBAE5B,OAAO,EAAE,kCAAkC,EAAE,WAAW,EAAE,WAAW;IAQxF,OAAO,KAAK,kBAAkB,GAY7B;YAEa,iBAAiB;IA2ClB,+BAA+B,CAC1C,QAAQ,EAAE,SAAS,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAUjB,2BAA2B,CACtC,QAAQ,EAAE,SAAS,EACnB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,OAAO,CAAC;IAgBN,2BAA2B,CAAC,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYnF,sCAAsC,CAAC,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAS1E,4BAA4B,CAAC,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAW9E"}
1
+ {"version":3,"file":"AmazonS3BuildCacheProvider.d.ts","sourceRoot":"","sources":["../src/AmazonS3BuildCacheProvider.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EACL,wBAAwB,EAGxB,WAAW,EAIZ,MAAM,qBAAqB,CAAC;AAK7B;;GAEG;AACH,MAAM,WAAW,sCAAsC;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,0CAA2C,SAAQ,sCAAsC;IACxG,UAAU,EAAE,MAAM,CAAC;CACpB;AACD;;;GAGG;AACH,MAAM,WAAW,wCAAyC,SAAQ,sCAAsC;IACtG,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,qBAAa,0BAA2B,YAAW,wBAAwB;IACzE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAEsB;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAqB;IAC5D,OAAO,CAAC,QAAQ,CAAC,mCAAmC,CAAU;IAC9D,OAAO,CAAC,mBAAmB,CAAqB;IAChD,OAAO,CAAC,YAAY,CAAc;IAElC,IAAW,mBAAmB,IAAI,OAAO,CAExC;IAED,OAAO,CAAC,UAAU,CAA6B;gBAG7C,OAAO,EAAE,wCAAwC,GAAG,0CAA0C,EAC9F,WAAW,EAAE,WAAW;IAS1B,OAAO,KAAK,WAAW,GActB;IAED,OAAO,KAAK,kBAAkB,GAY7B;YAEa,iBAAiB;IAoDlB,+BAA+B,CAC1C,QAAQ,EAAE,SAAS,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAUjB,2BAA2B,CACtC,QAAQ,EAAE,SAAS,EACnB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,OAAO,CAAC;IAkBN,2BAA2B,CAAC,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYnF,sCAAsC,CAAC,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAS1E,4BAA4B,CAAC,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAW9E"}
@@ -6,6 +6,7 @@ exports.AmazonS3BuildCacheProvider = void 0;
6
6
  const rush_sdk_1 = require("@rushstack/rush-sdk");
7
7
  const AmazonS3Client_1 = require("./AmazonS3Client");
8
8
  const WebClient_1 = require("./WebClient");
9
+ const DEFAULT_S3_REGION = 'us-east-1';
9
10
  class AmazonS3BuildCacheProvider {
10
11
  constructor(options, rushSession) {
11
12
  this._rushSession = rushSession;
@@ -18,9 +19,24 @@ class AmazonS3BuildCacheProvider {
18
19
  var _a;
19
20
  return (_a = rush_sdk_1.EnvironmentConfiguration.buildCacheWriteAllowed) !== null && _a !== void 0 ? _a : this._isCacheWriteAllowedByConfiguration;
20
21
  }
22
+ get _s3Endpoint() {
23
+ const options = this._options;
24
+ if ('s3Bucket' in options) {
25
+ // options: IAmazonS3BuildCacheProviderOptionsSimple
26
+ const bucket = options.s3Bucket;
27
+ if (options.s3Region === DEFAULT_S3_REGION) {
28
+ return `https://${bucket}.s3.amazonaws.com`;
29
+ }
30
+ else {
31
+ return `https://${bucket}.s3-${options.s3Region}.amazonaws.com`;
32
+ }
33
+ }
34
+ // options: IAmazonS3BuildCacheProviderOptionsAdvanced
35
+ return options.s3Endpoint;
36
+ }
21
37
  get _credentialCacheId() {
22
38
  if (!this.__credentialCacheId) {
23
- const cacheIdParts = ['aws-s3', this._options.s3Region, this._options.s3Bucket];
39
+ const cacheIdParts = ['aws-s3', this._options.s3Region, this._s3Endpoint];
24
40
  if (this._isCacheWriteAllowedByConfiguration) {
25
41
  cacheIdParts.push('cacheWriteAllowed');
26
42
  }
@@ -28,7 +44,7 @@ class AmazonS3BuildCacheProvider {
28
44
  }
29
45
  return this.__credentialCacheId;
30
46
  }
31
- async _getS3ClientAsync() {
47
+ async _getS3ClientAsync(terminal) {
32
48
  var _a;
33
49
  if (!this.__s3Client) {
34
50
  let credentials = AmazonS3Client_1.AmazonS3Client.tryDeserializeCredentials(this._environmentCredential);
@@ -56,13 +72,15 @@ class AmazonS3BuildCacheProvider {
56
72
  `${rush_sdk_1.EnvironmentVariableNames.RUSH_BUILD_CACHE_CREDENTIAL} environment variable`);
57
73
  }
58
74
  }
59
- this.__s3Client = new AmazonS3Client_1.AmazonS3Client(credentials, this._options, new WebClient_1.WebClient());
75
+ this.__s3Client = new AmazonS3Client_1.AmazonS3Client(credentials, Object.assign(Object.assign({}, this._options), {
76
+ // advanced options
77
+ s3Endpoint: this._s3Endpoint }), new WebClient_1.WebClient(), terminal);
60
78
  }
61
79
  return this.__s3Client;
62
80
  }
63
81
  async tryGetCacheEntryBufferByIdAsync(terminal, cacheId) {
64
82
  try {
65
- const client = await this._getS3ClientAsync();
83
+ const client = await this._getS3ClientAsync(terminal);
66
84
  return await client.getObjectAsync(this._s3Prefix ? `${this._s3Prefix}/${cacheId}` : cacheId);
67
85
  }
68
86
  catch (e) {
@@ -75,8 +93,9 @@ class AmazonS3BuildCacheProvider {
75
93
  terminal.writeErrorLine('Writing to S3 cache is not allowed in the current configuration.');
76
94
  return false;
77
95
  }
96
+ terminal.writeDebugLine('Uploading object with cacheId: ', cacheId);
78
97
  try {
79
- const client = await this._getS3ClientAsync();
98
+ const client = await this._getS3ClientAsync(terminal);
80
99
  await client.uploadObjectAsync(this._s3Prefix ? `${this._s3Prefix}/${cacheId}` : cacheId, objectBuffer);
81
100
  return true;
82
101
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AmazonS3BuildCacheProvider.js","sourceRoot":"","sources":["../src/AmazonS3BuildCacheProvider.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { ITerminal } from '@rushstack/node-core-library';\nimport {\n ICloudBuildCacheProvider,\n ICredentialCacheEntry,\n CredentialCache,\n RushSession,\n RushConstants,\n EnvironmentVariableNames,\n EnvironmentConfiguration\n} from '@rushstack/rush-sdk';\n\nimport { AmazonS3Client, IAmazonS3Credentials } from './AmazonS3Client';\nimport { WebClient } from './WebClient';\n\nexport interface IAmazonS3BuildCacheProviderOptions {\n s3Bucket: string;\n s3Region: string;\n s3Prefix?: string;\n isCacheWriteAllowed: boolean;\n}\n\nexport class AmazonS3BuildCacheProvider implements ICloudBuildCacheProvider {\n private readonly _options: IAmazonS3BuildCacheProviderOptions;\n private readonly _s3Prefix: string | undefined;\n private readonly _environmentCredential: string | undefined;\n private readonly _isCacheWriteAllowedByConfiguration: boolean;\n private __credentialCacheId: string | undefined;\n private _rushSession: RushSession;\n\n public get isCacheWriteAllowed(): boolean {\n return EnvironmentConfiguration.buildCacheWriteAllowed ?? this._isCacheWriteAllowedByConfiguration;\n }\n\n private __s3Client: AmazonS3Client | undefined;\n\n public constructor(options: IAmazonS3BuildCacheProviderOptions, rushSession: RushSession) {\n this._rushSession = rushSession;\n this._options = options;\n this._s3Prefix = options.s3Prefix;\n this._environmentCredential = EnvironmentConfiguration.buildCacheCredential;\n this._isCacheWriteAllowedByConfiguration = options.isCacheWriteAllowed;\n }\n\n private get _credentialCacheId(): string {\n if (!this.__credentialCacheId) {\n const cacheIdParts: string[] = ['aws-s3', this._options.s3Region, this._options.s3Bucket];\n\n if (this._isCacheWriteAllowedByConfiguration) {\n cacheIdParts.push('cacheWriteAllowed');\n }\n\n this.__credentialCacheId = cacheIdParts.join('|');\n }\n\n return this.__credentialCacheId;\n }\n\n private async _getS3ClientAsync(): Promise<AmazonS3Client> {\n if (!this.__s3Client) {\n let credentials: IAmazonS3Credentials | undefined = AmazonS3Client.tryDeserializeCredentials(\n this._environmentCredential\n );\n\n if (!credentials) {\n let cacheEntry: ICredentialCacheEntry | undefined;\n await CredentialCache.usingAsync(\n {\n supportEditing: false\n },\n (credentialsCache: CredentialCache) => {\n cacheEntry = credentialsCache.tryGetCacheEntry(this._credentialCacheId);\n }\n );\n\n if (cacheEntry) {\n const expirationTime: number | undefined = cacheEntry.expires?.getTime();\n if (expirationTime && expirationTime < Date.now()) {\n throw new Error(\n 'Cached Amazon S3 credentials have expired. ' +\n `Update the credentials by running \"rush ${RushConstants.updateCloudCredentialsCommandName}\".`\n );\n } else {\n credentials = AmazonS3Client.tryDeserializeCredentials(cacheEntry?.credential);\n }\n } else if (this._isCacheWriteAllowedByConfiguration) {\n throw new Error(\n \"An Amazon S3 credential hasn't been provided, or has expired. \" +\n `Update the credentials by running \"rush ${RushConstants.updateCloudCredentialsCommandName}\", ` +\n `or provide an <AccessKeyId>:<SecretAccessKey> pair in the ` +\n `${EnvironmentVariableNames.RUSH_BUILD_CACHE_CREDENTIAL} environment variable`\n );\n }\n }\n\n this.__s3Client = new AmazonS3Client(credentials, this._options, new WebClient());\n }\n\n return this.__s3Client;\n }\n\n public async tryGetCacheEntryBufferByIdAsync(\n terminal: ITerminal,\n cacheId: string\n ): Promise<Buffer | undefined> {\n try {\n const client: AmazonS3Client = await this._getS3ClientAsync();\n return await client.getObjectAsync(this._s3Prefix ? `${this._s3Prefix}/${cacheId}` : cacheId);\n } catch (e) {\n terminal.writeWarningLine(`Error getting cache entry from S3: ${e}`);\n return undefined;\n }\n }\n\n public async trySetCacheEntryBufferAsync(\n terminal: ITerminal,\n cacheId: string,\n objectBuffer: Buffer\n ): Promise<boolean> {\n if (!this.isCacheWriteAllowed) {\n terminal.writeErrorLine('Writing to S3 cache is not allowed in the current configuration.');\n return false;\n }\n\n try {\n const client: AmazonS3Client = await this._getS3ClientAsync();\n await client.uploadObjectAsync(this._s3Prefix ? `${this._s3Prefix}/${cacheId}` : cacheId, objectBuffer);\n return true;\n } catch (e) {\n terminal.writeWarningLine(`Error uploading cache entry to S3: ${e}`);\n return false;\n }\n }\n\n public async updateCachedCredentialAsync(terminal: ITerminal, credential: string): Promise<void> {\n await CredentialCache.usingAsync(\n {\n supportEditing: true\n },\n async (credentialsCache: CredentialCache) => {\n credentialsCache.setCacheEntry(this._credentialCacheId, credential);\n await credentialsCache.saveIfModifiedAsync();\n }\n );\n }\n\n public async updateCachedCredentialInteractiveAsync(terminal: ITerminal): Promise<void> {\n throw new Error(\n 'The interactive cloud credentials flow is not supported for Amazon S3.\\n' +\n 'Provide your credentials to rush using the --credential flag instead. Credentials must be ' +\n 'in the form of <ACCESS KEY ID>:<SECRET ACCESS KEY> or ' +\n '<ACCESS KEY ID>:<SECRET ACCESS KEY>:<SESSION TOKEN>.'\n );\n }\n\n public async deleteCachedCredentialsAsync(terminal: ITerminal): Promise<void> {\n await CredentialCache.usingAsync(\n {\n supportEditing: true\n },\n async (credentialsCache: CredentialCache) => {\n credentialsCache.deleteCacheEntry(this._credentialCacheId);\n await credentialsCache.saveIfModifiedAsync();\n }\n );\n }\n}\n"]}
1
+ {"version":3,"file":"AmazonS3BuildCacheProvider.js","sourceRoot":"","sources":["../src/AmazonS3BuildCacheProvider.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { ITerminal } from '@rushstack/node-core-library';\nimport {\n ICloudBuildCacheProvider,\n ICredentialCacheEntry,\n CredentialCache,\n RushSession,\n RushConstants,\n EnvironmentVariableNames,\n EnvironmentConfiguration\n} from '@rushstack/rush-sdk';\n\nimport { AmazonS3Client, IAmazonS3Credentials } from './AmazonS3Client';\nimport { WebClient } from './WebClient';\n\n/**\n * @public\n */\nexport interface IAmazonS3BuildCacheProviderOptionsBase {\n s3Region: string;\n s3Prefix: string | undefined;\n isCacheWriteAllowed: boolean;\n}\n\n/**\n * Advanced options where user has the specify the full http endpoint\n * @public\n */\nexport interface IAmazonS3BuildCacheProviderOptionsAdvanced extends IAmazonS3BuildCacheProviderOptionsBase {\n s3Endpoint: string;\n}\n/**\n * Simple options where user only provides the bucket and the endpoint is automatically built\n * @public\n */\nexport interface IAmazonS3BuildCacheProviderOptionsSimple extends IAmazonS3BuildCacheProviderOptionsBase {\n s3Bucket: string;\n}\n\nconst DEFAULT_S3_REGION: 'us-east-1' = 'us-east-1';\nexport class AmazonS3BuildCacheProvider implements ICloudBuildCacheProvider {\n private readonly _options:\n | IAmazonS3BuildCacheProviderOptionsSimple\n | IAmazonS3BuildCacheProviderOptionsAdvanced;\n private readonly _s3Prefix: string | undefined;\n private readonly _environmentCredential: string | undefined;\n private readonly _isCacheWriteAllowedByConfiguration: boolean;\n private __credentialCacheId: string | undefined;\n private _rushSession: RushSession;\n\n public get isCacheWriteAllowed(): boolean {\n return EnvironmentConfiguration.buildCacheWriteAllowed ?? this._isCacheWriteAllowedByConfiguration;\n }\n\n private __s3Client: AmazonS3Client | undefined;\n\n public constructor(\n options: IAmazonS3BuildCacheProviderOptionsSimple | IAmazonS3BuildCacheProviderOptionsAdvanced,\n rushSession: RushSession\n ) {\n this._rushSession = rushSession;\n this._options = options;\n this._s3Prefix = options.s3Prefix;\n this._environmentCredential = EnvironmentConfiguration.buildCacheCredential;\n this._isCacheWriteAllowedByConfiguration = options.isCacheWriteAllowed;\n }\n\n private get _s3Endpoint(): string {\n const options: IAmazonS3BuildCacheProviderOptionsSimple | IAmazonS3BuildCacheProviderOptionsAdvanced =\n this._options;\n if ('s3Bucket' in options) {\n // options: IAmazonS3BuildCacheProviderOptionsSimple\n const bucket: string = options.s3Bucket;\n if (options.s3Region === DEFAULT_S3_REGION) {\n return `https://${bucket}.s3.amazonaws.com`;\n } else {\n return `https://${bucket}.s3-${options.s3Region}.amazonaws.com`;\n }\n }\n // options: IAmazonS3BuildCacheProviderOptionsAdvanced\n return options.s3Endpoint;\n }\n\n private get _credentialCacheId(): string {\n if (!this.__credentialCacheId) {\n const cacheIdParts: string[] = ['aws-s3', this._options.s3Region, this._s3Endpoint];\n\n if (this._isCacheWriteAllowedByConfiguration) {\n cacheIdParts.push('cacheWriteAllowed');\n }\n\n this.__credentialCacheId = cacheIdParts.join('|');\n }\n\n return this.__credentialCacheId;\n }\n\n private async _getS3ClientAsync(terminal: ITerminal): Promise<AmazonS3Client> {\n if (!this.__s3Client) {\n let credentials: IAmazonS3Credentials | undefined = AmazonS3Client.tryDeserializeCredentials(\n this._environmentCredential\n );\n\n if (!credentials) {\n let cacheEntry: ICredentialCacheEntry | undefined;\n await CredentialCache.usingAsync(\n {\n supportEditing: false\n },\n (credentialsCache: CredentialCache) => {\n cacheEntry = credentialsCache.tryGetCacheEntry(this._credentialCacheId);\n }\n );\n\n if (cacheEntry) {\n const expirationTime: number | undefined = cacheEntry.expires?.getTime();\n if (expirationTime && expirationTime < Date.now()) {\n throw new Error(\n 'Cached Amazon S3 credentials have expired. ' +\n `Update the credentials by running \"rush ${RushConstants.updateCloudCredentialsCommandName}\".`\n );\n } else {\n credentials = AmazonS3Client.tryDeserializeCredentials(cacheEntry?.credential);\n }\n } else if (this._isCacheWriteAllowedByConfiguration) {\n throw new Error(\n \"An Amazon S3 credential hasn't been provided, or has expired. \" +\n `Update the credentials by running \"rush ${RushConstants.updateCloudCredentialsCommandName}\", ` +\n `or provide an <AccessKeyId>:<SecretAccessKey> pair in the ` +\n `${EnvironmentVariableNames.RUSH_BUILD_CACHE_CREDENTIAL} environment variable`\n );\n }\n }\n\n this.__s3Client = new AmazonS3Client(\n credentials,\n {\n ...this._options,\n // advanced options\n s3Endpoint: this._s3Endpoint\n },\n new WebClient(),\n terminal\n );\n }\n\n return this.__s3Client;\n }\n\n public async tryGetCacheEntryBufferByIdAsync(\n terminal: ITerminal,\n cacheId: string\n ): Promise<Buffer | undefined> {\n try {\n const client: AmazonS3Client = await this._getS3ClientAsync(terminal);\n return await client.getObjectAsync(this._s3Prefix ? `${this._s3Prefix}/${cacheId}` : cacheId);\n } catch (e) {\n terminal.writeWarningLine(`Error getting cache entry from S3: ${e}`);\n return undefined;\n }\n }\n\n public async trySetCacheEntryBufferAsync(\n terminal: ITerminal,\n cacheId: string,\n objectBuffer: Buffer\n ): Promise<boolean> {\n if (!this.isCacheWriteAllowed) {\n terminal.writeErrorLine('Writing to S3 cache is not allowed in the current configuration.');\n return false;\n }\n\n terminal.writeDebugLine('Uploading object with cacheId: ', cacheId);\n\n try {\n const client: AmazonS3Client = await this._getS3ClientAsync(terminal);\n await client.uploadObjectAsync(this._s3Prefix ? `${this._s3Prefix}/${cacheId}` : cacheId, objectBuffer);\n return true;\n } catch (e) {\n terminal.writeWarningLine(`Error uploading cache entry to S3: ${e}`);\n return false;\n }\n }\n\n public async updateCachedCredentialAsync(terminal: ITerminal, credential: string): Promise<void> {\n await CredentialCache.usingAsync(\n {\n supportEditing: true\n },\n async (credentialsCache: CredentialCache) => {\n credentialsCache.setCacheEntry(this._credentialCacheId, credential);\n await credentialsCache.saveIfModifiedAsync();\n }\n );\n }\n\n public async updateCachedCredentialInteractiveAsync(terminal: ITerminal): Promise<void> {\n throw new Error(\n 'The interactive cloud credentials flow is not supported for Amazon S3.\\n' +\n 'Provide your credentials to rush using the --credential flag instead. Credentials must be ' +\n 'in the form of <ACCESS KEY ID>:<SECRET ACCESS KEY> or ' +\n '<ACCESS KEY ID>:<SECRET ACCESS KEY>:<SESSION TOKEN>.'\n );\n }\n\n public async deleteCachedCredentialsAsync(terminal: ITerminal): Promise<void> {\n await CredentialCache.usingAsync(\n {\n supportEditing: true\n },\n async (credentialsCache: CredentialCache) => {\n credentialsCache.deleteCacheEntry(this._credentialCacheId);\n await credentialsCache.saveIfModifiedAsync();\n }\n );\n }\n}\n"]}
@@ -1,31 +1,45 @@
1
1
  /// <reference types="node" />
2
- import { IAmazonS3BuildCacheProviderOptions } from './AmazonS3BuildCacheProvider';
2
+ import { ITerminal } from '@rushstack/node-core-library';
3
+ import { IAmazonS3BuildCacheProviderOptionsAdvanced } from './AmazonS3BuildCacheProvider';
3
4
  import { WebClient } from './WebClient';
5
+ /**
6
+ * Credentials for authorizing and signing requests to an Amazon S3 endpoint.
7
+ *
8
+ * @public
9
+ */
4
10
  export interface IAmazonS3Credentials {
5
11
  accessKeyId: string;
6
12
  secretAccessKey: string;
7
13
  sessionToken: string | undefined;
8
14
  }
15
+ /**
16
+ * A helper for reading and updating objects on Amazon S3
17
+ *
18
+ * @public
19
+ */
9
20
  export declare class AmazonS3Client {
10
21
  private readonly _credentials;
11
- private readonly _s3Bucket;
22
+ private readonly _s3Endpoint;
12
23
  private readonly _s3Region;
13
24
  private readonly _webClient;
14
- constructor(credentials: IAmazonS3Credentials | undefined, options: IAmazonS3BuildCacheProviderOptions, webClient: WebClient);
25
+ private readonly _terminal;
26
+ constructor(credentials: IAmazonS3Credentials | undefined, options: IAmazonS3BuildCacheProviderOptionsAdvanced, webClient: WebClient, terminal: ITerminal);
27
+ static UriEncode(input: string): string;
15
28
  static tryDeserializeCredentials(credentialString: string | undefined): IAmazonS3Credentials | undefined;
16
29
  getObjectAsync(objectName: string): Promise<Buffer | undefined>;
17
30
  uploadObjectAsync(objectName: string, objectBuffer: Buffer): Promise<void>;
31
+ private _writeDebugLine;
18
32
  private _makeRequestAsync;
19
33
  _getSha256Hmac(key: string | Buffer, data: string): Buffer;
20
34
  _getSha256Hmac(key: string | Buffer, data: string, encoding: 'hex'): string;
21
35
  private _getSha256;
22
36
  private _getIsoDateString;
37
+ private _safeReadResponseText;
23
38
  private _throwS3Error;
24
- private _getHost;
25
39
  /**
26
- * Validates a S3 bucket name.
27
- * {@link https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-s3-bucket-naming-requirements.html}
40
+ * Validates a S3 endpoint which is http(s):// + hostname + port. Hostname validated according to RFC 1123
41
+ * {@link https://docs.aws.amazon.com/general/latest/gr/s3.html}
28
42
  */
29
- private _validateBucketName;
43
+ private _validateEndpoint;
30
44
  }
31
45
  //# sourceMappingURL=AmazonS3Client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AmazonS3Client.d.ts","sourceRoot":"","sources":["../src/AmazonS3Client.ts"],"names":[],"mappings":";AAMA,OAAO,EAAE,kCAAkC,EAAE,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAsC,SAAS,EAAE,MAAM,aAAa,CAAC;AAS5E,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAOD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;IAChE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;gBAGrC,WAAW,EAAE,oBAAoB,GAAG,SAAS,EAC7C,OAAO,EAAE,kCAAkC,EAC3C,SAAS,EAAE,SAAS;WAYR,yBAAyB,CACrC,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,oBAAoB,GAAG,SAAS;IAiBtB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAa/D,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAWzE,iBAAiB;IAkGxB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAC1D,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,GAAG,MAAM;IAWlF,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,QAAQ;IAQhB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;CAsC5B"}
1
+ {"version":3,"file":"AmazonS3Client.d.ts","sourceRoot":"","sources":["../src/AmazonS3Client.ts"],"names":[],"mappings":";AAGA,OAAO,EAA8B,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAIrF,OAAO,EAAE,0CAA0C,EAAE,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAsC,SAAS,EAAE,MAAM,aAAa,CAAC;AAO5E;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAUD;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;IAChE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IAEvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;gBAGpC,WAAW,EAAE,oBAAoB,GAAG,SAAS,EAC7C,OAAO,EAAE,0CAA0C,EACnD,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,SAAS;WAeP,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;WAiBhC,yBAAyB,CACrC,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,oBAAoB,GAAG,SAAS;IAiBtB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAc/D,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvF,OAAO,CAAC,eAAe;YAST,iBAAiB;IA6HxB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAC1D,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,GAAG,MAAM;IAWlF,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,iBAAiB;YAaX,qBAAqB;IASnC,OAAO,CAAC,aAAa;IAQrB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;CAsD1B"}
@@ -22,21 +22,49 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  };
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.AmazonS3Client = void 0;
25
+ const node_core_library_1 = require("@rushstack/node-core-library");
25
26
  const crypto = __importStar(require("crypto"));
26
27
  const fetch = __importStar(require("node-fetch"));
27
28
  const CONTENT_HASH_HEADER_NAME = 'x-amz-content-sha256';
28
29
  const DATE_HEADER_NAME = 'x-amz-date';
29
30
  const HOST_HEADER_NAME = 'host';
30
31
  const SECURITY_TOKEN_HEADER_NAME = 'x-amz-security-token';
31
- const DEFAULT_S3_REGION = 'us-east-1';
32
+ const protocolRegex = /^https?:\/\//;
33
+ const portRegex = /:(\d{1,5})$/;
34
+ /**
35
+ * A helper for reading and updating objects on Amazon S3
36
+ *
37
+ * @public
38
+ */
32
39
  class AmazonS3Client {
33
- constructor(credentials, options, webClient) {
40
+ constructor(credentials, options, webClient, terminal) {
34
41
  this._credentials = credentials;
35
- this._validateBucketName(options.s3Bucket);
36
- this._s3Bucket = options.s3Bucket;
42
+ this._terminal = terminal;
43
+ this._validateEndpoint(options.s3Endpoint);
44
+ this._s3Endpoint = options.s3Endpoint;
37
45
  this._s3Region = options.s3Region;
38
46
  this._webClient = webClient;
39
47
  }
48
+ // https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html#create-signature-presign-entire-payload
49
+ // We want to keep all slashes non encoded
50
+ static UriEncode(input) {
51
+ let output = '';
52
+ for (let i = 0; i < input.length; i += 1) {
53
+ const ch = input[i];
54
+ if (ch.match(/[A-Za-z0-9~._-]|\//)) {
55
+ output += ch;
56
+ }
57
+ else {
58
+ if (ch === ' ') {
59
+ output += '%20';
60
+ }
61
+ else {
62
+ output += `%${ch.charCodeAt(0).toString(16).toUpperCase()}`;
63
+ }
64
+ }
65
+ }
66
+ return output;
67
+ }
40
68
  static tryDeserializeCredentials(credentialString) {
41
69
  if (!credentialString) {
42
70
  return undefined;
@@ -52,6 +80,7 @@ class AmazonS3Client {
52
80
  };
53
81
  }
54
82
  async getObjectAsync(objectName) {
83
+ this._writeDebugLine('Reading object from S3');
55
84
  const response = await this._makeRequestAsync('GET', objectName);
56
85
  if (response.ok) {
57
86
  return await response.buffer();
@@ -63,7 +92,7 @@ class AmazonS3Client {
63
92
  return undefined;
64
93
  }
65
94
  else {
66
- this._throwS3Error(response);
95
+ this._throwS3Error(response, await this._safeReadResponseText(response));
67
96
  }
68
97
  }
69
98
  async uploadObjectAsync(objectName, objectBuffer) {
@@ -72,19 +101,30 @@ class AmazonS3Client {
72
101
  }
73
102
  const response = await this._makeRequestAsync('PUT', objectName, objectBuffer);
74
103
  if (!response.ok) {
75
- this._throwS3Error(response);
104
+ this._throwS3Error(response, await this._safeReadResponseText(response));
105
+ }
106
+ }
107
+ _writeDebugLine(...messageParts) {
108
+ // if the terminal has been closed then don't bother sending a debug message
109
+ try {
110
+ this._terminal.writeDebugLine(...messageParts);
111
+ }
112
+ catch (err) {
113
+ //
76
114
  }
77
115
  }
78
116
  async _makeRequestAsync(verb, objectName, body) {
79
117
  const isoDateString = this._getIsoDateString();
80
118
  const bodyHash = this._getSha256(body);
81
- const host = this._getHost();
82
119
  const headers = new fetch.Headers();
83
120
  headers.set(DATE_HEADER_NAME, isoDateString.dateTime);
84
121
  headers.set(CONTENT_HASH_HEADER_NAME, bodyHash);
122
+ // the host can be e.g. https://s3.aws.com or http://localhost:9000
123
+ const host = this._s3Endpoint.replace(protocolRegex, '');
124
+ const canonicalUri = AmazonS3Client.UriEncode(`/${objectName}`);
125
+ this._writeDebugLine(node_core_library_1.Colors.bold('Canonical URI: '), canonicalUri);
85
126
  if (this._credentials) {
86
127
  // Compute the authorization header. See https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
87
- const signedHeaderNames = [HOST_HEADER_NAME, CONTENT_HASH_HEADER_NAME, DATE_HEADER_NAME];
88
128
  const canonicalHeaders = [
89
129
  `${HOST_HEADER_NAME}:${host}`,
90
130
  `${CONTENT_HASH_HEADER_NAME}:${bodyHash}`,
@@ -92,10 +132,27 @@ class AmazonS3Client {
92
132
  ];
93
133
  // Handle signing with temporary credentials (via sts:assume-role)
94
134
  if (this._credentials.sessionToken) {
95
- signedHeaderNames.push(SECURITY_TOKEN_HEADER_NAME);
96
135
  canonicalHeaders.push(`${SECURITY_TOKEN_HEADER_NAME}:${this._credentials.sessionToken}`);
97
136
  }
98
- const signedHeaderNamesString = signedHeaderNames.join(';');
137
+ // the canonical headers must be sorted by header name
138
+ canonicalHeaders.sort((aHeader, bHeader) => {
139
+ const aHeaderName = aHeader.split(':')[0];
140
+ const bHeaderName = bHeader.split(':')[0];
141
+ if (aHeaderName < bHeaderName) {
142
+ return -1;
143
+ }
144
+ if (aHeaderName > bHeaderName) {
145
+ return 1;
146
+ }
147
+ return 0;
148
+ });
149
+ // the singed header names are derived from the canonicalHeaders
150
+ const signedHeaderNamesString = canonicalHeaders
151
+ .map((header) => {
152
+ const headerName = header.split(':')[0];
153
+ return headerName;
154
+ })
155
+ .join(';');
99
156
  // The canonical request looks like this:
100
157
  // GET
101
158
  // /test.txt
@@ -109,7 +166,7 @@ class AmazonS3Client {
109
166
  // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
110
167
  const canonicalRequest = [
111
168
  verb,
112
- `/${objectName}`,
169
+ canonicalUri,
113
170
  '',
114
171
  ...canonicalHeaders,
115
172
  '',
@@ -148,7 +205,14 @@ class AmazonS3Client {
148
205
  if (verb === 'PUT') {
149
206
  webFetchOptions.body = body;
150
207
  }
151
- const response = await this._webClient.fetchAsync(`https://${host}/${objectName}`, webFetchOptions);
208
+ const url = `${this._s3Endpoint}${canonicalUri}`;
209
+ this._writeDebugLine(node_core_library_1.Colors.bold(node_core_library_1.Colors.underline('Sending request to S3')));
210
+ this._writeDebugLine(node_core_library_1.Colors.bold('HOST: '), url);
211
+ this._writeDebugLine(node_core_library_1.Colors.bold('Headers: '));
212
+ headers.forEach((value, name) => {
213
+ this._writeDebugLine(node_core_library_1.Colors.cyan(`\t${name}: ${value}`));
214
+ });
215
+ const response = await this._webClient.fetchAsync(url, webFetchOptions);
152
216
  return response;
153
217
  }
154
218
  _getSha256Hmac(key, data, encoding) {
@@ -183,42 +247,58 @@ class AmazonS3Client {
183
247
  date: dateString.substring(0, 8)
184
248
  };
185
249
  }
186
- _throwS3Error(response) {
187
- throw new Error(`Amazon S3 responded with status code ${response.status} (${response.statusText})`);
188
- }
189
- _getHost() {
190
- if (this._s3Region === DEFAULT_S3_REGION) {
191
- return `${this._s3Bucket}.s3.amazonaws.com`;
250
+ async _safeReadResponseText(response) {
251
+ try {
252
+ return await response.text();
192
253
  }
193
- else {
194
- return `${this._s3Bucket}.s3-${this._s3Region}.amazonaws.com`;
254
+ catch (err) {
255
+ // ignore the error
195
256
  }
257
+ return undefined;
258
+ }
259
+ _throwS3Error(response, text) {
260
+ throw new Error(`Amazon S3 responded with status code ${response.status} (${response.statusText})${text ? `\n${text}` : ''}`);
196
261
  }
197
262
  /**
198
- * Validates a S3 bucket name.
199
- * {@link https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-s3-bucket-naming-requirements.html}
263
+ * Validates a S3 endpoint which is http(s):// + hostname + port. Hostname validated according to RFC 1123
264
+ * {@link https://docs.aws.amazon.com/general/latest/gr/s3.html}
200
265
  */
201
- _validateBucketName(s3BucketName) {
202
- if (!s3BucketName) {
203
- throw new Error('A S3 bucket name must be provided');
266
+ _validateEndpoint(s3Endpoint) {
267
+ let host = s3Endpoint;
268
+ if (!s3Endpoint) {
269
+ throw new Error('A S3 endpoint must be provided');
270
+ }
271
+ if (!s3Endpoint.match(protocolRegex)) {
272
+ throw new Error('The S3 endpoint must start with https:// or http://');
204
273
  }
205
- if (!s3BucketName.match(/^[a-z\d-.]{3,63}$/)) {
206
- throw new Error(`The bucket name "${s3BucketName}" is invalid. A S3 bucket name must only contain lowercase ` +
207
- 'alphanumerical characters, dashes, and periods and must be between 3 and 63 characters long.');
274
+ host = host.replace(protocolRegex, '');
275
+ if (host.match(/\//)) {
276
+ throw new Error('The path should be omitted from the endpoint. Use s3Prefix to specify a path');
208
277
  }
209
- if (!s3BucketName.match(/^[a-z\d]/)) {
210
- throw new Error(`The bucket name "${s3BucketName}" is invalid. A S3 bucket name must start with a lowercase ` +
211
- 'alphanumerical character.');
278
+ const portMatch = s3Endpoint.match(portRegex);
279
+ if (portMatch) {
280
+ const port = Number(portMatch[1]);
281
+ if (Number.isNaN(port) || port > 65535) {
282
+ throw new Error(`Port: ${port} is an invalid port number`);
283
+ }
284
+ host = host.replace(portRegex, '');
212
285
  }
213
- if (s3BucketName.match(/-$/)) {
214
- throw new Error(`The bucket name "${s3BucketName}" is invalid. A S3 bucket name must not end in a dash.`);
286
+ if (host.endsWith('.')) {
287
+ host = host.slice(0, host.length - 1);
215
288
  }
216
- if (s3BucketName.match(/(\.\.)|(\.-)|(-\.)/)) {
217
- throw new Error(`The bucket name "${s3BucketName}" is invalid. A S3 bucket name must not have consecutive periods or ` +
218
- 'dashes adjacent to periods.');
289
+ if (host.length > 253) {
290
+ throw new Error('The S3 endpoint is too long. RFC 1123 specifies a hostname should be no longer than 253 characters.');
219
291
  }
220
- if (s3BucketName.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)) {
221
- throw new Error(`The bucket name "${s3BucketName}" is invalid. A S3 bucket name must not be formatted as an IP address.`);
292
+ const subDomains = host.split('.');
293
+ const subDomainRegex = /^[a-zA-Z0-9-]+$/;
294
+ const isValid = subDomains.every((subDomain) => {
295
+ return (subDomainRegex.test(subDomain) &&
296
+ subDomain.length < 64 &&
297
+ !subDomain.startsWith('-') &&
298
+ !subDomain.endsWith('-'));
299
+ });
300
+ if (!isValid) {
301
+ throw new Error('Invalid S3 endpoint. Some part of the hostname contains invalid characters or is too long');
222
302
  }
223
303
  }
224
304
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AmazonS3Client.js","sourceRoot":"","sources":["../src/AmazonS3Client.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport * as crypto from 'crypto';\nimport * as fetch from 'node-fetch';\n\nimport { IAmazonS3BuildCacheProviderOptions } from './AmazonS3BuildCacheProvider';\nimport { IGetFetchOptions, IPutFetchOptions, WebClient } from './WebClient';\n\nconst CONTENT_HASH_HEADER_NAME: 'x-amz-content-sha256' = 'x-amz-content-sha256';\nconst DATE_HEADER_NAME: 'x-amz-date' = 'x-amz-date';\nconst HOST_HEADER_NAME: 'host' = 'host';\nconst SECURITY_TOKEN_HEADER_NAME: 'x-amz-security-token' = 'x-amz-security-token';\n\nconst DEFAULT_S3_REGION: 'us-east-1' = 'us-east-1';\n\nexport interface IAmazonS3Credentials {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken: string | undefined;\n}\n\ninterface IIsoDateString {\n date: string;\n dateTime: string;\n}\n\nexport class AmazonS3Client {\n private readonly _credentials: IAmazonS3Credentials | undefined;\n private readonly _s3Bucket: string;\n private readonly _s3Region: string;\n\n private readonly _webClient: WebClient;\n\n public constructor(\n credentials: IAmazonS3Credentials | undefined,\n options: IAmazonS3BuildCacheProviderOptions,\n webClient: WebClient\n ) {\n this._credentials = credentials;\n\n this._validateBucketName(options.s3Bucket);\n\n this._s3Bucket = options.s3Bucket;\n this._s3Region = options.s3Region;\n\n this._webClient = webClient;\n }\n\n public static tryDeserializeCredentials(\n credentialString: string | undefined\n ): IAmazonS3Credentials | undefined {\n if (!credentialString) {\n return undefined;\n }\n\n const fields: string[] = credentialString.split(':');\n if (fields.length < 2 || fields.length > 3) {\n throw new Error('Amazon S3 credential is in an unexpected format.');\n }\n\n return {\n accessKeyId: fields[0],\n secretAccessKey: fields[1],\n sessionToken: fields[2]\n };\n }\n\n public async getObjectAsync(objectName: string): Promise<Buffer | undefined> {\n const response: fetch.Response = await this._makeRequestAsync('GET', objectName);\n if (response.ok) {\n return await response.buffer();\n } else if (response.status === 404) {\n return undefined;\n } else if (response.status === 403 && !this._credentials) {\n return undefined;\n } else {\n this._throwS3Error(response);\n }\n }\n\n public async uploadObjectAsync(objectName: string, objectBuffer: Buffer): Promise<void> {\n if (!this._credentials) {\n throw new Error('Credentials are required to upload objects to S3.');\n }\n\n const response: fetch.Response = await this._makeRequestAsync('PUT', objectName, objectBuffer);\n if (!response.ok) {\n this._throwS3Error(response);\n }\n }\n\n private async _makeRequestAsync(\n verb: 'GET' | 'PUT',\n objectName: string,\n body?: Buffer\n ): Promise<fetch.Response> {\n const isoDateString: IIsoDateString = this._getIsoDateString();\n const bodyHash: string = this._getSha256(body);\n const host: string = this._getHost();\n const headers: fetch.Headers = new fetch.Headers();\n headers.set(DATE_HEADER_NAME, isoDateString.dateTime);\n headers.set(CONTENT_HASH_HEADER_NAME, bodyHash);\n\n if (this._credentials) {\n // Compute the authorization header. See https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html\n const signedHeaderNames: string[] = [HOST_HEADER_NAME, CONTENT_HASH_HEADER_NAME, DATE_HEADER_NAME];\n const canonicalHeaders: string[] = [\n `${HOST_HEADER_NAME}:${host}`,\n `${CONTENT_HASH_HEADER_NAME}:${bodyHash}`,\n `${DATE_HEADER_NAME}:${isoDateString.dateTime}`\n ];\n\n // Handle signing with temporary credentials (via sts:assume-role)\n if (this._credentials.sessionToken) {\n signedHeaderNames.push(SECURITY_TOKEN_HEADER_NAME);\n canonicalHeaders.push(`${SECURITY_TOKEN_HEADER_NAME}:${this._credentials.sessionToken}`);\n }\n\n const signedHeaderNamesString: string = signedHeaderNames.join(';');\n\n // The canonical request looks like this:\n // GET\n // /test.txt\n //\n // host:examplebucket.s3.amazonaws.com\n // range:bytes=0-9\n // x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n // x-amz-date:20130524T000000Z\n //\n // host;range;x-amz-content-sha256;x-amz-date\n // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n const canonicalRequest: string = [\n verb,\n `/${objectName}`,\n '', // we don't use query strings for these requests\n ...canonicalHeaders,\n '',\n signedHeaderNamesString,\n bodyHash\n ].join('\\n');\n const canonicalRequestHash: string = this._getSha256(canonicalRequest);\n\n const scope: string = `${isoDateString.date}/${this._s3Region}/s3/aws4_request`;\n // The string to sign looks like this:\n // AWS4-HMAC-SHA256\n // 20130524T423589Z\n // 20130524/us-east-1/s3/aws4_request\n // 7344ae5b7ee6c3e7e6b0fe0640412a37625d1fbfff95c48bbb2dc43964946972\n const stringToSign: string = [\n 'AWS4-HMAC-SHA256',\n isoDateString.dateTime,\n scope,\n canonicalRequestHash\n ].join('\\n');\n\n const dateKey: Buffer = this._getSha256Hmac(\n `AWS4${this._credentials.secretAccessKey}`,\n isoDateString.date\n );\n const dateRegionKey: Buffer = this._getSha256Hmac(dateKey, this._s3Region);\n const dateRegionServiceKey: Buffer = this._getSha256Hmac(dateRegionKey, 's3');\n const signingKey: Buffer = this._getSha256Hmac(dateRegionServiceKey, 'aws4_request');\n const signature: string = this._getSha256Hmac(signingKey, stringToSign, 'hex');\n\n const authorizationHeader: string = `AWS4-HMAC-SHA256 Credential=${this._credentials.accessKeyId}/${scope},SignedHeaders=${signedHeaderNamesString},Signature=${signature}`;\n\n headers.set('Authorization', authorizationHeader);\n if (this._credentials.sessionToken) {\n // Handle signing with temporary credentials (via sts:assume-role)\n headers.set('X-Amz-Security-Token', this._credentials.sessionToken);\n }\n }\n\n const webFetchOptions: IGetFetchOptions | IPutFetchOptions = {\n verb,\n headers\n };\n if (verb === 'PUT') {\n (webFetchOptions as IPutFetchOptions).body = body;\n }\n\n const response: fetch.Response = await this._webClient.fetchAsync(\n `https://${host}/${objectName}`,\n webFetchOptions\n );\n\n return response;\n }\n\n public _getSha256Hmac(key: string | Buffer, data: string): Buffer;\n public _getSha256Hmac(key: string | Buffer, data: string, encoding: 'hex'): string;\n public _getSha256Hmac(key: string | Buffer, data: string, encoding?: 'hex'): Buffer | string {\n const hash: crypto.Hash = crypto.createHmac('sha256', key);\n hash.update(data);\n if (encoding) {\n return hash.digest(encoding);\n } else {\n return hash.digest();\n }\n }\n\n private _getSha256(data?: string | Buffer): string {\n if (data) {\n const hash: crypto.Hash = crypto.createHash('sha256');\n hash.update(data);\n return hash.digest('hex');\n } else {\n // This is the null SHA256 hash\n return 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';\n }\n }\n\n private _getIsoDateString(date: Date = new Date()): IIsoDateString {\n let dateString: string = date.toISOString();\n dateString = dateString.replace(/[-:]/g, ''); // Remove separator characters\n dateString = dateString.substring(0, 15); // Drop milliseconds\n\n // dateTime is an ISO8601 date. It looks like \"20130524T423589\"\n // date is an ISO date. It looks like \"20130524\"\n return {\n dateTime: `${dateString}Z`,\n date: dateString.substring(0, 8)\n };\n }\n\n private _throwS3Error(response: fetch.Response): never {\n throw new Error(`Amazon S3 responded with status code ${response.status} (${response.statusText})`);\n }\n\n private _getHost(): string {\n if (this._s3Region === DEFAULT_S3_REGION) {\n return `${this._s3Bucket}.s3.amazonaws.com`;\n } else {\n return `${this._s3Bucket}.s3-${this._s3Region}.amazonaws.com`;\n }\n }\n\n /**\n * Validates a S3 bucket name.\n * {@link https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-s3-bucket-naming-requirements.html}\n */\n private _validateBucketName(s3BucketName: string): void {\n if (!s3BucketName) {\n throw new Error('A S3 bucket name must be provided');\n }\n\n if (!s3BucketName.match(/^[a-z\\d-.]{3,63}$/)) {\n throw new Error(\n `The bucket name \"${s3BucketName}\" is invalid. A S3 bucket name must only contain lowercase ` +\n 'alphanumerical characters, dashes, and periods and must be between 3 and 63 characters long.'\n );\n }\n\n if (!s3BucketName.match(/^[a-z\\d]/)) {\n throw new Error(\n `The bucket name \"${s3BucketName}\" is invalid. A S3 bucket name must start with a lowercase ` +\n 'alphanumerical character.'\n );\n }\n\n if (s3BucketName.match(/-$/)) {\n throw new Error(\n `The bucket name \"${s3BucketName}\" is invalid. A S3 bucket name must not end in a dash.`\n );\n }\n\n if (s3BucketName.match(/(\\.\\.)|(\\.-)|(-\\.)/)) {\n throw new Error(\n `The bucket name \"${s3BucketName}\" is invalid. A S3 bucket name must not have consecutive periods or ` +\n 'dashes adjacent to periods.'\n );\n }\n\n if (s3BucketName.match(/^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/)) {\n throw new Error(\n `The bucket name \"${s3BucketName}\" is invalid. A S3 bucket name must not be formatted as an IP address.`\n );\n }\n }\n}\n"]}
1
+ {"version":3,"file":"AmazonS3Client.js","sourceRoot":"","sources":["../src/AmazonS3Client.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { Colors, IColorableSequence, ITerminal } from '@rushstack/node-core-library';\nimport * as crypto from 'crypto';\nimport * as fetch from 'node-fetch';\n\nimport { IAmazonS3BuildCacheProviderOptionsAdvanced } from './AmazonS3BuildCacheProvider';\nimport { IGetFetchOptions, IPutFetchOptions, WebClient } from './WebClient';\n\nconst CONTENT_HASH_HEADER_NAME: 'x-amz-content-sha256' = 'x-amz-content-sha256';\nconst DATE_HEADER_NAME: 'x-amz-date' = 'x-amz-date';\nconst HOST_HEADER_NAME: 'host' = 'host';\nconst SECURITY_TOKEN_HEADER_NAME: 'x-amz-security-token' = 'x-amz-security-token';\n\n/**\n * Credentials for authorizing and signing requests to an Amazon S3 endpoint.\n *\n * @public\n */\nexport interface IAmazonS3Credentials {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken: string | undefined;\n}\n\ninterface IIsoDateString {\n date: string;\n dateTime: string;\n}\n\nconst protocolRegex: RegExp = /^https?:\\/\\//;\nconst portRegex: RegExp = /:(\\d{1,5})$/;\n\n/**\n * A helper for reading and updating objects on Amazon S3\n *\n * @public\n */\nexport class AmazonS3Client {\n private readonly _credentials: IAmazonS3Credentials | undefined;\n private readonly _s3Endpoint: string;\n private readonly _s3Region: string;\n\n private readonly _webClient: WebClient;\n\n private readonly _terminal: ITerminal;\n\n public constructor(\n credentials: IAmazonS3Credentials | undefined,\n options: IAmazonS3BuildCacheProviderOptionsAdvanced,\n webClient: WebClient,\n terminal: ITerminal\n ) {\n this._credentials = credentials;\n this._terminal = terminal;\n\n this._validateEndpoint(options.s3Endpoint);\n\n this._s3Endpoint = options.s3Endpoint;\n this._s3Region = options.s3Region;\n\n this._webClient = webClient;\n }\n\n // https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html#create-signature-presign-entire-payload\n // We want to keep all slashes non encoded\n public static UriEncode(input: string): string {\n let output: string = '';\n for (let i: number = 0; i < input.length; i += 1) {\n const ch: string = input[i];\n if (ch.match(/[A-Za-z0-9~._-]|\\//)) {\n output += ch;\n } else {\n if (ch === ' ') {\n output += '%20';\n } else {\n output += `%${ch.charCodeAt(0).toString(16).toUpperCase()}`;\n }\n }\n }\n return output;\n }\n\n public static tryDeserializeCredentials(\n credentialString: string | undefined\n ): IAmazonS3Credentials | undefined {\n if (!credentialString) {\n return undefined;\n }\n\n const fields: string[] = credentialString.split(':');\n if (fields.length < 2 || fields.length > 3) {\n throw new Error('Amazon S3 credential is in an unexpected format.');\n }\n\n return {\n accessKeyId: fields[0],\n secretAccessKey: fields[1],\n sessionToken: fields[2]\n };\n }\n\n public async getObjectAsync(objectName: string): Promise<Buffer | undefined> {\n this._writeDebugLine('Reading object from S3');\n const response: fetch.Response = await this._makeRequestAsync('GET', objectName);\n if (response.ok) {\n return await response.buffer();\n } else if (response.status === 404) {\n return undefined;\n } else if (response.status === 403 && !this._credentials) {\n return undefined;\n } else {\n this._throwS3Error(response, await this._safeReadResponseText(response));\n }\n }\n\n public async uploadObjectAsync(objectName: string, objectBuffer: Buffer): Promise<void> {\n if (!this._credentials) {\n throw new Error('Credentials are required to upload objects to S3.');\n }\n\n const response: fetch.Response = await this._makeRequestAsync('PUT', objectName, objectBuffer);\n if (!response.ok) {\n this._throwS3Error(response, await this._safeReadResponseText(response));\n }\n }\n\n private _writeDebugLine(...messageParts: (string | IColorableSequence)[]): void {\n // if the terminal has been closed then don't bother sending a debug message\n try {\n this._terminal.writeDebugLine(...messageParts);\n } catch (err) {\n //\n }\n }\n\n private async _makeRequestAsync(\n verb: 'GET' | 'PUT',\n objectName: string,\n body?: Buffer\n ): Promise<fetch.Response> {\n const isoDateString: IIsoDateString = this._getIsoDateString();\n const bodyHash: string = this._getSha256(body);\n const headers: fetch.Headers = new fetch.Headers();\n headers.set(DATE_HEADER_NAME, isoDateString.dateTime);\n headers.set(CONTENT_HASH_HEADER_NAME, bodyHash);\n\n // the host can be e.g. https://s3.aws.com or http://localhost:9000\n const host: string = this._s3Endpoint.replace(protocolRegex, '');\n const canonicalUri: string = AmazonS3Client.UriEncode(`/${objectName}`);\n this._writeDebugLine(Colors.bold('Canonical URI: '), canonicalUri);\n\n if (this._credentials) {\n // Compute the authorization header. See https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html\n const canonicalHeaders: string[] = [\n `${HOST_HEADER_NAME}:${host}`,\n `${CONTENT_HASH_HEADER_NAME}:${bodyHash}`,\n `${DATE_HEADER_NAME}:${isoDateString.dateTime}`\n ];\n\n // Handle signing with temporary credentials (via sts:assume-role)\n if (this._credentials.sessionToken) {\n canonicalHeaders.push(`${SECURITY_TOKEN_HEADER_NAME}:${this._credentials.sessionToken}`);\n }\n\n // the canonical headers must be sorted by header name\n canonicalHeaders.sort((aHeader, bHeader) => {\n const aHeaderName: string = aHeader.split(':')[0];\n const bHeaderName: string = bHeader.split(':')[0];\n if (aHeaderName < bHeaderName) {\n return -1;\n }\n if (aHeaderName > bHeaderName) {\n return 1;\n }\n return 0;\n });\n\n // the singed header names are derived from the canonicalHeaders\n const signedHeaderNamesString: string = canonicalHeaders\n .map((header) => {\n const headerName: string = header.split(':')[0];\n return headerName;\n })\n .join(';');\n\n // The canonical request looks like this:\n // GET\n // /test.txt\n //\n // host:examplebucket.s3.amazonaws.com\n // range:bytes=0-9\n // x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n // x-amz-date:20130524T000000Z\n //\n // host;range;x-amz-content-sha256;x-amz-date\n // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n const canonicalRequest: string = [\n verb,\n canonicalUri,\n '', // we don't use query strings for these requests\n ...canonicalHeaders,\n '',\n signedHeaderNamesString,\n bodyHash\n ].join('\\n');\n const canonicalRequestHash: string = this._getSha256(canonicalRequest);\n\n const scope: string = `${isoDateString.date}/${this._s3Region}/s3/aws4_request`;\n // The string to sign looks like this:\n // AWS4-HMAC-SHA256\n // 20130524T423589Z\n // 20130524/us-east-1/s3/aws4_request\n // 7344ae5b7ee6c3e7e6b0fe0640412a37625d1fbfff95c48bbb2dc43964946972\n const stringToSign: string = [\n 'AWS4-HMAC-SHA256',\n isoDateString.dateTime,\n scope,\n canonicalRequestHash\n ].join('\\n');\n\n const dateKey: Buffer = this._getSha256Hmac(\n `AWS4${this._credentials.secretAccessKey}`,\n isoDateString.date\n );\n const dateRegionKey: Buffer = this._getSha256Hmac(dateKey, this._s3Region);\n const dateRegionServiceKey: Buffer = this._getSha256Hmac(dateRegionKey, 's3');\n const signingKey: Buffer = this._getSha256Hmac(dateRegionServiceKey, 'aws4_request');\n const signature: string = this._getSha256Hmac(signingKey, stringToSign, 'hex');\n\n const authorizationHeader: string = `AWS4-HMAC-SHA256 Credential=${this._credentials.accessKeyId}/${scope},SignedHeaders=${signedHeaderNamesString},Signature=${signature}`;\n\n headers.set('Authorization', authorizationHeader);\n if (this._credentials.sessionToken) {\n // Handle signing with temporary credentials (via sts:assume-role)\n headers.set('X-Amz-Security-Token', this._credentials.sessionToken);\n }\n }\n\n const webFetchOptions: IGetFetchOptions | IPutFetchOptions = {\n verb,\n headers\n };\n if (verb === 'PUT') {\n (webFetchOptions as IPutFetchOptions).body = body;\n }\n\n const url: string = `${this._s3Endpoint}${canonicalUri}`;\n\n this._writeDebugLine(Colors.bold(Colors.underline('Sending request to S3')));\n this._writeDebugLine(Colors.bold('HOST: '), url);\n this._writeDebugLine(Colors.bold('Headers: '));\n headers.forEach((value, name) => {\n this._writeDebugLine(Colors.cyan(`\\t${name}: ${value}`));\n });\n\n const response: fetch.Response = await this._webClient.fetchAsync(url, webFetchOptions);\n\n return response;\n }\n\n public _getSha256Hmac(key: string | Buffer, data: string): Buffer;\n public _getSha256Hmac(key: string | Buffer, data: string, encoding: 'hex'): string;\n public _getSha256Hmac(key: string | Buffer, data: string, encoding?: 'hex'): Buffer | string {\n const hash: crypto.Hash = crypto.createHmac('sha256', key);\n hash.update(data);\n if (encoding) {\n return hash.digest(encoding);\n } else {\n return hash.digest();\n }\n }\n\n private _getSha256(data?: string | Buffer): string {\n if (data) {\n const hash: crypto.Hash = crypto.createHash('sha256');\n hash.update(data);\n return hash.digest('hex');\n } else {\n // This is the null SHA256 hash\n return 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';\n }\n }\n\n private _getIsoDateString(date: Date = new Date()): IIsoDateString {\n let dateString: string = date.toISOString();\n dateString = dateString.replace(/[-:]/g, ''); // Remove separator characters\n dateString = dateString.substring(0, 15); // Drop milliseconds\n\n // dateTime is an ISO8601 date. It looks like \"20130524T423589\"\n // date is an ISO date. It looks like \"20130524\"\n return {\n dateTime: `${dateString}Z`,\n date: dateString.substring(0, 8)\n };\n }\n\n private async _safeReadResponseText(response: fetch.Response): Promise<string | undefined> {\n try {\n return await response.text();\n } catch (err) {\n // ignore the error\n }\n return undefined;\n }\n\n private _throwS3Error(response: fetch.Response, text: string | undefined): never {\n throw new Error(\n `Amazon S3 responded with status code ${response.status} (${response.statusText})${\n text ? `\\n${text}` : ''\n }`\n );\n }\n\n /**\n * Validates a S3 endpoint which is http(s):// + hostname + port. Hostname validated according to RFC 1123\n * {@link https://docs.aws.amazon.com/general/latest/gr/s3.html}\n */\n private _validateEndpoint(s3Endpoint: string): void {\n let host: string = s3Endpoint;\n\n if (!s3Endpoint) {\n throw new Error('A S3 endpoint must be provided');\n }\n\n if (!s3Endpoint.match(protocolRegex)) {\n throw new Error('The S3 endpoint must start with https:// or http://');\n }\n\n host = host.replace(protocolRegex, '');\n\n if (host.match(/\\//)) {\n throw new Error('The path should be omitted from the endpoint. Use s3Prefix to specify a path');\n }\n\n const portMatch: RegExpMatchArray | null = s3Endpoint.match(portRegex);\n if (portMatch) {\n const port: number = Number(portMatch[1]);\n if (Number.isNaN(port) || port > 65535) {\n throw new Error(`Port: ${port} is an invalid port number`);\n }\n host = host.replace(portRegex, '');\n }\n\n if (host.endsWith('.')) {\n host = host.slice(0, host.length - 1);\n }\n\n if (host.length > 253) {\n throw new Error(\n 'The S3 endpoint is too long. RFC 1123 specifies a hostname should be no longer than 253 characters.'\n );\n }\n\n const subDomains: string[] = host.split('.');\n\n const subDomainRegex: RegExp = /^[a-zA-Z0-9-]+$/;\n const isValid: boolean = subDomains.every((subDomain) => {\n return (\n subDomainRegex.test(subDomain) &&\n subDomain.length < 64 &&\n !subDomain.startsWith('-') &&\n !subDomain.endsWith('-')\n );\n });\n\n if (!isValid) {\n throw new Error(\n 'Invalid S3 endpoint. Some part of the hostname contains invalid characters or is too long'\n );\n }\n }\n}\n"]}
@@ -4,13 +4,17 @@ import type { IRushPlugin, RushSession, RushConfiguration } from '@rushstack/rus
4
4
  */
5
5
  export interface IAmazonS3ConfigurationJson {
6
6
  /**
7
- * The Amazon S3 region of the bucket to use for build cache (e.g. "us-east-1").
7
+ * (Required unless s3Endpoint is specified) The name of the bucket to use for build cache (e.g. "my-bucket").
8
8
  */
9
- s3Region: string;
9
+ s3Bucket?: string;
10
+ /**
11
+ * (Required unless s3Bucket is specified) The Amazon S3 endpoint of the bucket to use for build cache (e.g. "my-bucket.s3.us-east-2.amazonaws.com" or "http://localhost:9000").
12
+ */
13
+ s3Endpoint?: string;
10
14
  /**
11
- * The name of the bucket in Amazon S3 to use for build cache.
15
+ * The Amazon S3 region of the bucket to use for build cache (e.g. "us-east-1").
12
16
  */
13
- s3Bucket: string;
17
+ s3Region: string;
14
18
  /**
15
19
  * An optional prefix ("folder") for cache items.
16
20
  */
@@ -1 +1 @@
1
- {"version":3,"file":"RushAmazonS3BuildCachePlugin.d.ts","sourceRoot":"","sources":["../src/RushAmazonS3BuildCachePlugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAUvF;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;GAEG;AACH,qBAAa,4BAA6B,YAAW,WAAW;IACvD,UAAU,EAAE,MAAM,CAAe;IAEjC,KAAK,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,iBAAiB,GAAG,IAAI;CAsB5E"}
1
+ {"version":3,"file":"RushAmazonS3BuildCachePlugin.d.ts","sourceRoot":"","sources":["../src/RushAmazonS3BuildCachePlugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAcvF;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;GAEG;AACH,qBAAa,4BAA6B,YAAW,WAAW;IACvD,UAAU,EAAE,MAAM,CAAe;IAEjC,KAAK,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,iBAAiB,GAAG,IAAI;CAoD5E"}
@@ -17,12 +17,39 @@ class RushAmazonS3BuildCachePlugin {
17
17
  rushSession.hooks.initialize.tap(PLUGIN_NAME, () => {
18
18
  rushSession.registerCloudBuildCacheProviderFactory('amazon-s3', (buildCacheConfig) => {
19
19
  const { amazonS3Configuration } = buildCacheConfig;
20
- return new AmazonS3BuildCacheProviderModule.AmazonS3BuildCacheProvider({
21
- s3Region: amazonS3Configuration.s3Region,
22
- s3Bucket: amazonS3Configuration.s3Bucket,
23
- s3Prefix: amazonS3Configuration.s3Prefix,
24
- isCacheWriteAllowed: !!amazonS3Configuration.isCacheWriteAllowed
25
- }, rushSession);
20
+ let options;
21
+ const { s3Endpoint, s3Bucket, s3Region } = amazonS3Configuration;
22
+ const s3Prefix = amazonS3Configuration.s3Prefix || undefined;
23
+ const isCacheWriteAllowed = !!amazonS3Configuration.isCacheWriteAllowed;
24
+ if (s3Prefix && s3Prefix[0] === '/') {
25
+ throw new Error('s3Prefix should not have a leading /');
26
+ }
27
+ // mutually exclusive
28
+ if (s3Bucket && s3Endpoint) {
29
+ throw new Error('Only one of "s3Bucket" or "s3Endpoint" must be provided.');
30
+ }
31
+ if (s3Endpoint) {
32
+ options = {
33
+ // IAmazonS3BuildCacheProviderOptionsAdvanced
34
+ s3Region,
35
+ s3Endpoint,
36
+ s3Prefix,
37
+ isCacheWriteAllowed
38
+ };
39
+ }
40
+ if (s3Bucket) {
41
+ options = {
42
+ // IAmazonS3BuildCacheProviderOptionsSimple
43
+ s3Region,
44
+ s3Bucket,
45
+ s3Prefix,
46
+ isCacheWriteAllowed
47
+ };
48
+ }
49
+ if (!options) {
50
+ throw new Error('You must provide either an s3Endpoint or s3Bucket');
51
+ }
52
+ return new AmazonS3BuildCacheProviderModule.AmazonS3BuildCacheProvider(options, rushSession);
26
53
  });
27
54
  });
28
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"RushAmazonS3BuildCachePlugin.js","sourceRoot":"","sources":["../src/RushAmazonS3BuildCachePlugin.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D,oEAAsD;AAItD,MAAM,gCAAgC,GAAkD,0BAAM,CAAC,IAAI,CACjG,8BAA8B,EAC9B,OAAO,CACR,CAAC;AAEF,MAAM,WAAW,GAAW,0BAA0B,CAAC;AA2BvD;;GAEG;AACH,MAAa,4BAA4B;IAAzC;QACS,eAAU,GAAW,WAAW,CAAC;IAwB1C,CAAC;IAtBQ,KAAK,CAAC,WAAwB,EAAE,UAA6B;QAClE,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,EAAE;YACjD,WAAW,CAAC,sCAAsC,CAChD,WAAW,EACX,CAAC,gBAAgB,EAA8B,EAAE;gBAI/C,MAAM,EAAE,qBAAqB,EAAE,GAAG,gBAA+B,CAAC;gBAClE,OAAO,IAAI,gCAAgC,CAAC,0BAA0B,CACpE;oBACE,QAAQ,EAAE,qBAAqB,CAAC,QAAQ;oBACxC,QAAQ,EAAE,qBAAqB,CAAC,QAAQ;oBACxC,QAAQ,EAAE,qBAAqB,CAAC,QAAQ;oBACxC,mBAAmB,EAAE,CAAC,CAAC,qBAAqB,CAAC,mBAAmB;iBACjE,EACD,WAAW,CACZ,CAAC;YACJ,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAzBD,oEAyBC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { Import } from '@rushstack/node-core-library';\nimport type { IRushPlugin, RushSession, RushConfiguration } from '@rushstack/rush-sdk';\nimport type { AmazonS3BuildCacheProvider } from './AmazonS3BuildCacheProvider';\n\nconst AmazonS3BuildCacheProviderModule: typeof import('./AmazonS3BuildCacheProvider') = Import.lazy(\n './AmazonS3BuildCacheProvider',\n require\n);\n\nconst PLUGIN_NAME: string = 'AmazonS3BuildCachePlugin';\n\n/**\n * @public\n */\nexport interface IAmazonS3ConfigurationJson {\n /**\n * The Amazon S3 region of the bucket to use for build cache (e.g. \"us-east-1\").\n */\n s3Region: string;\n\n /**\n * The name of the bucket in Amazon S3 to use for build cache.\n */\n s3Bucket: string;\n\n /**\n * An optional prefix (\"folder\") for cache items.\n */\n s3Prefix?: string;\n\n /**\n * If set to true, allow writing to the cache. Defaults to false.\n */\n isCacheWriteAllowed?: boolean;\n}\n\n/**\n * @public\n */\nexport class RushAmazonS3BuildCachePlugin implements IRushPlugin {\n public pluginName: string = PLUGIN_NAME;\n\n public apply(rushSession: RushSession, rushConfig: RushConfiguration): void {\n rushSession.hooks.initialize.tap(PLUGIN_NAME, () => {\n rushSession.registerCloudBuildCacheProviderFactory(\n 'amazon-s3',\n (buildCacheConfig): AmazonS3BuildCacheProvider => {\n type IBuildCache = typeof buildCacheConfig & {\n amazonS3Configuration: IAmazonS3ConfigurationJson;\n };\n const { amazonS3Configuration } = buildCacheConfig as IBuildCache;\n return new AmazonS3BuildCacheProviderModule.AmazonS3BuildCacheProvider(\n {\n s3Region: amazonS3Configuration.s3Region,\n s3Bucket: amazonS3Configuration.s3Bucket,\n s3Prefix: amazonS3Configuration.s3Prefix,\n isCacheWriteAllowed: !!amazonS3Configuration.isCacheWriteAllowed\n },\n rushSession\n );\n }\n );\n });\n }\n}\n"]}
1
+ {"version":3,"file":"RushAmazonS3BuildCachePlugin.js","sourceRoot":"","sources":["../src/RushAmazonS3BuildCachePlugin.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D,oEAAsD;AAQtD,MAAM,gCAAgC,GAAkD,0BAAM,CAAC,IAAI,CACjG,8BAA8B,EAC9B,OAAO,CACR,CAAC;AAEF,MAAM,WAAW,GAAW,0BAA0B,CAAC;AAgCvD;;GAEG;AACH,MAAa,4BAA4B;IAAzC;QACS,eAAU,GAAW,WAAW,CAAC;IAsD1C,CAAC;IApDQ,KAAK,CAAC,WAAwB,EAAE,UAA6B;QAClE,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,EAAE;YACjD,WAAW,CAAC,sCAAsC,CAChD,WAAW,EACX,CAAC,gBAAgB,EAA8B,EAAE;gBAI/C,MAAM,EAAE,qBAAqB,EAAE,GAAG,gBAA+B,CAAC;gBAClE,IAAI,OAGS,CAAC;gBACd,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,qBAAqB,CAAC;gBACjE,MAAM,QAAQ,GAAuB,qBAAqB,CAAC,QAAQ,IAAI,SAAS,CAAC;gBACjF,MAAM,mBAAmB,GAAY,CAAC,CAAC,qBAAqB,CAAC,mBAAmB,CAAC;gBAEjF,IAAI,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;oBACnC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;iBACzD;gBAED,qBAAqB;gBACrB,IAAI,QAAQ,IAAI,UAAU,EAAE;oBAC1B,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;iBAC7E;gBAED,IAAI,UAAU,EAAE;oBACd,OAAO,GAAG;wBACR,6CAA6C;wBAC7C,QAAQ;wBACR,UAAU;wBACV,QAAQ;wBACR,mBAAmB;qBACpB,CAAC;iBACH;gBACD,IAAI,QAAQ,EAAE;oBACZ,OAAO,GAAG;wBACR,2CAA2C;wBAC3C,QAAQ;wBACR,QAAQ;wBACR,QAAQ;wBACR,mBAAmB;qBACpB,CAAC;iBACH;gBACD,IAAI,CAAC,OAAO,EAAE;oBACZ,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;iBACtE;gBACD,OAAO,IAAI,gCAAgC,CAAC,0BAA0B,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;YAC/F,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAvDD,oEAuDC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { Import } from '@rushstack/node-core-library';\nimport type { IRushPlugin, RushSession, RushConfiguration } from '@rushstack/rush-sdk';\nimport type {\n AmazonS3BuildCacheProvider,\n IAmazonS3BuildCacheProviderOptionsAdvanced,\n IAmazonS3BuildCacheProviderOptionsSimple\n} from './AmazonS3BuildCacheProvider';\n\nconst AmazonS3BuildCacheProviderModule: typeof import('./AmazonS3BuildCacheProvider') = Import.lazy(\n './AmazonS3BuildCacheProvider',\n require\n);\n\nconst PLUGIN_NAME: string = 'AmazonS3BuildCachePlugin';\n\n/**\n * @public\n */\nexport interface IAmazonS3ConfigurationJson {\n /**\n * (Required unless s3Endpoint is specified) The name of the bucket to use for build cache (e.g. \"my-bucket\").\n */\n s3Bucket?: string;\n\n /**\n * (Required unless s3Bucket is specified) The Amazon S3 endpoint of the bucket to use for build cache (e.g. \"my-bucket.s3.us-east-2.amazonaws.com\" or \"http://localhost:9000\").\n */\n s3Endpoint?: string;\n\n /**\n * The Amazon S3 region of the bucket to use for build cache (e.g. \"us-east-1\").\n */\n s3Region: string;\n\n /**\n * An optional prefix (\"folder\") for cache items.\n */\n s3Prefix?: string;\n\n /**\n * If set to true, allow writing to the cache. Defaults to false.\n */\n isCacheWriteAllowed?: boolean;\n}\n\n/**\n * @public\n */\nexport class RushAmazonS3BuildCachePlugin implements IRushPlugin {\n public pluginName: string = PLUGIN_NAME;\n\n public apply(rushSession: RushSession, rushConfig: RushConfiguration): void {\n rushSession.hooks.initialize.tap(PLUGIN_NAME, () => {\n rushSession.registerCloudBuildCacheProviderFactory(\n 'amazon-s3',\n (buildCacheConfig): AmazonS3BuildCacheProvider => {\n type IBuildCache = typeof buildCacheConfig & {\n amazonS3Configuration: IAmazonS3ConfigurationJson;\n };\n const { amazonS3Configuration } = buildCacheConfig as IBuildCache;\n let options:\n | IAmazonS3BuildCacheProviderOptionsAdvanced\n | IAmazonS3BuildCacheProviderOptionsSimple\n | undefined;\n const { s3Endpoint, s3Bucket, s3Region } = amazonS3Configuration;\n const s3Prefix: undefined | string = amazonS3Configuration.s3Prefix || undefined;\n const isCacheWriteAllowed: boolean = !!amazonS3Configuration.isCacheWriteAllowed;\n\n if (s3Prefix && s3Prefix[0] === '/') {\n throw new Error('s3Prefix should not have a leading /');\n }\n\n // mutually exclusive\n if (s3Bucket && s3Endpoint) {\n throw new Error('Only one of \"s3Bucket\" or \"s3Endpoint\" must be provided.');\n }\n\n if (s3Endpoint) {\n options = {\n // IAmazonS3BuildCacheProviderOptionsAdvanced\n s3Region,\n s3Endpoint,\n s3Prefix,\n isCacheWriteAllowed\n };\n }\n if (s3Bucket) {\n options = {\n // IAmazonS3BuildCacheProviderOptionsSimple\n s3Region,\n s3Bucket,\n s3Prefix,\n isCacheWriteAllowed\n };\n }\n if (!options) {\n throw new Error('You must provide either an s3Endpoint or s3Bucket');\n }\n return new AmazonS3BuildCacheProviderModule.AmazonS3BuildCacheProvider(options, rushSession);\n }\n );\n });\n }\n}\n"]}
@@ -2,10 +2,14 @@
2
2
  import * as fetch from 'node-fetch';
3
3
  /**
4
4
  * For use with {@link WebClient}.
5
+ *
6
+ * @public
5
7
  */
6
8
  export declare type WebClientResponse = fetch.Response;
7
9
  /**
8
10
  * For use with {@link WebClient}.
11
+ *
12
+ * @public
9
13
  */
10
14
  export interface IWebFetchOptionsBase {
11
15
  timeoutMs?: number;
@@ -14,12 +18,16 @@ export interface IWebFetchOptionsBase {
14
18
  }
15
19
  /**
16
20
  * For use with {@link WebClient}.
21
+ *
22
+ * @public
17
23
  */
18
24
  export interface IGetFetchOptions extends IWebFetchOptionsBase {
19
25
  verb: 'GET' | never;
20
26
  }
21
27
  /**
22
28
  * For use with {@link WebClient}.
29
+ *
30
+ * @public
23
31
  */
24
32
  export interface IPutFetchOptions extends IWebFetchOptionsBase {
25
33
  verb: 'PUT';
@@ -27,6 +35,7 @@ export interface IPutFetchOptions extends IWebFetchOptionsBase {
27
35
  }
28
36
  /**
29
37
  * For use with {@link WebClient}.
38
+ * @public
30
39
  */
31
40
  export declare enum WebClientProxy {
32
41
  None = 0,
@@ -35,6 +44,8 @@ export declare enum WebClientProxy {
35
44
  }
36
45
  /**
37
46
  * A helper for issuing HTTP requests.
47
+ *
48
+ * @public
38
49
  */
39
50
  export declare class WebClient {
40
51
  readonly standardHeaders: fetch.Headers;
@@ -1 +1 @@
1
- {"version":3,"file":"WebClient.d.ts","sourceRoot":"","sources":["../src/WebClient.ts"],"names":[],"mappings":";AAaA,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAMpC;;GAEG;AACH,oBAAY,iBAAiB,GAAG,KAAK,CAAC,QAAQ,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACrB,OAAO,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC5D,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC5D,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,oBAAY,cAAc;IACxB,IAAI,IAAA;IACJ,MAAM,IAAA;IACN,OAAO,IAAA;CACR;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB,SAAgB,eAAe,EAAE,KAAK,CAAC,OAAO,CAAuB;IAE9D,MAAM,EAAE,MAAM,GAAG,SAAS,CAAS;IACnC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAgE;IAE7F,KAAK,EAAE,cAAc,CAAyB;;WAIvC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,GAAG,IAAI;IAMvE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAOtD,UAAU,CACrB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,GAC5C,OAAO,CAAC,iBAAiB,CAAC;CAsD9B"}
1
+ {"version":3,"file":"WebClient.d.ts","sourceRoot":"","sources":["../src/WebClient.ts"],"names":[],"mappings":";AAaA,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAMpC;;;;GAIG;AACH,oBAAY,iBAAiB,GAAG,KAAK,CAAC,QAAQ,CAAC;AAE/C;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACrB,OAAO,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC5D,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC5D,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,oBAAY,cAAc;IACxB,IAAI,IAAA;IACJ,MAAM,IAAA;IACN,OAAO,IAAA;CACR;AAED;;;;GAIG;AACH,qBAAa,SAAS;IACpB,SAAgB,eAAe,EAAE,KAAK,CAAC,OAAO,CAAuB;IAE9D,MAAM,EAAE,MAAM,GAAG,SAAS,CAAS;IACnC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAgE;IAE7F,KAAK,EAAE,cAAc,CAAyB;;WAIvC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,GAAG,IAAI;IAMvE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAOtD,UAAU,CACrB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,GAC5C,OAAO,CAAC,iBAAiB,CAAC;CAsD9B"}
package/lib/WebClient.js CHANGED
@@ -36,6 +36,7 @@ const node_core_library_1 = require("@rushstack/node-core-library");
36
36
  const createHttpsProxyAgent = node_core_library_1.Import.lazy('https-proxy-agent', require);
37
37
  /**
38
38
  * For use with {@link WebClient}.
39
+ * @public
39
40
  */
40
41
  var WebClientProxy;
41
42
  (function (WebClientProxy) {
@@ -45,6 +46,8 @@ var WebClientProxy;
45
46
  })(WebClientProxy = exports.WebClientProxy || (exports.WebClientProxy = {}));
46
47
  /**
47
48
  * A helper for issuing HTTP requests.
49
+ *
50
+ * @public
48
51
  */
49
52
  class WebClient {
50
53
  constructor() {
@@ -1 +1 @@
1
- {"version":3,"file":"WebClient.js","sourceRoot":"","sources":["../src/WebClient.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n// ===================================================================================================================\n// AS A TEMPORARY WORKAROUND, THIS FILE WAS COPY+PASTED FROM THE \"rush-lib\" PROJECT.\n//\n// Eventually we plan to convert it into a more generic API for \"node-core-library\" or\n// else replace it with a third party solution such as Axios. See the discussion here:\n// https://github.com/microsoft/rushstack/pull/3036#discussion_r758010126\n// ===================================================================================================================\n\nimport * as os from 'os';\nimport * as process from 'process';\nimport * as fetch from 'node-fetch';\nimport * as http from 'http';\nimport { Import } from '@rushstack/node-core-library';\n\nconst createHttpsProxyAgent: typeof import('https-proxy-agent') = Import.lazy('https-proxy-agent', require);\n\n/**\n * For use with {@link WebClient}.\n */\nexport type WebClientResponse = fetch.Response;\n\n/**\n * For use with {@link WebClient}.\n */\nexport interface IWebFetchOptionsBase {\n timeoutMs?: number;\n verb?: 'GET' | 'PUT';\n headers?: fetch.Headers;\n}\n\n/**\n * For use with {@link WebClient}.\n */\nexport interface IGetFetchOptions extends IWebFetchOptionsBase {\n verb: 'GET' | never;\n}\n\n/**\n * For use with {@link WebClient}.\n */\nexport interface IPutFetchOptions extends IWebFetchOptionsBase {\n verb: 'PUT';\n body?: Buffer;\n}\n\n/**\n * For use with {@link WebClient}.\n */\nexport enum WebClientProxy {\n None,\n Detect,\n Fiddler\n}\n\n/**\n * A helper for issuing HTTP requests.\n */\nexport class WebClient {\n public readonly standardHeaders: fetch.Headers = new fetch.Headers();\n\n public accept: string | undefined = '*/*';\n public userAgent: string | undefined = `rush node/${process.version} ${os.platform()} ${os.arch()}`;\n\n public proxy: WebClientProxy = WebClientProxy.Detect;\n\n public constructor() {}\n\n public static mergeHeaders(target: fetch.Headers, source: fetch.Headers): void {\n source.forEach((value, name) => {\n target.set(name, value);\n });\n }\n\n public addBasicAuthHeader(userName: string, password: string): void {\n this.standardHeaders.set(\n 'Authorization',\n 'Basic ' + Buffer.from(userName + ':' + password).toString('base64')\n );\n }\n\n public async fetchAsync(\n url: string,\n options?: IGetFetchOptions | IPutFetchOptions\n ): Promise<WebClientResponse> {\n const headers: fetch.Headers = new fetch.Headers();\n\n WebClient.mergeHeaders(headers, this.standardHeaders);\n\n if (options?.headers) {\n WebClient.mergeHeaders(headers, options.headers);\n }\n\n if (this.userAgent) {\n headers.set('user-agent', this.userAgent);\n }\n if (this.accept) {\n headers.set('accept', this.accept);\n }\n\n let proxyUrl: string = '';\n\n switch (this.proxy) {\n case WebClientProxy.Detect:\n if (process.env.HTTPS_PROXY) {\n proxyUrl = process.env.HTTPS_PROXY;\n } else if (process.env.HTTP_PROXY) {\n proxyUrl = process.env.HTTP_PROXY;\n }\n break;\n\n case WebClientProxy.Fiddler:\n // For debugging, disable cert validation\n // eslint-disable-next-line\n process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';\n proxyUrl = 'http://localhost:8888/';\n break;\n }\n\n let agent: http.Agent | undefined = undefined;\n if (proxyUrl) {\n agent = createHttpsProxyAgent(proxyUrl);\n }\n\n const timeoutMs: number = options?.timeoutMs !== undefined ? options.timeoutMs : 15 * 1000; // 15 seconds\n const requestInit: fetch.RequestInit = {\n method: options?.verb,\n headers: headers,\n agent: agent,\n timeout: timeoutMs\n };\n const putOptions: IPutFetchOptions | undefined = options as IPutFetchOptions | undefined;\n if (putOptions?.body) {\n requestInit.body = putOptions.body;\n }\n\n return await fetch.default(url, requestInit);\n }\n}\n"]}
1
+ {"version":3,"file":"WebClient.js","sourceRoot":"","sources":["../src/WebClient.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;;;;;;;;;;;;;;;;;;;;AAE3D,sHAAsH;AACtH,oFAAoF;AACpF,EAAE;AACF,sFAAsF;AACtF,uFAAuF;AACvF,yEAAyE;AACzE,sHAAsH;AAEtH,uCAAyB;AACzB,iDAAmC;AACnC,kDAAoC;AAEpC,oEAAsD;AAEtD,MAAM,qBAAqB,GAAuC,0BAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;AAuC5G;;;GAGG;AACH,IAAY,cAIX;AAJD,WAAY,cAAc;IACxB,mDAAI,CAAA;IACJ,uDAAM,CAAA;IACN,yDAAO,CAAA;AACT,CAAC,EAJW,cAAc,GAAd,sBAAc,KAAd,sBAAc,QAIzB;AAED;;;;GAIG;AACH,MAAa,SAAS;IAQpB;QAPgB,oBAAe,GAAkB,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAE9D,WAAM,GAAuB,KAAK,CAAC;QACnC,cAAS,GAAuB,aAAa,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;QAE7F,UAAK,GAAmB,cAAc,CAAC,MAAM,CAAC;IAE/B,CAAC;IAEhB,MAAM,CAAC,YAAY,CAAC,MAAqB,EAAE,MAAqB;QACrE,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;YAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,kBAAkB,CAAC,QAAgB,EAAE,QAAgB;QAC1D,IAAI,CAAC,eAAe,CAAC,GAAG,CACtB,eAAe,EACf,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,GAAG,GAAG,GAAG,QAAQ,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CACrE,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,UAAU,CACrB,GAAW,EACX,OAA6C;QAE7C,MAAM,OAAO,GAAkB,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAEnD,SAAS,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QAEtD,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,EAAE;YACpB,SAAS,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;SAClD;QAED,IAAI,IAAI,CAAC,SAAS,EAAE;YAClB,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;SAC3C;QACD,IAAI,IAAI,CAAC,MAAM,EAAE;YACf,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;SACpC;QAED,IAAI,QAAQ,GAAW,EAAE,CAAC;QAE1B,QAAQ,IAAI,CAAC,KAAK,EAAE;YAClB,KAAK,cAAc,CAAC,MAAM;gBACxB,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE;oBAC3B,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;iBACpC;qBAAM,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE;oBACjC,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;iBACnC;gBACD,MAAM;YAER,KAAK,cAAc,CAAC,OAAO;gBACzB,yCAAyC;gBACzC,2BAA2B;gBAC3B,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,GAAG,GAAG,CAAC;gBAClD,QAAQ,GAAG,wBAAwB,CAAC;gBACpC,MAAM;SACT;QAED,IAAI,KAAK,GAA2B,SAAS,CAAC;QAC9C,IAAI,QAAQ,EAAE;YACZ,KAAK,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;SACzC;QAED,MAAM,SAAS,GAAW,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,SAAS,MAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;QACzG,MAAM,WAAW,GAAsB;YACrC,MAAM,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,IAAI;YACrB,OAAO,EAAE,OAAO;YAChB,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,SAAS;SACnB,CAAC;QACF,MAAM,UAAU,GAAiC,OAAuC,CAAC;QACzF,IAAI,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,IAAI,EAAE;YACpB,WAAW,CAAC,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC;SACpC;QAED,OAAO,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC/C,CAAC;CACF;AAhFD,8BAgFC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n// ===================================================================================================================\n// AS A TEMPORARY WORKAROUND, THIS FILE WAS COPY+PASTED FROM THE \"rush-lib\" PROJECT.\n//\n// Eventually we plan to convert it into a more generic API for \"node-core-library\" or\n// else replace it with a third party solution such as Axios. See the discussion here:\n// https://github.com/microsoft/rushstack/pull/3036#discussion_r758010126\n// ===================================================================================================================\n\nimport * as os from 'os';\nimport * as process from 'process';\nimport * as fetch from 'node-fetch';\nimport * as http from 'http';\nimport { Import } from '@rushstack/node-core-library';\n\nconst createHttpsProxyAgent: typeof import('https-proxy-agent') = Import.lazy('https-proxy-agent', require);\n\n/**\n * For use with {@link WebClient}.\n *\n * @public\n */\nexport type WebClientResponse = fetch.Response;\n\n/**\n * For use with {@link WebClient}.\n *\n * @public\n */\nexport interface IWebFetchOptionsBase {\n timeoutMs?: number;\n verb?: 'GET' | 'PUT';\n headers?: fetch.Headers;\n}\n\n/**\n * For use with {@link WebClient}.\n *\n * @public\n */\nexport interface IGetFetchOptions extends IWebFetchOptionsBase {\n verb: 'GET' | never;\n}\n\n/**\n * For use with {@link WebClient}.\n *\n * @public\n */\nexport interface IPutFetchOptions extends IWebFetchOptionsBase {\n verb: 'PUT';\n body?: Buffer;\n}\n\n/**\n * For use with {@link WebClient}.\n * @public\n */\nexport enum WebClientProxy {\n None,\n Detect,\n Fiddler\n}\n\n/**\n * A helper for issuing HTTP requests.\n *\n * @public\n */\nexport class WebClient {\n public readonly standardHeaders: fetch.Headers = new fetch.Headers();\n\n public accept: string | undefined = '*/*';\n public userAgent: string | undefined = `rush node/${process.version} ${os.platform()} ${os.arch()}`;\n\n public proxy: WebClientProxy = WebClientProxy.Detect;\n\n public constructor() {}\n\n public static mergeHeaders(target: fetch.Headers, source: fetch.Headers): void {\n source.forEach((value, name) => {\n target.set(name, value);\n });\n }\n\n public addBasicAuthHeader(userName: string, password: string): void {\n this.standardHeaders.set(\n 'Authorization',\n 'Basic ' + Buffer.from(userName + ':' + password).toString('base64')\n );\n }\n\n public async fetchAsync(\n url: string,\n options?: IGetFetchOptions | IPutFetchOptions\n ): Promise<WebClientResponse> {\n const headers: fetch.Headers = new fetch.Headers();\n\n WebClient.mergeHeaders(headers, this.standardHeaders);\n\n if (options?.headers) {\n WebClient.mergeHeaders(headers, options.headers);\n }\n\n if (this.userAgent) {\n headers.set('user-agent', this.userAgent);\n }\n if (this.accept) {\n headers.set('accept', this.accept);\n }\n\n let proxyUrl: string = '';\n\n switch (this.proxy) {\n case WebClientProxy.Detect:\n if (process.env.HTTPS_PROXY) {\n proxyUrl = process.env.HTTPS_PROXY;\n } else if (process.env.HTTP_PROXY) {\n proxyUrl = process.env.HTTP_PROXY;\n }\n break;\n\n case WebClientProxy.Fiddler:\n // For debugging, disable cert validation\n // eslint-disable-next-line\n process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';\n proxyUrl = 'http://localhost:8888/';\n break;\n }\n\n let agent: http.Agent | undefined = undefined;\n if (proxyUrl) {\n agent = createHttpsProxyAgent(proxyUrl);\n }\n\n const timeoutMs: number = options?.timeoutMs !== undefined ? options.timeoutMs : 15 * 1000; // 15 seconds\n const requestInit: fetch.RequestInit = {\n method: options?.verb,\n headers: headers,\n agent: agent,\n timeout: timeoutMs\n };\n const putOptions: IPutFetchOptions | undefined = options as IPutFetchOptions | undefined;\n if (putOptions?.body) {\n requestInit.body = putOptions.body;\n }\n\n return await fetch.default(url, requestInit);\n }\n}\n"]}
package/lib/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  import { RushAmazonS3BuildCachePlugin } from './RushAmazonS3BuildCachePlugin';
2
+ export { AmazonS3Client, IAmazonS3Credentials } from './AmazonS3Client';
3
+ export { WebClient, IGetFetchOptions, IPutFetchOptions, WebClientResponse } from './WebClient';
2
4
  export default RushAmazonS3BuildCachePlugin;
5
+ export { IAmazonS3BuildCacheProviderOptionsBase, IAmazonS3BuildCacheProviderOptionsAdvanced, IAmazonS3BuildCacheProviderOptionsSimple } from './AmazonS3BuildCacheProvider';
3
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAE9E,eAAe,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAE9E,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC/F,eAAe,4BAA4B,CAAC;AAC5C,OAAO,EACL,sCAAsC,EACtC,0CAA0C,EAC1C,wCAAwC,EACzC,MAAM,8BAA8B,CAAC"}
package/lib/index.js CHANGED
@@ -2,6 +2,11 @@
2
2
  // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
3
  // See LICENSE in the project root for license information.
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.WebClient = exports.AmazonS3Client = void 0;
5
6
  const RushAmazonS3BuildCachePlugin_1 = require("./RushAmazonS3BuildCachePlugin");
7
+ var AmazonS3Client_1 = require("./AmazonS3Client");
8
+ Object.defineProperty(exports, "AmazonS3Client", { enumerable: true, get: function () { return AmazonS3Client_1.AmazonS3Client; } });
9
+ var WebClient_1 = require("./WebClient");
10
+ Object.defineProperty(exports, "WebClient", { enumerable: true, get: function () { return WebClient_1.WebClient; } });
6
11
  exports.default = RushAmazonS3BuildCachePlugin_1.RushAmazonS3BuildCachePlugin;
7
12
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;AAE3D,iFAA8E;AAE9E,kBAAe,2DAA4B,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { RushAmazonS3BuildCachePlugin } from './RushAmazonS3BuildCachePlugin';\n\nexport default RushAmazonS3BuildCachePlugin;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D,iFAA8E;AAE9E,mDAAwE;AAA/D,gHAAA,cAAc,OAAA;AACvB,yCAA+F;AAAtF,sGAAA,SAAS,OAAA;AAClB,kBAAe,2DAA4B,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { RushAmazonS3BuildCachePlugin } from './RushAmazonS3BuildCachePlugin';\n\nexport { AmazonS3Client, IAmazonS3Credentials } from './AmazonS3Client';\nexport { WebClient, IGetFetchOptions, IPutFetchOptions, WebClientResponse } from './WebClient';\nexport default RushAmazonS3BuildCachePlugin;\nexport {\n IAmazonS3BuildCacheProviderOptionsBase,\n IAmazonS3BuildCacheProviderOptionsAdvanced,\n IAmazonS3BuildCacheProviderOptionsSimple\n} from './AmazonS3BuildCacheProvider';\n"]}
@@ -1,32 +1,51 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-04/schema#",
3
3
  "title": "Configuration for build cache with Amazon S3 configuration",
4
-
5
4
  "type": "object",
6
-
7
- "additionalProperties": false,
8
-
9
- "required": ["s3Region", "s3Bucket"],
10
-
11
- "properties": {
12
- "s3Region": {
13
- "type": "string",
14
- "description": "(Required) The Amazon S3 region of the bucket to use for build cache (e.g. \"us-east-1\")."
5
+ "oneOf": [
6
+ {
7
+ "type": "object",
8
+ "required": ["s3Endpoint", "s3Region"],
9
+ "properties": {
10
+ "s3Endpoint": {
11
+ "type": "string",
12
+ "description": "(Required) The Amazon S3 endpoint of the bucket to use for build cache (e.g. \"s3.us-east-2.amazonaws.com\")."
13
+ },
14
+ "s3Region": {
15
+ "type": "string",
16
+ "description": "(Required) The Amazon S3 region of the bucket to use for build cache (e.g. \"us-east-1\")."
17
+ },
18
+ "s3Prefix": {
19
+ "type": "string",
20
+ "description": "An optional prefix (\"folder\") for cache items."
21
+ },
22
+ "isCacheWriteAllowed": {
23
+ "type": "boolean",
24
+ "description": "If set to true, allow writing to the cache. Defaults to false."
25
+ }
26
+ }
15
27
  },
16
-
17
- "s3Bucket": {
18
- "type": "string",
19
- "description": "(Required) The name of the bucket in Amazon S3 to use for build cache."
20
- },
21
-
22
- "s3Prefix": {
23
- "type": "string",
24
- "description": "An optional prefix (\"folder\") for cache items."
25
- },
26
-
27
- "isCacheWriteAllowed": {
28
- "type": "boolean",
29
- "description": "If set to true, allow writing to the cache. Defaults to false."
28
+ {
29
+ "type": "object",
30
+ "required": ["s3Bucket", "s3Region"],
31
+ "properties": {
32
+ "s3Bucket": {
33
+ "type": "string",
34
+ "description": "(Required unless s3Endpoint is specified) The name of the bucket to use for build cache (e.g. \"my-bucket\")."
35
+ },
36
+ "s3Region": {
37
+ "type": "string",
38
+ "description": "(Required) The Amazon S3 region of the bucket to use for build cache (e.g. \"us-east-1\")."
39
+ },
40
+ "s3Prefix": {
41
+ "type": "string",
42
+ "description": "An optional prefix (\"folder\") for cache items."
43
+ },
44
+ "isCacheWriteAllowed": {
45
+ "type": "boolean",
46
+ "description": "If set to true, allow writing to the cache. Defaults to false."
47
+ }
48
+ }
30
49
  }
31
- }
50
+ ]
32
51
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rushstack/rush-amazon-s3-build-cache-plugin",
3
- "version": "5.62.0-pr3059",
3
+ "version": "5.62.0",
4
4
  "description": "Rush plugin for Amazon S3 cloud build cache",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12,12 +12,12 @@
12
12
  "license": "MIT",
13
13
  "dependencies": {
14
14
  "@rushstack/node-core-library": "3.45.0",
15
- "@rushstack/rush-sdk": "5.62.0-pr3059",
15
+ "@rushstack/rush-sdk": "5.62.0",
16
16
  "https-proxy-agent": "~5.0.0",
17
17
  "node-fetch": "2.6.7"
18
18
  },
19
19
  "devDependencies": {
20
- "@microsoft/rush-lib": "5.62.0-pr3059",
20
+ "@microsoft/rush-lib": "5.62.0",
21
21
  "@rushstack/eslint-config": "2.5.1",
22
22
  "@rushstack/heft": "0.44.2",
23
23
  "@rushstack/heft-node-rig": "1.7.1",