fix(template): the generated app could not be built at all - #39
Merged
Conversation
A wizard-generated app failed `npm run build` out of the box, so every repo OpenBuild published was uninstallable. Three separate causes, each verified by running a real build of the emitted 72-file scaffold. 1. `terser-webpack-plugin` was declared by nobody. `@nextcloud/webpack-vue-config` requires it but does not depend on it and does not list it as a peer, so nothing installed it and webpack could not even start: `[webpack-cli] Error: Cannot find module 'terser-webpack-plugin'`. 2. `@nextcloud/axios@2.6.0` DROPPED its `require` export condition — a breaking change shipped as a minor bump. `@nextcloud/vue`'s CJS build require()s it, so the caret range `^2.5.0` floated into an unusable pair and webpack reported 14 copies of: `"." is not exported under the conditions ["require", …] from @nextcloud/axios`. Pinning the direct dependency is NOT enough — npm nests its own copy under `@nextcloud/vue`, which is the copy that actually breaks — so this needs an `overrides` entry to apply transitively. 2.5.2 still ships `./dist/index.cjs`. 3. `@conduction/nextcloud-vue` bundles a `@nextcloud/dialogs` chunk that reaches for Node's `path`. Webpack 5 stopped auto-polyfilling core modules, so the browser build could not resolve it. Mapping just `path` to `path-browserify` rather than adding a blanket node-polyfill plugin: it is the only core module the dependency touches, and a blanket polyfill grows every generated app's bundle for nothing. Verified end to end on the emitted scaffold: `npm install` (1321 packages) then `npm run build` goes from **14 errors → 1 → 0**, producing real bundles. Only the pre-existing entrypoint-size warnings remain. Worth a CI guard: generate an app and run `npm ci && npm run build`. All three defects are invisible to any test that stops at "the files were generated". Fixes #38.
Adding the @nextcloud/axios override REPLACED the existing libxmljs2 one instead of joining it. libxmljs2 is a native XML parser with a history of CVEs and `^0.37.0` is a security floor, so dropping it would have quietly lowered the generated app's baseline while fixing its build. Both entries belong in the map.
rubenvdlinde
added a commit
that referenced
this pull request
Jul 27, 2026
…#40) Publishing a generated app failed with 502 github_unreachable. GitHub was perfectly reachable — I verified /user, /repos/*, git refs GET and PATCH, blob create and tree create all succeeding through the same broker with the same credential before instrumenting the swallowed log line and finding the truth: POST /repos/{owner}/{repo}/git/trees -> 403 {"message":"Resource not accessible by personal access token"} The scaffold always emits 9 .github/workflows/* files, and writing those requires the token's workflow permission. A precise, actionable message from GitHub was being discarded in favour of a transport error that sends the reader at the network and the allow-rules. Two fixes: - 403 now maps to its own OUTCOME_FORBIDDEN / HTTP 403 rather than being folded into the gateway error. - brokerJson() logged the swallowed cause at debug and did not log non-2xx responses at all. On any instance running a normal loglevel — which the fleet does deliberately after the 163GB-log incident — an operator saw a bare 502 and nothing else. Now logged at warning with method, path, status and body prefix, which is the whole diagnosis. Merge note: this branch was stacked on #39 and still carried the package.json version that dropped the libxmljs2 security override. Merged development in and kept development's, so the pin survives.
rubenvdlinde
added a commit
that referenced
this pull request
Aug 5, 2026
#135) The embedded template snapshot's appinfo/info.xml hardcoded `<licence>agpl</licence>` while the very same file's description read "Free and open source under the EUPL-1.2 license". Every app OpenBuild has ever generated inherited that contradiction, and nothing failed. The `{{license}}` token was already wired end to end — ExportJobService -> RunExportJob -> PlaceholderResolver, defaulting to EUPL-1.2 at all three layers — and reached exactly one file, src/manifest.json. info.xml never consumed it. `<licence>{{license}}</licence>` closes that, and as a side effect a caller who picks a licence now gets the licence they picked. EUPL-1.2 is the schema-correct spelling, not a preference: the Nextcloud appstore's own info.xsd enumerates `EUPL-1.2` as an allowed <licence> value. It does NOT enumerate `eupl` — which is what upstream nextcloud-app-template's `main` branch currently ships, and is separately invalid. A test first, and shown to fail. testGeneratedAppDeclaresTheRequestedLicence() exports a real app and asserts info.xml declares EUPL-1.2 and does not declare agpl. Against the pre-fix snapshot it failed with the generated app's info.xml in the message; after the one-line change all 3 integration tests and all 746 unit tests pass. Nothing in the suite covered this before: the existing unresolved-placeholder assertion matches /\{\{[a-zA-Z]+\}\}/, and a hardcoded wrong value contains no placeholder to be left unresolved. Why this is a one-line Edit and not the whole-tree refresh docs/releasing.md prescribes. The refresh is unsafe as written, measured three ways: 1. There is no upstream ref this tree fast-forwards from. The declared sourceCommit 7ee06aae is not an ancestor of upstream main OR development; `git branch -r --contains` places it only on two abandoned wsl-rescue/* branches. 2. The snapshot carries OpenBuild-only fixes upstream never received — at least 10 commits have edited files inside lib/Resources/template/ since, among them c87d8c4 "fix(template): the generated app could not be built at all (#39)" and 12da26f "fix(export): make exported app a Tier-4 manifest consumer (ADR-024)". `rsync --delete` reverts both. 3. The dialects differ. OpenBuild resolves {{token}}; upstream's info.xml uses {APP_NAME}/{APP_SUMMARY}/{APP_DESCRIPTION}. PlaceholderResolver does not know that dialect and the unresolved-placeholder assertion does not match it, so a refreshed tree would ship literal {APP_NAME} into every generated app and no test would fail. .snapshot-meta.json and docs/releasing.md now say so at the point of use, so the next agent does not run the rsync on the strength of the doc alone. docs/releasing.md also now records that the "CI drift check" it describes does not exist — no workflow mentions drift or nextcloud-app-template — which is why the snapshot sat 86 days stale shipping the wrong licence. .path-manifest.txt no longer lists .snapshot-meta.json: the regeneration command in docs/releasing.md excludes it, so the checked-in manifest disagreed with its own generator. Co-authored-by: Ruben van der Linde <juan.claude@conduction.nl>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #38.
A wizard-generated app failed
npm run buildout of the box, so every repo OpenBuild published was uninstallable. Three separate causes, each found and verified by running a real build of the emitted 72-file scaffold.1.
terser-webpack-pluginwas declared by nobody@nextcloud/webpack-vue-configrequires it but neither depends on it nor lists it as a peer (its peers are@babel/core,babel-loader,css-loader,node-polyfill-webpack-plugin,sass,sass-loader,style-loader,ts-loader). Nothing installed it, so webpack could not start.2.
@nextcloud/axios@2.6.0dropped itsrequireexport — in a minor bump@nextcloud/vue's CJS buildrequire()s it, so the caret range^2.5.0floated into an unusable pair and webpack emitted 14 errors:Pinning the direct dependency is not enough — npm nests its own copy under
@nextcloud/vue, and that is the copy that breaks:So this needs an
overridesentry, which applies transitively.3. Node's
pathwas left unpolyfilled@conduction/nextcloud-vuebundles a@nextcloud/dialogschunk that reaches for Node'spath. Webpack 5 stopped auto-polyfilling core modules, so the browser build could not resolve it.Mapped just
pathtopath-browserifyrather than adding a blanket node-polyfill plugin — it is the only core module the dependency actually touches, and a blanket polyfill would grow every generated app's bundle for nothing.Verification
Ran the emitted scaffold through a real
npm install+npm run buildafter each fix:Only the pre-existing entrypoint-size warnings remain.
Suggested follow-up
A CI guard that generates an app and runs
npm ci && npm run build. All three defects are invisible to any test that stops at "the files were generated", which is why a scaffold that never built shipped.Related: #35 — push fails with a misleading
github_unreachable; the real cause there is GitHub rejecting the tree because the scaffold ships 9.github/workflows/*files and the brokered token lacks theworkflowpermission.