Compare commits
20
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c86a13559 | ||
|
|
0c2474ba20 | ||
|
|
b4387b0616
|
||
|
|
7f0c50801d
|
||
|
|
3893281fa1 | ||
|
|
22a9351de7
|
||
|
|
257c3edd85
|
||
|
|
a45659b3cc
|
||
|
|
86d353ec2d
|
||
|
|
71f06a3d68
|
||
|
|
8ce34c75f3 | ||
|
|
688c3a4b97
|
||
|
|
cff22c88dd | ||
|
|
47c70471e1 | ||
|
|
24f3311006 | ||
|
|
9a175925b6 | ||
|
|
716cc5f6f1 | ||
|
|
b3638f4d63 | ||
|
|
c6487dc258 | ||
|
|
cc7f09fa22 |
No files matched your search
@@ -2,12 +2,21 @@
|
||||
#/ public-key encryption for .env files /
|
||||
#/ [how it works](https://dotenvx.com/encryption) /
|
||||
#/----------------------------------------------------------/
|
||||
DOTENV_PUBLIC_KEY_DEV="02a9d0f6522086b04ac7763e008a6ad0273c58258715e613c977fd6c82a2030d92"
|
||||
|
||||
# .env.dev
|
||||
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
|
||||
#/ public-key encryption for .env files /
|
||||
#/ [how it works](https://dotenvx.com/encryption) /
|
||||
#/----------------------------------------------------------/
|
||||
DOTENV_PUBLIC_KEY="02a9d0f6522086b04ac7763e008a6ad0273c58258715e613c977fd6c82a2030d92"
|
||||
|
||||
# .env
|
||||
COM_ERMNVLDMR_STATIC_S3_BUCKET="encrypted:BL1vJLB9uPSvsUvrJah4UegRU8uPdFjE7sya+Fae3rwOmVzWsK5+TP4z4YzigTAuwit4FANvANvhRV46se+ZgprYplU8Cw1tZ5mHKO4rWAjif2sJCnsGXUKp8GZ2J7PrMH35MY0YrXX4NVqe18b2NEv2B9K7"
|
||||
COM_ERMNVLDMR_STATIC_S3_REGION="encrypted:BAj3iuo7tWWNR2V4yQ+LHnFCB4JcZp7TTfUtvv+2FPntNhwc/fV95IeFWz6S0z6aPRkDG+UQzPXNwdMz7y8ZbTHbduDDrgydHu1fST1NfQd2s1Bbpf+yJTG7dPBSc5RSNKZkkpB4v55qg/UC"
|
||||
COM_ERMNVLDMR_STATIC_S3_ENDPOINT_URL="encrypted:BNkQrgsrxtz544vuJbgwWUh5cPjHG5wbmHNcxTUWrzbFJJsdncaAitviCvIrS7Q41w+FiWkssv3D05Tu8Inn2P62E6G3PU4gNxO2oLdmGEdrzUqB/xvxOvLvos42j54QOZf7lOqMPKH6SL2/rheCAo10gSCicCqkfh4xsCAbYpyL"
|
||||
COM_ERMNVLDMR_STATIC_AWS_ACCESS_KEY_ID="encrypted:BCsp8p4bp3cB+9grxHo634isRUaFGU3MOc1lr8ClSFRYLjqjqukyrbJCTZWV2Xdo8iQ33EylmbzUltbwzTWMQcjJSvIskesVLyV93q60o/Mu9L9jg/apwHuBgHv4zVuHstJ0zkHWgapbNTIlWD3RCm0ujeX/H1gBH3U="
|
||||
COM_ERMNVLDMR_STATIC_AWS_SECRET_ACCESS_KEY="encrypted:BM7aPliOyiPrqFPZMfqpn534Nu+Y0Nbh/YRoxv2KY6XAi2dNO0LSEf97DqSUQYdsnu9iibZmVNktF2K/D6mtHr9+gumF6KsmaLH65KE7c9ly7NdFQHL9I26wxRq1Sr3Zgrfs954J6oURSlBRKqJFdcTsBltuuupGuqJHyLS9+DE0jD+bl0Qg0lA="
|
||||
COM_ERMNVLDMR_STATIC_S3_BUCKET="encrypted:BE65d5I9m1jrHGVNKDmfGL3Cjb7LjhB4tVWyNzSPU1Zu5Y00+R8NXgqSBrzBnXVj3vVinibjihVt3fStegyiqI52GtSycX1NlFSVJlRFWadUJ6SVK+SYfB7sFKSJ2jKGfwRaXKMzaKCdPWY+ni685oPXU9Rp"
|
||||
COM_ERMNVLDMR_STATIC_S3_REGION="encrypted:BPQ2+ms76A+PLFUeKL1Cnz8bdOcsJou03e8AyGwzdhozpcAwhP5yWXSqhTK8ZccAvmfjJcM4NK+x2a2r4g0ALd/UaQDa9xy2eZ6QnXwkoHXu2ce+F86/HuFnzX0qWqYTWi4q+N4="
|
||||
COM_ERMNVLDMR_STATIC_S3_ENDPOINT_URL="encrypted:BCcK2ih1QrABMImpUj31KIQYlbp9zhnafzrXINAwvyCaCjbOst6UGnhhi5M3LtQ+Fhgpx52ZKGjmke1oVxnvU6BBGz/lAbSD5euhHNbJ6EIoSqc25XpcmgucIQGx1cfiJxsL4sDR95FyR3fNaS9KGr005DwQaIRKGg86gALUCPhaLekq/Sd6Nn+TDoqmigLJPozdxp33AFpmWF+EwayYae2u"
|
||||
COM_ERMNVLDMR_STATIC_AWS_ACCESS_KEY_ID="encrypted:BPaBvzNo1eH3Z2ro+NFq4zLi4krZQT48h20r0etBfVFLDNS01gh5Uosy70Ip29rC8sa1JoP4Q8isJ2Xz0pr5Vxxa62vVFOJ9wJ6LkXbYF2bUm+UNyjfsIztJsMyiVZfPEEPEEKcniXZ3faqePwgLwCZSTa/Qry6znNmMV5FshSsP"
|
||||
COM_ERMNVLDMR_STATIC_AWS_SECRET_ACCESS_KEY="encrypted:BFiOxOo/TNvSIMOjyeOswLcl+9AVdqp4axW+I46c1CL0ckSucQS+7CLpO8fNfakmW2bSibqru+xfWUbQT7FDH1YB3CPXUc0qPm1PsG0FiLixDefQuDrC/JHuA0Z3/dOAkn/e94BBuPtyJXxcLRP1v517MijPa0MuNqhI9BS3jFHKjWivqc9AVPs2ybglo14b/nEO1GqlM0TYflPoDhHoWjM="
|
||||
SOPS_AGE_KEY="encrypted:BKiJVxKYhiadu0kNCw8wVLYdaEWZreAVu2PaujxhvcXeCowCGjivEuQD/uhduAy/CwwDWyQkgQJ2FJMEKA47e7QDjS9TMdPCXO19/teR7LThgqTf6AbSM3sebln5zalgyplay4A/7cVytPXuOvVkaYyoh9CgzTVsPJKXRq5yMaDEx/9/91Yo8zUMF3z1vq3/preI5baK4/1xXewR3OwfZqnNGyd7LXkdqyT5"
|
||||
COM_ERMNVLDMR_DEV_ROOT_CA_KEY="encrypted:BP+GhCwutIMF2hSxIs92VNSoEAvfdhYRFSi5MUpFfCs+pNxJkZ7tFOAadMqSlPiLBcm9+A3yIn/04/oEtRNjXk/jlZQ2G21mt30sd89NARjJO+94ui6XbllW65E5/T+d8fDmRhz4c4OcNoKk70aXcdJqd1CJT7IlYQn++zQvB2el9cp9e7E22rvGtvum40iISEt/pVU84uHyQg6ZeCn+1czM+nLhPiWOAgveTz3EAgExlJW2de1ilDIuBCFXJy5+KvnCj3WNwRTAbIYgCwFnZMOUE5jYlhP6OZ2DQ3sWv7noG/Xaa/P+EomP9dwcamlAU32tRruyA8ZSp/ah9E8hFfmBqgKfSbRlWq3qGkq/F935DZfU/C+PjRcepS/eLZQ0mfD23B21VX2CjOhEkp5V+9I5L8XkXDjDg56JDRGvF/umpdqF/KpmtimqcgB0yDVY1Z3yT1U5IZ6Yf4WlqzhchdJXRWfo0bqK8Tk0b442/6AQgRP3n6DIrCjcpmr4jx+RJiejOr3xv3QtHGafDdkF2gO7DrYztdd6XpRjij0orBUiyYECgPsLHuwd+/2jXnxLVTavVTQ0Ddz0LLNjaYxqr8KK/B+0MeS/iuLXmACs8XCKwQmV8hPYQ0C/JXGiL4GOH0V70r6ghBxCDDMpJjPasCbiCBxSAMLkQ7ZmLbO0WVPPUTMAsZ8aKUy2MLe466zizXN640B4H6bkI7x/KC29sroEJWpUufD7f+t2tnVx/KLeAxAC6afuUCGi4ibFiwLHZjfimYKaYpuMcSZHQ64iONixyfUDIRNIOWQSiX1V5EBJgBDE0JVDnExmQYAIG5isYQEorC/7z6IJeZHc4NuiKDWuvd+jwwj8tN8PVAcACJKpQHpyUU20kvp4dYgBEeiQZpBfXWrnmOJZ0HgDHbvVxPW+SHTL/uoi/Tl9xdKe+z3Q/MoT8iYXggMPcFQIqVemQUTw9Tu0oNsSj3xueR9awJc2Le9aYbKTg54MAtcBnfKXN+3cYQkNVbqC5RfiAe2D3gfbT1OgApTUNz8n8v1dgAzTa+EB0i+w8nTsb3N/N4yAXMNSKdGeUl2rFRuQO7qlWorKc3pE+nBC2ex0sOMEUFYUXcyULaxsKM3ddakCAv9/jesnkrZl2gX7YDp8iqiFzh7l8jMAFHtFCMp3CSXlBAOpfQlGLY80qTSi1e8GrnV9Ow9t0DdS1wP7J6ES9DiNuEep5atHD+0MC6u0Dw0GtmsNnNRp77my/KI8ILhF4VBVBFQ5BiJDfdpAA3RNlANo2gK5ZCKPw4tXCYUknjYdwkqvUaIQCJcr2n+WkE2eCUCqjXTFr/KMvRByY6Fb1vAwcGoCWjP2g0Tliz3hcyh/72+umn05NO3cJSAfDEjfcYBUQZ/LCcy86G0EsHXP3NabgeHNXf/Z/VG7zWqQAPX0KaVrF9XC6I2FLkg1aNGWZfJGv4uohdIny7rWU7FWPaxy9GMk6x7xBh/Hk/5/rrGHCNGs+SegLglDs2+v4P56316k4kg//dmVl/vh3h0KC2P/1xiJZ10I91k7HqZLb2qdXToa4yOHB5Euu49UV/Qtv+cZU03fuT81sEv3YT819OdmwUZgkq0PT/Lrck+pbNDaUOakpgkGAbXy6SCvjM87jYN9Ia7druulWWO0pPi4T8daQ26ZQmZp57gdkpuh/He4kC5m1aWpT2s5STSbapChd9tFNjPhjCG1DZb7h0X8jg1VkxqutDDu17jOXrhElT1ejvYqYy9lIG1b1r7Jmjxo7LgGQuPMX6CIo/BLTsuNJyC5SfSCxEm56LvJgoNk2Vaeq6mLDOaKm2PNwPuYPnJw9xWBM0EetJBnsYMutPDMXhrB7x/8DKy5/hCsKICTfhlsboUHmbSfsikvGPYx7YOaOo8xFh4yAQ73T8hFX7kn2DBfRzGkJm8SQJRPj0Hjsf5mEFUt/OrieK/Jw10HKD6hDoEFmUDv5svC3nUY0FX0fTyi0jRAri+burrCQ0HXk/qj4Xs+WLiJgfQbprV2TwTIlzOdLb/jD8PeiElNzBxmOonRj76QKcbkOnPkCWCMFKapOJskxe0ASJVjYt142D0ndTa6F7oFu72ue5U+hyoToKeb7ONZilhF0+yL7akNfFKURUa/X+cEoewuSM7r8whWwZyG+M2cwGUTRMJiAC9wK7yE8MqdrEuQtILnCA0yRTPOh1Z9ifUY0asL1Tw9cbdGRW8svHH/oVHkWe3P+yepTNrn7xQlX/O1/p4FXRm0gSE3Fdnbli8VZdwVn2SET3WkPH9rJT2F7/hovfhx+lQzBQq9ZBpNLrWRdhXzCpiaC04o7q/eef+8dZwU62A1pd8uZ+0dI5T7lOVXoo03uF863YSOKX8ad2CiYUJ8gMvMA8FSWnsUbxiDaiXPj8kni5TLsvrkH/CKY/okG5SdRCF/XCcRcXP5dxdsS90tIWdd0xi/2wbalmn3CPQ2eVeDI3dP/jCRBkfiNtD70flhxVngZMZW+Rr1c0Cpo//fMCiV5ksDIBGin5AfOPmvSnPYWV5zcU3QEfl2F5fLXgDCbpaYjaOYjyRt9ZTqabSf1Ar4L9yQFBr4N9cNg4jKskvbha4BDmUL8J7xz/F/OXHFnEDV2YkMcpBfGM2XhKDevceOQcjrPkF5/e5LdwQeYhqgzzm6ur42OwHB2HBU7ulXn/LCk2eYCFa2D1f+2F3ExbwAvnDMhymdQdUniF/NzOfG/1L3yplFsqi270T/2gT1LdOQ/Fgp3FA81k/sMLbICcF/qCp/nS3x+iMZn9B+WLjUQvJy2PehXNcpXEvXWM/e1X4PWDUhwQi7+wjJwERX2W5SB1XjQa7n/osZqpOUG2ShssPNpS3mecaV+MTpRPVYTPFiDTIgkG7F/DSND0c2PDWsBLFus0h4jG70L94uDxVNiMJb/xq/7d2+7FMNPh9UJSK75XcKDhrRYJo3iU7mfl1U89vJB179ulGrdMpWd8v5DHWEDrn3NRBa0KWVYKpqeQ0195XXH1YlZjzK7615K/5ooOGUL42PlX2Hw1zxOJubsIl6LZt8V6wa6zJgywuVLsZb+kpjtHTHmVZyhY8IUnAn6Uj8erYXapvZ8rlhUpjvVuosn8gB9JM6vNct5dVn4e8Wbt6KnRcogHYUJT32QRSWsHCaZNEqwbX+dYaLx1FQZ5daYbXSWwiHhtAJRuYutbmWwWCRTVzyNNMh3sMAI1EcmMoOIX0DEhXYNe7daErR6vMyizQ+h5YKUYK4nUu270M0x/YKRHlxwK8WkEqWv8vfhZqzV0YGud1Hwd5JwWhIEZQ7cc1uPeYJJ5kAaIFqCffmwecS2gzVvOgDkNC5eENXSk7SzDpVgm8IcfZV47L17ni75tyJCS/kreODjap6VdpDNTyS1YJaaO2TMX/s4kWiEZ22Ma4CEv2eQq1uGazGamli+ZwExAtehw=="
|
||||
COM_ERMNVLDMR_STATIC_PUBLIC_URL="encrypted:BEZbNy1WbNKhgUWmKO648nvuVNyMNXrMnq64WkjsScTOcE/HFtMX3nQktbIvEYiTSNHvoceUeW10E9i4n+C5ZEHKIBi9VUk3nuqVIcwAr6xTfqKjM1cJlRNKFQzwhHbBTYcImLoxFvM56KU4L/9Ei/XuQ/QRz0KTabhvGMM="
|
||||
File renamed without changes.
@@ -0,0 +1 @@
|
||||
[git.ermnvldmr.com]:22222,[45.150.33.239]:22222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBXcvf3Lds3AKfGHGh00IWR9tSRbBBqrV/CC6JBGmcrD
|
||||
@@ -0,0 +1,70 @@
|
||||
name: ci - next/build-golden-image
|
||||
'on':
|
||||
workflow_dispatch: null
|
||||
concurrency:
|
||||
group: ci-golden-image
|
||||
cancel-in-progress: false
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
validate-scripts:
|
||||
name: Validate - Provisioner Scripts
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Validate Shell Script Syntax
|
||||
run: bash -n ci/packer/scripts/*.sh
|
||||
build-image:
|
||||
name: Build - Golden Image
|
||||
needs: validate-scripts
|
||||
runs-on: apex-debian13
|
||||
outputs:
|
||||
image_name: ${{ steps.vars.outputs.image_name }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Calculate Image Name
|
||||
id: vars
|
||||
run: 'SHORT_SHA="$(echo ${{ github.sha }} | cut -c1-7)"
|
||||
|
||||
IMAGE_NAME="com-ermnvldmr-debian-12-k3s-amd64-${SHORT_SHA}.qcow2"
|
||||
|
||||
echo "image_name=${IMAGE_NAME}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
'
|
||||
- name: Install Build Tools
|
||||
run: 'sudo apt-get update
|
||||
|
||||
sudo apt-get install -y qemu-utils
|
||||
|
||||
'
|
||||
- name: Build Debian K3s Golden Image
|
||||
run: bash ci/scripts/build-golden-image.sh
|
||||
- name: Prepare and Upload Image Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: golden-image
|
||||
path: output-qemu/com-ermnvldmr-debian-12-k3s-amd64.qcow2
|
||||
retention-days: 30
|
||||
upload-s3:
|
||||
name: Upload - S3 Storage
|
||||
needs: build-image
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Download Image Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: golden-image
|
||||
path: output-qemu
|
||||
- name: Upload to Static S3 (/dev/images/)
|
||||
run: bash ci/scripts/upload-golden-image.sh "${IMAGE_NAME}"
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.STATIC_AWS_ACCESS_KEY_ID || secrets.COM_ERMNVLDMR_STATIC_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.STATIC_AWS_SECRET_ACCESS_KEY || secrets.COM_ERMNVLDMR_STATIC_AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_DEFAULT_REGION: ${{ secrets.STATIC_AWS_REGION || secrets.COM_ERMNVLDMR_STATIC_S3_REGION || 'auto' }}
|
||||
S3_ENDPOINT: ${{ secrets.STATIC_S3_ENDPOINT_URL || secrets.COM_ERMNVLDMR_STATIC_S3_ENDPOINT_URL }}
|
||||
S3_BUCKET: ${{ secrets.STATIC_S3_BUCKET || secrets.COM_ERMNVLDMR_STATIC_S3_BUCKET }}
|
||||
IMAGE_NAME: ${{ needs.build-image.outputs.image_name }}
|
||||
@@ -0,0 +1,332 @@
|
||||
name: packages/kits - next/required
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- packages/kits/**
|
||||
- packages/ui/**
|
||||
- packages/stl/**
|
||||
- packages/i18n/**
|
||||
- .gitea/workflows/packages-kits-next-required.yml
|
||||
- pnpm-lock.yaml
|
||||
- ci/scripts/**
|
||||
concurrency:
|
||||
group: ci-next-kits-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: apex-debian13
|
||||
outputs: {}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Pack dependency cache
|
||||
run: python3 ci/scripts/cache.py pack
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.dependency-cache.outputs.key }}
|
||||
path: .ci/pnpm-store.tar
|
||||
retention-days: 7
|
||||
build-configs:
|
||||
name: Build - Configs & UI
|
||||
needs: setup-stage
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build Storybook config
|
||||
run: pnpm --filter @ermnvldmr/storybook-config build
|
||||
- name: Build i18n
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Build UI
|
||||
run: pnpm --filter @ermnvldmr/ui build
|
||||
- name: Upload Rsbuild Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 30
|
||||
- name: Upload Storybook Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
retention-days: 30
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
retention-days: 30
|
||||
- name: Upload UI dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
retention-days: 30
|
||||
lint-kits:
|
||||
name: Lint - Kits
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Lint Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits lint
|
||||
typecheck-kits:
|
||||
name: Typecheck - Kits
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Typecheck Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits typecheck
|
||||
test-kits:
|
||||
name: Test - Unit
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Test Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits test:coverage
|
||||
build-storybook:
|
||||
name: Build - Storybook
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Build Kits Storybook
|
||||
run: pnpm --filter @ermnvldmr/kits build-storybook
|
||||
- name: Upload Storybook build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kits-storybook-static-${{ github.run_id }}
|
||||
path: packages/kits/storybook-static/
|
||||
retention-days: 30
|
||||
test-storybook:
|
||||
name: Test - Storybook
|
||||
needs: build-storybook
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm --filter @ermnvldmr/kits exec playwright install --with-deps chromium
|
||||
- name: Download Storybook build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: kits-storybook-static-${{ github.run_id }}
|
||||
path: packages/kits/storybook-static/
|
||||
- name: Test Storybook
|
||||
run: pnpm --filter @ermnvldmr/kits test:storybook:ci
|
||||
build-kits:
|
||||
name: Build - Kits
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Build Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits build
|
||||
@@ -0,0 +1,332 @@
|
||||
name: packages/kits - pr/required
|
||||
'on':
|
||||
pull_request:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- packages/kits/**
|
||||
- packages/ui/**
|
||||
- packages/stl/**
|
||||
- packages/i18n/**
|
||||
- .gitea/workflows/packages-kits-pr-required.yml
|
||||
- pnpm-lock.yaml
|
||||
- ci/scripts/**
|
||||
concurrency:
|
||||
group: ci-pr-kits-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: apex-debian13
|
||||
outputs: {}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Pack dependency cache
|
||||
run: python3 ci/scripts/cache.py pack
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.dependency-cache.outputs.key }}
|
||||
path: .ci/pnpm-store.tar
|
||||
retention-days: 7
|
||||
build-configs:
|
||||
name: Build - Configs & UI
|
||||
needs: setup-stage
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build Storybook config
|
||||
run: pnpm --filter @ermnvldmr/storybook-config build
|
||||
- name: Build i18n
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Build UI
|
||||
run: pnpm --filter @ermnvldmr/ui build
|
||||
- name: Upload Rsbuild Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 30
|
||||
- name: Upload Storybook Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
retention-days: 30
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
retention-days: 30
|
||||
- name: Upload UI dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
retention-days: 30
|
||||
lint-kits:
|
||||
name: Lint - Kits
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Lint Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits lint
|
||||
typecheck-kits:
|
||||
name: Typecheck - Kits
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Typecheck Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits typecheck
|
||||
test-kits:
|
||||
name: Test - Unit
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Test Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits test:coverage
|
||||
build-storybook:
|
||||
name: Build - Storybook
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Build Kits Storybook
|
||||
run: pnpm --filter @ermnvldmr/kits build-storybook
|
||||
- name: Upload Storybook build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kits-storybook-static-${{ github.run_id }}
|
||||
path: packages/kits/storybook-static/
|
||||
retention-days: 30
|
||||
test-storybook:
|
||||
name: Test - Storybook
|
||||
needs: build-storybook
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm --filter @ermnvldmr/kits exec playwright install --with-deps chromium
|
||||
- name: Download Storybook build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: kits-storybook-static-${{ github.run_id }}
|
||||
path: packages/kits/storybook-static/
|
||||
- name: Test Storybook
|
||||
run: pnpm --filter @ermnvldmr/kits test:storybook:ci
|
||||
build-kits:
|
||||
name: Build - Kits
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Build Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits build
|
||||
@@ -0,0 +1,133 @@
|
||||
name: packages/ssg - next/required
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- packages/ssg/**
|
||||
- .gitea/workflows/packages-ssg-next-required.yml
|
||||
- pnpm-lock.yaml
|
||||
- ci/scripts/**
|
||||
concurrency:
|
||||
group: ci-ssg-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: apex-debian13
|
||||
outputs: {}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Pack dependency cache
|
||||
run: python3 ci/scripts/cache.py pack
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.dependency-cache.outputs.key }}
|
||||
path: .ci/pnpm-store.tar
|
||||
retention-days: 7
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Upload Config Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 30
|
||||
lint-ssg:
|
||||
name: Lint - SSG
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Lint SSG package
|
||||
run: pnpm --filter @ermnvldmr/ssg lint
|
||||
typecheck-ssg:
|
||||
name: Typecheck - SSG
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Typecheck SSG package
|
||||
run: pnpm --filter @ermnvldmr/ssg typecheck
|
||||
build-ssg:
|
||||
name: Build - SSG
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Build SSG package
|
||||
run: pnpm --filter @ermnvldmr/ssg build
|
||||
@@ -0,0 +1,133 @@
|
||||
name: packages/ssg - pr/required
|
||||
'on':
|
||||
pull_request:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- packages/ssg/**
|
||||
- .gitea/workflows/packages-ssg-pr-required.yml
|
||||
- pnpm-lock.yaml
|
||||
- ci/scripts/**
|
||||
concurrency:
|
||||
group: ci-pr-ssg-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: apex-debian13
|
||||
outputs: {}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Pack dependency cache
|
||||
run: python3 ci/scripts/cache.py pack
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.dependency-cache.outputs.key }}
|
||||
path: .ci/pnpm-store.tar
|
||||
retention-days: 7
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Upload Config Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 30
|
||||
lint-ssg:
|
||||
name: Lint - SSG
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Lint SSG package
|
||||
run: pnpm --filter @ermnvldmr/ssg lint
|
||||
typecheck-ssg:
|
||||
name: Typecheck - SSG
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Typecheck SSG package
|
||||
run: pnpm --filter @ermnvldmr/ssg typecheck
|
||||
build-ssg:
|
||||
name: Build - SSG
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Build SSG package
|
||||
run: pnpm --filter @ermnvldmr/ssg build
|
||||
@@ -0,0 +1,156 @@
|
||||
name: packages/stl - next/required
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- packages/stl/**
|
||||
- .gitea/workflows/packages-stl-next-required.yml
|
||||
- pnpm-lock.yaml
|
||||
- ci/scripts/**
|
||||
concurrency:
|
||||
group: ci-stl-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: apex-debian13
|
||||
outputs: {}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Pack dependency cache
|
||||
run: python3 ci/scripts/cache.py pack
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.dependency-cache.outputs.key }}
|
||||
path: .ci/pnpm-store.tar
|
||||
retention-days: 7
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Upload Config Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 30
|
||||
lint-stl:
|
||||
name: Lint - STL
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Lint STL package
|
||||
run: pnpm --filter @ermnvldmr/stl lint
|
||||
typecheck-stl:
|
||||
name: Typecheck - STL
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Typecheck STL package
|
||||
run: pnpm --filter @ermnvldmr/stl typecheck
|
||||
test-stl:
|
||||
name: Test - STL
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Test STL package
|
||||
run: pnpm --filter @ermnvldmr/stl test:coverage
|
||||
build-stl:
|
||||
name: Build - STL
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Build STL package
|
||||
run: pnpm --filter @ermnvldmr/stl build
|
||||
@@ -0,0 +1,157 @@
|
||||
name: packages/stl - pr/required
|
||||
'on':
|
||||
pull_request:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- packages/stl/**
|
||||
- .gitea/workflows/packages-stl-pr-required.yml
|
||||
- pnpm-lock.yaml
|
||||
- ci/scripts/**
|
||||
workflow_dispatch: {}
|
||||
concurrency:
|
||||
group: ci-pr-stl-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: apex-debian13
|
||||
outputs: {}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Pack dependency cache
|
||||
run: python3 ci/scripts/cache.py pack
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.dependency-cache.outputs.key }}
|
||||
path: .ci/pnpm-store.tar
|
||||
retention-days: 7
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Upload Config Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 30
|
||||
lint-stl:
|
||||
name: Lint - STL
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Lint STL package
|
||||
run: pnpm --filter @ermnvldmr/stl lint
|
||||
typecheck-stl:
|
||||
name: Typecheck - STL
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Typecheck STL package
|
||||
run: pnpm --filter @ermnvldmr/stl typecheck
|
||||
test-stl:
|
||||
name: Test - STL
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Test STL package
|
||||
run: pnpm --filter @ermnvldmr/stl test:coverage
|
||||
build-stl:
|
||||
name: Build - STL
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Build STL package
|
||||
run: pnpm --filter @ermnvldmr/stl build
|
||||
@@ -0,0 +1,284 @@
|
||||
name: packages/ui - next/required
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- packages/ui/**
|
||||
- packages/stl/**
|
||||
- packages/i18n/**
|
||||
- .gitea/workflows/packages-ui-next-required.yml
|
||||
- pnpm-lock.yaml
|
||||
- ci/scripts/**
|
||||
concurrency:
|
||||
group: ci-ui-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: apex-debian13
|
||||
outputs: {}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Pack dependency cache
|
||||
run: python3 ci/scripts/cache.py pack
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.dependency-cache.outputs.key }}
|
||||
path: .ci/pnpm-store.tar
|
||||
retention-days: 7
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build Storybook config
|
||||
run: pnpm --filter @ermnvldmr/storybook-config build
|
||||
- name: Build i18n
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Upload Rsbuild Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 30
|
||||
- name: Upload Storybook Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
retention-days: 30
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
retention-days: 30
|
||||
lint-ui:
|
||||
name: Lint - UI
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Lint UI package
|
||||
run: pnpm --filter @ermnvldmr/ui lint
|
||||
typecheck-ui:
|
||||
name: Typecheck - UI
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Typecheck UI package
|
||||
run: pnpm --filter @ermnvldmr/ui typecheck
|
||||
test-ui:
|
||||
name: Test - Unit
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Test UI package
|
||||
run: pnpm --filter @ermnvldmr/ui test:coverage
|
||||
build-storybook:
|
||||
name: Build - Storybook
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Build UI Storybook
|
||||
run: pnpm --filter @ermnvldmr/ui build-storybook
|
||||
test-storybook:
|
||||
name: Test - Storybook
|
||||
needs: build-storybook
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm --filter @ermnvldmr/ui exec playwright install --with-deps chromium
|
||||
- name: Build UI Storybook
|
||||
run: pnpm --filter @ermnvldmr/ui build-storybook
|
||||
- name: Test Storybook
|
||||
run: pnpm --filter @ermnvldmr/ui test:storybook:ci
|
||||
build-ui:
|
||||
name: Build - UI
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Build UI package
|
||||
run: pnpm --filter @ermnvldmr/ui build
|
||||
@@ -0,0 +1,293 @@
|
||||
name: packages/ui - pr/required
|
||||
'on':
|
||||
pull_request:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- packages/ui/**
|
||||
- packages/stl/**
|
||||
- packages/i18n/**
|
||||
- .gitea/workflows/packages-ui-pr-required.yml
|
||||
- pnpm-lock.yaml
|
||||
- ci/scripts/**
|
||||
concurrency:
|
||||
group: ci-pr-ui-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: apex-debian13
|
||||
outputs: {}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Pack dependency cache
|
||||
run: python3 ci/scripts/cache.py pack
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.dependency-cache.outputs.key }}
|
||||
path: .ci/pnpm-store.tar
|
||||
retention-days: 7
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build Storybook config
|
||||
run: pnpm --filter @ermnvldmr/storybook-config build
|
||||
- name: Build i18n
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Upload Rsbuild Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 30
|
||||
- name: Upload Storybook Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
retention-days: 30
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
retention-days: 30
|
||||
lint-ui:
|
||||
name: Lint - UI
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Lint UI package
|
||||
run: pnpm --filter @ermnvldmr/ui lint
|
||||
typecheck-ui:
|
||||
name: Typecheck - UI
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Typecheck UI package
|
||||
run: pnpm --filter @ermnvldmr/ui typecheck
|
||||
test-ui:
|
||||
name: Test - Unit
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Test UI package
|
||||
run: pnpm --filter @ermnvldmr/ui test:coverage
|
||||
build-storybook:
|
||||
name: Build - Storybook
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Build UI Storybook
|
||||
run: pnpm --filter @ermnvldmr/ui build-storybook
|
||||
- name: Upload Storybook build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ui-storybook-static-${{ github.run_id }}
|
||||
path: packages/ui/storybook-static/
|
||||
retention-days: 30
|
||||
test-storybook:
|
||||
name: Test - Storybook
|
||||
needs: build-storybook
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm --filter @ermnvldmr/ui exec playwright install --with-deps chromium
|
||||
- name: Download Storybook build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-storybook-static-${{ github.run_id }}
|
||||
path: packages/ui/storybook-static/
|
||||
- name: Test Storybook
|
||||
run: pnpm --filter @ermnvldmr/ui test:storybook:ci
|
||||
build-ui:
|
||||
name: Build - UI
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Build UI package
|
||||
run: pnpm --filter @ermnvldmr/ui build
|
||||
@@ -0,0 +1,144 @@
|
||||
name: services/docs - next/required
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- services/docs/**
|
||||
- .gitea/workflows/services-docs-next-required.yml
|
||||
- ci/docker/docs/**
|
||||
- ci/config/nginx/**
|
||||
- ci/scripts/**
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ci-docs-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-hugo:
|
||||
name: Setup - Hugo
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup Hugo
|
||||
run: hugo version | grep extended
|
||||
|
||||
build-hugo:
|
||||
name: Build - Hugo Site
|
||||
needs: setup-hugo
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup Hugo
|
||||
run: hugo version | grep extended
|
||||
- name: Build Hugo site
|
||||
working-directory: services/docs
|
||||
run: hugo --gc --minify
|
||||
|
||||
release-docker:
|
||||
name: Release - Docker Image
|
||||
needs: build-hugo
|
||||
runs-on: apex-debian13
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
outputs:
|
||||
short_sha: ${{ steps.vars.outputs.short_sha }}
|
||||
version: ${{ steps.vars.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Log in to Gitea Container Registry
|
||||
run: python3 ci/scripts/docker.py login
|
||||
env:
|
||||
REGISTRY: git.ermnvldmr.com
|
||||
REGISTRY_USER: ${{ github.repository_owner }}
|
||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
- name: Compute version and metadata
|
||||
id: vars
|
||||
run: |
|
||||
SHORT_SHA="sha-$(echo ${{ github.sha }} | cut -c1-7)"
|
||||
VERSION=$(bash ci/scripts/calver-release.sh docs)
|
||||
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
- name: Create and push release tag
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/next'
|
||||
run: bash ci/scripts/calver-release.sh tag docs "${{ github.sha }}" "${{ steps.vars.outputs.version }}"
|
||||
env:
|
||||
GIT_WRITE_KEY: ${{ secrets.DOCS_DEPLOY_KEY }}
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
run: python3 ci/scripts/docker.py metadata
|
||||
env:
|
||||
IMAGES: git.ermnvldmr.com/${{ github.repository }}/docs
|
||||
TAG_RULES: |
|
||||
type=raw,value=latest
|
||||
type=raw,value=${{ steps.vars.outputs.version }}
|
||||
type=sha,format=short
|
||||
SOURCE_SHA: ${{ github.sha }}
|
||||
- name: Build and push Docker image
|
||||
run: python3 ci/scripts/docker.py build
|
||||
env:
|
||||
TAGS: ${{ steps.meta.outputs.tags }}
|
||||
LABELS: ${{ steps.meta.outputs.labels }}
|
||||
BUILD_ARGS: |
|
||||
COMMIT_SHA=${{ steps.vars.outputs.short_sha }}
|
||||
VERSION=${{ steps.vars.outputs.version }}
|
||||
DOCKERFILE: ci/docker/docs/Dockerfile
|
||||
PLATFORMS: ''
|
||||
PUSH: 'true'
|
||||
CONTEXT: .
|
||||
SOURCE_SHA: ${{ github.sha }}
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
|
||||
deploy:
|
||||
name: Deploy - Kubernetes (GitOps)
|
||||
needs: release-docker
|
||||
runs-on: apex-debian13
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/next'
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: next
|
||||
- name: Setup Kustomize
|
||||
run: test "$(kustomize version)" = v5.3.0
|
||||
- name: Deploy via GitOps
|
||||
run: |
|
||||
DOCS_TAG="${{ needs.release-docker.outputs.version }}"
|
||||
bash ci/scripts/deploy-gitops.sh docs "${DOCS_TAG}"
|
||||
env:
|
||||
GIT_WRITE_KEY: ${{ secrets.DOCS_DEPLOY_KEY }}
|
||||
|
||||
verify-rollout:
|
||||
name: Verify - Production Rollout
|
||||
needs:
|
||||
- release-docker
|
||||
- deploy
|
||||
runs-on: apex-debian13
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/next'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: next
|
||||
- name: Await production rollout
|
||||
run: |
|
||||
DOCS_TAG="${{ needs.release-docker.outputs.version }}"
|
||||
bash ci/scripts/await-rollout.sh docs "${DOCS_TAG}"
|
||||
@@ -0,0 +1,40 @@
|
||||
name: services/docs - pr/required
|
||||
'on':
|
||||
pull_request:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- services/docs/**
|
||||
- .gitea/workflows/services-docs-pr-required.yml
|
||||
- ci/scripts/**
|
||||
concurrency:
|
||||
group: ci-pr-docs-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-hugo:
|
||||
name: Setup - Hugo
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup Hugo
|
||||
run: hugo version | grep extended
|
||||
build-hugo:
|
||||
name: Build - Hugo Site
|
||||
needs: setup-hugo
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup Hugo
|
||||
run: hugo version | grep extended
|
||||
- name: Build Hugo site
|
||||
working-directory: services/docs
|
||||
run: hugo --gc --minify
|
||||
@@ -0,0 +1,195 @@
|
||||
name: services/static - next/required
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- services/static/**
|
||||
- packages/ui/**
|
||||
- packages/stl/**
|
||||
- packages/i18n/**
|
||||
- .gitea/workflows/services-static-next-required.yml
|
||||
- ci/scripts/**
|
||||
- pnpm-lock.yaml
|
||||
- ci/scripts/**
|
||||
concurrency:
|
||||
group: ci-static-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: apex-debian13
|
||||
outputs: {}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Pack dependency cache
|
||||
run: python3 ci/scripts/cache.py pack
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.dependency-cache.outputs.key }}
|
||||
path: .ci/pnpm-store.tar
|
||||
retention-days: 7
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build i18n
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Upload Config Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 30
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
retention-days: 30
|
||||
lint-static:
|
||||
name: Lint - Static
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Lint static service
|
||||
run: pnpm --filter @ermnvldmr/static lint
|
||||
typecheck-static:
|
||||
name: Typecheck - Static
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Typecheck static service
|
||||
run: pnpm --filter @ermnvldmr/static typecheck
|
||||
build-static:
|
||||
name: Build - Static Site
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Build static service
|
||||
run: pnpm --filter @ermnvldmr/static build
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: static-dist
|
||||
path: services/static/dist/
|
||||
retention-days: 30
|
||||
deploy-s3:
|
||||
name: Deploy - S3
|
||||
needs:
|
||||
- lint-static
|
||||
- typecheck-static
|
||||
- build-static
|
||||
runs-on: apex-debian13
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/next'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: static-dist
|
||||
path: services/static/dist/
|
||||
- name: Deploy to S3
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.STATIC_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.STATIC_AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_DEFAULT_REGION: ${{ secrets.STATIC_AWS_REGION }}
|
||||
S3_BUCKET: ${{ secrets.STATIC_S3_BUCKET }}
|
||||
AWS_REGION: ${{ secrets.STATIC_AWS_REGION }}
|
||||
S3_ENDPOINT_URL: ${{ secrets.STATIC_S3_ENDPOINT_URL }}
|
||||
run: 'chmod +x ci/scripts/deploy-static.sh
|
||||
|
||||
./ci/scripts/deploy-static.sh
|
||||
|
||||
'
|
||||
@@ -0,0 +1,166 @@
|
||||
name: services/static - pr/required
|
||||
'on':
|
||||
pull_request:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- services/static/**
|
||||
- packages/ui/**
|
||||
- packages/stl/**
|
||||
- packages/i18n/**
|
||||
- .gitea/workflows/services-static-pr-required.yml
|
||||
- ci/scripts/**
|
||||
- pnpm-lock.yaml
|
||||
- ci/scripts/**
|
||||
concurrency:
|
||||
group: ci-pr-static-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: apex-debian13
|
||||
outputs: {}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Pack dependency cache
|
||||
run: python3 ci/scripts/cache.py pack
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.dependency-cache.outputs.key }}
|
||||
path: .ci/pnpm-store.tar
|
||||
retention-days: 7
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build i18n
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Upload Config Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 30
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
retention-days: 30
|
||||
lint-static:
|
||||
name: Lint - Static
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Lint static service
|
||||
run: pnpm --filter @ermnvldmr/static lint
|
||||
typecheck-static:
|
||||
name: Typecheck - Static
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Typecheck static service
|
||||
run: pnpm --filter @ermnvldmr/static typecheck
|
||||
build-static:
|
||||
name: Build - Static Site
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Build static service
|
||||
run: pnpm --filter @ermnvldmr/static build
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: static-dist-${{ github.run_id }}
|
||||
path: services/static/dist/
|
||||
retention-days: 30
|
||||
@@ -0,0 +1,88 @@
|
||||
name: services/www - manual deploy
|
||||
'on':
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tested_sha:
|
||||
description: Full tested source commit (leave blank for latest)
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
tested_digest:
|
||||
description: OCI sha256 digest from the successful build (leave blank for latest)
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
build_run_id:
|
||||
description: Successful WWW build run ID (leave blank for latest)
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
release_tag:
|
||||
description: Previously approved release tag (leave blank for latest)
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
release_run_id:
|
||||
description: Approved release run ID (leave blank for latest)
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
permissions:
|
||||
contents: write
|
||||
actions: read
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy - Kubernetes (GitOps)
|
||||
runs-on: apex-debian13
|
||||
if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/next' && github.actor == 'deytenit'
|
||||
outputs:
|
||||
short_sha: ${{ steps.provenance.outputs.short_sha }}
|
||||
tested_digest: ${{ steps.provenance.outputs.tested_digest }}
|
||||
release_tag: ${{ steps.provenance.outputs.release_tag }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: next
|
||||
- name: Authenticate local registry
|
||||
run: python3 ci/scripts/docker.py login
|
||||
env:
|
||||
REGISTRY: git.ermnvldmr.com
|
||||
REGISTRY_USER: ${{ github.repository_owner }}
|
||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
- name: Verify successful build and approved release
|
||||
id: provenance
|
||||
run: python3 ci/scripts/provenance.py
|
||||
env:
|
||||
IS_DEPLOY: 'true'
|
||||
TESTED_SHA: ${{ inputs.tested_sha }}
|
||||
TESTED_DIGEST: ${{ inputs.tested_digest }}
|
||||
BUILD_RUN_ID: ${{ inputs.build_run_id }}
|
||||
RELEASE_RUN_ID: ${{ inputs.release_run_id }}
|
||||
RELEASE_TAG: ${{ inputs.release_tag }}
|
||||
PROVENANCE_TOKEN: ${{ github.token }}
|
||||
- name: Setup Kustomize
|
||||
run: test "$(kustomize version)" = v5.3.0
|
||||
- name: Deploy via GitOps
|
||||
run: |
|
||||
RELEASE_TAG="${{ steps.provenance.outputs.release_tag }}"
|
||||
TESTED_DIGEST="${{ steps.provenance.outputs.tested_digest }}"
|
||||
TESTED_SHA="${{ steps.provenance.outputs.tested_sha }}"
|
||||
|
||||
echo "Deploying www digest $TESTED_DIGEST ($RELEASE_TAG)..."
|
||||
bash ci/scripts/deploy-gitops.sh www "$TESTED_DIGEST"
|
||||
env:
|
||||
GIT_WRITE_KEY: ${{ secrets.WWW_DEPLOY_KEY }}
|
||||
|
||||
verify-rollout:
|
||||
name: Verify - Production Rollout
|
||||
needs: deploy
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: next
|
||||
- name: Await production rollout
|
||||
run: |
|
||||
bash ci/scripts/await-rollout.sh www "${{ needs.deploy.outputs.short_sha }}"
|
||||
@@ -0,0 +1,377 @@
|
||||
name: services/www - next/required
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- services/www/**
|
||||
- packages/ui/**
|
||||
- packages/stl/**
|
||||
- packages/i18n/**
|
||||
- .gitea/workflows/services-www-next-required.yml
|
||||
- ci/docker/www/**
|
||||
- ci/config/nginx/**
|
||||
- ci/scripts/**
|
||||
- pnpm-lock.yaml
|
||||
- ci/scripts/**
|
||||
workflow_dispatch: null
|
||||
concurrency:
|
||||
group: ci-www-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: apex-debian13
|
||||
outputs: {}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Pack dependency cache
|
||||
run: python3 ci/scripts/cache.py pack
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.dependency-cache.outputs.key }}
|
||||
path: .ci/pnpm-store.tar
|
||||
retention-days: 7
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build Storybook config
|
||||
run: pnpm --filter @ermnvldmr/storybook-config build
|
||||
- name: Upload Rsbuild Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 30
|
||||
- name: Upload Storybook Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
retention-days: 30
|
||||
- name: Build i18n package
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
retention-days: 30
|
||||
lint-www:
|
||||
name: Lint - WWW
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Lint WWW service
|
||||
run: pnpm --filter @ermnvldmr/www lint
|
||||
typecheck-www:
|
||||
name: Typecheck - WWW
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Typecheck WWW service
|
||||
run: pnpm --filter @ermnvldmr/www typecheck
|
||||
build-storybook:
|
||||
name: Build - Storybook
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Build WWW Storybook
|
||||
run: pnpm --filter @ermnvldmr/www build-storybook
|
||||
- name: Upload Storybook build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-static
|
||||
path: services/www/storybook-static/
|
||||
retention-days: 30
|
||||
test-www:
|
||||
name: Test - Unit
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Test WWW service
|
||||
run: pnpm --filter @ermnvldmr/www test:coverage
|
||||
test-storybook:
|
||||
name: Test - Storybook
|
||||
needs: build-storybook
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm --filter @ermnvldmr/www exec playwright install --with-deps chromium
|
||||
- name: Download Storybook build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-static
|
||||
path: services/www/storybook-static/
|
||||
- name: Serve Storybook and run tests with coverage
|
||||
run: pnpm --filter @ermnvldmr/www test:storybook:ci
|
||||
build-www:
|
||||
name: Build - WWW Service
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Build WWW service (all locales)
|
||||
run: pnpm --filter @ermnvldmr/www build:all
|
||||
env:
|
||||
RSDOCTOR: 'true'
|
||||
- name: Upload Rsdoctor report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsdoctor-report
|
||||
path: services/www/rsdoctor/
|
||||
retention-days: 30
|
||||
build-docker:
|
||||
name: Build - Docker Image
|
||||
needs:
|
||||
- lint-www
|
||||
- typecheck-www
|
||||
- test-www
|
||||
- test-storybook
|
||||
- build-www
|
||||
runs-on: apex-debian13
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
outputs:
|
||||
short_sha: ${{ steps.vars.outputs.short_sha }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Calculate Short SHA
|
||||
id: vars
|
||||
run: |
|
||||
SHORT_SHA="sha-$(echo ${{ github.sha }} | cut -c1-7)"
|
||||
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
|
||||
- name: Log in to Gitea Container Registry
|
||||
run: python3 ci/scripts/docker.py login
|
||||
env:
|
||||
REGISTRY: git.ermnvldmr.com
|
||||
REGISTRY_USER: ${{ github.repository_owner }}
|
||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
run: python3 ci/scripts/docker.py metadata
|
||||
env:
|
||||
IMAGES: git.ermnvldmr.com/${{ github.repository }}/www
|
||||
TAG_RULES: |
|
||||
type=sha,format=short
|
||||
SOURCE_SHA: ${{ github.sha }}
|
||||
- name: Build and push Docker image
|
||||
run: python3 ci/scripts/docker.py build
|
||||
env:
|
||||
TAGS: ${{ steps.meta.outputs.tags }}
|
||||
LABELS: ${{ steps.meta.outputs.labels }}
|
||||
BUILD_ARGS: |
|
||||
COMMIT_SHA=${{ steps.vars.outputs.short_sha }}
|
||||
VERSION=${{ steps.vars.outputs.short_sha }}
|
||||
DOCKERFILE: ci/docker/www/Dockerfile
|
||||
PLATFORMS: ''
|
||||
PUSH: 'true'
|
||||
CONTEXT: .
|
||||
SOURCE_SHA: ${{ github.sha }}
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
id: image
|
||||
- name: Record tested digest provenance
|
||||
env:
|
||||
IMAGE_DIGEST: ${{ steps.image.outputs.digest }}
|
||||
run: |
|
||||
mkdir -p .ci
|
||||
python3 - <<'PROVENANCE'
|
||||
import json, os
|
||||
from pathlib import Path
|
||||
Path('.ci/www-provenance.json').write_text(json.dumps({
|
||||
'sha': os.environ['GITHUB_SHA'],
|
||||
'digest': os.environ['IMAGE_DIGEST'],
|
||||
'run_id': os.environ['GITHUB_RUN_ID'],
|
||||
'repository': os.environ['GITHUB_REPOSITORY']
|
||||
}))
|
||||
PROVENANCE
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: www-provenance
|
||||
path: .ci/www-provenance.json
|
||||
retention-days: 30
|
||||
@@ -0,0 +1,302 @@
|
||||
name: services/www - pr/required
|
||||
'on':
|
||||
pull_request:
|
||||
branches:
|
||||
- next
|
||||
paths:
|
||||
- services/www/**
|
||||
- packages/ui/**
|
||||
- packages/stl/**
|
||||
- packages/i18n/**
|
||||
- .gitea/workflows/services-www-pr-required.yml
|
||||
- pnpm-lock.yaml
|
||||
- ci/scripts/**
|
||||
concurrency:
|
||||
group: ci-pr-www-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: apex-debian13
|
||||
outputs: {}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Pack dependency cache
|
||||
run: python3 ci/scripts/cache.py pack
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.dependency-cache.outputs.key }}
|
||||
path: .ci/pnpm-store.tar
|
||||
retention-days: 7
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build Storybook config
|
||||
run: pnpm --filter @ermnvldmr/storybook-config build
|
||||
- name: Upload Rsbuild Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 30
|
||||
- name: Upload Storybook Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
retention-days: 30
|
||||
- name: Build i18n package
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
retention-days: 30
|
||||
lint-www:
|
||||
name: Lint - WWW
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Lint WWW service
|
||||
run: pnpm --filter @ermnvldmr/www lint
|
||||
typecheck-www:
|
||||
name: Typecheck - WWW
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Typecheck WWW service
|
||||
run: pnpm --filter @ermnvldmr/www typecheck
|
||||
build-storybook:
|
||||
name: Build - Storybook
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Build WWW Storybook
|
||||
run: pnpm --filter @ermnvldmr/www build-storybook
|
||||
- name: Upload Storybook build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: www-storybook-static-${{ github.run_id }}
|
||||
path: services/www/storybook-static/
|
||||
retention-days: 30
|
||||
test-www:
|
||||
name: Test - Unit
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Test WWW service
|
||||
run: pnpm --filter @ermnvldmr/www test:coverage
|
||||
test-storybook:
|
||||
name: Test - Storybook
|
||||
needs: build-storybook
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm --filter @ermnvldmr/www exec playwright install --with-deps chromium
|
||||
- name: Download Storybook build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: www-storybook-static-${{ github.run_id }}
|
||||
path: services/www/storybook-static/
|
||||
- name: Serve Storybook and run tests with coverage
|
||||
run: pnpm --filter @ermnvldmr/www test:storybook:ci
|
||||
build-www:
|
||||
name: Build - WWW Service
|
||||
needs: build-configs
|
||||
runs-on: apex-debian13
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup environment
|
||||
run: bash ci/scripts/setup-env.sh 9.12.3 20
|
||||
- name: Restore repository dependency cache
|
||||
id: dependency-cache
|
||||
run: python3 ci/scripts/cache.py restore
|
||||
env:
|
||||
CACHE_TOKEN: ${{ github.token }}
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Build WWW service (all locales)
|
||||
run: pnpm --filter @ermnvldmr/www build:all
|
||||
env:
|
||||
RSDOCTOR: 'true'
|
||||
- name: Upload Rsdoctor report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsdoctor-report-${{ github.run_id }}
|
||||
path: services/www/rsdoctor/
|
||||
retention-days: 30
|
||||
@@ -0,0 +1,91 @@
|
||||
name: services/www - manual release
|
||||
'on':
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tested_sha:
|
||||
description: Full tested source commit (leave blank for latest)
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
tested_digest:
|
||||
description: OCI sha256 digest (leave blank for latest)
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
build_run_id:
|
||||
description: Successful WWW build run ID (leave blank for latest)
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
actions: read
|
||||
jobs:
|
||||
release:
|
||||
name: Release - Tag and Promote
|
||||
runs-on: apex-debian13
|
||||
if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/next' && github.actor == 'deytenit'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Authenticate local registry
|
||||
run: python3 ci/scripts/docker.py login
|
||||
env:
|
||||
REGISTRY: git.ermnvldmr.com
|
||||
REGISTRY_USER: ${{ github.repository_owner }}
|
||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
- name: Verify successful build and immutable digest
|
||||
id: provenance
|
||||
run: python3 ci/scripts/provenance.py
|
||||
env:
|
||||
TESTED_SHA: ${{ inputs.tested_sha }}
|
||||
TESTED_DIGEST: ${{ inputs.tested_digest }}
|
||||
BUILD_RUN_ID: ${{ inputs.build_run_id }}
|
||||
PROVENANCE_TOKEN: ${{ github.token }}
|
||||
- name: Promote tested digest and create release tag
|
||||
env:
|
||||
TESTED_SHA: ${{ steps.provenance.outputs.tested_sha }}
|
||||
TESTED_DIGEST: ${{ steps.provenance.outputs.tested_digest }}
|
||||
BUILD_RUN_ID: ${{ steps.provenance.outputs.build_run_id }}
|
||||
PROVENANCE_TOKEN: ${{ github.token }}
|
||||
GIT_WRITE_KEY: ${{ secrets.WWW_DEPLOY_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
VERSION=$(bash ci/scripts/calver-release.sh www)
|
||||
TAG_NAME="services/www-$VERSION"
|
||||
|
||||
echo "Tagging Git commit $TESTED_SHA with $TAG_NAME..."
|
||||
bash ci/scripts/calver-release.sh tag www "$TESTED_SHA" "$VERSION"
|
||||
|
||||
echo "Promoting Docker image to $VERSION and latest..."
|
||||
docker buildx imagetools create --prefer-index=false \
|
||||
"git.ermnvldmr.com/${{ github.repository }}/www@$TESTED_DIGEST" \
|
||||
--tag "git.ermnvldmr.com/${{ github.repository }}/www:$VERSION" \
|
||||
--tag "git.ermnvldmr.com/${{ github.repository }}/www:latest"
|
||||
|
||||
test "$(docker buildx imagetools inspect "git.ermnvldmr.com/${{ github.repository }}/www:$VERSION" --format '{{.Manifest.Digest}}')" = "$TESTED_DIGEST"
|
||||
|
||||
export VERSION TAG_NAME
|
||||
mkdir -p .ci
|
||||
python3 - <<'RELEASE_PROVENANCE'
|
||||
import json, os
|
||||
from pathlib import Path
|
||||
|
||||
Path('.ci/www-release.json').write_text(json.dumps({
|
||||
'sha': os.environ['TESTED_SHA'],
|
||||
'digest': os.environ['TESTED_DIGEST'],
|
||||
'run_id': os.environ['BUILD_RUN_ID'],
|
||||
'repository': os.environ['GITHUB_REPOSITORY'],
|
||||
'release_tag': os.environ['TAG_NAME'],
|
||||
'version': os.environ['VERSION']
|
||||
}))
|
||||
RELEASE_PROVENANCE
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: www-release-provenance
|
||||
path: .ci/www-release.json
|
||||
retention-days: 30
|
||||
@@ -1,134 +0,0 @@
|
||||
name: ci - next/build-golden-image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ci-golden-image
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validate-scripts:
|
||||
name: Validate - Provisioner Scripts
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate Shell Script Syntax
|
||||
run: bash -n ci/packer/scripts/*.sh
|
||||
|
||||
build-image:
|
||||
name: Build - Golden Image
|
||||
needs: validate-scripts
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
image_name: ${{ steps.vars.outputs.image_name }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Calculate Image Name
|
||||
id: vars
|
||||
run: |
|
||||
SHORT_SHA="$(echo ${{ github.sha }} | cut -c1-7)"
|
||||
IMAGE_NAME="com-ermnvldmr-debian-12-k3s-amd64-${SHORT_SHA}.qcow2"
|
||||
echo "image_name=${IMAGE_NAME}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Install Build Tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y qemu-utils
|
||||
|
||||
- name: Build Debian K3s Golden Image
|
||||
run: |
|
||||
mkdir -p output-qemu
|
||||
echo "Downloading official Debian 12 cloud image..."
|
||||
curl -fsSL -o base.qcow2 https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2
|
||||
|
||||
echo "Expanding disk image to 10G..."
|
||||
qemu-img resize base.qcow2 10G
|
||||
|
||||
echo "Connecting disk image via NBD..."
|
||||
sudo modprobe nbd max_part=8
|
||||
sudo qemu-nbd --connect=/dev/nbd0 base.qcow2
|
||||
sleep 1
|
||||
sudo partx -u /dev/nbd0 || true
|
||||
|
||||
echo "Mounting disk filesystem..."
|
||||
sudo mkdir -p /mnt/target
|
||||
sudo mount /dev/nbd0p1 /mnt/target
|
||||
|
||||
echo "Mounting host virtual filesystems and configuring DNS..."
|
||||
sudo rm -f /mnt/target/etc/resolv.conf
|
||||
echo "nameserver 1.1.1.1" | sudo tee /mnt/target/etc/resolv.conf
|
||||
echo "nameserver 8.8.8.8" | sudo tee -a /mnt/target/etc/resolv.conf
|
||||
sudo mount --bind /dev /mnt/target/dev
|
||||
sudo mount --bind /dev/pts /mnt/target/dev/pts
|
||||
sudo mount --bind /proc /mnt/target/proc
|
||||
sudo mount --bind /sys /mnt/target/sys
|
||||
|
||||
echo "Copying provisioner scripts..."
|
||||
sudo mkdir -p /mnt/target/tmp/scripts
|
||||
sudo cp ci/packer/scripts/*.sh /mnt/target/tmp/scripts/
|
||||
sudo chmod +x /mnt/target/tmp/scripts/*.sh
|
||||
|
||||
echo "Running provisioner scripts in chroot..."
|
||||
sudo chroot /mnt/target /bin/bash /tmp/scripts/01-base-system.sh
|
||||
sudo chroot /mnt/target /bin/bash /tmp/scripts/02-install-k3s.sh
|
||||
sudo chroot /mnt/target /bin/bash /tmp/scripts/03-cleanup.sh
|
||||
sudo rm -rf /mnt/target/tmp/scripts
|
||||
|
||||
echo "Unmounting filesystems..."
|
||||
sudo umount /mnt/target/sys
|
||||
sudo umount /mnt/target/proc
|
||||
sudo umount /mnt/target/dev/pts
|
||||
sudo umount /mnt/target/dev
|
||||
sudo umount /mnt/target
|
||||
sudo qemu-nbd --disconnect /dev/nbd0
|
||||
|
||||
echo "Compressing and optimizing qcow2 with 2M cluster size..."
|
||||
qemu-img convert -f qcow2 -O qcow2 -o cluster_size=2M -c base.qcow2 output-qemu/com-ermnvldmr-debian-12-k3s-amd64.qcow2
|
||||
echo "Golden image built successfully."
|
||||
|
||||
- name: Prepare and Upload Image Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: golden-image
|
||||
path: output-qemu/com-ermnvldmr-debian-12-k3s-amd64.qcow2
|
||||
retention-days: 1
|
||||
|
||||
upload-s3:
|
||||
name: Upload - S3 Storage
|
||||
needs: build-image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Image Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: golden-image
|
||||
path: output-qemu
|
||||
|
||||
- name: Upload to Static S3 (/dev/images/)
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.STATIC_AWS_ACCESS_KEY_ID || secrets.COM_ERMNVLDMR_STATIC_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.STATIC_AWS_SECRET_ACCESS_KEY || secrets.COM_ERMNVLDMR_STATIC_AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_DEFAULT_REGION: ${{ secrets.STATIC_AWS_REGION || secrets.COM_ERMNVLDMR_STATIC_S3_REGION || 'ru-central1' }}
|
||||
S3_ENDPOINT: ${{ secrets.STATIC_S3_ENDPOINT_URL || secrets.COM_ERMNVLDMR_STATIC_S3_ENDPOINT_URL }}
|
||||
S3_BUCKET: ${{ secrets.STATIC_S3_BUCKET || secrets.COM_ERMNVLDMR_STATIC_S3_BUCKET }}
|
||||
IMAGE_NAME: ${{ needs.build-image.outputs.image_name }}
|
||||
run: |
|
||||
mv output-qemu/com-ermnvldmr-debian-12-k3s-amd64.qcow2 "output-qemu/${IMAGE_NAME}"
|
||||
if [ -n "${S3_BUCKET}" ] && [ -n "${AWS_ACCESS_KEY_ID}" ]; then
|
||||
ENDPOINT_FLAG=""
|
||||
[ -n "${S3_ENDPOINT}" ] && ENDPOINT_FLAG="--endpoint-url=${S3_ENDPOINT}"
|
||||
echo "Cleaning up older images in dev/images/ to free bucket storage..."
|
||||
aws ${ENDPOINT_FLAG} s3 rm "s3://${S3_BUCKET}/dev/images/" --recursive || true
|
||||
aws ${ENDPOINT_FLAG} s3 cp "output-qemu/${IMAGE_NAME}" "s3://${S3_BUCKET}/dev/images/${IMAGE_NAME}"
|
||||
echo "Golden image uploaded to s3://${S3_BUCKET}/dev/images/${IMAGE_NAME}"
|
||||
else
|
||||
echo "S3 storage secrets not provided; skipping remote S3 upload. Image artifact is preserved in GitHub Actions run artifacts."
|
||||
fi
|
||||
@@ -1,51 +0,0 @@
|
||||
name: CodeQL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [next]
|
||||
paths-ignore:
|
||||
- '**/kustomization.yaml'
|
||||
pull_request:
|
||||
branches: [next]
|
||||
paths-ignore:
|
||||
- '**/kustomization.yaml'
|
||||
schedule:
|
||||
- cron: '43 8 * * 3'
|
||||
|
||||
concurrency:
|
||||
group: codeql-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
actions: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ['actions', 'javascript-typescript']
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
@@ -1,361 +0,0 @@
|
||||
name: packages/kits - next/required
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'packages/kits/**'
|
||||
- 'packages/ui/**'
|
||||
- 'packages/stl/**'
|
||||
- 'packages/i18n/**'
|
||||
- '.github/workflows/packages-kits-next-required.yml'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
concurrency:
|
||||
group: ci-next-kits-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
|
||||
- name: Setup Node.js with pnpm cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: cache-pnpm-store
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
build-configs:
|
||||
name: Build - Configs & UI
|
||||
needs: setup-stage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build Storybook config
|
||||
run: pnpm --filter @ermnvldmr/storybook-config build
|
||||
- name: Build i18n
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Build UI
|
||||
run: pnpm --filter @ermnvldmr/ui build
|
||||
- name: Upload Rsbuild Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 1
|
||||
- name: Upload Storybook Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
retention-days: 1
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
retention-days: 1
|
||||
- name: Upload UI dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
retention-days: 1
|
||||
|
||||
lint-kits:
|
||||
name: Lint - Kits
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Lint Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits lint
|
||||
|
||||
typecheck-kits:
|
||||
name: Typecheck - Kits
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Typecheck Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits typecheck
|
||||
|
||||
test-kits:
|
||||
name: Test - Unit
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Test Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits test:coverage
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/kits/coverage/lcov.info
|
||||
flags: kits
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
build-storybook:
|
||||
name: Build - Storybook
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Build Kits Storybook
|
||||
run: pnpm --filter @ermnvldmr/kits build-storybook
|
||||
- name: Upload Storybook build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kits-storybook-static-${{ github.run_id }}
|
||||
path: packages/kits/storybook-static/
|
||||
retention-days: 1
|
||||
|
||||
test-storybook:
|
||||
name: Test - Storybook
|
||||
needs: build-storybook
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm --filter @ermnvldmr/kits exec playwright install --with-deps chromium
|
||||
- name: Download Storybook build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: kits-storybook-static-${{ github.run_id }}
|
||||
path: packages/kits/storybook-static/
|
||||
- name: Test Storybook
|
||||
run: pnpm --filter @ermnvldmr/kits test:storybook:ci
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/kits/coverage/storybook/lcov.info
|
||||
flags: kits-storybook
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
build-kits:
|
||||
name: Build - Kits
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Build Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits build
|
||||
@@ -1,361 +0,0 @@
|
||||
name: packages/kits - pr/required
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'packages/kits/**'
|
||||
- 'packages/ui/**'
|
||||
- 'packages/stl/**'
|
||||
- 'packages/i18n/**'
|
||||
- '.github/workflows/packages-kits-pr-required.yml'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
concurrency:
|
||||
group: ci-pr-kits-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
|
||||
- name: Setup Node.js with pnpm cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: cache-pnpm-store
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
build-configs:
|
||||
name: Build - Configs & UI
|
||||
needs: setup-stage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build Storybook config
|
||||
run: pnpm --filter @ermnvldmr/storybook-config build
|
||||
- name: Build i18n
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Build UI
|
||||
run: pnpm --filter @ermnvldmr/ui build
|
||||
- name: Upload Rsbuild Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 1
|
||||
- name: Upload Storybook Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
retention-days: 1
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
retention-days: 1
|
||||
- name: Upload UI dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
retention-days: 1
|
||||
|
||||
lint-kits:
|
||||
name: Lint - Kits
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Lint Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits lint
|
||||
|
||||
typecheck-kits:
|
||||
name: Typecheck - Kits
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Typecheck Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits typecheck
|
||||
|
||||
test-kits:
|
||||
name: Test - Unit
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Test Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits test:coverage
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/kits/coverage/lcov.info
|
||||
flags: kits
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
build-storybook:
|
||||
name: Build - Storybook
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Build Kits Storybook
|
||||
run: pnpm --filter @ermnvldmr/kits build-storybook
|
||||
- name: Upload Storybook build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kits-storybook-static-${{ github.run_id }}
|
||||
path: packages/kits/storybook-static/
|
||||
retention-days: 1
|
||||
|
||||
test-storybook:
|
||||
name: Test - Storybook
|
||||
needs: build-storybook
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm --filter @ermnvldmr/kits exec playwright install --with-deps chromium
|
||||
- name: Download Storybook build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: kits-storybook-static-${{ github.run_id }}
|
||||
path: packages/kits/storybook-static/
|
||||
- name: Test Storybook
|
||||
run: pnpm --filter @ermnvldmr/kits test:storybook:ci
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/kits/coverage/storybook/lcov.info
|
||||
flags: kits-storybook
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
build-kits:
|
||||
name: Build - Kits
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download UI dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-dist-${{ github.run_id }}
|
||||
path: packages/ui/dist
|
||||
- name: Build Kits package
|
||||
run: pnpm --filter @ermnvldmr/kits build
|
||||
@@ -1,145 +0,0 @@
|
||||
name: packages/ssg - next/required
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'packages/ssg/**'
|
||||
- '.github/workflows/packages-ssg-next-required.yml'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
concurrency:
|
||||
group: ci-ssg-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
|
||||
- name: Setup Node.js with pnpm cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: cache-pnpm-store
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Upload Config Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 1
|
||||
|
||||
lint-ssg:
|
||||
name: Lint - SSG
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Lint SSG package
|
||||
run: pnpm --filter @ermnvldmr/ssg lint
|
||||
|
||||
typecheck-ssg:
|
||||
name: Typecheck - SSG
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Typecheck SSG package
|
||||
run: pnpm --filter @ermnvldmr/ssg typecheck
|
||||
|
||||
build-ssg:
|
||||
name: Build - SSG
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Build SSG package
|
||||
run: pnpm --filter @ermnvldmr/ssg build
|
||||
@@ -1,145 +0,0 @@
|
||||
name: packages/ssg - pr/required
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'packages/ssg/**'
|
||||
- '.github/workflows/packages-ssg-pr-required.yml'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
concurrency:
|
||||
group: ci-pr-ssg-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
|
||||
- name: Setup Node.js with pnpm cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: cache-pnpm-store
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Upload Config Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 1
|
||||
|
||||
lint-ssg:
|
||||
name: Lint - SSG
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Lint SSG package
|
||||
run: pnpm --filter @ermnvldmr/ssg lint
|
||||
|
||||
typecheck-ssg:
|
||||
name: Typecheck - SSG
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Typecheck SSG package
|
||||
run: pnpm --filter @ermnvldmr/ssg typecheck
|
||||
|
||||
build-ssg:
|
||||
name: Build - SSG
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Build SSG package
|
||||
run: pnpm --filter @ermnvldmr/ssg build
|
||||
@@ -1,176 +0,0 @@
|
||||
name: packages/stl - next/required
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'packages/stl/**'
|
||||
- '.github/workflows/packages-stl-next-required.yml'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
concurrency:
|
||||
group: ci-stl-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
|
||||
- name: Setup Node.js with pnpm cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: cache-pnpm-store
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Upload Config Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 1
|
||||
|
||||
lint-stl:
|
||||
name: Lint - STL
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Lint STL package
|
||||
run: pnpm --filter @ermnvldmr/stl lint
|
||||
|
||||
typecheck-stl:
|
||||
name: Typecheck - STL
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Typecheck STL package
|
||||
run: pnpm --filter @ermnvldmr/stl typecheck
|
||||
|
||||
test-stl:
|
||||
name: Test - STL
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Test STL package
|
||||
run: pnpm --filter @ermnvldmr/stl test:coverage
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/stl/coverage/lcov.info
|
||||
flags: stl
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
build-stl:
|
||||
name: Build - STL
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Build STL package
|
||||
run: pnpm --filter @ermnvldmr/stl build
|
||||
@@ -1,176 +0,0 @@
|
||||
name: packages/stl - pr/required
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'packages/stl/**'
|
||||
- '.github/workflows/packages-stl-pr-required.yml'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
concurrency:
|
||||
group: ci-pr-stl-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
|
||||
- name: Setup Node.js with pnpm cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: cache-pnpm-store
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Upload Config Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 1
|
||||
|
||||
lint-stl:
|
||||
name: Lint - STL
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Lint STL package
|
||||
run: pnpm --filter @ermnvldmr/stl lint
|
||||
|
||||
typecheck-stl:
|
||||
name: Typecheck - STL
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Typecheck STL package
|
||||
run: pnpm --filter @ermnvldmr/stl typecheck
|
||||
|
||||
test-stl:
|
||||
name: Test - STL
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Test STL package
|
||||
run: pnpm --filter @ermnvldmr/stl test:coverage
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/stl/coverage/lcov.info
|
||||
flags: stl
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
build-stl:
|
||||
name: Build - STL
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Build STL package
|
||||
run: pnpm --filter @ermnvldmr/stl build
|
||||
@@ -1,313 +0,0 @@
|
||||
name: packages/ui - next/required
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'packages/ui/**'
|
||||
- 'packages/stl/**'
|
||||
- 'packages/i18n/**'
|
||||
- '.github/workflows/packages-ui-next-required.yml'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
concurrency:
|
||||
group: ci-ui-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
|
||||
- name: Setup Node.js with pnpm cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: cache-pnpm-store
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build Storybook config
|
||||
run: pnpm --filter @ermnvldmr/storybook-config build
|
||||
- name: Build i18n
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Upload Rsbuild Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 1
|
||||
- name: Upload Storybook Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
retention-days: 1
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
retention-days: 1
|
||||
|
||||
lint-ui:
|
||||
name: Lint - UI
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Lint UI package
|
||||
run: pnpm --filter @ermnvldmr/ui lint
|
||||
|
||||
typecheck-ui:
|
||||
name: Typecheck - UI
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Typecheck UI package
|
||||
run: pnpm --filter @ermnvldmr/ui typecheck
|
||||
|
||||
test-ui:
|
||||
name: Test - Unit
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Test UI package
|
||||
run: pnpm --filter @ermnvldmr/ui test:coverage
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/ui/coverage/lcov.info
|
||||
flags: ui
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
build-storybook:
|
||||
name: Build - Storybook
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Build UI Storybook
|
||||
run: pnpm --filter @ermnvldmr/ui build-storybook
|
||||
|
||||
test-storybook:
|
||||
name: Test - Storybook
|
||||
needs: build-storybook
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm --filter @ermnvldmr/ui exec playwright install --with-deps chromium
|
||||
- name: Build UI Storybook
|
||||
run: pnpm --filter @ermnvldmr/ui build-storybook
|
||||
- name: Test Storybook
|
||||
run: pnpm --filter @ermnvldmr/ui test:storybook:ci
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/ui/coverage/storybook/lcov.info
|
||||
flags: ui-storybook
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
build-ui:
|
||||
name: Build - UI
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Build UI package
|
||||
run: pnpm --filter @ermnvldmr/ui build
|
||||
@@ -1,322 +0,0 @@
|
||||
name: packages/ui - pr/required
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'packages/ui/**'
|
||||
- 'packages/stl/**'
|
||||
- 'packages/i18n/**'
|
||||
- '.github/workflows/packages-ui-pr-required.yml'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
concurrency:
|
||||
group: ci-pr-ui-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
|
||||
- name: Setup Node.js with pnpm cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: cache-pnpm-store
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build Storybook config
|
||||
run: pnpm --filter @ermnvldmr/storybook-config build
|
||||
- name: Build i18n
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Upload Rsbuild Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 1
|
||||
- name: Upload Storybook Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
retention-days: 1
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
retention-days: 1
|
||||
|
||||
lint-ui:
|
||||
name: Lint - UI
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Lint UI package
|
||||
run: pnpm --filter @ermnvldmr/ui lint
|
||||
|
||||
typecheck-ui:
|
||||
name: Typecheck - UI
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Typecheck UI package
|
||||
run: pnpm --filter @ermnvldmr/ui typecheck
|
||||
|
||||
test-ui:
|
||||
name: Test - Unit
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Test UI package
|
||||
run: pnpm --filter @ermnvldmr/ui test:coverage
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/ui/coverage/lcov.info
|
||||
flags: ui
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
build-storybook:
|
||||
name: Build - Storybook
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Build UI Storybook
|
||||
run: pnpm --filter @ermnvldmr/ui build-storybook
|
||||
- name: Upload Storybook build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ui-storybook-static-${{ github.run_id }}
|
||||
path: packages/ui/storybook-static/
|
||||
retention-days: 1
|
||||
|
||||
test-storybook:
|
||||
name: Test - Storybook
|
||||
needs: build-storybook
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm --filter @ermnvldmr/ui exec playwright install --with-deps chromium
|
||||
- name: Download Storybook build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-storybook-static-${{ github.run_id }}
|
||||
path: packages/ui/storybook-static/
|
||||
- name: Test Storybook
|
||||
run: pnpm --filter @ermnvldmr/ui test:storybook:ci
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/ui/coverage/storybook/lcov.info
|
||||
flags: ui-storybook
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
build-ui:
|
||||
name: Build - UI
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Build UI package
|
||||
run: pnpm --filter @ermnvldmr/ui build
|
||||
@@ -1,189 +0,0 @@
|
||||
name: services/docs - next/required
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'services/docs/**'
|
||||
- '.github/workflows/services-docs-next-required.yml'
|
||||
- 'ci/docker/docs/**'
|
||||
- 'ci/config/nginx/**'
|
||||
- 'ci/scripts/**'
|
||||
|
||||
concurrency:
|
||||
group: ci-docs-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-hugo:
|
||||
name: Setup - Hugo
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v2
|
||||
with:
|
||||
hugo-version: 'latest'
|
||||
extended: true
|
||||
|
||||
build-hugo:
|
||||
name: Build - Hugo Site
|
||||
needs: setup-hugo
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v2
|
||||
with:
|
||||
hugo-version: 'latest'
|
||||
extended: true
|
||||
|
||||
- name: Build Hugo site
|
||||
working-directory: services/docs
|
||||
run: hugo --gc --minify
|
||||
|
||||
release-docker:
|
||||
name: Release - Docker Image
|
||||
needs: build-hugo
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}/docs
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=sha,format=short
|
||||
|
||||
- name: Compute short SHA
|
||||
id: vars
|
||||
run: echo "short_sha=sha-$(echo ${{ github.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ci/docker/docs/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
COMMIT_SHA=${{ steps.vars.outputs.short_sha }}
|
||||
VERSION=${{ steps.vars.outputs.short_sha }}
|
||||
|
||||
deploy:
|
||||
name: Deploy - Kubernetes (GitOps)
|
||||
needs: release-docker
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/next'
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: next
|
||||
token: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Kustomize
|
||||
uses: imranismail/setup-kustomize@v2
|
||||
with:
|
||||
kustomize-version: "5.3.0"
|
||||
|
||||
- name: Deploy via GitOps
|
||||
run: |
|
||||
DOCS_TAG="sha-$(echo ${{ github.sha }} | cut -c1-7)"
|
||||
bash ci/scripts/deploy-gitops.sh docs "${DOCS_TAG}"
|
||||
|
||||
verify-rollout:
|
||||
name: Verify - Production Rollout
|
||||
needs: deploy
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/next'
|
||||
steps:
|
||||
- name: Await production rollout
|
||||
run: |
|
||||
TARGET_COMMIT="sha-$(echo ${{ github.sha }} | cut -c1-7)"
|
||||
ENDPOINT="https://docs.ermnvldmr.com/version.json"
|
||||
|
||||
echo "========================================================"
|
||||
echo "Verifying production deployment on $ENDPOINT"
|
||||
echo "Expected commit: $TARGET_COMMIT"
|
||||
echo "========================================================"
|
||||
|
||||
MAX_ATTEMPTS=45
|
||||
ATTEMPT=1
|
||||
CF_BLOCKED_COUNT=0
|
||||
|
||||
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
|
||||
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: Polling $ENDPOINT..."
|
||||
|
||||
RESP_FILE=$(mktemp)
|
||||
HTTP_CODE=$(curl -sL --max-time 10 \
|
||||
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" \
|
||||
-H "Accept: application/json" \
|
||||
-o "$RESP_FILE" \
|
||||
-w "%{http_code}" \
|
||||
"$ENDPOINT" || echo "000")
|
||||
|
||||
if [ "$HTTP_CODE" = "200" ]; then
|
||||
LIVE_COMMIT=$(jq -r '.commit // empty' "$RESP_FILE" 2>/dev/null || true)
|
||||
echo " Live commit: ${LIVE_COMMIT:-<none>}"
|
||||
|
||||
if [ "$LIVE_COMMIT" = "$TARGET_COMMIT" ]; then
|
||||
echo "✅ Success! Production docs has successfully rolled out $TARGET_COMMIT."
|
||||
rm -f "$RESP_FILE"
|
||||
exit 0
|
||||
fi
|
||||
elif [ "$HTTP_CODE" = "403" ] || [ "$HTTP_CODE" = "503" ]; then
|
||||
echo " HTTP $HTTP_CODE: Cloudflare WAF/challenge encountered from runner IP."
|
||||
CF_BLOCKED_COUNT=$((CF_BLOCKED_COUNT + 1))
|
||||
else
|
||||
echo " HTTP $HTTP_CODE: Endpoint returned unexpected status code or timed out."
|
||||
fi
|
||||
|
||||
rm -f "$RESP_FILE"
|
||||
|
||||
if [ $CF_BLOCKED_COUNT -ge 5 ]; then
|
||||
echo "⚠️ Cloudflare WAF consistently blocked external probe requests from this runner IP (HTTP $HTTP_CODE)."
|
||||
echo "The GitOps release commit was already pushed to the repository; FluxCD will reconcile the cluster."
|
||||
echo "Skipping further external HTTP verification to avoid false negative."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
sleep 10
|
||||
done
|
||||
|
||||
echo "❌ Deployment verification timed out after 7.5 minutes."
|
||||
exit 1
|
||||
@@ -1,54 +0,0 @@
|
||||
name: services/docs - pr/required
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'services/docs/**'
|
||||
- '.github/workflows/services-docs-pr-required.yml'
|
||||
|
||||
concurrency:
|
||||
group: ci-pr-docs-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-hugo:
|
||||
name: Setup - Hugo
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v2
|
||||
with:
|
||||
hugo-version: 'latest'
|
||||
extended: true
|
||||
|
||||
build-hugo:
|
||||
name: Build - Hugo Site
|
||||
needs: setup-hugo
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v2
|
||||
with:
|
||||
hugo-version: 'latest'
|
||||
extended: true
|
||||
|
||||
- name: Build Hugo site
|
||||
working-directory: services/docs
|
||||
run: hugo --gc --minify
|
||||
@@ -1,205 +0,0 @@
|
||||
name: services/static - next/required
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'services/static/**'
|
||||
- 'packages/ui/**'
|
||||
- 'packages/stl/**'
|
||||
- 'packages/i18n/**'
|
||||
- '.github/workflows/services-static-next-required.yml'
|
||||
- 'ci/scripts/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
concurrency:
|
||||
group: ci-static-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
|
||||
- name: Setup Node.js with pnpm cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: cache-pnpm-store
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build i18n
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Upload Config Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 1
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
retention-days: 1
|
||||
|
||||
lint-static:
|
||||
name: Lint - Static
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Lint static service
|
||||
run: pnpm --filter @ermnvldmr/static lint
|
||||
|
||||
typecheck-static:
|
||||
name: Typecheck - Static
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Typecheck static service
|
||||
run: pnpm --filter @ermnvldmr/static typecheck
|
||||
|
||||
build-static:
|
||||
name: Build - Static Site
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Build static service
|
||||
run: pnpm --filter @ermnvldmr/static build
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: static-dist
|
||||
path: services/static/dist/
|
||||
retention-days: 1
|
||||
|
||||
deploy-s3:
|
||||
name: Deploy - S3
|
||||
needs: [lint-static, typecheck-static, build-static]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/next'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: static-dist
|
||||
path: services/static/dist/
|
||||
|
||||
- name: Deploy to S3
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.STATIC_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.STATIC_AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_DEFAULT_REGION: ${{ secrets.STATIC_AWS_REGION }}
|
||||
S3_BUCKET: ${{ secrets.STATIC_S3_BUCKET }}
|
||||
AWS_REGION: ${{ secrets.STATIC_AWS_REGION }}
|
||||
S3_ENDPOINT_URL: ${{ secrets.STATIC_S3_ENDPOINT_URL }}
|
||||
run: |
|
||||
chmod +x ci/scripts/deploy-static.sh
|
||||
./ci/scripts/deploy-static.sh
|
||||
@@ -1,178 +0,0 @@
|
||||
name: services/static - pr/required
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'services/static/**'
|
||||
- 'packages/ui/**'
|
||||
- 'packages/stl/**'
|
||||
- 'packages/i18n/**'
|
||||
- '.github/workflows/services-static-pr-required.yml'
|
||||
- 'ci/scripts/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
concurrency:
|
||||
group: ci-pr-static-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
|
||||
- name: Setup Node.js with pnpm cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: cache-pnpm-store
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build i18n
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Upload Config Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 1
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
retention-days: 1
|
||||
|
||||
lint-static:
|
||||
name: Lint - Static
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Lint static service
|
||||
run: pnpm --filter @ermnvldmr/static lint
|
||||
|
||||
typecheck-static:
|
||||
name: Typecheck - Static
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Typecheck static service
|
||||
run: pnpm --filter @ermnvldmr/static typecheck
|
||||
|
||||
build-static:
|
||||
name: Build - Static Site
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Config Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Build static service
|
||||
run: pnpm --filter @ermnvldmr/static build
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: static-dist-${{ github.run_id }}
|
||||
path: services/static/dist/
|
||||
retention-days: 1
|
||||
@@ -1,508 +0,0 @@
|
||||
name: services/www - next/required
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'services/www/**'
|
||||
- 'packages/ui/**'
|
||||
- 'packages/stl/**'
|
||||
- 'packages/i18n/**'
|
||||
- '.github/workflows/services-www-next-required.yml'
|
||||
- 'ci/docker/www/**'
|
||||
- 'ci/config/nginx/**'
|
||||
- 'ci/scripts/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ci-www-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
|
||||
- name: Setup Node.js with pnpm cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: cache-pnpm-store
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build Storybook config
|
||||
run: pnpm --filter @ermnvldmr/storybook-config build
|
||||
- name: Upload Rsbuild Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 1
|
||||
- name: Upload Storybook Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
retention-days: 1
|
||||
- name: Build i18n package
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
retention-days: 1
|
||||
|
||||
lint-www:
|
||||
name: Lint - WWW
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Lint WWW service
|
||||
run: pnpm --filter @ermnvldmr/www lint
|
||||
|
||||
typecheck-www:
|
||||
name: Typecheck - WWW
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Typecheck WWW service
|
||||
run: pnpm --filter @ermnvldmr/www typecheck
|
||||
|
||||
build-storybook:
|
||||
name: Build - Storybook
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Build WWW Storybook
|
||||
run: pnpm --filter @ermnvldmr/www build-storybook
|
||||
- name: Upload Storybook build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-static
|
||||
path: services/www/storybook-static/
|
||||
retention-days: 1
|
||||
|
||||
test-www:
|
||||
name: Test - Unit
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Test WWW service
|
||||
run: pnpm --filter @ermnvldmr/www test:coverage
|
||||
- name: Upload Unit coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./services/www/coverage/lcov.info
|
||||
flags: www
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
test-storybook:
|
||||
name: Test - Storybook
|
||||
needs: build-storybook
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm --filter @ermnvldmr/www exec playwright install --with-deps chromium
|
||||
- name: Download Storybook build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-static
|
||||
path: services/www/storybook-static/
|
||||
- name: Serve Storybook and run tests with coverage
|
||||
run: pnpm --filter @ermnvldmr/www test:storybook:ci
|
||||
- name: Upload Storybook coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./services/www/coverage/storybook/lcov.info
|
||||
flags: www-storybook
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
build-www:
|
||||
name: Build - WWW Service
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist
|
||||
path: packages/storybook-config/dist
|
||||
- name: Build WWW service (all locales)
|
||||
run: pnpm --filter @ermnvldmr/www build:all
|
||||
env:
|
||||
RSDOCTOR: 'true'
|
||||
- name: Upload Rsdoctor report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsdoctor-report
|
||||
path: services/www/rsdoctor/
|
||||
retention-days: 30
|
||||
|
||||
build-docker:
|
||||
name: Build - Docker Image
|
||||
needs: [lint-www, typecheck-www, test-www, test-storybook, build-www]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
outputs:
|
||||
short_sha: ${{ steps.vars.outputs.short_sha }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Calculate Short SHA
|
||||
id: vars
|
||||
run: |
|
||||
SHORT_SHA="sha-$(echo ${{ github.sha }} | cut -c1-7)"
|
||||
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ci/docker/www/Dockerfile
|
||||
push: true
|
||||
tags: ghcr.io/${{ github.repository }}/www:${{ steps.vars.outputs.short_sha }}
|
||||
build-args: |
|
||||
COMMIT_SHA=${{ steps.vars.outputs.short_sha }}
|
||||
VERSION=${{ steps.vars.outputs.short_sha }}
|
||||
|
||||
release-tag-docker:
|
||||
name: Release - Tag & Docker
|
||||
needs: build-docker
|
||||
runs-on: ubuntu-latest
|
||||
environment: services-www-releases-publish
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
outputs:
|
||||
version: ${{ steps.calver.outputs.version }}
|
||||
short_sha: ${{ needs.build-docker.outputs.short_sha }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Calculate CalVer Version
|
||||
id: calver
|
||||
run: |
|
||||
VERSION=$(bash ci/scripts/calver-release.sh)
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create and Push Tag
|
||||
run: |
|
||||
TAG_NAME="${{ steps.calver.outputs.version }}"
|
||||
git tag "$TAG_NAME"
|
||||
git push origin "$TAG_NAME"
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Tag and Push Release Docker Image
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
ghcr.io/${{ github.repository }}/www:${{ needs.build-docker.outputs.short_sha }} \
|
||||
--tag ghcr.io/${{ github.repository }}/www:${{ steps.calver.outputs.version }}
|
||||
|
||||
deploy:
|
||||
name: Deploy - Kubernetes (GitOps)
|
||||
needs: release-tag-docker
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: services-www-releases-deploy
|
||||
url: https://ermnvldmr.com
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: next
|
||||
token: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Kustomize
|
||||
uses: imranismail/setup-kustomize@v2
|
||||
with:
|
||||
kustomize-version: "5.3.0"
|
||||
|
||||
- name: Deploy via GitOps
|
||||
run: |
|
||||
WWW_TAG="${{ needs.release-tag-docker.outputs.short_sha }}"
|
||||
bash ci/scripts/deploy-gitops.sh www "${WWW_TAG}"
|
||||
|
||||
verify-rollout:
|
||||
name: Verify - Production Rollout
|
||||
needs: [release-tag-docker, deploy]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Await production rollout
|
||||
run: |
|
||||
TARGET_COMMIT="${{ needs.release-tag-docker.outputs.short_sha }}"
|
||||
ENDPOINT="https://www.ermnvldmr.com/version.json"
|
||||
|
||||
echo "========================================================"
|
||||
echo "Verifying production deployment on $ENDPOINT"
|
||||
echo "Expected commit: $TARGET_COMMIT"
|
||||
echo "========================================================"
|
||||
|
||||
MAX_ATTEMPTS=45
|
||||
ATTEMPT=1
|
||||
CF_BLOCKED_COUNT=0
|
||||
|
||||
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
|
||||
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: Polling $ENDPOINT..."
|
||||
|
||||
RESP_FILE=$(mktemp)
|
||||
HTTP_CODE=$(curl -sL --max-time 10 \
|
||||
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" \
|
||||
-H "Accept: application/json" \
|
||||
-o "$RESP_FILE" \
|
||||
-w "%{http_code}" \
|
||||
"$ENDPOINT" || echo "000")
|
||||
|
||||
if [ "$HTTP_CODE" = "200" ]; then
|
||||
LIVE_COMMIT=$(jq -r '.commit // empty' "$RESP_FILE" 2>/dev/null || true)
|
||||
echo " Live commit: ${LIVE_COMMIT:-<none>}"
|
||||
|
||||
if [ "$LIVE_COMMIT" = "$TARGET_COMMIT" ]; then
|
||||
echo "✅ Success! Production has successfully rolled out $TARGET_COMMIT."
|
||||
rm -f "$RESP_FILE"
|
||||
exit 0
|
||||
fi
|
||||
elif [ "$HTTP_CODE" = "403" ] || [ "$HTTP_CODE" = "503" ]; then
|
||||
echo " HTTP $HTTP_CODE: Cloudflare WAF/challenge encountered from runner IP."
|
||||
CF_BLOCKED_COUNT=$((CF_BLOCKED_COUNT + 1))
|
||||
else
|
||||
echo " HTTP $HTTP_CODE: Endpoint returned unexpected status code or timed out."
|
||||
fi
|
||||
|
||||
rm -f "$RESP_FILE"
|
||||
|
||||
if [ $CF_BLOCKED_COUNT -ge 5 ]; then
|
||||
echo "⚠️ Cloudflare WAF consistently blocked external probe requests from this runner IP (HTTP $HTTP_CODE)."
|
||||
echo "The GitOps release commit was already pushed to the repository; FluxCD will reconcile the cluster."
|
||||
echo "Skipping further external HTTP verification to avoid false negative."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
sleep 10
|
||||
done
|
||||
|
||||
echo "❌ Deployment verification timed out after 7.5 minutes."
|
||||
exit 1
|
||||
@@ -1,331 +0,0 @@
|
||||
name: services/www - pr/required
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [next]
|
||||
paths:
|
||||
- 'services/www/**'
|
||||
- 'packages/ui/**'
|
||||
- 'packages/stl/**'
|
||||
- 'packages/i18n/**'
|
||||
- '.github/workflows/services-www-pr-required.yml'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
concurrency:
|
||||
group: ci-pr-www-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
setup-stage:
|
||||
name: Setup - Stage
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
|
||||
- name: Setup Node.js with pnpm cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: cache-pnpm-store
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
build-configs:
|
||||
name: Build - Configs
|
||||
needs: setup-stage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build Rsbuild config
|
||||
run: pnpm --filter @ermnvldmr/rsbuild-config build
|
||||
- name: Build Storybook config
|
||||
run: pnpm --filter @ermnvldmr/storybook-config build
|
||||
- name: Upload Rsbuild Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
retention-days: 1
|
||||
- name: Upload Storybook Config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
retention-days: 1
|
||||
- name: Build i18n package
|
||||
run: pnpm --filter @ermnvldmr/i18n build
|
||||
- name: Upload i18n dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
retention-days: 1
|
||||
|
||||
lint-www:
|
||||
name: Lint - WWW
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Lint WWW service
|
||||
run: pnpm --filter @ermnvldmr/www lint
|
||||
|
||||
typecheck-www:
|
||||
name: Typecheck - WWW
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Typecheck WWW service
|
||||
run: pnpm --filter @ermnvldmr/www typecheck
|
||||
|
||||
build-storybook:
|
||||
name: Build - Storybook
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Build WWW Storybook
|
||||
run: pnpm --filter @ermnvldmr/www build-storybook
|
||||
- name: Upload Storybook build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: www-storybook-static-${{ github.run_id }}
|
||||
path: services/www/storybook-static/
|
||||
retention-days: 1
|
||||
|
||||
test-www:
|
||||
name: Test - Unit
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Test WWW service
|
||||
run: pnpm --filter @ermnvldmr/www test:coverage
|
||||
- name: Upload Unit coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./services/www/coverage/lcov.info
|
||||
flags: www
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
test-storybook:
|
||||
name: Test - Storybook
|
||||
needs: build-storybook
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Install Playwright browsers
|
||||
run: pnpm --filter @ermnvldmr/www exec playwright install --with-deps chromium
|
||||
- name: Download Storybook build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: www-storybook-static-${{ github.run_id }}
|
||||
path: services/www/storybook-static/
|
||||
- name: Serve Storybook and run tests with coverage
|
||||
run: pnpm --filter @ermnvldmr/www test:storybook:ci
|
||||
- name: Upload Storybook coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./services/www/coverage/storybook/lcov.info
|
||||
flags: www-storybook
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
build-www:
|
||||
name: Build - WWW Service
|
||||
needs: build-configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Download Rsbuild Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rsbuild-config-dist-${{ github.run_id }}
|
||||
path: packages/rsbuild-config/dist
|
||||
- name: Download i18n dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: i18n-dist-${{ github.run_id }}
|
||||
path: packages/i18n/dist
|
||||
- name: Download Storybook Config
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-config-dist-${{ github.run_id }}
|
||||
path: packages/storybook-config/dist
|
||||
- name: Build WWW service (all locales)
|
||||
run: pnpm --filter @ermnvldmr/www build:all
|
||||
env:
|
||||
RSDOCTOR: 'true'
|
||||
- name: Upload Rsdoctor report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rsdoctor-report-${{ github.run_id }}
|
||||
path: services/www/rsdoctor/
|
||||
retention-days: 7
|
||||
@@ -1,6 +1,5 @@
|
||||
# [ermnvldmr.com](https://ermnvldmr.com)
|
||||
|
||||
[](https://app.codecov.io/github/deytenit/ermnvldmr.com)
|
||||
|
||||
The monorepo powering the front pages of my own
|
||||
|
||||
@@ -58,7 +57,7 @@ Want to contribute or understand the codebase.
|
||||
- UI Library: `pnpm ui:dev`
|
||||
- **Development Guide**: [AGENTS.md](./AGENTS.md)
|
||||
- **Component Development**: `pnpm ui:storybook`
|
||||
- **Testing**: `pnpm test` or `pnpm test:coverage` (View results on [Codecov](https://app.codecov.io/github/deytenit/ermnvldmr.com))
|
||||
- **Testing**: `pnpm test` or `pnpm test:coverage`
|
||||
- **Code Quality**: `pnpm lint` and `pnpm typecheck`
|
||||
|
||||
### Designer
|
||||
|
||||
@@ -20,7 +20,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: docs
|
||||
image: ghcr.io/deytenit/ermnvldmr.com/docs:sha-91f5245
|
||||
image: git.ermnvldmr.com/deytenit/ermnvldmr.com/docs:sha-91f5245
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
|
||||
@@ -5,5 +5,6 @@ resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
images:
|
||||
- name: ghcr.io/deytenit/ermnvldmr.com/docs
|
||||
newTag: sha-fcf6895
|
||||
- name: git.ermnvldmr.com/deytenit/ermnvldmr.com/docs
|
||||
newName: git.ermnvldmr.com/deytenit/ermnvldmr.com/docs
|
||||
newTag: sha-4e0bb72
|
||||
@@ -6,4 +6,4 @@ metadata:
|
||||
data:
|
||||
DATABASE_TYPE: "postgresql"
|
||||
CLIENT_IP_HEADER: "X-Forwarded-For"
|
||||
TRACKER_SCRIPT_NAME: "beacon.js"
|
||||
TRACKER_SCRIPT_NAME: "trace.js"
|
||||
@@ -18,7 +18,7 @@ spec:
|
||||
labels:
|
||||
app: umami
|
||||
annotations:
|
||||
config/tracker-script-name: "beacon.js"
|
||||
config/tracker-script-name: "trace.js"
|
||||
spec:
|
||||
containers:
|
||||
- name: umami
|
||||
|
||||
@@ -20,7 +20,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: www
|
||||
image: ghcr.io/deytenit/ermnvldmr.com/www:sha-6d33b71
|
||||
image: git.ermnvldmr.com/deytenit/ermnvldmr.com/www:sha-6d33b71
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
|
||||
@@ -5,5 +5,6 @@ resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
images:
|
||||
- name: ghcr.io/deytenit/ermnvldmr.com/www
|
||||
newTag: sha-fcf6895
|
||||
- name: git.ermnvldmr.com/deytenit/ermnvldmr.com/www
|
||||
newName: git.ermnvldmr.com/deytenit/ermnvldmr.com/www
|
||||
newTag: sha-4e0bb72
|
||||
@@ -5,7 +5,7 @@ metadata:
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m
|
||||
url: https://github.com/deytenit/ermnvldmr.com.git
|
||||
url: https://git.ermnvldmr.com/deytenit/ermnvldmr.com.git
|
||||
ref:
|
||||
branch: next
|
||||
---
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,5 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- https://github.com/cert-manager/cert-manager/releases/download/v1.17.0/cert-manager.yaml
|
||||
- install.yaml
|
||||
- cluster-issuer.yaml
|
||||
@@ -20,7 +20,7 @@ spec:
|
||||
- host: www.ermnvldmr.com
|
||||
http:
|
||||
paths:
|
||||
- path: /beacon
|
||||
- path: /trace
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
@@ -30,7 +30,7 @@ spec:
|
||||
- host: docs.ermnvldmr.com
|
||||
http:
|
||||
paths:
|
||||
- path: /beacon
|
||||
- path: /trace
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
|
||||
@@ -6,4 +6,4 @@ metadata:
|
||||
spec:
|
||||
stripPrefix:
|
||||
prefixes:
|
||||
- /beacon
|
||||
- /trace
|
||||
@@ -0,0 +1 @@
|
||||
[git.ermnvldmr.com]:22222,[45.150.33.239]:22222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBXcvf3Lds3AKfGHGh00IWR9tSRbBBqrV/CC6JBGmcrD
|
||||
@@ -9,7 +9,8 @@ export INSTALL_K3S_SKIP_START="true"
|
||||
export INSTALL_K3S_VERSION="v1.31.2+k3s1"
|
||||
|
||||
# Download official K3s installer and install binary/systemd unit without starting
|
||||
curl -sfL https://get.k3s.io | sh -
|
||||
install -m 0755 /tmp/scripts/k3s /usr/local/bin/k3s
|
||||
INSTALL_K3S_SKIP_DOWNLOAD=true sh /tmp/scripts/k3s-install.sh
|
||||
|
||||
# Verify k3s binary is located in /usr/local/bin
|
||||
which k3s
|
||||
@@ -28,6 +29,7 @@ chmod 644 /etc/rancher/k3s/config.yaml
|
||||
|
||||
# Pre-stage default GitOps root sync manifest
|
||||
mkdir -p /etc/flux
|
||||
install -m 0644 /tmp/scripts/flux-install.yaml /etc/flux/install.yaml
|
||||
cat << 'EOF' > /etc/flux/cluster-sync.yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: GitRepository
|
||||
@@ -36,7 +38,7 @@ metadata:
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m
|
||||
url: https://github.com/deytenit/ermnvldmr.com.git
|
||||
url: https://git.ermnvldmr.com/deytenit/ermnvldmr.com.git
|
||||
ref:
|
||||
branch: next
|
||||
---
|
||||
@@ -119,7 +121,7 @@ else
|
||||
fi
|
||||
|
||||
echo "=== [Bootstrap] Installing Flux Operator / Custom Resources ==="
|
||||
kubectl apply -k "https://github.com/fluxcd/flux2/manifests/install?ref=v2.4.0" || true
|
||||
kubectl apply -f /etc/flux/install.yaml
|
||||
|
||||
echo "=== [Bootstrap] Applying initial GitOps root sync ==="
|
||||
if [ -f /etc/flux/cluster-sync.yaml ]; then
|
||||
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SERVICE="${1:-}"
|
||||
TARGET_COMMIT="${2:-}"
|
||||
ENDPOINT="${3:-https://${SERVICE}.ermnvldmr.com/version.json}"
|
||||
|
||||
if [ -z "$SERVICE" ] || [ -z "$TARGET_COMMIT" ]; then
|
||||
echo "Usage: $0 <service: www|docs> <target_commit> [endpoint_url]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "========================================================"
|
||||
echo "Verifying production deployment on $ENDPOINT"
|
||||
echo "Expected commit: $TARGET_COMMIT"
|
||||
echo "========================================================"
|
||||
|
||||
MAX_ATTEMPTS=45
|
||||
ATTEMPT=1
|
||||
CF_BLOCKED_COUNT=0
|
||||
|
||||
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
|
||||
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: Polling $ENDPOINT..."
|
||||
|
||||
RESP_FILE=$(mktemp)
|
||||
HTTP_CODE=$(curl -sL --max-time 10 \
|
||||
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" \
|
||||
-H "Accept: application/json" \
|
||||
-o "$RESP_FILE" \
|
||||
-w "%{http_code}" \
|
||||
"$ENDPOINT" || echo "000")
|
||||
|
||||
if [ "$HTTP_CODE" = "200" ]; then
|
||||
LIVE_COMMIT=$(jq -r '.commit // empty' "$RESP_FILE" 2>/dev/null || true)
|
||||
LIVE_VERSION=$(jq -r '.version // empty' "$RESP_FILE" 2>/dev/null || true)
|
||||
echo " Live commit: ${LIVE_COMMIT:-<none>}, Live version: ${LIVE_VERSION:-<none>}"
|
||||
|
||||
if [ "$LIVE_COMMIT" = "$TARGET_COMMIT" ] || [ "$LIVE_VERSION" = "$TARGET_COMMIT" ]; then
|
||||
echo "✅ Success! Production $SERVICE has successfully rolled out $TARGET_COMMIT."
|
||||
rm -f "$RESP_FILE"
|
||||
exit 0
|
||||
fi
|
||||
elif [ "$HTTP_CODE" = "403" ] || [ "$HTTP_CODE" = "503" ]; then
|
||||
echo " HTTP $HTTP_CODE: Cloudflare WAF/challenge encountered from runner IP."
|
||||
CF_BLOCKED_COUNT=$((CF_BLOCKED_COUNT + 1))
|
||||
else
|
||||
echo " HTTP $HTTP_CODE: Endpoint returned unexpected status code or timed out."
|
||||
fi
|
||||
|
||||
rm -f "$RESP_FILE"
|
||||
|
||||
if [ $CF_BLOCKED_COUNT -ge 5 ]; then
|
||||
echo "⚠️ Cloudflare WAF consistently blocked external probe requests from this runner IP (HTTP $HTTP_CODE)."
|
||||
echo "The GitOps release commit was already pushed to the repository; FluxCD will reconcile the cluster."
|
||||
echo "Skipping further external HTTP verification to avoid false negative."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
sleep 10
|
||||
done
|
||||
|
||||
echo "❌ Deployment verification timed out after 7.5 minutes."
|
||||
exit 1
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
mkdir -p output-qemu
|
||||
|
||||
echo "Downloading official Debian 12 cloud image..."
|
||||
curl -fsSL -o base.qcow2 https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2
|
||||
|
||||
echo "Expanding disk image to 10G..."
|
||||
qemu-img resize base.qcow2 10G
|
||||
|
||||
echo "Connecting disk image via NBD..."
|
||||
sudo modprobe nbd max_part=8
|
||||
sudo qemu-nbd --connect=/dev/nbd0 base.qcow2
|
||||
sleep 1
|
||||
sudo partx -u /dev/nbd0 || true
|
||||
|
||||
echo "Mounting disk filesystem..."
|
||||
sudo mkdir -p /mnt/target
|
||||
sudo mount /dev/nbd0p1 /mnt/target
|
||||
|
||||
echo "Mounting host virtual filesystems and configuring DNS..."
|
||||
sudo rm -f /mnt/target/etc/resolv.conf
|
||||
echo "nameserver 1.1.1.1" | sudo tee /mnt/target/etc/resolv.conf
|
||||
echo "nameserver 8.8.8.8" | sudo tee -a /mnt/target/etc/resolv.conf
|
||||
sudo mount --bind /dev /mnt/target/dev
|
||||
sudo mount --bind /dev/pts /mnt/target/dev/pts
|
||||
sudo mount --bind /proc /mnt/target/proc
|
||||
sudo mount --bind /sys /mnt/target/sys
|
||||
|
||||
echo "Copying provisioner scripts..."
|
||||
sudo mkdir -p /mnt/target/tmp/scripts
|
||||
sudo cp ci/packer/scripts/*.sh /mnt/target/tmp/scripts/
|
||||
sudo cp /opt/ci/assets/k3s /opt/ci/assets/k3s-install.sh /mnt/target/tmp/scripts/
|
||||
sudo cp /opt/ci/assets/flux-install-local.yaml /mnt/target/tmp/scripts/flux-install.yaml
|
||||
sudo chmod +x /mnt/target/tmp/scripts/*.sh
|
||||
|
||||
echo "Running provisioner scripts in chroot..."
|
||||
sudo chroot /mnt/target /bin/bash /tmp/scripts/01-base-system.sh
|
||||
sudo chroot /mnt/target /bin/bash /tmp/scripts/02-install-k3s.sh
|
||||
sudo chroot /mnt/target /bin/bash /tmp/scripts/03-cleanup.sh
|
||||
sudo rm -rf /mnt/target/tmp/scripts
|
||||
|
||||
echo "Unmounting filesystems..."
|
||||
sudo umount /mnt/target/sys
|
||||
sudo umount /mnt/target/proc
|
||||
sudo umount /mnt/target/dev/pts
|
||||
sudo umount /mnt/target/dev
|
||||
sudo umount /mnt/target
|
||||
sudo qemu-nbd --disconnect /dev/nbd0
|
||||
|
||||
echo "Compressing and optimizing qcow2 with 2M cluster size..."
|
||||
qemu-img convert -f qcow2 -O qcow2 -o cluster_size=2M -c base.qcow2 output-qemu/com-ermnvldmr-debian-12-k3s-amd64.qcow2
|
||||
echo "Golden image built successfully."
|
||||
@@ -0,0 +1,87 @@
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import tempfile
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
SERVER='https://git.ermnvldmr.com'
|
||||
|
||||
|
||||
def eligible(run, repository, current_run):
|
||||
if run.get('repository',{}).get('full_name')!=repository:
|
||||
return False
|
||||
if str(run.get('id'))==current_run:
|
||||
return True
|
||||
return run.get('head_branch')=='next' and run.get('event') in ('push','workflow_dispatch') and run.get('status')=='completed' and run.get('conclusion')=='success'
|
||||
|
||||
|
||||
def request(url):
|
||||
if not url.startswith(SERVER+'/'):
|
||||
raise ValueError('Unexpected cache artifact host')
|
||||
class LocalRedirect(urllib.request.HTTPRedirectHandler):
|
||||
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
||||
if not newurl.startswith(SERVER+'/'):
|
||||
raise ValueError('Unexpected cache storage host')
|
||||
return super().redirect_request(req,fp,code,msg,headers,newurl)
|
||||
req=urllib.request.Request(url,headers={'Authorization':'token '+os.environ['CACHE_TOKEN']})
|
||||
return urllib.request.build_opener(LocalRedirect).open(req,timeout=60)
|
||||
|
||||
|
||||
def api(path):
|
||||
with request(SERVER+'/api/v1/repos/'+os.environ['GITHUB_REPOSITORY']+path) as response:
|
||||
return json.load(response)
|
||||
|
||||
|
||||
def main():
|
||||
store=Path(subprocess.check_output(['pnpm','store','path','--silent'],text=True).strip())
|
||||
archive=Path('.ci/pnpm-store.tar')
|
||||
if sys.argv[1]=='pack':
|
||||
archive.parent.mkdir(exist_ok=True)
|
||||
store.mkdir(parents=True,exist_ok=True)
|
||||
with tarfile.open(archive,'w') as target:target.add(store,arcname='.')
|
||||
return
|
||||
versions=subprocess.check_output(['node','--version'],text=True).strip()+'-'+subprocess.check_output(['pnpm','--version'],text=True).strip()
|
||||
key='pnpm-'+platform.system().lower()+'-'+platform.machine()+'-'+versions+'-'+hashlib.sha256(Path('pnpm-lock.yaml').read_bytes()).hexdigest()
|
||||
with open(os.environ['GITHUB_OUTPUT'],'a') as output:output.write('key='+key+'\n')
|
||||
try:
|
||||
artifacts=api('/actions/artifacts?name='+urllib.parse.quote(key))['artifacts']
|
||||
artifacts.sort(key=lambda a:a['id'],reverse=True)
|
||||
for artifact in artifacts:
|
||||
if artifact.get('expired',True) or artifact.get('name')!=key:
|
||||
continue
|
||||
run_id=artifact.get('workflow_run',{}).get('id')
|
||||
if not run_id:
|
||||
continue
|
||||
run=api('/actions/runs/'+str(run_id))
|
||||
if not eligible(run,os.environ['GITHUB_REPOSITORY'],os.environ['GITHUB_RUN_ID']):
|
||||
continue
|
||||
with tempfile.TemporaryDirectory(prefix='pnpm-artifact-cache-') as directory:
|
||||
downloaded=Path(directory)/'cache.zip'
|
||||
with request(SERVER+'/api/v1/repos/'+os.environ['GITHUB_REPOSITORY']+'/actions/artifacts/'+str(artifact['id'])+'/zip') as response,downloaded.open('wb') as target:
|
||||
while chunk:=response.read(1048576):target.write(chunk)
|
||||
with zipfile.ZipFile(downloaded) as zipped:
|
||||
members=[name for name in zipped.namelist() if name.rsplit('/',1)[-1]=='pnpm-store.tar']
|
||||
if len(members)!=1:
|
||||
raise ValueError('Invalid cache archive')
|
||||
packed=Path(directory)/'store.tar'
|
||||
with zipped.open(members[0]) as source,packed.open('wb') as target:
|
||||
while chunk:=source.read(1048576):target.write(chunk)
|
||||
store.mkdir(parents=True,exist_ok=True)
|
||||
with tarfile.open(packed) as tar:tar.extractall(store,filter='data')
|
||||
print('Restored repository-scoped pnpm cache artifact')
|
||||
return
|
||||
except (urllib.error.HTTPError,urllib.error.URLError,ValueError,tarfile.TarError,zipfile.BadZipFile):
|
||||
print('Cache unavailable; dependencies will be fetched normally')
|
||||
return
|
||||
print('No eligible cache artifact; dependencies will be fetched normally')
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
@@ -1,21 +1,79 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
MODE="version"
|
||||
SERVICE=""
|
||||
COMMIT="HEAD"
|
||||
EXPLICIT_VERSION=""
|
||||
|
||||
if [ $# -ge 2 ] && [ "$1" = "tag" ]; then
|
||||
MODE="tag"
|
||||
SERVICE="$2"
|
||||
COMMIT="${3:-HEAD}"
|
||||
EXPLICIT_VERSION="${4:-}"
|
||||
elif [ $# -ge 1 ]; then
|
||||
SERVICE="$1"
|
||||
fi
|
||||
|
||||
YEAR_MONTH=$(date -u +'%Y.%m')
|
||||
git fetch --tags --quiet 2>/dev/null || true
|
||||
|
||||
# Find highest MICRO version for current YYYY.MM (matching tags formatted like YYYY.MM.MICRO)
|
||||
LATEST_TAG=$(git tag -l "${YEAR_MONTH}.*" | sort -V | tail -n1 || echo "")
|
||||
|
||||
if [ -z "$LATEST_TAG" ]; then
|
||||
MICRO=0
|
||||
else
|
||||
LATEST_MICRO="${LATEST_TAG#"${YEAR_MONTH}."}"
|
||||
if [[ "$LATEST_MICRO" =~ ^[0-9]+$ ]]; then
|
||||
MICRO=$((LATEST_MICRO + 1))
|
||||
if [ -n "$SERVICE" ]; then
|
||||
PREFIX="services/${SERVICE}-"
|
||||
if [ -n "$EXPLICIT_VERSION" ]; then
|
||||
VERSION="$EXPLICIT_VERSION"
|
||||
else
|
||||
MICRO=0
|
||||
PATTERN="${PREFIX}${YEAR_MONTH}.*"
|
||||
LATEST_TAG=$(git tag -l "$PATTERN" | sort -V | tail -n1 || echo "")
|
||||
if [ -z "$LATEST_TAG" ]; then
|
||||
MICRO=1
|
||||
else
|
||||
LATEST_MICRO="${LATEST_TAG#"${PREFIX}${YEAR_MONTH}."}"
|
||||
if [[ "$LATEST_MICRO" =~ ^[0-9]+$ ]]; then
|
||||
MICRO=$((LATEST_MICRO + 1))
|
||||
else
|
||||
MICRO=1
|
||||
fi
|
||||
fi
|
||||
VERSION="${YEAR_MONTH}.${MICRO}"
|
||||
fi
|
||||
TAG_NAME="${PREFIX}${VERSION}"
|
||||
else
|
||||
if [ -n "$EXPLICIT_VERSION" ]; then
|
||||
VERSION="$EXPLICIT_VERSION"
|
||||
else
|
||||
PATTERN="${YEAR_MONTH}.*"
|
||||
LATEST_TAG=$(git tag -l "$PATTERN" | sort -V | tail -n1 || echo "")
|
||||
if [ -z "$LATEST_TAG" ]; then
|
||||
MICRO=0
|
||||
else
|
||||
LATEST_MICRO="${LATEST_TAG#"${YEAR_MONTH}."}"
|
||||
if [[ "$LATEST_MICRO" =~ ^[0-9]+$ ]]; then
|
||||
MICRO=$((LATEST_MICRO + 1))
|
||||
else
|
||||
MICRO=0
|
||||
fi
|
||||
fi
|
||||
VERSION="${YEAR_MONTH}.${MICRO}"
|
||||
fi
|
||||
TAG_NAME="$VERSION"
|
||||
fi
|
||||
|
||||
echo "${YEAR_MONTH}.${MICRO}"
|
||||
if [ "$MODE" = "tag" ]; then
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
if git rev-parse -q --verify "refs/tags/$TAG_NAME" >/dev/null; then
|
||||
EXISTING_COMMIT=$(git rev-parse "refs/tags/$TAG_NAME")
|
||||
if [ "$EXISTING_COMMIT" = "$(git rev-parse "$COMMIT")" ]; then
|
||||
echo "Tag $TAG_NAME already points to $COMMIT"
|
||||
else
|
||||
echo "Tag $TAG_NAME exists but points to $EXISTING_COMMIT, skipping create"
|
||||
fi
|
||||
else
|
||||
echo "Creating tag $TAG_NAME on $COMMIT..."
|
||||
git tag "$TAG_NAME" "$COMMIT"
|
||||
fi
|
||||
echo "Pushing tag $TAG_NAME..."
|
||||
python3 "$ROOT_DIR/ci/scripts/git_write.py" "refs/tags/$TAG_NAME"
|
||||
echo "Successfully created and pushed tag $TAG_NAME"
|
||||
fi
|
||||
echo "$VERSION"
|
||||
@@ -19,17 +19,25 @@ fi
|
||||
|
||||
echo "Updating Kustomize image tag for '$SERVICE' to '$TAG'..."
|
||||
cd "$APP_DIR"
|
||||
kustomize edit set image "ghcr.io/deytenit/ermnvldmr.com/${SERVICE}:${TAG}"
|
||||
IMAGE="git.ermnvldmr.com/deytenit/ermnvldmr.com/${SERVICE}"
|
||||
if [[ "$TAG" =~ ^sha256:[0-9a-f]{64}$ ]]; then
|
||||
kustomize edit set image "${IMAGE}=${IMAGE}@${TAG}"
|
||||
elif [[ "$TAG" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]]; then
|
||||
kustomize edit set image "${IMAGE}=${IMAGE}:${TAG}"
|
||||
else
|
||||
echo "Invalid image digest or tag" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
git config user.name "adam-at-deytenit"
|
||||
git config user.email "221188529+adam-at-deytenit@users.noreply.github.com"
|
||||
git config user.name "APEX CI"
|
||||
git config user.email "adam@ermnvldmr.com"
|
||||
git add "ci/k8s/apps/${SERVICE}/kustomization.yaml"
|
||||
|
||||
if git diff --staged --quiet; then
|
||||
echo "No manifest changes to commit for $SERVICE."
|
||||
else
|
||||
git commit -m "chore(release): deploy ${SERVICE} ${TAG} [skip ci]"
|
||||
git push origin next
|
||||
git commit --no-gpg-sign -m "chore(release): deploy ${SERVICE} ${TAG} [skip ci]"
|
||||
python3 "$ROOT_DIR/ci/scripts/git_write.py" HEAD:next
|
||||
echo "Successfully deployed $SERVICE ($TAG) via GitOps."
|
||||
fi
|
||||
@@ -31,11 +31,21 @@ fi
|
||||
export AWS_DEFAULT_REGION="$AWS_REGION"
|
||||
ENDPOINT_URL="${S3_ENDPOINT_URL%/}"
|
||||
|
||||
# Upload all static files
|
||||
echo "Uploading static files..."
|
||||
aws s3 cp "$DIST_DIR/" "s3://$S3_BUCKET/" \
|
||||
--recursive \
|
||||
--endpoint-url="$ENDPOINT_URL" \
|
||||
--cache-control "max-age=300"
|
||||
# Set S3 max_concurrent_requests to 1 to prevent race conditions on S3-compatible endpoints
|
||||
aws configure set default.s3.max_concurrent_requests 1
|
||||
|
||||
echo "Static files deployed successfully!"
|
||||
# Upload all static files with retry
|
||||
echo "Uploading static files..."
|
||||
for attempt in 1 2 3; do
|
||||
echo "Attempt $attempt/3: syncing static files..."
|
||||
if aws s3 sync "$DIST_DIR/" "s3://$S3_BUCKET/" \
|
||||
--endpoint-url="$ENDPOINT_URL" \
|
||||
--cache-control "max-age=300"; then
|
||||
echo "Static files deployed successfully!"
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo "Error: Failed to upload static files after 3 attempts."
|
||||
exit 1
|
||||
@@ -0,0 +1,64 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def lines(value):
|
||||
return [line.strip() for line in value.splitlines() if line.strip()]
|
||||
|
||||
|
||||
def output(name, value):
|
||||
with open(os.environ['GITHUB_OUTPUT'], 'a') as stream:
|
||||
stream.write(f'{name}<<GITEA_VALUE\n{value}\nGITEA_VALUE\n')
|
||||
|
||||
|
||||
def metadata(images, rules, sha):
|
||||
result = []
|
||||
for rule in lines(rules):
|
||||
fields = dict(part.split('=', 1) for part in rule.split(',') if '=' in part)
|
||||
if fields.get('enable', 'true').lower() == 'false':
|
||||
continue
|
||||
kind = fields.get('type')
|
||||
if kind == 'sha':
|
||||
tag = 'sha-' + sha[:7]
|
||||
elif kind == 'raw':
|
||||
tag = fields['value']
|
||||
else:
|
||||
raise ValueError('Unsupported metadata rule')
|
||||
if not re.fullmatch(r'[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}', tag):
|
||||
raise ValueError('Invalid image tag')
|
||||
result.extend(image.lower() + ':' + tag for image in lines(images))
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
mode = sys.argv[1]
|
||||
if mode == 'login':
|
||||
subprocess.run(['docker', 'login', os.environ['REGISTRY'], '--username', os.environ['REGISTRY_USER'], '--password-stdin'], input=os.environ['REGISTRY_TOKEN'], text=True, check=True)
|
||||
elif mode == 'metadata':
|
||||
output('tags', '\n'.join(metadata(os.environ['IMAGES'], os.environ['TAG_RULES'], os.environ['SOURCE_SHA'])))
|
||||
output('labels', 'org.opencontainers.image.revision=' + os.environ['SOURCE_SHA'])
|
||||
elif mode == 'build':
|
||||
command = ['docker', 'buildx', 'build', '--metadata-file', '/tmp/image-metadata.json']
|
||||
for env, flag in [('TAGS', '--tag'), ('LABELS', '--label'), ('BUILD_ARGS', '--build-arg')]:
|
||||
for value in lines(os.environ.get(env, '')):
|
||||
command.extend([flag, value])
|
||||
command += ['--label', 'org.opencontainers.image.revision=' + os.environ['SOURCE_SHA'], '--label', 'com.ermnvldmr.ci.run=' + os.environ['RUN_ID']]
|
||||
if os.environ.get('PLATFORMS'):
|
||||
command += ['--platform', os.environ['PLATFORMS']]
|
||||
if os.environ.get('DOCKERFILE'):
|
||||
command += ['--file', os.environ['DOCKERFILE']]
|
||||
if os.environ.get('PUSH', 'false').lower() == 'true':
|
||||
command.append('--push')
|
||||
command.append(os.environ.get('CONTEXT', '.'))
|
||||
subprocess.run(command, check=True)
|
||||
output('digest', json.loads(Path('/tmp/image-metadata.json').read_text())['containerimage.digest'])
|
||||
else:
|
||||
raise ValueError('Unknown docker operation')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+139
-115
@@ -11,6 +11,7 @@ import {
|
||||
import { homedir, tmpdir } from 'node:os';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
/**
|
||||
* Checks whether a hostname entry exists in the specified hosts file.
|
||||
@@ -113,6 +114,56 @@ export function findWorkspaceRoot(startDir = dirname(fileURLToPath(import.meta.u
|
||||
return process.cwd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the development Root CA private key from the environment or .env.dev.
|
||||
*
|
||||
* @param {string} workspaceRoot - Absolute path to workspace root
|
||||
* @returns {string | undefined} The PEM private key string, or undefined if unavailable
|
||||
*/
|
||||
export function loadDevRootCaKey(workspaceRoot = findWorkspaceRoot()) {
|
||||
if (process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY) {
|
||||
const raw = process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY;
|
||||
return raw.includes('\\n') ? raw.replace(/\\n/g, '\n') : raw;
|
||||
}
|
||||
|
||||
const envDevPath = join(workspaceRoot, '.env.dev');
|
||||
const envKeysPath = join(workspaceRoot, '.env.keys');
|
||||
|
||||
if (!existsSync(envDevPath)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const candidatePackages = [
|
||||
join(workspaceRoot, 'packages/cli/package.json'),
|
||||
join(workspaceRoot, 'package.json'),
|
||||
];
|
||||
|
||||
for (const pkgPath of candidatePackages) {
|
||||
if (!existsSync(pkgPath)) continue;
|
||||
try {
|
||||
const req = createRequire(pkgPath);
|
||||
const dotenvx = req('@dotenvx/dotenvx');
|
||||
if (dotenvx && typeof dotenvx.config === 'function') {
|
||||
dotenvx.config({ path: envDevPath, envKeysFile: envKeysPath });
|
||||
const key = process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY;
|
||||
if (key) {
|
||||
return key.includes('\\n') ? key.replace(/\\n/g, '\n') : key;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Continue to next candidate package
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote public URL for the Ermnvldmr development root CA certificate.
|
||||
*/
|
||||
export const REMOTE_ROOT_CA_URL =
|
||||
'https://static.ermnvldmr.com/dev/ssl/certs/ErmnvldmrDevelopmentRootCA.pem';
|
||||
|
||||
/**
|
||||
* Ensures valid development certificates exist for localhost.ermnvldmr.com.
|
||||
*
|
||||
@@ -123,128 +174,101 @@ export function ensureDevCertificates(workspaceRoot = findWorkspaceRoot()) {
|
||||
const certsDir = join(workspaceRoot, '.certs');
|
||||
const certPath = join(certsDir, 'localhost.ermnvldmr.com.pem');
|
||||
const keyPath = join(certsDir, 'localhost.ermnvldmr.com-key.pem');
|
||||
const caCertPath = join(certsDir, 'ErmnvldmrDevelopmentRootCA.pem');
|
||||
const caKeyPath = join(certsDir, 'ErmnvldmrDevelopmentRootCA-key.pem');
|
||||
|
||||
ensureHostsEntry('localhost.ermnvldmr.com', '127.0.0.1');
|
||||
|
||||
if (existsSync(certPath) && existsSync(keyPath)) {
|
||||
return { certPath, keyPath };
|
||||
}
|
||||
const home = homedir();
|
||||
const globalCaDir = join(home, '.local/share/mkcert');
|
||||
const globalCaCert = join(globalCaDir, 'ErmnvldmrDevelopmentRootCA.pem');
|
||||
const globalCaKey = join(globalCaDir, 'ErmnvldmrDevelopmentRootCA-key.pem');
|
||||
|
||||
mkdirSync(certsDir, { recursive: true, mode: 0o700 });
|
||||
|
||||
const home = homedir();
|
||||
const hugoCaCert = join(home, '.local/share/mkcert/rootCA.pem');
|
||||
const hugoCaKey = join(home, '.local/share/mkcert/rootCA-key.pem');
|
||||
|
||||
let hasMkcertCli = false;
|
||||
try {
|
||||
execFileSync('mkcert', ['-help'], { stdio: 'ignore' });
|
||||
hasMkcertCli = true;
|
||||
} catch {
|
||||
hasMkcertCli = false;
|
||||
}
|
||||
|
||||
if (hasMkcertCli) {
|
||||
execFileSync(
|
||||
'mkcert',
|
||||
[
|
||||
'-cert-file',
|
||||
certPath,
|
||||
'-key-file',
|
||||
keyPath,
|
||||
'localhost.ermnvldmr.com',
|
||||
'*.localhost.ermnvldmr.com',
|
||||
'localhost',
|
||||
'127.0.0.1',
|
||||
'::1',
|
||||
],
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
chmodSync(keyPath, 0o600);
|
||||
return { certPath, keyPath };
|
||||
}
|
||||
|
||||
if (existsSync(hugoCaCert) && existsSync(hugoCaKey)) {
|
||||
const tempDir = tmpdir();
|
||||
const tempCsr = join(tempDir, `ermnvldmr-dev-${Date.now()}.csr`);
|
||||
const tempExt = join(tempDir, `ermnvldmr-dev-${Date.now()}.cnf`);
|
||||
|
||||
try {
|
||||
writeFileSync(
|
||||
tempExt,
|
||||
'subjectAltName=DNS:localhost.ermnvldmr.com,DNS:*.localhost.ermnvldmr.com,DNS:localhost,IP:127.0.0.1,IP:::1\n'
|
||||
);
|
||||
execFileSync(
|
||||
'openssl',
|
||||
[
|
||||
'req',
|
||||
'-new',
|
||||
'-nodes',
|
||||
'-newkey',
|
||||
'rsa:2048',
|
||||
'-keyout',
|
||||
keyPath,
|
||||
'-out',
|
||||
tempCsr,
|
||||
'-subj',
|
||||
'/CN=localhost.ermnvldmr.com',
|
||||
'-batch',
|
||||
],
|
||||
{ stdio: 'pipe' }
|
||||
);
|
||||
execFileSync(
|
||||
'openssl',
|
||||
[
|
||||
'x509',
|
||||
'-req',
|
||||
'-in',
|
||||
tempCsr,
|
||||
'-CA',
|
||||
hugoCaCert,
|
||||
'-CAkey',
|
||||
hugoCaKey,
|
||||
'-CAcreateserial',
|
||||
'-out',
|
||||
certPath,
|
||||
'-days',
|
||||
'825',
|
||||
'-extfile',
|
||||
tempExt,
|
||||
],
|
||||
{ stdio: 'pipe' }
|
||||
);
|
||||
chmodSync(keyPath, 0o600);
|
||||
chmodSync(certPath, 0o644);
|
||||
} finally {
|
||||
rmSync(tempCsr, { force: true });
|
||||
rmSync(tempExt, { force: true });
|
||||
// 1. Sync CA cert from global cache if available, or download from remote static URL
|
||||
if (!existsSync(caCertPath)) {
|
||||
if (existsSync(globalCaCert)) {
|
||||
writeFileSync(caCertPath, readFileSync(globalCaCert));
|
||||
chmodSync(caCertPath, 0o644);
|
||||
} else {
|
||||
try {
|
||||
execFileSync('curl', ['-sSfL', REMOTE_ROOT_CA_URL, '-o', caCertPath], {
|
||||
stdio: 'ignore',
|
||||
});
|
||||
chmodSync(caCertPath, 0o644);
|
||||
try {
|
||||
mkdirSync(globalCaDir, { recursive: true });
|
||||
writeFileSync(globalCaCert, readFileSync(caCertPath));
|
||||
chmodSync(globalCaCert, 0o644);
|
||||
} catch {
|
||||
// Best-effort global cache
|
||||
}
|
||||
} catch {
|
||||
// Failed to download, will fall back to generation if key also needed
|
||||
}
|
||||
}
|
||||
return { certPath, keyPath };
|
||||
}
|
||||
|
||||
const caDir = join(home, '.local/share/mkcert');
|
||||
mkdirSync(caDir, { recursive: true });
|
||||
execFileSync(
|
||||
'openssl',
|
||||
[
|
||||
'req',
|
||||
'-x509',
|
||||
'-nodes',
|
||||
'-newkey',
|
||||
'rsa:3072',
|
||||
'-keyout',
|
||||
hugoCaKey,
|
||||
'-out',
|
||||
hugoCaCert,
|
||||
'-days',
|
||||
'3650',
|
||||
'-subj',
|
||||
'/CN=mkcert development CA/O=mkcert development CA',
|
||||
'-batch',
|
||||
],
|
||||
{ stdio: 'pipe' }
|
||||
);
|
||||
// 2. Sync CA key from global cache if available, or decrypt from .env.dev
|
||||
if (!existsSync(caKeyPath)) {
|
||||
if (existsSync(globalCaKey)) {
|
||||
writeFileSync(caKeyPath, readFileSync(globalCaKey));
|
||||
chmodSync(caKeyPath, 0o600);
|
||||
} else {
|
||||
const decryptedKey = loadDevRootCaKey(workspaceRoot);
|
||||
if (decryptedKey) {
|
||||
writeFileSync(caKeyPath, decryptedKey);
|
||||
chmodSync(caKeyPath, 0o600);
|
||||
try {
|
||||
mkdirSync(globalCaDir, { recursive: true });
|
||||
writeFileSync(globalCaKey, decryptedKey);
|
||||
chmodSync(globalCaKey, 0o600);
|
||||
} catch {
|
||||
// Best-effort global cache
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If server cert and CA cert already exist, return them
|
||||
if (existsSync(certPath) && existsSync(keyPath) && existsSync(caCertPath)) {
|
||||
return { certPath, keyPath, caCertPath };
|
||||
}
|
||||
|
||||
// Ensure ErmnvldmrDevelopmentRootCA exists
|
||||
if (!existsSync(caCertPath) || !existsSync(caKeyPath)) {
|
||||
mkdirSync(globalCaDir, { recursive: true });
|
||||
execFileSync(
|
||||
'openssl',
|
||||
[
|
||||
'req',
|
||||
'-x509',
|
||||
'-nodes',
|
||||
'-newkey',
|
||||
'rsa:3072',
|
||||
'-keyout',
|
||||
caKeyPath,
|
||||
'-out',
|
||||
caCertPath,
|
||||
'-days',
|
||||
'3650',
|
||||
'-subj',
|
||||
'/CN=Ermnvldmr Development Root CA/O=ermnvldmr.com',
|
||||
'-batch',
|
||||
],
|
||||
{ stdio: 'pipe' }
|
||||
);
|
||||
chmodSync(caKeyPath, 0o600);
|
||||
chmodSync(caCertPath, 0o644);
|
||||
|
||||
writeFileSync(globalCaCert, readFileSync(caCertPath));
|
||||
writeFileSync(globalCaKey, readFileSync(caKeyPath));
|
||||
chmodSync(globalCaKey, 0o600);
|
||||
chmodSync(globalCaCert, 0o644);
|
||||
}
|
||||
|
||||
// Generate server certificate signed by ErmnvldmrDevelopmentRootCA
|
||||
const tempDir = tmpdir();
|
||||
const tempCsr = join(tempDir, `ermnvldmr-dev-${Date.now()}.csr`);
|
||||
const tempExt = join(tempDir, `ermnvldmr-dev-${Date.now()}.cnf`);
|
||||
@@ -280,9 +304,9 @@ export function ensureDevCertificates(workspaceRoot = findWorkspaceRoot()) {
|
||||
'-in',
|
||||
tempCsr,
|
||||
'-CA',
|
||||
hugoCaCert,
|
||||
caCertPath,
|
||||
'-CAkey',
|
||||
hugoCaKey,
|
||||
caKeyPath,
|
||||
'-CAcreateserial',
|
||||
'-out',
|
||||
certPath,
|
||||
@@ -300,7 +324,7 @@ export function ensureDevCertificates(workspaceRoot = findWorkspaceRoot()) {
|
||||
rmSync(tempExt, { force: true });
|
||||
}
|
||||
|
||||
return { certPath, keyPath };
|
||||
return { certPath, keyPath, caCertPath };
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
ref=sys.argv[1]
|
||||
if ref not in ('HEAD:next','trunk') and not re.fullmatch(r'refs/tags/[A-Za-z0-9][A-Za-z0-9._-]*(/[A-Za-z0-9][A-Za-z0-9._-]*)*',ref):
|
||||
raise ValueError('Unexpected publication ref')
|
||||
repository=os.environ['GITHUB_REPOSITORY']
|
||||
if not re.fullmatch(r'deytenit/[A-Za-z0-9_.-]+',repository):
|
||||
raise ValueError('Unexpected repository owner')
|
||||
with tempfile.TemporaryDirectory(prefix='gitea-write-key-') as directory:
|
||||
key = Path(directory) / 'key'
|
||||
key.write_text(os.environ['GIT_WRITE_KEY'])
|
||||
key.chmod(0o600)
|
||||
repo_root = Path(__file__).resolve().parents[2]
|
||||
candidates = [
|
||||
repo_root / '.gitea' / 'known_hosts',
|
||||
repo_root / 'ci' / 'known_hosts',
|
||||
Path(__file__).resolve().parent.parent / '.gitea' / 'known_hosts',
|
||||
Path(__file__).resolve().parent.parent / 'known_hosts',
|
||||
]
|
||||
known = next((c for c in candidates if c.exists()), None)
|
||||
if not known:
|
||||
raise FileNotFoundError(f'Could not find known_hosts in {[str(c) for c in candidates]}')
|
||||
env = dict(os.environ)
|
||||
env.pop('GIT_WRITE_KEY', None)
|
||||
env['GIT_SSH_COMMAND'] = shlex.join([
|
||||
'ssh', '-T', '-i', str(key),
|
||||
'-o', 'BatchMode=yes',
|
||||
'-o', 'ConnectTimeout=15',
|
||||
'-o', 'HostName=45.150.33.239',
|
||||
'-o', 'IdentitiesOnly=yes',
|
||||
'-o', 'StrictHostKeyChecking=yes',
|
||||
'-o', 'HostKeyAlgorithms=ssh-ed25519',
|
||||
'-o', f'UserKnownHostsFile={known}',
|
||||
])
|
||||
subprocess.run(['git', 'push', f'ssh://git@git.ermnvldmr.com:22222/{repository}.git', ref], env=env, check=True)
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
@@ -0,0 +1,161 @@
|
||||
import json
|
||||
import base64
|
||||
import io
|
||||
import zipfile
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import urllib.request
|
||||
|
||||
SERVER = 'https://git.ermnvldmr.com'
|
||||
REPO = 'deytenit/ermnvldmr.com'
|
||||
IMAGE = SERVER.removeprefix('https://') + '/' + REPO + '/www'
|
||||
|
||||
|
||||
def validate_input(sha, digest, run_id):
|
||||
if not re.fullmatch(r'[0-9a-f]{40}', sha):
|
||||
raise ValueError(f'Full source SHA required: {sha}')
|
||||
if not re.fullmatch(r'sha256:[0-9a-f]{64}', digest):
|
||||
raise ValueError(f'OCI sha256 digest required: {digest}')
|
||||
if not re.fullmatch(r'[1-9][0-9]*', str(run_id)):
|
||||
raise ValueError(f'Numeric build run ID required: {run_id}')
|
||||
|
||||
|
||||
def validate_run(run, sha):
|
||||
expected = {'head_sha': sha, 'head_branch': 'next', 'status': 'completed', 'conclusion': 'success'}
|
||||
if any(run.get(key) != value for key, value in expected.items()):
|
||||
raise ValueError('Build did not succeed for this next commit')
|
||||
if run.get('repository', {}).get('full_name') != REPO or (run.get('head_repository') and run['head_repository'].get('full_name') != REPO):
|
||||
raise ValueError('Build source repository mismatch')
|
||||
if run.get('path') != 'services-www-next-required.yml@refs/heads/next':
|
||||
raise ValueError('Wrong build workflow')
|
||||
if run.get('event') not in ('push', 'workflow_dispatch'):
|
||||
raise ValueError('Untrusted build event')
|
||||
|
||||
|
||||
def api(path):
|
||||
req = urllib.request.Request(SERVER + '/api/v1/repos/' + REPO + path, headers={'Authorization': 'token ' + os.environ['PROVENANCE_TOKEN']})
|
||||
with urllib.request.urlopen(req, timeout=30) as response:
|
||||
return json.load(response)
|
||||
|
||||
|
||||
def validate_labels(labels, sha, run_id):
|
||||
if labels.get('org.opencontainers.image.revision') != sha or labels.get('com.ermnvldmr.ci.run') != str(run_id):
|
||||
raise ValueError('Immutable image provenance mismatch')
|
||||
|
||||
|
||||
def validate_manifest(manifest, sha, digest, run_id):
|
||||
expected = {'sha': sha, 'digest': digest, 'run_id': str(run_id), 'repository': REPO}
|
||||
if any(str(manifest.get(k)) != str(v) for k, v in expected.items()):
|
||||
raise ValueError(f'Build artifact does not attest requested image digest: got {manifest}, expected {expected}')
|
||||
|
||||
|
||||
def artifact_manifest(run_id, name='www-provenance', filename='www-provenance.json'):
|
||||
artifacts = api('/actions/runs/' + str(run_id) + '/artifacts')['artifacts']
|
||||
matching = [a for a in artifacts if a.get('name') == name and not a.get('expired', True)]
|
||||
if len(matching) != 1:
|
||||
raise ValueError(f'Exactly one unexpired build provenance artifact required for {name} on run {run_id}, found {len(matching)}')
|
||||
class SameHostRedirect(urllib.request.HTTPRedirectHandler):
|
||||
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
||||
if not newurl.startswith(SERVER + '/'):
|
||||
raise ValueError('Unexpected artifact storage host')
|
||||
return super().redirect_request(req, fp, code, msg, headers, newurl)
|
||||
request = urllib.request.Request(SERVER + '/api/v1/repos/' + REPO + '/actions/artifacts/' + str(matching[0]['id']) + '/zip', headers={'Authorization': 'token ' + os.environ['PROVENANCE_TOKEN']})
|
||||
with urllib.request.build_opener(SameHostRedirect).open(request, timeout=30) as response:
|
||||
payload = response.read(1024 * 1024 + 1)
|
||||
if len(payload) > 1024 * 1024:
|
||||
raise ValueError('Unexpected provenance artifact size')
|
||||
with zipfile.ZipFile(io.BytesIO(payload)) as archive:
|
||||
members = [name for name in archive.namelist() if name.rsplit('/', 1)[-1] == filename]
|
||||
if len(members) != 1:
|
||||
raise ValueError('Missing or ambiguous provenance manifest')
|
||||
if archive.getinfo(members[0]).file_size > 65536:
|
||||
raise ValueError('Unexpected provenance document size')
|
||||
return json.loads(archive.read(members[0]))
|
||||
|
||||
|
||||
def main():
|
||||
sha = os.environ.get('TESTED_SHA', '').strip()
|
||||
digest = os.environ.get('TESTED_DIGEST', '').strip()
|
||||
run_id = os.environ.get('BUILD_RUN_ID', '').strip()
|
||||
release_id = os.environ.get('RELEASE_RUN_ID', '').strip()
|
||||
release_tag = os.environ.get('RELEASE_TAG', '').strip()
|
||||
is_deploy = os.environ.get('IS_DEPLOY', 'false').lower() == 'true' or bool(release_id or release_tag)
|
||||
|
||||
current = api('/actions/runs/' + os.environ['GITHUB_RUN_ID'])
|
||||
if current.get('event') != 'workflow_dispatch' or current.get('head_branch') != 'next':
|
||||
raise ValueError('Approval must be manually dispatched on protected next')
|
||||
actor = current.get('trigger_actor') or current.get('actor') or {}
|
||||
if actor.get('login') != 'deytenit':
|
||||
raise ValueError('Only the repository owner may approve promotion')
|
||||
if not api('/branches/next').get('protected'):
|
||||
raise ValueError('The next branch must be protected before promotion')
|
||||
|
||||
if is_deploy:
|
||||
if not release_id:
|
||||
runs_data = api('/actions/workflows/services-www-release-manual.yml/runs?branch=next&limit=10')
|
||||
candidates = [
|
||||
r for r in runs_data.get('workflow_runs', [])
|
||||
if r.get('status') == 'completed' and r.get('conclusion') == 'success'
|
||||
]
|
||||
if not candidates:
|
||||
raise ValueError('No successful completed run found for services-www-release-manual.yml on next')
|
||||
release_id = str(candidates[0]['id'])
|
||||
|
||||
release = api('/actions/runs/' + release_id)
|
||||
release_actor = release.get('trigger_actor') or release.get('actor') or {}
|
||||
if release.get('conclusion') != 'success' or release.get('status') != 'completed' or release.get('event') != 'workflow_dispatch' or release.get('head_branch') != 'next' or release.get('path') != 'services-www-release-manual.yml@refs/heads/next' or release_actor.get('login') != 'deytenit':
|
||||
raise ValueError('Successful independently approved release required')
|
||||
|
||||
rel_manifest = artifact_manifest(release_id, 'www-release-provenance', 'www-release.json')
|
||||
if not release_tag:
|
||||
release_tag = rel_manifest['release_tag']
|
||||
if not sha:
|
||||
sha = rel_manifest['sha']
|
||||
if not digest:
|
||||
digest = rel_manifest['digest']
|
||||
if not run_id:
|
||||
run_id = str(rel_manifest['run_id'])
|
||||
|
||||
expected_rel = {'sha': sha, 'digest': digest, 'run_id': str(run_id), 'repository': REPO, 'release_tag': release_tag}
|
||||
if any(str(rel_manifest.get(k)) != str(v) for k, v in expected_rel.items()):
|
||||
raise ValueError(f'Release approval does not match requested artifact: got {rel_manifest}, expected {expected_rel}')
|
||||
else:
|
||||
if not sha or not digest or not run_id:
|
||||
runs_data = api('/actions/workflows/services-www-next-required.yml/runs?branch=next&limit=10')
|
||||
candidates = [
|
||||
r for r in runs_data.get('workflow_runs', [])
|
||||
if r.get('status') == 'completed' and r.get('conclusion') == 'success'
|
||||
]
|
||||
if not candidates:
|
||||
raise ValueError('No successful completed run found for services-www-next-required.yml on next')
|
||||
build_run = candidates[0]
|
||||
run_id = str(build_run['id'])
|
||||
sha = build_run['head_sha']
|
||||
build_manifest = artifact_manifest(run_id, 'www-provenance', 'www-provenance.json')
|
||||
digest = build_manifest['digest']
|
||||
|
||||
validate_input(sha, digest, run_id)
|
||||
validate_run(api('/actions/runs/' + run_id), sha)
|
||||
validate_manifest(artifact_manifest(run_id), sha, digest, run_id)
|
||||
|
||||
auth = base64.b64encode(('deytenit:' + os.environ['PROVENANCE_TOKEN']).encode()).decode()
|
||||
subprocess.run(['git', '-c', 'http.https://git.ermnvldmr.com/.extraheader=AUTHORIZATION: basic ' + auth, 'fetch', 'origin', 'next'], check=True)
|
||||
subprocess.run(['git', 'merge-base', '--is-ancestor', sha, 'origin/next'], check=True)
|
||||
subprocess.run(['docker', 'pull', IMAGE + '@' + digest], check=True)
|
||||
data = json.loads(subprocess.check_output(['docker', 'image', 'inspect', IMAGE + '@' + digest], text=True))
|
||||
validate_labels(data[0]['Config'].get('Labels') or {}, sha, run_id)
|
||||
|
||||
with open(os.environ['GITHUB_OUTPUT'], 'a') as stream:
|
||||
stream.write(f'short_sha=sha-{sha[:7]}\n')
|
||||
stream.write(f'tested_sha={sha}\n')
|
||||
stream.write(f'tested_digest={digest}\n')
|
||||
stream.write(f'build_run_id={run_id}\n')
|
||||
if release_tag:
|
||||
stream.write(f'release_tag={release_tag}\n')
|
||||
if release_id:
|
||||
stream.write(f'release_run_id={release_id}\n')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
PNPM_VERSION="${1:-9.12.3}"
|
||||
NODE_VERSION="${2:-20}"
|
||||
|
||||
# Configure pnpm
|
||||
case "$PNPM_VERSION" in
|
||||
8|9|9.12.3) ;;
|
||||
*) echo "Unsupported pnpm version: $PNPM_VERSION" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
PNPM_BIN="/opt/ci/pnpm/$PNPM_VERSION/bin"
|
||||
if [ -d "$PNPM_BIN" ]; then
|
||||
if [ -n "${GITHUB_PATH:-}" ]; then
|
||||
echo "$PNPM_BIN" >> "$GITHUB_PATH"
|
||||
fi
|
||||
export PATH="$PNPM_BIN:$PATH"
|
||||
fi
|
||||
|
||||
# Configure Node.js
|
||||
case "$NODE_VERSION" in
|
||||
18*) major=18 ;;
|
||||
20*) major=20 ;;
|
||||
22*) major=22 ;;
|
||||
*) echo "Unsupported node version: $NODE_VERSION" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
NODE_BIN="/opt/ci/node/$major/bin"
|
||||
if [ -d "$NODE_BIN" ]; then
|
||||
if [ -n "${GITHUB_PATH:-}" ]; then
|
||||
echo "$NODE_BIN" >> "$GITHUB_PATH"
|
||||
fi
|
||||
export PATH="$NODE_BIN:$PATH"
|
||||
fi
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
IMAGE_NAME="${1:-com-ermnvldmr-debian-12-k3s-amd64.qcow2}"
|
||||
mv output-qemu/com-ermnvldmr-debian-12-k3s-amd64.qcow2 "output-qemu/${IMAGE_NAME}"
|
||||
|
||||
if [ -n "${S3_BUCKET:-}" ] && [ -n "${AWS_ACCESS_KEY_ID:-}" ]; then
|
||||
ENDPOINT_FLAG=""
|
||||
[ -n "${S3_ENDPOINT:-}" ] && ENDPOINT_FLAG="--endpoint-url=${S3_ENDPOINT}"
|
||||
echo "Cleaning up older images in dev/images/ to free bucket storage..."
|
||||
aws ${ENDPOINT_FLAG} s3 rm "s3://${S3_BUCKET}/dev/images/" --recursive --exclude "*" --include "com-ermnvldmr-debian-12-k3s-*.qcow2" || true
|
||||
aws ${ENDPOINT_FLAG} s3 cp "output-qemu/${IMAGE_NAME}" "s3://${S3_BUCKET}/dev/images/${IMAGE_NAME}"
|
||||
echo "Golden image uploaded to s3://${S3_BUCKET}/dev/images/${IMAGE_NAME}"
|
||||
else
|
||||
echo "S3 storage secrets not provided; skipping remote S3 upload. Image artifact is preserved in workflow run artifacts."
|
||||
fi
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
codecov:
|
||||
notify:
|
||||
wait_for_ci: true
|
||||
|
||||
ignore:
|
||||
- 'packages/eslint-config'
|
||||
- 'packages/rsbuild-config'
|
||||
- 'packages/storybook-config'
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: 80%
|
||||
threshold: 1%
|
||||
range: 10%
|
||||
stl:
|
||||
target: 80%
|
||||
flags:
|
||||
- stl
|
||||
ui:
|
||||
target: 80%
|
||||
flags:
|
||||
- ui
|
||||
ui-storybook:
|
||||
target: 80%
|
||||
flags:
|
||||
- ui-storybook
|
||||
www:
|
||||
target: 80%
|
||||
flags:
|
||||
- www
|
||||
www-storybook:
|
||||
target: 80%
|
||||
flags:
|
||||
- www-storybook
|
||||
|
||||
flags:
|
||||
stl:
|
||||
paths:
|
||||
- packages/stl
|
||||
ui:
|
||||
paths:
|
||||
- packages/ui
|
||||
ui-storybook:
|
||||
paths:
|
||||
- packages/ui
|
||||
www:
|
||||
paths:
|
||||
- services/www
|
||||
www-storybook:
|
||||
paths:
|
||||
- services/www
|
||||
+4
-1
@@ -14,7 +14,10 @@ export default [
|
||||
// Additional ignores for monorepo
|
||||
{
|
||||
ignores: [
|
||||
'services/docs/**/*', // Hugo content, no linting needed
|
||||
'services/docs/public/**',
|
||||
'services/docs/resources/**',
|
||||
'services/docs/assets/**',
|
||||
'services/docs/archetypes/**',
|
||||
'**/node_modules/**',
|
||||
'**/dist/**',
|
||||
'**/build/**',
|
||||
|
||||
@@ -40,6 +40,6 @@
|
||||
"@types/node": "^22.13.0",
|
||||
"sharp": "^0.35.2",
|
||||
"typescript": "^5.8.2",
|
||||
"vitest": "^4.0.18"
|
||||
"vitest": "^4.1.11"
|
||||
}
|
||||
}
|
||||
@@ -376,4 +376,33 @@ describe('runS3Sync', () => {
|
||||
const content = fs.readFileSync(srcPath, 'utf-8');
|
||||
expect(content).toContain('https://cdn.example.com/www/img.webp');
|
||||
});
|
||||
|
||||
it('should fallback to COM_ERMNVLDMR_STATIC_PUBLIC_URL when publicUrl is not explicitly configured', async () => {
|
||||
process.env.COM_ERMNVLDMR_STATIC_PUBLIC_URL = 'https://static.ermnvldmr.com';
|
||||
|
||||
const staticDir = path.join(tempDir, 'static');
|
||||
fs.mkdirSync(staticDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(staticDir, 'asset.png'), 'png-content');
|
||||
|
||||
const srcDir = path.join(tempDir, 'src');
|
||||
fs.mkdirSync(srcDir, { recursive: true });
|
||||
const srcPath = path.join(srcDir, 'index.ts');
|
||||
fs.writeFileSync(srcPath, 'const img = "/static/asset.png";');
|
||||
|
||||
const cmd: S3SyncCommandConfig = {
|
||||
name: 'sync',
|
||||
description: 'desc',
|
||||
bucket: 'com-ermnvldmr-static',
|
||||
targetPrefix: 'www',
|
||||
sourceDir: './static',
|
||||
type: 's3-sync',
|
||||
};
|
||||
|
||||
await runS3Sync(cmd, {});
|
||||
|
||||
const content = fs.readFileSync(srcPath, 'utf-8');
|
||||
expect(content).toContain('https://static.ermnvldmr.com/www/asset.png');
|
||||
|
||||
delete process.env.COM_ERMNVLDMR_STATIC_PUBLIC_URL;
|
||||
});
|
||||
});
|
||||
@@ -53,6 +53,7 @@ export async function runS3Sync(
|
||||
const bucket = cmd.bucket ?? process.env.S3_BUCKET;
|
||||
const region = cmd.region ?? process.env.AWS_REGION ?? process.env.S3_REGION;
|
||||
const endpoint = cmd.endpoint ?? process.env.S3_ENDPOINT_URL ?? process.env.S3_ENDPOINT;
|
||||
const publicUrl = cmd.publicUrl ?? process.env.COM_ERMNVLDMR_STATIC_PUBLIC_URL ?? process.env.S3_PUBLIC_URL;
|
||||
const accessKey = cmd.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID ?? process.env.S3_ACCESS_KEY_ID;
|
||||
const secretKey = cmd.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY ?? process.env.S3_SECRET_ACCESS_KEY;
|
||||
|
||||
@@ -149,8 +150,8 @@ export async function runS3Sync(
|
||||
*/
|
||||
const getS3Url = (finalName: string): string => {
|
||||
const normalizedKey = normalizeKey(cmd.targetPrefix, finalName);
|
||||
if (cmd.publicUrl) {
|
||||
const cleanPublicUrl = cmd.publicUrl.replace(/\/+$/, '');
|
||||
if (publicUrl) {
|
||||
const cleanPublicUrl = publicUrl.replace(/\/+$/, '');
|
||||
return `${cleanPublicUrl}/${normalizedKey}`;
|
||||
}
|
||||
if (bucket.includes('.')) {
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "node --test src/lib/rules/no-hanging-particles.test.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@eslint/js": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// @ts-check
|
||||
import { i18nPlugin } from '../plugins/i18n.js';
|
||||
import { markdownParser } from '../parsers/markdown.js';
|
||||
|
||||
/**
|
||||
* i18n typography and validation ESLint configuration.
|
||||
*/
|
||||
export const i18nConfig = [
|
||||
{
|
||||
files: ['**/*.i18n.{ts,tsx,js,jsx,mjs}'],
|
||||
plugins: {
|
||||
i18n: i18nPlugin,
|
||||
},
|
||||
rules: {
|
||||
'i18n/no-hanging-particles': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.{md,mdx}'],
|
||||
languageOptions: {
|
||||
parser: markdownParser,
|
||||
},
|
||||
plugins: {
|
||||
i18n: i18nPlugin,
|
||||
},
|
||||
rules: {
|
||||
'no-irregular-whitespace': 'off',
|
||||
'i18n/no-hanging-particles': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -6,6 +6,7 @@ import { jsdocConfig } from './jsdoc.js';
|
||||
import { testingConfig } from './testing.js';
|
||||
import { toolingConfig } from './tooling.js';
|
||||
import { defineMonorepoRestrictedImports } from './monorepo.js';
|
||||
import { i18nConfig } from './i18n.js';
|
||||
|
||||
/**
|
||||
* Common ignores for the entire monorepo.
|
||||
@@ -38,6 +39,9 @@ export function defineBaseConfig(rootDir) {
|
||||
...jsdocConfig,
|
||||
...testingConfig,
|
||||
...toolingConfig,
|
||||
...i18nConfig,
|
||||
...defineMonorepoRestrictedImports(rootDir),
|
||||
];
|
||||
}
|
||||
|
||||
export { i18nConfig };
|
||||
@@ -1,6 +1,7 @@
|
||||
// @ts-check
|
||||
import path from 'node:path';
|
||||
import { defineBaseConfig } from './config/index.js';
|
||||
import { defineBaseConfig, i18nConfig } from './config/index.js';
|
||||
import { i18nPlugin } from './plugins/i18n.js';
|
||||
|
||||
// Resolve the monorepo root relative to this library's location
|
||||
const rootDir = path.resolve(import.meta.dirname, '../../../../');
|
||||
@@ -10,4 +11,4 @@ const rootDir = path.resolve(import.meta.dirname, '../../../../');
|
||||
*/
|
||||
export const baseConfig = defineBaseConfig(rootDir);
|
||||
|
||||
export { defineBaseConfig };
|
||||
export { defineBaseConfig, i18nConfig, i18nPlugin };
|
||||
@@ -0,0 +1,61 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* Calculates start and end line/column locations for a string offset.
|
||||
*
|
||||
* @param {string} code - Entire source code.
|
||||
* @returns {import('eslint').AST.SourceLocation} Full document SourceLocation.
|
||||
*/
|
||||
function getDocumentLocation(code) {
|
||||
const lines = code.split('\n');
|
||||
const lastLine = lines.length;
|
||||
const lastColumn = lines[lines.length - 1].length;
|
||||
|
||||
return {
|
||||
start: { line: 1, column: 0 },
|
||||
end: { line: lastLine, column: lastColumn },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom lightweight parser for Markdown and MDX documents.
|
||||
* Produces an AST that enables typography linting and autofixing on prose text.
|
||||
*/
|
||||
export const markdownParser = {
|
||||
meta: {
|
||||
name: 'eslint-parser-markdown-typography',
|
||||
version: '1.0.0',
|
||||
},
|
||||
parseForESLint(code, options = {}) {
|
||||
const filePath = options.filePath || '';
|
||||
const isRu = /(?:^|[\\/])ru(?:[\\/]|\.ru\.)/i.test(filePath);
|
||||
const isEn = /(?:^|[\\/])en(?:[\\/]|\.en\.)/i.test(filePath);
|
||||
const locale = isRu ? 'ru' : isEn ? 'en' : undefined;
|
||||
|
||||
const loc = getDocumentLocation(code);
|
||||
|
||||
return {
|
||||
ast: {
|
||||
type: 'Program',
|
||||
range: [0, code.length],
|
||||
loc,
|
||||
body: [
|
||||
{
|
||||
type: 'MarkdownDocument',
|
||||
range: [0, code.length],
|
||||
loc,
|
||||
value: code,
|
||||
locale,
|
||||
filePath,
|
||||
},
|
||||
],
|
||||
tokens: [],
|
||||
comments: [],
|
||||
},
|
||||
visitorKeys: {
|
||||
Program: ['body'],
|
||||
MarkdownDocument: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
// @ts-check
|
||||
import { noHangingParticlesRule } from '../rules/no-hanging-particles.js';
|
||||
|
||||
/**
|
||||
* ESLint plugin providing i18n translation validation and typography rules.
|
||||
*/
|
||||
export const i18nPlugin = {
|
||||
meta: {
|
||||
name: 'eslint-plugin-i18n',
|
||||
version: '1.0.0',
|
||||
},
|
||||
rules: {
|
||||
'no-hanging-particles': noHangingParticlesRule,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,310 @@
|
||||
// @ts-check
|
||||
|
||||
const RU_PARTICLES = [
|
||||
'[а-яё]',
|
||||
'во', 'со', 'ко', 'до', 'по', 'на', 'из', 'за', 'от', 'об', 'не', 'ни', 'но', 'да', 'то', 'уж',
|
||||
'обо', 'под', 'над', 'при', 'про', 'для', 'без', 'или', 'как', 'что', 'еще', 'ещё', 'уже',
|
||||
'через', 'сквозь', 'перед', 'из-за', 'из-под',
|
||||
].join('|');
|
||||
|
||||
const EN_PARTICLES = [
|
||||
'a', 'an', 'the',
|
||||
'in', 'on', 'at', 'to', 'of', 'by', 'for', 'up', 'as', 'if', 'is', 'it', 'no',
|
||||
'and', 'or', 'nor', 'but', 'yet', 'so', 'with', 'into', 'from',
|
||||
].join('|');
|
||||
|
||||
const RU_TRAILING = ['ли', 'ль', 'же', 'ж', 'бы', 'б'].join('|');
|
||||
|
||||
const KNOWN_LOCALES = new Set(['en', 'ru']);
|
||||
const PLURAL_KEYS = new Set(['zero', 'one', 'two', 'few', 'many', 'other']);
|
||||
|
||||
/**
|
||||
* Applies non-breaking space typography rules to a translation string value.
|
||||
*
|
||||
* @param {string} input - The raw translation string.
|
||||
* @param {string} [locale] - The locale code ('en' | 'ru').
|
||||
* @returns {string} The transformed string with non-breaking spaces.
|
||||
*/
|
||||
export function applyTypography(input, locale) {
|
||||
let text = input.replaceAll(' ', '\u00A0');
|
||||
|
||||
/** @type {string[]} */
|
||||
const protectedTokens = [];
|
||||
text = text.replace(/\{[^}]+\}|<[^>]+>|\]\([^)]+\)/g, (match) => {
|
||||
const idx = protectedTokens.length;
|
||||
protectedTokens.push(match);
|
||||
return `__TYPO_PROT_${idx}__`;
|
||||
});
|
||||
|
||||
// Em-dash and en-dash (prepend non-breaking space)
|
||||
text = text.replace(/(?<=\S)[\t \u00A0]+([—–])[\t ]+/g, '\u00A0$1 ');
|
||||
|
||||
// Numbers & units/symbols
|
||||
text = text.replace(/(?<=\d)[\t ]+(?=[a-zA-Zа-яА-ЯёЁ%№$€₽]|__TYPO_PROT_)/g, '\u00A0');
|
||||
text = text.replace(/(?<=[№§$€₽])[\t ]+(?=\d|__TYPO_PROT_)/g, '\u00A0');
|
||||
|
||||
// Trailing particles (Russian)
|
||||
if (!locale || locale === 'ru') {
|
||||
const trailingRegex = new RegExp(`(?<=[а-яА-ЯёЁ\\w])\\s+(${RU_TRAILING})(?=[\\s\\u00A0.,!?:;»"')\\]]|$)`, 'gi');
|
||||
text = text.replace(trailingRegex, '\u00A0$1');
|
||||
}
|
||||
|
||||
// Leading particles (RU / EN)
|
||||
const particles = locale === 'en' ? EN_PARTICLES : locale === 'ru' ? RU_PARTICLES : `${RU_PARTICLES}|${EN_PARTICLES}`;
|
||||
const leadingRegex = new RegExp(`(?<=^|[\\s\\u00A0.,!?:;«"'(])(${particles})[ \\t]+(?=\\S)`, 'gi');
|
||||
text = text.replace(leadingRegex, '$1\u00A0');
|
||||
|
||||
// Restore protected tokens
|
||||
text = text.replace(/__TYPO_PROT_(\d+)__/g, (_, idx) => protectedTokens[Number(idx)]);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Masks frontmatter non-title/description lines.
|
||||
*
|
||||
* @param {string} text - Raw document text.
|
||||
* @param {(str: string) => string} mask - Masking function.
|
||||
* @returns {string} Text with frontmatter metadata masked.
|
||||
*/
|
||||
function maskFrontmatter(text, mask) {
|
||||
if (!text.startsWith('---')) return text;
|
||||
const endIdx = text.indexOf('\n---', 3);
|
||||
if (endIdx === -1) return text;
|
||||
const fm = text.slice(0, endIdx + 4);
|
||||
const rest = text.slice(endIdx + 4);
|
||||
|
||||
const lines = fm.split('\n');
|
||||
const processedLines = lines.map((line, idx) => {
|
||||
if (idx === 0 || idx === lines.length - 1) return mask(line);
|
||||
const trimmed = line.trim();
|
||||
if (trimmed.startsWith('title:') || trimmed.startsWith('description:')) {
|
||||
return line;
|
||||
}
|
||||
return mask(line);
|
||||
});
|
||||
return processedLines.join('\n') + rest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies typography rules to a Markdown/MDX document, protecting code blocks,
|
||||
* inline code, shortcodes, link URLs, HTML tags, and frontmatter metadata.
|
||||
*
|
||||
* @param {string} content - Markdown document content.
|
||||
* @param {string} [locale] - The document locale ('en' | 'ru').
|
||||
* @returns {string} Transformed markdown content.
|
||||
*/
|
||||
export function applyMarkdownTypography(content, locale) {
|
||||
/** @type {string[]} */
|
||||
const protectedTokens = [];
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function mask(str) {
|
||||
const idx = protectedTokens.length;
|
||||
protectedTokens.push(str);
|
||||
return `__MD_PROT_${idx}__`;
|
||||
}
|
||||
|
||||
let text = maskFrontmatter(content, mask);
|
||||
|
||||
// Mask fenced code blocks ```...``` and ~~~...~~~
|
||||
text = text.replace(/```[\s\S]*?```|~~~[\s\S]*?~~~/g, mask);
|
||||
// Mask Hugo shortcodes {{< ... >}} or {{% ... %}}
|
||||
text = text.replace(/\{\{[<%][\s\S]*?[>%]\}\}/g, mask);
|
||||
// Mask inline code `...`
|
||||
text = text.replace(/`[^`\n]+`/g, mask);
|
||||
// Mask Markdown link targets ](...)
|
||||
text = text.replace(/\]\([^)]+\)/g, mask);
|
||||
// Mask HTML tags
|
||||
text = text.replace(/<[^>]+>/g, mask);
|
||||
|
||||
// Apply typography rules
|
||||
text = applyTypography(text, locale);
|
||||
|
||||
// Restore protected tokens
|
||||
text = text.replace(/__MD_PROT_(\d+)__/g, (_, idx) => protectedTokens[Number(idx)]);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a string into a valid JS string literal with specified quote.
|
||||
*
|
||||
* @param {string} str - String to escape and wrap.
|
||||
* @param {string} quote - Quote character (' or ").
|
||||
* @returns {string} Serialized literal.
|
||||
*/
|
||||
function serializeStringLiteral(str, quote) {
|
||||
const escaped = str
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/\r/g, '\\r')
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\t/g, '\\t')
|
||||
.replace(new RegExp(quote, 'g'), `\\${quote}`);
|
||||
return `${quote}${escaped}${quote}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts property name from a property node.
|
||||
*
|
||||
* @param {any} prop - Property AST node.
|
||||
* @returns {string | null} Property name or null.
|
||||
*/
|
||||
function getPropertyName(prop) {
|
||||
if (!prop || !prop.key) return null;
|
||||
if (prop.key.type === 'Identifier') return prop.key.name;
|
||||
if (prop.key.type === 'Literal') return String(prop.key.value);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* ESLint rule module for enforcing non-breaking spaces in i18n translation values
|
||||
* and Markdown/MDX documentation.
|
||||
*
|
||||
* @type {import('eslint').Rule.RuleModule}
|
||||
*/
|
||||
export const noHangingParticlesRule = {
|
||||
meta: {
|
||||
type: 'layout',
|
||||
docs: {
|
||||
description: 'Enforce non-breaking spaces for hanging particles and numbers in i18n translation values and markdown documentation.',
|
||||
recommended: true,
|
||||
},
|
||||
fixable: 'whitespace',
|
||||
schema: [],
|
||||
messages: {
|
||||
hangingParticle: 'Translation value contains hanging particles or numbers that should be bound with a non-breaking space (\\u00A0).',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
/**
|
||||
* Checks and fixes a single string literal node.
|
||||
*
|
||||
* @param {any} node - String Literal AST node.
|
||||
* @param {string} [locale] - The locale code if known.
|
||||
*/
|
||||
function checkStringLiteral(node, locale) {
|
||||
if (!node || typeof node.value !== 'string') return;
|
||||
const original = node.value;
|
||||
const formatted = applyTypography(original, locale);
|
||||
|
||||
if (original !== formatted) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'hangingParticle',
|
||||
fix(fixer) {
|
||||
const raw = node.raw || '';
|
||||
const quote = raw.startsWith('"') ? '"' : "'";
|
||||
return fixer.replaceText(node, serializeStringLiteral(formatted, quote));
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects a locale entry inside a translation item.
|
||||
*
|
||||
* @param {any} prop - Locale property node (e.g. en or ru).
|
||||
*/
|
||||
function processLocaleEntry(prop) {
|
||||
const locale = getPropertyName(prop);
|
||||
if (!locale || !KNOWN_LOCALES.has(locale)) return;
|
||||
|
||||
const valueNode = prop.value;
|
||||
if (!valueNode) return;
|
||||
|
||||
if (valueNode.type === 'Literal') {
|
||||
checkStringLiteral(valueNode, locale);
|
||||
} else if (valueNode.type === 'ObjectExpression' && Array.isArray(valueNode.properties)) {
|
||||
for (const pluralProp of valueNode.properties) {
|
||||
if (pluralProp.type === 'Property') {
|
||||
const pluralKey = getPropertyName(pluralProp);
|
||||
if (pluralKey && PLURAL_KEYS.has(pluralKey) && pluralProp.value?.type === 'Literal') {
|
||||
checkStringLiteral(pluralProp.value, locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes keyset object dictionary properties.
|
||||
*
|
||||
* @param {any} objectNode - Keyset ObjectExpression AST node.
|
||||
*/
|
||||
function processKeysetObject(objectNode) {
|
||||
if (!objectNode || !Array.isArray(objectNode.properties)) return;
|
||||
|
||||
for (const dictEntry of objectNode.properties) {
|
||||
if (
|
||||
dictEntry.type === 'Property' &&
|
||||
dictEntry.value &&
|
||||
dictEntry.value.type === 'ObjectExpression' &&
|
||||
Array.isArray(dictEntry.value.properties)
|
||||
) {
|
||||
for (const localeProp of dictEntry.value.properties) {
|
||||
if (localeProp.type === 'Property') {
|
||||
processLocaleEntry(localeProp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
if (
|
||||
node.callee &&
|
||||
node.callee.type === 'Identifier' &&
|
||||
node.callee.name === 'createKeyset' &&
|
||||
node.arguments.length > 0 &&
|
||||
node.arguments[0].type === 'ObjectExpression'
|
||||
) {
|
||||
processKeysetObject(node.arguments[0]);
|
||||
}
|
||||
},
|
||||
|
||||
MarkdownDocument(node) {
|
||||
if (!node || typeof node.value !== 'string') return;
|
||||
const original = node.value;
|
||||
const formatted = applyMarkdownTypography(original, node.locale);
|
||||
|
||||
if (original === formatted) return;
|
||||
|
||||
const origLines = original.split('\n');
|
||||
const formLines = formatted.split('\n');
|
||||
|
||||
let reported = false;
|
||||
for (let i = 0; i < origLines.length; i++) {
|
||||
const origLine = origLines[i];
|
||||
const formLine = formLines[i];
|
||||
if (origLine !== formLine) {
|
||||
let diffCol = 0;
|
||||
while (
|
||||
diffCol < origLine.length &&
|
||||
diffCol < formLine.length &&
|
||||
origLine[diffCol] === formLine[diffCol]
|
||||
) {
|
||||
diffCol++;
|
||||
}
|
||||
|
||||
const isFirst = !reported;
|
||||
reported = true;
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: { line: i + 1, column: diffCol },
|
||||
end: { line: i + 1, column: diffCol + 1 },
|
||||
},
|
||||
messageId: 'hangingParticle',
|
||||
fix: isFirst ? (fixer) => fixer.replaceText(node, formatted) : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,251 @@
|
||||
// @ts-check
|
||||
import { describe, it } from 'node:test';
|
||||
import { RuleTester } from 'eslint';
|
||||
import { noHangingParticlesRule } from './no-hanging-particles.js';
|
||||
import { markdownParser } from '../parsers/markdown.js';
|
||||
|
||||
// Configure RuleTester for node:test
|
||||
RuleTester.describe = describe;
|
||||
RuleTester.it = it;
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
ruleTester.run('no-hanging-particles', noHangingParticlesRule, {
|
||||
valid: [
|
||||
{
|
||||
code: `
|
||||
createKeyset({
|
||||
'Yet capturing context': {
|
||||
en: 'Yet\\u00A0capturing context and\\u00A0architectural decisions',
|
||||
ru: 'Мои мысли и\\u00A0проекты',
|
||||
},
|
||||
});
|
||||
`,
|
||||
},
|
||||
{
|
||||
// Keys should not trigger errors even if they contain particles
|
||||
code: `
|
||||
createKeyset({
|
||||
'и иные проблемы': {
|
||||
en: 'and\\u00A0other issues',
|
||||
ru: 'и\\u00A0иные проблемы',
|
||||
},
|
||||
});
|
||||
`,
|
||||
},
|
||||
{
|
||||
// Manual {nbsp} token is allowed
|
||||
code: `
|
||||
createKeyset({
|
||||
manual: {
|
||||
en: 'Yet{nbsp}capturing',
|
||||
ru: 'и{nbsp}иные',
|
||||
},
|
||||
});
|
||||
`,
|
||||
},
|
||||
{
|
||||
// Not a createKeyset call
|
||||
code: `
|
||||
const regularObj = {
|
||||
en: 'Yet capturing without keyset',
|
||||
};
|
||||
`,
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
// English leading particle
|
||||
code: `
|
||||
createKeyset({
|
||||
key: {
|
||||
en: 'Yet capturing context',
|
||||
ru: 'Фиксация контекста',
|
||||
},
|
||||
});
|
||||
`,
|
||||
output: `
|
||||
createKeyset({
|
||||
key: {
|
||||
en: 'Yet\u00A0capturing context',
|
||||
ru: 'Фиксация контекста',
|
||||
},
|
||||
});
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
messageId: 'hangingParticle',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
// Russian leading particle
|
||||
code: `
|
||||
createKeyset({
|
||||
'и иные проблемы': {
|
||||
en: 'and other issues',
|
||||
ru: 'и иные проблемы',
|
||||
},
|
||||
});
|
||||
`,
|
||||
output: `
|
||||
createKeyset({
|
||||
'и иные проблемы': {
|
||||
en: 'and\u00A0other issues',
|
||||
ru: 'и\u00A0иные проблемы',
|
||||
},
|
||||
});
|
||||
`,
|
||||
errors: [
|
||||
{ messageId: 'hangingParticle' },
|
||||
{ messageId: 'hangingParticle' },
|
||||
],
|
||||
},
|
||||
{
|
||||
// Interpolation placeholder & trailing particles & dashes & numbers
|
||||
code: `
|
||||
createKeyset({
|
||||
welcome: {
|
||||
en: 'Inspect the {repositoryLink} for 10 items — all good',
|
||||
ru: 'Загляните в {repositoryLink}, как же так — это 10 вещей в 2026 году',
|
||||
},
|
||||
});
|
||||
`,
|
||||
output: `
|
||||
createKeyset({
|
||||
welcome: {
|
||||
en: 'Inspect the\u00A0{repositoryLink} for\u00A010\u00A0items\u00A0— all good',
|
||||
ru: 'Загляните в\u00A0{repositoryLink}, как\u00A0же так\u00A0— это 10\u00A0вещей в\u00A02026\u00A0году',
|
||||
},
|
||||
});
|
||||
`,
|
||||
errors: [
|
||||
{ messageId: 'hangingParticle' },
|
||||
{ messageId: 'hangingParticle' },
|
||||
],
|
||||
},
|
||||
{
|
||||
// HTML entity replacement
|
||||
code: `
|
||||
createKeyset({
|
||||
test: {
|
||||
en: 'Text with entity',
|
||||
ru: 'Текст с сущностью',
|
||||
},
|
||||
});
|
||||
`,
|
||||
output: `
|
||||
createKeyset({
|
||||
test: {
|
||||
en: 'Text\u00A0with\u00A0entity',
|
||||
ru: 'Текст\u00A0с\u00A0сущностью',
|
||||
},
|
||||
});
|
||||
`,
|
||||
errors: [
|
||||
{ messageId: 'hangingParticle' },
|
||||
{ messageId: 'hangingParticle' },
|
||||
],
|
||||
},
|
||||
{
|
||||
// Plural forms
|
||||
code: `
|
||||
createKeyset({
|
||||
counter: {
|
||||
en: {
|
||||
one: '{count} item',
|
||||
other: '{count} items in stock',
|
||||
},
|
||||
ru: {
|
||||
one: '{count} штука в наличии',
|
||||
few: '{count} штуки в наличии',
|
||||
many: '{count} штук в наличии',
|
||||
other: '{count} штук в наличии',
|
||||
},
|
||||
},
|
||||
});
|
||||
`,
|
||||
output: `
|
||||
createKeyset({
|
||||
counter: {
|
||||
en: {
|
||||
one: '{count} item',
|
||||
other: '{count} items in\u00A0stock',
|
||||
},
|
||||
ru: {
|
||||
one: '{count} штука в\u00A0наличии',
|
||||
few: '{count} штуки в\u00A0наличии',
|
||||
many: '{count} штук в\u00A0наличии',
|
||||
other: '{count} штук в\u00A0наличии',
|
||||
},
|
||||
},
|
||||
});
|
||||
`,
|
||||
errors: [
|
||||
{ messageId: 'hangingParticle' },
|
||||
{ messageId: 'hangingParticle' },
|
||||
{ messageId: 'hangingParticle' },
|
||||
{ messageId: 'hangingParticle' },
|
||||
{ messageId: 'hangingParticle' },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const markdownRuleTester = new RuleTester({
|
||||
languageOptions: {
|
||||
parser: markdownParser,
|
||||
},
|
||||
});
|
||||
|
||||
markdownRuleTester.run('no-hanging-particles (markdown)', noHangingParticlesRule, {
|
||||
valid: [
|
||||
{
|
||||
code: '# Title\n\nYet\\u00A0capturing context\\u00A0— for\\u00A010\\u00A0items',
|
||||
filename: 'content/en/overview.md',
|
||||
},
|
||||
{
|
||||
// Code blocks should be protected
|
||||
code: '```ts\nconst in = 10;\n```',
|
||||
filename: 'content/en/guide.md',
|
||||
},
|
||||
{
|
||||
// Inline code and link targets should be protected
|
||||
code: 'Use `in` keyword here [link](https://example.com/in/path).',
|
||||
filename: 'content/en/guide.md',
|
||||
},
|
||||
{
|
||||
// Hugo shortcodes should be protected
|
||||
code: '{{< card link="apex" title="apex" >}}',
|
||||
filename: 'content/en/guide.md',
|
||||
},
|
||||
{
|
||||
// Frontmatter tags/date should be protected
|
||||
code: '---\ntitle: "Title"\ntags: [a, in, on]\n---\nBody text',
|
||||
filename: 'content/en/guide.md',
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: '# Title\n\nYet capturing context — for 10 items',
|
||||
output: '# Title\n\nYet\u00A0capturing context\u00A0— for\u00A010\u00A0items',
|
||||
filename: 'content/en/overview.md',
|
||||
errors: [{ messageId: 'hangingParticle' }],
|
||||
},
|
||||
{
|
||||
code: 'Это руководство для заметок с нуля',
|
||||
output: 'Это руководство для\u00A0заметок с\u00A0нуля',
|
||||
filename: 'content/ru/overview.md',
|
||||
errors: [{ messageId: 'hangingParticle' }],
|
||||
},
|
||||
{
|
||||
code: '---\ntitle: "Руководство по развертыванию"\ntags: [a, in]\n---\nС нуля',
|
||||
output: '---\ntitle: "Руководство по\u00A0развертыванию"\ntags: [a, in]\n---\nС\u00A0нуля',
|
||||
filename: 'content/ru/guide.md',
|
||||
errors: [
|
||||
{ messageId: 'hangingParticle' },
|
||||
{ messageId: 'hangingParticle' },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -45,6 +45,6 @@
|
||||
"@vitest/coverage-v8": "^4.0.18",
|
||||
"react": "^19.1.0",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^4.0.18"
|
||||
"vitest": "^4.1.11"
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@
|
||||
"storybook": "^8.6.17",
|
||||
"storybook-react-rsbuild": "^1.0.3",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^4.0.18",
|
||||
"vitest": "^4.1.11",
|
||||
"wait-on": "^8.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -76,7 +76,7 @@ export function PageHead({
|
||||
|
||||
useEffect(() => {
|
||||
if (isCollapsible) {
|
||||
setIsCollapsed(scrollY > 200);
|
||||
setIsCollapsed(scrollY > 150);
|
||||
}
|
||||
}, [scrollY, isCollapsible]);
|
||||
|
||||
@@ -91,13 +91,13 @@ export function PageHead({
|
||||
<HStack align="center" className="min-h-[60px] py-2.5" gap={4} justify="between" wrap="wrap">
|
||||
<HStack align="center" className="flex-1" gap={4}>
|
||||
{addonLeft && <div className="flex-shrink-0 whitespace-nowrap">{addonLeft}</div>}
|
||||
{breadcrumbs && <div className="hidden sm:block">{breadcrumbs}</div>}
|
||||
{breadcrumbs && <div className="hidden sm:block text-label-l [&_ol]:text-label-l">{breadcrumbs}</div>}
|
||||
</HStack>
|
||||
{addonRight && <div className="flex-shrink-0">{addonRight}</div>}
|
||||
</HStack>
|
||||
</PageContainer>
|
||||
)}
|
||||
<PageContainer className={cn(hasTopBar ? 'pt-4 pb-8 sm:pt-6 sm:pb-12' : 'py-8 sm:py-12')}>
|
||||
<PageContainer className={cn(hasTopBar ? 'pt-2.5 pb-8 sm:pt-3.5 sm:pb-12' : 'pt-5 pb-8 sm:pt-9 sm:pb-12')}>
|
||||
<VStack gap={4}>
|
||||
<Header level={1}>{heading}</Header>
|
||||
{typeof caption === 'string' ? (
|
||||
@@ -117,7 +117,7 @@ export function PageHead({
|
||||
<HStack align="center" className="min-h-[60px] py-2.5" gap={4} justify="between" wrap="wrap">
|
||||
<HStack align="center" className="flex-1" gap={4}>
|
||||
{addonLeft && <div className="flex-shrink-0 whitespace-nowrap">{addonLeft}</div>}
|
||||
{breadcrumbs && <div className="hidden sm:block">{breadcrumbs}</div>}
|
||||
{breadcrumbs && <div className="hidden sm:block text-label-l [&_ol]:text-label-l">{breadcrumbs}</div>}
|
||||
<Header
|
||||
className={cn('min-w-0', breadcrumbs && 'sm:hidden')}
|
||||
level={4}
|
||||
|
||||
@@ -27,6 +27,6 @@
|
||||
"react-dom": "^19.1.0",
|
||||
"typescript": "^5.8.2",
|
||||
"vite-tsconfig-paths": "^6.1.1",
|
||||
"vitest": "^4.0.18"
|
||||
"vitest": "^4.1.11"
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,6 @@ export function defineServiceConfig(config: RsbuildConfig = {}): RsbuildConfig {
|
||||
distPath: {
|
||||
font: 'static/font',
|
||||
},
|
||||
filename: {
|
||||
font: '[name][ext]',
|
||||
},
|
||||
},
|
||||
html: {
|
||||
template: './src/static/index.html',
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
ensureHostsEntry,
|
||||
findWorkspaceRoot,
|
||||
hasHostsEntry,
|
||||
loadDevRootCaKey,
|
||||
localDevServer,
|
||||
} from './server.js';
|
||||
|
||||
@@ -21,9 +22,45 @@ describe('server dev configuration', () => {
|
||||
const certs = ensureDevCertificates();
|
||||
expect(existsSync(certs.certPath)).toBe(true);
|
||||
expect(existsSync(certs.keyPath)).toBe(true);
|
||||
expect(certs.caCertPath).toBeDefined();
|
||||
if (certs.caCertPath) {
|
||||
expect(existsSync(certs.caCertPath)).toBe(true);
|
||||
expect(certs.caCertPath).toContain('ErmnvldmrDevelopmentRootCA.pem');
|
||||
}
|
||||
expect(certs.certPath).toContain('.certs/localhost.ermnvldmr.com.pem');
|
||||
});
|
||||
|
||||
it('loads dev root CA key from environment variable when present', () => {
|
||||
const prevKey = process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY;
|
||||
try {
|
||||
process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY = 'mock-key-value\\nwith-newline';
|
||||
const key = loadDevRootCaKey();
|
||||
expect(key).toBe('mock-key-value\nwith-newline');
|
||||
} finally {
|
||||
if (prevKey !== undefined) {
|
||||
process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY = prevKey;
|
||||
} else {
|
||||
delete process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('loads dev root CA key from .env.dev when available', () => {
|
||||
const prevKey = process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY;
|
||||
delete process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY;
|
||||
try {
|
||||
const key = loadDevRootCaKey();
|
||||
expect(key).toBeDefined();
|
||||
if (key) {
|
||||
expect(key).toContain('BEGIN PRIVATE KEY');
|
||||
}
|
||||
} finally {
|
||||
if (prevKey !== undefined) {
|
||||
process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY = prevKey;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('returns production configuration without https when NODE_ENV is production', () => {
|
||||
const prevEnv = process.env.NODE_ENV;
|
||||
try {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from 'node:fs';
|
||||
import { homedir, tmpdir } from 'node:os';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
import type { ServerConfig } from '@rsbuild/core';
|
||||
|
||||
@@ -39,6 +40,10 @@ export interface DevCertificates {
|
||||
* Absolute path to the PEM private key file.
|
||||
*/
|
||||
keyPath: string;
|
||||
/**
|
||||
* Absolute path to the PEM Root CA certificate file.
|
||||
*/
|
||||
caCertPath?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,11 +151,76 @@ export function findWorkspaceRoot(startDir: string = process.cwd()): string {
|
||||
return startDir;
|
||||
}
|
||||
|
||||
interface DotenvxModule {
|
||||
config: (options: { path?: string; envKeysFile?: string }) => void;
|
||||
}
|
||||
|
||||
function isDotenvxModule(val: unknown): val is DotenvxModule {
|
||||
return (
|
||||
typeof val === 'object' &&
|
||||
val !== null &&
|
||||
'config' in val &&
|
||||
typeof val.config === 'function'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the development Root CA private key from the environment or .env.dev.
|
||||
*
|
||||
* @param workspaceRoot - Absolute path to workspace root
|
||||
* @returns The PEM private key string, or undefined if unavailable
|
||||
*/
|
||||
export function loadDevRootCaKey(
|
||||
workspaceRoot: string = findWorkspaceRoot()
|
||||
): string | undefined {
|
||||
if (process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY) {
|
||||
const raw = process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY;
|
||||
return raw.includes('\\n') ? raw.replace(/\\n/g, '\n') : raw;
|
||||
}
|
||||
|
||||
const envDevPath = join(workspaceRoot, '.env.dev');
|
||||
const envKeysPath = join(workspaceRoot, '.env.keys');
|
||||
|
||||
if (!existsSync(envDevPath)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const candidatePackages = [
|
||||
join(workspaceRoot, 'packages/cli/package.json'),
|
||||
join(workspaceRoot, 'package.json'),
|
||||
];
|
||||
|
||||
for (const pkgPath of candidatePackages) {
|
||||
if (!existsSync(pkgPath)) continue;
|
||||
try {
|
||||
const req = createRequire(pkgPath);
|
||||
const dotenvx: unknown = req('@dotenvx/dotenvx');
|
||||
if (isDotenvxModule(dotenvx)) {
|
||||
dotenvx.config({ path: envDevPath, envKeysFile: envKeysPath });
|
||||
const key = process.env.COM_ERMNVLDMR_DEV_ROOT_CA_KEY;
|
||||
if (key) {
|
||||
return key.includes('\\n') ? key.replace(/\\n/g, '\n') : key;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Continue to next candidate package
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote public URL for the Ermnvldmr development root CA certificate.
|
||||
*/
|
||||
export const REMOTE_ROOT_CA_URL =
|
||||
'https://static.ermnvldmr.com/dev/ssl/certs/ErmnvldmrDevelopmentRootCA.pem';
|
||||
|
||||
/**
|
||||
* Ensures that valid TLS development certificates exist for localhost.ermnvldmr.com.
|
||||
*
|
||||
* Checks for certificates in the workspace `.certs` directory. If absent, generates
|
||||
* new certificates signed by either an existing mkcert/Hugo root CA or an openssl-generated CA.
|
||||
* Checks for certificates in the workspace `.certs` directory. If absent, downloads
|
||||
* or uses ErmnvldmrDevelopmentRootCA.pem and generates certificates signed by it.
|
||||
*
|
||||
* @param workspaceRoot - Absolute path to workspace root
|
||||
* @returns Paths to the certificate and private key files
|
||||
@@ -161,128 +231,101 @@ export function ensureDevCertificates(
|
||||
const certsDir = join(workspaceRoot, '.certs');
|
||||
const certPath = join(certsDir, 'localhost.ermnvldmr.com.pem');
|
||||
const keyPath = join(certsDir, 'localhost.ermnvldmr.com-key.pem');
|
||||
const caCertPath = join(certsDir, 'ErmnvldmrDevelopmentRootCA.pem');
|
||||
const caKeyPath = join(certsDir, 'ErmnvldmrDevelopmentRootCA-key.pem');
|
||||
|
||||
ensureHostsEntry('localhost.ermnvldmr.com', '127.0.0.1');
|
||||
|
||||
if (existsSync(certPath) && existsSync(keyPath)) {
|
||||
return { certPath, keyPath };
|
||||
}
|
||||
const home = homedir();
|
||||
const globalCaDir = join(home, '.local/share/mkcert');
|
||||
const globalCaCert = join(globalCaDir, 'ErmnvldmrDevelopmentRootCA.pem');
|
||||
const globalCaKey = join(globalCaDir, 'ErmnvldmrDevelopmentRootCA-key.pem');
|
||||
|
||||
mkdirSync(certsDir, { recursive: true, mode: 0o700 });
|
||||
|
||||
const home = homedir();
|
||||
const hugoCaCert = join(home, '.local/share/mkcert/rootCA.pem');
|
||||
const hugoCaKey = join(home, '.local/share/mkcert/rootCA-key.pem');
|
||||
|
||||
let hasMkcertCli = false;
|
||||
try {
|
||||
execFileSync('mkcert', ['-help'], { stdio: 'ignore' });
|
||||
hasMkcertCli = true;
|
||||
} catch {
|
||||
hasMkcertCli = false;
|
||||
}
|
||||
|
||||
if (hasMkcertCli) {
|
||||
execFileSync(
|
||||
'mkcert',
|
||||
[
|
||||
'-cert-file',
|
||||
certPath,
|
||||
'-key-file',
|
||||
keyPath,
|
||||
'localhost.ermnvldmr.com',
|
||||
'*.localhost.ermnvldmr.com',
|
||||
'localhost',
|
||||
'127.0.0.1',
|
||||
'::1',
|
||||
],
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
chmodSync(keyPath, 0o600);
|
||||
return { certPath, keyPath };
|
||||
}
|
||||
|
||||
if (existsSync(hugoCaCert) && existsSync(hugoCaKey)) {
|
||||
const tempDir = tmpdir();
|
||||
const tempCsr = join(tempDir, `ermnvldmr-dev-${Date.now()}.csr`);
|
||||
const tempExt = join(tempDir, `ermnvldmr-dev-${Date.now()}.cnf`);
|
||||
|
||||
try {
|
||||
writeFileSync(
|
||||
tempExt,
|
||||
'subjectAltName=DNS:localhost.ermnvldmr.com,DNS:*.localhost.ermnvldmr.com,DNS:localhost,IP:127.0.0.1,IP:::1\n'
|
||||
);
|
||||
execFileSync(
|
||||
'openssl',
|
||||
[
|
||||
'req',
|
||||
'-new',
|
||||
'-nodes',
|
||||
'-newkey',
|
||||
'rsa:2048',
|
||||
'-keyout',
|
||||
keyPath,
|
||||
'-out',
|
||||
tempCsr,
|
||||
'-subj',
|
||||
'/CN=localhost.ermnvldmr.com',
|
||||
'-batch',
|
||||
],
|
||||
{ stdio: 'pipe' }
|
||||
);
|
||||
execFileSync(
|
||||
'openssl',
|
||||
[
|
||||
'x509',
|
||||
'-req',
|
||||
'-in',
|
||||
tempCsr,
|
||||
'-CA',
|
||||
hugoCaCert,
|
||||
'-CAkey',
|
||||
hugoCaKey,
|
||||
'-CAcreateserial',
|
||||
'-out',
|
||||
certPath,
|
||||
'-days',
|
||||
'825',
|
||||
'-extfile',
|
||||
tempExt,
|
||||
],
|
||||
{ stdio: 'pipe' }
|
||||
);
|
||||
chmodSync(keyPath, 0o600);
|
||||
chmodSync(certPath, 0o644);
|
||||
} finally {
|
||||
rmSync(tempCsr, { force: true });
|
||||
rmSync(tempExt, { force: true });
|
||||
// 1. Sync CA cert from global cache if available, or download from remote static URL
|
||||
if (!existsSync(caCertPath)) {
|
||||
if (existsSync(globalCaCert)) {
|
||||
writeFileSync(caCertPath, readFileSync(globalCaCert));
|
||||
chmodSync(caCertPath, 0o644);
|
||||
} else {
|
||||
try {
|
||||
execFileSync('curl', ['-sSfL', REMOTE_ROOT_CA_URL, '-o', caCertPath], {
|
||||
stdio: 'ignore',
|
||||
});
|
||||
chmodSync(caCertPath, 0o644);
|
||||
try {
|
||||
mkdirSync(globalCaDir, { recursive: true });
|
||||
writeFileSync(globalCaCert, readFileSync(caCertPath));
|
||||
chmodSync(globalCaCert, 0o644);
|
||||
} catch {
|
||||
// Best-effort global cache
|
||||
}
|
||||
} catch {
|
||||
// Failed to download, will fall back to generation if key also needed
|
||||
}
|
||||
}
|
||||
return { certPath, keyPath };
|
||||
}
|
||||
|
||||
const caDir = join(home, '.local/share/mkcert');
|
||||
mkdirSync(caDir, { recursive: true });
|
||||
execFileSync(
|
||||
'openssl',
|
||||
[
|
||||
'req',
|
||||
'-x509',
|
||||
'-nodes',
|
||||
'-newkey',
|
||||
'rsa:3072',
|
||||
'-keyout',
|
||||
hugoCaKey,
|
||||
'-out',
|
||||
hugoCaCert,
|
||||
'-days',
|
||||
'3650',
|
||||
'-subj',
|
||||
'/CN=mkcert development CA/O=mkcert development CA',
|
||||
'-batch',
|
||||
],
|
||||
{ stdio: 'pipe' }
|
||||
);
|
||||
// 2. Sync CA key from global cache if available, or decrypt from .env.dev
|
||||
if (!existsSync(caKeyPath)) {
|
||||
if (existsSync(globalCaKey)) {
|
||||
writeFileSync(caKeyPath, readFileSync(globalCaKey));
|
||||
chmodSync(caKeyPath, 0o600);
|
||||
} else {
|
||||
const decryptedKey = loadDevRootCaKey(workspaceRoot);
|
||||
if (decryptedKey) {
|
||||
writeFileSync(caKeyPath, decryptedKey);
|
||||
chmodSync(caKeyPath, 0o600);
|
||||
try {
|
||||
mkdirSync(globalCaDir, { recursive: true });
|
||||
writeFileSync(globalCaKey, decryptedKey);
|
||||
chmodSync(globalCaKey, 0o600);
|
||||
} catch {
|
||||
// Best-effort global cache
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If server cert and CA cert already exist, return them
|
||||
if (existsSync(certPath) && existsSync(keyPath) && existsSync(caCertPath)) {
|
||||
return { certPath, keyPath, caCertPath };
|
||||
}
|
||||
|
||||
// Ensure ErmnvldmrDevelopmentRootCA exists
|
||||
if (!existsSync(caCertPath) || !existsSync(caKeyPath)) {
|
||||
mkdirSync(globalCaDir, { recursive: true });
|
||||
execFileSync(
|
||||
'openssl',
|
||||
[
|
||||
'req',
|
||||
'-x509',
|
||||
'-nodes',
|
||||
'-newkey',
|
||||
'rsa:3072',
|
||||
'-keyout',
|
||||
caKeyPath,
|
||||
'-out',
|
||||
caCertPath,
|
||||
'-days',
|
||||
'3650',
|
||||
'-subj',
|
||||
'/CN=Ermnvldmr Development Root CA/O=ermnvldmr.com',
|
||||
'-batch',
|
||||
],
|
||||
{ stdio: 'pipe' }
|
||||
);
|
||||
chmodSync(caKeyPath, 0o600);
|
||||
chmodSync(caCertPath, 0o644);
|
||||
|
||||
writeFileSync(globalCaCert, readFileSync(caCertPath));
|
||||
writeFileSync(globalCaKey, readFileSync(caKeyPath));
|
||||
chmodSync(globalCaKey, 0o600);
|
||||
chmodSync(globalCaCert, 0o644);
|
||||
}
|
||||
|
||||
// Generate server certificate signed by ErmnvldmrDevelopmentRootCA
|
||||
const tempDir = tmpdir();
|
||||
const tempCsr = join(tempDir, `ermnvldmr-dev-${Date.now()}.csr`);
|
||||
const tempExt = join(tempDir, `ermnvldmr-dev-${Date.now()}.cnf`);
|
||||
@@ -318,9 +361,9 @@ export function ensureDevCertificates(
|
||||
'-in',
|
||||
tempCsr,
|
||||
'-CA',
|
||||
hugoCaCert,
|
||||
caCertPath,
|
||||
'-CAkey',
|
||||
hugoCaKey,
|
||||
caKeyPath,
|
||||
'-CAcreateserial',
|
||||
'-out',
|
||||
certPath,
|
||||
@@ -338,7 +381,7 @@ export function ensureDevCertificates(
|
||||
rmSync(tempExt, { force: true });
|
||||
}
|
||||
|
||||
return { certPath, keyPath };
|
||||
return { certPath, keyPath, caCertPath };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"build": "tsc",
|
||||
"lint": "eslint src --max-warnings 0",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest run",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -39,6 +40,8 @@
|
||||
"@rsbuild/core": "^1.7.3",
|
||||
"@rsbuild/plugin-react": "^1.1.1",
|
||||
"@rsbuild/plugin-type-check": "^1.1.1",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.1",
|
||||
"@types/node": "*",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
@@ -46,7 +49,8 @@
|
||||
"fast-glob": "^3.3.3",
|
||||
"jsdom": "^28.0.0",
|
||||
"rsbuild-plugin-dts": "^0.19.5",
|
||||
"typescript": "^5.8.2"
|
||||
"typescript": "^5.8.2",
|
||||
"vitest": "^4.1.11"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.1.0",
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { umamiAnalytics } from './umami';
|
||||
|
||||
describe('umamiAnalytics', () => {
|
||||
it('suppresses script injection in non-production environments when not forced', () => {
|
||||
const provider = umamiAnalytics({
|
||||
serverUrl: '/pulse',
|
||||
websiteId: 'test-website-id',
|
||||
});
|
||||
|
||||
expect(provider.getHtmlTags(false)).toEqual([]);
|
||||
});
|
||||
|
||||
it('injects script tag in non-production when force is true', () => {
|
||||
const provider = umamiAnalytics({
|
||||
serverUrl: '/pulse',
|
||||
websiteId: 'test-website-id',
|
||||
force: true,
|
||||
});
|
||||
|
||||
const tags = provider.getHtmlTags(false);
|
||||
expect(tags).toHaveLength(1);
|
||||
expect(tags[0]?.attrs?.src).toBe('/pulse/script.js');
|
||||
});
|
||||
|
||||
it('generates expected script attributes in production builds', () => {
|
||||
const provider = umamiAnalytics({
|
||||
serverUrl: '/trace/',
|
||||
scriptName: 'trace.js',
|
||||
hostUrl: '/trace',
|
||||
websiteId: '41c7699a-0675-4f49-bcd0-3df47a66b6b1',
|
||||
domains: 'ermnvldmr.com,www.ermnvldmr.com',
|
||||
doNotTrack: true,
|
||||
tag: 'landing',
|
||||
});
|
||||
|
||||
const tags = provider.getHtmlTags(true);
|
||||
expect(tags).toEqual([
|
||||
{
|
||||
tag: 'script',
|
||||
head: true,
|
||||
publicPath: false,
|
||||
attrs: {
|
||||
defer: true,
|
||||
src: '/trace/trace.js',
|
||||
'data-website-id': '41c7699a-0675-4f49-bcd0-3df47a66b6b1',
|
||||
'data-host-url': '/trace',
|
||||
'data-domains': 'ermnvldmr.com,www.ermnvldmr.com',
|
||||
'data-do-not-track': 'true',
|
||||
'data-tag': 'landing',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -5,14 +5,36 @@ import path from 'node:path';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { getPageOptions } from '../core/createPage.js';
|
||||
|
||||
import type { RsbuildPlugin } from '@rsbuild/core';
|
||||
|
||||
/**
|
||||
* Escapes HTML special characters for safe injection into markup.
|
||||
*
|
||||
* @param str - The raw string to escape.
|
||||
* @returns The escaped HTML string.
|
||||
* @example
|
||||
* ```ts
|
||||
* escapeHtml('<script>alert("xss")</script>');
|
||||
* ```
|
||||
*/
|
||||
function escapeHtml(str: string): string {
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
const SSR_RUNNER_SCRIPT = `
|
||||
import { createRsbuild } from '@rsbuild/core';
|
||||
|
||||
async function main() {
|
||||
const entries = JSON.parse(process.argv[1]);
|
||||
const ssrDistPath = process.argv[2];
|
||||
const locale = process.argv[3] || process.env.LOCALE || 'en';
|
||||
|
||||
const plugins = [];
|
||||
try {
|
||||
@@ -31,10 +53,35 @@ async function main() {
|
||||
// MDX plugins not installed or needed in this service
|
||||
}
|
||||
|
||||
if (locale !== 'en') {
|
||||
plugins.push({
|
||||
name: 'ermnvldmr:ssr-locale-mdx-replacement',
|
||||
setup(api) {
|
||||
api.modifyRspackConfig((config, { rspack }) => {
|
||||
config.plugins = [
|
||||
...(config.plugins ?? []),
|
||||
new rspack.NormalModuleReplacementPlugin(
|
||||
/\\.en\\.mdx$/,
|
||||
(resource) => {
|
||||
resource.request = resource.request.replace(/\\.en\\.mdx$/, '.' + locale + '.mdx');
|
||||
}
|
||||
),
|
||||
];
|
||||
return config;
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const ssrBuild = await createRsbuild({
|
||||
rsbuildConfig: {
|
||||
plugins,
|
||||
source: { entry: entries },
|
||||
source: {
|
||||
entry: entries,
|
||||
define: {
|
||||
'process.env.LOCALE': JSON.stringify(locale),
|
||||
},
|
||||
},
|
||||
output: {
|
||||
target: 'node',
|
||||
distPath: { root: ssrDistPath, js: '.' },
|
||||
@@ -119,11 +166,16 @@ export function ssgPlugin(): RsbuildPlugin {
|
||||
);
|
||||
const ssrDistPath = path.join(path.dirname(webDistPath), 'dist-ssr');
|
||||
|
||||
const locale = process.env.LOCALE ?? 'en';
|
||||
|
||||
console.log('[ssg-plugin] Building SSR bundles...');
|
||||
execFileSync(
|
||||
process.execPath,
|
||||
['--input-type=module', '-e', SSR_RUNNER_SCRIPT, JSON.stringify(entries), ssrDistPath],
|
||||
{ stdio: 'inherit' }
|
||||
['--input-type=module', '-e', SSR_RUNNER_SCRIPT, JSON.stringify(entries), ssrDistPath, locale],
|
||||
{
|
||||
stdio: 'inherit',
|
||||
env: { ...process.env, LOCALE: locale },
|
||||
}
|
||||
);
|
||||
console.log('[ssg-plugin] SSR bundles ready.');
|
||||
|
||||
@@ -175,8 +227,10 @@ export function ssgPlugin(): RsbuildPlugin {
|
||||
React.createElement(React.StrictMode, null, React.createElement(Component))
|
||||
);
|
||||
|
||||
const pageOptions = getPageOptions(Component);
|
||||
|
||||
const source = await fs.readFile(webHtmlPath, 'utf8');
|
||||
const injected = source.replace(
|
||||
let injected = source.replace(
|
||||
/<div id="root"[^>]*>\s*<\/div>/,
|
||||
`<div id="root">${renderedHtml}</div>`
|
||||
);
|
||||
@@ -187,9 +241,29 @@ export function ssgPlugin(): RsbuildPlugin {
|
||||
);
|
||||
}
|
||||
|
||||
if (pageOptions?.title) {
|
||||
injected = injected.replace(
|
||||
/<title>[^<]*<\/title>/i,
|
||||
`<title>${escapeHtml(pageOptions.title)}</title>`
|
||||
);
|
||||
}
|
||||
|
||||
if (pageOptions?.description) {
|
||||
if (/<meta\s+[^>]*name=["']description["'][^>]*>/i.test(injected)) {
|
||||
injected = injected.replace(
|
||||
/<meta\s+[^>]*name=["']description["'][^>]*>/i,
|
||||
`<meta name="description" content="${escapeHtml(pageOptions.description)}">`
|
||||
);
|
||||
} else {
|
||||
injected = injected.replace(
|
||||
/<\/head>/i,
|
||||
` <meta name="description" content="${escapeHtml(pageOptions.description)}">\n</head>`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const processed = await critters.process(injected);
|
||||
|
||||
const locale = process.env.LOCALE ?? 'en';
|
||||
const localized = processed.replace(
|
||||
/<html([^>]*)\slang="[^"]*"/,
|
||||
`<html$1 lang="${locale}"`
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { createPage, getPageOptions } from './createPage';
|
||||
|
||||
describe('createPage and getPageOptions', () => {
|
||||
beforeEach(() => {
|
||||
document.title = 'Default Title';
|
||||
const existingMeta = document.querySelector('meta[name="description"]');
|
||||
if (existingMeta) {
|
||||
existingMeta.remove();
|
||||
}
|
||||
});
|
||||
|
||||
it('attaches metadata options to component and retrieves them via getPageOptions', () => {
|
||||
function SamplePage(): React.JSX.Element {
|
||||
return <div>Sample</div>;
|
||||
}
|
||||
|
||||
createPage(SamplePage, {
|
||||
title: 'Sample Title',
|
||||
description: 'Sample Description',
|
||||
});
|
||||
|
||||
const options = getPageOptions(SamplePage);
|
||||
expect(options).toEqual({
|
||||
title: 'Sample Title',
|
||||
description: 'Sample Description',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns undefined for entities without metadata options', () => {
|
||||
function PlainComponent(): React.JSX.Element {
|
||||
return <div>Plain</div>;
|
||||
}
|
||||
|
||||
expect(getPageOptions(PlainComponent)).toBeUndefined();
|
||||
expect(getPageOptions(null)).toBeUndefined();
|
||||
expect(getPageOptions(undefined)).toBeUndefined();
|
||||
expect(getPageOptions('string')).toBeUndefined();
|
||||
expect(getPageOptions(123)).toBeUndefined();
|
||||
expect(getPageOptions({})).toBeUndefined();
|
||||
});
|
||||
|
||||
it('updates document.title and creates meta description in DOM', () => {
|
||||
function MetaPage(): React.JSX.Element {
|
||||
return <div>Meta</div>;
|
||||
}
|
||||
|
||||
createPage(MetaPage, {
|
||||
title: 'Custom Title',
|
||||
description: 'Custom Description',
|
||||
});
|
||||
|
||||
expect(document.title).toBe('Custom Title');
|
||||
const meta = document.querySelector('meta[name="description"]');
|
||||
expect(meta).not.toBeNull();
|
||||
expect(meta?.getAttribute('content')).toBe('Custom Description');
|
||||
});
|
||||
|
||||
it('updates existing meta description if already present in DOM', () => {
|
||||
const existingMeta = document.createElement('meta');
|
||||
existingMeta.setAttribute('name', 'description');
|
||||
existingMeta.setAttribute('content', 'Old Description');
|
||||
document.head.appendChild(existingMeta);
|
||||
|
||||
function AnotherMetaPage(): React.JSX.Element {
|
||||
return <div>Another Meta</div>;
|
||||
}
|
||||
|
||||
createPage(AnotherMetaPage, {
|
||||
description: 'Updated Description',
|
||||
});
|
||||
|
||||
const meta = document.querySelector('meta[name="description"]');
|
||||
expect(meta?.getAttribute('content')).toBe('Updated Description');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,121 @@
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Options for page creation.
|
||||
*/
|
||||
export interface PageOptions {
|
||||
/** Document title to set on mount. */
|
||||
title?: string;
|
||||
/** Content of the meta description tag. */
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal interface representing a component with attached page options.
|
||||
*/
|
||||
interface HasPageOptions {
|
||||
/** The attached page options. */
|
||||
__pageOptions?: PageOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type predicate checking if a value is an object or function with attached page options.
|
||||
*
|
||||
* @param value - The value to inspect.
|
||||
* @returns True if value has __pageOptions.
|
||||
* @example
|
||||
* ```ts
|
||||
* if (hasPageOptions(Component)) {
|
||||
* console.log(Component.__pageOptions);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
function hasPageOptions(value: unknown): value is HasPageOptions {
|
||||
return (
|
||||
(typeof value === 'function' || (typeof value === 'object' && value !== null)) &&
|
||||
'__pageOptions' in value
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the page metadata options attached to a component via createPage.
|
||||
*
|
||||
* @param component - The component to inspect.
|
||||
* @returns The associated PageOptions, or undefined if none are set.
|
||||
* @example
|
||||
* ```ts
|
||||
* const options = getPageOptions(MyPageComponent);
|
||||
* ```
|
||||
*/
|
||||
export function getPageOptions(component: unknown): PageOptions | undefined {
|
||||
if (hasPageOptions(component)) {
|
||||
return component.__pageOptions;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounts or hydrates a React component into the `#root` element.
|
||||
*
|
||||
* Attaches metadata options for SSG extraction. Skips DOM mounting during the SSG
|
||||
* server-side render pass (`typeof window === 'undefined'`). On the client, hydrates
|
||||
* asynchronously during idle time to unblock First Contentful Paint.
|
||||
*
|
||||
* @param Component - The React component to render.
|
||||
* @param options - Optional page metadata (title, description).
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* createPage(App, { title: 'My Page' });
|
||||
* ```
|
||||
*/
|
||||
export function createPage(Component: React.ComponentType, options: PageOptions = {}): void {
|
||||
Object.assign(Component, { __pageOptions: options });
|
||||
|
||||
if (typeof window === 'undefined') return; // SSG build context: skip DOM mounting
|
||||
const rootElement = document.getElementById('root');
|
||||
|
||||
if (options.title && document.title !== options.title) {
|
||||
document.title = options.title;
|
||||
}
|
||||
|
||||
if (options.description) {
|
||||
let metaDescription = document.querySelector('meta[name="description"]');
|
||||
if (!metaDescription) {
|
||||
metaDescription = document.createElement('meta');
|
||||
metaDescription.setAttribute('name', 'description');
|
||||
document.head.appendChild(metaDescription);
|
||||
}
|
||||
if (metaDescription.getAttribute('content') !== options.description) {
|
||||
metaDescription.setAttribute('content', options.description);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rootElement) return;
|
||||
|
||||
const mountOrHydrate = () => {
|
||||
void import('react-dom/client')
|
||||
.then(({ createRoot, hydrateRoot }) => {
|
||||
const node = React.createElement(
|
||||
React.StrictMode,
|
||||
null,
|
||||
React.createElement(Component)
|
||||
);
|
||||
|
||||
if (rootElement.hasChildNodes()) {
|
||||
hydrateRoot(rootElement, node);
|
||||
} else {
|
||||
createRoot(rootElement).render(node);
|
||||
}
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
console.error('[createPage] Client hydration failed:', err);
|
||||
});
|
||||
};
|
||||
|
||||
if ('requestIdleCallback' in window) {
|
||||
window.requestIdleCallback(mountOrHydrate);
|
||||
} else {
|
||||
setTimeout(mountOrHydrate, 1);
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Options for page creation.
|
||||
*/
|
||||
export interface PageOptions {
|
||||
/** Document title to set on mount. */
|
||||
title?: string;
|
||||
/** Content of the meta description tag. */
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounts or hydrates a React component into the `#root` element.
|
||||
*
|
||||
* Skips DOM mounting during the SSG server-side render pass
|
||||
* (`typeof window === 'undefined'`). On the client, hydrates asynchronously
|
||||
* during idle time to unblock First Contentful Paint.
|
||||
*
|
||||
* @param Component - The React component to render.
|
||||
* @param options - Optional page metadata (title, description).
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* createPage(App, { title: 'My Page' });
|
||||
* ```
|
||||
*/
|
||||
export function createPage(Component: React.ComponentType, options: PageOptions = {}): void {
|
||||
if (typeof window === 'undefined') return; // SSG build context: skip DOM mounting
|
||||
const rootElement = document.getElementById('root');
|
||||
|
||||
if (options.title) {
|
||||
document.title = options.title;
|
||||
}
|
||||
|
||||
if (options.description) {
|
||||
let metaDescription = document.querySelector('meta[name="description"]');
|
||||
if (!metaDescription) {
|
||||
metaDescription = document.createElement('meta');
|
||||
metaDescription.setAttribute('name', 'description');
|
||||
document.head.appendChild(metaDescription);
|
||||
}
|
||||
metaDescription.setAttribute('content', options.description);
|
||||
}
|
||||
|
||||
if (!rootElement) return;
|
||||
|
||||
const mountOrHydrate = () => {
|
||||
void import('react-dom/client')
|
||||
.then(({ createRoot, hydrateRoot }) => {
|
||||
const node = (
|
||||
<React.StrictMode>
|
||||
<Component />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
if (rootElement.hasChildNodes()) {
|
||||
hydrateRoot(rootElement, node);
|
||||
} else {
|
||||
createRoot(rootElement).render(node);
|
||||
}
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
console.error('[createPage] Client hydration failed:', err);
|
||||
});
|
||||
};
|
||||
|
||||
if ('requestIdleCallback' in window) {
|
||||
window.requestIdleCallback(mountOrHydrate);
|
||||
} else {
|
||||
setTimeout(mountOrHydrate, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { baseVitestConfig } from '@ermnvldmr/rsbuild-config/dev';
|
||||
import { mergeConfig } from 'vitest/config';
|
||||
|
||||
export default mergeConfig(baseVitestConfig, {
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
exclude: ['dist/**', 'node_modules/**'],
|
||||
},
|
||||
});
|
||||
@@ -53,7 +53,7 @@
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^4.0.18"
|
||||
"vitest": "^4.1.11"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.1.0",
|
||||
|
||||
@@ -30,6 +30,6 @@
|
||||
"babel-plugin-istanbul": "^7.0.1",
|
||||
"storybook-react-rsbuild": "^1.0.3",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^8.0.7"
|
||||
"vite": "^8.1.6"
|
||||
}
|
||||
}
|
||||
@@ -91,11 +91,11 @@
|
||||
"http-server": "^14.1.1",
|
||||
"jsdom": "^28.0.0",
|
||||
"playwright": "^1.57.0",
|
||||
"postcss": "^8.5.14",
|
||||
"postcss": "^8.5.23",
|
||||
"storybook": "^8.6.17",
|
||||
"storybook-react-rsbuild": "^1.0.3",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^4.0.18",
|
||||
"vitest": "^4.1.11",
|
||||
"wait-on": "^8.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -76,3 +76,15 @@ export const WithBullets: Story = {
|
||||
</Breadcrumbs>
|
||||
),
|
||||
};
|
||||
|
||||
export const Large: Story = {
|
||||
render: () => (
|
||||
<Breadcrumbs size="l">
|
||||
<Breadcrumbs.Item href="/">Home</Breadcrumbs.Item>
|
||||
<Breadcrumbs.Separator variant="slash" />
|
||||
<Breadcrumbs.Item href="/articles">Articles</Breadcrumbs.Item>
|
||||
<Breadcrumbs.Separator variant="slash" />
|
||||
<Breadcrumbs.Item isCurrent>Deep Learning</Breadcrumbs.Item>
|
||||
</Breadcrumbs>
|
||||
),
|
||||
};
|
||||
@@ -36,6 +36,16 @@ describe('components/Breadcrumbs', () => {
|
||||
expect(ol.className).toContain('text-label-m');
|
||||
});
|
||||
|
||||
it('supports custom size variant', () => {
|
||||
render(
|
||||
<Breadcrumbs size="l">
|
||||
<Breadcrumbs.Item href="/test">Home</Breadcrumbs.Item>
|
||||
</Breadcrumbs>
|
||||
);
|
||||
const ol = screen.getByRole('list');
|
||||
expect(ol.className).toContain('text-label-l');
|
||||
});
|
||||
|
||||
it('supports isCurrent state', () => {
|
||||
render(
|
||||
<Breadcrumbs>
|
||||
|
||||
@@ -16,15 +16,44 @@ import type { ClassNameProps, TestIdProps } from '@ermnvldmr/stl';
|
||||
export interface BreadcrumbsProps extends HStackProps {
|
||||
/** Breadcrumb items and separators */
|
||||
children: React.ReactNode;
|
||||
/**
|
||||
* Typography size variant for the breadcrumbs trail.
|
||||
* @default 'm'
|
||||
*/
|
||||
size?: 's' | 'm' | 'l';
|
||||
}
|
||||
|
||||
const breadcrumbsVariants = cva('font-sans font-medium', {
|
||||
variants: {
|
||||
size: {
|
||||
s: 'text-label-s',
|
||||
m: 'text-label-m',
|
||||
l: 'text-label-l',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: 'm',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Root component for hierarchical navigation.
|
||||
*/
|
||||
const BreadcrumbsRoot = memo(function Breadcrumbs({ children, className, ...props }: BreadcrumbsProps) {
|
||||
const BreadcrumbsRoot = memo(function Breadcrumbs({
|
||||
children,
|
||||
className,
|
||||
size = 'm',
|
||||
...props
|
||||
}: BreadcrumbsProps) {
|
||||
return (
|
||||
<nav aria-label="Breadcrumb" className={className}>
|
||||
<HStack align="center" as="ol" className="text-label-m font-sans font-medium" gap={2} {...props}>
|
||||
<nav aria-label="Breadcrumb">
|
||||
<HStack
|
||||
align="center"
|
||||
as="ol"
|
||||
className={cn(breadcrumbsVariants({ size }), className)}
|
||||
gap={2}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</HStack>
|
||||
</nav>
|
||||
@@ -126,7 +155,7 @@ const BreadcrumbsSeparator = memo(function BreadcrumbsSeparator({
|
||||
const content = {
|
||||
slash: '/',
|
||||
bull: '•',
|
||||
arrow: <ChevronRight className="size-4" />,
|
||||
arrow: <ChevronRight className="size-[1em]" />,
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Switch } from './Switch';
|
||||
import { VStack } from '../VStack/VStack';
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
@@ -8,6 +9,16 @@ const meta: Meta<typeof Switch> = {
|
||||
title: 'Components/Switch',
|
||||
component: Switch,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
size: {
|
||||
control: 'select',
|
||||
options: ['s', 'm', 'l'],
|
||||
},
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: ['solid', 'outline', 'ghost'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
@@ -19,6 +30,16 @@ export const Default: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const Sizes: Story = {
|
||||
render: () => (
|
||||
<VStack gap={4}>
|
||||
<Switch size="s">Small Switch</Switch>
|
||||
<Switch size="m">Medium Switch</Switch>
|
||||
<Switch size="l">Large Switch</Switch>
|
||||
</VStack>
|
||||
),
|
||||
};
|
||||
|
||||
export const Selected: Story = {
|
||||
args: {
|
||||
children: 'Selected State',
|
||||
|
||||
@@ -66,5 +66,18 @@ describe('components/Switch', () => {
|
||||
expect(track).toHaveClass('data-[selected=true]:bg-primary');
|
||||
expect(thumb).toHaveClass('data-[selected=true]:bg-primary-text');
|
||||
});
|
||||
|
||||
it('applies size variant classes to track and thumb', () => {
|
||||
const { container, rerender } = render(<Switch size="s">Small</Switch>);
|
||||
const track = container.querySelector('[aria-hidden="true"]');
|
||||
const thumb = track?.firstElementChild;
|
||||
|
||||
expect(track).toHaveClass('w-7', 'h-4');
|
||||
expect(thumb).toHaveClass('w-2.5', 'h-2.5');
|
||||
|
||||
rerender(<Switch size="l">Large</Switch>);
|
||||
expect(track).toHaveClass('w-14', 'h-8');
|
||||
expect(thumb).toHaveClass('w-6', 'h-6');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,11 @@ import type { AriaSwitchProps } from 'react-aria';
|
||||
*/
|
||||
export type SwitchVariant = 'solid' | 'outline' | 'ghost';
|
||||
|
||||
/**
|
||||
* Switch size variants.
|
||||
*/
|
||||
export type SwitchSize = 's' | 'm' | 'l';
|
||||
|
||||
/**
|
||||
* Switch color themes.
|
||||
*/
|
||||
@@ -39,12 +44,14 @@ export interface SwitchProps extends AriaSwitchProps, ClassNameProps, DataAttrib
|
||||
children?: React.ReactNode;
|
||||
/** Visual style variant */
|
||||
variant?: SwitchVariant;
|
||||
/** Switch size */
|
||||
size?: SwitchSize;
|
||||
/** Color theme */
|
||||
color?: SwitchColor;
|
||||
}
|
||||
|
||||
const trackVariants = cva(
|
||||
'relative w-11 h-6 transition-all duration-200 ease-in-out rounded-full outline-none flex items-center',
|
||||
'relative transition-all duration-200 ease-in-out rounded-full outline-none flex items-center',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
@@ -52,6 +59,11 @@ const trackVariants = cva(
|
||||
outline: 'border-2 data-[selected=false]:bg-transparent data-[selected=false]:border-muted',
|
||||
ghost: 'data-[selected=false]:bg-transparent data-[selected=false]:border-transparent',
|
||||
},
|
||||
size: {
|
||||
s: 'w-7 h-4',
|
||||
m: 'w-11 h-6',
|
||||
l: 'w-14 h-8',
|
||||
},
|
||||
color: {
|
||||
primary: '',
|
||||
'primary-negative': '',
|
||||
@@ -118,19 +130,25 @@ const trackVariants = cva(
|
||||
],
|
||||
defaultVariants: {
|
||||
variant: 'solid',
|
||||
size: 'm',
|
||||
color: 'primary',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const thumbVariants = cva(
|
||||
'w-4 h-4 transition-all duration-200 ease-in-out transform rounded-full shadow-sm data-[selected=false]:bg-[var(--rb-text)] data-[selected=false]:translate-x-0',
|
||||
'transition-all duration-200 ease-in-out transform rounded-full shadow-sm data-[selected=false]:bg-[var(--rb-text)] data-[selected=false]:translate-x-0',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
solid: 'ml-1 data-[selected=true]:translate-x-5',
|
||||
outline: 'ml-0.5 data-[selected=true]:translate-x-4.5',
|
||||
ghost: 'ml-1 data-[selected=true]:translate-x-5',
|
||||
solid: '',
|
||||
outline: '',
|
||||
ghost: '',
|
||||
},
|
||||
size: {
|
||||
s: 'w-2.5 h-2.5',
|
||||
m: 'w-4 h-4',
|
||||
l: 'w-6 h-6',
|
||||
},
|
||||
color: {
|
||||
primary: '',
|
||||
@@ -146,6 +164,17 @@ const thumbVariants = cva(
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
// size + variant offsets
|
||||
{ size: 's', variant: 'solid', className: 'ml-0.5 data-[selected=true]:translate-x-3.5' },
|
||||
{ size: 's', variant: 'outline', className: 'ml-0.5 data-[selected=true]:translate-x-2.5' },
|
||||
{ size: 's', variant: 'ghost', className: 'ml-0.5 data-[selected=true]:translate-x-3.5' },
|
||||
{ size: 'm', variant: 'solid', className: 'ml-1 data-[selected=true]:translate-x-5' },
|
||||
{ size: 'm', variant: 'outline', className: 'ml-0.5 data-[selected=true]:translate-x-4.5' },
|
||||
{ size: 'm', variant: 'ghost', className: 'ml-1 data-[selected=true]:translate-x-5' },
|
||||
{ size: 'l', variant: 'solid', className: 'ml-1 data-[selected=true]:translate-x-6' },
|
||||
{ size: 'l', variant: 'outline', className: 'ml-0.5 data-[selected=true]:translate-x-5.5' },
|
||||
{ size: 'l', variant: 'ghost', className: 'ml-1 data-[selected=true]:translate-x-6' },
|
||||
|
||||
// primary
|
||||
{ variant: 'solid', color: 'primary', className: 'data-[selected=true]:bg-primary-text' },
|
||||
{ variant: 'outline', color: 'primary', className: 'data-[selected=true]:bg-primary-text' },
|
||||
@@ -198,6 +227,7 @@ const thumbVariants = cva(
|
||||
],
|
||||
defaultVariants: {
|
||||
variant: 'solid',
|
||||
size: 'm',
|
||||
color: 'primary',
|
||||
},
|
||||
}
|
||||
@@ -216,7 +246,7 @@ const thumbVariants = cva(
|
||||
* ```
|
||||
*/
|
||||
export function Switch(props: SwitchProps): React.JSX.Element {
|
||||
const { children, className, variant = 'solid', color = 'primary' } = props;
|
||||
const { children, className, variant = 'solid', size = 'm', color = 'primary' } = props;
|
||||
const state = useToggleState(props);
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
const { inputProps } = useSwitch(props, state, ref);
|
||||
@@ -238,14 +268,14 @@ export function Switch(props: SwitchProps): React.JSX.Element {
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
trackVariants({ variant, color }),
|
||||
trackVariants({ variant, size, color }),
|
||||
isFocusVisible && 'ring-2 ring-offset-2 ring-[var(--rb-ring)]',
|
||||
props.isDisabled && 'grayscale-[0.5]'
|
||||
)}
|
||||
data-selected={state.isSelected}
|
||||
>
|
||||
<div
|
||||
className={thumbVariants({ variant, color })}
|
||||
className={thumbVariants({ variant, size, color })}
|
||||
data-selected={state.isSelected}
|
||||
/>
|
||||
</div>
|
||||
@@ -253,7 +283,7 @@ export function Switch(props: SwitchProps): React.JSX.Element {
|
||||
<Text
|
||||
bold={state.isSelected}
|
||||
color={state.isSelected ? 'default' : 'muted'}
|
||||
size="s"
|
||||
size={size}
|
||||
type="label"
|
||||
>
|
||||
{children}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Loaded 100 of 207 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user