Skip to content

Commit

Permalink
Add check commit SHAs
Browse files Browse the repository at this point in the history
  • Loading branch information
charphi committed Mar 26, 2024
1 parent 862157d commit 74c66fa
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 1 deletion.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Added

- Add check on GitHub Pull Request links [#173](https://github.com/nbbrd/heylogs/issues/173)
- Add check mentions of people and teams [#157](https://github.com/nbbrd/heylogs/issues/157)
- Add check on GitHub mentions of people and teams [#157](https://github.com/nbbrd/heylogs/issues/157)
- Add check on GitHub commit SHAs [#223](https://github.com/nbbrd/heylogs/issues/223)

## [0.7.2] - 2023-11-10

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package internal.heylogs.github;

import internal.heylogs.GitHostLink;
import lombok.NonNull;
import nbbrd.design.RepresentableAsString;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.lang.Integer.parseInt;

@RepresentableAsString
@lombok.Value
class GitHubCommitSHALink implements GitHostLink {

public static @NonNull GitHubCommitSHALink parse(@NonNull CharSequence text) {
Matcher m = PATTERN.matcher(text);
if (!m.matches()) throw new IllegalArgumentException(text.toString());
return new GitHubCommitSHALink(
m.group("protocol"),
m.group("host"),
m.group("port") != null ? parseInt(m.group("port")) : NO_PORT,
m.group("owner"),
m.group("repo"),
m.group("hash")
);
}

@NonNull String protocol;
@NonNull String host;
int port;
@NonNull String owner;
@NonNull String repo;
@NonNull String hash;

@Override
public String toString() {
return protocol + "://" + host + (port != NO_PORT ? (":" + port) : "") + "/" + owner + "/" + repo + "/commit/" + hash;
}

// https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/autolinked-references-and-urls#commit-shas
private static final Pattern PATTERN = Pattern.compile("(?<protocol>https?)://(?<host>[^:/$]+)(?::(?<port>\\d+))?/(?<owner>[a-z\\d](?:[a-z\\d]|-(?=[a-z\\d])){0,38})/(?<repo>[a-z\\d._-]{1,100})/commit/(?<hash>[0-9a-f]{40})");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package internal.heylogs.github;

import internal.heylogs.GitHostRef;
import lombok.NonNull;
import nbbrd.design.RepresentableAsString;
import nbbrd.design.StaticFactoryMethod;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

@RepresentableAsString
@lombok.Value(staticConstructor = "of")
class GitHubCommitSHARef implements GitHostRef<GitHubCommitSHALink> {

public enum Type {HASH, OWNER_HASH, OWNER_REPO_HASH}

@StaticFactoryMethod
public static @NonNull GitHubCommitSHARef parse(@NonNull CharSequence text) {
Matcher m = PATTERN.matcher(text);
if (!m.matches()) throw new IllegalArgumentException(text.toString());
return new GitHubCommitSHARef(
m.group("owner"),
m.group("repo"),
m.group("hash")
);
}

@StaticFactoryMethod
public static @NonNull GitHubCommitSHARef of(@NonNull GitHubCommitSHALink link, @NonNull Type type) {
switch (type) {
case HASH:
return new GitHubCommitSHARef(null, null, link.getHash().substring(0, 7));
case OWNER_HASH:
return new GitHubCommitSHARef(link.getOwner(), null, link.getHash().substring(0, 7));
case OWNER_REPO_HASH:
return new GitHubCommitSHARef(link.getOwner(), link.getRepo(), link.getHash().substring(0, 7));
default:
throw new RuntimeException();
}
}

@Nullable String owner;
@Nullable String repo;
@NonNull String hash;

@Override
public String toString() {
switch (getType()) {
case HASH:
return hash;
case OWNER_HASH:
return owner + "@" + hash;
case OWNER_REPO_HASH:
return owner + "/" + repo + "@" + hash;
default:
throw new RuntimeException();
}
}

public boolean isCompatibleWith(@NonNull GitHubCommitSHALink link) {
switch (getType()) {
case HASH:
return link.getHash().startsWith(hash);
case OWNER_HASH:
return owner.equals(link.getOwner()) && link.getHash().startsWith(hash);
case OWNER_REPO_HASH:
return owner.equals(link.getOwner()) && repo.equals(link.getRepo()) && link.getHash().startsWith(hash);
default:
throw new RuntimeException();
}
}

public Type getType() {
return owner != null ? (repo != null ? Type.OWNER_REPO_HASH : Type.OWNER_HASH) : Type.HASH;
}

// https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/autolinked-references-and-urls#commit-shas
private static final Pattern PATTERN = Pattern.compile("((?<owner>[a-z\\d](?:[a-z\\d]|-(?=[a-z\\d])){0,38})(?:/(?<repo>[a-z\\d._-]{1,100}))?@)?(?<hash>[0-9a-f]{7})");
}
24 changes: 24 additions & 0 deletions heylogs-api/src/main/java/internal/heylogs/github/GitHubRules.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ public Failure validate(@NonNull Node node) {
public Failure validate(@NonNull Node node) {
return node instanceof Link ? validateGitHubMentionRef((Link) node) : NO_PROBLEM;
}
},
GITHUB_COMMIT_SHA_REF {
@Override
public Failure validate(@NonNull Node node) {
return node instanceof Link ? validateGitHubCommitSHARef((Link) node) : NO_PROBLEM;
}
};

@Override
Expand Down Expand Up @@ -100,6 +106,24 @@ private static String getMentionMessage(GitHubMentionLink expected, GitHubMentio
return "Expecting GitHub mention ref " + GitHubMentionRef.of(expected) + ", found " + found;
}

@VisibleForTesting
static Failure validateGitHubCommitSHARef(Link link) {
return GitHostSupport.validateRef(
GitHubCommitSHALink::parse,
GitHubCommitSHARef::parse,
GitHubRules::isExpectedCommitSHA,
link, GITHUB_COMMIT_SHA_REF,
GitHubRules::getCommitSHAMessage);
}

private static boolean isExpectedCommitSHA(GitHubCommitSHALink expected) {
return expected.getHost().equals("github.com");
}

private static String getCommitSHAMessage(GitHubCommitSHALink expected, GitHubCommitSHARef found) {
return "Expecting GitHub commit SHA ref " + GitHubCommitSHARef.of(expected, found.getType()) + ", found " + found;
}

@SuppressWarnings("unused")
@MightBeGenerated
@ServiceProvider
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package internal.heylogs.github;

import org.junit.jupiter.api.Test;

import static internal.heylogs.GitHostLink.NO_PORT;
import static internal.heylogs.github.GitHubCommitSHALink.parse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.jupiter.api.Assertions.*;

class GitHubCommitSHALinkTest {

@Test
public void testRepresentableAsString() {
assertThatIllegalArgumentException()
.isThrownBy(() -> parse("https://github.com/nbbrd/heylogs/commit"));

assertThatIllegalArgumentException()
.isThrownBy(() -> parse("https://github.com/nbbrd/heylogs/862157d164a8afa1fdd3295c89ceb394efbcb82d"));

assertThat(parse("https://github.com/nbbrd/heylogs/commit/862157d164a8afa1fdd3295c89ceb394efbcb82d"))
.returns("https", GitHubCommitSHALink::getProtocol)
.returns("github.com", GitHubCommitSHALink::getHost)
.returns(NO_PORT, GitHubCommitSHALink::getPort)
.returns("nbbrd", GitHubCommitSHALink::getOwner)
.returns("heylogs", GitHubCommitSHALink::getRepo)
.returns("862157d164a8afa1fdd3295c89ceb394efbcb82d", GitHubCommitSHALink::getHash)
.hasToString("https://github.com/nbbrd/heylogs/commit/862157d164a8afa1fdd3295c89ceb394efbcb82d");

assertThat(parse("https://localhost:8080/nbbrd/heylogs/commit/862157d164a8afa1fdd3295c89ceb394efbcb82d"))
.returns("https", GitHubCommitSHALink::getProtocol)
.returns("localhost", GitHubCommitSHALink::getHost)
.returns(8080, GitHubCommitSHALink::getPort)
.returns("nbbrd", GitHubCommitSHALink::getOwner)
.returns("heylogs", GitHubCommitSHALink::getRepo)
.returns("862157d164a8afa1fdd3295c89ceb394efbcb82d", GitHubCommitSHALink::getHash)
.hasToString("https://localhost:8080/nbbrd/heylogs/commit/862157d164a8afa1fdd3295c89ceb394efbcb82d");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package internal.heylogs.github;

import org.junit.jupiter.api.Test;

import static internal.heylogs.github.GitHubCommitSHARef.of;
import static internal.heylogs.github.GitHubCommitSHARef.parse;
import static org.assertj.core.api.Assertions.*;

class GitHubCommitSHARefTest {

@Test
public void testRepresentableAsString() {
assertThatIllegalArgumentException()
.isThrownBy(() -> parse("#"));

assertThatIllegalArgumentException()
.isThrownBy(() -> parse("heylogs#173"));

assertThat(parse("862157d"))
.returns(null, GitHubCommitSHARef::getOwner)
.returns(null, GitHubCommitSHARef::getRepo)
.returns("862157d", GitHubCommitSHARef::getHash)
.hasToString("862157d");

assertThat(parse("nbbrd@862157d"))
.returns("nbbrd", GitHubCommitSHARef::getOwner)
.returns(null, GitHubCommitSHARef::getRepo)
.returns("862157d", GitHubCommitSHARef::getHash)
.hasToString("nbbrd@862157d");

assertThat(parse("nbbrd/heylogs@862157d"))
.returns("nbbrd", GitHubCommitSHARef::getOwner)
.returns("heylogs", GitHubCommitSHARef::getRepo)
.returns("862157d", GitHubCommitSHARef::getHash)
.hasToString("nbbrd/heylogs@862157d");
}

@SuppressWarnings("DataFlowIssue")
@Test
public void testFactories() {
assertThatNullPointerException().isThrownBy(() -> of(null, GitHubCommitSHARef.Type.HASH));
assertThatNullPointerException().isThrownBy(() -> of(commit, null));

assertThat(of(commit, GitHubCommitSHARef.Type.HASH).isCompatibleWith(commit)).isTrue();
assertThat(of(commit, GitHubCommitSHARef.Type.OWNER_HASH).isCompatibleWith(commit)).isTrue();
assertThat(of(commit, GitHubCommitSHARef.Type.OWNER_REPO_HASH).isCompatibleWith(commit)).isTrue();
}

@SuppressWarnings("DataFlowIssue")
@Test
public void testIsCompatibleWith() {
assertThatNullPointerException().isThrownBy(() -> parse("862157d").isCompatibleWith(null));

assertThat(parse("862157d").isCompatibleWith(commit)).isTrue();
assertThat(parse("000007d").isCompatibleWith(commit)).isFalse();

assertThat(parse("nbbrd@862157d").isCompatibleWith(commit)).isTrue();
assertThat(parse("nbbrd@000007d").isCompatibleWith(commit)).isFalse();
assertThat(parse("abcde@862157d").isCompatibleWith(commit)).isFalse();

assertThat(parse("nbbrd/heylogs@862157d").isCompatibleWith(commit)).isTrue();
assertThat(parse("nbbrd/heylogs@000007d").isCompatibleWith(commit)).isFalse();
assertThat(parse("abcde/heylogs@862157d").isCompatibleWith(commit)).isFalse();
assertThat(parse("nbbrd/abcdefg@862157d").isCompatibleWith(commit)).isFalse();
}

@Test
public void testGetType() {
assertThat(parse("862157d").getType()).isEqualTo(GitHubCommitSHARef.Type.HASH);
assertThat(parse("nbbrd@862157d").getType()).isEqualTo(GitHubCommitSHARef.Type.OWNER_HASH);
assertThat(parse("nbbrd/heylogs@862157d").getType()).isEqualTo(GitHubCommitSHARef.Type.OWNER_REPO_HASH);
}

private final GitHubCommitSHALink commit = GitHubCommitSHALink.parse("https://github.com/nbbrd/heylogs/commit/862157d164a8afa1fdd3295c89ceb394efbcb82d");
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public void testRules() {

assertThat(Nodes.of(Node.class).descendants(using("/InvalidGitHubMentionRef.md")).map(GITHUB_MENTION_REF::validate).filter(Objects::nonNull))
.hasSize(1);

assertThat(Nodes.of(Node.class).descendants(using("/InvalidGitHubCommitSHARef.md")).map(GITHUB_COMMIT_SHA_REF::validate).filter(Objects::nonNull))
.hasSize(1);
}

@Test
Expand Down Expand Up @@ -92,4 +95,20 @@ public void testValidateGitHubMentionRef() {
.contains(Failure.builder().rule(GITHUB_MENTION_REF).message("Expecting GitHub mention ref @charphi, found @user").line(2).column(1).build(), atIndex(0))
.hasSize(1);
}

@Test
public void testValidateGitHubCommitSHARef() {
assertThat(of(Link.class).descendants(using("/Main.md")))
.map(GitHubRules::validateGitHubCommitSHARef)
.isNotEmpty()
.filteredOn(Objects::nonNull)
.isEmpty();

assertThat(of(Link.class).descendants(using("/InvalidGitHubCommitSHARef.md")))
.map(GitHubRules::validateGitHubCommitSHARef)
.isNotEmpty()
.filteredOn(Objects::nonNull)
.contains(Failure.builder().rule(GITHUB_COMMIT_SHA_REF).message("Expecting GitHub commit SHA ref 862157d, found 0000000").line(2).column(1).build(), atIndex(0))
.hasSize(1);
}
}
2 changes: 2 additions & 0 deletions heylogs-api/src/test/resources/InvalidGitHubCommitSHARef.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[862157d](https://github.com/nbbrd/heylogs/commit/862157d164a8afa1fdd3295c89ceb394efbcb82d)
[0000000](https://github.com/nbbrd/heylogs/commit/862157d164a8afa1fdd3295c89ceb394efbcb82d)

0 comments on commit 74c66fa

Please sign in to comment.