CrackedRuby CrackedRuby

Overview

Artifact management addresses the storage, versioning, and distribution of compiled binaries, packages, libraries, container images, and other build outputs produced during software development. An artifact represents any file or set of files generated by the build process that needs preservation and distribution to other stages of the software delivery pipeline.

The practice emerged from the need to decouple build processes from deployment processes. Early development workflows rebuilt software from source for each deployment, creating inconsistencies between environments and wasting computational resources. Artifact management repositories store the exact binaries deployed to production, creating a single source of truth for what runs in each environment.

Modern artifact management systems function as specialized file servers with versioning capabilities, access controls, and metadata indexing. They store artifacts produced by continuous integration systems and serve them to deployment processes, developer workstations, and other build processes that depend on them.

# Artifact metadata example
artifact = {
  name: 'user-service',
  version: '2.3.1',
  type: 'jar',
  checksum: 'sha256:a3b2c1...',
  build_number: 1847,
  repository: 'releases',
  coordinates: 'com.example:user-service:2.3.1'
}

The relationship between source control and artifact management creates a complete audit trail. Source control tracks code changes, while artifact repositories track the built results of those changes. Each artifact links back to a specific source revision, enabling reconstruction of exactly what code produced a particular binary.

Artifact repositories differ from source control systems in critical ways. Source control optimizes for text diff operations and merge conflict resolution. Artifact repositories optimize for binary storage, checksumming, and efficient retrieval of large files. Attempting to store compiled binaries in source control creates repository bloat and slow clone operations.

Key Principles

Immutability forms the foundation of artifact management. Once published to a repository, an artifact version never changes. The file at coordinates com.example:service:1.2.3 remains identical across all time. This immutability guarantees that deployments remain reproducible - deploying version 1.2.3 today produces the same result as deploying it six months from now.

Violating immutability breaks reproducibility. If artifact 1.2.3 changes after initial publication, systems that cached the original version behave differently from systems that fetch the updated version. The version number becomes meaningless as an identifier of specific functionality.

Versioning schemes identify distinct artifact instances. Semantic versioning (major.minor.patch) indicates compatibility and change significance. Maven coordinates combine group ID, artifact ID, and version into unique identifiers: org.example:library:2.1.0. Ruby gems use name and version: rails-7.0.4.gem.

Snapshot versions represent work-in-progress artifacts. The version 1.2.3-SNAPSHOT indicates an unstable build that may change. Repositories handle snapshots differently from release versions - snapshots allow overwrites within the same version number, while releases enforce immutability. Teams use snapshots during active development and cut release versions for production deployments.

# Version comparison and constraints
class ArtifactVersion
  include Comparable
  
  attr_reader :major, :minor, :patch, :snapshot
  
  def initialize(version_string)
    parts = version_string.split('-')
    @snapshot = parts.include?('SNAPSHOT')
    
    version_parts = parts[0].split('.').map(&:to_i)
    @major, @minor, @patch = version_parts
  end
  
  def <=>(other)
    return major <=> other.major unless major == other.major
    return minor <=> other.minor unless minor == other.minor
    return patch <=> other.patch unless patch == other.patch
    snapshot ? -1 : 0  # Snapshots sort before releases
  end
  
  def release?
    !snapshot
  end
end

# => Usage
v1 = ArtifactVersion.new('2.3.1-SNAPSHOT')
v2 = ArtifactVersion.new('2.3.1')
v1 < v2  # => true

Repository types serve different purposes in the artifact lifecycle. Release repositories store immutable production artifacts. Snapshot repositories hold development builds. Remote repositories proxy external sources like Maven Central or RubyGems, caching artifacts locally. Virtual repositories aggregate multiple repositories behind a single URL.

Checksums and signatures verify artifact integrity and authenticity. Every artifact publish generates cryptographic checksums (SHA-256, SHA-512) stored alongside the artifact. Download clients verify checksums match before using artifacts, detecting corruption or tampering. Digital signatures using GPG or similar tools prove artifacts came from trusted publishers.

Metadata and indexing enable artifact discovery. Repositories maintain indices of available artifacts, versions, and dependencies. Search operations query this metadata rather than scanning file storage. Metadata includes publication timestamps, file sizes, dependent libraries, and custom properties.

The dependency graph represents the network of artifacts that depend on each other. A web application artifact depends on dozens of library artifacts, which themselves depend on other libraries. Artifact management systems track these dependencies, enabling dependency resolution and transitive dependency analysis.

Access control restricts artifact operations based on user identity and repository policy. Anonymous users might read from public repositories but not write. Authenticated developers can publish snapshots but only automated systems can publish releases. Repository administrators control these permission mappings.

Implementation Approaches

