Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/stale-cache-recovery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"twilio-run": patch
---

Automatically recover from stale `.twiliodeployinfo` cache entries during deploy. When a cached service SID references a service that was deleted externally (via Console or API), the deploy now detects the 20404 error, clears the stale cache entry, and retries the deployment instead of failing with a confusing error message.
27 changes: 26 additions & 1 deletion packages/twilio-run/src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '../flags';
import { printConfigInfo, printDeployedResources } from '../printers/deploy';
import { HttpError, saveLatestDeploymentData } from '../serverless-api/utils';
import { clearDeployInfoCache } from '../utils/deployInfoCache';
import {
getDebugFunction,
getOraSpinner,
Expand Down Expand Up @@ -123,7 +124,31 @@ export async function handler(
client.on('status-update', (evt) => {
spinner.text = evt.message + '\n';
});
const result = await client.deployLocalProject(config);

let result;
try {
result = await client.deployLocalProject(config);
} catch (deployErr) {
// If the cached service SID is stale (service was deleted externally),
// clear the cache and retry without the stale SID
const isStaleService = config.serviceSid && deployErr.code === 20404;
if (isStaleService) {
const accountSid = config.username.startsWith('AC')
? config.username
: externalCliOptions?.accountSid;
logger.warn(
`Cached service ${config.serviceSid} not found (it may have been deleted). Clearing cache and retrying.`
);
if (accountSid) {
clearDeployInfoCache(config.cwd, accountSid, config.region);
}
config.serviceSid = undefined;
result = await client.deployLocalProject(config);
} else {
throw deployErr;
}
}

spinner.text = 'Serverless project successfully deployed\n';
spinner.succeed();
printDeployedResources(config, result, config.outputFormat);
Expand Down
24 changes: 24 additions & 0 deletions packages/twilio-run/src/utils/deployInfoCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,30 @@ export function getDeployInfoCache(
return {};
}

export function clearDeployInfoCache(
baseDir: string,
username: string,
region: string = 'us1',
deployInfoCacheFileName: string = '.twiliodeployinfo'
): void {
const fullPath = path.resolve(baseDir, deployInfoCacheFileName);
debug('Clearing stale deploy info cache entry for %s:%s', username, region);
const currentDeployInfoCache = getDeployInfoCache(
baseDir,
deployInfoCacheFileName
);

delete currentDeployInfoCache[`${username}:${region}`];
delete currentDeployInfoCache[username];

try {
const data = JSON.stringify(currentDeployInfoCache, null, '\t');
fs.writeFileSync(fullPath, data, 'utf8');
} catch (err) {
debug('Failed to clear deploy info cache entry');
}
}

export function updateDeployInfoCache(
baseDir: string,
username: string,
Expand Down