diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 568990c..9a2e326 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,35 +1,37 @@ version: 2 updates: - - package-ecosystem: "github-actions" - directory: "/" + - package-ecosystem: github-actions + directory: / open-pull-requests-limit: 10 schedule: - interval: "daily" - time: "07:00" - timezone: "Europe/Berlin" + interval: daily + time: '07:00' + timezone: Europe/Berlin assignees: - ffried reviewers: - ffried - - package-ecosystem: "github-actions" - directory: ".github/actions/generate-action-code" + + - package-ecosystem: github-actions + directory: .github/actions/generate-action-code open-pull-requests-limit: 10 schedule: - interval: "daily" - time: "07:00" - timezone: "Europe/Berlin" + interval: daily + time: '07:00' + timezone: Europe/Berlin assignees: - ffried reviewers: - ffried - - package-ecosystem: "npm" - directory: "/" + + - package-ecosystem: npm + directory: / open-pull-requests-limit: 10 schedule: - interval: "daily" - time: "07:00" - timezone: "Europe/Berlin" + interval: daily + time: '07:00' + timezone: Europe/Berlin assignees: - ffried reviewers: diff --git a/README.md b/README.md index b38de59..d1ae129 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,10 @@ Note that this action needs to run on macOS. All other platforms will fail! See [action.yml](action.yml) for an overview of all inputs.
For more information about the various inputs, also see `man xcodebuild`. +**Notes:** +- If you are missing an input, you can pass them in the `build-settings` input. These will be passed along to `xcodebuild` as is. +- If an enum input validation fails because you use a value that isn't yet known to this action, set `disable-enum-validation` to `true`. + ## Outputs ### `unprocessed-command` diff --git a/action.yml b/action.yml index d2fd690..e1b3036 100644 --- a/action.yml +++ b/action.yml @@ -17,6 +17,9 @@ inputs: target: description: The target to build. See also `xcodebuild`'s `-target`. required: false + all-targets: + description: If `true`, all targets will be built. See also `xcodebuild`'s `-allTargets`. + required: false destination: description: The destination specifier to build. See also `xcodebuild`'s `-destination`. required: false @@ -32,6 +35,9 @@ inputs: xcconfig: description: The path to an xcconfig file with build settings overrides. See also `xcodebuild`'s `-xcconfig`. required: false + toolchain: + description: The toolchain identifier or name to use for building. See also `xcodebuild`'s `-toolchain`. + required: false jobs: description: The number of jobs to use for building. See also `xcodebuild`'s `-jobs`. required: false @@ -80,6 +86,12 @@ inputs: derived-data-path: description: The path that should be used for derived data. See also `xcodebuild`'s `-derivedDataPath`. required: false + default-package-registry-url: + description: The default package registry URL. See also `xcodebuild`'s `-defaultPackageRegistryURL`. + required: false + package-dependency-scm-to-registry-transformation: + description: The package dependency SCM to registry transformation. See also `xcodebuild`'s `-packageDependencySCMToRegistryTransformation`. + required: false disable-package-repository-cache: description: Whether the package repository cache should be disabled. See also `xcodebuild`'s `-disablePackageRepositoryCache`. required: false @@ -95,12 +107,24 @@ inputs: skip-macro-validation: description: Whether macro validation should be skipped. See also `xcodebuild`'s `-skipMacroValidation`. required: false + package-fingerprint-policy: + description: The package fingerprint checking policy. See also `xcodebuild`'s `-packageFingerprintPolicy`. + required: false + package-signing-entity-policy: + description: The package signing entity policy. See also `xcodebuild`'s `-packageSigningEntityPolicy`. + required: false xcroot: description: The path to a .xcroot to use for building and/or testing. See also `xcodebuild`'s `-xcroot`. required: false xctestrun: description: The path to a test run specification. See also `xcodebuild`'s `-xctestrun`. required: false + test-language: + description: The language to use for testing. See also `xcodebuild`'s `-testLanguage`. + required: false + test-region: + description: The region to use for testing. See also `xcodebuild`'s `-testRegion`. + required: false test-plan: description: The name of the test plan associated with the scheme to use for testing. See also `xcodebuild`'s `-testPlan`. required: false @@ -110,6 +134,21 @@ inputs: skip-testing: description: A (line-separated) list of tests to skip. See also `xcodebuild`'s `-skip-testing`. required: false + only-test-configuration: + description: A (line-separated) list of test configurations to run. See also `xcodebuild`'s `-only-test-configuration`. + required: false + skip-test-configuration: + description: A (line-separated) list of test configurations to skip. See also `xcodebuild`'s `-skip-test-configuration`. + required: false + test-iterations: + description: The number of iterations to run the tests. See also `xcodebuild`'s `-test-iterations`. + required: false + retry-tests-on-failure: + description: Whether tests should be retried on failure. See also `xcodebuild`'s `-retry-tests-on-failure`. + required: false + run-tests-until-failure: + description: Whether tests should be run until failure. See also `xcodebuild`'s `-run-tests-until-failure`. + required: false skip-unavailable-actions: description: Whether unavailable actions should be skipped instead of failing the execution. See also `xcodebuild`'s `-skipUnavailableActions`. required: false @@ -119,6 +158,21 @@ inputs: allow-provisioning-device-registration: description: Whether provisioning device registrations are allowed. See also `xcodebuild`'s `-allowProvisioningDeviceRegistration`. required: false + export-notarized-app: + description: Whether the app should be exported notarized. See also `xcodebuild`'s `-exportNotarizedApp`. + required: false + export-options-plist: + description: The path to an export options plist. See also `xcodebuild`'s `-exportOptionsPlist`. + required: false + export-archive: + description: Whether an archive should be exported. See also `xcodebuild`'s `-exportArchive`. + required: false + archive-path: + description: The path to where archives are created. See also `xcodebuild`'s `-archivePath`. + required: false + create-xcframework: + description: Whether an xcframework should be created. See also `xcodebuild`'s `-create-xcframework`. + required: false build-settings: description: Arbitrary, space separated build settings (e.g. PLATFORM_NAME=iphonesimulator). required: false @@ -130,6 +184,10 @@ inputs: description: The output formatter to use (e.g. xcpretty, xcbeautify, ...). The xcodebuild output will be piped into this formatter. required: false default: 'xcpretty --color' + disable-enum-input-validation: + description: Whether the input validation for enums should be disabled. Usually only needed if new values are added to the enums in the future. + required: false + default: 'false' dry-run: description: ' Whether the commands should only be composed and not actually run. Only used in test.' required: false diff --git a/package-lock.json b/package-lock.json index bede6c0..400806e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@tsconfig/node20": "^20.1.4", - "@types/node": "^20.6.2", + "@types/node": "^20.11.30", "@vercel/ncc": "^0.38.1", "typescript": "^5.4.3" }, @@ -45,10 +45,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz", - "integrity": "sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==", - "dev": true + "version": "20.11.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", + "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@vercel/ncc": { "version": "0.38.1", @@ -80,6 +83,12 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", diff --git a/package.json b/package.json index b306e10..c1e89a4 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ }, "devDependencies": { "@tsconfig/node20": "^20.1.4", - "@types/node": "^20.6.2", + "@types/node": "^20.11.30", "@vercel/ncc": "^0.38.1", "typescript": "^5.4.3" } diff --git a/src/main.ts b/src/main.ts index bd80c31..27479c9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -60,7 +60,7 @@ function argumentValueString(value: ICommandArgumentValue, function _cmdEscape(str: string): string { return str.replace(/((?!\\).|^)( )/g, `$1\\ `); } - let strValue = useResolvedValue ? value.resolvedValue : value.originalValue + let strValue = useResolvedValue ? value.resolvedValue : value.originalValue; return escapeValue ? _cmdEscape(strValue) : strValue; } @@ -140,6 +140,8 @@ async function main() { } const scheme = core.getInput('scheme', { required: !!workspace || !!spmPackage }); + const disableEnumInputValidation = core.getBooleanInput('disable-enum-input-validation'); + function _pushArg(name: string, value?: ICommandArgumentValue) { xcodebuildArgs.push({ name: `-${name}`, value: value }); } @@ -156,45 +158,64 @@ async function main() { _pushArg(name, { originalValue: value, resolvedValue: processedValue }); } - function _addInputArg(inputName: string, argName?: string, opts?: { isPath?: boolean, isList?: boolean }) { - if (opts?.isList) { + function _addInputArg(inputName: string, + argName?: string, + opts?: { isPath?: boolean, isList?: boolean, validValues?: string[] }): boolean { + if (opts?.isList) { // opts is guaranteed to be set in this branch. let values = core.getMultilineInput(inputName); - if (values) - values.forEach(value => _pushArgWithValue(argName ?? inputName, value, { - isPath: opts?.isPath, + if (!values) return false; + for (const value of values) { + if (!disableEnumInputValidation && opts.validValues && !opts.validValues.includes(value)) + throw new Error(`Invalid value for ${inputName}: ${value}! Valid values: ${opts.validValues.join(', ')}`); + _pushArgWithValue(argName ?? inputName, value, { + isPath: opts.isPath, skipEmptyValues: true, - })); + }); + } + return values.length > 0; } else { let value = core.getInput(inputName); - if (value) - _pushArgWithValue(argName ?? inputName, value, { - isPath: opts?.isPath, - skipEmptyValues: false, - }) + if (!value) return false; + if (!disableEnumInputValidation && opts?.validValues && !opts.validValues.includes(value)) + throw new Error(`Invalid value for ${inputName}: ${value}! Valid values: ${opts.validValues.join(', ')}`); + _pushArgWithValue(argName ?? inputName, value, { + isPath: opts?.isPath, + skipEmptyValues: false, + }); + return true; } } - function addInputArg(inputName: string, argName?: string) { - _addInputArg(inputName, argName); + function addInputArg(inputName: string, argName?: string): boolean { + return _addInputArg(inputName, argName); } - function addPathArg(inputName: string, argName?: string) { - _addInputArg(inputName, argName, { isPath: true }); + function addPathArg(inputName: string, argName?: string): boolean { + return _addInputArg(inputName, argName, { isPath: true }); } - function addListArg(inputName: string, argName?: string) { - _addInputArg(inputName, argName, { isList: true }); + function addListArg(inputName: string, argName?: string): boolean { + return _addInputArg(inputName, argName, { isList: true }); } - function addBoolArg(inputName: string, argName?: string) { + function addEnumArg(inputName: string, validValues: string[], argName?: string): boolean { + return _addInputArg(inputName, argName, { validValues }); + } + + function addBoolArg(inputName: string, argName?: string): boolean { const value = core.getInput(inputName); - if (value?.length) - _pushArgWithValue(argName ?? inputName, core.getBooleanInput(inputName) ? 'YES' : 'NO') + const hasValue = !!value && value.length > 0; + if (hasValue) + _pushArgWithValue(argName ?? inputName, core.getBooleanInput(inputName) ? 'YES' : 'NO'); + return hasValue; } - function addFlagArg(inputName: string, argName?: string) { - if (core.getInput(inputName).length && core.getBooleanInput(inputName)) + function addFlagArg(inputName: string, argName?: string): boolean { + const value = core.getInput(inputName); + const hasValue = !!value && value.length > 0; + if (hasValue && core.getBooleanInput(inputName)) _pushArg(argName ?? inputName); + return hasValue; } if (workspace) { @@ -204,12 +225,14 @@ async function main() { } if (scheme) _pushArgWithValue('scheme', scheme); - addInputArg('target'); + if (addInputArg('target') && addFlagArg('all-targets', 'alltargets')) + throw new Error('`target` and `all-targets` are mutually exclusive!'); addInputArg('destination'); addInputArg('configuration'); addInputArg('sdk'); addInputArg('arch'); addPathArg('xcconfig'); + addInputArg('toolchain'); addInputArg('jobs'); addFlagArg('parallelize-targets', 'parallelizeTargets'); addBoolArg('enable-code-coverage', 'enableCodeCoverage'); @@ -226,19 +249,37 @@ async function main() { addPathArg('cloned-source-packages-path', 'clonedSourcePackagesDirPath'); addPathArg('package-cache-path', 'packageCachePath'); addPathArg('derived-data-path', 'derivedDataPath'); + addInputArg('default-package-registry-url', 'defaultPackageRegistryURL'); + addEnumArg('package-dependency-scm-to-registry-transformation', + ['none', 'useRegistryIdentity', 'useRegistryIdentityAndSources'], + 'packageDependencySCMToRegistryTransformation'); addFlagArg('disable-package-repository-cache', 'disablePackageRepositoryCache'); addFlagArg('disable-automatic-package-resolution', 'disableAutomaticPackageResolution'); addFlagArg('skip-package-updates', 'skipPackageUpdates'); addFlagArg('skip-package-plugin-validation', 'skipPackagePluginValidation'); addFlagArg('skip-macro-validation', 'skipMacroValidation'); + addEnumArg('package-fingerprint-policy', ['warn', 'strict'], 'packageFingerprintPolicy'); + addEnumArg('package-signing-entity-policy', ['warn', 'strict'], 'packageSigningEntityPolicy'); addPathArg('xcroot'); addPathArg('xctestrun'); + addInputArg('test-language', 'testLanguage'); + addInputArg('test-region', 'testRegion'); addInputArg('test-plan', 'testPlan'); addListArg('only-testing'); addListArg('skip-testing'); + addListArg('only-test-configuration'); + addListArg('skip-test-configuration'); + addInputArg('test-iterations'); + addFlagArg('retry-tests-on-failure'); + addFlagArg('run-tests-until-failure'); addFlagArg('skip-unavailable-actions', 'skipUnavailableActions'); addFlagArg('allow-provisioning-updates', 'allowProvisioningUpdates'); addFlagArg('allow-provisioning-device-registration', 'allowProvisioningDeviceRegistration'); + addFlagArg('export-notarized-app', 'exportNotarizedApp'); + addPathArg('export-options-plist', 'exportOptionsPlist'); + addFlagArg('export-archive', 'exportArchive'); + addPathArg('archive-path', 'archivePath'); + addFlagArg('create-xcframework'); const buildSettings = core.getInput('build-settings'); if (buildSettings) @@ -285,7 +326,7 @@ async function main() { ...inv, ';', 'popd', - ] + ]; } unprocessedInvocation = _combinedInv(unprocessedInvocation, false); processedInvocation = _combinedInv(processedInvocation, true);