mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 20:11:43 +03:00
300 lines
13 KiB
YAML
300 lines
13 KiB
YAML
name: Nx Cloud Main
|
|
|
|
on:
|
|
workflow_call:
|
|
secrets:
|
|
NX_CLOUD_ACCESS_TOKEN:
|
|
required: false
|
|
NX_CLOUD_AUTH_TOKEN:
|
|
required: false
|
|
NX_CYPRESS_KEY:
|
|
required: false
|
|
inputs:
|
|
number-of-agents:
|
|
required: false
|
|
type: number
|
|
environment-variables:
|
|
required: false
|
|
type: string
|
|
init-commands:
|
|
required: false
|
|
type: string
|
|
final-commands:
|
|
required: false
|
|
type: string
|
|
parallel-commands:
|
|
required: false
|
|
type: string
|
|
parallel-commands-on-agents:
|
|
required: false
|
|
type: string
|
|
node-version:
|
|
required: false
|
|
type: string
|
|
yarn-version:
|
|
required: false
|
|
type: string
|
|
npm-version:
|
|
required: false
|
|
type: string
|
|
pnpm-version:
|
|
required: false
|
|
type: string
|
|
install-commands:
|
|
required: false
|
|
type: string
|
|
main-branch-name:
|
|
required: false
|
|
type: string
|
|
default: main
|
|
runs-on:
|
|
required: false
|
|
type: string
|
|
default: ubuntu-latest
|
|
# We needed this input in order to be able to configure out integration tests for this repo, it is not documented
|
|
# so as to not cause confusion/add noise, but technically any consumer of the workflow can use it if they want to.
|
|
working-directory:
|
|
required: false
|
|
type: string
|
|
|
|
env:
|
|
NX_CLOUD_DISTRIBUTED_EXECUTION: true
|
|
NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT: ${{ inputs.number-of-agents }}
|
|
NX_BRANCH: ${{ github.event.number || github.ref_name }}
|
|
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
|
NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }}
|
|
CYPRESS_RECORD_KEY: ${{ secrets.NX_CYPRESS_KEY }}
|
|
|
|
jobs:
|
|
main:
|
|
runs-on: ${{ inputs.runs-on }}
|
|
# The name of the job which will invoke this one is expected to be "Nx Cloud - Main Job", and whatever we call this will be appended
|
|
# to that one after a forward slash, so we keep this one intentionally short to produce "Nx Cloud - Main Job / Run" in the Github UI
|
|
name: Run
|
|
defaults:
|
|
run:
|
|
working-directory: ${{ inputs.working-directory || github.workspace }}
|
|
# Specify shell to help normalize across different operating systems
|
|
shell: bash
|
|
steps:
|
|
- uses: actions/checkout@v2
|
|
name: Checkout [Pull Request]
|
|
if: ${{ github.event_name == 'pull_request' }}
|
|
with:
|
|
# By default, PRs will be checked-out based on the Merge Commit, but we want the actual branch HEAD.
|
|
ref: ${{ github.event.pull_request.head.sha }}
|
|
# We need to fetch all branches and commits so that Nx affected has a base to compare against.
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/checkout@v2
|
|
name: Checkout [Default Branch]
|
|
if: ${{ github.event_name != 'pull_request' }}
|
|
with:
|
|
# We need to fetch all branches and commits so that Nx affected has a base to compare against.
|
|
fetch-depth: 0
|
|
|
|
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
|
uses: nrwl/nx-set-shas@v2
|
|
with:
|
|
main-branch-name: ${{ inputs.main-branch-name }}
|
|
|
|
- name: Detect package manager
|
|
id: package_manager
|
|
shell: bash
|
|
run: |
|
|
echo "::set-output name=name::$([[ -f ./yarn.lock ]] && echo "yarn" || ([[ -f ./pnpm-lock.yaml ]] && echo "pnpm") || echo "npm")"
|
|
# Set node/npm/yarn versions using volta, with optional overrides provided by the consumer
|
|
- uses: volta-cli/action@fdf4cf319494429a105efaa71d0e5ec67f338c6e
|
|
with:
|
|
node-version: "${{ inputs.node-version }}"
|
|
npm-version: "${{ inputs.npm-version }}"
|
|
yarn-version: "${{ inputs.yarn-version }}"
|
|
|
|
# Install pnpm with exact version provided by consumer or fallback to latest
|
|
- name: Install PNPM
|
|
if: steps.package_manager.outputs.name == 'pnpm'
|
|
uses: pnpm/action-setup@v2.2.1
|
|
with:
|
|
version: ${{ inputs.pnpm-version || 'latest' }}
|
|
|
|
- name: Print node/npm/yarn versions
|
|
id: versions
|
|
run: |
|
|
node_ver=$( node --version )
|
|
yarn_ver=$( yarn --version || true )
|
|
pnpm_ver=$( pnpm --version || true )
|
|
echo "Node: ${node_ver:1}"
|
|
echo "NPM: $( npm --version )"
|
|
if [[ $yarn_ver != '' ]]; then echo "Yarn: $yarn_ver"; fi
|
|
if [[ $pnpm_ver != '' ]]; then echo "PNPM: $pnpm_ver"; fi
|
|
echo "::set-output name=node_version::${node_ver:1}"
|
|
- name: Use the node_modules cache if available [npm]
|
|
if: steps.package_manager.outputs.name == 'npm'
|
|
uses: actions/cache@v2
|
|
with:
|
|
path: ~/.npm
|
|
key: ${{ runner.os }}-node-${{ steps.versions.outputs.node_version }}-${{ hashFiles('**/package-lock.json') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-node-${{ steps.versions.outputs.node_version }}-
|
|
- name: Use the node_modules cache if available [pnpm]
|
|
if: steps.package_manager.outputs.name == 'pnpm'
|
|
uses: actions/cache@v2
|
|
with:
|
|
path: ~/.pnpm-store
|
|
key: ${{ runner.os }}-node-${{ steps.versions.outputs.node_version }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-node-${{ steps.versions.outputs.node_version }}-
|
|
- name: Get yarn cache directory path
|
|
if: steps.package_manager.outputs.name == 'yarn'
|
|
id: yarn-cache-dir-path
|
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
|
|
|
- name: Use the node_modules cache if available [yarn]
|
|
if: steps.package_manager.outputs.name == 'yarn'
|
|
uses: actions/cache@v2
|
|
with:
|
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
|
key: ${{ runner.os }}-node-${{ steps.versions.outputs.node_version }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-node-${{ steps.versions.outputs.node_version }}-yarn-
|
|
- name: Process environment-variables
|
|
if: ${{ inputs.environment-variables != '' }}
|
|
uses: actions/github-script@v6
|
|
env:
|
|
ENV_VARS: ${{ inputs.environment-variables }}
|
|
with:
|
|
script: |
|
|
const { appendFileSync } = require('fs');
|
|
// trim spaces and escape quotes
|
|
const cleanStr = str => str
|
|
.trim()
|
|
.replaceAll(/`/g, "\`");
|
|
// parse variable to correct type
|
|
const parseStr = str =>
|
|
str === 'true' || str === 'TRUE'
|
|
? true
|
|
: str === 'false' || str === 'FALSE'
|
|
? false
|
|
: isNaN(str)
|
|
? str
|
|
: parseFloat(str);
|
|
const varsStr = process.env.ENV_VARS || '';
|
|
const vars = varsStr
|
|
.split('\n')
|
|
.map(variable => variable.trim())
|
|
.filter(variable => variable.indexOf('=') > 0)
|
|
.map(variable => ({
|
|
name: cleanStr(variable.split('=')[0]),
|
|
value: cleanStr(variable.slice(variable.indexOf('=') + 1))
|
|
}));
|
|
for (const v of vars) {
|
|
console.log(`Appending environment variable \`${v.name}\` with value \`${v.value}\` to ${process.env.GITHUB_ENV}`);
|
|
appendFileSync(process.env.GITHUB_ENV, `${v.name}=${parseStr(v.value)}\n`);
|
|
}
|
|
- name: Run any configured install-commands
|
|
if: ${{ inputs.install-commands != '' }}
|
|
run: |
|
|
${{ inputs.install-commands }}
|
|
- name: Install dependencies
|
|
if: ${{ inputs.install-commands == '' }}
|
|
run: |
|
|
if [ "${{ steps.package_manager.outputs.name == 'yarn' }}" == "true" ]; then
|
|
echo "Running yarn install --frozen-lockfile"
|
|
yarn install --frozen-lockfile
|
|
elif [ "${{ steps.package_manager.outputs.name == 'pnpm' }}" == "true" ]; then
|
|
echo "Running pnpm install --frozen-lockfile"
|
|
pnpm install --frozen-lockfile
|
|
else
|
|
echo "Running npm ci"
|
|
npm ci
|
|
fi
|
|
# An unfortunate side-effect of the way reusable workflows work is that by the time they are pulled into the "caller"
|
|
# repo, they are effectively completely embedded in that context. This means that we cannot reference any files which
|
|
# are local to this repo which defines the workflow, and we therefore need to work around this by embedding the contents
|
|
# of the shell utilities for executing commands into the workflow directly.
|
|
- name: Create command utils
|
|
uses: actions/github-script@v6
|
|
with:
|
|
script: |
|
|
const { writeFileSync } = require('fs');
|
|
const runCommandsInParallelScript = `
|
|
# Extract the provided commands from the stringified JSON array.
|
|
IFS=$'\n' read -d '' -a userCommands < <((jq -c -r '.[]') <<<"$1")
|
|
# Invoke the provided commands in parallel and collect their exit codes.
|
|
pids=()
|
|
for userCommand in "\${userCommands[@]}"; do
|
|
eval "$userCommand" & pids+=($!)
|
|
done
|
|
# If any one of the invoked commands exited with a non-zero exit code, exit the whole thing with code 1.
|
|
for pid in \${pids[*]}; do
|
|
if ! wait $pid; then
|
|
exit 1
|
|
fi
|
|
done
|
|
# All the invoked commands must have exited with code zero.
|
|
exit 0
|
|
`;
|
|
writeFileSync('./.github/workflows/run-commands-in-parallel.sh', runCommandsInParallelScript);
|
|
- name: Prepare command utils
|
|
# We need to escape the workspace path to be consistent cross-platform: https://github.com/actions/runner/issues/1066
|
|
run: chmod +x ${GITHUB_WORKSPACE//\\//}/.github/workflows/run-commands-in-parallel.sh
|
|
|
|
- name: Initialize the Nx Cloud distributed CI run
|
|
run: npx nx-cloud start-ci-run
|
|
|
|
# The good thing about the multi-line string input for sequential commands is that we can simply forward it on as is to the bash shell and it will behave
|
|
# how we want it to in terms of quote escaping, variable assignment etc
|
|
- name: Run any configured init-commands sequentially
|
|
if: ${{ inputs.init-commands != '' }}
|
|
run: |
|
|
${{ inputs.init-commands }}
|
|
- name: Process parallel commands configuration
|
|
uses: actions/github-script@v6
|
|
id: parallel_commands_config
|
|
env:
|
|
PARALLEL_COMMANDS: ${{ inputs.parallel-commands }}
|
|
PARALLEL_COMMANDS_ON_AGENTS: ${{ inputs.parallel-commands-on-agents }}
|
|
with:
|
|
# For the ones configured for main, explicitly set NX_CLOUD_DISTRIBUTED_EXECUTION to false, taking into account commands chained with &&
|
|
# within the strings. In order to properly escape single quotes we need to do some manual replacing and escaping so that the commands
|
|
# are forwarded onto the run-commands-in-parallel.sh script appropriately.
|
|
script: |
|
|
const parallelCommandsOnMainStr = process.env.PARALLEL_COMMANDS || '';
|
|
const parallelCommandsOnAgentsStr = process.env.PARALLEL_COMMANDS_ON_AGENTS || '';
|
|
const parallelCommandsOnMain = parallelCommandsOnMainStr
|
|
.split('\n')
|
|
.map(command => command.trim())
|
|
.filter(command => command.length > 0)
|
|
.map(s => s.replace(/'/g, '%27'));
|
|
const parallelCommandsOnAgents = parallelCommandsOnAgentsStr
|
|
.split('\n')
|
|
.map(command => command.trim())
|
|
.filter(command => command.length > 0)
|
|
.map(s => s.replace(/'/g, '%27'));
|
|
const formattedArrayOfCommands = [
|
|
...parallelCommandsOnMain.map(s => s
|
|
.split(' && ')
|
|
.map(s => `NX_CLOUD_DISTRIBUTED_EXECUTION=false ${s}`)
|
|
.join(' && ')
|
|
),
|
|
...parallelCommandsOnAgents,
|
|
];
|
|
const stringifiedEncodedArrayOfCommands = JSON.stringify(formattedArrayOfCommands)
|
|
.replace(/%27/g, "'\\''");
|
|
return stringifiedEncodedArrayOfCommands
|
|
result-encoding: string
|
|
|
|
- name: Run any configured parallel commands on main and agent jobs
|
|
# We need to escape the workspace path to be consistent cross-platform: https://github.com/actions/runner/issues/1066
|
|
run: ${GITHUB_WORKSPACE//\\//}/.github/workflows/run-commands-in-parallel.sh '${{ steps.parallel_commands_config.outputs.result }}'
|
|
|
|
# The good thing about the multi-line string input for sequential commands is that we can simply forward it on as is to the bash shell and it will behave
|
|
# how we want it to in terms of quote escaping, variable assignment etc
|
|
- name: Run any configured final-commands sequentially
|
|
if: ${{ inputs.final-commands != '' }}
|
|
run: |
|
|
${{ inputs.final-commands }}
|
|
- name: Stop all running agents for this CI run
|
|
# It's important that we always run this step, otherwise in the case of any failures in preceding non-Nx steps, the agents will keep running and waste billable minutes
|
|
if: ${{ always() }}
|
|
run: npx nx-cloud stop-all-agents |