Skip to content

New deploy service#244

Open
purplecabbage wants to merge 20 commits into
masterfrom
NewDeployService
Open

New deploy service#244
purplecabbage wants to merge 20 commits into
masterfrom
NewDeployService

Conversation

@purplecabbage
Copy link
Copy Markdown
Member

@purplecabbage purplecabbage commented May 19, 2026

Summary

Migrates static web deploy and undeploy from direct AWS S3 access (TVM / BYO credentials) to the App Builder deploy-service CDN API, using the user’s bearer token instead of short-lived S3 keys.

Storage layer (RemoteStorage)

  • Constructor now takes an Authorization header value instead of AWS credential objects.
  • List / delete / upload call deploy-service REST endpoints under /cdn-api/namespaces/{namespace}/files via fetch, with the service base URL chosen from CLI environment (@adobe/aio-lib-env: prod vs stage).
  • Upload payload matches the CDN API shape: JSON body with base64 file content, contentType, cacheControl, customHeaders, and a relative file.name (namespace is applied server-side).
  • Cache control and response headers behavior is preserved; adp-cache-control can still override manifest cache settings.
  • Batch uploads still walk the dist directory with klaw and upload in batches of 50.

Deploy / undeploy entry points

  • deploy-web: Requires config.ow.auth_handler.getAuthHeader(); validates hostname and namespace (required + [a-zA-Z0-9.-]+); clears any existing namespace deployment before upload; returns the same https://{namespace}.{hostname}/index.html URL.
  • undeploy-web: Same auth requirement; checks that files exist, deletes via deploy-service, and fails clearly if the delete request does not succeed.

Removed / dependencies

  • Removes lib/getS3Creds.js and its tests (no more TVM S3 credential fetch or BYO config.s3.creds path in this library).
  • Drops @aws-sdk/client-s3; adds @adobe/aio-lib-env.
  • Bumps version to 7.2.0 and minimum Node to >= 20.

Tests

  • remote-storage, deploy-web, and undeploy-web tests rewritten around mocked fetch and the new API contract.
  • Adds coverage for auth failures, deploy-service error paths, hostname/namespace validation, and “clear before deploy” behavior.
  • Jest setup gains shared fakeAppConfig / namespace helpers for the new config shape.

Motivation

App Builder static hosting is moving behind deploy-service so clients no longer need S3 credentials or direct bucket access. This aligns aio-lib-web with that model and simplifies credential handling to a single OAuth/bearer flow the CLI already provides.

Breaking changes

  • Callers must supply config.ow.auth_handler.getAuthHeader(); deploy/undeploy will fail without it.
  • RemoteStorage API changed: constructor signature, and folderExists / emptyFolder now require appConfig (namespace-driven) rather than S3 prefix + bucket creds.
  • BYO S3 credentials via getS3Creds are no longer supported in this package.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • I have signed the Adobe Open Source CLA.
  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 PR Reviewer

This diff migrates the web deployment infrastructure from AWS S3 (via TVM credentials) to a new deploy-service REST API using bearer token authentication. The code is generally well-structured with good error handling, input validation, and comprehensive test coverage. A few issues remain around hardcoded URLs, leftover commented-out code, package.json inconsistency, and a minor security concern with base64 content encoding.

📝 8 suggestion(s) - Please review inline comments below.

⚠️ Diff exceeded 100 000 chars and was truncated — some files may not have been reviewed.


💡 How to re-trigger

Comment /review or /pr-reviewer on this PR

