Skip to content

Commit

Permalink
Merge branch 'konveyor:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
wise-king-sullyman authored Mar 18, 2024
2 parents 591ff00 + 161fba8 commit 42c467c
Show file tree
Hide file tree
Showing 65 changed files with 970 additions and 444 deletions.
1 change: 1 addition & 0 deletions .github/workflows/image-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
image_name: "tackle2-ui"
containerfile: "./Dockerfile"
architectures: '[ "amd64", "arm64", "ppc64le", "s390x" ]'
extra-args: "--ulimit nofile=4096:4096"
secrets:
registry_username: ${{ secrets.QUAY_PUBLISH_ROBOT }}
registry_password: ${{ secrets.QUAY_PUBLISH_TOKEN }}
1 change: 1 addition & 0 deletions .github/workflows/pr-closed.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ jobs:
pull-requests: write
contents: write
if: github.event.pull_request.merged == true
secrets: inherit
uses: konveyor/release-tools/.github/workflows/cherry-pick.yml@main
151 changes: 151 additions & 0 deletions BRANDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Branding

The UI supports static branding at build time. Dynamically switching brands is not
possible with the current implementation.

## Summary

Each of the project modules need to do some branding enablement.

- `@konveyor-ui/common` pulls in the branding assets and packages the configuration,
strings and assets within the common package. The other modules pull branding
from the common module.

- `@konveyor-ui/client` uses branding from the common package:

- The location of `favicon.ico`, `manifest.json` and any other branding
assets that may be referenced in the `brandingStrings` are sourced from the
common package.

- The `brandingStrings` are used by the dev-server runtime, to fill out the
`index.html` template.

- The about modal and application masthead components use the branding strings
provided by the common module to display brand appropriate logos, titles and
about information. Since the common module provides all the information, it
is packaged directly into the app at build time.

- `@konveyor-ui/server` uses the `brandingStrings` from the common package to fill
out the `index.html` template.

## Providing alternate branding

To provide an alternate branding to the build, specify the path to the branding assets
with the `BRANDING` environment variable. Relative paths in `BRANDING` are computed
from the project source root.

Each brand requires the presence of at least the following files:

- `strings.json`
- `favicon.ico`
- `manifest.json`

With a file path of `/alt/custom-branding`, a build that uses an alternate branding
is run as:

```sh
> BRANDING=/alt/custom-branding npm run build
```

The dev server can also be run this way. Since file watching of the branding assets
is not implemented in the common module's build watch mode, it may be necessary to
manually build the common module before running the dev server. When working on a
brand, it is useful to run the dev server like this:

```sh
> export BRANDING=/alt/custom-branding
> npm run build -w common
> npm run start:dev
> unset BRANDING # when you don't want to use the custom branding path anymore
```

### File details

#### strings.json

The expected shape of `strings.json` is defined in [branding.ts](./common/src/branding.ts).

The default version of the file is [branding/strings.json](./branding/strings.json).

A minimal viable example of the file is:

```json
{
"application": {
"title": "Konveyor"
},
"about": {
"displayName": "Konveyor"
},
"masthead": {}
}
```

