mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 06:25:45 +01:00
Distinct between active and stale branches
This commit is contained in:
@@ -32,9 +32,13 @@ import javax.xml.bind.annotation.XmlAccessType;
|
|||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static java.time.Instant.now;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a branch in a repository.
|
* Represents a branch in a repository.
|
||||||
*
|
*
|
||||||
@@ -190,4 +194,13 @@ public final class Branch implements Serializable, Validateable {
|
|||||||
public Optional<Long> getLastCommitDate() {
|
public Optional<Long> getLastCommitDate() {
|
||||||
return Optional.ofNullable(lastCommitDate);
|
return Optional.ofNullable(lastCommitDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isStale() {
|
||||||
|
return getLastCommitDate()
|
||||||
|
.map(Instant::ofEpochMilli)
|
||||||
|
.map(d -> Duration.between(d, now()))
|
||||||
|
.map(d -> d.toDays())
|
||||||
|
.map(d -> d >= 14)
|
||||||
|
.orElse(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
62
scm-core/src/test/java/sonia/scm/repository/BranchTest.java
Normal file
62
scm-core/src/test/java/sonia/scm/repository/BranchTest.java
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.repository;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
|
||||||
|
import static java.time.Instant.now;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static sonia.scm.repository.Branch.normalBranch;
|
||||||
|
|
||||||
|
class BranchTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTagOldBranchAsStale() {
|
||||||
|
long moreThanTwoWeeksAgo =
|
||||||
|
now()
|
||||||
|
.minus(14, ChronoUnit.DAYS)
|
||||||
|
.minus(1, ChronoUnit.MINUTES)
|
||||||
|
.toEpochMilli();
|
||||||
|
|
||||||
|
Branch branch = normalBranch("hog", "42", moreThanTwoWeeksAgo);
|
||||||
|
|
||||||
|
assertThat(branch.isStale()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotTagNotSoOldBranchAsStale() {
|
||||||
|
long moreThanTwoWeeksAgo =
|
||||||
|
now()
|
||||||
|
.minus(14, ChronoUnit.DAYS)
|
||||||
|
.plus(1, ChronoUnit.MINUTES)
|
||||||
|
.toEpochMilli();
|
||||||
|
|
||||||
|
Branch branch = normalBranch("hog", "42", moreThanTwoWeeksAgo);
|
||||||
|
|
||||||
|
assertThat(branch.isStale()).isFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,14 +24,14 @@
|
|||||||
|
|
||||||
import { storiesOf } from "@storybook/react";
|
import { storiesOf } from "@storybook/react";
|
||||||
import { BranchSelector } from "./index";
|
import { BranchSelector } from "./index";
|
||||||
import { Branch } from "@scm-manager/ui-types/src";
|
import { Branch } from "@scm-manager/ui-types";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
const master = { name: "master", revision: "1", defaultBranch: true, _links: {} };
|
const master = { name: "master", revision: "1", defaultBranch: true, _links: {} };
|
||||||
const develop = { name: "develop", revision: "2", defaultBranch: false, _links: {} };
|
const develop = { name: "develop", revision: "2", defaultBranch: false, _links: {} };
|
||||||
|
|
||||||
const branchSelected = (branch?: Branch) => {};
|
const branchSelected = (branch?: Branch) => null;
|
||||||
|
|
||||||
const branches = [master, develop];
|
const branches = [master, develop];
|
||||||
|
|
||||||
@@ -42,6 +42,4 @@ const Wrapper = styled.div`
|
|||||||
|
|
||||||
storiesOf("BranchSelector", module)
|
storiesOf("BranchSelector", module)
|
||||||
.addDecorator(storyFn => <Wrapper>{storyFn()}</Wrapper>)
|
.addDecorator(storyFn => <Wrapper>{storyFn()}</Wrapper>)
|
||||||
.add("Default", () => (
|
.add("Default", () => <BranchSelector branches={branches} onSelectBranch={branchSelected} label="Select branch:" />);
|
||||||
<BranchSelector branches={branches} onSelectBranch={branchSelected} label="Select branch:" />
|
|
||||||
));
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export type Branch = {
|
|||||||
revision: string;
|
revision: string;
|
||||||
defaultBranch?: boolean;
|
defaultBranch?: boolean;
|
||||||
lastCommitDate?: string;
|
lastCommitDate?: string;
|
||||||
|
stale?: boolean;
|
||||||
_links: Links;
|
_links: Links;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,10 @@
|
|||||||
"createButton": "Branch erstellen"
|
"createButton": "Branch erstellen"
|
||||||
},
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"branches": "Branches",
|
"branches": {
|
||||||
|
"active": "Aktive Branches",
|
||||||
|
"stale": "Alte Branches"
|
||||||
|
},
|
||||||
"lastCommit": "Letzter Commit"
|
"lastCommit": "Letzter Commit"
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
|
|||||||
@@ -64,7 +64,10 @@
|
|||||||
"createButton": "Create Branch"
|
"createButton": "Create Branch"
|
||||||
},
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"branches": "Branches",
|
"branches": {
|
||||||
|
"active": "Active Branches",
|
||||||
|
"stale": "Stale Branches"
|
||||||
|
},
|
||||||
"lastCommit": "Last commit"
|
"lastCommit": "Last commit"
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
|
|||||||
@@ -30,10 +30,11 @@ import { apiClient, ConfirmAlert, ErrorNotification } from "@scm-manager/ui-comp
|
|||||||
type Props = {
|
type Props = {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
branches: Branch[];
|
branches: Branch[];
|
||||||
|
type: string;
|
||||||
fetchBranches: () => void;
|
fetchBranches: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const BranchTable: FC<Props> = ({ baseUrl, branches, fetchBranches }) => {
|
const BranchTable: FC<Props> = ({ baseUrl, branches, type, fetchBranches }) => {
|
||||||
const [t] = useTranslation("repos");
|
const [t] = useTranslation("repos");
|
||||||
const [showConfirmAlert, setShowConfirmAlert] = useState(false);
|
const [showConfirmAlert, setShowConfirmAlert] = useState(false);
|
||||||
const [error, setError] = useState<Error | undefined>();
|
const [error, setError] = useState<Error | undefined>();
|
||||||
@@ -92,7 +93,7 @@ const BranchTable: FC<Props> = ({ baseUrl, branches, fetchBranches }) => {
|
|||||||
<table className="card-table table is-hoverable is-fullwidth is-word-break">
|
<table className="card-table table is-hoverable is-fullwidth is-word-break">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{t("branches.table.branches")}</th>
|
<th>{t(`branches.table.branches.${type}`)}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>{renderRow()}</tbody>
|
<tbody>{renderRow()}</tbody>
|
||||||
|
|||||||
@@ -84,7 +84,28 @@ class BranchesOverview extends React.Component<Props> {
|
|||||||
const { baseUrl, branches, repository, fetchBranches, t } = this.props;
|
const { baseUrl, branches, repository, fetchBranches, t } = this.props;
|
||||||
if (branches && branches.length > 0) {
|
if (branches && branches.length > 0) {
|
||||||
orderBranches(branches);
|
orderBranches(branches);
|
||||||
return <BranchTable baseUrl={baseUrl} branches={branches} fetchBranches={() => fetchBranches(repository)} />;
|
const staleBranches = branches.filter(b => b.stale);
|
||||||
|
const activeBranches = branches.filter(b => !b.stale);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{activeBranches.length > 0 && (
|
||||||
|
<BranchTable
|
||||||
|
baseUrl={baseUrl}
|
||||||
|
type={"active"}
|
||||||
|
branches={activeBranches}
|
||||||
|
fetchBranches={() => fetchBranches(repository)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{staleBranches.length > 0 && (
|
||||||
|
<BranchTable
|
||||||
|
baseUrl={baseUrl}
|
||||||
|
type={"stale"}
|
||||||
|
branches={staleBranches}
|
||||||
|
fetchBranches={() => fetchBranches(repository)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return <Notification type="info">{t("branches.overview.noBranches")}</Notification>;
|
return <Notification type="info">{t("branches.overview.noBranches")}</Notification>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ public class BranchDto extends HalRepresentation {
|
|||||||
private boolean defaultBranch;
|
private boolean defaultBranch;
|
||||||
@JsonInclude(NON_NULL)
|
@JsonInclude(NON_NULL)
|
||||||
private Instant lastCommitDate;
|
private Instant lastCommitDate;
|
||||||
|
private boolean stale;
|
||||||
|
|
||||||
BranchDto(Links links, Embedded embedded) {
|
BranchDto(Links links, Embedded embedded) {
|
||||||
super(links, embedded);
|
super(links, embedded);
|
||||||
|
|||||||
Reference in New Issue
Block a user