├── Jenkinsfile ├── .github ├── CODEOWNERS ├── release-drafter.yml ├── dependabot.yml ├── workflows │ ├── release-drafter.yml │ ├── workflow-test.yaml │ └── test.yml └── aws_signer.py ├── .mvn ├── maven.config └── extensions.xml ├── .gitignore ├── src ├── main │ ├── java │ │ └── io │ │ │ └── jenkins │ │ │ └── plugins │ │ │ └── appdome │ │ │ └── build │ │ │ └── to │ │ │ └── secure │ │ │ ├── platform │ │ │ ├── PlatformType.java │ │ │ ├── SignType.java │ │ │ ├── PlatformDescriptor.java │ │ │ ├── ios │ │ │ │ ├── certificate │ │ │ │ │ └── method │ │ │ │ │ │ ├── CertificateMethodDescriptor.java │ │ │ │ │ │ ├── PrivateSign.java │ │ │ │ │ │ ├── CertificateMethod.java │ │ │ │ │ │ ├── AutoDevSign.java │ │ │ │ │ │ └── AutoSign.java │ │ │ │ └── IosPlatform.java │ │ │ ├── android │ │ │ │ ├── certificate │ │ │ │ │ └── method │ │ │ │ │ │ ├── CertificateMethodDescriptor.java │ │ │ │ │ │ ├── CertificateMethod.java │ │ │ │ │ │ ├── PrivateSign.java │ │ │ │ │ │ ├── AutoDevSign.java │ │ │ │ │ │ ├── AutoGoogleSign.java │ │ │ │ │ │ └── AutoSign.java │ │ │ │ ├── Datadog.java │ │ │ │ ├── Crashlytics.java │ │ │ │ └── AndroidPlatform.java │ │ │ └── Platform.java │ │ │ ├── SecondOutput.java │ │ │ ├── BuildToTest.java │ │ │ ├── DynamicCertificate.java │ │ │ ├── VendorManager.java │ │ │ ├── AppdomeBuilderConstants.java │ │ │ ├── StringWarp.java │ │ │ └── AppdomeBuilder.java │ └── resources │ │ ├── index.jelly │ │ └── io │ │ └── jenkins │ │ └── plugins │ │ └── appdome │ │ └── build │ │ └── to │ │ └── secure │ │ ├── platform │ │ ├── android │ │ │ ├── certificate │ │ │ │ └── method │ │ │ │ │ ├── AutoDevSign │ │ │ │ │ └── config.jelly │ │ │ │ │ ├── PrivateSign │ │ │ │ │ └── config.jelly │ │ │ │ │ └── AutoSign │ │ │ │ │ └── config.jelly │ │ │ └── AndroidPlatform │ │ │ │ └── config.jelly │ │ └── ios │ │ │ ├── IosPlatform │ │ │ └── config.jelly │ │ │ └── certificate │ │ │ └── method │ │ │ ├── PrivateSign │ │ │ └── config.jelly │ │ │ ├── AutoDevSign │ │ │ └── config.jelly │ │ │ └── AutoSign │ │ │ └── config.jelly │ │ └── AppdomeBuilder │ │ └── config.jelly └── test │ └── java │ └── io │ └── jenkins │ └── plugins │ └── appdome │ └── build │ └── to │ └── secure │ ├── AppdomeBuilderTest.java │ ├── Tests.java │ └── PipelineTest.java ├── LICENSE.md ├── README.md └── pom.xml /Jenkinsfile: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @jenkinsci/appdome-build-2secure-plugin-developers 2 | -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -Pconsume-incrementals 2 | -Pmight-produce-incrementals 3 | 4 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | _extends: .github 2 | tag-template: appdome-build-2secure-$NEXT_MINOR_VERSION 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | 3 | # mvn hpi:run 4 | work 5 | 6 | # IntelliJ IDEA project files 7 | *.iml 8 | *.iws 9 | *.ipr 10 | .idea 11 | 12 | # Eclipse project files 13 | .settings 14 | .classpath 15 | .project 16 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/PlatformType.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform; 2 | 3 | public enum PlatformType { 4 | IOS, 5 | ANDROID 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/index.jelly: -------------------------------------------------------------------------------- 1 | 2 |
3 | Appdome Build-2secure integrates Jenkins with 4 | Appdome 5 |
6 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/SignType.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform; 2 | 3 | public enum SignType { 4 | AUTO, 5 | PRIVATE, 6 | AUTODEV, 7 | NONE 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/PlatformDescriptor.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform; 2 | 3 | import hudson.model.Descriptor; 4 | 5 | public class PlatformDescriptor extends Descriptor { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/ios/certificate/method/CertificateMethodDescriptor.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method; 2 | 3 | import hudson.model.Descriptor; 4 | 5 | 6 | public class CertificateMethodDescriptor extends Descriptor { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/android/certificate/method/CertificateMethodDescriptor.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method; 2 | 3 | import hudson.model.Descriptor; 4 | 5 | public class CertificateMethodDescriptor extends Descriptor { 6 | } 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuring-dependabot-version-updates 2 | 3 | version: 2 4 | updates: 5 | - package-ecosystem: maven 6 | directory: / 7 | schedule: 8 | interval: monthly 9 | - package-ecosystem: github-actions 10 | directory: / 11 | schedule: 12 | interval: monthly 13 | -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | io.jenkins.tools.incrementals 4 | git-changelist-maven-extension 5 | 1.6 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/io/jenkins/plugins/appdome/build/to/secure/platform/android/certificate/method/AutoDevSign/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |
8 |
9 | 10 | 11 | 12 |
13 | -------------------------------------------------------------------------------- /src/main/resources/io/jenkins/plugins/appdome/build/to/secure/platform/android/certificate/method/PrivateSign/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |
8 |
9 | 10 | 11 | 12 |
13 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | # Automates creation of Release Drafts using Release Drafter 2 | # More Info: https://github.com/jenkinsci/.github/blob/master/.github/release-drafter.adoc 3 | 4 | on: 5 | push: 6 | branches: 7 | - master 8 | - main 9 | 10 | jobs: 11 | update_release_draft: 12 | runs-on: ubuntu-latest 13 | steps: 14 | # Drafts your next Release notes as Pull Requests are merged into the default branch 15 | - name: Update Release Draft 16 | uses: release-drafter/release-drafter@v5 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/SecondOutput.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure; 2 | 3 | import org.kohsuke.stapler.DataBoundConstructor; 4 | import org.kohsuke.stapler.DataBoundSetter; 5 | 6 | public class SecondOutput { 7 | private String secondOutput; 8 | 9 | @DataBoundConstructor 10 | public SecondOutput(String secondOutput) { 11 | this.secondOutput = secondOutput; 12 | } 13 | 14 | @DataBoundSetter 15 | public void setSecondOutput(String secondOutput) { 16 | this.secondOutput = secondOutput; 17 | } 18 | 19 | public String getSecondOutput() { 20 | return secondOutput; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/BuildToTest.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure; 2 | 3 | import org.kohsuke.stapler.DataBoundConstructor; 4 | import org.kohsuke.stapler.DataBoundSetter; 5 | 6 | public class BuildToTest { 7 | private String selectedVendor; 8 | 9 | @DataBoundConstructor 10 | public BuildToTest(String selectedVendor) { 11 | this.selectedVendor = selectedVendor; 12 | } 13 | 14 | @DataBoundSetter 15 | public void setSelectedVendor(String selectedVendor) { 16 | this.selectedVendor = selectedVendor; 17 | } 18 | 19 | public String getSelectedVendor() { 20 | return selectedVendor; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/DynamicCertificate.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure; 2 | 3 | import org.kohsuke.stapler.DataBoundConstructor; 4 | import org.kohsuke.stapler.DataBoundSetter; 5 | 6 | public class DynamicCertificate { 7 | private String dynamicCertificate; 8 | 9 | @DataBoundConstructor 10 | public DynamicCertificate(String dynamicCertificate) { 11 | this.dynamicCertificate = dynamicCertificate; 12 | } 13 | 14 | @DataBoundSetter 15 | public void setDynamicCertificate(String dynamicCertificate) { 16 | this.dynamicCertificate = dynamicCertificate; 17 | } 18 | 19 | public String getDynamicCertificate() { 20 | return this.dynamicCertificate; 21 | } 22 | 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/resources/io/jenkins/plugins/appdome/build/to/secure/platform/ios/IosPlatform/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 |
15 | 16 | 17 | 18 |
-------------------------------------------------------------------------------- /src/main/resources/io/jenkins/plugins/appdome/build/to/secure/platform/ios/certificate/method/PrivateSign/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 |
9 |
10 | 11 |
12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/android/Datadog.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.android; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | import org.jenkinsci.Symbol; 8 | import org.kohsuke.stapler.export.Exported; 9 | 10 | public class Datadog extends AbstractDescribableImpl { 11 | 12 | private final String datadogKey; 13 | 14 | @DataBoundConstructor 15 | public Datadog(String datadogKey) { 16 | this.datadogKey = datadogKey; 17 | } 18 | 19 | @Exported 20 | public String getDatadogKey() { 21 | return datadogKey; 22 | } 23 | 24 | @Symbol("Datadog") 25 | @Extension 26 | public static class DescriptorImpl extends Descriptor { 27 | public String getDisplayName() { 28 | return "Datadog Configuration"; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/android/certificate/method/CertificateMethod.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method; 2 | 3 | import hudson.ExtensionPoint; 4 | import hudson.model.Describable; 5 | import hudson.model.Descriptor; 6 | import io.jenkins.plugins.appdome.build.to.secure.platform.SignType; 7 | import jenkins.model.Jenkins; 8 | 9 | public class CertificateMethod implements Describable, ExtensionPoint { 10 | private SignType signType; 11 | 12 | public CertificateMethod(SignType signType) { 13 | this.signType = signType; 14 | } 15 | 16 | 17 | public SignType getSignType() { 18 | return signType; 19 | } 20 | 21 | public void setSignType(SignType signType) { 22 | this.signType = signType; 23 | } 24 | @Override 25 | public Descriptor getDescriptor() { 26 | return Jenkins.get().getDescriptorOrDie(getClass()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright 2023 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/VendorManager.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure; 2 | 3 | import hudson.util.ListBoxModel; 4 | 5 | public class VendorManager { 6 | 7 | public enum Vendor { 8 | SAUCELABS, 9 | BITBAR, 10 | LAMBDATEST, 11 | BROWSERSTACK, 12 | PERFECTO, 13 | FIREBASE, 14 | KATALON, 15 | TOSCA, 16 | AWS_DEVICE_FARM 17 | } 18 | 19 | private static final VendorManager instance = new VendorManager(); 20 | 21 | private VendorManager() { 22 | } 23 | 24 | public static VendorManager getInstance() { 25 | return instance; 26 | } 27 | 28 | public ListBoxModel getVendors() { 29 | ListBoxModel vendors = new ListBoxModel(); 30 | for (Vendor vendor : Vendor.values()) { 31 | String name = vendor.name(); 32 | String formattedName = name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 33 | vendors.add(formattedName, name); 34 | } 35 | return vendors; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/android/Crashlytics.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.android; 2 | 3 | import hudson.Extension; 4 | import hudson.model.Describable; 5 | import hudson.model.Descriptor; 6 | import jenkins.model.Jenkins; 7 | import org.jenkinsci.Symbol; 8 | import org.kohsuke.stapler.DataBoundConstructor; 9 | import org.kohsuke.stapler.export.Exported; 10 | 11 | public class Crashlytics implements Describable { 12 | 13 | private final String firebaseAppId; 14 | 15 | @DataBoundConstructor 16 | public Crashlytics(String firebaseAppId) { 17 | this.firebaseAppId = firebaseAppId; 18 | } 19 | 20 | @Exported 21 | public String getFirebaseAppId() { 22 | return firebaseAppId; 23 | } 24 | 25 | @Override 26 | public Descriptor getDescriptor() { 27 | return Jenkins.get().getDescriptor(Crashlytics.class); 28 | } 29 | 30 | @Symbol("Crashlytics") // ✅ Register as DSL symbol 31 | @Extension 32 | public static class DescriptorImpl extends Descriptor { 33 | @Override 34 | public String getDisplayName() { 35 | return "Crashlytics Configuration"; 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/ios/certificate/method/PrivateSign.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method; 2 | 3 | import hudson.Extension; 4 | import io.jenkins.plugins.appdome.build.to.secure.StringWarp; 5 | import io.jenkins.plugins.appdome.build.to.secure.platform.SignType; 6 | import org.jenkinsci.Symbol; 7 | import org.kohsuke.stapler.DataBoundConstructor; 8 | 9 | import java.util.List; 10 | 11 | public class PrivateSign extends CertificateMethod { 12 | private final List provisioningProfiles; 13 | 14 | @DataBoundConstructor 15 | public PrivateSign(List provisioningProfiles) { 16 | super(SignType.PRIVATE); 17 | this.provisioningProfiles = provisioningProfiles; 18 | } 19 | 20 | public List getProvisioningProfiles() { 21 | return provisioningProfiles; 22 | } 23 | 24 | public String getProvisioningProfilesPath() { 25 | return concatenateStrings(provisioningProfiles); 26 | } 27 | 28 | @Symbol("iOS_PrivateSign") 29 | @Extension 30 | public static final class DescriptorImpl extends CertificateMethodDescriptor { 31 | @Override 32 | public String getDisplayName() { 33 | return "Private Signing"; 34 | } 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Appdome Build-2secure-plugin for Jenkins 2 | 3 | 4 | ## Introduction 5 | 6 | The Build-2Secure plugin for Jenkins automates three important steps in delivering more secure mobile applications to your users faster:
7 | (1) Building app-level protections into mobile apps
8 | (2) Code signing the protected mobile app
9 | (3) Certifying the security of each protected mobile app.
10 | [The Appdome Build-2Secure](https://www.appdome.com) plugin for Jenkins can be used to deliver Certifed Secure™ mobile app security, anti-fraud, anti-malware, mobile anti-bot, and other cyber defense updates to mobile apps on the Appdome Cyber Defense Automation Platform. Use this plugin for Jenkins as a stand-alone DevSecOps integration or in combination with other DevSecOps integrations in your CI/CD pipeline. Developers have the option of using Freestyle or Pipeline projects to configure. 11 | 12 | 13 | ## Getting started 14 | 15 | For documentation on how to use the plugin, see the [Appdome Build-2secure plugin](https://www.appdome.com/how-to/devsecops-automation-mobile-cicd/mobile-app-security-anti-fraud-cicd/use-appdome-build-2secure-plugin-for-jenkins/). 16 | 17 | ## Issues 18 | 19 | Please report issues and enhancements through the [Github issue tracker](https://github.com/jenkinsci/appdome-build-2secure-plugin/issues/new/choose). 20 | -------------------------------------------------------------------------------- /src/main/resources/io/jenkins/plugins/appdome/build/to/secure/platform/android/AndroidPlatform/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 |
10 | 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
-------------------------------------------------------------------------------- /src/main/resources/io/jenkins/plugins/appdome/build/to/secure/platform/ios/certificate/method/AutoDevSign/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 | 22 |
23 |
24 |
25 | 26 |
27 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/ios/certificate/method/CertificateMethod.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method; 2 | 3 | import hudson.ExtensionPoint; 4 | import hudson.model.Describable; 5 | import hudson.model.Descriptor; 6 | 7 | import io.jenkins.plugins.appdome.build.to.secure.StringWarp; 8 | import io.jenkins.plugins.appdome.build.to.secure.platform.SignType; 9 | import jenkins.model.Jenkins; 10 | 11 | import java.util.List; 12 | 13 | public class CertificateMethod implements Describable, ExtensionPoint { 14 | private SignType signType; 15 | 16 | public CertificateMethod(SignType signType) { 17 | this.signType = signType; 18 | } 19 | 20 | 21 | public SignType getSignType() { 22 | return signType; 23 | } 24 | 25 | public void setSignType(SignType signType) { 26 | this.signType = signType; 27 | } 28 | 29 | public String concatenateStrings(List strList) { 30 | if (strList != null) { 31 | StringBuilder concatenatePath = new StringBuilder(); 32 | for (StringWarp path : strList) { 33 | concatenatePath.append(((StringWarp) path).getItem()).append(','); 34 | } 35 | return concatenatePath.substring(0, concatenatePath.length() - 1).trim(); 36 | } 37 | return null; 38 | } 39 | 40 | 41 | @Override 42 | public Descriptor getDescriptor() { 43 | return Jenkins.get().getDescriptorOrDie(getClass()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/Platform.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform; 2 | 3 | import hudson.ExtensionPoint; 4 | import hudson.model.Describable; 5 | import hudson.model.Descriptor; 6 | import jenkins.model.Jenkins; 7 | 8 | public abstract class Platform implements Describable, ExtensionPoint { 9 | 10 | private String appPath; 11 | private String fusionSetId; 12 | private PlatformType platformType; 13 | 14 | 15 | public Platform(PlatformType platformType) { 16 | this.platformType = platformType; 17 | } 18 | 19 | public PlatformType getPlatformType() { 20 | return platformType; 21 | } 22 | 23 | public void setPlatformType(PlatformType platformType) { 24 | this.platformType = platformType; 25 | } 26 | 27 | 28 | public String getAppPath() { 29 | return appPath; 30 | } 31 | 32 | 33 | public void setAppPath(String appPath) { 34 | this.appPath = appPath; 35 | } 36 | 37 | 38 | public void setFusionSetId(String fusionSetId) { 39 | this.fusionSetId = fusionSetId; 40 | } 41 | 42 | public String getFusionSetId() { 43 | return fusionSetId; 44 | } 45 | 46 | 47 | @Override 48 | public Descriptor getDescriptor() { 49 | return Jenkins.get().getDescriptorOrDie(getClass()); 50 | } 51 | 52 | // 53 | // @Extension 54 | // public static class DescriptorImpl extends Descriptor { 55 | // 56 | // 57 | // public String getDisplayName() { 58 | // return null; 59 | // } 60 | // } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/main/resources/io/jenkins/plugins/appdome/build/to/secure/platform/android/certificate/method/AutoSign/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 |
19 | 20 |
21 |
22 | 23 |
24 | 25 |
26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 |
37 |
38 | 39 |
40 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/ios/certificate/method/AutoDevSign.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method; 2 | 3 | import hudson.Extension; 4 | import io.jenkins.plugins.appdome.build.to.secure.StringWarp; 5 | import org.jenkinsci.Symbol; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | import io.jenkins.plugins.appdome.build.to.secure.platform.SignType; 8 | import java.util.List; 9 | 10 | public class AutoDevSign extends CertificateMethod { 11 | private final List provisioningProfiles; 12 | private final List entitlements; 13 | 14 | @DataBoundConstructor 15 | public AutoDevSign(List provisioningProfiles, List entitlements) { 16 | super(SignType.AUTODEV); 17 | this.provisioningProfiles = provisioningProfiles; 18 | this.entitlements = entitlements; 19 | } 20 | 21 | public List getProvisioningProfiles() { 22 | return provisioningProfiles; 23 | } 24 | 25 | public List getEntitlements() { 26 | return entitlements; 27 | } 28 | 29 | public String getEntitlementsPath() { 30 | return concatenateStrings(entitlements); 31 | } 32 | 33 | public String getProvisioningProfilesPath() { 34 | return concatenateStrings(provisioningProfiles); 35 | } 36 | 37 | 38 | @Symbol("iOS_AutoDevSign") 39 | @Extension 40 | public static final class DescriptorImpl extends CertificateMethodDescriptor { 41 | @Override 42 | public String getDisplayName() { 43 | return "Auto-Dev Signing"; 44 | } 45 | 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/resources/io/jenkins/plugins/appdome/build/to/secure/platform/ios/certificate/method/AutoSign/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 |
16 | 17 | 18 | 19 |
20 | 21 |
22 |
23 | 24 |
25 |
26 |
27 | 28 | 29 | 30 |
31 | 32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/AppdomeBuilderConstants.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure; 2 | 3 | public interface AppdomeBuilderConstants { 4 | /** 5 | * Environment variables 6 | **/ 7 | String APP_PATH = "APP_PATH"; 8 | String KEYSTORE_PATH_ENV = "KEYSTORE_PATH"; 9 | String MOBILE_PROVISION_PROFILE_PATHS_ENV = "MOBILE_PROVISION_PROFILE_PATHS"; 10 | String ENTITLEMENTS_PATHS_ENV = "ENTITLEMENTS_PATHS"; 11 | String APPDOME_HEADER_ENV_NAME = "APPDOME_CLIENT_HEADER"; 12 | String APPDOME_BUILDE2SECURE_VERSION = "Jenkins/1.3.0"; 13 | 14 | /** 15 | * FLAGS 16 | **/ 17 | 18 | String KEY_FLAG = " --api_key "; 19 | String FUSION_SET_ID_FLAG = " --fusion_set_id "; 20 | String TEAM_ID_FLAG = " --team_id "; 21 | String APP_FLAG = " --app "; 22 | String OUTPUT_FLAG = " --output "; 23 | String SIGN_ON_APPDOME_FLAG = " --sign_on_appdome "; 24 | String KEYSTORE_FLAG = " --keystore "; 25 | String KEYSTORE_PASS_FLAG = " --keystore_pass "; 26 | String KEYSOTRE_ALIAS_FLAG = " --keystore_alias "; 27 | String KEY_PASS_FLAG = " --key_pass "; 28 | String PROVISION_PROFILES_FLAG = " --provisioning_profiles "; 29 | String ENTITLEMENTS_FLAG = " --entitlements "; 30 | String PRIVATE_SIGN_FLAG = " --private_signing "; 31 | String AUTO_DEV_PRIVATE_SIGN_FLAG = " --auto_dev_private_signing "; 32 | String GOOGLE_PLAY_SIGN_FLAG = " --google_play_signing "; 33 | String FINGERPRINT_FLAG = " --signing_fingerprint "; 34 | String CERTIFIED_SECURE_PDF_FLAG = " --certificate_output "; 35 | String CERTIFIED_SECURE_JSON_FLAG = " --certificate_json "; 36 | String BUILD_WITH_LOGS = " --build_logs "; 37 | String BUILD_TO_TEST = " --build_to_test_vendor "; 38 | String SECOND_OUTPUT = " --second_output "; 39 | 40 | String DYNAMIC_CERTIFICATE = " --cert_pinning_zip "; 41 | 42 | 43 | String DEOBFUSCATION_OUTPUT = " --deobfuscation_script_output "; 44 | String FIREBASE_APP_ID = " --app_id "; 45 | String DATADOG_API_KEY = " --datadog_api_key "; 46 | String WORKFLOW_OUTPUT_LOGS_FLAG = " --workflow_output_logs "; 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/android/certificate/method/PrivateSign.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method; 2 | 3 | import hudson.Extension; 4 | import hudson.Util; 5 | import hudson.util.FormValidation; 6 | import io.jenkins.plugins.appdome.build.to.secure.platform.SignType; 7 | import jenkins.model.Jenkins; 8 | import org.jenkinsci.Symbol; 9 | import org.kohsuke.stapler.DataBoundConstructor; 10 | import org.kohsuke.stapler.DataBoundSetter; 11 | import org.kohsuke.stapler.QueryParameter; 12 | import org.kohsuke.stapler.verb.POST; 13 | 14 | public class PrivateSign extends CertificateMethod { 15 | 16 | private final String fingerprint; 17 | private Boolean googleSigning; 18 | 19 | @DataBoundConstructor 20 | public PrivateSign(String fingerprint) { 21 | super(SignType.PRIVATE); 22 | this.fingerprint = fingerprint; 23 | } 24 | 25 | public String getFingerprint() { 26 | return fingerprint; 27 | } 28 | 29 | public Boolean getGoogleSigning() { 30 | return googleSigning; 31 | } 32 | 33 | @DataBoundSetter 34 | public void setGoogleSigning(Boolean googleSigning) { 35 | this.googleSigning = googleSigning; 36 | } 37 | 38 | @Symbol("Android_PrivateSign") 39 | @Extension 40 | public static final class DescriptorImpl extends CertificateMethodDescriptor { 41 | 42 | @POST 43 | public FormValidation doCheckFingerprint(@QueryParameter String fingerprint) { 44 | Jenkins.get().checkPermission(Jenkins.READ); 45 | if (fingerprint != null && Util.fixEmptyAndTrim(fingerprint) == null) { 46 | return FormValidation.error("Fingerprint must be provided."); 47 | }else if (fingerprint != null && fingerprint.contains(" ")) { 48 | return FormValidation.error("White spaces are not allowed in FingerPrint."); 49 | } 50 | // Perform any additional validation here 51 | return FormValidation.ok(); 52 | } 53 | 54 | @Override 55 | public String getDisplayName() { 56 | return "Private Signing"; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/android/certificate/method/AutoDevSign.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method; 2 | 3 | import hudson.Extension; 4 | import hudson.Util; 5 | import hudson.util.FormValidation; 6 | import io.jenkins.plugins.appdome.build.to.secure.platform.SignType; 7 | import jenkins.model.Jenkins; 8 | import org.jenkinsci.Symbol; 9 | import org.kohsuke.stapler.DataBoundConstructor; 10 | import org.kohsuke.stapler.DataBoundSetter; 11 | import org.kohsuke.stapler.QueryParameter; 12 | import org.kohsuke.stapler.verb.POST; 13 | 14 | public class AutoDevSign extends CertificateMethod { 15 | 16 | private final String fingerprint; 17 | private Boolean googleSigning; 18 | 19 | @DataBoundConstructor 20 | public AutoDevSign(String fingerprint) { 21 | 22 | super(SignType.AUTODEV); 23 | this.fingerprint = fingerprint; 24 | } 25 | 26 | public String getFingerprint() { 27 | return fingerprint; 28 | } 29 | 30 | public Boolean getGoogleSigning() { 31 | return googleSigning; 32 | } 33 | 34 | @DataBoundSetter 35 | public void setGoogleSigning(Boolean googleSigning) { 36 | this.googleSigning = googleSigning; 37 | } 38 | 39 | @Symbol("Android_AutoDevSign") 40 | @Extension 41 | public static final class DescriptorImpl extends CertificateMethodDescriptor { 42 | 43 | @POST 44 | public FormValidation doCheckFingerprint(@QueryParameter String fingerprint) { 45 | Jenkins.get().checkPermission(Jenkins.READ); 46 | if (fingerprint != null && Util.fixEmptyAndTrim(fingerprint) == null) { 47 | return FormValidation.error("Fingerprint must be provided."); 48 | }else if (fingerprint != null && fingerprint.contains(" ")) { 49 | return FormValidation.error("White spaces are not allowed in Fingerprint."); 50 | } 51 | // Perform any additional validation here 52 | return FormValidation.ok(); 53 | } 54 | 55 | @Override 56 | public String getDisplayName() { 57 | return "Auto-Dev Signing"; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/android/certificate/method/AutoGoogleSign.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method; 2 | 3 | import hudson.Extension; 4 | import hudson.Util; 5 | import hudson.model.AbstractDescribableImpl; 6 | import hudson.model.Descriptor; 7 | import hudson.util.FormValidation; 8 | import jenkins.model.Jenkins; 9 | import org.jenkinsci.Symbol; 10 | import org.kohsuke.stapler.DataBoundConstructor; 11 | import org.kohsuke.stapler.DataBoundSetter; 12 | import org.kohsuke.stapler.QueryParameter; 13 | import org.kohsuke.stapler.verb.POST; 14 | 15 | public class AutoGoogleSign extends AbstractDescribableImpl { 16 | 17 | private String googleSignFingerPrint; 18 | 19 | 20 | private Boolean isEnableGoogleSign; 21 | 22 | @DataBoundConstructor 23 | public AutoGoogleSign(String googleSignFingerPrint) { 24 | if (googleSignFingerPrint != null) { 25 | this.googleSignFingerPrint = googleSignFingerPrint; 26 | this.isEnableGoogleSign = true; 27 | } else { 28 | this.googleSignFingerPrint = null; 29 | this.isEnableGoogleSign = false; 30 | } 31 | } 32 | 33 | public String getGoogleSignFingerPrint() { 34 | return googleSignFingerPrint; 35 | } 36 | 37 | public Boolean getIsEnableGoogleSign() { 38 | return isEnableGoogleSign; 39 | } 40 | 41 | @DataBoundSetter 42 | public void setGoogleSignFingerPrint(String googleSignFingerPrint) { 43 | if (googleSignFingerPrint != null) { 44 | this.googleSignFingerPrint = googleSignFingerPrint; 45 | this.isEnableGoogleSign = true; 46 | } else { 47 | this.googleSignFingerPrint = null; 48 | this.isEnableGoogleSign = false; 49 | } 50 | } 51 | 52 | 53 | @Symbol({"Android_AutoGoogleSign", "SignOnAppdome_GoogleSign"}) 54 | @Extension 55 | public static class DescriptorImpl extends Descriptor { 56 | 57 | @POST 58 | public FormValidation doCheckGoogleSignFingerPrint(@QueryParameter String googleSignFingerPrint) { 59 | Jenkins.get().checkPermission(Jenkins.READ); 60 | if (googleSignFingerPrint != null && Util.fixEmptyAndTrim(googleSignFingerPrint) == null) { 61 | return FormValidation.error("If Google Sign is enabled, fingerprint must be provided."); 62 | }else if (googleSignFingerPrint != null && googleSignFingerPrint.contains(" ")) { 63 | return FormValidation.error("White spaces are not allowed in FingerPrint."); 64 | } 65 | // Perform any additional validation here 66 | return FormValidation.ok(); 67 | } 68 | public String getDisplayName() { 69 | return ""; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/resources/io/jenkins/plugins/appdome/build/to/secure/AppdomeBuilder/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 |
7 | 8 |
9 | 10 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 |
37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
78 | -------------------------------------------------------------------------------- /.github/aws_signer.py: -------------------------------------------------------------------------------- 1 | import os 2 | import boto3 3 | import json 4 | 5 | 6 | def main(): 7 | aws_access_key_id = os.environ.get('AWS_ACCESS_KEY_ID') 8 | aws_secret_access_key = os.environ.get('AWS_SECRET_ACCESS_KEY') 9 | aws_default_region = 'eu-central-1' 10 | bucket_name = "appdome-automation-vanilla-apps" 11 | objects = { 12 | 'aab_app.aab': 'Thomas/PipelineFiles/Apps/FileFinder.aab', 13 | 'apk_app_1.apk': 'Thomas/PipelineFiles/Apps/TimeCard.apk', 14 | 'apk_app_2.apk': 'Thomas/PipelineFiles/Apps/AndroidMediaPlayer.apk', 15 | 'keystore_file.keystore': 'Thomas/PipelineFiles/appdome.keystore', 16 | 'ipa_app_1.ipa': 'Thomas/PipelineFiles/Apps/FileFinder.ipa', 17 | 'ipa_app_2.ipa': 'Thomas/PipelineFiles/Apps/Trends256-iOS16.ipa', 18 | 'ipa_app_3.ipa': 'Thomas/PipelineFiles/Apps/Trends256-ForWatchKit.ipa', 19 | 'certificate_file1.p12': 'Thomas/PipelineFiles/AutomationCert.p12', 20 | 'certificate_file2.p12': 'Thomas/PipelineFiles/meirtsvi75.p12', 21 | 'ipa_1_mobile_provisioning.mobileprovision': 'Thomas/PipelineFiles/Automation.mobileprovision', 22 | 'ipa_1_entitlements.plist': 'Thomas/PipelineFiles/AutomationEntitlements.plist', 23 | 'ipa_2_mobile_provisioning_1.mobileprovision': 'Thomas/PipelineFiles/Trendsappstoredist.mobileprovision', 24 | 'ipa_2_mobile_provisioning_2.mobileprovision': 'Thomas/PipelineFiles/Trends_watchkit_appstoredist.mobileprovision', 25 | 'ipa_2_mobile_provisioning_3.mobileprovision': 'Thomas/PipelineFiles/Trends_watchkit_extension_appstoredist.mobileprovision', 26 | 'ipa_2_entitlements_1.plist': 'Thomas/PipelineFiles/main.plist', 27 | 'ipa_2_entitlements_2.plist': 'Thomas/PipelineFiles/watchkit.plist', 28 | 'ipa_2_entitlements_3.plist': 'Thomas/PipelineFiles/watchkitextension.plist', 29 | } 30 | 31 | if aws_access_key_id is None or aws_secret_access_key is None: 32 | print("Missing required environment variables.") 33 | exit(1) 34 | 35 | s3 = boto3.client('s3', aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key, 36 | region_name=aws_default_region) 37 | 38 | presigned_urls = {} 39 | 40 | for key, object_key in objects.items(): 41 | presigned_url = s3.generate_presigned_url( 42 | 'get_object', 43 | Params={ 44 | 'Bucket': bucket_name, 45 | 'Key': object_key 46 | }, 47 | ExpiresIn=3600 # 1 hour 48 | ) 49 | presigned_urls[key] = presigned_url 50 | 51 | destination_folder = "presigned_urls" 52 | # Specify the path to the JSON file 53 | json_file_path = os.path.join(destination_folder, 'presigned_urls.json') 54 | 55 | # Create destination folder if it doesn't exist 56 | os.makedirs(destination_folder, exist_ok=True) 57 | # Write the dictionary to the JSON file 58 | with open(json_file_path, 'w') as json_file: 59 | json.dump(presigned_urls, json_file) 60 | 61 | print(f"The presigned URLs have been written to {json_file_path}") 62 | 63 | 64 | if __name__ == "__main__": 65 | main() 66 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.jenkins-ci.plugins 6 | plugin 7 | 4.55 8 | 9 | 10 | io.jenkins.plugins 11 | appdome-build-2secure 12 | ${revision}${changelist} 13 | hpi 14 | Appdome Build-2secure 15 | https://github.com/jenkinsci/${project.artifactId}-plugin 16 | 17 | 18 | MIT License 19 | https://opensource.org/licenses/MIT 20 | 21 | 22 | 23 | scm:git:https://github.com/${gitHubRepo} 24 | scm:git:https://github.com/${gitHubRepo} 25 | appdome-build-2secure-1.2.8 26 | https://github.com/${gitHubRepo} 27 | 28 | 29 | 1.2.10 30 | -SNAPSHOT 31 | 32 | 2.361.4 33 | jenkinsci/${project.artifactId}-plugin 34 | true 35 | 36 | 37 | 38 | 39 | 40 | io.jenkins.tools.bom 41 | bom-2.387.x 42 | 2543.vfb_1a_5fb_9496d 43 | pom 44 | import 45 | 46 | 47 | 48 | 49 | 50 | org.jenkins-ci.plugins 51 | credentials 52 | 1139.veb_9579fca_33b_ 53 | test 54 | 55 | 56 | 57 | 58 | 59 | 60 | repo.jenkins-ci.org 61 | https://repo.jenkins-ci.org/public/ 62 | 63 | 64 | 65 | 66 | repo.jenkins-ci.org 67 | https://repo.jenkins-ci.org/public/ 68 | 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-surefire-plugin 75 | 76 | methods 77 | 6 78 | 79 | ${jenkins.version} 80 | 81 | 0 82 | true 83 | false 84 | false 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/StringWarp.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure; 2 | 3 | import hudson.Extension; 4 | import hudson.Util; 5 | import hudson.model.AbstractDescribableImpl; 6 | import hudson.model.Descriptor; 7 | import hudson.util.FormValidation; 8 | import jenkins.model.Jenkins; 9 | import org.jenkinsci.Symbol; 10 | import org.kohsuke.stapler.DataBoundConstructor; 11 | import org.kohsuke.stapler.QueryParameter; 12 | import org.kohsuke.stapler.verb.POST; 13 | 14 | public class StringWarp extends AbstractDescribableImpl { 15 | 16 | private final String item; 17 | 18 | @DataBoundConstructor 19 | public StringWarp(String item) { 20 | this.item = item; 21 | } 22 | 23 | public String getItem() { 24 | return this.item; 25 | } 26 | 27 | public String getProvisioningProfiles() { 28 | return this.item; 29 | } 30 | 31 | public String getEntitlements() { 32 | return this.item; 33 | } 34 | 35 | public String getSecondOutput() { 36 | return this.item; 37 | } 38 | 39 | public String getDynamicCertificate() { 40 | return this.item; 41 | } 42 | 43 | 44 | @Symbol("StringWarp") 45 | @Extension 46 | public static class DescriptorImpl extends Descriptor { 47 | 48 | @POST 49 | public FormValidation doCheckProvisioningProfiles(@QueryParameter String item) { 50 | Jenkins.get().checkPermission(Jenkins.READ); 51 | if (item != null && Util.fixEmptyAndTrim(item) == null) { 52 | return FormValidation.warning("please add at least one Provisioning Profile file and ensure that all entries" + 53 | " are filled without any empty fields.Or please ensure that a valid path is provided " + 54 | "for Provisioning Profile files in the environment variable named 'MOBILE_PROVISION_PROFILE_PATHS' instead"); 55 | } else if (item != null && item.contains(" ")) { 56 | return FormValidation.error("White spaces are not allowed in the path."); 57 | } else if (item != null && !(item.lastIndexOf(".") != -1 58 | && item.substring(item.lastIndexOf(".")).equals(".mobileprovision"))) { 59 | return FormValidation.error("Provisioning profile - File extension is not allowed," + 60 | " allowed extensions are: '.mobileprovision'. Please rename your file or upload a different file."); 61 | } 62 | return FormValidation.ok(); 63 | } 64 | 65 | @POST 66 | public FormValidation doCheckEntitlements(@QueryParameter String item) { 67 | Jenkins.get().checkPermission(Jenkins.READ); 68 | if (item != null && Util.fixEmptyAndTrim(item) == null) { 69 | return FormValidation.warning("please add at least one entitlement file and ensure that all entries" + 70 | " are filled without any empty fields.Or please ensure that a valid path is provided " + 71 | "for entitlements files in the environment variable named 'ENTITLEMENTS_PATHS' instead"); 72 | } else if (item != null && item.contains(" ")) { 73 | return FormValidation.error("White spaces are not allowed in the path."); 74 | } else if (item != null && !(item.lastIndexOf(".") != -1 75 | && item.substring(item.lastIndexOf(".")).equals(".plist"))) { 76 | return FormValidation.error("Entitlements - File extension is not allowed," + 77 | " allowed extensions are: '.plist'. Please rename your file or upload a different file."); 78 | } 79 | 80 | // Perform any additional validation here 81 | return FormValidation.ok(); 82 | } 83 | 84 | public String getDisplayName() { 85 | return ""; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/ios/IosPlatform.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.ios; 2 | 3 | import hudson.DescriptorExtensionList; 4 | import hudson.Extension; 5 | import hudson.Util; 6 | import hudson.model.Descriptor; 7 | import hudson.util.FormValidation; 8 | import io.jenkins.plugins.appdome.build.to.secure.platform.Platform; 9 | import io.jenkins.plugins.appdome.build.to.secure.platform.PlatformDescriptor; 10 | import io.jenkins.plugins.appdome.build.to.secure.platform.PlatformType; 11 | import io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method.CertificateMethod; 12 | import jenkins.model.Jenkins; 13 | import org.jenkinsci.Symbol; 14 | import org.kohsuke.stapler.DataBoundConstructor; 15 | import org.kohsuke.stapler.DataBoundSetter; 16 | import org.kohsuke.stapler.QueryParameter; 17 | import org.kohsuke.stapler.verb.POST; 18 | 19 | import static io.jenkins.plugins.appdome.build.to.secure.AppdomeBuilder.isHttpUrl; 20 | 21 | public class IosPlatform extends Platform { 22 | 23 | private final CertificateMethod certificateMethod; 24 | 25 | @DataBoundConstructor 26 | public IosPlatform(CertificateMethod certificateMethod) { 27 | super(PlatformType.IOS); 28 | this.certificateMethod = certificateMethod; 29 | } 30 | 31 | @DataBoundSetter 32 | public void setAppPath(String appPath) { 33 | super.setAppPath(appPath); 34 | } 35 | 36 | 37 | @DataBoundSetter 38 | public void setFusionSetId(String fusionSetId) { 39 | super.setFusionSetId(fusionSetId); 40 | } 41 | 42 | public String getAppPath() { 43 | return super.getAppPath(); 44 | } 45 | 46 | public String getFusionSetId() { 47 | return super.getFusionSetId(); 48 | } 49 | 50 | public CertificateMethod getCertificateMethod() { 51 | return certificateMethod; 52 | } 53 | 54 | public DescriptorExtensionList> getCertificateMethodDescriptors() { 55 | return Jenkins.get().getDescriptorList(CertificateMethod.class); 56 | } 57 | 58 | @Symbol("IosPlatform") 59 | @Extension 60 | public static final class DescriptorImpl extends PlatformDescriptor { 61 | 62 | @POST 63 | public FormValidation doCheckAppPath(@QueryParameter String appPath) { 64 | Jenkins.get().checkPermission(Jenkins.READ); 65 | if (appPath != null && Util.fixEmptyAndTrim(appPath) == null) { 66 | return FormValidation.warning("Application path was not provided.\n " + 67 | "Please ensure that a valid path is provided for non-protected applications in the environment variable called 'APP_PATH'."); 68 | } else if (appPath != null && appPath.contains(" ")) { 69 | return FormValidation.error("White spaces are not allowed in the path."); 70 | } else if (appPath != null && isHttpUrl(appPath)) { 71 | return FormValidation.ok("Application remote url provided."); 72 | } else if (appPath != null && !(appPath.endsWith(".ipa"))) { 73 | return FormValidation.error("iOS app - File extension is not allowed," + 74 | " allowed extensions are: '.ipa'. Please rename your file or upload a different file."); 75 | } 76 | // Perform any additional validation here 77 | return FormValidation.ok(); 78 | } 79 | 80 | @POST 81 | public FormValidation doCheckFusionSetId(@QueryParameter String fusionSetId) { 82 | Jenkins.get().checkPermission(Jenkins.READ); 83 | if (fusionSetId != null && Util.fixEmptyAndTrim(fusionSetId) == null) { 84 | return FormValidation.error("FusionSet-ID must be provided."); 85 | } else if (fusionSetId != null && fusionSetId.contains(" ")) { 86 | return FormValidation.error("White spaces are not allowed in FusionSetId."); 87 | } 88 | // Perform any additional validation here 89 | return FormValidation.ok("Chosen fusionSet: " + fusionSetId); 90 | } 91 | 92 | @Override 93 | public String getDisplayName() { 94 | return "iOS"; 95 | } 96 | 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/ios/certificate/method/AutoSign.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method; 2 | 3 | import hudson.Extension; 4 | import hudson.Util; 5 | import hudson.util.FormValidation; 6 | import hudson.util.Secret; 7 | import io.jenkins.plugins.appdome.build.to.secure.StringWarp; 8 | import io.jenkins.plugins.appdome.build.to.secure.platform.SignType; 9 | import jenkins.model.Jenkins; 10 | import org.jenkinsci.Symbol; 11 | import org.kohsuke.stapler.DataBoundConstructor; 12 | import org.kohsuke.stapler.DataBoundSetter; 13 | import org.kohsuke.stapler.QueryParameter; 14 | import org.kohsuke.stapler.verb.POST; 15 | 16 | import java.util.List; 17 | 18 | public class AutoSign extends CertificateMethod { 19 | 20 | @SuppressWarnings("lgtm[jenkins/plaintext-storage]") 21 | private final String keystorePath; 22 | private final Secret keystorePassword; 23 | private List provisioningProfiles; 24 | private final List entitlements; 25 | 26 | @DataBoundConstructor 27 | public AutoSign(String keystorePath, Secret keystorePassword, List provisioningProfiles, List entitlements) { 28 | super(SignType.AUTO); 29 | this.keystorePath = keystorePath; 30 | this.keystorePassword = keystorePassword; 31 | this.provisioningProfiles = provisioningProfiles; 32 | this.entitlements = entitlements; 33 | } 34 | 35 | public String getKeystorePath() { 36 | return keystorePath; 37 | } 38 | 39 | public Secret getKeystorePassword() { 40 | return keystorePassword; 41 | } 42 | 43 | public List getProvisioningProfiles() { 44 | return provisioningProfiles; 45 | } 46 | 47 | @DataBoundSetter 48 | public void setProvisioningProfiles(List provisioningProfiles) { 49 | this.provisioningProfiles = provisioningProfiles; 50 | } 51 | 52 | public List getEntitlements() { 53 | return entitlements; 54 | } 55 | 56 | 57 | public String getEntitlementsPath() { 58 | return concatenateStrings(entitlements); 59 | } 60 | 61 | public String getProvisioningProfilesPath() { 62 | return concatenateStrings(provisioningProfiles); 63 | } 64 | 65 | 66 | @Symbol({"iOS_AutoSign", "iOS_SignOnAppdome"}) 67 | @Extension 68 | public static final class DescriptorImpl extends CertificateMethodDescriptor { 69 | 70 | 71 | @POST 72 | public FormValidation doCheckKeystorePath(@QueryParameter String keystorePath) { 73 | Jenkins.get().checkPermission(Jenkins.READ); 74 | if (keystorePath != null && Util.fixEmptyAndTrim(keystorePath) == null) { 75 | return FormValidation.warning("Path to keystore file must be provided." + 76 | "Or please ensure that a valid path is provided for non-protected applications " + 77 | "in the environment variable named 'KEYSTORE_PATH`'."); 78 | } else if (keystorePath != null && keystorePath.contains(" ")) { 79 | return FormValidation.error("White spaces are not allowed in the path."); 80 | } else if (keystorePath != null && !(keystorePath.lastIndexOf(".") != -1 81 | && keystorePath.substring(keystorePath.lastIndexOf(".")).equals(".p12"))) { 82 | return FormValidation.error("iOS keystore - File extension is not allowed," + 83 | " allowed extensions are: '.p12'. Please rename your file or upload a different file."); 84 | } 85 | // Perform any additional validation here 86 | return FormValidation.ok(); 87 | } 88 | 89 | @POST 90 | public FormValidation doCheckKeystorePassword(@QueryParameter Secret keystorePassword) { 91 | Jenkins.get().checkPermission(Jenkins.READ); 92 | if (keystorePassword != null && Util.fixEmptyAndTrim(keystorePassword.getPlainText()) == null) { 93 | return FormValidation.error("Keystore's password must be provided."); 94 | } 95 | // Perform any additional validation here 96 | return FormValidation.ok(); 97 | } 98 | 99 | 100 | @Override 101 | public String getDisplayName() { 102 | return "Sign On Appdome"; 103 | } 104 | 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/android/certificate/method/AutoSign.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method; 2 | 3 | import hudson.Extension; 4 | import hudson.Util; 5 | import hudson.util.FormValidation; 6 | import hudson.util.Secret; 7 | import io.jenkins.plugins.appdome.build.to.secure.platform.SignType; 8 | import jenkins.model.Jenkins; 9 | import org.jenkinsci.Symbol; 10 | import org.kohsuke.stapler.DataBoundConstructor; 11 | import org.kohsuke.stapler.DataBoundSetter; 12 | import org.kohsuke.stapler.QueryParameter; 13 | import org.kohsuke.stapler.verb.POST; 14 | 15 | public class AutoSign extends CertificateMethod { 16 | 17 | @SuppressWarnings("lgtm[jenkins/plaintext-storage]") 18 | private final String keystorePath; 19 | private final Secret keystorePassword; 20 | private final Secret keystoreAlias; 21 | private final Secret keyPass; 22 | private AutoGoogleSign googleSignFingerPrint; 23 | private Boolean isEnableGoogleSign; 24 | 25 | @DataBoundConstructor 26 | public AutoSign(String keystorePath, Secret keystorePassword, Secret keystoreAlias, Secret keyPass, AutoGoogleSign googleSignFingerPrint) { 27 | super(SignType.AUTO); 28 | this.keystorePath = keystorePath; 29 | this.keystorePassword = keystorePassword; 30 | this.keystoreAlias = keystoreAlias; 31 | this.keyPass = keyPass; 32 | this.googleSignFingerPrint = googleSignFingerPrint; 33 | } 34 | 35 | @DataBoundSetter 36 | public void setGoogleSignFingerPrint(AutoGoogleSign googleSignFingerPrint) { 37 | this.googleSignFingerPrint = googleSignFingerPrint; 38 | } 39 | 40 | public Boolean getEnableGoogleSign() { 41 | return isEnableGoogleSign; 42 | } 43 | 44 | public void setEnableGoogleSign(Boolean enableGoogleSign) { 45 | isEnableGoogleSign = enableGoogleSign; 46 | } 47 | 48 | public String getKeystorePath() { 49 | return keystorePath; 50 | } 51 | 52 | public Secret getKeystorePassword() { 53 | return keystorePassword; 54 | } 55 | 56 | public Secret getKeystoreAlias() { 57 | return keystoreAlias; 58 | } 59 | 60 | public Secret getKeyPass() { 61 | return keyPass; 62 | } 63 | 64 | 65 | public String getGoogleSignFingerPrint() { 66 | if (googleSignFingerPrint != null) { 67 | return googleSignFingerPrint.getGoogleSignFingerPrint(); 68 | } 69 | return null; 70 | } 71 | 72 | public Boolean getIsEnableGoogleSign() { 73 | if (googleSignFingerPrint != null) { 74 | return googleSignFingerPrint.getIsEnableGoogleSign(); 75 | } 76 | return false; 77 | } 78 | 79 | 80 | @DataBoundSetter 81 | public void setGoogleSign(AutoGoogleSign googleSignFingerPrint) { 82 | this.googleSignFingerPrint = googleSignFingerPrint; 83 | } 84 | 85 | @Symbol({"Android_AutoSign", "Android_SignOnAppdome"}) 86 | @Extension 87 | public static final class DescriptorImpl extends CertificateMethodDescriptor { 88 | 89 | @POST 90 | public FormValidation doCheckKeystorePath(@QueryParameter String keystorePath) { 91 | Jenkins.get().checkPermission(Jenkins.READ); 92 | if (keystorePath != null && Util.fixEmptyAndTrim(keystorePath) == null) { 93 | return FormValidation.warning("Path to keystore file must be provided." + 94 | "Or please ensure that a valid path is provided for non-protected applications in the environment variable named 'KEYSTORE_PATH'."); 95 | } else if (keystorePath != null && keystorePath.contains(" ")) { 96 | return FormValidation.error("White spaces are not allowed in the path."); 97 | } 98 | // Perform any additional validation here 99 | return FormValidation.ok(); 100 | } 101 | 102 | @POST 103 | public FormValidation doCheckKeystorePassword(@QueryParameter Secret keystorePassword) { 104 | Jenkins.get().checkPermission(Jenkins.READ); 105 | if (keystorePassword != null && Util.fixEmptyAndTrim(keystorePassword.getPlainText()) == null) { 106 | return FormValidation.error("Keystore's Password must be provided."); 107 | } 108 | // Perform any additional validation here 109 | return FormValidation.ok(); 110 | } 111 | 112 | @POST 113 | public FormValidation doCheckKeystoreAlias(@QueryParameter Secret keystoreAlias) { 114 | Jenkins.get().checkPermission(Jenkins.READ); 115 | if (keystoreAlias != null && Util.fixEmptyAndTrim(keystoreAlias.getPlainText()) == null) { 116 | return FormValidation.error("Keystore alias must be provided."); 117 | } 118 | // Perform any additional validation here 119 | return FormValidation.ok(); 120 | } 121 | 122 | @POST 123 | public FormValidation doCheckKeyPass(@QueryParameter Secret keyPass) { 124 | Jenkins.get().checkPermission(Jenkins.READ); 125 | if (keyPass != null && Util.fixEmptyAndTrim(keyPass.getPlainText()) == null) { 126 | return FormValidation.error("Key pass must be provided."); 127 | } 128 | // Perform any additional validation here 129 | return FormValidation.ok(); 130 | } 131 | 132 | 133 | @POST 134 | public FormValidation doCheckGoogleSignFingerPrint(@QueryParameter String googleSignFingerPrint) { 135 | Jenkins.get().checkPermission(Jenkins.READ); 136 | if (googleSignFingerPrint != null && Util.fixEmptyAndTrim(googleSignFingerPrint) == null) { 137 | return FormValidation.error("If Google Sign is enabled, fingerprint must be provided."); 138 | }else if (googleSignFingerPrint != null && googleSignFingerPrint.contains(" ")) { 139 | return FormValidation.error("White spaces are not allowed in FingerPrint."); 140 | } 141 | // Perform any additional validation here 142 | return FormValidation.ok(); 143 | } 144 | 145 | @Override 146 | public String getDisplayName() { 147 | return "Sign On Appdome"; 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/platform/android/AndroidPlatform.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure.platform.android; 2 | 3 | import hudson.DescriptorExtensionList; 4 | import hudson.Extension; 5 | import hudson.Util; 6 | import hudson.model.Descriptor; 7 | import hudson.util.FormValidation; 8 | import io.jenkins.cli.shaded.org.apache.commons.lang.StringUtils; 9 | import io.jenkins.plugins.appdome.build.to.secure.platform.Platform; 10 | import io.jenkins.plugins.appdome.build.to.secure.platform.PlatformDescriptor; 11 | import io.jenkins.plugins.appdome.build.to.secure.platform.PlatformType; 12 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method.CertificateMethod; 13 | import jenkins.model.Jenkins; 14 | import org.jenkinsci.Symbol; 15 | import org.kohsuke.stapler.DataBoundConstructor; 16 | import org.kohsuke.stapler.DataBoundSetter; 17 | import org.kohsuke.stapler.QueryParameter; 18 | import org.kohsuke.stapler.verb.POST; 19 | 20 | import static io.jenkins.plugins.appdome.build.to.secure.AppdomeBuilder.isHttpUrl; 21 | 22 | public class AndroidPlatform extends Platform { 23 | 24 | private final CertificateMethod certificateMethod; 25 | private Crashlytics crashlytics; 26 | 27 | private Datadog datadog; 28 | 29 | @DataBoundConstructor 30 | public AndroidPlatform(CertificateMethod certificateMethod) { 31 | super(PlatformType.ANDROID); 32 | this.certificateMethod = certificateMethod; 33 | } 34 | 35 | 36 | // Crashlytics 37 | public Boolean getIsCrashlytics() { 38 | return this.crashlytics != null && this.crashlytics.getFirebaseAppId() != null; 39 | } 40 | 41 | public Crashlytics getCrashlytics() { // Changed from getCrashlyticsPublisher to getCrashlytics 42 | return crashlytics; 43 | } 44 | 45 | @DataBoundSetter 46 | public void setCrashlytics(Crashlytics crashlytics) { 47 | this.crashlytics = crashlytics; 48 | } 49 | 50 | public String getFirebaseAppId() { 51 | if (this.crashlytics != null) { 52 | return this.crashlytics.getFirebaseAppId().toString(); 53 | } else 54 | return null; 55 | } 56 | 57 | // DATADOG 58 | public Boolean getIsDatadog() { 59 | return this.datadog != null && this.datadog.getDatadogKey() != null; 60 | } 61 | 62 | public Datadog getDatadog() { 63 | return datadog; 64 | } 65 | 66 | @DataBoundSetter 67 | public void setDatadog(Datadog datadog) { 68 | this.datadog = datadog; 69 | } 70 | 71 | public String getDatadogKey() { 72 | if (this.datadog != null) { 73 | return this.datadog.getDatadogKey(); 74 | } else { 75 | return null; 76 | } 77 | } 78 | 79 | 80 | public String getAppPath() { 81 | return super.getAppPath(); 82 | } 83 | 84 | @DataBoundSetter 85 | public void setAppPath(String appPath) { 86 | super.setAppPath(appPath); 87 | } 88 | 89 | public String getFusionSetId() { 90 | return super.getFusionSetId(); 91 | } 92 | 93 | @DataBoundSetter 94 | public void setFusionSetId(String fusionSetId) { 95 | super.setFusionSetId(fusionSetId); 96 | } 97 | 98 | public CertificateMethod getCertificateMethod() { 99 | return certificateMethod; 100 | } 101 | 102 | 103 | public DescriptorExtensionList> getCertificateMethodDescriptors() { 104 | return Jenkins.get().getDescriptorList(CertificateMethod.class); 105 | } 106 | 107 | @Symbol("AndroidPlatform") 108 | @Extension 109 | public static final class DescriptorImpl extends PlatformDescriptor { 110 | 111 | @POST 112 | public FormValidation doCheckAppPath(@QueryParameter String appPath) { 113 | Jenkins.get().checkPermission(Jenkins.READ); 114 | if (appPath != null && Util.fixEmptyAndTrim(appPath) == null) { 115 | return FormValidation.warning("Application path was not provided.\n " + 116 | "Or please ensure that a valid path is provided for non-protected applications" + 117 | " in the environment variable named 'APP_PATH'."); 118 | } else if (appPath != null && appPath.contains(" ")) { 119 | return FormValidation.error("White spaces are not allowed in the path."); 120 | } else if (appPath != null && isHttpUrl(appPath)) { 121 | return FormValidation.ok("Application remote url provided."); 122 | } else if (appPath != null && !(appPath.endsWith(".aab") || appPath.endsWith(".apk"))) { 123 | return FormValidation.error("Android app - File extension is not allowed," + 124 | " allowed extensions are: '.apk' or '.aab'. Please rename your file or upload a different file."); 125 | } 126 | // Perform any additional validation here 127 | return FormValidation.ok(); 128 | } 129 | 130 | @POST 131 | public FormValidation doCheckFusionSetId(@QueryParameter String fusionSetId) { 132 | Jenkins.get().checkPermission(Jenkins.READ); 133 | if (fusionSetId != null && Util.fixEmptyAndTrim(fusionSetId) == null) { 134 | return FormValidation.error("FusionSet-ID must be provided."); 135 | } else if (fusionSetId != null && fusionSetId.contains(" ")) { 136 | return FormValidation.error("White spaces are not allowed in FusionSetId."); 137 | } 138 | // Perform any additional validation here 139 | return FormValidation.ok("Chosen fusionSet: " + fusionSetId); 140 | } 141 | 142 | @POST 143 | public FormValidation doCheckFirebaseAppId(@QueryParameter String firebaseAppId) { 144 | Jenkins.get().checkPermission(Jenkins.ADMINISTER); // Ensure correct permission 145 | 146 | if (StringUtils.isBlank(firebaseAppId)) { 147 | return FormValidation.error("Firebase App ID must be provided."); 148 | } else if (firebaseAppId.contains(" ")) { 149 | return FormValidation.error("White spaces are not allowed in Firebase App ID."); 150 | } 151 | 152 | // Additional validation logic 153 | try { 154 | // Simulate additional checks here 155 | return FormValidation.ok("Chosen Firebase App ID: " + firebaseAppId); 156 | } catch (Exception e) { 157 | return FormValidation.error("An error occurred: " + e.getMessage()); 158 | } 159 | } 160 | 161 | 162 | @POST 163 | public FormValidation doCheckDatadogKey(@QueryParameter String datadogKey) { 164 | Jenkins.get().checkPermission(Jenkins.ADMINISTER); // Ensure correct permission 165 | 166 | if (StringUtils.isBlank(datadogKey)) { 167 | return FormValidation.error("Firebase App ID must be provided."); 168 | } else if (datadogKey.contains(" ")) { 169 | return FormValidation.error("White spaces are not allowed in Firebase App ID."); 170 | } 171 | 172 | // Additional validation logic 173 | try { 174 | // Simulate additional checks here 175 | return FormValidation.ok("Datadog key: " + datadogKey); 176 | } catch (Exception e) { 177 | return FormValidation.error("An error occurred: " + e.getMessage()); 178 | } 179 | } 180 | 181 | @Override 182 | public String getDisplayName() { 183 | return "Android"; 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /.github/workflows/workflow-test.yaml: -------------------------------------------------------------------------------- 1 | name: Maven CI 2 | 3 | on: 4 | # push: 5 | # branches: 6 | # - main 7 | # - testing-enviorment 8 | # tags: 9 | # - v* 10 | # pull_request: 11 | # branches: 12 | # - main 13 | # - testing-enviorment 14 | workflow_dispatch: 15 | inputs: 16 | branch: 17 | description: 'Branch or tag to run tests on' 18 | required: true 19 | default: 'main' 20 | environment: 21 | description: 'Deployment environment' 22 | required: true 23 | default: 'development' 24 | type: choice 25 | options: 26 | - development 27 | - staging 28 | - production 29 | buildParams: 30 | description: 'Build parameters in JSON format' 31 | required: true 32 | default: '{"appdomeServerBaseUrl":"default-appdomeServerBaseUrl",teamId":"default-teamId", "signOption":"default-signOption", "appFilePath":"default-appFilePath", "keystoreFilePath":"default-keystoreFilePath", "certificateFilePath":"default-certificateFilePath", "fusionSetId":"default-fusionSetId", "signFingerprint":"default-signFingerprint", "entitlementsPath":"default1,default2", "mobileProvisionProfilesPath":"default1,default2", "buildToTest":"default-buildToTest", "buildWithLogs":"false", "googlePlaySign":"false", "secondOutput":"default-secondOutput","outputName":"protected_app","firebaseAppId":"default-firebaseAppId","datadogKey":"default-datadogKey","workflowOutputLogs":"false"}' 33 | secretParams: 34 | description: 'Build parameters in JSON format' 35 | required: true 36 | default: '{"APPDOME_API_TOKEN":"default-APPDOME_API_TOKEN", "KEYSTORE_ALIAS":"default-KEYSTORE_ALIAS", "KEYSTORE_KEY_PASS":"default-KEYSTORE_KEY_PASS", "KEYSTORE_PASSWORD":"default-KEYSTORE_PASSWORD", "P12_PASSWORD":"default-P12_PASSWORD"}' 37 | logLevel: 38 | description: 'Set the log level for Maven builds' 39 | required: false 40 | default: 'info' 41 | type: choice 42 | options: 43 | - info 44 | - warning 45 | - debug 46 | dispatch_id: 47 | description: 'run_unique_id' 48 | required: true 49 | type: string 50 | 51 | jobs: 52 | build-and-test: 53 | name: run_${{ github.event.inputs.dispatch_id }} 54 | runs-on: ubuntu-latest 55 | steps: 56 | - uses: actions/checkout@v2 57 | with: 58 | ref: ${{ github.event.inputs.branch }} 59 | 60 | - name: Set up JDK 11 61 | uses: actions/setup-java@v2 62 | with: 63 | java-version: '11' 64 | distribution: 'temurin' 65 | cache: 'maven' 66 | 67 | - name: Install jq 68 | run: sudo apt-get install jq 69 | 70 | - name: Parse Secret Params 71 | id: parse_secrets 72 | run: | 73 | echo "Parsing secret parameters..." 74 | secretParams=$(echo '${{ github.event.inputs.secretParams }}' | jq -r '.') 75 | echo "APPDOME_API_TOKEN_PIPELINE=$(echo "$secretParams" | jq -r '.APPDOME_API_TOKEN')" >> $GITHUB_ENV 76 | echo "KEYSTORE_ALIAS_PIPELINE=$(echo "$secretParams" | jq -r '.KEYSTORE_ALIAS')" >> $GITHUB_ENV 77 | echo "KEYSTORE_KEY_PASS_PIPELINE=$(echo "$secretParams" | jq -r '.KEYSTORE_KEY_PASS')" >> $GITHUB_ENV 78 | echo "KEYSTORE_PASSWORD_PIPELINE=$(echo "$secretParams" | jq -r '.KEYSTORE_PASSWORD')" >> $GITHUB_ENV 79 | echo "P12_PASSWORD_PIPELINE=$(echo "$secretParams" | jq -r '.P12_PASSWORD')" >> $GITHUB_ENV 80 | 81 | 82 | - name: Download files from AWS S3 URLs 83 | id: download_files # Assign an ID to the step to use outputs 84 | run: | 85 | # Function to get file extension from URL 86 | get_file_extension() { 87 | local url=$1 88 | if [[ "$url" == *".apk"* ]]; then 89 | echo "apk" 90 | elif [[ "$url" == *".aab"* ]]; then 91 | echo "aab" 92 | elif [[ "$url" == *".ipa"* ]]; then 93 | echo "ipa" 94 | else 95 | echo "unknown" 96 | fi 97 | } 98 | 99 | # Create output directory 100 | mkdir -p /tmp/output 101 | 102 | # Extract parameters from JSON input 103 | buildParams='${{ github.event.inputs.buildParams }}' 104 | appFilePath=$(echo $buildParams | jq -r '.appFilePath') 105 | keystoreFilePath=$(echo $buildParams | jq -r '.keystoreFilePath') 106 | certificateFilePath=$(echo $buildParams | jq -r '.certificateFilePath') 107 | entitlementsPath=$(echo $buildParams | jq -r '.entitlementsPath') 108 | mobileProvisionProfilesPath=$(echo $buildParams | jq -r '.mobileProvisionProfilesPath') 109 | APPDOME_SERVER_BASE_URL=$(echo $buildParams | jq -r '.appdomeServerBaseUrl') 110 | 111 | # Initialize local file paths 112 | localAppFilePath="" 113 | localKeystoreFilePath="" 114 | localCertificateFilePath="" 115 | localEntitlementsPaths="" 116 | localMobileProvisionPaths="" 117 | 118 | # Download app file with dynamic extension 119 | if [ "$appFilePath" != "None" ]; then 120 | extension=$(get_file_extension "$appFilePath") 121 | 122 | # Set a default name based on the detected extension 123 | localFileName="/tmp/app.$extension" 124 | 125 | # Download the file 126 | curl -L -o "$localFileName" "$appFilePath" 127 | echo "Downloaded app file to $localFileName" 128 | localAppFilePath="$localFileName" 129 | else 130 | echo "App file path is 'None', skipping download." 131 | fi 132 | 133 | # Download keystore file 134 | if [ "$keystoreFilePath" != "None" ]; then 135 | curl -L -o /tmp/keystore.keystore "$keystoreFilePath" 136 | echo "Downloaded keystore file to /tmp/keystore.keystore" 137 | localKeystoreFilePath="/tmp/keystore.keystore" 138 | else 139 | echo "Keystore file path is 'None', skipping download." 140 | fi 141 | 142 | # Download certificate file 143 | if [ "$certificateFilePath" != "None" ]; then 144 | curl -L -o /tmp/certificate.p12 "$certificateFilePath" 145 | echo "Downloaded certificate file to /tmp/certificate.p12" 146 | localCertificateFilePath="/tmp/certificate.p12" 147 | else 148 | echo "Certificate file path is 'None', skipping download." 149 | fi 150 | 151 | # Download entitlementsPath files, only if they are not "None" 152 | if [ "$entitlementsPath" != "None" ]; then 153 | IFS=',' read -ra entitlementUrls <<< "$entitlementsPath" 154 | counter=1 155 | for url in "${entitlementUrls[@]}"; do 156 | fileName="/tmp/entitlement${counter}.plist" 157 | curl -L -o "$fileName" "$url" 158 | localEntitlementsPaths="$localEntitlementsPaths,$fileName" 159 | counter=$((counter+1)) 160 | done 161 | echo "Downloaded entitlement files: $localEntitlementsPaths" 162 | else 163 | echo "Entitlements path is 'None', skipping download." 164 | fi 165 | 166 | localEntitlementsPaths="${localEntitlementsPaths#,}" 167 | 168 | # Download mobileProvisionProfilesPath files, only if they are not "None" 169 | if [ "$mobileProvisionProfilesPath" != "None" ]; then 170 | IFS=',' read -ra provisionUrls <<< "$mobileProvisionProfilesPath" 171 | counter=1 172 | for url in "${provisionUrls[@]}"; do 173 | # Use a constant name for provision files 174 | fileName="/tmp/provision${counter}.mobileprovision" 175 | curl -L -o "$fileName" "$url" 176 | 177 | # If localMobileProvisionPaths is empty, don't add the comma 178 | if [ -z "$localMobileProvisionPaths" ]; then 179 | localMobileProvisionPaths="$fileName" 180 | else 181 | localMobileProvisionPaths="$localMobileProvisionPaths,$fileName" 182 | fi 183 | 184 | counter=$((counter+1)) 185 | done 186 | echo "Downloaded mobile provision profiles: $localMobileProvisionPaths" 187 | else 188 | echo "Mobile provision profiles path is 'None', skipping download." 189 | fi 190 | 191 | # Set outputs for next steps using Environment Files 192 | echo "Setting environment variables for next steps..." 193 | 194 | echo "appFilePath: $localAppFilePath" 195 | echo "appFilePath=$localAppFilePath" >> $GITHUB_ENV 196 | 197 | echo "keystoreFilePath: $localKeystoreFilePath" 198 | echo "keystoreFilePath=$localKeystoreFilePath" >> $GITHUB_ENV 199 | 200 | echo "certificateFilePath: $localCertificateFilePath" 201 | echo "certificateFilePath=$localCertificateFilePath" >> $GITHUB_ENV 202 | echo "mobileProvisionProfilesPath: $localMobileProvisionPaths" 203 | echo "mobileProvisionProfilesPath=$localMobileProvisionPaths" >> $GITHUB_ENV 204 | 205 | echo "entitlementsPath: $localEntitlementsPaths" 206 | echo "entitlementsPath=$localEntitlementsPaths" >> $GITHUB_ENV 207 | 208 | echo "APPDOME_SERVER_BASE_URL: $APPDOME_SERVER_BASE_URL" 209 | echo "APPDOME_SERVER_BASE_URL=$APPDOME_SERVER_BASE_URL" >> $GITHUB_ENV 210 | 211 | - name: Build and Test with Maven 212 | run: | 213 | # Extract other parameters from JSON input 214 | buildParams='${{ github.event.inputs.buildParams }}' 215 | appdomeServerBaseUrl=$(echo $buildParams | jq -r '.appdomeServerBaseUrl') 216 | teamId=$(echo $buildParams | jq -r '.teamId') 217 | signOption=$(echo $buildParams | jq -r '.signOption') 218 | fusionSetId=$(echo $buildParams | jq -r '.fusionSetId') 219 | firebaseAppId=$(echo $buildParams | jq -r '.firebaseAppId') 220 | workflowOutputLogs=$(echo $buildParams | jq -r '.workflowOutputLogs') 221 | datadogKey=$(echo $buildParams | jq -r '.datadogKey') 222 | signFingerprint=$(echo $buildParams | jq -r '.signFingerprint') 223 | buildToTest=$(echo $buildParams | jq -r '.buildToTest') 224 | buildWithLogs=$(echo $buildParams | jq -r '.buildWithLogs') 225 | googlePlaySign=$(echo $buildParams | jq -r '.googlePlaySign') 226 | secondOutput=$(echo $buildParams | jq -r '.secondOutput') 227 | outputName=$(echo $buildParams | jq -r '.outputName') # Fixed this line 228 | # Use the environment variables directly 229 | appFilePath="$appFilePath" 230 | keystoreFilePath="$keystoreFilePath" 231 | certificateFilePath="$certificateFilePath" 232 | entitlementsPath="$entitlementsPath" 233 | mobileProvisionProfilesPath="$mobileProvisionProfilesPath" 234 | 235 | # Echo each parameter to verify parsing worked correctly 236 | echo "Parsed Parameters:" 237 | echo "appdomeServerBaseUrl: $appdomeServerBaseUrl" 238 | echo "teamId: $teamId" 239 | echo "signOption: $signOption" 240 | echo "appFilePath: $appFilePath" 241 | echo "keystoreFilePath: $keystoreFilePath" 242 | echo "certificateFilePath: $certificateFilePath" 243 | echo "fusionSetId: $fusionSetId" 244 | echo "firebaseAppId: $firebaseAppId" 245 | echo "datadogKey: $datadogKey" 246 | echo "workflowOutputLogs: $workflowOutputLogs" 247 | echo "signFingerprint: $signFingerprint" 248 | echo "entitlementsPath: $entitlementsPath" 249 | echo "mobileProvisionProfilesPath: $mobileProvisionProfilesPath" 250 | echo "buildToTest: $buildToTest" 251 | echo "buildWithLogs: $buildWithLogs" 252 | echo "googlePlaySign: $googlePlaySign" 253 | echo "secondOutput: $secondOutput" 254 | echo "outputName: $outputName" 255 | 256 | # Run the Maven command with extracted parameters 257 | mvn -V --color always -ntp -B -Djenkins.test.timeout=700 -Dsurefire.printSummary=true -Dsurefire.useFile=false clean verify \ 258 | -Dtest=PipelineTest#workFlowTest \ 259 | -DteamId="$teamId" \ 260 | -DsignOption="$signOption" \ 261 | -DappFilePath="$appFilePath" \ 262 | -DkeystoreFilePath="$keystoreFilePath" \ 263 | -DcertificateFilePath="$certificateFilePath" \ 264 | -DfusionSetId="$fusionSetId" \ 265 | -DsignFingerprint="$signFingerprint" \ 266 | -DentitlementsPath="$entitlementsPath" \ 267 | -DmobileProvisionProfilesPath="$mobileProvisionProfilesPath" \ 268 | -DbuildToTest="$buildToTest" \ 269 | -DbuildWithLogs="$buildWithLogs" \ 270 | -DgooglePlaySign="$googlePlaySign" \ 271 | -DsecondOutput="$secondOutput" \ 272 | -DoutputName="$outputName" \ 273 | -DfirebaseAppId="$firebaseAppId" \ 274 | -DdatadogKey="$datadogKey" \ 275 | -DworkflowOutputLogs="$workflowOutputLogs" \ 276 | -Denvironment="${{ github.event.inputs.environment }}" \ 277 | package 278 | env: 279 | APPDOME_SERVER_BASE_URL: ${{ env.APPDOME_SERVER_BASE_URL }} 280 | APPDOME_API_TOKEN: ${{ env.APPDOME_API_TOKEN_PIPELINE }} 281 | KEYSTORE_ALIAS: ${{ env.KEYSTORE_ALIAS_PIPELINE }} 282 | KEYSTORE_KEY_PASS: ${{ env.KEYSTORE_KEY_PASS_PIPELINE }} 283 | KEYSTORE_PASSWORD: ${{ env.KEYSTORE_PASSWORD_PIPELINE }} 284 | P12_PASSWORD: ${{ env.P12_PASSWORD_PIPELINE }} 285 | 286 | # Step to upload the downloaded files as artifacts (using wildcard) 287 | - name: Upload artifacts 288 | uses: actions/upload-artifact@v4 289 | with: 290 | name: downloaded-files-artifact 291 | path: /home/runner/work/appdome-build-2secure-plugin/appdome-build-2secure-plugin/tmp/output/* 292 | if-no-files-found: warn -------------------------------------------------------------------------------- /src/test/java/io/jenkins/plugins/appdome/build/to/secure/AppdomeBuilderTest.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure; 2 | 3 | import hudson.EnvVars; 4 | import hudson.FilePath; 5 | import hudson.model.FreeStyleBuild; 6 | import hudson.model.FreeStyleProject; 7 | import hudson.model.Result; 8 | import hudson.slaves.EnvironmentVariablesNodeProperty; 9 | import hudson.util.Secret; 10 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.AndroidPlatform; 11 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method.AutoDevSign; 12 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method.AutoGoogleSign; 13 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method.AutoSign; 14 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method.PrivateSign; 15 | import io.jenkins.plugins.appdome.build.to.secure.platform.ios.IosPlatform; 16 | import org.junit.Before; 17 | import org.junit.Rule; 18 | import org.junit.Test; 19 | import org.jvnet.hudson.test.JenkinsRule; 20 | 21 | import java.io.File; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.logging.Logger; 25 | 26 | import static org.junit.Assert.assertTrue; 27 | 28 | public class AppdomeBuilderTest { 29 | 30 | @Rule 31 | public JenkinsRule jenkins = new JenkinsRule(); 32 | private static final String PATH_TO_FILES = "downloaded_files/"; 33 | final String androidFusionSet = "8c693120-7cab-11ee-8275-c54d0e1c9b7a"; 34 | final String iosFusionSet = "13ded0a0-7cad-11ee-b531-29c8c84aedcc"; 35 | final String fingerprint = "8DF593C1B6EAA6EADADCE36831FE82B08CAC8D74"; 36 | private String token; 37 | final String teamId = "46002310-7cab-11ee-bfde-d76f94716e7a"; 38 | private String aabAppPath; 39 | private String apkApp1Path; 40 | private String apkApp2Path; 41 | private String certificateFile1Path; 42 | private String certificateFile2Path; 43 | private String ipa1EntitlementsPath; 44 | private String ipa1MobileProvisioningPath; 45 | private String ipa2Entitlements1Path; 46 | private String ipa2Entitlements2Path; 47 | private String ipa2Entitlements3Path; 48 | private String ipa2MobileProvisioning1Path; 49 | private String ipa2MobileProvisioning2Path; 50 | private String ipa2MobileProvisioning3Path; 51 | private String ipaApp1Path; 52 | private String ipaApp2Path; 53 | private String ipaApp3Path; 54 | private String keystoreFilePath; 55 | 56 | private String keystoreAlias; 57 | private String keystoreKeyPass; 58 | private String keystorePassword; 59 | private String p12Password; 60 | 61 | @Before 62 | public void setUp() throws Exception { 63 | this.token = System.getenv("APPDOME_API_TOKEN"); 64 | this.keystoreAlias = System.getenv("KEYSTORE_ALIAS"); 65 | this.keystoreKeyPass = System.getenv("KEYSTORE_KEY_PASS"); 66 | this.keystorePassword = System.getenv("KEYSTORE_PASSWORD"); 67 | this.p12Password = System.getenv("P12_PASSWORD"); 68 | 69 | 70 | this. 71 | setCommonEnvironmentVariables(); 72 | setFiles(); 73 | } 74 | 75 | private void setFiles() { 76 | this.aabAppPath = buildFilePath("aab_app.aab"); 77 | this.apkApp1Path = buildFilePath("apk_app_1.apk"); 78 | this.apkApp2Path = buildFilePath("apk_app_2.apk"); 79 | this.certificateFile1Path = buildFilePath("certificate_file1.p12"); 80 | this.certificateFile2Path = buildFilePath("certificate_file2.p12"); 81 | this.ipa1EntitlementsPath = buildFilePath("ipa_1_entitlements.plist"); 82 | this.ipa1MobileProvisioningPath = buildFilePath("ipa_1_mobile_provisioning.mobileprovision"); 83 | this.ipa2Entitlements1Path = buildFilePath("ipa_2_entitlements_1.plist"); 84 | this.ipa2Entitlements2Path = buildFilePath("ipa_2_entitlements_2.plist"); 85 | this.ipa2Entitlements3Path = buildFilePath("ipa_2_entitlements_3.plist"); 86 | this.ipa2MobileProvisioning1Path = buildFilePath("ipa_2_mobile_provisioning_1.mobileprovision"); 87 | this.ipa2MobileProvisioning2Path = buildFilePath("ipa_2_mobile_provisioning_2.mobileprovision"); 88 | this.ipa2MobileProvisioning3Path = buildFilePath("ipa_2_mobile_provisioning_3.mobileprovision"); 89 | this.ipaApp1Path = buildFilePath("ipa_app_1.ipa"); 90 | this.ipaApp2Path = buildFilePath("ipa_app_2.ipa"); 91 | this.ipaApp3Path = buildFilePath("ipa_app_3.ipa"); 92 | this.keystoreFilePath = buildFilePath("keystore_file.keystore"); 93 | 94 | 95 | } 96 | 97 | 98 | private String buildFilePath(String filename) { 99 | File file = new File(PATH_TO_FILES, filename); 100 | System.out.println(filename + " : " + file.getAbsolutePath().toString()); 101 | if (!file.exists()) { 102 | throw new IllegalStateException("Required file not found: " + file.getAbsolutePath()); 103 | } 104 | return file.getAbsolutePath(); 105 | } 106 | 107 | private void setCommonEnvironmentVariables() { 108 | EnvironmentVariablesNodeProperty prop = new EnvironmentVariablesNodeProperty(); 109 | EnvVars env = prop.getEnvVars(); 110 | env.put("APPDOME_SERVER_BASE_URL", "https://qamaster.dev.appdome.com"); 111 | jenkins.jenkins.getGlobalNodeProperties().add(prop); 112 | } 113 | 114 | @Test 115 | public void testApkAndroidPrivateSignBuild() throws Exception { 116 | FreeStyleProject project = jenkins.createFreeStyleProject(); 117 | // Create configuration objects 118 | PrivateSign privateSign = new PrivateSign(fingerprint); 119 | privateSign.setGoogleSigning(false); 120 | AndroidPlatform androidPlatform = new AndroidPlatform(privateSign); 121 | androidPlatform.setFusionSetId(androidFusionSet); 122 | androidPlatform.setAppPath(this.apkApp1Path); 123 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, androidPlatform, null,null); 124 | 125 | appdomeBuilder.setBuildToTest(null); 126 | appdomeBuilder.setBuildWithLogs(true); 127 | 128 | project.getBuildersList().add(appdomeBuilder); 129 | checkingResults(project, false); 130 | } 131 | 132 | @Test 133 | public void testAAbAndroidPrivateSignBuild() throws Exception { 134 | FreeStyleProject project = jenkins.createFreeStyleProject(); 135 | // Create configuration objects 136 | PrivateSign privateSign = new PrivateSign(fingerprint); 137 | privateSign.setGoogleSigning(false); 138 | AndroidPlatform androidPlatform = new AndroidPlatform(privateSign); 139 | androidPlatform.setFusionSetId(androidFusionSet); 140 | androidPlatform.setAppPath(this.aabAppPath); 141 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, androidPlatform, null,null); 142 | 143 | appdomeBuilder.setBuildToTest(null); 144 | appdomeBuilder.setBuildWithLogs(true); 145 | 146 | project.getBuildersList().add(appdomeBuilder); 147 | checkingResults(project, false); 148 | 149 | } 150 | 151 | @Test 152 | public void testApkAndroidAutoDevSignBuild() throws Exception { 153 | FreeStyleProject project = jenkins.createFreeStyleProject(); 154 | // Create configuration objects 155 | AutoDevSign autoDevSign = new AutoDevSign(fingerprint); 156 | autoDevSign.setGoogleSigning(true); 157 | AndroidPlatform androidPlatform = new AndroidPlatform(autoDevSign); 158 | androidPlatform.setFusionSetId(androidFusionSet); 159 | androidPlatform.setAppPath(this.apkApp2Path); 160 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, androidPlatform, null,null); 161 | BuildToTest buildToTest = new BuildToTest(VendorManager.Vendor.SAUCELABS.name()); 162 | appdomeBuilder.setBuildToTest(buildToTest); 163 | appdomeBuilder.setBuildWithLogs(false); 164 | 165 | project.getBuildersList().add(appdomeBuilder); 166 | checkingResults(project, false); 167 | } 168 | 169 | @Test 170 | public void testAabAndroidAutoDevSignBuild() throws Exception { 171 | FreeStyleProject project = jenkins.createFreeStyleProject(); 172 | // Create configuration objects 173 | AutoDevSign autoDevSign = new AutoDevSign(fingerprint); 174 | autoDevSign.setGoogleSigning(true); 175 | AndroidPlatform androidPlatform = new AndroidPlatform(autoDevSign); 176 | androidPlatform.setFusionSetId(androidFusionSet); 177 | androidPlatform.setAppPath(this.aabAppPath); 178 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, androidPlatform, null,null); 179 | BuildToTest buildToTest = new BuildToTest(VendorManager.Vendor.SAUCELABS.name()); 180 | appdomeBuilder.setBuildToTest(buildToTest); 181 | appdomeBuilder.setBuildWithLogs(false); 182 | 183 | project.getBuildersList().add(appdomeBuilder); 184 | checkingResults(project, false); 185 | } 186 | 187 | @Test 188 | public void testApkAndroidAutoSignBuild() throws Exception { 189 | FreeStyleProject project = jenkins.createFreeStyleProject(); 190 | 191 | // Create configuration objects 192 | 193 | AutoSign autoSign = 194 | new AutoSign(this.keystoreFilePath, 195 | Secret.fromString(this.keystorePassword), Secret.fromString(this.keystoreAlias), 196 | Secret.fromString(keystoreKeyPass), null); 197 | 198 | AndroidPlatform androidPlatform = new AndroidPlatform(autoSign); 199 | androidPlatform.setFusionSetId(androidFusionSet); 200 | androidPlatform.setAppPath(apkApp1Path); 201 | 202 | 203 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, 204 | androidPlatform, null,null); 205 | 206 | appdomeBuilder.setBuildToTest(null); 207 | 208 | project.getBuildersList().add(appdomeBuilder); 209 | checkingResults(project, false); 210 | } 211 | 212 | @Test 213 | public void testAabAndroidAutoSignBuild() throws Exception { 214 | FreeStyleProject project = jenkins.createFreeStyleProject(); 215 | 216 | // Create configuration objects 217 | 218 | AutoSign autoSign = 219 | new AutoSign(this.keystoreFilePath, 220 | Secret.fromString(this.keystorePassword), Secret.fromString(this.keystoreAlias), 221 | Secret.fromString(keystoreKeyPass), null); 222 | 223 | AndroidPlatform androidPlatform = new AndroidPlatform(autoSign); 224 | androidPlatform.setFusionSetId(androidFusionSet); 225 | androidPlatform.setAppPath(aabAppPath); 226 | 227 | 228 | File secondOutputLocation = new File("/home/runner/work/appdome-build-2secure-plugin/appdome-build-2secure-plugin/output/"); 229 | secondOutputLocation.mkdirs(); 230 | String secondOutputPath = secondOutputLocation.getPath() + File.separator + "second_output.apk"; 231 | 232 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, 233 | androidPlatform, new StringWarp(secondOutputPath),null); 234 | 235 | appdomeBuilder.setBuildToTest(null); 236 | 237 | 238 | project.getBuildersList().add(appdomeBuilder); 239 | checkingResults(project, false); 240 | } 241 | 242 | 243 | @Test 244 | public void testIosAutoSignBuild() throws Exception { 245 | FreeStyleProject project = jenkins.createFreeStyleProject(); 246 | List provision_profiles = new ArrayList<>(); 247 | provision_profiles.add(new StringWarp(ipa2MobileProvisioning1Path)); 248 | provision_profiles.add(new StringWarp(ipa2MobileProvisioning2Path)); 249 | provision_profiles.add(new StringWarp(ipa2MobileProvisioning3Path)); 250 | List entitlements = new ArrayList<>(); 251 | entitlements.add(new StringWarp(ipa2Entitlements1Path)); 252 | entitlements.add(new StringWarp(ipa2Entitlements2Path)); 253 | entitlements.add(new StringWarp(ipa2Entitlements3Path)); 254 | 255 | // Create configuration objects 256 | io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method.AutoSign autoSign 257 | = new io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method. 258 | AutoSign(this.certificateFile2Path, Secret.fromString(this.p12Password), provision_profiles, entitlements); 259 | 260 | IosPlatform iosPlatform = new IosPlatform(autoSign); 261 | iosPlatform.setFusionSetId(iosFusionSet); 262 | iosPlatform.setAppPath(this.ipaApp2Path); 263 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, iosPlatform, null,null); 264 | BuildToTest buildToTest = new BuildToTest(VendorManager.Vendor.SAUCELABS.name()); 265 | appdomeBuilder.setBuildToTest(buildToTest); 266 | appdomeBuilder.setBuildWithLogs(true); 267 | 268 | project.getBuildersList().add(appdomeBuilder); 269 | checkingResults(project, false); 270 | } 271 | 272 | private void checkingResults(FreeStyleProject project, boolean isSecondOutput) throws Exception { 273 | FreeStyleBuild build = jenkins.buildAndAssertSuccess(project); 274 | String consoleOutput = build.getLog(); 275 | FilePath workspace = build.getWorkspace(); 276 | 277 | // Check that the file exists in the workspace 278 | FilePath output_location = workspace.child("output"); 279 | assertTrue("output_location should exist", output_location.exists()); 280 | if (isSecondOutput) { 281 | jenkins.assertLogContains("Download Second Output", build); 282 | } 283 | System.out.println("build console output = " + consoleOutput); 284 | System.out.println("build status = " + build.getResult().toString()); 285 | jenkins.assertBuildStatus(Result.SUCCESS, build); // Check build status 286 | } 287 | 288 | @Test 289 | public void testIosPrivateSignBuild() throws Exception { 290 | FreeStyleProject project = jenkins.createFreeStyleProject(); 291 | List provision_profiles = new ArrayList<>(); 292 | provision_profiles.add(new StringWarp(ipa1MobileProvisioningPath)); 293 | 294 | 295 | // Create configuration objects 296 | io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method.PrivateSign privateSign 297 | = new io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method. 298 | PrivateSign(provision_profiles); 299 | IosPlatform iosPlatform = new IosPlatform(privateSign); 300 | iosPlatform.setFusionSetId(iosFusionSet); 301 | iosPlatform.setAppPath(this.ipaApp1Path); 302 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, iosPlatform, null,null); 303 | BuildToTest buildToTest = new BuildToTest(VendorManager.Vendor.BROWSERSTACK.name()); 304 | appdomeBuilder.setBuildToTest(buildToTest); 305 | appdomeBuilder.setBuildWithLogs(false); 306 | 307 | project.getBuildersList().add(appdomeBuilder); 308 | checkingResults(project, false); 309 | 310 | } 311 | 312 | @Test 313 | public void testIosAutoDevPrivateSignBuild() throws Exception { 314 | FreeStyleProject project = jenkins.createFreeStyleProject(); 315 | List provision_profiles = new ArrayList<>(); 316 | provision_profiles.add(new StringWarp(ipa1MobileProvisioningPath)); 317 | List entitlements = new ArrayList<>(); 318 | entitlements.add(new StringWarp(this.ipa1EntitlementsPath)); 319 | 320 | // Create configuration objects 321 | io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method.AutoDevSign 322 | autoDevSign = new io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method. 323 | AutoDevSign(provision_profiles, entitlements); 324 | IosPlatform iosPlatform = new IosPlatform(autoDevSign); 325 | iosPlatform.setFusionSetId(iosFusionSet); 326 | iosPlatform.setAppPath(this.ipaApp3Path); 327 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, iosPlatform, null,null); 328 | 329 | project.getBuildersList().add(appdomeBuilder); 330 | checkingResults(project, false); 331 | 332 | } 333 | 334 | 335 | } -------------------------------------------------------------------------------- /src/test/java/io/jenkins/plugins/appdome/build/to/secure/Tests.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure; 2 | 3 | import hudson.FilePath; 4 | import hudson.model.FreeStyleBuild; 5 | import hudson.model.FreeStyleProject; 6 | import hudson.model.Result; 7 | import hudson.util.Secret; 8 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.AndroidPlatform; 9 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.Crashlytics; 10 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.Datadog; 11 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method.AutoDevSign; 12 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method.AutoGoogleSign; 13 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method.AutoSign; 14 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.certificate.method.PrivateSign; 15 | import io.jenkins.plugins.appdome.build.to.secure.platform.ios.IosPlatform; 16 | import org.junit.Test; 17 | import org.jvnet.hudson.test.JenkinsRule; 18 | 19 | import java.io.File; 20 | import java.util.List; 21 | import java.util.logging.Logger; 22 | 23 | import static org.junit.Assert.assertTrue; 24 | 25 | public class Tests { 26 | 27 | public static final String PLUGIN_TMP_OUTPUT = "/home/runner/work/appdome-build-2secure-plugin/appdome-build-2secure-plugin/tmp/output/"; 28 | 29 | public static void testAndroidAutoSignBuild(JenkinsRule jenkins, String token, String teamId, String appPath, String fusionSet, String keystoreFilePath, 30 | String keystorePassword, String keystoreAlias, String keystoreKeyPass, 31 | String fingerprint, StringWarp secondOutput, BuildToTest buildToTest, 32 | Boolean buildWithLogs, String outputName, Crashlytics crashlytics, Datadog datadog, Boolean workflowOutputLogs, Logger logger) throws Exception { 33 | logger.info("Inside testAndroidAutoSignBuild"); 34 | String output_location = PLUGIN_TMP_OUTPUT + outputName + "." + getFileExtension(appPath); 35 | 36 | FreeStyleProject project = jenkins.createFreeStyleProject(); 37 | // Create configuration objects 38 | AutoGoogleSign autoGoogleSign = null; 39 | if (fingerprint != null) { 40 | autoGoogleSign = new AutoGoogleSign(fingerprint); 41 | } 42 | Boolean isSecondOutput = false; 43 | if (secondOutput != null) { 44 | isSecondOutput = true; 45 | } 46 | 47 | 48 | AutoSign autoSign = 49 | new AutoSign(keystoreFilePath, 50 | Secret.fromString(keystorePassword), Secret.fromString(keystoreAlias), 51 | Secret.fromString(keystoreKeyPass), autoGoogleSign); 52 | 53 | AndroidPlatform androidPlatform = new AndroidPlatform(autoSign); 54 | androidPlatform.setFusionSetId(fusionSet); 55 | androidPlatform.setAppPath(appPath); 56 | 57 | if (crashlytics != null) { 58 | androidPlatform.setCrashlytics(new Crashlytics(crashlytics.getFirebaseAppId())); 59 | } 60 | 61 | if (datadog != null) { 62 | androidPlatform.setDatadog(new Datadog(datadog.getDatadogKey())); 63 | } 64 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, 65 | androidPlatform, secondOutput, null); 66 | 67 | appdomeBuilder.setBuildToTest(buildToTest); 68 | appdomeBuilder.setBuildWithLogs(buildWithLogs); 69 | appdomeBuilder.setWorkflowOutputLogs(workflowOutputLogs); 70 | appdomeBuilder.setOutputLocation(output_location); 71 | 72 | 73 | logger.info("The protected app will be saved to: " + output_location); 74 | project.getBuildersList().add(appdomeBuilder); 75 | checkingResults(project, isSecondOutput, jenkins, logger); 76 | } 77 | 78 | public static void testAndroidPrivateSignBuild(JenkinsRule jenkins, String token, String teamId, String appPath, String fusionSet, String fingerprint, 79 | StringWarp secondOutput, BuildToTest buildToTest, Boolean buildWithLogs, 80 | Boolean googleSigning, String outputName, Crashlytics crashlytics, Datadog datadog, Boolean workflowOutputLogs, Logger logger) throws Exception { 81 | logger.info("Inside testAndroidPrivateSignBuild"); 82 | String output_location = PLUGIN_TMP_OUTPUT + outputName + "." + getFileExtension(appPath); 83 | 84 | FreeStyleProject project = jenkins.createFreeStyleProject(); 85 | Boolean isSecondOutput = false; 86 | if (secondOutput != null) { 87 | isSecondOutput = true; 88 | } 89 | // Create configuration objects 90 | PrivateSign privateSign = new PrivateSign(fingerprint); 91 | privateSign.setGoogleSigning(googleSigning); 92 | AndroidPlatform androidPlatform = new AndroidPlatform(privateSign); 93 | androidPlatform.setFusionSetId(fusionSet); 94 | androidPlatform.setAppPath(appPath); 95 | if (crashlytics != null) { 96 | androidPlatform.setCrashlytics(new Crashlytics(crashlytics.getFirebaseAppId())); 97 | } 98 | if (androidPlatform.getDatadog() != null) { 99 | androidPlatform.setDatadog(new Datadog(androidPlatform.getDatadog().getDatadogKey())); 100 | } 101 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, androidPlatform, secondOutput, null); 102 | appdomeBuilder.setBuildWithLogs(buildWithLogs); 103 | appdomeBuilder.setWorkflowOutputLogs(workflowOutputLogs); 104 | appdomeBuilder.setBuildToTest(buildToTest); 105 | appdomeBuilder.setOutputLocation(output_location); 106 | logger.info("The protected app will be saved to: " + output_location); 107 | project.getBuildersList().add(appdomeBuilder); 108 | checkingResults(project, isSecondOutput, jenkins, logger); 109 | } 110 | 111 | public static void testAndroidAutoDevSignBuild(JenkinsRule jenkins, String token, String teamId, String appPath, String fusionSet, String fingerprint, 112 | StringWarp secondOutput, BuildToTest buildToTest, Boolean buildWithLogs, 113 | Boolean googleSigning, String outputName, Crashlytics crashlytics, Datadog datadog, Boolean workflowOutputLogs, Logger logger) throws Exception { 114 | logger.info("Inside testAndroidAutoDevSignBuild"); 115 | String output_location = PLUGIN_TMP_OUTPUT + outputName + ".sh"; 116 | 117 | FreeStyleProject project = jenkins.createFreeStyleProject(); 118 | // Create configuration objects 119 | Boolean isSecondOutput = false; 120 | if (secondOutput != null) { 121 | isSecondOutput = true; 122 | } 123 | 124 | AutoDevSign autoDevSign = new AutoDevSign(fingerprint); 125 | if (googleSigning == null) { 126 | googleSigning = false; 127 | } else if (googleSigning) { 128 | googleSigning = true; 129 | } 130 | 131 | autoDevSign.setGoogleSigning(googleSigning); 132 | AndroidPlatform androidPlatform = new AndroidPlatform(autoDevSign); 133 | androidPlatform.setFusionSetId(fusionSet); 134 | androidPlatform.setAppPath(appPath); 135 | if (crashlytics != null) { 136 | androidPlatform.setCrashlytics(new Crashlytics(crashlytics.getFirebaseAppId())); 137 | } 138 | if (datadog != null) { 139 | androidPlatform.setDatadog(new Datadog(datadog.getDatadogKey())); 140 | } 141 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, androidPlatform, secondOutput, null); 142 | appdomeBuilder.setBuildToTest(buildToTest); 143 | appdomeBuilder.setBuildWithLogs(buildWithLogs); 144 | appdomeBuilder.setWorkflowOutputLogs(workflowOutputLogs); 145 | appdomeBuilder.setOutputLocation(output_location); 146 | logger.info("The protected app will be saved to: " + output_location); 147 | project.getBuildersList().add(appdomeBuilder); 148 | checkingResults(project, isSecondOutput, jenkins, logger); 149 | } 150 | 151 | 152 | public static void testIosAutoSignBuild(JenkinsRule jenkins, String token, String teamId, String appPath, String fusionSet, 153 | String certificateFilePath, String certificatePassword, List 154 | provisionProfiles, List entitlements, BuildToTest buildToTest, 155 | Boolean buildWithLogs, String outputName, Boolean workflowOutputLogs, Logger logger) throws Exception { 156 | logger.info("Inside testIosAutoSignBuild"); 157 | String output_location = PLUGIN_TMP_OUTPUT + outputName + ".ipa"; 158 | FreeStyleProject project = jenkins.createFreeStyleProject(); 159 | // Create configuration objects 160 | io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method.AutoSign autoSign 161 | = new io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method. 162 | AutoSign(certificateFilePath, Secret.fromString(certificatePassword), provisionProfiles, entitlements); 163 | 164 | IosPlatform iosPlatform = new IosPlatform(autoSign); 165 | iosPlatform.setFusionSetId(fusionSet); 166 | iosPlatform.setAppPath(appPath); 167 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, iosPlatform, null, null); 168 | appdomeBuilder.setBuildToTest(buildToTest); 169 | appdomeBuilder.setBuildWithLogs(buildWithLogs); 170 | appdomeBuilder.setWorkflowOutputLogs(workflowOutputLogs); 171 | appdomeBuilder.setOutputLocation(output_location); 172 | logger.info("The protected app will be saved to: " + output_location); 173 | project.getBuildersList().add(appdomeBuilder); 174 | checkingResults(project, false, jenkins, logger); 175 | } 176 | 177 | 178 | public static void testIosPrivateSignBuild(JenkinsRule jenkins, String token, String teamId, String appPath, String fusionSet, 179 | List provisionProfiles, BuildToTest buildToTest, 180 | Boolean buildWithLogs, String outputName, Boolean workflowOutputLogs, Logger logger) throws Exception { 181 | logger.info("Inside testIosPrivateSignBuild"); 182 | String output_location = PLUGIN_TMP_OUTPUT + outputName + ".ipa"; 183 | 184 | FreeStyleProject project = jenkins.createFreeStyleProject(); 185 | 186 | // Create configuration objects 187 | io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method.PrivateSign privateSign 188 | = new io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method. 189 | PrivateSign(provisionProfiles); 190 | IosPlatform iosPlatform = new IosPlatform(privateSign); 191 | iosPlatform.setFusionSetId(fusionSet); 192 | iosPlatform.setAppPath(appPath); 193 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, iosPlatform, null, null); 194 | appdomeBuilder.setBuildToTest(buildToTest); 195 | appdomeBuilder.setBuildWithLogs(buildWithLogs); 196 | appdomeBuilder.setWorkflowOutputLogs(workflowOutputLogs); 197 | appdomeBuilder.setOutputLocation(output_location); 198 | logger.info("The protected app will be saved to: " + output_location); 199 | 200 | project.getBuildersList().add(appdomeBuilder); 201 | logger.info("Printing provision profiles:"); 202 | // Loop through each item in the provisionProfiles list 203 | for (StringWarp provisionProfile : provisionProfiles) { 204 | String filePath = provisionProfile.getProvisioningProfiles(); // Assuming StringWarp has a method to get the string 205 | 206 | // Log the provision profile being checked 207 | logger.info("Checking provision profile: " + filePath); 208 | 209 | // Create a File object for the profile 210 | File file = new File(filePath); 211 | 212 | // Check if the file exists and log the result 213 | if (file.exists()) { 214 | // Log the size of the file 215 | long fileSizeInBytes = file.length(); 216 | logger.info("Provision profile exists. Size: " + fileSizeInBytes + " bytes"); 217 | } else { 218 | logger.warning("Provision profile does not exist: " + filePath); 219 | } 220 | } 221 | checkingResults(project, false, jenkins, logger); 222 | 223 | } 224 | 225 | 226 | public static void testIosAutoDevPrivateSignBuild(JenkinsRule jenkins, String token, String teamId, String appPath, String fusionSet, 227 | List provisionProfiles, List entitlements, 228 | BuildToTest buildToTest, Boolean buildWithLogs, String outputName, Boolean workflowOutputLogs, Logger logger) throws Exception { 229 | logger.info("Inside testIosAutoDevPrivateSignBuild"); 230 | String output_location = PLUGIN_TMP_OUTPUT + outputName + ".sh"; 231 | 232 | FreeStyleProject project = jenkins.createFreeStyleProject(); 233 | 234 | // Create configuration objects 235 | io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method.AutoDevSign 236 | autoDevSign = new io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method. 237 | AutoDevSign(provisionProfiles, entitlements); 238 | IosPlatform iosPlatform = new IosPlatform(autoDevSign); 239 | iosPlatform.setFusionSetId(fusionSet); 240 | iosPlatform.setAppPath(appPath); 241 | AppdomeBuilder appdomeBuilder = new AppdomeBuilder(Secret.fromString(token), teamId, iosPlatform, null, null); 242 | appdomeBuilder.setBuildToTest(buildToTest); 243 | appdomeBuilder.setWorkflowOutputLogs(workflowOutputLogs); 244 | appdomeBuilder.setBuildWithLogs(buildWithLogs); 245 | appdomeBuilder.setOutputLocation(output_location); 246 | logger.info("The protected app will be saved to: " + output_location); 247 | project.getBuildersList().add(appdomeBuilder); 248 | checkingResults(project, false, jenkins, logger); 249 | 250 | } 251 | 252 | 253 | private static void checkingResults(FreeStyleProject project, boolean isSecondOutput, JenkinsRule jenkins, Logger logger) throws Exception { 254 | FreeStyleBuild build = jenkins.buildAndAssertSuccess(project); 255 | String consoleOutput = build.getLog(); 256 | 257 | // Get the workspace of the current build 258 | FilePath workspace = build.getWorkspace(); 259 | if (workspace == null) { 260 | throw new IllegalStateException("Workspace not found for the build"); 261 | } 262 | 263 | // Print the current working directory (pwd) 264 | String currentWorkingDirectory = System.getProperty("user.dir"); 265 | System.out.println("Current Working Directory (pwd): " + currentWorkingDirectory); 266 | 267 | 268 | // Define the output location inside /tmp/output/ 269 | FilePath output_location = new FilePath(new File(PLUGIN_TMP_OUTPUT)); 270 | 271 | // Print the path to PLUGIN_TMP_OUTPUT "/home/runner/work/appdome-build-2secure-plugin/appdome-build-2secure-plugin/tmp/output/" 272 | System.out.println("Output Location Path: " + output_location.getRemote()); 273 | 274 | // Check if the directory exists and print its contents 275 | if (output_location.exists()) { 276 | System.out.println("\"/home/runner/work/appdome-build-2secure-plugin/appdome-build-2secure-plugin/tmp/output/\" exists. Listing files:"); 277 | for (FilePath file : output_location.list()) { 278 | System.out.println(file.getName()); 279 | } 280 | } else { 281 | System.out.println("\"/home/runner/work/appdome-build-2secure-plugin/appdome-build-2secure-plugin/tmp/output/\" does not exist."); 282 | } 283 | 284 | // Further assertions and logging 285 | if (isSecondOutput) { 286 | jenkins.assertLogContains("Download Second Output", build); 287 | } 288 | 289 | System.out.println("build console output = " + consoleOutput); 290 | System.out.println("build status = " + build.getResult().toString()); 291 | jenkins.assertBuildStatus(Result.SUCCESS, build); 292 | } 293 | 294 | private static String getFileExtension(String fileName) { 295 | if (fileName == null || fileName.isEmpty()) { 296 | return null; 297 | } 298 | 299 | int dotIndex = fileName.lastIndexOf('.'); 300 | if (dotIndex >= 0) { // Make sure there is a '.' in the filename 301 | return fileName.substring(dotIndex + 1).toLowerCase(); 302 | } else { 303 | return null; // No extension found 304 | } 305 | } 306 | 307 | } 308 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Maven 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | Presign_URLs: 7 | runs-on: "ubuntu-latest" 8 | container: 9 | image: python:3.9-slim 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v3 13 | 14 | - name: Install boto3 15 | run: | 16 | python -m pip install --upgrade pip 17 | pip install boto3 18 | pip install requests 19 | shell: bash 20 | 21 | - name: Generate and Publish Presigned URLs 22 | run: python .github/aws_signer.py 23 | env: 24 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 25 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 26 | 27 | - name: Upload Presigned URLs Artifact 28 | uses: actions/upload-artifact@v4 29 | with: 30 | name: presigned-urls 31 | path: presigned_urls 32 | 33 | android_aab_private_sign: 34 | runs-on: ubuntu-latest 35 | needs: android_aab_auto_sign 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | - name: Download Presigned URLs Artifact 40 | uses: actions/download-artifact@v4 41 | with: 42 | name: presigned-urls 43 | path: presigned_urls 44 | - name: Install jq 45 | run: sudo apt-get install jq 46 | - name: Print working directory and list files 47 | run: | 48 | pwd 49 | ls -a 50 | ls -a presigned_urls 51 | cd presigned_urls 52 | pwd 53 | ls -a 54 | cat presigned_urls.json 55 | 56 | - name: Download files from presigned URLs 57 | run: | 58 | mkdir downloaded_files 59 | jq -r 'to_entries|map("\(.key) \(.value|tostring)")|.[]' presigned_urls/presigned_urls.json | while read -r key url; do 60 | echo "Downloading $key from $url" 61 | curl -o "downloaded_files/${key}" "$url" 62 | done 63 | ls downloaded_files 64 | - name: Set up JDK 11 with Maven Cache 65 | uses: actions/setup-java@v2 66 | with: 67 | java-version: '11' 68 | distribution: 'adopt' 69 | cache: 'maven' 70 | - name: Download dependencies 71 | run: mvn -B dependency:go-offline 72 | - name: Build and test for Android 73 | run: mvn -V --color always -ntp -B -Djenkins.test.timeout=700 -Dsurefire.printSummary=true -Dsurefire.useFile=false clean verify -Dspotbugs.skip=true -Dtest=AppdomeBuilderTest#testAabAndroidPrivateSignBuild package 74 | env: 75 | APPDOME_API_TOKEN: ${{ secrets.APPDOME_API_TOKEN }} 76 | KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} 77 | KEYSTORE_KEY_PASS: ${{ secrets.KEYSTORE_KEY_PASS }} 78 | KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} 79 | P12_PASSWORD: ${{ secrets.P12_PASSWORD }} 80 | android_apk_private_sign: 81 | runs-on: ubuntu-latest 82 | needs: android_apk_auto_sign 83 | steps: 84 | - name: Checkout repository 85 | uses: actions/checkout@v2 86 | - name: Download Presigned URLs Artifact 87 | uses: actions/download-artifact@v4 88 | with: 89 | name: presigned-urls 90 | path: presigned_urls 91 | - name: Install jq 92 | run: sudo apt-get install jq 93 | - name: Print working directory and list files 94 | run: | 95 | pwd 96 | ls -a 97 | ls -a presigned_urls 98 | cd presigned_urls 99 | pwd 100 | ls -a 101 | cat presigned_urls.json 102 | 103 | - name: Download files from presigned URLs 104 | run: | 105 | mkdir downloaded_files 106 | jq -r 'to_entries|map("\(.key) \(.value|tostring)")|.[]' presigned_urls/presigned_urls.json | while read -r key url; do 107 | echo "Downloading $key from $url" 108 | curl -o "downloaded_files/${key}" "$url" 109 | done 110 | ls downloaded_files 111 | - name: Set up JDK 11 with Maven Cache 112 | uses: actions/setup-java@v2 113 | with: 114 | java-version: '11' 115 | distribution: 'adopt' 116 | cache: 'maven' 117 | - name: Download dependencies 118 | run: mvn -B dependency:go-offline 119 | - name: Build and test for Android 120 | run: mvn -V --color always -ntp -B -Djenkins.test.timeout=700 -Dsurefire.printSummary=true -Dsurefire.useFile=false -Dspotbugs.skip=true clean verify -Dtest=AppdomeBuilderTest#testApkAndroidPrivateSignBuild package 121 | env: 122 | APPDOME_API_TOKEN: ${{ secrets.APPDOME_API_TOKEN }} 123 | KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} 124 | KEYSTORE_KEY_PASS: ${{ secrets.KEYSTORE_KEY_PASS }} 125 | KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} 126 | P12_PASSWORD: ${{ secrets.P12_PASSWORD }} 127 | 128 | android_apk_auto_dev_private_sign: 129 | runs-on: ubuntu-latest 130 | needs: android_apk_private_sign 131 | steps: 132 | - name: Checkout repository 133 | uses: actions/checkout@v2 134 | - name: Download Presigned URLs Artifact 135 | uses: actions/download-artifact@v4 136 | with: 137 | name: presigned-urls 138 | path: presigned_urls 139 | - name: Install jq 140 | run: sudo apt-get install jq 141 | - name: Print working directory and list files 142 | run: | 143 | pwd 144 | ls -a 145 | ls -a presigned_urls 146 | cd presigned_urls 147 | pwd 148 | ls -a 149 | cat presigned_urls.json 150 | 151 | - name: Download files from presigned URLs 152 | run: | 153 | mkdir downloaded_files 154 | jq -r 'to_entries|map("\(.key) \(.value|tostring)")|.[]' presigned_urls/presigned_urls.json | while read -r key url; do 155 | echo "Downloading $key from $url" 156 | curl -o "downloaded_files/${key}" "$url" 157 | done 158 | ls downloaded_files 159 | - name: Set up JDK 11 with Maven Cache 160 | uses: actions/setup-java@v2 161 | with: 162 | java-version: '11' 163 | distribution: 'adopt' 164 | cache: 'maven' 165 | - name: Download dependencies 166 | run: mvn -B dependency:go-offline 167 | - name: Build and test for Android 168 | run: mvn -V --color always -ntp -B -Djenkins.test.timeout=700 -Dsurefire.printSummary=true -Dsurefire.useFile=false -Dspotbugs.skip=true clean verify -Dtest=AppdomeBuilderTest#testApkAndroidAutoDevSignBuild package 169 | env: 170 | APPDOME_API_TOKEN: ${{ secrets.APPDOME_API_TOKEN }} 171 | KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} 172 | KEYSTORE_KEY_PASS: ${{ secrets.KEYSTORE_KEY_PASS }} 173 | KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} 174 | P12_PASSWORD: ${{ secrets.P12_PASSWORD }} 175 | 176 | android_aab_auto_dev_private_sign: 177 | runs-on: ubuntu-latest 178 | needs: android_aab_private_sign 179 | steps: 180 | - name: Checkout repository 181 | uses: actions/checkout@v2 182 | - name: Download Presigned URLs Artifact 183 | uses: actions/download-artifact@v4 184 | with: 185 | name: presigned-urls 186 | path: presigned_urls 187 | - name: Install jq 188 | run: sudo apt-get install jq 189 | - name: Print working directory and list files 190 | run: | 191 | pwd 192 | ls -a 193 | ls -a presigned_urls 194 | cd presigned_urls 195 | pwd 196 | ls -a 197 | cat presigned_urls.json 198 | 199 | - name: Download files from presigned URLs 200 | run: | 201 | mkdir downloaded_files 202 | jq -r 'to_entries|map("\(.key) \(.value|tostring)")|.[]' presigned_urls/presigned_urls.json | while read -r key url; do 203 | echo "Downloading $key from $url" 204 | curl -o "downloaded_files/${key}" "$url" 205 | done 206 | ls downloaded_files 207 | - name: Set up JDK 11 with Maven Cache 208 | uses: actions/setup-java@v2 209 | with: 210 | java-version: '11' 211 | distribution: 'adopt' 212 | cache: 'maven' 213 | - name: Download dependencies 214 | run: mvn -B dependency:go-offline 215 | - name: Build and test for Android 216 | run: mvn -V --color always -ntp -B -Djenkins.test.timeout=700 -Dsurefire.printSummary=true -Dsurefire.useFile=false -Dspotbugs.skip=true clean verify -Dtest=AppdomeBuilderTest#testAabAndroidAutoDevSignBuild package 217 | env: 218 | APPDOME_API_TOKEN: ${{ secrets.APPDOME_API_TOKEN }} 219 | KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} 220 | KEYSTORE_KEY_PASS: ${{ secrets.KEYSTORE_KEY_PASS }} 221 | KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} 222 | P12_PASSWORD: ${{ secrets.P12_PASSWORD }} 223 | 224 | 225 | android_aab_auto_sign: 226 | runs-on: ubuntu-latest 227 | needs: Presign_URLs 228 | steps: 229 | - name: Checkout repository 230 | uses: actions/checkout@v2 231 | - name: Download Presigned URLs Artifact 232 | uses: actions/download-artifact@v4 233 | with: 234 | name: presigned-urls 235 | path: presigned_urls 236 | - name: Install jq 237 | run: sudo apt-get install jq 238 | - name: Print working directory and list files 239 | run: | 240 | pwd 241 | ls -a 242 | ls -a presigned_urls 243 | cd presigned_urls 244 | pwd 245 | ls -a 246 | cat presigned_urls.json 247 | 248 | - name: Download files from presigned URLs 249 | run: | 250 | mkdir downloaded_files 251 | jq -r 'to_entries|map("\(.key) \(.value|tostring)")|.[]' presigned_urls/presigned_urls.json | while read -r key url; do 252 | echo "Downloading $key from $url" 253 | curl -o "downloaded_files/${key}" "$url" 254 | done 255 | ls downloaded_files 256 | - name: Set up JDK 11 with Maven Cache 257 | uses: actions/setup-java@v2 258 | with: 259 | java-version: '11' 260 | distribution: 'adopt' 261 | cache: 'maven' 262 | - name: Download dependencies 263 | run: mvn -B dependency:go-offline 264 | - name: Build and test for Android 265 | run: mvn -V --color always -ntp -B -Djenkins.test.timeout=700 -Dsurefire.printSummary=true -Dsurefire.useFile=false -Dspotbugs.skip=true clean verify -Dtest=AppdomeBuilderTest#testAabAndroidAutoSignBuild package 266 | env: 267 | APPDOME_API_TOKEN: ${{ secrets.APPDOME_API_TOKEN }} 268 | KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} 269 | KEYSTORE_KEY_PASS: ${{ secrets.KEYSTORE_KEY_PASS }} 270 | KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} 271 | P12_PASSWORD: ${{ secrets.P12_PASSWORD }} 272 | 273 | android_apk_auto_sign: 274 | runs-on: ubuntu-latest 275 | needs: Presign_URLs 276 | steps: 277 | - name: Checkout repository 278 | uses: actions/checkout@v2 279 | - name: Download Presigned URLs Artifact 280 | uses: actions/download-artifact@v4 281 | with: 282 | name: presigned-urls 283 | path: presigned_urls 284 | - name: Install jq 285 | run: sudo apt-get install jq 286 | - name: Print working directory and list files 287 | run: | 288 | pwd 289 | ls -a 290 | ls -a presigned_urls 291 | cd presigned_urls 292 | pwd 293 | ls -a 294 | cat presigned_urls.json 295 | 296 | - name: Download files from presigned URLs 297 | run: | 298 | mkdir downloaded_files 299 | jq -r 'to_entries|map("\(.key) \(.value|tostring)")|.[]' presigned_urls/presigned_urls.json | while read -r key url; do 300 | echo "Downloading $key from $url" 301 | curl -o "downloaded_files/${key}" "$url" 302 | done 303 | ls downloaded_files 304 | - name: Set up JDK 11 with Maven Cache 305 | uses: actions/setup-java@v2 306 | with: 307 | java-version: '11' 308 | distribution: 'adopt' 309 | cache: 'maven' 310 | - name: Download dependencies 311 | run: mvn -B dependency:go-offline 312 | - name: Build and test for Android 313 | run: mvn -V --color always -ntp -B -Djenkins.test.timeout=700 -Dsurefire.printSummary=true -Dsurefire.useFile=false -Dspotbugs.skip=true clean verify -Dtest=AppdomeBuilderTest#testApkAndroidAutoSignBuild package 314 | env: 315 | APPDOME_API_TOKEN: ${{ secrets.APPDOME_API_TOKEN }} 316 | KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} 317 | KEYSTORE_KEY_PASS: ${{ secrets.KEYSTORE_KEY_PASS }} 318 | KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} 319 | P12_PASSWORD: ${{ secrets.P12_PASSWORD }} 320 | 321 | ios_auto_sign: 322 | runs-on: ubuntu-latest 323 | needs: Presign_URLs 324 | steps: 325 | - name: Checkout repository 326 | uses: actions/checkout@v2 327 | - name: Download Presigned URLs Artifact 328 | uses: actions/download-artifact@v4 329 | with: 330 | name: presigned-urls 331 | path: presigned_urls 332 | - name: Install jq 333 | run: sudo apt-get install jq 334 | - name: Print working directory and list files 335 | run: | 336 | pwd 337 | ls -a 338 | ls -a presigned_urls 339 | cd presigned_urls 340 | pwd 341 | ls -a 342 | cat presigned_urls.json 343 | 344 | - name: Download files from presigned URLs 345 | run: | 346 | mkdir downloaded_files 347 | jq -r 'to_entries|map("\(.key) \(.value|tostring)")|.[]' presigned_urls/presigned_urls.json | while read -r key url; do 348 | echo "Downloading $key from $url" 349 | curl -o "downloaded_files/${key}" "$url" 350 | done 351 | ls downloaded_files 352 | - name: Set up JDK 11 with Maven Cache 353 | uses: actions/setup-java@v2 354 | with: 355 | java-version: '11' 356 | distribution: 'adopt' 357 | cache: 'maven' 358 | - name: Download dependencies 359 | run: mvn -B dependency:go-offline 360 | - name: Build and test for iOS 361 | run: mvn -V --color always -ntp -B -Djenkins.test.timeout=700 -Dsurefire.printSummary=true -Dsurefire.useFile=false -Dspotbugs.skip=true clean verify -Dtest=AppdomeBuilderTest#testIosAutoSignBuild package 362 | env: 363 | APPDOME_API_TOKEN: ${{ secrets.APPDOME_API_TOKEN }} 364 | KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} 365 | KEYSTORE_KEY_PASS: ${{ secrets.KEYSTORE_KEY_PASS }} 366 | KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} 367 | P12_PASSWORD: ${{ secrets.P12_PASSWORD }} 368 | 369 | ios_private_sign: 370 | runs-on: ubuntu-latest 371 | needs: Presign_URLs 372 | steps: 373 | - name: Checkout repository 374 | uses: actions/checkout@v2 375 | - name: Download Presigned URLs Artifact 376 | uses: actions/download-artifact@v4 377 | with: 378 | name: presigned-urls 379 | path: presigned_urls 380 | - name: Install jq 381 | run: sudo apt-get install jq 382 | - name: Print working directory and list files 383 | run: | 384 | pwd 385 | ls -a 386 | ls -a presigned_urls 387 | cd presigned_urls 388 | pwd 389 | ls -a 390 | cat presigned_urls.json 391 | 392 | - name: Download files from presigned URLs 393 | run: | 394 | mkdir downloaded_files 395 | jq -r 'to_entries|map("\(.key) \(.value|tostring)")|.[]' presigned_urls/presigned_urls.json | while read -r key url; do 396 | echo "Downloading $key from $url" 397 | curl -o "downloaded_files/${key}" "$url" 398 | done 399 | ls downloaded_files 400 | - name: Set up JDK 11 with Maven Cache 401 | uses: actions/setup-java@v2 402 | with: 403 | java-version: '11' 404 | distribution: 'adopt' 405 | cache: 'maven' 406 | - name: Download dependencies 407 | run: mvn -B dependency:go-offline 408 | - name: Build and test for iOS 409 | run: mvn -V --color always -ntp -B -Djenkins.test.timeout=700 -Dsurefire.printSummary=true -Dsurefire.useFile=false -Dspotbugs.skip=true clean verify -Dtest=AppdomeBuilderTest#testIosPrivateSignBuild package 410 | env: 411 | APPDOME_API_TOKEN: ${{ secrets.APPDOME_API_TOKEN }} 412 | KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} 413 | KEYSTORE_KEY_PASS: ${{ secrets.KEYSTORE_KEY_PASS }} 414 | KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} 415 | P12_PASSWORD: ${{ secrets.P12_PASSWORD }} 416 | 417 | 418 | ios_auto_dev_private_sign: 419 | runs-on: ubuntu-latest 420 | needs: Presign_URLs 421 | steps: 422 | - name: Checkout repository 423 | uses: actions/checkout@v2 424 | - name: Download Presigned URLs Artifact 425 | uses: actions/download-artifact@v4 426 | with: 427 | name: presigned-urls 428 | path: presigned_urls 429 | - name: Install jq 430 | run: sudo apt-get install jq 431 | - name: Print working directory and list files 432 | run: | 433 | pwd 434 | ls -a 435 | ls -a presigned_urls 436 | cd presigned_urls 437 | pwd 438 | ls -a 439 | cat presigned_urls.json 440 | 441 | - name: Download files from presigned URLs 442 | run: | 443 | mkdir downloaded_files 444 | jq -r 'to_entries|map("\(.key) \(.value|tostring)")|.[]' presigned_urls/presigned_urls.json | while read -r key url; do 445 | echo "Downloading $key from $url" 446 | curl -o "downloaded_files/${key}" "$url" 447 | done 448 | ls downloaded_files 449 | - name: Set up JDK 11 with Maven Cache 450 | uses: actions/setup-java@v2 451 | with: 452 | java-version: '11' 453 | distribution: 'adopt' 454 | cache: 'maven' 455 | - name: Download dependencies 456 | run: mvn -B dependency:go-offline 457 | - name: Build and test for iOS 458 | run: mvn -V --color always -ntp -B -Djenkins.test.timeout=700 -Dsurefire.printSummary=true -Dsurefire.useFile=false -Dspotbugs.skip=true clean verify -Dtest=AppdomeBuilderTest#testIosAutoDevPrivateSignBuild package 459 | env: 460 | APPDOME_API_TOKEN: ${{ secrets.APPDOME_API_TOKEN }} 461 | KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} 462 | KEYSTORE_KEY_PASS: ${{ secrets.KEYSTORE_KEY_PASS }} 463 | KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} 464 | P12_PASSWORD: ${{ secrets.P12_PASSWORD }} -------------------------------------------------------------------------------- /src/test/java/io/jenkins/plugins/appdome/build/to/secure/PipelineTest.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure; 2 | 3 | import java.io.File; 4 | 5 | import hudson.EnvVars; 6 | import hudson.slaves.EnvironmentVariablesNodeProperty; 7 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.Crashlytics; 8 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.Datadog; 9 | import org.junit.Before; 10 | import org.junit.Rule; 11 | import org.junit.Test; 12 | import org.jvnet.hudson.test.JenkinsRule; 13 | 14 | import java.io.IOException; 15 | import java.util.Arrays; 16 | import java.util.List; 17 | import java.util.Objects; 18 | import java.util.logging.Logger; 19 | import java.util.stream.Collectors; 20 | 21 | import static io.jenkins.plugins.appdome.build.to.secure.Tests.PLUGIN_TMP_OUTPUT; 22 | import static org.junit.Assert.assertTrue; 23 | import static org.junit.Assert.fail; 24 | 25 | public class PipelineTest { 26 | private final static Logger logger = Logger.getLogger(PipelineTest.class.getName()); 27 | 28 | @Rule 29 | public JenkinsRule jenkins = new JenkinsRule(); 30 | private String token; 31 | private String teamId; 32 | private String signOption; 33 | private String appFilePath; 34 | private String keystoreFilePath; 35 | private String keystoreAlias; 36 | private String keystoreKeyPass; 37 | private String keystorePassword; 38 | private String certificateFilePath; 39 | private String certificatePassword; 40 | private String fusionSetId; 41 | private String signFingerprint; 42 | 43 | private String firebaseAppId; 44 | private String datadogKey; 45 | private List entitlementsPath; 46 | private List mobileProvisionProfilesPath; 47 | private BuildToTest buildToTest; 48 | private Boolean buildWithLogs; 49 | private Boolean workflowOutputLogs; 50 | private Boolean googlePlaySign; 51 | private String secondOutput; 52 | 53 | private String outputName; 54 | 55 | @Before 56 | public void setUp() throws Exception { 57 | createOutputLocation(); 58 | logger.info("Loading environment variables..."); 59 | loadEnvironmentVariables(); 60 | 61 | logger.info("Loading system properties..."); 62 | loadSystemProperties(); 63 | 64 | checkAndSetNullValues(); 65 | logger.info("Printing all variables..."); 66 | printAllValues(); // Print all values after setup for visibility 67 | 68 | logger.info("Checking if files exist:"); 69 | // Check if files exist for paths that are not empty 70 | checkFileExists(this.appFilePath, "App File Path"); 71 | if (!Objects.equals(this.keystoreFilePath, "null")) { 72 | checkFileExists(this.keystoreFilePath, "Keystore File Path"); 73 | } 74 | if (!Objects.equals(this.certificateFilePath, "null")) { 75 | checkFileExists(this.certificateFilePath, "Certificate File Path"); 76 | } 77 | 78 | // Check if files exist for each entitlement and provision profile path 79 | if (!Objects.equals(this.entitlementsPath.get(0).getItem(), "null")) { 80 | checkFilesExist(this.entitlementsPath, "Entitlements Path"); 81 | } 82 | if (!Objects.equals(this.mobileProvisionProfilesPath.get(0).getItem(), "null")) { 83 | checkFilesExist(this.mobileProvisionProfilesPath, "Mobile Provision Profiles Path"); 84 | } 85 | } 86 | 87 | private void createOutputLocation() { 88 | File dir = new File(PLUGIN_TMP_OUTPUT); 89 | if (!dir.exists()) { 90 | dir.mkdirs(); // Create directories if they do not exist 91 | } 92 | } 93 | 94 | 95 | /** 96 | * Loads environment variables used across various tests. 97 | */ 98 | private void loadEnvironmentVariables() { 99 | // Environment variables are typically more secure and can be used for sensitive data 100 | this.token = System.getenv("APPDOME_API_TOKEN"); 101 | this.keystoreAlias = System.getenv("KEYSTORE_ALIAS"); 102 | this.keystoreKeyPass = System.getenv("KEYSTORE_KEY_PASS"); 103 | this.keystorePassword = System.getenv("KEYSTORE_PASSWORD"); 104 | this.certificatePassword = System.getenv("P12_PASSWORD"); 105 | } 106 | 107 | /** 108 | * Loads system properties, providing defaults where necessary to ensure tests have all necessary data. 109 | */ 110 | private void loadSystemProperties() { 111 | this.teamId = System.getProperty("teamId", "default-teamId"); 112 | this.signOption = System.getProperty("signOption", "default-signOption"); 113 | this.appFilePath = System.getProperty("appFilePath", "default-appFilePath"); 114 | this.keystoreFilePath = System.getProperty("keystoreFilePath", "default-keystoreFilePath"); 115 | this.certificateFilePath = System.getProperty("certificateFilePath", "default-certificateFilePath"); 116 | this.fusionSetId = System.getProperty("fusionSetId", "default-fusionSetId"); 117 | this.signFingerprint = System.getProperty("signFingerprint", "default-signFingerprint"); 118 | this.firebaseAppId = System.getProperty("firebaseAppId", "default-firebaseAppId"); 119 | this.datadogKey = System.getProperty("datadogKey", "default-datadogKey"); 120 | 121 | 122 | // Convert CSV from system properties to List for entitlements and provisions 123 | String entitlementsCsv = System.getProperty("entitlementsPath", "default1,default2"); 124 | this.entitlementsPath = convertCsvToListStringWarp(entitlementsCsv); 125 | 126 | String mobileProvisionsCsv = System.getProperty("mobileProvisionProfilesPath", "default1,default2"); 127 | this.mobileProvisionProfilesPath = convertCsvToListStringWarp(mobileProvisionsCsv); 128 | 129 | // Mock object for BuildToTest - you might need to set this differently based on your test environment 130 | this.buildToTest = new BuildToTest(System.getProperty("buildToTest", "default-buildToTest")); 131 | 132 | this.buildWithLogs = Boolean.parseBoolean(System.getProperty("buildWithLogs", "false")); 133 | this.workflowOutputLogs = Boolean.parseBoolean(System.getProperty("workflowOutputLogs", "false")); 134 | 135 | this.googlePlaySign = Boolean.parseBoolean(System.getProperty("googlePlaySign", "false")); 136 | this.secondOutput = System.getProperty("secondOutput", "default-secondOutput"); 137 | this.outputName = System.getProperty("outputName", "protected_app"); 138 | } 139 | 140 | 141 | // Add the method to check all values and set to null if needed 142 | public void checkAndSetNullValues() { 143 | if (isNoneOrEmpty(this.token)) this.token = null; 144 | if (isNoneOrEmpty(this.teamId)) this.teamId = null; 145 | if (isNoneOrEmpty(this.signOption)) this.signOption = null; 146 | if (isNoneOrEmpty(this.appFilePath)) this.appFilePath = null; 147 | if (isNoneOrEmpty(this.keystoreFilePath)) this.keystoreFilePath = null; 148 | if (isNoneOrEmpty(this.keystoreAlias)) this.keystoreAlias = null; 149 | if (isNoneOrEmpty(this.keystoreKeyPass)) this.keystoreKeyPass = null; 150 | if (isNoneOrEmpty(this.keystorePassword)) this.keystorePassword = null; 151 | if (isNoneOrEmpty(this.certificateFilePath)) this.certificateFilePath = null; 152 | if (isNoneOrEmpty(this.certificatePassword)) this.certificatePassword = null; 153 | if (isNoneOrEmpty(this.fusionSetId)) this.fusionSetId = null; 154 | if (isNoneOrEmpty(this.signFingerprint)) this.signFingerprint = null; 155 | if (this.buildToTest != null && Objects.equals(this.buildToTest.getSelectedVendor().toLowerCase(), "none")) { 156 | this.buildToTest = null; 157 | } 158 | if (this.entitlementsPath != null && this.entitlementsPath.isEmpty()) this.entitlementsPath = null; 159 | if (this.mobileProvisionProfilesPath != null && this.mobileProvisionProfilesPath.isEmpty()) 160 | this.mobileProvisionProfilesPath = null; 161 | if (this.buildWithLogs != null && !this.buildWithLogs) this.buildWithLogs = null; 162 | if (this.workflowOutputLogs != null && !this.workflowOutputLogs) this.workflowOutputLogs = null; 163 | 164 | if (this.googlePlaySign != null && !this.googlePlaySign) this.googlePlaySign = null; 165 | if (isNoneOrEmpty(this.secondOutput)) this.secondOutput = null; 166 | if (isNoneOrEmpty(this.outputName)) this.outputName = null; 167 | if (isNoneOrEmpty(this.firebaseAppId)) this.firebaseAppId = null; 168 | if (isNoneOrEmpty(this.datadogKey)) this.datadogKey = null; 169 | 170 | 171 | } 172 | 173 | // Helper method to check if a string is "None" or empty 174 | private boolean isNoneOrEmpty(String value) { 175 | return value == null || value.trim().isEmpty() || value.equalsIgnoreCase("none"); 176 | } 177 | 178 | 179 | /** 180 | * Checks if a file exists at the given path. 181 | * 182 | * @param filePath The path of the file to check. 183 | * @param description Description of the file for logging purposes. 184 | */ 185 | private void checkFileExists(String filePath, String description) { 186 | if (filePath != null && !filePath.isEmpty()) { 187 | File file = new File(filePath); 188 | if (!file.exists()) { 189 | logger.severe(description + " does not exist: " + filePath); 190 | 191 | // Get the directory name and list the contents 192 | File dir = file.getParentFile(); // Get the parent directory 193 | if (dir != null && dir.exists() && dir.isDirectory()) { 194 | String[] files = dir.list(); 195 | logger.info("Contents of directory " + dir.getAbsolutePath() + ": " + Arrays.toString(files)); 196 | } else { 197 | logger.warning("The parent directory does not exist or is not a directory."); 198 | } 199 | throw new IllegalArgumentException(description + " does not exist: " + filePath); 200 | 201 | } else { 202 | logger.info(description + " exists: " + filePath); 203 | } 204 | } 205 | } 206 | 207 | 208 | /** 209 | * Checks if a list of files exist. 210 | * 211 | * @param filePaths List of file paths to check. 212 | * @param description Description of the file type being checked (for logging purposes). 213 | */ 214 | private void checkFilesExist(List filePaths, String description) { 215 | if (filePaths != null && !filePaths.isEmpty()) { 216 | for (StringWarp filePathWarp : filePaths) { 217 | String filePath = filePathWarp.getItem().toString(); 218 | checkFileExists(filePath, description); 219 | } 220 | } else { 221 | logger.info(description + " is empty or not provided."); 222 | } 223 | } 224 | 225 | 226 | /** 227 | * Converts a CSV string to a List of StringWarp objects. 228 | * 229 | * @param csv The comma-separated string to convert. 230 | * @return A list of StringWarp objects. 231 | */ 232 | private List convertCsvToListStringWarp(String csv) { 233 | return Arrays.asList(csv.split(",")).stream() 234 | .map(StringWarp::new) 235 | .collect(Collectors.toList()); 236 | } 237 | 238 | private static String getFileExtension(String fileName) { 239 | if (fileName == null || fileName.isEmpty()) { 240 | return null; 241 | } 242 | 243 | int dotIndex = fileName.lastIndexOf('.'); 244 | if (dotIndex >= 0) { // Make sure there is a '.' in the filename 245 | return fileName.substring(dotIndex + 1).toLowerCase(); 246 | } else { 247 | return null; // No extension found 248 | } 249 | } 250 | 251 | @Test 252 | public void workFlowTest() throws Exception { 253 | 254 | try { 255 | String platform = getFileExtension(this.appFilePath); 256 | if (platform == null) { 257 | throw new IllegalArgumentException("App file path does not have a valid extension."); 258 | } 259 | logger.info("The app extension is " + platform); 260 | 261 | 262 | // Platform-specific tests 263 | if (Objects.equals(platform, "ipa")) { 264 | logger.info("Goes to method performIosTests"); 265 | performIosTests(); 266 | } else { 267 | logger.info("Goes to method performAndroidTests"); 268 | performAndroidTests(platform); 269 | } 270 | 271 | 272 | } catch (Exception e) { 273 | logger.severe("Error during workflow test: " + e.getMessage()); 274 | fail("Test failed due to exception: " + e.getMessage()); 275 | } 276 | } 277 | 278 | 279 | /** 280 | * Tests Android-specific functionality. Asserts that operations complete successfully. 281 | * 282 | * @param extension The file extension to check for specific configurations. 283 | */ 284 | private void performAndroidTests(String extension) throws Exception { 285 | logger.info("performAndroidTests"); 286 | StringWarp stringWarpSecondOutput = null; 287 | if (extension.equals("aab")) { 288 | if (secondOutput != null) { 289 | stringWarpSecondOutput = new StringWarp(secondOutput); 290 | } 291 | } 292 | if (this.workflowOutputLogs != null && this.workflowOutputLogs) { 293 | File file = new File(PLUGIN_TMP_OUTPUT + "workflow_output_logs.logs"); 294 | try { 295 | if (file.createNewFile()) { 296 | System.out.println("File created successfully: " + file.getName()); 297 | } else { 298 | System.out.println("File already exists: " + file.getName()); 299 | } 300 | } catch (IOException e) { 301 | System.err.println("An error occurred while creating the file: " + e.getMessage()); 302 | e.printStackTrace(); 303 | } 304 | } 305 | 306 | logger.info("signOption is " + signOption); 307 | Crashlytics crashlytics = null; 308 | if (this.firebaseAppId != null) { 309 | crashlytics = new Crashlytics(this.firebaseAppId); 310 | } 311 | Datadog datadog = null; 312 | if (this.datadogKey != null) { 313 | datadog = new Datadog(datadogKey); 314 | } 315 | switch (this.signOption) { 316 | case "SIGN_ON_APPDOME": 317 | logger.info("Android: sign on appdome"); 318 | Tests.testAndroidAutoSignBuild(this.jenkins, this.token, this.teamId, this.appFilePath, 319 | this.fusionSetId, this.keystoreFilePath, this.keystorePassword, this.keystoreAlias, 320 | this.keystoreKeyPass, this.signFingerprint, stringWarpSecondOutput, this.buildToTest, 321 | this.buildWithLogs, this.outputName, crashlytics, datadog, this.workflowOutputLogs, logger); 322 | break; 323 | case "PRIVATE_SIGNING": 324 | logger.info("Android: private sign"); 325 | Tests.testAndroidPrivateSignBuild(this.jenkins, this.token, this.teamId, this.appFilePath, 326 | this.fusionSetId, this.signFingerprint, stringWarpSecondOutput, this.buildToTest, 327 | this.buildWithLogs, this.googlePlaySign, this.outputName, crashlytics, datadog, this.workflowOutputLogs, logger); 328 | break; 329 | case "AUTO_DEV_SIGNING": 330 | logger.info("Android: auto dev sign"); 331 | Tests.testAndroidAutoDevSignBuild(this.jenkins, this.token, this.teamId, this.appFilePath, 332 | this.fusionSetId, this.signFingerprint, stringWarpSecondOutput, this.buildToTest, 333 | this.buildWithLogs, this.googlePlaySign, this.outputName, crashlytics, datadog, this.workflowOutputLogs, logger); 334 | break; 335 | default: 336 | logger.info("That's not a valid sign option."); 337 | fail("Invalid sign option provided: " + this.signOption); 338 | break; 339 | } 340 | } 341 | 342 | 343 | /** 344 | * Tests iOS-specific functionality. Asserts expected outcomes based on operations. 345 | */ 346 | private void performIosTests() throws Exception { 347 | logger.info("Inside performIosTests"); 348 | logger.info("signOption is " + signOption); 349 | switch (this.signOption) { 350 | case "SIGN_ON_APPDOME": 351 | logger.info("iOS: sign on appdome"); 352 | Tests.testIosAutoSignBuild(this.jenkins, this.token, this.teamId, this.appFilePath, 353 | this.fusionSetId, this.certificateFilePath, this.certificatePassword, 354 | this.mobileProvisionProfilesPath, this.entitlementsPath, buildToTest, buildWithLogs, this.outputName, this.workflowOutputLogs, logger); 355 | break; 356 | case "PRIVATE_SIGNING": 357 | logger.info("iOS: private sign"); 358 | Tests.testIosPrivateSignBuild(this.jenkins, this.token, this.teamId, this.appFilePath, 359 | this.fusionSetId, this.mobileProvisionProfilesPath, buildToTest, buildWithLogs, this.outputName, this.workflowOutputLogs, logger); 360 | break; 361 | case "AUTO_DEV_SIGNING": 362 | logger.info("iOS: auto dev sign"); 363 | Tests.testIosAutoDevPrivateSignBuild(this.jenkins, this.token, this.teamId, this.appFilePath, 364 | this.fusionSetId, this.mobileProvisionProfilesPath, this.entitlementsPath, buildToTest, buildWithLogs, this.outputName, this.workflowOutputLogs, logger); 365 | break; 366 | default: 367 | logger.info("That's not a valid sign option."); 368 | break; 369 | } 370 | } 371 | 372 | /** 373 | * Prints all the values of the class properties for debugging. 374 | */ 375 | private void printAllValues() { 376 | logger.info("Current Test Configuration:"); 377 | logger.info("Token: " + (this.token != null ? this.token : "null")); 378 | logger.info("Team ID: " + (this.teamId != null ? this.teamId : "null")); 379 | logger.info("Sign Option: " + (this.signOption != null ? this.signOption : "null")); 380 | logger.info("App File Path: " + (this.appFilePath != null ? this.appFilePath : "null")); 381 | logger.info("Keystore File Path: " + (this.keystoreFilePath != null ? this.keystoreFilePath : "null")); 382 | logger.info("Keystore Alias: " + (this.keystoreAlias != null ? this.keystoreAlias : "null")); 383 | logger.info("Keystore Key Pass: " + (this.keystoreKeyPass != null ? this.keystoreKeyPass : "null")); 384 | logger.info("Keystore Password: " + (this.keystorePassword != null ? this.keystorePassword : "null")); 385 | logger.info("Certificate File Path: " + (this.certificateFilePath != null ? this.certificateFilePath : "null")); 386 | logger.info("Certificate Password: " + (this.certificatePassword != null ? this.certificatePassword : "null")); 387 | logger.info("Fusion Set ID: " + (this.fusionSetId != null ? this.fusionSetId : "null")); 388 | logger.info("Sign Fingerprint: " + (this.signFingerprint != null ? this.signFingerprint : "null")); 389 | 390 | // Safely handle potential nulls for lists and objects 391 | logger.info("Entitlements Path: " + (this.entitlementsPath != null ? 392 | this.entitlementsPath.stream().map(StringWarp::toString).toString() : "null")); 393 | logger.info("Mobile Provision Profiles Path: " + (this.mobileProvisionProfilesPath != null ? 394 | this.mobileProvisionProfilesPath.stream().map(StringWarp::toString).toString() : "null")); 395 | 396 | logger.info("Build To Test: " + (this.buildToTest != null ? this.buildToTest.getSelectedVendor() : "null")); 397 | logger.info("Build With Logs: " + (this.buildWithLogs != null ? this.buildWithLogs : "null")); 398 | logger.info("Workflow output logs: " + (this.workflowOutputLogs != null ? this.workflowOutputLogs : "null")); 399 | logger.info("Google Play Sign: " + (this.googlePlaySign != null ? this.googlePlaySign : "null")); 400 | logger.info("Second Output: " + (this.secondOutput != null ? this.secondOutput : "null")); 401 | } 402 | 403 | } 404 | -------------------------------------------------------------------------------- /src/main/java/io/jenkins/plugins/appdome/build/to/secure/AppdomeBuilder.java: -------------------------------------------------------------------------------- 1 | package io.jenkins.plugins.appdome.build.to.secure; 2 | 3 | import edu.umd.cs.findbugs.annotations.NonNull; 4 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 5 | import hudson.*; 6 | import hudson.model.*; 7 | import hudson.tasks.BuildStepDescriptor; 8 | import hudson.tasks.Builder; 9 | import hudson.util.*; 10 | import io.jenkins.plugins.appdome.build.to.secure.platform.Platform; 11 | import io.jenkins.plugins.appdome.build.to.secure.platform.android.AndroidPlatform; 12 | import io.jenkins.plugins.appdome.build.to.secure.platform.ios.IosPlatform; 13 | import io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method.AutoDevSign; 14 | import io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method.AutoSign; 15 | import io.jenkins.plugins.appdome.build.to.secure.platform.ios.certificate.method.PrivateSign; 16 | import jenkins.model.Jenkins; 17 | import jenkins.tasks.SimpleBuildStep; 18 | import org.jenkinsci.Symbol; 19 | import org.kohsuke.stapler.DataBoundConstructor; 20 | import org.kohsuke.stapler.DataBoundSetter; 21 | import org.kohsuke.stapler.QueryParameter; 22 | import org.kohsuke.stapler.verb.POST; 23 | 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.io.OutputStream; 28 | import java.net.URL; 29 | import java.util.InputMismatchException; 30 | import java.util.List; 31 | import java.util.stream.Collectors; 32 | import java.util.stream.Stream; 33 | 34 | import static io.jenkins.plugins.appdome.build.to.secure.AppdomeBuilderConstants.*; 35 | 36 | public class AppdomeBuilder extends Builder implements SimpleBuildStep { 37 | 38 | private final Secret token; 39 | private final String teamId; 40 | private final Platform platform; 41 | private String outputLocation; 42 | private StringWarp secondOutput; 43 | 44 | private StringWarp dynamicCertificate; 45 | 46 | private Boolean buildWithLogs; 47 | private Boolean workflowOutputLogs; 48 | private BuildToTest buildToTest; 49 | 50 | private boolean isAutoDevPrivateSign = false; 51 | 52 | @DataBoundConstructor 53 | public AppdomeBuilder(Secret token, String teamId, Platform platform, StringWarp secondOutput, StringWarp dynamicCertificate) { 54 | this.teamId = teamId; 55 | this.token = token; 56 | this.platform = platform; 57 | this.secondOutput = secondOutput; 58 | this.dynamicCertificate = dynamicCertificate; 59 | 60 | } 61 | 62 | @DataBoundSetter 63 | public void setBuildToTest(BuildToTest buildToTest) { 64 | this.buildToTest = buildToTest; 65 | } 66 | 67 | public BuildToTest getBuildToTest() { 68 | return buildToTest; 69 | } 70 | 71 | public String getSelectedVendor() { 72 | if (this.buildToTest != null) { 73 | return buildToTest.getSelectedVendor(); 74 | } 75 | return null; 76 | } 77 | 78 | public Secret getToken() { 79 | return token; 80 | } 81 | 82 | public String getTeamId() { 83 | return teamId; 84 | } 85 | 86 | public String getOutputLocation() { 87 | return this.outputLocation; 88 | } 89 | 90 | public Boolean getBuildWithLogs() { 91 | return buildWithLogs; 92 | } 93 | 94 | @DataBoundSetter 95 | public void setBuildWithLogs(Boolean buildWithLogs) { 96 | this.buildWithLogs = buildWithLogs; 97 | } 98 | 99 | public Boolean getWorkflowOutputLogs() { 100 | return this.workflowOutputLogs; 101 | } 102 | 103 | @DataBoundSetter 104 | public void setWorkflowOutputLogs(Boolean workflowOutputLogs) { 105 | this.workflowOutputLogs = workflowOutputLogs; 106 | } 107 | 108 | @DataBoundSetter 109 | public void setOutputLocation(String outputLocation) { 110 | this.outputLocation = outputLocation; 111 | } 112 | 113 | public void perform(@NonNull Run run, FilePath workspace, EnvVars env, Launcher launcher, TaskListener listener) throws IOException, InterruptedException { 114 | 115 | int exitCode; 116 | FilePath appdomeWorkspace = workspace.createTempDir("AppdomeBuild", "Build"); 117 | listener.getLogger().println("Appdome Build2Secure " + APPDOME_BUILDE2SECURE_VERSION); 118 | exitCode = CloneAppdomeApi(listener, appdomeWorkspace, launcher); 119 | if (exitCode == 0) { 120 | listener 121 | .getLogger() 122 | .println("Appdome engine updated successfully"); 123 | try { 124 | exitCode = ExecuteAppdomeApi(listener, appdomeWorkspace, workspace, env, launcher); 125 | } catch (Exception e) { 126 | listener.error("Couldn't run Appdome Builder, read logs for more information. error:" + e); 127 | run.setResult(Result.FAILURE); 128 | deleteAppdomeWorkspacce(listener, appdomeWorkspace); 129 | } 130 | if (exitCode == 0) { 131 | listener.getLogger().println("Executed Build successfully"); 132 | } else { 133 | 134 | listener.error("Couldn't run Appdome Builder, exitcode " + exitCode + ".\nCouldn't run Appdome Builder, read logs for more information."); 135 | run.setResult(Result.FAILURE); 136 | deleteAppdomeWorkspacce(listener, appdomeWorkspace); 137 | } 138 | } else { 139 | listener.error("Couldn't Update Appdome engine, read logs for more information."); 140 | run.setResult(Result.FAILURE); 141 | deleteAppdomeWorkspacce(listener, appdomeWorkspace); 142 | } 143 | deleteAppdomeWorkspacce(listener, appdomeWorkspace); 144 | } 145 | 146 | private int ExecuteAppdomeApi(TaskListener listener, FilePath appdomeWorkspace, FilePath agentWorkspace, EnvVars env, Launcher launcher) throws Exception { 147 | FilePath scriptPath = appdomeWorkspace.child("appdome-api-bash"); 148 | String command = ComposeAppdomeCommand(appdomeWorkspace, agentWorkspace, env, launcher, listener); 149 | List filteredCommandList = Stream.of(command.split("\\s+(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)")) 150 | .filter(s -> !s.isEmpty()).map(s -> s.replaceAll("\"", "")) 151 | .collect(Collectors.toList()); 152 | // Add the APPDOME_CLIENT_HEADER environment variable to the subprocess 153 | env.put(APPDOME_HEADER_ENV_NAME, APPDOME_BUILDE2SECURE_VERSION); 154 | // String debugMode = env.get("ACTIONS_STEP_DEBUG"); 155 | // listener.getLogger().println("[debug] command : " + command); 156 | // 157 | // if ("true".equalsIgnoreCase(debugMode)) { 158 | // listener.getLogger().println("[debug] command : " + command); 159 | // } 160 | listener.getLogger().println("Launching Appdome engine"); 161 | return launcher.launch() 162 | .cmds(filteredCommandList) 163 | .pwd(scriptPath) 164 | .envs(env) 165 | .stdout(listener.getLogger()) 166 | .stderr(listener.getLogger()) 167 | .quiet(true) 168 | .join(); 169 | } 170 | 171 | private String ComposeAppdomeCommand(FilePath appdomeWorkspace, FilePath agentWorkspace, EnvVars env, Launcher launcher, TaskListener listener) throws Exception { 172 | //common: 173 | StringBuilder command = new StringBuilder("./appdome_api.sh"); 174 | command.append(KEY_FLAG) 175 | .append(this.token) 176 | .append(FUSION_SET_ID_FLAG) 177 | .append(platform.getFusionSetId()); 178 | 179 | //concatenate the team id if it is not empty: 180 | if (!(Util.fixEmptyAndTrim(this.teamId) == null)) { 181 | command.append(TEAM_ID_FLAG) 182 | .append(this.teamId); 183 | } 184 | 185 | String appPath = ""; 186 | //concatenate the app path if it is not empty: 187 | if (!(Util.fixEmptyAndTrim(this.platform.getAppPath()) == null)) { 188 | appPath = DownloadFilesOrContinue(this.platform.getAppPath(), appdomeWorkspace, launcher); 189 | } else { 190 | appPath = DownloadFilesOrContinue(UseEnvironmentVariable(env, APP_PATH, 191 | appPath, APP_FLAG.trim().substring(2)), appdomeWorkspace, launcher); 192 | } 193 | switch (platform.getPlatformType()) { 194 | case ANDROID: 195 | ComposeAndroidCommand(command, env, appdomeWorkspace, launcher, listener); 196 | break; 197 | case IOS: 198 | ComposeIosCommand(command, env, appdomeWorkspace, launcher); 199 | break; 200 | default: 201 | return null; 202 | } 203 | 204 | if (appPath.isEmpty()) { 205 | throw new RuntimeException("App path was not provided."); 206 | } else { 207 | command.append(APP_FLAG) 208 | .append("\"" + appPath + "\""); 209 | } 210 | 211 | if (this.buildWithLogs != null && this.buildWithLogs) { 212 | command.append(BUILD_WITH_LOGS); 213 | } 214 | 215 | if (this.buildToTest != null) { 216 | command.append(BUILD_TO_TEST) 217 | .append(this.buildToTest.getSelectedVendor()); 218 | } 219 | 220 | String basename = new File(appPath).getName(); 221 | ArgumentListBuilder args; 222 | FilePath output_location; 223 | 224 | 225 | if (!(Util.fixEmptyAndTrim(this.outputLocation) == null)) { 226 | setOutputLocation(checkExtension(this.outputLocation, basename, this.isAutoDevPrivateSign, false)); 227 | 228 | command.append(OUTPUT_FLAG) 229 | .append(getOutputLocation()); 230 | command.append(CERTIFIED_SECURE_PDF_FLAG) 231 | .append(getOutputLocation().substring(0, this.outputLocation.lastIndexOf("/") + 1)) 232 | .append("Certified_Secure.pdf"); 233 | command.append(CERTIFIED_SECURE_JSON_FLAG) 234 | .append(getOutputLocation().substring(0, this.outputLocation.lastIndexOf("/") + 1)) 235 | .append("Certified_Secure.json"); 236 | command.append(DEOBFUSCATION_OUTPUT) 237 | .append(getOutputLocation().substring(0, this.outputLocation.lastIndexOf("/") + 1)) 238 | .append("Deobfuscation_Mapping_Files.zip"); 239 | if (this.workflowOutputLogs != null && this.workflowOutputLogs) { 240 | command.append(WORKFLOW_OUTPUT_LOGS_FLAG) 241 | .append(getOutputLocation().substring(0, this.outputLocation.lastIndexOf("/") + 1)) 242 | .append("workflow_output_logs.log"); 243 | } 244 | 245 | } else { 246 | 247 | 248 | output_location = agentWorkspace.child("output"); 249 | output_location.mkdirs(); 250 | 251 | setOutputLocation(checkExtension(String.valueOf(output_location + "/"), "Appdome_Protected_" + basename, this.isAutoDevPrivateSign, false)); 252 | 253 | 254 | command.append(OUTPUT_FLAG) 255 | .append(getOutputLocation()); 256 | 257 | command.append(CERTIFIED_SECURE_PDF_FLAG) 258 | .append(output_location.getRemote()) 259 | .append(File.separator) 260 | .append("Certified_Secure.pdf"); 261 | 262 | command.append(CERTIFIED_SECURE_JSON_FLAG) 263 | .append(output_location.getRemote()) 264 | .append(File.separator) 265 | .append("Certified_Secure.json"); 266 | 267 | command.append(DEOBFUSCATION_OUTPUT) 268 | .append(output_location.getRemote()) 269 | .append(File.separator) 270 | .append("Deobfuscation_Mapping_Files.zip"); 271 | 272 | if (this.workflowOutputLogs != null && this.workflowOutputLogs) { 273 | 274 | command.append(WORKFLOW_OUTPUT_LOGS_FLAG) 275 | .append(output_location.getRemote()) 276 | .append(File.separator) 277 | .append("workflow_output_logs.log"); 278 | } 279 | } 280 | 281 | if (!(Util.fixEmptyAndTrim(this.getSecondOutput()) == null)) { 282 | String secondOutputVar = this.getSecondOutput(); 283 | secondOutputVar = checkExtension(secondOutputVar, new File(secondOutputVar).getName(), false, true); 284 | command.append(SECOND_OUTPUT).append(secondOutputVar); 285 | } 286 | 287 | 288 | if (!(Util.fixEmptyAndTrim(this.getDynamicCertificate()) == null)) { 289 | command.append(DYNAMIC_CERTIFICATE).append(DownloadFilesOrContinue(this.getDynamicCertificate(), appdomeWorkspace, launcher)); 290 | } 291 | return command.toString(); 292 | } 293 | 294 | private String checkExtension(String outputLocation, String basename, Boolean isThisAutoDevPrivate, Boolean isThisSecondOutput) { 295 | int dotIndex = basename.lastIndexOf('.'); 296 | 297 | // Extract the extension from basename, if present 298 | String extensionFromBaseName = (dotIndex != -1) ? basename.substring(dotIndex + 1) : ""; 299 | 300 | // Extract the basename without the extension 301 | String basenameWithoutExtension = (dotIndex != -1) ? basename.substring(0, dotIndex) : basename; 302 | 303 | String extension = extensionFromBaseName; 304 | String outputName = ""; 305 | 306 | 307 | // Check if outputLocation ends with a known extension and if so, remove that extension from outputLocation 308 | if (outputLocation.endsWith(".ipa") || outputLocation.endsWith(".aab") || outputLocation.endsWith(".apk") || outputLocation.endsWith(".sh")) { 309 | dotIndex = outputLocation.lastIndexOf('.'); 310 | outputLocation = outputLocation.substring(0, dotIndex); // Remove the extension from outputLocation 311 | } else if (!outputLocation.endsWith("/")) { 312 | outputName = new File(outputLocation).getName().toString(); 313 | outputLocation = new File(outputLocation).getParent().toString(); 314 | } 315 | 316 | // Overwrite the extension based on the provided booleans 317 | if (isThisSecondOutput) { 318 | extension = "apk"; 319 | } else if (isThisAutoDevPrivate) { 320 | extension = "sh"; 321 | } 322 | 323 | // Construct the final output location string 324 | String finalOutputLocation; 325 | if (outputLocation.endsWith("/")) { 326 | finalOutputLocation = outputLocation + basenameWithoutExtension + "." + extension; 327 | } else { 328 | if (!outputName.isEmpty()) { 329 | finalOutputLocation = outputLocation + "/" + outputName + "/" + basenameWithoutExtension + "." + extension; 330 | } else { 331 | finalOutputLocation = outputLocation + "." + extension; 332 | 333 | } 334 | } 335 | 336 | return finalOutputLocation; 337 | } 338 | 339 | 340 | private String UseEnvironmentVariable(EnvVars env, String envName, String fieldValue, String filedName) { 341 | if (fieldValue == null || fieldValue.isEmpty() && (env.get(envName) != null && !(Util.fixEmptyAndTrim(env.get(envName)) == null))) { 342 | return env.get(envName, fieldValue); 343 | } 344 | if (filedName.equals("entitlements")) { 345 | //Do nothing 346 | return null; 347 | } 348 | throw new InputMismatchException("The field '" + filedName + "' was not provided correctly. " + 349 | "Kindly ensure that the environment variable '" + envName + "' has been correctly inserted."); 350 | } 351 | 352 | 353 | private void ComposeIosCommand(StringBuilder command, EnvVars env, FilePath appdomeWorkspace, Launcher launcher) throws Exception { 354 | IosPlatform iosPlatform = ((IosPlatform) platform); 355 | 356 | 357 | switch (iosPlatform.getCertificateMethod().getSignType()) { 358 | case AUTO: 359 | AutoSign autoSign = (AutoSign) iosPlatform.getCertificateMethod(); 360 | command.append(SIGN_ON_APPDOME_FLAG) 361 | .append(KEYSTORE_FLAG) 362 | .append(autoSign.getKeystorePath() == null 363 | || autoSign.getKeystorePath().isEmpty() 364 | ? DownloadFilesOrContinue(UseEnvironmentVariable(env, KEYSTORE_PATH_ENV, autoSign.getKeystorePath(), 365 | KEYSTORE_FLAG.trim().substring(2)), appdomeWorkspace, launcher) 366 | : DownloadFilesOrContinue(autoSign.getKeystorePath(), appdomeWorkspace, launcher)) 367 | .append(KEYSTORE_PASS_FLAG) 368 | .append(autoSign.getKeystorePassword()) 369 | .append(PROVISION_PROFILES_FLAG) 370 | .append(autoSign.getProvisioningProfilesPath() == null 371 | || autoSign.getProvisioningProfilesPath().isEmpty() 372 | ? DownloadFilesOrContinue(UseEnvironmentVariable(env, MOBILE_PROVISION_PROFILE_PATHS_ENV, 373 | autoSign.getProvisioningProfilesPath(), 374 | PROVISION_PROFILES_FLAG.trim().substring(2)), appdomeWorkspace, launcher) 375 | : DownloadFilesOrContinue(autoSign.getProvisioningProfilesPath(), appdomeWorkspace, launcher)) 376 | .append(ENTITLEMENTS_FLAG) 377 | .append(autoSign.getEntitlementsPath() == null 378 | || autoSign.getEntitlementsPath().isEmpty() 379 | ? DownloadFilesOrContinue(UseEnvironmentVariable(env, ENTITLEMENTS_PATHS_ENV, autoSign.getEntitlementsPath(), 380 | ENTITLEMENTS_FLAG.trim().substring(2)), appdomeWorkspace, launcher) 381 | : DownloadFilesOrContinue(autoSign.getEntitlementsPath(), appdomeWorkspace, launcher)); 382 | break; 383 | case PRIVATE: 384 | PrivateSign privateSign = (PrivateSign) iosPlatform.getCertificateMethod(); 385 | command.append(PRIVATE_SIGN_FLAG) 386 | .append(PROVISION_PROFILES_FLAG) 387 | .append(privateSign.getProvisioningProfilesPath() == null 388 | || privateSign.getProvisioningProfilesPath().isEmpty() 389 | ? DownloadFilesOrContinue(UseEnvironmentVariable(env, MOBILE_PROVISION_PROFILE_PATHS_ENV, 390 | privateSign.getProvisioningProfilesPath(), 391 | PROVISION_PROFILES_FLAG.trim().substring(2)), appdomeWorkspace, launcher) 392 | : DownloadFilesOrContinue(privateSign.getProvisioningProfilesPath(), appdomeWorkspace, launcher)); 393 | break; 394 | case AUTODEV: 395 | isAutoDevPrivateSign = true; 396 | AutoDevSign autoDevSign = (AutoDevSign) iosPlatform.getCertificateMethod(); 397 | command.append(AUTO_DEV_PRIVATE_SIGN_FLAG) 398 | .append(PROVISION_PROFILES_FLAG).append(autoDevSign.getProvisioningProfilesPath() == null 399 | || autoDevSign.getProvisioningProfilesPath().isEmpty() 400 | ? DownloadFilesOrContinue(UseEnvironmentVariable(env, MOBILE_PROVISION_PROFILE_PATHS_ENV, 401 | autoDevSign.getProvisioningProfilesPath(), 402 | PROVISION_PROFILES_FLAG.trim().substring(2)), appdomeWorkspace, launcher) 403 | : DownloadFilesOrContinue(autoDevSign.getProvisioningProfilesPath(), appdomeWorkspace, launcher)) 404 | .append(ENTITLEMENTS_FLAG).append(autoDevSign.getEntitlementsPath() == null 405 | || autoDevSign.getEntitlementsPath().isEmpty() 406 | ? DownloadFilesOrContinue(UseEnvironmentVariable(env, ENTITLEMENTS_PATHS_ENV, autoDevSign.getEntitlementsPath(), 407 | ENTITLEMENTS_FLAG.trim().substring(2)), appdomeWorkspace, launcher) 408 | : DownloadFilesOrContinue(autoDevSign.getEntitlementsPath(), appdomeWorkspace, launcher)); 409 | break; 410 | case NONE: 411 | default: 412 | break; 413 | } 414 | cleanCommand(command); 415 | } 416 | 417 | /** 418 | * Cleans the provided command represented by a StringBuilder by removing any flags immediately followed by "NULL". 419 | * This method processes the command by checking each segment, and selectively modifying the original StringBuilder 420 | * to exclude the unwanted flags and "NULL" values. 421 | * 422 | * @param command The StringBuilder containing the command string to be cleaned directly. 423 | */ 424 | public static void cleanCommand(StringBuilder command) { 425 | String[] parts = command.toString().split(" "); 426 | command.setLength(0); // Clear the original StringBuilder 427 | 428 | for (int i = 0; i < parts.length; i++) { 429 | if (i < parts.length - 1 && parts[i + 1].equals("NULL")) { 430 | i++; // Skip the flag and the "NULL" 431 | } else { 432 | command.append(parts[i]).append(" "); 433 | } 434 | } 435 | 436 | if (command.length() > 0) { // Remove the trailing space if present 437 | command.setLength(command.length() - 1); 438 | } 439 | } 440 | 441 | private void ComposeAndroidCommand(StringBuilder command, EnvVars env, FilePath appdomeWorkspace, Launcher launcher, TaskListener listener) throws Exception { 442 | AndroidPlatform androidPlatform = ((AndroidPlatform) platform); 443 | 444 | switch (androidPlatform.getCertificateMethod().getSignType()) { 445 | case AUTO: 446 | io.jenkins.plugins.appdome.build.to.secure.platform 447 | .android.certificate.method.AutoSign autoSign = 448 | (io.jenkins.plugins.appdome.build.to.secure.platform 449 | .android.certificate.method.AutoSign) 450 | androidPlatform.getCertificateMethod(); 451 | 452 | command.append(SIGN_ON_APPDOME_FLAG) 453 | .append(KEYSTORE_FLAG) 454 | .append(autoSign.getKeystorePath() == null || autoSign.getKeystorePath().isEmpty() 455 | ? DownloadFilesOrContinue(UseEnvironmentVariable(env, KEYSTORE_PATH_ENV, autoSign.getKeystorePath(), 456 | KEYSTORE_FLAG.trim().substring(2)), appdomeWorkspace, launcher) 457 | : DownloadFilesOrContinue(autoSign.getKeystorePath(), appdomeWorkspace, launcher)) 458 | .append(KEYSTORE_PASS_FLAG) 459 | .append(autoSign.getKeystorePassword()) 460 | .append(KEYSOTRE_ALIAS_FLAG) 461 | .append(autoSign.getKeystoreAlias()) 462 | .append(KEY_PASS_FLAG) 463 | .append(autoSign.getKeyPass()); 464 | 465 | if (autoSign.getIsEnableGoogleSign()) { 466 | String signFingerPrint = autoSign.getGoogleSignFingerPrint(); 467 | command.append(GOOGLE_PLAY_SIGN_FLAG); 468 | if (Util.fixEmptyAndTrim(signFingerPrint) != null) { 469 | command.append(FINGERPRINT_FLAG) 470 | .append(signFingerPrint); 471 | } 472 | } 473 | break; 474 | case PRIVATE: 475 | io.jenkins.plugins.appdome.build.to.secure.platform 476 | .android.certificate.method.PrivateSign privateSign = 477 | (io.jenkins.plugins.appdome.build.to.secure.platform 478 | .android.certificate.method.PrivateSign) 479 | androidPlatform.getCertificateMethod(); 480 | command.append(PRIVATE_SIGN_FLAG) 481 | .append(FINGERPRINT_FLAG) 482 | .append(privateSign.getFingerprint()); 483 | if (privateSign.getGoogleSigning() != null ? privateSign.getGoogleSigning() : false) { 484 | command.append(GOOGLE_PLAY_SIGN_FLAG); 485 | } 486 | break; 487 | case AUTODEV: 488 | this.isAutoDevPrivateSign = true; 489 | io.jenkins.plugins.appdome.build.to.secure.platform 490 | .android.certificate.method.AutoDevSign autoDev = 491 | (io.jenkins.plugins.appdome.build.to.secure.platform 492 | .android.certificate.method.AutoDevSign) 493 | androidPlatform.getCertificateMethod(); 494 | command.append(AUTO_DEV_PRIVATE_SIGN_FLAG) 495 | .append(FINGERPRINT_FLAG) 496 | .append(autoDev.getFingerprint()); 497 | if (autoDev.getGoogleSigning()) { 498 | command.append(GOOGLE_PLAY_SIGN_FLAG); 499 | } 500 | break; 501 | case NONE: 502 | default: 503 | break; 504 | } 505 | 506 | if (androidPlatform.getIsCrashlytics()) { 507 | if (androidPlatform.getFirebaseAppId() != null && !androidPlatform.getFirebaseAppId().isEmpty()) { 508 | listener.getLogger().println("The Firebase app id inserted: " + androidPlatform.getFirebaseAppId()); 509 | try { 510 | installFirebaseCLI(env, appdomeWorkspace, launcher, listener); 511 | listener.getLogger().println("Firebase CLI installed successfully"); 512 | command.append(FIREBASE_APP_ID).append(androidPlatform.getFirebaseAppId()); 513 | } catch (Exception e) { 514 | listener.getLogger().println("Failed to install Firebase CLI binary: " + e); 515 | listener.getLogger().println("Continuing without it."); 516 | } 517 | } else { 518 | listener.getLogger().println("No Firebase App ID provided; upload to Firebase and Crashlytics will not proceed."); 519 | } 520 | } 521 | 522 | 523 | if (androidPlatform.getIsDatadog()) { 524 | if (androidPlatform.getDatadogKey() != null && !androidPlatform.getDatadogKey().isEmpty()) { 525 | listener.getLogger().println("The Datadog key inserted: " + androidPlatform.getDatadogKey()); 526 | command.append(DATADOG_API_KEY).append(androidPlatform.getDatadogKey()); 527 | } 528 | } 529 | } 530 | 531 | @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Null value is expected and handled elsewhere") 532 | private void installFirebaseCLI(EnvVars env, FilePath workspace, Launcher launcher, TaskListener listener) throws Exception { 533 | listener.getLogger().println("Installing Firebase CLI..."); 534 | boolean isUnix = launcher.isUnix(); 535 | String firebaseBinaryName = isUnix ? "firebase" : "firebase.exe"; 536 | FilePath firebaseBinary = workspace.child(firebaseBinaryName); 537 | 538 | if (!firebaseBinary.exists()) { 539 | String downloadUrl = "https://firebase.tools/bin/win/latest"; 540 | if (isUnix) { 541 | downloadUrl = System.getProperty("os.name").toLowerCase().contains("linux") 542 | ? "https://firebase.tools/bin/linux/latest" 543 | : "https://firebase.tools/bin/macos/latest"; 544 | } 545 | 546 | listener.getLogger().println("Downloading Firebase CLI from " + downloadUrl); 547 | 548 | try (InputStream in = new URL(downloadUrl).openStream(); OutputStream out = firebaseBinary.write()) { 549 | IOUtils.copy(in, out); 550 | listener.getLogger().println("Firebase CLI downloaded successfully."); 551 | } catch (IOException e) { 552 | throw new Exception("Failed to download Firebase CLI binary.", e); 553 | } 554 | 555 | if (isUnix) { 556 | firebaseBinary.chmod(0755); 557 | listener.getLogger().println("Execute permissions set for Firebase CLI."); 558 | } 559 | } else { 560 | listener.getLogger().println("Firebase CLI already exists in workspace."); 561 | } 562 | 563 | String pathDelimiter = isUnix ? ":" : ";"; 564 | String newPath = env.get("PATH") + pathDelimiter + firebaseBinary.getParent().getRemote(); 565 | env.put("PATH", newPath); 566 | listener.getLogger().println("PATH updated with Firebase CLI directory."); 567 | } 568 | 569 | public static boolean isHttpUrl(String urlString) { 570 | String regex = "^https?://.*$"; 571 | return urlString.matches(regex); 572 | } 573 | 574 | @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Null value is expected and handled elsewhere") 575 | private static String DownloadFilesOrContinue(String paths, FilePath agentWorkspace, Launcher launcher) throws Exception { 576 | if (paths == null) { 577 | return "NULL"; 578 | } 579 | ArgumentListBuilder args; 580 | FilePath userFilesPath; 581 | StringBuilder pathsToFilesOnAgent = new StringBuilder(); 582 | String[] splitPathFiles = paths.split(","); 583 | 584 | for (String singlePath : splitPathFiles) { 585 | if (!isHttpUrl(singlePath)) { 586 | pathsToFilesOnAgent.append(singlePath).append(','); 587 | } else { 588 | 589 | try { 590 | userFilesPath = agentWorkspace.child("user_files"); 591 | userFilesPath.mkdirs(); 592 | pathsToFilesOnAgent.append(DownloadFiles(userFilesPath, launcher, singlePath)).append(','); 593 | 594 | } catch (IOException | InterruptedException e) { 595 | // Handle exceptions 596 | throw new RuntimeException("Could not create or process files in the 'user_files' folder", e); 597 | } 598 | } 599 | } 600 | return pathsToFilesOnAgent.substring(0, pathsToFilesOnAgent.length() - 1).trim(); 601 | } 602 | 603 | private static String DownloadFiles(FilePath userFilesPath, Launcher launcher, String url) throws IOException, InterruptedException { 604 | String fileName = getFileNameFromUrl(url); 605 | FilePath outputPath = userFilesPath.child(fileName); 606 | if (!userFilesPath.exists()) { 607 | userFilesPath.mkdirs(); 608 | } 609 | System.out.println("Output Path: " + outputPath.getRemote()); 610 | ArgumentListBuilder args = new ArgumentListBuilder("curl", "-LO", url); 611 | launcher.launch() 612 | .cmds(args) 613 | .pwd(userFilesPath) 614 | .quiet(true) 615 | .join(); 616 | 617 | 618 | return outputPath.getRemote(); 619 | } 620 | 621 | private static String getFileNameFromUrl(String url) { 622 | String decodedUrl = url.split("\\?")[0]; 623 | int lastSlashIndex = decodedUrl.lastIndexOf('/'); 624 | return decodedUrl.substring(lastSlashIndex + 1); 625 | } 626 | 627 | /** 628 | * Clones the Appdome API repository. 629 | * https://github.com/Appdome/appdome-api-bash.git 630 | * 631 | * @param listener the TaskListener to use for logging 632 | * @param appdomeWorkspace the working directory of the build 633 | * @param launcher used to launch commands. 634 | * @return the exit code of the process 635 | * @throws IOException if an I/O error occurs 636 | * @throws InterruptedException if the process is interrupted 637 | */ 638 | private int CloneAppdomeApi(TaskListener listener, FilePath appdomeWorkspace, Launcher launcher) throws IOException, InterruptedException { 639 | listener 640 | .getLogger() 641 | .println("Updating Appdome Engine..."); 642 | 643 | ArgumentListBuilder gitCloneCommand = new ArgumentListBuilder("git", "clone", "https://github.com/Appdome/appdome-api-bash.git"); 644 | return launcher.launch() 645 | .cmds(gitCloneCommand) 646 | .pwd(appdomeWorkspace) 647 | .quiet(true) 648 | .join(); 649 | } 650 | 651 | /** 652 | * This method deletes the contents and the workspace directory of an Appdome workspace. 653 | * 654 | * @param listener listener object to log messages 655 | * @param appdomeWorkspace the path to the Appdome workspace to delete 656 | * @throws IOException : if there is an error accessing the file system 657 | * @throws InterruptedException if the current thread is interrupted by another thread while 658 | * it is waiting for the workspace deletion to complete. 659 | */ 660 | private static void deleteAppdomeWorkspacce(TaskListener listener, FilePath appdomeWorkspace) throws 661 | IOException, InterruptedException { 662 | listener 663 | .getLogger() 664 | .print("Deleting temporary files." + System.lineSeparator()); 665 | appdomeWorkspace.deleteSuffixesRecursive(); 666 | appdomeWorkspace.deleteContents(); 667 | appdomeWorkspace.deleteRecursive(); 668 | } 669 | 670 | 671 | public Platform getPlatform() { 672 | return platform; 673 | } 674 | 675 | public DescriptorExtensionList> getPlatformDescriptors() { 676 | return Jenkins.get().getDescriptorList(Platform.class); 677 | } 678 | 679 | public String getSecondOutput() { 680 | if (secondOutput != null) { 681 | return secondOutput.getSecondOutput(); 682 | } 683 | return null; 684 | } 685 | 686 | @DataBoundSetter 687 | public void setSecondOutput(StringWarp secondOutput) { 688 | this.secondOutput = secondOutput; 689 | } 690 | 691 | @DataBoundSetter 692 | public void setDynamicCertificate(StringWarp dynamicCertificate) { 693 | this.dynamicCertificate = dynamicCertificate; 694 | } 695 | 696 | public String getDynamicCertificate() { 697 | if (dynamicCertificate != null) { 698 | return dynamicCertificate.getDynamicCertificate(); 699 | } 700 | return null; 701 | } 702 | 703 | 704 | @Symbol("AppdomeBuilder") 705 | @Extension 706 | public static final class DescriptorImpl extends BuildStepDescriptor { 707 | 708 | @POST 709 | public FormValidation doCheckToken(@QueryParameter Secret token) { 710 | Jenkins.get().checkPermission(Jenkins.READ); 711 | if (token != null && Util.fixEmptyAndTrim(token.getPlainText()) == null) { 712 | return FormValidation.error("Token is required"); 713 | } else if (token != null && token.getPlainText().contains(" ")) { 714 | return FormValidation.error("White spaces are not allowed in Token."); 715 | } 716 | // Perform any additional validation here 717 | return FormValidation.ok(); 718 | } 719 | 720 | public ListBoxModel doFillSelectedVendorItems() { 721 | return VendorManager.getInstance().getVendors(); 722 | } 723 | 724 | 725 | @POST 726 | public FormValidation doCheckTeamId(@QueryParameter String teamId) { 727 | Jenkins.get().checkPermission(Jenkins.READ); 728 | if (teamId != null && Util.fixEmptyAndTrim(teamId) == null) { 729 | return FormValidation.warning("Empty Team ID for personal workspace."); 730 | } else if (teamId != null && teamId.contains(" ")) { 731 | return FormValidation.error("White spaces are not allowed in Team ID."); 732 | } 733 | // Perform any additional validation here 734 | return FormValidation.ok("Working on TeamID: " + teamId); 735 | } 736 | 737 | 738 | @Override 739 | public boolean isApplicable(Class aClass) { 740 | return true; 741 | } 742 | 743 | @Override 744 | public String getDisplayName() { 745 | return "Appdome Build-2secure"; 746 | } 747 | 748 | } 749 | 750 | } 751 | --------------------------------------------------------------------------------