Introduce top-level "Environments" within `meltano.yml` config

## Background

As Meltano becomes the end-to-end tool for more parts of dataops pipelines, there's a need to create a top-level construct and understanding of the project's SQL data repository (Snowflake, Spark, Athena, etc.) and that repository's corresponding "environments" such as "prod", "beta", "cicd", and "dev". Some of these environments may be dynamically keyed and fully "disposable", such as "cicd-<build_num>", and "dev-<person_id>".

## Today's Solutions

Today there are two or three places where a user needs to specify their data platform information - for instance "target-snowflake" config, "dbt profiles", and the analyze config, and possibly a "reverse-etl" connector such as "tap-snowflake". There's not yet a meltano-native construct for reusing this config.

## Proposed Solution

### Environment config as a top-level construct

We propose adding a top-level `environments:` config element. This could be a combination of "smart" and "passthrough" settings, where "smart" settings are known and expected by meltano, and "passthrough" settings are anything that the target (or dbt profile) may require.

### Environment Inheritance

To ease onboarding to support DRY principals - we should allow environments to inherit from each other. For instance, prod may be identical to dev except in the credentials and db name. Server name, and other connection-level parameters may be identical between these environments and we should allow the config to be reused - in the same way that the `inherits-from` construct supports this for plugins today.

### Strawman Sample

This is only intended for conversation - the exact spec will evolve based on input:

`meltano.yml`:

```yaml
project_id: ...
plugins: ...
schedules: ...
plugins:
  extractors:
  # this section unchanged - extractors referenced by name below in `sources`
  - name: tap-github
    namespace: tap_github
    executable: tap-github
    capabilities:
    - state
    - catalog
    - discover
    settings:
    - name: searches
      kind: array
    - name: auth_token
      kind: password
    - name: start_date
      value: '2010-01-01T00:00:00Z'
  # ...

environments:

- name: prod
  job_id_prefix: prod_             # will be appended with ${TAP_NAMESPACE} to make default job IDs

  # The `destination` is the singular landing location ("target") for this environment
  # Other environments inherit this destination and can customize either with `vars` or by overriding completely
  default_loader: target-snowflake

  enable_transforms: true  # or false if the target does not support transforms (e.g. target-jsonl or target-s3-csv)

  # Here we list all the sources that `prod` should contain
  # These reference names declared under `plugins->extractors`
  # Other environments may inherit this list or override it
  default_extractors:
  - tap-github
  - tap-zendesk
  - tap-slack
  - tap-github

  config:
    plugins:
      loaders:
      - name: target-snowflake
        config:
          db_name: "${DB_NAME}"                # References an environment-scoped env var
          load_schema: "${TAP_NAMESPACE}_raw"  # Dynamic from the name of the currently-executing tap
          account: "${SNOWFLAKE_ACCOUNT}"
          user: "${SNOWFLAKE_USER}"
          role: prod_loader_role
          # ...

  env:
    # Env vars can be declared here when multiple plugins need to reference the same values
    DB_NAME: "Waffleshop_DB"
    SNOWFLAKE_ACCOUNT: waffleshop.snowflakecomputing.net
    SNOWFLAKE_USER: prod_loader

- name: beta
  job_id_prefix: beta_                       # will be appended with ${TAP_NAMESPACE} to make default job IDs
  inherits_from: prod

  config:
    plugins:
      loaders:
      - name: target-snowflake
        config:
          role: beta_loader_role

  env:
    DB_NAME: "Waffleshop_Beta"
    SNOWFLAKE_USER: beta_loader

# The following environments are "disposable", meaning they are intentionally keyed for temporarily use

- name: user_dev
  job_id_prefix: dev_${USER}_                # will be appended with ${TAP_NAMESPACE} to make default job IDs
  inherits_from: beta

  config:
    plugins:
      loaders:
      - name: target-snowflake
        config:
          role: beta_loader_role
          load_schema: "${USER}dev_${TAP_NAMESPACE}_raw"        # Dynamic based on the USER environment variable

  env:
    DB_NAME: "Development"
    SNOWFLAKE_USER: "${USER}"

- name: cicd
  job_id_prefix: cicd_b${GITHUB_BUILD_NUM}_  # will be appended with ${TAP_NAMESPACE} to make default job IDs
  inherits_from: beta
  sources:
  config:
    plugins:
      extractors:
      # Overrides to make CICD run faster using a subset of data
      - name: tap-github
        config:
          searches:
          - {name: "target-athena", query: "target-athena+fork:only"}
          
      loaders:
      - name: target-snowflake
        config:
          role: cicd_loader_role
          load_schema: "raw_b${GITHUB_BUILD_NUM}_${TAP_NAMESPACE}" # Dynamic based on build number from Github CI pipeline
```

In the simplest interations, all of the environment's config would be available to invoked plugins via a name prefix such as `$MELTANO_ENV_{VAR_NAME}`, such as `$MELTANO_ENV_SNOWFLAKE_USER`, `$MELTANO_ENV_DB_NAME`. Importantly, since we expect to be toggling the environment back and forth at runtime, the generated environment variables would not have the name of the environment contained within the variable name. However, we probably could still expose the environment name itself in something like `$MELTANO_ENV_NAME`, in case a plugin has native configuration which can branch based on that name.

### Possibility for native understanding of needs of different plugin types

The above is a simple "passthrough" approach where these keywords might not be natively understood by Meltano. However, we may choose to create subprofiles (now or in a future iteration) where meltano could send slightly different config to a transformer versus what it would send to a loader.

## Best reason not to build

None I can think of. It is one level of additional config to setup, so we may want to ensure it is optional. Especially for EL-only pipelines where there are no further steps, we would not want a user to _necessarily_ need to configure an environment if they are happy with simple tap->target configuration as they perform today.

Story points: 12.0