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.XmlRootElement;
|
||||
import java.io.Serializable;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static java.time.Instant.now;
|
||||
|
||||
/**
|
||||
* Represents a branch in a repository.
|
||||
*
|
||||
@@ -190,4 +194,13 @@ public final class Branch implements Serializable, Validateable {
|
||||
public Optional<Long> getLastCommitDate() {
|
||||
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 { BranchSelector } from "./index";
|
||||
import { Branch } from "@scm-manager/ui-types/src";
|
||||
import { Branch } from "@scm-manager/ui-types";
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const master = { name: "master", revision: "1", defaultBranch: true, _links: {} };
|
||||
const develop = { name: "develop", revision: "2", defaultBranch: false, _links: {} };
|
||||
|
||||
const branchSelected = (branch?: Branch) => {};
|
||||
const branchSelected = (branch?: Branch) => null;
|
||||
|
||||
const branches = [master, develop];
|
||||
|
||||
@@ -42,6 +42,4 @@ const Wrapper = styled.div`
|
||||
|
||||
storiesOf("BranchSelector", module)
|
||||
.addDecorator(storyFn => <Wrapper>{storyFn()}</Wrapper>)
|
||||
.add("Default", () => (
|
||||
<BranchSelector branches={branches} onSelectBranch={branchSelected} label="Select branch:" />
|
||||
));
|
||||
.add("Default", () => <BranchSelector branches={branches} onSelectBranch={branchSelected} label="Select branch:" />);
|
||||
|
||||
@@ -29,6 +29,7 @@ export type Branch = {
|
||||
revision: string;
|
||||
defaultBranch?: boolean;
|
||||
lastCommitDate?: string;
|
||||
stale?: boolean;
|
||||
_links: Links;
|
||||
};
|
||||
|
||||
|
||||
@@ -64,7 +64,10 @@
|
||||
"createButton": "Branch erstellen"
|
||||
},
|
||||
"table": {
|
||||
"branches": "Branches",
|
||||
"branches": {
|
||||
"active": "Aktive Branches",
|
||||
"stale": "Alte Branches"
|
||||
},
|
||||
"lastCommit": "Letzter Commit"
|
||||
},
|
||||
"create": {
|
||||
|
||||
@@ -64,7 +64,10 @@
|
||||
"createButton": "Create Branch"
|
||||
},
|
||||
"table": {
|
||||
"branches": "Branches",
|
||||
"branches": {
|
||||
"active": "Active Branches",
|
||||
"stale": "Stale Branches"
|
||||
},
|
||||
"lastCommit": "Last commit"
|
||||
},
|
||||
"create": {
|
||||
|
||||
@@ -30,10 +30,11 @@ import { apiClient, ConfirmAlert, ErrorNotification } from "@scm-manager/ui-comp
|
||||
type Props = {
|
||||
baseUrl: string;
|
||||
branches: Branch[];
|
||||
type: string;
|
||||
fetchBranches: () => void;
|
||||
};
|
||||
|
||||
const BranchTable: FC<Props> = ({ baseUrl, branches, fetchBranches }) => {
|
||||
const BranchTable: FC<Props> = ({ baseUrl, branches, type, fetchBranches }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
const [showConfirmAlert, setShowConfirmAlert] = useState(false);
|
||||
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">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t("branches.table.branches")}</th>
|
||||
<th>{t(`branches.table.branches.${type}`)}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{renderRow()}</tbody>
|
||||
|
||||
@@ -84,7 +84,28 @@ class BranchesOverview extends React.Component<Props> {
|
||||
const { baseUrl, branches, repository, fetchBranches, t } = this.props;
|
||||
if (branches && branches.length > 0) {
|
||||
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>;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ public class BranchDto extends HalRepresentation {
|
||||
private boolean defaultBranch;
|
||||
@JsonInclude(NON_NULL)
|
||||
private Instant lastCommitDate;
|
||||
private boolean stale;
|
||||
|
||||
BranchDto(Links links, Embedded embedded) {
|
||||
super(links, embedded);
|
||||
|
||||
Reference in New Issue
Block a user