Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Archive a macOS release binary and include in release #3

Merged
merged 7 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/archive.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
on:
release:
types: [published]
permissions:
contents: write
name: Build and upload xcstringslint release binary
jobs:
build-macos:
name: Build macOS Executable
runs-on: macos-latest
steps:

- name: Checkout
uses: actions/checkout@v3

- name: Build macOS binary
id: build
run: |
swift build -c release --arch x86_64 --arch arm64
mv $(swift build -c release --arch x86_64 --arch arm64 --show-bin-path) artifact
echo "path=artifact/xcstringslint" >> "$GITHUB_OUTPUT"

- name: Upload binary to release
uses: softprops/action-gh-release@v2
with:
files: ${{ steps.build.outputs.path }}
11 changes: 6 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ let package = Package(
targets: ["XCStringsLint"]
),
.plugin(
name: "StringCatalogLinterPlugin",
targets: ["StringCatalogLinterPlugin"]
name: "XCStringsLintBuildToolPlugin",
targets: ["XCStringsLintBuildToolPlugin"]
)
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"),
.package(url: "https://github.com/jpsim/Yams", from: "5.1.2")
.package(url: "https://github.com/jpsim/Yams", from: "5.1.0")
],
targets: [
.target(
Expand All @@ -31,7 +31,8 @@ let package = Package(
.process("Resources")
],
plugins: [
// TODO: Unfortunately we cannot use this library to meta-lint this library directly. However, once we compile the library to an executable we might be able to
// !!!: We cannot meta-plugin from the same package.
// If we wanted to do this we could first compile the plugin to a binary.
//.plugin(name: "StringCatalogLinterPlugin")
]
),
Expand All @@ -51,7 +52,7 @@ let package = Package(
]
),
.plugin(
name: "StringCatalogLinterPlugin",
name: "XCStringsLintBuildToolPlugin",
capability: .buildTool(),
dependencies: ["XCStringsLint"]
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import Foundation
import PackagePlugin

let toolName = "XCStringsLint"
let configRegex = try! Regex("\\.?xcstringslint\\.ya?ml")

@main
struct StringCatalogLinterPlugin: BuildToolPlugin {

let fileManager = FileManager()
struct XCStringsLintBuildToolPlugin {
private let fileManager = FileManager()
let toolName = "XCStringsLint"

enum Error: Swift.Error, CustomStringConvertible {
case incorrectTargetType
Expand All @@ -26,15 +23,8 @@ struct StringCatalogLinterPlugin: BuildToolPlugin {
}
}

func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
guard target is SourceModuleTarget else {
throw Error.incorrectTargetType
}

return try commandsForTarget(context: context, target: target)
}

private func configPath(rootPath: Path, targetFiles: FileList?) throws -> String {
private let configRegex = try! Regex("\\.?xcstringslint\\.ya?ml")
func configPath(rootPath: Path, targetFiles: FileList?) throws -> String {
let rootConfigs = try fileManager
.contentsOfDirectory(atPath: rootPath.string)
.filter({ $0.contains(configRegex) })
Expand All @@ -59,18 +49,20 @@ struct StringCatalogLinterPlugin: BuildToolPlugin {
return config!
}

private func commandsForTarget(context: PluginContext, target: Target) throws -> [Command] {
let toolPath = try context.tool(named: toolName).path
let displayName = "Running String Catalog linter for \(target.name)"

let config = try configPath(rootPath: context.package.directory, targetFiles: target.sourceModule?.sourceFiles)
func commandsForTarget(
named targetName: String,
files: FileList?,
config: String,
toolPath: Path
) throws -> [Command] {
let displayName = "Running String Catalog linter for \(targetName)"

let catalogs = target.sourceModule?.sourceFiles.filter {
let catalogs = files?.filter {
$0.path.lastComponent.hasSuffix(".xcstrings")
} ?? []

if catalogs.isEmpty {
Diagnostics.warning("No xcstrings files found in \(target.name)")
Diagnostics.warning("No xcstrings files found in \(targetName)")
return [] // no-op
}

Expand All @@ -89,43 +81,49 @@ struct StringCatalogLinterPlugin: BuildToolPlugin {
}
}

// MARK: - BuildToolPlugin

extension XCStringsLintBuildToolPlugin: BuildToolPlugin {
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
guard target is SourceModuleTarget else {
throw Error.incorrectTargetType
}

let toolPath = try context.tool(named: toolName).path
let config = try configPath(
rootPath: context.package.directory,
targetFiles: target.sourceModule?.sourceFiles
)

return try commandsForTarget(
named: target.name,
files: target.sourceModule?.sourceFiles,
config: config,
toolPath: toolPath
)
}
}

// MARK: - XcodeBuildToolPlugin

#if canImport(XcodeProjectPlugin)

import XcodeProjectPlugin

extension StringCatalogLinterPlugin: XcodeBuildToolPlugin {

extension XCStringsLintBuildToolPlugin: XcodeBuildToolPlugin {
func createBuildCommands(context: XcodePluginContext, target: XcodeTarget) throws -> [Command] {
let toolPath = try context.tool(named: toolName).path

let displayName = "Running String Catalog linter for \(target.displayName)"

let config = try configPath(
rootPath: context.xcodeProject.directory,
targetFiles: target.inputFiles
)

let catalogs = target.inputFiles.filter {
$0.path.lastComponent.hasSuffix(".xcstrings")
}

if catalogs.isEmpty {
Diagnostics.warning("No xcstrings files found in \(target.displayName)")
return [] // no-op
}

let arguments: [CustomStringConvertible] = [
"--config", config,
"--reporter", "xcode",
] + catalogs.map(\.path)

return [
.buildCommand(
displayName: displayName,
executable: toolPath,
arguments: arguments
)
]
return try commandsForTarget(
named: target.displayName,
files: target.inputFiles,
config: config,
toolPath: toolPath
)
}
}

Expand Down
33 changes: 17 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,21 @@ To ignore a specific rule, include `[no-lint:rule-name]` in the key's comment, e
## Example Output

```
$ swift run xcstringslint Sources/StringCatalogValidator/Resources/Localizable.xcstrings --require-locale en fr

Validation failed for key: `found state `%@`, expected %@`
- missing translation for 1 locale: fr
Validation failed for key: `found state `%@`, expected one of %@`
- missing translation for 1 locale: fr
Validation failed for key: `missing translation for %lld locale: %@`
- missing translation for 1 locale: fr
Validation failed for key: `should not have extraction state `%@``
- missing translation for 1 locale: fr
Validation failed for key: `should not have state %@`
- missing translation for 1 locale: fr
Validation failed for key: `should not have state `%@``
- missing translation for 1 locale: fr

[Error]: Found 6 validation issues in catalog: Sources/StringCatalogValidator/Resources/Localizable.xcstrings
$ swift run xcstringslint Sources/StringCatalogValidator/Resources/Localizable.xcstrings

`Rejects an entry if its extraction state matches any of the provided values. Known extractions states: %@`:
⚠️ require-localization-state: no translation state found
⚠️ require-locale: missing translation for 1 locale: en
`Rejects an entry if its extraction state matchs any of the provided values. Known extractions states: %@`:
⚠️ require-extraction-state: should not have extraction state `stale`
`found state `%@`, expected %@`:
⚠️ require-extraction-state: should not have extraction state `stale`
`found state `%@`, expected `%@``:
⚠️ require-localization-state: found state `new`, expected `translated`
`found state `%@`, expected one of %@`:
⚠️ require-extraction-state: should not have extraction state `stale`
`found state `%@`, expected one of: %@`:
⚠️ require-localization-state: found state `new`, expected `translated`

[Warning]: Found 7 total issues in 6 keys
```
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ struct CommandLineReporter: Reporter {
)

if errorCount > 0 {
print(message + ", \(errorCount) serious")
print("\n[Error]: " + message + ", \(errorCount) serious")
throw ExitCode.failure
} else if !results.isEmpty {
print(message)
print("\n[Warning]: " + message)
}
}
}
Expand Down
Loading