starter-kit/Makefile
Pino Toscano a92a4b7726 spec: automatic Provides generation from node_modules
One of the requirements of Fedora packages that contain the result of
nodejs modules is to list them as Provides. Listing them manually in the
spec file would be a manual job, too error prone.

As solution, create the static list of nodejs modules as RPM Provides,
and include it in the spec file so it is used when building the RPM
package. This is done by querying npm for the list of locally installed
modules, and using jq to output that as Provides. There are manglings
done:
- the '@' in the name of a module is removed, and '/' replaced with '-'
- e.g. "-alpha" or "-beta-" in the version of a module are turned into
  "~something", as "-" is not accepted as version by RPM (whereas the
  tilde is the compliant way for pre-releases in versioning guidelines)

Sadly this static inclusion is the only possible way at the moment,
as a in-package dependency generator is not possible, nor any other form
of dynamic filling of package relationships only at build/install time.
2023-03-17 10:02:25 +01:00

219 lines
7.9 KiB
Makefile

# extract name from package.json
PACKAGE_NAME := $(shell awk '/"name":/ {gsub(/[",]/, "", $$2); print $$2}' package.json)
RPM_NAME := cockpit-$(PACKAGE_NAME)
VERSION := $(shell T=$$(git describe 2>/dev/null) || T=1; echo $$T | tr '-' '.')
ifeq ($(TEST_OS),)
TEST_OS = centos-8-stream
endif
export TEST_OS
TARFILE=$(RPM_NAME)-$(VERSION).tar.xz
NODE_CACHE=$(RPM_NAME)-node-$(VERSION).tar.xz
SPEC=$(RPM_NAME).spec
SPEC_LIST=nodejs_provides.list
PREFIX ?= /usr/local
APPSTREAMFILE=org.cockpit-project.$(PACKAGE_NAME).metainfo.xml
VM_IMAGE=$(CURDIR)/test/images/$(TEST_OS)
# stamp file to check for node_modules/
NODE_MODULES_TEST=package-lock.json
# 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: $(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 = 54f2fd58a3645c4a222e2b1b9b5f1b9321bed998 # 285 + Move to a webpack module
$(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
#
LINGUAS=$(basename $(notdir $(wildcard po/*.po)))
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 $$(find src/ -name '*.js' -o -name '*.jsx')
po/$(PACKAGE_NAME).html.pot: $(NODE_MODULES_TEST) $(COCKPIT_REPO_STAMP)
pkg/lib/html2po.js -o $@ $$(find src -name '*.html')
po/$(PACKAGE_NAME).manifest.pot: $(NODE_MODULES_TEST) $(COCKPIT_REPO_STAMP)
pkg/lib/manifest2po.js 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).metainfo.pot
msgcat --sort-output --output-file=$@ $^
po/LINGUAS:
echo $(LINGUAS) | tr ' ' '\n' > $@
#
# Build/Install/dist
#
%.spec: packaging/%.spec.in $(SPEC_LIST)
sed -e 's/%{VERSION}/$(VERSION)/g' \
-e '/%{NODEJS_PROVIDES}/{r $(SPEC_LIST)' -e 'd}' \
$< > $@
$(DIST_TEST): $(NODE_MODULES_TEST) $(COCKPIT_REPO_STAMP) $(shell find src/ -type f) package.json webpack.config.js
NODE_ENV=$(NODE_ENV) node_modules/.bin/webpack
watch:
NODE_ENV=$(NODE_ENV) npm run watch
clean:
rm -rf dist/
rm -f $(SPEC)
rm -f po/LINGUAS
rm -f $(SPEC_LIST)
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: $(DIST_TEST)
mkdir -p ~/.local/share/cockpit
ln -s `pwd`/dist ~/.local/share/cockpit/$(PACKAGE_NAME)
# 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)
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 package-lock.json (so that
# node_modules/ can be reconstructed if necessary)
$(TARFILE): export NODE_ENV=production
$(TARFILE): $(DIST_TEST) $(SPEC)
if type appstream-util >/dev/null 2>&1; then appstream-util validate-relax --nonet *.metainfo.xml; fi
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/
$(NODE_CACHE): $(NODE_MODULES_TEST)
tar --xz $(TAR_ARGS) -cf $@ node_modules
node-cache: $(NODE_CACHE)
$(SPEC_LIST): $(NODE_MODULES_TEST)
npm ls --json | \
jq -r 'def fn: gsub("@"; "") | gsub("/"; "-"); def fv: gsub("-(?<a>alpha|beta|rc)-?"; "~\(.a)"); .dependencies | to_entries[] | "Provides: bundled(nodejs-\(.key | fn)) = \(.value.version | fv)"' \
> $@
# convenience target for developers
srpm: $(TARFILE) $(NODE_CACHE) $(SPEC)
rpmbuild -bs \
--define "_sourcedir `pwd`" \
--define "_srcrpmdir `pwd`" \
$(SPEC)
# convenience target for developers
rpm: $(TARFILE) $(NODE_CACHE) $(SPEC)
mkdir -p "`pwd`/output"
mkdir -p "`pwd`/rpmbuild"
rpmbuild -bb \
--define "_sourcedir `pwd`" \
--define "_specdir `pwd`" \
--define "_builddir `pwd`/rpmbuild" \
--define "_srcrpmdir `pwd`" \
--define "_rpmdir `pwd`/output" \
--define "_buildrootdir `pwd`/build" \
$(SPEC)
find `pwd`/output -name '*.rpm' -printf '%f\n' -exec mv {} . \;
rm -r "`pwd`/rpmbuild"
rm -r "`pwd`/output" "`pwd`/build"
ifeq ("$(TEST_SCENARIO)","pybridge")
COCKPIT_PYBRIDGE_REF = main
COCKPIT_WHEEL = cockpit-0-py3-none-any.whl
$(COCKPIT_WHEEL):
# aka: pip wheel git+https://github.com/cockpit-project/cockpit.git@${COCKPIT_PYBRIDGE_REF}
rm -rf tmp/pybridge
git init tmp/pybridge
git -C tmp/pybridge remote add origin https://github.com/cockpit-project/cockpit
git -C tmp/pybridge fetch --depth=1 origin ${COCKPIT_PYBRIDGE_REF}
git -C tmp/pybridge reset --hard FETCH_HEAD
cp "$$(tmp/pybridge/tools/make-wheel)" $@
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 --no-network --fresh \
$(VM_CUSTOMIZE_FLAGS) \
--upload $(NODE_CACHE):/var/tmp/ --build $(TARFILE) \
--script $(CURDIR)/test/vm.install $(TEST_OS)
# convenience target for the above
vm: $(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: 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
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 --ignore-scripts
env -u NODE_ENV npm prune
.PHONY: all clean install devel-install devel-uninstall print-version dist node-cache rpm prepare-check check vm print-vm