Skip to content

Commit 20b1b35

Browse files
authored
[Feature] Get frontend build version (#556)
* build(version): generate frontend metadata during ci image build * feat(version): add frontend build version to version endpoint * test(version): cover frontend build version
1 parent eb7e893 commit 20b1b35

10 files changed

Lines changed: 244 additions & 11 deletions

File tree

.github/workflows/main.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ on:
1111

1212
env:
1313
REGISTRY: ghcr.io
14+
FRONTEND_REF: dev
1415

1516
jobs:
1617
build:
@@ -73,9 +74,22 @@ jobs:
7374
uses: actions/checkout@v4
7475
with:
7576
repository: ita-social-projects/oitClient
76-
ref: dev
77+
ref: ${{ env.FRONTEND_REF }}
7778
path: frontend
7879

80+
- name: Capture frontend build metadata
81+
run: |
82+
COMMIT_ID=$(git -C frontend rev-parse HEAD)
83+
COMMIT_TIME=$(date -u -d "$(git -C frontend log -1 --format=%cI)" +%Y-%m-%dT%H:%M:%SZ)
84+
VERSION=$(node -p "require('./frontend/package.json').version || ''")
85+
{
86+
echo "oit.frontend.commit-id=$COMMIT_ID"
87+
echo "oit.frontend.short-commit-id=${COMMIT_ID:0:7}"
88+
echo "oit.frontend.commit-time=$COMMIT_TIME"
89+
echo "oit.frontend.branch=$FRONTEND_REF"
90+
echo "oit.frontend.version=$VERSION"
91+
} > src/main/resources/frontend-info.properties
92+
7993
- name: Build frontend
8094
run: |
8195
cd frontend

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,7 @@ build/
4141
.vscode/
4242

4343
myenv-example
44-
/.env
44+
/.env
45+
46+
# Generated by CI during the backend image build
47+
src/main/resources/frontend-info.properties
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.itasocialacademy.oitassist.version.config;
2+
3+
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.context.annotation.PropertySource;
5+
6+
@Configuration
7+
@PropertySource(value = "classpath:frontend-info.properties", ignoreResourceNotFound = true)
8+
public class VersionConfig {
9+
}

src/main/java/com/itasocialacademy/oitassist/version/controller/VersionController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ public class VersionController {
2424
@Operation(
2525
summary = "Get application build version",
2626
description = """
27-
Returns the build version of the running application: the commit the backend
28-
was built from and the date the artifact was built.
27+
Returns the build version of the running application: the commits the backend
28+
and the bundled frontend were built from, and the date the artifact was built.
2929
Values that were not available at build time are returned as null.
3030
Publicly accessible, no authentication required.
3131
""")

src/main/java/com/itasocialacademy/oitassist/version/dao/dto/response/VersionResponse.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
@Schema(description = "Build version of the running application")
77
public record VersionResponse(
88
@Schema(description = "Backend build information") BackendVersion backend,
9+
@Schema(description = "Frontend build information") FrontendVersion frontend,
910
@Schema(description = "Date and time when the artifact was built") Instant buildTime) {
1011
@Schema(description = "Backend build information")
1112
public record BackendVersion(
@@ -19,4 +20,17 @@ public record BackendVersion(
1920
@Schema(description = "Branch the artifact was built from", example = "dev") String branch,
2021
@Schema(description = "Artifact version", example = "0.0.1-SNAPSHOT") String version) {
2122
}
23+
24+
@Schema(description = "Frontend build information")
25+
public record FrontendVersion(
26+
@Schema(
27+
description = "Full hash of the commit the frontend was built from",
28+
example = "5164fc9928b3cfde344f3320fee540fba1f78873") String commitId,
29+
@Schema(
30+
description = "Short hash of the commit the frontend was built from",
31+
example = "5164fc9") String shortCommitId,
32+
@Schema(description = "Date and time of the commit") Instant commitTime,
33+
@Schema(description = "Branch the frontend was built from", example = "dev") String branch,
34+
@Schema(description = "Frontend version", example = "1.4.2") String version) {
35+
}
2236
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.itasocialacademy.oitassist.version.properties;
2+
3+
import org.springframework.boot.context.properties.ConfigurationProperties;
4+
5+
@ConfigurationProperties(prefix = "oit.frontend")
6+
public record FrontendVersionProperties(
7+
String commitId,
8+
String shortCommitId,
9+
String commitTime,
10+
String branch,
11+
String version) {
12+
public FrontendVersionProperties {
13+
commitId = blankToNull(commitId);
14+
shortCommitId = blankToNull(shortCommitId);
15+
commitTime = blankToNull(commitTime);
16+
branch = blankToNull(branch);
17+
version = blankToNull(version);
18+
}
19+
20+
private static String blankToNull(String value) {
21+
return value == null || value.isBlank() ? null : value.strip();
22+
}
23+
}

src/main/java/com/itasocialacademy/oitassist/version/service/VersionServiceImpl.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.itasocialacademy.oitassist.version.service;
22

33
import com.itasocialacademy.oitassist.version.dao.dto.response.VersionResponse;
4+
import com.itasocialacademy.oitassist.version.properties.FrontendVersionProperties;
45
import com.itasocialacademy.oitassist.version.service.interfaces.VersionService;
56
import lombok.extern.slf4j.Slf4j;
67
import org.jspecify.annotations.NonNull;
@@ -9,30 +10,38 @@
910
import org.springframework.boot.info.GitProperties;
1011
import org.springframework.stereotype.Service;
1112
import java.time.Instant;
13+
import java.time.OffsetDateTime;
14+
import java.time.format.DateTimeParseException;
1215

1316
@Slf4j
1417
@Service
1518
public class VersionServiceImpl implements VersionService {
1619
private final GitProperties gitProperties;
1720
private final BuildProperties buildProperties;
21+
private final FrontendVersionProperties frontendVersionProperties;
1822

1923
public VersionServiceImpl(
2024
ObjectProvider<@NonNull GitProperties> gitPropertiesProvider,
21-
ObjectProvider<@NonNull BuildProperties> buildPropertiesProvider) {
25+
ObjectProvider<@NonNull BuildProperties> buildPropertiesProvider,
26+
FrontendVersionProperties frontendVersionProperties) {
2227
this.gitProperties = gitPropertiesProvider.getIfAvailable();
2328
this.buildProperties = buildPropertiesProvider.getIfAvailable();
29+
this.frontendVersionProperties = frontendVersionProperties;
2430

2531
if (this.gitProperties == null) {
2632
log.warn("git.properties is missing, commit data will not be reported");
2733
}
2834
if (this.buildProperties == null) {
2935
log.warn("build-info.properties is missing, build data will not be reported");
3036
}
37+
if (frontendVersionProperties.commitId() == null) {
38+
log.warn("frontend-info.properties is missing, frontend build data will not be reported");
39+
}
3140
}
3241

3342
@Override
3443
public VersionResponse getVersion() {
35-
return new VersionResponse(getBackendVersion(), getBuildTime());
44+
return new VersionResponse(getBackendVersion(), getFrontendVersion(), getBuildTime());
3645
}
3746

3847
private VersionResponse.BackendVersion getBackendVersion() {
@@ -47,11 +56,32 @@ private VersionResponse.BackendVersion getBackendVersion() {
4756
getArtifactVersion());
4857
}
4958

59+
private VersionResponse.FrontendVersion getFrontendVersion() {
60+
return new VersionResponse.FrontendVersion(
61+
frontendVersionProperties.commitId(),
62+
frontendVersionProperties.shortCommitId(),
63+
parseCommitTime(frontendVersionProperties.commitTime()),
64+
frontendVersionProperties.branch(),
65+
frontendVersionProperties.version());
66+
}
67+
5068
private String getArtifactVersion() {
5169
return buildProperties == null ? null : buildProperties.getVersion();
5270
}
5371

5472
private Instant getBuildTime() {
5573
return buildProperties == null ? null : buildProperties.getTime();
5674
}
75+
76+
private static Instant parseCommitTime(String commitTime) {
77+
if (commitTime == null) {
78+
return null;
79+
}
80+
try {
81+
return OffsetDateTime.parse(commitTime).toInstant();
82+
} catch (DateTimeParseException e) {
83+
log.warn("Frontend commit time '{}' is not a valid date, it will not be reported", commitTime);
84+
return null;
85+
}
86+
}
5787
}

src/test/java/com/itasocialacademy/oitassist/version/controller/VersionControllerTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ class VersionControllerTest {
4040
private static final String SHORT_COMMIT_ID = "3534bab";
4141
private static final String COMMIT_TIME = "2026-08-19T08:22:05Z";
4242
private static final String BUILD_TIME = "2026-08-19T19:55:29.490Z";
43+
private static final String FRONTEND_COMMIT_ID = "9f1c2ab7d4e58306b1c0f2a4d7e93b5c8a6d1e42";
44+
private static final String FRONTEND_SHORT_COMMIT_ID = "9f1c2ab";
45+
private static final String FRONTEND_COMMIT_TIME = "2026-08-21T10:15:00Z";
46+
private static final String FRONTEND_VERSION = "1.4.2";
4347

4448
@Autowired
4549
private MockMvc mockMvc;
@@ -60,6 +64,12 @@ void getVersion_ShouldReturnVersion_WhenCallerIsAnonymous() throws Exception {
6064
Instant.parse(COMMIT_TIME),
6165
"dev",
6266
"0.0.1-SNAPSHOT"),
67+
new VersionResponse.FrontendVersion(
68+
FRONTEND_COMMIT_ID,
69+
FRONTEND_SHORT_COMMIT_ID,
70+
Instant.parse(FRONTEND_COMMIT_TIME),
71+
"dev",
72+
FRONTEND_VERSION),
6373
Instant.parse(BUILD_TIME)));
6474

6575
mockMvc.perform(get(ENDPOINT))
@@ -70,6 +80,11 @@ void getVersion_ShouldReturnVersion_WhenCallerIsAnonymous() throws Exception {
7080
.andExpect(jsonPath("$.backend.commitTime").value(COMMIT_TIME))
7181
.andExpect(jsonPath("$.backend.branch").value("dev"))
7282
.andExpect(jsonPath("$.backend.version").value("0.0.1-SNAPSHOT"))
83+
.andExpect(jsonPath("$.frontend.commitId").value(FRONTEND_COMMIT_ID))
84+
.andExpect(jsonPath("$.frontend.shortCommitId").value(FRONTEND_SHORT_COMMIT_ID))
85+
.andExpect(jsonPath("$.frontend.commitTime").value(FRONTEND_COMMIT_TIME))
86+
.andExpect(jsonPath("$.frontend.branch").value("dev"))
87+
.andExpect(jsonPath("$.frontend.version").value(FRONTEND_VERSION))
7388
.andExpect(jsonPath("$.buildTime").value(BUILD_TIME));
7489
}
7590

@@ -78,6 +93,7 @@ void getVersion_ShouldReturnOkWithEmptyValues_WhenBuildMetadataIsMissing() throw
7893
when(versionService.getVersion()).thenReturn(
7994
new VersionResponse(
8095
new VersionResponse.BackendVersion(null, null, null, null, null),
96+
new VersionResponse.FrontendVersion(null, null, null, null, null),
8197
null));
8298

8399
mockMvc.perform(get(ENDPOINT))
@@ -86,6 +102,12 @@ void getVersion_ShouldReturnOkWithEmptyValues_WhenBuildMetadataIsMissing() throw
86102
.andExpect(jsonPath("$.backend.commitId").isEmpty())
87103
.andExpect(jsonPath("$.backend.commitTime").isEmpty())
88104
.andExpect(jsonPath("$.backend.branch").isEmpty())
105+
.andExpect(jsonPath("$.frontend").exists())
106+
.andExpect(jsonPath("$.frontend.commitId").isEmpty())
107+
.andExpect(jsonPath("$.frontend.shortCommitId").isEmpty())
108+
.andExpect(jsonPath("$.frontend.commitTime").isEmpty())
109+
.andExpect(jsonPath("$.frontend.branch").isEmpty())
110+
.andExpect(jsonPath("$.frontend.version").isEmpty())
89111
.andExpect(jsonPath("$.buildTime").isEmpty());
90112
}
91113

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.itasocialacademy.oitassist.version.properties;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
class FrontendVersionPropertiesTest {
8+
9+
private static final String COMMIT_ID = "9f1c2ab7d4e58306b1c0f2a4d7e93b5c8a6d1e42";
10+
private static final String SHORT_COMMIT_ID = "9f1c2ab";
11+
private static final String COMMIT_TIME = "2026-08-21T10:15:00Z";
12+
private static final String BRANCH = "dev";
13+
private static final String VERSION = "1.4.2";
14+
15+
@Test
16+
void constructor_ShouldReplaceValuesWithNull_WhenTheyAreBlank() {
17+
FrontendVersionProperties properties = new FrontendVersionProperties("", " ", "", "", "");
18+
19+
assertThat(properties.commitId()).isNull();
20+
assertThat(properties.shortCommitId()).isNull();
21+
assertThat(properties.commitTime()).isNull();
22+
assertThat(properties.branch()).isNull();
23+
assertThat(properties.version()).isNull();
24+
}
25+
26+
@Test
27+
void constructor_ShouldKeepValues_WhenTheyArePresent() {
28+
FrontendVersionProperties properties = new FrontendVersionProperties(
29+
COMMIT_ID, SHORT_COMMIT_ID, COMMIT_TIME, BRANCH, VERSION);
30+
31+
assertThat(properties.commitId()).isEqualTo(COMMIT_ID);
32+
assertThat(properties.shortCommitId()).isEqualTo(SHORT_COMMIT_ID);
33+
assertThat(properties.commitTime()).isEqualTo(COMMIT_TIME);
34+
assertThat(properties.branch()).isEqualTo(BRANCH);
35+
assertThat(properties.version()).isEqualTo(VERSION);
36+
}
37+
38+
@Test
39+
void constructor_ShouldStripValues_WhenTheyAreSurroundedByWhitespace() {
40+
FrontendVersionProperties properties = new FrontendVersionProperties(
41+
" " + COMMIT_ID + " ",
42+
" " + SHORT_COMMIT_ID + " ",
43+
" " + COMMIT_TIME + " ",
44+
" " + BRANCH + " ",
45+
" " + VERSION + " ");
46+
47+
assertThat(properties.commitId()).isEqualTo(COMMIT_ID);
48+
assertThat(properties.shortCommitId()).isEqualTo(SHORT_COMMIT_ID);
49+
assertThat(properties.commitTime()).isEqualTo(COMMIT_TIME);
50+
assertThat(properties.branch()).isEqualTo(BRANCH);
51+
assertThat(properties.version()).isEqualTo(VERSION);
52+
}
53+
}

0 commit comments

Comments
 (0)