<commit_before>
require 'fulmar/infrastructure/model/transfer/base'
require 'time'
require 'active_support'

module Fulmar
  module Infrastructure
    module Model
      module Transfer
        # Provides syncing with versioning on the server
        #
        # Every deployment results in a new directory in the releases directory with the deployment time as
        # the folder name. A symlink 'current' points to this new directory after publish() is called.
        class RsyncWithVersions < Base
          TIME_FOLDER = '%Y-%m-%d_%H%M%S'.freeze
          TIME_READABLE = '%Y-%m-%d %H:%M:%S'.freeze

          DEFAULT_CONFIG = {
            temp_dir: 'temp',
            releases_dir: 'releases',
            shared_dir: 'shared',
            rsync: {
              exclude: nil,
              exclude_file: nil,
              chown: nil,
              chmod: nil,
              delete:  true
            },
            symlinks: {},
            limit_releases: 5,
            shared: []
          }.freeze

          # @param [Fulmar::Domain::Service::ConfigurationService] config
          def initialize(config)
            @config = config
            @config.merge(DEFAULT_CONFIG)
            super(@config)

            if @config[:rsync][:exclude_file].blank? && File.exist?(@config[:local_path] + '/.rsyncignore')
              @config[:rsync][:exclude_file] = @config[:local_path] + '/.rsyncignore'
            end

            @release_time = Time.now
          end

          # Ensures all needed services are set up
          def prepare
            super
            @remote_shell = Fulmar::Shell.new @config[:remote_path], @config.ssh_user_and_host
            @remote_shell.debug = @config[:debug]
          end

          # Copy the files via rsync to the release_path on the remote machine
          # @return [true, false] success
          def transfer
            prepare unless @prepared

            raise 'Deployment failed when trying to prepare remote directories for sync.' unless create_paths
            raise 'Deployment failed. Cannot sync files.' unless @local_shell.run(rsync_command)
            raise 'Deployment failed when trying to move file from temporary upload dir.' unless copy_temp_to_release
            raise 'Deployment failed when creating symlinks for shared folders' unless add_shared
          end

          # Publishes the current release (i.e. sets the 'current' symlink)
          # @return [true, false] success
          def publish
            prepare unless @prepared
            create_symlink
          end

          # Gets the currently generated absolute release path
          # @return [String] the release directory
          def release_path
            @config[:remote_path] + '/' + release_dir
          end

          # Gets the currently generated release directory
          # @return [String] the release directory
          def release_dir
            @config[:releases_dir] + '/' + @release_time.strftime('%Y-%m-%d_%H%M%S')
          end

          # Lists the existing releases on the remote machine
          # @param plain [boolean] if the list should be plain directory names or more readable time strings with a star for the current release
          # @return [Array] list of dirs or dates/times
          def list_releases(plain = true)
            prepare unless @prepared
            @remote_shell.run "ls -1 '#{@config[:releases_dir]}'"
            list = @remote_shell.last_output.select { |dir| dir.match(/^\d{4}-\d{2}-\d{2}_\d{6}/) }
            return list if plain

            current = current_release
            list.collect do |item|
              Time.strptime(item, TIME_FOLDER).strftime(TIME_READABLE) + (item == current ? ' *' : '')
            end
          end

          # Cleans up old releases limited by :limit_releases
          # @return [true, false] success
          def cleanup
            limit = @config[:limit_releases].to_i
            return true unless limit > 0
            releases = list_releases.sort
            return true if releases.length <= limit
            obsolete_dirs = releases[0, releases.length - limit].collect { |dir| "\"#{@config[:releases_dir]}/#{dir}\"" }
            @remote_shell.run "rm -fr #{obsolete_dirs.join(' ')}"
          end

          # Return the release at which the "current" symlinks points at
          def current_release
            prepare unless @prepared
            @remote_shell.run 'readlink -f current'
            @remote_shell.last_output.first.split('/').last
          end

          # Reverts to a given release
          #
          # @params release [String] the release folder or time string (which is found in the output list)
          # @return [true, false] success
          def revert(release = last_release)
            prepare unless @prepared
<commit_msg>Allow setting version number via environment variable<commit_after>
require 'fulmar/infrastructure/model/transfer/base'
require 'time'
require 'active_support'

