Compare commits
14 Commits
daeeffa995
...
0.1.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
8ee4e436aa
|
|||
|
064976ed6e
|
|||
|
45a1520a2b
|
|||
|
f7f3ac5dc7
|
|||
|
15454e3686
|
|||
|
63012131d4
|
|||
|
a013e6fe81
|
|||
|
9d8f3368ec
|
|||
|
88a1f181cb
|
|||
|
a6227a80db
|
|||
|
d1a47e2ac6
|
|||
|
86dc084e7d
|
|||
|
d3a9aa2f00
|
|||
|
1820988894
|
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 }}
|
||||||
12
README.md
12
README.md
@@ -21,7 +21,7 @@ brew install michael/formula/hpa
|
|||||||
Installation on platforms other than `macOS` are currently being worked on, along with support for
|
Installation on platforms other than `macOS` are currently being worked on, along with support for
|
||||||
running in a `docker` container.
|
running in a `docker` container.
|
||||||
|
|
||||||
### Ensuring dependencies are installed.
|
### Ensuring dependencies are installed
|
||||||
|
|
||||||
This application requires some dependencies to be installed on your system, you can install the
|
This application requires some dependencies to be installed on your system, you can install the
|
||||||
dependencies with the following command.
|
dependencies with the following command.
|
||||||
@@ -44,7 +44,7 @@ repositories, and encrypt / decrypt variable files. The playbook get's installed
|
|||||||
> NOTE: All commands accept a `--help` option which will display the arguments and options a command
|
> 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.
|
> can use, along with example usage of the commands.
|
||||||
|
|
||||||
### Configure the application.
|
### Configure the application
|
||||||
|
|
||||||
When you first download the application you can setup the configuration file for your use case.
|
When you first download the application you can setup the configuration file for your use case.
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ allows your template to expand over time.
|
|||||||
Once your template is setup, make sure that your configuration file is setup to point to your
|
Once your template is setup, make sure that your configuration file is setup to point to your
|
||||||
customized template.
|
customized template.
|
||||||
|
|
||||||
## Creating a project.
|
## Creating a project
|
||||||
|
|
||||||
The first step after having your template defined is to create a project that uses it. The below
|
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.
|
command will create a template in the `~/consults/my-first-consult` directory.
|
||||||
@@ -99,7 +99,7 @@ Or if your configuration has `directory` set in the `template` section.
|
|||||||
hpa create --use-local-template ~/consults/my-first-consult
|
hpa create --use-local-template ~/consults/my-first-consult
|
||||||
```
|
```
|
||||||
|
|
||||||
## Generating output files.
|
## Generating output files
|
||||||
|
|
||||||
Once you have created a project and edited the contents to your liking. You can then generate the
|
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.
|
final output file (typically a pdf) that can be sent to your customer.
|
||||||
@@ -122,7 +122,7 @@ Currently the supported output file types are:
|
|||||||
1. LaTeX
|
1. LaTeX
|
||||||
1. HTML
|
1. HTML
|
||||||
|
|
||||||
## Build command.
|
## Build command
|
||||||
|
|
||||||
The command line tool goes through an intermediate step when generating output, which is called
|
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
|
`build`. The build step generates the final output files using defined variables that are located in
|
||||||
@@ -147,7 +147,7 @@ like to generate output for.
|
|||||||
hpa build --project-directory ~/consults/my-first-consult
|
hpa build --project-directory ~/consults/my-first-consult
|
||||||
```
|
```
|
||||||
|
|
||||||
## Some General Usage Notes:
|
## Some General Usage Notes
|
||||||
|
|
||||||
There is often a lot of output to the console when running commands, which can be problematic if you
|
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
|
want to pipe the output into other command line tools, so all options accept a `-q | --quiet` flag
|
||||||
|
|||||||
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 {
|
func isDirectory(_ url: URL) -> Bool {
|
||||||
var isDirectory: ObjCBool = false
|
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
|
return isDirectory.boolValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ extension PlaybookClient.RunPlaybook.SharedRunOptions {
|
|||||||
) async throws -> T {
|
) async throws -> T {
|
||||||
@Dependency(\.commandClient) var commandClient
|
@Dependency(\.commandClient) var commandClient
|
||||||
|
|
||||||
|
try await ensurePlaybookExists()
|
||||||
|
|
||||||
return try await commandClient.run(
|
return try await commandClient.run(
|
||||||
logging: loggingOptions,
|
logging: loggingOptions,
|
||||||
quiet: quiet,
|
quiet: quiet,
|
||||||
@@ -101,6 +103,17 @@ extension PlaybookClient.RunPlaybook.SharedRunOptions {
|
|||||||
return (arguments, output)
|
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)
|
@_spi(Internal)
|
||||||
|
|||||||
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 fileClient = FileClient.liveValue
|
||||||
|
|
||||||
let vaultFilePath = url.appending(path: fileName)
|
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)!
|
let output = try await fileClient.findVaultFile(url)!
|
||||||
|
|
||||||
#expect(output.cleanFilePath == vaultFilePath.cleanFilePath)
|
#expect(output.cleanFilePath == vaultFilePath.cleanFilePath)
|
||||||
@@ -43,7 +47,11 @@ struct FileClientTests {
|
|||||||
try await fileClient.createDirectory(subDir)
|
try await fileClient.createDirectory(subDir)
|
||||||
|
|
||||||
let vaultFilePath = subDir.appending(path: fileName)
|
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)!
|
let output = try await fileClient.findVaultFile(url)!
|
||||||
|
|
||||||
#expect(output.cleanFilePath == vaultFilePath.cleanFilePath)
|
#expect(output.cleanFilePath == vaultFilePath.cleanFilePath)
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ struct PlaybookClientTests: TestCase {
|
|||||||
@Test
|
@Test
|
||||||
func generateTemplate() async throws {
|
func generateTemplate() async throws {
|
||||||
try await withCapturingCommandClient("generateTemplate") {
|
try await withCapturingCommandClient("generateTemplate") {
|
||||||
|
$0.fileClient.isDirectory = { _ in true }
|
||||||
$0.configurationClient = .mock()
|
$0.configurationClient = .mock()
|
||||||
$0.playbookClient = .liveValue
|
$0.playbookClient = .liveValue
|
||||||
} run: {
|
} run: {
|
||||||
@@ -180,6 +181,7 @@ struct PlaybookClientTests: TestCase {
|
|||||||
operation: @Sendable @escaping () async throws -> Void
|
operation: @Sendable @escaping () async throws -> Void
|
||||||
) async rethrows {
|
) async rethrows {
|
||||||
try await withDependencies {
|
try await withDependencies {
|
||||||
|
$0.fileClient.isDirectory = { _ in true }
|
||||||
$0.configurationClient = .mock(configuration)
|
$0.configurationClient = .mock(configuration)
|
||||||
$0.commandClient = .capturing(capturing)
|
$0.commandClient = .capturing(capturing)
|
||||||
$0.playbookClient = .liveValue
|
$0.playbookClient = .liveValue
|
||||||
|
|||||||
@@ -2,23 +2,76 @@
|
|||||||
# Build the executable
|
# Build the executable
|
||||||
ARG SWIFT_IMAGE_VERSION="6.0.3"
|
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
|
WORKDIR /build
|
||||||
|
|
||||||
|
# Resolve dependencies, this creates a cached layer.
|
||||||
COPY ./Package.* ./
|
COPY ./Package.* ./
|
||||||
RUN swift package resolve
|
RUN --mount=type=cache,target=/build/.build swift package resolve
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN swift build -c release -Xswiftc -g
|
|
||||||
|
|
||||||
# Run image
|
# Build the application.
|
||||||
FROM swift:${SWIFT_IMAGE_VERSION}-slim
|
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 \
|
apt-get -q install -y \
|
||||||
ansible \
|
ansible \
|
||||||
curl \
|
curl \
|
||||||
|
imagemagick \
|
||||||
pandoc \
|
pandoc \
|
||||||
texlive \
|
texlive \
|
||||||
|
texlive-xetex \
|
||||||
|
libjemalloc2 \
|
||||||
|
libcurl4 \
|
||||||
|
tzdata \
|
||||||
|
vim \
|
||||||
&& rm -r /var/lib/apt/lists/*
|
&& rm -r /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY --from=build /build/.build/release/hpa /usr/local/bin
|
# Install the hpa executable.
|
||||||
CMD ["/bin/bash", "-xc", "/usr/local/bin/hpa"]
|
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[@]}"
|
||||||
Reference in New Issue
Block a user