UiPath CLI 用户指南
此页为您提供完整的.gitlab-ci.yml ,用于安装 CLI,使用外部应用程序进行身份验证,打包并发布 UiPath 解决方案,将其部署到跨多个租户的 Orchestrator(通过矩阵),并运行 Test Manager 套件。将其拖放到存储库的根目录中,设置三个 CI/CD 变量,然后它就会运行。
有关基本原则(身份验证、缓存、工具预安装、版本固定),请参阅操作方法:从 CI 部署到 Orchestrator 。本页面重点介绍 GitLab 语法,包括 GitLab 特定功能(具有键定作用域的缓存, parallel: matrix )。
先决条件
在复制以下 YAML 之前:
- 在 UiPath 中使用管道所需的
OR.*作用域创建外部应用程序。请参阅身份验证 — 流程2 。 - 将密码存储为 CI/CD 变量:
- 项目→设置→CI/CD→变量。
- 添加
UIPATH_CLIENT_ID和UIPATH_CLIENT_SECRET。将两者标记为“已掩码”和“已保护” (因此它们仅在受保护的分支/标签上公开)。 - 添加
UIPATH_TENANT_DEV、UIPATH_TENANT_STAGE、UIPATH_TENANT_PROD以及租户名称(未掩码 — 租户名称不敏感)。
- 如果您需要测试作业,请配置 Test Manager 项目和测试集。将
TEST_SET_KEY和PROJECT_KEY添加为常规变量。
.gitLab-ci.yml
# -----------------------------------------------------------------------------
# Deploy UiPath Solution
# -----------------------------------------------------------------------------
# Auth: External Application, env.VAR_NAME prefix (never the literal value).
# Cache: npm global node_modules, keyed by CLI version.
# Matrix: deploy job fans out across dev / stage / prod tenants.
# -----------------------------------------------------------------------------
image: node:20
stages:
- build
- deploy
- test
variables:
CLI_VERSION: '1.0.0'
SOLUTION_NAME: 'my-solution'
SOLUTION_DIR: './my-solution'
OUTPUT_DIR: './dist'
SOLUTION_VERSION: '1.2.0-ci.$CI_PIPELINE_IID'
# Workspace-local npm prefix so installs need no sudo and are cacheable.
NPM_PREFIX: "$CI_PROJECT_DIR/.npm-global"
# Re-usable install block. GitLab does not have anchors for script:; we use
# YAML anchors on a hidden job and extend from it.
.install-uip: &install-uip |
set -euo pipefail
mkdir -p "$NPM_PREFIX"
npm config set prefix "$NPM_PREFIX"
export PATH="$NPM_PREFIX/bin:$PATH"
if ! command -v uip >/dev/null; then
npm install -g "@uipath/cli@$CLI_VERSION"
fi
uip --version
cache:
key: "uip-$CLI_VERSION"
paths:
- .npm-global/lib/node_modules
policy: pull-push
# -----------------------------------------------------------------------------
# Stage: build
# -----------------------------------------------------------------------------
pack:
stage: build
script:
- *install-uip
- mkdir -p "$OUTPUT_DIR"
- |
uip solution pack "$SOLUTION_DIR" "$OUTPUT_DIR" \
--name "$SOLUTION_NAME" \
--version "$SOLUTION_VERSION"
artifacts:
paths:
- "$OUTPUT_DIR/*.zip"
expire_in: 30 days
# -----------------------------------------------------------------------------
# Stage: deploy — matrix across environments
# -----------------------------------------------------------------------------
deploy:
stage: deploy
needs:
- job: pack
artifacts: true
parallel:
matrix:
- ENVIRONMENT: dev
TENANT_VAR: UIPATH_TENANT_DEV
- ENVIRONMENT: stage
TENANT_VAR: UIPATH_TENANT_STAGE
- ENVIRONMENT: prod
TENANT_VAR: UIPATH_TENANT_PROD
environment:
name: uipath/$ENVIRONMENT
rules:
# Prod only on protected branches — set protection under Settings → Repository.
- if: '$ENVIRONMENT == "prod" && $CI_COMMIT_REF_PROTECTED != "true"'
when: never
- when: on_success
script:
- *install-uip
- |
# Resolve the per-environment tenant from the matrix variable.
UIPATH_TENANT="${!TENANT_VAR}"
if [ -z "$UIPATH_TENANT" ]; then
echo "Tenant variable $TENANT_VAR is empty; set it in CI/CD settings." >&2
exit 3
fi
uip login \
--client-id env.UIPATH_CLIENT_ID \
--client-secret env.UIPATH_CLIENT_SECRET \
--tenant "$UIPATH_TENANT"
ARTIFACT=$(find "$OUTPUT_DIR" -maxdepth 1 -name "*.zip" | head -1)
uip solution publish "$ARTIFACT"
uip solution deploy run \
--name "$SOLUTION_NAME-$ENVIRONMENT-$CI_PIPELINE_IID" \
--package-name "$SOLUTION_NAME" \
--package-version "$SOLUTION_VERSION" \
--folder-name MySolution \
--folder-path Shared
# -----------------------------------------------------------------------------
# Stage: test
# -----------------------------------------------------------------------------
test:
stage: test
needs:
- job: "deploy: [dev, UIPATH_TENANT_DEV]" # depend on the dev leg of the matrix
optional: true
rules:
- if: '$TEST_SET_KEY == null || $TEST_SET_KEY == ""'
when: never
- when: on_success
script:
- *install-uip
- |
uip login \
--client-id env.UIPATH_CLIENT_ID \
--client-secret env.UIPATH_CLIENT_SECRET \
--tenant "$UIPATH_TENANT_DEV"
EXECUTION_ID=$(uip tm testsets run \
--test-set-key "$TEST_SET_KEY" \
--output-filter "Data.ExecutionId" \
--output plain)
echo "started execution $EXECUTION_ID"
if ! uip tm wait \
--execution-id "$EXECUTION_ID" \
--project-key "$PROJECT_KEY" \
--timeout 1800; then
code=$?
case "$code" in
2) echo "test run did not finish within 30 minutes" >&2; exit 2 ;;
*) echo "wait failed (exit $code)" >&2; exit "$code" ;;
esac
fi
uip tm report get \
--execution-id "$EXECUTION_ID" \
--project-key "$PROJECT_KEY"
FAILED=$(uip tm report get \
--execution-id "$EXECUTION_ID" \
--project-key "$PROJECT_KEY" \
--output-filter "Data.Failed" \
--output plain)
if [ "$FAILED" -gt 0 ]; then
echo "$FAILED test case(s) failed" >&2
exit 1
fi
# -----------------------------------------------------------------------------
# Deploy UiPath Solution
# -----------------------------------------------------------------------------
# Auth: External Application, env.VAR_NAME prefix (never the literal value).
# Cache: npm global node_modules, keyed by CLI version.
# Matrix: deploy job fans out across dev / stage / prod tenants.
# -----------------------------------------------------------------------------
image: node:20
stages:
- build
- deploy
- test
variables:
CLI_VERSION: '1.0.0'
SOLUTION_NAME: 'my-solution'
SOLUTION_DIR: './my-solution'
OUTPUT_DIR: './dist'
SOLUTION_VERSION: '1.2.0-ci.$CI_PIPELINE_IID'
# Workspace-local npm prefix so installs need no sudo and are cacheable.
NPM_PREFIX: "$CI_PROJECT_DIR/.npm-global"
# Re-usable install block. GitLab does not have anchors for script:; we use
# YAML anchors on a hidden job and extend from it.
.install-uip: &install-uip |
set -euo pipefail
mkdir -p "$NPM_PREFIX"
npm config set prefix "$NPM_PREFIX"
export PATH="$NPM_PREFIX/bin:$PATH"
if ! command -v uip >/dev/null; then
npm install -g "@uipath/cli@$CLI_VERSION"
fi
uip --version
cache:
key: "uip-$CLI_VERSION"
paths:
- .npm-global/lib/node_modules
policy: pull-push
# -----------------------------------------------------------------------------
# Stage: build
# -----------------------------------------------------------------------------
pack:
stage: build
script:
- *install-uip
- mkdir -p "$OUTPUT_DIR"
- |
uip solution pack "$SOLUTION_DIR" "$OUTPUT_DIR" \
--name "$SOLUTION_NAME" \
--version "$SOLUTION_VERSION"
artifacts:
paths:
- "$OUTPUT_DIR/*.zip"
expire_in: 30 days
# -----------------------------------------------------------------------------
# Stage: deploy — matrix across environments
# -----------------------------------------------------------------------------
deploy:
stage: deploy
needs:
- job: pack
artifacts: true
parallel:
matrix:
- ENVIRONMENT: dev
TENANT_VAR: UIPATH_TENANT_DEV
- ENVIRONMENT: stage
TENANT_VAR: UIPATH_TENANT_STAGE
- ENVIRONMENT: prod
TENANT_VAR: UIPATH_TENANT_PROD
environment:
name: uipath/$ENVIRONMENT
rules:
# Prod only on protected branches — set protection under Settings → Repository.
- if: '$ENVIRONMENT == "prod" && $CI_COMMIT_REF_PROTECTED != "true"'
when: never
- when: on_success
script:
- *install-uip
- |
# Resolve the per-environment tenant from the matrix variable.
UIPATH_TENANT="${!TENANT_VAR}"
if [ -z "$UIPATH_TENANT" ]; then
echo "Tenant variable $TENANT_VAR is empty; set it in CI/CD settings." >&2
exit 3
fi
uip login \
--client-id env.UIPATH_CLIENT_ID \
--client-secret env.UIPATH_CLIENT_SECRET \
--tenant "$UIPATH_TENANT"
ARTIFACT=$(find "$OUTPUT_DIR" -maxdepth 1 -name "*.zip" | head -1)
uip solution publish "$ARTIFACT"
uip solution deploy run \
--name "$SOLUTION_NAME-$ENVIRONMENT-$CI_PIPELINE_IID" \
--package-name "$SOLUTION_NAME" \
--package-version "$SOLUTION_VERSION" \
--folder-name MySolution \
--folder-path Shared
# -----------------------------------------------------------------------------
# Stage: test
# -----------------------------------------------------------------------------
test:
stage: test
needs:
- job: "deploy: [dev, UIPATH_TENANT_DEV]" # depend on the dev leg of the matrix
optional: true
rules:
- if: '$TEST_SET_KEY == null || $TEST_SET_KEY == ""'
when: never
- when: on_success
script:
- *install-uip
- |
uip login \
--client-id env.UIPATH_CLIENT_ID \
--client-secret env.UIPATH_CLIENT_SECRET \
--tenant "$UIPATH_TENANT_DEV"
EXECUTION_ID=$(uip tm testsets run \
--test-set-key "$TEST_SET_KEY" \
--output-filter "Data.ExecutionId" \
--output plain)
echo "started execution $EXECUTION_ID"
if ! uip tm wait \
--execution-id "$EXECUTION_ID" \
--project-key "$PROJECT_KEY" \
--timeout 1800; then
code=$?
case "$code" in
2) echo "test run did not finish within 30 minutes" >&2; exit 2 ;;
*) echo "wait failed (exit $code)" >&2; exit "$code" ;;
esac
fi
uip tm report get \
--execution-id "$EXECUTION_ID" \
--project-key "$PROJECT_KEY"
FAILED=$(uip tm report get \
--execution-id "$EXECUTION_ID" \
--project-key "$PROJECT_KEY" \
--output-filter "Data.Failed" \
--output plain)
if [ "$FAILED" -gt 0 ]; then
echo "$FAILED test case(s) failed" >&2
exit 1
fi
演练
顶级设置
image: node:20— 每个作业都在官方 Node.js 20 映像中运行。CLI 要求 Node 18 及更高版本。如果您的 GitLab 运行器已安装 Node,并且您不需要容器,则可以删除其并改用shell执行程序。variables:— 管道范围内的值。SOLUTION_VERSION对$CI_PIPELINE_IID进行插值(项目作用域内的增量管道编号 — 比$CI_JOB_ID更适合版本控制,后者是全局性的非单调元素)。.install-uip锚点— GitLab 不允许您直接锚点script:块,但您可以锚点包含 shell 字符串的 YAML 节点,并将其与- *install-uip拼接。安装防护与其他秘钥相同:工作区本地前缀、条件 CLI 安装。工具会在首次使用时自动安装,因此锚点仅处理主机。cache:— 密钥uip-$CLI_VERSION可确保 CLI 版本提升会使缓存完全失效。policy: pull-push在入口处读取,在作业成功退出时写入。如果您大规模运行并希望缩短每个作业的秒数,请拆分为一个专用的“为缓存设定种子”作业,该作业运行pull-push,并让所有其他作业仅使用policy: pull。
打包作业
- 通过共享锚点安装 CLI 。
- 具有显式版本的
uip solution pack— 请参阅uip solution pack。 artifacts:将.zip带到下一阶段。路径为通配符$OUTPUT_DIR/*.zip路径,因此系统会选取uip solution pack生成的任何文件名。expire_in: 30 days阻止 GitLab 的工件存储无限增长;如果您需要更长的可追溯性,则可以颠倒它。
使用并行部署作业:矩阵
矩阵展开为三个作业—— deploy: [dev, UIPATH_TENANT_DEV] 、 deploy: [stage, UIPATH_TENANT_STAGE] 、 deploy: [prod, UIPATH_TENANT_PROD] ——这三个作业并行运行。每个都具有不同的$ENVIRONMENT和$TENANT_VAR ,并使用 bash 间接扩展 ( ${!TENANT_VAR} ) 从右侧的 CI/CD 变量读取每个环境的租户。
environment: name: uipath/$ENVIRONMENT— GitLab 在“环境”视图中跟踪部署,因此每个租户都会获得每个环境的历史记录,并带有回滚按钮。rules:— 第一条规则阻止prod不受保护的分支。结合设置 → 存储库 → 受保护的分支(您可以在其中将main标记为“受保护”),这是阻止功能分支意外部署到生产的方法。UIPATH_CLIENT_*变量也应标记为“受保护” ,以便仅解析受保护的引用。uip login --client-id env.UIPATH_CLIENT_ID --client-secret env.UIPATH_CLIENT_SECRET— 支持使用env.VAR_NAME前缀将密码传递给 CLI,而无需密码出现在 Shell 命令行中。当标记为“掩码”时,GitLab 会掩码日志中的变量,但无论怎样,env.前缀都是一种纵深防御措施。请参阅身份验证 — env.VAR_NAME 前缀。- 部署名称—
$SOLUTION_NAME-$ENVIRONMENT-$CI_PIPELINE_IID使每个部署可追踪到特定管道运行和环境。
在并行矩阵作业中,如果一条分支出现故障,默认情况下其他分支会继续运行。如果您希望 Prod 等待开发和阶段,请将矩阵转换为三个连续的作业(或在它们之间使用needs: )。
测试作业
needs:通过扩展名称引用矩阵分支"deploy: [dev, UIPATH_TENANT_DEV]"。如果rules:块跳过了开发阶段,则optional: true会使依赖项成为非严重。- 未设置
rules:时,TEST_SET_KEY会跳过作业,模式与其他模式相同。 - 启动 → 等待 → 验证—操作方法:从 CLI 运行测试中的规范测试模式。从
2退出uip tm wait意味着超时(非身份验证失败)。
常见变体
使用手动门进行序列提升
如果您希望 Prod 需要手动单击而不是受保护的分支门控,请将矩阵拆分为三个作业,并将when: manual添加到 Prod 中:
deploy-dev:
stage: deploy
# …as deploy above, fixed to UIPATH_TENANT_DEV…
deploy-stage:
stage: deploy
needs: [ pack, deploy-dev ]
# …as deploy above, fixed to UIPATH_TENANT_STAGE…
deploy-prod:
stage: deploy
needs: [ pack, deploy-stage ]
when: manual # requires a reviewer to click "Play"
allow_failure: false
environment:
name: uipath/prod
# …as deploy above, fixed to UIPATH_TENANT_PROD…
deploy-dev:
stage: deploy
# …as deploy above, fixed to UIPATH_TENANT_DEV…
deploy-stage:
stage: deploy
needs: [ pack, deploy-dev ]
# …as deploy above, fixed to UIPATH_TENANT_STAGE…
deploy-prod:
stage: deploy
needs: [ pack, deploy-stage ]
when: manual # requires a reviewer to click "Play"
allow_failure: false
environment:
name: uipath/prod
# …as deploy above, fixed to UIPATH_TENANT_PROD…
《操作方法:打包并发布解决方案 — 跨租户推广一个包》中更深入的介绍。
回滚
通过单独的作业使用管道变量手动触发:
rollback:
stage: deploy
when: manual
rules:
- if: '$ROLLBACK_VERSION != null && $ROLLBACK_VERSION != ""'
script:
- *install-uip
- |
uip login \
--client-id env.UIPATH_CLIENT_ID \
--client-secret env.UIPATH_CLIENT_SECRET \
--tenant "$UIPATH_TENANT_PROD"
uip solution deploy run \
--name "$SOLUTION_NAME-rollback" \
--package-name "$SOLUTION_NAME" \
--package-version "$ROLLBACK_VERSION" \
--folder-name MySolution \
--folder-path Shared
rollback:
stage: deploy
when: manual
rules:
- if: '$ROLLBACK_VERSION != null && $ROLLBACK_VERSION != ""'
script:
- *install-uip
- |
uip login \
--client-id env.UIPATH_CLIENT_ID \
--client-secret env.UIPATH_CLIENT_SECRET \
--tenant "$UIPATH_TENANT_PROD"
uip solution deploy run \
--name "$SOLUTION_NAME-rollback" \
--package-name "$SOLUTION_NAME" \
--package-version "$ROLLBACK_VERSION" \
--folder-name MySolution \
--folder-path Shared
从CI/CD → 管道 → 运行管道启动管道,并将ROLLBACK_VERSION设置为目标版本(例如1.1.9 )。有关破坏性回滚(卸载并删除工件),请参阅操作方法:打包并发布解决方案 — 回滚。
跳过测试
未设置 CI/CD 变量中的TEST_SET_KEY 。test作业的rules:块会完全跳过该作业。
常见错误
- 已掩码 != 受保护。掩码变量隐藏日志中的值,但仍然在所有分支(包括短期功能分支)上可用。受保护的变量仅在受保护的引用上公开。两者都需要用于身份验证密码,否则推送的功能分支可能会在生产环境中运行
uip login。 - 间接扩展需要 bash。
${!TENANT_VAR}是一项 bash 功能;某些最小映像中的默认sh不支持。默认情况下,node:20映像包含 bash;在基于alpine映像上,在显式的每个环境变量上添加apk add bash或切换到 case 语句。 - Matrix 作业名称包含空格。
needs: - job: "deploy: [dev, UIPATH_TENANT_DEV]"— 名称中包含: [,因此请在 YAML 中将其引用。 - 缓存路径是相对于工作区的。缓存条目
.npm-global/lib/node_modules可以正常运行,因为NPM_PREFIX="$CI_PROJECT_DIR/.npm-global"会将其放置在工作区中。如果将前缀移出,缓存将停止工作。 set -euo pipefail必须位于每个多行脚本的顶部。如果没有它,打包失败后可以“成功”发布过时的工件。请参阅脚本编写模式—严格 shell 选项。
另请参阅
- 操作方法:从 CI 部署到 Orchestrator — 与平台无关的指南。
- 操作方法:打包并发布解决方案— 版本控制和回滚。
- 操作方法:从 CLI 运行测试— 启动 → 等待 → 验证模式。
- CI/CD 秘方:Azure Pipelines 、 GitHub Actions 、 Jenkins — 其他平台中的相同管道。