Centralized repository architecture positions a single artifact server as the organization's primary artifact store. All build systems publish to this central location, and all deployment systems retrieve from it. This approach simplifies access control, backup procedures, and audit logging. The central repository becomes a critical infrastructure component requiring high availability.

Organizations implement centralized repositories using dedicated server instances running Artifactory, Nexus, or similar platforms. The repository server handles authentication, storage management, and API access. Build pipelines authenticate using service accounts with publish permissions. Deployment systems use read-only credentials.

# Centralized repository configuration
class ArtifactRepository
  def initialize(config)
    @base_url = config[:url]
    @repository = config[:repository]
    @credentials = config[:credentials]
    @http_client = build_http_client
  end
  
  def publish(artifact)
    path = artifact_path(artifact)
    url = "#{@base_url}/#{@repository}/#{path}"
    
    response = @http_client.put(url) do |req|
      req.headers['Content-Type'] = artifact.content_type
      req.headers['X-Checksum-Sha256'] = artifact.checksum
      req.body = artifact.read
    end
    
    raise PublishError, response.body unless response.success?
    
    { url: url, checksum: artifact.checksum }
  end
  
  def fetch(coordinates)
    path = coordinates_to_path(coordinates)
    url = "#{@base_url}/#{@repository}/#{path}"
    
    response = @http_client.get(url)
    raise NotFoundError, coordinates unless response.success?
    
    verify_checksum(response.body, response.headers['X-Checksum-Sha256'])
    response.body
  end
  
  private
  
  def artifact_path(artifact)
    "#{artifact.group_id.tr('.', '/')}/#{artifact.artifact_id}/#{artifact.version}/#{artifact.filename}"
  end
end

Distributed repository architecture replicates artifacts across multiple geographic regions or organizational boundaries. Each region maintains a local repository that mirrors critical artifacts, reducing network latency and improving availability. Changes propagate between repositories using replication protocols.

Distributed architectures address global development teams and multi-region deployments. Developers in Asia pull artifacts from an Asian repository instance rather than crossing oceans to reach a US-based server. Replication latency becomes a consideration - artifacts published in one region may take minutes to appear in others.

Proxy and cache patterns reduce external bandwidth and improve build reliability. A proxy repository sits between internal build systems and external artifact sources like Maven Central. When a build requests an external artifact, the proxy downloads it once and caches it locally. Subsequent requests serve from the cache.

Caching external dependencies protects against external repository outages. If Maven Central becomes unavailable, cached artifacts remain accessible. This pattern also enforces organizational policies - administrators can block specific external artifacts or require security scanning before cache entry.

Namespace partitioning organizes artifacts into logical groupings. Group IDs in Maven coordinates serve this purpose: com.example.authentication for authentication services, com.example.data for data access libraries. Ruby gems use flat namespacing but often employ prefixes: activerecord, activesupport.

Clear namespace conventions prevent naming conflicts and communicate artifact ownership. The namespace com.example.mobile immediately identifies artifacts belonging to the mobile team. Enforcing namespace permissions ensures teams publish only to their designated namespaces.

Retention policies manage repository growth over time. Snapshot repositories accumulate builds quickly - daily builds of dozens of projects create thousands of snapshot artifacts monthly. Retention policies automatically delete snapshots older than a threshold (30 days, 90 days) or keep only the latest N builds per artifact.

Release artifacts typically receive different treatment. Major versions might retain indefinitely to support legacy deployments, while minor versions expire after a year. Custom retention rules account for regulatory requirements or specific artifact importance.

Tools & Ecosystem

JFrog Artifactory provides comprehensive artifact management with support for multiple package formats. It stores Maven artifacts, Docker images, npm packages, RubyGems, and numerous other formats in a single platform. Artifactory offers both cloud-hosted and self-hosted deployment options.

Artifactory's repository types include local repositories for internal artifacts, remote repositories that proxy external sources, and virtual repositories that aggregate others. Advanced features include build integration that captures complete build metadata, promotion workflows that move artifacts between repositories, and replication across geographic regions.

The platform exposes a REST API for artifact operations:

require 'net/http'
require 'json'

class ArtifactoryClient
  def initialize(url, api_key)
    @url = url
    @api_key = api_key
  end
  
  def upload_gem(gem_file, repository = 'gems-local')
    uri = URI("#{@url}/#{repository}/#{File.basename(gem_file)}")
    
    request = Net::HTTP::Put.new(uri)
    request['X-JFrog-Art-Api'] = @api_key
    request.body = File.read(gem_file)
    
    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end
    
    JSON.parse(response.body)
  end
  
  def search_artifacts(name, repository = 'gems-local')
    uri = URI("#{@url}/api/search/artifact")
    uri.query = URI.encode_www_form(name: name, repos: repository)
    
    request = Net::HTTP::Get.new(uri)
    request['X-JFrog-Art-Api'] = @api_key
    
    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end
    
    JSON.parse(response.body)['results']
  end