At build time, the json file is processed as an [ejs](https://ejs.co/) template. The
variable `brandingRoot` is provided as the relative root of the branding
assets within the build of the common module. Consider the location of `strings.json`
in your branding directory as the base `brandingRoot` when creating a new brand.

For example, to properly reference a logo within this branding structure:

```
special-brand/
images/
masthead-logo.svg
about-logo.svg
strings.json
```

Use a url string like this:

```json
{
"about": {
"imageSrc": "<%= brandingRoot %>/images/about-logo.svg"
}
}
```

and in the output of `BRANDING=special-brand npm run build -w common`, the `imageSrc`
will be `branding/images/about-logo.svg` with all of the files in `special-branding/*`
copied to and available to the client and server modules from
`@konveyor-ui/common/branding/*`.

#### favicon.ico

A standard favorite icon file `favicon.ico` is required to be in the same directory
as `strings.json`

#### manifest.json

A standard [web app manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest)
file `manifest.json` is required to be in the same directory as `strings.json`.

## Technical details

All branding strings and assets are pulled in to the common module. The client and
server modules access the branding from the common module build.

The `common` module relies on rollup packaging to embed all of the brand for easy
use. The use of branding strings in `client` and `server` modules is straight forward.
Pulling in `strings.json` and providing the base path to the brand assets is a
more complicated.

The `common` module provides the `brandingAssetPath()` function to let the build time
code find the root path to all brand assets. Webpack configuration files use this
function to source the favicon.ico, manifest.json and other brand assets to be copied
to the application bundle.

The `brandingStrings` is typed and sourced from a json file. To pass typescript builds,
a stub json file needs to be available at transpile time. By using a typescript paths
of `@branding/strings.json`, the stub json is found at transpile time. The generated
javascript will still import the path alias. The
[virtual rollup plugin](https://github.com/rollup/plugins/tree/master/packages/virtual)
further transform the javascript output by replacing the `@branding/strings.json` import
with a dynamically built module containing the contents of the brand's `strings.json`.
The brand json becomes a virtual module embedded in the common module.

A build for a custom brand will fail (1) if the expected files cannot be read, or (2)
if `strings.json` is not a valid JSON file. **Note:** The context of `stings.json` is
not currently validated. If something is missing or a url is malformed, it will only
be visible as a runtime error.
8 changes: 4 additions & 4 deletions INTERNATIONALIZATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ locales: ["en", "es", "myLanguageCode"]
npm extract
```

The previous command created a file `public/locales/{myLanguageCode}/translation.json`; the content of this file should be the translated new language. As a reference you can use the english version of the translation located at [public/locales/en/translation.json](https://github.com/konveyor/tackle-ui/blob/main/public/locales/en/translation.json)
The previous command created a file `public/locales/{myLanguageCode}/translation.json`; the content of this file should be the translated new language. As a reference you can use the english version of the translation located at [public/locales/en/translation.json](https://github.com/konveyor/tackle2-ui/blob/main/client/public/locales/en/translation.json)

> As soon as you feel confident, open a new Pull Request with your changes and make it part of the official repository.
## How to see the new translation in action?

To see your changes in action you will need to start Tackle UI in development mode. For starting Tackle UI in development mode follow the instruction at [Starting the UI](https://github.com/konveyor/tackle-ui#starting-the-ui)
To see your changes in action you will need to start Tackle UI in development mode. For starting Tackle UI in development mode follow the instruction at [Development section](https://github.com/konveyor/tackle2-ui/blob/main/README.md#development)

Steps:

- Start Tackle UI in dev mode following [Starting the UI](https://github.com/konveyor/tackle-ui#starting-the-ui) instructions.
- Start Tackle UI in dev mode following the instructions in the [Development section](https://github.com/konveyor/tackle2-ui/blob/main/README.md#development).
- Go to Keycloak http://localhost:8180/auth/admin/ and use `username=admin, password=admin`. Go to `Realm settings > themes > Supported locales` and select the new language you are adding. Finally click on `Save`.
- Go to http://localhost:3000/ and you should be redirected to the Login page where you are able to select your new language.

Expand All @@ -45,4 +45,4 @@ At this point you should be able to see your new language already translated int
## Why the questionnaire (assessment process) is not translated?

The questionnaire is data comming from https://github.com/konveyor/tackle-pathfinder hence the translation to a new language of the questionnaire should be done in that repository.
To accommodate diverse user needs, including internationalization, our custom assessment module supports the uploading of YAML files. This flexibility allows for the easy adaptation of assessments to different languages or specific requirements. If you're looking to offer assessments in a new language, simply create and upload a YAML file tailored to that language.
41 changes: 26 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,25 @@ $ minikube config set memory 10240
$ minikube config set cpus 4
```

From a terminal with administrator access (but not logged in as root), run:
Note: Depending on your driver, administrator access may be required. Common choices include Docker for container-based virtualization and KVM for hardware-assisted virtualization on Linux systems. If you're not sure which driver is best for you or if you're encountering compatibility issues, Minikube also supports auto-selecting a driver based on your system's capabilities and installed software.

From a terminal run:

```sh
$ minikube start --addons=dashboard --addons=ingress
```

Note: We need to enable the dashboard and ingress addons. The dashboard addon installs the dashboard service that exposes the Kubernetes objects in a user interface. The ingress addon allows us to create Ingress CRs to expose the Tackle UI and Tackle Hub API.

Since the olm addon is disabled until OLM issue [2534](https://github.com/operator-framework/operator-lifecycle-manager/issues/2534) is resolved we need to install the [OLM manually](https://github.com/operator-framework/operator-lifecycle-manager/releases) i.e. for version `v0.27.0` we can use:

```sh
$ minikube start --addons=dashboard --addons=ingress --addons=olm
curl -L https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.27.0/install.sh -o install.sh
chmod +x install.sh
./install.sh v0.27.0
```

Note: We need to enable the dashboard, ingress and olm addons. The dashboard addon installs the dashboard service that exposes the Kubernetes objects in a user interface. The ingress addon allows us to create Ingress CRs to expose the Tackle UI and Tackle Hub API. The olm addon allows us to use an operator to deploy Tackle.
See also official Konveyor instructions for [Provisioning Minikube](https://konveyor.github.io/konveyor/installation/#provisioning-minikube).

### Configuring kubectl for minikube

Expand Down Expand Up @@ -83,7 +95,9 @@ You will need `kubectl` on your PATH and configured to control minikube in order
### Installing the Konveyor operator
The [konveyor/operator git repository](https://github.com/konveyor/operator) provides a script to install Tackle locally using `kubectl`. You can [inspect its source here](https://github.com/konveyor/operator/blob/main/hack/install-tackle.sh). This script creates the `konveyor-tackle` namespace, CatalogSource, OperatorGroup, Subscription and Tackle CR, then waits for deployments to be ready.
Follow the official instructions for [Installing Konveyor Operator](https://konveyor.github.io/konveyor/installation/#installing-konveyor-operator)
Alternatively, the [konveyor/operator git repository](https://github.com/konveyor/operator) provides a script to install Tackle locally using `kubectl`. You can [inspect its source here](https://github.com/konveyor/operator/blob/main/hack/install-tackle.sh). This script creates the `konveyor-tackle` namespace, CatalogSource, OperatorGroup, Subscription and Tackle CR, then waits for deployments to be ready.
#### Customizing the install script (optional)
Expand Down Expand Up @@ -126,7 +140,7 @@ $ npm run start:dev
## Understanding the local development environment
Tackle2 runs in a Kubernetes compatible environment (Openshift, Kubernetes or minikube) and is usually deployed with Tackle2 Operator (OLM).
Tackle2 runs in a Kubernetes compatible environment (i.e. Openshift, Kubernetes or minikube) and is usually deployed with Tackle2 Operator (OLM).
Although the UI pod has access to tackle2 APIs from within the cluster, the UI can also be executed outside the cluster and access Tackle APIs endpoints by proxy.
The React and Patternfly based UI is composed of web pages served by an http server with proxy capabilities.
Expand Down Expand Up @@ -182,26 +196,23 @@ port and only show the URL instead of opening the default browser directly:
$ minikube dashboard --port=18080 --url=true
```
Second, we can use the `kubectl proxy` command to enable access to the dashboard. The following
command sets up the proxy to listen on any network interface (useful for remote access), to the
18080/tcp port (easy to remember), and with requests filtering disabled (less secure, but necessary):
Second, we can use the `kubectl port-forward` command to enable access to the dashboard:
```sh
$ kubectl proxy --address=0.0.0.0 --port 18080 --disable-filter=true
$ kubectl port-forward svc/kubernetes-dashboard -n kubernetes-dashboard 30090:80
```
We can now access the minikube dashboard through the proxy. Use the following URL as a template,
replacing the IP address with your workstation IP address:
`http://192.168.0.1:18080/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/#/`
We can now access the minikube dashboard on `http://localhost:30090`
## Troubleshooting
Note - The steps described are executed on a Fedora 35 workstation, but will likely work on any recent Linux distribution.
The only prerequisites are to enable virtualization extensions in the BIOS/EFI of the machine, to install libvirt and to add our user to the libvirt group.
Note - The steps described are executed on a Fedora 38 workstation, but will likely work on any recent Linux distribution.
- For minikube setups that rely on virtualization, the only prerequisites are to enable virtualization extensions in the BIOS/EFI of the machine, to install libvirt and to add our user to the libvirt group.
- Ensure that your minikube installation directory is available in your `$PATH` environment variable. This is usually `/usr/local/bin/` or something similar depending on your OS of choice.
- The following command gives us the IP address assigned to the virtual machine created by Minikube.
- The following command gives us the IP address assigned to the node created by Minikube.
It's used when interacting with tackle UI image installed on the minikube cluster.
```sh
Expand Down
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
4 changes: 2 additions & 2 deletions client/public/manifest.json → branding/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"short_name": "tackle-ui",
"name": "Tackle UI",
"short_name": "konveyor-ui",
"name": "Konveyor UI",
"icons": [
{
"src": "favicon.ico",
Expand Down
21 changes: 21 additions & 0 deletions branding/strings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"application": {
"title": "Konveyor",
"name": "Konveyor Tackle UI",
"description": "Konveyor/Tackle UI"
},
"about": {
"displayName": "Konveyor",
"imageSrc": "<%= brandingRoot %>/images/masthead-logo.svg",
"documentationUrl": "https://konveyor.github.io/konveyor/"
},
"masthead": {
"leftBrand": {
"src": "<%= brandingRoot %>/images/masthead-logo.svg",
"alt": "brand",
"height": "60px"
},
"leftTitle": null,
"rightBrand": null
}
}
68 changes: 68 additions & 0 deletions client/__mocks__/react-i18next.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* eslint-env node */

// Adapted from https://github.com/i18next/react-i18next/blob/master/example/test-jest/src/__mocks__/react-i18next.js
import React from "react";
import * as reactI18next from "react-i18next";

const hasChildren = (node) =>
node && (node.children || (node.props && node.props.children));

const getChildren = (node) =>
node && node.children ? node.children : node.props && node.props.children;

const renderNodes = (reactNodes) => {
if (typeof reactNodes === "string") {
return reactNodes;
}

return Object.keys(reactNodes).map((key, i) => {
const child = reactNodes[key];
const isElement = React.isValidElement(child);

if (typeof child === "string") {
return child;
}
if (hasChildren(child)) {
const inner = renderNodes(getChildren(child));
return React.cloneElement(child, { ...child.props, key: i }, inner);
}
if (typeof child === "object" && !isElement) {
return Object.keys(child).reduce(
(str, childKey) => `${str}${child[childKey]}`,
""
);
}

return child;
});
};

const useMock = [(k) => k, { changeLanguage: () => new Promise(() => {}) }];
useMock.t = (k) => k;
useMock.i18n = { changeLanguage: () => new Promise(() => {}) };

module.exports = {
Trans: ({ children, i18nKey }) =>
!children
? i18nKey
: Array.isArray(children)
? renderNodes(children)
: renderNodes([children]),

Translation: ({ children }) => children((k) => k, { i18n: {} }),

useTranslation: () => useMock,

initReactI18next: {
type: "3rdParty",
init: () => {},
},

// mock if needed
withTranslation: reactI18next.withTranslation,
I18nextProvider: reactI18next.I18nextProvider,
setDefaults: reactI18next.setDefaults,
getDefaults: reactI18next.getDefaults,
setI18n: reactI18next.setI18n,
getI18n: reactI18next.getI18n,
};
5 changes: 4 additions & 1 deletion client/config/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ const config: JestConfigWithTsJest = {
"@patternfly/react-icons/dist/esm/icons/":
"<rootDir>/__mocks__/fileMock.js",

// other mocks
"react-i18next": "<rootDir>/__mocks__/react-i18next.js",

// match the paths in tsconfig.json
"@app/(.*)": "<rootDir>/src/app/$1",
"@assets/(.*)":
Expand All @@ -44,7 +47,7 @@ const config: JestConfigWithTsJest = {
},

// Code to set up the testing framework before each test file in the suite is executed
setupFilesAfterEnv: ["<rootDir>/src/app/setupTests.ts"],
setupFilesAfterEnv: ["<rootDir>/src/app/test-config/setupTests.ts"],
};

export default config;
Loading

0 comments on commit 42c467c

Please sign in to comment.