Bring up to date with cockpit starter kit
This encompasses a number of changes to the build process.
This commit is contained in:
parent
a0fffde59d
commit
235f110ec7
32 changed files with 533 additions and 1172 deletions
4
.babelrc
4
.babelrc
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"presets": ["@babel/env",
|
||||
"@babel/preset-react"]
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
node_modules/*
|
||||
pkg/lib/*
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@
|
|||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "standard", "standard-react"],
|
||||
"parser": "@babel/eslint-parser",
|
||||
"extends": ["eslint:recommended", "standard", "standard-jsx", "react-app"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "7",
|
||||
"ecmaFeatures": {
|
||||
|
|
@ -15,22 +14,16 @@
|
|||
},
|
||||
"plugins": ["flowtype", "react", "react-hooks"],
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
4,
|
||||
"indent": ["error", 4,
|
||||
{
|
||||
"ObjectExpression": "first",
|
||||
"CallExpression": {"arguments": "first"},
|
||||
"MemberExpression": 2,
|
||||
"ignoredNodes": [ "JSXAttribute" ]
|
||||
}
|
||||
],
|
||||
}],
|
||||
"newline-per-chained-call": ["error", { "ignoreChainWithDepth": 2 }],
|
||||
"lines-between-class-members": [
|
||||
"error",
|
||||
"always",
|
||||
{ "exceptAfterSingleLine": true }
|
||||
],
|
||||
"no-var": "error",
|
||||
"lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }],
|
||||
"prefer-promise-reject-errors": ["error", { "allowEmptyReject": true }],
|
||||
"react/jsx-indent": ["error", 4],
|
||||
"semi": ["error", "always", { "omitLastInOneLineBlock": true }],
|
||||
|
|
@ -43,22 +36,13 @@
|
|||
"curly": "off",
|
||||
"jsx-quotes": "off",
|
||||
"key-spacing": "off",
|
||||
"new-cap": "off",
|
||||
"no-console": "off",
|
||||
"prefer-const": "off",
|
||||
"quotes": "off",
|
||||
"react/jsx-closing-bracket-location": "off",
|
||||
"react/jsx-curly-spacing": "off",
|
||||
"react/jsx-indent-props": "off",
|
||||
"react/jsx-handler-names": "off",
|
||||
"react/prop-types": "off",
|
||||
"space-before-function-paren": "off",
|
||||
"standard/no-callback-literal": "off",
|
||||
|
||||
"eqeqeq": "off",
|
||||
"import/no-webpack-loader-syntax": "off",
|
||||
"object-property-newline": "off",
|
||||
"react/jsx-no-bind": "off"
|
||||
"standard/no-callback-literal": "off"
|
||||
},
|
||||
"globals": {
|
||||
"require": false,
|
||||
|
|
|
|||
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -4,15 +4,16 @@
|
|||
*.rpm
|
||||
node_modules/
|
||||
dist/
|
||||
lib/
|
||||
/*.spec
|
||||
/.vagrant
|
||||
package-lock.json
|
||||
Test*FAIL*
|
||||
bots/
|
||||
/bots
|
||||
test/common/
|
||||
test/images/
|
||||
pkg
|
||||
*.pot
|
||||
POTFILES*
|
||||
tmp/
|
||||
.mypy_cache
|
||||
/po/LINGUAS
|
||||
/tools
|
||||
|
|
|
|||
38
.stylelintrc.json
Normal file
38
.stylelintrc.json
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"extends": "stylelint-config-standard-scss",
|
||||
"rules": {
|
||||
"declaration-colon-newline-after": null,
|
||||
"selector-list-comma-newline-after": null,
|
||||
|
||||
"at-rule-empty-line-before": null,
|
||||
"declaration-colon-space-before": null,
|
||||
"declaration-empty-line-before": null,
|
||||
"custom-property-empty-line-before": null,
|
||||
"comment-empty-line-before": null,
|
||||
"scss/double-slash-comment-empty-line-before": null,
|
||||
"scss/dollar-variable-colon-space-after": null,
|
||||
|
||||
"custom-property-pattern": null,
|
||||
"declaration-block-no-duplicate-properties": null,
|
||||
"declaration-block-no-redundant-longhand-properties": null,
|
||||
"declaration-block-no-shorthand-property-overrides": null,
|
||||
"declaration-block-single-line-max-declarations": null,
|
||||
"font-family-no-duplicate-names": null,
|
||||
"function-url-quotes": null,
|
||||
"indentation": null,
|
||||
"keyframes-name-pattern": null,
|
||||
"max-line-length": null,
|
||||
"no-descending-specificity": null,
|
||||
"no-duplicate-selectors": null,
|
||||
"scss/at-extend-no-missing-placeholder": null,
|
||||
"scss/at-import-partial-extension": null,
|
||||
"scss/at-mixin-pattern": null,
|
||||
"scss/comment-no-empty": null,
|
||||
"scss/dollar-variable-pattern": null,
|
||||
"scss/double-slash-comment-whitespace-inside": null,
|
||||
"scss/no-global-function-names": null,
|
||||
"scss/operator-no-unspaced": null,
|
||||
"selector-class-pattern": null,
|
||||
"selector-id-pattern": null
|
||||
}
|
||||
}
|
||||
13
.tasks
13
.tasks
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# When run automated, randomize to minimize stampeding herd
|
||||
if [ -t 0 ]; then
|
||||
chance=10
|
||||
else
|
||||
chance=$(shuf -i 0-10 -n 1)
|
||||
fi
|
||||
|
||||
if [ $chance -gt 9 ]; then
|
||||
# Open issues for things that need doing on a regular basis
|
||||
bots/npm-trigger
|
||||
fi
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
dist: trusty
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- "8"
|
||||
script:
|
||||
- npm install
|
||||
- npm run build
|
||||
17
Dockerfile
17
Dockerfile
|
|
@ -1,17 +0,0 @@
|
|||
FROM fedora:35
|
||||
|
||||
COPY . cockpit-session-recording
|
||||
|
||||
RUN sudo dnf -y install \
|
||||
git \
|
||||
gnupg \
|
||||
intltool \
|
||||
libappstream-glib \
|
||||
make \
|
||||
npm \
|
||||
rpm-build \
|
||||
rpmdevtools \
|
||||
rsync \
|
||||
tar
|
||||
|
||||
RUN cd cockpit-session-recording && make rpm
|
||||
221
Makefile
221
Makefile
|
|
@ -3,24 +3,45 @@ PACKAGE_NAME := $(shell awk '/"name":/ {gsub(/[",]/, "", $$2); print $$2}' packa
|
|||
RPM_NAME := cockpit-$(PACKAGE_NAME)
|
||||
VERSION := $(shell T=$$(git describe 2>/dev/null) || T=1; echo $$T | tr '-' '.')
|
||||
ifeq ($(TEST_OS),)
|
||||
TEST_OS = rhel-x
|
||||
TEST_OS = centos-8-stream
|
||||
endif
|
||||
export TEST_OS
|
||||
TARFILE=cockpit-$(PACKAGE_NAME)-$(VERSION).tar.xz
|
||||
TARFILE=$(RPM_NAME)-$(VERSION).tar.xz
|
||||
NODE_CACHE=$(RPM_NAME)-node-$(VERSION).tar.xz
|
||||
SPEC=$(RPM_NAME).spec
|
||||
# rpmspec -q behaves differently in Fedora ≥ 37
|
||||
RPMQUERY=$(shell rpmspec -D"VERSION $(VERSION)" -q --srpm $(SPEC).in).rpm
|
||||
SRPMFILE=$(subst noarch,src,$(RPMQUERY))
|
||||
RPMFILE=$(subst src,noarch,$(RPMQUERY))
|
||||
PREFIX ?= /usr/local
|
||||
APPSTREAMFILE=org.cockpit-project.$(PACKAGE_NAME).metainfo.xml
|
||||
VM_IMAGE=$(CURDIR)/test/images/$(TEST_OS)
|
||||
# stamp file to check if/when npm install ran
|
||||
# stamp file to check for node_modules/
|
||||
NODE_MODULES_TEST=package-lock.json
|
||||
# one example file in dist/ from webpack to check if that already ran
|
||||
WEBPACK_TEST=dist/manifest.json
|
||||
# one example file in src/lib to check if it was already checked out
|
||||
LIB_TEST=src/lib/cockpit-po-plugin.js
|
||||
# one example file in dist/ from bundler to check if that already ran
|
||||
DIST_TEST=dist/manifest.json
|
||||
# one example file in pkg/lib to check if it was already checked out
|
||||
COCKPIT_REPO_STAMP=pkg/lib/cockpit-po-plugin.js
|
||||
# common arguments for tar, mostly to make the generated tarballs reproducible
|
||||
TAR_ARGS = --sort=name --mtime "@$(shell git show --no-patch --format='%at')" --mode=go=rX,u+rw,a-s --numeric-owner --owner=0 --group=0
|
||||
|
||||
all: $(WEBPACK_TEST)
|
||||
all: $(DIST_TEST)
|
||||
|
||||
# checkout common files from Cockpit repository required to build this project;
|
||||
# this has no API stability guarantee, so check out a stable tag when you start
|
||||
# a new project, use the latest release, and update it from time to time
|
||||
COCKPIT_REPO_FILES = \
|
||||
pkg/lib \
|
||||
test/common \
|
||||
tools/git-utils.sh \
|
||||
tools/make-bots \
|
||||
$(NULL)
|
||||
|
||||
COCKPIT_REPO_URL = https://github.com/cockpit-project/cockpit.git
|
||||
COCKPIT_REPO_COMMIT = 6073b2703acd68e216bd9dbc116c30d2d7a9701c # 288.1 + esbuild plugin updates
|
||||
|
||||
$(COCKPIT_REPO_FILES): $(COCKPIT_REPO_STAMP)
|
||||
COCKPIT_REPO_TREE = '$(strip $(COCKPIT_REPO_COMMIT))^{tree}'
|
||||
$(COCKPIT_REPO_STAMP): Makefile
|
||||
@git rev-list --quiet --objects $(COCKPIT_REPO_TREE) -- 2>/dev/null || \
|
||||
git fetch --no-tags --no-write-fetch-head --depth=1 $(COCKPIT_REPO_URL) $(COCKPIT_REPO_COMMIT)
|
||||
git archive $(COCKPIT_REPO_TREE) -- $(COCKPIT_REPO_FILES) | tar x
|
||||
|
||||
#
|
||||
# i18n
|
||||
|
|
@ -28,95 +49,98 @@ all: $(WEBPACK_TEST)
|
|||
|
||||
LINGUAS=$(basename $(notdir $(wildcard po/*.po)))
|
||||
|
||||
po/POTFILES.js.in:
|
||||
mkdir -p $(dir $@)
|
||||
find src/ -name '*.js' -o -name '*.jsx' > $@
|
||||
|
||||
po/$(PACKAGE_NAME).js.pot: po/POTFILES.js.in
|
||||
xgettext --default-domain=cockpit --output=$@ --language=C --keyword= \
|
||||
--keyword=_:1,1t --keyword=_:1c,2,1t --keyword=C_:1c,2 \
|
||||
po/$(PACKAGE_NAME).js.pot:
|
||||
xgettext --default-domain=$(PACKAGE_NAME) --output=$@ --language=C --keyword= \
|
||||
--keyword=_:1,1t --keyword=_:1c,2,2t --keyword=C_:1c,2 \
|
||||
--keyword=N_ --keyword=NC_:1c,2 \
|
||||
--keyword=gettext:1,1t --keyword=gettext:1c,2,2t \
|
||||
--keyword=ngettext:1,2,3t --keyword=ngettext:1c,2,3,4t \
|
||||
--keyword=gettextCatalog.getString:1,3c --keyword=gettextCatalog.getPlural:2,3,4c \
|
||||
--from-code=UTF-8 --files-from=$^
|
||||
--from-code=UTF-8 $$(find src/ -name '*.js' -o -name '*.jsx')
|
||||
|
||||
po/POTFILES.html.in:
|
||||
mkdir -p $(dir $@)
|
||||
find src -name '*.html' > $@
|
||||
po/$(PACKAGE_NAME).html.pot: $(NODE_MODULES_TEST) $(COCKPIT_REPO_STAMP)
|
||||
pkg/lib/html2po.js -o $@ $$(find src -name '*.html')
|
||||
|
||||
po/$(PACKAGE_NAME).html.pot: po/POTFILES.html.in
|
||||
po/html2po -f $^ -o $@
|
||||
po/$(PACKAGE_NAME).manifest.pot: $(NODE_MODULES_TEST) $(COCKPIT_REPO_STAMP)
|
||||
pkg/lib/manifest2po.js src/manifest.json -o $@
|
||||
|
||||
po/$(PACKAGE_NAME).manifest.pot:
|
||||
po/manifest2po src/manifest.json -o $@
|
||||
po/$(PACKAGE_NAME).metainfo.pot: $(APPSTREAMFILE)
|
||||
xgettext --default-domain=$(PACKAGE_NAME) --output=$@ $<
|
||||
|
||||
po/$(PACKAGE_NAME).pot: po/$(PACKAGE_NAME).html.pot po/$(PACKAGE_NAME).js.pot po/$(PACKAGE_NAME).manifest.pot
|
||||
po/$(PACKAGE_NAME).pot: po/$(PACKAGE_NAME).html.pot po/$(PACKAGE_NAME).js.pot po/$(PACKAGE_NAME).manifest.pot po/$(PACKAGE_NAME).metainfo.pot
|
||||
msgcat --sort-output --output-file=$@ $^
|
||||
|
||||
# Update translations against current PO template
|
||||
update-po: po/$(PACKAGE_NAME).pot
|
||||
for lang in $(LINGUAS); do \
|
||||
msgmerge --output-file=po/$$lang.po po/$$lang.po $<; \
|
||||
done
|
||||
|
||||
dist/po.%.js: po/%.po $(NODE_MODULES_TEST)
|
||||
mkdir -p $(dir $@)
|
||||
po/po2json -m po/po.empty.js -o $@.js.tmp $<
|
||||
mv $@.js.tmp $@
|
||||
po/LINGUAS:
|
||||
echo $(LINGUAS) | tr ' ' '\n' > $@
|
||||
|
||||
#
|
||||
# Build/Install/dist
|
||||
#
|
||||
|
||||
%.spec: %.spec.in
|
||||
sed -e 's/%{VERSION}/$(VERSION)/g' $< > $@
|
||||
$(SPEC): packaging/$(SPEC).in $(NODE_MODULES_TEST)
|
||||
provides=$$(npm ls --omit dev --package-lock-only --depth=Infinity | grep -Eo '[^[:space:]]+@[^[:space:]]+' | sort -u | sed 's/^/Provides: bundled(npm(/; s/\(.*\)@/\1)) = /'); \
|
||||
awk -v p="$$provides" '{gsub(/%{VERSION}/, "$(VERSION)"); gsub(/%{NPM_PROVIDES}/, p)}1' $< > $@
|
||||
|
||||
$(WEBPACK_TEST): $(NODE_MODULES_TEST) $(LIB_TEST) $(shell find src/ -type f) package.json webpack.config.js $(patsubst %,dist/po.%.js,$(LINGUAS))
|
||||
NODE_ENV=$(NODE_ENV) node_modules/.bin/webpack
|
||||
$(DIST_TEST): $(NODE_MODULES_TEST) $(COCKPIT_REPO_STAMP) $(shell find src/ -type f) package.json build.js
|
||||
NODE_ENV=$(NODE_ENV) ./build.js
|
||||
|
||||
watch:
|
||||
NODE_ENV=$(NODE_ENV) node_modules/.bin/webpack --watch
|
||||
NODE_ENV=$(NODE_ENV) npm run watch
|
||||
|
||||
clean:
|
||||
rm -rf dist/
|
||||
[ ! -e cockpit-$(PACKAGE_NAME).spec.in ] || rm -f cockpit-$(PACKAGE_NAME).spec
|
||||
rm -f $(SPEC)
|
||||
rm -f po/LINGUAS
|
||||
|
||||
install: $(WEBPACK_TEST)
|
||||
mkdir -p $(DESTDIR)/usr/share/cockpit/$(PACKAGE_NAME)
|
||||
cp -r dist/* $(DESTDIR)/usr/share/cockpit/$(PACKAGE_NAME)
|
||||
mkdir -p $(DESTDIR)/usr/share/metainfo/
|
||||
cp org.cockpit-project.$(PACKAGE_NAME).metainfo.xml $(DESTDIR)/usr/share/metainfo/
|
||||
install: $(DIST_TEST) po/LINGUAS
|
||||
mkdir -p $(DESTDIR)$(PREFIX)/share/cockpit/$(PACKAGE_NAME)
|
||||
cp -r dist/* $(DESTDIR)$(PREFIX)/share/cockpit/$(PACKAGE_NAME)
|
||||
mkdir -p $(DESTDIR)$(PREFIX)/share/metainfo/
|
||||
msgfmt --xml -d po \
|
||||
--template $(APPSTREAMFILE) \
|
||||
-o $(DESTDIR)$(PREFIX)/share/metainfo/$(APPSTREAMFILE)
|
||||
|
||||
# this requires a built source tree and avoids having to install anything system-wide
|
||||
devel-install: $(WEBPACK_TEST)
|
||||
devel-install: $(DIST_TEST)
|
||||
mkdir -p ~/.local/share/cockpit
|
||||
ln -s `pwd`/dist ~/.local/share/cockpit/$(PACKAGE_NAME)
|
||||
|
||||
dist: $(TARFILE)
|
||||
# assumes that there was symlink set up using the above devel-install target,
|
||||
# and removes it
|
||||
devel-uninstall:
|
||||
rm -f ~/.local/share/cockpit/$(PACKAGE_NAME)
|
||||
|
||||
# when building a distribution tarball, call webpack with a 'production' environment
|
||||
print-version:
|
||||
@echo "$(VERSION)"
|
||||
|
||||
dist: $(TARFILE)
|
||||
@ls -1 $(TARFILE)
|
||||
|
||||
# when building a distribution tarball, call bundler with a 'production' environment
|
||||
# we don't ship node_modules for license and compactness reasons; we ship a
|
||||
# pre-built dist/ (so it's not necessary) and ship packge-lock.json (so that
|
||||
# pre-built dist/ (so it's not necessary) and ship package-lock.json (so that
|
||||
# node_modules/ can be reconstructed if necessary)
|
||||
$(TARFILE): export NODE_ENV=production
|
||||
$(TARFILE): $(WEBPACK_TEST) cockpit-$(PACKAGE_NAME).spec
|
||||
$(TARFILE): $(DIST_TEST) $(SPEC)
|
||||
if type appstream-util >/dev/null 2>&1; then appstream-util validate-relax --nonet *.metainfo.xml; fi
|
||||
touch -r package.json $(NODE_MODULES_TEST)
|
||||
touch dist/*
|
||||
tar --xz -cf cockpit-$(PACKAGE_NAME)-$(VERSION).tar.xz --transform 's,^,cockpit-$(PACKAGE_NAME)/,' \
|
||||
--exclude cockpit-$(PACKAGE_NAME).spec.in --exclude node_modules \
|
||||
$$(git ls-files) src/lib package-lock.json cockpit-$(PACKAGE_NAME).spec dist/
|
||||
tar --xz $(TAR_ARGS) -cf $(TARFILE) --transform 's,^,$(RPM_NAME)/,' \
|
||||
--exclude packaging/$(SPEC).in --exclude node_modules \
|
||||
$$(git ls-files) $(COCKPIT_REPO_FILES) $(NODE_MODULES_TEST) $(SPEC) dist/
|
||||
|
||||
srpm: $(TARFILE) cockpit-$(PACKAGE_NAME).spec
|
||||
$(NODE_CACHE): $(NODE_MODULES_TEST)
|
||||
tar --xz $(TAR_ARGS) -cf $@ node_modules
|
||||
|
||||
node-cache: $(NODE_CACHE)
|
||||
|
||||
# convenience target for developers
|
||||
srpm: $(TARFILE) $(NODE_CACHE) $(SPEC)
|
||||
rpmbuild -bs \
|
||||
--define "_sourcedir `pwd`" \
|
||||
--define "_srcrpmdir `pwd`" \
|
||||
cockpit-$(PACKAGE_NAME).spec
|
||||
$(SPEC)
|
||||
|
||||
rpm: $(RPMFILE)
|
||||
|
||||
$(RPMFILE): $(TARFILE) cockpit-$(PACKAGE_NAME).spec
|
||||
# convenience target for developers
|
||||
rpm: $(TARFILE) $(NODE_CACHE) $(SPEC)
|
||||
mkdir -p "`pwd`/output"
|
||||
mkdir -p "`pwd`/rpmbuild"
|
||||
rpmbuild -bb \
|
||||
|
|
@ -126,55 +150,58 @@ $(RPMFILE): $(TARFILE) cockpit-$(PACKAGE_NAME).spec
|
|||
--define "_srcrpmdir `pwd`" \
|
||||
--define "_rpmdir `pwd`/output" \
|
||||
--define "_buildrootdir `pwd`/build" \
|
||||
cockpit-$(PACKAGE_NAME).spec
|
||||
$(SPEC)
|
||||
find `pwd`/output -name '*.rpm' -printf '%f\n' -exec mv {} . \;
|
||||
rm -r "`pwd`/rpmbuild"
|
||||
rm -r "`pwd`/output" "`pwd`/build"
|
||||
# sanity check
|
||||
test -e "$(RPMFILE)"
|
||||
|
||||
# build a VM with locally built rpm installed
|
||||
$(VM_IMAGE): $(RPMFILE) bots
|
||||
rm -f $(VM_IMAGE) $(VM_IMAGE).qcow2
|
||||
bots/image-customize -v -i `pwd`/$(RPMFILE) -s $(CURDIR)/test/vm.install $(TEST_OS)
|
||||
bots/image-customize -v -u ./test/files/1.journal:/var/log/journal/1.journal $(TEST_OS)
|
||||
bots/image-customize -v -u ./test/files/binary-rec.journal:/var/log/journal/binary-rec.journal $(TEST_OS)
|
||||
ifeq ("$(TEST_SCENARIO)","pybridge")
|
||||
COCKPIT_PYBRIDGE_REF = main
|
||||
COCKPIT_WHEEL = cockpit-0-py3-none-any.whl
|
||||
|
||||
$(COCKPIT_WHEEL):
|
||||
pip wheel git+https://github.com/cockpit-project/cockpit.git@${COCKPIT_PYBRIDGE_REF}
|
||||
|
||||
VM_DEPENDS = $(COCKPIT_WHEEL)
|
||||
VM_CUSTOMIZE_FLAGS = --install $(COCKPIT_WHEEL)
|
||||
endif
|
||||
|
||||
# build a VM with locally built distro pkgs installed
|
||||
# disable networking, VM images have mock/pbuilder with the common build dependencies pre-installed
|
||||
$(VM_IMAGE): $(TARFILE) $(NODE_CACHE) bots test/vm.install $(VM_DEPENDS)
|
||||
bots/image-customize --fresh \
|
||||
$(VM_CUSTOMIZE_FLAGS) \
|
||||
--upload $(NODE_CACHE):/var/tmp/ --build $(TARFILE) \
|
||||
--upload ./test/files/1.journal:/var/log/journal/1.journal \
|
||||
--upload ./test/files/binary-rec.journal:/var/log/journal/binary-rec.journal \
|
||||
--script $(CURDIR)/test/vm.install $(TEST_OS)
|
||||
|
||||
# convenience target for the above
|
||||
vm: $(VM_IMAGE)
|
||||
echo $(VM_IMAGE)
|
||||
@echo $(VM_IMAGE)
|
||||
|
||||
# convenience target to print the filename of the test image
|
||||
print-vm:
|
||||
@echo $(VM_IMAGE)
|
||||
|
||||
# convenience target to setup all the bits needed for the integration tests
|
||||
# without actually running them
|
||||
prepare-check: $(NODE_MODULES_TEST) $(VM_IMAGE) test/common
|
||||
|
||||
# run the browser integration tests; skip check for SELinux denials
|
||||
# this will run all tests/check-* and format them as TAP
|
||||
check: $(NODE_MODULES_TEST) $(VM_IMAGE) test/common
|
||||
TEST_AUDIT_NO_SELINUX=1 test/common/run-tests
|
||||
check: prepare-check
|
||||
TEST_AUDIT_NO_SELINUX=1 test/common/run-tests ${RUN_TESTS_OPTIONS}
|
||||
|
||||
# checkout Cockpit's bots for standard test VM images and API to launch them
|
||||
# must be from master, as only that has current and existing images; but testvm.py API is stable
|
||||
# support CI testing against a bots change
|
||||
bots:
|
||||
git clone --quiet --reference-if-able $${XDG_CACHE_HOME:-$$HOME/.cache}/cockpit-project/bots https://github.com/cockpit-project/bots.git
|
||||
if [ -n "$$COCKPIT_BOTS_REF" ]; then git -C bots fetch --quiet --depth=1 origin "$$COCKPIT_BOTS_REF"; git -C bots checkout --quiet FETCH_HEAD; fi
|
||||
@echo "checked out bots/ ref $$(git -C bots rev-parse HEAD)"
|
||||
|
||||
# checkout Cockpit's test API; this has no API stability guarantee, so check out a stable tag
|
||||
# when you start a new project, use the latest release, and update it from time to time
|
||||
test/common:
|
||||
git fetch --depth=1 https://github.com/cockpit-project/cockpit.git 267
|
||||
git checkout --force FETCH_HEAD -- test/common
|
||||
git reset test/common
|
||||
|
||||
# checkout Cockpit's PF/React/build library; again this has no API stability guarantee, so check out a stable tag
|
||||
$(LIB_TEST):
|
||||
git clone -b 256 --depth=1 https://github.com/cockpit-project/cockpit.git tmp/cockpit
|
||||
mv tmp/cockpit/pkg/lib src/
|
||||
rm -rf tmp/cockpit
|
||||
bots: tools/make-bots
|
||||
tools/make-bots
|
||||
|
||||
$(NODE_MODULES_TEST): package.json
|
||||
# if it exists already, npm install won't update it; force that so that we always get up-to-date packages
|
||||
rm -f package-lock.json
|
||||
# unset NODE_ENV, skips devDependencies otherwise
|
||||
env -u NODE_ENV npm install
|
||||
env -u NODE_ENV npm install --ignore-scripts
|
||||
env -u NODE_ENV npm prune
|
||||
|
||||
.PHONY: all clean install devel-install dist srpm rpm check vm update-po
|
||||
.PHONY: all clean install devel-install devel-uninstall print-version dist node-cache rpm prepare-check check vm print-vm
|
||||
|
|
|
|||
89
README.md
89
README.md
|
|
@ -17,6 +17,9 @@ GitHub Organization:
|
|||
* [scribery.github.io](http://scribery.github.io/)
|
||||
* [Scribery](https://github.com/Scribery)
|
||||
|
||||
This project is based on the [Cockpit Starter Kit](https://github.com/cockpit-project/starter-kit).
|
||||
See [Starter Kit Intro](http://cockpit-project.org/blog/cockpit-starter-kit.html) for details.
|
||||
|
||||
# Getting and building the source
|
||||
|
||||
Make sure you have `npm` available (usually from your distribution package).
|
||||
|
|
@ -30,15 +33,17 @@ make
|
|||
|
||||
# Installing
|
||||
|
||||
`make install` compiles and installs the package in `/usr/share/cockpit/`. The
|
||||
`make install` compiles and installs the package in `/usr/local/share/cockpit/`. The
|
||||
convenience targets `srpm` and `rpm` build the source and binary rpms,
|
||||
respectively. Both of these make use of the `dist-gzip` target, which is used
|
||||
respectively. Both of these make use of the `dist` target, which is used
|
||||
to generate the distribution tarball. In `production` mode, source files are
|
||||
automatically minified and compressed. Set `NODE_ENV=production` if you want to
|
||||
duplicate this behavior.
|
||||
|
||||
For development, you usually want to run your module straight out of the git
|
||||
tree. To do that, link that to the location were `cockpit-bridge` looks for packages:
|
||||
tree. To do that, run `make devel-install`, which links your checkout to the
|
||||
location were cockpit-bridge looks for packages. If you prefer to do
|
||||
this manually:
|
||||
|
||||
```
|
||||
mkdir -p ~/.local/share/cockpit
|
||||
|
|
@ -48,12 +53,39 @@ ln -s `pwd`/dist ~/.local/share/cockpit/session-recording
|
|||
After changing the code and running `make` again, reload the Cockpit page in
|
||||
your browser.
|
||||
|
||||
You can also use
|
||||
[watch mode](https://esbuild.github.io/api/#watch) to
|
||||
automatically update the bundle on every code change with
|
||||
|
||||
$ npm run watch
|
||||
|
||||
or
|
||||
|
||||
$ make watch
|
||||
|
||||
When developing against a virtual machine, watch mode can also automatically upload
|
||||
the code changes by setting the `RSYNC` environment variable to
|
||||
the remote hostname.
|
||||
|
||||
$ RSYNC=c make watch
|
||||
|
||||
When developing against a remote host as a normal user, `RSYNC_DEVEL` can be
|
||||
set to upload code changes to `~/.local/share/cockpit/` instead of
|
||||
`/usr/local`.
|
||||
|
||||
$ RSYNC_DEVEL=example.com make watch
|
||||
|
||||
To "uninstall" the locally installed version, run `make devel-uninstall`, or
|
||||
remove manually the symlink:
|
||||
|
||||
rm ~/.local/share/cockpit/starter-kit
|
||||
|
||||
# Running eslint
|
||||
|
||||
Cockpit Starter Kit uses [ESLint](https://eslint.org/) to automatically check
|
||||
JavaScript code style in `.jsx` and `.es6` files.
|
||||
JavaScript code style in `.js` and `.jsx` files.
|
||||
|
||||
The linter is executed within every build as a webpack preloader.
|
||||
eslint is executed within every build.
|
||||
|
||||
For developer convenience, the ESLint can be started explicitly by:
|
||||
|
||||
|
|
@ -65,6 +97,49 @@ Violations of some rules can be fixed automatically by:
|
|||
|
||||
Rules configuration can be found in the `.eslintrc.json` file.
|
||||
|
||||
# Credits
|
||||
## Running stylelint
|
||||
|
||||
Cockpit-session-recording is based on [starter-kit](http://cockpit-project.org/blog/cockpit-starter-kit.html).
|
||||
Cockpit uses [Stylelint](https://stylelint.io/) to automatically check CSS code
|
||||
style in `.css` and `scss` files.
|
||||
|
||||
styleint is executed within every build.
|
||||
|
||||
For developer convenience, the Stylelint can be started explicitly by:
|
||||
|
||||
$ npm run stylelint
|
||||
|
||||
Violations of some rules can be fixed automatically by:
|
||||
|
||||
$ npm run stylelint:fix
|
||||
|
||||
Rules configuration can be found in the `.stylelintrc.json` file.
|
||||
|
||||
During fast iterative development, you can also choose to not run eslint/stylelint.
|
||||
This speeds up the build and avoids build failures due to e. g. ill-formatted
|
||||
css or other issues:
|
||||
|
||||
$ make LINT=0
|
||||
|
||||
# Running tests locally
|
||||
|
||||
Run `make check` to build an RPM, install it into a standard Cockpit test VM
|
||||
(centos-8-stream by default), and run the test/check-application integration test on
|
||||
it. This uses Cockpit's Chrome DevTools Protocol based browser tests, through a
|
||||
Python API abstraction. Note that this API is not guaranteed to be stable, so
|
||||
if you run into failures and don't want to adjust tests, consider checking out
|
||||
Cockpit's test/common from a tag instead of main (see the `test/common`
|
||||
target in `Makefile`).
|
||||
|
||||
After the test VM is prepared, you can manually run the test without rebuilding
|
||||
the VM, possibly with extra options for tracing and halting on test failures
|
||||
(for interactive debugging):
|
||||
|
||||
TEST_OS=centos-8-stream test/check-application -tvs
|
||||
|
||||
It is possible to setup the test environment without running the tests:
|
||||
|
||||
TEST_OS=centos-8-stream make prepare-check
|
||||
|
||||
You can also run the test against a different Cockpit image, for example:
|
||||
|
||||
TEST_OS=fedora-34 make check
|
||||
|
|
|
|||
30
Vagrantfile
vendored
30
Vagrantfile
vendored
|
|
@ -1,30 +0,0 @@
|
|||
Vagrant.configure(2) do |config|
|
||||
config.vm.box = "fedora/28-cloud-base"
|
||||
config.vm.network "forwarded_port", guest: 9090, host: 9090
|
||||
|
||||
if Dir.glob("dist/*").length == 0
|
||||
config.vm.post_up_message = "NOTE: Distribution directory is empty. Run `make` to see your module show up in cockpit"
|
||||
end
|
||||
|
||||
config.vm.synced_folder ".", "/vagrant", disabled: true
|
||||
config.vm.synced_folder "dist/", "/usr/local/share/cockpit/" + File.basename(Dir.pwd), type: "rsync", create: true
|
||||
|
||||
config.vm.provider "libvirt" do |libvirt|
|
||||
libvirt.memory = 1024
|
||||
end
|
||||
|
||||
config.vm.provider "virtualbox" do |virtualbox|
|
||||
virtualbox.memory = 1024
|
||||
end
|
||||
|
||||
config.vm.provision "shell", inline: <<-EOF
|
||||
set -eu
|
||||
|
||||
sudo dnf install -y cockpit
|
||||
|
||||
printf "[WebService]\nAllowUnencrypted=true\n" > /etc/cockpit/cockpit.conf
|
||||
|
||||
systemctl enable cockpit.socket
|
||||
systemctl start cockpit.socket
|
||||
EOF
|
||||
end
|
||||
136
build.js
Executable file
136
build.js
Executable file
|
|
@ -0,0 +1,136 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
|
||||
import copy from 'esbuild-plugin-copy';
|
||||
|
||||
import { cleanPlugin } from './pkg/lib/esbuild-cleanup-plugin.js';
|
||||
import { cockpitCompressPlugin } from './pkg/lib/esbuild-compress-plugin.js';
|
||||
import { cockpitPoEsbuildPlugin } from './pkg/lib/cockpit-po-plugin.js';
|
||||
import { cockpitRsyncEsbuildPlugin } from './pkg/lib/cockpit-rsync-plugin.js';
|
||||
import { esbuildStylesPlugins } from './pkg/lib/esbuild-common.js';
|
||||
import { eslintPlugin } from './pkg/lib/esbuild-eslint-plugin.js';
|
||||
import { stylelintPlugin } from './pkg/lib/esbuild-stylelint-plugin.js';
|
||||
|
||||
const useWasm = os.arch() !== 'x64';
|
||||
const esbuild = (await import(useWasm ? 'esbuild-wasm' : 'esbuild')).default;
|
||||
|
||||
const production = process.env.NODE_ENV === 'production';
|
||||
const watchMode = process.env.ESBUILD_WATCH === "true";
|
||||
// linters dominate the build time, so disable them for production builds by default, but enable in watch mode
|
||||
const lint = process.env.LINT ? (process.env.LINT !== 0) : (watchMode || !production);
|
||||
// List of directories to use when using import statements
|
||||
const nodePaths = ['pkg/lib'];
|
||||
const outdir = 'dist';
|
||||
|
||||
// Obtain package name from package.json
|
||||
const packageJson = JSON.parse(fs.readFileSync('package.json'));
|
||||
|
||||
function notifyEndPlugin() {
|
||||
return {
|
||||
name: 'notify-end',
|
||||
setup(build) {
|
||||
let startTime;
|
||||
|
||||
build.onStart(() => {
|
||||
startTime = new Date();
|
||||
});
|
||||
|
||||
build.onEnd(() => {
|
||||
const endTime = new Date();
|
||||
const timeStamp = endTime.toTimeString().split(' ')[0];
|
||||
console.log(`${timeStamp}: Build finished in ${endTime - startTime} ms`);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
||||
// similar to fs.watch(), but recursively watches all subdirectories
|
||||
function watch_dirs(dir, on_change) {
|
||||
const callback = (ev, dir, fname) => {
|
||||
// only listen for "change" events, as renames are noisy
|
||||
// ignore hidden files
|
||||
const isHidden = /^\./.test(fname);
|
||||
if (ev !== "change" || isHidden) {
|
||||
return;
|
||||
}
|
||||
on_change(path.join(dir, fname));
|
||||
};
|
||||
|
||||
fs.watch(dir, {}, (ev, path) => callback(ev, dir, path));
|
||||
|
||||
// watch all subdirectories in dir
|
||||
const d = fs.opendirSync(dir);
|
||||
let dirent;
|
||||
|
||||
while ((dirent = d.readSync()) !== null) {
|
||||
if (dirent.isDirectory())
|
||||
watch_dirs(path.join(dir, dirent.name), on_change);
|
||||
}
|
||||
d.closeSync();
|
||||
}
|
||||
|
||||
const context = await esbuild.context({
|
||||
...!production ? { sourcemap: "linked" } : {},
|
||||
bundle: true,
|
||||
entryPoints: ['./src/index.js'],
|
||||
external: ['*.woff', '*.woff2', '*.jpg', '*.svg', '../../assets*'], // Allow external font files which live in ../../static/fonts
|
||||
legalComments: 'external', // Move all legal comments to a .LEGAL.txt file
|
||||
loader: { ".js": "jsx" },
|
||||
minify: production,
|
||||
nodePaths,
|
||||
outdir,
|
||||
target: ['es2020'],
|
||||
plugins: [
|
||||
cleanPlugin(),
|
||||
...lint
|
||||
? [
|
||||
stylelintPlugin({ filter: new RegExp(cwd + '\/src\/.*\.(css?|scss?)$') }),
|
||||
eslintPlugin({ filter: new RegExp(cwd + '\/src\/.*\.(jsx?|js?)$') })
|
||||
]
|
||||
: [],
|
||||
// Esbuild will only copy assets that are explicitly imported and used
|
||||
// in the code. This is a problem for index.html and manifest.json which are not imported
|
||||
copy({
|
||||
assets: [
|
||||
{ from: ['./src/manifest.json'], to: ['./manifest.json'] },
|
||||
{ from: ['./src/index.html'], to: ['./index.html'] },
|
||||
]
|
||||
}),
|
||||
...esbuildStylesPlugins,
|
||||
cockpitPoEsbuildPlugin(),
|
||||
...production ? [cockpitCompressPlugin()] : [],
|
||||
cockpitRsyncEsbuildPlugin({ dest: packageJson.name }),
|
||||
notifyEndPlugin(),
|
||||
]
|
||||
});
|
||||
|
||||
try {
|
||||
await context.rebuild();
|
||||
} catch (e) {
|
||||
if (!watchMode)
|
||||
process.exit(1);
|
||||
// ignore errors in watch mode
|
||||
}
|
||||
|
||||
if (watchMode) {
|
||||
const on_change = async path => {
|
||||
console.log("change detected:", path);
|
||||
await context.cancel();
|
||||
|
||||
try {
|
||||
await context.rebuild();
|
||||
} catch (e) {} // ignore in watch mode
|
||||
};
|
||||
|
||||
watch_dirs('src', on_change);
|
||||
|
||||
// wait forever until Control-C
|
||||
await new Promise(() => {});
|
||||
}
|
||||
|
||||
context.dispose();
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# This is a script run to release welder-web through Cockpituous:
|
||||
# https://github.com/cockpit-project/cockpituous/tree/master/release
|
||||
|
||||
# Anything that start with 'job' may run in a way that it SIGSTOP's
|
||||
# itself when preliminary preparition and then gets a SIGCONT in
|
||||
# order to complete its work.
|
||||
#
|
||||
# Check cockpituous documentation for available release targets.
|
||||
|
||||
RELEASE_SOURCE="_release/source"
|
||||
RELEASE_SPEC="cockpit-session-recording.spec"
|
||||
RELEASE_SRPM="_release/srpm"
|
||||
|
||||
job release-source
|
||||
job release-srpm -V
|
||||
|
||||
# Once you have a Fedora package and add the https://pagure.io/user/cockpit
|
||||
# user to your project's maintainers, you can also upload to Fedora automatically:
|
||||
|
||||
## Authenticate for pushing into Fedora dist-git (works in Cockpituous release container)
|
||||
# cat ~/.fedora-password | kinit cockpit@FEDORAPROJECT.ORG
|
||||
## Do fedora builds for the tag, using tarball
|
||||
# job release-koji -k master
|
||||
# job release-koji f29
|
||||
# job release-bodhi F29
|
||||
|
||||
# These are likely the first of your release targets; but run them after Fedora uploads,
|
||||
# so that failures there will fail the release early, before publishing on GitHub
|
||||
|
||||
# job release-github
|
||||
# job release-copr @myorg/myrepo
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="addon">
|
||||
<id>org.cockpit-project.session-recording</id>
|
||||
<id>org.cockpit_project.session-recording</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<name>Session Recording</name>
|
||||
<summary>
|
||||
Session Recording module for Cockpit
|
||||
</summary>
|
||||
<summary>Session Recording module for Cockpit</summary>
|
||||
<description>
|
||||
<p>
|
||||
Provides Session Recording module for Cockpit. Provides list of recorded by tlog terminal sessions from Journal.
|
||||
Provides Session Recoridng moduel for Cockpit. Provides list of recorded by tlog terminal sessions from Journal.
|
||||
Allows to play them in a player with various controls. Shows correlated logs which happened during session.
|
||||
</p>
|
||||
</description>
|
||||
<extends>org.cockpit_project.cockpit</extends>
|
||||
<launchable type="cockpit-manifest">session-recording</launchable>
|
||||
<url type="homepage">https://github.com/Scribery/cockpit-session-recording</url>
|
||||
<url type="bugtracker">https://github.com/Scribery/cockpit-session-recording/issues</url>
|
||||
<update_contact>cockpit-devel_AT_lists.fedorahosted.org</update_contact>
|
||||
</component>
|
||||
|
|
|
|||
76
package.json
76
package.json
|
|
@ -1,65 +1,51 @@
|
|||
{
|
||||
"name": "session-recording",
|
||||
"version": "0.1.0",
|
||||
"description": "Module for Cockpit which provides session recording configuration and playback",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"repository": "git@github.com:Scribery/cockpit-session-recording.git",
|
||||
"author": "",
|
||||
"license": "LGPL-2.1",
|
||||
"scripts": {
|
||||
"watch": "webpack --watch",
|
||||
"build": "webpack",
|
||||
"watch": "ESBUILD_WATCH='true' ./build.js",
|
||||
"build": "./build.js",
|
||||
"eslint": "eslint --ext .js --ext .jsx src/",
|
||||
"eslint:fix": "eslint --fix --ext .js --ext .jsx src/"
|
||||
"eslint:fix": "eslint --fix --ext .js --ext .jsx src/",
|
||||
"stylelint": "stylelint src/*{.css,scss}",
|
||||
"stylelint:fix": "stylelint --fix src/*{.css,scss}"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.12",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"babel-loader": "^9.1.2",
|
||||
"chrome-remote-interface": "^0.32.0",
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"eslint": "^8.31.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-config-standard-react": "^9.2.0",
|
||||
"argparse": "^2.0.1",
|
||||
"chrome-remote-interface": "^0.32.1",
|
||||
"esbuild": "^0.17.15",
|
||||
"esbuild-plugin-copy": "^2.1.1",
|
||||
"esbuild-plugin-replace": "^1.3.0",
|
||||
"esbuild-sass-plugin": "^2.8.0",
|
||||
"esbuild-wasm": "^0.17.16",
|
||||
"eslint": "^8.13.0",
|
||||
"eslint-config-react-app": "^7.0.0",
|
||||
"eslint-config-standard": "^17.0.0-1",
|
||||
"eslint-config-standard-jsx": "^11.0.0-1",
|
||||
"eslint-plugin-flowtype": "^8.0.3",
|
||||
"eslint-plugin-import": "^2.27.4",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-react": "^7.14.3",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-standard": "^4.1.0",
|
||||
"eslint-webpack-plugin": "^3.2.0",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"eslint-plugin-react": "^7.29.4",
|
||||
"eslint-plugin-react-hooks": "^4.4.0",
|
||||
"htmlparser": "^1.7.7",
|
||||
"jed": "^1.1.1",
|
||||
"mini-css-extract-plugin": "^2.7.2",
|
||||
"po2json": "^1.0.0-alpha",
|
||||
"sass": "^1.57.1",
|
||||
"sass-loader": "^13.2.0",
|
||||
"sizzle": "^2.3.9",
|
||||
"stdio": "^2.1.1",
|
||||
"string-replace-loader": "^3.1.0",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1"
|
||||
"qunit": "^2.9.3",
|
||||
"sass": "^1.61.0",
|
||||
"sizzle": "^2.3.3",
|
||||
"stylelint": "^14.9.1",
|
||||
"stylelint-config-standard-scss": "^5.0.0",
|
||||
"stylelint-formatter-pretty": "^3.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@patternfly/patternfly": "4.132.2",
|
||||
"@patternfly/react-core": "4.152.4",
|
||||
"@patternfly/react-icons": "^4.19.8",
|
||||
"@patternfly/react-table": "4.29.58",
|
||||
"buffer": "^6.0.3",
|
||||
"comment-json": "^4.2.3",
|
||||
"core-js": "3.27.1",
|
||||
"date-fns": "2.29.3",
|
||||
"ini": "^3.0.1",
|
||||
"jquery": "3.6.3",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"throttle-debounce": "5.0.0",
|
||||
"xterm": "^3.14.5"
|
||||
"@patternfly/patternfly": "4.224.4",
|
||||
"@patternfly/react-core": "4.276.9",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,26 +7,37 @@ URL: https://github.com/Scribery/%{name}
|
|||
Source: https://github.com/Scribery/%{name}/releases/download/%{version}/%{name}-%{version}.tar.xz
|
||||
|
||||
BuildArch: noarch
|
||||
BuildRequires: libappstream-glib
|
||||
BuildRequires: nodejs
|
||||
BuildRequires: make
|
||||
BuildRequires: libappstream-glib
|
||||
BuildRequires: gettext
|
||||
%if 0%{?rhel} && 0%{?rhel} <= 8
|
||||
BuildRequires: libappstream-glib-devel
|
||||
%endif
|
||||
|
||||
Requires: cockpit-system
|
||||
Requires: tlog
|
||||
|
||||
%{NPM_PROVIDES}
|
||||
|
||||
%description
|
||||
Cockpit module providing session recording configuration and playback.
|
||||
This module allows viewing and playback of journal-stored terminal session
|
||||
recordings generated by the tlog component.
|
||||
|
||||
%prep
|
||||
%setup -qn cockpit-session-recording
|
||||
%setup -q -n %{name}
|
||||
|
||||
%build
|
||||
# Nothing to build
|
||||
|
||||
%install
|
||||
%make_install
|
||||
%make_install PREFIX=/usr
|
||||
appstream-util validate-relax --nonet %{buildroot}/%{_datadir}/metainfo/*
|
||||
|
||||
%files
|
||||
%{_datadir}/cockpit/session-recording
|
||||
%{_datadir}/metainfo/org.cockpit-project.session-recording.metainfo.xml
|
||||
%{_datadir}/cockpit/*
|
||||
%{_datadir}/metainfo/*
|
||||
|
||||
%changelog
|
||||
* Wed Jan 13 2021 Justin Stephenson <jstephen@redhat.com> - 7-1
|
||||
15
po/de.po
15
po/de.po
|
|
@ -4,27 +4,20 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: starter-kit 1.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-08-29 00:14+0200\n"
|
||||
"POT-Creation-Date: 2022-03-09 16:09+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1\n"
|
||||
|
||||
#: src/index.html:20
|
||||
msgid "Cockpit Starter Kit"
|
||||
msgstr "Cockpit Bausatz"
|
||||
|
||||
#: src/app.jsx:42
|
||||
#: src/app.jsx:43
|
||||
msgid "Running on $0"
|
||||
msgstr "Läuft auf $0"
|
||||
|
||||
#: src/manifest.json
|
||||
msgid "Starter Kit"
|
||||
msgstr "Bausatz"
|
||||
|
||||
#: src/app.jsx:29
|
||||
msgid "Unknown"
|
||||
msgstr "Unbekannt"
|
||||
|
|
|
|||
264
po/html2po
264
po/html2po
|
|
@ -1,264 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
* Extracts translatable strings from HTML files in the following forms:
|
||||
*
|
||||
* <tag translate>String</tag>
|
||||
* <tag translate context="value">String</tag>
|
||||
* <tag translate="...">String</tag>
|
||||
* <tag translate-attr attr="String"></tag>
|
||||
*
|
||||
* Supports the following Glade compatible forms:
|
||||
*
|
||||
* <tag translatable="yes">String</tag>
|
||||
* <tag translatable="yes" context="value">String</tag>
|
||||
*
|
||||
* Supports the following angular-gettext compatible forms:
|
||||
*
|
||||
* <translate>String</translate>
|
||||
* <tag translate-plural="Plural">Singular</tag>
|
||||
*
|
||||
* Note that some of the use of the translated may not support all the strings
|
||||
* depending on the code actually using these strings to translate the HTML.
|
||||
*/
|
||||
|
||||
|
||||
function fatal(message, code) {
|
||||
console.log((filename || "html2po") + ": " + message);
|
||||
process.exit(code || 1);
|
||||
}
|
||||
|
||||
function usage() {
|
||||
console.log("usage: html2po input output");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
var fs, htmlparser, path, stdio;
|
||||
|
||||
try {
|
||||
fs = require('fs');
|
||||
path = require('path');
|
||||
htmlparser = require('htmlparser');
|
||||
stdio = require('stdio');
|
||||
} catch (ex) {
|
||||
fatal(ex.message, 127); /* missing looks for this */
|
||||
}
|
||||
|
||||
var opts = stdio.getopt({
|
||||
directory: { key: "d", args: 1, description: "Base directory for input files" },
|
||||
output: { key: "o", args: 1, description: "Output file" },
|
||||
from: { key: "f", args: 1, description: "File containing list of input files" },
|
||||
});
|
||||
|
||||
if (!opts.from && opts.args.length < 1) {
|
||||
usage();
|
||||
}
|
||||
|
||||
var input = opts.args;
|
||||
var entries = { };
|
||||
|
||||
/* Filename being parsed and offset of line number */
|
||||
var filename = null;
|
||||
var offsets = 0;
|
||||
|
||||
/* The HTML parser we're using */
|
||||
var handler = new htmlparser.DefaultHandler(function(error, dom) {
|
||||
if (error)
|
||||
fatal(error);
|
||||
else
|
||||
walk(dom);
|
||||
});
|
||||
|
||||
prepare();
|
||||
|
||||
/* Decide what input files to process */
|
||||
function prepare() {
|
||||
if (opts.from) {
|
||||
fs.readFile(opts.from, { encoding: "utf-8"}, function(err, data) {
|
||||
if (err)
|
||||
fatal(err.message);
|
||||
input = data.split("\n").filter(function(value) {
|
||||
return !!value;
|
||||
}).concat(input);
|
||||
step();
|
||||
});
|
||||
} else {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
/* Now process each file in turn */
|
||||
function step() {
|
||||
filename = input.shift();
|
||||
if (filename === undefined) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Qualify the filename if necessary */
|
||||
var full = filename;
|
||||
if (opts.directory)
|
||||
full = path.join(opts.directory, filename);
|
||||
|
||||
fs.readFile(full, { encoding: "utf-8"}, function(err, data) {
|
||||
if (err)
|
||||
fatal(err.message);
|
||||
|
||||
var parser = new htmlparser.Parser(handler, { includeLocation: true });
|
||||
parser.parseComplete(data);
|
||||
step();
|
||||
});
|
||||
}
|
||||
|
||||
/* Process an array of nodes */
|
||||
function walk(children) {
|
||||
if (!children)
|
||||
return;
|
||||
|
||||
children.forEach(function(child) {
|
||||
var line = (child.location || { }).line || 0;
|
||||
var offset = line - 1;
|
||||
|
||||
/* Scripts get their text processed as HTML */
|
||||
if (child.type == 'script' && child.children) {
|
||||
var parser = new htmlparser.Parser(handler, { includeLocation: true });
|
||||
|
||||
/* Make note of how far into the outer HTML file we are */
|
||||
offsets += offset;
|
||||
|
||||
child.children.forEach(function(node) {
|
||||
parser.parseChunk(node.raw);
|
||||
});
|
||||
parser.done();
|
||||
|
||||
offsets -= offset;
|
||||
|
||||
/* Tags get extracted as usual */
|
||||
} else if (child.type == 'tag') {
|
||||
tag(child);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Process a single loaded tag */
|
||||
function tag(node) {
|
||||
|
||||
var tasks, line, entry;
|
||||
var attrs = node.attribs || { };
|
||||
var nest = true;
|
||||
|
||||
/* Extract translate strings */
|
||||
if ("translate" in attrs || "translatable" in attrs) {
|
||||
tasks = (attrs["translate"] || attrs["translatable"] || "yes").split(" ");
|
||||
|
||||
/* Calculate the line location taking into account nested parsing */
|
||||
line = (node.location || { })["line"] || 0;
|
||||
line += offsets;
|
||||
|
||||
entry = {
|
||||
msgctxt: attrs['translate-context'] || attrs['context'],
|
||||
msgid_plural: attrs['translate-plural'],
|
||||
locations: [ filename + ":" + line ]
|
||||
};
|
||||
|
||||
/* For each thing listed */
|
||||
tasks.forEach(function(task) {
|
||||
var copy = Object.assign({}, entry);
|
||||
|
||||
/* The element text itself */
|
||||
if (task == "yes" || task == "translate") {
|
||||
copy.msgid = extract(node.children);
|
||||
nest = false;
|
||||
|
||||
/* An attribute */
|
||||
} else if (task) {
|
||||
copy.msgid = attrs[task];
|
||||
}
|
||||
|
||||
if (copy.msgid)
|
||||
push(copy);
|
||||
});
|
||||
}
|
||||
|
||||
/* Walk through all the children */
|
||||
if (nest)
|
||||
walk(node.children);
|
||||
}
|
||||
|
||||
/* Push an entry onto the list */
|
||||
function push(entry) {
|
||||
var key = entry.msgid + "\0" + entry.msgid_plural + "\0" + entry.msgctxt;
|
||||
var prev = entries[key];
|
||||
if (prev) {
|
||||
prev.locations = prev.locations.concat(entry.locations);
|
||||
} else {
|
||||
entries[key] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract the given text */
|
||||
function extract(children) {
|
||||
if (!children)
|
||||
return null;
|
||||
|
||||
var i, len, node, str = [];
|
||||
children.forEach(function(node) {
|
||||
if (node.type == 'tag' && node.children)
|
||||
str.push(extract(node.children))
|
||||
else if (node.type == 'text' && node.data)
|
||||
str.push(node.data);
|
||||
});
|
||||
|
||||
return str.join("");
|
||||
}
|
||||
|
||||
/* Escape a string for inclusion in po file */
|
||||
function escape(string) {
|
||||
var bs = string.split('\\').join('\\\\').split('"').join('\\"');
|
||||
return bs.split("\n").map(function(line) {
|
||||
return '"' + line + '"';
|
||||
}).join("\n");
|
||||
}
|
||||
|
||||
/* Finish by writing out the strings */
|
||||
function finish() {
|
||||
var result = [
|
||||
'msgid ""',
|
||||
'msgstr ""',
|
||||
'"Project-Id-Version: PACKAGE_VERSION\\n"',
|
||||
'"MIME-Version: 1.0\\n"',
|
||||
'"Content-Type: text/plain; charset=UTF-8\\n"',
|
||||
'"Content-Transfer-Encoding: 8bit\\n"',
|
||||
'"X-Generator: Cockpit html2po\\n"',
|
||||
'',
|
||||
];
|
||||
|
||||
var msgid, entry;
|
||||
for (msgid in entries) {
|
||||
entry = entries[msgid];
|
||||
result.push('#: ' + entry.locations.join(" "));
|
||||
if (entry.msgctxt)
|
||||
result.push('msgctxt ' + escape(entry.msgctxt));
|
||||
result.push('msgid ' + escape(entry.msgid));
|
||||
if (entry.msgid_plural) {
|
||||
result.push('msgid_plural ' + escape(entry.msgid_plural));
|
||||
result.push('msgstr[0] ""');
|
||||
result.push('msgstr[1] ""');
|
||||
} else {
|
||||
result.push('msgstr ""');
|
||||
}
|
||||
result.push('');
|
||||
}
|
||||
|
||||
var data = result.join('\n');
|
||||
if (!opts.output) {
|
||||
process.stdout.write(data);
|
||||
process.exit(0);
|
||||
} else {
|
||||
fs.writeFile(opts.output, data, function(err) {
|
||||
if (err)
|
||||
fatal(err.message);
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
185
po/manifest2po
185
po/manifest2po
|
|
@ -1,185 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
* Extracts translatable strings from manifest.json files.
|
||||
*
|
||||
*/
|
||||
|
||||
function fatal(message, code) {
|
||||
console.log((filename || "manifest2po") + ": " + message);
|
||||
process.exit(code || 1);
|
||||
}
|
||||
|
||||
function usage() {
|
||||
console.log("usage: manifest2po [-o output] input...");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
var fs, path, stdio;
|
||||
|
||||
try {
|
||||
fs = require('fs');
|
||||
path = require('path');
|
||||
stdio = require('stdio');
|
||||
} catch (ex) {
|
||||
fatal(ex.message, 127); /* missing looks for this */
|
||||
}
|
||||
|
||||
var opts = stdio.getopt({
|
||||
directory: { key: "d", args: 1, description: "Base directory for input files" },
|
||||
output: { key: "o", args: 1, description: "Output file" },
|
||||
from: { key: "f", args: 1, description: "File containing list of input files" },
|
||||
});
|
||||
|
||||
if (!opts.from && opts.args.length < 1) {
|
||||
usage();
|
||||
}
|
||||
|
||||
var input = opts.args;
|
||||
var entries = { };
|
||||
|
||||
/* Filename being parsed */
|
||||
var filename = null;
|
||||
|
||||
prepare();
|
||||
|
||||
/* Decide what input files to process */
|
||||
function prepare() {
|
||||
if (opts.from) {
|
||||
fs.readFile(opts.from, { encoding: "utf-8"}, function(err, data) {
|
||||
if (err)
|
||||
fatal(err.message);
|
||||
input = data.split("\n").filter(function(value) {
|
||||
return !!value;
|
||||
}).concat(input);
|
||||
step();
|
||||
});
|
||||
} else {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
/* Now process each file in turn */
|
||||
function step() {
|
||||
filename = input.shift();
|
||||
if (filename === undefined) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.basename(filename) != "manifest.json")
|
||||
return step();
|
||||
|
||||
fs.readFile(filename, { encoding: "utf-8"}, function(err, data) {
|
||||
if (err)
|
||||
fatal(err.message);
|
||||
|
||||
process_manifest(JSON.parse(data));
|
||||
|
||||
return step();
|
||||
});
|
||||
}
|
||||
|
||||
function process_manifest(manifest) {
|
||||
if (manifest.menu)
|
||||
process_menu(manifest.menu);
|
||||
if (manifest.tools)
|
||||
process_menu(manifest.tools);
|
||||
}
|
||||
|
||||
function process_keywords(keywords) {
|
||||
keywords.forEach(v => {
|
||||
v.matches.forEach(keyword =>
|
||||
push({
|
||||
msgid: keyword,
|
||||
locations: [ filename ]
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function process_docs(docs) {
|
||||
docs.forEach(doc => {
|
||||
push({
|
||||
msgid: doc.label,
|
||||
locations: [ filename ]
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function process_menu(menu) {
|
||||
for (var m in menu) {
|
||||
if (menu[m].label) {
|
||||
push({
|
||||
msgid: menu[m].label,
|
||||
locations: [ filename ]
|
||||
});
|
||||
}
|
||||
if (menu[m].keywords)
|
||||
process_keywords(menu[m].keywords);
|
||||
if (menu[m].docs)
|
||||
process_docs(menu[m].docs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Push an entry onto the list */
|
||||
function push(entry) {
|
||||
var key = entry.msgid + "\0" + entry.msgid_plural + "\0" + entry.msgctxt;
|
||||
var prev = entries[key];
|
||||
if (prev) {
|
||||
prev.locations = prev.locations.concat(entry.locations);
|
||||
} else {
|
||||
entries[key] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
/* Escape a string for inclusion in po file */
|
||||
function escape(string) {
|
||||
var bs = string.split('\\').join('\\\\').split('"').join('\\"');
|
||||
return bs.split("\n").map(function(line) {
|
||||
return '"' + line + '"';
|
||||
}).join("\n");
|
||||
}
|
||||
|
||||
/* Finish by writing out the strings */
|
||||
function finish() {
|
||||
var result = [
|
||||
'msgid ""',
|
||||
'msgstr ""',
|
||||
'"Project-Id-Version: PACKAGE_VERSION\\n"',
|
||||
'"MIME-Version: 1.0\\n"',
|
||||
'"Content-Type: text/plain; charset=UTF-8\\n"',
|
||||
'"Content-Transfer-Encoding: 8bit\\n"',
|
||||
'"X-Generator: Cockpit manifest2po\\n"',
|
||||
'',
|
||||
];
|
||||
|
||||
var msgid, entry;
|
||||
for (msgid in entries) {
|
||||
entry = entries[msgid];
|
||||
result.push('#: ' + entry.locations.join(" "));
|
||||
if (entry.msgctxt)
|
||||
result.push('msgctxt ' + escape(entry.msgctxt));
|
||||
result.push('msgid ' + escape(entry.msgid));
|
||||
if (entry.msgid_plural) {
|
||||
result.push('msgid_plural ' + escape(entry.msgid_plural));
|
||||
result.push('msgstr[0] ""');
|
||||
result.push('msgstr[1] ""');
|
||||
} else {
|
||||
result.push('msgstr ""');
|
||||
}
|
||||
result.push('');
|
||||
}
|
||||
|
||||
var data = result.join('\n');
|
||||
if (!opts.output) {
|
||||
process.stdout.write(data);
|
||||
process.exit(0);
|
||||
} else {
|
||||
fs.writeFile(opts.output, data, function(err) {
|
||||
if (err)
|
||||
fatal(err.message);
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
(function (root, data) {
|
||||
var loaded, module;
|
||||
|
||||
/* Load into Cockpit locale */
|
||||
if (typeof cockpit === 'object') {
|
||||
cockpit.locale(data)
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
if (!loaded)
|
||||
root.po = data;
|
||||
|
||||
/* The syntax of this line is important by po2json */
|
||||
}(this, {"":{"language":"en"}}));
|
||||
127
po/po2json
127
po/po2json
|
|
@ -1,127 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
function fatal(message, code) {
|
||||
console.log((filename || "html2po") + ": " + message);
|
||||
process.exit(code || 1);
|
||||
}
|
||||
|
||||
function usage() {
|
||||
console.log("usage: po2json [--module=template.js] input output");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
var fs, po2json, Jed, stdio;
|
||||
|
||||
try {
|
||||
fs = require('fs');
|
||||
po2json = require('po2json');
|
||||
Jed = require('jed');
|
||||
stdio = require('stdio');
|
||||
} catch(ex) {
|
||||
fatal(ex.message, 127); /* missing looks for this */
|
||||
}
|
||||
|
||||
var argi = 2;
|
||||
var filename = null;
|
||||
|
||||
var opts = stdio.getopt({
|
||||
module: { key: "m", args: 1, description: "Module template to include" },
|
||||
output: { key: "o", args: 1, description: "Output file" },
|
||||
});
|
||||
|
||||
if (opts.args.length != 1) {
|
||||
usage();
|
||||
}
|
||||
|
||||
parse();
|
||||
|
||||
function prepareHeader(header) {
|
||||
var body, statement, plurals = header["plural-forms"], ret = null;
|
||||
if (plurals) {
|
||||
try {
|
||||
/* Check that the plural forms isn't being sneaky since we build a function here */
|
||||
Jed.PF.parse(plurals);
|
||||
} catch(ex) {
|
||||
fatal("bad plural forms: " + ex.message, 1);
|
||||
}
|
||||
|
||||
/* A function for the front end */
|
||||
statement = header["plural-forms"];
|
||||
if (statement[statement.length - 1] != ';')
|
||||
statement += ';';
|
||||
ret = 'function(n) {\nvar nplurals, plural;\n' + statement + '\nreturn plural;\n}';
|
||||
|
||||
/* Added back in later */
|
||||
delete header["plural-forms"];
|
||||
}
|
||||
|
||||
/* We don't need to be transferring this */
|
||||
delete header["project-id-version"];
|
||||
delete header["report-msgid-bugs-to"];
|
||||
delete header["pot-creation-date"];
|
||||
delete header["po-revision-date"];
|
||||
delete header["last-translator"];
|
||||
delete header["language-team"];
|
||||
delete header["mime-version"];
|
||||
delete header["content-type"];
|
||||
delete header["content-transfer-encoding"];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse and process the po data */
|
||||
function parse() {
|
||||
filename = opts.args[0];
|
||||
po2json.parseFile(opts.args[0], { "fuzzy": true }, function(err, jsonData) {
|
||||
var plurals, pos;
|
||||
|
||||
if (err)
|
||||
fatal(err.message);
|
||||
|
||||
var header = jsonData[""];
|
||||
if (header)
|
||||
plurals = prepareHeader(header);
|
||||
|
||||
var data = JSON.stringify(jsonData, null, 1);
|
||||
|
||||
/* We know the brace in is the location to insert our function */
|
||||
if (plurals) {
|
||||
pos = data.indexOf('{', 1);
|
||||
data = data.substr(0, pos + 1) + "'plural-forms':" + String(plurals) + "," + data.substr(pos + 1);
|
||||
}
|
||||
|
||||
if (data == JSON.stringify({}))
|
||||
finish("");
|
||||
else
|
||||
wrap(data);
|
||||
});
|
||||
}
|
||||
|
||||
/* Wrap the data if desired */
|
||||
function wrap(data) {
|
||||
if (opts.module) {
|
||||
filename = opts.module;
|
||||
fs.readFile(opts.module, { encoding: "utf-8" }, function(err, template) {
|
||||
if (err)
|
||||
fatal(err.message);
|
||||
data = template.replace('{"":{"language":"en"}}', data);
|
||||
finish(data);
|
||||
});
|
||||
} else {
|
||||
finish(data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write it out */
|
||||
function finish(data) {
|
||||
if (opts.output) {
|
||||
fs.writeFile(opts.output, data, function(err) {
|
||||
if (err)
|
||||
fatal(err.message);
|
||||
process.exit(0);
|
||||
});
|
||||
} else {
|
||||
process.stdout.write(data);
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
import cockpit from 'cockpit';
|
||||
import React from 'react';
|
||||
import './app.scss';
|
||||
import View from "./recordings.jsx";
|
||||
|
||||
const _ = cockpit.gettext;
|
||||
|
|
@ -29,8 +28,7 @@ export class Application extends React.Component {
|
|||
super();
|
||||
this.state = { hostname: _("Unknown") };
|
||||
|
||||
cockpit.file('/etc/hostname').read()
|
||||
.done((content) => {
|
||||
cockpit.file('/etc/hostname').watch(content => {
|
||||
this.setState({ hostname: content.trim() });
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
This file is part of Cockpit.
|
||||
|
||||
Copyright (C) 2017 Red Hat, Inc.
|
||||
|
||||
Cockpit is free software; you can redistribute it and/or modify it
|
||||
|
|
@ -15,25 +13,22 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
|
||||
along with this package; If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title translate>Session Recording</title>
|
||||
<title translate>Cockpit Session Recording</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="index.css">
|
||||
|
||||
<script type="text/javascript" src="../base1/cockpit.js"></script>
|
||||
<script type="text/javascript" src="po.js"></script>
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
<script type="text/javascript" src="po.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="pf-m-redhat-form">
|
||||
<div class="ct-page-fill" id="app"></div>
|
||||
<body class="pf-m-redhat-font">
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
|||
13
src/index.js
13
src/index.js
|
|
@ -17,12 +17,11 @@
|
|||
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "./lib/patternfly/patternfly-4-cockpit.scss";
|
||||
|
||||
import "core-js/stable";
|
||||
import "cockpit-dark-theme";
|
||||
import "patternfly/patternfly-4-cockpit.scss";
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { Application } from './app.jsx';
|
||||
/*
|
||||
* PF4 overrides need to come after the JSX components imports because
|
||||
|
|
@ -31,8 +30,10 @@ import { Application } from './app.jsx';
|
|||
* out of the dist/index.js and since it will maintain the order of the imported CSS,
|
||||
* the overrides will be correctly in the end of our stylesheet.
|
||||
*/
|
||||
import "./lib/patternfly/patternfly-4-overrides.scss";
|
||||
import "patternfly/patternfly-4-overrides.scss";
|
||||
import './app.scss';
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
ReactDOM.render(React.createElement(Application, {}), document.getElementById('app'));
|
||||
const root = createRoot(document.getElementById('app'));
|
||||
root.render(<Application />);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"name": "session-recording",
|
||||
|
||||
"requires": {
|
||||
"cockpit": "122"
|
||||
"cockpit": "137"
|
||||
},
|
||||
|
||||
"menu": {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.pf-c-progress__indicator:after {
|
||||
.pf-c-progress__indicator::after {
|
||||
content: "";
|
||||
position: relative;
|
||||
width: 20px;
|
||||
|
|
|
|||
|
|
@ -623,7 +623,7 @@ const PacketBuffer = class {
|
|||
|
||||
function SearchEntry(props) {
|
||||
return (
|
||||
<span className="search-result"><a onClick={(e) => props.fastForwardToTS(props.pos, e)}>{formatDuration(props.pos)}</a></span>
|
||||
<span className="search-result"><a href="#search-result" onClick={(e) => props.fastForwardToTS(props.pos, e)}>{formatDuration(props.pos)}</a></span>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,12 @@ import configparser
|
|||
TEST_DIR = os.path.dirname(__file__)
|
||||
sys.path.append(os.path.join(TEST_DIR, "common"))
|
||||
sys.path.append(os.path.join(os.path.dirname(TEST_DIR), "bots/machine"))
|
||||
from testlib import *
|
||||
import testlib
|
||||
|
||||
# Test with pre-recorded journal files
|
||||
class TestApplication(MachineCase):
|
||||
# Nondestructive tests all run in the same running VM. This allows them to run in Packit, Fedora, and RHEL dist-git gating
|
||||
# They must not permanently change any file or configuration on the system in a way that influences other tests.
|
||||
@testlib.nondestructive
|
||||
class TestApplication(testlib.MachineCase):
|
||||
def _login(self, loc="/session-recording", wait="#app"):
|
||||
self.login_and_go(loc)
|
||||
b = self.browser
|
||||
|
|
@ -408,4 +410,4 @@ class TestApplication(MachineCase):
|
|||
b.wait_present("#app")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_main()
|
||||
testlib.test_main()
|
||||
|
|
|
|||
1
test/reference-image
Normal file
1
test/reference-image
Normal file
|
|
@ -0,0 +1 @@
|
|||
fedora-36
|
||||
8
test/run
8
test/run
|
|
@ -4,5 +4,11 @@ set -eu
|
|||
# This is the expected entry point for Cockpit CI; will be called without
|
||||
# arguments but with an appropriate $TEST_OS, and optionally $TEST_SCENARIO
|
||||
|
||||
[ -z "${TEST_SCENARIO:-}" ] || export TEST_BROWSER="$TEST_SCENARIO"
|
||||
TEST_SCENARIO="${TEST_SCENARIO:-}"
|
||||
[ "${TEST_SCENARIO}" = "${TEST_SCENARIO##firefox}" ] || export TEST_BROWSER=firefox
|
||||
export RUN_TESTS_OPTIONS=--track-naughties
|
||||
|
||||
# linters are off by default for production builds, but we want to run them in CI
|
||||
export LINT=1
|
||||
|
||||
make check
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
# image-customize script to enable cockpit in test VMs
|
||||
# The application RPM will be installed separately
|
||||
set -eu
|
||||
# image-customize script to prepare a bots VM for testing this application
|
||||
# The application package will be installed separately
|
||||
set -eux
|
||||
|
||||
# don't force https:// (self-signed cert)
|
||||
printf "[WebService]\\nAllowUnencrypted=true\\n" > /etc/cockpit/cockpit.conf
|
||||
|
|
@ -11,5 +11,5 @@ if type firewall-cmd >/dev/null 2>&1; then
|
|||
fi
|
||||
systemctl enable cockpit.socket
|
||||
|
||||
# HACK: See https://github.com/cockpit-project/cockpit/issues/14133
|
||||
mkdir -p /usr/share/cockpit/packagekit
|
||||
# needed for testAppMenu
|
||||
dnf install -y cockpit-packagekit
|
||||
|
|
|
|||
|
|
@ -1,202 +0,0 @@
|
|||
const path = require("path");
|
||||
const copy = require("copy-webpack-plugin");
|
||||
const extract = require("mini-css-extract-plugin");
|
||||
const fs = require("fs");
|
||||
const webpack = require("webpack");
|
||||
const CompressionPlugin = require("compression-webpack-plugin");
|
||||
const ESLintPlugin = require('eslint-webpack-plugin');
|
||||
|
||||
var externals = {
|
||||
cockpit: "cockpit",
|
||||
};
|
||||
|
||||
/* These can be overridden, typically from the Makefile.am */
|
||||
const srcdir = (process.env.SRCDIR || __dirname) + path.sep + "src";
|
||||
const builddir = process.env.SRCDIR || __dirname;
|
||||
const distdir = builddir + path.sep + "dist";
|
||||
const section = process.env.ONLYDIR || null;
|
||||
const libdir = path.resolve(srcdir, "lib")
|
||||
// absolute path disables recursive module resolution, so build a relative one
|
||||
const nodedir = path.relative(process.cwd(), path.resolve((process.env.SRCDIR || __dirname), "node_modules"));
|
||||
|
||||
/* A standard nodejs and webpack pattern */
|
||||
var production = process.env.NODE_ENV === "production";
|
||||
|
||||
var info = {
|
||||
entries: {
|
||||
index: [
|
||||
"./index.js",
|
||||
],
|
||||
},
|
||||
files: [
|
||||
"index.html",
|
||||
"manifest.json"
|
||||
],
|
||||
};
|
||||
|
||||
var output = {
|
||||
path: distdir,
|
||||
filename: "[name].js",
|
||||
sourceMapFilename: "[file].map",
|
||||
};
|
||||
|
||||
// Non-JS files which are copied verbatim to dist/
|
||||
const copy_files = [
|
||||
"./src/index.html",
|
||||
"./src/manifest.json",
|
||||
];
|
||||
|
||||
/*
|
||||
* Note that we're avoiding the use of path.join as webpack and nodejs
|
||||
* want relative paths that start with ./ explicitly.
|
||||
*
|
||||
* In addition we mimic the VPATH style functionality of GNU Makefile
|
||||
* where we first check builddir, and then srcdir.
|
||||
*/
|
||||
|
||||
function vpath(/* ... */) {
|
||||
var filename = Array.prototype.join.call(arguments, path.sep);
|
||||
var expanded = builddir + path.sep + filename;
|
||||
if (fs.existsSync(expanded)) return expanded;
|
||||
expanded = srcdir + path.sep + filename;
|
||||
return expanded;
|
||||
}
|
||||
|
||||
/* Qualify all the paths in entries */
|
||||
Object.keys(info.entries).forEach(function (key) {
|
||||
if (section && key.indexOf(section) !== 0) {
|
||||
delete info.entries[key];
|
||||
return;
|
||||
}
|
||||
|
||||
info.entries[key] = info.entries[key].map(function (value) {
|
||||
if (value.indexOf("/") === -1) return value;
|
||||
else return vpath(value);
|
||||
});
|
||||
});
|
||||
|
||||
/* Qualify all the paths in files listed */
|
||||
var files = [];
|
||||
info.files.forEach(function (value) {
|
||||
if (!section || value.indexOf(section) === 0)
|
||||
files.push({ from: vpath("src", value), to: value });
|
||||
});
|
||||
info.files = files;
|
||||
|
||||
const plugins = [
|
||||
new copy({ patterns: copy_files }),
|
||||
new extract({filename: "[name].css"}),
|
||||
new ESLintPlugin({ extensions: ["js", "jsx"], exclude: ["spec", "node_modules", "src/lib"] }),
|
||||
];
|
||||
|
||||
/* Only minimize when in production mode */
|
||||
if (production) {
|
||||
plugins.unshift(
|
||||
new CompressionPlugin({
|
||||
test: /\.(css|js|html)$/,
|
||||
deleteOriginalAssets: true,
|
||||
}));
|
||||
}
|
||||
|
||||
var babel_loader = {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: [
|
||||
[
|
||||
"@babel/env",
|
||||
{
|
||||
targets: {
|
||||
chrome: "57",
|
||||
firefox: "52",
|
||||
safari: "10.3",
|
||||
edge: "16",
|
||||
opera: "44",
|
||||
},
|
||||
},
|
||||
],
|
||||
"@babel/preset-react",
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
mode: production ? "production" : "development",
|
||||
resolve: {
|
||||
modules: [libdir, nodedir],
|
||||
},
|
||||
entry: info.entries,
|
||||
externals: externals,
|
||||
output: output,
|
||||
devtool: "source-map",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
exclude: /node_modules/,
|
||||
use: babel_loader,
|
||||
test: /\.(js|jsx)$/,
|
||||
},
|
||||
/* HACK: remove unwanted fonts from PatternFly's css */
|
||||
{
|
||||
test: /patternfly-4-cockpit.scss$/,
|
||||
use: [
|
||||
extract.loader,
|
||||
{
|
||||
loader: "css-loader",
|
||||
options: {
|
||||
sourceMap: true,
|
||||
url: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: "string-replace-loader",
|
||||
options: {
|
||||
multiple: [
|
||||
{
|
||||
search: /src:url\("patternfly-icons-fake-path\/pficon[^}]*/g,
|
||||
replace: "src:url('fonts/patternfly.woff')format('woff');",
|
||||
},
|
||||
{
|
||||
search: /@font-face[^}]*patternfly-fonts-fake-path[^}]*}/g,
|
||||
replace: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sourceMap: !production,
|
||||
sassOptions: {
|
||||
outputStyle: production ? 'compressed' : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.s?css$/,
|
||||
exclude: /patternfly-4-cockpit.scss/,
|
||||
use: [
|
||||
extract.loader,
|
||||
{
|
||||
loader: "css-loader",
|
||||
options: {
|
||||
sourceMap: true,
|
||||
url: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sourceMap: !production,
|
||||
sassOptions: {
|
||||
outputStyle: production ? 'compressed' : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: plugins,
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue