Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
8ee4e436aa
|
|||
|
064976ed6e
|
|||
|
45a1520a2b
|
|||
|
f7f3ac5dc7
|
|||
|
15454e3686
|
|||
|
63012131d4
|
|||
|
a013e6fe81
|
|||
|
9d8f3368ec
|
|||
|
88a1f181cb
|
|||
|
a6227a80db
|
|||
|
d1a47e2ac6
|
|||
|
86dc084e7d
|
|||
|
d3a9aa2f00
|
|||
|
1820988894
|
|||
| daeeffa995 |
@@ -2,7 +2,7 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "dev"]
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
||||
55
.gitea/workflows/docker.yaml
Normal file
55
.gitea/workflows/docker.yaml
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Build docker images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request: {}
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Setup docker buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Container Registery
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.housh.dev
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: git.housh.dev/michael/swift-hpa
|
||||
tags: |
|
||||
type=schedule
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
@@ -15,5 +15,3 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: true
|
||||
|
||||
20
LICENSE
Normal file
20
LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2024 Michael Housh
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
179
README.md
Normal file
179
README.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# swift-hpa
|
||||
|
||||
A command-line application for managing home performance assessment projects from user defined
|
||||
template repositories.
|
||||
|
||||
This tool is a wrapper around several other command line applications, the primary ones being:
|
||||
|
||||
1. `ansible-playbook`
|
||||
1. `ansible-vault`
|
||||
1. `pandoc`
|
||||
|
||||
## Installation
|
||||
|
||||
You can install the application using homebrew.
|
||||
|
||||
```bash
|
||||
brew tap michael/formula https://git.housh.dev/michael/homebrew-formula
|
||||
brew install michael/formula/hpa
|
||||
```
|
||||
|
||||
Installation on platforms other than `macOS` are currently being worked on, along with support for
|
||||
running in a `docker` container.
|
||||
|
||||
### Ensuring dependencies are installed
|
||||
|
||||
This application requires some dependencies to be installed on your system, you can install the
|
||||
dependencies with the following command.
|
||||
|
||||
```bash
|
||||
hpa utils install-dependencies
|
||||
```
|
||||
|
||||
The dependencies installed are:
|
||||
|
||||
1. ansible
|
||||
1. imagemagick
|
||||
1. pandoc
|
||||
1. texLive
|
||||
|
||||
It will also download an ansible-playbook that is used to generate output files, template
|
||||
repositories, and encrypt / decrypt variable files. The playbook get's installed to
|
||||
`~/.local/share/hpa/playbook`.
|
||||
|
||||
> NOTE: All commands accept a `--help` option which will display the arguments and options a command
|
||||
> can use, along with example usage of the commands.
|
||||
|
||||
### Configure the application
|
||||
|
||||
When you first download the application you can setup the configuration file for your use case.
|
||||
|
||||
```bash
|
||||
hpa utils generate-config
|
||||
```
|
||||
|
||||
This will create a configuration file in the default location: `~/.config/hpa/config.toml`, which
|
||||
can be edited to suit your needs.
|
||||
|
||||
## Getting Started
|
||||
|
||||
The first step to getting started is creating your template. This is used to create projects. The
|
||||
template defines the structure of a project and defines variables which are used to generate the
|
||||
final output files of a project.
|
||||
|
||||
You can generate the template using following command:
|
||||
|
||||
```bash
|
||||
hpa utils generate-template --path ~/projects/my-template
|
||||
```
|
||||
|
||||
Where the `--path` is where you would like the template to be on your local system.
|
||||
|
||||
It is recommended that after you get your template setup to your liking that you turn it into a
|
||||
`git` repository. Therefore your projects can be pinned to specific version of the template. This
|
||||
allows your template to expand over time.
|
||||
|
||||
Once your template is setup, make sure that your configuration file is setup to point to your
|
||||
customized template.
|
||||
|
||||
## Creating a project
|
||||
|
||||
The first step after having your template defined is to create a project that uses it. The below
|
||||
command will create a template in the `~/consults/my-first-consult` directory.
|
||||
|
||||
```bash
|
||||
hpa create ~/consults/my-first-consult
|
||||
```
|
||||
|
||||
The above assumes that your template is a `git` repository and that your configuration is setup
|
||||
properly. If you want to experiment with a local template that is on your system then you can you
|
||||
can use one of the following command options.
|
||||
|
||||
```bash
|
||||
hpa create --template-dir ~/projects/my-template ~/consults/my-first-consult
|
||||
```
|
||||
|
||||
Or if your configuration has `directory` set in the `template` section.
|
||||
|
||||
```bash
|
||||
hpa create --use-local-template ~/consults/my-first-consult
|
||||
```
|
||||
|
||||
## Generating output files
|
||||
|
||||
Once you have created a project and edited the contents to your liking. You can then generate the
|
||||
final output file (typically a pdf) that can be sent to your customer.
|
||||
|
||||
```bash
|
||||
hpa generate pdf
|
||||
```
|
||||
|
||||
The above _assumes_ that you are inside your project directory, if you would like to generate an
|
||||
output file from outside of your project directory you can specify the path to the project you would
|
||||
like to generate output for.
|
||||
|
||||
```bash
|
||||
hpa generate pdf --project-directory ~/consults/my-first-consult
|
||||
```
|
||||
|
||||
Currently the supported output file types are:
|
||||
|
||||
1. PDF
|
||||
1. LaTeX
|
||||
1. HTML
|
||||
|
||||
## Build command
|
||||
|
||||
The command line tool goes through an intermediate step when generating output, which is called
|
||||
`build`. The build step generates the final output files using defined variables that are located in
|
||||
your project directory or in your template directory. It will decrypt any sensitive data stored in
|
||||
`vault` files as well.
|
||||
|
||||
These files get placed inside a directory in the project, default location is `.build`. The generate
|
||||
commands by default build the project for you, unless you specify the `--no-build` option.
|
||||
|
||||
You can explore the contents of the `.build` directory or if you'd like to separate the build and
|
||||
generate steps, you can build a project using the following command:
|
||||
|
||||
```bash
|
||||
hpa build
|
||||
```
|
||||
|
||||
The above _assumes_ that you are inside your project directory, if you would like to generate an
|
||||
output file from outside of your project directory you can specify the path to the project you would
|
||||
like to generate output for.
|
||||
|
||||
```bash
|
||||
hpa build --project-directory ~/consults/my-first-consult
|
||||
```
|
||||
|
||||
## Some General Usage Notes
|
||||
|
||||
There is often a lot of output to the console when running commands, which can be problematic if you
|
||||
want to pipe the output into other command line tools, so all options accept a `-q | --quiet` flag
|
||||
which will suppress logging output and allow piping into other commands.
|
||||
|
||||
Along the similar line, if you would like to increase the logging output then all commands accept
|
||||
`-v | --verbose` that will increase the logging output. This can be passed multiple times, so for
|
||||
the highest log output you can do `-vvv`.
|
||||
|
||||
## Uninstalling
|
||||
|
||||
You can uninstall the application using:
|
||||
|
||||
```bash
|
||||
brew uninstall hpa
|
||||
```
|
||||
|
||||
Also remove the configuration and playbook directories.
|
||||
|
||||
```bash
|
||||
rm -rf ~/.config/hpa
|
||||
rm -rf ~/.local/share/hpa
|
||||
```
|
||||
|
||||
## LICENSE
|
||||
|
||||
This project is licensed under the `MIT` license.
|
||||
|
||||
[See license](https://git.housh.dev/michael/swift-hpa/LICENSE)
|
||||
24
Release.md
Normal file
24
Release.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Release Workflow Steps
|
||||
|
||||
This is a reminder of the steps used to create a release and update the homebrew formula.
|
||||
|
||||
> Note: These steps apply to the version hosted on `gitea`, on `github` more of these steps can be
|
||||
> automated in `ci`, but there are no `macOS` host runners currently in `gitea`, so the bottles need
|
||||
> built on `macOS`.
|
||||
|
||||
1. Update the version in `Sources/hpa/Version.swift`.
|
||||
1. Tag the commit with the next version tag.
|
||||
1. Push the tagged commit, this will initiate the release being created.
|
||||
1. Get the `sha` of the `*.tar.gz` in the release.
|
||||
1. `just get-release-sha`
|
||||
1. Update the homebrew formula url, sha256, and version at top of the homebrew formula.
|
||||
1. `cd $(brew --repo michael/formula)`
|
||||
1. Build and generate a homebrew bottle.
|
||||
1. `just bottle`
|
||||
1. Update the `bottle do` section of homebrew formula with output during previous step.
|
||||
1. Also make sure the `root_url` in the bottle section points to the new release.
|
||||
1. Upload the bottle `*.tar.gz` file that was created to the release.
|
||||
1. Generate a pull-request to the formula repo.
|
||||
1. Generate a pull-request to this repo to merge into main.
|
||||
1. Remove the bottle from current directory.
|
||||
1. `just remove-bottles`
|
||||
@@ -120,7 +120,11 @@ struct LiveFileClient: Sendable {
|
||||
|
||||
func isDirectory(_ url: URL) -> Bool {
|
||||
var isDirectory: ObjCBool = false
|
||||
manager.fileExists(atPath: url.cleanFilePath, isDirectory: &isDirectory)
|
||||
#if os(Linux)
|
||||
_ = manager.fileExists(atPath: url.cleanFilePath, isDirectory: &isDirectory)
|
||||
#else
|
||||
manager.fileExists(atPath: url.cleanFilePath, isDirectory: &isDirectory)
|
||||
#endif
|
||||
return isDirectory.boolValue
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,8 @@ extension PlaybookClient.RunPlaybook.SharedRunOptions {
|
||||
) async throws -> T {
|
||||
@Dependency(\.commandClient) var commandClient
|
||||
|
||||
try await ensurePlaybookExists()
|
||||
|
||||
return try await commandClient.run(
|
||||
logging: loggingOptions,
|
||||
quiet: quiet,
|
||||
@@ -101,6 +103,17 @@ extension PlaybookClient.RunPlaybook.SharedRunOptions {
|
||||
return (arguments, output)
|
||||
}
|
||||
}
|
||||
|
||||
private func ensurePlaybookExists() async throws {
|
||||
@Dependency(\.fileClient) var fileClient
|
||||
@Dependency(\.playbookClient.repository) var repository
|
||||
|
||||
let directory = try await repository.directory()
|
||||
let exists = try await fileClient.isDirectory(URL(filePath: directory))
|
||||
if !exists {
|
||||
try await repository.install()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@_spi(Internal)
|
||||
|
||||
@@ -40,7 +40,10 @@ struct CreateCommand: AsyncParsableCommand {
|
||||
var templateDir: String?
|
||||
|
||||
@Flag(
|
||||
name: .shortAndLong,
|
||||
name: [
|
||||
.short,
|
||||
.customLong("use-local-template")
|
||||
],
|
||||
help: "Force using a local template directory."
|
||||
)
|
||||
var localTemplateDir = false
|
||||
|
||||
7
TODO.md
Normal file
7
TODO.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# TODO
|
||||
|
||||
- [ ] Build docker images in ci.
|
||||
- [ ] Generate documentation for docker usage.
|
||||
- [ ] Generally need to create a local wrapper script to mount volumes.
|
||||
- [ ] Completions can be installed / used with the wrapper script by calling
|
||||
`docker run --it --rm <image> --generate-completion-script <shell> > /path/to/completions/on/local`
|
||||
@@ -23,7 +23,11 @@ struct FileClientTests {
|
||||
let fileClient = FileClient.liveValue
|
||||
|
||||
let vaultFilePath = url.appending(path: fileName)
|
||||
FileManager.default.createFile(atPath: vaultFilePath.cleanFilePath, contents: nil)
|
||||
#if os(Linux)
|
||||
_ = FileManager.default.createFile(atPath: vaultFilePath.cleanFilePath, contents: nil)
|
||||
#else
|
||||
FileManager.default.createFile(atPath: vaultFilePath.cleanFilePath, contents: nil)
|
||||
#endif
|
||||
let output = try await fileClient.findVaultFile(url)!
|
||||
|
||||
#expect(output.cleanFilePath == vaultFilePath.cleanFilePath)
|
||||
@@ -43,7 +47,11 @@ struct FileClientTests {
|
||||
try await fileClient.createDirectory(subDir)
|
||||
|
||||
let vaultFilePath = subDir.appending(path: fileName)
|
||||
FileManager.default.createFile(atPath: vaultFilePath.cleanFilePath, contents: nil)
|
||||
#if os(Linux)
|
||||
_ = FileManager.default.createFile(atPath: vaultFilePath.cleanFilePath, contents: nil)
|
||||
#else
|
||||
FileManager.default.createFile(atPath: vaultFilePath.cleanFilePath, contents: nil)
|
||||
#endif
|
||||
let output = try await fileClient.findVaultFile(url)!
|
||||
|
||||
#expect(output.cleanFilePath == vaultFilePath.cleanFilePath)
|
||||
|
||||
@@ -145,6 +145,7 @@ struct PlaybookClientTests: TestCase {
|
||||
@Test
|
||||
func generateTemplate() async throws {
|
||||
try await withCapturingCommandClient("generateTemplate") {
|
||||
$0.fileClient.isDirectory = { _ in true }
|
||||
$0.configurationClient = .mock()
|
||||
$0.playbookClient = .liveValue
|
||||
} run: {
|
||||
@@ -180,6 +181,7 @@ struct PlaybookClientTests: TestCase {
|
||||
operation: @Sendable @escaping () async throws -> Void
|
||||
) async rethrows {
|
||||
try await withDependencies {
|
||||
$0.fileClient.isDirectory = { _ in true }
|
||||
$0.configurationClient = .mock(configuration)
|
||||
$0.commandClient = .capturing(capturing)
|
||||
$0.playbookClient = .liveValue
|
||||
|
||||
@@ -2,23 +2,76 @@
|
||||
# Build the executable
|
||||
ARG SWIFT_IMAGE_VERSION="6.0.3"
|
||||
|
||||
FROM swift:${SWIFT_IMAGE_VERSION} AS build
|
||||
# ============================================================
|
||||
# Build Swift Image
|
||||
# ============================================================
|
||||
FROM docker.io/swift:${SWIFT_IMAGE_VERSION} AS build
|
||||
|
||||
# Install OS updates
|
||||
RUN export DEBIAN_FRONTEND=nointeractive DEBCONF_NOINTERACTIVE_SEEN=true && \
|
||||
apt-get -q update && \
|
||||
apt-get -q dist-upgrade -y && \
|
||||
apt-get install -y libjemalloc-dev
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Resolve dependencies, this creates a cached layer.
|
||||
COPY ./Package.* ./
|
||||
RUN swift package resolve
|
||||
RUN --mount=type=cache,target=/build/.build swift package resolve
|
||||
|
||||
COPY . .
|
||||
RUN swift build -c release -Xswiftc -g
|
||||
|
||||
# Run image
|
||||
FROM swift:${SWIFT_IMAGE_VERSION}-slim
|
||||
# Build the application.
|
||||
RUN --mount=type=cache,target=/build/.build \
|
||||
swift build -c release \
|
||||
--product hpa \
|
||||
--static-swift-stdlib \
|
||||
-Xlinker -ljemalloc
|
||||
|
||||
RUN export DEBIAN_FRONTEND=nointeractive DEBCONF_NOINTERACTIVE_SEEN=true && apt-get -q update && \
|
||||
# Switch to staging area.
|
||||
WORKDIR /staging
|
||||
|
||||
# Copy main executable to staging area.
|
||||
RUN --mount=type=cache,target=/build/.build \
|
||||
cp "$(swift build --package-path /build -c release --show-bin-path)/hpa" ./
|
||||
|
||||
# ============================================================
|
||||
# Run Image
|
||||
# ============================================================
|
||||
FROM docker.io/ubuntu:noble
|
||||
|
||||
# Update base image and install needed packages.
|
||||
#
|
||||
# NOTE: NB: Installs vim as minimal text editor to use inside the container, bc
|
||||
# when I mount my home directory / use my neovim config it requires
|
||||
# neovim v11+, but generally only going to edit ansible vault files
|
||||
# inside the container.
|
||||
RUN export DEBIAN_FRONTEND=nointeractive DEBCONF_NOINTERACTIVE_SEEN=true && \
|
||||
apt-get -q update && \
|
||||
apt-get -q dist-upgrade -y && \
|
||||
apt-get -q install -y \
|
||||
ansible \
|
||||
curl \
|
||||
imagemagick \
|
||||
pandoc \
|
||||
texlive \
|
||||
texlive-xetex \
|
||||
libjemalloc2 \
|
||||
libcurl4 \
|
||||
tzdata \
|
||||
vim \
|
||||
&& rm -r /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=build /build/.build/release/hpa /usr/local/bin
|
||||
CMD ["/bin/bash", "-xc", "/usr/local/bin/hpa"]
|
||||
# Install the hpa executable.
|
||||
COPY --from=build /staging/hpa /usr/local/bin
|
||||
|
||||
# Install the entrypoint script and make execuatable.
|
||||
COPY docker/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh && mkdir /root/project
|
||||
|
||||
# Set workdir and volume mounts.
|
||||
WORKDIR /root/project
|
||||
VOLUME /root/project
|
||||
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
CMD ["--help"]
|
||||
|
||||
27
docker/entrypoint.sh
Normal file
27
docker/entrypoint.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
declare -a args
|
||||
|
||||
# Allows to attach to a shell inside the container, or run ansbile commands,
|
||||
# otherwise run the 'hpa' script with the given arguments.
|
||||
#
|
||||
while [[ $# -gt 0 ]]; do
|
||||
if [[ $1 == "/bin/bash" ]] || [[ $1 == "bash" ]]; then
|
||||
shift
|
||||
/bin/bash "$@"
|
||||
exit $?
|
||||
elif [[ $1 == "/bin/sh" ]] || [[ $1 == "sh" ]]; then
|
||||
shift
|
||||
/bin/sh "$@"
|
||||
exit $?
|
||||
elif [[ $1 =~ ^ansible ]]; then
|
||||
exec "$@"
|
||||
exit $?
|
||||
else
|
||||
args+=("$1")
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
# If we made it here then run the hpa script.
|
||||
/usr/local/bin/hpa "${args[@]}"
|
||||
17
justfile
17
justfile
@@ -6,6 +6,8 @@ tap_url := "https://git.housh.dev/michael/homebrew-formula"
|
||||
tap := "michael/formula"
|
||||
formula := "hpa"
|
||||
|
||||
release_base_url := "https://git.housh.dev/michael/swift-hpa/archive"
|
||||
|
||||
# Build and bottle homebrew formula.
|
||||
bottle:
|
||||
@brew uninstall {{formula}} || true
|
||||
@@ -50,6 +52,12 @@ test-docker *ARGS: (build-docker-test)
|
||||
{{docker_image_name}}:test \
|
||||
swift test {{ARGS}}
|
||||
|
||||
alias td := test-docker
|
||||
|
||||
# Remove bottles
|
||||
remove-bottles:
|
||||
rm -rf *.gz
|
||||
|
||||
# Run the application.
|
||||
run *ARGS:
|
||||
swift run hpa {{ARGS}}
|
||||
@@ -67,3 +75,12 @@ update-version:
|
||||
--allow-writing-to-package-directory \
|
||||
update-version \
|
||||
hpa
|
||||
|
||||
# Get the sha256 sum of the release and copy to clipboard.
|
||||
get-release-sha prefix="": (build "release")
|
||||
version=$(.build/release/hpa --version) && \
|
||||
url="{{release_base_url}}/{{prefix}}${version}.tar.gz" && \
|
||||
sha=$(curl "$url" | shasum -a 256) && \
|
||||
stripped="${sha% *}" && \
|
||||
echo "$stripped" | pbcopy && \
|
||||
echo "Copied sha to clipboard: $stripped"
|
||||
|
||||
Reference in New Issue
Block a user