Merge branch 'release/v0.1.10'

This commit is contained in:
Glenn Y. Rolland 2023-11-19 00:04:15 +01:00
commit e9553e278b
15 changed files with 508 additions and 99 deletions

View file

@ -5,7 +5,7 @@ name: default
steps: steps:
- name: build:binary - name: build:binary
image: crystallang/crystal:1.7.3 image: crystallang/crystal:1.10.1-alpine
environment: environment:
PACKAGE_BASENAME: mfm_linux_amd64 PACKAGE_BASENAME: mfm_linux_amd64
volumes: volumes:
@ -13,19 +13,27 @@ steps:
path: /_cache path: /_cache
commands: commands:
- pwd - pwd
- apt-get update && # - |
apt-get install -y cmake g++ libevent-dev libpcre3-dev libyaml-dev # apt-get update && \
# apt-get install -y \
# cmake g++ \
# libevent-dev libpcre3-dev \
# libyaml-dev liblzma-dev
- shards install - shards install
- shards build --production --static - shards build --production --static
- strip bin/mfm - strip bin/mfm
- ./bin/mfm --version
- mkdir -p /_cache/bin - mkdir -p /_cache/bin
- cp -r bin/mfm /_cache/bin/$PACKAGE_BASENAME - cp -r bin/mfm /_cache/bin/$PACKAGE_BASENAME
- name: publish:tag - name: publish:tag
image: curlimages/curl image: alpine
environment: environment:
PACKAGE_UPLOAD_URL: https://code.apps.glenux.net/api/packages/glenux/generic/mfm PACKAGE_UPLOAD_URL: https://code.apps.glenux.net/api/v1/packages/glenux/generic/mfm
RELEASES_URL: https://code.apps.glenux.net/api/v1/repos/glenux/mfm/releases
PACKAGE_BASENAME: mfm_linux_amd64 PACKAGE_BASENAME: mfm_linux_amd64
RELEASE_UPLOAD_TOKEN:
from_secret: RELEASE_UPLOAD_TOKEN
PACKAGE_UPLOAD_TOKEN: PACKAGE_UPLOAD_TOKEN:
from_secret: PACKAGE_UPLOAD_TOKEN from_secret: PACKAGE_UPLOAD_TOKEN
when: when:
@ -36,16 +44,52 @@ steps:
- name: cache - name: cache
path: /_cache path: /_cache
commands: commands:
- apk add --update --no-cache curl jq
- env |grep DRONE - env |grep DRONE
- | - |
curl -H "Authorization: token $PACKAGE_UPLOAD_TOKEN" \ curl -H "Authorization: token $PACKAGE_UPLOAD_TOKEN" \
--upload-file /_cache/bin/$PACKAGE_BASENAME \ --upload-file "/_cache/bin/$PACKAGE_BASENAME" \
$PACKAGE_UPLOAD_URL/$DRONE_TAG/$PACKAGE_BASENAME "$PACKAGE_UPLOAD_URL/$DRONE_TAG/$PACKAGE_BASENAME"
- |
set -x
curl -X POST \
-H "Authorization: token $RELEASE_UPLOAD_TOKEN" \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d "{\"body\": \"DRAFT\", \"draft\": true, \"name\": \"$DRONE_TAG - DRAFT\", \"prerelease\": false, \"tag_name\": \"$DRONE_TAG\", \"target_commitish\": \"$DRONE_COMMIT_SHA\"}" \
"$RELEASES_URL"
- |
curl -X 'GET' \
-H 'accept: application/json' \
"$RELEASES_URL/tags/$DRONE_TAG"
- |
TAG_ID="$(curl -X 'GET' \
-H 'accept: application/json' \
"$RELEASES_URL/tags/$DRONE_TAG" | jq -r .id)"
echo "TAG_ID=$TAG_ID"
- |
set -x
curl -X POST \
-H "Authorization: token $RELEASE_UPLOAD_TOKEN" \
-H "accept: application/json" \
-H "Content-Type: multipart/form-data" \
-F "attachment=@/_cache/bin/$PACKAGE_BASENAME" \
"$RELEASES_URL/$TAG_ID/assets?name=$PACKAGE_BASENAME"
# FIXME: handle multi-arch # FIXME: handle multi-arch
# FIXME: publish only on tags # FIXME: publish only on tags
services:
- name: docker
image: docker:dind
privileged: true
volumes:
- name: dockersock
path: /var/run
volumes: volumes:
- name: cache - name: cache
temp: {} temp: {}
- name: dockersock
temp: {}
# #

