11import * as clientS3 from '@aws-sdk/client-s3' ;
2+ import { fromIni } from '@aws-sdk/credential-provider-ini' ;
3+ import { fromTemporaryCredentials } from '@aws-sdk/credential-providers' ;
24import { getSignedUrl } from '@aws-sdk/s3-request-presigner' ;
35import type { RemoteArtifact , RemoteBuildCache } from '@rnef/tools' ;
46import type { Readable } from 'stream' ;
@@ -27,13 +29,13 @@ type ProviderConfig = {
2729 */
2830 region : string ;
2931 /**
30- * The access key ID for the S3 server.
32+ * The access key ID for the S3 server. Not required when using IAM roles or other auth methods.
3133 */
32- accessKeyId : string ;
34+ accessKeyId ? : string ;
3335 /**
34- * The secret access key for the S3 server.
36+ * The secret access key for the S3 server. Not required when using IAM roles or other auth methods.
3537 */
36- secretAccessKey : string ;
38+ secretAccessKey ? : string ;
3739 /**
3840 * The directory to store artifacts in the S3 server.
3941 */
@@ -46,6 +48,22 @@ type ProviderConfig = {
4648 * The time in seconds for the presigned URL to expire. By default, it is 24 hours.
4749 */
4850 linkExpirationTime ?: number ;
51+ /**
52+ * AWS profile name to use for authentication. Useful for local development.
53+ */
54+ profile ?: string ;
55+ /**
56+ * Role ARN to assume for authentication. Useful for cross-account access.
57+ */
58+ roleArn ?: string ;
59+ /**
60+ * Session name when assuming a role.
61+ */
62+ roleSessionName ?: string ;
63+ /**
64+ * External ID when assuming a role (for additional security).
65+ */
66+ externalId ?: string ;
4967} ;
5068
5169export class S3BuildCache implements RemoteBuildCache {
@@ -58,14 +76,37 @@ export class S3BuildCache implements RemoteBuildCache {
5876
5977 constructor ( config : ProviderConfig ) {
6078 this . config = config ;
61- this . s3 = new clientS3 . S3Client ( {
79+
80+ const s3Config : clientS3 . S3ClientConfig = {
6281 endpoint : config . endpoint ,
6382 region : config . region ,
64- credentials : {
83+ } ;
84+
85+ if ( config . accessKeyId && config . secretAccessKey ) {
86+ s3Config . credentials = {
6587 accessKeyId : config . accessKeyId ,
6688 secretAccessKey : config . secretAccessKey ,
67- } ,
68- } ) ;
89+ } ;
90+ } else if ( config . roleArn ) {
91+ // Use STS to assume a role
92+ s3Config . credentials = fromTemporaryCredentials ( {
93+ params : {
94+ RoleArn : config . roleArn ,
95+ RoleSessionName : config . roleSessionName ?? 's3-build-cache-session' ,
96+ ExternalId : config . externalId ,
97+ } ,
98+ // Optional: use named profile as source credentials
99+ masterCredentials : config . profile
100+ ? fromIni ( { profile : config . profile } )
101+ : undefined ,
102+ } ) ;
103+ } else if ( config . profile ) {
104+ // Use shared config file (e.g. ~/.aws/credentials) with a profile
105+ s3Config . credentials = fromIni ( { profile : config . profile } ) ;
106+ }
107+
108+ this . s3 = new clientS3 . S3Client ( s3Config ) ;
109+
69110 const awsBucket = config . bucket ?? '' ;
70111 const bucketTokens = awsBucket . split ( '/' ) ;
71112 this . bucket = bucketTokens . shift ( ) as string ;
0 commit comments