Skip to content

ci: give Dependabot PRs a working test run - #1276

Merged
bmc08gt merged 1 commit into
code/cashfrom
claude/zealous-ardinghelli-9a97ce
Aug 20, 2026
Merged

ci: give Dependabot PRs a working test run#1276
bmc08gt merged 1 commit into
code/cashfrom
claude/zealous-ardinghelli-9a97ce

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Problem

Every Dependabot PR fails CI the same way, before Gradle runs at all:

##[error]encodedString value is not set

GitHub does not expose repository Actions secrets to pull_request runs opened by
dependabot[bot] — Dependabot reads from its own secret store — or by forks. So every
secrets.* in flipcash-tests resolves to an empty string, and timheuer/base64-to-file@v1
fails on the empty FLIPCASH2_GOOGLE_SERVICES.

The effect is loss of signal: dependency PRs are permanently red, so a genuinely broken bump
looks exactly like a healthy one, and merges go through --admin on the strength of local
verification.

A second failure was hiding behind the first

Fixing only google-services.json moves the red one step later. The secrets Gradle plugin copies
every local.properties entry into BuildConfig verbatim, so writing empty values emits invalid
Java and compileDebugJavaWithJavac fails with 4 errors:

public static final String BUGSNAG_API_KEY = ;

An empty string is not a safe fallback. Each key needs a non-empty value.

Approach

Of the options on the table, this takes the committed-placeholder route, because it needs no
repo admin and it gates on the secret being empty rather than on github.actor == 'dependabot[bot]' — which fixes fork PRs for free.

That is safe because this lane runs generateEmojiList flipcashTestDebug :apps:flipcash:app:lintDebug. It compiles, unit-tests and lints; it never contacts Firebase,
Bugsnag, Mixpanel or Coinbase.

Changes

.github/workflows/ci.yml

  • Provision google-services.json — drops the third-party timheuer/base64-to-file@v1 for a
    plain base64 --decode, falling back to .github/ci/google-services.placeholder.json.
  • Write local.properties — one step instead of four, with a zero-filled placeholder per key.
    Keys are substituted independently, so a run with partial secrets still uses the real ones it
    has. The job logs a ::notice naming every value it faked.
  • Secrets now travel via env: instead of being interpolated into the shell script. The previous
    form pasted secret text straight into a command; a value containing a quote or backtick could
    break or escape it.
  • Adds gradle/actions/wrapper-validation@v4 after checkout — a cheap supply-chain guard against
    a tampered gradle-wrapper.jar.

.github/ci/ — the placeholder Firebase config plus a README explaining why these fixtures
exist and that real keys must never go in them.

Verification

  • Ran the exact Fastlane lane task set locally with only placeholders in place:
    generateEmojiList flipcashTestDebug :apps:flipcash:app:lintDebugBUILD SUCCESSFUL,
    "Lint found no new issues". This is the real Dependabot path end-to-end, not just a YAML check.
  • Both shell steps exercised in all three modes — all secrets empty, all present, partial. With
    real secrets the generated local.properties is byte-identical to what the old workflow
    produced, so nothing changes for normal PRs.
  • ci.yml parses as YAML and the step list is as expected.
  • Wrapper validation will pass, not immediately go red: gradle-wrapper.jar is the only one in
    the tree and its sha256 7a9ce74c… matches Gradle's published 9.7.0 checksum.

Checked and deliberately left alone

  • The labeler is fine. I expected it to 403 on Dependabot PRs given the read-only token, but
    gh pr checks on live Dependabot PRs shows area-labels and type-label both passing.
  • maestro.yml and build-fcash2-upload-android.yml still use timheuer/base64-to-file@v1
    and the interpolate-secrets-into-shell pattern. They genuinely need real credentials and never
    run on Dependabot PRs, so they are out of scope here — the same env-var hardening would apply as
    a follow-up.

GitHub does not expose repository Actions secrets to `pull_request` runs
opened by dependabot[bot] (Dependabot reads from its own secret store) or
by forks, so every `secrets.*` in the CI job resolved to an empty string on
those runs. The job died at the `timheuer/base64-to-file@v1` step with
"encodedString value is not set" — before Gradle ever ran. Every dependency
PR was permanently red, so a genuinely broken bump looked exactly like a
healthy one and merges had to go through `--admin`.

The `flipcash-tests` lane only runs `generateEmojiList flipcashTestDebug
:apps:flipcash:app:lintDebug` — it compiles, unit-tests and lints, and never
contacts Firebase, Bugsnag, Mixpanel or Coinbase — so it does not need real
credentials. Fall back to committed placeholders when a secret is empty:

- google-services.json: replace the third-party base64 action with a plain
  `base64 --decode`, falling back to .github/ci/google-services.placeholder.json.
- local.properties: an *empty* value is not a safe fallback either, because
  the secrets Gradle plugin copies each entry into BuildConfig verbatim and
  emits `public static final String BUGSNAG_API_KEY = ;`. Each key gets a
  zero-filled placeholder instead, substituted independently so a run with
  partial secrets still uses the real ones it has.

Both fall back on the secret being empty rather than on `github.actor`, which
covers fork PRs too. Runs with real secrets produce a byte-identical
local.properties to before. The job logs a notice naming every value it faked.

Secrets are now passed through the environment instead of being interpolated
into the shell script, so a value containing a quote or backtick cannot break
or escape the command.

Also adds gradle/actions/wrapper-validation@v4 as a supply-chain guard on
gradle-wrapper.jar (the committed jar matches the published Gradle 9.7.0
checksum, and it is the only wrapper jar in the tree).

Verified by running the exact Fastlane lane task set locally with only the
placeholders in place: BUILD SUCCESSFUL, lint found no new issues.
@bmc08gt
bmc08gt requested a review from jeffyanta as a code owner August 20, 2026 16:07
@bmc08gt
bmc08gt merged commit d75ebbb into code/cash Aug 20, 2026
3 checks passed
@bmc08gt
bmc08gt deleted the claude/zealous-ardinghelli-9a97ce branch August 20, 2026 17:05
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.

1 participant