Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
Expand Down Expand Up @@ -77,7 +77,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6

- name: Set up Docker Buildx
id: buildx
Expand Down Expand Up @@ -109,10 +109,10 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6

- name: Download docs artifacts
uses: actions/download-artifact@v5
uses: actions/download-artifact@v7
with:
name: docs
path: .pipeline/output/doc
Expand Down
11 changes: 7 additions & 4 deletions lib/pix/command/help.ex
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ defmodule Pix.Command.Help do

defp help_cmd("run") do
"""
#{_cmd("pix run")} [#{_opt("--output")}] [#{_opt("--ssh")}] [#{_opt("--arg ARG")} ...] [#{_opt("--progress PROGRESS")}] [#{_opt("--target TARGET")} [#{_opt("--tag TAG")}]] [#{_opt("--no-cache")}] [#{_opt("--no-cache-filter TARGET")} ...] PIPELINE
#{_cmd("pix run")} [#{_opt("--output")}] [#{_opt("--ssh [SPEC]")} ...] [#{_opt("--arg ARG")} ...] [#{_opt("--progress PROGRESS")}] [#{_opt("--target TARGET")} [#{_opt("--tag TAG")}]] [#{_opt("--no-cache")}] [#{_opt("--no-cache-filter TARGET")} ...] PIPELINE

Run PIPELINE.

Expand All @@ -91,15 +91,16 @@ defmodule Pix.Command.Help do
#{_opt("--no-cache-filter")}* Do not cache specified targets
#{_opt("--progress")} Set type of progress output - "auto", "plain", "tty", "rawjson" (default "auto")
#{_opt("--secret")}* Forward one or more secrets to `buildx build`
#{_opt("--ssh")} Forward SSH agent to `buildx build`
#{_opt("--ssh")}* Forward SSH agent/keys to `buildx build` (format: default[=<socket>] | <id>=<path>).
Can be specified multiple times.
#{_opt("--tag")} Tag the TARGET's docker image (default: no tag)
#{_opt("--target")} Run PIPELINE for a specific TARGET (default: all the PIPELINE targets)
"""
end

defp help_cmd("shell") do
"""
#{_cmd("pix shell")} [#{_opt("--ssh")}] [#{_opt("--arg ARG")} ...] [#{_opt("--target TARGET")}] [#{_opt("--host")}] PIPELINE [COMMAND]
#{_cmd("pix shell")} [#{_opt("--ssh [SPEC]")} ...] [#{_opt("--arg ARG")} ...] [#{_opt("--target TARGET")}] [#{_opt("--host")}] PIPELINE [COMMAND]

Shell into the specified target of the PIPELINE.

Expand All @@ -113,7 +114,9 @@ defmodule Pix.Command.Help do
OPTIONS:
#{_opt("--arg")}* Set one or more pipeline ARG (format KEY=value)
#{_opt("--secret")}* Forward one or more secrets to `buildx build`
#{_opt("--ssh")} Forward SSH agent to shell container
#{_opt("--ssh")}* Forward SSH agent/keys to the build and shell container (format: default[=<socket>] | <id>=<path>).
Custom key files are mounted read-only into the shell container.
Can be specified multiple times.
#{_opt("--target")} The shell target
"""
end
Expand Down
2 changes: 1 addition & 1 deletion lib/pix/command/run.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defmodule Pix.Command.Run do
output: :boolean,
progress: :string,
secret: [:string, :keep],
ssh: :boolean,
ssh: [:string, :keep],
tag: :string,
target: :string
]
Expand Down
2 changes: 1 addition & 1 deletion lib/pix/command/shell.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Pix.Command.Shell do
arg: [:string, :keep],
host: :boolean,
secret: [:string, :keep],
ssh: :boolean,
ssh: [:string, :keep],
target: :string
]

Expand Down
49 changes: 41 additions & 8 deletions lib/pix/docker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ defmodule Pix.Docker do
end
end

