Table of Contents
sprocketship makes it easy to develop, manage, and deploy stored procedures in Snowflake. Using the language of your choosing, you can write the contents of your stored procedure separately from its configurations (e.g., EXECUTE AS, RETURN TYPE, etc.).
pip install sprocketship
sprocketship liftoff [DIR]Deploys all stored procedures from DIR (defaults to current directory) to Snowflake. Sprocketship will process all .js files found in the directory tree and execute CREATE PROCEDURE statements.
Options:
--show- Display the rendered SQL for each procedure during deployment--dry-run- Preview what would be deployed without connecting to Snowflake or executing SQL--only PROCEDURE_NAME- Deploy only specified procedure(s). Can be used multiple times.
Behavior:
- If a procedure fails to deploy, sprocketship will continue processing remaining procedures
- Exit code 1 if any procedure fails, 0 if all succeed
- Role switching: Uses
use_rolefrom config if specified, otherwise uses default role fromsnowflake.role
# Deploy from current directory
sprocketship liftoff
# Deploy from specific directory
sprocketship liftoff ./my_procedures
# Deploy and show generated SQL
sprocketship liftoff --show
# Preview what would be deployed without connecting to Snowflake
sprocketship liftoff --dry-run
# Deploy only specific procedures
sprocketship liftoff --only create_database --only drop_databasesprocketship build [DIR]Generates SQL files for all procedures without deploying to Snowflake. Useful for reviewing generated SQL, version control, or CI/CD pipelines.
Options:
--target PATH- Custom output directory (defaults totarget/sprocketship/)
Output:
- Creates one
.sqlfile per procedure with the full CREATE PROCEDURE statement - Files are named
{procedure_name}.sql
# Build to default location (target/sprocketship/)
sprocketship build
# Build to custom location
sprocketship build --target ./output/proceduresSprocketship requires a .sprocketship.yml configuration file at the root of your project directory (where you run the sprocketship command). Your stored procedures should be organized in subdirectories.
my_project/
├── dbt_models/
│ ├── customers.sql
│ └── products.sql
├── procedures/
│ ├── useradmin/
│ │ ├── create_database_writer_role.js
│ │ └── create_database_reader_role.js
│ └── sysadmin/
│ └── create_temp_database.js
└── .sprocketship.yml
Important: The YAML path to each procedure in .sprocketship.yml must mirror the file path structure in your procedures/ directory. For example:
- File:
procedures/sysadmin/create_temp_database.js - YAML path:
procedures.sysadmin.create_temp_database
procedures:
development:
create_temp_database:
database: !env_var SNOWFLAKE_DATABASE
schema: !env_var SNOWFLAKE_SCHEMA
...
admin:
create_database_reader:
database: !env_var SNOWFLAKE_DATABASE
schema: !env_var SNOWFLAKE_SCHEMA
...
create_database_writer:
database: !env_var SNOWFLAKE_DATABASE
schema: !env_var SNOWFLAKE_SCHEMA
...Note on Environment Variables: The !env_var tag is provided by ABSQL and substitutes environment variables at runtime. Make sure required environment variables are set before running sprocketship commands:
export SNOWFLAKE_DATABASE=my_database
export SNOWFLAKE_SCHEMA=my_schema
export SNOWFLAKE_ACCOUNT=my_account
# ... etcSprocketship uses a + prefix to mark parameters as cascading defaults. These defaults are inherited by all procedures in subdirectories, greatly reducing configuration duplication.
How it works:
- Parameters prefixed with
+cascade down the directory tree - Child directories inherit parent defaults unless they override them
- Individual procedures can override any inherited default
- Configuration priority (highest to lowest):
- File frontmatter (in the
.jsfile itself) - Direct procedure config (exact YAML path match)
- Cascading defaults from parent directories (closer ancestors override distant ones)
- File frontmatter (in the
procedures:
# for all procedures, default to the below database and schema
+database: !env_var SNOWFLAKE_DATABASE
+schema: !env_var SNOWFLAKE_SCHEMA
development:
# for all procedures in the development directory,
# default to using the sysadmin role
+use_role: sysadmin
create_temp_database:
args:
- name: Name of argument
type: Type of argument
default: (Optional) default value for the argument
returns: varcharThanks to ABSQL, sprocketship also provides the ability to define parameters using file frontmatter. Suppose we have a file create_database_writer_role.js, we can define parameters for the stored procedure within the file using frontmatter:
/*
database: my_database
schema: my_schema
language: javascript
execute_as: owner
use_role: sysadmin
*/sprocketship will automatically parse and apply the parameters defined in the frontmatter to the stored procedure.
When setting up your sprocketship project, we recommend setting more general parameters (e.g., database, schema, language, etc.) in the .sprocketship.yml file, and anything that's specific to a given procedure should be defined in the file frontmatter of that procedure, such as the args or return type. Example below:
# .sprocketship.yml
procedures:
+database: my_database
+schema: my_schema
+language: javascript
+execute_as: owner
sysadmin:
+use_role: sysadmin
useradmin:
+use_role: useradminIn the above .sprocketship.yml, we've set the database, schema, language, and executor at the highest level. This means that all procedures in the sysadmin and useradmin directories will inherit these defaults unless overridden. Now we can define procedure-specific
parameters in the file frontmatter:
// procedures/useradmin/create_role.js
/*
args:
- name: role_name
type: varchar
returns: varchar
comment: |
Creates a role with the provided name
*/
var roleName = ROLE_NAME;
snowflake.execute(`CREATE ROLE IF NOT EXISTS ${roleName}`)database: my_database # Database where procedure will be stored
schema: my_schema # Schema where procedure will be stored
language: javascript # Language of the procedure (currently only javascript)
execute_as: owner # 'owner' or 'caller'
use_role: sysadmin # Role to switch to before creating procedure
copy_grants: true # (Optional) Copy grants from existing procedure
args: # Procedure arguments
- name: arg_name # Argument name (use snake_case)
type: varchar # Snowflake data type
default: optional_value # (Optional) default value
returns: varchar # Return type (can include NOT NULL)
comment: | # (Optional) procedure description
Multi-line comment describing
what this procedure does
grant_usage: # (Optional) Grant usage after creation
role: # Grant to roles
- analyst_role
- developer_role
user: # Grant to users
- john.doe@company.comExample with grant_usage:
procedures:
+database: analytics
+schema: procedures
+language: javascript
+execute_as: owner
utilities:
format_phone_number:
args:
- name: phone_number
type: varchar
returns: varchar
comment: Formats a phone number to standard format
grant_usage:
role:
- analyst_role
- reporting_role# Clone the repository
git clone https://github.com/nicklausroach/sprocketship.git
cd sprocketship
# Install in development mode with dev dependencies
pip install -e ".[dev]"
# Or use the Makefile
make installThe project includes several tools to maintain code quality:
# Run all checks (recommended before committing)
make check
# Run individual checks
make test # Run tests with pytest
make lint # Run ruff linter
make type-check # Run mypy type checker
make dead-code # Detect unused code with vulture
# Format code
make format # Auto-format with ruffAutomated checks:
- pytest - Comprehensive test suite
- mypy - Static type checking
- ruff - Fast linting and formatting
- vulture - Dead code detection (finds unused functions, variables, imports)
When developing, you can run the CLI module directly:
python -m sprocketship.cli liftoff
python -m sprocketship.cli buildThe project includes a comprehensive test suite covering:
- Configuration hierarchy and merging
- SQL template rendering and validation
- CLI command execution
- ABSQL integration
Run tests before submitting pull requests:
pytest tests/Problem: Procedure not found or configuration not applied.
Solution: Ensure your YAML structure mirrors the procedures/ directory structure exactly:
- File:
procedures/sysadmin/create_db.js - YAML:
procedures.sysadmin.create_db(no.jsextension in YAML)
Problem: !env_var tags not substituting values.
Solution: Ensure all required environment variables are exported before running sprocketship:
export SNOWFLAKE_ACCOUNT=my_account
export SNOWFLAKE_USER=my_user
export SNOWFLAKE_PASSWORD=my_password
export SNOWFLAKE_ROLE=my_role
export SNOWFLAKE_WAREHOUSE=my_warehouseProblem: Procedure arguments not accessible in JavaScript code.
Solution: Remember the naming convention:
- In YAML/frontmatter:
snake_case(e.g.,database_name) - In JavaScript code:
UPPER_SNAKE_CASE(e.g.,DATABASE_NAME)
/*
args:
- name: database_name
type: varchar
*/
// Access as:
var dbName = DATABASE_NAME;Problem: Procedure creation fails with permission errors.
Solution: Use the use_role parameter to switch to an appropriate role before creating the procedure:
procedures:
sysadmin:
+use_role: sysadmin
create_database:
# procedure config...Behavior: When one procedure fails, sprocketship continues processing remaining procedures and exits with code 1.
What to check:
- Review the error traceback for the failed procedure
- Verify all required parameters are present
- Check Snowflake permissions for the role being used
- Test the procedure SQL locally using
sprocketship build --show
sprocketship currently only supports Javascript-based stored procedures (Python support coming soon!). Additionally, there are a few options from the CREATE STORED PROCEDURE function that are not yet supported:
CALLED ON NULL INPUT | { RETURNS NULL ON NULL INPUT | STRICT }VOLATILE | IMMUTABLE(deprecated)
Distributed under the MIT License. See LICENSE for more information.