chore(build): migrate to pnpm (#1558)

This commit is contained in:
Chaim Lev-Ari
2025-12-29 10:14:57 +02:00
committed by GitHub
parent 20b87f8bb9
commit 36417a0726
29 changed files with 19619 additions and 19053 deletions

View File

@@ -114,7 +114,13 @@ overrides:
'@typescript-eslint/explicit-module-boundary-types': off
'@typescript-eslint/no-unused-vars': 'error'
'@typescript-eslint/no-explicit-any': 'error'
'jsx-a11y/label-has-associated-control': ['error', { 'assert': 'either', controlComponents: ['Input', 'Checkbox'] }]
'jsx-a11y/label-has-associated-control':
- error
- assert: either
controlComponents:
- Input
- Checkbox
'jsx-a11y/control-has-associated-label': off
'react/function-component-definition': ['error', { 'namedComponents': 'function-declaration' }]
'react/jsx-no-bind': off
'no-await-in-loop': 'off'

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
cd $(dirname -- "$0") && yarn lint-staged
cd $(dirname -- "$0") && pnpm lint-staged

View File

@@ -77,7 +77,7 @@ The feature request process is similar to the bug report process but has an extr
## Build and run Portainer locally
Ensure you have Docker, Node.js, yarn, and Golang installed in the correct versions.
Ensure you have Docker, Node.js, pnpm, and Golang installed in the correct versions.
Install dependencies:

View File

@@ -20,7 +20,7 @@ all: tidy deps build-server build-client ## Build the client, server and downloa
build-all: all ## Alias for the 'all' target (used by CI)
build-client: init-dist ## Build the client
export NODE_ENV=$(ENV) && yarn build --config $(WEBPACK_CONFIG)
export NODE_ENV=$(ENV) && pnpm run build --config $(WEBPACK_CONFIG)
build-server: init-dist ## Build the server binary
./build/build_binary.sh "$(PLATFORM)" "$(ARCH)"
@@ -29,7 +29,7 @@ build-image: build-all ## Build the Portainer image locally
docker buildx build --load -t portainerci/portainer-ce:$(TAG) -f build/linux/Dockerfile .
build-storybook: ## Build and serve the storybook files
yarn storybook:build
pnpm run storybook:build
##@ Build dependencies
.PHONY: deps server-deps client-deps tidy
@@ -39,7 +39,7 @@ server-deps: init-dist ## Download dependant server binaries
@./build/download_binaries.sh $(PLATFORM) $(ARCH)
client-deps: ## Install client dependencies
yarn
pnpm install
tidy: ## Tidy up the go.mod file
@go mod tidy
@@ -55,7 +55,7 @@ clean: ## Remove all build and download artifacts
test: test-server test-client ## Run all tests
test-client: ## Run client tests
yarn test $(ARGS) --coverage
pnpm run test $(ARGS) --coverage
test-server: ## Run server tests
$(GOTESTSUM) --format pkgname-and-test-fails --format-hide-empty-pkg --hide-summary skipped -- -cover -covermode=atomic -coverprofile=coverage.out ./...
@@ -67,7 +67,7 @@ dev: ## Run both the client and server in development mode
make dev-client
dev-client: ## Run the client in development mode
yarn dev
pnpm run dev
dev-server: build-server ## Run the server in development mode
@./dev/run_container.sh
@@ -81,7 +81,7 @@ dev-server-podman: build-server ## Run the server in development mode
format: format-client format-server ## Format all code
format-client: ## Format client code
yarn format
pnpm run format
format-server: ## Format server code
go fmt ./...
@@ -91,7 +91,7 @@ format-server: ## Format server code
lint: lint-client lint-server ## Lint all code
lint-client: ## Lint client code
yarn lint
pnpm run lint
lint-server: tidy ## Lint server code
golangci-lint run --timeout=10m -c .golangci.yaml
@@ -109,8 +109,8 @@ docs-build: init-dist ## Build docs
cd api && $(SWAG) init -o "../dist/docs" -ot "yaml" -g ./http/handler/handler.go --parseDependency --parseInternal --parseDepth 2 -p pascalcase --markdownFiles ./
docs-validate: docs-build ## Validate docs
yarn swagger2openapi --warnOnly dist/docs/swagger.yaml -o dist/docs/openapi.yaml
yarn swagger-cli validate dist/docs/openapi.yaml
pnpm swagger2openapi --warnOnly dist/docs/swagger.yaml -o dist/docs/openapi.yaml
pnpm swagger-cli validate dist/docs/openapi.yaml
##@ Helpers
.PHONY: help

View File

@@ -630,7 +630,7 @@ func main() {
Str("build_number", build.BuildNumber).
Str("image_tag", build.ImageTag).
Str("nodejs_version", build.NodejsVersion).
Str("yarn_version", build.YarnVersion).
Str("pnpm_version", build.PnpmVersion).
Str("webpack_version", build.WebpackVersion).
Str("go_version", build.GoVersion).
Msg("starting Portainer")

View File

@@ -1,4 +1,9 @@
import { Header, flexRender, TableMeta } from '@tanstack/react-table';
import {
Header,
flexRender,
TableMeta,
ColumnMeta,
} from '@tanstack/react-table';
import { filterHOC } from './Filter';
import { TableHeaderCell } from './TableHeaderCell';
@@ -19,12 +24,9 @@ export function TableHeaderRow<D extends DefaultType = DefaultType>({
<tr>
{headers.map((header) => {
const sortDirection = header.column.getIsSorted();
const {
meta: { className, width, filter = filterHOC('Filter') } = {
className: '',
width: undefined,
},
} = header.column.columnDef;
const { className, filter, width } = parseMeta(
header.column.columnDef.meta
);
return (
<TableHeaderCell
@@ -60,3 +62,28 @@ export function TableHeaderRow<D extends DefaultType = DefaultType>({
</tr>
);
}
function parseMeta<D extends DefaultType = DefaultType>(
meta: ColumnMeta<D, unknown> | undefined
) {
if (!meta) {
return {
className: '',
width: undefined,
filter: filterHOC('Filter'),
};
}
const className =
'className' in meta && typeof meta.className === 'string'
? meta.className
: undefined;
const width =
'width' in meta && typeof meta.width === 'string' ? meta.width : undefined;
const filter =
'filter' in meta && typeof meta.filter === 'function'
? meta.filter
: filterHOC('Filter');
return { className, width, filter };
}

View File

@@ -1,4 +1,4 @@
import { Cell, flexRender } from '@tanstack/react-table';
import { Cell, ColumnMeta, flexRender } from '@tanstack/react-table';
import clsx from 'clsx';
import { DefaultType } from './types';
@@ -20,10 +20,18 @@ export function TableRow<D extends DefaultType = DefaultType>({
onClick={onClick}
>
{cells.map((cell) => (
<td key={cell.id} className={cell.column.columnDef.meta?.className}>
<td key={cell.id} className={getClassName(cell.column.columnDef.meta)}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
);
}
function getClassName<D extends DefaultType = DefaultType>(
meta: ColumnMeta<D, unknown> | undefined
) {
return !!meta && 'className' in meta && typeof meta.className === 'string'
? meta.className
: '';
}

View File

@@ -22,5 +22,5 @@ export type FilesTableMeta = TableMeta<FileData> & {
export function isFilesTableMeta(
meta?: TableMeta<FileData>
): meta is FilesTableMeta {
return !!meta && meta.table === 'files';
return !!meta && 'table' in meta && meta.table === 'files';
}

View File

@@ -11,5 +11,5 @@ export type ContainerNetworkTableMeta = TableMeta<TableNetwork> & {
export function isContainerNetworkTableMeta(
meta?: TableMeta<TableNetwork>
): meta is ContainerNetworkTableMeta {
return !!meta && meta.table === 'container-networks';
return !!meta && 'table' in meta && meta.table === 'container-networks';
}

View File

@@ -10,5 +10,5 @@ export type TableMeta = BaseTableMeta<NodeViewModel> & {
export function isTableMeta(
meta?: BaseTableMeta<NodeViewModel>
): meta is TableMeta {
return !!meta && meta.table === 'nodes';
return !!meta && 'table' in meta && meta.table === 'nodes';
}

View File

@@ -8,7 +8,7 @@ interface TableMeta {
}
function isTableMeta(meta: BaseTableMeta<VolumeViewModel>): meta is TableMeta {
return meta.table === 'volumes';
return !!meta && 'table' in meta && meta.table === 'volumes';
}
export function getTableMeta(meta?: BaseTableMeta<VolumeViewModel>): TableMeta {

View File

@@ -112,7 +112,7 @@ export function subscribe(listener: Listener) {
}
export function unsubscribe(listener: Listener) {
_.remove<Listener>(store.listeners, listener);
_.remove(store.listeners, listener);
}
function buildUrl(action = '') {

View File

@@ -19,7 +19,7 @@ export interface VersionResponse {
BuildNumber: string;
ImageTag: string;
NodejsVersion: string;
YarnVersion: string;
PnpmVersion: string;
WebpackVersion: string;
GoVersion: string;
GitCommit: string;

View File

@@ -126,7 +126,7 @@ function BuildInfoModal({ closeModal }: { closeModal: () => void }) {
<span className="text-muted small">
Nodejs {Build.NodejsVersion}
</span>
<span className="text-muted small">Yarn v{Build.YarnVersion}</span>
<span className="text-muted small">pnpm v{Build.PnpmVersion}</span>
<span className="text-muted small">
Webpack v{Build.WebpackVersion}
</span>

View File

@@ -16,8 +16,8 @@ mkdir -p dist
BUILDNUMBER=${BUILDNUMBER:-"N/A"}
CONTAINER_IMAGE_TAG=${CONTAINER_IMAGE_TAG:-"N/A"}
NODE_VERSION=${NODE_VERSION:-$(node -v)}
YARN_VERSION=${YARN_VERSION:-$(yarn --version)}
WEBPACK_VERSION=${WEBPACK_VERSION:-$(yarn list webpack --depth=0 | grep webpack | awk -F@ '{print $2}')}
PNPM_VERSION=${PNPM_VERSION:-$(pnpm -v)}
WEBPACK_VERSION=${WEBPACK_VERSION:-$(pnpm list webpack --depth=0 | grep webpack | awk '{print $2}')}
GO_VERSION=${GO_VERSION:-$(go version | awk '{print $3}')}
GIT_COMMIT_HASH=${GIT_COMMIT_HASH:-$(git rev-parse --short HEAD)}
@@ -48,7 +48,7 @@ ldflags="-s -X 'github.com/portainer/liblicense.LicenseServerBaseURL=https://api
-X 'github.com/portainer/portainer/pkg/build.BuildNumber=${BUILDNUMBER}' \
-X 'github.com/portainer/portainer/pkg/build.ImageTag=${CONTAINER_IMAGE_TAG}' \
-X 'github.com/portainer/portainer/pkg/build.NodejsVersion=${NODE_VERSION}' \
-X 'github.com/portainer/portainer/pkg/build.YarnVersion=${YARN_VERSION}' \
-X 'github.com/portainer/portainer/pkg/build.PnpmVersion=${PNPM_VERSION}' \
-X 'github.com/portainer/portainer/pkg/build.WebpackVersion=${WEBPACK_VERSION}' \
-X 'github.com/portainer/portainer/pkg/build.GitCommit=${GIT_COMMIT_HASH}' \
-X 'github.com/portainer/portainer/pkg/build.GoVersion=${GO_VERSION}' \

View File

@@ -29,4 +29,4 @@ multiarch:
docker buildx create --name=buildx-multi-arch --driver=docker-container --driver-opt=network=host
portainer:
yarn build
pnpm run build

View File

@@ -48,8 +48,8 @@ display_configuration() {
/usr/local/go/bin/go version
info "Node version"
node -v
info "Yarn version"
yarn -v
info "Pnpm version"
pnpm -v
info "Docker version"
docker version
}

View File

@@ -40,10 +40,8 @@ RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - \
RUN curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
&& apt-get install -y nodejs
# Install Yarn
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
&& apt-get update && apt-get -y install yarn
# Install Package manager
RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.bashrc" SHELL="$(which bash)" bash -
# Install Golang
RUN cd /tmp \

View File

@@ -1,6 +1,6 @@
module.exports = {
'*.(js|ts){,x}': 'yarn lint',
'*.(js|ts){,x}': 'pnpm run lint',
'*.(ts){,x}': () => 'tsc --noEmit',
'*.{js,ts,tsx,css,md,html,json}': 'yarn format',
'*.{js,ts,tsx,css,md,html,json}': 'pnpm run format',
'*.go': () => 'make lint-server',
};

View File

@@ -23,7 +23,7 @@
"format": "prettier --log-level warn --write \"**/*.{js,css,html,jsx,tsx,ts}\"",
"lint": "eslint --cache --fix './**/*.{js,jsx,ts,tsx}'",
"test": "vitest run",
"sb": "yarn storybook",
"sb": "pnpm run storybook",
"storybook": "storybook dev -p 6006",
"storybook:build": "storybook build -o ./dist/storybook",
"analyze-webpack": "webpack --config ./webpack/webpack.analyze.js",
@@ -33,6 +33,7 @@
"engines": {
"node": "^22"
},
"packageManager": "pnpm@10.26.2",
"dependencies": {
"@aws-crypto/sha256-js": "^2.0.0",
"@codemirror/autocomplete": "^6.4.0",
@@ -79,7 +80,7 @@
"angularjs-slider": "^6.4.0",
"axios": "^1.7",
"axios-cache-interceptor": "^1.4.1",
"axios-progress-bar": "portainer/progress-bar-4-axios",
"axios-progress-bar": "git://github.com/portainer/progress-bar-4-axios",
"babel-plugin-angularjs-annotate": "^0.10.0",
"bootstrap": "^3.4.0",
"buffer": "^6.0.3",
@@ -107,6 +108,7 @@
"jsdom": "^24",
"json-schema": "^0.4.0",
"lodash": "^4.17.21",
"lodash-es": "npm:lodash@4.17.21",
"lucide-react": "^0.468.0",
"markdown-to-jsx": "^7.7.4",
"moment": "^2.29.1",
@@ -162,8 +164,11 @@
"@types/filesize": "^5.0.2",
"@types/filesize-parser": "^1.5.1",
"@types/jquery": "^3.5.10",
"@types/json-schema": "^7.0.15",
"@types/lodash": "^4.17.21",
"@types/mustache": "^4.1.2",
"@types/nprogress": "^0.2.0",
"@types/qs": "^6.9.17",
"@types/react": "^17.0.37",
"@types/react-datetime-picker": "^3.4.1",
"@types/react-dom": "^17.0.11",
@@ -209,7 +214,6 @@
"msw": "^2.0.11",
"msw-storybook-addon": "2.0.0--canary.122.b3ed3b1.0",
"ngtemplate-loader": "^2.1.0",
"plop": "^4.0.0",
"postcss": "^8.4.33",
"postcss-loader": "^7.3.3",
"prettier": "^3.0.3",
@@ -236,21 +240,27 @@
"webpack-dev-server": "^4.15.1",
"webpack-merge": "^5.9.0"
},
"resolutions": {
"**/jquery": "^3.6.0",
"pnpm": {
"overrides": {
"jquery": "^3.6.0",
"decompress": "^4.2.1",
"**/lodash": "^4.17.21",
"lodash": "^4.17.21",
"js-yaml": "^3.14.0",
"minimist": "^1.2.6",
"http-proxy": "^1.18.1",
"**/@uirouter/react": "^1.0.7",
"**/@uirouter/angularjs": "1.0.11",
"**/moment": "^2.21.0",
"msw/**/wrap-ansi": "^7.0.0"
"@uirouter/react": "^1.0.7",
"@uirouter/angularjs": "1.0.11",
"moment": "^2.21.0",
"msw>wrap-ansi": "^7.0.0",
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11"
},
"configDependencies": {
"@pnpm/plugin-types-fixer": "0.1.0+sha512-bLww63gRHi7siYTqFJb5qNdcXadU0jv20Et6z5AryMZ7FlLolbEJOrXLpg8+amQZNHHNW1dfFUBGVw/9ezQbFg=="
}
},
"browserslist": "last 2 versions",
"msw": {
"workerDirectory": ".storybook/public"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
}

View File

@@ -24,8 +24,8 @@ var (
// NodejsVersion is the version of Node.js used in the build.
NodejsVersion string
// YarnVersion is the version of Yarn used in the build.
YarnVersion string
// PnpmVersion is the version of pnpm used in the build.
PnpmVersion string
// WebpackVersion is the version of Webpack used in the build.
WebpackVersion string
@@ -55,7 +55,7 @@ type (
BuildNumber string
ImageTag string
NodejsVersion string
YarnVersion string
PnpmVersion string
WebpackVersion string
GoVersion string
GitCommit string
@@ -81,7 +81,7 @@ func GetBuildInfo() BuildInfo {
BuildNumber: BuildNumber,
ImageTag: ImageTag,
NodejsVersion: NodejsVersion,
YarnVersion: YarnVersion,
PnpmVersion: PnpmVersion,
WebpackVersion: WebpackVersion,
GoVersion: GoVersion,
GitCommit: GitCommit,

View File

@@ -1,16 +0,0 @@
# Plop generator
We use [plop.js](https://plopjs.com/) to generate angular components in our app (in the future we might use it for other things).
in order to create a component with the name `exampleComponent`, go in your terminal to the folder in which you want to create the component (for example, if I want to create it in the portainer module components, I'll go to `./app/portainer/components`). then execute the following line:
```
yarn plop exampleComponent
```
this will create the following files and folders:
```
example-component/index.js - the component file
example-component/exampleComponent.html - the template file
example-component/exampleComponentController.js - the component controller file
```

View File

@@ -1,6 +0,0 @@
class {{properCase name}}Controller {
/* @ngInject */
constructor() {}
}
export default {{properCase name}}Controller;

View File

@@ -1 +0,0 @@
{{name}}

View File

@@ -1,9 +0,0 @@
import angular from 'angular';
import controller from './{{dashCase name}}.controller.js'
export const {{camelCase name}} = {
templateUrl: './{{dashCase name}}.html',
controller,
};
angular.module('portainer.{{module}}').component('{{camelCase name}}', {{camelCase name}})

View File

@@ -1,46 +0,0 @@
module.exports = function (plop) {
// use of INIT_CWD instead of process.cwd() because yarn changes the cwd
const cwd = process.env.INIT_CWD;
plop.addHelper('cwd', () => cwd);
plop.setGenerator('component', {
prompts: [
{
type: 'input',
name: 'name',
message: 'component name please',
},
{
type: 'input',
name: 'module',
message: 'module name please',
default: `${getCurrentPortainerModule(cwd)}`,
// when: false
},
], // array of inquirer prompts
actions: [
{
type: 'add',
path: `{{cwd}}/{{dashCase name}}/index.js`,
templateFile: './plop-templates/component.js.hbs',
},
{
type: 'add',
path: `{{cwd}}/{{dashCase name}}/{{dashCase name}}.controller.js`,
templateFile: './plop-templates/component-controller.js.hbs',
},
{
type: 'add',
path: `{{cwd}}/{{dashCase name}}/{{dashCase name}}.html`,
templateFile: './plop-templates/component.html.hbs',
},
], // array of actions
});
};
function getCurrentPortainerModule(cwd) {
const match = cwd.match(/\/app\/([^\/]*)(\/.*)?$/);
if (!match || !match.length || match[1] === 'portainer') {
return 'app';
}
return match[1];
}

19508
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
/// <reference types="vitest" />
import { defineConfig } from 'vite';
import { defineConfig } from 'vitest/config';
import tsconfigPaths from 'vite-tsconfig-paths';
import svgr from 'vite-plugin-svgr';

18912
yarn.lock

File diff suppressed because it is too large Load Diff