@spec run(image :: String.t(), ssh_fwd? :: boolean(), opts(), cmd_args :: [String.t()]) :: status :: non_neg_integer()
def run(image, ssh_fwd?, opts, cmd_args) do
ssh_opts = if ssh_fwd?, do: run_opts_ssh_forward(), else: []
@spec run(image :: String.t(), ssh_specs :: [String.t()], opts(), cmd_args :: [String.t()]) ::
status :: non_neg_integer()
def run(image, ssh_specs, opts, cmd_args) do
ssh_opts = if ssh_specs != [], do: run_opts_ssh_forward(ssh_specs), else: []
opts = opts ++ ssh_opts ++ run_opts_docker_outside_of_docker()
args = ["run"] ++ opts_encode(opts) ++ Pix.Env.pix_docker_run_opts() ++ [image] ++ cmd_args

Expand Down Expand Up @@ -73,8 +74,16 @@ defmodule Pix.Docker do
:ok
end

@spec run_opts_ssh_forward :: opts()
defp run_opts_ssh_forward do
@spec run_opts_ssh_forward([String.t()]) :: opts()
defp run_opts_ssh_forward(ssh_specs) do
agent_opts = if "default" in ssh_specs, do: run_opts_ssh_agent_forward(), else: []
{volume_opts, key_paths} = run_opts_ssh_key_mounts(ssh_specs)
git_ssh_opts = run_opts_git_ssh_command(key_paths)
agent_opts ++ volume_opts ++ git_ssh_opts
end

@spec run_opts_ssh_agent_forward :: opts()
defp run_opts_ssh_agent_forward do
ssh_sock =
cond do
:os.type() == {:unix, :darwin} ->
Expand All @@ -101,16 +110,40 @@ defmodule Pix.Docker do
end
end

@spec run_opts_ssh_key_mounts([String.t()]) :: {opts(), key_paths :: [String.t()]}
defp run_opts_ssh_key_mounts(ssh_specs) do
Enum.reduce(ssh_specs, {[], []}, fn spec, {vol_acc, key_path_acc} ->
case String.split(spec, "=", parts: 2) do
[_id, path] when path != "" ->
path = Path.expand(path)
container_path = "/root/.ssh/#{Path.basename(path)}"
Pix.Report.internal(">>> mounting SSH key #{inspect(path)} as #{container_path} into shell container\n")
{[{:volume, "#{path}:#{container_path}:ro"} | vol_acc], [container_path | key_path_acc]}

_ ->
{vol_acc, key_path_acc}
end
end)
end

@spec run_opts_git_ssh_command([String.t()]) :: opts()
defp run_opts_git_ssh_command([]), do: []

defp run_opts_git_ssh_command(key_paths) do
identity_flags = Enum.map_join(key_paths, " ", &"-i #{&1}")
[env: "GIT_SSH_COMMAND=ssh #{identity_flags} -o StrictHostKeyChecking=no"]
end

@spec run_opts_docker_outside_of_docker :: opts()
defp run_opts_docker_outside_of_docker do
docker_socket = "/var/run/docker.sock"
Pix.Report.internal(">>> Supporting docker outside-of docker via socket mount (#{docker_socket})\n")
[volume: "#{docker_socket}:#{docker_socket}"]
end

@spec build(ssh_fwd? :: boolean(), opts(), String.t()) :: exit_status :: non_neg_integer()
def build(ssh_fwd?, opts, ctx) do
ssh_opts = if ssh_fwd?, do: [ssh: "default"], else: []
@spec build(ssh_specs :: [String.t()], opts(), String.t()) :: exit_status :: non_neg_integer()
def build(ssh_specs, opts, ctx) do
ssh_opts = Enum.map(ssh_specs, fn spec -> {:ssh, spec} end)

builder_opts =
case buildx_builder() do
Expand Down
20 changes: 14 additions & 6 deletions lib/pix/pipeline.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ defmodule Pix.Pipeline do
| {:output, boolean()}
| {:progress, String.t()}
| {:secret, String.t()}
| {:ssh, boolean()}
| {:ssh, String.t()}
| {:tag, String.t()}
| {:target, String.t()}
]

@type shell_cli_opts :: [
{:arg, String.t()}
| {:host, boolean()}
| {:ssh, boolean()}
| {:secret, String.t()}
| {:ssh, String.t()}
| {:target, String.t()}
]

Expand Down Expand Up @@ -198,7 +199,9 @@ defmodule Pix.Pipeline do
Pix.Report.info("\nRunning pipeline (targets: #{inspect(targets)})\n\n")

build_opts = build_opts ++ [file: dockerfile_path]
Pix.Docker.build(cli_opts[:ssh], build_opts, ".") |> halt_on_error()
ssh_opts = Keyword.get_values(cli_opts, :ssh)

Pix.Docker.build(ssh_opts, build_opts, ".") |> halt_on_error()

if cli_opts[:output] do
Pix.Report.info("\nExported pipeline outputs to #{output_dir()}:\n")
Expand All @@ -216,7 +219,9 @@ defmodule Pix.Pipeline do
build_opts = build_opts ++ [target: cli_opts[:target], file: dockerfile_path]
build_opts = add_run_tag_option(build_opts, cli_opts)

Pix.Docker.build(cli_opts[:ssh], build_opts, ".") |> halt_on_error()
ssh_opts = Keyword.get_values(cli_opts, :ssh)

Pix.Docker.build(ssh_opts, build_opts, ".") |> halt_on_error()

:ok
end
Expand Down Expand Up @@ -281,7 +286,9 @@ defmodule Pix.Pipeline do
defp execute_shell_build(build_opts, shell_target, cli_opts) do
Pix.Report.info("\nBuilding pipeline (target=#{shell_target})\n\n")

Pix.Docker.build(cli_opts[:ssh], build_opts, ".")
ssh_opts = Keyword.get_values(cli_opts, :ssh)

Pix.Docker.build(ssh_opts, build_opts, ".")
|> halt_on_error()

:ok
Expand All @@ -292,9 +299,10 @@ defmodule Pix.Pipeline do
Pix.Report.info("\nEntering shell\n")

opts = shell_run_options(shell_target, from, cli_opts)
ssh_opts = Keyword.get_values(cli_opts, :ssh)

shell_docker_image
|> Pix.Docker.run(cli_opts[:ssh], opts, cmd_args)
|> Pix.Docker.run(ssh_opts, opts, cmd_args)
|> halt_on_error()

:ok
Expand Down
4 changes: 2 additions & 2 deletions lib/pix/user_settings.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ defmodule Pix.UserSettings do
command: %{
run: %{
cli_opts: [
ssh: true
ssh: "default"
]
},
shell: %{
cli_opts: [
ssh: true
ssh: "default"
]
}
}
Expand Down
4 changes: 2 additions & 2 deletions shell_completions/pix.fish
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ complete -c pix -n "__fish_seen_subcommand_from graph" -l "format" -d "Output fo
# run command options
complete -c pix -n "__fish_seen_subcommand_from run" -a "(__pix_get_pipelines)" -d "Pipeline"
complete -c pix -n "__fish_seen_subcommand_from run" -l "output" -d "Output the target artifacts under .pipeline/output directory"
complete -c pix -n "__fish_seen_subcommand_from run" -l "ssh" -d "Forward SSH agent to buildx build"
complete -c pix -n "__fish_seen_subcommand_from run" -l "ssh" -d "Forward SSH agent/keys to buildx build (default, or id=path)" -rf
complete -c pix -n "__fish_seen_subcommand_from run" -l "arg" -d "Set one or more pipeline ARG (format KEY=value)" -a "(__pix_get_run_target_args)"
complete -c pix -n "__fish_seen_subcommand_from run" -l "progress" -d "Set type of progress output" -a "auto plain tty rawjson"
complete -c pix -n "__fish_seen_subcommand_from run" -l "secret" -d "Forward one or more secrets to `buildx build`"
Expand All @@ -81,7 +81,7 @@ complete -c pix -n "__fish_seen_subcommand_from run" -l "no-cache-filter" -d "Do

# shell command options
complete -c pix -n "__fish_seen_subcommand_from shell" -a "(__pix_get_pipelines)" -d "Pipeline"
complete -c pix -n "__fish_seen_subcommand_from shell" -l "ssh" -d "Forward SSH agent to shell container"
complete -c pix -n "__fish_seen_subcommand_from shell" -l "ssh" -d "Forward SSH agent/keys to shell container (default, or id=path)" -rf
complete -c pix -n "__fish_seen_subcommand_from shell" -l "arg" -d "Set one or more pipeline ARG (format KEY=value)"
complete -c pix -n "__fish_seen_subcommand_from shell" -l "secret" -d "Forward one or more secrets to `buildx build`"
complete -c pix -n "__fish_seen_subcommand_from shell" -l "target" -d "The shell target"
Expand Down