View file

@ -5,6 +5,8 @@
# Copyright © 2023 Glenn Y. Rolland <glenux@glenux.net> # Copyright © 2023 Glenn Y. Rolland <glenux@glenux.net>
--> -->
[![Build Status](https://cicd.apps.glenux.net/api/badges/glenux/mfm/status.svg)](https://cicd.apps.glenux.net/glenux/mfm)
# Minimalist Fuse Manager (MFM) # Minimalist Fuse Manager (MFM)
MFM is a Crystal-lang CLI designed to streamline the management of various FUSE filesystems, such as sshfs, gocryptfs, httpdirfs, and more. Through its user-friendly interface, users can effortlessly mount and unmount filesystems, get real-time filesystem status, and handle errors proficiently. MFM is a Crystal-lang CLI designed to streamline the management of various FUSE filesystems, such as sshfs, gocryptfs, httpdirfs, and more. Through its user-friendly interface, users can effortlessly mount and unmount filesystems, get real-time filesystem status, and handle errors proficiently.
@ -17,11 +19,27 @@ Before using MFM, make sure the following tools are installed on your system:
- **sshfs**: <https://github.com/libfuse/sshfs> - **sshfs**: <https://github.com/libfuse/sshfs>
- **httpdirfs**: <https://github.com/fangfufu/httpdirfs> - **httpdirfs**: <https://github.com/fangfufu/httpdirfs>
- **fzf**: <https://github.com/junegunn/fzf> - **fzf**: <https://github.com/junegunn/fzf>
- libpcre3
- libevent-2.1
For Debian/Ubuntu you can use the following command:
```shell-session
$ sudo apt-get update && sudo apt-get install libpcre3 libevent-2.1-7 fzf gocryptfs httpdirfs sshfs
```
## Building from source
To build from source, you'll also need: To build from source, you'll also need:
- **crystal-lang**: <https://crystal-lang.org/> - **crystal-lang**: <https://crystal-lang.org/>
For Debian/Ubuntu you can use the following command:
```shell-session
$ sudo apt-get update && sudo apt-get install libpcre3-dev libevent-2.1-dev
```
## Installation ## Installation
### 1. From Source ### 1. From Source
@ -34,7 +52,8 @@ To build from source, you'll also need:
### 2. Binary Download ### 2. Binary Download
Alternatively, download a pre-compiled binary version of MFM. Alternatively, download [a pre-compiled binary
version](https://code.apps.glenux.net/glenux/mfm/releases) of MFM.
## Usage ## Usage
@ -47,7 +66,7 @@ Global options:
-c, --config FILE Specify configuration file -c, --config FILE Specify configuration file
-h, --help Display this help -h, --help Display this help
Commands: Commands (not implemented yet):
create Add a new filesystem create Add a new filesystem
delete Remove an existing filesystem delete Remove an existing filesystem
edit Modify the configuration edit Modify the configuration
@ -59,7 +78,8 @@ Commands:
## Configuration ## Configuration
MFM uses a YAML configuration file, typically found at `~/.config/mfm.yml`, to detail the filesystem names, types, and respective configurations. MFM uses a YAML configuration file, typically found at `~/.config/mfm.yml`, to
detail the filesystem names, types, and respective configurations.
### YAML File Format ### YAML File Format
@ -84,7 +104,7 @@ filesystems:
- type: httpdirfs - type: httpdirfs
name: "Debian Repository" name: "Debian Repository"
url: "http://ftp.debian.org/debian/" url: "http://ftp.debian.org/debian/"
# Add more filesystems as needed # Add more filesystems as needed
``` ```
@ -100,7 +120,7 @@ Contributing to MFM:
6. **Submit a Pull Request**: Begin a pull request to the main repository and explain your changes. 6. **Submit a Pull Request**: Begin a pull request to the main repository and explain your changes.
7. **Review**: Await feedback from the maintainers and respond as necessary. 7. **Review**: Await feedback from the maintainers and respond as necessary.
By contributing, you agree to our code of conduct and GPL-2 license terms. By contributing, you agree to our code of conduct and license terms.
## Authors and Contributors ## Authors and Contributors

2
Vagrantfile vendored
View file

@ -30,5 +30,5 @@ Vagrant.configure('2') do |config|
machine.vm.network 'forwarded_port', guest: 80, host: 1080, host_ip: '127.0.0.1' machine.vm.network 'forwarded_port', guest: 80, host: 1080, host_ip: '127.0.0.1'
end end
config.vm.provision 'shell', path: 'scripts/vagrant.provision.sh' config.vm.provision 'shell', path: 'scripts/vagrant-provision/base.sh'
end end

64
scripts/ci.crossbuild-alpine.sh Executable file
View file

@ -0,0 +1,64 @@
#!/bin/sh -eu
# vim: set ts=2 sw=2 et:
LOCAL_PROJECT_PATH="${1-$PWD}"
TARGET_ARCH="${2-amd64}"
DOCKER_IMAGE=""
BUILD_COMMAND=" \
shards build --static --release \
&& chown 1000:1000 -R bin \
&& find bin -type f -maxdepth 1 -exec mv {} {}_${TARGET_ARCH} \; \
"
INSTALL_CRYSTAL=" \
echo '@edge http://dl-cdn.alpinelinux.org/alpine/edge/community' >>/etc/apk/repositories \
&& apk add --update --no-cache --force-overwrite \
crystal@edge \
g++ \
gc-dev \
libxml2-dev \
llvm16-dev \
llvm16-static \
make \
musl-dev \
openssl-dev \
openssl-libs-static \
pcre-dev \
shards@edge \
yaml-dev \
yaml-static \
zlib-dev \
zlib-static \
"
# setup arch
case "$TARGET_ARCH" in
amd64) DOCKER_IMAGE="alpine" ;;
arm64) DOCKER_IMAGE="multiarch/alpine:aarch64-edge" ;;
armel) DOCKER_IMAGE="multiarch/alpine:armv7-edge" ;;
# armhf) DOCKER_IMAGE="multiarch/alpine:armhf-edge" ;;
# i386) DOCKER_IMAGE="multiarch/alpine:x86-edge" ;;
mips) DOCKER_IMAGE="multiarch/alpine:mips-edge" ;;
mipsel) DOCKER_IMAGE="multiarch/alpine:mipsel-edge" ;;
powerpc) DOCKER_IMAGE="multiarch/alpine:powerpc-edge" ;;
ppc64el) DOCKER_IMAGE="multiarch/alpine:ppc64el-edge" ;;
s390x) DOCKER_IMAGE="multiarch/alpine:s390x-edge" ;;
esac
# Compile Crystal project statically for target architecture
docker pull multiarch/qemu-user-static:register
docker run \
--rm \
--privileged \
multiarch/qemu-user-static:register \
--reset
docker run \
-it \
-v "$LOCAL_PROJECT_PATH:/app" \
-w /app \
--rm \
"$DOCKER_IMAGE" \
/bin/sh -c "$INSTALL_CRYSTAL && $BUILD_COMMAND"

