| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- import Foundation
- import Danger
- fileprivate extension Danger.File {
- var isInSources: Bool { hasPrefix("Sources/") }
- var isInTests: Bool { hasPrefix("Tests/") }
- var isInDemos: Bool { hasPrefix("Demos/") }
- var isInBenchmarking: Bool { hasPrefix("Benchmarking/") }
- var isInVendor: Bool { contains("/Vendor/") }
- var isInFMDB: Bool { contains("/FMDB/") }
- var isSourceFile: Bool {
- fileType == .swift || fileType == .m || fileType == .mm || fileType == .h
- }
- private static let spmOnlyTargetNames: Set<String> = [
- "CocoaLumberjackSwiftLogBackend",
- ]
- var isSPMOnlySourceFile: Bool {
- guard isSourceFile else { return false }
- if isInSources {
- return Self.spmOnlyTargetNames.contains(where: { contains("/\($0)/") })
- } else if isInTests {
- return Self.spmOnlyTargetNames.contains(where: { contains("/\($0)Tests/") })
- }
- return false
- }
- var isSwiftPackageDefintion: Bool {
- hasPrefix("Package") && fileType == .swift
- }
- var isDangerfile: Bool {
- self == "Dangerfile.swift"
- }
- }
- let danger = Danger()
- let git = danger.git
- // Sometimes it's a README fix, or something like that
- let isDeclaredTrivial = danger.github?.pullRequest.title.contains("#trivial") ?? false
- let hasSourceChanges = (git.modifiedFiles + git.createdFiles).contains(where: \.isInSources)
- // Make it more obvious that a PR is a work in progress and shouldn't be merged yet
- if danger.github?.pullRequest.title.contains("WIP") == true && danger.github?.pullRequest.draft != true {
- warn("PR is marked as Work in Progress. Please consider marking the PR as Draft in GitHub to prevent merges.")
- }
- // Warn when there is a big PR
- if let additions = danger.github?.pullRequest.additions,
- let deletions = danger.github?.pullRequest.deletions,
- case let sum = additions + deletions, sum > 1000 {
- warn("Pull request is relatively big (\(sum) lines changed). If this PR contains multiple changes, consider splitting it into separate PRs for easier reviews.")
- }
- // Warn when library files has been updated but not tests.
- if hasSourceChanges && !git.modifiedFiles.contains(where: \.isInTests) {
- warn("The library files were changed, but the tests remained unmodified. Consider updating or adding to the tests to match the library changes.")
- }
- // Run SwiftLint
- SwiftLint.lint(.modifiedAndCreatedFiles(directory: "Sources"), inline: true)
- // Added (or removed) library files need to be added (or removed) from the
- // Carthage Xcode project to avoid breaking things for our Carthage users.
- let xcodeProjectFile: Danger.File = "Lumberjack.xcodeproj/project.pbxproj"
- let xcodeProjectWasModified = git.modifiedFiles.contains(xcodeProjectFile)
- if (git.createdFiles + git.deletedFiles).contains(where: { $0.isInSources && $0.isSourceFile && !$0.isSPMOnlySourceFile })
- && !xcodeProjectWasModified {
- fail("Added or removed library files require the Carthage Xcode project to be updated.")
- }
- // Check xcodeproj settings are not changed
- // Check to see if any of our project files contains a line with "SOURCE_ROOT" which indicates that the file isn't in sync with Finder.
- if xcodeProjectWasModified {
- let acceptedSettings: Set<String> = [
- "APPLICATION_EXTENSION_API_ONLY",
- "ASSETCATALOG_COMPILER_APPICON_NAME",
- "ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME",
- "ATTRIBUTES",
- "CODE_SIGN_IDENTITY",
- "COMBINE_HIDPI_IMAGES",
- "FRAMEWORK_VERSION",
- "GCC_PRECOMPILE_PREFIX_HEADER",
- "GCC_PREFIX_HEADER",
- "IBSC_MODULE",
- "INFOPLIST_FILE",
- "MODULEMAP_FILE",
- "PRIVATE_HEADERS_FOLDER_PATH",
- "PRODUCT_BUNDLE_IDENTIFIER",
- "PRODUCT_NAME",
- "PUBLIC_HEADERS_FOLDER_PATH",
- "SDKROOT",
- "SUPPORTED_PLATFORMS",
- "TARGETED_DEVICE_FAMILY",
- "WRAPPER_EXTENSION",
- ]
- [xcodeProjectFile]
- .lazy
- .filter { FileManager.default.fileExists(atPath: $0) }
- .forEach { projectFile in
- danger.utils.readFile(projectFile).split(separator: "\n").enumerated().forEach { (offset, line) in
- if line.contains("sourceTree = SOURCE_ROOT;") &&
- line.contains("PBXFileReference") &&
- !line.contains("path = Sources/CocoaLumberjackSwiftSupport/include/") {
- warn(message: "Files should be in sync with project structure", file: projectFile, line: offset + 1)
- }
- if let range = line.range(of: "[A-Z_]+ = .*;", options: .regularExpression) {
- let setting = String(line[range].prefix(while: { $0 != " " }))
- if !acceptedSettings.contains(setting) {
- warn(message: "Xcode settings need to remain in Configs/*.xcconfig. Please move " + setting + " to the xcconfig file", file: projectFile, line: offset + 1)
- }
- }
- }
- }
- }
- // Check Copyright
- let copyrightLines = (
- source: [
- "// Software License Agreement (BSD License)",
- "//",
- "// Copyright (c) 2010-2025, Deusty, LLC",
- "// All rights reserved.",
- "//",
- "// Redistribution and use of this software in source and binary forms,",
- "// with or without modification, are permitted provided that the following conditions are met:",
- "//",
- "// * Redistributions of source code must retain the above copyright notice,",
- "// this list of conditions and the following disclaimer.",
- "//",
- "// * Neither the name of Deusty nor the names of its contributors may be used",
- "// to endorse or promote products derived from this software without specific",
- "// prior written permission of Deusty, LLC.",
- ],
- demos: [
- "//",
- "// ",
- "// ",
- "//",
- "// CocoaLumberjack Demos",
- "//",
- ],
- benchmarking: [
- "//",
- "// ",
- "// ",
- "//",
- "// CocoaLumberjack Benchmarking",
- "//",
- ]
- )
- // let sourcefilesToCheck = Dir.glob("*/*/*") // uncomment when we want to test all the files (locally)
- let sourcefilesToCheck = Set(git.modifiedFiles + git.createdFiles)
- let filesWithInvalidCopyright = sourcefilesToCheck.lazy
- .filter { $0.isSourceFile }
- .filter { !$0.isSwiftPackageDefintion }
- .filter { !$0.isDangerfile }
- .filter { !$0.isInVendor && !$0.isInFMDB }
- .filter { FileManager.default.fileExists(atPath: $0) }
- .filter {
- // Use correct copyright lines depending on source file location
- let (expectedLines, shouldMatchExactly): (Array<String>, Bool)
- if $0.isInDemos {
- expectedLines = copyrightLines.demos
- shouldMatchExactly = false
- } else if $0.isInBenchmarking {
- expectedLines = copyrightLines.benchmarking
- shouldMatchExactly = false
- } else {
- expectedLines = copyrightLines.source
- shouldMatchExactly = true
- }
- let actualLines = danger.utils.readFile($0).split(separator: "\n").lazy.map(String.init)
- if shouldMatchExactly {
- return !actualLines.starts(with: expectedLines)
- } else {
- return !zip(actualLines, expectedLines).allSatisfy { $0.starts(with: $1) }
- }
- }
- if !filesWithInvalidCopyright.isEmpty {
- filesWithInvalidCopyright.sorted().forEach {
- warn(message: "Invalid copyright!", file: $0, line: 1)
- }
- warn("""
- Copyright is not valid. See our default copyright in all of our files (Sources, Demos and Benchmarking use different formats).<br/>
- Invalid files:<br/>
- \(filesWithInvalidCopyright.map { "- \($0)" }.joined(separator: "<br/>\n"))
- """)
- }
|