Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for global deployment file #15

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 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
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,27 @@ It supports mapping multiple local and remote paths, excluded path, and more.

## Commands

- `TransferInit` - create a config file and open it. Just edit if it already exists.
- `TransferInit [project|global]?` - create a project-level or global config file and open it. Just edit if it already exists. If the argument is omitted, the command is called with "project".
- `DiffRemote` - open a diff view with the remote file.
- `TransferRepeat` - repeat the last transfer command (except TransferInit, DiffRemote).
- `TransferUpload [path]` - upload the given file or directory.
- `TransferDownload [path]` - download the given file or directory.
- `TransferDirDiff [path]` - diff the directory with the remote one and display the changed files in the quickfix.

## Deployment config example
Run `TransferInit project` or `TransferInit global` to create or open the `deployment.lua` config file.
If there are both a project-level and a global config file, the settings in the project-level config file will overwrite the global settings.

```lua
-- .nvim/deployment.lua
-- Project config file <project root>/.nvim/deployment.lua
-- or global config file ~/.local/share/nvim/deployment.lua
return {
["example_name"] = {
host = "myhost",
username = "web", -- optional
mappings = {
{
["local"] = "live", -- path relative to project root
["local"] = "live", -- path relative to project root (for project config file) or absolute path (for global config file)
["remote"] = "/var/www/example.com", -- absolute path or relative to user home
},
{
Expand All @@ -47,7 +50,7 @@ return {
},
},
excludedPaths = { -- optional
"live/src/", -- local path relative to project root
"live/src/", -- local path relative to project root (for project config file) or absolute path (for global config file)
"test/src/",
},
},
Expand Down
36 changes: 27 additions & 9 deletions lua/transfer/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,34 @@ local function create_autocmd()
})
end

---@param is_project_config boolean
---@return string
local function get_config_path(is_project_config)
if is_project_config then
local dir = vim.loop.cwd() .. "/.nvim"
if vim.fn.isdirectory(dir) == 0 then
vim.fn.mkdir(dir)
end
return dir .. "/deployment.lua"
end
return vim.fn.stdpath("data") .. "/deployment.lua"
end

M.setup = function()
create_autocmd()

-- TransferInit - create a config file and open it. Just edit if it already exists
vim.api.nvim_create_user_command("TransferInit", function()
-- TransferInit - create a project-level or global config file and open it. Just edit if it already exists
vim.api.nvim_create_user_command("TransferInit", function(selected)
local arg = selected.fargs[1] or "local"
if arg ~= "project" and arg ~= "global" then
vim.notify("Invalid argument: %s" .. arg, vim.log.levels.WARN, {
title = "Transfer.nvim",
icon = "",
})
return
end
local config = require("transfer.config")
local template = config.options.config_template
local template = arg == "project" and config.options.config_template_local or config.options.config_template_global
-- if template is a function, call it
if type(template) == "function" then
template = template()
Expand All @@ -29,16 +50,13 @@ M.setup = function()
if type(template) == "string" then
template = vim.fn.split(template, "\n")
end
local path = vim.loop.cwd() .. "/.nvim"
if vim.fn.isdirectory(path) == 0 then
vim.fn.mkdir(path)
end
path = path .. "/deployment.lua"

local path = get_config_path(arg == "project")
if vim.fn.filereadable(path) == 0 then
vim.fn.writefile(template, path)
end
vim.cmd("edit " .. path)
end, { nargs = 0 })
end, { nargs = "?" })

-- TransferRepeat - repeat the last transfer command
vim.api.nvim_create_user_command("TransferRepeat", function()
Expand Down
20 changes: 18 additions & 2 deletions lua/transfer/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ local M = {}

M.defaults = {
-- deployment config template: can be a string, a function or a table of lines
config_template = [[
config_template_local = [[
return {
["server1"] = {
host = "server1",
mappings = {
{
["local"] = "domains/example.com",
["local"] = "domains/example.com", -- path relative to project root
["remote"] = "/var/www/example.com",
},
},
Expand All @@ -17,6 +17,22 @@ return {
-- },
},
}
]],
config_template_global = [[
return {
["server1"] = {
host = "server1",
mappings = {
{
["local"] = "~/myproject/domains/example", -- absolute path to project root
["remote"] = "/var/www/example.com",
},
},
-- excludedPaths = {
-- "src", -- absolute path
-- },
},
}
]],
close_diffview_mapping = "<leader>b", -- buffer related mapping to close diffview, set to nil to disable mapping
upload_rsync_params = {
Expand Down
105 changes: 66 additions & 39 deletions lua/transfer/transfer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,29 @@ local config = require("transfer.config")

local M = {}

-- reloads the buffer after a transfer
-- refreshes the neo-tree if the buffer is a neo-tree
-- @param bufnr number
-- @return void
local function file_exists(path)
return vim.fn.filereadable(path) == 1
end

local function load_config(path)
if file_exists(path) then
local success, result = pcall(dofile, path)
if success then
return result
else
vim.notify(
"Error loading config file: " .. path .. "\n" .. result,
vim.log.levels.ERROR,
{ title = "Transfer.nvim" }
)
end
end
return {}
end

---reloads the buffer after a transfer
---refreshes the neo-tree if the buffer is a neo-tree
---@param bufnr number
local function reload_buffer(bufnr)
local filetype = vim.api.nvim_buf_get_option(bufnr, "filetype")
if filetype == "neo-tree" then
Expand All @@ -22,9 +41,9 @@ local function reload_buffer(bufnr)
end
end

-- convert the given local absolute path to a relative project root path
-- @param absolute_path string
-- @return string
---convert the given local absolute path to a relative project root path
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep the format "-- comment", not "---comment"

---@param absolute_path string
---@return string
local function normalize_local_path(absolute_path)
local cwd = vim.loop.cwd()
local found, found_end = string.find(absolute_path, cwd, 1, true)
Expand All @@ -35,10 +54,10 @@ local function normalize_local_path(absolute_path)
return string.gsub(absolute_path, "^/", "")
end

-- check if the given path matches the given pattern
-- @param path string
-- @param pattern string
-- @return boolean
---check if the given path matches the given pattern
---@param path string
---@param pattern string
---@return boolean
local function path_matches(path, pattern)
pattern = string.gsub(pattern, "/$", "")
path = string.gsub(path, "/$", "")
Expand All @@ -56,10 +75,10 @@ local function path_matches(path, pattern)
return false
end

-- get the remote path for scp
-- @param deployment table
-- @param remote_file string
-- @return string
---get the remote path for scp
---@param deployment table
---@param remote_file string
---@return string
local function build_scp_path(deployment, remote_file)
local remote_path = "scp://"
if deployment.username then
Expand All @@ -70,10 +89,10 @@ local function build_scp_path(deployment, remote_file)
return remote_path
end

-- get the excluded paths for the given directory
-- @param deployment table
-- @param dir string
-- @return table
---get the excluded paths for the given directory
---@param deployment table
---@param dir string
---@return table
function M.excluded_paths_for_dir(deployment, dir)
local excludedPaths = {}
if deployment and deployment.excludedPaths and #deployment.excludedPaths > 0 then
Expand All @@ -97,15 +116,23 @@ function M.excluded_paths_for_dir(deployment, dir)
return excludedPaths
end

-- get the remote path for scp
-- @param local_path string
-- @return string
---get the remote path for scp
---@param local_path string
---@return string?
function M.remote_scp_path(local_path)
local cwd = vim.loop.cwd()
local config_file = cwd .. "/.nvim/deployment.lua"
if vim.fn.filereadable(config_file) ~= 1 then
local project_config_file = vim.loop.cwd() .. "/.nvim/deployment.lua"
local global_config_file = vim.fn.stdpath("data") .. "/deployment.lua"
local project_deployment_conf = load_config(project_config_file)
local global_deployment_conf = load_config(global_config_file)
local merged_deployment_conf = global_deployment_conf
-- Merge configurations, project overrides global
for name, deployment in pairs(project_deployment_conf) do
merged_deployment_conf[name] = deployment
end

if vim.tbl_isempty(merged_deployment_conf) then
vim.notify(
"No deployment config found in \n" .. config_file .. "\n\nRun `:TransferInit` to create it",
"No deployment config found in \n" .. project_config_file .. " or " .. global_config_file,
vim.log.levels.WARN,
{
title = "Transfer.nvim",
Expand All @@ -115,12 +142,12 @@ function M.remote_scp_path(local_path)
)
return nil
end
local deployment_conf = dofile(config_file)

-- remove cwd from local file path
local_path = normalize_local_path(local_path)

local skip_reason
for name, deployment in pairs(deployment_conf) do
for name, deployment in pairs(merged_deployment_conf) do
local skip = false
if deployment.excludedPaths ~= nil then
for _, excluded in pairs(deployment.excludedPaths) do
Expand Down Expand Up @@ -183,9 +210,9 @@ function M.remote_scp_path(local_path)
return nil
end

-- get the remote path for rsync
-- @param local_path string
-- @return string
---get the remote path for rsync
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason to change comment style?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am using Treesitter parser luap for lua docstrings and they are only highlighted when using ---:
image

I can revert it if you want though

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bug in your highlighting. Treesitter itself and other popular lua plugins use --space in their codebase.

---@param local_path string
---@return string
function M.remote_rsync_path(local_path)
local remote_path, deployment = M.remote_scp_path(local_path)
if remote_path == nil then
Expand All @@ -198,9 +225,9 @@ function M.remote_rsync_path(local_path)
return remote_path, deployment
end

-- upload the given file
-- @param local_path string
-- @return void
---upload the given file
---@param local_path string
---@return void
function M.upload_file(local_path)
if local_path == nil then
local_path = vim.fn.expand("%:p")
Expand Down Expand Up @@ -249,8 +276,8 @@ function M.upload_file(local_path)
})
end

-- Replace local file with remote copy
-- @param local_path string|nil
---Replace local file with remote copy
---@param local_path string|nil
function M.download_file(local_path)
if local_path == nil then
local_path = vim.fn.expand("%:p")
Expand Down Expand Up @@ -305,9 +332,9 @@ function M.download_file(local_path)
})
end

-- Sync local and remote directory
-- @param dir string
-- @param upload boolean
---Sync local and remote directory
---@param dir string
---@param upload boolean
function M.sync_dir(dir, upload)
local remote_path, deployment = M.remote_rsync_path(dir)
if remote_path == nil then
Expand Down