diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..5887b50
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,4 @@
+output
+replacement
+demo
+*.md
diff --git a/.gitignore b/.gitignore
index 5def8e9..46c228d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,14 @@ generate/*
!generate/tpl
!generate/*.sh
!generate/*.xml
+!generate/*.md
+!generate/dictReplace.txt
lang-replacement
# godiff*
# transfer*
+
+output/*
+!output/*.sh
+!output/.cache
+!output/.cache/Dockerfile
+node_modules
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 4f5fad7..fea1ae6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -26,18 +26,18 @@ RUN apk --update add \
WORKDIR /generate
RUN wget https://hub.fastgit.org/rinetd/transfer/releases/download/v1.0.2/transfer-v1.0.2-linux-amd64.tar.gz; \
tar -zxf transfer-v1.0.2-linux-amd64.tar.gz && rm -f transfer-v1.0.2-linux-amd64.tar.gz
+RUN wget https://hub.fastgit.org/covrom/xml2json/releases/download/1.0/xml2json; chmod +x xml2json
COPY --from=builder /src/lang-replacement /generate
COPY --from=builder /src/godiff /generate
ADD ./generate/tpl/ /generate/tpl/
ADD ./generate/gitdiff.sh /generate
+ADD ./generate/dictReplace.txt /generate
ADD ./entry.sh /generate
RUN find /generate
# EXPOSE 80
ENV GENERATE_REPO="https://gitee.com/g-devops/fk-portainer" \
- GENERATE_BRANCH="br-v29-lang" \
GENERATE_OUTPUT="portainer_zh.xml" \
- REPLACE_REPO="https://gitee.com/g-devops/fk-portainer" \
- REPLACE_BRANCH="release/2.9" \
- EXEC_TYPE="GENERATE"
+ CMP1="2.9.1" \
+ CMP2="origin/br-lang2"
ENTRYPOINT /generate/entry.sh
diff --git a/README.md b/README.md
index a722cbd..2acd770 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,57 @@
- Konga v0.14.9 全支持
- Portainer v2.9.0 半汉化(docker+porainer部分)
-## Dev
+## 操作说明
+
+- pt >> infrastlabs/portainer-cn:latest #基于官方v2.9.1, 生成portainer-cn汉化版镜像
+- registry.cn-shenzhen.aliyuncs.com/infrastlabs/lang-replacement
+ - dict #字典生成
+ - cache #node_modules @v291
+ - replace,latest #汉化程序+Node构建 >> 生成public.tar.gz
+
+**汉化(容器)**
+
+```bash
+# choice1: 直接使用汉化的容器(基于官方v2.9.1, 替换/public)
+docker run -it --rm --net=host -v /var/run/docker.sock:/var/run/docker.sock registry.cn-shenzhen.aliyuncs.com/infrastlabs/portainer-cn
+
+# choice2: 生成public.tar.gz, 手动挂载到容器内使用
+# choice2_step1: 容器运行(node环境: 替换后 直接构建输出public.tar.gz)
+$ docker run -it --rm -v $(pwd)/output:/output registry.cn-shenzhen.aliyuncs.com/infrastlabs/lang-replacement
+# choice2_step1: 挂载/public目录来使用
+tar -zxf public.tar.gz
+docker run -it --rm --net=host -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd)/public:/public portainer/portainer-ce:2.9.1-alpine
+```
+
+**汉化(二进制用法)** 二进制运行替换后,手工build前端工程
+
+```bash
+# step1: 二进制运行替换
+# headless @ barge in .../lang-replacement/generate |15:53:12 |dev U:1 ?:2 ✗|
+$ ./lang-replacement ./portainer_zh.xml $(pwd)/portainer/app
+...
+i= 49 portainer/views/users/edit/user.html
+(replace)j= 0 {Change user password} > {修改密码}
+Replace-Copied 4325 bytes, backdir: /_ext/working/_ct/lang-replacement/generate/portainer/app/.lang-replacement/portainer/views/users/edit/user.html!
+i= 50 portainer/views/users/users.html
+(replace)j= 0 {Add a new user} > {添加用户}
+(replace)j= 1 {Users} > {用户管理}
+Replace-Copied 7171 bytes, backdir: /_ext/working/_ct/lang-replacement/generate/portainer/app/.lang-replacement/portainer/views/users/users.html!
+FINISH!
+
+# step2: 基于上1步替换后的源码, 手工build前端工程(Portainer需要在源码层做汉化替换)
+# ...
+```
+
+**生成汉化字典**
+
+```bash
+# headless @ barge in .../_ct/lang-replacement |14:42:10 |dev U:1 ✗| #dindMnt
+$ docker run -it --rm -v $(pwd)/output:/output registry.cn-shenzhen.aliyuncs.com/infrastlabs/lang-replacement:generate
+
+```
+
+## Dev开发说明
```bash
# headless @ barge in .../_ct/lang-replacement |11:39:37 |master ↑2 U:1 ?:1 ✗|
@@ -25,8 +75,9 @@ $ go build -o godiff -x -v -ldflags "-s -w $flags" ./diff/main.go
# -rwxr-xr-x 1 headless headless 2.6M 10月 9 10:09 main00*
```
-## Replacement模版生成
+**Replacement模版生成**
+```bash
TODO: git diff 锁定到行? > tpl > Replace指定行
**Portainer汉化**
@@ -40,26 +91,15 @@ TODO: git diff 锁定到行? > tpl > Replace指定行
- app/edge
https://www.bejson.com/xml2json/
+```
-## Use
+**buildPortainer**
```bash
-# 生成汉化字典
-# headless @ barge in .../_ct/lang-replacement |14:42:10 |dev U:1 ✗| #dindMnt
-$ docker run -it --rm -v /mnt/data/$(pwd)/output:/output registry.cn-shenzhen.aliyuncs.com/infrastlabs/lang-replacement
-
-# 汉化
-# headless @ barge in .../lang-replacement/generate |15:53:12 |dev U:1 ?:2 ✗|
-$ ./lang-replacement ./portainer_zh.xml $(pwd)/portainer/app
-...
-i= 49 portainer/views/users/edit/user.html
-(replace)j= 0 {Change user password} > {修改密码}
-Replace-Copied 4325 bytes, backdir: /_ext/working/_ct/lang-replacement/generate/portainer/app/.lang-replacement/portainer/views/users/edit/user.html!
-i= 50 portainer/views/users/users.html
-(replace)j= 0 {Add a new user} > {添加用户}
-(replace)j= 1 {Users} > {用户管理}
-Replace-Copied 7171 bytes, backdir: /_ext/working/_ct/lang-replacement/generate/portainer/app/.lang-replacement/portainer/views/users/users.html!
-FINISH!
+# https://www.cnblogs.com/ccti7/p/13956678.html
+# npm install image-webpack-loader --save-dev
+yarn add image-webpack-loader -D
+yarn add image-webpack-loader -g
```
@@ -68,3 +108,4 @@ FINISH!
- https://github.com/jsonljd/konga-lang-plugin
- http://www.zzvips.com/article/167183.html
- https://blog.csdn.net/qiuyoujie/article/details/79289181
+
diff --git a/custom.md b/custom.md
new file mode 100644
index 0000000..85ef908
--- /dev/null
+++ b/custom.md
@@ -0,0 +1,38 @@
+
+**dict/public.tar.gz**
+
+```bash
+# dict生成
+# ENV GENERATE_REPO="https://gitee.com/g-devops/fk-portainer" \
+# GENERATE_OUTPUT="portainer_zh.xml" \
+# CMP1="2.9.1" \
+# CMP2="origin/br-lang2"
+docker run -it --rm -e CMP1=2.9.1 -e CMP2=origin/br-lang3 -v /mnt/data/$(pwd)/output:/output registry.cn-shenzhen.aliyuncs.com/infrastlabs/lang-replacement:dict
+
+
+# xml2json
+# wget https://hub.fastgit.org/covrom/xml2json/releases/download/1.0/xml2json
+cat portainer_zh.xml |./xml2json |jq
+
+```
+
+**publicMount**
+
+```bash
+# barge: 生成public.tar.gz
+# ENV \
+# REPO="https://gitee.com/g-devops/fk-portainer" \
+# # BRANCH="release/2.9" \
+# TAG="2.9.1"
+
+# TAG=2.9.0
+docker run -it --rm -e BRANCH=sam-custom -v /mnt/data/$(pwd)/output:/output registry.cn-shenzhen.aliyuncs.com/infrastlabs/lang-replacement:replace
+
+
+
+# tar -zxf public.tar.gz
+public=/mnt/data/$(pwd)/output/portainer/dist/public
+docker run -it --rm --net=host -v /var/run/docker.sock:/var/run/docker.sock -v $public:/public portainer/portainer-ce:2.9.1-alpine
+
+
+```
diff --git a/entry.sh b/entry.sh
index 5a446a1..ea2d2cb 100755
--- a/entry.sh
+++ b/entry.sh
@@ -1,15 +1,36 @@
#!/bin/bash
-outPath="/output" && mkdir -p $outPath
-# if EXEC_TYPE="GENERATE"
-# $AUTH
-git clone -b $GENERATE_BRANCH $GENERATE_REPO srcGen
-export SOURCE=srcGen/app
-export CMP1=055c57
-export CMP2=br-v29-lang
-export OUTPUT=$outPath/$GENERATE_OUTPUT
-./gitdiff.sh
+# $AUTH #-b $GENERATE_BRANCH
+# TODO /output/.cache/pt0_dict
+mkdir -p /output/.cache; srcGenerate=/output/.cache/pt0_dict
+# git clone $GENERATE_REPO $srcGenerate #each newDir, just normal clone.
+# repo
+function getRepo(){
+ errExit(){
+ echo "$1"
+ exit 1
+ }
+ if [ ! -d $srcGenerate ]; then
+ git clone $GENERATE_REPO $srcGenerate #--depth=1
+ else
+ cd $srcGenerate;
+ git fetch
+ # if both tag:
+ git fetch origin tag $CMP1
+ git fetch origin tag $CMP2
+ fi
+}
+getRepo
+
-cat $OUTPUT |wc
-# SOURCE=srcReplace
+outPath="/output" && mkdir -p $outPath
+# export CMP1=055c57
+# export CMP2=br-lang2
+export SOURCE=$srcGenerate/app
+export OUTPUT=$outPath/$GENERATE_OUTPUT
+/generate/gitdiff.sh
+# view
+cat $OUTPUT |wc; tail -30 $OUTPUT
+# /generate/transfer -f -s $OUTPUT -t /tmp/view.json; cat /tmp/view.json |jq #tranferErr: got "null"
+cat $OUTPUT |/generate/xml2json |jq
diff --git a/generate/dictReplace.txt b/generate/dictReplace.txt
new file mode 100644
index 0000000..f6e9938
--- /dev/null
+++ b/generate/dictReplace.txt
@@ -0,0 +1,13 @@
+|
+|
+|
+|
+|
+
+|
+|
+|
+
+
+
+
diff --git a/generate/gen_portainer.sh b/generate/gen_portainer.sh
index c3b33f7..74c738a 100644
--- a/generate/gen_portainer.sh
+++ b/generate/gen_portainer.sh
@@ -8,7 +8,8 @@ BRANCH="br-v29-lang"
# export CMP1=055c57
# export CMP2=origin/br-v29-lang
-export SOURCE="/_ext/bbox/_ee/fk-portainer/app"
+export SOURCE="/_ext/working/_ct/fk-portainer/app"
+# export SOURCE="/_ext/bbox/_ee/fk-portainer/app"
export CMP1=604f2823428aa26401b5b0f1ba118eb494325edb
export CMP2=br-lang2 #origin/br-v29-lang
# export SOURCE="portainer/app"
diff --git a/generate/gitdiff.sh b/generate/gitdiff.sh
index ec9a42c..0fa330d 100755
--- a/generate/gitdiff.sh
+++ b/generate/gitdiff.sh
@@ -116,3 +116,18 @@ cat $tmp/root.txt > $tmp/root.json
# touch $OUTPUT #if notExist, transfer err (in alpine)
$cur/transfer -f -s $tmp/root.json -t $OUTPUT
# rm -rf $tmp
+
+function dictReplace(){
+ echo "==[dictReplace]==============="
+ cat $cur/dictReplace.txt |grep -v "^#" |grep -v "^$" | while read one; do
+ # echo $one
+ f1=$(echo $one |cut -d'|' -f1)
+ f2=$(echo $one |cut -d'|' -f2)
+ f1=$(echo $f1 |sed "s/\[/\\\[/g" |sed "s/\!/\\\!/g" |sed "s/\]/\\\]/g")
+ f2=$(echo $f2 |sed "s/\[/\\\[/g" |sed "s/\!/\\\!/g" |sed "s/\]/\\\]/g")
+ echo "$f1"; echo "$f2"; echo ""
+
+ sed -i "s^$f1^$f2^g" $OUTPUT #$cur/portainer_zh.xml
+ done
+}
+dictReplace
\ No newline at end of file
diff --git a/generate/portainer_zh.xml b/generate/portainer_zh.xml
index 11640aa..8b1b05a 100644
--- a/generate/portainer_zh.xml
+++ b/generate/portainer_zh.xml
@@ -135,7 +135,7 @@
-
+
@@ -143,7 +143,10 @@
-
-
+
+
+
+
docker/views/containers/containers.html
@@ -205,7 +208,10 @@
-
-
+
+
+
+
docker/views/events/events.html
@@ -215,6 +221,10 @@
+
+
+
+
docker/views/images/images.html
@@ -259,7 +269,7 @@
-
+
@@ -271,7 +281,10 @@
-
-
+
+
+
+
docker/views/networks/networks.html
@@ -339,7 +352,10 @@
-
-
+
+
+
+
docker/views/volumes/volumes.html
@@ -450,7 +466,10 @@
-
-
+
+
+
+
portainer/views/registries/registries.html
@@ -488,7 +507,7 @@
-
-
+
diff --git a/generate/rep_portainer.sh b/generate/rep_portainer.sh
deleted file mode 100644
index c2ec23c..0000000
--- a/generate/rep_portainer.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-cur=$(cd "$(dirname "$0")"; pwd)
-cd $cur
-
-REPO="https://gitee.com/g-devops/fk-portainer"
-BRANCH="release/2.9"
-# git clone --depth=1 -b $BRANCH $REPO portainer
-
-./lang-replacement ./portainer_zh.xml ./portainer/app
diff --git a/generate/test.md b/generate/test.md
new file mode 100644
index 0000000..d8528e7
--- /dev/null
+++ b/generate/test.md
@@ -0,0 +1,8 @@
+
+```bash
+headless @ barge in .../lang-replacement/generate |17:31:51 |dev U:3 ?:3 ✗|
+$ cat t1.txt |sed "s^<\!\[CDATA\[^bb/^g"
+bb/
+# $ cat t1.txt
+> go源码编译/jqEnv >> 运行时:Clone代码_br-lang2生成dict (entry/gitdiff.sh)
+ img="lang-replacement:dict" #v1-generate
+ docker build $cache $pull -t $repo/$ns/$img -f Dockerfile .
+ # push
+ docker push $repo/$ns/$img
+ # barge=/mnt/data/ ## dind: out-binary
+ docker run -it --rm --entrypoint=bash -v $barge$(pwd)/generate:/mnt $repo/$ns/$img -c "ls -lh /mnt/; cp -a transfer godiff lang-replacement xml2json /mnt/; ls -lh /mnt/"
+ ;;
+ cache) #node_modules @v291
+ img="lang-replacement:cache" #v1-generate
+ cd output/.cache; docker build $cache $pull -t $repo/$ns/$img -f Dockerfile .
+ docker push $repo/$ns/$img
+ ;;
+ pt) #在上一步基础上直接生成CN版 portainer-ce镜像.
+ img="portainer-cn:latest" #-v291
+ cd replacement; docker build $cache $pull -t $repo/$ns/$img -f pt.Dockerfile .
+ docker push $repo/$ns/$img
+ ;;
+ slim) #*) >> node-slim
+ img="lang-replacement:replace-slim"
+ cd replacement; docker build $cache $pull -t $repo/$ns/$img -f Dockerfile . #cd replacement
+ docker push $repo/$ns/$img
+ ;;
+ *) #./replacement/Dockerfile >> Node环境+lang_placement+node_modules >> 运行时: Clone发版的代码, 替换CN, npm构建生成public.tar.gz
+ img="lang-replacement:replace" #v1-generate
+ cd replacement; docker build $cache $pull -t $repo/$ns/$img -f Dockerfile.alpine . #cd replacement
+ docker push $repo/$ns/$img
+ # latest
+ img2="lang-replacement:latest"
+ docker tag $repo/$ns/$img docker push $repo/$ns/$img2
+ docker push $repo/$ns/$img2
+ ;;
+esac
-# dind: out-binary
-# barge=/mnt/data/
-docker run -it --rm --entrypoint=bash -v $barge$(pwd)/generate:/mnt $repo/$ns/$img -c "ls -lh /mnt/; cp -a transfer godiff lang-replacement /mnt/; ls -lh /mnt/"
diff --git a/output/.cache/Dockerfile b/output/.cache/Dockerfile
new file mode 100644
index 0000000..20c7f61
--- /dev/null
+++ b/output/.cache/Dockerfile
@@ -0,0 +1,2 @@
+FROM scratch
+ADD ./node_modules /.cache/node_modules
\ No newline at end of file
diff --git a/output/dev_clone.sh b/output/dev_clone.sh
new file mode 100644
index 0000000..1b0f481
--- /dev/null
+++ b/output/dev_clone.sh
@@ -0,0 +1,29 @@
+
+REPO="https://gitee.com/g-devops/fk-portainer"
+# BRANCH="release/2.9"
+TAG="2.9.0" #TAG
+
+errExit(){
+ echo "$1"
+ exit 1
+}
+test -z "$BRANCH" && test -z "$TAG" && errExit "BRANCH/TAG both emp, must set one"
+if [ ! -z "$BRANCH" ]; then
+ # ; git pull
+ test -d pt0 && (cd pt0; git fetch; git checkout origin/$BRANCH) || git clone -b $BRANCH $REPO pt0 #--depth=1
+else
+ # test -z "$BRANCH" && BRANCH=$TAG #use tag
+ # -b "br-$TAG"
+ test -d pt0 && (cd pt0; git fetch origin tag $TAG; git checkout $TAG) || git clone -b $TAG $REPO pt0 #--depth=1
+fi
+
+
+echo building...; sleep 10
+# REPLACE
+mkdir -p .cache/node_modules
+rm -rf portainer0; cp -a pt0 portainer0
+cd portainer0
+ rm -rf node_modules; ln -s ../.cache/node_modules .;
+ yarn config set registry https://registry.npm.taobao.org -g
+ yarn config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g
+ yarn install #imageUtils's deps hand breadIns; #395M
diff --git a/replacement/.dockerignore b/replacement/.dockerignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/replacement/.dockerignore
@@ -0,0 +1 @@
+node_modules
diff --git a/replacement/Dockerfile b/replacement/Dockerfile
new file mode 100644
index 0000000..2283659
--- /dev/null
+++ b/replacement/Dockerfile
@@ -0,0 +1,52 @@
+FROM registry.cn-shenzhen.aliyuncs.com/infrastlabs/lang-replacement:dict as bins
+FROM registry.cn-shenzhen.aliyuncs.com/infrastlabs/lang-replacement:cache as cache
+
+# ref: dvp-ci-mgr.ui-frontend
+# FROM node:10.15.0-alpine AS builder
+ # ref: docs-devops_vuepress
+ # node:14.13.1-alpine #39.36 MB
+ # node:14.13.1-stretch-slim #57.83 MB
+# FROM node:14.13.1-alpine AS builder
+FROM node:14.13.1-stretch-slim AS builder
+
+# RUN domain="mirrors.aliyun.com" \
+# && echo "http://$domain/alpine/v3.8/main" > /etc/apk/repositories \
+# && echo "http://$domain/alpine/v3.8/community" >> /etc/apk/repositories \
+# && apk add git bash curl wget jq
+# portainer: yarn install
+# RUN apk add autoconf libtool libpng automake gcc
+RUN domain="mirrors.163.com" \
+ && echo "deb http://$domain/debian/ stretch main contrib non-free" > /etc/apt/sources.list \
+ && echo "deb http://$domain/debian/ stretch-updates main contrib non-free">> /etc/apt/sources.list; \
+ \
+ echo 'apt update -qq && apt install -yq --no-install-recommends $@ && apt-get clean && rm -rf /var/lib/apt/lists/*; ' > /usr/local/bin/apt.sh \
+ && chmod +x /usr/local/bin/apt.sh
+RUN apt.sh \ git bash curl wget jq libpng*
+
+RUN \
+ # npm
+ npm -v; \
+ npm config set registry=https://registry.npm.taobao.org -g; \
+ npm config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g; \
+ # npm install -g yarn #installed
+ # grunt
+ npm install -g grunt-cli; \
+ grunt -h; \
+ # yarn
+ yarn -v; \
+ yarn config set registry https://registry.npm.taobao.org -g; \
+ yarn config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g
+
+# TODO: node_mods from res_repo
+# ADD ./node_modules /.cache/node_modules
+COPY --from=cache /.cache/node_modules /.cache/node_modules
+ADD ./entry.sh /entry.sh
+ADD ./conf/webpack.production.js /conf/webpack.production.js
+ADD ./conf/gruntfile.js /conf/gruntfile.js
+COPY --from=bins /generate/lang-replacement /usr/local/bin/
+WORKDIR /output
+
+# VOLUME ["/data"]
+# EXPOSE 8080
+ENTRYPOINT ["/entry.sh"]
+# RUN apk add libpng
\ No newline at end of file
diff --git a/replacement/Dockerfile.alpine b/replacement/Dockerfile.alpine
new file mode 100644
index 0000000..1b9eb19
--- /dev/null
+++ b/replacement/Dockerfile.alpine
@@ -0,0 +1,48 @@
+FROM registry.cn-shenzhen.aliyuncs.com/infrastlabs/lang-replacement:dict as bins
+FROM registry.cn-shenzhen.aliyuncs.com/infrastlabs/lang-replacement:cache as cache
+
+# ref: dvp-ci-mgr.ui-frontend
+# FROM node:10.15.0-alpine AS builder
+# ref: docs-devops_vuepress
+FROM node:14.13.1-alpine AS builder
+MAINTAINER sam
+
+RUN domain="mirrors.aliyun.com" \
+&& echo "http://$domain/alpine/v3.8/main" > /etc/apk/repositories \
+&& echo "http://$domain/alpine/v3.8/community" >> /etc/apk/repositories \
+&& apk add git bash curl wget jq
+# portainer: yarn install
+# RUN apk add autoconf libtool libpng automake gcc
+
+RUN \
+ # npm
+ npm -v; \
+ npm config set registry=https://registry.npm.taobao.org -g; \
+ npm config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g; \
+ # npm install -g yarn #installed
+ # grunt
+ npm install -g grunt-cli; \
+ grunt -h; \
+ # yarn
+ yarn -v; \
+ yarn config set registry https://registry.npm.taobao.org -g; \
+ yarn config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g
+
+# TODO: node_mods from res_repo
+# ADD ./node_modules /.cache/node_modules
+COPY --from=cache /.cache/node_modules /.cache/node_modules
+ADD ./entry.sh /entry.sh
+ADD ./conf/webpack.production.js /conf/webpack.production.js
+ADD ./conf/gruntfile.js /conf/gruntfile.js
+COPY --from=bins /generate/lang-replacement /usr/local/bin/
+WORKDIR /output
+
+ENV \
+ REPO="https://gitee.com/g-devops/fk-portainer" \
+ # BRANCH="release/2.9" \
+ TAG="2.9.1"
+
+# VOLUME ["/data"]
+# EXPOSE 8080
+ENTRYPOINT ["/entry.sh"]
+RUN apk add libpng
\ No newline at end of file
diff --git a/replacement/conf/gruntfile.js b/replacement/conf/gruntfile.js
new file mode 100644
index 0000000..f637395
--- /dev/null
+++ b/replacement/conf/gruntfile.js
@@ -0,0 +1,279 @@
+var os = require('os');
+var loadGruntTasks = require('load-grunt-tasks');
+const webpackDevConfig = require('./webpack/webpack.develop');
+const webpackProdConfig = require('./webpack/webpack.production');
+const webpackTestingConfig = require('./webpack/webpack.testing');
+
+var arch = os.arch();
+if (arch === 'x64') arch = 'amd64';
+
+var portainer_data = '${PORTAINER_DATA:-/tmp/portainer}';
+var portainer_root = process.env.PORTAINER_PROJECT ? process.env.PORTAINER_PROJECT : process.env.PWD;
+
+module.exports = function (grunt) {
+ loadGruntTasks(grunt, {
+ pattern: ['grunt-*', 'gruntify-*'],
+ });
+
+ grunt.initConfig({
+ root: 'dist',
+ distdir: 'dist/public',
+ binaries: {
+ dockerLinuxVersion: '19.03.13',
+ dockerWindowsVersion: '19-03-12',
+ dockerLinuxComposeVersion: '1.27.4',
+ dockerWindowsComposeVersion: '1.28.0',
+ dockerComposePluginVersion: '2.0.0-beta.6',
+ helmVersion: 'v3.6.3',
+ komposeVersion: 'v1.22.0',
+ kubectlVersion: 'v1.18.0',
+ },
+ config: gruntfile_cfg.config,
+ env: gruntfile_cfg.env,
+ src: gruntfile_cfg.src,
+ clean: gruntfile_cfg.clean,
+ eslint: gruntfile_cfg.eslint,
+ shell: gruntfile_cfg.shell,
+ copy: gruntfile_cfg.copy,
+ webpack: gruntfile_cfg.webpack,
+ });
+
+ grunt.registerTask('lint', ['eslint']);
+
+ grunt.registerTask('build:server', [
+ // 'shell:build_binary:linux:' + arch,
+ // 'shell:download_docker_binary:linux:' + arch,
+ // 'shell:download_docker_compose_binary:linux:' + arch,
+ // 'shell:download_helm_binary:linux:' + arch,
+ // 'shell:download_kompose_binary:linux:' + arch,
+ // 'shell:download_kubectl_binary:linux:' + arch,
+ ]);
+
+ grunt.registerTask('build:client', ['config:dev', 'env:dev', 'webpack:dev']);
+
+ grunt.registerTask('build', ['build:server', 'build:client', 'copy:assets']);
+
+ grunt.registerTask('start:server', ['build:server', 'copy:assets', 'shell:run_container']);
+
+ grunt.registerTask('start:localserver', ['shell:build_binary:linux:' + arch, 'shell:run_localserver']);
+
+ grunt.registerTask('start:client', ['shell:install_yarndeps', 'config:dev', 'env:dev', 'webpack:devWatch']);
+
+ grunt.registerTask('start', ['start:server', 'start:client']);
+
+ grunt.registerTask('start:toolkit', ['start:localserver', 'start:client']);
+
+ grunt.task.registerTask('release', 'release::', function (p = 'linux', a = arch) {
+ grunt.task.run([
+ 'config:prod',
+ 'env:prod',
+ 'clean:all',
+ 'copy:assets',
+ 'shell:build_binary:' + p + ':' + a,
+ 'shell:download_docker_binary:' + p + ':' + a,
+ 'shell:download_docker_compose_binary:' + p + ':' + a,
+ 'shell:download_helm_binary:' + p + ':' + a,
+ 'shell:download_kompose_binary:' + p + ':' + a,
+ 'shell:download_kubectl_binary:' + p + ':' + a,
+ 'webpack:prod',
+ ]);
+ });
+
+ grunt.task.registerTask('devopsbuild', 'devopsbuild:::', function (p, a, env = 'prod') {
+ grunt.task.run([
+ 'config:prod',
+ `env:${env}`,
+ 'clean:all',
+ 'copy:assets',
+ // 'shell:build_binary_azuredevops:' + p + ':' + a,
+ // 'shell:download_docker_binary:' + p + ':' + a,
+ // 'shell:download_docker_compose_binary:' + p + ':' + a,
+ // 'shell:download_helm_binary:' + p + ':' + a,
+ // 'shell:download_kompose_binary:' + p + ':' + a,
+ // 'shell:download_kubectl_binary:' + p + ':' + a,
+ `webpack:${env}`,
+ ]);
+ });
+};
+
+/***/
+var gruntfile_cfg = {};
+
+gruntfile_cfg.env = {
+ dev: {
+ NODE_ENV: 'development',
+ },
+ prod: {
+ NODE_ENV: 'production',
+ },
+ testing: {
+ NODE_ENV: 'testing',
+ },
+};
+
+gruntfile_cfg.webpack = {
+ dev: webpackDevConfig,
+ devWatch: Object.assign({ watch: true }, webpackDevConfig),
+ prod: webpackProdConfig,
+ testing: webpackTestingConfig,
+};
+
+gruntfile_cfg.config = {
+ dev: { options: { variables: { environment: 'development' } } },
+ prod: { options: { variables: { environment: 'production' } } },
+};
+
+gruntfile_cfg.src = {
+ js: ['app/**/__module.js', 'app/**/*.js', '!app/**/*.spec.js'],
+ jsTpl: ['<%= distdir %>/templates/**/*.js'],
+ html: ['index.html'],
+ tpl: ['app/**/*.html'],
+ css: ['assets/css/app.css', 'app/**/*.css'],
+};
+
+gruntfile_cfg.clean = {
+ server: ['<%= root %>/portainer'],
+ client: ['<%= distdir %>/*'],
+ all: ['<%= root %>/*'],
+};
+
+gruntfile_cfg.eslint = {
+ src: ['gruntfile.js', '<%= src.js %>'],
+ options: { configFile: '.eslintrc.yml' },
+};
+
+gruntfile_cfg.copy = {
+ assets: {
+ files: [],
+ },
+};
+
+gruntfile_cfg.shell = {
+ build_binary: { command: shell_build_binary },
+ build_binary_azuredevops: { command: shell_build_binary_azuredevops },
+ download_docker_binary: { command: shell_download_docker_binary },
+ download_kompose_binary: { command: shell_download_kompose_binary },
+ download_kubectl_binary: { command: shell_download_kubectl_binary },
+ download_helm_binary: { command: shell_download_helm_binary },
+ download_docker_compose_binary: { command: shell_download_docker_compose_binary },
+ run_container: { command: shell_run_container },
+ run_localserver: { command: shell_run_localserver, options: { async: true } },
+ install_yarndeps: { command: shell_install_yarndeps },
+};
+
+function shell_build_binary(p, a) {
+ var binfile = 'dist/portainer';
+ if (p === 'linux') {
+ return ['if [ -f ' + binfile + ' ]; then', 'echo "Portainer binary exists";', 'else', 'build/build_binary.sh ' + p + ' ' + a + ';', 'fi'].join(' ');
+ } else {
+ return [
+ 'powershell -Command "& {if (Get-Item -Path ' + binfile + '.exe -ErrorAction:SilentlyContinue) {',
+ 'Write-Host "Portainer binary exists"',
+ '} else {',
+ '& ".\\build\\build_binary.ps1" -platform ' + p + ' -arch ' + a + '',
+ '}}"',
+ ].join(' ');
+ }
+}
+
+function shell_build_binary_azuredevops(p, a) {
+ return 'build/build_binary_azuredevops.sh ' + p + ' ' + a + ';';
+}
+
+function shell_run_container() {
+ return [
+ 'docker rm -f portainer',
+ 'docker run -d -p 8000:8000 -p 9000:9000 -p 9443:9443 -v ' +
+ portainer_root +
+ '/dist:/app -v ' +
+ portainer_data +
+ ':/data -v /var/run/docker.sock:/var/run/docker.sock:z -v /var/run/docker.sock:/var/run/alternative.sock:z -v /tmp:/tmp --name portainer portainer/base /app/portainer',
+ ].join(';');
+}
+
+function shell_run_localserver() {
+ return './dist/portainer';
+}
+
+function shell_install_yarndeps() {
+ return 'yarn';
+}
+
+function shell_download_docker_binary(p, a) {
+ var ps = { windows: 'win', darwin: 'mac' };
+ var as = { amd64: 'x86_64', arm: 'armhf', arm64: 'aarch64' };
+ var ip = ps[p] === undefined ? p : ps[p];
+ var ia = as[a] === undefined ? a : as[a];
+ var binaryVersion = p === 'windows' ? '<%= binaries.dockerWindowsVersion %>' : '<%= binaries.dockerLinuxVersion %>';
+
+ return [
+ 'if [ -f dist/docker ] || [ -f dist/docker.exe ]; then',
+ 'echo "docker binary exists";',
+ 'else',
+ 'build/download_docker_binary.sh ' + ip + ' ' + ia + ' ' + binaryVersion + ';',
+ 'fi',
+ ].join(' ');
+}
+
+function shell_download_docker_compose_binary(p, a) {
+ var ps = { windows: 'win', darwin: 'mac' };
+ var as = { arm: 'armhf', arm64: 'aarch64' };
+ var ip = ps[p] || p;
+ var ia = as[a] || a;
+ var binaryVersion = p === 'windows' ? '<%= binaries.dockerWindowsComposeVersion %>' : '<%= binaries.dockerLinuxComposeVersion %>';
+
+ // plugin
+ if (p === 'linux' && a !== 'amd64') {
+ if (a === 'arm64') {
+ ia = 'arm64';
+ }
+
+ if (a === 'arm') {
+ ia = 'armv7';
+ }
+ binaryVersion = '<%= binaries.dockerComposePluginVersion %>';
+ }
+
+ return `
+ if [ -f dist/docker-compose ] || [ -f dist/docker-compose.exe ] || [ -f dist/docker-compose.plugin ] || [ -f dist/docker-compose.plugin.exe ]; then
+ echo "Docker Compose binary exists";
+ else
+ build/download_docker_compose_binary.sh ${ip} ${ia} ${binaryVersion};
+ fi`;
+}
+
+function shell_download_helm_binary(p, a) {
+ var binaryVersion = '<%= binaries.helmVersion %>';
+
+ return [
+ 'if [ -f dist/helm ] || [ -f dist/helm.exe ]; then',
+ 'echo "helm binary exists";',
+ 'else',
+ 'build/download_helm_binary.sh ' + p + ' ' + a + ' ' + binaryVersion + ';',
+ 'fi',
+ ].join(' ');
+}
+
+function shell_download_kompose_binary(p, a) {
+ var binaryVersion = '<%= binaries.komposeVersion %>';
+
+ return [
+ 'if [ -f dist/kompose ] || [ -f dist/kompose.exe ]; then',
+ 'echo "kompose binary exists";',
+ 'else',
+ 'build/download_kompose_binary.sh ' + p + ' ' + a + ' ' + binaryVersion + ';',
+ 'fi',
+ ].join(' ');
+}
+
+function shell_download_kubectl_binary(p, a) {
+ var binaryVersion = '<%= binaries.kubectlVersion %>';
+
+ return [
+ 'if [ -f dist/kubectl ] || [ -f dist/kubectl.exe ]; then',
+ 'echo "kubectl binary exists";',
+ 'else',
+ 'build/download_kubectl_binary.sh ' + p + ' ' + a + ' ' + binaryVersion + ';',
+ 'fi',
+ ].join(' ');
+}
diff --git a/replacement/conf/webpack.production.js b/replacement/conf/webpack.production.js
new file mode 100644
index 0000000..7204968
--- /dev/null
+++ b/replacement/conf/webpack.production.js
@@ -0,0 +1,39 @@
+const webpackMerge = require('webpack-merge');
+const commonConfig = require('./webpack.common.js');
+
+module.exports = webpackMerge(commonConfig, {
+ mode: 'production',
+ // devtool: 'source-map',
+ module: {
+ rules: [
+ /* {
+ test: /\.(woff|woff2|eot|ttf|ico)$/,
+ use: [
+ {
+ loader: 'url-loader',
+ options: { limit: 25000 },
+ },
+ ],
+ },
+ {
+ test: /\.(gif|png|jpe?g|svg)$/i,
+ use: [
+ 'file-loader',
+ {
+ loader: 'image-webpack-loader',
+ options: {},
+ },
+ ],
+ }, */
+ {
+ test: /\.(woff|woff2|eot|ttf|svg|ico|png|jpg|gif)$/,
+ use: [
+ {
+ loader: 'file-loader',
+ // options: { limit: 25000 }
+ },
+ ],
+ },
+ ],
+ },
+});
diff --git a/replacement/entry.sh b/replacement/entry.sh
new file mode 100755
index 0000000..c7ccc27
--- /dev/null
+++ b/replacement/entry.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+# cur=$(cd "$(dirname "$0")"; pwd)
+# cd $cur
+
+function npmBuild(){
+ cd /output/portainer
+
+ # # npm
+ # npm -v
+ # npm config set registry=https://registry.npm.taobao.org -g
+ # npm config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g
+ # # npm install -g yarn #installed
+ # # grunt
+ # npm install -g grunt-cli
+ # grunt -h
+
+ # # yarn
+ # yarn -v
+ # yarn config set registry https://registry.npm.taobao.org -g
+ # yarn config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g
+ # .cache
+ # mkdir -p /output/.cache/node_modules; rm -rf node_modules; ln -s /output/.cache/node_modules .;
+ rm -rf node_modules; ln -s /.cache/node_modules .;
+ # yarn install
+
+ ## grunt build
+ # grunt build #OK
+ # npm run build
+ ## grunt build prod
+ rm -f webpack/webpack.production.js; cp /conf/webpack.production.js webpack/webpack.production.js
+ rm -f gruntfile.js; cp /conf/gruntfile.js gruntfile.js
+ grunt devopsbuild
+}
+
+# repo
+function getRepo(){
+ errExit(){
+ echo "$1"
+ exit 1
+ }
+ test -z "$BRANCH" && test -z "$TAG" && errExit "BRANCH/TAG both emp, must set one"
+ if [ ! -z "$BRANCH" ]; then
+ test -d pt0 && (cd pt0; git fetch -t; git checkout origin/$BRANCH) || (git clone -b $BRANCH $REPO pt0; cd pt0; git checkout origin/$BRANCH) #--depth=1
+ else
+ test -d pt0 && (cd pt0; git fetch origin tag $TAG; git checkout $TAG) || git clone -b $TAG $REPO pt0 #--depth=1
+ fi
+}
+# REPO="https://gitee.com/g-devops/fk-portainer"
+# BRANCH="release/2.9"
+# TAG="2.9.0" #TAG
+# test -d pt0 && (cd pt0; git pull) || git clone --depth=1 -b $BRANCH $REPO pt0
+getRepo
+
+# dict portainer_zh.xml
+curl -O https://gitee.com/g-devops/lang-replacement/raw/dev/generate/portainer_zh.xml
+
+# REPLACE
+rm -rf portainer; cp -a pt0 portainer
+# out app/.lang-replacement
+lang-replacement ./portainer_zh.xml ./portainer/app
+rm -rf .lang-replacement; mv ./portainer/app/.lang-replacement .
+# BUILD
+npmBuild
+
+# PACK
+cd /output/portainer/dist; tar -zcf ./public.tar.gz public
+ls -lh /output/portainer/dist/
\ No newline at end of file
diff --git a/replacement/pt.Dockerfile b/replacement/pt.Dockerfile
new file mode 100644
index 0000000..32203c1
--- /dev/null
+++ b/replacement/pt.Dockerfile
@@ -0,0 +1,52 @@
+FROM registry.cn-shenzhen.aliyuncs.com/infrastlabs/lang-replacement:dict as bins
+FROM registry.cn-shenzhen.aliyuncs.com/infrastlabs/lang-replacement:cache as cache
+
+# ref: dvp-ci-mgr.ui-frontend
+# FROM node:10.15.0-alpine AS builder
+# ref: docs-devops_vuepress
+FROM node:14.13.1-alpine AS builder
+MAINTAINER sam
+
+RUN domain="mirrors.aliyun.com" \
+&& echo "http://$domain/alpine/v3.8/main" > /etc/apk/repositories \
+&& echo "http://$domain/alpine/v3.8/community" >> /etc/apk/repositories \
+&& apk add git bash curl wget jq
+# portainer: yarn install
+# RUN apk add autoconf libtool libpng automake gcc
+
+RUN \
+ # npm
+ npm -v; \
+ npm config set registry=https://registry.npm.taobao.org -g; \
+ npm config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g; \
+ # npm install -g yarn #installed
+ # grunt
+ npm install -g grunt-cli; \
+ grunt -h; \
+ # yarn
+ yarn -v; \
+ yarn config set registry https://registry.npm.taobao.org -g; \
+ yarn config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g
+
+# TODO: node_mods from res_repo
+# ADD ./node_modules /.cache/node_modules
+COPY --from=cache /.cache/node_modules /.cache/node_modules
+ADD ./entry.sh /entry.sh
+ADD ./conf/webpack.production.js /conf/webpack.production.js
+ADD ./conf/gruntfile.js /conf/gruntfile.js
+COPY --from=bins /generate/lang-replacement /usr/local/bin/
+WORKDIR /output
+
+# VOLUME ["/data"]
+# EXPOSE 8080
+ENTRYPOINT ["/entry.sh"]
+RUN apk add libpng
+
+#####################
+#just lastStage build
+RUN /entry.sh
+FROM portainer/portainer-ce:2.9.1-alpine
+RUN rm -rf /public
+COPY --from=builder /output/portainer/dist/public/ /public/
+RUN ls -lh /public
+