Merge pull request #43 from getlarge/repo-sync/ticketing-exercises/main #147
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: 'ticketing CI' | |
on: | |
push: | |
branches: | |
- main | |
paths-ignore: | |
- README.md | |
- docs | |
- '**/CHANGELOG.md' | |
# auto-generated | |
- 'apps/**/openapi.json' | |
- 'apps/**/package*.json' | |
- 'libs/ng/shared/data-access/src/lib/generated/**' | |
- CHALLENGES.md | |
- exercises | |
- assets | |
pull_request: | |
branches: | |
- main | |
types: | |
- ready_for_review | |
- opened | |
- reopened | |
- synchronize | |
paths-ignore: | |
- README.md | |
- docs | |
- '**/CHANGELOG.md' | |
# auto-generated | |
- 'apps/**/openapi.json' | |
- 'apps/**/package*.json' | |
- CHALLENGES.md | |
- exercises | |
- assets | |
release: | |
types: | |
- published | |
- edited | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.ref }}-ci | |
cancel-in-progress: true | |
env: | |
NODE_VERSION: 20.x | |
CI_WORKFLOW: ci.yaml | |
CD_WORKFLOW: cd.yaml | |
BUILD_FOLDER: dist | |
BUILD_ARTIFACTS: build | |
COVERAGE_FOLDER: coverage | |
COVERAGE_ARTIFACTS: coverage | |
COVERAGE_APPS_UNIT_TESTS_ARTIFACTS: libs-unit-coverage | |
COVERAGE_APPS_E2E_TESTS_ARTIFACTS: apps-e2e-coverage | |
COVERAGE_LIBS_UNIT_TESTS_ARTIFACTS: apps-unit-coverage | |
COVERAGE_APPS_UNIT_TESTS_FOLDER: coverage/apps/unit | |
COVERAGE_APPS_E2E_TESTS_FOLDER: coverage/apps/e2e | |
COVERAGE_LIBS_UNIT_TESTS_FOLDER: coverage/libs | |
NX_CLOUD_DISTRIBUTED_EXECUTION: true | |
NX_DISTRIBUTED_TASK_EXECUTION: true | |
NX_VERBOSE_LOGGING: true | |
NX_CLOUD_DISTRIBUTED_EXECUTION_STOP_AGENTS_ON_FAILURE: false | |
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} | |
NX_MAX_AGENTS: ${{ vars.NX_MAX_AGENTS || 8 }} | |
STEP_SET_FETCH_REF: 'Set fetch-ref' | |
STEP_SETUP_PROJECT: 'Setup node, checkout and install project dependencies' | |
# test jobs env. variables | |
NODE_ENV: ${{ vars.NODE_ENV || 'local' }} | |
LOG_LEVEL: ${{ vars.LOG_LEVEL || 'warn' }} | |
GLOBAL_API_PREFIX: api | |
JWT_ISSUER: ${{ secrets.JWT_ISSUER }} | |
JWT_ALGORITHM: ${{ secrets.JWT_ALGORITHM }} | |
JWT_EXPIRES_IN: ${{ secrets.JWT_EXPIRES_IN }} | |
JWT_PUBLIC_KEY: ${{ secrets.JWT_PUBLIC_KEY }} | |
JWT_PRIVATE_KEY: ${{ secrets.JWT_PRIVATE_KEY }} | |
SESSION_KEY: ${{ secrets.SESSION_KEY }} | |
STRIPE_PUBLISHABLE_KEY: ${{ secrets.STRIPE_PUBLISHABLE_KEY }} | |
STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }} | |
STRIPE_ENDPOINT_SECRET: ${{ secrets.STRIPE_ENDPOINT_SECRET }} | |
PROXY_SERVER_URLS: http://localhost | |
FRONTEND_URL: http://localhost | |
AUTH_SERVICE_PORT: 3000 | |
EXPIRATION_SERVICE_PORT: 3030 | |
ORDERS_SERVICE_PORT: 3020 | |
PAYMENTS_SERVICE_PORT: 3040 | |
TICKETS_SERVICE_PORT: 3010 | |
FRONTEND_PORT: 4200 | |
ORY_BASE_PATH: ${{ secrets.ORY_BASE_PATH }} | |
ORY_API_KEY: ${{ secrets.ORY_API_KEY }} | |
ORY_ACTION_API_KEY: ${{ secrets.ORY_ACTION_API_KEY }} | |
RMQ_URL: http://localhost:5672 | |
MONGODB_URI: mongodb://localhost:27017 | |
REDIS_URL: redis://localhost:6379 | |
jobs: | |
# INIT | |
init: | |
runs-on: ubuntu-latest | |
if: github.event_name == 'release' || github.event_name == 'push' || !github.event.pull_request.draft | |
timeout-minutes: 10 | |
steps: | |
- name: ${{ env.STEP_SET_FETCH_REF }} | |
id: fetch-ref | |
uses: haya14busa/action-cond@v1 | |
with: | |
cond: ${{ github.ref == 'refs/head/main' }} | |
if_true: ${{ github.ref }} | |
if_false: ${{ github.event.pull_request.head.ref }} | |
# Needed as long as we use local actions | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ steps.fetch-ref.outputs.value }} | |
# init dependencies cache so that next jobs can start faster | |
- name: ${{ env.STEP_SETUP_PROJECT }} | |
id: setup | |
uses: ./.github/actions/checkout-and-yarn | |
with: | |
fetch-depth: 0 | |
fetch-ref: ${{ steps.fetch-ref.outputs.value }} | |
node-version: ${{ env.NODE_VERSION }} | |
token: ${{ secrets.GITHUB_TOKEN }} | |
- name: Get branch names | |
id: branch-name | |
uses: tj-actions/branch-names@v7 | |
- name: Get current version | |
id: package-version | |
uses: martinbeentjes/[email protected] | |
- name: Derive appropriate SHAs for base and head for `nx affected` commands | |
id: set-shas | |
uses: | |
nrwl/nx-set-shas@v4 | |
# get commits count and increment by 1 for safety | |
- name: Find depth of NX_BASE | |
id: commit-depth | |
run: | | |
depth=$(git rev-list HEAD ^${{ steps.set-shas.outputs.base }} --count) | |
depth=$((depth + 1)) | |
echo "BASE_DEPTH=$depth" >> $GITHUB_ENV | |
echo "depth=$depth" >> $GITHUB_OUTPUT | |
# set value to current version on release event or to an autogenerated tag | |
- name: Set tag | |
id: tag | |
run: | | |
if [[ "${{ github.event_name }}" == "release" ]]; then | |
echo "value=$(echo v${{ steps.package-version.outputs.current-version }})" >> $GITHUB_OUTPUT | |
fi | |
- name: List affected apps since previous workflow run | |
id: check-apps | |
run: echo "affected=$(yarn affected:apps --base=${{ steps.set-shas.outputs.base }} --plain)" >> $GITHUB_OUTPUT | |
- name: List affected libs since previous workflow run | |
id: check-libs | |
run: echo "affected=$(yarn affected:libs --base=${{ steps.set-shas.outputs.base }} --plain)" >> $GITHUB_OUTPUT | |
# check if any app is affected between this commit and the previous successful workflow run commit | |
- name: Check if any app was affected since previous workflow run | |
id: has-apps-affected | |
run: echo "value=$([[ -z '${{ steps.check-apps.outputs.affected }}' ]] && echo 'false' || echo 'true')" >> $GITHUB_OUTPUT | |
# check if any lib is affected between this commit and the previous successful workflow run commit | |
- name: Check if any lib was affected since previous workflow run | |
id: has-libs-affected | |
run: echo "value=$([[ -z '${{ steps.check-libs.outputs.affected }}' ]] && echo 'false' || echo 'true')" >> $GITHUB_OUTPUT | |
# check if any project is affected between this commit and the previous successful workflow run commit | |
- name: Check if any project was affected | |
id: has-projects-affected | |
run: | | |
if [[ "${{ steps.check-apps.outputs.affected }}" == "" && "${{ steps.check-libs.outputs.affected }}" == "" ]]; then | |
echo "value=false" >> $GITHUB_OUTPUT | |
else | |
echo "value=true" >> $GITHUB_OUTPUT | |
fi | |
# Run comparisons between latest successful workflow commit and current commit | |
- name: Check if OpenAPI specs have changed | |
id: open-api | |
run: echo "has-changed=$(yarn git:check-diff --silent -- -b ${{ steps.set-shas.outputs.base }} -p 'apps/**/openapi.json')" >> $GITHUB_OUTPUT | |
- name: Check if package.json has changed | |
id: package-json | |
run: echo "has-changed=$(yarn git:check-diff --silent -- -b ${{ steps.set-shas.outputs.base }} -p 'package.json')" >> $GITHUB_OUTPUT | |
- name: Check if regenerate job should be run | |
id: regenerate | |
run: | | |
if [[ "${{ steps.branch-name.outputs.current_branch }}" != 'main' ]] && \ | |
[[ ${{ steps.open-api.outputs.has-changed }} =~ "true" ]] || \ | |
[[ ${{ steps.package-json.outputs.has-changed }} =~ "true" ]]; then | |
echo "should-run=true" >> $GITHUB_OUTPUT | |
else | |
echo "should-run=false" >> $GITHUB_OUTPUT | |
fi | |
# dynamically set number of Nx agents to start | |
- name: Count number of Nx Cloud agents required | |
id: nx-cloud-agents | |
env: | |
AFFECTED_APPS: ${{ steps.check-apps.outputs.affected || '' }} | |
MAX_AGENTS: ${{ env.NX_MAX_AGENTS }} | |
run: | | |
if [[ "${{ github.event_name }}" == "release" ]]; then | |
count=$(node --eval " | |
const maxAgents = +process.env.MAX_AGENTS; | |
const count = Math.round(maxAgents); | |
console.log(count);" | |
) | |
else | |
count=$(yarn get:apps | node --eval " | |
const apps = require('fs').readFileSync(0).toString().trim().split(','); | |
const affectedApps = process.env.AFFECTED_APPS.split(',').map(e => e.trim()) || [] | |
const maxAgents = +process.env.MAX_AGENTS; | |
const count = Math.round(maxAgents * (affectedApps.length / apps.length)) || 1; | |
console.log(count);" | |
) | |
fi | |
echo "count=$count" >> $GITHUB_OUTPUT | |
echo "matrix=$(echo $count | node --eval " | |
const length = parseInt(require('fs').readFileSync(0).toString().trim()); | |
const result = JSON.stringify([...Array(length)].map((e, i) => i + 1)); | |
console.log(result);" | |
)" >> $GITHUB_OUTPUT | |
- name: Show outputs | |
run: | | |
echo "event name: ${{ github.event_name }}" | |
echo "affected apps: ${{ steps.check-apps.outputs.affected }}" | |
echo "affected libs: ${{ steps.check-libs.outputs.affected }}" | |
echo "current-branch: ${{ steps.branch-name.outputs.current_branch }}" | |
echo "should-update-models: ${{ steps.open-api.outputs.has-changed }}" | |
echo "should-update-packages: ${{ steps.package-json.outputs.has-changed }}" | |
echo "should-run-regenerate: ${{ steps.regenerate.outputs.should-run }}" | |
- name: Create job summary | |
run: | | |
echo "## CI initialized! :rocket: " >> $GITHUB_STEP_SUMMARY | |
echo "- base commit SHA: ${{ steps.set-shas.outputs.base }}" >> $GITHUB_STEP_SUMMARY | |
echo "- head commit SHA: ${{ steps.set-shas.outputs.head }}" >> $GITHUB_STEP_SUMMARY | |
echo "- base commit depth: ${{ steps.commit-depth.outputs.depth }}" >> $GITHUB_STEP_SUMMARY | |
echo "- current branch: ${{ steps.branch-name.outputs.current_branch }}" >> $GITHUB_STEP_SUMMARY | |
echo "- current version: ${{ steps.package-version.outputs.current-version }}" >> $GITHUB_STEP_SUMMARY | |
echo "- tag: ${{ steps.tag.outputs.value }}" >> $GITHUB_STEP_SUMMARY | |
echo "- affected apps: ${{ steps.check-apps.outputs.affected }}" >> $GITHUB_STEP_SUMMARY | |
echo "- affected libs: ${{ steps.check-libs.outputs.affected }}" >> $GITHUB_STEP_SUMMARY | |
echo "- Nx agents count: ${{ steps.nx-cloud-agents.outputs.count }}" >> $GITHUB_STEP_SUMMARY | |
outputs: | |
fetch-ref: ${{ steps.fetch-ref.outputs.value }} | |
base: ${{ steps.set-shas.outputs.base }} | |
head: ${{ steps.set-shas.outputs.head }} | |
base-depth: ${{ steps.commit-depth.outputs.depth}} | |
current-branch: ${{ steps.branch-name.outputs.current_branch }} | |
should-update-models: ${{ steps.open-api.outputs.has-changed }} | |
should-update-packages: ${{ steps.package-json.outputs.has-changed }} | |
should-run-regenerate: ${{ steps.regenerate.outputs.should-run }} | |
deps-cache-hit: ${{ steps.setup.outputs.cache-hit }} | |
affected-apps: ${{ steps.check-apps.outputs.affected }} | |
affected-libs: ${{ steps.check-libs.outputs.affected }} | |
projects-affected: ${{ steps.has-projects-affected.outputs.value }} | |
agents-count: ${{ steps.nx-cloud-agents.outputs.count }} | |
agents-matrix: ${{ steps.nx-cloud-agents.outputs.matrix }} | |
job-status: ${{ job.status }} | |
# REGENERATE | |
regenerate: | |
needs: init | |
runs-on: ubuntu-latest | |
if: | | |
needs.init.outputs.should-run-regenerate == 'true' | |
&& needs.init.outputs.current-branch != 'main' | |
&& github.event_name != 'release' | |
&& (github.event_name == 'push' || !github.event.pull_request.draft) | |
timeout-minutes: 5 | |
env: | |
NX_BASE: ${{ needs.init.outputs.base }} | |
NX_HEAD: ${{ needs.init.outputs.head }} | |
BASE_DEPTH: ${{ needs.init.outputs.base-depth }} | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
ref: ${{ needs.init.outputs.fetch-ref }} | |
- name: ${{ env.STEP_SETUP_PROJECT }} | |
id: setup | |
uses: ./.github/actions/checkout-and-yarn | |
with: | |
fetch-depth: 0 | |
fetch-ref: ${{ needs.init.outputs.fetch-ref }} | |
node-version: ${{ env.NODE_VERSION }} | |
token: ${{ secrets.GITHUB_TOKEN }} | |
- name: Regenerate frontend models | |
if: needs.init.outputs.should-update-models == 'true' | |
run: yarn ng:openapi | |
- name: Run prettier | |
if: needs.init.outputs.should-update-models == 'true' | |
run: npx nx format:write --projects=shared-ng-open-api | |
- name: Add, commit and push generated models | |
if: needs.init.outputs.should-update-models == 'true' | |
id: commit-ng-models | |
uses: stefanzweifel/git-auto-commit-action@v5 | |
with: | |
commit_message: 'chore: regenerate frontend models [skip ci]' | |
file_pattern: 'libs/ng/shared/open-api/src/lib/generated/*' | |
commit_options: '--no-verify' | |
- if: steps.commit-ng-models.outputs.changes_detected == 'true' | |
run: echo "BASE_DEPTH=$((${{ env.BASE_DEPTH }} + 1))" >> $GITHUB_ENV | |
- name: Cancel workflow on failure | |
if: failure() | |
uses: andymckay/[email protected] | |
outputs: | |
base-depth: ${{ env.BASE_DEPTH }} | |
job-status: ${{ job.status }} | |
# AGENTS | |
agents: | |
runs-on: ubuntu-latest | |
needs: [init, regenerate] | |
if: | | |
!failure() && !cancelled() && | |
needs.init.outputs.projects-affected == 'true' && | |
(github.event_name == 'release' || github.event_name == 'push' || !github.event.pull_request.draft) | |
name: Agent | |
timeout-minutes: 15 | |
env: | |
NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT: ${{ needs.init.outputs.agents-count }} | |
services: | |
mongo: | |
image: mongo | |
ports: | |
- 27017:27017 | |
strategy: | |
matrix: | |
# number of agents proportional to number of affected projects | |
agent: ${{ fromJSON(needs.init.outputs.agents-matrix) }} | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ needs.init.outputs.fetch-ref }} | |
- name: ${{ env.STEP_SETUP_PROJECT }} | |
id: setup | |
uses: ./.github/actions/checkout-and-yarn | |
with: | |
fetch-ref: ${{ needs.init.outputs.fetch-ref }} | |
node-version: ${{ env.NODE_VERSION }} | |
token: ${{ secrets.GITHUB_TOKEN }} | |
- name: Start Nx Agent ${{ matrix.agent }} | |
run: npx nx-cloud start-agent | |
main: | |
runs-on: ubuntu-latest | |
needs: [init, regenerate] | |
if: | | |
!failure() && !cancelled() && | |
needs.init.outputs.projects-affected == 'true' && | |
(github.event_name == 'release' || github.event_name == 'push' || !github.event.pull_request.draft) | |
services: | |
rabbitmq: | |
image: ghcr.io/getlarge/ticketing/rabbitmq:latest | |
credentials: | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
env: | |
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS: | |
'-rabbit auth_backends [rabbit_auth_backend_internal,rabbit_auth_backend_cache] | |
-rabbitmq_auth_backend_cache cached_backend rabbit_auth_backend_http | |
-rabbitmq_auth_backend_cache cache_ttl 5000 | |
-rabbitmq_auth_backend_http http_method post | |
-rabbitmq_auth_backend_http user_path "http://172.17.0.1:3000/user" | |
-rabbitmq_auth_backend_http vhost_path "http://172.17.0.1:3000/vhost" | |
-rabbitmq_auth_backend_http resource_path "http://172.17.0.1:3000/resource" | |
-rabbitmq_auth_backend_http topic_path "http://172.17.0.1:3000/topic"' | |
options: >- | |
--health-cmd "rabbitmq-diagnostics status" | |
--health-interval 10s | |
--health-timeout 5s | |
--health-retries 5 | |
ports: | |
- 5672:5672 | |
- 15672:15672 | |
auth-mongo: | |
image: mongo | |
ports: | |
- 27017:27017 | |
orders-mongo: | |
image: mongo | |
ports: | |
- 27018:27017 | |
tickets-mongo: | |
image: mongo | |
ports: | |
- 27019:27017 | |
payments-mongo: | |
image: mongo | |
ports: | |
- 27020:27017 | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ needs.init.outputs.fetch-ref }} | |
- name: Compute Git fetch depth | |
id: fetch-depth | |
run: echo "value=${{ needs.regenerate.outputs.base-depth || needs.init.outputs.base-depth }}" >> $GITHUB_OUTPUT | |
- name: ${{ env.STEP_SETUP_PROJECT }} | |
id: setup | |
uses: ./.github/actions/checkout-and-yarn | |
with: | |
# fetch-depth: ${{ steps.fetch-depth.outputs.value }} | |
fetch-depth: 0 | |
fetch-ref: ${{ needs.init.outputs.fetch-ref }} | |
node-version: ${{ env.NODE_VERSION }} | |
token: ${{ secrets.GITHUB_TOKEN }} | |
- name: Derive appropriate SHAs for base and head for `nx affected` commands | |
uses: nrwl/nx-set-shas@v4 | |
- run: npx nx-cloud start-ci-run | |
- name: Get Nx apps to build | |
id: build-apps | |
run: | | |
if [[ "${{ github.event_name }}" == "release" ]]; then | |
echo "list=$(yarn get:apps)" >> $GITHUB_OUTPUT | |
else | |
echo "list=$(yarn affected:apps | tr -d ' ')" >> $GITHUB_OUTPUT | |
fi | |
- name: Run verifications for affected apps | |
uses: jameshenry/parallel-bash-commands@v1 | |
with: | |
cmd1: yarn affected:lint --parallel=4 --exclude=workspace --verbose | |
cmd2: yarn affected:test --parallel=4 --exclude=workspace --ci --verbose | |
cmd3: npx nx run-many --target=build --parallel=4 --projects=${{ steps.build-apps.outputs.list }} --verbose | |
# running e2e tests in parallel create conflicts in DB | |
# cmd4: NX_CLOUD_DISTRIBUTED_EXECUTION=false yarn affected:e2e:backend --ci --verbose | |
- name: Stop Nx agents | |
if: always() | |
run: npx nx-cloud stop-all-agents | |
# - name: Run Code PushUp | |
# run: npx code-pushup autorun --upload.apiKey=${{ secrets.CODE_PUSHUP_API_KEY }} | |
# - name: Upload Code PushUp report | |
# uses: actions/upload-artifact@v4 | |
# with: | |
# name: code-pushup-report | |
# path: .code-pushup | |
# retention-days: 5 | |
- name: Upload build output | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ env.BUILD_ARTIFACTS }} | |
path: ${{ env.BUILD_FOLDER }} | |
retention-days: 5 | |
- name: Check libs coverage reports existence | |
id: check-libs-coverage | |
uses: andstor/file-existence-action@v3 | |
with: | |
files: ${{ env.COVERAGE_LIBS_UNIT_TESTS_FOLDER }} | |
- name: Upload libs unit tests coverage reports | |
if: steps.check-libs-coverage.outputs.files_exists == 'true' | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ env.COVERAGE_LIBS_UNIT_TESTS_ARTIFACTS }} | |
path: ${{ env.COVERAGE_LIBS_UNIT_TESTS_FOLDER }} | |
retention-days: 1 | |
- name: Check apps coverage reports existence | |
id: check-apps-coverage | |
uses: andstor/file-existence-action@v3 | |
with: | |
files: ${{ env.COVERAGE_APPS_UNIT_TESTS_FOLDER }} | |
- name: Upload apps unit tests coverage reports | |
if: steps.check-apps-coverage.outputs.files_exists == 'true' | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ env.COVERAGE_APPS_UNIT_TESTS_ARTIFACTS }} | |
path: ${{ env.COVERAGE_APPS_UNIT_TESTS_FOLDER }} | |
retention-days: 1 | |
- name: Check coverage reports existence | |
uses: andstor/file-existence-action@v3 | |
id: check-coverage | |
with: | |
files: ${{ env.COVERAGE_APPS_E2E_TESTS_FOLDER }} | |
- name: Upload apps e2e tests coverage reports | |
if: ${{ steps.check-coverage.outputs.files_exists == 'true' }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ env.COVERAGE_APPS_E2E_TESTS_ARTIFACTS }} | |
path: ${{ env.COVERAGE_APPS_E2E_TESTS_FOLDER }} | |
retention-days: 1 | |
- name: Cancel workflow on failure | |
if: failure() | |
uses: andymckay/[email protected] | |
outputs: | |
has-apps-coverage: ${{ steps.check-apps-coverage.outputs.files_exists }} | |
has-libs-coverage: ${{ steps.check-libs-coverage.outputs.files_exists }} | |
fetch-ref: ${{ needs.init.outputs.fetch-ref }} |