- add new experimental OFFLINE mode which will only generate changelogs based on commits
- FIX https://github.com/mikepenz/release-changelog-builder-action/issues/1459
This commit is contained in:
@@ -104,7 +104,8 @@ export const DefaultConfiguration: Configuration = {
|
||||
},
|
||||
base_branches: [], // target branches for the merged PR ignoring PRs with different target branch, by default it will get all PRs
|
||||
custom_placeholders: [],
|
||||
trim_values: false // defines if values are being trimmed prior to inserting
|
||||
trim_values: false, // defines if values are being trimmed prior to inserting
|
||||
offlineMode: false // defines if the action should run in offline mode, disabling API requests
|
||||
}
|
||||
|
||||
export const DefaultCommitConfiguration: Configuration = {
|
||||
|
||||
+14
-2
@@ -5,6 +5,7 @@ import {ReleaseNotesBuilder} from './releaseNotesBuilder.js'
|
||||
import {Configuration} from './configuration.js'
|
||||
import {GithubRepository} from './repositories/GithubRepository.js'
|
||||
import {GiteaRepository} from './repositories/GiteaRepository.js'
|
||||
import {OfflineRepository} from './repositories/OfflineRepository.js'
|
||||
|
||||
async function run(): Promise<void> {
|
||||
const supportedPlatform = {
|
||||
@@ -52,7 +53,15 @@ async function run(): Promise<void> {
|
||||
}
|
||||
|
||||
// mode of the action (PR, COMMIT, HYBRID)
|
||||
const mode = resolveMode(core.getInput('mode'), core.getInput('commitMode') === 'true')
|
||||
let mode = resolveMode(core.getInput('mode'), core.getInput('commitMode') === 'true')
|
||||
const offlineMode = core.getInput('offlineMode') === 'true'
|
||||
|
||||
// If offline mode is enabled, ensure commit mode is used
|
||||
if (offlineMode && mode !== 'COMMIT') {
|
||||
core.warning('⚠️ Offline mode requires commit mode. Switching to commit mode.')
|
||||
mode = 'COMMIT'
|
||||
}
|
||||
|
||||
core.info(`ℹ️ Running in ${mode} mode.`)
|
||||
|
||||
// merge configs, use default values from DefaultConfig on missing definition
|
||||
@@ -78,7 +87,10 @@ async function run(): Promise<void> {
|
||||
const exportOnly = core.getInput('exportOnly') === 'true'
|
||||
const cache = core.getInput('cache')
|
||||
|
||||
const repositoryUtils = new supportedPlatform[platform](token, baseUrl, repositoryPath)
|
||||
// Use OfflineRepository if offline mode is enabled, otherwise use the selected platform
|
||||
const repositoryUtils = offlineMode
|
||||
? new OfflineRepository(repositoryPath)
|
||||
: new supportedPlatform[platform](token, baseUrl, repositoryPath)
|
||||
const result = await new ReleaseNotesBuilder(
|
||||
baseUrl,
|
||||
repositoryUtils,
|
||||
|
||||
@@ -33,6 +33,80 @@ class GitCommandManager {
|
||||
return creationDate.stdout.trim().replace(/"/g, '')
|
||||
}
|
||||
|
||||
async getAllTags(): Promise<string[]> {
|
||||
const tagsOutput = await this.execGit(['tag', '-l'])
|
||||
return tagsOutput.stdout.trim().split('\n').filter(tag => tag.trim() !== '')
|
||||
}
|
||||
|
||||
async getTagCommit(tagName: string): Promise<string> {
|
||||
const commitOutput = await this.execGit(['rev-list', '-n', '1', tagName])
|
||||
return commitOutput.stdout.trim()
|
||||
}
|
||||
|
||||
async getDiffStats(base: string, head: string): Promise<{
|
||||
changedFiles: number;
|
||||
additions: number;
|
||||
deletions: number;
|
||||
changes: number;
|
||||
}> {
|
||||
const diffOutput = await this.execGit(['diff', '--numstat', `${base}..${head}`])
|
||||
const lines = diffOutput.stdout.trim().split('\n').filter(line => line.trim() !== '')
|
||||
|
||||
let additions = 0
|
||||
let deletions = 0
|
||||
|
||||
for (const line of lines) {
|
||||
const parts = line.split('\t')
|
||||
if (parts.length >= 2) {
|
||||
additions += parseInt(parts[0], 10) || 0
|
||||
deletions += parseInt(parts[1], 10) || 0
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
changedFiles: lines.length,
|
||||
additions,
|
||||
deletions,
|
||||
changes: additions + deletions
|
||||
}
|
||||
}
|
||||
|
||||
async getCommitsBetween(base: string, head: string): Promise<{
|
||||
count: number;
|
||||
commits: {
|
||||
sha: string;
|
||||
subject: string
|
||||
message: string;
|
||||
author: string;
|
||||
authorName: string;
|
||||
authorDate: string;
|
||||
}[];
|
||||
}> {
|
||||
const logOutput = await this.execGit([
|
||||
'log',
|
||||
'--pretty=format:%H|%an|%ae|%aI|%s|%b',
|
||||
`${base}..${head}`
|
||||
])
|
||||
|
||||
const lines = logOutput.stdout.trim().split('\n').filter(line => line.trim() !== '')
|
||||
const commits = lines.map(line => {
|
||||
const [sha, authorName, authorEmail, authorDate, subject, body] = line.split('|')
|
||||
return {
|
||||
sha,
|
||||
subject,
|
||||
message: body,
|
||||
author: authorEmail,
|
||||
authorName,
|
||||
authorDate
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
count: commits.length,
|
||||
commits
|
||||
}
|
||||
}
|
||||
|
||||
static async createCommandManager(workingDirectory: string): Promise<GitCommandManager> {
|
||||
const result = new GitCommandManager()
|
||||
await result.initializeCommandManager(workingDirectory)
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface PullConfiguration {
|
||||
sort: Sort | string // "ASC" or "DESC"
|
||||
tag_resolver: TagResolver
|
||||
base_branches: string[]
|
||||
offlineMode?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import * as core from "@actions/core";
|
||||
import moment from "moment";
|
||||
import { BaseRepository } from "./BaseRepository.js";
|
||||
import { TagInfo } from "../pr-collector/tags.js";
|
||||
import { DiffInfo } from "../pr-collector/commits.js";
|
||||
import { PullRequestInfo } from "../pr-collector/pullRequests.js";
|
||||
import { createCommandManager } from "../pr-collector/gitHelper.js";
|
||||
|
||||
export class OfflineRepository extends BaseRepository {
|
||||
|
||||
constructor(repositoryPath: string) {
|
||||
super("", "offline", repositoryPath);
|
||||
this.url = this.defaultUrl;
|
||||
}
|
||||
|
||||
get defaultUrl(): string {
|
||||
return "offline";
|
||||
}
|
||||
|
||||
get homeUrl(): string {
|
||||
return "offline";
|
||||
}
|
||||
|
||||
async getTags(owner: string, repo: string, maxTagsToFetch: number): Promise<TagInfo[]> {
|
||||
core.info(`ℹ️ Retrieving tags from local repository in offline mode`);
|
||||
const gitHelper = await createCommandManager(this.repositoryPath);
|
||||
const tags = await gitHelper.getAllTags();
|
||||
|
||||
// Limit the number of tags to maxTagsToFetch
|
||||
const limitedTags = tags.slice(0, maxTagsToFetch);
|
||||
|
||||
// Convert to TagInfo objects
|
||||
const tagInfos: TagInfo[] = [];
|
||||
for (const tag of limitedTags) {
|
||||
const commit = await gitHelper.getTagCommit(tag);
|
||||
tagInfos.push({
|
||||
name: tag,
|
||||
commit
|
||||
});
|
||||
}
|
||||
|
||||
core.info(`ℹ️ Retrieved ${tagInfos.length} tags from local repository`);
|
||||
return tagInfos;
|
||||
}
|
||||
|
||||
async fillTagInformation(repositoryPath: string, owner: string, repo: string, tagInfo: TagInfo): Promise<TagInfo> {
|
||||
return this.getTagByCreateTime(repositoryPath, tagInfo);
|
||||
}
|
||||
|
||||
async getDiffRemote(owner: string, repo: string, base: string, head: string): Promise<DiffInfo> {
|
||||
core.info(`ℹ️ Getting diff information from local repository in offline mode`);
|
||||
const gitHelper = await createCommandManager(this.repositoryPath);
|
||||
|
||||
// Get diff stats
|
||||
const diffStats = await gitHelper.getDiffStats(base, head);
|
||||
|
||||
// Get commits
|
||||
const commitInfo = await gitHelper.getCommitsBetween(base, head);
|
||||
|
||||
return {
|
||||
changedFiles: diffStats.changedFiles,
|
||||
additions: diffStats.additions,
|
||||
deletions: diffStats.deletions,
|
||||
changes: diffStats.changes,
|
||||
commits: commitInfo.count,
|
||||
commitInfo: commitInfo.commits.map(commit => ({
|
||||
sha: commit.sha,
|
||||
summary: commit.subject.split('\n')[0],
|
||||
message: commit.message,
|
||||
author: commit.author,
|
||||
authorName: commit.authorName,
|
||||
authorDate: moment(commit.authorDate),
|
||||
committer: "",
|
||||
committerName: "",
|
||||
commitDate: moment(commit.authorDate),
|
||||
prNumber: undefined
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async getForCommitHash(owner: string, repo: string, commit_sha: string, maxPullRequests: number): Promise<PullRequestInfo[]> {
|
||||
core.info(`⚠️ getForCommitHash not supported in offline mode`);
|
||||
return [];
|
||||
}
|
||||
|
||||
async getBetweenDates(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
owner: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
repo: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
fromDate: moment.Moment,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
toDate: moment.Moment,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
maxPullRequests: number
|
||||
): Promise<PullRequestInfo[]> {
|
||||
core.info(`⚠️ getBetweenDates not supported in offline mode`);
|
||||
return [];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async getOpen(owner: string, repo: string, maxPullRequests: number): Promise<PullRequestInfo[]> {
|
||||
core.info(`⚠️ getOpen not supported in offline mode`);
|
||||
return [];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async getReviews(owner: string, repo: string, pr: PullRequestInfo): Promise<void> {
|
||||
core.info(`⚠️ getReviews not supported in offline mode`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user