end

# => Usage
client = ArtifactoryClient.new('https://artifacts.example.com/artifactory', ENV['ARTIFACTORY_API_KEY'])
result = client.upload_gem('pkg/myapp-1.2.3.gem')
# => {"repo":"gems-local","path":"myapp-1.2.3.gem","created":"2025-10-07T15:30:00.000Z"}

Sonatype Nexus Repository serves similar purposes with a focus on Java ecosystem integration. Nexus Repository Manager handles Maven, npm, Docker, and other formats. The platform includes security scanning capabilities that analyze artifacts for known vulnerabilities during upload.

Nexus provides repository health checks, identifying artifacts with security issues or license compliance problems. Organizations use these features to enforce policies - blocking deployment of artifacts with critical vulnerabilities or incompatible licenses.

GitHub Packages integrates artifact management directly into GitHub repositories. It supports Docker containers, npm packages, RubyGems, and Maven artifacts. GitHub Packages links artifacts to source repositories automatically, creating tight coupling between code and build outputs.

Authentication uses GitHub personal access tokens or GitHub Actions secrets. Publishing from GitHub Actions requires minimal configuration:

# .github/workflows/publish.yml integration
# Gemspec configuration for GitHub Packages
Gem::Specification.new do |spec|
  spec.name = 'example-gem'
  spec.version = '1.0.0'
  spec.authors = ['Development Team']
  
  spec.metadata['allowed_push_host'] = 'https://rubygems.pkg.github.com/organization'
end

AWS CodeArtifact provides managed artifact repositories in AWS infrastructure. It integrates with AWS IAM for access control and AWS CloudTrail for audit logging. CodeArtifact supports Maven, npm, Python, and NuGet repositories with cross-region replication.

The service handles repository infrastructure, backups, and scaling automatically. Pricing follows AWS patterns - charges based on storage volume and data transfer. Organizations already using AWS services find tight integration with CodePipeline and other AWS tools.

GitLab Package Registry includes artifact management in GitLab's integrated DevOps platform. The registry stores packages alongside source code, merge requests, and CI/CD pipelines. It supports Maven, npm, Composer, NuGet, and PyPI formats.

RubyGems.org serves as the public repository for Ruby gems but organizations often run private gem servers. Tools like gem server, geminabox, and gemfury provide private gem hosting:

# Publishing to private gem server using geminabox
require 'gemfury'

class PrivateGemPublisher
  def initialize(push_token)
    @client = Gemfury::Client.new(user_api_key: push_token)
  end
  
  def publish(gem_file)
    response = @client.push_gem(File.new(gem_file))
    
    {
      name: response['name'],
      version: response['version'],
      published_at: response['created_at']
    }
  rescue Gemfury::NotFound
    raise "Failed to publish gem"
  end
  
  def versions(gem_name)
    @client.list_gem_versions(gem_name).map do |version|
      {
        version: version['version'],
        created: version['created_at']
      }
    end
  end
end

Docker Registry stores container images, a specialized form of artifact. Docker Hub provides public hosting, while Docker Registry and Harbor provide self-hosted alternatives. Container registries implement the OCI Distribution Specification for standardized image storage and retrieval.

Ruby Implementation

Ruby projects generate artifacts primarily as gems - packages containing Ruby code, dependencies, and metadata. The gem build command compiles a gemspec into a .gem file, which functions as the distributable artifact.

# myapp.gemspec - defines the artifact
Gem::Specification.new do |spec|
  spec.name        = 'myapp'
  spec.version     = '2.1.0'
  spec.summary     = 'Application service'
  spec.description = 'Core business logic service'
  spec.authors     = ['Development Team']
  spec.email       = 'dev@example.com'
  spec.files       = Dir['lib/**/*.rb', 'bin/*', 'README.md']
  spec.binaries    = ['myapp-server']
  spec.require_paths = ['lib']
  
  # Runtime dependencies become artifact dependencies
  spec.add_runtime_dependency 'sinatra', '~> 3.0'
  spec.add_runtime_dependency 'sequel', '~> 5.0'
  
  # Development dependencies not included in production artifact
  spec.add_development_dependency 'rspec', '~> 3.12'
  spec.add_development_dependency 'rubocop', '~> 1.50'
  
  # Metadata for artifact discovery
  spec.metadata = {
    'source_code_uri' => 'https://github.com/example/myapp',
    'changelog_uri' => 'https://github.com/example/myapp/CHANGELOG.md',
    'bug_tracker_uri' => 'https://github.com/example/myapp/issues'
  }
end