module Fulmar
  module Infrastructure
    module Model
      module Transfer
        # Provides syncing with versioning on the server
        #
        # Every deployment results in a new directory in the releases directory with the deployment time as
        # the folder name. A symlink 'current' points to this new directory after publish() is called.
        class RsyncWithVersions < Base
          TIME_FOLDER = '%Y-%m-%d_%H%M%S'.freeze
          TIME_READABLE = '%Y-%m-%d %H:%M:%S'.freeze

          DEFAULT_CONFIG = {
            temp_dir: 'temp',
            releases_dir: 'releases',
            shared_dir: 'shared',
            rsync: {
              exclude: nil,
              exclude_file: nil,
              chown: nil,
              chmod: nil,
              delete:  true
            },
            symlinks: {},
            limit_releases: 5,
            version_name: Time.now.strftime('%Y-%m-%d_%H%M%S'),
            shared: []
          }.freeze

          # @param [Fulmar::Domain::Service::ConfigurationService] config
          def initialize(config)
            @config = config
            @config.merge(DEFAULT_CONFIG)
            super(@config)

            if @config[:rsync][:exclude_file].blank? && File.exist?(@config[:local_path] + '/.rsyncignore')
              @config[:rsync][:exclude_file] = @config[:local_path] + '/.rsyncignore'
            end
          end

          # Ensures all needed services are set up
          def prepare
            super
            @remote_shell = Fulmar::Shell.new @config[:remote_path], @config.ssh_user_and_host
            @remote_shell.debug = @config[:debug]

            if /^[A-Z0-9\-_]+$/ =~ @config[:version_name]
              @config[:version_name] = ENV[@config[:version_name]].split('/').last
            end
          end

          # Copy the files via rsync to the release_path on the remote machine
          # @return [true, false] success
          def transfer
            prepare unless @prepared

            raise 'Deployment failed when trying to prepare remote directories for sync.' unless create_paths
            raise 'Deployment failed. Cannot sync files.' unless @local_shell.run(rsync_command)
            raise 'Deployment failed when trying to move file from temporary upload dir.' unless copy_temp_to_release
            raise 'Deployment failed when creating symlinks for shared folders' unless add_shared
          end

          # Publishes the current release (i.e. sets the 'current' symlink)
          # @return [true, false] success
          def publish
            prepare unless @prepared
            create_symlink
          end

          # Gets the currently generated absolute release path
          # @return [String] the release directory
          def release_path
            @config[:remote_path] + '/' + release_dir
          end

          # Gets the currently generated release directory
          # @return [String] the release directory
          def release_dir
            @config[:releases_dir] + '/' + @config[:version_name]
          end

          # Lists the existing releases on the remote machine
          # @param plain [boolean] if the list should be plain directory names or more readable time strings with a star for the current release
          # @return [Array] list of dirs or dates/times
          def list_releases(plain = true)
            prepare unless @prepared
            @remote_shell.run "ls -1tr '#{@config[:releases_dir]}'"
            list = @remote_shell.last_output
            return list if plain

            current = current_release
            list.collect do |item|
              if item =~ /^\d{4}-\d{2}-\d{2}_\d{6}/
                Time.strptime(item, TIME_FOLDER).strftime(TIME_READABLE) + (item == current ? ' *' : '')
              else
                item + (item == current ? ' *' : '')
              end
            end
          end

          # Cleans up old releases limited by :limit_releases
          # @return [true, false] success
          def cleanup
            limit = @config[:limit_releases].to_i
            return true unless limit > 0
            releases = list_releases
            return true if releases.length <= limit
            obsolete_dirs = releases[0, releases.length - limit].collect { |dir| "\"#{@config[:releases_dir]}/#{dir}\"" }
            @remote_shell.run "rm -fr #{obsolete_dirs.join(' ')}"
          end

          # Return the release at which the "current" symlinks points at
          def current_release
            prepare unless @prepared
            @remote_shell.run 'readlink -f current'
            @remote_shell.last_output.first.split('/').last
          end

          # Reverts to a given release
          #
          # @params release [String] the release folder or time string (which is found in the output list)
          # @return [true, false] success
          def revert(release = last_release)
            prepare unless @prepared

{'fraction_non_alphanumeric': 0.08970632454645686, 'fraction_numerical': 0.0018041495439510874, 'mean_word_length': 1.9039580908032596, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '531577', 'n_tokens_mistral': 2622, 'n_tokens_neox': 2521, 'n_words': 818}