Initial commit
This commit is contained in:
commit
f06242ebb5
33 changed files with 6060 additions and 0 deletions
16
src/init.luau
Normal file
16
src/init.luau
Normal file
|
@ -0,0 +1,16 @@
|
|||
--!strict
|
||||
local release = require("./release")
|
||||
local sync = require("./sync")
|
||||
|
||||
local res = sync("jecs")
|
||||
if not res.ok then
|
||||
print(`Can't continue: {res.err}`)
|
||||
return
|
||||
end
|
||||
|
||||
if res.val == false then
|
||||
print(`No changes made. Aborting.`)
|
||||
return
|
||||
end
|
||||
|
||||
release("jecs", { pesde = "marked/jecs_nightly", wally = "mark-marks/jecs-nightly" })
|
10
src/read_version.luau
Normal file
10
src/read_version.luau
Normal file
|
@ -0,0 +1,10 @@
|
|||
--!strict
|
||||
local fs = require("@lune/fs")
|
||||
local serde = require("@lune/serde")
|
||||
local types = require("./types")
|
||||
|
||||
local manifest_contents = fs.readFile("jecs/pesde.toml")
|
||||
local manifest: types.PesdeManifest = serde.decode("toml", manifest_contents) or error("Couldn't decode manifest.")
|
||||
local jecs_version = manifest.version
|
||||
|
||||
print(jecs_version)
|
161
src/release.luau
Normal file
161
src/release.luau
Normal file
|
@ -0,0 +1,161 @@
|
|||
--!strict
|
||||
local datetime = require("@lune/datetime")
|
||||
local fs = require("@lune/fs")
|
||||
local net = require("@lune/net")
|
||||
local process = require("@lune/process")
|
||||
local serde = require("@lune/serde")
|
||||
local stdio = require("@lune/stdio")
|
||||
|
||||
local progress_bar = require("./util/progress")
|
||||
local result = require("./util/result")
|
||||
local types = require("./types")
|
||||
|
||||
-- Returns an ISO 8601 date (YYYYmmddThhmmssZ)
|
||||
local function iso_date_light(now: datetime.DateTime): string
|
||||
return now:formatUniversalTime("%Y%m%dT%H%M%SZ")
|
||||
end
|
||||
|
||||
local function make_pesde_manifest(version: string, scope: string): types.PesdeManifest
|
||||
return {
|
||||
name = scope,
|
||||
version = version,
|
||||
authors = { "jecs authors" },
|
||||
repository = "https://git.devmarked.win/jecs-nightly",
|
||||
license = "MIT",
|
||||
includes = {
|
||||
"init.luau",
|
||||
"pesde.toml",
|
||||
"README.md",
|
||||
"CHANGELOG.md",
|
||||
"LICENSE",
|
||||
".luaurc",
|
||||
},
|
||||
|
||||
target = {
|
||||
environment = "luau",
|
||||
lib = "jecs.luau",
|
||||
},
|
||||
|
||||
indices = {
|
||||
default = "https://github.com/daimond113/pesde-index",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function make_wally_manifest(version: string, scope: string): types.WallyManifest
|
||||
return {
|
||||
package = {
|
||||
name = scope,
|
||||
version = version,
|
||||
registry = "https://github.com/UpliftGames/wally-index",
|
||||
realm = "shared",
|
||||
license = "MIT",
|
||||
include = {
|
||||
"default.project.json",
|
||||
"jecs.luau",
|
||||
"wally.toml",
|
||||
"README.md",
|
||||
"CHANGELOG.md",
|
||||
"LICENSE",
|
||||
},
|
||||
exclude = { "**" },
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local function round_to(n: number, places: number)
|
||||
local x = 10 ^ (places or 0)
|
||||
return math.round(n * x) / x
|
||||
end
|
||||
|
||||
--- Fetches the given file raw from the jecs github
|
||||
local function fetch_raw(file: string): result.Identity<string>
|
||||
local res = net.request(`https://raw.githubusercontent.com/Ukendio/jecs/refs/heads/main/{file}`)
|
||||
if not res.ok then
|
||||
return result(false, `Not ok: {res.statusMessage} @ {res.statusCode}`)
|
||||
end
|
||||
return result(true, res.body)
|
||||
end
|
||||
|
||||
local function release(origin: string, scopes: { wally: string, pesde: string }, dry: boolean?): result.Identity<nil>
|
||||
local begin = os.clock()
|
||||
local now = datetime.now()
|
||||
|
||||
local progress = progress_bar
|
||||
.new()
|
||||
:withStage("init", "Initializing")
|
||||
:withStage("pull", "Pull latest version")
|
||||
:withStage("prepare", "Preparing manifests")
|
||||
:withStage("release (pesde)", "Releasing on pesde")
|
||||
:withStage("release (wally)", "Releasing on wally")
|
||||
progress:start()
|
||||
|
||||
dry = dry or true
|
||||
|
||||
if not fs.metadata(origin).exists then
|
||||
progress:stop()
|
||||
stdio.ewrite(`🔥 {origin} is not a valid directory which exists.\n`)
|
||||
return result(false, `{origin} is not a valid directory which exists.`)
|
||||
end
|
||||
|
||||
progress:nextStage()
|
||||
|
||||
local wally_contents = fetch_raw("wally.toml")
|
||||
if not wally_contents.ok then
|
||||
progress:stop()
|
||||
stdio.ewrite(`🔥 Couldn't get the jecs wally manifest:\n{wally_contents.err}\n`)
|
||||
return result(false, `Couldn't get the jecs wally manifest:\n{wally_contents.err}`)
|
||||
end
|
||||
|
||||
local parsed: types.WallyManifest = serde.decode("toml", wally_contents.val)
|
||||
local version = `{parsed.package.version}-nightly.{iso_date_light(now)}`
|
||||
|
||||
progress:nextStage()
|
||||
|
||||
do
|
||||
local manifest = make_pesde_manifest(version, scopes.pesde)
|
||||
local encoded = serde.encode("toml", manifest)
|
||||
fs.writeFile(`{origin}/pesde.toml`, encoded)
|
||||
end
|
||||
|
||||
do
|
||||
local manifest = make_wally_manifest(version, scopes.wally)
|
||||
local encoded = serde.encode("toml", manifest)
|
||||
fs.writeFile(`{origin}/wally.toml`, encoded)
|
||||
end
|
||||
|
||||
progress:nextStage()
|
||||
|
||||
local cwd = process.cwd .. origin
|
||||
|
||||
if not dry then
|
||||
process.spawn("pesde", { "publish", "-y" }, { cwd = cwd })
|
||||
else
|
||||
process.spawn("pesde", { "publish", "-d", "-y" }, { cwd = cwd })
|
||||
end
|
||||
|
||||
progress:nextStage()
|
||||
|
||||
if not dry then
|
||||
process.spawn("wally", { "publish" }, { cwd = cwd })
|
||||
else
|
||||
process.spawn("wally", { "package", "--output", "wally.tar.gz" }, { cwd = cwd })
|
||||
end
|
||||
|
||||
progress:stop()
|
||||
|
||||
local took = round_to((os.clock() - begin) * 1_000, 2)
|
||||
if not dry then
|
||||
print(`🚀 Published packages {stdio.style("dim")}(took {took}ms){stdio.style("reset")}`)
|
||||
else
|
||||
print(`📦 Packaged packages {stdio.style("dim")}(took {took}ms){stdio.style("reset")}`)
|
||||
end
|
||||
print({
|
||||
pesde = `{scopes.pesde}@{version}`,
|
||||
wally = `{scopes.wally}@{version}`,
|
||||
})
|
||||
|
||||
return result(true, nil)
|
||||
end
|
||||
|
||||
return release
|
110
src/sync.luau
Normal file
110
src/sync.luau
Normal file
|
@ -0,0 +1,110 @@
|
|||
--!strict
|
||||
local fs = require("@lune/fs")
|
||||
local net = require("@lune/net")
|
||||
local stdio = require("@lune/stdio")
|
||||
|
||||
local progress_bar = require("./util/progress")
|
||||
local result = require("./util/result")
|
||||
|
||||
--- Fetches the given file raw from the jecs github
|
||||
local function fetch_raw(file: string): result.Identity<string>
|
||||
local res = net.request(`https://raw.githubusercontent.com/Ukendio/jecs/refs/heads/main/{file}`)
|
||||
if not res.ok then
|
||||
return result(false, `Not ok: {res.statusMessage} @ {res.statusCode}`)
|
||||
end
|
||||
return result(true, res.body)
|
||||
end
|
||||
|
||||
local function save_if_diff(filepath: string, contents: string): result.Identity<nil>
|
||||
if not fs.metadata(filepath).exists then
|
||||
fs.writeFile(filepath, contents)
|
||||
return result(true, nil)
|
||||
end
|
||||
|
||||
local existing = fs.readFile(filepath)
|
||||
if existing == contents then
|
||||
return result(false, "Contents are the same.")
|
||||
end
|
||||
|
||||
fs.writeFile(filepath, contents)
|
||||
return result(true, nil)
|
||||
end
|
||||
|
||||
local function round_to(n: number, places: number)
|
||||
local x = 10 ^ (places or 0)
|
||||
return math.round(n * x) / x
|
||||
end
|
||||
|
||||
--- Synchronizes the required files from the jecs main branch.
|
||||
local function sync(to: string): result.Identity<boolean>
|
||||
local begin = os.clock()
|
||||
|
||||
local progress = progress_bar
|
||||
.new()
|
||||
:withStage("init", "Initializing")
|
||||
:withStage("fetch", "Fetching latest files")
|
||||
:withStage("save", "Saving files")
|
||||
progress:start()
|
||||
|
||||
if not fs.metadata(to).exists then
|
||||
fs.writeDir(to)
|
||||
end
|
||||
|
||||
progress:nextStage()
|
||||
|
||||
local includes = {
|
||||
"jecs.luau",
|
||||
"README.md",
|
||||
"CHANGELOG.md",
|
||||
"LICENSE",
|
||||
".luaurc",
|
||||
"default.project.json",
|
||||
}
|
||||
|
||||
local sources = {}
|
||||
|
||||
for _, file in includes do
|
||||
local contents = fetch_raw(file)
|
||||
if not contents.ok then
|
||||
progress:stop()
|
||||
stdio.ewrite(`🔥 Couldn't get the latest source for {file}:\n{contents.err}\n`)
|
||||
return result(false, `Couldn't get the latest source for {file}.`)
|
||||
end
|
||||
|
||||
sources[file] = contents.val
|
||||
end
|
||||
|
||||
progress:nextStage()
|
||||
|
||||
local sources_modified = {}
|
||||
local any_changed = false
|
||||
for file, contents in sources do
|
||||
local res = save_if_diff(`{to}/{file}`, contents)
|
||||
if res.ok then
|
||||
any_changed = true
|
||||
table.insert(sources_modified, file)
|
||||
end
|
||||
end
|
||||
|
||||
if not any_changed then
|
||||
progress:stop()
|
||||
local took = round_to((os.clock() - begin) * 1_000, 2)
|
||||
print(
|
||||
`🕛 Finished synchronizing, no changes since latest source {stdio.style("dim")}(took {took}ms){stdio.style(
|
||||
"reset"
|
||||
)}`
|
||||
)
|
||||
return result(true, false)
|
||||
end
|
||||
|
||||
progress:stop()
|
||||
|
||||
local took = round_to((os.clock() - begin) * 1_000, 2)
|
||||
print(`🪨 Finished synchronizing {stdio.style("dim")}(took {took}ms){stdio.style("reset")}`)
|
||||
print(`Changed files:`)
|
||||
print(sources_modified)
|
||||
|
||||
return result(true, true)
|
||||
end
|
||||
|
||||
return sync
|
78
src/types.luau
Normal file
78
src/types.luau
Normal file
|
@ -0,0 +1,78 @@
|
|||
--!strict
|
||||
export type SPDXLicense =
|
||||
"MIT"
|
||||
| "Apache-2.0"
|
||||
| "BSD-2-Clause"
|
||||
| "BSD-3-Clause"
|
||||
| "GPL-2.0"
|
||||
| "GPL-3.0"
|
||||
| "LGPL-2.1"
|
||||
| "LGPL-3.0"
|
||||
| "MPL-2.0"
|
||||
| "ISC"
|
||||
| "Unlicense"
|
||||
| "WTFPL"
|
||||
| "Zlib"
|
||||
| "CC0-1.0"
|
||||
| "CC-BY-4.0"
|
||||
| "CC-BY-SA-4.0"
|
||||
| "BSL-1.0"
|
||||
| "EPL-2.0"
|
||||
| "AGPL-3.0"
|
||||
|
||||
export type DependencySpecifier = ((
|
||||
{ name: string, version: string, index: string? }
|
||||
| { workspace: string, version: string }
|
||||
| { repo: string, rev: string, path: string? }
|
||||
) & {
|
||||
target: string?,
|
||||
}) | { wally: string, version: string, index: string? }
|
||||
|
||||
export type PackageTarget = {
|
||||
environment: "luau" | "lune" | "roblox" | "roblox_server",
|
||||
lib: string,
|
||||
} | {
|
||||
environment: "luau" | "lune",
|
||||
bin: string,
|
||||
}
|
||||
|
||||
export type PesdeManifest = {
|
||||
name: string,
|
||||
version: string,
|
||||
description: string?,
|
||||
license: SPDXLicense?,
|
||||
authors: { string }?,
|
||||
repository: string?,
|
||||
private: boolean?,
|
||||
includes: { string }?,
|
||||
pesde_version: string?,
|
||||
workspace_members: { string }?,
|
||||
target: PackageTarget,
|
||||
build_files: { string }?,
|
||||
scripts: { [string]: string }?,
|
||||
indices: { [string]: string },
|
||||
wally_indices: { [string]: string }?,
|
||||
overrides: { [string]: DependencySpecifier }?,
|
||||
patches: { [string]: { [string]: string } }?,
|
||||
place: { [string]: string }?,
|
||||
dependencies: { [string]: DependencySpecifier }?,
|
||||
peer_dependencies: { [string]: DependencySpecifier }?,
|
||||
dev_dependencies: { [string]: DependencySpecifier }?,
|
||||
}
|
||||
|
||||
export type WallyManifest = {
|
||||
package: {
|
||||
name: string,
|
||||
version: string,
|
||||
registry: string,
|
||||
realm: string,
|
||||
license: string?,
|
||||
exclude: { string }?,
|
||||
include: { string }?,
|
||||
},
|
||||
dependencies: {
|
||||
[string]: string,
|
||||
}?,
|
||||
}
|
||||
|
||||
return "<types>"
|
97
src/util/progress.luau
Normal file
97
src/util/progress.luau
Normal file
|
@ -0,0 +1,97 @@
|
|||
--!strict
|
||||
--> Inspired by Rokit's progress bar: https://github.com/rojo-rbx/rokit/blob/a303faf/src/util/progress.rs
|
||||
-- Original: https://github.com/pesde-pkg/tooling/blob/main/toolchainlib/src/utils/progress.luau
|
||||
local task = require("@lune/task")
|
||||
local stdio = require("@lune/stdio")
|
||||
|
||||
local result = require("./result")
|
||||
|
||||
-- FORMAT: {SPINNER} {MESSAGE} {BAR} {STAGE}
|
||||
local SPINNERS = { "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" }
|
||||
local BAR_COMPONENT = "▇"
|
||||
local MAX_BAR_LENGTH = 30
|
||||
|
||||
local ProgressBar = {}
|
||||
type ProgressBar = {
|
||||
stages: { { tag: string, message: string } },
|
||||
currentStageIndex: number,
|
||||
finished: boolean,
|
||||
thread: thread?,
|
||||
}
|
||||
export type ProgressBarImpl = typeof(setmetatable({} :: ProgressBar, { __index = ProgressBar }))
|
||||
|
||||
function ProgressBar.new(): ProgressBarImpl
|
||||
return setmetatable(
|
||||
{
|
||||
stages = {},
|
||||
currentStageIndex = 1,
|
||||
finished = false,
|
||||
} :: ProgressBar,
|
||||
{
|
||||
__index = ProgressBar,
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function ProgressBar.withStage(self: ProgressBarImpl, tag: string, msg: string): ProgressBarImpl
|
||||
table.insert(self.stages, { tag = tag, message = msg })
|
||||
return self
|
||||
end
|
||||
|
||||
function ProgressBar.start(self: ProgressBarImpl)
|
||||
local BAR_LENGTH = MAX_BAR_LENGTH // #self.stages
|
||||
local TOTAL_BAR_LENGTH = BAR_LENGTH * #self.stages
|
||||
local BAR = string.rep(BAR_COMPONENT, BAR_LENGTH)
|
||||
local MAX_MESSAGE_LENGTH = 0
|
||||
for _, stage in self.stages do
|
||||
local len = #stage.message
|
||||
if len > MAX_MESSAGE_LENGTH then
|
||||
MAX_MESSAGE_LENGTH = len
|
||||
end
|
||||
end
|
||||
|
||||
self.thread = task.spawn(function()
|
||||
while true do
|
||||
for _, spinner in SPINNERS do
|
||||
if self.finished then
|
||||
return
|
||||
end
|
||||
|
||||
local stage = self.stages[self.currentStageIndex]
|
||||
stdio.ewrite(
|
||||
`\x1b[2K\x1b[0G{stdio.color("cyan")}{spinner} {stage.message}{stdio.color("reset")}{string.rep(
|
||||
" ",
|
||||
MAX_MESSAGE_LENGTH - #stage.message
|
||||
)} [{stdio.style("dim")}{string.rep(BAR, self.currentStageIndex)}{string.rep(
|
||||
" ",
|
||||
TOTAL_BAR_LENGTH - (BAR_LENGTH * self.currentStageIndex)
|
||||
)}{stdio.style("reset")}] {stdio.style("bold")}{self.currentStageIndex} / {#self.stages}{stdio.style(
|
||||
"reset"
|
||||
)}`
|
||||
)
|
||||
|
||||
task.wait(0.1)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function ProgressBar.stop(self: ProgressBarImpl)
|
||||
-- Trigger upvalue, kill thread and clean progress bar remnant
|
||||
self.finished = true
|
||||
stdio.ewrite("\x1b[2K\x1b[0G")
|
||||
end
|
||||
|
||||
function ProgressBar.nextStage(self: ProgressBarImpl): result.Identity<nil>
|
||||
local inc = self.currentStageIndex + 1
|
||||
if inc > #self.stages then
|
||||
-- TODO: Make this a result
|
||||
self.finished = true
|
||||
return result(false, "OutOfBounds - Attempted to advance past last stage")
|
||||
end
|
||||
|
||||
self.currentStageIndex = inc
|
||||
return result(true, nil)
|
||||
end
|
||||
|
||||
return ProgressBar
|
24
src/util/result.luau
Normal file
24
src/util/result.luau
Normal file
|
@ -0,0 +1,24 @@
|
|||
--!strict
|
||||
export type Identity<T> = {
|
||||
ok: true,
|
||||
val: T,
|
||||
} | {
|
||||
ok: false,
|
||||
err: string,
|
||||
}
|
||||
|
||||
local function construct<T>(ok: boolean, value: T & string): Identity<T>
|
||||
if ok then
|
||||
return {
|
||||
ok = true,
|
||||
val = value,
|
||||
}
|
||||
else
|
||||
return {
|
||||
ok = false,
|
||||
err = value,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return (construct :: any) :: (<T>(ok: true, value: T) -> Identity<T>) & (<T>(ok: false, value: string) -> Identity<T>)
|
Loading…
Add table
Add a link
Reference in a new issue