Building and publishing gems integrates with artifact repositories:

# Rakefile - automates artifact creation and publishing
require 'rubygems/package_task'
require 'rake/clean'

spec = Gem::Specification.load('myapp.gemspec')

Gem::PackageTask.new(spec) do |pkg|
  pkg.need_tar = false
end

namespace :artifact do
  desc 'Publish gem to private repository'
  task :publish => :gem do
    gem_file = "pkg/myapp-#{spec.version}.gem"
    
    # Push to private repository
    sh "gem push #{gem_file} --host https://gems.example.com"
    
    # Record publication metadata
    File.write('pkg/publish.json', JSON.pretty_generate({
      artifact: spec.name,
      version: spec.version,
      published_at: Time.now.iso8601,
      checksum: Digest::SHA256.file(gem_file).hexdigest
    }))
  end
  
  desc 'Verify artifact integrity'
  task :verify do
    gem_file = "pkg/myapp-#{spec.version}.gem"
    
    # Verify gem can be unpacked
    Dir.mktmpdir do |tmpdir|
      sh "gem unpack #{gem_file} --target=#{tmpdir}"
      puts "Artifact verified successfully"
    end
  end
end

Rails applications don't typically distribute as gems but generate deployment artifacts through asset compilation and dependency bundling:

# lib/tasks/artifact.rake
namespace :artifact do
  desc 'Prepare deployment artifact'
  task :prepare => :environment do
    # Precompile assets to create static artifact files
    Rake::Task['assets:precompile'].invoke
    
    # Bundle dependencies for deployment
    sh 'bundle config set --local deployment true'
    sh 'bundle package --all'
    
    # Create artifact manifest
    manifest = {
      rails_version: Rails.version,
      ruby_version: RUBY_VERSION,
      asset_digest: asset_digest,
      bundled_gems: bundled_gem_list,
      created_at: Time.now.iso8601
    }
    
    File.write('artifact_manifest.json', JSON.pretty_generate(manifest))
  end
  
  def asset_digest
    Rails.application.assets_manifest.files.transform_values { |attrs| attrs['integrity'] }
  end
  
  def bundled_gem_list
    Bundler.load.specs.map do |spec|
      {
        name: spec.name,
        version: spec.version.to_s,
        source: spec.source.class.name
      }
    end
  end
end

Managing gem dependencies involves configuring Bundler to fetch from private repositories:

# Gemfile - configure artifact sources
source 'https://rubygems.org'

# Private gem server for internal artifacts
source 'https://gems.example.com' do
  gem 'internal-auth-lib', '~> 2.0'
  gem 'internal-logging', '~> 1.5'
end

# Public gems
gem 'rails', '~> 7.0.0'
gem 'pg', '~> 1.4'

# Lock specific versions for reproducibility
gem 'redis', '5.0.6'  # Exact version pin

group :development, :test do
  gem 'rspec-rails', '~> 6.0'
end

Bundler configuration for artifact authentication:

# Bundle configuration stored in .bundle/config
# Credentials for private artifact repositories

# Configure programmatically
Bundler.settings.set_global('gems.example.com', ENV['GEM_SERVER_TOKEN'])

# Or via bundle config command:
# bundle config gems.example.com $GEM_SERVER_TOKEN

Docker containers represent another artifact format for Ruby applications:

# Dockerfile - defines container artifact
FROM ruby:3.2-slim

WORKDIR /app

# Copy dependency specifications first for layer caching
COPY Gemfile Gemfile.lock ./

# Install dependencies from artifact repositories
RUN bundle config set --local deployment true && \
    bundle config set --local without development:test && \
    bundle install

# Copy application code
COPY . .

# Precompile assets as part of artifact
RUN bundle exec rake assets:precompile

# Artifact metadata as labels
LABEL version="2.1.0" \
      build.number="1847" \
      build.timestamp="2025-10-07T15:30:00Z" \
      source.repository="https://github.com/example/myapp" \
      source.commit="a3b2c1d4e5f6"

CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

Practical Examples

Continuous Integration Pipeline demonstrates artifact creation and publication in an automated build:

# ci/build_pipeline.rb - orchestrates artifact lifecycle
require 'json'
require 'digest'
require 'net/http'

