Skip to content

Commit 654f31d

Browse files
iivvaannxxclaude
andcommitted
fix: skip dist check and guard ow.package when packages is empty
When a manifest declares `packages: {}` the build step produces no output so the dist directory is never created. The existing dist check was throwing "missing files in dist/…, maybe you forgot to build your actions?" even though there was nothing to build. * deploy-actions.js: gate the dist check on `hasAnyActions` — only validate the build directory when at least one package actually defines actions. Two tests cover both branches of `pkg.actions || {}` (empty packages and package with no actions key), keeping 100% branch coverage on deploy-actions.js. * utils.js (replacePackagePlaceHolder): guard the `packageNames[0]` assignment so an empty packages object no longer clobbers `ow.package` with `undefined`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 86eba0a commit 654f31d

4 files changed

Lines changed: 61 additions & 2 deletions

File tree

src/deploy-actions.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,12 @@ async function deployActions (config, deployConfig = {}, logFunc) {
5757
// checks
5858
// a. missing credentials
5959
utils.checkOpenWhiskCredentials(config)
60-
// b. missing build files
60+
// b. missing build files — only required when at least one package defines actions
6161
const dist = config.actions.dist
62+
const hasAnyActions = Object.values(config.manifest.full.packages)
63+
.some(pkg => Object.keys(pkg.actions || {}).length > 0)
6264
if (
65+
hasAnyActions &&
6366
(!deployConfig.filterEntities || deployConfig.filterEntities.actions) &&
6467
(!fs.pathExistsSync(dist) || !fs.lstatSync(dist).isDirectory() || !fs.readdirSync(dist).length === 0)
6568
) {

src/utils.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2104,7 +2104,9 @@ function replacePackagePlaceHolder (config) {
21042104
// Using custom package name.
21052105
// Set config.ow.package so that syncProject can use it as project name for annotations.
21062106
const packageNames = Object.keys(packages)
2107-
modifiedConfig.ow.package = packageNames[0]
2107+
if (packageNames.length > 0) {
2108+
modifiedConfig.ow.package = packageNames[0]
2109+
}
21082110
}
21092111
return modifiedConfig
21102112
}

test/deploy.actions.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,20 @@ test('Deploy actions should pass if there are no build files and filter does not
583583
await expect(deployActions(global.sampleAppConfig, { filterEntities: { triggers: ['trigger1'] } })).resolves.toEqual({})
584584
})
585585

586+
test('Deploy actions should succeed when packages: {} (empty packages, no actions defined)', async () => {
587+
const emptyPackagesConfig = deepCopy(global.sampleAppConfig)
588+
emptyPackagesConfig.manifest.full.packages = {}
589+
runtimeLibUtils.processPackage.mockReturnValue(deepCopy(mockEntities))
590+
await expect(deployActions(emptyPackagesConfig)).resolves.toBeDefined()
591+
})
592+
593+
test('Deploy actions should succeed when a package has no actions key (pkg.actions || {} guard)', async () => {
594+
const noActionsPkgConfig = deepCopy(global.sampleAppConfig)
595+
noActionsPkgConfig.manifest.full.packages = { emptyPkg: {} }
596+
runtimeLibUtils.processPackage.mockReturnValue(deepCopy(mockEntities))
597+
await expect(deployActions(noActionsPkgConfig)).resolves.toBeDefined()
598+
})
599+
586600
// lonely
587601
test('if actions are deployed and part of the manifest it should return their url', async () => {
588602
addSampleAppReducedFiles()

test/utils.test.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3097,3 +3097,43 @@ describe('loadIMSCredentialsFromEnv', () => {
30973097
expect(result.scopes).toBe('not json')
30983098
})
30993099
})
3100+
3101+
describe('replacePackagePlaceHolder', () => {
3102+
test('leaves ow.package unchanged when packages is empty (packages: {})', () => {
3103+
const config = {
3104+
ow: { package: 'my-pkg' },
3105+
manifest: {
3106+
packagePlaceholder: '__APP_PACKAGE__',
3107+
full: { packages: {} }
3108+
}
3109+
}
3110+
const result = utils.replacePackagePlaceHolder(config)
3111+
expect(result.ow.package).toBe('my-pkg')
3112+
})
3113+
3114+
test('renames placeholder package to ow.package', () => {
3115+
const config = {
3116+
ow: { package: 'my-pkg' },
3117+
manifest: {
3118+
packagePlaceholder: '__APP_PACKAGE__',
3119+
full: { packages: { __APP_PACKAGE__: { actions: {} } } }
3120+
}
3121+
}
3122+
const result = utils.replacePackagePlaceHolder(config)
3123+
expect(result.ow.package).toBe('my-pkg')
3124+
expect(result.manifest.full.packages['my-pkg']).toBeDefined()
3125+
expect(result.manifest.full.packages.__APP_PACKAGE__).toBeUndefined()
3126+
})
3127+
3128+
test('sets ow.package to first package name when no placeholder matches', () => {
3129+
const config = {
3130+
ow: { package: 'ignored' },
3131+
manifest: {
3132+
packagePlaceholder: '__APP_PACKAGE__',
3133+
full: { packages: { 'custom-pkg': { actions: {} } } }
3134+
}
3135+
}
3136+
const result = utils.replacePackagePlaceHolder(config)
3137+
expect(result.ow.package).toBe('custom-pkg')
3138+
})
3139+
})

0 commit comments

Comments
 (0)