mirror of
https://github.com/seepine/action-miniprogram-ci.git
synced 2025-01-18 10:51:08 +08:00
chore(release): v1.0.0
This commit is contained in:
commit
826fb2bfd9
16
.editorconfig
Normal file
16
.editorconfig
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Editor configuration, see http://editorconfig.org
|
||||||
|
|
||||||
|
# 表示是最顶层的 EditorConfig 配置文件
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*] # 表示所有文件适用
|
||||||
|
charset = utf-8 # 设置文件字符集为 utf-8
|
||||||
|
indent_style = space # 缩进风格(tab | space)
|
||||||
|
indent_size = 2 # 缩进大小
|
||||||
|
end_of_line = lf # 控制换行类型(lf | cr | crlf)
|
||||||
|
trim_trailing_whitespace = true # 去除行首的任意空白字符
|
||||||
|
insert_final_newline = true # 始终在文件末尾插入一个新行
|
||||||
|
|
||||||
|
[*.md] # 表示仅 md 文件适用以下规则
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
5
.eslintignore
Normal file
5
.eslintignore
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
dist/
|
||||||
|
lib/
|
||||||
|
node_modules/
|
||||||
|
jest.config.js
|
||||||
|
__tests__/
|
55
.eslintrc.json
Normal file
55
.eslintrc.json
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["jest", "@typescript-eslint"],
|
||||||
|
"extends": ["plugin:github/recommended"],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 9,
|
||||||
|
"sourceType": "module",
|
||||||
|
"project": "./tsconfig.json"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"i18n-text/no-en": "off",
|
||||||
|
"eslint-comments/no-use": "off",
|
||||||
|
"import/no-namespace": "off",
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "error",
|
||||||
|
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
|
||||||
|
"@typescript-eslint/no-require-imports": "error",
|
||||||
|
"@typescript-eslint/array-type": "error",
|
||||||
|
"@typescript-eslint/await-thenable": "error",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "error",
|
||||||
|
"camelcase": "off",
|
||||||
|
"@typescript-eslint/consistent-type-assertions": "error",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
|
||||||
|
"@typescript-eslint/func-call-spacing": ["error", "never"],
|
||||||
|
"@typescript-eslint/no-array-constructor": "error",
|
||||||
|
"@typescript-eslint/no-empty-interface": "error",
|
||||||
|
"@typescript-eslint/no-explicit-any": "error",
|
||||||
|
"@typescript-eslint/no-extraneous-class": "error",
|
||||||
|
"@typescript-eslint/no-for-in-array": "error",
|
||||||
|
"@typescript-eslint/no-inferrable-types": "error",
|
||||||
|
"@typescript-eslint/no-misused-new": "error",
|
||||||
|
"@typescript-eslint/no-namespace": "error",
|
||||||
|
"@typescript-eslint/no-non-null-assertion": "warn",
|
||||||
|
"@typescript-eslint/no-unnecessary-qualifier": "error",
|
||||||
|
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
||||||
|
"@typescript-eslint/no-useless-constructor": "error",
|
||||||
|
"@typescript-eslint/no-var-requires": "error",
|
||||||
|
"@typescript-eslint/prefer-for-of": "warn",
|
||||||
|
"@typescript-eslint/prefer-function-type": "warn",
|
||||||
|
"@typescript-eslint/prefer-includes": "error",
|
||||||
|
"@typescript-eslint/prefer-string-starts-ends-with": "error",
|
||||||
|
"@typescript-eslint/promise-function-async": "error",
|
||||||
|
"@typescript-eslint/require-array-sort-compare": "error",
|
||||||
|
"@typescript-eslint/restrict-plus-operands": "error",
|
||||||
|
"semi": "off",
|
||||||
|
"@typescript-eslint/semi": ["error", "never"],
|
||||||
|
"@typescript-eslint/type-annotation-spacing": "error",
|
||||||
|
"@typescript-eslint/unbound-method": "error"
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es6": true,
|
||||||
|
"jest/globals": true
|
||||||
|
}
|
||||||
|
}
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
dist/** -diff linguist-generated=true
|
53
.github/workflows/check-dist.yml
vendored
Normal file
53
.github/workflows/check-dist.yml
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# `dist/index.js` is a special file in Actions.
|
||||||
|
# When you reference an action with `uses:` in a workflow,
|
||||||
|
# `index.js` is the code that will run.
|
||||||
|
# For our project, we generate this file through a build process from other source files.
|
||||||
|
# We need to make sure the checked-in `index.js` actually matches what we expect it to be.
|
||||||
|
name: Check dist/
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-dist:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set Node.js 16.x
|
||||||
|
uses: actions/setup-node@v3.6.0
|
||||||
|
with:
|
||||||
|
node-version: 16.x
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Rebuild the dist/ directory
|
||||||
|
run: |
|
||||||
|
npm run build
|
||||||
|
npm run package
|
||||||
|
|
||||||
|
- name: Compare the expected and actual dist/ directories
|
||||||
|
run: |
|
||||||
|
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
|
||||||
|
echo "Detected uncommitted changes after build. See status below:"
|
||||||
|
git diff
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
id: diff
|
||||||
|
|
||||||
|
# If index.js was different than expected, upload the expected version as an artifact
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||||
|
with:
|
||||||
|
name: dist
|
||||||
|
path: dist/
|
71
.github/workflows/codeql-analysis.yml
vendored
Normal file
71
.github/workflows/codeql-analysis.yml
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
# The branches below must be a subset of the branches above
|
||||||
|
branches: [ main ]
|
||||||
|
schedule:
|
||||||
|
- cron: '31 7 * * 3'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ 'TypeScript' ]
|
||||||
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
|
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
source-root: src
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 https://git.io/JvXDl
|
||||||
|
|
||||||
|
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||||
|
# and modify them (or add more) to build your code if your project
|
||||||
|
# uses a compiled language
|
||||||
|
|
||||||
|
#- run: |
|
||||||
|
# make bootstrap
|
||||||
|
# make release
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
99
.gitignore
vendored
Normal file
99
.gitignore
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# Dependency directory
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# nuxt.js build output
|
||||||
|
.nuxt
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# OS metadata
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Ignore built ts files
|
||||||
|
__tests__/runner/*
|
||||||
|
lib/**/*
|
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
dist/
|
||||||
|
lib/
|
||||||
|
node_modules/
|
10
.prettierrc.json
Normal file
10
.prettierrc.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
12
.vscode/settings.json
vendored
Normal file
12
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
// 保存时执行eslint
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": true
|
||||||
|
},
|
||||||
|
// 从不展示扩展缩写
|
||||||
|
"emmet.showExpandedAbbreviation": "never",
|
||||||
|
// 关闭代码区域小地图
|
||||||
|
"editor.minimap.enabled": false,
|
||||||
|
// 代码过长换行
|
||||||
|
"editor.wordWrap": "on"
|
||||||
|
}
|
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
@ -0,0 +1 @@
|
|||||||
|
* @actions/actions-runtime
|
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 GitHub, Inc. and contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
114
README.md
Normal file
114
README.md
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
# action-miniprogram-ci
|
||||||
|
|
||||||
|
将 [miniprogram-ci](https://developers.weixin.qq.com/miniprogram/dev/devtools/ci.html) 封装为 action,通过 Actions 实现小程序自动化上传/预览。
|
||||||
|
|
||||||
|
## 一、用法
|
||||||
|
|
||||||
|
appid 和 setting 相关设置将会自动从 `project.config.json` 文件中读取,请确保 `project-path` 参数设置正确
|
||||||
|
|
||||||
|
### 1.1 上传
|
||||||
|
|
||||||
|
```yml
|
||||||
|
- name: Miniprogram Ci Upload
|
||||||
|
uses: seepine/action-miniprogram-ci@v1
|
||||||
|
with:
|
||||||
|
project-path: ./dist/build/mp-weixin
|
||||||
|
private-key: ${{ secrets.MP_PRIVATE_KEY }}
|
||||||
|
version: 1.0.0
|
||||||
|
desc: '修复了一些已知问题'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 预览
|
||||||
|
|
||||||
|
默认生成终端二维码,可通过日志查看到所生成二维码
|
||||||
|
|
||||||
|
```yml
|
||||||
|
- name: Miniprogram Ci Preview
|
||||||
|
uses: seepine/action-miniprogram-ci@v1
|
||||||
|
with:
|
||||||
|
mode: preview
|
||||||
|
project-path: ./dist/build/mp-weixin
|
||||||
|
private-key: ${{ secrets.MP_PRIVATE_KEY }}
|
||||||
|
robot: 2 # 可指定与上传不同的 ci 机器人
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 输出内容
|
||||||
|
|
||||||
|
默认会将 appid 和 version 输出到 `outputs` 中,后续步骤可通过 `steps.mpci.outputs.appid` 等获取。
|
||||||
|
|
||||||
|
同时更可设置输出文件,自定义任何输出内容,例如搭配 `peter-evans/create-or-update-comment@v3` 实现 GitBots 自动将二维码评论到工单或 PR 中。
|
||||||
|
|
||||||
|
````yml
|
||||||
|
- name: Miniprogram Ci Preview
|
||||||
|
uses: seepine/action-miniprogram-ci@v1
|
||||||
|
with:
|
||||||
|
mode: preview
|
||||||
|
project-path: ./dist/build/mp-weixin
|
||||||
|
private-key: ${{ secrets.MP_PRIVATE_KEY }}
|
||||||
|
robot: 2
|
||||||
|
# 默认输出在 ./output.txt
|
||||||
|
output: |
|
||||||
|
**扫码预览**
|
||||||
|
```
|
||||||
|
{qrcode}
|
||||||
|
```
|
||||||
|
|
||||||
|
- name: Create comment
|
||||||
|
uses: peter-evans/create-or-update-comment@v3
|
||||||
|
with:
|
||||||
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
|
body-path: output.txt # 读取上一步生成的output.txt文件内容
|
||||||
|
````
|
||||||
|
|
||||||
|
## 二、输入(Inputs)
|
||||||
|
|
||||||
|
| Name | Description | Default |
|
||||||
|
| ------------------ | ------------------------------------------------------------------------------------------------------------- | --------------------- |
|
||||||
|
| mode | ci 模式,可选 upload/preview. | upload |
|
||||||
|
| type | 小程序类型,可选 miniProgram/miniProgramPlugin/miniGame/miniGamePlugin | miniProgram |
|
||||||
|
| project-path | 项目的路径,即 project.config.json 所在的目录,默认根路径,即./, 若 uniapp 项目,一般传入./dist/build/mp-weixin | ./ |
|
||||||
|
| private-key | 私钥,在获取项目属性和上传时用于鉴权使用,在微信公众平台上登录后下载, 建议通过 secrets 设置 | |
|
||||||
|
| private-key-path | 私钥完整路径,private-key 为空时使用路径值,例如./private.key | |
|
||||||
|
| ignores | 指定需要排除的规则,默认忽略 node_modules 路径 | node_modules/**/* |
|
||||||
|
| version | 自定义版本号,空则根据时间戳自动生成 YYYY.MMDD.HHmmss | YYYY.MMDD.HHmmss |
|
||||||
|
| desc | 自定义备注 | 'fix some bug.' |
|
||||||
|
| robot | 指定使用哪一个 ci 机器人,可选值:1 ~ 30 | 1 |
|
||||||
|
| threads | 指定本地编译过程中开启的线程数 | 默认获取 cpu 线程数 |
|
||||||
|
| qrcode-format | (preview 有效) 返回二维码文件的格式,可选 terminal/image/base64, 可设置 output 参数将其输出到 output 文件中 | terminal |
|
||||||
|
| qrcode-output-dest | (preview 有效) 若 qrcode-format 非 terminal 时设置二维码文件输出路径 | ./preview-qrcode.png |
|
||||||
|
| page-path | (preview 有效) 预览页面路径 | |
|
||||||
|
| search-query | (preview 有效) 预览页面路径启动参数 | |
|
||||||
|
| scene | (preview 有效) 具体含义见场景值列表 | 1011 |
|
||||||
|
| output | 设置输出文件,可使用{appid},{version},{qrcode}作为占位符 | |
|
||||||
|
| output-path | 设置输出文件路径 | ./output.txt |
|
||||||
|
|
||||||
|
## 三、输出(Outputs)
|
||||||
|
|
||||||
|
### 3.1 输出参数
|
||||||
|
|
||||||
|
通过 `steps.[step_id].outputs.appid` 取值
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| ------- | -------------------- |
|
||||||
|
| appid | 读取到的小程序 appid |
|
||||||
|
| version | 版本号 |
|
||||||
|
|
||||||
|
### 3.2 输出文件
|
||||||
|
|
||||||
|
设置了 output 输出文本有值,可通过{appid},{version},{qrcode}作为占位符,例如
|
||||||
|
|
||||||
|
````yml
|
||||||
|
- name: Miniprogram Ci Preview
|
||||||
|
uses: seepine/action-miniprogram-ci@v1
|
||||||
|
with:
|
||||||
|
mode: preview
|
||||||
|
project-path: ./dist/build/mp-weixin
|
||||||
|
private-key: ${{ secrets.MP_PRIVATE_KEY }}
|
||||||
|
# 默认输出在 ./output.txt
|
||||||
|
output: |
|
||||||
|
自动预览完成,appid为{appid},版本号为{version}
|
||||||
|
**扫码预览**
|
||||||
|
```
|
||||||
|
{qrcode}
|
||||||
|
```
|
||||||
|
````
|
BIN
__tests__/dist/preview.png
vendored
Normal file
BIN
__tests__/dist/preview.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
41
__tests__/dist/project.config.json
vendored
Normal file
41
__tests__/dist/project.config.json
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"description": "小程序项目配置文件。",
|
||||||
|
"packOptions": {
|
||||||
|
"ignore": []
|
||||||
|
},
|
||||||
|
"setting": {
|
||||||
|
"urlCheck": false,
|
||||||
|
"es6": true,
|
||||||
|
"postcss": true,
|
||||||
|
"minified": true,
|
||||||
|
"newFeature": true,
|
||||||
|
"es7": true,
|
||||||
|
"minify": true,
|
||||||
|
"minifyJS": true,
|
||||||
|
"minifyWXML": true,
|
||||||
|
"minifyWXSS": true,
|
||||||
|
"autoPrefixWXSS": true
|
||||||
|
},
|
||||||
|
"compileType": "miniprogram",
|
||||||
|
"libVersion": "",
|
||||||
|
"appid": "wx2ec97gd31w864xxxx",
|
||||||
|
"projectname": "",
|
||||||
|
"condition": {
|
||||||
|
"search": {
|
||||||
|
"current": -1,
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"conversation": {
|
||||||
|
"current": -1,
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"game": {
|
||||||
|
"current": -1,
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"miniprogram": {
|
||||||
|
"current": -1,
|
||||||
|
"list": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
__tests__/main.test.ts
Normal file
32
__tests__/main.test.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import * as process from 'process'
|
||||||
|
import * as cp from 'child_process'
|
||||||
|
import * as path from 'path'
|
||||||
|
import {expect, test} from '@jest/globals'
|
||||||
|
|
||||||
|
// shows how the runner will run a javascript action with env / stdout protocol
|
||||||
|
test('test runs', () => {
|
||||||
|
process.env['INPUT_MODE'] = 'test'
|
||||||
|
process.env['INPUT_PROJECT-PATH'] = '../__tests__/dist'
|
||||||
|
process.env['INPUT_PRIVATE-KEY'] = 'theInputPrivateKey'
|
||||||
|
const np = process.execPath
|
||||||
|
const ip = path.join(__dirname, '..', 'lib', 'main.js')
|
||||||
|
const options: cp.ExecFileSyncOptions = {
|
||||||
|
env: process.env
|
||||||
|
}
|
||||||
|
console.log(cp.execFileSync(np, [ip], options).toString())
|
||||||
|
})
|
||||||
|
|
||||||
|
// shows how the runner will run a javascript action with env / stdout protocol
|
||||||
|
test('test output', () => {
|
||||||
|
process.env['INPUT_MODE'] = 'test'
|
||||||
|
process.env['INPUT_PROJECT-PATH'] = '../__tests__/dist'
|
||||||
|
process.env['INPUT_PRIVATE-KEY'] = 'theInputPrivateKey'
|
||||||
|
process.env['INPUT_OUTPUT'] =
|
||||||
|
'appid={appid},version={version}\n```\n{qrcode}\n```\n'
|
||||||
|
const np = process.execPath
|
||||||
|
const ip = path.join(__dirname, '..', 'lib', 'main.js')
|
||||||
|
const options: cp.ExecFileSyncOptions = {
|
||||||
|
env: process.env
|
||||||
|
}
|
||||||
|
console.log(cp.execFileSync(np, [ip], options).toString())
|
||||||
|
})
|
8
__tests__/qrcode.test.ts
Normal file
8
__tests__/qrcode.test.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import {test} from '@jest/globals'
|
||||||
|
import {getQrCode, generate} from '../src/utils/qrcode'
|
||||||
|
|
||||||
|
test('qrcode generate', async () => {
|
||||||
|
const qrcodeData = await getQrCode('./__tests__/dist/preview.png')
|
||||||
|
const qrcode = await generate(qrcodeData)
|
||||||
|
console.log(qrcode)
|
||||||
|
})
|
69
action.yml
Normal file
69
action.yml
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
name: 'action-miniprogram-ci'
|
||||||
|
description: '将 miniprogram-ci 封装为 action,通过 Actions 实现小程序自动化上传/预览'
|
||||||
|
author: 'seepine'
|
||||||
|
inputs:
|
||||||
|
mode:
|
||||||
|
required: false
|
||||||
|
description: 'ci模式,默认upload,可选preview'
|
||||||
|
default: 'upload'
|
||||||
|
type:
|
||||||
|
required: false
|
||||||
|
description: '小程序类型,默认miniProgram,可选miniProgram/miniProgramPlugin/miniGame/miniGamePlugin'
|
||||||
|
default: 'miniProgram'
|
||||||
|
project-path:
|
||||||
|
required: false
|
||||||
|
description: '项目的路径,即 project.config.json 所在的目录,默认根路径,即./, 若uniapp项目,一般传入./dist/build/mp-weixin'
|
||||||
|
default: './'
|
||||||
|
private-key:
|
||||||
|
required: false
|
||||||
|
description: '私钥,在获取项目属性和上传时用于鉴权使用,在微信公众平台上登录后下载'
|
||||||
|
private-key-path:
|
||||||
|
required: false
|
||||||
|
description: '私钥完整路径,private-key为空时使用路径值,例如./private.key'
|
||||||
|
ignores:
|
||||||
|
required: false
|
||||||
|
description: '指定需要排除的规则,默认忽略node_modules路径'
|
||||||
|
default: 'node_modules/**/*'
|
||||||
|
version:
|
||||||
|
required: false
|
||||||
|
description: '自定义版本号,空则根据时间戳自动生成YYYY.MMDD.HHmmss'
|
||||||
|
desc:
|
||||||
|
required: false
|
||||||
|
description: '自定义备注'
|
||||||
|
default: 'fix some bug.'
|
||||||
|
robot:
|
||||||
|
required: false
|
||||||
|
description: '指定使用哪一个 ci 机器人,可选值:1 ~ 30'
|
||||||
|
default: 1
|
||||||
|
threads:
|
||||||
|
required: false
|
||||||
|
description: '指定本地编译过程中开启的线程数,默认获取cpu线程数'
|
||||||
|
# 以下当mode为preview有效
|
||||||
|
qrcode-format:
|
||||||
|
required: false
|
||||||
|
description: '返回二维码文件的格式 "image" 或 "base64",默认值 "terminal" 会输出到日志中,可设置output参数将其输出到output文件中'
|
||||||
|
default: 'terminal'
|
||||||
|
qrcode-output-dest:
|
||||||
|
required: false
|
||||||
|
description: '若qrcode-format设置非terminal,则需要设置此路径值,默认./preview-qrcode.png'
|
||||||
|
default: './preview-qrcode.png'
|
||||||
|
page-path:
|
||||||
|
required: false
|
||||||
|
description: '预览页面路径'
|
||||||
|
search-query:
|
||||||
|
required: false
|
||||||
|
description: '预览页面路径启动参数'
|
||||||
|
scene:
|
||||||
|
required: false
|
||||||
|
description: '默认值 1011,具体含义见场景值列表'
|
||||||
|
# 设置输出文件相关
|
||||||
|
output:
|
||||||
|
required: false
|
||||||
|
description: '设置输出文件,可使用{appid},{version},{qrcode}作为占位符'
|
||||||
|
output-path:
|
||||||
|
required: false
|
||||||
|
description: '设置输出文件路径,默认为./output.txt'
|
||||||
|
default: './output.txt'
|
||||||
|
runs:
|
||||||
|
using: 'node16'
|
||||||
|
main: 'dist/index.js'
|
57396
dist/index.js
generated
vendored
Normal file
57396
dist/index.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/index.js.map
generated
vendored
Normal file
1
dist/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2039
dist/licenses.txt
generated
vendored
Normal file
2039
dist/licenses.txt
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
dist/sourcemap-register.js
generated
vendored
Normal file
1
dist/sourcemap-register.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
9
jest.config.js
Normal file
9
jest.config.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
clearMocks: true,
|
||||||
|
moduleFileExtensions: ['js', 'ts'],
|
||||||
|
testMatch: ['**/*.test.ts'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': 'ts-jest'
|
||||||
|
},
|
||||||
|
verbose: true
|
||||||
|
}
|
46
package.json
Normal file
46
package.json
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "action-miniprogram-ci",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "将 miniprogram-ci 封装为 action, 通过 Actions 实现小程序自动化上传/预览",
|
||||||
|
"main": "lib/main.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"format": "prettier --write **/*.ts",
|
||||||
|
"format-check": "prettier --check **/*.ts",
|
||||||
|
"lint": "eslint src/**/*.ts",
|
||||||
|
"package": "ncc build --source-map --license licenses.txt",
|
||||||
|
"test": "jest",
|
||||||
|
"all": "npm run build && npm run format && npm run lint && npm run package && npm test"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/seepine/action-miniprogram-ci.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"actions",
|
||||||
|
"miniprogram-ci"
|
||||||
|
],
|
||||||
|
"author": "seepine",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/core": "~1.10.0",
|
||||||
|
"jimp": "~0.22.10",
|
||||||
|
"jsqr": "~1.4.0",
|
||||||
|
"qrcode-terminal": "~0.12.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "~18.16.3",
|
||||||
|
"@typescript-eslint/parser": "~5.59.2",
|
||||||
|
"@vercel/ncc": "~0.36.1",
|
||||||
|
"eslint": "~8.39.0",
|
||||||
|
"eslint-plugin-github": "~4.7.0",
|
||||||
|
"eslint-plugin-jest": "~27.2.1",
|
||||||
|
"jest": "~29.5.0",
|
||||||
|
"js-yaml": "~4.1.0",
|
||||||
|
"miniprogram-ci": "~1.9.8",
|
||||||
|
"prettier": "~2.8.8",
|
||||||
|
"ts-jest": "~29.1.0",
|
||||||
|
"typescript": "~5.0.4"
|
||||||
|
}
|
||||||
|
}
|
53
src/ci/preview.ts
Normal file
53
src/ci/preview.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import {Preview} from '../type'
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import {generate, getQrCode} from '../utils/qrcode'
|
||||||
|
import fs from 'fs'
|
||||||
|
import {getArgs} from './utils'
|
||||||
|
import * as cp from 'child_process'
|
||||||
|
|
||||||
|
export const preview = async (option: Preview): Promise<string> => {
|
||||||
|
const previewArgs = []
|
||||||
|
if (option.qrcodeFormat === 'terminal') {
|
||||||
|
previewArgs.push(
|
||||||
|
...[
|
||||||
|
'--qrcode-format',
|
||||||
|
'image',
|
||||||
|
'--qrcode-output-dest',
|
||||||
|
option.qrcodeOutputDest
|
||||||
|
]
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
previewArgs.push(...['--qrcode-format', option.qrcodeFormat])
|
||||||
|
if (option.qrcodeOutputDest) {
|
||||||
|
previewArgs.push(...['--qrcode-output-dest', option.qrcodeOutputDest])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (option.pagePath) {
|
||||||
|
previewArgs.push(...['--preview-page-path', option.pagePath])
|
||||||
|
}
|
||||||
|
if (option.searchQuery) {
|
||||||
|
previewArgs.push(...['--preview-search-query', option.searchQuery])
|
||||||
|
}
|
||||||
|
if (option.scene) {
|
||||||
|
previewArgs.push(...['--scene', `${option.scene}`])
|
||||||
|
}
|
||||||
|
const command = [
|
||||||
|
'npx',
|
||||||
|
'miniprogram-ci',
|
||||||
|
'preview',
|
||||||
|
...getArgs(option),
|
||||||
|
...previewArgs
|
||||||
|
].join(' ')
|
||||||
|
|
||||||
|
core.info('Command:')
|
||||||
|
core.info(` ${command}`)
|
||||||
|
cp.execSync(command)
|
||||||
|
if (option.qrcodeFormat === 'terminal') {
|
||||||
|
const qrcodeData = await getQrCode(option.qrcodeOutputDest)
|
||||||
|
const qrcode = await generate(qrcodeData)
|
||||||
|
core.info(`Generate terminal qrcode:\n${qrcode}`)
|
||||||
|
fs.promises.unlink(option.qrcodeOutputDest)
|
||||||
|
return qrcode
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
14
src/ci/upload.ts
Normal file
14
src/ci/upload.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {Upload} from '../type'
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import {getArgs} from './utils'
|
||||||
|
import * as cp from 'child_process'
|
||||||
|
|
||||||
|
export const upload = async (option: Upload): Promise<void> => {
|
||||||
|
const command = ['npx', 'miniprogram-ci', 'upload', ...getArgs(option)].join(
|
||||||
|
' '
|
||||||
|
)
|
||||||
|
|
||||||
|
core.info('Command:')
|
||||||
|
core.info(` ${command}`)
|
||||||
|
cp.execSync(command)
|
||||||
|
}
|
62
src/ci/utils.ts
Normal file
62
src/ci/utils.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import {CiOption} from '../type'
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
|
export const getArgs = (option: CiOption): string[] => {
|
||||||
|
const args = [
|
||||||
|
'--project-type',
|
||||||
|
`'${option.type}'`,
|
||||||
|
'--appid',
|
||||||
|
`'${option.appid}'`,
|
||||||
|
'--project-path',
|
||||||
|
`'${option.projectPath}'`
|
||||||
|
]
|
||||||
|
if (option.privateKeyPath) {
|
||||||
|
args.push(...['--private-key-path', `'${option.privateKeyPath}'`])
|
||||||
|
}
|
||||||
|
if (option.ignores) {
|
||||||
|
args.push(...['--project-ignores', `'${option.ignores.toString()}'`])
|
||||||
|
}
|
||||||
|
if (option.version) {
|
||||||
|
args.push(...['--upload-version', `'${option.version}'`])
|
||||||
|
}
|
||||||
|
if (option.desc) {
|
||||||
|
args.push(...['--upload-description', `'${option.desc}'`])
|
||||||
|
}
|
||||||
|
if (option.threads) {
|
||||||
|
args.push(...['--threads', `${option.threads.toString()}`])
|
||||||
|
}
|
||||||
|
if (option.robot) {
|
||||||
|
args.push(...['--robot', `${option.robot.toString()}`])
|
||||||
|
}
|
||||||
|
if (option.setting) {
|
||||||
|
if (option.setting.es6) {
|
||||||
|
args.push(...['--enable-es6', `true`])
|
||||||
|
}
|
||||||
|
if (option.setting.es7) {
|
||||||
|
args.push(...['--enable-es7', `true`])
|
||||||
|
}
|
||||||
|
if (option.setting.autoPrefixWXSS) {
|
||||||
|
args.push(...['--enable-autoprefixwxss', `true`])
|
||||||
|
}
|
||||||
|
if (option.setting.autoPrefixWXSS) {
|
||||||
|
args.push(...['--enable-autoprefixwxss', `true`])
|
||||||
|
}
|
||||||
|
if (option.setting.autoPrefixWXSS) {
|
||||||
|
args.push(...['--enable-autoprefixwxss', `true`])
|
||||||
|
}
|
||||||
|
if (option.setting.minifyWXSS) {
|
||||||
|
args.push(...['--enable-minify-wxss', `true`])
|
||||||
|
}
|
||||||
|
if (option.setting.minifyWXML) {
|
||||||
|
args.push(...['--enable-minify-wxml', `true`])
|
||||||
|
}
|
||||||
|
if (option.setting.minifyJS) {
|
||||||
|
args.push(...['--enable-minify-js', `true`])
|
||||||
|
}
|
||||||
|
if (option.setting.minify) {
|
||||||
|
args.push(...['--enable-minify', `true`])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
core.debug(args.toString())
|
||||||
|
return args
|
||||||
|
}
|
1
src/index.d.ts
vendored
Normal file
1
src/index.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
declare module 'qrcode-terminal'
|
150
src/main.ts
Normal file
150
src/main.ts
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import * as core from '@actions/core'
|
||||||
|
import {
|
||||||
|
formatDate,
|
||||||
|
getInput,
|
||||||
|
getInputAsArray,
|
||||||
|
getInputAsInt,
|
||||||
|
Inputs,
|
||||||
|
Outputs,
|
||||||
|
stringify
|
||||||
|
} from './utils/index'
|
||||||
|
import {
|
||||||
|
CiOption,
|
||||||
|
filterMode,
|
||||||
|
filterQrcodeFormat,
|
||||||
|
filterType,
|
||||||
|
Preview
|
||||||
|
} from './type'
|
||||||
|
import path from 'path'
|
||||||
|
import * as fs from 'fs'
|
||||||
|
import * as os from 'os'
|
||||||
|
import {upload} from './ci/upload'
|
||||||
|
import {preview} from './ci/preview'
|
||||||
|
|
||||||
|
const defaultPrivateKeyPath = '../private.key'
|
||||||
|
|
||||||
|
async function run(): Promise<void> {
|
||||||
|
let autoPrivateKey = false
|
||||||
|
let isTest = false
|
||||||
|
let qrcode = ''
|
||||||
|
const workspace = process.env.GITHUB_WORKSPACE || __dirname
|
||||||
|
let option: CiOption
|
||||||
|
try {
|
||||||
|
core.debug(new Date().toTimeString())
|
||||||
|
|
||||||
|
option = {
|
||||||
|
mode: filterMode(getInput(Inputs.mode)),
|
||||||
|
appid: '',
|
||||||
|
type: filterType(getInput(Inputs.type)),
|
||||||
|
projectPath: getInput(Inputs.projectPath) || '',
|
||||||
|
privateKey: getInput(Inputs.privateKey),
|
||||||
|
privateKeyPath: getInput(Inputs.privateKeyPath),
|
||||||
|
ignores: getInputAsArray(Inputs.ignores),
|
||||||
|
|
||||||
|
version: getInput(Inputs.version) || '',
|
||||||
|
desc: getInput(Inputs.desc),
|
||||||
|
robot: getInputAsInt(Inputs.robot),
|
||||||
|
threads: getInputAsInt(Inputs.threads)
|
||||||
|
}
|
||||||
|
if (getInput(Inputs.mode) === 'test') {
|
||||||
|
isTest = true
|
||||||
|
}
|
||||||
|
if (!option.projectPath) {
|
||||||
|
option.projectPath = './'
|
||||||
|
}
|
||||||
|
option.projectPath = path.resolve(workspace, `${option.projectPath}`)
|
||||||
|
|
||||||
|
if (option.privateKey) {
|
||||||
|
autoPrivateKey = true
|
||||||
|
option.privateKeyPath = path.resolve(workspace, defaultPrivateKeyPath)
|
||||||
|
fs.writeFileSync(option.privateKeyPath, option.privateKey)
|
||||||
|
core.info('read privateKey success and write to privateKeyPath.')
|
||||||
|
option.privateKey = ''
|
||||||
|
} else {
|
||||||
|
if (!option.privateKeyPath) {
|
||||||
|
throw Error('privateKey 和 privateKeyPath 不能都为空')
|
||||||
|
}
|
||||||
|
option.privateKeyPath = path.resolve(workspace, option.privateKeyPath)
|
||||||
|
}
|
||||||
|
if (option.ignores?.length === 0) {
|
||||||
|
option.ignores = ['node_modules/**/*']
|
||||||
|
}
|
||||||
|
const projectConfig = JSON.parse(
|
||||||
|
fs
|
||||||
|
.readFileSync(`${option.projectPath}/project.config.json`)
|
||||||
|
.toString('utf8')
|
||||||
|
)
|
||||||
|
option.appid = projectConfig.appid
|
||||||
|
option.setting = projectConfig.setting
|
||||||
|
|
||||||
|
if (!option.version) {
|
||||||
|
option.version = formatDate(new Date(), 'YYYY.MMDD.HHmmss')
|
||||||
|
}
|
||||||
|
if (!option.desc) {
|
||||||
|
option.desc = 'fix some bug.'
|
||||||
|
}
|
||||||
|
if (!option.threads) {
|
||||||
|
option.threads = os.cpus().length
|
||||||
|
}
|
||||||
|
|
||||||
|
// handler
|
||||||
|
if (isTest) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('Test option:')
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(`${stringify(option)}`)
|
||||||
|
qrcode = 'xxxxx\nx x x\nxxxxx'
|
||||||
|
} else if (option.mode === 'upload') {
|
||||||
|
core.info('Options:')
|
||||||
|
core.info(`${stringify(option)}`)
|
||||||
|
upload(option)
|
||||||
|
} else if (option.mode === 'preview') {
|
||||||
|
const previewOption: Preview = {
|
||||||
|
...option,
|
||||||
|
qrcodeFormat: filterQrcodeFormat(getInput(Inputs.qrcodeFormat)),
|
||||||
|
qrcodeOutputDest:
|
||||||
|
getInput(Inputs.qrcodeOutputDest) || './preview-qrcode.png',
|
||||||
|
pagePath: getInput(Inputs.pagePath),
|
||||||
|
searchQuery: getInput(Inputs.searchQuery),
|
||||||
|
scene: getInputAsInt(Inputs.scene)
|
||||||
|
}
|
||||||
|
|
||||||
|
previewOption.qrcodeOutputDest = path.resolve(
|
||||||
|
workspace,
|
||||||
|
previewOption.qrcodeOutputDest
|
||||||
|
)
|
||||||
|
core.info('Options:')
|
||||||
|
core.info(`${stringify(previewOption)}`)
|
||||||
|
qrcode = await preview(previewOption)
|
||||||
|
}
|
||||||
|
if (autoPrivateKey) {
|
||||||
|
fs.promises.unlink(option.privateKeyPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
core.setOutput(Outputs.appid, option.appid)
|
||||||
|
core.setOutput(Outputs.version, option.version)
|
||||||
|
core.info(` ⚙ ::set-output:: appid=${option.appid}`)
|
||||||
|
core.info(` ⚙ ::set-output:: version=${option.version}`)
|
||||||
|
core.info(` ✅ Success - ${option.mode} done.`)
|
||||||
|
let outputTemplate = getInput(Inputs.output)
|
||||||
|
if (outputTemplate) {
|
||||||
|
const outputPath = path.resolve(
|
||||||
|
workspace,
|
||||||
|
getInput(Inputs.outputPath) || './output.txt'
|
||||||
|
)
|
||||||
|
outputTemplate = outputTemplate
|
||||||
|
.replace(/{appid}/g, option.appid)
|
||||||
|
.replace(/{version}/g, option.version)
|
||||||
|
.replace(/{qrcode}/g, qrcode)
|
||||||
|
fs.writeFileSync(outputPath, outputTemplate)
|
||||||
|
core.info(` ✅ Success - write output file to ${outputPath}.`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (autoPrivateKey) {
|
||||||
|
fs.promises.unlink(defaultPrivateKeyPath)
|
||||||
|
}
|
||||||
|
if (error instanceof Error) core.setFailed(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run()
|
59
src/type/index.ts
Normal file
59
src/type/index.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
const modeDic = ['upload', 'preview'] as const
|
||||||
|
type Mode = (typeof modeDic)[number]
|
||||||
|
|
||||||
|
const typeDic = [
|
||||||
|
'miniProgram',
|
||||||
|
'miniGame',
|
||||||
|
'miniProgramPlugin',
|
||||||
|
'miniGamePlugin'
|
||||||
|
] as const
|
||||||
|
type Type = (typeof typeDic)[number]
|
||||||
|
|
||||||
|
export const filterType = (type?: string): Type => {
|
||||||
|
if (typeDic.find(item => item === type)) {
|
||||||
|
return type as Type
|
||||||
|
}
|
||||||
|
return 'miniProgram'
|
||||||
|
}
|
||||||
|
export const filterMode = (type?: string): Mode => {
|
||||||
|
if (modeDic.find(item => item === type)) {
|
||||||
|
return type as Mode
|
||||||
|
}
|
||||||
|
return 'upload'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CiOption = {
|
||||||
|
mode: Mode
|
||||||
|
appid: string
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
setting?: any
|
||||||
|
type: Type
|
||||||
|
projectPath: string
|
||||||
|
privateKey?: string
|
||||||
|
privateKeyPath?: string
|
||||||
|
ignores?: string[]
|
||||||
|
version: string
|
||||||
|
desc?: string
|
||||||
|
robot?: number
|
||||||
|
threads?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Upload = CiOption & {}
|
||||||
|
|
||||||
|
const qrcodeFormatDic = ['base64', 'image', 'terminal'] as const
|
||||||
|
type qrcodeFormat = (typeof qrcodeFormatDic)[number]
|
||||||
|
|
||||||
|
export const filterQrcodeFormat = (type?: string): qrcodeFormat => {
|
||||||
|
if (qrcodeFormatDic.find(item => item === type)) {
|
||||||
|
return type as qrcodeFormat
|
||||||
|
}
|
||||||
|
return 'terminal'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Preview = CiOption & {
|
||||||
|
qrcodeFormat: qrcodeFormat
|
||||||
|
qrcodeOutputDest: string
|
||||||
|
pagePath?: string
|
||||||
|
searchQuery?: string
|
||||||
|
scene?: number
|
||||||
|
}
|
39
src/utils/actions.ts
Normal file
39
src/utils/actions.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
|
export function getInput(
|
||||||
|
name: string,
|
||||||
|
options?: core.InputOptions
|
||||||
|
): string | undefined {
|
||||||
|
const get = core.getInput(name, options)
|
||||||
|
return get || undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getInputAsArray(
|
||||||
|
name: string,
|
||||||
|
options?: core.InputOptions
|
||||||
|
): string[] {
|
||||||
|
return core
|
||||||
|
.getInput(name, options)
|
||||||
|
.split('\n')
|
||||||
|
.map(s => s.replace(/^!\s+/, '!').trim())
|
||||||
|
.filter(x => x !== '')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getInputAsInt(
|
||||||
|
name: string,
|
||||||
|
options?: core.InputOptions
|
||||||
|
): number | undefined {
|
||||||
|
const value = parseInt(core.getInput(name, options))
|
||||||
|
if (isNaN(value) || value < 0) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getInputAsBool(
|
||||||
|
name: string,
|
||||||
|
options?: core.InputOptions
|
||||||
|
): boolean {
|
||||||
|
const result = core.getInput(name, options)
|
||||||
|
return result.toLowerCase() === 'true'
|
||||||
|
}
|
28
src/utils/constants.ts
Normal file
28
src/utils/constants.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/* eslint-disable no-shadow */
|
||||||
|
export enum Inputs {
|
||||||
|
mode = 'mode',
|
||||||
|
type = 'type',
|
||||||
|
projectPath = 'project-path',
|
||||||
|
privateKey = 'private-key',
|
||||||
|
privateKeyPath = 'private-key-path',
|
||||||
|
ignores = 'ignores',
|
||||||
|
version = 'version',
|
||||||
|
desc = 'desc',
|
||||||
|
robot = 'robot',
|
||||||
|
threads = 'threads',
|
||||||
|
// preview input
|
||||||
|
qrcodeFormat = 'qrcode-format',
|
||||||
|
qrcodeOutputDest = 'qrcode-output-dest',
|
||||||
|
pagePath = 'page-path',
|
||||||
|
searchQuery = 'search-query',
|
||||||
|
scene = 'scene',
|
||||||
|
// output
|
||||||
|
output = 'output',
|
||||||
|
outputPath = 'output-path'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Outputs {
|
||||||
|
appid = 'appid',
|
||||||
|
version = 'version',
|
||||||
|
outputPath = 'outputPath'
|
||||||
|
}
|
17
src/utils/format.ts
Normal file
17
src/utils/format.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const padZero = (n: number): string => {
|
||||||
|
return n < 10 ? `0${n}` : n.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatDate = (date: Date, format: string): string => {
|
||||||
|
const map = {
|
||||||
|
YYYY: date.getFullYear(),
|
||||||
|
MM: padZero(date.getMonth() + 1),
|
||||||
|
DD: padZero(date.getDate()),
|
||||||
|
HH: padZero(date.getHours()),
|
||||||
|
mm: padZero(date.getMinutes()),
|
||||||
|
ss: padZero(date.getSeconds())
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
return format.replace(/YYYY|MM|DD|HH|mm|ss/g, matched => map[matched])
|
||||||
|
}
|
8
src/utils/index.ts
Normal file
8
src/utils/index.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export * from './actions'
|
||||||
|
export * from './constants'
|
||||||
|
export * from './format'
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export const stringify = (obj: any): string => {
|
||||||
|
return JSON.stringify(obj, null, 2)
|
||||||
|
}
|
27
src/utils/qrcode.ts
Normal file
27
src/utils/qrcode.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import Jimp from 'jimp'
|
||||||
|
import jsQR, {QRCode} from 'jsqr'
|
||||||
|
import qrcodeTerminal from 'qrcode-terminal'
|
||||||
|
|
||||||
|
export const getQrCode = async (path: string): Promise<QRCode> => {
|
||||||
|
const jimp = await Jimp.read(path)
|
||||||
|
const pixels = Uint8ClampedArray.from(jimp.bitmap.data)
|
||||||
|
const qrcode = jsQR(pixels, jimp.getWidth(), jimp.getHeight())
|
||||||
|
if (!qrcode) {
|
||||||
|
throw Error(`get '${path}' file data failure`)
|
||||||
|
}
|
||||||
|
return qrcode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generate = async (qrcode: QRCode): Promise<string> => {
|
||||||
|
return new Promise(RES => {
|
||||||
|
qrcodeTerminal.generate(
|
||||||
|
qrcode.data,
|
||||||
|
{
|
||||||
|
small: true
|
||||||
|
},
|
||||||
|
(res: string) => {
|
||||||
|
RES(res.trim())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
12
tsconfig.json
Normal file
12
tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||||
|
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||||
|
"outDir": "./lib", /* Redirect output structure to the directory. */
|
||||||
|
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
|
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||||
|
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "**/*.test.ts"]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user