Skip to content

Fix CI workflows for pull requests from forks - #526

Merged
w1am merged 4 commits into
masterfrom
fix-fork-pr-ci
Aug 12, 2026
Merged

w1am merged 4 commits into
masterfrom
fix-fork-pr-ci

Conversation

@w1am

@w1am w1am commented Aug 12, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes DEV-1881

Community contributors currently can't get a green build: CI fails with errors on every pull request opened from a fork (see #505), even after a maintainer approves the run.

With this change, outside contributions work as expected:

  • A contributor forks the repo and opens a pull request.
  • A maintainer clicks "Approve and run workflows".
  • The build, code quality checks, and test suite run and report results on the PR.

This removes a barrier for anyone outside the organization who wants to contribute to the Node.js client, and reviews no longer need workarounds like re-pushing contributor branches internally.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Fix GitHub Actions CI for fork pull requests (no vars/secrets)

🐞 Bug fix ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Make runtime/image resolution work when vars are missing on fork PR runs
• Dynamically shrink the test matrix to lts when Cloudsmith secrets aren’t available
• Gate Cloudsmith login and ensure QA workflow inherits secrets for staging images
Diagram

graph TD
  A["main.yml (CI triggers)"] --> B["select_runtimes job"] --> C["tests.yml (reusable workflow)"] --> D["load-configuration.yml"] --> E["Test matrix run"]
  C --> F["Cloudsmith login (conditional)"]
  F --> G{{"Cloudsmith registry"}}
  H["qa.yml (dispatch)"] --> C
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use `pull_request_target` to access secrets on fork PRs
  • ➕ Full test matrix (including private/staging images) can run on fork PRs
  • ➕ Avoids conditional logic around missing secrets/vars
  • ➖ Higher security risk: untrusted PR code can influence workflow execution context
  • ➖ Requires careful hardening (checkout strategy, permissions, token scope) to avoid secret exfiltration
2. Split into separate workflows for forks vs trusted PRs
  • ➕ Clear separation of security posture and capabilities
  • ➕ Simpler per-workflow logic (no dynamic matrix computation)
  • ➖ More duplication and maintenance overhead
  • ➖ Harder to keep behavior aligned between the two workflows
3. Precompute runtime config as committed repo data (checked-in JSON/YAML)
  • ➕ Eliminates reliance on GitHub vars for runtime image mapping
  • ➕ More transparent and reviewable config changes
  • ➖ Harder to manage when values must remain centralized across repos/org
  • ➖ May increase churn if image mappings change frequently

Recommendation: The chosen approach (fork-safe defaults + secret-gated Cloudsmith steps + dynamic matrix selection) is the best security/utility tradeoff: it preserves meaningful CI signal for external contributors without granting secrets to untrusted contexts. pull_request_target would increase coverage but is a materially different risk profile; keeping secrets unavailable on forks is the safer default.

Files changed (4) +56 / -16

Bug fix (2) +33 / -12
load-configuration.ymlMake runtime image resolution fork-safe with jq + lts fallback +30/-12

Make runtime image resolution fork-safe with jq + lts fallback

• Moves runtime configuration selection into a shell script using 'jq' instead of 'fromJSON(vars...)'. Adds a fork-safe fallback mapping for 'lts' when 'vars' are absent and fails fast with a clear error for unknown runtimes; keeps explicit passthrough behavior for 'qa' inputs.

.github/workflows/load-configuration.yml

tests.ymlSkip Cloudsmith login when secrets are missing (fork/Dependabot) +3/-0

Skip Cloudsmith login when secrets are missing (fork/Dependabot)

• Introduces a 'SECRETS_AVAILABLE' env guard and conditions the Cloudsmith docker login step on it, preventing failures when secrets are unavailable in fork-originated runs.

.github/workflows/tests.yml

Other (2) +23 / -4
main.ymlSelect runtime matrix based on secret availability; fix triggers and checkout +22/-4

Select runtime matrix based on secret availability; fix triggers and checkout

• Fixes the push trigger branch filter and bumps 'actions/checkout' to v4 for code quality. Adds a 'select_runtimes' job that emits either the full runtime matrix or 'lts' only, and wires the tests matrix to that output.

.github/workflows/main.yml

qa.ymlInherit secrets for QA dispatch workflow +1/-0

Inherit secrets for QA dispatch workflow

• Adds 'secrets: inherit' so the QA workflow can access Cloudsmith credentials when invoking reusable workflows, enabling authenticated pulls of staging images.

.github/workflows/qa.yml

@qodo-code-review

qodo-code-review Bot commented Aug 12, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Fork tests still need auth ✗ Dismissed 🐞 Bug ☼ Reliability
Description
tests.yml now skips the Cloudsmith login when secrets are unavailable, but the test harness still
pulls docker.eventstore.com/... images (e.g., cert generation) by default, so fork PR runs can
still fail on registry auth/pull errors even when KURRENT_IMAGE is set to a public LTS image.
Code

.github/workflows/tests.yml[R69-72]

      - name: Login to Cloudsmith
+        if: env.SECRETS_AVAILABLE == 'true'
        uses: docker/login-action@v3
        with:
Evidence
The login step is now conditional on secrets, but the test harness still defines and uses a
cert-generation container image hosted at docker.eventstore.com, and most tests create secure
clusters by default (which includes the cert-gen service). Another workflow in this repo logs into
docker.eventstore.com with Cloudsmith credentials, indicating that registry typically requires
authentication.

.github/workflows/tests.yml[63-76]
packages/test/src/utils/dockerImages.ts[1-13]
packages/test/src/utils/Cluster.ts[38-61]
packages/test/src/utils/index.ts[20-24]
.github/workflows/main.yml[127-133]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Fork/no-secrets runs skip the registry login, but the Jest cluster brings up a `cert-gen` service using an image hosted at `docker.eventstore.com`. If that registry requires auth (as implied by other workflows), fork PRs will still fail when Docker Compose pulls that image.

## Issue Context
- The workflow change intentionally makes Cloudsmith login conditional on secrets availability.
- The test harness pulls at least one image from `docker.eventstore.com` regardless of the selected KURRENT runtime image.

## Fix Focus Areas
- .github/workflows/tests.yml[64-76]
- packages/test/src/utils/dockerImages.ts[1-13]
- packages/test/src/utils/Cluster.ts[38-61]

### Suggested implementation direction
- Provide a public, anonymously pullable `certGen` image source for no-secrets runs. Options:
 - Mirror `docker.eventstore.com/eventstore-utils/es-gencert-cli:latest` to a public registry and update `dockerImages.certGen` accordingly.
 - Or make `certGen` configurable via an env var (e.g., `CERTGEN_IMAGE`) and set it in workflows for fork/no-secrets runs.
 - Or run tests in insecure mode for no-secrets runs (only if test suite can be switched globally without code changes).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Dependabot image fallback mismatch ✓ Resolved 🐞 Bug ☼ Reliability
Description
main.yml selects only [lts] when secrets are unavailable (forks/Dependabot), but
load-configuration.yml falls back to a public LTS image only when vars.KURRENTDB_DOCKER_IMAGES
is absent; for Dependabot (vars present, secrets absent) this can still resolve to a private
registry while tests.yml skips login, causing image pull failures.
Code

.github/workflows/load-configuration.yml[R69-73]

+          # vars are not passed to runs triggered from forks, fall back to the
+          # anonymously pullable lts image so outside PRs can still run tests.
+          if [ -z "$IMAGES" ]; then
+            IMAGES='{"lts":{"registry":"docker.kurrent.io/eventstore","image":"eventstoredb-ee","tag":"lts"}}'
+          fi
Evidence
The runtime selection is explicitly based on secrets availability (including Dependabot), while the
image fallback is only triggered by missing vars. With the new conditional login, any private-image
resolution in the no-secrets path can break pulls.

.github/workflows/main.yml[67-84]
.github/workflows/load-configuration.yml[69-78]
.github/workflows/tests.yml[63-76]
packages/test/src/utils/dockerImages.ts[1-6]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow decides the runtime matrix based on secrets availability, but the image configuration fallback in `load-configuration.yml` is based only on whether `vars.KURRENTDB_DOCKER_IMAGES` is empty. In Dependabot/no-secrets runs, vars are typically available while secrets are not, so the workflow may still select an authenticated registry image but will skip `docker/login-action`, leading to pull failures.

