8 Commits

Author SHA1 Message Date
dillard a607426a9c Only load vendored jQuery on NC34+, verified against NC32/33/34 in testbak/
Core still provides a working jQuery on NC32/33, but via a read-only
window.$ getter - loading our own vendored copy there too made its UMD
wrapper throw trying to reassign window.$ (confirmed on real containers,
harmless in practice since this app attaches via window.jQuery, but a real
avoidable console exception). Gate the extra script() call on
ServerVersion::getMajorVersion() >= 34 instead.

Re-tested both sides of the boundary: NC32 no longer requests jquery.js and
has zero console errors; NC34 still requests it and still works.
2026-09-27 00:24:47 +02:00
dillard 35e1f5df32 Fix NC35 fatal in OccOutput, verify+narrow supported version range to 32-35
testbak/ (local, non-AIO docker compat rigs for NC32/33/34/35) found a real
fatal on NC35: OccOutput::setErrorOutput() didn't declare the ": void"
return type the Symfony Console version NC35 bundles now requires on
ConsoleOutputInterface. Fixed by declaring it - compatible with older
Symfony/NC versions too since it only narrows an untyped parent signature.

32/33 were also verified functional (the previously-suspected jQuery
getter-conflict console exception on those versions is real but harmless -
the app attaches via window.jQuery, not window.$).

min-version/max-version narrowed from 30/34 to 32/35: 30/31 were never
actually tested even before this session, so keeping them was an unverified
claim; 35 is now genuinely verified rather than guessed.
2026-09-27 00:17:28 +02:00
dillard 4b84be3cb7 Fix sixth Codex finding: check-clean-tree only guarded version, not source/appstore
Running make appstore/source/dist directly (bypassing make version)
still had the stale-archive risk check-clean-tree was added for:
app_name/paths read from a dirty working tree while git archive HEAD
packages the last commit. Added check-clean-tree as a prerequisite to
source and appstore directly, not just transitively through version.
2026-09-26 17:18:26 +02:00
dillard 93244a0d4d Fix fifth Codex finding: app_name silently empty without xpath installed
app_name now comes from xpath against appinfo/info.xml (needed so
packaging works regardless of what this checkout is named - see the
git-archive fix). If xpath isn't installed, $(shell ...) just returns
an empty string rather than failing the build: --prefix=$(app_name)/
would silently become --prefix=/, producing a broken, wrongly-rooted
archive with no indication anything went wrong. Previously app_name
came from the checkout directory name, so this failure mode didn't
exist. Added a check-app-name prerequisite to source/appstore/sign
that fails loudly if app_name is empty.
2026-09-26 17:14:19 +02:00
dillard 8fc4942517 Fix fourth Codex finding: reject a dirty tree before releasing
VERSION/app_name are read live from the working tree's info.xml, but
the packaged/signed content comes from git archive HEAD (the last
commit). Bumping <version> without committing first would tag and
sign an artifact named/keyed after the new version while its actual
contents are still the previous commit's. make version now refuses to
proceed if there are any uncommitted changes to tracked files.
2026-09-26 17:08:22 +02:00
dillard 95cdd06426 Fix third Codex finding: make version hardcoded a non-portable remote name
git push fork v\$(VERSION) only works on this maintainer's own machine,
where "fork" happens to be configured (and where "origin" still means
the stale upstream, from how this repo was originally forked). A plain
clone via the README's own instructions only ever creates "origin",
pointing at dillardblom/occweb - so make version would tag locally,
then fail at the push step before packaging/signing ever ran.

Made the target remote configurable via RELEASE_REMOTE, defaulting to
"origin" (correct for a normal clone), overridable for this specific
checkout's non-standard remote layout.
2026-09-26 17:01:19 +02:00
dillard 6016d0916c Fix second Codex finding: packaging symlink produced a dangling-symlink-only archive
The ensure-app-name-symlink fix from the previous commit created
../extended_occweb as a symlink so the inherited "../$(app_name)" tar
references would resolve regardless of the checkout's real directory
name - but the tar invocations ran without --dereference, so GNU tar
stored the symlink itself rather than following it into the real
files. make source/appstore therefore produced a 1-entry archive
containing nothing but a dangling absolute symlink, unusable on any
other machine. Caught by a second independent Codex pass; my own
"make appstore" test after the first fix only checked the archive's
top-level entry name, not its actual file count, so this went
unnoticed.