class BuildPipeline
  attr_reader :version, :build_number
  
  def initialize
    @version = File.read('VERSION').strip
    @build_number = ENV['CI_BUILD_NUMBER']
    @artifact_repo = ENV['ARTIFACT_REPOSITORY_URL']
  end
  
  def execute
    validate_environment
    build_artifacts
    run_tests
    calculate_checksums
    publish_artifacts
    tag_release
  end
  
  private
  
  def validate_environment
    required_vars = %w[CI_BUILD_NUMBER ARTIFACT_REPOSITORY_URL REPOSITORY_TOKEN]
    missing = required_vars.select { |var| ENV[var].nil? }
    
    raise "Missing environment variables: #{missing.join(', ')}" if missing.any?
  end
  
  def build_artifacts
    puts "Building version #{@version} (build #{@build_number})"
    
    # Build gem artifact
    sh 'gem build myapp.gemspec'
    
    # Build Docker image artifact
    sh "docker build -t myapp:#{@version}-#{@build_number} ."
    sh "docker tag myapp:#{@version}-#{@build_number} myapp:latest"
  end
  
  def run_tests
    sh 'bundle exec rspec'
  end
  
  def calculate_checksums
    gem_file = "myapp-#{@version}.gem"
    
    @checksums = {
      sha256: Digest::SHA256.file(gem_file).hexdigest,
      sha512: Digest::SHA512.file(gem_file).hexdigest
    }
    
    File.write("#{gem_file}.sha256", @checksums[:sha256])
    File.write("#{gem_file}.sha512", @checksums[:sha512])
  end
  
  def publish_artifacts
    gem_file = "myapp-#{@version}.gem"
    
    # Publish gem to artifact repository
    uri = URI("#{@artifact_repo}/gems/#{gem_file}")
    request = Net::HTTP::Put.new(uri)
    request['Authorization'] = "Bearer #{ENV['REPOSITORY_TOKEN']}"
    request['X-Checksum-Sha256'] = @checksums[:sha256]
    request.body = File.read(gem_file)
    
    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end
    
    raise "Failed to publish: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
    
    # Publish Docker image
    sh "docker push myapp:#{@version}-#{@build_number}"
    sh "docker push myapp:latest"
    
    record_publication_metadata
  end
  
  def record_publication_metadata
    metadata = {
      version: @version,
      build_number: @build_number,
      checksums: @checksums,
      artifacts: [
        {
          type: 'gem',
          filename: "myapp-#{@version}.gem",
          url: "#{@artifact_repo}/gems/myapp-#{@version}.gem"
        },
        {
          type: 'docker',
          tag: "myapp:#{@version}-#{@build_number}",
          registry: ENV['DOCKER_REGISTRY']
        }
      ],
      built_at: Time.now.iso8601,
      source_commit: ENV['GIT_COMMIT']
    }
    
    File.write('build_metadata.json', JSON.pretty_generate(metadata))
  end
  
  def tag_release
    return unless release_build?
    
    sh "git tag v#{@version}"
    sh "git push origin v#{@version}"
  end
  
  def release_build?
    !@version.include?('SNAPSHOT') && ENV['CI_BRANCH'] == 'main'
  end
  
  def sh(cmd)
    puts "$ #{cmd}"
    system(cmd) || raise("Command failed: #{cmd}")
  end
end

Deployment Process retrieves artifacts and deploys to production:

# deploy/artifact_deployer.rb
class ArtifactDeployer
  def initialize(environment)
    @environment = environment
    @config = load_config(environment)
  end
  
  def deploy(artifact_coordinates)
    artifact = fetch_artifact(artifact_coordinates)
    verify_artifact(artifact)
    prepare_deployment_directory
    extract_artifact(artifact)
    update_dependencies
    restart_services
    verify_deployment
  end
  
  private
  
  def fetch_artifact(coordinates)
    # Coordinates: name:version or name:version:build_number
    name, version, build_number = coordinates.split(':')
    
    url = if build_number
      "#{@config[:repository]}/gems/#{name}-#{version}.gem"
    else
      "#{@config[:repository]}/gems/#{name}-#{version}.gem"
    end
    
    uri = URI(url)
    request = Net::HTTP::Get.new(uri)
    request['Authorization'] = "Bearer #{@config[:token]}"
    
    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end
    
    raise "Artifact not found: #{coordinates}" unless response.is_a?(Net::HTTPSuccess)
    
    {
      content: response.body,
      checksum: response['X-Checksum-Sha256'],
      filename: "#{name}-#{version}.gem"
    }
  end
  
  def verify_artifact(artifact)
    computed = Digest::SHA256.hexdigest(artifact[:content])
    
    if artifact[:checksum] && computed != artifact[:checksum]
      raise "Checksum mismatch! Expected #{artifact[:checksum]}, got #{computed}"
    end
    
    # Write artifact to disk
    File.binwrite("deploy/#{artifact[:filename]}", artifact[:content])
    
    # Verify gem structure
    Dir.mktmpdir do |tmpdir|
      system("gem unpack deploy/#{artifact[:filename]} --target=#{tmpdir}")
      raise "Invalid gem structure" unless $?.success?
    end
  end
  
  def prepare_deployment_directory
    FileUtils.mkdir_p('deploy/releases')
    @release_path = "deploy/releases/#{Time.now.strftime('%Y%m%d%H%M%S')}"
    FileUtils.mkdir_p(@release_path)
  end
  
  def extract_artifact(artifact)
    system("gem unpack deploy/#{artifact[:filename]} --target=#{@release_path}")
  end
  
  def update_dependencies
    Dir.chdir(@release_path) do
      # Install dependencies from artifact repositories
      system('bundle config set --local deployment true')
      system('bundle install')
    end
  end
  
  def load_config(environment)
    JSON.parse(File.read("config/deploy/#{environment}.json"), symbolize_names: true)
  end