## Issue Context
- `main.yml` explicitly treats Dependabot as “no secrets”.
- `load-configuration.yml` cannot currently distinguish “vars available but secrets unavailable”.

## Fix Focus Areas
- .github/workflows/main.yml[67-84]
- .github/workflows/load-configuration.yml[69-78]
- .github/workflows/tests.yml[64-76]

### Suggested implementation direction
- Add an optional input to `load-configuration.yml` (e.g., `anonymous_only: boolean`) and have `tests.yml` pass it based on the same `SECRETS_AVAILABLE` check.
- In `load-configuration.yml`, if `anonymous_only` is true, override `IMAGES` to the public LTS config even if `vars.KURRENTDB_DOCKER_IMAGES` is present.
- Alternatively, perform the override in the caller (tests workflow) and pass explicit `registry/image/tag` for the anonymous LTS case.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .github/workflows/tests.yml
Comment thread .github/workflows/load-configuration.yml Outdated
@w1am
w1am merged commit bf0d4d6 into master Aug 12, 2026
114 of 117 checks passed
@w1am
w1am deleted the fix-fork-pr-ci branch August 12, 2026 12:15

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚨 @w1am Failed to create cherry Pick PR due to error:

RequestError [HttpError]: Resource not accessible by integration
   at /home/runner/work/_actions/kurrent-io/Automations/master/cherry-pick-pr-for-label/node_modules/@octokit/request/dist-node/index.js:66:23
   at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
 status: '403',
 headers: {
   'access-control-allow-origin': '*',
   'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning',
   'content-encoding': 'gzip',
   'content-security-policy': "default-src 'none'",
   'content-type': 'application/json; charset=utf-8',
   date: 'Wed, 12 Aug 2026 12:15:59 GMT',
   'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin',
   server: 'github.com',
   'strict-transport-security': 'max-age=31536000; includeSubdomains; preload',
   'transfer-encoding': 'chunked',
   vary: 'Accept-Encoding, Accept, X-Requested-With',
   'x-accepted-github-permissions': 'contents=write',
   'x-content-type-options': 'nosniff',
   'x-frame-options': 'deny',
   'x-github-api-version-selected': '2022-11-28',
   'x-github-edge-region': 'sea',
   'x-github-media-type': 'github.v3; format=json',
   'x-github-request-id': '4028:57C74:2ECBBDC:300FE3E:6A7C63FF',
   'x-ratelimit-limit': '5000',
   'x-ratelimit-remaining': '4995',
   'x-ratelimit-reset': '1786540556',
   'x-ratelimit-resource': 'core',
   'x-ratelimit-used': '5',
   'x-xss-protection': '0'
 },
 request: {
   method: 'POST',
   url: 'https://api.github.com/repos/kurrent-io/KurrentDB-Client-NodeJS/merges',
   headers: {
     accept: 'application/vnd.github.v3+json',
     'user-agent': 'octokit-core.js/3.3.2 Node.js/24',
     authorization: 'bearer [REDACTED]',
     'content-type': 'application/json; charset=utf-8'
   },
   body: '{"base":"cherry-pick-cherry-pick/526/fix-fork-pr-ci-release/v1.3-a21ecf1f-40f1-4cec-b196-f8389854200b","commit_message":"Merge 0b7537d7b001709989b82461ce637e6b67a7fca6 into cherry-pick-cherry-pick/526/fix-fork-pr-ci-release/v1.3-a21ecf1f-40f1-4cec-b196-f8389854200b [skip ci]\\n\\n\\nskip-checks: true\\n","head":"0b7537d7b001709989b82461ce637e6b67a7fca6"}',
   request: { agent: [Agent], hook: [Function: bound bound register] }
 },
 documentation_url: 'https://docs.github.com/rest/branches/branches#merge-a-branch'
}

🚨👉 Check https://github.com/kurrent-io/KurrentDB-Client-NodeJS/actions/runs/31595604054

@linear-code

linear-code Bot commented Aug 17, 2026

Copy link
Copy Markdown

DEV-1881

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant