docs: def publish(version, stage, config_file):
      """Publish a version of the project to a stage."""
      config = _load_config(config_file)
      cfn = boto3.client('cloudformation')
  
      if version is None:
          version = config['devstage']
      elif version not in config['stage_environments'].keys() and \
              not version.isdigit():
          raise ValueError('Invalid version. Use a stage name or a numeric '
                           'version number.')
      if version == stage:
          raise ValueError('Cannot deploy a stage into itself.')
  
      # obtain previous deployment
      try:
          previous_deployment = cfn.describe_stacks(
              StackName=config['name'])['Stacks'][0]
      except botocore.exceptions.ClientError:
          raise RuntimeError('This project has not been deployed yet.')
  
      # preserve package from previous deployment
      bucket = _get_from_stack(previous_deployment, 'Parameter',
                               'LambdaS3Bucket')
      lambda_package = _get_from_stack(previous_deployment, 'Parameter',
                                       'LambdaS3Key')
  
      # prepare cloudformation template
      template_body = get_cfn_template(config)
      parameters = [
          {'ParameterKey': 'LambdaS3Bucket', 'ParameterValue': bucket},
          {'ParameterKey': 'LambdaS3Key', 'ParameterValue': lambda_package},
      ]
      stages = list(config['stage_environments'].keys())
      stages.sort()
      for s in stages:
          param = s.title() + 'Version'
          if s != stage:
              v = _get_from_stack(previous_deployment, 'Parameter', param) \
                  if previous_deployment else '$LATEST'
              v = v or '$LATEST'
          else:
              if version.isdigit():
                  # explicit version number
                  v = version
              else:
                  # publish version from a stage
                  v = _get_from_stack(previous_deployment, 'Parameter',
                                      version.title() + 'Version')
                  if v == '$LATEST':
                      # publish a new version from $LATEST
                      lmb = boto3.client('lambda')
                      v = lmb.publish_version(FunctionName=_get_from_stack(
                          previous_deployment, 'Output', 'FunctionArn'))[
                              'Version']
          parameters.append({'ParameterKey': param, 'ParameterValue': v})
  
      # run the cloudformation template
      print('Publishing {}:{} to {}...'.format(config['name'], version, stage))
      cfn.update_stack(StackName=config['name'], TemplateBody=template_body,
                       Parameters=parameters,
                       Capabilities=['CAPABILITY_IAM'])
      waiter = cfn.get_waiter('stack_update_complete')
  
      # wait for cloudformation to do its thing
      try:
          waiter.wait(StackName=config['name'])
      except botocore.exceptions.ClientError:
          raise
  
      # we are done, show status info and exit
      _print_status(config)