end

Multi-Format Artifact Management handles different artifact types in a unified workflow:

class MultiFormatArtifactManager
  def initialize(repository_url, credentials)
    @repository_url = repository_url
    @credentials = credentials
  end
  
  def publish_release(version)
    artifacts = []
    
    # Build and publish gem
    gem_artifact = build_gem(version)
    publish_gem(gem_artifact)
    artifacts << gem_artifact
    
    # Build and publish Docker image
    docker_artifact = build_docker_image(version)
    publish_docker_image(docker_artifact)
    artifacts << docker_artifact
    
    # Generate release notes
    generate_release_manifest(version, artifacts)
  end
  
  def build_gem(version)
    gem_file = "myapp-#{version}.gem"
    system("gem build myapp.gemspec")
    
    {
      type: 'gem',
      filename: gem_file,
      version: version,
      checksum: Digest::SHA256.file(gem_file).hexdigest,
      size: File.size(gem_file)
    }
  end
  
  def publish_gem(artifact)
    uri = URI("#{@repository_url}/gems/#{artifact[:filename]}")
    
    File.open(artifact[:filename], 'rb') do |file|
      request = Net::HTTP::Put.new(uri)
      request['Authorization'] = "Bearer #{@credentials[:token]}"
      request['Content-Type'] = 'application/octet-stream'
      request['X-Checksum-Sha256'] = artifact[:checksum]
      request.body = file.read
      
      response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
        http.request(request)
      end
      
      raise "Publish failed: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
    end
    
    artifact[:published_url] = uri.to_s
    artifact[:published_at] = Time.now.iso8601
  end
  
  def build_docker_image(version)
    image_tag = "myapp:#{version}"
    system("docker build -t #{image_tag} .")
    
    # Get image digest
    digest = `docker inspect --format='{{index .RepoDigests 0}}' #{image_tag}`.strip
    
    {
      type: 'docker',
      tag: image_tag,
      digest: digest,
      version: version
    }
  end
  
  def generate_release_manifest(version, artifacts)
    manifest = {
      version: version,
      released_at: Time.now.iso8601,
      artifacts: artifacts,
      git_commit: `git rev-parse HEAD`.strip,
      git_tag: "v#{version}",
      changelog: extract_changelog(version)
    }
    
    File.write("releases/#{version}.json", JSON.pretty_generate(manifest))
  end
end

Common Patterns

Snapshot and Release Workflow separates development artifacts from production artifacts:

class SnapshotReleaseWorkflow
  def initialize
    @version = calculate_version
    @is_release = release_branch?
  end
  
  def publish
    artifact_version = @is_release ? @version : "#{@version}-SNAPSHOT"
    repository = @is_release ? 'releases' : 'snapshots'
    
    build_artifact(artifact_version)
    publish_to_repository(repository, artifact_version)
    
    if @is_release
      tag_release
      promote_to_production_repo
    end
  end
  
  private
  
  def calculate_version
    # Read from VERSION file or git tags
    File.read('VERSION').strip
  end
  
  def release_branch?
    current_branch = `git rev-parse --abbrev-ref HEAD`.strip
    current_branch == 'main' || current_branch.start_with?('release/')
  end
  
  def publish_to_repository(repository, version)
    # Snapshots allow overwrite, releases do not
    allow_overwrite = repository == 'snapshots'
    
    # Implementation details...
  end
end

Dependency Lock Pattern ensures reproducible builds by locking artifact versions:

# Gemfile.lock serves this purpose for Ruby
# Example of custom lock file for non-gem artifacts

class DependencyLock
  def initialize(lockfile_path = 'artifact.lock')
    @lockfile_path = lockfile_path
    @dependencies = load_lock_file
  end
  
  def resolve_dependencies(requirements)
    resolved = {}
    
    requirements.each do |name, constraint|
      locked_version = @dependencies[name]
      
      if locked_version && satisfies_constraint?(locked_version, constraint)
        resolved[name] = locked_version
      else
        # Resolve new version and update lock
        resolved[name] = resolve_version(name, constraint)
        @dependencies[name] = resolved[name]
      end
    end
    
    save_lock_file
    resolved
  end
  
  private
  
  def load_lock_file
    return {} unless File.exist?(@lockfile_path)
    JSON.parse(File.read(@lockfile_path))
  end
  
  def save_lock_file
    File.write(@lockfile_path, JSON.pretty_generate({
      locked_at: Time.now.iso8601,
      dependencies: @dependencies
    }))
  end
