9 Commits

Author SHA1 Message Date
63012131d4 feat: Removes image creation on dev branch, only create for main branch.
All checks were successful
CI / Run Tests (pull_request) Successful in 1m25s
Build docker images / docker (pull_request) Successful in 6m44s
CI / Run Tests (push) Successful in 1m26s
Build docker images / docker (push) Successful in 6m48s
2025-11-04 14:09:30 -05:00
a013e6fe81 feat: Adds docker ci workflow. 2025-11-04 14:09:30 -05:00
9d8f3368ec feat: Adds todos, updates Dockerfile to use hpa as the entrypoint. 2025-11-04 14:09:30 -05:00
88a1f181cb feat: Updates Dockerfile, WIP 2025-11-04 14:09:30 -05:00
a6227a80db feat: Updates release notes. 2025-11-04 14:09:29 -05:00
d1a47e2ac6 feat: Updates to check if playbook is installed prior to running any of the commands, updates tests. 2025-11-04 14:09:29 -05:00
86dc084e7d feat: Documenting release workflow. 2025-11-04 14:09:29 -05:00
d3a9aa2f00 feat: More readme, also removes ci from dev branch and only runs for pulls and pushes to main. 2025-11-04 14:08:29 -05:00
1820988894 feat: Fix merge conflicts. 2025-11-04 14:06:33 -05:00
10 changed files with 179 additions and 17 deletions

View 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 }}

View File

@@ -21,7 +21,7 @@ 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.
### Ensuring dependencies are installed
This application requires some dependencies to be installed on your system, you can install the
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
> 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.
@@ -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
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
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
```
## Generating output files.
## 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.
@@ -122,7 +122,7 @@ Currently the supported output file types are:
1. LaTeX
1. HTML
## Build command.
## 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
@@ -147,7 +147,7 @@ like to generate output for.
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
want to pipe the output into other command line tools, so all options accept a `-q | --quiet` flag

24
Release.md Normal file
View 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`

View File

@@ -120,7 +120,11 @@ struct LiveFileClient: Sendable {
func isDirectory(_ url: URL) -> Bool {
var isDirectory: ObjCBool = false
#if os(Linux)
_ = manager.fileExists(atPath: url.cleanFilePath, isDirectory: &isDirectory)
#else
manager.fileExists(atPath: url.cleanFilePath, isDirectory: &isDirectory)
#endif
return isDirectory.boolValue
}

View File

@@ -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)

7
TODO.md Normal file
View 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`

View File

@@ -23,7 +23,11 @@ struct FileClientTests {
let fileClient = FileClient.liveValue
let vaultFilePath = url.appending(path: fileName)
#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)
#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)

View File

@@ -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

View File

@@ -2,23 +2,70 @@
# 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
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 \
libjemalloc2 \
libcurl4 \
tzdata \
&& rm -r /var/lib/apt/lists/*
COPY --from=build /build/.build/release/hpa /usr/local/bin
CMD ["/bin/bash", "-xc", "/usr/local/bin/hpa"]
COPY --from=build /staging/hpa /usr/local/bin
# Setup volumes
RUN mkdir /config && \
mkdir /consults && \
mkdir /playbook && \
mkdir /template && \
mkdir -p /root/.local/share/hpa && \
ln -sfv /config /root/.config && \
ln -sfv /playbook /root/.local/share/hpa/playbook
VOLUME /config /consults /playbook /template
ENTRYPOINT [ "/usr/local/bin/hpa" ]
CMD ["--help"]

View File

@@ -52,6 +52,8 @@ test-docker *ARGS: (build-docker-test)
{{docker_image_name}}:test \
swift test {{ARGS}}
alias td := test-docker
# Remove bottles
remove-bottles:
rm -rf *.gz