| load("@io_bazel_rules_go//go:def.bzl", "go_context") | 
 |  | 
 | # _parse_migrations takes a list of golang-migrate compatible schema files (in | 
 | # the format <timestamp>_some_description_{up,down}.sql) and splits them into | 
 | # 'up' and 'down' dictionaries, each a map from timestamp to underlying file. | 
 | # | 
 | # It also does some checks on the provided file names, making sure that | 
 | # golang-migrate will parse them correctly. | 
 | def _parse_migrations(files): | 
 |     uppers = {} | 
 |     downers = {} | 
 |  | 
 |     # Ensure filename fits golang-migrate format, sort into 'up' and 'down' files. | 
 |     for file in files: | 
 |         if not file.basename.endswith(".up.sql") and not file.basename.endswith(".down.sql"): | 
 |             fail("migration %s must end woth .{up,down}.sql" % file.basename) | 
 |         if len(file.basename.split('.')) != 3: | 
 |             fail("migration %s must not contain any . other than in .{up,down}.sql extension" % file.basename) | 
 |         first = file.basename.split('.')[0] | 
 |         if len(first.split('_')) < 2: | 
 |             fail("migration %s must be in <timestamp>_<name>.{up,down}.sql format" % file.basename) | 
 |         timestamp = first.split('_')[0] | 
 |         if not timestamp.isdigit(): | 
 |             fail("migration %s must be in <timestamp>_<name>.{up,down}.sql format" % file.basename) | 
 |         timestamp = int(timestamp) | 
 |         if timestamp < 1662136250: | 
 |             fail("migration %s must be in <timestamp>_<name>.{up,down}.sql format" % file.basename) | 
 |  | 
 |         if file.basename.endswith('.up.sql'): | 
 |             if timestamp in uppers: | 
 |                fail("migration %s conflicts with %s" % [file.basename, uppers[timestamp].basename]) | 
 |             uppers[timestamp] = file | 
 |         if file.basename.endswith('.down.sql'): | 
 |             if timestamp in downers: | 
 |                fail("migration %s conflicts with %s" % [file.basename, downers[timestamp].basename]) | 
 |             downers[timestamp] = file | 
 |  | 
 |     # Check each 'up' has a corresponding 'down', and vice-versa. | 
 |     for timestamp, up in uppers.items(): | 
 |         if timestamp not in downers: | 
 |             fail("%s has no corresponding 'down' migration" % up.basename) | 
 |         if downers[timestamp].basename.replace('down.sql', 'up.sql') != up.basename: | 
 |             fail("%s has no corresponding 'down' migration" % up.basename) | 
 |     for timestamp, down in downers.items(): | 
 |         if timestamp not in uppers: | 
 |             fail("%s has no corresponding 'up' migration" % down.basename) | 
 |         if uppers[timestamp].basename.replace('up.sql', 'down.sql') != down.basename: | 
 |             fail("%s has no corresponding 'up' migration" % down.basename) | 
 |  | 
 |     return uppers, downers | 
 |  | 
 | def _sqlc_go_library(ctx): | 
 |     go = go_context(ctx) | 
 |  | 
 |     importpath_parts = ctx.attr.importpath.split("/") | 
 |     package_name = importpath_parts[-1] | 
 |  | 
 |     # Split migrations into 'up' and 'down'. Only pass 'up' to sqlc. Use both | 
 |     # to generate golang-migrate compatible bindata. | 
 |     uppers, downers = _parse_migrations(ctx.files.migrations) | 
 |  | 
 |     # Make sure given queries have no repeating basenames. This ensures clean | 
 |     # mapping source SQL file name and generated Go file. | 
 |     query_basenames = [] | 
 |     for query in ctx.files.queries: | 
 |         if query.basename in query_basenames: | 
 |             fail("duplicate %s base name in query files" % query.basename) | 
 |         query_basenames.append(query.basename) | 
 |  | 
 |     # Go files generated by sqlc. | 
 |     sqlc_go_sources = [ | 
 |         # db.go and models.go always exist. | 
 |         ctx.actions.declare_file("db.go"), | 
 |         ctx.actions.declare_file("models.go"), | 
 |     ] | 
 |     # For every query file, basename.go is also generated. | 
 |     for basename in query_basenames: | 
 |         sqlc_go_sources.append(ctx.actions.declare_file(basename + ".go")) | 
 |  | 
 |     migrations_source = ctx.actions.declare_file("migrations.go") | 
 |  | 
 |     # Cockroachdb is PostgreSQL with some extra overrides to fix Go/SQL type | 
 |     # mappings. | 
 |     overrides = [] | 
 |     if ctx.attr.dialect == "cockroachdb": | 
 |         overrides = [ | 
 |             # INT is 64-bit in cockroachdb (32-bit in postgres). | 
 |             { "go_type": "int64", "db_type": "pg_catalog.int4" }, | 
 |         ] | 
 |  | 
 |     config = ctx.actions.declare_file("_config.yaml") | 
 |     # All paths in config are relative to the config file. However, Bazel paths | 
 |     # are relative to the execution root/CWD. To make things work regardless of | 
 |     # config file placement, we prepend all config paths with a `../../ ...` | 
 |     # path walk that makes the path be execroot relative again. | 
 |     config_walk = '../' * config.path.count('/') | 
 |     config_data = json.encode({ | 
 |         "version": 2, | 
 |         "sql": [ | 
 |             { | 
 |                 "schema": [config_walk + up.path for up in uppers.values()], | 
 |                 "queries": [config_walk + query.path for query in ctx.files.queries], | 
 |                 "engine": "postgresql", | 
 |                 "gen": { | 
 |                     "go": { | 
 |                         "package": package_name, | 
 |                         "out": config_walk + sqlc_go_sources[0].dirname, | 
 |                         "overrides": overrides, | 
 |                     }, | 
 |                 }, | 
 |             }, | 
 |         ], | 
 |     }) | 
 |     ctx.actions.write(config, config_data) | 
 |  | 
 |     # Generate types/functions using sqlc. | 
 |     ctx.actions.run( | 
 |         mnemonic = "SqlcGen", | 
 |         executable = ctx.executable._sqlc, | 
 |         arguments = [ | 
 |             "generate", | 
 |             "-f", config.path, | 
 |         ], | 
 |         inputs = [ | 
 |             config | 
 |         ] + uppers.values() + ctx.files.queries, | 
 |         outputs = sqlc_go_sources, | 
 |     ) | 
 |  | 
 |     # Generate migrations bindata. | 
 |     ctx.actions.run( | 
 |         mnemonic = "MigrationsEmbed", | 
 |         executable = ctx.executable._bindata, | 
 |         arguments = [ | 
 |             "-pkg", package_name, | 
 |             "-prefix", ctx.label.workspace_root, | 
 |             "-o", migrations_source.path, | 
 |         ] + [up.path for up in uppers.values()] + [down.path for down in downers.values()], | 
 |         inputs = uppers.values() + downers.values(), | 
 |         outputs = [migrations_source], | 
 |     ) | 
 |  | 
 |     library = go.new_library(go, srcs = sqlc_go_sources + [migrations_source], importparth = ctx.attr.importpath) | 
 |     source = go.library_to_source(go, ctx.attr, library, ctx.coverage_instrumented()) | 
 |     return [ | 
 |         library, | 
 |         source, | 
 |         OutputGroupInfo(go_generated_srcs = depset(library.srcs)), | 
 |     ] | 
 |  | 
 |  | 
 | sqlc_go_library = rule( | 
 |     implementation = _sqlc_go_library, | 
 |     attrs = { | 
 |         "migrations": attr.label_list( | 
 |             allow_files = True, | 
 |         ), | 
 |         "queries": attr.label_list( | 
 |             allow_files = True, | 
 |         ), | 
 |         "importpath": attr.string( | 
 |             mandatory = True, | 
 |         ), | 
 |         "dialect": attr.string( | 
 |             mandatory = True, | 
 |             values = ["postgresql", "cockroachdb"], | 
 |         ), | 
 |         "_sqlc": attr.label( | 
 |             default = Label("@com_github_kyleconroy_sqlc//cmd/sqlc"), | 
 |             allow_single_file = True, | 
 |             executable = True, | 
 |             cfg = "exec", | 
 |         ), | 
 |         "_bindata": attr.label( | 
 |             default = Label("@com_github_kevinburke_go_bindata//go-bindata"), | 
 |             allow_single_file = True, | 
 |             executable = True, | 
 |             cfg = "exec", | 
 |         ), | 
 |     }, | 
 |     toolchains = ["@io_bazel_rules_go//go:toolchain"], | 
 | ) |