end

Promotion Pipeline Pattern moves artifacts through quality gates:

class ArtifactPromotionPipeline
  STAGES = ['snapshot', 'testing', 'staging', 'production'].freeze
  
  def initialize(artifact_id)
    @artifact = fetch_artifact_metadata(artifact_id)
    @current_stage = @artifact[:stage] || 'snapshot'
  end
  
  def promote
    next_stage = STAGES[STAGES.index(@current_stage) + 1]
    raise "Already at final stage" if next_stage.nil?
    
    validate_promotion_criteria(next_stage)
    copy_artifact_to_stage(next_stage)
    update_metadata(next_stage)
    notify_stakeholders(next_stage)
    
    next_stage
  end
  
  private
  
  def validate_promotion_criteria(target_stage)
    case target_stage
    when 'testing'
      raise "Build must pass" unless @artifact[:build_status] == 'passed'
    when 'staging'
      raise "Tests must pass" unless @artifact[:test_status] == 'passed'
      raise "Security scan required" unless @artifact[:security_scan_passed]
    when 'production'
      raise "Approval required" unless @artifact[:approved_by]
      raise "Staging validation required" unless @artifact[:staging_validated]
    end
  end
  
  def copy_artifact_to_stage(stage)
    source_repo = repository_for_stage(@current_stage)
    target_repo = repository_for_stage(stage)
    
    # Copy artifact between repositories
    artifact_path = @artifact[:path]
    
    # Implementation uses repository APIs to copy
    # Some systems support server-side copy without download/upload
  end
end

Artifact Mirroring Pattern maintains local copies of external dependencies:

class ArtifactMirror
  def initialize(external_source, local_repository)
    @external = external_source
    @local = local_repository
    @cache_ttl = 3600 # 1 hour
  end
  
  def fetch(artifact_coordinates)
    # Check local cache first
    if cached = @local.fetch(artifact_coordinates)
      return cached if cache_valid?(cached)
    end
    
    # Fetch from external source
    external_artifact = @external.fetch(artifact_coordinates)
    
    # Store in local repository
    @local.store(artifact_coordinates, external_artifact, {
      cached_at: Time.now,
      source: @external.name
    })
    
    external_artifact
  end
  
  def preload_dependencies(manifest)
    # Bulk mirror dependencies before they're needed
    manifest.each do |dependency|
      fetch(dependency) unless @local.exists?(dependency)
    end
  end
  
  private
  
  def cache_valid?(cached_artifact)
    cached_time = cached_artifact[:cached_at]
    Time.now - Time.parse(cached_time) < @cache_ttl
  end
end

Security Implications

Access Control prevents unauthorized artifact publication and consumption. Production artifact repositories require authentication for all operations. Read access might be open within the organization but write access restricts to automated build systems and designated release engineers.

Role-based access control maps users to permissions. Developers receive read access to all repositories and write access to snapshot repositories. CI/CD systems use service accounts with write permissions to specific repositories. Repository administrators control permission assignments.

class ArtifactAccessControl
  def initialize(repository, auth_provider)
    @repository = repository
    @auth = auth_provider
  end
  
  def authorize_read(user, artifact)
    permissions = @auth.get_permissions(user)
    
    if @repository == 'public'
      return true
    elsif @repository == 'snapshots'
      permissions.include?(:read_snapshots) || permissions.include?(:admin)
    elsif @repository == 'releases'
      permissions.include?(:read_releases) || permissions.include?(:admin)
    else
      permissions.include?(:admin)
    end
  end
  
  def authorize_write(user, artifact)
    permissions = @auth.get_permissions(user)
    
    # Only CI systems and admins can write to releases
    if @repository == 'releases'
      return permissions.include?(:ci_publisher) || permissions.include?(:admin)
    end
    
    # Developers can write to snapshots
    if @repository == 'snapshots'
      return permissions.include?(:developer) || permissions.include?(:admin)
    end
    
    false
  end
end

Checksum Verification protects against corrupted or tampered artifacts. Every artifact download must verify checksums match before execution. Clients reject artifacts with mismatched checksums and report the discrepancy.

