2021-02-02 08:07:43 +03:00
|
|
|
/// Package release utilities. Especially, utilities to load `CHANGELOG.md`, extract the newest
|
|
|
|
/// entry, and use it to generate package version and description.
|
|
|
|
|
2021-11-05 14:51:43 +03:00
|
|
|
const fss = require('fs')
|
|
|
|
const path = require('path')
|
|
|
|
const paths = require('./paths')
|
2021-02-02 08:07:43 +03:00
|
|
|
const semver = require('semver')
|
2021-11-16 12:04:56 +03:00
|
|
|
const yaml = require('js-yaml')
|
2021-02-02 08:07:43 +03:00
|
|
|
|
|
|
|
// =================
|
|
|
|
// === Constants ===
|
|
|
|
// =================
|
|
|
|
|
2021-11-16 12:04:56 +03:00
|
|
|
const config = yaml.load(fss.readFileSync(path.join(paths.gui.root, 'config.yaml'), 'utf-8'))
|
|
|
|
|
2021-02-02 08:07:43 +03:00
|
|
|
const CHANGELOG_FILE_NAME = 'CHANGELOG.md'
|
2021-11-16 12:04:56 +03:00
|
|
|
const CHANGELOG_FILE = path.join(paths.gui.root, CHANGELOG_FILE_NAME)
|
|
|
|
const ENGINE_VERSION = config.engineVersionSupported
|
2021-02-02 08:07:43 +03:00
|
|
|
|
|
|
|
// ===============
|
|
|
|
// === Version ===
|
|
|
|
// ===============
|
|
|
|
|
|
|
|
class NextReleaseVersion {
|
|
|
|
/// Version used for config files when building the package with "next version" in changelog.
|
|
|
|
toString() {
|
2021-11-05 14:51:43 +03:00
|
|
|
return '0.0.0'
|
2021-02-02 08:07:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
isPrerelease() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Version {
|
2021-11-05 14:51:43 +03:00
|
|
|
constructor(major, minor, patch, tag, tagVersion, rcTag, rcTagVersion) {
|
|
|
|
this.major = major
|
|
|
|
this.minor = minor
|
|
|
|
this.patch = patch
|
|
|
|
this.tag = tag
|
|
|
|
this.tagVersion = parseInt(tagVersion)
|
|
|
|
this.rcTag = rcTag
|
2021-04-30 16:21:43 +03:00
|
|
|
this.rcTagVersion = rcTagVersion
|
2021-02-02 08:07:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
lt(that) {
|
2021-11-05 14:51:43 +03:00
|
|
|
if (this.major < that.major) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (this.minor < that.minor) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (this.patch < that.patch) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (this.tag === 'alpha' && that.tag === 'beta') {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (this.tag === 'alpha' && that.tag === 'rc') {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (this.tag === 'beta' && that.tag === 'rc') {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (this.tagVersion < that.tagVersion) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (this.rcTagVersion < that.rcTagVersion) {
|
|
|
|
return true
|
|
|
|
}
|
2021-02-02 08:07:43 +03:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
isPrerelease() {
|
2021-11-05 14:51:43 +03:00
|
|
|
if (this.tag) {
|
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
2021-02-02 08:07:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
toString() {
|
|
|
|
let suffix = ''
|
|
|
|
if (this.tag) {
|
|
|
|
suffix = `-${this.tag}.${this.tagVersion}`
|
2021-04-30 16:21:43 +03:00
|
|
|
if (this.rcTag) {
|
|
|
|
suffix += `.${this.rcTag}.${this.rcTagVersion}`
|
|
|
|
}
|
2021-02-02 08:07:43 +03:00
|
|
|
}
|
|
|
|
return `${this.major}.${this.minor}.${this.patch}${suffix}`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================
|
|
|
|
// === ChangelogEntry ===
|
|
|
|
// ======================
|
|
|
|
|
|
|
|
class ChangelogEntry {
|
2021-11-05 14:51:43 +03:00
|
|
|
constructor(version, body) {
|
2021-02-02 08:07:43 +03:00
|
|
|
this.version = version
|
2021-11-05 14:51:43 +03:00
|
|
|
this.body = body
|
2021-02-02 08:07:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
assert_is_newest_version_defined() {
|
|
|
|
if (this.version instanceof NextReleaseVersion) {
|
|
|
|
throw `The newest entry in CHANGELOG.md does not have version assigned.`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_is_unstable() {
|
2021-03-05 01:09:14 +03:00
|
|
|
this.assert_is_newest_version_defined()
|
2021-02-02 08:07:43 +03:00
|
|
|
if (!this.isPrerelease()) {
|
2021-11-05 14:51:43 +03:00
|
|
|
throw 'Assertion failed. The version is stable.'
|
2021-02-02 08:07:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_is_stable() {
|
2021-03-05 01:09:14 +03:00
|
|
|
this.assert_is_newest_version_defined()
|
2021-02-02 08:07:43 +03:00
|
|
|
if (this.isPrerelease()) {
|
2021-11-05 14:51:43 +03:00
|
|
|
throw 'Assertion failed. The version is unstable.'
|
2021-02-02 08:07:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isPrerelease() {
|
|
|
|
return this.version.isPrerelease()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// =================
|
|
|
|
// === Changelog ===
|
|
|
|
// =================
|
|
|
|
|
|
|
|
class Changelog {
|
|
|
|
constructor() {
|
|
|
|
this.entries = changelogEntries()
|
|
|
|
}
|
|
|
|
|
|
|
|
newestEntry() {
|
|
|
|
return this.entries[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
currentVersion() {
|
|
|
|
return this.newestEntry().version
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function changelogSections() {
|
2021-11-05 14:51:43 +03:00
|
|
|
let text = '\n' + fss.readFileSync(CHANGELOG_FILE, 'utf8')
|
2021-02-02 08:07:43 +03:00
|
|
|
let chunks = text.split(/\r?\n#(?!#)/)
|
2021-11-05 14:51:43 +03:00
|
|
|
return chunks.filter(s => s != '')
|
2021-02-02 08:07:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function changelogEntries() {
|
2021-11-05 14:51:43 +03:00
|
|
|
let sections = changelogSections()
|
|
|
|
let entries = []
|
2021-02-02 08:07:43 +03:00
|
|
|
let firstSection = true
|
|
|
|
for (let section of sections) {
|
|
|
|
let splitPoint = section.indexOf('\n')
|
2021-11-05 14:51:43 +03:00
|
|
|
let header = section.substring(0, splitPoint)
|
|
|
|
let body = section.substring(splitPoint).trim()
|
2021-02-02 08:07:43 +03:00
|
|
|
if (firstSection && header.startsWith(' Next Release')) {
|
2021-11-05 14:51:43 +03:00
|
|
|
let version = new NextReleaseVersion()
|
|
|
|
entries.push(new ChangelogEntry(version, body))
|
2021-02-02 08:07:43 +03:00
|
|
|
} else {
|
2021-11-05 14:51:43 +03:00
|
|
|
let headerReg =
|
|
|
|
/^ Enso (?<major>[0-9]+)\.(?<minor>[0-9]+)\.(?<patch>[0-9]+)(-(?<tag>alpha|beta|rc)\.(?<tagVersion>[0-9]+))?(.(?<rcTag>rc)\.(?<rcTagVersion>[0-9]+))? \((?<year>[0-9][0-9][0-9][0-9])-(?<month>[0-9][0-9])-(?<day>[0-9][0-9])\)/
|
|
|
|
let match = header.match(headerReg)
|
2021-02-02 08:07:43 +03:00
|
|
|
if (!match) {
|
|
|
|
throw `Improper changelog entry header: '${header}'. See the 'CHANGELOG_TEMPLATE.md' for details.`
|
|
|
|
}
|
2021-11-05 14:51:43 +03:00
|
|
|
let grps = match.groups
|
|
|
|
let version = new Version(
|
|
|
|
grps.major,
|
|
|
|
grps.minor,
|
|
|
|
grps.patch,
|
|
|
|
grps.tag,
|
|
|
|
grps.tagVersion,
|
|
|
|
grps.rcTag,
|
|
|
|
grps.rcTagVersion
|
|
|
|
)
|
|
|
|
entries.push(new ChangelogEntry(version, body))
|
2021-02-02 08:07:43 +03:00
|
|
|
}
|
|
|
|
firstSection = false
|
|
|
|
}
|
|
|
|
|
2021-11-05 14:51:43 +03:00
|
|
|
let firstEntry = true
|
2021-02-02 08:07:43 +03:00
|
|
|
let lastVersion = null
|
|
|
|
for (let entry of entries) {
|
|
|
|
if (!(firstEntry && entry.version instanceof NextReleaseVersion)) {
|
|
|
|
if (lastVersion !== null) {
|
|
|
|
if (!entry.version.lt(lastVersion)) {
|
|
|
|
let v1 = entry.version.toString()
|
|
|
|
let v2 = lastVersion.toString()
|
|
|
|
throw `Versions are not properly ordered in the changelog (${v1} >= ${v2}).`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lastVersion = entry.version
|
|
|
|
}
|
2021-11-05 14:51:43 +03:00
|
|
|
firstEntry = false
|
2021-02-02 08:07:43 +03:00
|
|
|
}
|
|
|
|
return entries
|
|
|
|
}
|
|
|
|
|
|
|
|
function changelog() {
|
2021-11-05 14:51:43 +03:00
|
|
|
return new Changelog()
|
2021-02-02 08:07:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function currentVersion() {
|
|
|
|
return changelog().currentVersion()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ===============
|
|
|
|
// === Exports ===
|
|
|
|
// ===============
|
|
|
|
|
2021-11-05 14:51:43 +03:00
|
|
|
module.exports = { ENGINE_VERSION, Version, NextReleaseVersion, changelog, currentVersion }
|