From 5feb3a19a8f26f7c1bdf77fefe988b0b6157195f Mon Sep 17 00:00:00 2001 From: Tomasz Warczykowski Date: Mon, 13 Jul 2026 13:51:38 +0000 Subject: [PATCH 1/5] ci(demo): build and deploy demo-stack images to the demo server over SSH Co-authored-by: Norbert Kulus Signed-off-by: norbert-kulus-blockydevs --- .github/workflows/cd.yml | 60 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index b2ad1fc51..6bed8248f 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -2,26 +2,74 @@ name: CD on: push: - branches: [main] + # branches: [main] tags: - 'v*' jobs: # Placeholder for future deployment - deploy: - name: Deploy - runs-on: ubuntu-latest - if: false # Disabled until deployment is configured + build-deploy: + name: Build and deploy + runs-on: self-hosted + environment: demo + if: ${{ github.ref == 'refs/heads/main' }} # run only on main branch steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Deployment placeholder run: echo 'Deployment configuration coming soon' + - name: Build web + run: docker build --build-arg VITE_API_BASE_URL=${{ vars.VITE_API_BASE_URL }} -f apps/web/Dockerfile . --tag openlinker-web + - name: Export openlinker-worker as tar + run: docker save -o openlinker-web.tar openlinker-web + - name: Build worker + run: docker build -f Dockerfile --target worker . --tag openlinker-worker + - name: Export openlinker-worker as tar + run: docker save -o openlinker-worker.tar openlinker-worker + - name: Build api + run: docker build -f Dockerfile --target production . --tag openlinker-api + - name: Export openlinker-api as tar + run: docker save -o openlinker-api.tar openlinker-api + - name: Build migrate + run: docker build -f Dockerfile --target base . --tag openlinker-migrate + - name: Export openlinker-migrate as tar + run: docker save -o openlinker-migrate.tar openlinker-migrate + - name: Export image as tar + run: docker save -o openlinker-web.tar openlinker-web + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + known_hosts: 'just-a-placeholder-so-we-dont-get-errors' + # name: id_rsa # optional + + - name: Adding Known Hosts + run: ssh-keyscan -p ${{ secrets.SSH_PORT }} -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts + + - name: Copy images to server + run: rsync -avz -e "ssh -p ${{ secrets.SSH_PORT }}" ./openlinker-{migrate,api,web,worker}.tar ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_IMAGES }}/ + + + - name: Sync prestashop module + run: rsync -avz --delete -e "ssh -p ${{ secrets.SSH_PORT }}" ./apps/prestashop-module/ ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_PRESTA_MODULE }}/ + + - name: Sync prestashop init scripts + run: rsync -avz --delete -e "ssh -p ${{ secrets.SSH_PORT }}" ./docker/prestashop/ ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_PRESTA_INITSCRIPTS }}/ + + - name: Sync woocommerce init scripts + run: rsync -avz --delete -e "ssh -p ${{ secrets.SSH_PORT }}" ./docker/prestashop/ ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_WC_INITSCRIPTS }}/ + + # - name: Copy variables on server + # run: rsync -avz -e "ssh -p ${{ secrets.SSH_PORT }}" .env-docker ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_ENV }}/.env + + - name: Deploy + run: ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_HOST }} "bash -s deploy.sh" From f23c4177c83f96c833ba5131b7dd99f0b97de94a Mon Sep 17 00:00:00 2001 From: Tomasz Warczykowski Date: Mon, 13 Jul 2026 16:25:10 +0200 Subject: [PATCH 2/5] ci(demo): fix trigger gate, web-export label, WooCommerce sync source, and deploy invocation Co-authored-by: Norbert Kulus Signed-off-by: norbert-kulus-blockydevs --- .github/workflows/cd.yml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 6bed8248f..c2185581d 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -7,21 +7,18 @@ on: - 'v*' jobs: - # Placeholder for future deployment build-deploy: name: Build and deploy runs-on: self-hosted environment: demo - if: ${{ github.ref == 'refs/heads/main' }} # run only on main branch + if: ${{ github.event.base_ref == 'refs/heads/main' }} # run only on main branch steps: - uses: actions/checkout@v6 - - name: Deployment placeholder - run: echo 'Deployment configuration coming soon' - name: Build web run: docker build --build-arg VITE_API_BASE_URL=${{ vars.VITE_API_BASE_URL }} -f apps/web/Dockerfile . --tag openlinker-web - - name: Export openlinker-worker as tar + - name: Export openlinker-web as tar run: docker save -o openlinker-web.tar openlinker-web - name: Build worker @@ -42,9 +39,6 @@ jobs: - name: Export openlinker-migrate as tar run: docker save -o openlinker-migrate.tar openlinker-migrate - - name: Export image as tar - run: docker save -o openlinker-web.tar openlinker-web - - name: Install SSH key uses: shimataro/ssh-key-action@v2 with: @@ -66,10 +60,10 @@ jobs: run: rsync -avz --delete -e "ssh -p ${{ secrets.SSH_PORT }}" ./docker/prestashop/ ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_PRESTA_INITSCRIPTS }}/ - name: Sync woocommerce init scripts - run: rsync -avz --delete -e "ssh -p ${{ secrets.SSH_PORT }}" ./docker/prestashop/ ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_WC_INITSCRIPTS }}/ + run: rsync -avz --delete -e "ssh -p ${{ secrets.SSH_PORT }}" ./docker/woocommerce/ ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_WC_INITSCRIPTS }}/ # - name: Copy variables on server # run: rsync -avz -e "ssh -p ${{ secrets.SSH_PORT }}" .env-docker ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_ENV }}/.env - name: Deploy - run: ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_HOST }} "bash -s deploy.sh" + run: ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_HOST }} "bash ~/deploy.sh" From 62bc6c72357773c12a8bbf90b832b61e38baf5fb Mon Sep 17 00:00:00 2001 From: Tomasz Warczykowski Date: Tue, 14 Jul 2026 11:13:10 +0200 Subject: [PATCH 3/5] ci(cd): drop broken base_ref gate and set explicit workflow permissions The base_ref check never matched since the workflow only triggers on tag pushes, not branch pushes. Replace it with an explicit empty permissions block and clean up leftover commented-out lines. Signed-off-by: norbert-kulus-blockydevs --- .github/workflows/cd.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index c2185581d..fc39def82 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -2,7 +2,6 @@ name: CD on: push: - # branches: [main] tags: - 'v*' @@ -11,7 +10,7 @@ jobs: name: Build and deploy runs-on: self-hosted environment: demo - if: ${{ github.event.base_ref == 'refs/heads/main' }} # run only on main branch + permissions: {} steps: - uses: actions/checkout@v6 @@ -44,7 +43,6 @@ jobs: with: key: ${{ secrets.SSH_KEY }} known_hosts: 'just-a-placeholder-so-we-dont-get-errors' - # name: id_rsa # optional - name: Adding Known Hosts run: ssh-keyscan -p ${{ secrets.SSH_PORT }} -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts @@ -52,7 +50,6 @@ jobs: - name: Copy images to server run: rsync -avz -e "ssh -p ${{ secrets.SSH_PORT }}" ./openlinker-{migrate,api,web,worker}.tar ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_IMAGES }}/ - - name: Sync prestashop module run: rsync -avz --delete -e "ssh -p ${{ secrets.SSH_PORT }}" ./apps/prestashop-module/ ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_PRESTA_MODULE }}/ @@ -62,8 +59,5 @@ jobs: - name: Sync woocommerce init scripts run: rsync -avz --delete -e "ssh -p ${{ secrets.SSH_PORT }}" ./docker/woocommerce/ ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_WC_INITSCRIPTS }}/ - # - name: Copy variables on server - # run: rsync -avz -e "ssh -p ${{ secrets.SSH_PORT }}" .env-docker ${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_PATH_ENV }}/.env - - name: Deploy run: ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_HOST }} "bash ~/deploy.sh" From 08716d0a92e1c43730dea826594e81ab73e77cb5 Mon Sep 17 00:00:00 2001 From: norbert-kulus-blockydevs Date: Tue, 14 Jul 2026 12:20:32 +0200 Subject: [PATCH 4/5] test(ksef): de-flake aes-cipher wrong-key spec AES-256-CBC has no key verification: decrypting with a random wrong key yields accidentally valid PKCS#7 padding ~0.4% of the time, so the old toThrow assertion failed randomly in CI. The wrong-key test now accepts both legal outcomes (KsefSessionCryptoException or garbage != plaintext), and a new deterministic test covers the exception-wrapping path via a truncated ciphertext, which always fails decipher.final(). No production code changes. Closes #1538 Co-Authored-By: Claude Fable 5 Signed-off-by: norbert-kulus-blockydevs --- .../crypto/__tests__/aes-cipher.spec.ts | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/libs/integrations/ksef/src/infrastructure/crypto/__tests__/aes-cipher.spec.ts b/libs/integrations/ksef/src/infrastructure/crypto/__tests__/aes-cipher.spec.ts index 7f75dccdd..e62ef2aaf 100644 --- a/libs/integrations/ksef/src/infrastructure/crypto/__tests__/aes-cipher.spec.ts +++ b/libs/integrations/ksef/src/infrastructure/crypto/__tests__/aes-cipher.spec.ts @@ -31,9 +31,25 @@ describe('aes-cipher', () => { expect(() => encryptAesCbc('x', key, new Uint8Array(8))).toThrow(KsefSessionCryptoException); }); - it('should throw KsefSessionCryptoException when decrypting with the wrong key', () => { - const ciphertext = encryptAesCbc('secret', key, iv); + it('should never recover the plaintext when decrypting with the wrong key', () => { + // CBC has no key verification: a wrong key usually fails PKCS#7 padding + // validation (throws), but ~0.4% of random keys yield accidentally valid + // padding and return garbage instead. Both outcomes are legal (#1538). + const plaintext = 'secret'; + const ciphertext = encryptAesCbc(plaintext, key, iv); const wrongKey = new Uint8Array(randomBytes(KSEF_AES_KEY_BYTES)); - expect(() => decryptAesCbc(ciphertext, wrongKey, iv)).toThrow(KsefSessionCryptoException); + try { + const result = decryptAesCbc(ciphertext, wrongKey, iv); + expect(result).not.toBe(plaintext); + } catch (err) { + expect(err).toBeInstanceOf(KsefSessionCryptoException); + } + }); + + it('should throw KsefSessionCryptoException when the ciphertext length is not a multiple of the block size', () => { + // decipher.final() always throws on a truncated ciphertext, so this covers + // the wrapping path of decryptAesCbc deterministically. + const truncated = encryptAesCbc('secret', key, iv).slice(0, -1); + expect(() => decryptAesCbc(truncated, key, iv)).toThrow(KsefSessionCryptoException); }); }); From 44c8a5df5a7d9a3c3cb2a563672bb432527c9621 Mon Sep 17 00:00:00 2001 From: norbert-kulus-blockydevs Date: Tue, 14 Jul 2026 13:07:30 +0200 Subject: [PATCH 5/5] test(ksef): move wrong-key success assertion out of try/catch Signed-off-by: norbert-kulus-blockydevs --- .../src/infrastructure/crypto/__tests__/aes-cipher.spec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/integrations/ksef/src/infrastructure/crypto/__tests__/aes-cipher.spec.ts b/libs/integrations/ksef/src/infrastructure/crypto/__tests__/aes-cipher.spec.ts index e62ef2aaf..ede6046fb 100644 --- a/libs/integrations/ksef/src/infrastructure/crypto/__tests__/aes-cipher.spec.ts +++ b/libs/integrations/ksef/src/infrastructure/crypto/__tests__/aes-cipher.spec.ts @@ -38,12 +38,14 @@ describe('aes-cipher', () => { const plaintext = 'secret'; const ciphertext = encryptAesCbc(plaintext, key, iv); const wrongKey = new Uint8Array(randomBytes(KSEF_AES_KEY_BYTES)); + let result: string | undefined; try { - const result = decryptAesCbc(ciphertext, wrongKey, iv); - expect(result).not.toBe(plaintext); + result = decryptAesCbc(ciphertext, wrongKey, iv); } catch (err) { expect(err).toBeInstanceOf(KsefSessionCryptoException); + return; } + expect(result).not.toBe(plaintext); }); it('should throw KsefSessionCryptoException when the ciphertext length is not a multiple of the block size', () => {