def download_and_verify(artifact_url, expected_checksum)
  response = Net::HTTP.get_response(URI(artifact_url))
  raise "Download failed" unless response.is_a?(Net::HTTPSuccess)
  
  content = response.body
  actual_checksum = Digest::SHA256.hexdigest(content)
  
  if actual_checksum != expected_checksum
    raise SecurityError, "Checksum mismatch for #{artifact_url}. " \
                        "Expected: #{expected_checksum}, Got: #{actual_checksum}"
  end
  
  content
end

Signature Verification proves artifact authenticity through cryptographic signatures. Publishers sign artifacts with private keys, and consumers verify signatures using public keys. This prevents artifact substitution attacks.

Organizations maintain key infrastructure for artifact signing. Build systems access signing keys through secure credential management. Public keys distribute to developers and deployment systems for verification.

Vulnerability Scanning identifies security issues in artifacts before deployment. Automated scanners analyze artifacts for known vulnerabilities in dependencies. Critical vulnerabilities block artifact promotion to production.

Integration with vulnerability databases provides up-to-date threat information. Scanners check dependencies against CVE databases and report findings. Security teams review scan results and approve exceptions when necessary.

Artifact Immutability prevents tampering after publication. Once an artifact publishes to a release repository, the system rejects any attempts to modify it. This immutability provides assurance that artifacts haven't changed since their security scan and approval.

Attempting to republish to an existing version fails:

def publish_artifact(artifact, version)
  coordinates = "#{artifact.name}:#{version}"
  
  if repository.exists?(coordinates) && repository.type == 'releases'
    raise ImmutabilityError, "Artifact #{coordinates} already exists and cannot be modified"
  end
  
  repository.store(coordinates, artifact)
end

Credential Management protects artifact repository access tokens. Build systems retrieve credentials from secret management services rather than storing them in source control. Credentials rotate regularly and have limited scopes.

Token scopes limit damage from credential compromise. A token with only read access to snapshots cannot publish to production repositories. Time-limited tokens expire automatically, requiring regular renewal.

Reference

Artifact Types and Formats

Type Extension Description Primary Use
Ruby Gem .gem Packaged Ruby library or application Ruby dependencies
Maven JAR .jar Java archive Java libraries and applications
Maven WAR .war Web application archive Java web applications
Docker Image N/A Container image Application deployment
npm Package .tgz Node.js package archive JavaScript dependencies
Python Wheel .whl Python distribution format Python dependencies
Tarball .tar.gz Compressed archive Source distributions
Debian Package .deb Debian package format Linux software distribution

Repository Types

Repository Type Mutability Purpose Typical Retention
Snapshot Mutable Development builds 30-90 days
Release Immutable Production artifacts Indefinite or 1+ years
Remote Cached Proxy external sources Based on usage
Virtual N/A Aggregate repositories N/A

Common Artifact Coordinates

Ecosystem Coordinate Format Example
Maven groupId:artifactId:version com.example:service:2.1.0
Ruby Gems name-version rails-7.0.4
npm @scope/name@version @company/package@1.2.3
Docker registry/repository:tag docker.io/myapp:2.1.0
PyPI name==version requests==2.28.0

Metadata Fields

Field Type Description Required
name String Artifact identifier Yes
version String Version number Yes
checksum String SHA-256 or SHA-512 hash Recommended
size Integer File size in bytes No
published_at Timestamp Publication time No
build_number Integer CI build identifier No
source_commit String Git commit SHA Recommended
dependencies Array Required artifacts Recommended

Security Controls

Control Purpose Implementation
Authentication Verify user identity API tokens, OAuth
Authorization Control access permissions RBAC, ACLs
Checksum Verification Detect corruption and tampering SHA-256, SHA-512
Signature Verification Prove authenticity GPG, code signing
Vulnerability Scanning Identify known issues CVE database lookup
Access Logging Audit trail Centralized logging
Encryption in Transit Protect network transfer TLS/HTTPS
Encryption at Rest Protect stored artifacts Filesystem encryption

Bundler Configuration Commands

Command Purpose
bundle config set --local deployment true Enable deployment mode
bundle config set --local without development:test Skip development dependencies
bundle package --all Cache all dependencies locally
bundle config gems.example.com TOKEN Set credentials for private repository
bundle install --frozen Install exact versions from lockfile
bundle outdated Check for newer versions

Repository API Operations

Operation HTTP Method Purpose
Publish Artifact PUT Upload artifact to repository
Fetch Artifact GET Download artifact by coordinates
Delete Artifact DELETE Remove artifact from repository
Search Artifacts GET Query available artifacts
Get Metadata GET Retrieve artifact metadata
List Versions GET Get all versions of an artifact

Checksum Algorithms

Algorithm Output Length Security Level Use Case
MD5 128 bits Weak Legacy compatibility only
SHA-1 160 bits Weak Deprecated
SHA-256 256 bits Strong Standard integrity verification
SHA-512 512 bits Strong High-security requirements