module Prick ### TIME EPOCH = Time.at(0).utc ### DIRECTORIES AND FILE NAMES # Shared files (part of the installation) SHARE_PATH = "#{File.dirname(File.dirname(__dir__))}/lib/share" LIBEXEC_PATH = "#{File.dirname(File.dirname(__dir__))}/lib/libexec" # Project directories DIRS = [ MIGRATION_DIR = "migration", SCHEMA_DIR = "schema", SCHEMA_PRICK_DIR = "#{SCHEMA_DIR}/prick", PUBLIC_DIR = "#{SCHEMA_DIR}/public", BIN_DIR = "bin", LIBEXEC_DIR = "libexec", VAR_DIR = "var", CACHE_DIR = "#{VAR_DIR}/cache", SPOOL_DIR = "#{VAR_DIR}/spool", TMP_DIR = "tmp", CLONE_DIR = "tmp/clones", SPEC_DIR = "spec" ] # Prick project root directory PRICK_DIR = Dir.getwd # Search path for executables PRICK_PATH = "#{ENV['PATH']}:#{PRICK_DIR}/#{BIN_DIR}:#{PRICK_DIR}/#{LIBEXEC_DIR}" # Project specification file PRICK_PROJECT_FILE = "prick.yml" PRICK_PROJECT_PATH = PRICK_PROJECT_FILE # # Environment specification file # PRICK_ENVIRONMENT_FILE = "prick.environment.yml" # PRICK_ENVIRONMENTS_PATH = PRICK_ENVIRONMENT_FILE # Context file PRICK_CONTEXT_FILE = ".prick-context" PRICK_CONTEXT_PATH = PRICK_CONTEXT_FILE # Fox state file (contains anchors and table sizes) FOX_STATE_FILE = ".fox-state.yml" FOX_STATE_PATH = FOX_STATE_FILE # Reflections file REFLECTIONS_FILE = "reflections.yml" REFLECTIONS_PATH = File.join(SCHEMA_DIR, REFLECTIONS_FILE) # Schema data file SCHEMA_VERSION_FILE = "data.sql" SCHEMA_VERSION_PATH = File.join(SCHEMA_PRICK_DIR, SCHEMA_VERSION_FILE) # Rspec temporary directory SPEC_TMP_DIR = "spec" SPEC_TMP_PATH = File.join(TMP_DIR, SPEC_TMP_DIR) # Migration diff files DIFF_FILE = "diff.sql" DIFF_FILES = [ BEFORE_TABLES_DIFF_FILE = "diff.before-tables.sql", TABLES_DIFF_FILE = "diff.tables.sql", AFTER_TABLES_DIFF_FILE = "diff.after-tables.sql" ] # Not in use: # The project state file PROJECT_STATE_FILE = ".prick-project" PROJECT_STATE_PATH = PROJECT_STATE_FILE # The the .prick-migration file PRICK_MIGRATION_FILE = ".prick-migration" PRICK_MIGRATION_PATH = File.join(MIGRATION_DIR, PRICK_MIGRATION_FILE) # The the .prick-feature file PRICK_FEATURE_FILE = ".prick-feature" PRICK_FEATURE_PATH = File.join(MIGRATION_DIR, PRICK_FEATURE_FILE) # The strip-comments executable STRIP_COMMENTS_NAME = "strip-comments" STRIP_COMMENTS_PATH = File.join(LIBEXEC_PATH, "strip-comments") # Diff files # DIFF_FILE = "diff.sql" # BEFORE_TABLES_DIFF_NAME = "diff.before-tables.sql" # TABLES_DIFF_NAME = "diff.tables.sql" # AFTER_TABLES_DIFF_NAME = "diff.after-tables.sql" # BEFORE_TABLES_DIFF_PATH = File.join(MIGRATION_DIR, BEFORE_TABLES_DIFF_NAME) # TABLES_DIFF_PATH = File.join(MIGRATION_DIR, TABLES_DIFF_NAME) # AFTER_TABLES_DIFF_PATH = File.join(MIGRATION_DIR, AFTER_TABLES_DIFF_NAME) # # TABLES_DIFF_SHARE_PATH = File.join(SHARE_PATH, "diff", TABLES_DIFF_NAME) # Dump files DUMP_EXT = "dump.gz" DUMP_GLOB = "*-[0-9]*.#{DUMP_EXT}" def self.dump_glob(project_name) "#{project_name}-*.#{DUMP_EXT}" end ### REGULAR EXPRESSIONS # Matches a system name. System names are used for objects that are external to prick # (like usernames) NAME_SUB_RE = /[a-z][a-z0-9_-]*/ NAME_RE = /^#{NAME_SUB_RE}$/ # Matches an identifier. Identifiers consist of lower case letters, digits # and underscores but not dashes because they're used as separators IDENT_SUB_RE = /[a-z][a-z0-9_]*/ IDENT_RE = /^#{IDENT_SUB_RE}$/ # Matches a project name PROJECT_NAME_SUB_RE = IDENT_SUB_RE PROJECT_NAME_RE = IDENT_RE # Matches a custom name CUSTOM_NAME_SUB_RE = IDENT_SUB_RE CUSTOM_NAME_RE = IDENT_RE # Matches a feature name FEATURE_NAME_SUB_RE = /(?!initial)#{IDENT_SUB_RE}/ FEATURE_NAME_RE = /^#{FEATURE_NAME_SUB_RE}$/ # Matches a postgres user name USER_NAME_SUB_RE = NAME_SUB_RE USER_NAME_RE = NAME_RE # Matches a major.minor.patch ('MMP') version # # The *_SEMVER REs are derived from the canonical RE # # / # (0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*) # (?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))? # (?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))? # /x # # https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string. # MMP_SEMVER_SUB_RE = /(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)/ MMP_SEMVER_RE = /^#{MMP_SEMVER_SUB_RE}$/ # Matches the prelease part of a semantic version # PRE_SEMVER_SUB_RE = /pre\.\d+/ PRE_SEMVER_RE = /^#{PRE_SEMVER_SUB_RE}$/ # Matches a semantic version # SEMVER_SUB_RE = /#{MMP_SEMVER_SUB_RE}(?:-#{PRE_SEMVER_SUB_RE})?/ SEMVER_RE = /^#{SEMVER_SUB_RE}$/ # Tag RE. The general syntax for a tag is '-v_' # # The RE defines the following captures: # $1 - custom name, can be nil # $2 - semantic version # $3 - feature name, can be nil # TAG_SUB_RE = / (?:(#{CUSTOM_NAME_SUB_RE})-)? v (#{SEMVER_SUB_RE}) (?:_(#{FEATURE_NAME_SUB_RE}))? /x TAG_RE = /^#{TAG_SUB_RE}$/ # TODO: Handle initial branches (0.0.0-initial) # Branch RE. The general syntax for a branch is '-_' # # The RE defines the following captures: # $1 - custom name, can be nil # $2 - semantic version # $3 - feature name, can be nil # BRANCH_SUB_RE = / (?:(#{CUSTOM_NAME_SUB_RE})-)? (#{SEMVER_SUB_RE}) (?:_(#{FEATURE_NAME_SUB_RE}))? /x BRANCH_RE = /^#{BRANCH_SUB_RE}$/ # Tag or branch RE. The general syntax for a branch is '-v?_' # # The RE defines the following captures: # $1 - custom name, can be nil # $2 - tag, nil or 'v' # $3 - semantic version # $4 - feature name, can be nil # VERSION_SUB_RE = / (?:(#{CUSTOM_NAME_SUB_RE})-)? (v)? (#{SEMVER_SUB_RE}) (?:_(#{FEATURE_NAME_SUB_RE}))? /x VERSION_RE = /^#{VERSION_SUB_RE}$/ # Matches an abstract release (either a release or a prerelease) # # The RE defines the following captures: # $1 - custom name, can be nil # $2 - semantic version # ABSTRACT_RELEASE_SUB_RE = / (?:(#{CUSTOM_NAME_SUB_RE})-)? (#{SEMVER_SUB_RE}) /x ABSTRACT_RELEASE_RE = /^#{ABSTRACT_RELEASE_SUB_RE}$/ # Project release RE. The general syntax is '--' # # The RE defines the following captures: # $1 - project # $2 - version # $3 - custom name, can be nil # $4 - semantic version # PROJECT_SUB_RE = /(#{PROJECT_NAME_SUB_RE})-(#{ABSTRACT_RELEASE_SUB_RE})/ PROJECT_RE = /^#{PROJECT_SUB_RE}$/ def self.project_sub_re(project_name) /(#{Regexp.escape(project_name)})(-#{VERSION_SUB_RE})/ end def self.release_re(project_name) /^#{project_sub_re(project_name)}$/ end # Matches versioned databases. Note that databases never include the feature name. # Features use the project database instead of a feature-specific database # # The RE defines the following captures # $1 - project # $2 - version # $3 - custom name, can be nil # $4 - semantic version # DATABASE_SUB_RE = PROJECT_SUB_RE DATABASE_RE = /^#{DATABASE_SUB_RE}$/ def self.database_sub_re(project_name) project_sub_re(project_name) end def self.database_re(project_name) /^#{database_sub_re(project_name)}$/ end # Matches project database and versioned databases # # The RE defines the following captures # $1 - project # $2 - version, can be nil # $3 - custom name, can be nil # $4 - semantic version, can be nil # ALL_DATABASES_SUB_RE = /(#{PROJECT_NAME_SUB_RE})(?:-(#{ABSTRACT_RELEASE_SUB_RE}))?/ ALL_DATABASES_RE = /^#{ALL_DATABASES_SUB_RE}$/ def self.all_databases_sub_re(project_name) /(#{project_name})(?:-(#{ABSTRACT_RELEASE_SUB_RE}))?/ end def self.all_databases_re(project_name) /^#{all_databases_sub_re(project_name)}$/ end # Matches temporary databases. Mostly useful when debugging because temporary databases # should be deleted on exit TMP_DATABASES_SUB_RE = /#{PROJECT_NAME_SUB_RE}-(?:base|next)/ TMP_DATABASES_RE = /^#{TMP_DATABASES_SUB_RE}$/ def self.tmp_databases_sub_re(project_name) /#{project_name}-(?:base|next)/ end def self.tmp_databases_re(project_name) /^#{tmp_databases_sub_re(project_name)}$/ end end