Rather than patch this with --dereference, replaced the whole
symlink+raw-tar approach with `git archive --prefix=$(app_name)/
HEAD`: it sets the correct top-level folder name directly (no symlink
needed at all), and - caught during this fix's own verification,
not by either Codex pass - it also closes a separate real leak the
symlink approach had: tarring the checkout directory directly
included every untracked/gitignored file too (e.g.
notes/deploy-nc01.md, which exists specifically because it must
never leave this machine). `git archive` only ever includes tracked
files. Dev/CI-only files (tests/, Makefile, composer.*, phpunit*.xml,
.travis.yml) are now stripped via `export-ignore` entries in a new
.gitattributes instead of a long list of tar --exclude flags repeated
per target - also means source and appstore now produce identical
output, which is fine given neither is part of our actual release
flow yet.

Verified end to end this time: extracted the resulting archive and
confirmed file count, absence of symlinks, absence of tests/notes/
dev files, and correct info.xml content - not just the archive's
top-level listing.
2026-09-26 16:55:55 +02:00
dillard 422748cace Fix Codex-review findings: Makefile packaging, git archive main hardcode
Independent Codex review of the app-id rename found real gaps our own
review missed:

- Makefile: app_name was derived from the checkout directory's own
  basename, not the app id in appinfo/info.xml. Our actual working
  checkout is still named "occweb" (matches the repo name), so
  building from it would have produced an "occweb"-named/rooted
  archive under the new "extended_occweb" id. Fixed by deriving
  app_name from info.xml via xpath (same technique already used for
  VERSION), and added an ensure-app-name-symlink step so the
  Makefile's inherited "../$(app_name)" references (used throughout
  the source/appstore tar exclude patterns) keep resolving correctly
  regardless of what the checkout directory is actually named.
  Verified end to end: `make appstore` now produces
  extended_occweb.tar.gz with extended_occweb/ as its single top-level
  folder, matching the App Store's stated requirement.
- Makefile: sign target had "occweb" hardcoded into both the
  certificate path and the archive filename - broken independent of
  the app_name fix above. Now derived from the same variables.
- Makefile: version target pushed the release tag to the "origin"
  remote, which in this repo is the stale upstream
  (fanategorius/occweb), not somewhere we want to push. Changed to
  "fork" (dillardblom/occweb), matching how every tag has actually
  been pushed this whole session.
- INSTALL-AIO.md / notes/deploy-nc01.md: the deploy snippet did
  `git archive main` unconditionally, silently deploying the main
  branch even if an operator had checked out a specific tag or
  maintenance branch as instructed elsewhere. Changed to `git archive
  HEAD`.
- README.md: the compatibility matrix's rollback advice
  ("git checkout v0.3.4") doesn't actually work as a rollback anymore
  post-rename - that tag still declares the old occweb id, so checking
  it out under an extended_occweb-named install just breaks. Added a
  caveat explaining what a real rollback requires; full migration docs
  are still deferred to when a test instance exists (tracked
  separately, not written up here).