Comment thread lib/remote-storage.js
Comment thread lib/remote-storage.js Outdated
Comment thread lib/remote-storage.js
Comment thread lib/remote-storage.js
const allResults = []
if (!this._authToken) {
throw new Error('cannot upload files, Authorization is required')
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented-out sleep/rate-limiting code should be removed or replaced with a real implementation if rate limiting is a concern.

Comment thread package.json Outdated
"@adobe/aio-lib-core-tvm": "^4",
"@aws-sdk/client-s3": "^3.1045.0",
"@adobe/aio-lib-env": "^3.0.1",
"@smithy/node-http-handler": "^4.0.2",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smithy/node-http-handler is still listed as a dependency but its usage in remote-storage.js was commented out. If it's no longer needed, it should be removed from dependencies.

Comment thread src/deploy-web.js
throw new Error('config.app.hostname and config.ow.namespace are invalid')
}
if (await remoteStorage.folderExists('/', config)) {
await remoteStorage.emptyFolder('/', config)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warning message about overwriting an existing deployment was removed. Users will no longer be informed before their deployment is overwritten, which could be surprising. Consider re-adding the log warning.

Suggested change
await remoteStorage.emptyFolder('/', config)
if (await remoteStorage.folderExists('/', config)) {
if (log) {
log('warning: an existing deployment will be overwritten')
}
await remoteStorage.emptyFolder('/', config)
}

Comment thread src/deploy-web.js Outdated
if (!config.app.hostname || !config.ow.namespace) {
throw new Error('config.app.hostname and config.ow.namespace are required')
}
if (!config.app.hostname.match(/^[a-zA-Z0-9.-]+$/) || !config.ow.namespace.match(/^[a-zA-Z0-9.-]+$/)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex /^[a-zA-Z0-9.-]+$/ for hostname validation disallows valid hostnames with underscores (though uncommon) and may be overly strict or too lenient depending on requirements. Additionally, the error message doesn't distinguish which field is invalid, making debugging harder.

Suggested change
if (!config.app.hostname.match(/^[a-zA-Z0-9.-]+$/) || !config.ow.namespace.match(/^[a-zA-Z0-9.-]+$/)) {
if (!config.app.hostname.match(/^[a-zA-Z0-9.-]+$/)) {
throw new Error('config.app.hostname is invalid')
}
if (!config.ow.namespace.match(/^[a-zA-Z0-9_-]+$/)) {
throw new Error('config.ow.namespace is invalid')
}

Comment thread lib/remote-storage.js Outdated
const { NodeHttpHandler } = require('@smithy/node-http-handler')
const { ProxyAgent } = require('proxy-agent')
// const { NodeHttpHandler } = require('@smithy/node-http-handler')
// const { ProxyAgent } = require('proxy-agent')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commented-out imports for NodeHttpHandler and ProxyAgent suggest proxy support was removed. If proxy support is intentionally dropped, ensure this is documented. If it should be preserved, the fetch API used as replacement does not automatically honor HTTP_PROXY/HTTPS_PROXY environment variables in Node.js without additional configuration (e.g., using a custom dispatcher with undici/node-fetch).

@codecov
Copy link
Copy Markdown

codecov Bot commented May 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 PR Reviewer

The diff represents a significant architectural change from AWS S3/TVM-based storage to a custom deployment service API using bearer token auth. Most previous suggestions have been partially addressed (e.g., AIO_DEPLOYMENT_SERVICE_URL env var is now supported, proxy support is restored via undici). However, several issues remain: commented-out code is still present, the warning for overwriting deployments was removed without re-adding it, and the namespace validation regex still uses the same pattern for both fields despite different valid character sets.

📝 6 suggestion(s) (1 new, 5 re-raised)

⚠️ Diff exceeded 100 000 chars and was truncated — some files may not have been reviewed.


💡 How to re-trigger

Comment /review or /pr-reviewer on this PR

Comment thread lib/remote-storage.js Outdated
const { getCliEnv, PROD_ENV } = require('@adobe/aio-lib-env')

const fileExtensionPattern = /\*\.[0-9a-zA-Z]+$/
// or https://deploy-service.dev.app-builder.adp.adobe.io
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Re-raised] Commented-out code and stale comments ('// or https://...', '// or http://localhost:3000', etc.) should be removed before merging. These add noise and suggest incomplete cleanup.

Suggested change
// or https://deploy-service.dev.app-builder.adp.adobe.io
const deploymentServiceUrl = process.env.AIO_DEPLOYMENT_SERVICE_URL ||
(getCliEnv() === PROD_ENV
? 'https://deploy-service.app-builder.adp.adobe.io'
: 'https://deploy-service.stg.app-builder.corp.adp.adobe.io')

Comment thread lib/remote-storage.js
let relativeDir = path.dirname(path.relative(dir, file))
// path.relative returns '.' for files in the root directory, normalize to empty string
relativeDir = relativeDir === '.' ? '' : relativeDir

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Re-raised] Commented-out sleep/rate-limiting code should be removed or replaced with a real implementation if rate limiting is a concern.

Suggested change
const res = await Promise.all(fileBatch.map(async file => {

Comment thread lib/remote-storage.js
if (Object.keys(responseHeaders).length > 0) {
uploadParams.Metadata = responseHeaders
// server expected body is: { contentType, cacheControl, customHeaders: {}, file: { name, content } }
// file.name is the path relative to namespace (e.g., 'images/photo.jpg' or 'index.html')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Re-raised] File content is sent as base64-encoded string in a JSON body. For large static assets this doubles memory usage (Buffer + base64 string) and increases payload size by ~33%. Consider using multipart/form-data or a streaming upload approach for files above a certain size threshold.

Comment thread src/deploy-web.js Outdated
Comment thread src/deploy-web.js
Comment thread package.json Outdated
@github-actions github-actions Bot dismissed their stale review May 20, 2026 01:09

Superseded by new review

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 PR Reviewer

The diff represents a significant architectural change from AWS S3/TVM-based storage to a custom CDN deploy-service API using bearer token auth. Most previous suggestions have been addressed (namespace regex now uses /^[a-zA-Z0-9_-]+$/, warning log is re-added, separate hostname/namespace validation). However, several previously flagged issues remain: commented-out code in remote-storage.js, the commented-out sleep/rate-limiting code, the @adobe/aio-lib-core-tvm unused dependency, and the base64 upload concern. The test coverage is thorough and the refactor is generally clean.

📝 5 suggestion(s) (1 new, 4 re-raised)

⚠️ Diff exceeded 100 000 chars and was truncated — some files may not have been reviewed.


💡 How to re-trigger

Comment /review or /pr-reviewer on this PR

Comment thread lib/remote-storage.js Outdated
Comment thread lib/remote-storage.js
const allResults = []
if (!this._authToken) {
throw new Error('cannot upload files, Authorization is required')
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Re-raised] Commented-out sleep/rate-limiting code should be removed. If rate limiting is a concern, implement it properly or remove the comment entirely.

Suggested change
}
const res = await Promise.all(fileBatch.map(async file => {

Comment thread package.json Outdated
Comment thread lib/remote-storage.js
Comment thread src/deploy-web.js
log('warning: an existing deployment will be overwritten')
}
await remoteStorage.emptyFolder(config.s3.folder + '/')
await remoteStorage.emptyFolder('/', config)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uploadDir is called with config.s3.folder which may be undefined if s3 config is absent. Add a guard or default value to prevent passing undefined as basePath, which would cause uploadDir to throw a confusing error.

Suggested change
await remoteStorage.emptyFolder('/', config)
const s3Folder = config.s3?.folder ?? ''
await remoteStorage.uploadDir(dist, s3Folder, config, _log)

@github-actions github-actions Bot dismissed their stale review May 20, 2026 17:00

Superseded by new review

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 PR Reviewer

The diff represents a significant architectural migration from AWS S3/TVM-based file storage to a new deploy-service REST API using bearer token authentication. The code is generally well-structured and the tests are comprehensive. A few issues remain: commented-out code, potential undefined config access, and the joi package remains in dependencies even though it's no longer used in remote-storage.js.

📝 5 suggestion(s) (2 new, 3 re-raised)

⚠️ Diff exceeded 100 000 chars and was truncated — some files may not have been reviewed.


💡 How to re-trigger

Comment /review or /pr-reviewer on this PR

Comment thread lib/remote-storage.js
Comment thread lib/remote-storage.js
Comment thread package.json
"joi": "^17.2.1",
"klaw": "^4",
"lodash.clonedeep": "^4.5.0",
"mime-types": "^2.1.24",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

joi is still listed as a dependency but is no longer used in remote-storage.js (the Joi validation in the constructor was removed). Remove it to keep the dependency list clean.

Suggested change
"mime-types": "^2.1.24",
"lodash.clonedeep": "^4.5.0",

Comment thread src/deploy-web.js
await remoteStorage.emptyFolder('/', config)
}
const _log = log ? (f) => log(`deploying ${path.relative(dist, f)}`) : null
await remoteStorage.uploadDir(dist, config.s3.folder, config, _log)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Re-raised] config.s3?.folder may be undefined if the s3 config is absent, which would pass undefined as basePath to uploadDir, causing a confusing 'basePath must be a valid string' error. Add a guard or default.

Suggested change
await remoteStorage.uploadDir(dist, config.s3.folder, config, _log)
const s3Folder = config.s3?.folder ?? ''
await remoteStorage.uploadDir(dist, s3Folder, config, _log)

Comment thread src/undeploy-web.js
if (!(await remoteStorage.folderExists('/', config))) {
throw new Error(`cannot undeploy static files, there is no deployment for ${config.s3.folder}`)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message still references config.s3.folder which may be undefined under the new API. Update the error message to reference the namespace instead, or guard against undefined.

Suggested change
throw new Error(`cannot undeploy static files, there is no deployment for namespace ${config.ow.namespace}`)

@github-actions github-actions Bot dismissed their stale review May 20, 2026 19:17

Superseded by new review

@purplecabbage
Copy link
Copy Markdown
Member Author

/review

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 PR Reviewer

This is a well-structured refactor replacing AWS S3 direct SDK usage with a deploy-service HTTP API. The code is generally clean, tests are comprehensive, and the migration is logical. However, several previously raised issues remain unaddressed, and a few new concerns exist in the updated files.

📝 6 suggestion(s) (2 new, 4 re-raised)

⚠️ Diff exceeded 100 000 chars and was truncated — some files may not have been reviewed.


💡 How to re-trigger

Comment /review or /pr-reviewer on this PR

Comment thread lib/remote-storage.js
const { NodeHttpHandler } = require('@smithy/node-http-handler')
const { ProxyAgent } = require('proxy-agent')
const { EnvHttpProxyAgent } = require('undici')
const { codes, logAndThrow } = require('./StorageError')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Re-raised] Commented-out code and stale comments ('// dev deploy => ...', '// run local => ...') should be removed before merging.

Suggested change
const { codes, logAndThrow } = require('./StorageError')
const deploymentServiceUrl = process.env.AIO_DEPLOYMENT_SERVICE_URL ||
(getCliEnv() === PROD_ENV
? 'https://deploy-service.app-builder.adp.adobe.io'
: 'https://deploy-service.stg.app-builder.corp.adp.adobe.io')

Comment thread lib/remote-storage.js
if (!this._authToken) {
throw new Error('cannot upload files, Authorization is required')
}
while (fileBatch.length > 0) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Re-raised] Commented-out sleep/rate-limiting code should be removed. If rate limiting is needed, implement it properly or open a tracked issue.

Suggested change
while (fileBatch.length > 0) {
const res = await Promise.all(fileBatch.map(async file => {

Comment thread package.json
"fs-extra": "^11",
"joi": "^17.2.1",
"klaw": "^4",
"lodash.clonedeep": "^4.5.0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

joi is still listed as a dependency but is no longer used in remote-storage.js. Remove it to keep the dependency list clean.

Suggested change
"lodash.clonedeep": "^4.5.0",
"lodash.clonedeep": "^4.5.0",

Comment thread src/deploy-web.js
await remoteStorage.emptyFolder('/', config)
}
const _log = log ? (f) => log(`deploying ${path.relative(dist, f)}`) : null
await remoteStorage.uploadDir(dist, config.s3.folder, config, _log)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Re-raised] config.s3?.folder may be undefined if the s3 config is absent, causing a confusing 'basePath must be a valid string' error downstream in uploadDir. Add a guard or default.

Suggested change
await remoteStorage.uploadDir(dist, config.s3.folder, config, _log)
const s3Folder = config.s3?.folder ?? ''
await remoteStorage.uploadDir(dist, s3Folder, config, _log)

Comment thread src/undeploy-web.js

const remoteStorage = new RemoteStorage(creds)
const remoteStorage = new RemoteStorage(bearerToken)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Re-raised] Error message still references config.s3.folder which may be undefined under the new API. Update to reference the namespace instead.

Suggested change
throw new Error(`cannot undeploy static files, there is no deployment for namespace ${config.ow.namespace}`)

Comment thread lib/remote-storage.js
@github-actions github-actions Bot dismissed their stale review May 20, 2026 19:25

Superseded by new review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant