Skip to content

ci(demo): build and deploy demo-stack images to the demo server over SSH#1515

Merged
piotrswierzy merged 10 commits into
mainfrom
add_ci
Jul 14, 2026
Merged

ci(demo): build and deploy demo-stack images to the demo server over SSH#1515
piotrswierzy merged 10 commits into
mainfrom
add_ci

Conversation

@tomektom

@tomektom tomektom commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Replaces the disabled placeholder deploy job in .github/workflows/cd.yml with a working build-deploy job that continuously deploys the demo stack on push to main.
  • Builds the four demo images (web via apps/web/Dockerfile with VITE_API_BASE_URL, api/worker/migrate via the root Dockerfile targets production/worker/base) on a self-hosted runner, transfers them to the demo server over SSH, syncs the PrestaShop module and PrestaShop/WooCommerce init scripts, and triggers the server-side deploy.sh.
  • Distinct from [CI/CD] Publish Docker images to GHCR on release (multi-arch, SBOM + scan + signing) #1497 (GHCR image publishing on release) - this is CD of main to our own demo server.

Related issues

Closes #1516

Test plan

  • Push to main (or re-run the workflow) and confirm the build-deploy job runs in the demo environment on the self-hosted runner.
  • Verify on the demo server that the four images were loaded, apps/prestashop-module/ and the init-script directories are in their destination paths, and deploy.sh restarted the stack.
  • Smoke-check the public demo UI + API after the run.

Note: review findings on the current draft are tracked in the PR comments and as acceptance criteria on #1516 (trigger/if mismatch means the job cannot currently run; remote bash -s deploy.sh is a stdin no-op; the WooCommerce sync step sources ./docker/prestashop/). These need to be resolved before the test plan above can pass.

Quality gate

  • pnpm lint passes (zero errors)
  • pnpm type-check passes (zero errors)
  • pnpm test passes (all unit tests green)
  • Optional, Docker required: pnpm test:integration passes - needed only if you touched apps/api/test/integration/** or any plugin's infrastructure/adapters/.

(Workflow-only change - no TS/lint surface touched.)

Migrations

  • No ORM entity changes - trivially satisfied.

ADR

  • No architectural decision - workflow-only change; trivially satisfied.

DCO sign-off

Sign off every commit with git commit -s. OpenLinker uses the
Developer Certificate of Origin
as its contributor attestation. The current commit is unsigned; it will be
amended with -s (and a GPG signature) before merge.


By submitting this pull request, I confirm that my contributions
are made under the terms of the Apache License 2.0, and I
certify the Developer Certificate of Origin
by signing off my commits.

@norbert-kulus-blockydevs

norbert-kulus-blockydevs commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Independent review of .github/workflows/cd.yml

Updated after the fix commit (f23c4177) and the author's responses. Resolutions recorded per finding.

Fixed in f23c4177

  • WooCommerce sync source - now rsyncs ./docker/woocommerce/ (was ./docker/prestashop/ with --delete).
  • Deploy invocation - now ssh ... "bash ~/deploy.sh" (was the bash -s deploy.sh stdin no-op).
  • Export steps - correctly named per image; redundant trailing web export removed.
  • Placeholder step/comment - removed.

Wontfix - author's call, accepted for the demo environment 🟡

  • Runtime ssh-keyscan / placeholder known_hosts (TOFU) - accepted risk on a controlled network; revisit if this pipeline ever targets more than the demo box.
  • shimataro/ssh-key-action@v2 mutable tag - author's consistency point is fair (the same argument would apply to actions/checkout); SHA-pinning left as optional hardening.
  • No concurrency group - author prefers no dedup logic; overlapping deploys are unlikely at demo cadence.
  • Tar cleanup / slim migrate target / deploy-contract docs - deferred.

Clarified - not a bug ✅

  • SSH_HOST without a user - the secret embeds user@host by convention. A one-line comment in the workflow would save the next person the question, but non-blocking.

One remaining open point: the trigger gate

Author's intent is tag-only deploys (not every push to main) - that's fine and changes the earlier recommendation (no need to restore branches: [main]). The concern is only the mechanism:

github.event.base_ref on a tag-push event is populated only when the tag is pushed in the same operation as the branch update (e.g. git push origin main v1.0.0). For the standard flow - git tag v1.0.0 && git push origin v1.0.0 on a commit already on main - base_ref is null, so if: github.event.base_ref == 'refs/heads/main' evaluates false and the job silently skips. This is a long-standing GitHub Actions gotcha, not conjecture.

Since the trigger is already tags-only, the if adds nothing: drop it, or use if: startsWith(github.ref, 'refs/tags/v').

Decisive 2-minute test: tag an existing commit on main and push just the tag. If the build-deploy job runs, the review is wrong and this point closes. If it's skipped, the if needs the change above.


Tracked in #1516 (acceptance criteria updated to reflect the wontfix decisions). Both commits on the branch are now GPG-signed with DCO sign-off.

@norbert-kulus-blockydevs norbert-kulus-blockydevs changed the title add CI/CD ci(demo): build and deploy demo-stack images to the demo server over SSH Jul 13, 2026
tomektom and others added 2 commits July 13, 2026 16:25
Co-authored-by: Norbert Kulus <norbert.kulus@silksh.pl>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…, and deploy invocation

Co-authored-by: Norbert Kulus <norbert.kulus@silksh.pl>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

@piotrswierzy piotrswierzy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tech Lead Review — PR #1515

Verdict: ❌ Request changes (posted as comment). One functional blocker remains: as written, the build-deploy job cannot execute in the standard tag-push flow, so the pipeline never actually deploys. Everything else is solid and well within scope.

What this PR does

Replaces the disabled if: false placeholder deploy job in .github/workflows/cd.yml with a working build-deploy job (environment demo, self-hosted runner) that builds the four demo images (apps/web/Dockerfile with VITE_API_BASE_URL; root Dockerfile targets production/worker/base), docker saves them to tars, rsyncs the tars + the PrestaShop module + PrestaShop/WooCommerce init scripts to the demo server over SSH, and runs the remote deploy.sh. Workflow-only change (+51/-9, 1 file). Closes #1516.

Overall assessment

No architecture/boundary surface — no TS, no CORE↔Integration concerns, no ORM/migration, no FE. The build targets correctly match the Dockerfiles I checked locally (base/production/worker in root Dockerfile; VITE_API_BASE_URL ARG in apps/web/Dockerfile; docker/woocommerce/ exists). Secrets are all referenced via secrets.*/vars.* — no hardcoded credentials. The earlier fix commit already resolved the WooCommerce-source bug, the bash -s stdin no-op, and the export-step naming, all confirmed in the current diff. The single remaining defect is the trigger gate.

BLOCKING

  • .github/workflows/cd.yml:14 — trigger gate never true on a normal tag push. on.push.branches is commented out (line 5), so only v* tags fire the workflow; but if: github.event.base_ref == 'refs/heads/main' is null for the standard git tag vX && git push origin vX flow, so the job silently skips. Drop the if or use if: startsWith(github.ref, 'refs/tags/v'). This is #1516's first (unchecked) acceptance criterion; a linked successful demo run is also still required by #1516.

IMPORTANT

  • (none — the previously-flagged WooCommerce sync, deploy invocation, and export-step defects are fixed in the current head.)

SUGGESTION

  • cd.yml:5 — delete the commented-out # branches: [main] (and the commented Copy variables on server step) and add a one-line "tags only" comment, so the intended trigger surface is unambiguous.
  • cd.yml:16actions/checkout jumps @v4 → @v6 in a deploy-only PR; confirm v6 is intended and aligned with the other workflows.
  • Least privilege (non-blocking): consider a top-level permissions: {} block — the job makes no GitHub API calls, so it doesn't need the default GITHUB_TOKEN scopes. (ssh-keyscan TOFU host-key handling and third-party-action SHA-pinning were consciously deferred as wontfix for the demo box in #1516 — not re-litigating those.)

Positive observations

  • Correct use of environment: demo for environment-scoped secrets/protection.
  • Because the trigger is push (not pull_request), fork PRs can't execute code on the self-hosted runner — the main self-hosted-runner exposure is avoided by design.
  • Build targets, build-arg, and init-script paths all verified against the repo; scope is tight and matches the issue.
  • Prior review findings were genuinely addressed rather than hand-waved; commits are GPG-signed with DCO sign-off.

Once the trigger gate is fixed and one demo run is linked, this is an easy approve.

Comment thread .github/workflows/cd.yml Outdated
Comment thread .github/workflows/cd.yml Outdated
Comment thread .github/workflows/cd.yml
- uses: actions/checkout@v4
- name: Deployment placeholder
run: echo 'Deployment configuration coming soon'
- uses: actions/checkout@v6

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION — this bumps actions/checkout from @v4 (baseline cd.yml/ci.yml) straight to @v6 in a deploy-only PR. Please confirm v6 is an intended, published major and align it with the version the other workflows use, so CI/CD don't drift on the same action. (Third-party-action SHA-pinning was consciously deferred in #1516 — this is just about not silently jumping two majors here.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can downgrade actions/checkout but I don't think it's worth. New versions works same way as older and improve CI security

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 <norbert.kulus@blockydevs.com>
@norbert-kulus-blockydevs

Copy link
Copy Markdown
Collaborator

The Test job failure on run 29322744163 is unrelated to this PR (it only touches .github/workflows/cd.yml). Diagnosed as an inherently flaky KSeF unit test: aes-cipher.spec.ts wrong-key case, where AES-CBC decryption with a random wrong key can accidentally produce valid PKCS#7 padding (~0.4% of runs), so decryptAesCbc returns garbage instead of throwing. Filed as #1538 with the fix plan - work on it is starting now. Re-running the job here should pass.

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 <noreply@anthropic.com>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
@norbert-kulus-blockydevs

Copy link
Copy Markdown
Collaborator

Fix for the flaky test is up: #1539 (test-only change, verified with a 100k-iteration loop - 0 failures). Once it merges, this flake goes away; in the meantime re-running the failed Test job here should pass.

@piotrswierzy piotrswierzy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tech Lead Re-review — PR #1515

Re-review after update: prior trigger-gate blocker resolved. ✅ Approve — the build-deploy job can now actually run on the intended trigger, and the second changed file is a clean test-only improvement. Scope stays tight and workflow-only (plus one KSeF spec).

Verdict

The one BLOCKING finding from my prior review (build-deploy job could never execute because if: github.event.base_ref == 'refs/heads/main' is null on a v* tag push) is fixed: the job now carries no if: guard at all, so it runs unconditionally on the v* tag push the workflow fires on (cd.yml:9-10 tags, cd.yml:12-16 job header). The pipeline will now deploy.

Confirmed resolved against the current head

  • Trigger gateif clause removed; job runs on tag push. ✅
  • Remote deploy invocationssh … "bash ~/deploy.sh" (cd.yml:63) runs the remote script directly; the earlier bash -s deploy.sh stdin no-op is gone. ✅
  • WooCommerce sync sourceSync woocommerce init scripts now rsyncs ./docker/woocommerce/ (cd.yml:57), not the PrestaShop path. ✅
  • Least-privilege suggestion adopted — top-level permissions: {} on the job (cd.yml:15), since it makes no GitHub API calls. ✅
  • Commented-out # branches: [main] and the stale Copy variables on server step were dropped; trigger surface is now unambiguous (tags only).

Second changed file — no new issue

libs/integrations/ksef/…/aes-cipher.spec.ts (#1538): de-flakes the CBC wrong-key test by accepting either legal outcome — a KsefSessionCryptoException from PKCS#7 padding failure, or a returned garbage plaintext (≠ original) on the ~0.4% of random keys that yield accidentally-valid padding — and adds a deterministic truncated-ciphertext throw case. Correct CBC semantics, tightens rather than weakens the assertion. Good.

Non-blocking notes (unchanged / carried forward)

  • SUGGESTION — cd.yml:16 actions/checkout@v6 in a deploy-only PR; confirm v6 is intended and aligned with the other workflows (not a blocker).
  • ssh-keyscan TOFU host-key handling and third-party-action SHA-pinning remain consciously deferred for the demo box per #1516 — not re-litigating.
  • Merge gate, not a code blocker: #1516's acceptance still asks for one linked successful demo workflow run — worth attaching before/at merge, but it doesn't hold up the code approval.

Nice turnaround — the prior findings were genuinely addressed rather than papered over.

@piotrswierzy
piotrswierzy merged commit c3f374d into main Jul 14, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TASK] Infrastructure - CD pipeline: automated demo-environment deployment on push to main

3 participants