Vendor anaconda-webui and build it into the installer image
Replaces installing a patched anaconda-webui RPM from the forge's package registry: installer/anaconda-webui/vendor/ is upstream release 86 verbatim (see its PROVENANCE.md), and installer/Containerfile builds it with upstream's own `make install` in a native-platform stage, then copies the result over stock Fedora anaconda-webui. The produced file set matches Fedora's anaconda-webui-86-1.fc46 exactly. Edits to the installer UI now happen in vendor/ and ship on the next `task build-installer-iso`, with no RPM, registry or CI in between. Also follows bootc-image-builder's rename of --bootc-installer-payload-ref to --installer-payload-ref. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
39de966517
commit
255042fe8d
378 changed files with 494732 additions and 92 deletions
21
Taskfile.yml
21
Taskfile.yml
|
|
@ -3,16 +3,6 @@ version: '3'
|
||||||
vars:
|
vars:
|
||||||
IMAGE: quine-linux
|
IMAGE: quine-linux
|
||||||
INSTALLER_IMAGE: quine-installer
|
INSTALLER_IMAGE: quine-installer
|
||||||
# dnf .repo for the patched anaconda-webui, built by the fork's CI.
|
|
||||||
RPM_REPO_URL: https://forge.quinefoundation.com/api/packages/quine/rpm.repo
|
|
||||||
# Checksum of that repo's current metadata; passed as a build arg so the
|
|
||||||
# installer's dnf layer is rebuilt exactly when a new RPM gets published.
|
|
||||||
RPM_REPO_REV:
|
|
||||||
sh: |
|
|
||||||
[ -n "{{.RPM_REPO_URL}}" ] || exit 0
|
|
||||||
base=$(curl -fsSL "{{.RPM_REPO_URL}}" | sed -n 's/^baseurl=//p' | head -1)
|
|
||||||
sum() { if command -v sha256sum >/dev/null; then sha256sum; else shasum -a 256; fi; }
|
|
||||||
curl -fsSL "$base/repodata/repomd.xml" 2>/dev/null | sum | cut -c1-16
|
|
||||||
OUTPUT_DIR: ./output
|
OUTPUT_DIR: ./output
|
||||||
# Any docker-CLI-compatible runtime works for plain builds. Prefers podman,
|
# Any docker-CLI-compatible runtime works for plain builds. Prefers podman,
|
||||||
# falls back to docker; override with CONTAINER_RUNTIME=... (env or task var).
|
# falls back to docker; override with CONTAINER_RUNTIME=... (env or task var).
|
||||||
|
|
@ -77,22 +67,19 @@ tasks:
|
||||||
Build the patched-Anaconda installer-environment container (see
|
Build the patched-Anaconda installer-environment container (see
|
||||||
installer/Containerfile and docs/patched-anaconda-installer.md). This
|
installer/Containerfile and docs/patched-anaconda-installer.md). This
|
||||||
is separate from the quine-linux OS image itself. Defaults to the
|
is separate from the quine-linux OS image itself. Defaults to the
|
||||||
anaconda-webui build at {{.RPM_REPO_URL}}; override with
|
anaconda-webui built from installer/anaconda-webui/vendor/.
|
||||||
`task build-installer-image RPM_REPO_URL=...` (empty = stock Fedora).
|
|
||||||
cmds:
|
cmds:
|
||||||
- sudo {{.CONTAINER_RUNTIME}} build --platform {{.PLATFORM}} -t {{.INSTALLER_IMAGE}} --build-arg RPM_REPO_URL={{.RPM_REPO_URL}} --build-arg RPM_REPO_REV={{.RPM_REPO_REV}} -f installer/Containerfile installer/
|
- sudo {{.CONTAINER_RUNTIME}} build --platform {{.PLATFORM}} -t {{.INSTALLER_IMAGE}} -f installer/Containerfile installer/
|
||||||
|
|
||||||
build-installer-iso:
|
build-installer-iso:
|
||||||
desc: >-
|
desc: >-
|
||||||
Build a bootable install ISO using the patched-Anaconda installer
|
Build a bootable install ISO using the patched-Anaconda installer
|
||||||
image to install the quine-linux bootc image, via
|
image to install the quine-linux bootc image, via
|
||||||
bootc-image-builder's `bootc-installer` type. Output lands in
|
bootc-image-builder's `bootc-installer` type. Output lands in
|
||||||
{{.OUTPUT_DIR}}/bootc-installer/install.iso. Uses the same RPM_REPO_URL
|
{{.OUTPUT_DIR}}/bootc-installer/install.iso.
|
||||||
default/override as build-installer-image.
|
|
||||||
deps: [build-image]
|
deps: [build-image]
|
||||||
cmds:
|
cmds:
|
||||||
- task: build-installer-image
|
- task: build-installer-image
|
||||||
vars: { RPM_REPO_URL: "{{.RPM_REPO_URL}}" }
|
|
||||||
- task: export-to-storage
|
- task: export-to-storage
|
||||||
vars: { IMAGES: "{{.IMAGE}} {{.INSTALLER_IMAGE}}" }
|
vars: { IMAGES: "{{.IMAGE}} {{.INSTALLER_IMAGE}}" }
|
||||||
- mkdir -p {{.OUTPUT_DIR}}
|
- mkdir -p {{.OUTPUT_DIR}}
|
||||||
|
|
@ -104,7 +91,7 @@ tasks:
|
||||||
{{.BIB_IMAGE}}
|
{{.BIB_IMAGE}}
|
||||||
--target-arch {{.TARGET_ARCH}}
|
--target-arch {{.TARGET_ARCH}}
|
||||||
--type bootc-installer
|
--type bootc-installer
|
||||||
--bootc-installer-payload-ref localhost/{{.IMAGE}}
|
--installer-payload-ref localhost/{{.IMAGE}}
|
||||||
localhost/{{.INSTALLER_IMAGE}}
|
localhost/{{.INSTALLER_IMAGE}}
|
||||||
generates:
|
generates:
|
||||||
- "{{.OUTPUT_DIR}}/bootc-installer/install.iso"
|
- "{{.OUTPUT_DIR}}/bootc-installer/install.iso"
|
||||||
|
|
|
||||||
|
|
@ -11,26 +11,26 @@ custom Anaconda build.
|
||||||
|
|
||||||
bootc-image-builder's `bootc-installer` type is different: it takes **two**
|
bootc-image-builder's `bootc-installer` type is different: it takes **two**
|
||||||
containers -- an "installer environment" container that has Anaconda
|
containers -- an "installer environment" container that has Anaconda
|
||||||
installed in it, and (via `--bootc-installer-payload-ref`) the actual OS to
|
installed in it, and (via `--installer-payload-ref`) the actual OS to
|
||||||
install. The installer environment is just a container you build with a
|
install. The installer environment is just a container you build with a
|
||||||
`Containerfile`, so that's the hook: build your own installer image with
|
`Containerfile`, so that's the hook: build your own installer image with
|
||||||
your patched Anaconda `dnf install`ed instead of Fedora's, and point
|
your patched Anaconda in it instead of Fedora's, and point
|
||||||
bootc-image-builder at it.
|
bootc-image-builder at it.
|
||||||
|
|
||||||
`installer/Containerfile` in this repo is that installer environment. It's
|
`installer/Containerfile` in this repo is that installer environment. It's
|
||||||
unrelated to the quine-linux OS image built by the root `Containerfile` --
|
unrelated to the quine-linux OS image built by the root `Containerfile` --
|
||||||
Anaconda only exists in this side container, never in the installed system.
|
Anaconda only exists in this side container, never in the installed system.
|
||||||
|
|
||||||
## Which Anaconda to fork
|
## Which Anaconda to patch
|
||||||
|
|
||||||
Fedora's `anaconda` package resolves to one of two UIs depending on the
|
Fedora's `anaconda` package resolves to one of two UIs depending on the
|
||||||
image's release-identity package: `anaconda-gui` (old GTK hub-and-spoke) on
|
image's release-identity package: `anaconda-gui` (old GTK hub-and-spoke) on
|
||||||
server/basic/IoT identities, `anaconda-webui` (Cockpit + React/PatternFly)
|
server/basic/IoT identities, `anaconda-webui` (Cockpit + React/PatternFly)
|
||||||
everywhere else. All current upstream feature work happens on the WebUI, so
|
everywhere else. All current upstream feature work happens on the WebUI, so
|
||||||
that's the one to fork:
|
that's the one to patch: https://github.com/rhinstaller/anaconda-webui,
|
||||||
|
vendored in this repo at `installer/anaconda-webui/vendor/` (see
|
||||||
- https://github.com/rhinstaller/anaconda-webui, forked to
|
`installer/anaconda-webui/PROVENANCE.md` for the pinned release and how to
|
||||||
https://github.com/philip-peterson/quine-anaconda-webui
|
update it).
|
||||||
|
|
||||||
`installer/Containerfile` depends on `anaconda-webui` explicitly (not just
|
`installer/Containerfile` depends on `anaconda-webui` explicitly (not just
|
||||||
the bare `anaconda` metapackage) so the UI doesn't depend on incidentally
|
the bare `anaconda` metapackage) so the UI doesn't depend on incidentally
|
||||||
|
|
@ -38,61 +38,31 @@ matching a release-identity package.
|
||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
|
|
||||||
1. **Fork** `rhinstaller/anaconda-webui` to
|
1. **Edit** the web UI directly in `installer/anaconda-webui/vendor/`
|
||||||
`philip-peterson/quine-anaconda-webui` on GitHub, make your panel/control
|
(mostly `src/`, React/PatternFly). Your changes are this repo's diff
|
||||||
changes there.
|
against the vendored upstream release.
|
||||||
2. **Get it built as an RPM your installer container can `dnf install`.**
|
2. **Build the installer image and ISO:**
|
||||||
The fork's own Forgejo Actions workflow does this, as a self-hosted
|
|
||||||
stand-in for COPR: `.forgejo/workflows/rpm.yml` runs
|
|
||||||
`.forgejo/build-rpm.sh` in a fresh `fedora:rawhide` container (upstream's
|
|
||||||
`make srpm`, then `rpmbuild`) and uploads the RPM to the forge's package
|
|
||||||
registry. Setup, once:
|
|
||||||
- Host the fork on `forge.quinefoundation.com` (Forgejo Actions only runs
|
|
||||||
for repos on the forge), under the account the package should be
|
|
||||||
published as -- the `quine` org by default
|
|
||||||
(`quine/quine-anaconda-webui`).
|
|
||||||
- Add a repo secret `RPM_UPLOAD_TOKEN`: an access token for that account
|
|
||||||
with `write:package` scope, from a user
|
|
||||||
allowed to publish packages for that org.
|
|
||||||
- The result is served at
|
|
||||||
`https://forge.quinefoundation.com/api/packages/quine/rpm.repo`,
|
|
||||||
already wired in as the `RPM_REPO_URL` default in `Taskfile.yml`.
|
|
||||||
Publishing under a different owner means overriding `RPM_REPO_URL=` on
|
|
||||||
the `task` invocations below.
|
|
||||||
|
|
||||||
Versioning: the build fetches upstream's tags so `git describe` yields
|
|
||||||
e.g. `86.3.gabcdef0` (3 commits past upstream 86), and the Release is
|
|
||||||
`<run number>.quine`, so each build is a new version that sorts above
|
|
||||||
Fedora's own. The installer also gives this repo dnf `priority=10`, so
|
|
||||||
it wins even when Fedora ships a newer upstream release.
|
|
||||||
3. **Build the installer image and ISO:**
|
|
||||||
```sh
|
```sh
|
||||||
task build-installer-iso
|
task build-installer-iso
|
||||||
```
|
```
|
||||||
This builds the quine-linux OS image (if needed), builds
|
`installer/Containerfile` builds the vendored source with upstream's own
|
||||||
`installer/Containerfile` with that repo enabled, and produces
|
`make install` (the same step Fedora's RPM runs), installs stock Fedora
|
||||||
`output/bootc-installer/install.iso`. `RPM_REPO_URL=` (empty) builds
|
`anaconda-webui` for its dependencies, and copies the built files over
|
||||||
with stock Fedora `anaconda-webui` instead.
|
it. The ISO lands in `output/bootc-installer/install.iso`.
|
||||||
4. **Iterate:** push a change to your fork, wait for its RPM workflow,
|
3. **Iterate:** edit, rerun. The build stage is cached until something in
|
||||||
rerun `task build-installer-iso`. The Taskfile passes a checksum of the
|
`vendor/` changes.
|
||||||
repo's metadata into the build, so a newly published RPM invalidates
|
|
||||||
the cached `dnf install` layer and gets picked up.
|
|
||||||
|
|
||||||
## Local alternative (no forge)
|
The build stage runs on the build host's native architecture (the output is
|
||||||
|
platform-independent web assets and scripts), and needs network access: it
|
||||||
If you'd rather not publish the fork anywhere: build the RPM locally with
|
fetches upstream's npm package cache and cockpit's shared lib from GitHub.
|
||||||
`mock` targeting the same Fedora release/arch as the installer base,
|
|
||||||
`createrepo_c` the output into a directory, and either bind-mount that
|
|
||||||
directory into `installer/Containerfile`'s build context and add it as a
|
|
||||||
`file://` repo, or drop the built RPMs into a `dnf localinstall` step. This
|
|
||||||
avoids any external service but means keeping the mock chroot's Fedora
|
|
||||||
release in sync with the installer base by hand.
|
|
||||||
|
|
||||||
## Caveats
|
## Caveats
|
||||||
|
|
||||||
- `anaconda-webui`'s package/dependency structure moves with each Fedora
|
- The installer base tracks Fedora rawhide, while `vendor/` is pinned to one
|
||||||
release; if `dnf install anaconda-webui` starts failing, check what
|
upstream release. If rawhide's `anaconda-webui` moves well ahead, its
|
||||||
`anaconda.spec.in` in your fork currently requires.
|
dependencies (e.g. `anaconda-core`) may stop matching the vendored UI;
|
||||||
|
re-vendor a newer release (see PROVENANCE.md) or pin the base to a
|
||||||
|
stable Fedora release.
|
||||||
- The installer environment's Fedora release should stay close to whatever
|
- The installer environment's Fedora release should stay close to whatever
|
||||||
release the actual `quine-linux` image (and thus the ISO's kernel/dracut)
|
release the actual `quine-linux` image (and thus the ISO's kernel/dracut)
|
||||||
is built against, to avoid mismatches between the boot environment and the
|
is built against, to avoid mismatches between the boot environment and the
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,51 @@
|
||||||
# Installer-environment container for bootc-image-builder's `bootc-installer`
|
# Installer-environment container for bootc-image-builder's `bootc-installer`
|
||||||
# image type. This is NOT the quine-linux OS -- it's the throwaway Anaconda
|
# image type. This is NOT the quine-linux OS -- it's the throwaway Anaconda
|
||||||
# environment the ISO boots into, which then installs the real quine-linux
|
# environment the ISO boots into, which then installs the real quine-linux
|
||||||
# bootc image (passed separately via --bootc-installer-payload-ref). See
|
# bootc image (passed separately via --installer-payload-ref). See
|
||||||
# docs/patched-anaconda-installer.md for the full workflow this fits into.
|
# docs/patched-anaconda-installer.md for the full workflow this fits into.
|
||||||
|
|
||||||
|
# Builds the patched anaconda-webui from the vendored source in
|
||||||
|
# anaconda-webui/vendor/ (see anaconda-webui/PROVENANCE.md) with upstream's
|
||||||
|
# own `make install`, i.e. exactly the files its RPM would ship. Runs on the
|
||||||
|
# build host's native platform: the output is platform-independent (web
|
||||||
|
# assets and scripts), and emulated builds of this stage are both slow and
|
||||||
|
# unreliable (tar fails under emulation on Apple Silicon).
|
||||||
|
FROM --platform=$BUILDPLATFORM registry.fedoraproject.org/fedora:rawhide AS anaconda-webui
|
||||||
|
|
||||||
|
# Upstream version the vendored source corresponds to, and the gitlink its
|
||||||
|
# node_modules submodule pinned (the npm package cache, fetched at build).
|
||||||
|
# Update both when re-vendoring.
|
||||||
|
ARG ANACONDA_WEBUI_VERSION=86
|
||||||
|
ARG NODE_MODULES_REF=99a26de1065b0c39bad3d11197a37e1a8b0edb99
|
||||||
|
|
||||||
|
RUN dnf install -y git make npm diffutils gettext rpm-build && dnf clean all
|
||||||
|
|
||||||
|
COPY anaconda-webui/vendor/ /src/
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
# The upstream build assumes a git checkout: tools/node-modules reads the
|
||||||
|
# node_modules gitlink from the index, and the Makefile fetches cockpit's
|
||||||
|
# shared lib into the repo's object store. Recreate just enough of one.
|
||||||
|
RUN set -eux; \
|
||||||
|
git init -q; \
|
||||||
|
git add -A; \
|
||||||
|
git update-index --add --cacheinfo "160000,${NODE_MODULES_REF},node_modules"; \
|
||||||
|
git -c user.name=build -c user.email=build@localhost commit -q -m vendored
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
make package-lock.json; \
|
||||||
|
# node_modules ships only x86_64's esbuild binary; add the host's if needed.
|
||||||
|
v="$(node -p 'require("./node_modules/esbuild/package.json").version')"; \
|
||||||
|
case "$(uname -m)" in \
|
||||||
|
aarch64) npm install --no-save --ignore-scripts "@esbuild/linux-arm64@${v}" ;; \
|
||||||
|
esac; \
|
||||||
|
make install DESTDIR=/out VERSION="${ANACONDA_WEBUI_VERSION}"
|
||||||
|
|
||||||
FROM quay.io/fedora/fedora-bootc:rawhide
|
FROM quay.io/fedora/fedora-bootc:rawhide
|
||||||
|
|
||||||
# URL of a dnf .repo file for the repository holding a build of the patched
|
# Stock Fedora anaconda-webui pulls in the right dependencies; the patched
|
||||||
# anaconda-webui fork -- by default the Forgejo RPM registry its CI publishes
|
# build is laid over it below.
|
||||||
# to (see docs/patched-anaconda-installer.md). Override at build time with
|
|
||||||
# --build-arg RPM_REPO_URL=...; empty means stock Fedora anaconda-webui.
|
|
||||||
ARG RPM_REPO_URL
|
|
||||||
# Set by the Taskfile to the repo metadata's checksum; only here so a newly
|
|
||||||
# published RPM invalidates the build cache for the layer below.
|
|
||||||
ARG RPM_REPO_REV
|
|
||||||
|
|
||||||
# priority=10 (default 99) makes dnf take anaconda-webui from that repo even
|
|
||||||
# when Fedora's own build is newer. Forgejo signs the repo metadata but not
|
|
||||||
# the RPMs themselves, so verify the signed metadata (which carries each
|
|
||||||
# package's checksum) instead of per-package signatures.
|
|
||||||
RUN set -eux; \
|
RUN set -eux; \
|
||||||
echo "anaconda-webui repo rev: ${RPM_REPO_REV:-none}"; \
|
|
||||||
if [ -n "${RPM_REPO_URL}" ]; then \
|
|
||||||
curl -fsSL "${RPM_REPO_URL}" -o /etc/yum.repos.d/quine-anaconda.repo; \
|
|
||||||
sed -i 's/^gpgcheck=.*/gpgcheck=0/' /etc/yum.repos.d/quine-anaconda.repo; \
|
|
||||||
printf '\nrepo_gpgcheck=1\npriority=10\n' >> /etc/yum.repos.d/quine-anaconda.repo; \
|
|
||||||
fi; \
|
|
||||||
dnf install -y \
|
dnf install -y \
|
||||||
anaconda-webui \
|
anaconda-webui \
|
||||||
anaconda \
|
anaconda \
|
||||||
|
|
@ -42,6 +62,10 @@ RUN set -eux; \
|
||||||
prefixdevname; \
|
prefixdevname; \
|
||||||
dnf clean all
|
dnf clean all
|
||||||
|
|
||||||
|
# Replace the packaged web UI wholesale so no stale upstream assets linger.
|
||||||
|
RUN rm -rf /usr/share/cockpit/anaconda-webui
|
||||||
|
COPY --from=anaconda-webui /out/ /
|
||||||
|
|
||||||
# Fedora 43+ path fixup -- image-builder expects the EFI directory under
|
# Fedora 43+ path fixup -- image-builder expects the EFI directory under
|
||||||
# /boot/efi, not wherever the fedora-bootc base puts it.
|
# /boot/efi, not wherever the fedora-bootc base puts it.
|
||||||
RUN mkdir -p /boot/efi && cp -ra /usr/lib/efi/*/*/EFI /boot/efi
|
RUN mkdir -p /boot/efi && cp -ra /usr/lib/efi/*/*/EFI /boot/efi
|
||||||
|
|
|
||||||
44
installer/anaconda-webui/PROVENANCE.md
Normal file
44
installer/anaconda-webui/PROVENANCE.md
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Provenance: vendored `anaconda-webui` source
|
||||||
|
|
||||||
|
## Origin
|
||||||
|
|
||||||
|
- Upstream project: [`rhinstaller/anaconda-webui`](https://github.com/rhinstaller/anaconda-webui)
|
||||||
|
(Anaconda's Cockpit/React web UI, shipped in Fedora as the `anaconda-webui`
|
||||||
|
package).
|
||||||
|
- **Pinned release:** tag `86`, commit
|
||||||
|
`cd472cd02dec354bb9df463b8724b16bad27202f` ("Show "Installation completed"
|
||||||
|
instead of "Initializing..." after reboot").
|
||||||
|
- **Date vendored:** 2026-09-27.
|
||||||
|
- `vendor/` is `git archive 86` verbatim. Local changes are made directly in
|
||||||
|
`vendor/` and show up in this repo's history against that baseline.
|
||||||
|
|
||||||
|
## Submodules not vendored
|
||||||
|
|
||||||
|
Upstream has two git submodules, which `git archive` leaves out:
|
||||||
|
|
||||||
|
- `node_modules` -> [`rhinstaller/node-cache`](https://github.com/rhinstaller/node-cache),
|
||||||
|
gitlink `99a26de1065b0c39bad3d11197a37e1a8b0edb99`. This is the npm package
|
||||||
|
cache the build needs; upstream's `tools/node-modules` fetches it by that
|
||||||
|
sha at build time. The sha is recorded as `NODE_MODULES_REF` in
|
||||||
|
`installer/Containerfile`, which recreates the gitlink before building.
|
||||||
|
- `test/reference` (pixel-test screenshots): not needed to build.
|
||||||
|
|
||||||
|
The build also fetches cockpit's shared `pkg/lib` at the commit pinned in
|
||||||
|
`vendor/Makefile` (`COCKPIT_REPO_COMMIT`). So building needs network access
|
||||||
|
to GitHub; it is not offline-capable.
|
||||||
|
|
||||||
|
## How it's built and used
|
||||||
|
|
||||||
|
`installer/Containerfile`'s `anaconda-webui` stage runs upstream's own
|
||||||
|
`make install` (what the Fedora RPM's `%install` runs) and the final stage
|
||||||
|
copies the result over stock Fedora `anaconda-webui`, which is still
|
||||||
|
installed for its dependencies. Verified at vendoring time: the produced file
|
||||||
|
set matches Fedora's `anaconda-webui-86-1.fc46` file list exactly.
|
||||||
|
|
||||||
|
## Updating to a new upstream release
|
||||||
|
|
||||||
|
1. Replace `vendor/` with `git archive <new tag>` of upstream, then re-apply
|
||||||
|
local changes (diff `vendor/` against the old baseline first to see them).
|
||||||
|
2. Update `ANACONDA_WEBUI_VERSION` and `NODE_MODULES_REF` in
|
||||||
|
`installer/Containerfile` (`git ls-tree <new tag> node_modules`).
|
||||||
|
3. Update the tag, commit and date above.
|
||||||
1
installer/anaconda-webui/vendor/.cockpit-ci/container
vendored
Normal file
1
installer/anaconda-webui/vendor/.cockpit-ci/container
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ghcr.io/cockpit-project/tasks:2026-09-19
|
||||||
1
installer/anaconda-webui/vendor/.cockpit-ci/run
vendored
Symbolic link
1
installer/anaconda-webui/vendor/.cockpit-ci/run
vendored
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../test/run
|
||||||
2
installer/anaconda-webui/vendor/.eslintignore
vendored
Normal file
2
installer/anaconda-webui/vendor/.eslintignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
node_modules/*
|
||||||
|
pkg/lib/*
|
||||||
64
installer/anaconda-webui/vendor/.eslintrc.json
vendored
Normal file
64
installer/anaconda-webui/vendor/.eslintrc.json
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2022": true
|
||||||
|
},
|
||||||
|
"extends": ["eslint:recommended", "standard", "standard-jsx", "standard-react"],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2022
|
||||||
|
},
|
||||||
|
"plugins": ["react", "react-hooks", "simple-import-sort", "sort-destructure-keys"],
|
||||||
|
"rules": {
|
||||||
|
"comma-dangle": "off",
|
||||||
|
"indent": ["error", 4,
|
||||||
|
{
|
||||||
|
"ObjectExpression": "first",
|
||||||
|
"CallExpression": {"arguments": "first"},
|
||||||
|
"MemberExpression": 2,
|
||||||
|
"ignoredNodes": [ "JSXAttribute" ]
|
||||||
|
}],
|
||||||
|
"jsx-quotes": ["error", "prefer-double"],
|
||||||
|
"lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }],
|
||||||
|
"newline-per-chained-call": ["error", { "ignoreChainWithDepth": 2 }],
|
||||||
|
"no-console": "error",
|
||||||
|
"no-unused-vars": ["error", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }],
|
||||||
|
"no-var": "error",
|
||||||
|
"prefer-promise-reject-errors": ["error", { "allowEmptyReject": true }],
|
||||||
|
"quotes": ["error", "double" ],
|
||||||
|
"semi": ["error", "always", { "omitLastInOneLineBlock": true }],
|
||||||
|
"sort-destructure-keys/sort-destructure-keys": 2,
|
||||||
|
"sort-keys": ["error", "asc", {"caseSensitive": true, "natural": true, "minKeys": 2}],
|
||||||
|
"simple-import-sort/imports": ["error", { "groups": [
|
||||||
|
["^cockpit$"],
|
||||||
|
["^"],
|
||||||
|
["^react", "^@patternfly"],
|
||||||
|
["/apis"],
|
||||||
|
["/actions", "/reducer"],
|
||||||
|
["/helpers"],
|
||||||
|
["/components.*js[x]$"],
|
||||||
|
["/contexts.*js[x]$"],
|
||||||
|
["/hooks.*js[x]$"],
|
||||||
|
["^cockpit-", "^os-release", "^dialogs"],
|
||||||
|
["^\\."],
|
||||||
|
["\\.scss"]
|
||||||
|
] }],
|
||||||
|
"import/extensions": ["error", "ignorePackages", {
|
||||||
|
"js": "always",
|
||||||
|
"jsx": "always"
|
||||||
|
}],
|
||||||
|
"import/no-useless-path-segments": "error",
|
||||||
|
|
||||||
|
"react/hook-use-state": ["error", { "allowDestructuredState": false }],
|
||||||
|
"react/jsx-closing-bracket-location": "off",
|
||||||
|
"react/jsx-indent": ["error", 4],
|
||||||
|
"react/prop-types": "off",
|
||||||
|
|
||||||
|
"react-hooks/exhaustive-deps": "error",
|
||||||
|
"react-hooks/rules-of-hooks": "error"
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"require": false,
|
||||||
|
"module": false
|
||||||
|
}
|
||||||
|
}
|
||||||
8
installer/anaconda-webui/vendor/.flake8
vendored
Normal file
8
installer/anaconda-webui/vendor/.flake8
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[flake8]
|
||||||
|
# we want 118, but fix tests first
|
||||||
|
#max-line-length = 118
|
||||||
|
max-line-length = 149
|
||||||
|
# E402: module level import not at top of file; needs updating the tests to current cockpit standard
|
||||||
|
# W504: line break after binary operator
|
||||||
|
# E741: ambiguous variable name 'l'
|
||||||
|
ignore = E402, W504, E741
|
||||||
2
installer/anaconda-webui/vendor/.github/codeql-config.yml
vendored
Normal file
2
installer/anaconda-webui/vendor/.github/codeql-config.yml
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
paths:
|
||||||
|
- src
|
||||||
38
installer/anaconda-webui/vendor/.github/dependabot.yml
vendored
Normal file
38
installer/anaconda-webui/vendor/.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
# Set update schedule for npm packages.
|
||||||
|
# FIXME: Update our npm development dependencies monthly.
|
||||||
|
# See: https://github.com/dependabot/dependabot-core/issues/2390
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
# run these when most of our developers don't work
|
||||||
|
day: "sunday"
|
||||||
|
timezone: "Europe/Berlin"
|
||||||
|
open-pull-requests-limit: 3
|
||||||
|
commit-message:
|
||||||
|
prefix: "[no-test]"
|
||||||
|
labels:
|
||||||
|
- "node_modules"
|
||||||
|
groups:
|
||||||
|
eslint:
|
||||||
|
patterns:
|
||||||
|
- "eslint*"
|
||||||
|
esbuild:
|
||||||
|
patterns:
|
||||||
|
- "esbuild*"
|
||||||
|
stylelint:
|
||||||
|
patterns:
|
||||||
|
- "stylelint*"
|
||||||
|
patternfly:
|
||||||
|
patterns:
|
||||||
|
- "@patternfly*"
|
||||||
|
ignore:
|
||||||
|
- dependency-name: 'react'
|
||||||
|
# https://github.com/patternfly/patternfly-react/issues/11345
|
||||||
|
versions: '>= 19'
|
||||||
|
|
||||||
|
# The plugins we use didn't work with anything after 8 at some point
|
||||||
|
- dependency-name: "eslint"
|
||||||
|
update-types: ["version-update:semver-major"]
|
||||||
31
installer/anaconda-webui/vendor/.github/workflows/cockpit-lib-update.yml
vendored
Normal file
31
installer/anaconda-webui/vendor/.github/workflows/cockpit-lib-update.yml
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
name: cockpit-lib-update
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 2 * * 4'
|
||||||
|
# can be run manually on https://github.com/rhinstaller/anaconda-webui/actions
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
cockpit-lib-update:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
statuses: write
|
||||||
|
steps:
|
||||||
|
- name: Set up dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y make
|
||||||
|
|
||||||
|
- name: Set up configuration and secrets
|
||||||
|
run: |
|
||||||
|
printf '[user]\n\tname = Cockpit Project\n\temail=cockpituous@gmail.com\n' > ~/.gitconfig
|
||||||
|
echo '${{ secrets.GITHUB_TOKEN }}' > ~/.config/github-token
|
||||||
|
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Run cockpit-lib-update
|
||||||
|
run: |
|
||||||
|
make bots
|
||||||
|
bots/cockpit-lib-update
|
||||||
33
installer/anaconda-webui/vendor/.github/workflows/codeql.yml
vendored
Normal file
33
installer/anaconda-webui/vendor/.github/workflows/codeql.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
name: CodeQL
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language:
|
||||||
|
- javascript
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
queries: +security-and-quality
|
||||||
|
config-file: ./.github/codeql-config.yml
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
||||||
|
with:
|
||||||
|
category: "/language:${{ matrix.language }}"
|
||||||
96
installer/anaconda-webui/vendor/.github/workflows/dependabot.yml
vendored
Normal file
96
installer/anaconda-webui/vendor/.github/workflows/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
name: update node_modules
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, reopened, synchronize, labeled]
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITHUB_BASE: rhinstaller/anaconda-webui
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
dependabot:
|
||||||
|
environment: npm-update
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
timeout-minutes: 5
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ contains(github.event.pull_request.labels.*.name, 'node_modules') }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.ref }}
|
||||||
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Clear node_modules label
|
||||||
|
uses: actions/github-script@v9
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
try {
|
||||||
|
await github.rest.issues.removeLabel({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
name: 'node_modules'
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (e.name == 'HttpError' && e.status == 404) {
|
||||||
|
/* expected: 404 if label is unset */
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Rebase pull request and drop node_modules changes
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
git config --global user.name "GitHub Workflow"
|
||||||
|
git config --global user.email "cockpituous@cockpit-project.org"
|
||||||
|
git fetch origin main:main
|
||||||
|
if ! git rebase main; then
|
||||||
|
git reset node_modules
|
||||||
|
GIT_EDITOR=true git rebase --continue
|
||||||
|
git show --stat
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Update node_modules for package.json changes
|
||||||
|
run: |
|
||||||
|
make tools/node-modules
|
||||||
|
git config --global user.name "GitHub Workflow"
|
||||||
|
git config --global user.email "cockpituous@cockpit-project.org"
|
||||||
|
eval $(ssh-agent)
|
||||||
|
ssh-add - <<< '${{ secrets.NODE_CACHE_DEPLOY_KEY }}'
|
||||||
|
./tools/node-modules install
|
||||||
|
./tools/node-modules push
|
||||||
|
git add node_modules
|
||||||
|
ssh-add -D
|
||||||
|
ssh-agent -k
|
||||||
|
|
||||||
|
- name: Clear [no-test] prefix from PR title
|
||||||
|
if: ${{ contains(github.event.pull_request.title, '[no-test]') }}
|
||||||
|
uses: actions/github-script@v9
|
||||||
|
env:
|
||||||
|
TITLE: '${{ github.event.pull_request.title }}'
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const title = process.env['TITLE'].replace(/\[no-test\]\W+ /, '')
|
||||||
|
await github.rest.pulls.update({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
pull_number: context.issue.number,
|
||||||
|
title,
|
||||||
|
});
|
||||||
|
|
||||||
|
- name: Force push node_modules update
|
||||||
|
run: |
|
||||||
|
# Dependabot prefixes the commit with [no-test] which we don't want to keep in the commit
|
||||||
|
title=$(git show --pretty="%s" -s | sed -E "s/\[no-test\]\W+ /infra/")
|
||||||
|
body=$(git show -s --pretty="%b")
|
||||||
|
git commit --amend -m "${title}" -m "${body}" --no-edit node_modules
|
||||||
|
eval $(ssh-agent)
|
||||||
|
ssh-add - <<< '${{ secrets.SELF_DEPLOY_KEY }}'
|
||||||
|
git push --force 'git@github.com:${{ github.repository }}' '${{ github.head_ref }}'
|
||||||
|
ssh-add -D
|
||||||
|
ssh-agent -k
|
||||||
55
installer/anaconda-webui/vendor/.github/workflows/docs-check.yml
vendored
Normal file
55
installer/anaconda-webui/vendor/.github/workflows/docs-check.yml
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
name: Check Documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'src/components/storage/scenarios/**'
|
||||||
|
- 'src/components/*/**.jsx'
|
||||||
|
- 'docs/generate-docs.js'
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- 'src/components/storage/scenarios/**'
|
||||||
|
- 'src/components/*/**.jsx'
|
||||||
|
- 'docs/generate-docs.js'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-docs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
|
||||||
|
- name: Generate documentation
|
||||||
|
run: make docs
|
||||||
|
|
||||||
|
- name: Check for documentation changes
|
||||||
|
id: check_changes
|
||||||
|
run: |
|
||||||
|
if git diff --quiet docs/installation-steps.rst; then
|
||||||
|
echo "✅ Documentation is up to date"
|
||||||
|
echo "changes=false" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "❌ Documentation needs to be updated"
|
||||||
|
echo "changes=true" >> $GITHUB_OUTPUT
|
||||||
|
echo ""
|
||||||
|
echo "=== Documentation changes needed ==="
|
||||||
|
git diff docs/installation-steps.rst
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Fail if documentation is outdated
|
||||||
|
if: steps.check_changes.outputs.changes == 'true'
|
||||||
|
run: |
|
||||||
|
echo "::error::Documentation is outdated. Please run 'make docs' and commit the changes."
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
- name: Success message
|
||||||
|
if: steps.check_changes.outputs.changes == 'false'
|
||||||
|
run: |
|
||||||
|
echo "✅ All documentation is current and up to date!"
|
||||||
86
installer/anaconda-webui/vendor/.github/workflows/release.yml
vendored
Normal file
86
installer/anaconda-webui/vendor/.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
name: release
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
# this is a glob, not a regexp
|
||||||
|
- '[0-9]*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITHUB_BASE: rhinstaller/anaconda-webui
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
source:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/cockpit-project/tasks:latest
|
||||||
|
options: --user root
|
||||||
|
permissions:
|
||||||
|
# create GitHub release
|
||||||
|
contents: write
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install gh
|
||||||
|
run: dnf install -y gh
|
||||||
|
|
||||||
|
# https://github.blog/2022-04-12-git-security-vulnerability-announced/
|
||||||
|
- name: Pacify git's permission check
|
||||||
|
run: git config --global --add safe.directory /__w/anaconda-webui/anaconda-webui
|
||||||
|
|
||||||
|
- name: Workaround for https://github.com/actions/checkout/pull/697
|
||||||
|
run: |
|
||||||
|
TAG_VERSION="$(git describe --tags)"
|
||||||
|
git fetch --force origin ${TAG_VERSION}:refs/tags/${TAG_VERSION}
|
||||||
|
|
||||||
|
- name: Build release
|
||||||
|
run: make dist
|
||||||
|
|
||||||
|
- name: Create a release
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
|
||||||
|
TAG_VERSION="$(git describe --tags)"
|
||||||
|
git tag -l --format='%(contents:body)' ${TAG_VERSION} > release-note.txt
|
||||||
|
|
||||||
|
REPO_NAME="${GITHUB_REPOSITORY#*/}"
|
||||||
|
gh release create --title ${TAG_VERSION} --notes-file release-note.txt \
|
||||||
|
${TAG_VERSION} \
|
||||||
|
${REPO_NAME}-${TAG_VERSION}.tar.xz
|
||||||
|
|
||||||
|
node-cache:
|
||||||
|
# doesn't depend on it, but let's make sure the build passes before we do this
|
||||||
|
needs: [source]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: node-cache
|
||||||
|
# done via deploy key, token needs no write permissions at all
|
||||||
|
permissions: {}
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up git
|
||||||
|
run: |
|
||||||
|
git config user.name "GitHub Workflow"
|
||||||
|
git config user.email "cockpituous@cockpit-project.org"
|
||||||
|
|
||||||
|
- name: Tag node-cache
|
||||||
|
run: |
|
||||||
|
set -eux
|
||||||
|
# this is a shared repo, prefix with project name
|
||||||
|
TAG="${GITHUB_REPOSITORY#*/}-$(basename $GITHUB_REF)"
|
||||||
|
make tools/node-modules
|
||||||
|
tools/node-modules checkout
|
||||||
|
cd node_modules
|
||||||
|
git tag "$TAG"
|
||||||
|
git remote add cache "ssh://git@github.com/${GITHUB_REPOSITORY%/*}/node-cache"
|
||||||
|
eval $(ssh-agent)
|
||||||
|
ssh-add - <<< '${{ secrets.DEPLOY_KEY }}'
|
||||||
|
# make this idempotent: delete an existing tag
|
||||||
|
git push cache :"$TAG" || true
|
||||||
|
git push cache tag "$TAG"
|
||||||
|
ssh-add -D
|
||||||
68
installer/anaconda-webui/vendor/.github/workflows/reposchutz.yml
vendored
Normal file
68
installer/anaconda-webui/vendor/.github/workflows/reposchutz.yml
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
name: repository
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened, reopened, synchronize, labeled, unlabeled]
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITHUB_BASE: rhinstaller/anaconda-webui
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Protection checks
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
timeout-minutes: 5
|
||||||
|
env:
|
||||||
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone target branch
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Fetch PR commits
|
||||||
|
run: git fetch origin "${BASE_SHA}" "${HEAD_SHA}"
|
||||||
|
|
||||||
|
- name: Clear .github-changes label
|
||||||
|
if: ${{ !endsWith(github.event.action, 'labeled') }}
|
||||||
|
uses: actions/github-script@v9
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
try {
|
||||||
|
await github.rest.issues.removeLabel({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
name: '.github-changes'
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (e.name == 'HttpError' && e.status == 404) {
|
||||||
|
/* expected: 404 if label is unset */
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Check for .github changes
|
||||||
|
# We want to run this check any time the .github-changes label is not
|
||||||
|
# set, which needs to include the case where we just unset it above.
|
||||||
|
if: ${{ !endsWith(github.event.action, 'labeled') ||
|
||||||
|
!contains(github.event.pull_request.labels.*.name, '.github-changes') }}
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
git log --full-history --exit-code --patch "${HEAD_SHA}" --not "${BASE_SHA}" -- .github >&2
|
||||||
|
|
||||||
|
- name: Check for node_modules availability and package.json consistency
|
||||||
|
run: |
|
||||||
|
# Make tools/node-modules available
|
||||||
|
make tools/node-modules
|
||||||
|
# for each commit in the PR which modifies package.json or node_modules...
|
||||||
|
for commit in $(git log --reverse --full-history --format=%H \
|
||||||
|
"${HEAD_SHA}" --not "${BASE_SHA}" -- package.json node_modules); do
|
||||||
|
# ... check that package.json and node_modules/.package.json are in sync
|
||||||
|
tools/node-modules verify "${commit}"
|
||||||
|
done
|
||||||
37
installer/anaconda-webui/vendor/.github/workflows/tasks-container-update.yml
vendored
Normal file
37
installer/anaconda-webui/vendor/.github/workflows/tasks-container-update.yml
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
name: tasks-container-update
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 2 * * 1'
|
||||||
|
# can be run manually on https://github.com/rhinstaller/anaconda-webui/actions
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
tasks-container-update:
|
||||||
|
environment: self
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
statuses: write
|
||||||
|
container:
|
||||||
|
image: ghcr.io/cockpit-project/tasks
|
||||||
|
options: --user root
|
||||||
|
steps:
|
||||||
|
- name: Set up configuration and secrets
|
||||||
|
run: |
|
||||||
|
printf '[user]\n\tname = Cockpit Project\n\temail=cockpituous@gmail.com\n' > ~/.gitconfig
|
||||||
|
mkdir -p ~/.config
|
||||||
|
echo '${{ secrets.GITHUB_TOKEN }}' > ~/.config/github-token
|
||||||
|
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ssh-key: ${{ secrets.DEPLOY_KEY }}
|
||||||
|
|
||||||
|
# https://github.blog/2022-04-12-git-security-vulnerability-announced/
|
||||||
|
- name: Pacify git's permission check
|
||||||
|
run: git config --global --add safe.directory /__w/anaconda-webui/anaconda-webui
|
||||||
|
|
||||||
|
- name: Run tasks-container-update
|
||||||
|
run: |
|
||||||
|
make bots
|
||||||
|
bots/tasks-container-update
|
||||||
56
installer/anaconda-webui/vendor/.github/workflows/test-compose.yml
vendored
Normal file
56
installer/anaconda-webui/vendor/.github/workflows/test-compose.yml
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
name: test-compose
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 1 * * *'
|
||||||
|
# can be run manually on https://github.com/rhinstaller/anaconda-webui/actions
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
compose_id:
|
||||||
|
description: 'Compose ID to test (leave empty to test against current event)'
|
||||||
|
staging:
|
||||||
|
description: 'Whether to test the staging wiki'
|
||||||
|
default: 'false'
|
||||||
|
jobs:
|
||||||
|
test-compose:
|
||||||
|
environment: 'fedora-wiki'
|
||||||
|
permissions:
|
||||||
|
statuses: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get compose ID
|
||||||
|
id: get-compose
|
||||||
|
run: |
|
||||||
|
if [ -n "${{ github.event.inputs.compose_id }}" ]; then
|
||||||
|
echo "compose_id=${{ github.event.inputs.compose_id }}" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y python3-pip
|
||||||
|
sudo pip3 install wikitcms
|
||||||
|
COMPOSE_ID=$(python3 test/fedora-wiki/get_current_compose.py)
|
||||||
|
echo "compose_id=$COMPOSE_ID" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Trigger and verify compose tests
|
||||||
|
timeout-minutes: 90
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
mkdir -p ~/.config/cockpit-dev
|
||||||
|
echo "${{ github.token }}" >> ~/.config/cockpit-dev/github-token
|
||||||
|
|
||||||
|
export TEST_COMPOSE=${{ steps.get-compose.outputs.compose_id }}
|
||||||
|
if [ "${{ github.event.inputs.staging }}" = "true" ]; then
|
||||||
|
make test-compose-staging
|
||||||
|
else
|
||||||
|
make test-compose
|
||||||
|
fi
|
||||||
|
|
||||||
|
bots/tests-status - --wait 2>&1 | tee tests-status.log
|
||||||
|
|
||||||
|
if grep -q '^ not ok ' tests-status.log; then
|
||||||
|
echo "Failed test cases:"
|
||||||
|
grep '^ not ok ' tests-status.log
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
55
installer/anaconda-webui/vendor/.github/workflows/validate-wiki-testmap.yml
vendored
Normal file
55
installer/anaconda-webui/vendor/.github/workflows/validate-wiki-testmap.yml
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
name: validate-wiki-testmap
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- test/*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions: {}
|
||||||
|
container:
|
||||||
|
image: ghcr.io/cockpit-project/tasks
|
||||||
|
options: --user root
|
||||||
|
timeout-minutes: 10
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# https://github.blog/2022-04-12-git-security-vulnerability-announced/
|
||||||
|
- name: Pacify git's permission check
|
||||||
|
run: git config --global --add safe.directory /__w/anaconda-webui/anaconda-webui
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get list of valid test names
|
||||||
|
id: list_tests
|
||||||
|
run: |
|
||||||
|
# Bring ./test/common
|
||||||
|
make pkg/lib/cockpit-po-plugin.js
|
||||||
|
./test/common/run-tests -l > valid_tests.txt
|
||||||
|
echo "tests<<EOF" >> $GITHUB_OUTPUT
|
||||||
|
cat valid_tests.txt >> $GITHUB_OUTPUT
|
||||||
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Validate JSON entries
|
||||||
|
run: |
|
||||||
|
valid_tests=$(echo "${{ steps.list_tests.outputs.tests }}" | tr '\n' ' ')
|
||||||
|
|
||||||
|
jq -c 'to_entries[] | .value.tests[]' test/fedora-wiki/wiki-testmap.json | while read -r item; do
|
||||||
|
name=$(echo "$item" | jq -r '.testname')
|
||||||
|
url="https://fedoraproject.org/wiki/$(echo "$item" | jq -r '."fedora-wiki-testcase"')"
|
||||||
|
|
||||||
|
echo "Validating test case: $name - $url"
|
||||||
|
|
||||||
|
# Check if the URL exists
|
||||||
|
if ! curl --head --silent --fail "$url" > /dev/null; then
|
||||||
|
echo "❌ URL does not exist: $url"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if the name exists in the list of tests
|
||||||
|
if ! grep -q -w "$name" <<< "$valid_tests"; then
|
||||||
|
echo "❌ Test name '$name' not found in run-tests output"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
46
installer/anaconda-webui/vendor/.github/workflows/weblate-sync-po.yml
vendored
Normal file
46
installer/anaconda-webui/vendor/.github/workflows/weblate-sync-po.yml
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
name: weblate-sync-po
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Run this on Tuesday evening (UTC), so that it's ready for release on
|
||||||
|
# Wednesday, with some spare time
|
||||||
|
- cron: '0 18 * * 2'
|
||||||
|
# can be run manually on https://github.com/rhinstaller/anaconda-webui/actions
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
po-refresh:
|
||||||
|
environment: self
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
statuses: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Set up dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y --no-install-recommends gettext
|
||||||
|
|
||||||
|
- name: Clone source repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ssh-key: ${{ secrets.DEPLOY_KEY }}
|
||||||
|
path: src
|
||||||
|
|
||||||
|
- name: Clone weblate repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: ${{ github.repository }}-l10n
|
||||||
|
path: weblate
|
||||||
|
|
||||||
|
- name: Copy .po files from weblate repository
|
||||||
|
run: cp weblate/*.po src/po/
|
||||||
|
|
||||||
|
- name: Run po-refresh bot
|
||||||
|
run: |
|
||||||
|
cd src
|
||||||
|
make bots
|
||||||
|
git config --global user.name "GitHub Workflow"
|
||||||
|
git config --global user.email "cockpituous@cockpit-project.org"
|
||||||
|
mkdir -p ~/.config/cockpit-dev
|
||||||
|
echo ${{ github.token }} >> ~/.config/cockpit-dev/github-token
|
||||||
|
COVERAGE_THRESHOLD=0 bots/po-refresh
|
||||||
42
installer/anaconda-webui/vendor/.github/workflows/weblate-sync-pot.yml
vendored
Normal file
42
installer/anaconda-webui/vendor/.github/workflows/weblate-sync-pot.yml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
name: weblate-sync-pot
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Run this every morning
|
||||||
|
- cron: '45 2 * * *'
|
||||||
|
# can be run manually on https://github.com/rhinstaller/anaconda-webui/actions
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pot-upload:
|
||||||
|
environment: anaconda-webui-l10n
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Set up dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y --no-install-recommends npm make gettext appstream
|
||||||
|
|
||||||
|
- name: Clone source repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: src
|
||||||
|
|
||||||
|
- name: Generate .pot file
|
||||||
|
run: make -C src po/anaconda-webui.pot
|
||||||
|
|
||||||
|
- name: Clone weblate repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: weblate
|
||||||
|
repository: ${{ github.repository }}-l10n
|
||||||
|
ssh-key: ${{ secrets.DEPLOY_KEY }}
|
||||||
|
|
||||||
|
- name: Commit .pot to weblate repo
|
||||||
|
run: |
|
||||||
|
cp src/po/anaconda-webui.pot weblate/anaconda-webui.pot
|
||||||
|
git config --global user.name "GitHub Workflow"
|
||||||
|
git config --global user.email "cockpituous@cockpit-project.org"
|
||||||
|
git -C weblate commit -m "Update source file" -- anaconda-webui.pot
|
||||||
|
git -C weblate push
|
||||||
34
installer/anaconda-webui/vendor/.gitignore
vendored
Normal file
34
installer/anaconda-webui/vendor/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Please keep this file sorted (LC_COLLATE=C.UTF-8),
|
||||||
|
# grouped into the 3 categories below:
|
||||||
|
# - general patterns (match in all directories)
|
||||||
|
# - patterns to match files at the toplevel
|
||||||
|
# - patterns to match files in subdirs
|
||||||
|
|
||||||
|
# general patterns
|
||||||
|
*.pyc
|
||||||
|
*.rpm
|
||||||
|
|
||||||
|
# toplevel (/...)
|
||||||
|
/*updates*.img
|
||||||
|
/Test*.html
|
||||||
|
/Test*.json
|
||||||
|
/Test*.log*
|
||||||
|
/Test*.png
|
||||||
|
/Test*.xml
|
||||||
|
/VERSION.txt
|
||||||
|
/anaconda-webui.spec
|
||||||
|
/bots
|
||||||
|
/anaconda-webui-*.tar.xz
|
||||||
|
/dist
|
||||||
|
/package-lock.json
|
||||||
|
/pkg
|
||||||
|
/tmp/
|
||||||
|
/tools
|
||||||
|
|
||||||
|
# subdirs (/subdir/...)
|
||||||
|
/po/*.pot
|
||||||
|
/po/LINGUAS
|
||||||
|
/test/common/
|
||||||
|
/test/images
|
||||||
|
/test/ks*.cfg
|
||||||
|
/test/report.json
|
||||||
7
installer/anaconda-webui/vendor/.gitmodules
vendored
Normal file
7
installer/anaconda-webui/vendor/.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
[submodule "test/reference"]
|
||||||
|
path = test/reference
|
||||||
|
url = https://github.com/rhinstaller/pixel-test-reference
|
||||||
|
branch = empty
|
||||||
|
[submodule "node_modules"]
|
||||||
|
path = node_modules
|
||||||
|
url = https://github.com/rhinstaller/node-cache
|
||||||
33
installer/anaconda-webui/vendor/.stylelintrc.json
vendored
Normal file
33
installer/anaconda-webui/vendor/.stylelintrc.json
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"extends": "stylelint-config-standard-scss",
|
||||||
|
"rules": {
|
||||||
|
"at-rule-empty-line-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,
|
||||||
|
"keyframes-name-pattern": null,
|
||||||
|
"media-feature-range-notation": "prefix",
|
||||||
|
"no-descending-specificity": null,
|
||||||
|
"no-duplicate-selectors": null,
|
||||||
|
"scss/at-extend-no-missing-placeholder": null,
|
||||||
|
"scss/at-mixin-pattern": null,
|
||||||
|
"scss/comment-no-empty": null,
|
||||||
|
"scss/dollar-variable-pattern": null,
|
||||||
|
"scss/double-slash-comment-whitespace-inside": null,
|
||||||
|
"scss/load-partial-extension": null,
|
||||||
|
"scss/no-global-function-names": null,
|
||||||
|
"scss/operator-no-unspaced": null,
|
||||||
|
"selector-class-pattern": null,
|
||||||
|
"selector-id-pattern": null
|
||||||
|
}
|
||||||
|
}
|
||||||
104
installer/anaconda-webui/vendor/HACKING.rst
vendored
Normal file
104
installer/anaconda-webui/vendor/HACKING.rst
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
Hacking on Anaconda Web UI
|
||||||
|
==========================
|
||||||
|
|
||||||
|
The commands here assume you're in the top level of the Anaconda Web UI git
|
||||||
|
repository checkout.
|
||||||
|
|
||||||
|
Setting up development container
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
The anaconda team uses a containerized development environment using toolbx.
|
||||||
|
If you can install `toolbx <https://containertoolbx.org/>`_ or
|
||||||
|
`distrobox <https://distrobox.privatedns.org/>`_ on your system, it is highly
|
||||||
|
recommended to do that:
|
||||||
|
|
||||||
|
- It is known to work and gives you reproducible results.
|
||||||
|
- It avoids having to install development packages on your main machine.
|
||||||
|
|
||||||
|
If you are not interested in dealing with containers, just skip this part and continue on the next one::
|
||||||
|
|
||||||
|
sudo dnf install toolbox
|
||||||
|
|
||||||
|
To create and enter a development toolbx for Anaconda Web UI run this command::
|
||||||
|
|
||||||
|
toolbox create --image ghcr.io/cockpit-project/tasks -c anaconda-webui
|
||||||
|
toolbox enter anaconda-webui
|
||||||
|
|
||||||
|
|
||||||
|
Working on Anaconda Web UI
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
To prepare Anaconda Web UI sources, you need to run this command::
|
||||||
|
|
||||||
|
make dist
|
||||||
|
|
||||||
|
The easiest way to test changes you make is to set up a test VM.
|
||||||
|
You can find intructions for preparing a test VM at ``test/README.rst``.
|
||||||
|
|
||||||
|
Running eslint
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Anaconda Web UI uses `ESLint <https://eslint.org/>`_ to automatically check
|
||||||
|
JavaScript code style in `.js` and `.jsx` files.
|
||||||
|
|
||||||
|
ESLint is executed as part of `test/common/static-code`, aka. `make codecheck`.
|
||||||
|
|
||||||
|
For developer convenience, the ESLint can be started explicitly by::
|
||||||
|
|
||||||
|
npm run eslint
|
||||||
|
|
||||||
|
Violations of some rules can be fixed automatically by::
|
||||||
|
|
||||||
|
npm run eslint:fix
|
||||||
|
|
||||||
|
Rules configuration can be found in the `.eslintrc.json` file.
|
||||||
|
|
||||||
|
Running stylelint
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Cockpit uses `Stylelint <https://stylelint.io/>`_ to automatically check CSS code
|
||||||
|
style in `.css` and `scss` files.
|
||||||
|
|
||||||
|
Styleint is executed as part of `test/common/static-code`, aka. `make codecheck`.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Development with rsync mode
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
When developing the Web UI, after every change to your sources we need to re-build
|
||||||
|
and the contents of dist directory need to be copied to the SSH target's
|
||||||
|
/usr/share/cockpit/anaconda-webui directory.
|
||||||
|
|
||||||
|
For automating this, you need to set up the SSH `test-updates` alias,
|
||||||
|
as described in `<test/README.rst>`_.
|
||||||
|
|
||||||
|
Then you can run::
|
||||||
|
|
||||||
|
make rsync
|
||||||
|
|
||||||
|
Updating `node_modules`
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Before making any changes, ensure the following environment variable is set::
|
||||||
|
|
||||||
|
GITHUB_BASE="rhinstaller/anaconda-webui"
|
||||||
|
|
||||||
|
If you need to update package.json — for example, to add or remove a dependency — run::
|
||||||
|
|
||||||
|
tools/node-modules install
|
||||||
|
|
||||||
|
This command regenerates the node_modules cache based on your updated package.json using npm install.
|
||||||
|
|
||||||
|
Note: Your locally rebuilt changes to `node_modules` won't be used by others. A new
|
||||||
|
version will be created by a GitHub workflow when you open your pull request.
|
||||||
|
|
||||||
|
For more details, see the `Cockpit HACKING.md guide <https://github.com/cockpit-project/cockpit/blob/main/HACKING.md#updating-node_modules>`_.
|
||||||
502
installer/anaconda-webui/vendor/LICENSE
vendored
Normal file
502
installer/anaconda-webui/vendor/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,502 @@
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Suite 500, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts
|
||||||
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
|
the version number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some
|
||||||
|
specially designated software packages--typically libraries--of the
|
||||||
|
Free Software Foundation and other authors who decide to use it. You
|
||||||
|
can use it too, but we suggest you first think carefully about whether
|
||||||
|
this license or the ordinary General Public License is the better
|
||||||
|
strategy to use in any particular case, based on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use,
|
||||||
|
not price. Our General Public Licenses are designed to make sure that
|
||||||
|
you have the freedom to distribute copies of free software (and charge
|
||||||
|
for this service if you wish); that you receive source code or can get
|
||||||
|
it if you want it; that you can change the software and use pieces of
|
||||||
|
it in new free programs; and that you are informed that you can do
|
||||||
|
these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
distributors to deny you these rights or to ask you to surrender these
|
||||||
|
rights. These restrictions translate to certain responsibilities for
|
||||||
|
you if you distribute copies of the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link other code with the library, you must provide
|
||||||
|
complete object files to the recipients, so that they can relink them
|
||||||
|
with the library after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the
|
||||||
|
library, and (2) we offer you this license, which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that
|
||||||
|
there is no warranty for the free library. Also, if the library is
|
||||||
|
modified by someone else and passed on, the recipients should know
|
||||||
|
that what they have is not the original version, so that the original
|
||||||
|
author's reputation will not be affected by problems that might be
|
||||||
|
introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of
|
||||||
|
any free program. We wish to make sure that a company cannot
|
||||||
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
restrictive license from a patent holder. Therefore, we insist that
|
||||||
|
any patent license obtained for a version of the library must be
|
||||||
|
consistent with the full freedom of use specified in this license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License. This license, the GNU Lesser
|
||||||
|
General Public License, applies to certain designated libraries, and
|
||||||
|
is quite different from the ordinary General Public License. We use
|
||||||
|
this license for certain libraries in order to permit linking those
|
||||||
|
libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using
|
||||||
|
a shared library, the combination of the two is legally speaking a
|
||||||
|
combined work, a derivative of the original library. The ordinary
|
||||||
|
General Public License therefore permits such linking only if the
|
||||||
|
entire combination fits its criteria of freedom. The Lesser General
|
||||||
|
Public License permits more lax criteria for linking other code with
|
||||||
|
the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it
|
||||||
|
does Less to protect the user's freedom than the ordinary General
|
||||||
|
Public License. It also provides other free software developers Less
|
||||||
|
of an advantage over competing non-free programs. These disadvantages
|
||||||
|
are the reason we use the ordinary General Public License for many
|
||||||
|
libraries. However, the Lesser license provides advantages in certain
|
||||||
|
special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to
|
||||||
|
encourage the widest possible use of a certain library, so that it becomes
|
||||||
|
a de-facto standard. To achieve this, non-free programs must be
|
||||||
|
allowed to use the library. A more frequent case is that a free
|
||||||
|
library does the same job as widely used non-free libraries. In this
|
||||||
|
case, there is little to gain by limiting the free library to free
|
||||||
|
software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free
|
||||||
|
programs enables a greater number of people to use a large body of
|
||||||
|
free software. For example, permission to use the GNU C Library in
|
||||||
|
non-free programs enables many more people to use the whole GNU
|
||||||
|
operating system, as well as its variant, the GNU/Linux operating
|
||||||
|
system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the
|
||||||
|
users' freedom, it does ensure that the user of a program that is
|
||||||
|
linked with the Library has the freedom and the wherewithal to run
|
||||||
|
that program using a modified version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, whereas the latter must
|
||||||
|
be combined with the library in order to run.
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other
|
||||||
|
program which contains a notice placed by the copyright holder or
|
||||||
|
other authorized party saying it may be distributed under the terms of
|
||||||
|
this Lesser General Public License (also called "this License").
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control compilation
|
||||||
|
and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does
|
||||||
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a
|
||||||
|
table of data to be supplied by an application program that uses
|
||||||
|
the facility, other than as an argument passed when the facility
|
||||||
|
is invoked, then you must make a good faith effort to ensure that,
|
||||||
|
in the event an application does not supply such function or
|
||||||
|
table, the facility still operates, and performs whatever part of
|
||||||
|
its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library. To do
|
||||||
|
this, you must alter all the notices that refer to this License, so
|
||||||
|
that they refer to the ordinary GNU General Public License, version 2,
|
||||||
|
instead of to this License. (If a newer version than version 2 of the
|
||||||
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
|
that version instead if you wish.) Do not make any other change in
|
||||||
|
these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a
|
||||||
|
work, in isolation, is not a derivative work of the Library, and
|
||||||
|
therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License.
|
||||||
|
Section 6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a derivative
|
||||||
|
work. (Executables containing this object code plus portions of the
|
||||||
|
Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood
|
||||||
|
that the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (1) uses at run time a
|
||||||
|
copy of the library already present on the user's computer system,
|
||||||
|
rather than copying library functions into the executable, and (2)
|
||||||
|
will operate properly with a modified version of the library, if
|
||||||
|
the user installs one, as long as the modified version is
|
||||||
|
interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at
|
||||||
|
least three years, to give the same user the materials
|
||||||
|
specified in Subsection 6a, above, for a charge no more
|
||||||
|
than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the above
|
||||||
|
specified materials from the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the materials to be distributed need not include anything that is
|
||||||
|
normally distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties with
|
||||||
|
this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Library at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Library by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License may add
|
||||||
|
an explicit geographical distribution limitation excluding those countries,
|
||||||
|
so that distribution is permitted only in or among countries not thus
|
||||||
|
excluded. In such case, this License incorporates the limitation as if
|
||||||
|
written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Lesser General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
possible use to the public, we recommend making it free software that
|
||||||
|
everyone can redistribute and change. You can do so by permitting
|
||||||
|
redistribution under these terms (or, alternatively, under the terms of the
|
||||||
|
ordinary General Public License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is
|
||||||
|
safest to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least the
|
||||||
|
"copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the library's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
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 this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||||
|
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
252
installer/anaconda-webui/vendor/Makefile
vendored
Normal file
252
installer/anaconda-webui/vendor/Makefile
vendored
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
# Copyright (C) 2023 Red Hat, Inc.
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
|
||||||
|
# stamp file to check if/when npm install ran
|
||||||
|
# one example file in dist/ to check if that already ran
|
||||||
|
DIST_TEST=dist/manifest.json
|
||||||
|
PACKAGE_NAME := $(shell awk '/"name":/ {gsub(/[",]/, "", $$2); print $$2}' package.json)
|
||||||
|
RPM_NAME := $(PACKAGE_NAME)
|
||||||
|
VERSION := $(shell T=$$(git describe 2>/dev/null) || T=1; echo $$T | tr '-' '.')
|
||||||
|
TARFILE=$(RPM_NAME)-$(VERSION).tar.xz
|
||||||
|
SPEC=$(RPM_NAME).spec
|
||||||
|
# one example file in pkg/lib to check if it was already checked out
|
||||||
|
COCKPIT_REPO_STAMP=pkg/lib/cockpit-po-plugin.js
|
||||||
|
# stamp file to check if/when npm install ran
|
||||||
|
NODE_MODULES_TEST=package-lock.json
|
||||||
|
|
||||||
|
ifeq ($(TEST_OS),)
|
||||||
|
ifdef TEST_COMPOSE
|
||||||
|
ifneq ($(findstring Rawhide,$(TEST_COMPOSE)),)
|
||||||
|
TEST_OS=fedora-rawhide-boot
|
||||||
|
else
|
||||||
|
TEST_OS=fedora-$(word 2,$(subst -, ,$(TEST_COMPOSE)))-boot
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
TEST_OS=fedora-rawhide-boot
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
BASE_OS=$(word 1,$(subst -, ,$(TEST_OS)))
|
||||||
|
RELEASE=$(word 2,$(subst -, ,$(TEST_OS)))
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Anaconda specific variables
|
||||||
|
ifeq ($(TEST_OS),)
|
||||||
|
PAYLOAD=fedora-rawhide-anaconda-payload
|
||||||
|
else
|
||||||
|
VARIANT=$(shell echo $(TEST_OS) | sed 's/-boot//')
|
||||||
|
PAYLOAD=$(VARIANT)-anaconda-payload
|
||||||
|
endif
|
||||||
|
|
||||||
|
GITHUB_BASE=rhinstaller/anaconda-webui
|
||||||
|
UPDATES_IMG=updates.img
|
||||||
|
TEST_LIVE_OS=fedora-rawhide-live-boot
|
||||||
|
|
||||||
|
export GITHUB_BASE
|
||||||
|
export TEST_OS
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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 -o $@ $$(find src -name '*.html')
|
||||||
|
|
||||||
|
po/$(PACKAGE_NAME).manifest.pot: $(NODE_MODULES_TEST) $(COCKPIT_REPO_STAMP)
|
||||||
|
pkg/lib/manifest2po src/manifest.json -o $@
|
||||||
|
|
||||||
|
po/$(PACKAGE_NAME).pot: po/$(PACKAGE_NAME).html.pot po/$(PACKAGE_NAME).js.pot po/$(PACKAGE_NAME).manifest.pot
|
||||||
|
msgcat --sort-output --output-file=$@ $^
|
||||||
|
|
||||||
|
po/LINGUAS:
|
||||||
|
echo $(LINGUAS) | tr ' ' '\n' > $@
|
||||||
|
|
||||||
|
#
|
||||||
|
# Build/Install/dist
|
||||||
|
#
|
||||||
|
all: $(DIST_TEST)
|
||||||
|
|
||||||
|
dist_libexec_SCRIPTS = webui-desktop browser-ext gnome-control-center-ext
|
||||||
|
# makes sure it gets built as part of `make` and `make dist`
|
||||||
|
dist_noinst_DATA = \
|
||||||
|
$(DIST_TEST) \
|
||||||
|
$(COCKPIT_REPO_STAMP) \
|
||||||
|
package-lock.json \
|
||||||
|
package.json \
|
||||||
|
build.js
|
||||||
|
|
||||||
|
VERSION.txt: $(SPEC)
|
||||||
|
rpmspec -q --queryformat "%{version}\n" $(SPEC) | head -1 > $@
|
||||||
|
|
||||||
|
$(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' $< > $@
|
||||||
|
|
||||||
|
$(DIST_TEST): $(COCKPIT_REPO_STAMP) $(shell find src/ -type f) package.json build.js
|
||||||
|
$(MAKE) VERSION.txt && \
|
||||||
|
$(MAKE) package-lock.json && \
|
||||||
|
NODE_ENV=production ./build.js
|
||||||
|
|
||||||
|
watch: VERSION.txt
|
||||||
|
rm -f dist/*
|
||||||
|
NODE_ENV=$(NODE_ENV) ESBUILD_WATCH=true ./build.js
|
||||||
|
|
||||||
|
rsync:
|
||||||
|
RSYNC=$${RSYNC:-test-updates} make watch
|
||||||
|
|
||||||
|
# Generate comprehensive installation documentation (includes steps and storage scenarios)
|
||||||
|
# NOTE: Run this and commit changes when modifying UI components - CI will check if docs are current
|
||||||
|
.PHONY: docs
|
||||||
|
docs:
|
||||||
|
node docs/generate-docs.js
|
||||||
|
@echo "Installation documentation generated in docs/"
|
||||||
|
|
||||||
|
install: $(DIST_TEST) po/LINGUAS
|
||||||
|
mkdir -p $(DESTDIR)/usr/share/cockpit/$(PACKAGE_NAME)
|
||||||
|
cp -r dist/* $(DESTDIR)/usr/share/cockpit/$(PACKAGE_NAME)
|
||||||
|
mkdir -p $(DESTDIR)/usr/share/anaconda
|
||||||
|
cp -r firefox-theme $(DESTDIR)/usr/share/anaconda/
|
||||||
|
mkdir -p $(DESTDIR)/usr/share/applications
|
||||||
|
cp extlinks.desktop $(DESTDIR)/usr/share/applications/
|
||||||
|
cp anaconda-gnome-control-center.desktop $(DESTDIR)/usr/share/applications/
|
||||||
|
mkdir -p $(DESTDIR)/usr/libexec/anaconda
|
||||||
|
cp webui-desktop $(DESTDIR)/usr/libexec/anaconda
|
||||||
|
cp browser-ext $(DESTDIR)/usr/libexec/anaconda
|
||||||
|
cp gnome-control-center-ext $(DESTDIR)/usr/libexec/anaconda
|
||||||
|
cp src/scripts/cockpit-coproc-wrapper.sh $(DESTDIR)/usr/libexec/anaconda/
|
||||||
|
cp src/scripts/cockpit-pin-auth $(DESTDIR)/usr/libexec/anaconda/
|
||||||
|
cp src/scripts/anaconda-cockpit-conf-merge $(DESTDIR)/usr/libexec/anaconda/
|
||||||
|
mkdir -p $(DESTDIR)/usr/lib/systemd/system/
|
||||||
|
cp src/systemd/webui-cockpit-ws.service $(DESTDIR)/usr/lib/systemd/system/
|
||||||
|
cp src/systemd/cockpit-pin-auth@.service $(DESTDIR)/usr/lib/systemd/system/
|
||||||
|
cp src/systemd/cockpit-pin-auth.socket $(DESTDIR)/usr/lib/systemd/system/
|
||||||
|
cp src/systemd/webui-tls-proxy.service $(DESTDIR)/usr/lib/systemd/system/
|
||||||
|
cp src/scripts/webui-tls-proxy $(DESTDIR)/usr/libexec/anaconda/
|
||||||
|
mkdir -p $(DESTDIR)/etc/anaconda/cockpit/conf.d/
|
||||||
|
cp src/config/cockpit/cockpit.conf $(DESTDIR)/etc/anaconda/cockpit/cockpit.conf
|
||||||
|
# Staged config, not active by default — webui-desktop copies it to conf.d/ at runtime
|
||||||
|
mkdir -p $(DESTDIR)/usr/share/anaconda/cockpit/conf.d/
|
||||||
|
cp src/config/cockpit/conf.d/50-remote-auth.conf $(DESTDIR)/usr/share/anaconda/cockpit/conf.d/
|
||||||
|
|
||||||
|
dist: $(TARFILE)
|
||||||
|
@ls -1 $(TARFILE)
|
||||||
|
|
||||||
|
# when building a distribution tarball, call bundler with a 'production' environment
|
||||||
|
# we don't ship most node_modules for license and compactness reasons, only the ones necessary for running tests
|
||||||
|
# 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)
|
||||||
|
tar --xz $(TAR_ARGS) -cf $(TARFILE) --transform 's,^,$(RPM_NAME)/,' \
|
||||||
|
--exclude '*.in' --exclude test/reference \
|
||||||
|
$$(git ls-files | grep -v node_modules) \
|
||||||
|
$(COCKPIT_REPO_FILES) $(NODE_MODULES_TEST) $(SPEC) VERSION.txt \
|
||||||
|
dist/
|
||||||
|
|
||||||
|
srpm: $(TARFILE) $(SPEC)
|
||||||
|
rpmbuild -bs \
|
||||||
|
--define "_sourcedir `pwd`" \
|
||||||
|
--define "_srcrpmdir `pwd`" \
|
||||||
|
$(SPEC)
|
||||||
|
|
||||||
|
EXTRA_DIST = dist src firefox-theme
|
||||||
|
|
||||||
|
# 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/node-modules \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
COCKPIT_REPO_URL = https://github.com/cockpit-project/cockpit.git
|
||||||
|
COCKPIT_REPO_COMMIT = 853699ab9697053d299921db46a5900c7a23649e # 368
|
||||||
|
|
||||||
|
$(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
|
||||||
|
|
||||||
|
.PHONY: codecheck
|
||||||
|
codecheck: test/common $(NODE_MODULES_TEST)
|
||||||
|
test/common/static-code
|
||||||
|
|
||||||
|
# checkout Cockpit's bots for standard test VM images and API to launch them
|
||||||
|
# must be from main, as only that has current and existing images; but testvm.py API is stable
|
||||||
|
# support CI testing against a bots change
|
||||||
|
# Workaround cockpit's expectation for test/images directory. This is not really needed in our case
|
||||||
|
# as we consume ready ISOs
|
||||||
|
# https://github.com/cockpit-project/cockpit/blob/main/test/common/testlib.py#L1118
|
||||||
|
bots: test/common
|
||||||
|
test/common/make-bots
|
||||||
|
cd test && ln -sf ../bots/images images
|
||||||
|
|
||||||
|
live-vm: bots $(UPDATES_IMG)
|
||||||
|
./test/webui_testvm.py $(TEST_LIVE_OS)
|
||||||
|
|
||||||
|
prepare-test-deps: bots test/common test/reference payload images
|
||||||
|
|
||||||
|
.PHONY: payload
|
||||||
|
payload: bots
|
||||||
|
bots/image-download $(PAYLOAD)
|
||||||
|
@echo "Unpacking $(PAYLOAD) to tmp for local serving"
|
||||||
|
@rm -rf tmp/$(PAYLOAD) && mkdir -p tmp/$(PAYLOAD)
|
||||||
|
@tar -C tmp/$(PAYLOAD) -xzf bots/images/$(PAYLOAD)
|
||||||
|
@echo "Payload unpacked into tmp/$(PAYLOAD)"
|
||||||
|
|
||||||
|
.PHONY: images
|
||||||
|
images: bots
|
||||||
|
# Download ISO images for testing a compose
|
||||||
|
@if [ -n "$(TEST_COMPOSE)" ]; then \
|
||||||
|
test/download-iso "$(TEST_OS)" "$(TEST_COMPOSE)" "$(RELEASE)"; \
|
||||||
|
fi
|
||||||
|
bots/image-download "$(TEST_OS)"
|
||||||
|
|
||||||
|
$(UPDATES_IMG): prepare-test-deps
|
||||||
|
test/prepare-updates-img
|
||||||
|
|
||||||
|
create-updates.img: bots
|
||||||
|
-rm *updates.img
|
||||||
|
make $(UPDATES_IMG)
|
||||||
|
|
||||||
|
test/reference: test/common
|
||||||
|
test/common/pixel-tests pull
|
||||||
|
|
||||||
|
update-reference-images: test/common test/reference
|
||||||
|
test/common/pixel-tests push
|
||||||
|
|
||||||
|
# We want tools/node-modules to run every time package-lock.json is requested
|
||||||
|
# See https://www.gnu.org/software/make/manual/html_node/Force-Targets.html
|
||||||
|
FORCE:
|
||||||
|
$(NODE_MODULES_TEST): FORCE tools/node-modules
|
||||||
|
tools/node-modules make_package_lock_json
|
||||||
|
|
||||||
|
.PHONY: print-test-os
|
||||||
|
print-test-os:
|
||||||
|
@echo $(TEST_OS)
|
||||||
|
|
||||||
|
.PHONY: test-compose
|
||||||
|
test-compose: bots
|
||||||
|
bots/tests-trigger --force "-" "${TEST_OS}/compose-${TEST_COMPOSE}"
|
||||||
|
bots/tests-trigger --force "-" "${TEST_OS}/bios-compose-${TEST_COMPOSE}"
|
||||||
|
|
||||||
|
.PHONY: test-compose-staging
|
||||||
|
test-compose-staging: bots
|
||||||
|
bots/tests-trigger --force "-" "${TEST_OS}/compose-${TEST_COMPOSE}-staging"
|
||||||
|
bots/tests-trigger --force "-" "${TEST_OS}/bios-compose-${TEST_COMPOSE}-staging"
|
||||||
46
installer/anaconda-webui/vendor/README.rst
vendored
Normal file
46
installer/anaconda-webui/vendor/README.rst
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
Anaconda Web UI
|
||||||
|
===============
|
||||||
|
|
||||||
|
Getting the source
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Here's where to get the code::
|
||||||
|
|
||||||
|
git clone https://github.com/rhinstaller/anaconda-webui.git
|
||||||
|
cd anaconda-webui
|
||||||
|
|
||||||
|
Development instructions
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
See `<HACKING.rst>`_ and `<test/README.rst>`_ for details about how to efficiently change the code,
|
||||||
|
run, and test it.
|
||||||
|
|
||||||
|
Automated release
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The intention is that the only manual step for releasing a project is to create
|
||||||
|
a signed tag for the version number, which includes a summary of the noteworthy
|
||||||
|
changes::
|
||||||
|
|
||||||
|
123
|
||||||
|
|
||||||
|
- this new feature
|
||||||
|
- fix bug #123
|
||||||
|
|
||||||
|
Pushing the release tag triggers the `release.yml <.github/workflows/release.yml>`_
|
||||||
|
`GitHub action <https://github.com/features/actions>`_ workflow. This creates the
|
||||||
|
official release tarball and publishes as upstream release to GitHub.
|
||||||
|
|
||||||
|
The Fedora and COPR releases are done with `Packit <https://packit.dev/>`_.
|
||||||
|
see the `packit.yaml <./packit.yaml>`_ control file.
|
||||||
|
|
||||||
|
Automated maintenance
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
It is important to keep your `NPM modules <./package.json>`_ up to date, to keep
|
||||||
|
up with security updates and bug fixes. This is done with dependabot, see the
|
||||||
|
`dependabot.yml <./.github/dependabot.yml>`_ control file.
|
||||||
|
Similarly, translations are refreshed every Tuesday evening (or manually) through the
|
||||||
|
`weblate-sync-po.yml <.github/workflows/weblate-sync-po.yml>`_ action.
|
||||||
|
Conversely, the PO template is uploaded to weblate every day through the
|
||||||
|
`weblate-sync-pot.yml <.github/workflows/weblate-sync-pot.yml>`_ action.
|
||||||
16
installer/anaconda-webui/vendor/anaconda-gnome-control-center.desktop
vendored
Normal file
16
installer/anaconda-webui/vendor/anaconda-gnome-control-center.desktop
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Name=Keyboard External
|
||||||
|
Exec=/usr/libexec/anaconda/gnome-control-center-ext keyboard
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
MimeType=x-scheme-handler/anaconda-gnome-control-center;
|
||||||
|
StartupNotify=true
|
||||||
|
Categories=GNOME;GTK;Settings;HardwareSettings;
|
||||||
|
Actions=Region;
|
||||||
|
NoDisplay=true
|
||||||
|
X-Desktop-File-Install-Version=0.26
|
||||||
|
|
||||||
|
[Desktop Action Region]
|
||||||
|
Name=Region & Language
|
||||||
|
Exec=/usr/libexec/anaconda/gnome-control-center-ext keyboard
|
||||||
40
installer/anaconda-webui/vendor/browser-ext
vendored
Executable file
40
installer/anaconda-webui/vendor/browser-ext
vendored
Executable file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Copyright (C) 2025 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License as published
|
||||||
|
# by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# This should be eventually get shared across projects:
|
||||||
|
# https://issues.redhat.com/browse/INSTALLER-3755
|
||||||
|
|
||||||
|
# Get the default browser for the 'https' protocol
|
||||||
|
DEFAULT_BROWSER=$(xdg-mime query default x-scheme-handler/https)
|
||||||
|
URL="${1/extlink/https}"
|
||||||
|
|
||||||
|
# If Firefox is the default browser, use the custom profile
|
||||||
|
if [[ "$DEFAULT_BROWSER" == "org.mozilla.firefox.desktop" ]]; then
|
||||||
|
# Create a temporary Firefox profile
|
||||||
|
PROFILE_DIR="/tmp/anaconda-extlink-firefox-profile"
|
||||||
|
mkdir -p "$PROFILE_DIR"
|
||||||
|
|
||||||
|
# Copy the custom user.js for the Firefox settings
|
||||||
|
cp /usr/share/anaconda/firefox-theme/extlink/user.js "$PROFILE_DIR/"
|
||||||
|
|
||||||
|
# Launch Firefox with the custom profile
|
||||||
|
/usr/bin/firefox --profile "$PROFILE_DIR" --no-remote "$URL"
|
||||||
|
else
|
||||||
|
# For any other browser, use xdg-open
|
||||||
|
xdg-open "$URL"
|
||||||
|
fi
|
||||||
|
|
||||||
124
installer/anaconda-webui/vendor/build.js
vendored
Executable file
124
installer/anaconda-webui/vendor/build.js
vendored
Executable file
|
|
@ -0,0 +1,124 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
import esbuild from "esbuild";
|
||||||
|
import copy from "esbuild-plugin-copy";
|
||||||
|
import { sassPlugin } from "esbuild-sass-plugin";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
import { cockpitPoEsbuildPlugin } from "./pkg/lib/cockpit-po-plugin.js";
|
||||||
|
import { cockpitRsyncEsbuildPlugin } from "./pkg/lib/cockpit-rsync-plugin.js";
|
||||||
|
import { cleanPlugin } from "./pkg/lib/esbuild-cleanup-plugin.js";
|
||||||
|
import { cockpitCompressPlugin } from "./pkg/lib/esbuild-compress-plugin.js";
|
||||||
|
|
||||||
|
const production = process.env.NODE_ENV === "production";
|
||||||
|
const watchMode = process.env.ESBUILD_WATCH === "true" || false;
|
||||||
|
/* List of directories to use when resolving import statements */
|
||||||
|
const nodePaths = ["pkg/lib"];
|
||||||
|
const outdir = "dist";
|
||||||
|
|
||||||
|
// Obtain package name from package.json
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync("package.json"));
|
||||||
|
|
||||||
|
const getTime = () => new Date().toTimeString()
|
||||||
|
.split(" ")[0];
|
||||||
|
|
||||||
|
// similar to fs.watch(), but recursively watches all subdirectories
|
||||||
|
function watchDirs (dir, onChange) {
|
||||||
|
const callback = (ev, dir, fname) => {
|
||||||
|
// only listen for "change" events, as renames are noisy
|
||||||
|
// ignore hidden files and the "4913" temporary file created by vim
|
||||||
|
const isHidden = /^\./.test(fname);
|
||||||
|
if (ev !== "change" || isHidden || fname === "4913") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onChange(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()) {
|
||||||
|
watchDirs(path.join(dir, dirent.name), onChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.closeSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = await esbuild.context({
|
||||||
|
bundle: true,
|
||||||
|
entryPoints: ["./src/index.js", "./src/anacondaLogin.js"],
|
||||||
|
external: ["*.woff", "*.woff2", "*.jpg", "*.svg", "../../assets*"],
|
||||||
|
legalComments: "external", // Move all legal comments to a .LEGAL.txt file
|
||||||
|
loader: {
|
||||||
|
".js": "jsx",
|
||||||
|
".py": "text",
|
||||||
|
".txt": "text",
|
||||||
|
},
|
||||||
|
minify: production,
|
||||||
|
nodePaths,
|
||||||
|
outdir,
|
||||||
|
plugins: [
|
||||||
|
cleanPlugin(),
|
||||||
|
// 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: ["./images/qr-code-feedback.svg"], to: ["./qr-code-feedback.svg"] },
|
||||||
|
{ from: ["./src/manifest.json"], to: ["./manifest.json"] },
|
||||||
|
{ from: ["./src/index.html"], to: ["./index.html"] },
|
||||||
|
{ from: ["./src/login.html"], to: ["./login.html"] },
|
||||||
|
{ from: ["./VERSION.txt"], to: ["./VERSION.txt"] },
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
sassPlugin({
|
||||||
|
loadPaths: [...nodePaths, "node_modules"],
|
||||||
|
quietDeps: true,
|
||||||
|
}),
|
||||||
|
cockpitPoEsbuildPlugin(),
|
||||||
|
// Login assets are served via /cockpit/static/ which does not
|
||||||
|
// support .gz transparent serving, causing MIME type mismatch.
|
||||||
|
cockpitCompressPlugin({ exclude: /anacondaLogin\.(js|css)$/ }),
|
||||||
|
cockpitRsyncEsbuildPlugin({ dest: packageJson.name }),
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "notify-end",
|
||||||
|
setup (build) {
|
||||||
|
build.onEnd(() => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(`${getTime()}: Build finished`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sourcemap: "linked",
|
||||||
|
target: ["es2020"],
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await context.rebuild();
|
||||||
|
} catch (e) {
|
||||||
|
if (!watchMode) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
// ignore errors in watch mode
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watchMode) {
|
||||||
|
const onChange = async path => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log("change detected:", path);
|
||||||
|
await context.cancel();
|
||||||
|
try {
|
||||||
|
await context.rebuild();
|
||||||
|
} catch (e) {} // ignore in watch mode
|
||||||
|
};
|
||||||
|
|
||||||
|
watchDirs("src", onChange);
|
||||||
|
// wait forever until Control-C
|
||||||
|
await new Promise(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
context.dispose();
|
||||||
40
installer/anaconda-webui/vendor/docs/README-documentation.rst
vendored
Normal file
40
installer/anaconda-webui/vendor/docs/README-documentation.rst
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
Documentation Generation System
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
This system automatically generates comprehensive documentation for Anaconda
|
||||||
|
Web UI. The generated documentation is ready for readthedocs.io integration and
|
||||||
|
includes complete information about the installation flow.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
Generate Documentation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To generate/update all documentation:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
make docs
|
||||||
|
|
||||||
|
This generates:
|
||||||
|
|
||||||
|
- ``docs/installation-steps.rst`` - Complete installation guide including all pages and storage scenarios
|
||||||
|
|
||||||
|
Continuous Integration
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
A GitHub Action (``.github/workflows/docs-check.yml``) automatically verifies that documentation stays up-to-date with code changes.
|
||||||
|
|
||||||
|
Contributing
|
||||||
|
------------
|
||||||
|
|
||||||
|
When adding new scenarios or pages:
|
||||||
|
|
||||||
|
1. Add JSDoc ``@description`` comments in page or scenario file
|
||||||
|
2. Run ``make docs`` to update documentation
|
||||||
|
|
||||||
|
CI will automatically verify that documentation stays current with code changes.
|
||||||
176
installer/anaconda-webui/vendor/docs/customization.rst
vendored
Normal file
176
installer/anaconda-webui/vendor/docs/customization.rst
vendored
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
Customizing the Installer Web UI
|
||||||
|
=================================
|
||||||
|
|
||||||
|
This document describes how distributions and derivatives (such as Bazzite and Bluefin) can customize the Installer Web UI to match their branding and requirements.
|
||||||
|
|
||||||
|
Customization can be broadly categorized into two areas:
|
||||||
|
|
||||||
|
- **Branding and chroming** - Changing the visual appearance and naming without changing behavior (see :ref:`branding-and-chroming`)
|
||||||
|
- **Changing installer behavior** - Modifying defaults and functionality through configuration files (see :ref:`changing-installer-behavior`)
|
||||||
|
|
||||||
|
These customizations can be deployed in different ways:
|
||||||
|
|
||||||
|
- **Build-time customization**: Changes are part of the image when building your own images or using image building tools (e.g., Image Builder)
|
||||||
|
- **Runtime customization**: Changes are provided with a product image at runtime when using existing images (e.g., official RHEL boot ISO or official Fedora boot ISO)
|
||||||
|
|
||||||
|
.. _branding-and-chroming:
|
||||||
|
|
||||||
|
Branding and Chroming the User Interface
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
The Installer Web UI can be customized to match your distribution's visual identity through branding. Cockpit provides branding files per distribution variant, located in the `Cockpit repository <https://github.com/cockpit-project/cockpit/tree/main/src/branding/>`_. The Installer Web UI utilizes these Cockpit branding files and adds anaconda-specific branding through the ``.anaconda`` CSS class wrapper in your distribution's Cockpit ``branding.css`` file.
|
||||||
|
|
||||||
|
We propose using the following template, which defines three brand color variables:
|
||||||
|
|
||||||
|
- ``--brand-default`` - Primary brand color (used in light theme and hover states in dark theme)
|
||||||
|
- ``--brand-default-light`` - Lighter variant (used in dark theme)
|
||||||
|
- ``--brand-default-dark`` - Darker variant (used for hover states in light theme)
|
||||||
|
|
||||||
|
Example branding CSS:
|
||||||
|
|
||||||
|
.. code-block:: css
|
||||||
|
|
||||||
|
.anaconda {
|
||||||
|
/* Brand palette */
|
||||||
|
--brand-default-light: #51a2da;
|
||||||
|
--brand-default: #3c6eb4;
|
||||||
|
--brand-default-dark: #294172;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
background-image: url("logo.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(.pf-v6-theme-dark) .anaconda {
|
||||||
|
/* Default (light theme) token mapping */
|
||||||
|
--pf-t--global--color--brand--default: var(--brand-default);
|
||||||
|
--pf-t--global--color--brand--hover: var(--brand-default-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v6-theme-dark .anaconda {
|
||||||
|
/* Dark theme token mapping */
|
||||||
|
--pf-t--global--color--brand--default: var(--brand-default-light);
|
||||||
|
--pf-t--global--color--brand--hover: var(--brand-default);
|
||||||
|
}
|
||||||
|
|
||||||
|
The CSS file is imported as-is, so you can add whatever customizations you want through CSS.
|
||||||
|
|
||||||
|
Logos are automatically detected and symlinked from system directories during package installation. You only need to reference ``logo.png`` in your CSS using ``background-image: url("logo.png")``. The build system will create the appropriate symlinks.
|
||||||
|
|
||||||
|
See the `Fedora branding example <https://github.com/cockpit-project/cockpit/blob/main/src/branding/fedora/branding.css>`_ for a complete reference implementation that you can use as a template.
|
||||||
|
|
||||||
|
.. _changing-installer-behavior:
|
||||||
|
|
||||||
|
Changing installer behavior through configuration files
|
||||||
|
-------------------------------------------------------
|
||||||
|
|
||||||
|
The installer's behavior and defaults can be customized through Anaconda configuration files. For complete documentation on Anaconda configuration files, see the `Anaconda configuration files documentation <https://anaconda-installer.readthedocs.io/en/latest/developer/configuration-files.html>`_.
|
||||||
|
|
||||||
|
Hiding Web UI pages
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The Installer Web UI allows you to hide specific installation pages that may not be relevant for your distribution or ISO variant. This is useful when certain steps are handled elsewhere (e.g., by first-boot tools like GNOME Initial Setup).
|
||||||
|
|
||||||
|
Pages are hidden using the ``hidden_webui_pages`` option in the ``[User Interface]`` section of configuration files.
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[User Interface]
|
||||||
|
hidden_webui_pages =
|
||||||
|
anaconda-screen-accounts
|
||||||
|
anaconda-screen-date-time
|
||||||
|
|
||||||
|
Available page IDs
|
||||||
|
''''''''''''''''''
|
||||||
|
|
||||||
|
The following page IDs can be used with ``hidden_webui_pages``:
|
||||||
|
|
||||||
|
- ``anaconda-screen-language`` - Installation language and keyboard selection
|
||||||
|
- ``anaconda-screen-date-time`` - Date, time, and timezone configuration
|
||||||
|
- ``anaconda-screen-software-selection`` - Package environment selection
|
||||||
|
- ``anaconda-screen-accounts`` - User account and root password creation
|
||||||
|
|
||||||
|
Example use case
|
||||||
|
''''''''''''''''
|
||||||
|
|
||||||
|
In Fedora Workstation, the account creation page is hidden because user account setup is handled by GNOME Initial Setup during first boot. The date and time page is also hidden as it's configured automatically. See `anaconda/data/profile.d/fedora-workstation.conf <https://github.com/rhinstaller/anaconda/blob/main/data/profile.d/fedora-workstation.conf>`_ for a complete example.
|
||||||
|
|
||||||
|
Default web browser
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The default browser is set using the ``webui_web_engine`` option in the ``[User Interface]`` section of configuration files.
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[User Interface]
|
||||||
|
# The default browser for the Web UI.
|
||||||
|
webui_web_engine = firefox
|
||||||
|
|
||||||
|
The value can be either an executable name available in ``$PATH`` (e.g., ``firefox``) or an absolute path to the executable (e.g., ``/usr/bin/firefox``).
|
||||||
|
|
||||||
|
**Important**: When changing the default browser, ensure that the specified browser is available in the installation environment. This typically means adding the browser package to your custom Live image or boot ISO.
|
||||||
|
|
||||||
|
Example use case
|
||||||
|
''''''''''''''''
|
||||||
|
|
||||||
|
The default browser for Fedora is `Slitherer <https://gitlab.com/VelocityLimitless/Projects/slitherer>`_. Fedora Workstation overrides this to use Firefox, while Fedora KDE uses Slitherer (inherited from the base Fedora profile). See the respective profile files:
|
||||||
|
|
||||||
|
- `anaconda/data/profile.d/fedora-workstation.conf <https://github.com/rhinstaller/anaconda/blob/main/data/profile.d/fedora-workstation.conf>`_
|
||||||
|
- `anaconda/data/profile.d/fedora.conf <https://github.com/rhinstaller/anaconda/blob/main/data/profile.d/fedora.conf>`_
|
||||||
|
|
||||||
|
.. _cockpit-web-service-configuration:
|
||||||
|
|
||||||
|
Cockpit Web Service Configuration
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The Installer Web UI runs on Cockpit's web service (``cockpit-ws``). Its configuration lives under an Anaconda-owned path at ``/etc/anaconda/cockpit/`` instead of the default ``/etc/cockpit/`` to prevent installer-specific settings (such as disabled TLS or authentication) from leaking into the installed system.
|
||||||
|
|
||||||
|
The base configuration file is ``/etc/anaconda/cockpit/cockpit.conf``. It uses the standard `cockpit.conf(5) <https://cockpit-project.org/guide/latest/cockpit.conf.5.html>`_ INI format.
|
||||||
|
|
||||||
|
Default settings:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[WebService]
|
||||||
|
AllowUnencrypted = true
|
||||||
|
LoginTo = false
|
||||||
|
AllowMultiHost = false
|
||||||
|
|
||||||
|
[Session]
|
||||||
|
IdleTimeout = 0
|
||||||
|
|
||||||
|
conf.d drop-in overrides
|
||||||
|
'''''''''''''''''''''''''
|
||||||
|
|
||||||
|
To customize the Cockpit web service for specific environments without modifying the base configuration, place ``*.conf`` files in ``/etc/anaconda/cockpit/conf.d/``. Files are applied in lexicographic order, and later files override values from earlier ones and from the base configuration.
|
||||||
|
|
||||||
|
Example -- enable TLS for a network-based installation:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
# /etc/anaconda/cockpit/conf.d/50-tls.conf
|
||||||
|
[WebService]
|
||||||
|
AllowUnencrypted = false
|
||||||
|
|
||||||
|
Example -- set a custom login title:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
# /etc/anaconda/cockpit/conf.d/50-branding.conf
|
||||||
|
[WebService]
|
||||||
|
LoginTitle = My Custom Installer
|
||||||
|
|
||||||
|
The merged configuration is written to ``/run/anaconda/cockpit/cockpit.conf`` at service start and is used by ``cockpit-ws`` via the ``XDG_CONFIG_DIRS`` environment variable.
|
||||||
|
|
||||||
|
For the full list of available cockpit.conf options, see the `cockpit.conf(5) man page <https://cockpit-project.org/guide/latest/cockpit.conf.5.html>`_.
|
||||||
|
|
||||||
|
Future: Add-on Support
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Add-on support for the Installer Web UI is planned but not yet implemented. This will allow extending the Web UI with custom functionality.
|
||||||
|
|
||||||
|
For tracking this feature, see `INSTALLER-2958 <https://issues.redhat.com/browse/INSTALLER-2958>`_.
|
||||||
416
installer/anaconda-webui/vendor/docs/generate-docs.js
vendored
Executable file
416
installer/anaconda-webui/vendor/docs/generate-docs.js
vendored
Executable file
|
|
@ -0,0 +1,416 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Auto-generate documentation for Anaconda storage scenarios
|
||||||
|
* This script extracts @description from JSDoc comments in scenario files
|
||||||
|
*/
|
||||||
|
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
// Mock cockpit dependency for Node.js environment
|
||||||
|
global.cockpit = {
|
||||||
|
gettext: (text) => text,
|
||||||
|
locale: () => "en"
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
const log = console.log;
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
const warn = console.warn;
|
||||||
|
|
||||||
|
// Utility function to find all index.js files in a directory recursively
|
||||||
|
function findIndexFiles (dir) {
|
||||||
|
const files = [];
|
||||||
|
|
||||||
|
function traverse (currentDir) {
|
||||||
|
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = path.join(currentDir, entry.name);
|
||||||
|
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
traverse(fullPath);
|
||||||
|
} else if (entry.name === "index.js") {
|
||||||
|
files.push(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(dir);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global mocking system - create mocked versions of all files once
|
||||||
|
class MockingSystem {
|
||||||
|
constructor () {
|
||||||
|
this.mockedFiles = new Map();
|
||||||
|
this.tempDir = path.join(__dirname, ".temp-mocks");
|
||||||
|
}
|
||||||
|
|
||||||
|
async initialize () {
|
||||||
|
// Create temp directory
|
||||||
|
if (fs.existsSync(this.tempDir)) {
|
||||||
|
fs.rmSync(this.tempDir, { recursive: true });
|
||||||
|
}
|
||||||
|
fs.mkdirSync(this.tempDir, { recursive: true });
|
||||||
|
|
||||||
|
// Mock all index.js files that import JSX
|
||||||
|
await this.mockAllIndexFiles();
|
||||||
|
|
||||||
|
// Mock steps.js
|
||||||
|
await this.mockStepsFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup () {
|
||||||
|
if (fs.existsSync(this.tempDir)) {
|
||||||
|
fs.rmSync(this.tempDir, { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async mockAllIndexFiles () {
|
||||||
|
// Find all index.js files in component directories
|
||||||
|
const srcDir = path.resolve(__dirname, "../src/components");
|
||||||
|
const indexFiles = findIndexFiles(srcDir);
|
||||||
|
|
||||||
|
for (const filePath of indexFiles) {
|
||||||
|
await this.createMockedFile(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async mockScenarioFiles (scenarioFiles) {
|
||||||
|
// Mock scenario files that aren't already mocked
|
||||||
|
for (const filePath of scenarioFiles) {
|
||||||
|
if (!this.mockedFiles.has(filePath)) {
|
||||||
|
await this.createMockedFile(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createMockedFile (originalPath) {
|
||||||
|
let content = fs.readFileSync(originalPath, "utf8");
|
||||||
|
|
||||||
|
// Apply comprehensive mocking
|
||||||
|
content = this.applyMocking(content);
|
||||||
|
|
||||||
|
// Create relative path for temp file
|
||||||
|
const relativePath = path.relative(path.resolve(__dirname, "../src"), originalPath);
|
||||||
|
const mockedPath = path.join(this.tempDir, relativePath);
|
||||||
|
|
||||||
|
// Ensure directory exists
|
||||||
|
fs.mkdirSync(path.dirname(mockedPath), { recursive: true });
|
||||||
|
|
||||||
|
// Write mocked file
|
||||||
|
fs.writeFileSync(mockedPath, content);
|
||||||
|
|
||||||
|
// Store mapping
|
||||||
|
this.mockedFiles.set(originalPath, mockedPath);
|
||||||
|
|
||||||
|
return mockedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
async mockStepsFile () {
|
||||||
|
const stepsPath = path.resolve(__dirname, "../src/components/steps.js");
|
||||||
|
let content = fs.readFileSync(stepsPath, "utf8");
|
||||||
|
|
||||||
|
// Apply basic mocking for steps.js
|
||||||
|
content = this.applyMocking(content);
|
||||||
|
|
||||||
|
// Update imports to point to mocked files
|
||||||
|
content = content.replace(
|
||||||
|
/import\s+\{\s*Page\s+as\s+([^}]+)\s*\}\s+from\s+["']([^"']+)["'];?/g,
|
||||||
|
(match, pageName, importPath) => {
|
||||||
|
// Convert relative import to absolute
|
||||||
|
const absoluteImportPath = path.resolve(path.dirname(stepsPath), importPath);
|
||||||
|
const mockedPath = this.mockedFiles.get(absoluteImportPath);
|
||||||
|
const relativeToTemp = path.relative(path.dirname(path.join(this.tempDir, "components/steps.js")), mockedPath);
|
||||||
|
return `import { Page as ${pageName} } from "./${relativeToTemp}";`;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write mocked steps.js
|
||||||
|
const mockedStepsPath = path.join(this.tempDir, "components/steps.js");
|
||||||
|
fs.mkdirSync(path.dirname(mockedStepsPath), { recursive: true });
|
||||||
|
fs.writeFileSync(mockedStepsPath, content);
|
||||||
|
|
||||||
|
return mockedStepsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMocking (content) {
|
||||||
|
// Mock cockpit import
|
||||||
|
content = content.replace(
|
||||||
|
/import\s+cockpit\s+from\s+["']cockpit["'];?/g,
|
||||||
|
"const cockpit = { gettext: (text) => text, format: (str, ...args) => str };"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mock debug import
|
||||||
|
content = content.replace(
|
||||||
|
/import\s+\{\s*debug\s*\}\s+from\s+["'][^"']*["'];?/g,
|
||||||
|
"const debug = () => {};"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mock all JSX file imports with comprehensive function mocks
|
||||||
|
content = content.replace(
|
||||||
|
/import\s+\{\s*([^}]+)\s*\}\s+from\s+["'][^"']*\.jsx["'];?/g,
|
||||||
|
(match, componentNames) => {
|
||||||
|
const names = componentNames.split(",").map(name => name.trim())
|
||||||
|
.filter(name => name.length > 0);
|
||||||
|
const mocks = names.map(name => {
|
||||||
|
// Create different types of mocks based on naming patterns
|
||||||
|
if (name.startsWith("use") || name.startsWith("get")) {
|
||||||
|
return `const ${name} = () => null;`;
|
||||||
|
} else {
|
||||||
|
return `const ${name} = {};`;
|
||||||
|
}
|
||||||
|
}).join("\n");
|
||||||
|
return mocks;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
async importMockedSteps () {
|
||||||
|
const mockedStepsPath = path.join(this.tempDir, "components/steps.js");
|
||||||
|
const moduleUrl = "file://" + mockedStepsPath;
|
||||||
|
const module = await import(moduleUrl);
|
||||||
|
return module.getSteps;
|
||||||
|
}
|
||||||
|
|
||||||
|
async importMockedScenario (originalPath) {
|
||||||
|
const mockedPath = this.mockedFiles.get(originalPath);
|
||||||
|
if (!mockedPath) {
|
||||||
|
throw new Error(`No mocked version found for ${originalPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleUrl = "file://" + mockedPath;
|
||||||
|
const module = await import(moduleUrl);
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get step order from the getSteps function
|
||||||
|
async function getOrderedSteps (mockingSystem) {
|
||||||
|
const getSteps = await mockingSystem.importMockedSteps();
|
||||||
|
|
||||||
|
// Call getSteps with mock arguments
|
||||||
|
const steps = getSteps({}, {}, {});
|
||||||
|
|
||||||
|
return steps
|
||||||
|
.flat()
|
||||||
|
.filter(step => step.id && step._description);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract scenario information using the mocking system
|
||||||
|
async function extractScenarioInfo (indexPath, mockingSystem) {
|
||||||
|
if (!fs.existsSync(indexPath)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = fs.readFileSync(indexPath, "utf8");
|
||||||
|
|
||||||
|
// Extract JSDoc comments
|
||||||
|
const jsdocMatch = content.match(/\/\*\*\s*([\s\S]*?)\*\//);
|
||||||
|
|
||||||
|
let description = null;
|
||||||
|
if (jsdocMatch) {
|
||||||
|
const jsdocContent = jsdocMatch[1];
|
||||||
|
|
||||||
|
// Extract description
|
||||||
|
const descMatch = jsdocContent.match(/@description\s+(.*?)(?=@|$)/s);
|
||||||
|
if (descMatch) {
|
||||||
|
description = descMatch[1]
|
||||||
|
.replace(/\s*\*\s*/g, " ")
|
||||||
|
.replace(/\s+/g, " ")
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the pre-mocked file from the mocking system
|
||||||
|
const module = await mockingSystem.importMockedScenario(indexPath);
|
||||||
|
|
||||||
|
if (!module.scenario) {
|
||||||
|
warn(`No scenario export found in ${indexPath}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
description,
|
||||||
|
...module.scenario,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract page information using the mocking system
|
||||||
|
async function extractPageInfo (filePath, mockingSystem) {
|
||||||
|
// Use the pre-mocked file from the mocking system
|
||||||
|
const module = await mockingSystem.importMockedScenario(filePath);
|
||||||
|
|
||||||
|
// Check if this file has a Page class, and if so extract page info
|
||||||
|
if (module.Page) {
|
||||||
|
// Create instance to get properties (pass dummy args for constructors that expect them)
|
||||||
|
const pageInstance = new module.Page(false, "erase-all");
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
description: pageInstance._description || null,
|
||||||
|
...pageInstance,
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // No page definition in this file
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate dynamic flow overview from page data
|
||||||
|
function generateFlowOverview (orderedSteps) {
|
||||||
|
let overview = "";
|
||||||
|
|
||||||
|
orderedSteps.forEach((step, index) => {
|
||||||
|
const stepNumber = index + 1;
|
||||||
|
overview += `${stepNumber}. **${!step.isFinal ? step.label : "Installation progress"}**\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return overview;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the main documentation content
|
||||||
|
function generateStepsRst (orderedSteps, scenarios) {
|
||||||
|
const flowOverview = generateFlowOverview(orderedSteps);
|
||||||
|
|
||||||
|
// Generate storage scenarios section
|
||||||
|
let scenariosSection = `
|
||||||
|
Storage Partitioning Scenarios
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The Web UI installer provides several storage partitioning scenarios to accommodate different installation needs:
|
||||||
|
|
||||||
|
.. figure:: images/storage-scenario-overview.png
|
||||||
|
:width: 500px
|
||||||
|
:alt: Storage scenarios overview interface
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
scenarios.forEach((scenario, index) => {
|
||||||
|
scenariosSection += `**${scenario.getLabel() || scenario.docsLabel}**\n\n`;
|
||||||
|
if (scenario.description) {
|
||||||
|
scenariosSection += `${scenario.description}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if individual scenario image exists and include it
|
||||||
|
const scenarioImagePath = path.join(__dirname, "images", `storage-scenario-${scenario.id}.png`);
|
||||||
|
if (fs.existsSync(scenarioImagePath)) {
|
||||||
|
scenariosSection += `
|
||||||
|
.. figure:: images/storage-scenario-${scenario.id}.png
|
||||||
|
:width: 500px
|
||||||
|
:alt: ${scenario.getLabel() || scenario.docsLabel} scenario interface
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add spacing between scenarios except for the last one
|
||||||
|
if (index < scenarios.length - 1) {
|
||||||
|
scenariosSection += "\n";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const rstContent = `
|
||||||
|
Installation Flow Overview
|
||||||
|
==========================
|
||||||
|
|
||||||
|
This document describes the step-by-step process for installing the system using the Web UI.
|
||||||
|
|
||||||
|
${flowOverview}
|
||||||
|
Detailed Step Descriptions
|
||||||
|
=============================
|
||||||
|
|
||||||
|
${orderedSteps.map((step, index) => {
|
||||||
|
const stepNumber = index + 1;
|
||||||
|
const stepTitle = `${stepNumber}. ${!step.isFinal ? step.label : "Installation progress"}`;
|
||||||
|
let stepContent = stepTitle + "\n" + "-".repeat(stepTitle.length) + `\n\n${step.description || "No description available."}\n`;
|
||||||
|
|
||||||
|
// Add scenarios subsection for the Installation method step
|
||||||
|
if (step.id === "anaconda-screen-method") {
|
||||||
|
stepContent += `\n${scenariosSection}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stepContent;
|
||||||
|
}).join("\n")}
|
||||||
|
`;
|
||||||
|
|
||||||
|
return rstContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main function to generate documentation
|
||||||
|
async function generateDocs () {
|
||||||
|
const mockingSystem = new MockingSystem();
|
||||||
|
|
||||||
|
try {
|
||||||
|
log("Starting documentation generation...");
|
||||||
|
log("Initializing mocking system...");
|
||||||
|
|
||||||
|
// Initialize the global mocking system
|
||||||
|
await mockingSystem.initialize();
|
||||||
|
|
||||||
|
log("Getting ordered steps...");
|
||||||
|
// Get ordered steps from getSteps()
|
||||||
|
const orderedSteps = await getOrderedSteps(mockingSystem);
|
||||||
|
|
||||||
|
log("Processing scenarios and pages...");
|
||||||
|
// Get all scenarios and pages
|
||||||
|
const scenariosDir = path.join(__dirname, "..", "src", "components", "storage", "scenarios");
|
||||||
|
const scenarioIndexFiles = findIndexFiles(scenariosDir);
|
||||||
|
|
||||||
|
// Filter out the root scenarios/index.js file - only include files in subdirectories
|
||||||
|
const scenarioFiles = scenarioIndexFiles.filter(filePath => {
|
||||||
|
const relativePath = path.relative(scenariosDir, filePath);
|
||||||
|
return relativePath !== "index.js"; // Exclude the root index.js
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock scenario files before trying to import them
|
||||||
|
await mockingSystem.mockScenarioFiles(scenarioFiles);
|
||||||
|
|
||||||
|
const scenarioPromises = scenarioFiles.map(filePath => extractScenarioInfo(filePath, mockingSystem));
|
||||||
|
const scenarios = await Promise.all(scenarioPromises);
|
||||||
|
|
||||||
|
// Find page files using findIndexFiles and filter out scenarios directory
|
||||||
|
const componentsDir = path.resolve(__dirname, "../src/components");
|
||||||
|
const allIndexFiles = findIndexFiles(componentsDir);
|
||||||
|
|
||||||
|
const pageFiles = allIndexFiles
|
||||||
|
.filter(filePath => !filePath.startsWith(scenariosDir));
|
||||||
|
|
||||||
|
// Mock page files before trying to import them
|
||||||
|
await mockingSystem.mockScenarioFiles(pageFiles);
|
||||||
|
|
||||||
|
const pagePromises = pageFiles.map(filePath => extractPageInfo(filePath, mockingSystem));
|
||||||
|
const pages = await Promise.all(pagePromises);
|
||||||
|
|
||||||
|
// Match pages with steps by ID
|
||||||
|
const stepsWithPageData = orderedSteps.map(step => {
|
||||||
|
const matchingPage = pages.find(page => page?.id === step.id);
|
||||||
|
return {
|
||||||
|
...step,
|
||||||
|
description: matchingPage?.description,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
log("Generating documentation...");
|
||||||
|
// Generate the documentation
|
||||||
|
const stepsRst = generateStepsRst(stepsWithPageData, scenarios);
|
||||||
|
|
||||||
|
// Write the documentation
|
||||||
|
const outputPath = path.join(__dirname, "installation-steps.rst");
|
||||||
|
fs.writeFileSync(outputPath, stepsRst);
|
||||||
|
|
||||||
|
log("Documentation generation completed!");
|
||||||
|
} finally {
|
||||||
|
// Always cleanup temp files
|
||||||
|
mockingSystem.cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the documentation generation
|
||||||
|
generateDocs();
|
||||||
BIN
installer/anaconda-webui/vendor/docs/images/storage-scenario-mount-point-mapping.png
vendored
Normal file
BIN
installer/anaconda-webui/vendor/docs/images/storage-scenario-mount-point-mapping.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
BIN
installer/anaconda-webui/vendor/docs/images/storage-scenario-overview.png
vendored
Normal file
BIN
installer/anaconda-webui/vendor/docs/images/storage-scenario-overview.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
BIN
installer/anaconda-webui/vendor/docs/images/storage-scenario-use-configured-storage.png
vendored
Normal file
BIN
installer/anaconda-webui/vendor/docs/images/storage-scenario-use-configured-storage.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
101
installer/anaconda-webui/vendor/docs/installation-steps.rst
vendored
Normal file
101
installer/anaconda-webui/vendor/docs/installation-steps.rst
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
|
||||||
|
Installation Flow Overview
|
||||||
|
==========================
|
||||||
|
|
||||||
|
This document describes the step-by-step process for installing the system using the Web UI.
|
||||||
|
|
||||||
|
1. **Welcome**
|
||||||
|
2. **Network**
|
||||||
|
3. **Date and time**
|
||||||
|
4. **Software selection**
|
||||||
|
5. **Installation method**
|
||||||
|
6. **Storage configuration**
|
||||||
|
7. **Create Account**
|
||||||
|
8. **Review and install**
|
||||||
|
9. **Installation progress**
|
||||||
|
|
||||||
|
Detailed Step Descriptions
|
||||||
|
=============================
|
||||||
|
|
||||||
|
1. Welcome
|
||||||
|
----------
|
||||||
|
|
||||||
|
Select the language & keyboard to use during installation and for the target system.
|
||||||
|
|
||||||
|
2. Network
|
||||||
|
----------
|
||||||
|
|
||||||
|
Configure network connections for the system.
|
||||||
|
|
||||||
|
3. Date and time
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Configure your system's timezone, date, and time settings. You can also set up network time synchronization.
|
||||||
|
|
||||||
|
4. Software selection
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Select packages to install by choosing a base environment.
|
||||||
|
|
||||||
|
5. Installation method
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Choose the target device(s) for the installation and the partitioning scenario.
|
||||||
|
|
||||||
|
|
||||||
|
Storage Partitioning Scenarios
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The Web UI installer provides several storage partitioning scenarios to accommodate different installation needs:
|
||||||
|
|
||||||
|
.. figure:: images/storage-scenario-overview.png
|
||||||
|
:width: 500px
|
||||||
|
:alt: Storage scenarios overview interface
|
||||||
|
|
||||||
|
**Use entire disk**
|
||||||
|
|
||||||
|
Completely erases all data on the selected disks and automatically creates a new partition layout. Use this for clean installations when you do not need to preserve any existing data.
|
||||||
|
|
||||||
|
**Mount point assignment**
|
||||||
|
|
||||||
|
Allows you to manually assign mount points to specific devices for complete control over the partition layout. Use this if you have custom storage requirements or want to reuse existing partitions selectively.
|
||||||
|
|
||||||
|
.. figure:: images/storage-scenario-mount-point-mapping.png
|
||||||
|
:width: 500px
|
||||||
|
:alt: Mount point assignment scenario interface
|
||||||
|
|
||||||
|
**Reinstall Fedora**
|
||||||
|
|
||||||
|
Reinstalls Fedora while preserving your existing home directory and user data. Use when you want to refresh your Fedora installation while keeping all your personal files and settings. This option only appears when exactly one existing Fedora system is detected and the system has only the default mount points.
|
||||||
|
|
||||||
|
**Use configured storage**
|
||||||
|
|
||||||
|
|
||||||
|
.. figure:: images/storage-scenario-use-configured-storage.png
|
||||||
|
:width: 500px
|
||||||
|
:alt: Use configured storage scenario interface
|
||||||
|
|
||||||
|
**Use free space**
|
||||||
|
|
||||||
|
Installs using only unallocated free space, preserving existing partitions and data. Use when you want to dual-boot with existing operating systems. This option only appears when existing partitions are detected on the selected disks.
|
||||||
|
|
||||||
|
6. Storage configuration
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Automatic partitioning configuration, disk encryption, and storage options.
|
||||||
|
|
||||||
|
7. Create Account
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Set up user accounts and administrator passwords for your system.
|
||||||
|
|
||||||
|
8. Review and install
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Review your installation settings and start the installation process.
|
||||||
|
|
||||||
|
9. Installation progress
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Monitor the installation progress and completion.
|
||||||
|
|
||||||
5
installer/anaconda-webui/vendor/docs/release-notes/index.rst
vendored
Normal file
5
installer/anaconda-webui/vendor/docs/release-notes/index.rst
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
.. This page serves as an index for release notes.
|
||||||
|
|
||||||
|
This section contains release notes for Anaconda Web UI, organized by Fedora release.
|
||||||
|
|
||||||
|
.. include:: release-notes.rst
|
||||||
52
installer/anaconda-webui/vendor/docs/release-notes/release-notes.rst
vendored
Normal file
52
installer/anaconda-webui/vendor/docs/release-notes/release-notes.rst
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
Release notes Web UI
|
||||||
|
====================
|
||||||
|
|
||||||
|
This document describes major Anaconda Web UI changes in Fedora releases.
|
||||||
|
|
||||||
|
Fedora 44
|
||||||
|
#########
|
||||||
|
|
||||||
|
Keyboard and localization
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Multiple keyboard layouts on Workstation
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Multiple keyboard layouts can be selected on Workstation, similar to non-GNOME
|
||||||
|
variants. This is needed for many languages (for example, Russian with the
|
||||||
|
Cyrillic alphabet).
|
||||||
|
|
||||||
|
Sensible keyboard defaults with language selection
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
When you choose a language, the installer suggests matching keyboard defaults
|
||||||
|
on non-GNOME systems. On Workstation ISO, keyboard layout is not set
|
||||||
|
automatically according to language selection; use GNOME’s keyboard settings to
|
||||||
|
add or switch layouts if needed.
|
||||||
|
|
||||||
|
See also:
|
||||||
|
- https://bugzilla.redhat.com/show_bug.cgi?id=2402459
|
||||||
|
|
||||||
|
Storage
|
||||||
|
-------
|
||||||
|
|
||||||
|
Validation warnings for automatic partitioning
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
When automatic partitioning is selected, the Web UI can show validation warnings
|
||||||
|
when the chosen setup may need attention. These are warnings, not errors: the
|
||||||
|
user can ignore them and continue if they want. For example, when choosing to
|
||||||
|
share the disk with another OS, if the existing EFI partition is smaller than
|
||||||
|
the recommended size of 1 GiB, a warning is shown so the user is aware before
|
||||||
|
proceeding.
|
||||||
|
|
||||||
|
Other
|
||||||
|
-----
|
||||||
|
|
||||||
|
Improvements in error reporting
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The error reporting dialog now supports a flow that uses a Bugzilla API key to
|
||||||
|
register and automatically upload the needed logs. Stack traces are
|
||||||
|
automatically included in reports with readable source-mapped output. Users can
|
||||||
|
also restrict the report to a limited group.
|
||||||
12
installer/anaconda-webui/vendor/docs/release-notes/template.rst
vendored
Normal file
12
installer/anaconda-webui/vendor/docs/release-notes/template.rst
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
:Type: Localization / Storage / Network / ...
|
||||||
|
:Summary: Summarize the change
|
||||||
|
|
||||||
|
:Description:
|
||||||
|
Describe the change in detail. The text
|
||||||
|
can be as long as you need.
|
||||||
|
|
||||||
|
Use empty lines to separate paragraphs.
|
||||||
|
|
||||||
|
:Links:
|
||||||
|
- Link to the bug on Bugzilla.
|
||||||
|
- Link to the pull request on GitHub.
|
||||||
94
installer/anaconda-webui/vendor/docs/release.rst
vendored
Normal file
94
installer/anaconda-webui/vendor/docs/release.rst
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
=================
|
||||||
|
Release Process
|
||||||
|
=================
|
||||||
|
|
||||||
|
We trigger releases manually, not on a fixed schedule, and rely on GitHub Actions and Packit to handle the rest automatically.
|
||||||
|
|
||||||
|
This document outlines the detailed release procedure.
|
||||||
|
|
||||||
|
1. **Review release blockers**
|
||||||
|
|
||||||
|
Issues and PRs that are urgent for the next release should have the
|
||||||
|
`release-blocker <https://github.com/rhinstaller/anaconda-webui/labels/release-blocker>`_ label.
|
||||||
|
|
||||||
|
Review the list and confirm with the team whether any pending items remain for the current release.
|
||||||
|
|
||||||
|
2. **Identify the previous release**
|
||||||
|
|
||||||
|
Run::
|
||||||
|
|
||||||
|
PREV=$(git describe --abbrev=0 --tags); echo $PREV
|
||||||
|
|
||||||
|
3. **Review recent changes**
|
||||||
|
|
||||||
|
Generate a changelog with::
|
||||||
|
|
||||||
|
git shortlog --no-merges $PREV..
|
||||||
|
|
||||||
|
Focus on new features. Skip test-only changes and minor bug fixes.
|
||||||
|
|
||||||
|
4. **Prepare the release summary and tag**
|
||||||
|
|
||||||
|
Use the changelog to create a "release news" summary. This appears in:
|
||||||
|
|
||||||
|
- the GitHub release page
|
||||||
|
- downstream package changelogs (via the tag message)
|
||||||
|
|
||||||
|
Follow this tagging format::
|
||||||
|
|
||||||
|
123
|
||||||
|
|
||||||
|
- Add this cool new feature
|
||||||
|
- Support Python 3
|
||||||
|
- Fix wrong color on the bikeshed (rhbz#123456)
|
||||||
|
|
||||||
|
Create the tag::
|
||||||
|
|
||||||
|
git tag -s <new-tag>
|
||||||
|
|
||||||
|
Or use ``-a`` if you don't have a GPG key.
|
||||||
|
|
||||||
|
Push the tag to the main branch::
|
||||||
|
|
||||||
|
git push origin <new-tag>
|
||||||
|
|
||||||
|
5. **GitHub release automation**
|
||||||
|
|
||||||
|
Tagging triggers the `release GitHub workflow <https://github.com/rhinstaller/anaconda-webui/blob/main/.github/workflows/release.yml>`_,
|
||||||
|
which creates the upstream GitHub release.
|
||||||
|
|
||||||
|
To monitor the release progress and view logs:
|
||||||
|
|
||||||
|
- Visit the `Releases page <https://github.com/rhinstaller/anaconda-webui/releases>`_
|
||||||
|
- Click the tagged commit (7-character hash)
|
||||||
|
- Click the *Statuses* icon (✅, ❌, or 🟡) next to the commit subject
|
||||||
|
- Select a "propose-downstream" or "rpm-build" **Details** link to access the full run logs
|
||||||
|
|
||||||
|
Example logs page: ``https://github.com/rhinstaller/anaconda-webui/runs/...``
|
||||||
|
|
||||||
|
6. **Packit downstream packaging**
|
||||||
|
|
||||||
|
Packit will automatically open `Fedora dist-git pull requests <https://src.fedoraproject.org/rpms/anaconda-webui/pull-requests>`_
|
||||||
|
for Rawhide and supported Fedora releases.
|
||||||
|
|
||||||
|
Wait for the PRs to appear and pass:
|
||||||
|
|
||||||
|
- Scratch build checks
|
||||||
|
- Integration testing
|
||||||
|
|
||||||
|
Merge the PRs once they're ready.
|
||||||
|
|
||||||
|
Merging triggers:
|
||||||
|
|
||||||
|
- Koji builds
|
||||||
|
- Bodhi updates
|
||||||
|
|
||||||
|
Skipping the Koji Build
|
||||||
|
=======================
|
||||||
|
|
||||||
|
If you need to do a combined Bodhi update with another package (e.g. ``anaconda``) and want to
|
||||||
|
handle the Koji build and Bodhi update manually, add the ``skip-build`` label to the dist-git pull
|
||||||
|
request **before merging**. This prevents Packit from triggering the automatic Koji build.
|
||||||
|
|
||||||
|
You will then need to perform the Koji build and Bodhi update manually.
|
||||||
|
|
||||||
51
installer/anaconda-webui/vendor/docs/testing.rst
vendored
Normal file
51
installer/anaconda-webui/vendor/docs/testing.rst
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
Fix ISO image refreshes for the WebUI tests
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
In order to test the WebUI code we use the ISO images specified in `Cockpit's bots repository <https://github.com/cockpit-project/bots/tree/main/images>`_.
|
||||||
|
|
||||||
|
These ISO files are direct downloads from the official distribution server and their
|
||||||
|
`creation process is defined in bots repository <https://github.com/cockpit-project/bots/blob/main/images/scripts/fedora-rawhide-boot.bootstrap>`_.
|
||||||
|
|
||||||
|
The purpose of using these ISOs for the tests instead of downloading from the official server is to gate the image updates.
|
||||||
|
|
||||||
|
The default refresh period of the test images is one week, but they can also be `refreshed manually <https://github.com/cockpit-project/bots#refreshing-a-test-image>`_.
|
||||||
|
|
||||||
|
Image refreshes happen with `Pull Requests which update the current image SHA <https://github.com/cockpit-project/bots/pull/2981>`_.
|
||||||
|
The tests defined in `tests/` will run on the Pull request gating the `relevant to anaconda image refreshes <https://github.com/cockpit-project/bots/blob/main/lib/testmap.py>`_.
|
||||||
|
|
||||||
|
Image refreshes with successfull CI will be merged automagically from Cockpit team.
|
||||||
|
|
||||||
|
In the case an updated dependency makes the WebUI tests on the image refresh fail, the on-duty team
|
||||||
|
member is in charge of debugging the failure.
|
||||||
|
|
||||||
|
For this take the following steps:
|
||||||
|
|
||||||
|
* Locally checkout the bots repository branch used in the failing refresh PR. The path to the local bots checkout should be the `bots`.
|
||||||
|
* Create the test VM with the new image and debug by following the `WebUI test instructions <https://github.com/rhinstaller/anaconda-webui/tree/main/test#readme>`_
|
||||||
|
|
||||||
|
When the reason for the breackage is identified there are two options to go forward:
|
||||||
|
|
||||||
|
* If the failure comes from an intended change in behaviour adjust Anaconda or the tests
|
||||||
|
* If the failure uncovers an actual regression, file bugs for the given Fedora components. If it does not look like the issue will be
|
||||||
|
fixed quickly, work around in Anaconda or add a `naughty override file <https://github.com/cockpit-project/bots/tree/main/naughty/>`_, thus marking the expected failure pattern.
|
||||||
|
|
||||||
|
Fix a class of NPM errors that can break Web UI tests
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
This issue manifests as failed build of Anaconda RPMs due to NPM errors, that prevents Web UI tests to be started at all. This differs
|
||||||
|
from an actual bug in NPM dependency specification or NPM package maintainers going all "it works on my machine" again in two key aspects:
|
||||||
|
|
||||||
|
* the same NPM error suddenly breaks Web UI tests on *all* PRs
|
||||||
|
* when Web UI tests are run locally outside of Cockpit CI the build succeeds and the test are started
|
||||||
|
|
||||||
|
What you are seeing is breakage in NPM caching the Cockpit CI is using to avoid issues with the massive NPM consumption of the infra.
|
||||||
|
The mechanism it usually works fine, but sometimes the cache update mechanism can get stuck, resulting in the cache going stale causing
|
||||||
|
this issue to manifest.
|
||||||
|
|
||||||
|
How to fix:
|
||||||
|
|
||||||
|
* retry the test run on the PR - sometimes no all the builders are currently affected, retrying might run the test on different builder
|
||||||
|
* report the issue to the Cockpit team - on #cockpit on Libera Chat IRC or as a new issue in https://github.com/cockpit-project/cockpit/issues
|
||||||
|
|
||||||
|
In the end, someone from the Cockpit team will tell the builders in the Cockpit infra to drop their cache, fixing the issue or the affected
|
||||||
|
builder possibly gets cleaned up by some automated mechanism over time.
|
||||||
67
installer/anaconda-webui/vendor/extlinks.desktop
vendored
Normal file
67
installer/anaconda-webui/vendor/extlinks.desktop
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Name=Browser External
|
||||||
|
GenericName=Web Browser
|
||||||
|
GenericName[ca]=Navegador web
|
||||||
|
GenericName[cs]=Webový prohlížeč
|
||||||
|
GenericName[es]=Navegador web
|
||||||
|
GenericName[fa]=مرورگر اینترنتی
|
||||||
|
GenericName[fi]=WWW-selain
|
||||||
|
GenericName[fr]=Navigateur Web
|
||||||
|
GenericName[hu]=Webböngésző
|
||||||
|
GenericName[it]=Browser Web
|
||||||
|
GenericName[ja]=ウェブ・ブラウザ
|
||||||
|
GenericName[ko]=웹 브라우저
|
||||||
|
GenericName[nb]=Nettleser
|
||||||
|
GenericName[nl]=Webbrowser
|
||||||
|
GenericName[nn]=Nettlesar
|
||||||
|
GenericName[no]=Nettleser
|
||||||
|
GenericName[pl]=Przeglądarka WWW
|
||||||
|
GenericName[pt]=Navegador Web
|
||||||
|
GenericName[pt_BR]=Navegador Web
|
||||||
|
GenericName[sk]=Internetový prehliadač
|
||||||
|
GenericName[sv]=Webbläsare
|
||||||
|
Comment=Browse the Web
|
||||||
|
Comment[ca]=Navegueu per el web
|
||||||
|
Comment[cs]=Prohlížení stránek World Wide Webu
|
||||||
|
Comment[de]=Im Internet surfen
|
||||||
|
Comment[es]=Navegue por la web
|
||||||
|
Comment[fa]=صفحات شبکه جهانی اینترنت را مرور نمایید
|
||||||
|
Comment[fi]=Selaa Internetin WWW-sivuja
|
||||||
|
Comment[fr]=Navigue sur Internet
|
||||||
|
Comment[hu]=A világháló böngészése
|
||||||
|
Comment[it]=Esplora il web
|
||||||
|
Comment[ja]=ウェブを閲覧します
|
||||||
|
Comment[ko]=웹을 돌아 다닙니다
|
||||||
|
Comment[nb]=Surf på nettet
|
||||||
|
Comment[nl]=Verken het internet
|
||||||
|
Comment[nn]=Surf på nettet
|
||||||
|
Comment[no]=Surf på nettet
|
||||||
|
Comment[pl]=Przeglądanie stron WWW
|
||||||
|
Comment[pt]=Navegue na Internet
|
||||||
|
Comment[pt_BR]=Navegue na Internet
|
||||||
|
Comment[sk]=Prehliadanie internetu
|
||||||
|
Comment[sv]=Surfa på webben
|
||||||
|
TryExec=/usr/libexec/anaconda/browser-ext
|
||||||
|
Exec=/usr/libexec/anaconda/browser-ext %u
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
MimeType=x-scheme-handler/extlink;
|
||||||
|
StartupNotify=true
|
||||||
|
Categories=Network;WebBrowser;
|
||||||
|
Actions=new-window;new-private-window;profile-manager-window;
|
||||||
|
NoDisplay=true
|
||||||
|
X-Desktop-File-Install-Version=0.26
|
||||||
|
|
||||||
|
|
||||||
|
[Desktop Action new-window]
|
||||||
|
Name=Open a New Window
|
||||||
|
Exec=firefox -new-window
|
||||||
|
|
||||||
|
[Desktop Action new-private-window]
|
||||||
|
Name=Open a New Private Window
|
||||||
|
Exec=firefox -private-window
|
||||||
|
|
||||||
|
[Desktop Action profile-manager-window]
|
||||||
|
Name=Open Profile Manager
|
||||||
|
Exec=firefox -ProfileManager
|
||||||
12
installer/anaconda-webui/vendor/firefox-theme/default/chrome/userChrome.css
vendored
Normal file
12
installer/anaconda-webui/vendor/firefox-theme/default/chrome/userChrome.css
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
/* type selector "tab" is not standard CSS */
|
||||||
|
/* stylelint-disable selector-type-no-unknown */
|
||||||
|
|
||||||
|
/* Hide Firefox UI when there's just one tab */
|
||||||
|
#navigator-toolbox:has(tab:only-of-type) {
|
||||||
|
visibility: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide the close button */
|
||||||
|
.titlebar-close {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
57
installer/anaconda-webui/vendor/firefox-theme/default/user.js
vendored
Normal file
57
installer/anaconda-webui/vendor/firefox-theme/default/user.js
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* global user_pref */
|
||||||
|
// Let us use userChrome.css
|
||||||
|
user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true);
|
||||||
|
|
||||||
|
// We want to use :has()
|
||||||
|
user_pref("layout.css.has-selector.enabled", true);
|
||||||
|
|
||||||
|
// Dev stuff (for easier UI adusting with ctrl+shift+alt+i)
|
||||||
|
user_pref("browser.aboutConfig.showWarning", false);
|
||||||
|
user_pref("devtools.chrome.enabled", true);
|
||||||
|
user_pref("devtools.debugger.remote-enabled", true);
|
||||||
|
user_pref("devtools.inspector.showUserAgentStyles", true);
|
||||||
|
|
||||||
|
// Don't have any startup page
|
||||||
|
user_pref("browser.startup.page", 0);
|
||||||
|
user_pref("browser.startup.homepage", "about:blank");
|
||||||
|
user_pref("browser.startup.homepage_override.once", {});
|
||||||
|
|
||||||
|
// Hide the bookmarks
|
||||||
|
user_pref("browser.toolbars.bookmarks.visibility", "never");
|
||||||
|
|
||||||
|
// Don't show anything special in the new tab page'
|
||||||
|
user_pref("browser.newtabpage.activity-stream.default.sites", "");
|
||||||
|
user_pref("browser.newtabpage.activity-stream.showSponsored", false);
|
||||||
|
user_pref("browser.newtabpage.activity-stream.showSponsoredTopSites", false);
|
||||||
|
|
||||||
|
// Remove UI stuff
|
||||||
|
user_pref("browser.uiCustomization.state", "{\"placements\":{\"widget-overflow-fixed-list\":[],\"unified-extensions-area\":[],\"nav-bar\":[\"back-button\",\"forward-button\",\"stop-reload-button\",\"urlbar-container\",\"downloads-button\",\"unified-extensions-button\"],\"toolbar-menubar\":[\"menubar-items\"],\"TabsToolbar\":[\"tabbrowser-tabs\",\"alltabs-button\"],\"PersonalToolbar\":[\"personal-bookmarks\"]},\"seen\":[\"save-to-pocket-button\",\"developer-button\"],\"dirtyAreaCache\":[\"nav-bar\",\"TabsToolbar\",\"toolbar-menubar\",\"PersonalToolbar\"],\"currentVersion\":19,\"newElementCount\":3}");
|
||||||
|
|
||||||
|
// Don't set a placeholder for the tab before the page loads and sets title
|
||||||
|
user_pref("browser.urlbar.placeholderName", "");
|
||||||
|
|
||||||
|
// Turn off calling home
|
||||||
|
user_pref("app.normandy.enabled", false);
|
||||||
|
user_pref("browser.discovery.enabled", false);
|
||||||
|
user_pref("datareporting.healthreport.uploadEnabled", false);
|
||||||
|
user_pref("datareporting.policy.dataSubmissionEnabled", false);
|
||||||
|
user_pref("toolkit.telemetry.unified", false);
|
||||||
|
user_pref("trailhead.firstrun.didSeeAboutWelcome", true);
|
||||||
|
|
||||||
|
// Don't show `Allow this site to open the PROTOCOL link with APPLICATION` dialog
|
||||||
|
user_pref("network.protocol-handler.warn-external-default", false);
|
||||||
|
user_pref("network.protocol-handler.external.anaconda-gnome-control-center", true);
|
||||||
|
user_pref("network.protocol-handler.external.extlink", true);
|
||||||
|
|
||||||
|
// Don't show the `This site is trying to open a popup` dialog
|
||||||
|
user_pref("dom.disable_open_during_load", false);
|
||||||
|
|
||||||
|
// Disable password manager popups
|
||||||
|
user_pref("signon.showAutoCompleteFooter", false);
|
||||||
|
user_pref("signon.rememberSignons", false);
|
||||||
|
|
||||||
|
// Disable the translate popup
|
||||||
|
user_pref("browser.translations.enable", false);
|
||||||
|
|
||||||
|
// Don't auto-hide the toolbar in fullscreen
|
||||||
|
user_pref("browser.fullscreen.autohide", false);
|
||||||
9
installer/anaconda-webui/vendor/firefox-theme/extlink/user.js
vendored
Normal file
9
installer/anaconda-webui/vendor/firefox-theme/extlink/user.js
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
/* global user_pref */
|
||||||
|
// Don't have any startup page
|
||||||
|
user_pref("browser.startup.page", 0);
|
||||||
|
user_pref("browser.startup.homepage", "about:blank");
|
||||||
|
user_pref("browser.startup.homepage_override.once", {});
|
||||||
|
|
||||||
|
// Disable password manager popups
|
||||||
|
user_pref("signon.showAutoCompleteFooter", false);
|
||||||
|
user_pref("signon.rememberSignons", false);
|
||||||
12
installer/anaconda-webui/vendor/firefox-theme/live/chrome/userChrome.css
vendored
Normal file
12
installer/anaconda-webui/vendor/firefox-theme/live/chrome/userChrome.css
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
/* type selector "tab" is not standard CSS */
|
||||||
|
/* stylelint-disable selector-type-no-unknown */
|
||||||
|
|
||||||
|
/* Hide Firefox UI when there's just one tab */
|
||||||
|
#navigator-toolbox:has(tab:only-of-type) {
|
||||||
|
visibility: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide the close button */
|
||||||
|
.titlebar-close {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
54
installer/anaconda-webui/vendor/firefox-theme/live/user.js
vendored
Normal file
54
installer/anaconda-webui/vendor/firefox-theme/live/user.js
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
/* global user_pref */
|
||||||
|
// Let us use userChrome.css
|
||||||
|
user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true);
|
||||||
|
|
||||||
|
// We want to use :has()
|
||||||
|
user_pref("layout.css.has-selector.enabled", true);
|
||||||
|
|
||||||
|
// Dev stuff (for easier UI adusting with ctrl+shift+alt+i)
|
||||||
|
user_pref("browser.aboutConfig.showWarning", false);
|
||||||
|
user_pref("devtools.chrome.enabled", true);
|
||||||
|
user_pref("devtools.debugger.remote-enabled", true);
|
||||||
|
user_pref("devtools.inspector.showUserAgentStyles", true);
|
||||||
|
|
||||||
|
// Don't have any startup page
|
||||||
|
user_pref("browser.startup.page", 0);
|
||||||
|
user_pref("browser.startup.homepage", "about:blank");
|
||||||
|
user_pref("browser.startup.homepage_override.once", "");
|
||||||
|
|
||||||
|
// Hide the bookmarks
|
||||||
|
user_pref("browser.toolbars.bookmarks.visibility", "never");
|
||||||
|
|
||||||
|
// Don't show anything special in the new tab page'
|
||||||
|
user_pref("browser.newtabpage.activity-stream.default.sites", "");
|
||||||
|
user_pref("browser.newtabpage.activity-stream.showSponsored", false);
|
||||||
|
user_pref("browser.newtabpage.activity-stream.showSponsoredTopSites", false);
|
||||||
|
|
||||||
|
// Remove UI stuff
|
||||||
|
user_pref("browser.uiCustomization.state", "{\"placements\":{\"widget-overflow-fixed-list\":[],\"unified-extensions-area\":[],\"nav-bar\":[\"back-button\",\"forward-button\",\"stop-reload-button\",\"urlbar-container\",\"downloads-button\",\"unified-extensions-button\"],\"toolbar-menubar\":[\"menubar-items\"],\"TabsToolbar\":[\"tabbrowser-tabs\",\"alltabs-button\"],\"PersonalToolbar\":[\"personal-bookmarks\"]},\"seen\":[\"save-to-pocket-button\",\"developer-button\"],\"dirtyAreaCache\":[\"nav-bar\",\"TabsToolbar\",\"toolbar-menubar\",\"PersonalToolbar\"],\"currentVersion\":19,\"newElementCount\":3}");
|
||||||
|
|
||||||
|
// Don't set a placeholder for the tab before the page loads and sets title
|
||||||
|
user_pref("browser.urlbar.placeholderName", "");
|
||||||
|
|
||||||
|
// Turn off calling home
|
||||||
|
user_pref("app.normandy.enabled", false);
|
||||||
|
user_pref("browser.discovery.enabled", false);
|
||||||
|
user_pref("datareporting.healthreport.uploadEnabled", false);
|
||||||
|
user_pref("datareporting.policy.dataSubmissionEnabled", false);
|
||||||
|
user_pref("toolkit.telemetry.unified", false);
|
||||||
|
user_pref("trailhead.firstrun.didSeeAboutWelcome", true);
|
||||||
|
|
||||||
|
// Don't show `Allow this site to open the PROTOCOL link with APPLICATION` dialog
|
||||||
|
user_pref("network.protocol-handler.warn-external-default", false);
|
||||||
|
user_pref("network.protocol-handler.external.anaconda-gnome-control-center", true);
|
||||||
|
user_pref("network.protocol-handler.external.extlink", true);
|
||||||
|
|
||||||
|
// Don't show the `This site is trying to open a popup` dialog
|
||||||
|
user_pref("dom.disable_open_during_load", false);
|
||||||
|
|
||||||
|
// Disable password manager popups
|
||||||
|
user_pref("signon.showAutoCompleteFooter", false);
|
||||||
|
user_pref("signon.rememberSignons", false);
|
||||||
|
|
||||||
|
// Disable the translate popup
|
||||||
|
user_pref("browser.translations.enable", false);
|
||||||
32
installer/anaconda-webui/vendor/gnome-control-center-ext
vendored
Executable file
32
installer/anaconda-webui/vendor/gnome-control-center-ext
vendored
Executable file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Copyright (C) 2025 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License as published
|
||||||
|
# by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Panel name argument is mandatory
|
||||||
|
if [[ -z "$1" ]]; then
|
||||||
|
echo "Error: Panel name argument is required" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PANEL="$1"
|
||||||
|
|
||||||
|
# Use gapplication to activate existing instance (brings window to front if running)
|
||||||
|
# This handles single-instance app activation automatically
|
||||||
|
gapplication launch org.gnome.Settings 2>/dev/null
|
||||||
|
|
||||||
|
# Launch with panel argument
|
||||||
|
# Since gnome-control-center is single-instance, this will switch panels in existing window
|
||||||
|
exec /usr/bin/gnome-control-center "$PANEL"
|
||||||
926
installer/anaconda-webui/vendor/images/qr-code-feedback.svg
vendored
Normal file
926
installer/anaconda-webui/vendor/images/qr-code-feedback.svg
vendored
Normal file
|
|
@ -0,0 +1,926 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="2257px" height="2257px" viewBox="0 0 2257 2257" enable-background="new 0 0 2257 2257" xml:space="preserve">
|
||||||
|
<rect x="0" y="0" width="2257" height="2257" fill="rgb(255,255,255)" /><g transform="translate(122,122)"><g transform="translate(488,0) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(549,0) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,0) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,0) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,0) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,0) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,0) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,0) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,0) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,0) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,61) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(549,61) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,61) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(854,61) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,61) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,61) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,61) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,61) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,122) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,122) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,122) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(854,122) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,122) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,122) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,122) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,122) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,122) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,122) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,183) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,183) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,183) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,183) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,183) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(549,244) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,244) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,244) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,244) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,244) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,244) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,244) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(549,305) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,305) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,305) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,305) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(854,305) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,305) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,305) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,305) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,305) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,305) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,305) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,366) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,366) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,366) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(854,366) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,366) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,366) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,366) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,366) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,366) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,427) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,427) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,427) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,427) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,427) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,427) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,427) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,427) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,427) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,427) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(0,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(122,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(183,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(305,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(366,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(427,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(854,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1891,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,488) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(244,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(305,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(427,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1830,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,549) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(0,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(122,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(183,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(305,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(366,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(549,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1891,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,610) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(61,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(122,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(244,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1891,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,671) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(0,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(61,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(244,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(366,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(549,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(854,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1525,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1891,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,732) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(61,793) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(122,793) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(183,793) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,793) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,793) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,793) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(854,793) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,793) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,793) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,793) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1830,793) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1891,793) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(61,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(122,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(183,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(305,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(366,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(427,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1830,854) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(305,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(549,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1525,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1830,915) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(122,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(244,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(366,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(427,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(854,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1525,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1830,976) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(61,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(244,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(305,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1525,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1891,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,1037) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(61,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(305,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(366,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(427,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1525,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1830,1098) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(0,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(122,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(183,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1525,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,1159) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(0,1220) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(61,1220) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(366,1220) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(427,1220) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,1220) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,1220) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,1220) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,1220) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1525,1220) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,1220) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,1220) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1830,1220) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(0,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(122,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(183,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(305,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(427,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1830,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,1281) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(183,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(244,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(305,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(366,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(427,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1525,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1830,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1891,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,1342) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(61,1403) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(183,1403) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(244,1403) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,1403) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,1403) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,1403) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,1403) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,1403) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1525,1403) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,1403) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,1403) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(0,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(122,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(366,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1525,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,1464) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,1525) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,1525) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,1525) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(854,1525) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,1525) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,1525) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,1525) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,1525) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,1525) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,1525) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,1525) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1891,1525) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,1586) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,1586) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,1586) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,1586) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,1586) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,1586) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,1586) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,1586) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,1586) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,1586) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,1586) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,1586) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(549,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1830,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1891,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,1647) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(549,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1525,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1830,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,1708) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(549,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(732,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(854,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,1769) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,1830) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(610,1830) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,1830) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(854,1830) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(915,1830) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(976,1830) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,1830) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,1830) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,1830) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,1830) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,1830) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,1830) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(549,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(671,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(854,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1220,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1342,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1464,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1525,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1647,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1769,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1952,1891) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(488,1952) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(793,1952) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1037,1952) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1098,1952) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1159,1952) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1281,1952) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1403,1952) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1586,1952) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,1952) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1830,1952) scale(0.61,0.61)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(0,0) scale(4.27, 4.27)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<g>
|
||||||
|
<rect x="15" y="15" style="fill:none;" width="70" height="70"/>
|
||||||
|
<path d="M85,0H15H0v15v70v15h15h70h15V85V15V0H85z M85,85H15V15h70V85z"/>
|
||||||
|
</g>
|
||||||
|
</g></g><g transform="translate(1586,0) scale(4.27, 4.27)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<g>
|
||||||
|
<rect x="15" y="15" style="fill:none;" width="70" height="70"/>
|
||||||
|
<path d="M85,0H15H0v15v70v15h15h70h15V85V15V0H85z M85,85H15V15h70V85z"/>
|
||||||
|
</g>
|
||||||
|
</g></g><g transform="translate(0,1586) scale(4.27, 4.27)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<g>
|
||||||
|
<rect x="15" y="15" style="fill:none;" width="70" height="70"/>
|
||||||
|
<path d="M85,0H15H0v15v70v15h15h70h15V85V15V0H85z M85,85H15V15h70V85z"/>
|
||||||
|
</g>
|
||||||
|
</g></g><g transform="translate(122,122) scale(1.83, 1.83)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(1708,122) scale(1.83, 1.83)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g><g transform="translate(122,1708) scale(1.83, 1.83)"><g transform="" style="fill: rgb(0, 0, 0);">
|
||||||
|
<rect width="100" height="100"/>
|
||||||
|
</g></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 62 KiB |
55
installer/anaconda-webui/vendor/package.json
vendored
Normal file
55
installer/anaconda-webui/vendor/package.json
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"name": "anaconda-webui",
|
||||||
|
"description": "Scaffolding for a cockpit module",
|
||||||
|
"type": "module",
|
||||||
|
"main": "index.js",
|
||||||
|
"author": "",
|
||||||
|
"license": "LGPL-2.1",
|
||||||
|
"scripts": {
|
||||||
|
"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/",
|
||||||
|
"stylelint": "stylelint 'src/**/*{.css,scss}'",
|
||||||
|
"stylelint:fix": "stylelint --fix 'src/**/*{.css,scss}'"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"esbuild": "0.28.2",
|
||||||
|
"esbuild-plugin-copy": "2.1.1",
|
||||||
|
"esbuild-sass-plugin": "3.7.0",
|
||||||
|
"eslint": "8.57.0",
|
||||||
|
"eslint-config-standard": "17.1.0",
|
||||||
|
"eslint-config-standard-jsx": "11.0.0",
|
||||||
|
"eslint-config-standard-react": "13.0.0",
|
||||||
|
"eslint-plugin-import": "2.31.0",
|
||||||
|
"eslint-plugin-jsx-a11y": "6.10.2",
|
||||||
|
"eslint-plugin-node": "11.1.0",
|
||||||
|
"eslint-plugin-promise": "6.6.0",
|
||||||
|
"eslint-plugin-react": "7.37.5",
|
||||||
|
"eslint-plugin-react-hooks": "4.6.2",
|
||||||
|
"eslint-plugin-simple-import-sort": "^12.0.0",
|
||||||
|
"eslint-plugin-sort-destructure-keys": "^2.0.0",
|
||||||
|
"gettext-parser": "8.0.0",
|
||||||
|
"glob": "13.0.1",
|
||||||
|
"htmlparser": "1.7.7",
|
||||||
|
"jed": "1.1.1",
|
||||||
|
"qunit": "2.25.0",
|
||||||
|
"sass": "1.98.0",
|
||||||
|
"stdio": "2.1.3",
|
||||||
|
"stylelint": "17.14.1",
|
||||||
|
"stylelint-config-recommended-scss": "17.0.1",
|
||||||
|
"stylelint-config-standard-scss": "17.0.0",
|
||||||
|
"stylelint-formatter-pretty": "4.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@patternfly/patternfly": "6.6.1",
|
||||||
|
"@patternfly/react-core": "6.6.1",
|
||||||
|
"@patternfly/react-table": "6.6.1",
|
||||||
|
"dequal": "2.0.3",
|
||||||
|
"luxon": "^3.6.1",
|
||||||
|
"react": "18.3.1",
|
||||||
|
"react-dom": "18.3.1",
|
||||||
|
"stacktrace-js": "2.0.2",
|
||||||
|
"throttle-debounce": "5.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
120
installer/anaconda-webui/vendor/packaging/anaconda-webui.spec.in
vendored
Normal file
120
installer/anaconda-webui/vendor/packaging/anaconda-webui.spec.in
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
Name: anaconda-webui
|
||||||
|
Version: %{VERSION}
|
||||||
|
Release: 1%{?dist}
|
||||||
|
Summary: Anaconda installer Web interface
|
||||||
|
License: LGPL-2.1-or-later AND MIT
|
||||||
|
URL: https://github.com/rhinstaller/%{name}
|
||||||
|
|
||||||
|
Source0: https://github.com/rhinstaller/%{name}/releases/download/%{version}/%{name}-%{version}.tar.xz
|
||||||
|
BuildArch: noarch
|
||||||
|
BuildRequires: libappstream-glib
|
||||||
|
BuildRequires: make
|
||||||
|
BuildRequires: gettext
|
||||||
|
# Needed for the unitdir macro
|
||||||
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
|
%global anacondacorever 0
|
||||||
|
|
||||||
|
%if 0%{?fedora} > 41
|
||||||
|
%global anacondacorever 42.5
|
||||||
|
%endif
|
||||||
|
|
||||||
|
%global webui_slitherer_spin_identities fedora-release-identity-budgie or fedora-release-identity-cinnamon or fedora-release-identity-kde-desktop or fedora-release-identity-kde-mobile or fedora-release-identity-lxde or fedora-release-identity-lxqt or fedora-release-identity-matecompiz or fedora-release-identity-soas or fedora-release-identity-sway or fedora-release-identity-xfce or fedora-release-identity-i3
|
||||||
|
|
||||||
|
%global cockpitver 275
|
||||||
|
|
||||||
|
%define _unitdir /usr/lib/systemd/system
|
||||||
|
|
||||||
|
Requires: cockpit-storaged
|
||||||
|
Requires: cockpit-networkmanager
|
||||||
|
Requires: cockpit-bridge >= %{cockpitver}
|
||||||
|
Requires: cockpit-ws >= %{cockpitver}
|
||||||
|
Requires: anaconda-core >= %{anacondacorever}
|
||||||
|
Requires: python3-bugzilla
|
||||||
|
# Firefox dependency needs to be specified there as cockpit web-view does not have a hard dependency on Firefox as
|
||||||
|
# it can often fall back to a diferent browser. This does not work in the limited installer
|
||||||
|
# environment, so we need to make sure Firefox is available. Exclude on RHEL, only Flatpak version will be there.
|
||||||
|
%if ! 0%{?rhel}
|
||||||
|
Requires: (slitherer if (%{webui_slitherer_spin_identities}) else firefox)
|
||||||
|
%endif
|
||||||
|
%if 0%{?fedora}
|
||||||
|
Requires: system-logos
|
||||||
|
%endif
|
||||||
|
BuildRequires: desktop-file-utils
|
||||||
|
|
||||||
|
%{NPM_PROVIDES}
|
||||||
|
|
||||||
|
%description
|
||||||
|
Anaconda installer Web interface
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup -q -n %{name}
|
||||||
|
|
||||||
|
%build
|
||||||
|
# Nothing to build
|
||||||
|
|
||||||
|
%install
|
||||||
|
%make_install PREFIX=/usr
|
||||||
|
|
||||||
|
desktop-file-install --dir=%{buildroot}%{_datadir}/applications %{buildroot}%{_datadir}/applications/extlinks.desktop
|
||||||
|
desktop-file-install --dir=%{buildroot}%{_datadir}/applications %{buildroot}%{_datadir}/applications/anaconda-gnome-control-center.desktop
|
||||||
|
|
||||||
|
%check
|
||||||
|
exit 0
|
||||||
|
# We have some integration tests, but those require running a VM, so that would
|
||||||
|
# be an overkill for RPM check script.
|
||||||
|
|
||||||
|
%files
|
||||||
|
%dir %{_datadir}/cockpit/anaconda-webui
|
||||||
|
%doc README.rst
|
||||||
|
%license LICENSE dist/index.js.LEGAL.txt dist/anacondaLogin.js.LEGAL.txt
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/login.html
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/anacondaLogin.js
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/anacondaLogin.js.map
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/anacondaLogin.js.LEGAL.txt
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/anacondaLogin.css
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/anacondaLogin.css.map
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/qr-code-feedback.svg
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/index.js.LEGAL.txt
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/index.html
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/index.js.gz
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/index.js.map
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/index.css.gz
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/index.css.map
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/manifest.json
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/po.*.js.gz
|
||||||
|
%{_datadir}/cockpit/anaconda-webui/VERSION.txt
|
||||||
|
%{_libexecdir}/anaconda/cockpit-coproc-wrapper.sh
|
||||||
|
%{_libexecdir}/anaconda/anaconda-cockpit-conf-merge
|
||||||
|
%dir /etc/anaconda/cockpit
|
||||||
|
%config(noreplace) /etc/anaconda/cockpit/cockpit.conf
|
||||||
|
%dir /etc/anaconda/cockpit/conf.d
|
||||||
|
%dir %{_datadir}/anaconda/cockpit
|
||||||
|
%dir %{_datadir}/anaconda/cockpit/conf.d
|
||||||
|
%{_datadir}/anaconda/cockpit/conf.d/50-remote-auth.conf
|
||||||
|
%{_libexecdir}/anaconda/cockpit-pin-auth
|
||||||
|
%dir %{_datadir}/anaconda/firefox-theme
|
||||||
|
%dir %{_datadir}/anaconda/firefox-theme/default
|
||||||
|
%dir %{_datadir}/anaconda/firefox-theme/default/chrome
|
||||||
|
%{_datadir}/anaconda/firefox-theme/default/user.js
|
||||||
|
%{_datadir}/anaconda/firefox-theme/default/chrome/userChrome.css
|
||||||
|
%dir %{_datadir}/anaconda/firefox-theme/live
|
||||||
|
%dir %{_datadir}/anaconda/firefox-theme/live/chrome
|
||||||
|
%{_datadir}/anaconda/firefox-theme/live/user.js
|
||||||
|
%{_datadir}/anaconda/firefox-theme/live/chrome/userChrome.css
|
||||||
|
%dir %{_datadir}/anaconda/firefox-theme/extlink
|
||||||
|
%{_datadir}/anaconda/firefox-theme/extlink/user.js
|
||||||
|
%{_libexecdir}/anaconda/webui-desktop
|
||||||
|
%{_libexecdir}/anaconda/browser-ext
|
||||||
|
%{_libexecdir}/anaconda/gnome-control-center-ext
|
||||||
|
%{_datadir}/applications/extlinks.desktop
|
||||||
|
%{_datadir}/applications/anaconda-gnome-control-center.desktop
|
||||||
|
%{_unitdir}/webui-cockpit-ws.service
|
||||||
|
%{_unitdir}/cockpit-pin-auth.socket
|
||||||
|
%{_unitdir}/cockpit-pin-auth@.service
|
||||||
|
%{_unitdir}/webui-tls-proxy.service
|
||||||
|
%{_libexecdir}/anaconda/webui-tls-proxy
|
||||||
|
|
||||||
|
|
||||||
|
# The changelog is automatically generated and merged
|
||||||
|
%changelog
|
||||||
53
installer/anaconda-webui/vendor/packit.yaml
vendored
Normal file
53
installer/anaconda-webui/vendor/packit.yaml
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
upstream_project_url: https://github.com/rhinstaller/anaconda-webui
|
||||||
|
# enable notification of failed downstream jobs as issues
|
||||||
|
issue_repository: https://github.com/rhinstaller/anaconda-webui
|
||||||
|
specfile_path: anaconda-webui.spec
|
||||||
|
upstream_package_name: anaconda-webui
|
||||||
|
downstream_package_name: anaconda-webui
|
||||||
|
# use the nicely formatted release description from our upstream release, instead of git shortlog
|
||||||
|
copy_upstream_release_description: true
|
||||||
|
|
||||||
|
srpm_build_deps:
|
||||||
|
- npm
|
||||||
|
- make
|
||||||
|
|
||||||
|
actions:
|
||||||
|
post-upstream-clone: make anaconda-webui.spec
|
||||||
|
create-archive: make dist
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- job: tests
|
||||||
|
trigger: pull_request
|
||||||
|
targets:
|
||||||
|
- fedora-development
|
||||||
|
|
||||||
|
- job: copr_build
|
||||||
|
trigger: pull_request
|
||||||
|
targets:
|
||||||
|
- fedora-development
|
||||||
|
|
||||||
|
# for cross-project testing
|
||||||
|
- job: copr_build
|
||||||
|
trigger: commit
|
||||||
|
owner: "@rhinstaller"
|
||||||
|
project: "Anaconda-webui"
|
||||||
|
preserve_project: True
|
||||||
|
|
||||||
|
- job: propose_downstream
|
||||||
|
trigger: release
|
||||||
|
dist_git_branches:
|
||||||
|
- fedora-development
|
||||||
|
|
||||||
|
- job: koji_build
|
||||||
|
trigger: commit
|
||||||
|
require:
|
||||||
|
label:
|
||||||
|
absent:
|
||||||
|
- skip-build
|
||||||
|
dist_git_branches:
|
||||||
|
- fedora-development
|
||||||
|
|
||||||
|
- job: bodhi_update
|
||||||
|
trigger: koji_build
|
||||||
|
dist_git_branches:
|
||||||
|
- fedora-development
|
||||||
0
installer/anaconda-webui/vendor/po/.placeholder
vendored
Normal file
0
installer/anaconda-webui/vendor/po/.placeholder
vendored
Normal file
6432
installer/anaconda-webui/vendor/po/af.po
vendored
Normal file
6432
installer/anaconda-webui/vendor/po/af.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
1867
installer/anaconda-webui/vendor/po/am.po
vendored
Normal file
1867
installer/anaconda-webui/vendor/po/am.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
5439
installer/anaconda-webui/vendor/po/ar.po
vendored
Normal file
5439
installer/anaconda-webui/vendor/po/ar.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
6333
installer/anaconda-webui/vendor/po/as.po
vendored
Normal file
6333
installer/anaconda-webui/vendor/po/as.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
3894
installer/anaconda-webui/vendor/po/ast.po
vendored
Normal file
3894
installer/anaconda-webui/vendor/po/ast.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8336
installer/anaconda-webui/vendor/po/be.po
vendored
Normal file
8336
installer/anaconda-webui/vendor/po/be.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
7477
installer/anaconda-webui/vendor/po/bg.po
vendored
Normal file
7477
installer/anaconda-webui/vendor/po/bg.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
3053
installer/anaconda-webui/vendor/po/bn.po
vendored
Normal file
3053
installer/anaconda-webui/vendor/po/bn.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
6576
installer/anaconda-webui/vendor/po/bn_IN.po
vendored
Normal file
6576
installer/anaconda-webui/vendor/po/bn_IN.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8307
installer/anaconda-webui/vendor/po/ca.po
vendored
Normal file
8307
installer/anaconda-webui/vendor/po/ca.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8608
installer/anaconda-webui/vendor/po/cs.po
vendored
Normal file
8608
installer/anaconda-webui/vendor/po/cs.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
1901
installer/anaconda-webui/vendor/po/cy.po
vendored
Normal file
1901
installer/anaconda-webui/vendor/po/cy.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8478
installer/anaconda-webui/vendor/po/da.po
vendored
Normal file
8478
installer/anaconda-webui/vendor/po/da.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8725
installer/anaconda-webui/vendor/po/de.po
vendored
Normal file
8725
installer/anaconda-webui/vendor/po/de.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
2935
installer/anaconda-webui/vendor/po/el.po
vendored
Normal file
2935
installer/anaconda-webui/vendor/po/el.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
2777
installer/anaconda-webui/vendor/po/en_GB.po
vendored
Normal file
2777
installer/anaconda-webui/vendor/po/en_GB.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
7469
installer/anaconda-webui/vendor/po/eo.po
vendored
Normal file
7469
installer/anaconda-webui/vendor/po/eo.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8702
installer/anaconda-webui/vendor/po/es.po
vendored
Normal file
8702
installer/anaconda-webui/vendor/po/es.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
2735
installer/anaconda-webui/vendor/po/et.po
vendored
Normal file
2735
installer/anaconda-webui/vendor/po/et.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
3788
installer/anaconda-webui/vendor/po/eu.po
vendored
Normal file
3788
installer/anaconda-webui/vendor/po/eu.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
2880
installer/anaconda-webui/vendor/po/fa.po
vendored
Normal file
2880
installer/anaconda-webui/vendor/po/fa.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8638
installer/anaconda-webui/vendor/po/fi.po
vendored
Normal file
8638
installer/anaconda-webui/vendor/po/fi.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
2505
installer/anaconda-webui/vendor/po/fil.po
vendored
Normal file
2505
installer/anaconda-webui/vendor/po/fil.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8909
installer/anaconda-webui/vendor/po/fr.po
vendored
Normal file
8909
installer/anaconda-webui/vendor/po/fr.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8750
installer/anaconda-webui/vendor/po/fur.po
vendored
Normal file
8750
installer/anaconda-webui/vendor/po/fur.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
3943
installer/anaconda-webui/vendor/po/fy.po
vendored
Normal file
3943
installer/anaconda-webui/vendor/po/fy.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
3118
installer/anaconda-webui/vendor/po/ga.po
vendored
Normal file
3118
installer/anaconda-webui/vendor/po/ga.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
3472
installer/anaconda-webui/vendor/po/gl.po
vendored
Normal file
3472
installer/anaconda-webui/vendor/po/gl.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
5741
installer/anaconda-webui/vendor/po/gu.po
vendored
Normal file
5741
installer/anaconda-webui/vendor/po/gu.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8385
installer/anaconda-webui/vendor/po/he.po
vendored
Normal file
8385
installer/anaconda-webui/vendor/po/he.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
4191
installer/anaconda-webui/vendor/po/hi.po
vendored
Normal file
4191
installer/anaconda-webui/vendor/po/hi.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8510
installer/anaconda-webui/vendor/po/hr.po
vendored
Normal file
8510
installer/anaconda-webui/vendor/po/hr.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8647
installer/anaconda-webui/vendor/po/hu.po
vendored
Normal file
8647
installer/anaconda-webui/vendor/po/hu.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
5022
installer/anaconda-webui/vendor/po/ia.po
vendored
Normal file
5022
installer/anaconda-webui/vendor/po/ia.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8675
installer/anaconda-webui/vendor/po/id.po
vendored
Normal file
8675
installer/anaconda-webui/vendor/po/id.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
2100
installer/anaconda-webui/vendor/po/is.po
vendored
Normal file
2100
installer/anaconda-webui/vendor/po/is.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8842
installer/anaconda-webui/vendor/po/it.po
vendored
Normal file
8842
installer/anaconda-webui/vendor/po/it.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8660
installer/anaconda-webui/vendor/po/ja.po
vendored
Normal file
8660
installer/anaconda-webui/vendor/po/ja.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8043
installer/anaconda-webui/vendor/po/ka.po
vendored
Normal file
8043
installer/anaconda-webui/vendor/po/ka.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
1871
installer/anaconda-webui/vendor/po/kab.po
vendored
Normal file
1871
installer/anaconda-webui/vendor/po/kab.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8496
installer/anaconda-webui/vendor/po/kk.po
vendored
Normal file
8496
installer/anaconda-webui/vendor/po/kk.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
3600
installer/anaconda-webui/vendor/po/km.po
vendored
Normal file
3600
installer/anaconda-webui/vendor/po/km.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
5875
installer/anaconda-webui/vendor/po/kn.po
vendored
Normal file
5875
installer/anaconda-webui/vendor/po/kn.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
8521
installer/anaconda-webui/vendor/po/ko.po
vendored
Normal file
8521
installer/anaconda-webui/vendor/po/ko.po
vendored
Normal file
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue