[PUI] Add licenses texts to PUI (#6855)

* compile a license texts bundle

* add backend license extraction on install

* change path for licenses

* add to gitignore

* Add api to expose license paths

* add texts

* add frontend rendering of licensing files

* Handle errors when fetching license information

* Format backend packages.txt in json

* Improved API rendering:

- Handle file errors
- Render as JSON object

* Improve frontend modal rendering

- Separate frontend / backend into tabs
- Split packages into accordion

* Generate JSON file for fronten deps

* Fix rendering for frontend deps

* Update src/frontend/src/components/modals/LicenseModal.tsx

Co-authored-by: Lukas <76838159+wolflu05@users.noreply.github.com>

* Update src/frontend/src/components/modals/LicenseModal.tsx

Co-authored-by: Lukas <76838159+wolflu05@users.noreply.github.com>

* make reading of licenses objects dynamic

* remove unsued import

* style fixes

* style fixes

* default to first value

* use new syntax to call docker compose

* merge fix

* fix path

* Roll back #6942

* Update qc_checks.yaml

Run migration checks when requirements file changes

---------

Co-authored-by: Oliver Walters <oliver.henry.walters@gmail.com>
Co-authored-by: Lukas <76838159+wolflu05@users.noreply.github.com>
This commit is contained in:
Matthias Mair 2024-04-04 00:31:20 +01:00 committed by GitHub
parent 7cbf99bea7
commit fddcb629b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 308 additions and 13 deletions

View File

@ -48,8 +48,9 @@ jobs:
migrations:
- '**/migrations/**'
- '.github/workflows**'
- 'src/backend/requirements.txt'
api:
- 'InvenTree/InvenTree/api_version.py'
- 'src/backend/InvenTree/InvenTree/api_version.py'
frontend:
- 'src/frontend/**'

1
.gitignore vendored
View File

@ -85,6 +85,7 @@ env/
# Locale stats file
src/backend/InvenTree/InvenTree/locale_stats.json
src/backend/InvenTree/InvenTree/licenses.txt
# node.js
node_modules/

View File

@ -1,6 +1,9 @@
"""Main JSON interface views."""
import json
import logging
import sys
from pathlib import Path
from django.conf import settings
from django.db import transaction
@ -31,6 +34,60 @@ from .status import check_system_health, is_worker_running
from .version import inventreeApiText
from .views import AjaxView
logger = logging.getLogger('inventree')
class LicenseViewSerializer(serializers.Serializer):
"""Serializer for license information."""
backend = serializers.CharField(help_text='Backend licenses texts', read_only=True)
frontend = serializers.CharField(
help_text='Frontend licenses texts', read_only=True
)
class LicenseView(APIView):
"""Simple JSON endpoint for InvenTree license information."""
permission_classes = [permissions.IsAuthenticated]
def read_license_file(self, path: Path) -> list:
"""Extract license information from the provided file.
Arguments:
path: Path to the license file
Returns: A list of items containing the license information
"""
# Check if the file exists
if not path.exists():
logger.error("License file not found at '%s'", path)
return []
try:
data = json.loads(path.read_text())
except json.JSONDecodeError as e:
logger.exception("Failed to parse license file '%s': %s", path, e)
return []
except Exception as e:
logger.exception("Exception while reading license file '%s': %s", path, e)
return []
# Ensure consistent string between backend and frontend licenses
return [{key.lower(): value for key, value in entry.items()} for entry in data]
@extend_schema(responses={200: OpenApiResponse(response=LicenseViewSerializer)})
def get(self, request, *args, **kwargs):
"""Return information about the InvenTree server."""
backend = Path(__file__).parent.joinpath('licenses.txt')
frontend = Path(__file__).parent.parent.joinpath(
'web/static/web/.vite/dependencies.json'
)
return JsonResponse({
'backend': self.read_license_file(backend),
'frontend': self.read_license_file(frontend),
})
class VersionViewSerializer(serializers.Serializer):
"""Serializer for a single version."""

View File

@ -1,11 +1,14 @@
"""InvenTree API version information."""
# InvenTree API version
INVENTREE_API_VERSION = 185
INVENTREE_API_VERSION = 186
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """
v186 - 2024-03-26 : https://github.com/inventree/InvenTree/pull/6855
- Adds license information to the API
v185 - 2024-03-24 : https://github.com/inventree/InvenTree/pull/6836
- Remove /plugin/activate endpoint
- Update docstrings and typing for various API endpoints (no functional changes)

View File

@ -39,7 +39,14 @@ from stock.urls import stock_urls
from web.urls import api_urls as web_api_urls
from web.urls import urlpatterns as platform_urls
from .api import APISearchView, InfoView, NotFoundView, VersionTextView, VersionView
from .api import (
APISearchView,
InfoView,
LicenseView,
NotFoundView,
VersionTextView,
VersionView,
)
from .magic_login import GetSimpleLoginView
from .social_auth_urls import (
EmailListView,
@ -99,6 +106,7 @@ apipatterns = [
name='schema',
),
# InvenTree information endpoints
path('license/', LicenseView.as_view(), name='api-license'), # license info
path(
'version-text', VersionTextView.as_view(), name='api-version-text'
), # version text

View File

@ -18,7 +18,7 @@ django-maintenance-mode # Shut down application while reloading
django-markdownify # Markdown rendering
django-mptt # Modified Preorder Tree Traversal
django-markdownify # Markdown rendering
django-money>=3.0.0,<3.5.0 # Django app for currency management # FIXED 2023-10-31 3.3.0 breaks due to https://github.com/django-money/django-money/issues/731
django-money>=3.0.0,<3.3.0 # Django app for currency management # FIXED 2023-10-31 3.3.0 breaks due to https://github.com/django-money/django-money/issues/731
django-mptt # Modified Preorder Tree Traversal
django-redis>=5.0.0 # Redis integration
django-q2 # Background task scheduling
@ -41,6 +41,7 @@ gunicorn # Gunicorn web server
pdf2image # PDF to image conversion
pillow # Image manipulation
pint==0.21 # Unit conversion # FIXED 2023-05-30 breaks tests https://github.com/matmair/InvenTree/actions/runs/5095665936/jobs/9160852560
pip-licenses # License information for installed packages
python-barcode[images] # Barcode generator
python-dotenv # Environment variable management
pyyaml>=6.0.1 # YAML parsing

View File

@ -96,7 +96,7 @@ django-js-asset==2.2.0
# via django-mptt
django-maintenance-mode==0.21.1
django-markdownify==0.9.3
django-money==3.4.1
django-money==3.2.0
django-mptt==0.16.0
django-otp==1.3.0
# via django-allauth-2fa
@ -230,6 +230,9 @@ pillow==10.2.0
# qrcode
# weasyprint
pint==0.21
pip-licenses==4.3.4
prettytable==3.10.0
# via pip-licenses
protobuf==4.25.3
# via
# googleapis-common-protos
@ -329,6 +332,8 @@ urllib3==2.2.1
# dulwich
# requests
# sentry-sdk
wcwidth==0.2.13
# via prettytable
weasyprint==61.2
# via django-weasyprint
webencodings==0.5.1

View File

@ -65,6 +65,7 @@
"@types/react-router-dom": "^5.3.3",
"@vitejs/plugin-react": "^4.2.1",
"babel-plugin-macros": "^3.1.0",
"rollup-plugin-license": "^3.3.1",
"nyc": "^15.1.0",
"typescript": "^5.3.3",
"vite": "^5.2.7",

View File

@ -0,0 +1,90 @@
import { Trans, t } from '@lingui/macro';
import {
Accordion,
Alert,
Divider,
Group,
LoadingOverlay,
Space,
Stack,
Tabs,
Text
} from '@mantine/core';
import { useQuery } from '@tanstack/react-query';
import { api } from '../../App';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { apiUrl } from '../../states/ApiState';
export function LicenceView(entries: Readonly<any[]>) {
return (
<Stack spacing="xs">
<Divider />
{entries?.length > 0 && (
<Accordion variant="contained" defaultValue="-">
{entries?.map((entry: any, index: number) => (
<Accordion.Item key={entry.name} value={`entry-${index}`}>
<Accordion.Control>
<Group position="apart" grow>
<Text>{entry.name}</Text>
<Text>{entry.license}</Text>
<Space />
<Text>{entry.version}</Text>
</Group>
</Accordion.Control>
<Accordion.Panel style={{ whiteSpace: 'pre-line' }}>
{entry.licensetext || t`No license text available`}
</Accordion.Panel>
</Accordion.Item>
))}
</Accordion>
)}
</Stack>
);
}
export function LicenseModal() {
const { data, isFetching, isError } = useQuery({
queryKey: ['license'],
queryFn: () =>
api
.get(apiUrl(ApiEndpoints.license))
.then((res) => res.data ?? {})
.catch(() => {})
});
return (
<Stack spacing="xs">
<Divider />
<LoadingOverlay visible={isFetching} />
{isFetching && (
<Text>
<Trans>Loading license information</Trans>
</Text>
)}
{isError ? (
<Alert color="red" title={t`Error`}>
<Text>
<Trans>Failed to fetch license information</Trans>
</Text>
</Alert>
) : (
<Tabs defaultValue={Object.keys(data)[0] ?? ''}>
<Tabs.List>
{Object.keys(data ?? {}).map((key) => (
<Tabs.Tab key={key} value={key}>
<Trans>{key} Packages</Trans>
</Tabs.Tab>
))}
</Tabs.List>
{Object.keys(data ?? {}).map((key) => (
<Tabs.Panel key={key} value={key}>
{LicenceView(data[key] ?? [])}
</Tabs.Panel>
))}
</Tabs>
)}
</Stack>
);
}

View File

@ -10,6 +10,7 @@ import { ModalsProvider } from '@mantine/modals';
import { Notifications } from '@mantine/notifications';
import { AboutInvenTreeModal } from '../components/modals/AboutInvenTreeModal';
import { LicenseModal } from '../components/modals/LicenseModal';
import { QrCodeModal } from '../components/modals/QrCodeModal';
import { ServerInfoModal } from '../components/modals/ServerInfoModal';
import { useLocalState } from '../states/LocalState';
@ -65,7 +66,8 @@ export function ThemeContext({ children }: { children: JSX.Element }) {
modals={{
qr: QrCodeModal,
info: ServerInfoModal,
about: AboutInvenTreeModal
about: AboutInvenTreeModal,
license: LicenseModal
}}
>
{children}

View File

@ -96,8 +96,19 @@ function aboutInvenTree() {
innerProps: {}
});
}
function licenseInfo() {
return openContextModal({
modal: 'license',
title: (
<StylishText size="xl">
<Trans>License Information</Trans>
</StylishText>
),
size: 'xl',
innerProps: {}
});
}
// TODO @matmair: Add the following pages and adjust the links
export const aboutLinks: DocumentationLinkItem[] = [
{
id: 'instance',
@ -114,8 +125,7 @@ export const aboutLinks: DocumentationLinkItem[] = [
{
id: 'licenses',
title: <Trans>Licenses</Trans>,
description: <Trans>Licenses for packages used by InvenTree</Trans>,
link: '/licenses',
placeholder: true
description: <Trans>Licenses for dependencies of the service</Trans>,
action: licenseInfo
}
];

View File

@ -40,6 +40,7 @@ export enum ApiEndpoints {
news = 'news/',
global_status = 'generic/status/',
version = 'version/',
license = 'license/',
sso_providers = 'auth/providers/',
group_list = 'user/group/',
owner_list = 'user/owner/',

View File

@ -1,5 +1,6 @@
import react from '@vitejs/plugin-react';
import { platform, release } from 'node:os';
import license from 'rollup-plugin-license';
import { defineConfig, splitVendorChunkPlugin } from 'vite';
import istanbul from 'vite-plugin-istanbul';
@ -19,6 +20,19 @@ export default defineConfig({
}
}),
splitVendorChunkPlugin(),
license({
sourcemap: true,
thirdParty: {
includePrivate: true,
multipleVersions: true,
output: {
file: '../../InvenTree/web/static/web/.vite/dependencies.json',
template(dependencies) {
return JSON.stringify(dependencies);
}
}
}
}),
istanbul({
include: 'src/*',
exclude: ['node_modules', 'test/'],

View File

@ -942,7 +942,7 @@
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15":
version "1.4.15"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
@ -1769,6 +1769,11 @@ aria-hidden@^1.1.3:
dependencies:
tslib "^2.0.0"
array-find-index@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@ -2061,6 +2066,11 @@ commander@^10.0.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
commenting@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/commenting/-/commenting-1.1.0.tgz#fae14345c6437b8554f30bc6aa6c1e1633033590"
integrity sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@ -2481,7 +2491,7 @@ glob-parent@~5.1.0:
dependencies:
is-glob "^4.0.1"
glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.2.0:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@ -2863,7 +2873,7 @@ lodash.sortby@^4.7.0:
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==
lodash@^4.17.19, lodash@^4.17.21:
lodash@^4.17.19, lodash@^4.17.21, lodash@~4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -2897,6 +2907,13 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
magic-string@~0.30.0:
version "0.30.8"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.8.tgz#14e8624246d2bedba70d5462aa99ac9681844613"
integrity sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15"
make-dir@^3.0.0, make-dir@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@ -2958,6 +2975,16 @@ minimatch@^3.0.4, minimatch@^3.1.1:
dependencies:
brace-expansion "^1.1.7"
mkdirp@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50"
integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==
moment@~2.30.1:
version "2.30.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
moo@^0.5.1:
version "0.5.2"
resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c"
@ -3115,6 +3142,11 @@ package-hash@^4.0.0:
lodash.flattendeep "^4.4.0"
release-zalgo "^1.0.0"
package-name-regex@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/package-name-regex/-/package-name-regex-2.0.6.tgz#b54bcb04d950e38082b7bb38fa558e01c1679334"
integrity sha512-gFL35q7kbE/zBaPA3UKhp2vSzcPYx2ecbYuwv1ucE9Il6IIgBDweBlH8D68UFGZic2MkllKa2KHCfC1IQBQUYA==
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@ -3514,6 +3546,21 @@ rimraf@^3.0.0:
dependencies:
glob "^7.1.3"
rollup-plugin-license@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/rollup-plugin-license/-/rollup-plugin-license-3.3.1.tgz#73b68e33477524198d6f3f9befc905f59bf37c53"
integrity sha512-lwZ/J8QgSnP0unVOH2FQuOBkeiyp0EBvrbYdNU33lOaYD8xP9Zoki+PGoWMD31EUq8Q07GGocSABTYlWMKkwuw==
dependencies:
commenting "~1.1.0"
glob "~7.2.0"
lodash "~4.17.21"
magic-string "~0.30.0"
mkdirp "~3.0.0"
moment "~2.30.1"
package-name-regex "~2.0.6"
spdx-expression-validate "~2.0.0"
spdx-satisfies "~5.0.1"
rollup@^4.13.0:
version "4.13.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.13.0.tgz#dd2ae144b4cdc2ea25420477f68d4937a721237a"
@ -3643,6 +3690,54 @@ spawn-wrap@^2.0.0:
signal-exit "^3.0.2"
which "^2.0.1"
spdx-compare@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/spdx-compare/-/spdx-compare-1.0.0.tgz#2c55f117362078d7409e6d7b08ce70a857cd3ed7"
integrity sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==
dependencies:
array-find-index "^1.0.2"
spdx-expression-parse "^3.0.0"
spdx-ranges "^2.0.0"
spdx-exceptions@^2.1.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66"
integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==
spdx-expression-parse@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
dependencies:
spdx-exceptions "^2.1.0"
spdx-license-ids "^3.0.0"
spdx-expression-validate@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/spdx-expression-validate/-/spdx-expression-validate-2.0.0.tgz#25c9408e1c63fad94fff5517bb7101ffcd23350b"
integrity sha512-b3wydZLM+Tc6CFvaRDBOF9d76oGIHNCLYFeHbftFXUWjnfZWganmDmvtM5sm1cRwJc/VDBMLyGGrsLFd1vOxbg==
dependencies:
spdx-expression-parse "^3.0.0"
spdx-license-ids@^3.0.0:
version "3.0.17"
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz#887da8aa73218e51a1d917502d79863161a93f9c"
integrity sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==
spdx-ranges@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/spdx-ranges/-/spdx-ranges-2.1.1.tgz#87573927ba51e92b3f4550ab60bfc83dd07bac20"
integrity sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==
spdx-satisfies@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/spdx-satisfies/-/spdx-satisfies-5.0.1.tgz#9feeb2524686c08e5f7933c16248d4fdf07ed6a6"
integrity sha512-Nwor6W6gzFp8XX4neaKQ7ChV4wmpSh2sSDemMFSzHxpTw460jxFYeOn+jq4ybnSSw/5sc3pjka9MQPouksQNpw==
dependencies:
spdx-compare "^1.0.0"
spdx-expression-parse "^3.0.0"
spdx-ranges "^2.0.0"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"

View File

@ -244,6 +244,12 @@ def install(c, uv=False):
# Run plugins install
plugins(c, uv=uv)
# Compile license information
lic_path = managePyDir().joinpath('InvenTree', 'licenses.txt')
c.run(
f'pip-licenses --format=json --with-license-file --no-license-path > {lic_path}'
)
@task(help={'tests': 'Set up test dataset at the end'})
def setup_dev(c, tests=False):