1.6 KiB · text History d70ff9f
defmodule GitGud.Packages.PackageFile do
  @moduledoc """
  One uploaded artifact: a wheel, an sdist, a Hex `.tar`, a `.crate`,
  a `.deb`.

  A version usually has more than one — PyPI publishes an sdist plus a
  wheel per platform, apt publishes a `.deb` per architecture — which
  is why files hang off `PackageVersion` rather than being folded into
  it.

  Bytes live in `GitGud.Storage` under `storage_bucket`/`storage_key`;
  only the checksum and the index-relevant metadata live here. Keys are
  content-addressed by `sha256`, so re-uploading identical bytes is
  free.
  """
  use Ecto.Schema

  import Ecto.Changeset

  alias GitGud.Accounts.User
  alias GitGud.Packages.PackageVersion

  schema "package_files" do
    field :filename, :string
    field :content_type, :string
    field :size, :integer, default: 0
    field :sha256, :string
    field :md5, :string
    field :storage_bucket, :string
    field :storage_key, :string
    field :metadata, :map, default: %{}

    belongs_to :package_version, PackageVersion
    belongs_to :uploader, User

    timestamps(type: :utc_datetime)
  end

  @doc false
  def changeset(file, attrs) do
    file
    |> cast(attrs, [
      :filename,
      :content_type,
      :size,
      :sha256,
      :md5,
      :storage_bucket,
      :storage_key,
      :metadata,
      :uploader_id
    ])
    |> validate_required([:filename, :sha256, :storage_bucket, :storage_key])
    |> validate_length(:filename, max: 300)
    |> validate_format(:sha256, ~r/\A[0-9a-f]{64}\z/, message: "must be lowercase hex SHA-256")
    |> unique_constraint([:package_version_id, :filename])
  end
end