82
scripts/ci.crossbuild-debian.sh Executable file
View file

@ -0,0 +1,82 @@
#!/bin/sh -eu
# vim: set ts=2 sw=2 et:
LOCAL_PROJECT_PATH="${1-$PWD}"
TARGET_ARCH="${2-amd64}"
DOCKER_IMAGE=""
BUILD_COMMAND=" \
shards build --static --release \
&& chown 1000:1000 -R bin \
&& find bin -type f -maxdepth 1 -exec mv {} {}_${TARGET_ARCH} \; \
"
# crystal
INSTALL_CRYSTAL=" \
sed -i -e 's/Types: deb/Types: deb deb-src/' /etc/apt/sources.list.d/debian.sources \
&& echo 'deb http://deb.debian.org/debian unstable main' > /etc/apt/sources.list.d/sid.list \
&& echo 'deb-src http://deb.debian.org/debian unstable main' >> /etc/apt/sources.list.d/sid.list \
&& apt-get update \
&& apt-get install -y \
g++ \
libxml2-dev \
llvm-dev \
make \
libssl-dev \
libpcre3-dev \
libyaml-dev \
zlib1g-dev \
dpkg-dev \
debuild \
&& apt source crystal \
&& apt build-dep crystal \
&& ls -lF \
&& debuild -b -uc -us \
"
# setup arch
case "$TARGET_ARCH" in
amd64) DOCKER_IMAGE="debian" ;;
arm64) DOCKER_IMAGE="arm64v8/debian" ;;
armel) DOCKER_IMAGE="arm32v7/debian" ;;
armhf) DOCKER_IMAGE="armhf/debian" ;;
i386) DOCKER_IMAGE="x86/debian" ;;
mips) DOCKER_IMAGE="mips/debian" ;;
mipsel) DOCKER_IMAGE="mipsel/debian" ;;
powerpc) DOCKER_IMAGE="powerpc/debian" ;;
ppc64el) DOCKER_IMAGE="ppc64el/debian" ;;
s390x) DOCKER_IMAGE="s390x/debian" ;;
esac
# Compile Crystal project statically for target architecture
docker pull multiarch/qemu-user-static
docker run \
--rm \
--privileged \
multiarch/qemu-user-static \
--reset -p yes
set -x
docker run \
-it \
-v "$LOCAL_PROJECT_PATH:/app" \
-w /app \
--rm \
--platform linux/arm64 \
"$DOCKER_IMAGE"
exit 0
set -x
docker run \
-it \
-v "$LOCAL_PROJECT_PATH:/app" \
-w /app \
--rm \
--platform linux/arm64 \
"$DOCKER_IMAGE" \
/bin/sh -c "$INSTALL_CRYSTAL && $BUILD_COMMAND"

View file

@ -0,0 +1,86 @@
#!/bin/sh -eu
# vim: set ts=2 sw=2 et:
LOCAL_PROJECT_PATH="${1-$PWD}"
TARGET_ARCH="${2-arm64}"
DOCKER_IMAGE=""
BUILD_COMMAND=" \
shards build --static --release \
&& chown 1000:1000 -R bin \
&& find bin -type f -maxdepth 1 -exec mv {} {}_${TARGET_ARCH} \; \
"
# crystal
INSTALL_CRYSTAL=" \
sed -i -e '/^deb/d' /etc/apt/sources.list \
&& sed -i -e '/jessie.updates/d' /etc/apt/sources.list \
&& sed -i -e 's/^# deb/deb/' /etc/apt/sources.list \
&& apt-get update"
cat > /dev/null <<EOF
"
&& apt-get install -y \
g\+\+ \
gcc \
curl \
autoconf \
automake \
python2 \
libxml2-dev \
llvm-dev \
make \
libssl-dev \
libpcre2-dev \
libyaml-dev \
zlib1g-dev \
"
EOF
# setup arch
case "$TARGET_ARCH" in
amd64) DOCKER_IMAGE="debian:8" ;;
arm64) DOCKER_IMAGE="arm64v8/debian:8" ;;
armel) DOCKER_IMAGE="arm32v7/debian" ;;
armhf) DOCKER_IMAGE="armhf/debian" ;;
i386) DOCKER_IMAGE="x86/debian" ;;
mips) DOCKER_IMAGE="mips/debian" ;;
mipsel) DOCKER_IMAGE="mipsel/debian" ;;
powerpc) DOCKER_IMAGE="powerpc/debian" ;;
ppc64el) DOCKER_IMAGE="ppc64el/debian" ;;
s390x) DOCKER_IMAGE="s390x/debian" ;;
esac
# Compile Crystal project statically for target architecture
docker pull multiarch/qemu-user-static
docker run \
--rm \
--privileged \
multiarch/qemu-user-static \
--reset -p yes
set -x
docker run \
-it \
-v "$LOCAL_PROJECT_PATH:/app" \
-w /app \
--rm \
--platform linux/arm64 \
"$DOCKER_IMAGE" \
/bin/sh -c "$INSTALL_CRYSTAL && bash"
exit 0
set -x
docker run \
-it \
-v "$LOCAL_PROJECT_PATH:/app" \
-w /app \
--rm \
--platform linux/arm64 \
"$DOCKER_IMAGE" \
/bin/sh -c "$INSTALL_CRYSTAL && $BUILD_COMMAND"

View file

@ -0,0 +1,32 @@
#!/bin/sh
set -e
set -u
USER="$(test -d /vagrant && echo "vagrant" || echo "debian")"
HOSTNAME="$(hostname)"
export DEBIAN_FRONTEND=noninteractive
echo "Installing required system packages"
apt-get update --allow-releaseinfo-change
apt-get install -y \
apt-transport-https \
ca-certificates \
git \
curl \
wget \
vim \
gnupg2 \
software-properties-common
# echo "Installing mfm requirements"
# apt-get install -y \
# fzf \
# sshfs \
# httpdirfs \
# libyaml-0-2 \
# libyaml-dev \
# libpcre3-dev \
# libevent-dev

View file

@ -9,26 +9,6 @@ HOSTNAME="$(hostname)"
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
echo "Installing required system packages"
apt-get update --allow-releaseinfo-change
apt-get install -y \
apt-transport-https \
ca-certificates \
git \
curl \
wget \
vim \
gnupg2 \
software-properties-common
echo "Installing recording requirements"
apt-get install -y \
tmux \
mdp \
bat \
asciinema \
termtosvg
echo "Installing mfm requirements" echo "Installing mfm requirements"
apt-get install -y \ apt-get install -y \
fzf \ fzf \
@ -39,24 +19,6 @@ apt-get install -y \
libpcre3-dev \ libpcre3-dev \
libevent-dev libevent-dev
#!/bin/sh
set -e
set -u
USER="$(test -d /vagrant && echo "vagrant" || echo "debian")"
CLUSTERS_DIR=/home/$USER/clusters
# Installation de kompose
if [ ! -f /usr/local/bin/kompose ]; then
DL="$(mktemp)"
curl \
-L https://github.com/kubernetes/kompose/releases/download/v1.22.0/kompose-linux-amd64 \
-o "$DL"
chmod +x "$DL"
mv "$DL" /usr/local/bin/kompose
fi
# Installing asdf # Installing asdf
su - "$USER" -c "git config --global advice.detachedHead false" su - "$USER" -c "git config --global advice.detachedHead false"
su - "$USER" -c "rm -rf ~/.asdf" su - "$USER" -c "rm -rf ~/.asdf"

View file

@ -0,0 +1,22 @@
#!/bin/sh
# install crystal
set -e
set -u
USER="$(test -d /vagrant && echo "vagrant" || echo "debian")"
HOSTNAME="$(hostname)"
export DEBIAN_FRONTEND=noninteractive
echo "Installing required system packages"
apt-get update --allow-releaseinfo-change
echo "Installing recording requirements"
apt-get install -y \
tmux \
mdp \
bat \
asciinema \
termtosvg

View file

@ -1,5 +1,9 @@
version: 2.0 version: 2.0
shards: shards:
crinja:
git: https://github.com/straight-shoota/crinja.git
version: 0.8.1
shellwords: shellwords:
git: https://github.com/sztheory/shellwords-crystal.git git: https://github.com/sztheory/shellwords-crystal.git
version: 0.1.0 version: 0.1.0

View file

@ -18,6 +18,8 @@ targets:
# Short description of gx-vault # Short description of gx-vault
dependencies: dependencies:
crinja:
github: straight-shoota/crinja
shellwords: shellwords:
github: szTheory/shellwords-crystal github: szTheory/shellwords-crystal

View file

@ -8,7 +8,10 @@ require "./config"
require "./fzf" require "./fzf"
module GX module GX
VERSION="v0.1.9"
class Cli class Cli
Log = ::Log.for("cli")
@config : Config @config : Config
@ -27,65 +30,87 @@ module GX
parser.banner = "Usage: #{PROGRAM_NAME} [options]\n\nGlobal options" parser.banner = "Usage: #{PROGRAM_NAME} [options]\n\nGlobal options"
parser.on("-c", "--config FILE", "Set configuration file") do |path| parser.on("-c", "--config FILE", "Set configuration file") do |path|
Log.info { "Configuration set to #{path}" }
@config.path = path @config.path = path
end end
parser.on("-v", "--verbose", "Set more verbosity") do |flag|
Log.info { "Verbosity enabled" }
@config.verbose = true
end
parser.on("--version", "Show version") do |flag|
@config.mode = Config::Mode::ShowVersion
end
parser.on("-h", "--help", "Show this help") do |flag| parser.on("-h", "--help", "Show this help") do |flag|
STDOUT.puts parser STDOUT.puts parser
exit(0) exit(0)
end end
parser.separator("\nCommands") parser.separator("\nCommands")
parser.on("create", "Create vault") do parser.on("config", "Manage configuration") do
@config.mode = Config::Mode::Add parser.banner = "Usage: #{PROGRAM_NAME} config [commands] [options]\n\nGlobal options"
parser.separator("\nCommands")
parser.banner = "Usage: #{PROGRAM_NAME} create [options]\n\nGlobal options" parser.on("create", "Create vault") do
parser.separator("\nCommand options") @config.mode = Config::Mode::ConfigAdd
parser.on("-n", "--name", "Set vault name") do |name| parser.banner = "Usage: #{PROGRAM_NAME} config create [commands] [options]\n\nGlobal options"
add_args = add_args.merge({ name: name }) parser.separator("\nCommand options")
parser.on("-n", "--name", "Set vault name") do |name|
add_args = add_args.merge({ name: name })
end
parser.on("-p", "--path", "Set vault encrypted path") do |path|
add_args = add_args.merge({ path: path })
end
end end
parser.on("-p", "--path", "Set vault encrypted path") do |path|
add_args = add_args.merge({ path: path }) parser.on("delete", "Delete vault") do
@config.mode = Config::Mode::ConfigAdd
parser.banner = "Usage: #{PROGRAM_NAME} delete [options]\n\nGlobal options"
parser.separator("\nCommand options")
parser.on("-n", "--name", "Set vault name") do |name|
delete_args = delete_args.merge({ name: name })
end
end end
end
parser.on("delete", "Delete vault") do parser.on("edit", "Edit configuration") do |flag|
@config.mode = Config::Mode::Add @config.mode = Config::Mode::ConfigEdit
parser.banner = "Usage: #{PROGRAM_NAME} delete [options]\n\nGlobal options"
parser.separator("\nCommand options")
parser.on("-n", "--name", "Set vault name") do |name|
delete_args = delete_args.merge({ name: name })
end end
end end
parser.on("edit", "Edit configuration") do |flag|
@config.mode = Config::Mode::Edit
end
end end
pparser.parse(args) pparser.parse(args)
end end
def run() def run()
@config.load_from_file case @config.mode
when Config::Mode::ShowVersion
STDOUT.puts "#{PROGRAM_NAME} #{VERSION}"
when Config::Mode::Mount
@config.load_from_file
mount
end
end
def mount()
names_display = {} of String => NamedTuple(filesystem: Filesystem, ansi_name: String) names_display = {} of String => NamedTuple(filesystem: Filesystem, ansi_name: String)
@config.filesystems.each do |filesystem| @config.filesystems.each do |filesystem|
fs_str = filesystem.type.ljust(12,' ') fs_str = filesystem.type.ljust(12,' ')
result_name =
if filesystem.mounted? suffix = ""
"#{fs_str} #{filesystem.name} [open]" suffix_ansi = ""
else if filesystem.mounted?
"#{fs_str} #{filesystem.name}" suffix = "[open]"
end suffix_ansi = "[#{ "open".colorize(:green) }]"
ansi_name = end
if filesystem.mounted?
"#{fs_str.colorize(:dark_gray)} #{filesystem.name} [#{ "open".colorize(:green) }]" result_name = "#{fs_str} #{filesystem.name} #{suffix}".strip
else ansi_name = "#{fs_str.colorize(:dark_gray)} #{filesystem.name} #{suffix_ansi}".strip
"#{fs_str.colorize(:dark_gray)} #{filesystem.name}"
end
names_display[result_name] = { names_display[result_name] = {
filesystem: filesystem, filesystem: filesystem,
@ -93,7 +118,7 @@ module GX
} }
end end
result_filesystem_name = Fzf.run(names_display.values.map(&.[:ansi_name]).sort) result_filesystem_name = Fzf.run(names_display.values.map(&.[:ansi_name]).sort).strip
selected_filesystem = names_display[result_filesystem_name][:filesystem] selected_filesystem = names_display[result_filesystem_name][:filesystem]
puts ">> #{selected_filesystem.name}".colorize(:yellow) puts ">> #{selected_filesystem.name}".colorize(:yellow)

View file

@ -3,14 +3,20 @@
# SPDX-FileCopyrightText: 2023 Glenn Y. Rolland <glenux@glenux.net> # SPDX-FileCopyrightText: 2023 Glenn Y. Rolland <glenux@glenux.net>
# Copyright © 2023 Glenn Y. Rolland <glenux@glenux.net> # Copyright © 2023 Glenn Y. Rolland <glenux@glenux.net>
require "crinja"
require "./filesystems" require "./filesystems"
module GX module GX
class Config class Config
Log = ::Log.for("config")
enum Mode enum Mode
Add ConfigAdd
Edit ConfigDelete
Run ConfigEdit
ShowVersion
Mount
end end
record NoArgs record NoArgs
@ -19,36 +25,69 @@ module GX
getter filesystems : Array(Filesystem) getter filesystems : Array(Filesystem)
getter home_dir : String getter home_dir : String
property verbose : Bool
property mode : Mode property mode : Mode
property path : String property path : String?
property args : AddArgs.class | DelArgs.class | NoArgs.class property args : AddArgs.class | DelArgs.class | NoArgs.class
DEFAULT_CONFIG_PATH = "mfm.yml"
def initialize() def initialize()
if !ENV["HOME"]? if !ENV["HOME"]?
raise "Home directory not found" raise "Home directory not found"
end end
@home_dir = ENV["HOME"] @home_dir = ENV["HOME"]
@mode = Mode::Run @verbose = false
@mode = Mode::Mount
@filesystems = [] of Filesystem @filesystems = [] of Filesystem
@path = File.join(@home_dir, ".config", DEFAULT_CONFIG_PATH) @path = nil
@args = NoArgs @args = NoArgs
end end
def detect_config_file()
possible_files = [
File.join(@home_dir, ".config", "mfm", "config.yaml"),
File.join(@home_dir, ".config", "mfm", "config.yml"),
File.join(@home_dir, ".config", "mfm.yaml"),
File.join(@home_dir, ".config", "mfm.yml"),
File.join("/etc", "mfm", "config.yaml"),
File.join("/etc", "mfm", "config.yml"),
]
possible_files.each do |file_path|
if File.exists?(file_path)
Log.info { "Configuration file found: #{file_path}" }
return file_path if File.exists?(file_path)
else
Log.debug { "Configuration file not found: #{file_path}" }
end
end
Log.error { "No configuration file found in any of the standard locations" }
raise "Configuration file not found"
end
def load_from_file def load_from_file
path = @path
if path.nil?
path = detect_config_file()
end
@path = path
@filesystems = [] of Filesystem @filesystems = [] of Filesystem
if !File.exists? @path if !File.exists? path
STDERR.puts "Error: file #{@path} does not exist!".colorize(:red) Log.error { "File #{path} does not exist!".colorize(:red) }
exit(1) exit(1)
end end
load_filesystems(@path) load_filesystems(path)
end end
private def load_filesystems(config_path : String) private def load_filesystems(config_path : String)
yaml_data = YAML.parse(File.read(config_path)) file_data = File.read(config_path)
# FIXME: render template on a value basis (instead of global)
file_patched = Crinja.render(file_data, {"env" => ENV.to_h})
yaml_data = YAML.parse(file_patched)
vaults_data = yaml_data["filesystems"].as_a vaults_data = yaml_data["filesystems"].as_a
vaults_data.each do |filesystem_data| vaults_data.each do |filesystem_data|

View file

@ -12,6 +12,7 @@ module GX
getter remote_path : String = "" getter remote_path : String = ""
getter remote_user : String = "" getter remote_user : String = ""
getter remote_host : String = "" getter remote_host : String = ""
getter remote_port : String = "22"
@[YAML::Field(key: "mount_dir", ignore: true)] @[YAML::Field(key: "mount_dir", ignore: true)]
getter mount_dir : String = "" getter mount_dir : String = ""
@ -34,7 +35,11 @@ module GX
error = STDERR error = STDERR
process = Process.new( process = Process.new(
"sshfs", "sshfs",
["#{remote_user}@#{remote_host}:#{remote_path}", mount_dir], [
"-p", remote_port,
"#{remote_user}@#{remote_host}:#{remote_path}",
mount_dir
],
input: input, input: input,
output: output, output: output,
error: error error: error

View file

@ -6,11 +6,33 @@
require "yaml" require "yaml"
require "colorize" require "colorize"
require "json" require "json"
require "log"
require "./filesystems/gocryptfs" require "./filesystems/gocryptfs"
require "./config" require "./config"
require "./cli" require "./cli"
struct BaseFormat < Log::StaticFormatter
def run
string @entry.severity.label.downcase
string "("
source
string "): "
message
end
end
Log.setup do |config|
backend = Log::IOBackend.new(formatter: BaseFormat)
config.bind "*", Log::Severity::Info, backend
if ENV["LOG_LEVEL"]?
level = Log::Severity.parse(ENV["LOG_LEVEL"]) || Log::Severity::Info
config.bind "*", level, backend
end
end
app = GX::Cli.new app = GX::Cli.new
app.parse_command_line(ARGV) app.parse_command_line(ARGV)
app.run app.run