diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..db30c78d1 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,91 @@ +name: Build + +on: + push: + # branches: [feat/xxx] + tags: + - 'v*.*.*' +jobs: + release: + name: build and release electron app + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [windows-latest, macos-12, ubuntu-latest] + + steps: + - name: Check out git repository + uses: actions/checkout@v3.0.0 + + - name: Install Node.js + uses: actions/setup-node@v3.0.0 + with: + node-version: '16' + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v3 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: Install + run: | + yarn install --frozen-lockfile + echo "${{ secrets.QINIU_ENV_JS }}" > qiniu_env.js + + - name: Release for Windows + if: matrix.os == 'windows-latest' + run: | + yarn release + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + + - name: Release for MacOS + if: matrix.os == 'macos-12' + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} + P12_PASSWORD: ${{ secrets.P12_PASSWORD }} + # BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + run: | + # create variables + CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 + # PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + + # import certificate and provisioning profile from secrets + echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode --output $CERTIFICATE_PATH + # echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode --output $PP_PATH + + # create temporary keychain + security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + # import certificate to keychain + security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + # apply provisioning profile + # mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles + # cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles + echo "${{ secrets.NOTARIZE_JS }}" > scripts/notarize.js + yarn release + + # clean up keychain and provisioning profile + security delete-keychain $RUNNER_TEMP/app-signing.keychain-db + + - name: Release for Linux + if: matrix.os == 'ubuntu-latest' + run: | + yarn release + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3f978dd4c..3bacca79b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,19 +1,20 @@ name: Release +# github release event list: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release + on: - push: - # branches: [feat/v0.1.0] - tags: - - 'v*.*.*' + release: + types: [published] + jobs: release: - name: build and release electron app + name: Qi niu published runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [windows-latest, macos-12, ubuntu-latest] + os: [ubuntu-latest] steps: - name: Check out git repository @@ -24,68 +25,9 @@ jobs: with: node-version: '16' - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - - uses: actions/cache@v3 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: Install - run: | - yarn install --frozen-lockfile - echo "${{ secrets.QINIU_ENV_JS }}" > qiniu_env.js - - - name: Release for Windows - if: matrix.os == 'windows-latest' - run: | - yarn release - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - - - name: Release for MacOS x86 - if: matrix.os == 'macos-12' - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} - P12_PASSWORD: ${{ secrets.P12_PASSWORD }} - # BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }} - KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} - run: | - # create variables - CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 - # PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision - KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db - - # import certificate and provisioning profile from secrets - echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode --output $CERTIFICATE_PATH - # echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode --output $PP_PATH - - # create temporary keychain - security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - security set-keychain-settings -lut 21600 $KEYCHAIN_PATH - security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - - # import certificate to keychain - security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH - security list-keychain -d user -s $KEYCHAIN_PATH - - # apply provisioning profile - # mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles - # cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles - echo "${{ secrets.NOTARIZE_JS }}" > scripts/notarize.js - yarn release - - # clean up keychain and provisioning profile - security delete-keychain $RUNNER_TEMP/app-signing.keychain-db - - name: Release for Linux if: matrix.os == 'ubuntu-latest' run: | - yarn release - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + npm i -g qiniu@6.x + echo "${{ secrets.QINIU_ENV_JS }}" > qiniu_env.js + node publish.js diff --git a/.gitignore b/.gitignore index 3005730a0..81e57cd4f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ out/ !*.config.js !.*.js !upload.js +!publish.js # dependencies node_modules diff --git a/publish.js b/publish.js new file mode 100644 index 000000000..096e5d560 --- /dev/null +++ b/publish.js @@ -0,0 +1,63 @@ +const qiniu = require('qiniu'); +const package = require('./package.json'); +const { AK, SK, bucket } = require('./qiniu_env.js'); + +qiniu.conf.ACCESS_KEY = AK; +qiniu.conf.SECRET_KEY = SK; + +const toLatest = name => name.replace(/\d+\.\d+\.\d+/, 'latest'); +const onlyName = name => name.replace(/release\//, ''); + +// * 构建客户端实例 +const client = new qiniu.rs.Client(); + +// * 删除单个文件 +const removeFile = (spaceName, file) => + new Promise(resolve => { + client.remove(spaceName, file, (err, ret) => (err ? resolve(false) : resolve(true))); + }); + +// * 拷贝单个文件 +const cpFile = (fromFile, toFile) => + new Promise(resolve => { + client.copy(bucket, fromFile, bucket, toFile, err => (err ? resolve(false) : resolve(true))); + }); + +const version = package.version; +const fileList = [ + 'release/Postcat-Setup-?.exe', + 'release/Postcat-?-arm64.dmg', + 'release/Postcat-?-arm64-mac.zip', + 'release/Postcat-?.dmg', + 'release/Postcat-?-mac.zip', + 'release/Postcat-?.AppImage', + 'release/latest.yml', + 'release/latest-linux.yml', + 'release/latest-mac.yml' +].map(it => it.replace(/\?/, `${version}`)); + +const main = async () => { + const deleteResult = await Promise.all( + fileList.map(async it => { + const isOK = await removeFile(bucket, `download/latest/${toLatest(onlyName(it))}`); + return Promise.resolve(isOK || false); + }) + ); + console.log('删除latest文件结果', deleteResult); + const copyFile = await Promise.all( + fileList.map(async it => { + const isOK = await cpFile(`download/${version}/${onlyName(it)}`, `download/latest/${toLatest(onlyName(it))}`); + return Promise.resolve(isOK || false); + }) + ); + console.log('拷贝latest文件结果', copyFile); + const copyYml = await Promise.all( + fileList.map(async it => { + const isOK = await cpFile(`download/${version}/${onlyName(it)}`, `download/latest/${onlyName(it)}`); + return Promise.resolve(isOK || false); + }) + ); + console.log('拷贝版本文件结果', copyYml); +}; + +main(); diff --git a/upload.js b/upload.js index 0ab7a5172..7663b9d26 100644 --- a/upload.js +++ b/upload.js @@ -1,6 +1,5 @@ const qiniu = require('qiniu'); -const YAML = require('yaml'); -const fs = require('fs'); +const { promises } = require('fs'); const { AK, SK, bucket } = require('./qiniu_env.js'); const package = require('./package.json'); @@ -13,6 +12,13 @@ const uptoken = (bucket, key) => new qiniu.rs.PutPolicy(bucket + ':' + key).toke const toLatest = name => name.replace(/\d+\.\d+\.\d+/, 'latest'); const onlyName = name => name.replace(/release\//, ''); +// * 检测文件是否存在 +const isExists = async filePath => + await promises + .access(filePath) + .then(() => true) + .catch(_ => false); + // * 构建客户端实例 const client = new qiniu.rs.Client(); @@ -42,6 +48,7 @@ const version = package.version; const fileList = [ 'release/Postcat-Setup-?.exe', 'release/Postcat-?-arm64.dmg', + 'release/Postcat-?-arm64-mac.zip', 'release/Postcat-?.dmg', 'release/Postcat-?-mac.zip', 'release/Postcat-?.AppImage', @@ -53,41 +60,36 @@ const fileList = [ // it.replace(/\?/, `${version}`) // ); -const app = async () => { +const main = async () => { const uploadResult = await Promise.all( - fileList.map(async it => { - let isOK; - // * 生成上传 Token - try { - if (it.endsWith('.yml')) { - await removeFile(bucket, `download/${onlyName(it)}`); - const token = uptoken(bucket, `download/${onlyName(it)}`); - isOK = await uploadFile(token, `download/${onlyName(it)}`, it); - } else { - const token = uptoken(bucket, `download/${version}/${it.replace(/release\//, '')}`); - isOK = await uploadFile(token, `download/${version}/${it.replace(/release\//, '')}`, it); - } - } catch (error) { - console.log('error', error); - } - return Promise.resolve(isOK || false); - }) + fileList + .filter(async it => await isExists(it)) + .map(async it => { + // * 生成上传 Token + const token = uptoken(bucket, `download/${version}/${it.replace(/release\//, '')}`); + const isOK = await uploadFile(token, `download/${version}/${it.replace(/release\//, '')}`, it); + return Promise.resolve(isOK || false); + }) ); console.log('上传结果:', uploadResult); - const deleteResult = await Promise.all( - fileList.map(async it => { - const isOK = await removeFile(bucket, `download/latest/${toLatest(onlyName(it))}`); - Promise.resolve(isOK || false); - }) - ); - console.log('删除结果:', deleteResult); - const copyResult = await Promise.all( - fileList.map(async it => { - const isOK = await cpFile(`download/${version}/${onlyName(it)}`, `download/latest/${toLatest(onlyName(it))}`); - Promise.resolve(isOK || false); - }) - ); - console.log('拷贝结果', copyResult); + // const deleteResult = await Promise.all( + // fileList + // .filter(async it => await isExists(it)) + // .map(async it => { + // const isOK = await removeFile(bucket, `download/latest/${toLatest(onlyName(it))}`); + // return Promise.resolve(isOK || false); + // }) + // ); + // console.log('删除结果:', deleteResult); + // const copyResult = await Promise.all( + // fileList + // .filter(async it => await isExists(it)) + // .map(async it => { + // const isOK = await cpFile(`download/${version}/${onlyName(it)}`, `download/latest/${toLatest(onlyName(it))}`); + // return Promise.resolve(isOK || false); + // }) + // ); + // console.log('拷贝结果', copyResult); }; -app(); +main();