Skip to content

chore(ingestion): support dg for code location environment variables - #837

Merged
jirhiker merged 3 commits into
stagingfrom
chore/dagster-env-var-setup
Aug 18, 2026
Merged

chore(ingestion): support dg for code location environment variables#837
jirhiker merged 3 commits into
stagingfrom
chore/dagster-env-var-setup

Conversation

@jirhiker

Copy link
Copy Markdown
Member

Setting the code location's environment variables by hand in the UI is a dozen
clicks per variable across two scopes. This makes it three commands.

Why dg and not dagster-cloud

The dagster-cloud CLI has no environment-variable command at all — its
deployment settings and organization settings cover deployment config, not
env vars. I grepped the installed package for secret/env-var mutations: nothing.
dg plus create env is the only CLI path.

The tool.dg blocks

dg refuses to run outside a directory it recognises as a project, which is
what the earlier MissingPyProjectDagsterBlockException was pointing at.

I had deferred adding these because dg can auto-scan defs/** as components
rather than importing a Definitions object, and I did not want to change how
the location loads on a guess. Verified rather than assumed: dagster_cloud.yaml
names the entry point explicitly, definitions still resolve all four assets with
the blocks in place, and the 41 tests pass.

The script

Three phases, each one pass:

./automated_ingestion/scripts/set_code_location_env.sh storage
./automated_ingestion/scripts/set_code_location_env.sh vendor
./automated_ingestion/scripts/set_code_location_env.sh database

Secrets use --from-local-env, so values are read from the shell and never
appear as arguments — not in shell history, not in process listings.

storage sets a different bucket per scope: full (prod deployment) gets
production, branch gets staging.

database is separate on purpose. It should wait until
automated_ingestion/sql/ingestion_role.sql has been run. Pointing CLOUD_SQL_*
at a role that does not exist yet makes database_connectivity fail in a way
that looks exactly like the serverless-to-Cloud-SQL problem the asset exists to
test. It also prefers IAM auth when no password is exported, which removes a
credential rather than rotating one.

Requires a user token

An agent token authenticates — a currentDeployment query returns 200 —
but is not authorized for these mutations. dg reports that as
KeyError: 'message' from its own error handler rather than as a permission
error, which is a confusing hour if you do not know to check the token prefix.
Noted in the script header.

🤖 Generated with Claude Code

The dagster-cloud CLI has no environment-variable command at all; dg does, but
refuses to run outside a directory it recognises as a project. Adding the
tool.dg blocks satisfies that check. It does not affect what the deployed code
location loads, since dagster_cloud.yaml names the entry point explicitly --
verified by loading the definitions and running the suite with the blocks in
place.

The script sets each phase in one pass. Secrets go through --from-local-env
rather than arguments, so they never reach the command line or shell history.

Phases are separate deliberately. `database` waits on ingestion_role.sql having
been run: pointing CLOUD_SQL_* at a role that does not exist makes
database_connectivity fail in a way that looks like the
serverless-to-Cloud-SQL problem it exists to test.

Note for whoever runs this: it needs a Dagster+ *user* token. An agent token
authenticates and returns data for queries, but is unauthorized for these
mutations, and dg surfaces that as a KeyError on its own error handler rather
than as a permission message.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Coverage

79.26% total — gate is 75%.

1 similar comment
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Coverage

79.26% total — gate is 75%.

jirhiker and others added 2 commits August 18, 2026 15:52
CLOUD_SQL_USER means different things in the two authentication modes and
db/engine.py passes it straight to the connector either way. The script set it
to the plain role name unconditionally and then chose IAM auth when no password
was exported, so the two settings contradicted each other -- a combination that
fails as an authentication error looking like a missing grant. It now derives
the value from whichever branch it takes.

The role DDL leads with the IAM role for the same reason, since that is the
configured path, and states the exact string CLOUD_SQL_USER has to match.

IAM authentication also needs GCP-side grants that this configuration did not
create: cloudsql.client, cloudsql.instanceUser, and the service account
registered as a CLOUD_IAM_SERVICE_ACCOUNT database user. Without them the
Postgres role exists but cannot be reached. They are gated on a
cloud_sql_instance variable so the storage half can still be applied before the
database half is decided.

Variables are no longer set with --global. This Dagster+ deployment hosts other
code locations, and deployment-level scope made the vendor and database
credentials readable by all of them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
database_connectivity failed in Dagster+ with ModuleNotFoundError: No module
named 'db', while the code location itself loaded and ingestion_heartbeat ran.

The image copies the repository to /opt/dagster/app but never installs it: the
generated requirements omit the project, and the build template only runs
`pip install .` when a setup.py exists. So db and domain are importable only
while that directory is on sys.path -- true when Dagster loads the code
location, not guaranteed in the separate process that executes a step, which is
exactly where the loader's lazy imports run.

Locally an editable install puts the repository on sys.path unconditionally,
which is why 43 tests pass and the failure appeared only once deployed. Two
tests now cover it: one asserts the path entry exists, the other imports db
from a process whose working directory is not the repository.

Importing automated_ingestion now adds the repository root itself, so the
coupling is satisfied wherever the package is imported from rather than
depending on how it was launched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Coverage

79.27% total — gate is 75%.

Coverage for the Python files changed in this PR
Name Stmts Miss Cover Missing
automated_ingestion/__init__.py 5 0 100%
TOTAL 5 0 100%

@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Your pull request is automatically being deployed to Dagster Cloud.

Location Status Link Updated
ocotillo-automated-ingestion View in Cloud Aug 18, 2026 at 11:06 PM (UTC)

@jirhiker
jirhiker merged commit 9198fbb into staging Aug 18, 2026
10 checks passed
@jirhiker
jirhiker deleted the chore/dagster-env-var-setup branch August 18, 2026 23:03
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