Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
66e286f267
|
|||
|
cb25dba7de
|
|||
|
9b99b35436
|
|||
|
0c6e9e1228
|
|||
|
faa28749bc
|
|||
|
fb246df01a
|
|||
|
0b153d7990
|
@@ -1,68 +0,0 @@
|
|||||||
#
|
|
||||||
name: Create and publish a Docker image
|
|
||||||
|
|
||||||
# Configures this workflow to run every time a change is pushed to the branch called `release`.
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: ['release']
|
|
||||||
tags:
|
|
||||||
- '*'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.
|
|
||||||
env:
|
|
||||||
REGISTRY: git.housh.dev
|
|
||||||
IMAGE_NAME: ${{ gitea.repository }}
|
|
||||||
|
|
||||||
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
|
|
||||||
jobs:
|
|
||||||
build-and-push-image:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
attestations: write
|
|
||||||
id-token: write
|
|
||||||
#
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ gitea.actor }}
|
|
||||||
password: ${{ secrets.CONTAINER_TOKEN }}
|
|
||||||
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
|
||||||
with:
|
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=sha
|
|
||||||
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
|
|
||||||
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
|
|
||||||
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
|
|
||||||
- name: Build and push Docker image
|
|
||||||
id: push
|
|
||||||
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: docker/Dockerfile
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
|
|
||||||
# This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see "[AUTOTITLE](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)."
|
|
||||||
# - name: Generate artifact attestation
|
|
||||||
# uses: actions/attest-build-provenance@v1
|
|
||||||
# with:
|
|
||||||
# subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
|
|
||||||
# subject-digest: ${{ steps.push.outputs.digest }}
|
|
||||||
# push-to-registry: true
|
|
||||||
# github-token: ${{ secrets.CONTAINER_TOKEN }}
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,3 +10,4 @@ DerivedData/
|
|||||||
.swiftpm/*
|
.swiftpm/*
|
||||||
./hpa.toml
|
./hpa.toml
|
||||||
./Version.*
|
./Version.*
|
||||||
|
/*.json
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ let package = Package(
|
|||||||
name: "ConfigurationClient",
|
name: "ConfigurationClient",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"CodersClient",
|
"CodersClient",
|
||||||
|
"CommandClient",
|
||||||
"FileClient",
|
"FileClient",
|
||||||
.product(name: "Dependencies", package: "swift-dependencies"),
|
.product(name: "Dependencies", package: "swift-dependencies"),
|
||||||
.product(name: "DependenciesMacros", package: "swift-dependencies"),
|
.product(name: "DependenciesMacros", package: "swift-dependencies"),
|
||||||
@@ -114,8 +115,8 @@ let package = Package(
|
|||||||
.target(
|
.target(
|
||||||
name: "PlaybookClient",
|
name: "PlaybookClient",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"CodersClient",
|
|
||||||
"CommandClient",
|
"CommandClient",
|
||||||
|
"CodersClient",
|
||||||
"ConfigurationClient",
|
"ConfigurationClient",
|
||||||
"FileClient",
|
"FileClient",
|
||||||
.product(name: "Dependencies", package: "swift-dependencies"),
|
.product(name: "Dependencies", package: "swift-dependencies"),
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import CodersClient
|
import CodersClient
|
||||||
|
import CommandClient
|
||||||
import Dependencies
|
import Dependencies
|
||||||
import DependenciesMacros
|
import DependenciesMacros
|
||||||
import FileClient
|
import FileClient
|
||||||
@@ -155,6 +156,7 @@ struct LiveConfigurationClient {
|
|||||||
private let environment: [String: String]
|
private let environment: [String: String]
|
||||||
|
|
||||||
@Dependency(\.coders) var coders
|
@Dependency(\.coders) var coders
|
||||||
|
@Dependency(\.commandClient) var commandClient
|
||||||
@Dependency(\.fileClient) var fileManager
|
@Dependency(\.fileClient) var fileManager
|
||||||
@Dependency(\.logger) var logger
|
@Dependency(\.logger) var logger
|
||||||
|
|
||||||
@@ -245,19 +247,13 @@ struct LiveConfigurationClient {
|
|||||||
try await fileManager.createDirectory(fileDirectory)
|
try await fileManager.createDirectory(fileDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: The hpa file needs to be copied somewhere on the system during install and
|
|
||||||
// not use bundle, as it only works if the tool was built on the users system.
|
|
||||||
if case .toml = file {
|
if case .toml = file {
|
||||||
// In the case of toml, we copy the internal resource that includes
|
// Copy the file using curl, because when installed as a pre-built binary we
|
||||||
// usage comments in the file.
|
// don't have access to bundled resources.
|
||||||
guard let resourceFile = Bundle.module.url(
|
try await commandClient.run(
|
||||||
forResource: HPAKey.resourceFileName,
|
quiet: true,
|
||||||
withExtension: HPAKey.resourceFileExtension
|
["curl", HPAKey.tomlConfigUrl, "--output", fileUrl.path]
|
||||||
) else {
|
)
|
||||||
throw ConfigurationError.resourceNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
try await fileManager.copy(resourceFile, fileUrl)
|
|
||||||
} else {
|
} else {
|
||||||
// Json does not allow comments, so we write the mock configuration
|
// Json does not allow comments, so we write the mock configuration
|
||||||
// to the file path.
|
// to the file path.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public enum HPAKey {
|
|||||||
public static let resourceFileExtension = "toml"
|
public static let resourceFileExtension = "toml"
|
||||||
public static let defaultFileName = "config.toml"
|
public static let defaultFileName = "config.toml"
|
||||||
public static let defaultFileNameWithoutExtension = "config"
|
public static let defaultFileNameWithoutExtension = "config"
|
||||||
|
public static let tomlConfigUrl = "https://git.housh.dev/michael/swift-hpa/raw/branch/main/Sources/ConfigurationClient/Resources/hpa.toml"
|
||||||
}
|
}
|
||||||
|
|
||||||
extension [String: String] {
|
extension [String: String] {
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
// Do not set this variable, it is set during the build process.
|
// Do not set this variable, it is set during the build process.
|
||||||
let VERSION: String? = nil
|
let VERSION: String? = "0.1.1"
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ struct ConfigurationClientTests: TestCase {
|
|||||||
@Test(arguments: ["config.toml", "config.json"])
|
@Test(arguments: ["config.toml", "config.json"])
|
||||||
func generateConfigFile(fileName: String) async throws {
|
func generateConfigFile(fileName: String) async throws {
|
||||||
try await withTestLogger(key: "generateConfigFile") {
|
try await withTestLogger(key: "generateConfigFile") {
|
||||||
|
$0.asyncShellClient = .liveValue
|
||||||
|
$0.commandClient = .liveValue
|
||||||
$0.coders = .liveValue
|
$0.coders = .liveValue
|
||||||
$0.fileClient = .liveValue
|
$0.fileClient = .liveValue
|
||||||
} operation: {
|
} operation: {
|
||||||
@@ -35,18 +37,6 @@ struct ConfigurationClientTests: TestCase {
|
|||||||
#expect(FileManager.default.fileExists(atPath: tempFile.cleanFilePath))
|
#expect(FileManager.default.fileExists(atPath: tempFile.cleanFilePath))
|
||||||
#expect(fileClient.fileExists(tempFile))
|
#expect(fileClient.fileExists(tempFile))
|
||||||
#expect(output == tempFile.cleanFilePath)
|
#expect(output == tempFile.cleanFilePath)
|
||||||
|
|
||||||
// Ensure that we do not overwrite files if they exist.
|
|
||||||
do {
|
|
||||||
_ = try await configuration.generate(.init(
|
|
||||||
force: false,
|
|
||||||
json: fileName.hasSuffix("json"),
|
|
||||||
path: .file(File(tempFile)!)
|
|
||||||
))
|
|
||||||
#expect(Bool(false))
|
|
||||||
} catch {
|
|
||||||
#expect(Bool(true))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,6 +44,8 @@ struct ConfigurationClientTests: TestCase {
|
|||||||
@Test(arguments: ["config.toml", "config.json", nil])
|
@Test(arguments: ["config.toml", "config.json", nil])
|
||||||
func loadConfigFile(fileName: String?) async throws {
|
func loadConfigFile(fileName: String?) async throws {
|
||||||
try await withTestLogger(key: "generateConfigFile") {
|
try await withTestLogger(key: "generateConfigFile") {
|
||||||
|
$0.asyncShellClient = .liveValue
|
||||||
|
$0.commandClient = .liveValue
|
||||||
$0.coders = .liveValue
|
$0.coders = .liveValue
|
||||||
$0.fileClient = .liveValue
|
$0.fileClient = .liveValue
|
||||||
} operation: {
|
} operation: {
|
||||||
@@ -77,6 +69,8 @@ struct ConfigurationClientTests: TestCase {
|
|||||||
@Test(arguments: ["config.toml", "config.json", ".hparc.json", ".hparc.toml"])
|
@Test(arguments: ["config.toml", "config.json", ".hparc.json", ".hparc.toml"])
|
||||||
func findConfiguration(fileName: String) async throws {
|
func findConfiguration(fileName: String) async throws {
|
||||||
try await withTestLogger(key: "findConfiguration") {
|
try await withTestLogger(key: "findConfiguration") {
|
||||||
|
$0.asyncShellClient = .liveValue
|
||||||
|
$0.commandClient = .liveValue
|
||||||
$0.fileClient = .liveValue
|
$0.fileClient = .liveValue
|
||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.logger) var logger
|
@Dependency(\.logger) var logger
|
||||||
@@ -106,6 +100,8 @@ struct ConfigurationClientTests: TestCase {
|
|||||||
@Test(arguments: ["config.toml", "config.json", ".hparc.json", ".hparc.toml"])
|
@Test(arguments: ["config.toml", "config.json", ".hparc.json", ".hparc.toml"])
|
||||||
func findXdgConfiguration(fileName: String) async throws {
|
func findXdgConfiguration(fileName: String) async throws {
|
||||||
try await withTestLogger(key: "findXdgConfiguration") {
|
try await withTestLogger(key: "findXdgConfiguration") {
|
||||||
|
$0.asyncShellClient = .liveValue
|
||||||
|
$0.commandClient = .liveValue
|
||||||
$0.fileClient = .liveValue
|
$0.fileClient = .liveValue
|
||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.logger) var logger
|
@Dependency(\.logger) var logger
|
||||||
@@ -145,6 +141,8 @@ struct ConfigurationClientTests: TestCase {
|
|||||||
@Test
|
@Test
|
||||||
func writeCreatesBackupFile() async throws {
|
func writeCreatesBackupFile() async throws {
|
||||||
try await withDependencies {
|
try await withDependencies {
|
||||||
|
$0.asyncShellClient = .liveValue
|
||||||
|
$0.commandClient = .liveValue
|
||||||
$0.fileClient = .liveValue
|
$0.fileClient = .liveValue
|
||||||
} operation: {
|
} operation: {
|
||||||
let client = ConfigurationClient.liveValue
|
let client = ConfigurationClient.liveValue
|
||||||
@@ -152,7 +150,7 @@ struct ConfigurationClientTests: TestCase {
|
|||||||
try await withGeneratedConfigFile(named: "config.toml", client: client) { configFile in
|
try await withGeneratedConfigFile(named: "config.toml", client: client) { configFile in
|
||||||
@Dependency(\.fileClient) var fileClient
|
@Dependency(\.fileClient) var fileClient
|
||||||
|
|
||||||
let backupUrl = configFile.url.appendingPathExtension(".back")
|
let backupUrl = configFile.url.appendingPathExtension("back")
|
||||||
#expect(fileClient.fileExists(backupUrl) == false)
|
#expect(fileClient.fileExists(backupUrl) == false)
|
||||||
|
|
||||||
let config = Configuration()
|
let config = Configuration()
|
||||||
|
|||||||
26
justfile
26
justfile
@@ -2,11 +2,25 @@ docker_image_name := "swift-hpa"
|
|||||||
install_path := "~/.local/share/bin/hpa"
|
install_path := "~/.local/share/bin/hpa"
|
||||||
completion_path := "~/.local/share/zsh/completions/_hpa"
|
completion_path := "~/.local/share/zsh/completions/_hpa"
|
||||||
|
|
||||||
|
tap_url := "https://git.housh.dev/michael/homebrew-formula"
|
||||||
|
tap := "michael/formula"
|
||||||
|
formula := "hpa"
|
||||||
|
|
||||||
|
# Build and bottle homebrew formula.
|
||||||
|
bottle:
|
||||||
|
@brew uninstall {{formula}} || true
|
||||||
|
@brew tap {{tap}} {{tap_url}}
|
||||||
|
@brew install --build-bottle {{formula}}
|
||||||
|
@brew bottle {{formula}} --json
|
||||||
|
bottle="$(ls *.gz)" && mv "${bottle}" "${bottle/--/-}"
|
||||||
|
|
||||||
|
# Build the command-line tool.
|
||||||
build mode="debug":
|
build mode="debug":
|
||||||
swift build -c {{mode}}
|
swift build -c {{mode}}
|
||||||
|
|
||||||
alias b := build
|
alias b := build
|
||||||
|
|
||||||
|
# Build the docker image.
|
||||||
build-docker file="Dockerfile" tag="latest":
|
build-docker file="Dockerfile" tag="latest":
|
||||||
@docker build \
|
@docker build \
|
||||||
--file docker/{{file}} \
|
--file docker/{{file}} \
|
||||||
@@ -14,32 +28,36 @@ build-docker file="Dockerfile" tag="latest":
|
|||||||
|
|
||||||
build-docker-test: (build-docker "Dockerfile.test" "test")
|
build-docker-test: (build-docker "Dockerfile.test" "test")
|
||||||
|
|
||||||
|
# Build the docker test image used for testing.
|
||||||
|
build-docker-test: (build-docker "Dockerfile.test" "test")
|
||||||
|
|
||||||
|
# Run tests.
|
||||||
test *ARGS:
|
test *ARGS:
|
||||||
swift test {{ARGS}}
|
swift test {{ARGS}}
|
||||||
|
|
||||||
alias t := test
|
alias t := test
|
||||||
|
|
||||||
|
# Run tests in docker container.
|
||||||
test-docker *ARGS: (build-docker-test)
|
test-docker *ARGS: (build-docker-test)
|
||||||
@docker run --rm \
|
@docker run --rm \
|
||||||
--network host \
|
--network host \
|
||||||
{{docker_image_name}}:test \
|
{{docker_image_name}}:test \
|
||||||
swift test {{ARGS}}
|
swift test {{ARGS}}
|
||||||
|
|
||||||
|
# Run the application.
|
||||||
run *ARGS:
|
run *ARGS:
|
||||||
swift run hpa {{ARGS}}
|
swift run hpa {{ARGS}}
|
||||||
|
|
||||||
alias r := run
|
alias r := run
|
||||||
|
|
||||||
|
# Clean the build folder.
|
||||||
clean:
|
clean:
|
||||||
rm -rf .build
|
rm -rf .build
|
||||||
|
|
||||||
|
# Bump the version based on the git tag.
|
||||||
update-version:
|
update-version:
|
||||||
@swift package \
|
@swift package \
|
||||||
--disable-sandbox \
|
--disable-sandbox \
|
||||||
--allow-writing-to-package-directory \
|
--allow-writing-to-package-directory \
|
||||||
update-version \
|
update-version \
|
||||||
hpa
|
hpa
|
||||||
|
|
||||||
install: (build "release")
|
|
||||||
@cp .build/release/hpa {{install_path}}
|
|
||||||
@{{install_path}} --generate-completion-script zsh > {{completion_path}}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user