Skip to content

Commit

Permalink
Add a step to CI to validate all the workspace crates are set to the …
Browse files Browse the repository at this point in the history
…correct versions (#443)
  • Loading branch information
Dekkonot authored Sep 3, 2024
1 parent 500f653 commit c5f705e
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ on:
- master

jobs:
validate:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- uses: ok-nick/[email protected]

- name: Validate Crate Versions
run: lune run validate-crate-versions

build:

runs-on: ubuntu-latest
Expand Down
81 changes: 81 additions & 0 deletions .lune/semver.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
--!strict
local SemVer = {}
SemVer.__index = SemVer

export type SemVer = {
major: number,
minor: number?,
patch: number?,
}

local function compare(a: number, b: number): number
if a > b then
return 1
elseif a < b then
return -1
end

return 0
end

function SemVer.__tostring(self: SemVer): string
return string.format("%i.%i.%i", self.major, self.minor or 0, self.patch or 0)
end

--[[
Constructs a new SemVer from the provided parts.
]]
function SemVer.new(major: number, minor: number?, patch: number?): SemVer
return (setmetatable({
major = major,
minor = minor,
patch = patch,
}, SemVer) :: any) :: SemVer
end

--[[
Parses `version` into a SemVer.
]]
function SemVer.parse(version: string)
local major, minor, patch, _ = version:match("^(%d+)%.(%d+)%.(%d+)(.*)$")
local realVersion = {
major = tonumber(major),
minor = tonumber(minor),
patch = tonumber(patch),
}
if realVersion.major == nil then
error(`could not parse major version from '{version}'`, 2)
end
if minor and realVersion.minor == nil then
error(`could not parse minor version from '{version}'`, 2)
end
if patch and realVersion.patch == nil then
error(`could not parse patch version from '{version}'`, 2)
end

return (setmetatable(realVersion, SemVer) :: any) :: SemVer
end

--[[
Compares two SemVers and returns their status compared to one another.
If `1` is returned, a is 'newer' than b.
If `-1` is returned, a is 'older' than b.
If `0` is returned, they are identical.
]]
function SemVer.compare(a: SemVer, b: SemVer)
local major = compare(a.major, b.major)
local minor = compare(a.minor or 0, b.minor or 0)

if major ~= 0 then
return major
end

if minor ~= 0 then
return minor
end

return compare(a.patch or 0, b.patch or 0)
end

return SemVer
107 changes: 107 additions & 0 deletions .lune/validate-crate-versions.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
--!strict
--#selene: allow(incorrect_standard_library_use)

local IGNORE_CRATE_LIST = {
"rbx_util",
"rbx_reflector",
}

local fs = require("@lune/fs")
local serde = require("@lune/serde")
local stdio = require("@lune/stdio")
local process = require("@lune/process")

local SemVer = require("semver")

type WorkspaceCargo = {
workspace: {
members: { string },
},
}

type CrateCargo = {
package: {
name: string,
version: string,
},
dependencies: { [string]: Dependency },
["dev-dependencies"]: { [string]: Dependency }?,
}

type Dependency = string | { version: string?, path: string?, features: { string }, optional: boolean? }

local function warn(...)
stdio.write(`[{stdio.color("yellow")}WARN{stdio.color("reset")}] `)
print(...)
end

local function processDependencies(dependency_list: { [string]: Dependency }, output: { [string]: Dependency })
for name, dependency in dependency_list do
if typeof(dependency) == "string" then
continue
end
if dependency.path then
output[name] = dependency
end
end
end

local workspace: WorkspaceCargo = serde.decode("toml", fs.readFile("Cargo.toml"))

local crate_info = {}

for _, crate_name in workspace.workspace.members do
if table.find(IGNORE_CRATE_LIST, crate_name) then
continue
end
local cargo: CrateCargo = serde.decode("toml", fs.readFile(`{crate_name}/Cargo.toml`))
local dependencies = {}
local dev_dependencies = {}
processDependencies(cargo.dependencies, dependencies)
if cargo.package["dev-dependencies"] then
processDependencies(cargo["dev-dependencies"] :: any, dev_dependencies)
end
crate_info[crate_name] = {
version = SemVer.parse(cargo.package.version),
dependencies = dependencies,
dev_dependencies = dev_dependencies,
}
end

table.freeze(crate_info)

local all_ok = true

for crate_name, cargo in crate_info do
for name, dependency in cargo.dependencies do
if typeof(dependency) == "string" then
error("invariant: string dependency made it into path list")
end
if not crate_info[name] then
warn(`Dependency {name} of crate {crate_name} has a path but is not local to this workspace.`)
all_ok = false
continue
end
if not dependency.version then
warn(`Dependency {name} of crate {crate_name} has a path but no version specified. Please fix this.`)
all_ok = false
continue
end
local dependency_version = SemVer.parse(dependency.version :: string)
local cmp = SemVer.compare(crate_info[name].version, dependency_version)
if cmp == 0 then
continue
else
all_ok = false
warn(
`Dependency {name} of crate {crate_name} has a version mismatch. Current version: {dependency_version}. Should be: {crate_info[name].version}`
)
end
end
end

if all_ok then
process.exit(0)
else
process.exit(1)
end
1 change: 1 addition & 0 deletions aftman.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ rojo = "rojo-rbx/[email protected]"
run-in-roblox = "rojo-rbx/[email protected]"
selene = "Kampfkarren/[email protected]"
stylua = "JohnnyMorganz/[email protected]"
lune = "lune-org/[email protected]"

0 comments on commit c5f705e

Please sign in to comment.