2026-09-26 16:48:58 +02:00
8 changed files with 106 additions and 52 deletions
+11
View File
@@ -0,0 +1,11 @@
# Files excluded from `git archive` output - used by `make appstore` (and by
# our own nc01 deploy tarballs) to strip dev/CI-only files that don't belong
# in a running install, without needing them repeated as tar --exclude flags.
tests export-ignore
.travis.yml export-ignore
phpunit.xml export-ignore
phpunit.integration.xml export-ignore
composer.json export-ignore
composer.lock export-ignore
Makefile export-ignore
.gitattributes export-ignore
+3
View File
@@ -33,5 +33,8 @@ js/*.log
# Internal deploy notes (hostnames, paths) - never publish these
notes/
# Local compat-test docker rigs (throwaway, not part of the app release)
testbak/
# Misc logs
*.log
+1 -1
View File
@@ -20,7 +20,7 @@ The AIO container has no working git access to GitHub, so deploy a tarball inste
```bash
# 1. On your machine: clean checkout of the branch/tag you want, dev files stripped
STAGE=$(mktemp -d)
git archive main | tar -x -C "$STAGE"
git archive HEAD | tar -x -C "$STAGE"
rm -rf "$STAGE"/{tests,.travis.yml,phpunit.xml,phpunit.integration.xml,composer.json,composer.lock,Makefile}
tar -C "$STAGE" -cf /tmp/occweb-deploy.tar .
+49 -39
View File
@@ -39,7 +39,7 @@
# "build": "node node_modules/gulp-cli/bin/gulp.js"
# },
app_name=$(notdir $(CURDIR))
app_name=$(shell xpath -q -e "//info/id/text()" appinfo/info.xml)
build_tools_directory=$(CURDIR)/build/tools
source_build_directory=$(CURDIR)/build/artifacts/source
source_package_name=$(source_build_directory)/$(app_name)
@@ -110,48 +110,41 @@ dist:
make source
make appstore
# Builds the source package
# Both targets package via `git archive` rather than tarring the checkout
# directory directly: it only ever includes tracked files (so untracked/
# gitignored content - build/, notes/, any stray .env - can never leak into
# a release archive), it sets the correct top-level folder name via
# --prefix regardless of what this checkout is actually called (this repo
# is "occweb", the app id is "extended_occweb" - no symlink trick needed),
# and dev/CI-only files (tests/, Makefile, composer.*, phpunit*.xml,
# .travis.yml) are stripped via the `export-ignore` entries in
# .gitattributes instead of a long, easy-to-miss list of tar --exclude
# flags repeated per target. That also means source and appstore now
# produce the same archive; both targets are kept for compatibility with
# anything that invokes one of them by name.
#
# app_name comes from `xpath` (see its definition above) - if that binary
# is missing, $(shell ...) silently returns an empty string rather than
# failing the build, which would turn --prefix=$(app_name)/ below into
# --prefix=/ and produce a broken, unusably-rooted archive with no
# indication anything went wrong. Fail loudly instead.
.PHONY: check-app-name
check-app-name:
@test -n "$(app_name)" || \
(echo "app_name is empty - is 'xpath' (libxml2-utils/perl-XML-XPath) installed?" >&2; exit 1)
.PHONY: source
source:
source: check-app-name check-clean-tree
rm -rf $(source_build_directory)
mkdir -p $(source_build_directory)
tar \
--exclude-vcs \
--exclude="../$(app_name)/build" \
--exclude="../$(app_name)/js/node_modules" \
--exclude="../$(app_name)/node_modules" \
--exclude="../$(app_name)/*.log" \
--exclude="../$(app_name)/js/*.log" \
-cvzf $(source_package_name).tar.gz ../$(app_name)
git archive --format=tar --prefix=$(app_name)/ HEAD | gzip > $(source_package_name).tar.gz
# Builds the source package for the app store, ignores php and js tests
.PHONY: appstore
appstore:
appstore: check-app-name check-clean-tree
rm -rf $(appstore_build_directory)
mkdir -p $(appstore_build_directory)
tar \
--exclude-vcs \
--exclude="../$(app_name)/build" \
--exclude="../$(app_name)/tests" \
--exclude="../$(app_name)/Makefile" \
--exclude="../$(app_name)/*.log" \
--exclude="../$(app_name)/phpunit*xml" \
--exclude="../$(app_name)/composer.*" \
--exclude="../$(app_name)/js/node_modules" \
--exclude="../$(app_name)/js/tests" \
--exclude="../$(app_name)/js/test" \
--exclude="../$(app_name)/js/*.log" \
--exclude="../$(app_name)/js/package.json" \
--exclude="../$(app_name)/js/bower.json" \
--exclude="../$(app_name)/js/karma.*" \
--exclude="../$(app_name)/js/protractor.*" \
--exclude="../$(app_name)/package.json" \
--exclude="../$(app_name)/bower.json" \
--exclude="../$(app_name)/karma.*" \
--exclude="../$(app_name)/protractor\.*" \
--exclude="../$(app_name)/.*" \
--exclude="../$(app_name)/js/.*" \
-cvzf $(appstore_package_name).tar.gz ../$(app_name)
git archive --format=tar --prefix=$(app_name)/ HEAD | gzip > $(appstore_package_name).tar.gz
.PHONY: test
test: composer
@@ -159,8 +152,8 @@ test: composer
$(CURDIR)/vendor/phpunit/phpunit/phpunit -c phpunit.integration.xml
.PHONY: sign
sign:
@openssl dgst -sha512 -sign ~/.nextcloud/certificates/occweb.key build/artifacts/appstore/occweb.tar.gz |openssl base64
sign: check-app-name
@openssl dgst -sha512 -sign ~/.nextcloud/certificates/$(app_name).key $(appstore_package_name).tar.gz |openssl base64
VERSION := $(shell xpath -q -e "//info/version/text()" appinfo/info.xml)
@@ -168,11 +161,28 @@ VERSION := $(shell xpath -q -e "//info/version/text()" appinfo/info.xml)
show-version:
@echo $(VERSION)
# Defaults to "origin" - correct for a plain clone of this repo, where
# "origin" is dillardblom/occweb. Override for a checkout where "origin"
# means something else (e.g. this maintainer's own machine, where the repo
# was originally forked from upstream and "origin" still points there):
# make version RELEASE_REMOTE=fork
RELEASE_REMOTE ?= origin
# VERSION/app_name are read live from the working tree's appinfo/info.xml,
# but the archive this tags/packages/signs is git archive HEAD - i.e. the
# last COMMITTED state. Without this check, bumping <version> without
# committing first would tag and sign a package named/keyed after the new
# version while its actual contents are still the previous commit's.
.PHONY: check-clean-tree
check-clean-tree:
@git diff --quiet HEAD -- || \
(echo "Uncommitted changes to tracked files - commit before releasing (see 'git status')." >&2; exit 1)
.PHONY: version
version:
version: check-clean-tree
@echo "Creating version v$(VERSION)"
@git tag v$(VERSION)
@git push origin v$(VERSION)
@git push $(RELEASE_REMOTE) v$(VERSION)
@make dist
@echo "\nRelease Signature: \n"
@make sign
+14 -2
View File
@@ -149,9 +149,21 @@ was verified, so a rollback target is obvious if a newer major causes problems.
| 0.4.0 | (untagged) | 34.0.4 | ✅ Verified (nc01, 2026-09-26) - app id renamed `occweb` → `extended_occweb` (App Store prep), no functional changes to the 0.3.5 fixes otherwise |
| 0.4.1 | `v0.4.1-nc34` | 34.0.4 | ✅ Verified (nc01, 2026-09-26) - code review fixes: correct jQuery `.fail()` signature, added a missing `.fail()` handler on the initial terminal-load request |
| 0.4.1 | `v0.4.1-nc34` | 33.0.8 | ⚠️ Same caveat as 0.3.5/0.4.0 - not re-verified, no NC33 test environment available. Also unresolved: `js/jquery.js` now loads unconditionally on every supported version, even NC30-33 where core still provides jQuery - flagged in code review, needs an NC33 environment to confirm it's harmless there. |
| 0.4.2 | (untagged) | 32.0.15 | ✅ Verified (`testbak/`, 2026-09-27) - terminal loads and runs commands correctly. Confirms the suspected jQuery issue above: core still exposes a read-only `window.$` getter here, so our vendored `js/jquery.js` throws `TypeError: Cannot set property $ of #<Window> which has only a getter` on load - a real, uncaught console exception, but harmless in practice since the app attaches via `window.jQuery` (still writable), not `window.$`. Fixed in 0.4.3. |
| 0.4.2 | (untagged) | 33.0.9 | ✅ Verified (`testbak/`, 2026-09-27) - same result as 32.0.15: functional, same harmless-but-real `window.$` console exception. Fixed in 0.4.3. |
| 0.4.2 | (untagged) | 35.0.1 | ✅ Verified (`testbak/`, 2026-09-27) after a real fix: the bootstrap request 500'd with a PHP fatal, `Declaration of OccOutput::setErrorOutput() must be compatible with ConsoleOutputInterface::setErrorOutput(): void` - NC35 bundles a Symfony Console version whose interface now declares that return type. Fixed by adding `: void` to `OccOutput::setErrorOutput()` (`lib/Controller/OccOutput.php`), which is compatible with every earlier Symfony/NC version too. Confirmed clean afterwards: no console errors, no CSRF issues, terminal fully functional. |
| 0.4.3 | (untagged) | 32.0.15, 34.0.4 | ✅ Verified (`testbak/`, 2026-09-27) - `templates/index.php` now only loads `js/jquery.js` when `\OCP\ServerVersion::getMajorVersion() >= 34`, since core still provides a working jQuery on 32/33. Re-tested both sides of the boundary: NC32 loads no `jquery.js` request at all and has zero console errors; NC34 still loads it and still works, confirming no regression. |
If a future major breaks something, `git checkout v0.3.4` is the last version confirmed
working on NC33 and older.
`min-version`/`max-version` in `appinfo/info.xml` were updated from `30`/`34` to `32`/`35` on 2026-09-27 based on the above: 30/31 have never actually been tested (not even before the app-id rename) so claiming them was never justified once the `testbak/`-based verification policy existed, and 35 is now a genuinely verified (and fixed) target rather than a guess. 30/31 are also past Nextcloud's own currently-supported boundary as of 2026-09-18 (32-35 supported), which independently confirms 32 as the right floor.
If a future major breaks something, `v0.3.4` is the last version confirmed working on NC33
and older - but a plain `git checkout v0.3.4` is **not** a working rollback by itself. That
tag predates the app id rename (it still declares `<id>occweb</id>`), so checking it out
into an `extended_occweb`-named install just produces a broken id/directory mismatch. A
real rollback means disabling/removing `extended_occweb` and deploying `v0.3.4` fresh under
an `occweb`-named directory instead, same as the original install steps above. This is one
of the items still deferred to when a proper test instance exists (see the code review
notes in git history around 0.4.1) - not yet written up in full.
## TODOs:
See [open issues](https://github.com/dillardblom/occweb/issues)
+11 -7
View File
@@ -5,7 +5,7 @@
<name>Extended OCC Web</name>
<summary>OCC Commands in a web terminal</summary>
<description><![CDATA[Run OCC Commands in a web terminal]]></description>
<version>0.4.1</version>
<version>0.4.3</version>
<licence>agpl</licence>
<author mail="dillard.blom@ensembia.com" >Dillard Blom</author>
<namespace>OCCWeb</namespace>
@@ -15,12 +15,16 @@
<repository>https://github.com/dillardblom/occweb</repository>
<screenshot>https://github.com/dillardblom/occweb/raw/main/appinfo/screenshot.png</screenshot>
<dependencies>
<!-- max-version must equal the highest NC core version this fork has
actually been verified on - never a value inherited from upstream
or guessed ahead. Test against the next major's beta and bump
this BEFORE it goes final, so occ:upgrade never has to fall back
to auto-disabling the app. -->
<nextcloud min-version="30" max-version="34"/>
<!-- min-version and max-version must equal the lowest/highest NC core
versions this fork has actually been verified on (via testbak/,
see its README) - never inherited from upstream or guessed at
either end. min-version was 30 pre-testbak, unverified since the
app-id rename/jQuery fixes - narrowed to 32 (2026-09-27) once 30/31
turned out to never have been re-tested and 32 was confirmed
working. Test against the next major's beta and bump max-version
BEFORE it goes final, so occ:upgrade never has to fall back to
auto-disabling the app. -->
<nextcloud min-version="32" max-version="35"/>
</dependencies>
<navigations>
<navigation role="admin">
+6 -1
View File
@@ -17,7 +17,12 @@ class OccOutput extends BufferedOutput implements ConsoleOutputInterface
return $this;
}
public function setErrorOutput(OutputInterface $error)
// NC35 bundles a Symfony Console version whose ConsoleOutputInterface
// declares this method `: void` - PHP 8 fatals ("Declaration must be
// compatible") without a matching return type here. Declaring `: void`
// is a valid narrowing of the untyped signature older Symfony/NC
// versions have, so this stays compatible with NC30-34 too.
public function setErrorOutput(OutputInterface $error): void
{
}
+11 -2
View File
@@ -2,8 +2,17 @@
// Nextcloud's global jQuery has been deprecated since NC19 and is no longer
// reliably present on NC34+ (see e.g. serverinfo needing to bundle its own) -
// term.js (jquery.terminal) and index.js both require jQuery to be loaded
// first, so ship and load our own copy rather than relying on core.
script('extended_occweb', 'jquery');
// first, so ship and load our own copy where core no longer provides one.
//
// Only do this on NC34+: on NC32/33 core still exposes a working jQuery, but
// via a read-only `window.$` getter - loading our vendored copy there too
// makes its UMD wrapper throw trying to reassign `window.$` (confirmed via
// testbak/, see the README compat matrix). Harmless in practice (this app
// attaches via window.jQuery, not window.$) but a real, avoidable console
// exception, so skip the extra load where core's own jQuery already works.
if (\OCP\Server::get(\OCP\ServerVersion::class)->getMajorVersion() >= 34) {
script('extended_occweb', 'jquery');
}
script('extended_occweb', 'index');
script('extended_occweb', 'term');
script('extended_occweb', 'unix_formatting');