mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
AAE-32905 Rework pr pipeline (#11853)
* [AAE-32905] - Improve ADF workflow * [ci:force] * [AAE-32905] - Improve cache use * [AAE-32905] - Improve cache use * [AAE-32905] - Improved workflows * [AAE-32905] - Checking cache * [AAE-32905] - Checking cache * [AAE-32905] - Fix cli * [AAE-32905] - Fix cache * [AAE-32905] - Fix cache * [AAE-32905] - Fix cache * [AAE-32905] - improving workflow and adding some storybook tests * [AAE-32905] - improving workflow and adding some storybook tests * [AAE-32905] - Added more improvements * [AAE-32905] - Added more improvements * [AAE-32905] - bugfix * [AAE-32905] - Added missing part to the cache * [AAE-32905] - bugfix * [AAE-32905] - fixed cache poisoning * [AAE-32905] - timing jobs * [AAE-32905] - chosen to build all together as it's more efficient * [AAE-32905] - Fixed script injection issue
This commit is contained in:
+2
-2
@@ -10,9 +10,9 @@
|
||||
# precedence. When someone opens a pull request that only
|
||||
# modifies JS files, only @js-owner and not the global
|
||||
# owner(s) will be requested for a review.
|
||||
/e2e/ @eromano @cristinaj
|
||||
/e2e/ @eromano
|
||||
|
||||
# The `docs/*` pattern will match files like
|
||||
# `docs/getting-started.md` but not further nested files like
|
||||
# `docs/build-app/troubleshooting.md`.
|
||||
/docs/ @m-hulbert @eromano
|
||||
/docs/ @eromano
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
name: 'Save Nx Cache'
|
||||
description: 'Save Nx cache and dist outputs only when the job succeeds'
|
||||
inputs:
|
||||
cache-suffix:
|
||||
description: 'Must match the cache-suffix used in the setup action'
|
||||
required: true
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Save nx cache
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: |
|
||||
nxcache
|
||||
.nx
|
||||
dist
|
||||
key: ${{ runner.os }}-nxcache-${{ inputs.cache-suffix }}-${{ hashFiles('nx.json') }}-${{ github.sha }}
|
||||
@@ -6,6 +6,15 @@ inputs:
|
||||
required: false
|
||||
type: boolean
|
||||
default: 'true'
|
||||
cache-suffix:
|
||||
description: 'Suffix to make Nx cache key unique per job (e.g. matrix project name)'
|
||||
required: false
|
||||
default: 'default'
|
||||
full-setup:
|
||||
description: 'Run git-latest-tag, npm-tag, and before-install (requires fetch-depth: 0). Set to false for matrix jobs that only need node/cache.'
|
||||
required: false
|
||||
type: boolean
|
||||
default: 'true'
|
||||
act:
|
||||
description: 'enable act debug'
|
||||
required: false
|
||||
@@ -15,36 +24,50 @@ outputs:
|
||||
npm-tag:
|
||||
description: 'NPM tag'
|
||||
value: ${{ steps.set-npm-tag.outputs.npm-tag }}
|
||||
node-modules-cache-hit:
|
||||
description: 'Whether node_modules cache was hit'
|
||||
value: ${{ steps.node-modules-cache.outputs.cache-hit }}
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Set consistent machine ID for Nx cache
|
||||
shell: bash
|
||||
run: echo "nx-github-actions" | sudo tee /etc/machine-id > /dev/null
|
||||
- name: install NPM
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache-dependency-path: package-lock.json
|
||||
- name: get latest tag sha
|
||||
if: ${{ inputs.full-setup == 'true' }}
|
||||
id: tag-sha
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/git-latest-tag@f95467b62527ce087c9a1b7e55c467061cf827a0 # v17.6.1
|
||||
# CACHE
|
||||
- name: Node Modules cache
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/git-latest-tag@8d76614b374e521d792b4a1c9827409960b66bae # v17.6.0
|
||||
- name: load "NPM TAG"
|
||||
if: ${{ inputs.full-setup == 'true' }}
|
||||
id: set-npm-tag
|
||||
uses: ./.github/actions/set-npm-tag
|
||||
- name: Cache node modules
|
||||
id: node-modules-cache
|
||||
if: ${{ inputs.enable-node-modules-cache == 'true' }}
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
env:
|
||||
cache-name: node-modules-cache
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-node-modules-${{ hashFiles('.nvmrc') }}-${{ hashFiles('package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-modules-${{ hashFiles('.nvmrc') }}-
|
||||
- name: Restore nx cache
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
key: .npm-${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
nxcache
|
||||
.nx
|
||||
dist
|
||||
key: ${{ runner.os }}-nxcache-${{ inputs.cache-suffix }}-${{ hashFiles('nx.json') }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
.npm-${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
.npm-${{ runner.os }}-build-
|
||||
.npm-${{ runner.os }}-
|
||||
- name: load "NPM TAG"
|
||||
id: set-npm-tag
|
||||
uses: ./.github/actions/set-npm-tag
|
||||
${{ runner.os }}-nxcache-${{ inputs.cache-suffix }}-${{ hashFiles('nx.json') }}-
|
||||
${{ runner.os }}-nxcache-${{ inputs.cache-suffix }}-
|
||||
- name: before install script
|
||||
if: ${{ inputs.full-setup == 'true' }}
|
||||
uses: ./.github/actions/before-install
|
||||
with:
|
||||
act: ${{ inputs.act }}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
name: "Build Lib Workflow"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
base_ref:
|
||||
description: 'Base branch for affected calculation'
|
||||
required: false
|
||||
type: string
|
||||
default: 'develop'
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=5120"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build affected libs"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup environment
|
||||
id: setup-env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
cache-suffix: build
|
||||
full-setup: 'false'
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.setup-env.outputs.node-modules-cache-hit != 'true' }}
|
||||
run: npm ci
|
||||
- name: Build affected libs
|
||||
env:
|
||||
BASE_REF: ${{ inputs.base_ref }}
|
||||
run: npx nx affected --target=build --base=origin/$BASE_REF --head=HEAD --configuration=production --exclude=stories
|
||||
- name: Save nx cache
|
||||
if: ${{ success() }}
|
||||
uses: ./.github/actions/save-nx-cache
|
||||
with:
|
||||
cache-suffix: build
|
||||
+214
-186
@@ -31,16 +31,14 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
BASE_REF: ${{ github.base_ref }}
|
||||
HEAD_REF: ${{ github.head_ref }}
|
||||
GH_COMMIT: ${{ github.sha }}
|
||||
GH_BUILD_NUMBER: ${{ github.run_id }}
|
||||
LOG_LEVEL: "ERROR"
|
||||
NODE_OPTIONS: "--max-old-space-size=5120"
|
||||
|
||||
jobs:
|
||||
pre-checks:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
code-changed: ${{ steps.path-filter.outputs.code-changed }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -57,150 +55,121 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Detect code changes
|
||||
id: path-filter
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" != "pull_request" ]; then
|
||||
echo "Not a PR event — assuming code changed"
|
||||
echo "code-changed=true" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
FILES=$(gh api /repos/$GITHUB_REPOSITORY/pulls/${{ github.event.pull_request.number }}/files --paginate --jq '.[].filename')
|
||||
|
||||
CODE_CHANGED="false"
|
||||
while IFS= read -r file; do
|
||||
case "$file" in
|
||||
*.md|docs/*|.github/*.md|.github/CODEOWNERS|.github/dependabot.yml|LICENSE*|NOTICE*|.editorconfig|.gitattributes)
|
||||
;;
|
||||
*)
|
||||
CODE_CHANGED="true"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done <<< "$FILES"
|
||||
|
||||
echo "code-changed=$CODE_CHANGED" >> $GITHUB_OUTPUT
|
||||
echo "Code changed: $CODE_CHANGED"
|
||||
|
||||
check-if-pr-is-approved:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check if PR approval can be skipped
|
||||
id: skip-check
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
ACTOR: ${{ github.actor }}
|
||||
DEVEL_FLAG: ${{ inputs.devel }}
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message || github.event.pull_request.title || '' }}
|
||||
run: |
|
||||
skip="false"
|
||||
|
||||
if [ "$EVENT_NAME" == "schedule" ] || [ "$EVENT_NAME" == "workflow_dispatch" ]; then
|
||||
echo -e "\033[32mSchedule/dispatch event — skipping approval check\033[0m"
|
||||
skip="true"
|
||||
elif [ "$ACTOR" == "dependabot[bot]" ] || [ "$ACTOR" == "alfresco-build" ]; then
|
||||
echo -e "\033[32mCommit by $ACTOR — skipping approval check\033[0m"
|
||||
skip="true"
|
||||
elif [[ "$COMMIT_MESSAGE" == *"[ci:force]"* ]]; then
|
||||
echo -e "\033[32m[ci:force] flag detected — skipping approval check\033[0m"
|
||||
skip="true"
|
||||
elif [[ "$DEVEL_FLAG" == "true" ]]; then
|
||||
echo -e "\033[32mDevel flag — skipping approval check\033[0m"
|
||||
skip="true"
|
||||
fi
|
||||
|
||||
echo "skip=$skip" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get PR number
|
||||
if: ${{ steps.skip-check.outputs.skip != 'true' }}
|
||||
id: pr-number
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
if [ -z "$PR_NUMBER" ]; then
|
||||
PR_NUMBER=$(gh pr view --json number --jq '.number' 2>/dev/null || echo "")
|
||||
fi
|
||||
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
|
||||
echo "PR: $PR_NUMBER"
|
||||
|
||||
- name: Check if PR is approved
|
||||
if: ${{ steps.skip-check.outputs.skip != 'true' && steps.pr-number.outputs.pr_number != '' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
PR_NUMBER: ${{ steps.pr-number.outputs.pr_number }}
|
||||
run: |
|
||||
echo "Checking approval for PR: $PR_NUMBER"
|
||||
checkApproval=$(gh api /repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews | jq '.[] | select(.state == "APPROVED") | .user.login')
|
||||
if [[ $checkApproval ]]; then
|
||||
echo -e "\033[32mPR approved\033[0m"
|
||||
else
|
||||
echo -e "\033[31mPR NOT approved\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
setup:
|
||||
timeout-minutes: 30
|
||||
name: "Setup"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [check-if-pr-is-approved, pre-checks]
|
||||
if: ${{ needs.pre-checks.outputs.code-changed == 'true' }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get branch name
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/get-branch-name@f95467b62527ce087c9a1b7e55c467061cf827a0 # v17.6.1
|
||||
|
||||
- name: Save commit message
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/get-commit-message@f95467b62527ce087c9a1b7e55c467061cf827a0 # v17.6.1
|
||||
- name: Setup environment
|
||||
id: setup-env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
header-only: true
|
||||
|
||||
- name: ci:force flag parser
|
||||
shell: bash
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
ACTOR: ${{ github.actor }}
|
||||
cache-suffix: setup
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.setup-env.outputs.node-modules-cache-hit != 'true' }}
|
||||
run: npm ci
|
||||
- name: Bundle
|
||||
run: |
|
||||
if [ "$EVENT_NAME" == "schedule" ] || [ "$ACTOR" == "dependabot[bot]" ]; then
|
||||
echo -e "\033[32mci:force check can be skipped\033[0m"
|
||||
skip_check="true"
|
||||
elif [[ "$COMMIT_MESSAGE" == *"[ci:force]"* ]]; then
|
||||
echo -e "\033[32m[ci:force] flag detected. No need for approval.\033[0m"
|
||||
skip_check="true"
|
||||
fi
|
||||
|
||||
- name: Get PR number
|
||||
if: ${{ github.event_name != 'schedule' && github.event_name != 'workflow_dispatch' }}
|
||||
uses: kamatama41/get-pr-number-action@ff143e03abaa79d346ca5407d49d335b5def979e # v1.0.1
|
||||
id: action
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: show pr number
|
||||
shell: bash
|
||||
env:
|
||||
PR_NUMBER: ${{ steps.action.outputs.number }}
|
||||
run: |
|
||||
echo "PR: $PR_NUMBER"
|
||||
|
||||
- name: check if pr is approved
|
||||
env:
|
||||
DEVEL_FLAG: ${{ inputs.devel }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
skip_check: "false"
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
ACTOR: ${{ github.actor }}
|
||||
PR_NUMBER: ${{ steps.action.outputs.number }}
|
||||
run: |
|
||||
if [ "$EVENT_NAME" == "schedule" ] || [ "$ACTOR" == "dependabot[bot]" ]; then
|
||||
echo -e "\033[32mci:force check can be skipped\033[0m"
|
||||
skip_check="true"
|
||||
elif [[ "$COMMIT_MESSAGE" == *"[ci:force]"* ]]; then
|
||||
echo -e "\033[32m[ci:force] flag detected. No need for approval.\033[0m"
|
||||
skip_check="true"
|
||||
fi
|
||||
|
||||
if [ "$ACTOR" == "dependabot[bot]" ] || [ "$ACTOR" == "alfresco-build" ]; then
|
||||
echo -e "\033[32mCommit by $ACTOR. No need for approval.\033[0m"
|
||||
skip_check="true"
|
||||
fi
|
||||
if [ "$EVENT_NAME" == "schedule" ] || [ "$EVENT_NAME" == "workflow_dispatch" ]; then
|
||||
echo -e "\033[32mSchedule event\033[0m"
|
||||
skip_check="true"
|
||||
fi
|
||||
|
||||
if [[ "$DEVEL_FLAG" == "true" ]]; then
|
||||
echo -e "\033[32mDevel flag\033[0m"
|
||||
skip_check="true"
|
||||
fi
|
||||
|
||||
if [ "$skip_check" == "false" ]; then
|
||||
echo "Checking PR approval"
|
||||
prNumber=$PR_NUMBER
|
||||
echo "PR: $prNumber"
|
||||
checkApproval=$(gh api /repos/$GITHUB_REPOSITORY/pulls/$prNumber/reviews | jq '.[] | select(.state == "APPROVED") | .user.login')
|
||||
if [[ $checkApproval ]]; then
|
||||
echo -e "\033[32mPR approved\033[0m"
|
||||
else
|
||||
echo -e "\033[31mPR NOT approved\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
setup:
|
||||
# long timeout required when cache has to be recreated
|
||||
timeout-minutes: 30
|
||||
name: "Setup"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [check-if-pr-is-approved, pre-checks]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for all tags and branches
|
||||
- uses: ./.github/actions/setup
|
||||
- name: install
|
||||
run: |
|
||||
npm ci
|
||||
npm run bundle:js-api
|
||||
npm run bundle:cli
|
||||
- uses: ./.github/actions/upload-node-modules-and-artifacts
|
||||
|
||||
unit-tests:
|
||||
timeout-minutes: 30
|
||||
name: "Unit tests: ${{ matrix.unit-tests.name }}"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [setup]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
# max-parallel: 4
|
||||
matrix:
|
||||
unit-tests:
|
||||
- name: js-api
|
||||
exclude: "core,insights,content-services,process-services,process-services-cloud,eslint-plugin-eslint-angular"
|
||||
- name: content-services
|
||||
exclude: "insights,core,extensions,process-services,process-services-cloud,eslint-plugin-eslint-angular,js-api"
|
||||
- name: core
|
||||
exclude: "insights,content-services,process-services,process-services-cloud,eslint-plugin-eslint-angular,js-api"
|
||||
- name: insights
|
||||
exclude: "core,extensions,content-services,process-services-cloud,process-services,eslint-plugin-eslint-angular,js-api"
|
||||
- name: process-services
|
||||
exclude: "core,extensions,content-services,process-services-cloud,insights,eslint-plugin-eslint-angular,js-api"
|
||||
- name: process-services-cloud
|
||||
exclude: "insights,core,extensions,content-services,process-services$,eslint-plugin-eslint-angular,js-api"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Save nx cache
|
||||
if: ${{ success() }}
|
||||
uses: ./.github/actions/save-nx-cache
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for all tags and branches
|
||||
- uses: ./.github/actions/setup
|
||||
- uses: ./.github/actions/download-node-modules-and-artifacts
|
||||
- name: Run unit tests
|
||||
env:
|
||||
EXCLUDE_PATTERN: ${{ matrix.unit-tests.exclude }}
|
||||
run: |
|
||||
/usr/bin/xvfb-run --auto-servernum npm run test:affected -- $NX_CALCULATION_FLAGS --exclude=$EXCLUDE_PATTERN
|
||||
cache-suffix: setup
|
||||
|
||||
lint:
|
||||
# long timeout required when cache has to be recreated
|
||||
timeout-minutes: 30
|
||||
name: "Lint"
|
||||
runs-on: ubuntu-latest
|
||||
@@ -209,71 +178,133 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for all tags and branches
|
||||
- uses: ./.github/actions/setup
|
||||
- uses: ./.github/actions/download-node-modules-and-artifacts
|
||||
- run: npm run lint:affected -- $NX_CALCULATION_FLAGS
|
||||
|
||||
build-libs:
|
||||
# long timeout required when cache has to be recreated
|
||||
timeout-minutes: 30
|
||||
name: "Build libs"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [setup]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
fetch-depth: 0
|
||||
- name: Setup environment
|
||||
id: setup-env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for all tags and branches
|
||||
- uses: ./.github/actions/setup
|
||||
- uses: ./.github/actions/download-node-modules-and-artifacts
|
||||
- run: NX_REJECT_UNKNOWN_LOCAL_CACHE=0 npm run build:affected -- $NX_CALCULATION_FLAGS --prod
|
||||
- uses: ./.github/actions/upload-node-modules-and-artifacts
|
||||
cache-suffix: lint
|
||||
full-setup: 'false'
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.setup-env.outputs.node-modules-cache-hit != 'true' }}
|
||||
run: npm ci
|
||||
- name: Run lint
|
||||
env:
|
||||
BASE_REF: ${{ github.base_ref || 'develop' }}
|
||||
run: npx nx affected --target=lint --base=origin/$BASE_REF --head=HEAD
|
||||
- name: Save nx cache
|
||||
if: ${{ success() }}
|
||||
uses: ./.github/actions/save-nx-cache
|
||||
with:
|
||||
cache-suffix: lint
|
||||
|
||||
trigger-build:
|
||||
name: "Build Libs"
|
||||
needs: [setup]
|
||||
uses: ./.github/workflows/build-lib-workflow.yml
|
||||
with:
|
||||
base_ref: ${{ github.base_ref || 'develop' }}
|
||||
|
||||
build-storybook:
|
||||
timeout-minutes: 30
|
||||
name: "Build storybook"
|
||||
runs-on: ubuntu-latest
|
||||
name: "Build Storybook"
|
||||
needs: [setup]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for all tags and branches
|
||||
- uses: ./.github/actions/setup
|
||||
- uses: ./.github/actions/download-node-modules-and-artifacts
|
||||
- run: npm run build-storybook
|
||||
- uses: ./.github/actions/upload-node-modules-and-artifacts
|
||||
fetch-depth: 0
|
||||
- name: Setup environment
|
||||
id: setup-env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
cache-suffix: storybook
|
||||
full-setup: 'false'
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.setup-env.outputs.node-modules-cache-hit != 'true' }}
|
||||
run: npm ci
|
||||
- name: Build Storybook
|
||||
env:
|
||||
BASE_REF: ${{ github.base_ref || 'develop' }}
|
||||
run: |
|
||||
npx nx affected --target=build-storybook --base=origin/$BASE_REF --head=HEAD --configuration=ci
|
||||
- name: Save nx cache
|
||||
if: ${{ success() }}
|
||||
uses: ./.github/actions/save-nx-cache
|
||||
with:
|
||||
cache-suffix: storybook
|
||||
|
||||
trigger-unit-tests:
|
||||
name: "Unit Tests"
|
||||
needs: [setup]
|
||||
uses: ./.github/workflows/unit-test-workflow.yml
|
||||
with:
|
||||
base_ref: ${{ github.base_ref || 'develop' }}
|
||||
|
||||
PR-size-check:
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check PR size
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.issue.number,
|
||||
});
|
||||
|
||||
const additions = pr.additions;
|
||||
const deletions = pr.deletions;
|
||||
const totalChanges = additions + deletions;
|
||||
const changedFiles = pr.changed_files;
|
||||
|
||||
let size = 'S';
|
||||
if (totalChanges > 1000 || changedFiles > 30) size = 'XL';
|
||||
else if (totalChanges > 500 || changedFiles > 20) size = 'L';
|
||||
else if (totalChanges > 200 || changedFiles > 10) size = 'M';
|
||||
|
||||
core.summary
|
||||
.addHeading('PR Size: ' + size, 3)
|
||||
.addTable([
|
||||
[{data: 'Metric', header: true}, {data: 'Count', header: true}],
|
||||
['Files changed', String(changedFiles)],
|
||||
['Additions', '+' + String(additions)],
|
||||
['Deletions', '-' + String(deletions)],
|
||||
['Total changes', String(totalChanges)],
|
||||
]);
|
||||
|
||||
if (size === 'XL') {
|
||||
core.summary.addRaw('⚠️ This PR is very large. Consider splitting it into smaller PRs for easier review.');
|
||||
}
|
||||
|
||||
await core.summary.write();
|
||||
|
||||
if (size === 'XL') {
|
||||
core.warning('This PR has ' + totalChanges + ' changes across ' + changedFiles + ' files. Consider splitting it for easier review.');
|
||||
}
|
||||
|
||||
PR-forbidden-labels:
|
||||
if: ${{ inputs.cron-run == '' || inputs.cron-run == 'false' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: checkoutRepo
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: PR contains forbidden labels
|
||||
id: pr-forbidden
|
||||
- name: Check for forbidden labels
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const issueHasLabels = require('./scripts/github/update/check-issue-has-label.js');
|
||||
const checkLabels = ['next version ➡️', 'do not merge🙅🏻♂️'];
|
||||
const { data: issue } = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
});
|
||||
const labels = issue.labels?.map(item => item.name) || [];
|
||||
const forbidden = ['next version ➡️', 'do not merge🙅🏻♂️'];
|
||||
|
||||
const hasLabel = await issueHasLabels({github, context, checkLabels})
|
||||
|
||||
if(hasLabel) {
|
||||
if (forbidden.some(l => labels.includes(l))) {
|
||||
core.setFailed('The PR contains a forbidden label! You are not allowed to merge until the label is there.');
|
||||
}
|
||||
- name: Check value after
|
||||
env:
|
||||
STEP_RESULT: ${{ toJson(steps.pr-forbidden.*.result) }}
|
||||
HAS_FAILURE: ${{ contains(toJson(steps.pr-forbidden.*.result), 'failure') }}
|
||||
run: |
|
||||
echo "Step result: $STEP_RESULT"
|
||||
echo "Has failure: $HAS_FAILURE"
|
||||
|
||||
finalize:
|
||||
if: ${{ always() }}
|
||||
@@ -284,16 +315,13 @@ jobs:
|
||||
check-if-pr-is-approved,
|
||||
pre-checks,
|
||||
setup,
|
||||
unit-tests,
|
||||
trigger-unit-tests,
|
||||
lint,
|
||||
build-libs,
|
||||
trigger-build,
|
||||
build-storybook,
|
||||
PR-forbidden-labels,
|
||||
]
|
||||
steps:
|
||||
- name: Check job execution status
|
||||
if: >-
|
||||
${{
|
||||
contains(needs.*.result, 'failure')
|
||||
|| contains(needs.*.result, 'cancelled')
|
||||
}}
|
||||
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
|
||||
run: exit 1
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
name: "Unit Tests Workflow"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
base_ref:
|
||||
description: 'Base branch for affected calculation'
|
||||
required: false
|
||||
type: string
|
||||
default: 'develop'
|
||||
|
||||
jobs:
|
||||
generate-affected-matrix:
|
||||
name: "Generate affected matrix"
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
unitMatrix: ${{ steps.set-matrix.outputs.unitMatrix }}
|
||||
hasProjects: ${{ steps.set-matrix.outputs.hasProjects }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup environment
|
||||
id: setup-env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
cache-suffix: test-matrix
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.setup-env.outputs.node-modules-cache-hit != 'true' }}
|
||||
run: npm ci
|
||||
- name: Generate affected projects matrix
|
||||
id: set-matrix
|
||||
env:
|
||||
BASE_REF: ${{ inputs.base_ref }}
|
||||
run: |
|
||||
echo "Base ref is $BASE_REF"
|
||||
AFFECTED_UNIT=$(npx nx show projects --affected --target=test --base=origin/$BASE_REF --head=HEAD --select=projects --plain --exclude=cli,stories,eslint-angular)
|
||||
echo "Affected projects for UNIT: $AFFECTED_UNIT"
|
||||
|
||||
if [ -z "$AFFECTED_UNIT" ]; then
|
||||
echo "No affected projects found"
|
||||
echo "hasProjects=false" >> $GITHUB_OUTPUT
|
||||
echo "unitMatrix=[]" >> $GITHUB_OUTPUT
|
||||
else
|
||||
UNIT_MATRIX_JSON=$(echo "$AFFECTED_UNIT" | xargs -n1 | jq -R -s -c 'split("\n") | map(select(length > 0)) | map({ "project": . })')
|
||||
echo "Matrix UNIT: $UNIT_MATRIX_JSON"
|
||||
echo "hasProjects=true" >> $GITHUB_OUTPUT
|
||||
echo "unitMatrix=$UNIT_MATRIX_JSON" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Save nx cache
|
||||
if: ${{ success() }}
|
||||
uses: ./.github/actions/save-nx-cache
|
||||
with:
|
||||
cache-suffix: test-matrix
|
||||
|
||||
unit-tests:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
needs: generate-affected-matrix
|
||||
if: ${{ needs.generate-affected-matrix.outputs.hasProjects == 'true' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include: ${{ fromJson(needs.generate-affected-matrix.outputs.unitMatrix) }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup environment
|
||||
id: setup-env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
cache-suffix: test-${{ matrix.project }}
|
||||
full-setup: 'false'
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.setup-env.outputs.node-modules-cache-hit != 'true' }}
|
||||
run: npm ci
|
||||
- name: Run unit tests for ${{ matrix.project }}
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=5120"
|
||||
run: |
|
||||
xvfb-run --auto-servernum npx nx run ${{ matrix.project }}:test
|
||||
- name: Save nx cache
|
||||
if: ${{ success() }}
|
||||
uses: ./.github/actions/save-nx-cache
|
||||
with:
|
||||
cache-suffix: test-${{ matrix.project }}
|
||||
@@ -0,0 +1,62 @@
|
||||
# CI Commands and Labels
|
||||
|
||||
Reference for flags and labels that control CI behavior on pull requests.
|
||||
|
||||
## PR Title Flags
|
||||
|
||||
Include these flags anywhere in the **PR title** to modify CI behavior.
|
||||
|
||||
| Flag | Effect |
|
||||
|------|--------|
|
||||
| `[ci:force]` | Skips the PR approval check, allowing the full pipeline to run without an approved review. Useful for testing CI changes on draft PRs. |
|
||||
|
||||
Example: `[AAE-12345] Fix pagination component [ci:force]`
|
||||
|
||||
## Commit Message Flags
|
||||
|
||||
Include these flags in a **commit message** to modify how Nx calculates affected projects.
|
||||
|
||||
| Flag | Effect |
|
||||
|------|--------|
|
||||
| `[affected:*]` | Forces all Nx targets to run against all projects, ignoring the affected calculation. Equivalent to `--all`. |
|
||||
|
||||
## PR Labels
|
||||
|
||||
Labels applied to the PR that affect CI behavior.
|
||||
|
||||
| Label | Effect |
|
||||
|-------|--------|
|
||||
| `do not merge` | Blocks the PR from passing the finalize check. CI will fail until the label is removed. |
|
||||
| `next version` | Same as above - blocks the PR from merging. Used to hold PRs for the next release cycle. |
|
||||
| `hxp-upstream` | (Release only) Triggers the HxP upstream propagation workflow after a successful release. |
|
||||
|
||||
## Automatic Skip Conditions
|
||||
|
||||
The approval check is automatically skipped for:
|
||||
|
||||
- **Scheduled runs** (`schedule` event)
|
||||
- **Workflow dispatch** (`workflow_dispatch` event)
|
||||
- **Dependabot PRs** (actor is `dependabot[bot]`)
|
||||
- **Build bot PRs** (actor is `alfresco-build`)
|
||||
- **Devel flag** (when `devel: true` is passed via `workflow_call`)
|
||||
|
||||
## Path-Based Filtering
|
||||
|
||||
PRs that only modify non-code files skip the build, test, and lint jobs entirely. Files considered non-code:
|
||||
|
||||
- Markdown files (`*.md`)
|
||||
- Documentation (`docs/`)
|
||||
- `LICENSE`, `NOTICE`
|
||||
- `.editorconfig`, `.gitattributes`
|
||||
- `.github/CODEOWNERS`, `.github/dependabot.yml`
|
||||
|
||||
## PR Size Check
|
||||
|
||||
Every PR gets an automatic size classification displayed in the job summary:
|
||||
|
||||
| Size | Criteria |
|
||||
|------|----------|
|
||||
| **S** | < 200 total changes and < 10 files |
|
||||
| **M** | 200-500 total changes or 10-20 files |
|
||||
| **L** | 500-1000 total changes or 20-30 files |
|
||||
| **XL** | > 1000 total changes or > 30 files (warning annotation added) |
|
||||
@@ -7,6 +7,7 @@
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "nx:run-commands",
|
||||
"outputs": ["{workspaceRoot}/dist/libs/cli"],
|
||||
"options": {
|
||||
"command": "cd lib/cli && npm run dist"
|
||||
},
|
||||
@@ -30,6 +31,7 @@
|
||||
},
|
||||
"copyToNodeModules": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": false,
|
||||
"options": {
|
||||
"commands": [
|
||||
{
|
||||
|
||||
+11
@@ -19,6 +19,7 @@ import { Meta, StoryObj } from '@storybook/angular';
|
||||
import { CardViewBoolItemComponent } from './card-view-boolitem.component';
|
||||
import { CardViewBoolItemModel } from '../../public-api';
|
||||
import { cardViewSharedMeta } from '../../stories/card-view-shared-meta';
|
||||
import { expect, within } from 'storybook/test';
|
||||
|
||||
const meta: Meta<CardViewBoolItemComponent> = {
|
||||
...cardViewSharedMeta,
|
||||
@@ -50,5 +51,15 @@ export const CardViewBoolItem: Story = {
|
||||
default: false,
|
||||
editable: true
|
||||
})
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const label = canvas.getByText(/Agree to all terms and conditions/i);
|
||||
await expect(label).toBeVisible();
|
||||
|
||||
const toggle = canvas.getByRole('checkbox');
|
||||
await expect(toggle).toBeVisible();
|
||||
await expect(toggle).toBeChecked();
|
||||
}
|
||||
};
|
||||
|
||||
+19
@@ -19,6 +19,7 @@ import { Meta, StoryObj } from '@storybook/angular';
|
||||
import { CardViewTextItemComponent } from './card-view-textitem.component';
|
||||
import { CardViewTextItemModel } from '../../public-api';
|
||||
import { cardViewSharedMeta } from '../../stories/card-view-shared-meta';
|
||||
import { expect, within } from 'storybook/test';
|
||||
|
||||
const meta: Meta<CardViewTextItemComponent> = {
|
||||
...cardViewSharedMeta,
|
||||
@@ -50,6 +51,15 @@ export const ClickableCardViewTextItem: Story = {
|
||||
clickable: true,
|
||||
icon: 'close'
|
||||
})
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const label = canvas.getByText(/CardView Text Item - Clickable template/i);
|
||||
await expect(label).toBeVisible();
|
||||
|
||||
const value = canvas.getByText(/click here/i);
|
||||
await expect(value).toBeVisible();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -104,5 +114,14 @@ export const DefaultCardViewTextItem: Story = {
|
||||
icon: 'close',
|
||||
multiline: false
|
||||
})
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const label = canvas.getByText(/CardView Text Item - Default template/i);
|
||||
await expect(label).toBeVisible();
|
||||
|
||||
const value = canvas.getByText(/input here/i);
|
||||
await expect(value).toBeVisible();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ import { ADF_COMMENTS_SERVICE } from './interfaces/comments.token';
|
||||
import { commentsStoriesData } from './mocks/comments.stories.mock';
|
||||
import { CommentsServiceStoriesMock } from './mocks/comments.service.stories.mock';
|
||||
import { provideStoryCore } from '../stories/core-story.providers';
|
||||
import { expect, within } from 'storybook/test';
|
||||
|
||||
const meta: Meta<CommentsComponent> = {
|
||||
component: CommentsComponent,
|
||||
@@ -92,6 +93,12 @@ export const SingleCommentWithAvatar: Story = {
|
||||
args: {
|
||||
comments: [commentsStoriesData[0]],
|
||||
readOnly: true
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const commentText = canvas.getByText(/I've done this task, what's next\?/i);
|
||||
await expect(commentText).toBeVisible();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -112,11 +119,23 @@ export const NoComments: Story = {
|
||||
args: {
|
||||
comments: [],
|
||||
readOnly: true
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const commentElements = canvas.queryAllByText(/I've done this task/i);
|
||||
await expect(commentElements.length).toBe(0);
|
||||
}
|
||||
};
|
||||
|
||||
export const Comments: Story = {
|
||||
render: (args) => ({
|
||||
props: args
|
||||
})
|
||||
}),
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const commentInput = canvas.getByRole('textbox');
|
||||
await expect(commentInput).toBeVisible();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
|
||||
import { PaginationComponent } from './pagination.component';
|
||||
import { provideStoryCore } from '../stories/core-story.providers';
|
||||
import { expect, within } from 'storybook/test';
|
||||
|
||||
const meta: Meta<PaginationComponent> = {
|
||||
component: PaginationComponent,
|
||||
@@ -95,5 +96,20 @@ type Story = StoryObj<PaginationComponent>;
|
||||
export const Pagination: Story = {
|
||||
render: (args) => ({
|
||||
props: args
|
||||
})
|
||||
}),
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const paginationRange = canvas.getByText(/1-25 of 100/i);
|
||||
await expect(paginationRange).toBeVisible();
|
||||
|
||||
const pageSizeSelector = canvas.getByRole('combobox');
|
||||
await expect(pageSizeSelector).toBeVisible();
|
||||
|
||||
const nextButton = canvas.getByRole('button', { name: /next/i });
|
||||
await expect(nextButton).toBeVisible();
|
||||
|
||||
const previousButton = canvas.getByRole('button', { name: /previous/i });
|
||||
await expect(previousButton).toBeVisible();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ import { ToolbarComponent } from './toolbar.component';
|
||||
import { ToolbarDividerComponent } from './toolbar-divider.component';
|
||||
import { ToolbarTitleComponent } from './toolbar-title.component';
|
||||
import { provideStoryCore } from '../stories/core-story.providers';
|
||||
import { expect, within } from 'storybook/test';
|
||||
|
||||
type ToolbarStoryArgs = ToolbarComponent & {
|
||||
toolbarTitle?: boolean;
|
||||
@@ -118,5 +119,11 @@ export const Toolbar: Story = {
|
||||
</ng-container>
|
||||
<ng-container *ngIf="${args.toolbarDivider}">left<adf-toolbar-divider></adf-toolbar-divider>right</ng-container>
|
||||
</adf-toolbar>`
|
||||
})
|
||||
}),
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const toolbar = canvas.getByRole('toolbar');
|
||||
await expect(toolbar).toBeVisible();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
},
|
||||
"copyToNodeModules": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": false,
|
||||
"options": {
|
||||
"commands": [
|
||||
{
|
||||
|
||||
@@ -14,14 +14,35 @@
|
||||
},
|
||||
"stylelint": {
|
||||
"cache": true
|
||||
}
|
||||
},
|
||||
"tasksRunnerOptions": {
|
||||
"default": {
|
||||
"runner": "nx/tasks-runners/default",
|
||||
"options": {
|
||||
"runtimeCacheInputs": ["node -v"]
|
||||
}
|
||||
},
|
||||
"build-storybook": {
|
||||
"cache": true,
|
||||
"inputs": ["default", "^production"],
|
||||
"outputs": ["{options.outputDir}"]
|
||||
},
|
||||
"build-cjs": {
|
||||
"cache": true,
|
||||
"inputs": ["production", "^production"]
|
||||
},
|
||||
"build-esm2015": {
|
||||
"cache": true,
|
||||
"inputs": ["production", "^production"]
|
||||
},
|
||||
"build-esm5": {
|
||||
"cache": true,
|
||||
"inputs": ["production", "^production"]
|
||||
},
|
||||
"build-types": {
|
||||
"cache": true,
|
||||
"inputs": ["production", "^production"]
|
||||
},
|
||||
"build-schematics": {
|
||||
"cache": true,
|
||||
"inputs": ["production", "^production"]
|
||||
},
|
||||
"license": {
|
||||
"cache": true,
|
||||
"outputs": ["{workspaceRoot}/licenses.txt"]
|
||||
}
|
||||
},
|
||||
"namedInputs": {
|
||||
|
||||
Reference in New Issue
Block a user