Skip to content

Commit

Permalink
Merge branch 'release/v0.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
zacksiri committed Feb 20, 2024
2 parents e2b11e6 + 3705c9b commit bce4383
Show file tree
Hide file tree
Showing 18 changed files with 523 additions and 21 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Publish

on:
push:
tags:
- v*.*.*

jobs:
publish:
name: Publish to hex
runs-on: ubuntu-latest
environment: Release
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: "26"
elixir-version: "1.15.7"
- uses: cucumber/[email protected]
with:
hex-api-key: ${{ secrets.HEX_API_KEY }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ icepak-*.tar

# Temporary files, for example, from tests.
/tmp/

.envrc
2 changes: 2 additions & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
erlang 26.2.1
elixir 1.15.7-otp-26
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
# Icepak

**TODO: Add description**
Icepak github action will do the following:

- Process metdata for all the built items
- Interact with [polar](https://github.com/upmaru/polar) and publish images

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `icepak` to your list of dependencies in `mix.exs`:

```elixir
def deps do
[
{:icepak, "~> 0.1.0"}
]
end
```shell
mix escript.install hex icepak
```

Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
Expand Down
24 changes: 23 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,29 @@ inputs:
runs:
using: "composite"
steps:
- name: Create Mix Directory
run: |
mkdir -p ~/.mix
shell: bash

- uses: erlef/setup-beam@v1
with:
otp-version: "26.2.1"
elixir-version: "1.15.7"
elixir-version: "1.15.7"

- name: Cache IcePAK
id: cache-pakman
uses: actions/cache@v4
with:
path: ~/.mix
key: ${{ runner.arch }}-icepak-0.1.0

- name: Install Pakman
if: steps.cache-pakman.outputs.cache-hit != 'true'
run: |
mix local.rebar --force
mix local.hex --force
mix escript.install hex icepak 0.1.0 --force
shell: bash
env:
MIX_ENV: prod
3 changes: 3 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Config

import_config "#{config_env()}.exs"
3 changes: 3 additions & 0 deletions config/dev.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Config

config :icepak, :env, :dev
1 change: 1 addition & 0 deletions config/prod.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import Config
3 changes: 3 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Config

config :icepak, :env, :test
15 changes: 3 additions & 12 deletions lib/icepak.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,7 @@ defmodule Icepak do
Documentation for `Icepak`.
"""

@doc """
Hello world.
## Examples
iex> Icepak.hello()
:world
"""
def hello do
:world
end
defdelegate push(options),
to: Icepak.Push,
as: :perform
end
25 changes: 25 additions & 0 deletions lib/icepak/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@ defmodule Icepak.Application do

use Application

@cacerts CAStore.file_path()
|> File.read!()
|> :public_key.pem_decode()
|> Enum.map(fn {_, cert, _} -> cert end)

@impl true
def start(_type, _args) do
children = [
{Finch, finch_options(Application.get_env(:icepak, :env))}
# Starts a worker by calling: Icepak.Worker.start_link(arg)
# {Icepak.Worker, arg}
]
Expand All @@ -17,4 +23,23 @@ defmodule Icepak.Application do
opts = [strategy: :one_for_one, name: Icepak.Supervisor]
Supervisor.start_link(children, opts)
end

defp finch_options(env) when env in [:test, :dev], do: [name: Icepak.Finch]

defp finch_options(_) do
[
name: Icepak.Finch,
pools: %{
default: [
size: 10,
conn_opts: [
transport_opts: [
verify: :verify_peer,
cacerts: @cacerts
]
]
]
}
]
end
end
39 changes: 39 additions & 0 deletions lib/icepak/cli.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
defmodule Icepak.CLI do
@commands %{
"push" => :push
}

@switches %{
push: [
switches: [
path: :string,
serial: :string,
os: :string,
arch: :string,
release: :string,
variant: :string
]
]
}

require Logger

def main(args \\ []) do
command = List.first(args)
call = Map.get(@commands, command)

if call do
switches = Map.get(@switches, command, switches: [])

{options, _, _} = OptionParser.parse(args, switches)

apply(Icepak, call, [options])
else
IO.puts("""
Unknown command, please use one of the following:
- push - will push the built image
""")
end
end
end
125 changes: 125 additions & 0 deletions lib/icepak/item.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
defmodule Icepak.Item do
@derive Jason.Encoder

defstruct name: "",
file_type: "",
size: 0,
hash: "",
path: "",
source: nil,
is_metadata: false,
combined_hashes: []

@combinations %{
"combined_squashfs_sha256" => ["incus.tar.xz", "rootfs.squashfs"]
}

defmodule Hash do
defstruct [:name, :hash]

@type t :: %__MODULE__{
name: String.t(),
hash: String.t()
}
end

@type t :: %__MODULE__{
name: String.t(),
file_type: String.t(),
size: integer(),
hash: String.t(),
path: String.t(),
source: String.t() | nil,
is_metadata: boolean(),
combined_hashes: [
Hash.t()
]
}

def prepare("incus.tar.xz" = file, %{base_path: base_path, storage_path: storage_path}) do
hash_ref = :crypto.hash_init(:sha256)
full_path = Path.join(base_path, file)
%{size: size} = File.stat!(full_path)

storage_path = Path.join(storage_path, file)

hash =
full_path
|> calculate_hash(hash_ref)
|> finalize_hash()

combined_hashes =
Enum.map(@combinations, fn {key, values} ->
files = Enum.map(values, fn f -> Path.join(base_path, f) end)

%{
name: key,
hash:
files
|> Enum.reduce(hash_ref, &calculate_hash/2)
|> finalize_hash()
}
end)

[
%__MODULE__{
name: file,
file_type: "incus.tar.xz",
size: size,
hash: hash,
path: storage_path,
source: full_path,
is_metadata: true,
combined_hashes: combined_hashes
},
%__MODULE__{
name: "lxd.tar.xz",
file_type: "lxd.tar.xz",
size: size,
hash: hash,
path: storage_path,
is_metadata: true,
combined_hashes: combined_hashes
}
]
end

def prepare("rootfs.squashfs" = file, %{base_path: base_path, storage_path: storage_path}) do
hash_ref = :crypto.hash_init(:sha256)
full_path = Path.join(base_path, file)
%{size: size} = File.stat!(full_path)

storage_path = Path.join(storage_path, file)

hash =
full_path
|> calculate_hash(hash_ref)
|> finalize_hash()

[
%__MODULE__{
name: "root.squashfs",
file_type: "squashfs",
size: size,
hash: hash,
path: storage_path,
source: full_path
}
]
end

defp calculate_hash(file, hash_ref) do
file
|> File.stream!([], 2048)
|> Enum.reduce(hash_ref, fn chunk, prev_ref ->
:crypto.hash_update(prev_ref, chunk)
end)
end

defp finalize_hash(hash) do
hash
|> :crypto.hash_final()
|> Base.encode16()
|> String.downcase()
end
end
51 changes: 51 additions & 0 deletions lib/icepak/polar.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
defmodule Icepak.Polar do
require Logger

def get_product(client, key) do
key = Base.url_encode64(key)

Req.get!(client, url: "/publish/products/#{key}")
end

def create_version(client, product_id, version_params) do
Req.post!(client,
url: "/publish/products/#{product_id}/versions",
json: %{version: version_params}
)
end

def get_storage(client) do
Req.get!(client, url: "/publish/storage")
end

def authenticate do
auth_token = System.get_env("POLAR_AUTH_TOKEN")

body = %{
user: %{
password: auth_token
}
}

client = client()

client
|> Req.update(url: "/publish/sessions", json: body)
|> Req.post()
|> case do
{:ok, %{body: %{"data" => %{"token" => session_token}}}} ->
Logger.info("[Polar] Authenticated")

Req.update(client, headers: [{"authorization", session_token}])

_ ->
raise "Failed to authenticate with Polar"
end
end

def client do
endpoint = System.get_env("POLAR_ENDPOINT", "https://images.opsmaru.com")

Req.new(base_url: endpoint, finch: Icepak.Finch)
end
end
Loading

0 comments on commit bce4383

Please sign in to comment.