├── src
├── main
│ ├── resources
│ │ ├── org
│ │ │ └── jenkinsci
│ │ │ │ └── plugins
│ │ │ │ └── appio
│ │ │ │ ├── AppioRecorder
│ │ │ │ ├── config_ja.properties
│ │ │ │ ├── help-appName.html
│ │ │ │ ├── help-appFile.html
│ │ │ │ └── config.jelly
│ │ │ │ ├── AppioCredentials
│ │ │ │ ├── help-apiKey.html
│ │ │ │ ├── help-s3Bucket.html
│ │ │ │ ├── help-s3AccessKey.html
│ │ │ │ ├── help-s3SecretKey.html
│ │ │ │ └── credentials.jelly
│ │ │ │ └── AppioProjectAction
│ │ │ │ └── floatingBox.jelly
│ │ └── index.jelly
│ ├── webapp
│ │ └── images
│ │ │ └── 24x24
│ │ │ └── Appio-logo.png
│ └── java
│ │ └── org
│ │ └── jenkinsci
│ │ └── plugins
│ │ └── appio
│ │ ├── model
│ │ ├── AppioVersion.java
│ │ ├── AppioApps.java
│ │ ├── AppioApp.java
│ │ ├── AppioVersionObject.java
│ │ └── AppioAppObject.java
│ │ ├── AppioAction.java
│ │ ├── AppioProjectAction.java
│ │ ├── AppioCredentials.java
│ │ ├── service
│ │ ├── S3Service.java
│ │ └── AppioService.java
│ │ └── AppioRecorder.java
└── test
│ └── java
│ └── org
│ └── jenkinsci
│ └── plugins
│ └── appio
│ ├── S3ServiceTest.java
│ └── AppioServiceTest.java
├── test.properties.blank
├── README.md
└── pom.xml
/src/main/resources/org/jenkinsci/plugins/appio/AppioRecorder/config_ja.properties:
--------------------------------------------------------------------------------
1 | Build\ package=ビルド・パッケージ
2 | Application\ name=アプリケーション名
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appio/AppioRecorder/help-appName.html:
--------------------------------------------------------------------------------
1 |
The application name that will be used by App.io
--------------------------------------------------------------------------------
/src/main/webapp/images/24x24/Appio-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/app.io-plugin/master/src/main/webapp/images/24x24/Appio-logo.png
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appio/AppioCredentials/help-apiKey.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
4 |
5 | Upload Xcode simulator builds to App.io using Amazon S3
6 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appio/AppioProjectAction/floatingBox.jelly:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appio/AppioRecorder/help-appFile.html:
--------------------------------------------------------------------------------
1 | The path to the iOS application (.app) package: this needs to be a simulator build and will usually be located in e.g. ${WORKSPACE}/build/Debug-iphonesimulator
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appio/AppioCredentials/help-s3Bucket.html:
--------------------------------------------------------------------------------
1 | The Amazon S3 bucket that will be used to upload builds to App.io. The AWS user should have read/write permissions to the bucket: it is recommended to use IAM roles to limit access.
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appio/AppioCredentials/help-s3AccessKey.html:
--------------------------------------------------------------------------------
1 | The Amazon AWS user credentials used to upload the build files to S3. The AWS user should have read/write permissions to the bucket: it is recommended to use IAM roles to limit access.
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appio/AppioCredentials/help-s3SecretKey.html:
--------------------------------------------------------------------------------
1 | The Amazon AWS user credentials used to upload the build files to S3. The AWS user should have read/write permissions to the bucket: it is recommended to use IAM roles to limit access.
--------------------------------------------------------------------------------
/test.properties.blank:
--------------------------------------------------------------------------------
1 | # AppioServiceTest properties
2 | Appio.appName=
3 | Appio.apiKeyUnencoded=
4 | Appio.badKey=
5 | Appio.badName=
6 |
7 | # S3ServiceTest properties
8 | # Also used in AppioServiceTest.addVersionS3()
9 | S3.accessKey=
10 | S3.secretKey=
11 | S3.bucketName=
12 | S3.badBucket=
13 | S3.keyName=
14 | S3.uploadFile=
15 | S3.badFile=
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appio/AppioRecorder/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appio/AppioCredentials/credentials.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | app.io-plugin
2 | =============
3 |
4 | Jenkins CI plugin for App.io: uploads iOS simulator builds to App.io (via AWS S3 service) so that a live simulator link is available on the main project page. See the [jenkins-ci.org wiki page](https://wiki.jenkins-ci.org/display/JENKINS/App.io+Plugin) for more details.
5 |
6 | ## Unit Testing
7 | To run unit tests, you will need to set a number of test properties to define the App.io and Amazon S3 credentials to use and other test parameters. The property file for the tests is test.properties: there is a blank template provided in the repo, add your test properties and rename to test.properties. These tests are skipped when running release:perform, so there is no dependency on the local test.properties.
8 |
9 | Warning: unit tests will over-write any existing App.io deployments with the same name - please use a different app name (Appio.appName).
10 |
11 |
12 | ### App.io Test Properties
13 | 1. Appio.appName=name for app on App.io
14 | 2. Appio.apiKeyUnencoded=App.io API key from https://app.io/account/api
15 | 3. Appio.badKey=An unrecognized API key
16 | 4. Appio.badName=An unrecognized app name: make sure app does not exist
17 |
18 | ### Amazon S3 Test Properties
19 | 1. S3.accessKey=AWS Access Key
20 | 2. S3.secretKey=AWS Secret Key
21 | 3. S3.bucketName=AWS S3 Bucket
22 | 4. S3.badBucket=An unrecognized S3 bucket: make sure bucket does not exist
23 | 5. S3.keyName=An unrecognized AWS Access Key
24 | 6. S3.uploadFile=Path to zipped .app package to use for testing: just zip up any successful simulator build
25 | 7. S3.badFile=An unrecognized filepath
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appio/model/AppioVersion.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio.model;
26 |
27 | import com.google.gson.annotations.Expose;
28 |
29 | import javax.xml.bind.annotation.XmlRootElement;
30 |
31 | /**
32 | * Model class for App.io REST API v1
33 | *
34 | * @see http://docs.app.io/api/versions
35 | * @author Mark Prichard
36 | */
37 | @XmlRootElement
38 | public class AppioVersion {
39 | @Expose
40 | private AppioVersionObject version;
41 |
42 | public AppioVersionObject getVersion() {
43 | return version;
44 | }
45 |
46 | public void setVersion(AppioVersionObject version) {
47 | this.version = version;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appio/AppioAction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio;
26 |
27 | import hudson.model.Action;
28 |
29 | /**
30 | * @author Kohsuke Kawaguchi
31 | * @author Mark Prichard
32 | */
33 | public class AppioAction implements Action {
34 |
35 | private final String appURL;
36 |
37 | public AppioAction(String appURL) {
38 | this.appURL = appURL;
39 | }
40 |
41 | public String getIconFileName() {
42 | return ("/plugin/appio/images/24x24/Appio-logo.png");
43 | }
44 |
45 | public String getDisplayName() {
46 | return "App.io iOS Simulator";
47 | }
48 |
49 | public String getUrlName() {
50 | return appURL;
51 | }
52 |
53 | public String getAppURL() {
54 | return appURL;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appio/model/AppioApps.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio.model;
26 |
27 | import javax.xml.bind.annotation.XmlRootElement;
28 |
29 | /**
30 | * Model class for App.io REST API v1
31 | *
32 | * @see http://docs.app.io/api
33 | * @author Mark Prichard
34 | */
35 | @XmlRootElement
36 | public class AppioApps {
37 | private AppioAppObject[] apps;
38 |
39 | public AppioApps() {
40 | super();
41 | }
42 |
43 | public AppioApps(AppioAppObject[] apps) {
44 | super();
45 | this.apps = apps;
46 | }
47 |
48 | public AppioAppObject[] getApps() {
49 | return apps;
50 | }
51 |
52 | public void setApps(AppioAppObject[] apps) {
53 | this.apps = apps;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appio/model/AppioApp.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio.model;
26 |
27 | import com.google.gson.annotations.Expose;
28 |
29 | import javax.xml.bind.annotation.XmlRootElement;
30 |
31 | /**
32 | * Model class for App.io REST API v1
33 | *
34 | * @see http://docs.app.io/api
35 | * @author Mark Prichard
36 | */
37 | @XmlRootElement
38 | public class AppioApp {
39 | @Expose
40 | private AppioAppObject app;
41 |
42 | public AppioApp() {
43 | super();
44 | }
45 |
46 | public AppioApp(AppioAppObject app) {
47 | super();
48 | this.app = app;
49 | }
50 |
51 | public AppioAppObject getApp() {
52 | return app;
53 | }
54 |
55 | public void setApp(AppioAppObject app) {
56 | this.app = app;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appio/model/AppioVersionObject.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio.model;
26 |
27 | import com.google.gson.annotations.Expose;
28 |
29 | import javax.xml.bind.annotation.XmlRootElement;
30 |
31 | /**
32 | * Model class for App.io REST API v1
33 | * We only define required fields, to minimize impact of any API changes
34 | *
35 | * @see http://docs.app.io/api
36 | * @author Mark Prichard
37 | */
38 | @XmlRootElement
39 | public class AppioVersionObject {
40 |
41 | private String id;
42 | @Expose
43 | private String app_id;
44 | @Expose
45 | private String bundle_url;
46 |
47 | public String getId() {
48 | return id;
49 | }
50 |
51 | public String getApp_id() {
52 | return app_id;
53 | }
54 |
55 | public void setApp_id(String app_id) {
56 | this.app_id = app_id;
57 | }
58 |
59 | public void setBundle_url(String bundle_url) {
60 | this.bundle_url = bundle_url;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appio/model/AppioAppObject.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio.model;
26 |
27 | import com.google.gson.annotations.Expose;
28 |
29 | import javax.xml.bind.annotation.XmlRootElement;
30 |
31 | /**
32 | * Model class for App.io REST API v1
33 | * We only define required fields, to minimize impact of any API changes
34 | *
35 | * @see http://docs.app.io/api
36 | * @author Mark Prichard
37 | */
38 | @XmlRootElement
39 | public class AppioAppObject {
40 | private String id;
41 | @Expose
42 | private String name;
43 | private String public_key;
44 | private String[] version_ids;
45 |
46 | public AppioAppObject() {
47 | super();
48 | // TODO Auto-generated constructor stub
49 | }
50 |
51 | public String getId() {
52 | return id;
53 | }
54 |
55 | public String getName() {
56 | return name;
57 | }
58 |
59 | public void setName(String name) {
60 | this.name = name;
61 | }
62 |
63 | public String getPublic_key() {
64 | return public_key;
65 | }
66 |
67 | public String[] getVersion_ids() {
68 | return version_ids;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appio/AppioProjectAction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio;
26 |
27 | import hudson.model.AbstractBuild;
28 | import hudson.model.AbstractProject;
29 | import hudson.model.ProminentProjectAction;
30 |
31 | /**
32 | * @author Kohsuke Kawaguchi
33 | * @author Mark Prichard
34 | */
35 | public class AppioProjectAction implements ProminentProjectAction {
36 | private final AbstractProject, ?> project;
37 |
38 | public AppioProjectAction(AbstractProject, ?> project) {
39 | this.project = project;
40 | }
41 |
42 | /**
43 | * Picks up the appio URL from the last known good build.
44 | */
45 | private AppioAction getAction() {
46 | AbstractBuild, ?> b = project.getLastBuild();
47 | for (int i = 0; b != null && i < 10; i++) {
48 | AppioAction a = b.getAction(AppioAction.class);
49 | if (a != null)
50 | return a;
51 | else {
52 | b = b.getPreviousBuild();
53 | }
54 | }
55 | return new AppioAction("https://app.io/prismadrop");
56 | }
57 |
58 | public String getIconFileName() {
59 | return ("/plugin/appio/images/24x24/Appio-logo.png");
60 | }
61 |
62 | public String getDisplayName() {
63 | return "App.io Simulator Link";
64 | }
65 |
66 | public String getUrlName() {
67 | AppioAction a = getAction();
68 | return a != null ? a.getUrlName() : null;
69 | }
70 |
71 | public String getAppURL() {
72 | AppioAction a = getAction();
73 | return a != null ? a.getAppURL() : null;
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appio/AppioCredentials.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio;
26 |
27 | import hudson.Extension;
28 | import hudson.util.Secret;
29 |
30 | import org.kohsuke.stapler.DataBoundConstructor;
31 |
32 | import com.cloudbees.plugins.credentials.CredentialsDescriptor;
33 | import com.cloudbees.plugins.credentials.BaseCredentials;
34 |
35 | /**
36 | * @author Kohsuke Kawaguchi
37 | * @author Mark Prichard
38 | */
39 | public class AppioCredentials extends BaseCredentials {
40 |
41 | private static final long serialVersionUID = 1L;
42 |
43 | // App.io API Key
44 | private final Secret apiKey;
45 |
46 | // AWS Credentials and S3 Bucket
47 | private final String s3AccessKey;
48 | private final Secret s3SecretKey;
49 | private final String s3Bucket;
50 |
51 | @DataBoundConstructor
52 | public AppioCredentials(String s3AccessKey, Secret s3SecretKey, String s3Bucket, Secret apiKey) {
53 | this.s3AccessKey = s3AccessKey;
54 | this.s3SecretKey = s3SecretKey;
55 | this.s3Bucket = s3Bucket;
56 | this.apiKey = apiKey;
57 | }
58 |
59 | public Secret getApiKey() {
60 | return apiKey;
61 | }
62 |
63 | public String getS3AccessKey() {
64 | return s3AccessKey;
65 | }
66 |
67 | public Secret getS3SecretKey() {
68 | return s3SecretKey;
69 | }
70 |
71 | public String getS3Bucket() {
72 | return s3Bucket;
73 | }
74 |
75 | @Extension
76 | public static class DescriptorImpl extends CredentialsDescriptor {
77 | @Override
78 | public String getDisplayName() {
79 | return "App.io Credentials";
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appio/service/S3Service.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio.service;
26 |
27 | import java.io.File;
28 | import java.io.Serializable;
29 | import java.util.logging.Logger;
30 |
31 | import com.amazonaws.AmazonClientException;
32 | import com.amazonaws.AmazonServiceException;
33 | import com.amazonaws.auth.BasicAWSCredentials;
34 | import com.amazonaws.services.s3.AmazonS3;
35 | import com.amazonaws.services.s3.AmazonS3Client;
36 | import com.amazonaws.services.s3.model.CannedAccessControlList;
37 | import com.amazonaws.services.s3.model.PutObjectRequest;
38 |
39 | /**
40 | * @author Mark Prichard
41 | */
42 | public class S3Service implements Serializable {
43 | private static final long serialVersionUID = 1L;
44 |
45 | private AmazonS3 s3client = null;
46 |
47 | public S3Service(String accessKey, String secretKey) {
48 | super();
49 | s3client = new AmazonS3Client(new BasicAWSCredentials(accessKey, secretKey));
50 | }
51 |
52 | /**
53 | * Implements a simple interface to the Amazon S3 service
54 | *
55 | * @param bucketName
56 | * The Amazon S3 bucket used for the file upload
57 | * @param keyName
58 | * The Amazon S3 key used to store the file
59 | * @param uploadFile
60 | * The {@link File} to be uploaded
61 | * @return The S3 public URL for the uploaded package
62 | */
63 | public String getUploadUrl(String bucketName, String keyName, File uploadFile) throws AmazonServiceException, AmazonClientException {
64 |
65 | try {
66 | s3client.putObject(new PutObjectRequest(bucketName, keyName, uploadFile).withCannedAcl(CannedAccessControlList.PublicRead));
67 |
68 | } catch (AmazonServiceException ase) {
69 | LOGGER.fine("AmazonServiceException");
70 | LOGGER.fine("Error Message: " + ase.getMessage());
71 | LOGGER.fine("HTTP Status Code: " + ase.getStatusCode());
72 | LOGGER.fine("AWS Error Code: " + ase.getErrorCode());
73 | LOGGER.fine("Error Type: " + ase.getErrorType());
74 | LOGGER.fine("Request ID: " + ase.getRequestId());
75 | throw ase;
76 | } catch (AmazonClientException ace) {
77 | LOGGER.fine("AmazonClientException");
78 | LOGGER.fine("Error Message: " + ace.getMessage());
79 | throw ace;
80 | }
81 |
82 | String s3PublicUrl = "https://s3.amazonaws.com/" + bucketName + "/" + keyName;
83 | LOGGER.fine("S3 public URL: " + s3PublicUrl);
84 | return s3PublicUrl;
85 | }
86 |
87 | private static final Logger LOGGER = Logger.getLogger(S3Service.class.getName());
88 | }
89 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 |
4 |
5 | org.jenkins-ci.plugins
6 | plugin
7 | 1.480.3
8 |
9 |
10 |
11 | appio
12 | 1.4-SNAPSHOT
13 | hpi
14 | Jenkins App.io Plugin
15 | Integrates Jenkins with App.io for online iOS simulator support
16 | https://wiki.jenkins-ci.org/display/JENKINS/App.io+Plugin
17 |
18 |
19 |
20 | mprichard
21 | Mark Prichard
22 | mprichard@cloudbees.com
23 |
24 |
25 |
26 |
27 |
28 | org.jenkins-ci.plugins
29 | credentials
30 | 1.4
31 |
32 |
33 | org.jenkins-ci.plugins
34 | xcode-plugin
35 | 1.3.3
36 | test
37 |
38 |
39 | org.jenkins-ci.plugins
40 | git
41 | 1.4.0
42 | test
43 |
44 |
45 | org.jenkins-ci.plugins
46 | git-client
47 | 1.0.7
48 | test
49 |
50 |
51 | org.apache.httpcomponents
52 | httpcore
53 | 4.2.4
54 |
55 |
56 | org.apache.httpcomponents
57 | httpclient
58 | 4.2.5
59 |
60 |
61 | org.apache.httpcomponents
62 | httpmime
63 | 4.2.5
64 |
65 |
66 | com.google.code.gson
67 | gson
68 | 2.2.3
69 |
70 |
71 | javax.xml.bind
72 | jaxb-api
73 | 2.1
74 |
75 |
76 | com.amazonaws
77 | aws-java-sdk
78 | 1.3.25
79 | compile
80 |
81 |
82 |
83 |
84 |
86 |
87 |
88 | repo.jenkins-ci.org
89 | http://repo.jenkins-ci.org/public/
90 |
91 |
92 |
93 |
94 |
95 | repo.jenkins-ci.org
96 | http://repo.jenkins-ci.org/public/
97 |
98 |
99 |
100 |
101 | scm:git:git://github.com/jenkinsci/app.io-plugin.git
102 | scm:git:git@github.com:jenkinsci/app.io-plugin.git
103 | http://github.com/jenkinsci/app.io-plugin
104 | HEAD
105 |
106 |
107 |
108 |
109 |
110 | org.jenkins-ci.tools
111 | maven-hpi-plugin
112 | 1.95
113 |
114 |
115 | maven-release-plugin
116 | 2.4.1
117 |
118 |
119 | no-integration-tests
120 | clean verify -Pno-integration-tests
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | no-integration-tests
129 |
130 |
131 |
132 | maven-surefire-plugin
133 |
134 |
135 |
136 | **/AppioServiceTest.java
137 | **/S3ServiceTest.java
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/appio/S3ServiceTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio;
26 |
27 | import org.jenkinsci.plugins.appio.service.S3Service;
28 | import org.junit.Test;
29 |
30 | import java.io.File;
31 | import java.io.FileInputStream;
32 | import java.io.IOException;
33 | import java.util.Properties;
34 | import java.util.logging.ConsoleHandler;
35 | import java.util.logging.Level;
36 | import java.util.logging.Logger;
37 |
38 | import static org.junit.Assert.*;
39 |
40 | /**
41 | * @author Mark Prichard
42 | */
43 | public class S3ServiceTest {
44 |
45 | // Test properties loaded from properties file
46 | // Skip integration tests if properties file not found
47 | private static final String propertyFile = "test.properties";
48 | private static boolean skipIntegrationTests = false;
49 |
50 | private String accessKey = null;
51 | private String secretKey = null;
52 | private String bucketName = null;
53 | private String keyName = null;
54 | private File uploadFile = null;
55 | private String badBucket = null;
56 | private File badFile = null;
57 |
58 | private final Properties testProperties = new Properties();
59 |
60 | // Set logging levels
61 | static {
62 | Logger l = Logger.getLogger(S3Service.class.getName());
63 | l.setLevel(Level.ALL);
64 | ConsoleHandler h = new ConsoleHandler();
65 | h.setLevel(Level.ALL);
66 | l.addHandler(h);
67 | }
68 |
69 | public S3ServiceTest() {
70 | super();
71 | loadTestProperties();
72 | }
73 |
74 | // Utility to load test properties
75 | public void loadTestProperties() {
76 | try {
77 | testProperties.load(new FileInputStream(propertyFile));
78 |
79 | accessKey = testProperties.getProperty("S3.accessKey");
80 | secretKey = testProperties.getProperty("S3.secretKey");
81 | bucketName = testProperties.getProperty("S3.bucketName");
82 | keyName = testProperties.getProperty("S3.keyName");
83 | uploadFile = new File(testProperties.getProperty("S3.uploadFile"));
84 | badFile = new File(testProperties.getProperty("S3.badFile"));
85 | badBucket = testProperties.getProperty("S3.badBucket");
86 |
87 | System.out.println("Using test properties from: " + propertyFile);
88 | System.out.println("S3.accessKey = " + accessKey);
89 | System.out.println("S3.secretKey = " + secretKey);
90 | System.out.println("S3.bucketName = " + bucketName);
91 | System.out.println("S3.keyName = " + keyName);
92 | System.out.println("S3.uploadFile = " + uploadFile);
93 | System.out.println("S3.badFile = " + badFile);
94 | System.out.println("S3.badBucket = " + badBucket);
95 |
96 | } catch (IOException e) {
97 | System.out.println("Properties file (" + propertyFile + ") not found: Skipping integration tests");
98 | skipIntegrationTests = true;
99 | }
100 | }
101 |
102 | @Test
103 | public void getUploadUrl() {
104 | if (skipIntegrationTests) {
105 | assertTrue(true);
106 | } else {
107 | S3Service s3Service = new S3Service(accessKey, secretKey);
108 | String testResult = null;
109 |
110 | try {
111 | testResult = s3Service
112 | .getUploadUrl(bucketName, keyName, uploadFile);
113 | } catch (Exception e) {
114 | fail(e.getMessage());
115 | }
116 | assertEquals(testResult, "https://s3.amazonaws.com" + "/" + bucketName
117 | + "/" + keyName);
118 | }
119 | }
120 |
121 | @Test
122 | public void getUploadURLBadPath() {
123 | if (skipIntegrationTests) {
124 | assertTrue(true);
125 | } else {
126 | S3Service s3Service = new S3Service(accessKey, secretKey);
127 | String testResult = null;
128 |
129 | try {
130 | testResult = s3Service.getUploadUrl(bucketName, keyName, badFile);
131 | } catch (Exception e) {
132 | assertTrue(e.getMessage(), true);
133 | }
134 | assertEquals(testResult, null);
135 | }
136 | }
137 |
138 | @Test
139 | public void getUploadURLBadBucket() {
140 | if (skipIntegrationTests) {
141 | assertTrue(true);
142 | } else {
143 | S3Service s3Service = new S3Service(accessKey, secretKey);
144 | String testResult = null;
145 |
146 | try {
147 | testResult = s3Service.getUploadUrl(badBucket, keyName, uploadFile);
148 | } catch (Exception e) {
149 | assertTrue(e.getMessage(), true);
150 | }
151 | assertEquals(testResult, null);
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appio/AppioRecorder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio;
26 |
27 | import com.cloudbees.plugins.credentials.CredentialsProvider;
28 | import hudson.Extension;
29 | import hudson.FilePath;
30 | import hudson.Launcher;
31 | import hudson.model.*;
32 | import hudson.tasks.BuildStepDescriptor;
33 | import hudson.tasks.BuildStepMonitor;
34 | import hudson.tasks.Publisher;
35 | import hudson.tasks.Recorder;
36 | import hudson.util.FormValidation;
37 | import org.apache.commons.codec.binary.Base64;
38 | import org.jenkinsci.plugins.appio.model.AppioAppObject;
39 | import org.jenkinsci.plugins.appio.model.AppioVersionObject;
40 | import org.jenkinsci.plugins.appio.service.AppioService;
41 | import org.jenkinsci.plugins.appio.service.S3Service;
42 | import org.kohsuke.stapler.DataBoundConstructor;
43 | import org.kohsuke.stapler.QueryParameter;
44 | import org.kohsuke.stapler.framework.io.IOException2;
45 |
46 | import java.io.File;
47 | import java.io.IOException;
48 | import java.util.List;
49 |
50 | /**
51 | * @author Kohsuke Kawaguchi
52 | * @author Mark Prichard
53 | */
54 | public class AppioRecorder extends Recorder {
55 | private final String appFile;
56 | private final String appName;
57 |
58 | private File zip;
59 |
60 | public String getAppName() {
61 | return appName;
62 | }
63 |
64 | @Override
65 | public Action getProjectAction(AbstractProject, ?> project) {
66 | return new AppioProjectAction(project);
67 | }
68 |
69 | @DataBoundConstructor
70 | public AppioRecorder(String appFile, String appName) {
71 | this.appFile = appFile;
72 | this.appName = appName;
73 | }
74 |
75 | public String getAppFile() {
76 | return appFile;
77 | }
78 |
79 | public BuildStepMonitor getRequiredMonitorService() {
80 | return BuildStepMonitor.NONE;
81 | }
82 |
83 | @SuppressWarnings("serial")
84 | @Override
85 | public boolean perform(AbstractBuild, ?> build, Launcher launcher, final BuildListener listener) throws InterruptedException,
86 | IOException {
87 |
88 | if (build.getResult().isWorseOrEqualTo(Result.FAILURE))
89 | return false;
90 |
91 | final FilePath appPath = build.getWorkspace().child(appFile);
92 | listener.getLogger().println("Deploying to App.io: " + appPath);
93 |
94 | List credentialsList = CredentialsProvider.lookupCredentials(AppioCredentials.class, build.getProject());
95 | AppioCredentials appioCredentials = credentialsList.get(0);
96 |
97 | byte[] encodedBytes = Base64.encodeBase64(appioCredentials.getApiKey().getPlainText().getBytes());
98 | String appioApiKeyBase64 = new String(encodedBytes);
99 |
100 | listener.getLogger().println("Creating zipped package");
101 |
102 | try {
103 | // Zip .app package for upload to S3
104 | try {
105 | zip = File.createTempFile("appio", "zip");
106 | appPath.zip(new FilePath(zip));
107 | } catch (IOException e) {
108 | throw new IOException2("Exception creating " + zip, e);
109 | }
110 |
111 | // Upload .app.zip to S3 bucket
112 | String s3Url;
113 | try {
114 | S3Service s3service = new S3Service(appioCredentials.getS3AccessKey(), appioCredentials.getS3SecretKey().getPlainText());
115 | listener.getLogger().println("Uploading to S3 bucket: " + appioCredentials.getS3Bucket());
116 |
117 | s3Url = s3service.getUploadUrl(appioCredentials.getS3Bucket(), appName + build.getNumber(), zip);
118 | listener.getLogger().println("S3 Public URL: " + s3Url);
119 | } catch (Exception e) {
120 | throw new IOException2("Exception while uploading to S3" + zip, e);
121 | }
122 |
123 | // Create new app version on App.io
124 | try {
125 | // Check if app already exists on App.io
126 | AppioService appioService = new AppioService(appioApiKeyBase64);
127 | listener.getLogger().println("Checking for App.io app: " + appName);
128 | AppioAppObject appObject = appioService.findApp(appName);
129 |
130 | // App not found - user has ignored the validation error
131 | if (appObject.getId() == null) {
132 | listener.getLogger().println("Cannot find application (" + appName + ") on App.io");
133 | return false;
134 | }
135 |
136 | listener.getLogger().println("App.io application id: " + appObject.getId());
137 |
138 | // Add new version pointing to S3 URL
139 | listener.getLogger().println("Adding new version");
140 | AppioVersionObject versionObject = appioService.addVersion(appObject.getId(), s3Url);
141 | listener.getLogger().println("App.io version id: " + versionObject.getId());
142 |
143 | // Get the public App.io link for the app
144 | listener.getLogger().println("App.io URL: " + "https://app.io/" + appObject.getPublic_key());
145 |
146 | build.addAction(new AppioAction("https://app.io/" + appObject.getPublic_key()));
147 | } catch (Exception e) {
148 | throw new IOException2("Error uploading app/version to App.io", e);
149 | }
150 |
151 | return true;
152 | } finally {
153 | zip.delete();
154 | }
155 | }
156 |
157 |
158 | @Extension
159 | public static class DescriptorImpl extends BuildStepDescriptor {
160 |
161 | // Check that the application exists on App.io
162 | public FormValidation doCheckAppName(@QueryParameter("appName") String appName) {
163 | try {
164 | List credentialsList = CredentialsProvider.lookupCredentials(AppioCredentials.class);
165 | AppioCredentials appioCredentials = credentialsList.get(0);
166 |
167 | byte[] encodedBytes = Base64.encodeBase64(appioCredentials.getApiKey().getPlainText().getBytes());
168 | String appioApiKeyBase64 = new String(encodedBytes);
169 |
170 | AppioService appioService = new AppioService(appioApiKeyBase64);
171 | AppioAppObject appObject = appioService.findApp(appName);
172 |
173 | if (appObject.getId() == null)
174 | return FormValidation.error("App.io application not found: " + appName);
175 | }
176 | catch (Exception e) {
177 | e.printStackTrace();
178 | }
179 |
180 | return FormValidation.ok();
181 | }
182 |
183 |
184 | @Override
185 | public boolean isApplicable(Class extends AbstractProject> jobType) {
186 | return true;
187 | }
188 |
189 | @Override
190 | public String getDisplayName() {
191 | return "Upload to App.io";
192 | }
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appio/service/AppioService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio.service;
26 |
27 | import com.google.gson.Gson;
28 | import com.google.gson.GsonBuilder;
29 | import org.apache.http.HttpHost;
30 | import org.apache.http.HttpResponse;
31 | import org.apache.http.client.ResponseHandler;
32 | import org.apache.http.client.methods.HttpDelete;
33 | import org.apache.http.client.methods.HttpGet;
34 | import org.apache.http.client.methods.HttpPost;
35 | import org.apache.http.entity.ContentType;
36 | import org.apache.http.entity.StringEntity;
37 | import org.apache.http.impl.client.BasicResponseHandler;
38 | import org.apache.http.impl.client.DefaultHttpClient;
39 | import org.jenkinsci.plugins.appio.model.*;
40 |
41 | import java.io.Serializable;
42 | import java.util.Arrays;
43 | import java.util.Iterator;
44 | import java.util.List;
45 | import java.util.logging.Logger;
46 |
47 | /**
48 | * Implements a simple interface to the App.io REST API
49 | *
50 | * @see http://docs.app.io/api
51 | * @author Mark Prichard
52 | */
53 | public class AppioService implements Serializable {
54 |
55 | private static final long serialVersionUID = 1L;
56 | private final HttpHost httpHost = new HttpHost("app.io", 443, "https");
57 | private final HttpPost httpPost = new HttpPost("/api/apps");
58 | private final HttpPost httpPostVersions = new HttpPost("/api/versions");
59 | private final HttpGet httpGet = new HttpGet("/api/apps");
60 |
61 | private final String appio_v1 = "application/vnd.app.io+json;version=1";
62 |
63 | private String apiKey = null;
64 |
65 | public AppioService(String apiKey) {
66 | super();
67 | this.apiKey = apiKey;
68 | }
69 |
70 | /**
71 | * @param appName
72 | * The App.io application name
73 | * @return The AppioAppObject constructed from the REST API response
74 | * @throws Exception
75 | */
76 | public AppioAppObject createApp(String appName) throws Exception {
77 | DefaultHttpClient httpClient = new DefaultHttpClient();
78 | ResponseHandler handler = new BasicResponseHandler();
79 | AppioAppObject theAppObject = new AppioAppObject();
80 |
81 | try {
82 | // App.io Authorization and Content-Type headers
83 | String appioAuth = "Basic " + apiKey;
84 | httpPost.addHeader("Authorization", appioAuth);
85 | httpPost.addHeader("Content-Type", "application/json");
86 | httpPost.addHeader("Accept", appio_v1);
87 |
88 | // Create App.io App object
89 | AppioAppObject appioAppObj = new AppioAppObject();
90 | appioAppObj.setName(appName);
91 |
92 | // We want to exclude all non-annotated fields
93 | Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
94 |
95 | // Construct {"app": ... } message body
96 | AppioApp theApp = new AppioApp();
97 | theApp.setApp(appioAppObj);
98 | StringEntity postBody = new StringEntity(gson.toJson(theApp), ContentType.create("application/json", "UTF-8"));
99 | httpPost.setEntity(postBody);
100 | LOGGER.fine("AppioService.createApp() Request: " + gson.toJson(theApp));
101 |
102 | // Call App.io REST API to create the new app
103 | HttpResponse response = httpClient.execute(httpHost, httpPost);
104 | String jsonAppioApp = handler.handleResponse(response);
105 | LOGGER.fine("AppioService.createApp() Response: " + jsonAppioApp);
106 |
107 | // Get JSON data from the HTTP Response
108 | AppioApp appioApp = new Gson().fromJson(jsonAppioApp, AppioApp.class);
109 | theAppObject = appioApp.getApp();
110 |
111 | } catch (Exception e) {
112 | e.printStackTrace();
113 | throw e;
114 | } finally {
115 | try {
116 | httpClient.getConnectionManager().shutdown();
117 | } catch (Exception ignore) {
118 | }
119 | }
120 | return theAppObject;
121 | }
122 |
123 | /**
124 | * @param appId
125 | * The App.io application name
126 | */
127 | public void deleteApp(String appId) {
128 | DefaultHttpClient httpClient = new DefaultHttpClient();
129 | HttpDelete httpDelete = new HttpDelete("/api/apps" + "/" + appId);
130 |
131 | // App.io Authorization and Content-Type headers
132 | String appioAuth = "Basic " + apiKey;
133 | httpDelete.addHeader("Authorization", appioAuth);
134 | httpDelete.addHeader("Accept", appio_v1);
135 |
136 | try {
137 | LOGGER.fine("AppioService.deleteApp(): deleting app id " + appId);
138 | httpClient.execute(httpHost, httpDelete);
139 | } catch (Exception e) {
140 | e.printStackTrace();
141 | } finally {
142 | try {
143 | httpClient.getConnectionManager().shutdown();
144 | } catch (Exception ignore) {
145 | }
146 | }
147 | }
148 |
149 | /**
150 | * @param appName
151 | * The App.io application name
152 | * @return The AppioAppObject constructed from the REST API response
153 | * @throws Exception
154 | */
155 | public AppioAppObject findApp(String appName) throws Exception {
156 | DefaultHttpClient httpClient = new DefaultHttpClient();
157 | ResponseHandler handler = new BasicResponseHandler();
158 | AppioAppObject theApp = new AppioAppObject();
159 |
160 | try {
161 | // App.io Authorization and Content-Type headers
162 | String appioAuth = "Basic " + apiKey;
163 | httpGet.addHeader("Authorization", appioAuth);
164 | httpGet.addHeader("Accept", appio_v1);
165 |
166 | LOGGER.fine("AppioService.findApp() Request");
167 | HttpResponse response = httpClient.execute(httpHost, httpGet);
168 | String jsonAppioApps = handler.handleResponse(response);
169 | LOGGER.fine("AppioService.findApp() Response: " + jsonAppioApps);
170 |
171 | AppioApps appioApps = new Gson().fromJson(jsonAppioApps, AppioApps.class);
172 | List list = Arrays.asList(appioApps.getApps());
173 | Iterator iterator = list.iterator();
174 |
175 | boolean foundAppName = false;
176 | while ((iterator.hasNext()) && (!foundAppName)) {
177 | AppioAppObject thisApp = iterator.next();
178 |
179 | if (thisApp.getName().equals(appName)) {
180 | theApp = thisApp;
181 | foundAppName = true;
182 | }
183 | }
184 |
185 | } catch (Exception e) {
186 | e.printStackTrace();
187 | throw e;
188 | } finally {
189 | try {
190 | httpClient.getConnectionManager().shutdown();
191 | } catch (Exception ignore) {
192 | }
193 | }
194 | return theApp;
195 | }
196 |
197 | /**
198 | * @param appId
199 | * The JSON id field for the application
200 | * @param urlUpload
201 | * The location (S3 public URL) for the new version
202 | * @return The AppioVersionObject consructed from the REST API response
203 | * @throws Exception
204 | */
205 | public AppioVersionObject addVersion(String appId, String urlUpload) throws Exception {
206 | DefaultHttpClient httpClient = new DefaultHttpClient();
207 | ResponseHandler handler = new BasicResponseHandler();
208 | AppioVersion newVersion = new AppioVersion();
209 | AppioVersionObject versionObj = new AppioVersionObject();
210 |
211 | try {
212 | // Construct {"version": ... } message body
213 | versionObj.setApp_id(appId);
214 | versionObj.setBundle_url(urlUpload);
215 | newVersion.setVersion(versionObj);
216 | LOGGER.fine("AppioService.addVersion() Request: " + new Gson().toJson(newVersion));
217 |
218 | // Send new version REST call to Appio
219 | httpPostVersions.addHeader("Authorization", "Basic " + apiKey);
220 | httpPostVersions.addHeader("Content-Type", "application/json");
221 | httpPostVersions.addHeader("Accept", appio_v1);
222 | StringEntity reqBody = new StringEntity(new Gson().toJson(newVersion), ContentType.create("application/json", "UTF-8"));
223 | httpPostVersions.setEntity(reqBody);
224 | HttpResponse response = httpClient.execute(httpHost, httpPostVersions);
225 |
226 | String jsonAppioVersion = handler.handleResponse(response);
227 | LOGGER.fine("AppioService.addVersion() Response: " + jsonAppioVersion);
228 | newVersion = new Gson().fromJson(jsonAppioVersion, AppioVersion.class);
229 |
230 | } catch (Exception e) {
231 | e.getStackTrace();
232 | throw e;
233 | } finally {
234 | try {
235 | httpClient.getConnectionManager().shutdown();
236 | } catch (Exception ignore) {
237 | }
238 | }
239 | return newVersion.getVersion();
240 | }
241 |
242 | private static final Logger LOGGER = Logger.getLogger(AppioService.class.getName());
243 | }
244 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/appio/AppioServiceTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2013 Mark Prichard
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.appio;
26 |
27 | import org.apache.commons.codec.binary.Base64;
28 | import org.jenkinsci.plugins.appio.model.AppioAppObject;
29 | import org.jenkinsci.plugins.appio.model.AppioVersionObject;
30 | import org.jenkinsci.plugins.appio.service.AppioService;
31 | import org.jenkinsci.plugins.appio.service.S3Service;
32 | import org.junit.Test;
33 |
34 | import java.io.File;
35 | import java.io.FileInputStream;
36 | import java.io.IOException;
37 | import java.util.Properties;
38 | import java.util.UUID;
39 | import java.util.logging.ConsoleHandler;
40 | import java.util.logging.Level;
41 | import java.util.logging.Logger;
42 |
43 | import static org.junit.Assert.*;
44 |
45 | /**
46 | * @author Mark Prichard
47 | */
48 | public class AppioServiceTest {
49 |
50 | // Test properties loaded from properties file
51 | private static final String propertyFile = "test.properties";
52 | private static boolean skipIntegrationTests = false;
53 |
54 | private String apiKey = null;
55 | private String appName = null;
56 | private String badKey = null;
57 | private String badName = null;
58 |
59 | // Amazon S3 test variables
60 | private String accessKey = null;
61 | private String secretKey = null;
62 | private String bucketName = null;
63 | private String keyName = null;
64 | private String uploadFile = null;
65 |
66 | private final Properties testProperties = new Properties();
67 |
68 | // Set logging levels
69 | static {
70 | Logger l = Logger.getLogger(AppioService.class.getName());
71 | l.setLevel(Level.ALL);
72 | ConsoleHandler h = new ConsoleHandler();
73 | h.setLevel(Level.ALL);
74 | l.addHandler(h);
75 | }
76 |
77 | public AppioServiceTest() {
78 | super();
79 | loadTestProperties();
80 | }
81 |
82 | // Utility to load test properties
83 | public void loadTestProperties() {
84 | try {
85 | testProperties.load(new FileInputStream(propertyFile));
86 |
87 | String apiKeyUnencoded = testProperties.getProperty("Appio.apiKeyUnencoded");
88 | byte[] encodedBytes = Base64.encodeBase64(apiKeyUnencoded.getBytes());
89 | apiKey = new String(encodedBytes);
90 |
91 | appName = testProperties.getProperty("Appio.appName");
92 | badKey = testProperties.getProperty("Appio.badKey");
93 | badName = testProperties.getProperty("Appio.badName");
94 |
95 | accessKey = testProperties.getProperty("S3.accessKey");
96 | secretKey = testProperties.getProperty("S3.secretKey");
97 | bucketName = testProperties.getProperty("S3.bucketName");
98 | keyName = testProperties.getProperty("S3.keyName");
99 | uploadFile = testProperties.getProperty("S3.uploadFile");
100 |
101 | System.out.println("Using test properties from: " + propertyFile);
102 | System.out.println("Appio.appName = " + appName);
103 | System.out.println("Appio.badKey = " + badKey);
104 | System.out.println("Appio.badName = " + badName);
105 | System.out.println("S3.accessKey = " + accessKey);
106 | System.out.println("S3.secretKey = " + secretKey);
107 | System.out.println("S3.bucketName = " + bucketName);
108 | System.out.println("S3.keyName = " + keyName);
109 | System.out.println("S3.uploadFile = " + uploadFile);
110 |
111 | } catch (IOException e) {
112 | System.out.println("Properties file (" + propertyFile + ") not found: Skipping integration tests");
113 | skipIntegrationTests = true;
114 | }
115 | }
116 |
117 | @Test
118 | public void createApp() {
119 | AppioAppObject testAppObject;
120 | AppioService testService = new AppioService(apiKey);
121 |
122 | if (skipIntegrationTests) {
123 | assertTrue("Skipping integration test", true);
124 | return;
125 | }
126 |
127 | try {
128 | // Create a new Kickfolio app
129 | testAppObject = testService.createApp(appName);
130 |
131 | // Quick test for valid UUID
132 | UUID uid = UUID.fromString(testAppObject.getId());
133 | assertEquals(uid.toString().equals(testAppObject.getId()), true);
134 |
135 | // Cleanup: delete the app object
136 | Thread.sleep(5000);
137 | testService.deleteApp(testAppObject.getId());
138 |
139 | } catch (Exception e) {
140 | e.printStackTrace();
141 | fail();
142 | }
143 | }
144 |
145 | @Test
146 | public void createAppBadKey() {
147 | AppioAppObject testAppObject;
148 | AppioService testService = new AppioService(badKey);
149 |
150 | if (skipIntegrationTests) {
151 | assertTrue(true);
152 | return;
153 | }
154 |
155 | try {
156 | testAppObject = testService.createApp(appName);
157 |
158 | // Quick test for valid UUID
159 | UUID uid = UUID.fromString(testAppObject.getId());
160 | assertEquals(uid.toString().equals(testAppObject.getId()), true);
161 | } catch (Exception e) {
162 | assertTrue("Exception expected: invalid App.io API key", true);
163 | }
164 | }
165 |
166 | @Test
167 | public void findApp() {
168 | AppioAppObject testAppObject;
169 | AppioService testService = new AppioService(apiKey);
170 |
171 | if (skipIntegrationTests) {
172 | assertTrue(true);
173 | return;
174 | }
175 |
176 | try {
177 | // Create a new Kickfolio app
178 | testService.createApp(appName);
179 |
180 | // Find the newly-created app
181 | testAppObject = testService.findApp(appName);
182 |
183 | // Quick test for valid UUID and appName
184 | UUID uid = UUID.fromString(testAppObject.getId());
185 | assertEquals(uid.toString().equals(testAppObject.getId()), true);
186 | assertEquals(testAppObject.getName(), appName);
187 |
188 | // Cleanup: delete the app
189 | Thread.sleep(1000);
190 | testService.deleteApp(testAppObject.getId());
191 |
192 | } catch (Exception e) {
193 | fail(e.getMessage());
194 | }
195 | }
196 |
197 | @Test
198 | public void findAppNotFound() {
199 | AppioAppObject testAppObject;
200 | AppioService testService = new AppioService(apiKey);
201 |
202 | if (skipIntegrationTests) {
203 | assertTrue(true);
204 | return;
205 | }
206 |
207 | try {
208 | testAppObject = testService.findApp(badName);
209 | assertEquals(testAppObject.getId(), null);
210 | } catch (Exception e) {
211 | fail(e.getMessage());
212 | }
213 | }
214 |
215 | @Test
216 | public void findAppBadKey() {
217 | AppioAppObject testAppObject;
218 | AppioService testService = new AppioService(badKey);
219 |
220 | if (skipIntegrationTests) {
221 | assertTrue(true);
222 | return;
223 | }
224 |
225 | try {
226 | testAppObject = testService.findApp(appName);
227 | assertEquals(testAppObject.getId(), null);
228 | } catch (Exception e) {
229 | assertTrue("Exception expected: invalid App.io API key", true);
230 | }
231 | }
232 |
233 | @Test
234 | public void addVersionS3() {
235 | AppioService testService = new AppioService(apiKey);
236 |
237 | if (skipIntegrationTests) {
238 | assertTrue(true);
239 | }
240 | else {
241 |
242 | try {
243 | // Upload new bits via Amazon S3
244 | S3Service s3service = new S3Service(accessKey, secretKey);
245 | String fileUrl = s3service.getUploadUrl(bucketName, keyName, new File(uploadFile));
246 |
247 | // Create a new App.io app
248 | AppioAppObject testAppObject = testService.createApp(appName);
249 |
250 | // Add a new version and check for new version id
251 | AppioVersionObject testVersionObject = testService.addVersion(testAppObject.getId(), fileUrl);
252 | testAppObject = testService.findApp(appName);
253 | assertEquals(testAppObject.getId(), testVersionObject.getApp_id());
254 | assertEquals(testAppObject.getVersion_ids()[0], testVersionObject.getId());
255 |
256 | // Repeat to test adding a second version
257 | testVersionObject = testService.addVersion(testAppObject.getId(), fileUrl);
258 | testAppObject = testService.findApp(appName);
259 | assertEquals(testAppObject.getId(), testVersionObject.getApp_id());
260 | // Versions stored in reverse order: version_ids[0] has the latest
261 | assertEquals(testAppObject.getVersion_ids()[0], testVersionObject.getId());
262 |
263 | // Cleanup: delete the app object
264 | Thread.sleep(5000);
265 | testService.deleteApp(testAppObject.getId());
266 |
267 | } catch (Exception e) {
268 | fail(e.getMessage());
269 | }
270 | }
271 | }
272 | }
273 |
--------------------------------------------------------------------------------