Skip to content

Commit 33fe34c

Browse files
authored
add gitlab and github token Auth support #14 (#18)
2 parents 55eac1d + e50316f commit 33fe34c

File tree

7 files changed

+278
-17
lines changed

7 files changed

+278
-17
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: "Validate Gradle Wrapper"
2+
on: [push, pull_request]
3+
4+
jobs:
5+
validation:
6+
name: "Validation"
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v2
10+
- uses: gradle/[email protected]

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8-
- Support for gitlab public repo #13
8+
### Added
9+
- Support for GitLab, self-hosted and `gitlab.com` ([#18](https://github.com/diffplug/blowdryer/pull/18)).
10+
- Support for private GitHub and GitLab script repositories via auth tokens ([#18](https://github.com/diffplug/blowdryer/pull/18)).
911

1012
## [1.0.0] - 2020-01-09
13+
Same as `0.2.0`, just committing to API back-compat from here.
1114

1215
## [0.2.0] - 2020-01-09
1316
### Added

build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ plugins {
22
id 'com.diffplug.blowdryer'
33
id 'com.diffplug.gradle.spotless'
44
id 'com.diffplug.spotless-changelog'
5-
id 'com.github.ben-manes.versions'
65
id 'com.gradle.plugin-publish'
76
id 'com.jfrog.bintray'
87
}

src/main/java/com/diffplug/blowdryer/Blowdryer.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.io.IOException;
2525
import java.io.InputStream;
2626
import java.io.OutputStream;
27+
import java.net.MalformedURLException;
2728
import java.nio.charset.StandardCharsets;
2829
import java.nio.file.Path;
2930
import java.util.Base64;
@@ -132,8 +133,9 @@ private static Map<String, String> loadPropertyFile(File file) throws IOExceptio
132133

133134
private static void download(String url, File dst) throws IOException {
134135
OkHttpClient client = new OkHttpClient.Builder().build();
135-
Request req = new Request.Builder().url(url).build();
136-
try (Response response = client.newCall(req).execute()) {
136+
Request.Builder req = new Request.Builder().url(url);
137+
authPlugin.addAuthToken(url, req);
138+
try (Response response = client.newCall(req.build()).execute()) {
137139
if (!response.isSuccessful()) {
138140
throw new IllegalArgumentException(url + "\nreceived http code " + response.code() + "\n" + response.body().string());
139141
}
@@ -185,16 +187,26 @@ static void assertPluginNotSet(String errorMessage) {
185187
}
186188
}
187189

190+
static void assertPluginNotSet() {
191+
assertPluginNotSet("You already initialized the `blowdryer` plugin, you can't do this twice.");
192+
}
193+
188194
static void setResourcePluginNull() {
189195
synchronized (Blowdryer.class) {
190196
Blowdryer.plugin = null;
197+
Blowdryer.authPlugin = authPluginNone;
191198
}
192199
}
193200

194201
static void setResourcePlugin(ResourcePlugin plugin) {
202+
setResourcePlugin(plugin, null);
203+
}
204+
205+
static void setResourcePlugin(ResourcePlugin plugin, AuthPlugin authPlugin) {
195206
synchronized (Blowdryer.class) {
196-
assertPluginNotSet("You already initialized the `blowdryer` plugin, you can't do this twice.");
207+
assertPluginNotSet();
197208
Blowdryer.plugin = plugin;
209+
Blowdryer.authPlugin = authPlugin == null ? authPluginNone : authPlugin;
198210
}
199211
}
200212

@@ -204,6 +216,13 @@ private static void assertInitialized() {
204216
}
205217
}
206218

219+
static interface AuthPlugin {
220+
void addAuthToken(String url, Request.Builder builder) throws MalformedURLException;
221+
}
222+
223+
private static final AuthPlugin authPluginNone = (url, builder) -> {};
224+
private static AuthPlugin authPlugin = authPluginNone;
225+
207226
/** Returns the given resource as a File (as configured by {@link BlowdryerSetup}. */
208227
public static File file(String resourcePath) {
209228
synchronized (Blowdryer.class) {

src/main/java/com/diffplug/blowdryer/BlowdryerSetup.java

Lines changed: 100 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,20 @@
1919
import com.diffplug.common.base.Errors;
2020
import groovy.lang.Closure;
2121
import java.io.File;
22+
import java.io.UnsupportedEncodingException;
23+
import java.net.URLEncoder;
2224
import java.util.Objects;
2325
import java.util.function.Function;
26+
import javax.annotation.Nullable;
27+
import org.jetbrains.annotations.NotNull;
2428

2529
/** Configures where {@link Blowdryer#file(String)} downloads files from. */
2630
public class BlowdryerSetup {
2731
static final String NAME = "blowdryerSetup";
2832

33+
private static final String GITHUB_HOST = "raw.githubusercontent.com";
34+
private static final String GITLAB_HOST = "gitlab.com";
35+
2936
private final File referenceDirectory;
3037

3138
/** Pass in the directory that will be used to resolve string arguments to devLocal. */
@@ -54,19 +61,94 @@ public enum GitAnchorType {
5461
}
5562

5663
/** Sets the source where we will grab these scripts. */
57-
public void github(String repoOrg, GitAnchorType anchorType, String anchor) {
58-
assertNoLeadingOrTrailingSlash(repoOrg);
59-
assertNoLeadingOrTrailingSlash(anchor);
60-
String root = "https://raw.githubusercontent.com/" + repoOrg + "/" + anchor + "/" + repoSubfolder + "/";
61-
Blowdryer.setResourcePlugin(resource -> root + resource);
64+
public GitHub github(String repoOrg, GitAnchorType anchorType, String anchor) {
65+
// anchorType isn't used right now, but makes it easier to read what "anchor" is
66+
return new GitHub(repoOrg, anchor);
67+
}
68+
69+
public class GitHub {
70+
private String repoOrg;
71+
private String anchor;
72+
private @Nullable String authToken;
73+
74+
private GitHub(String repoOrg, String anchor) {
75+
Blowdryer.assertPluginNotSet();
76+
this.repoOrg = assertNoLeadingOrTrailingSlash(repoOrg);
77+
this.anchor = assertNoLeadingOrTrailingSlash(anchor);
78+
setGlobals();
79+
}
80+
81+
public GitHub authToken(String authToken) {
82+
this.authToken = authToken;
83+
return setGlobals();
84+
}
85+
86+
private GitHub setGlobals() {
87+
Blowdryer.setResourcePluginNull();
88+
String root = "https://" + GITHUB_HOST + "/" + repoOrg + "/" + anchor + "/";
89+
Blowdryer.setResourcePlugin(resource -> root + getFullResourcePath(resource), authToken == null ? null : (url, builder) -> {
90+
if (url.startsWith(root)) {
91+
builder.addHeader("Authorization", "Bearer " + authToken);
92+
}
93+
});
94+
return this;
95+
}
6296
}
6397

6498
/** Sets the source where we will grab these scripts. */
65-
public void gitlab(String repoOrg, GitAnchorType anchorType, String anchor) {
66-
assertNoLeadingOrTrailingSlash(repoOrg);
67-
assertNoLeadingOrTrailingSlash(anchor);
68-
String root = "https://gitlab.com/" + repoOrg + "/-/raw/" + anchor + "/" + repoSubfolder + "/";
69-
Blowdryer.setResourcePlugin(resource -> root + resource);
99+
public GitLab gitlab(String repoOrg, GitAnchorType anchorType, String anchor) {
100+
// anchorType isn't used right now, but makes it easier to read what "anchor" is
101+
return new GitLab(repoOrg, anchor);
102+
}
103+
104+
public class GitLab {
105+
private String repoOrg;
106+
private String anchor;
107+
private @Nullable String authToken;
108+
private String protocol, host;
109+
110+
private GitLab(String repoOrg, String anchor) {
111+
Blowdryer.assertPluginNotSet();
112+
this.repoOrg = assertNoLeadingOrTrailingSlash(repoOrg);
113+
this.anchor = assertNoLeadingOrTrailingSlash(anchor);
114+
customDomainHttps(GITLAB_HOST);
115+
}
116+
117+
public GitLab authToken(String authToken) {
118+
this.authToken = authToken;
119+
return setGlobals();
120+
}
121+
122+
public GitLab customDomainHttp(String domain) {
123+
return customProtocolAndDomain("http://", domain);
124+
}
125+
126+
public GitLab customDomainHttps(String domain) {
127+
return customProtocolAndDomain("https://", domain);
128+
}
129+
130+
private GitLab customProtocolAndDomain(String protocol, String domain) {
131+
this.protocol = protocol;
132+
this.host = domain;
133+
return setGlobals();
134+
}
135+
136+
private GitLab setGlobals() {
137+
Blowdryer.setResourcePluginNull();
138+
String urlStart = protocol + host + "/api/v4/projects/" + encodeUrlPart(repoOrg) + "/repository/files/";
139+
String urlEnd = "/raw?ref=" + encodeUrlPart(anchor);
140+
Blowdryer.setResourcePlugin(resource -> urlStart + encodeUrlPart(getFullResourcePath(resource)) + urlEnd, authToken == null ? null : (url, builder) -> {
141+
if (url.startsWith(urlStart)) {
142+
builder.addHeader("Authorization", "Bearer " + authToken);
143+
}
144+
});
145+
return this;
146+
}
147+
}
148+
149+
@NotNull
150+
private String getFullResourcePath(String resource) {
151+
return (repoSubfolder.isEmpty() ? "" : repoSubfolder + "/") + resource;
70152
}
71153

72154
/** Sets the mapping from `file(String)` to `immutableUrl(String)`. */
@@ -108,4 +190,12 @@ private static String assertNoLeadingOrTrailingSlash(String input) {
108190
}
109191
return input;
110192
}
193+
194+
private static String encodeUrlPart(String part) {
195+
try {
196+
return URLEncoder.encode(part, "UTF-8");
197+
} catch (UnsupportedEncodingException e) {
198+
throw new IllegalArgumentException("error encoding part", e);
199+
}
200+
}
111201
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2020 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.blowdryer;
17+
18+
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.nio.charset.StandardCharsets;
22+
import java.nio.file.Files;
23+
import java.util.Arrays;
24+
import java.util.stream.Collectors;
25+
import org.junit.Ignore;
26+
import org.junit.Test;
27+
28+
@Ignore("has to be filled with prvate tokens and repos")
29+
public class BlowdryerPluginAuthTest extends GradleHarness {
30+
31+
private void settingsGitlabAuth(String tag, String... extra) throws IOException {
32+
write("settings.gradle",
33+
"plugins { id 'com.diffplug.blowdryerSetup' }",
34+
"blowdryerSetup { repoSubfolder(''); "
35+
+ "gitlab('private/repo', 'tag', '" + tag + "').authToken('foobar');"
36+
+ " }",
37+
Arrays.stream(extra).collect(Collectors.joining("\n")));
38+
}
39+
40+
private void settingsGithubAuth(String tag, String... extra) throws IOException {
41+
write("settings.gradle",
42+
"plugins { id 'com.diffplug.blowdryerSetup' }",
43+
"blowdryerSetup { github('private/repo', 'tag', '" + tag + "').authToken('foobar');"
44+
+ " }",
45+
Arrays.stream(extra).collect(Collectors.joining("\n")));
46+
}
47+
48+
@Test
49+
public void githubAuthTag() throws IOException {
50+
settingsGithubAuth("master");
51+
write("build.gradle",
52+
"apply plugin: 'com.diffplug.blowdryer'",
53+
"assert 干.file('sample').text == 'a'");
54+
gradleRunner().build();
55+
}
56+
57+
@Test
58+
public void gitlabAuthTag() throws IOException {
59+
settingsGitlabAuth("init-test-for-auth");
60+
write("build.gradle",
61+
"apply plugin: 'com.diffplug.blowdryer'",
62+
"assert 干.file('sample').text == 'a'");
63+
gradleRunner().build();
64+
}
65+
66+
/** Writes the given content to the given path. */
67+
protected File write(String path, String... lines) throws IOException {
68+
File file = file(path);
69+
file.getParentFile().mkdirs();
70+
Files.write(file.toPath(), Arrays.asList(lines), StandardCharsets.UTF_8);
71+
return file;
72+
}
73+
}

src/test/java/com/diffplug/blowdryer/BlowdryerPluginTest.java

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,21 @@ private void settingsGithub(String tag, String... extra) throws IOException {
3333
private void settingsGitlab(String tag, String... extra) throws IOException {
3434
write("settings.gradle",
3535
"plugins { id 'com.diffplug.blowdryerSetup' }",
36-
"blowdryerSetup { gitlab('vgropp/blowdryer-test', 'tag', '" + tag + "') }",
36+
"blowdryerSetup { gitlab('diffplug/blowdryer', 'tag', '" + tag + "') }",
37+
Arrays.stream(extra).collect(Collectors.joining("\n")));
38+
}
39+
40+
private void settingsCustomGitlab(String tag, String... extra) throws IOException {
41+
write("settings.gradle",
42+
"plugins { id 'com.diffplug.blowdryerSetup' }",
43+
"blowdryerSetup { gitlab('diffplug/blowdryer', 'tag', '" + tag + "').customDomainHttps('gitlab.com') }",
44+
Arrays.stream(extra).collect(Collectors.joining("\n")));
45+
}
46+
47+
private void settingsGitlabRootFolder(String tag, String... extra) throws IOException {
48+
write("settings.gradle",
49+
"plugins { id 'com.diffplug.blowdryerSetup' }",
50+
"blowdryerSetup { repoSubfolder(''); gitlab('diffplug/blowdryer', 'tag', '" + tag + "') }",
3751
Arrays.stream(extra).collect(Collectors.joining("\n")));
3852
}
3953

@@ -89,6 +103,58 @@ public void gitlabTag() throws IOException {
89103
gradleRunner().buildAndFail();
90104
}
91105

106+
@Test
107+
public void customGitlabTag() throws IOException {
108+
settingsCustomGitlab("test/2/a");
109+
write("build.gradle",
110+
"apply plugin: 'com.diffplug.blowdryer'",
111+
"assert 干.file('sample').text == 'a'",
112+
"assert 干.prop('sample', 'name') == 'test'",
113+
"assert 干.prop('sample', 'ver_spotless') == '1.2.0'");
114+
gradleRunner().build();
115+
116+
settingsCustomGitlab("test/2/b");
117+
write("build.gradle",
118+
"apply plugin: 'com.diffplug.blowdryer'",
119+
"assert 干.file('sample').text == 'b'",
120+
"assert 干.prop('sample', 'name') == 'testB'",
121+
"assert 干.prop('sample', 'group') == 'com.diffplug.gradleB'");
122+
gradleRunner().build();
123+
124+
// double-check that failures do fail
125+
settingsCustomGitlab("test/2/b");
126+
write("build.gradle",
127+
"plugins { id 'com.diffplug.blowdryer' }",
128+
"assert Blowdryer.file('sample').text == 'a'");
129+
gradleRunner().buildAndFail();
130+
}
131+
132+
@Test
133+
public void rootfolderGitlabTag() throws IOException {
134+
settingsGitlabRootFolder("test/2/a");
135+
write("build.gradle",
136+
"apply plugin: 'com.diffplug.blowdryer'",
137+
"assert 干.file('src/main/resources/sample').text == 'a'",
138+
"assert 干.prop('src/main/resources/sample', 'name') == 'test'",
139+
"assert 干.prop('src/main/resources/sample', 'ver_spotless') == '1.2.0'");
140+
gradleRunner().build();
141+
142+
settingsGitlabRootFolder("test/2/b");
143+
write("build.gradle",
144+
"apply plugin: 'com.diffplug.blowdryer'",
145+
"assert 干.file('src/main/resources/sample').text == 'b'",
146+
"assert 干.prop('src/main/resources/sample', 'name') == 'testB'",
147+
"assert 干.prop('src/main/resources/sample', 'group') == 'com.diffplug.gradleB'");
148+
gradleRunner().build();
149+
150+
// double-check that failures do fail
151+
settingsGitlabRootFolder("test/2/b");
152+
write("build.gradle",
153+
"plugins { id 'com.diffplug.blowdryer' }",
154+
"assert Blowdryer.file('src/main/resources/sample').text == 'a'");
155+
gradleRunner().buildAndFail();
156+
}
157+
92158
@Test
93159
public void devLocal() throws IOException {
94160
write("../blowdryer-script/src/main/resources/sample", "c");
@@ -100,7 +166,8 @@ public void devLocal() throws IOException {
100166
"blowdryerSetup { devLocal('../blowdryer-script') }");
101167
write("build.gradle",
102168
"apply plugin: 'com.diffplug.blowdryer'",
103-
"assert 干.file('sample').text == 'c\\n'",
169+
// .replace('\\r', '') fixes test on windows
170+
"assert 干.file('sample').text.replace('\\r', '') == 'c\\n'",
104171
"assert 干.prop('sample', 'name') == 'test'",
105172
"assert 干.prop('sample', 'group') == 'com.diffplug.gradle'");
106173
gradleRunner().build();

0 commit comments

Comments
 (0)