{
"name": "lemmy-ui",
- "version": "0.18.1",
+ "version": "0.18.2",
"description": "An isomorphic UI for lemmy",
"repository": "https://github.com/LemmyNet/lemmy-ui",
"license": "AGPL-3.0",
"scripts": {
"analyze": "webpack --mode=none",
"prebuild:dev": "yarn clean && node generate_translations.js",
- "build:dev": "webpack --env COMMIT_HASH=$(git rev-parse --short HEAD) --mode=development",
+ "build:dev": "webpack --env LEMMY_UI_DISABLE_CSP=true --env COMMIT_HASH=$(git rev-parse --short HEAD) --mode=development",
"prebuild:prod": "yarn clean && node generate_translations.js",
"build:prod": "webpack --env COMMIT_HASH=$(git rev-parse --short HEAD) --mode=production",
"clean": "yarn run rimraf dist",
"clean-webpack-plugin": "^4.0.0",
"cookie": "^0.5.0",
"copy-webpack-plugin": "^11.0.0",
- "cross-fetch": "^3.1.5",
+ "cross-fetch": "^4.0.0",
"css-loader": "^6.7.3",
"date-fns": "^2.30.0",
"emoji-mart": "^5.4.0",
"express": "~4.18.2",
"history": "^5.3.0",
"html-to-text": "^9.0.5",
- "i18next": "^22.4.15",
- "inferno": "^8.1.1",
- "inferno-create-element": "^8.1.1",
+ "i18next": "^23.2.8",
+ "inferno": "^8.2.2",
+ "inferno-create-element": "^8.2.2",
"inferno-helmet": "^5.2.1",
- "inferno-hydrate": "^8.1.1",
+ "inferno-hydrate": "^8.2.2",
"inferno-i18next-dess": "0.0.2",
- "inferno-router": "^8.1.1",
- "inferno-server": "^8.1.1",
+ "inferno-router": "^8.2.2",
+ "inferno-server": "^8.2.2",
"jwt-decode": "^3.1.2",
- "lemmy-js-client": "0.18.0-rc.2",
+ "lemmy-js-client": "0.18.1",
"lodash.isequal": "^4.5.0",
"lodash.merge": "^4.6.2",
"markdown-it": "^13.0.1",
"mini-css-extract-plugin": "^2.7.5",
"register-service-worker": "^1.7.2",
"run-node-webpack-plugin": "^1.3.0",
- "sanitize-html": "^2.10.0",
- "sass": "^1.62.1",
- "sass-loader": "^13.2.2",
+ "sanitize-html": "^2.11.0",
+ "sass": "^1.63.6",
+ "sass-loader": "^13.3.2",
"serialize-javascript": "^6.0.1",
"service-worker-webpack": "^1.0.0",
"sharp": "^0.32.1",
"tippy.js": "^6.3.7",
"toastify-js": "^1.12.0",
"tributejs": "^5.1.3",
- "webpack": "5.82.1",
- "webpack-cli": "^5.1.1",
+ "webpack": "5.88.1",
+ "webpack-cli": "^5.1.4",
"webpack-node-externals": "^3.0.0"
},
"devDependencies": {
"@types/lodash.isequal": "^4.5.6",
"@types/markdown-it": "^12.2.3",
"@types/markdown-it-container": "^2.0.5",
- "@types/node": "^20.1.2",
+ "@types/node": "^20.4.0",
"@types/path-browserify": "^1.0.0",
"@types/sanitize-html": "^2.9.0",
"@types/serialize-javascript": "^5.0.1",
"@types/toastify-js": "^1.11.1",
- "@typescript-eslint/eslint-plugin": "^5.59.5",
- "@typescript-eslint/parser": "^5.59.5",
- "eslint": "^8.40.0",
+ "@typescript-eslint/eslint-plugin": "^5.61.0",
+ "@typescript-eslint/parser": "^5.61.0",
+ "eslint": "^8.44.0",
"eslint-plugin-inferno": "^7.32.2",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-prettier": "^4.2.1",
"husky": "^8.0.3",
"import-sort-style-module": "^6.0.0",
- "lint-staged": "^13.2.2",
+ "lint-staged": "^13.2.3",
"prettier": "^2.8.8",
"prettier-plugin-import-sort": "^0.0.7",
"prettier-plugin-organize-imports": "^3.2.2",
- "prettier-plugin-packagejson": "^2.4.3",
+ "prettier-plugin-packagejson": "^2.4.4",
"rimraf": "^5.0.0",
"sortpack": "^2.3.4",
"style-loader": "^3.3.2",
- "terser": "^5.17.3",
- "typescript": "^5.0.4",
+ "terser": "^5.18.2",
+ "typescript": "^5.1.6",
"typescript-language-server": "^3.3.2",
"webpack-bundle-analyzer": "^4.9.0",
- "webpack-dev-server": "4.15.0"
+ "webpack-dev-server": "4.15.1"
},
"packageManager": "yarn@1.22.19",
"engines": {
{
- "version": "0.18.1",
- "serverSha256": "sha256-jYbrbIRyXo2G113ReG32oZ56ed2FEB/ZBcqYAxoxzGQ=",
- "serverCargoSha256": "sha256-7DNMNPSjzYY45DlR6Eo2q6QdwrMrRb51cFOnXfOuub0=",
- "uiSha256": "sha256-tc7fGA4okIv+3kq5t6I+EN+owdekCgAdk0EtkDgodIU=",
- "uiYarnDepsSha256": "sha256-/T1PQ0XsGY82/RlLq94hJgv8Yx1bIf2gcklhgtPu1RM="
+ "serverVersion": "0.18.2",
+ "uiVersion": "0.18.2",
+ "serverSha256": "sha256-T08CjsRREgGJb1vXJrYihYaCin8NNHtsG+2PUHoI4Ho=",
+ "serverCargoSha256": "sha256-nTZcLOpsbdeGzpz3PzgXZEGZHMbvSDA5rB2A3S9tMF8=",
+ "uiSha256": "sha256-qFFnmdCONjfPyfp8v0VonPQP8G5b2DVpxEUAQT731Z0=",
+ "uiYarnDepsSha256": "sha256-fRJpA9WstNNNOePoqotJKYmlikkcjc34iM0WO8+a/3Q="
}
, Security, protobuf, rustfmt, nixosTests }:
let
pinData = lib.importJSON ./pin.json;
- version = pinData.version;
+ version = pinData.serverVersion;
in rustPlatform.buildRustPackage rec {
inherit version;
pname = "lemmy-server";
};
preConfigure = ''
- echo "pub const VERSION: &str = \"${version}\";" > "crates/utils/src/version.rs"
+ echo 'pub const VERSION: &str = "${version}";' > crates/utils/src/version.rs
'';
cargoSha256 = pinData.serverCargoSha256;
PROTOC_INCLUDE = "${protobuf}/include";
nativeBuildInputs = [ protobuf rustfmt ];
- passthru.updateScript = ./update.sh;
+ passthru.updateScript = ./update.py;
passthru.tests.lemmy-server = nixosTests.lemmy;
doCheck = false;
};
name = "lemmy-ui";
- version = pinData.version;
+ version = pinData.uiVersion;
src = fetchFromGitHub {
owner = "LemmyNet";
sha256 = pinData.uiYarnDepsSha256;
};
- postPatch = ''
- echo "export const VERSION = '${version}';" > "src/shared/version.ts"
- '';
-
yarnPreBuild = ''
export npm_config_nodedir=${nodejs}
'';
export HOME=$PWD/yarn_home
ln -sf $PWD/node_modules $PWD/deps/lemmy-ui/
+ echo 'export const VERSION = "${version}";' > $PWD/deps/lemmy-ui/src/shared/version.ts
yarn --offline build:prod
'';
--- /dev/null
+#! /usr/bin/env nix-shell
+#! nix-shell -i python3 -p python3 python3.pkgs.semver nix-prefetch-github
+from urllib.request import Request, urlopen
+import dataclasses
+import subprocess
+import hashlib
+import os.path
+import semver
+import base64
+from typing import (
+ Optional,
+ Dict,
+ List,
+)
+import json
+import os
+
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+NIXPKGS = os.path.abspath(os.path.join(SCRIPT_DIR, "../../../../"))
+
+
+OWNER = "LemmyNet"
+UI_REPO = "lemmy-ui"
+SERVER_REPO = "lemmy"
+
+
+@dataclasses.dataclass
+class Pin:
+ serverVersion: str
+ uiVersion: str
+ serverSha256: str = ""
+ serverCargoSha256: str = ""
+ uiSha256: str = ""
+ uiYarnDepsSha256: str = ""
+
+ filename: Optional[str] = None
+
+ def write(self) -> None:
+ if not self.filename:
+ raise ValueError("No filename set")
+
+ with open(self.filename, "w") as fd:
+ pin = dataclasses.asdict(self)
+ del pin["filename"]
+ json.dump(pin, fd, indent=2)
+ fd.write("\n")
+
+
+def github_get(path: str) -> Dict:
+ """Send a GET request to Gituhb, optionally adding GITHUB_TOKEN auth header"""
+ url = f"https://api.github.com/{path.lstrip('/')}"
+ print(f"Retreiving {url}")
+
+ req = Request(url)
+
+ if "GITHUB_TOKEN" in os.environ:
+ req.add_header("authorization", f"Bearer {os.environ['GITHUB_TOKEN']}")
+
+ with urlopen(req) as resp:
+ return json.loads(resp.read())
+
+
+def get_latest_release(owner: str, repo: str) -> str:
+ return github_get(f"/repos/{owner}/{repo}/releases/latest")["tag_name"]
+
+
+def sha256_url(url: str) -> str:
+ sha256 = hashlib.sha256()
+ with urlopen(url) as resp:
+ while data := resp.read(1024):
+ sha256.update(data)
+ return "sha256-" + base64.urlsafe_b64encode(sha256.digest()).decode()
+
+
+def prefetch_github(owner: str, repo: str, rev: str) -> str:
+ """Prefetch github rev and return sha256 hash"""
+ print(f"Prefetching {owner}/{repo}({rev})")
+
+ proc = subprocess.run(
+ ["nix-prefetch-github", owner, repo, "--rev", rev, "--fetch-submodules"],
+ check=True,
+ stdout=subprocess.PIPE,
+ )
+
+ sha256 = json.loads(proc.stdout)["sha256"]
+ if not sha256.startswith("sha256-"): # Work around bug in nix-prefetch-github
+ return "sha256-" + sha256
+
+ return sha256
+
+
+def get_latest_tag(owner: str, repo: str, prerelease: bool = False) -> str:
+ """Get the latest tag from a Github Repo"""
+ tags: List[str] = []
+
+ # As the Github API doesn't have any notion of "latest" for tags we need to
+ # collect all of them and sort so we can figure out the latest one.
+ i = 0
+ while i <= 100: # Prevent infinite looping
+ i += 1
+ resp = github_get(f"/repos/{owner}/{repo}/tags?page={i}")
+ if not resp:
+ break
+
+ # Filter out unparseable tags
+ for tag in resp:
+ try:
+ parsed = semver.Version.parse(tag["name"])
+ if (
+ semver.Version.parse(tag["name"])
+ and not prerelease
+ and parsed.prerelease
+ ): # Filter out release candidates
+ continue
+ except ValueError:
+ continue
+ else:
+ tags.append(tag["name"])
+
+ # Sort and return latest
+ return sorted(tags, key=lambda name: semver.Version.parse(name))[-1]
+
+
+def get_fod_hash(attr: str) -> str:
+ """
+ Get fixed output hash for attribute.
+ This depends on a fixed output derivation with an empty hash.
+ """
+
+ print(f"Getting fixed output hash for {attr}")
+
+ proc = subprocess.run(["nix-build", NIXPKGS, "-A", attr], stderr=subprocess.PIPE)
+ if proc.returncode != 1:
+ raise ValueError("Expected nix-build to fail")
+
+ # Iterate list in reverse order so we get the "got:" line early
+ for line in proc.stderr.decode().split("\n")[::-1]:
+ cols = line.split()
+ if cols and cols[0] == "got:":
+ return cols[1]
+
+ raise ValueError("No fixed output hash found")
+
+
+def make_server_pin(pin: Pin, attr: str) -> None:
+ pin.serverSha256 = prefetch_github(OWNER, SERVER_REPO, pin.serverVersion)
+ pin.write()
+ pin.serverCargoSha256 = get_fod_hash(attr)
+ pin.write()
+
+
+def make_ui_pin(pin: Pin, package_json: str, attr: str) -> None:
+ # Save a copy of package.json
+ print("Getting package.json")
+ with urlopen(
+ f"https://raw.githubusercontent.com/{OWNER}/{UI_REPO}/{pin.uiVersion}/package.json"
+ ) as resp:
+ with open(os.path.join(SCRIPT_DIR, package_json), "wb") as fd:
+ fd.write(resp.read())
+
+ pin.uiSha256 = prefetch_github(OWNER, UI_REPO, pin.uiVersion)
+ pin.write()
+ pin.uiYarnDepsSha256 = get_fod_hash(attr)
+ pin.write()
+
+
+if __name__ == "__main__":
+ # Get server version
+ server_version = get_latest_tag(OWNER, SERVER_REPO)
+
+ # Get UI version (not always the same as lemmy-server)
+ ui_version = get_latest_tag(OWNER, UI_REPO)
+
+ pin = Pin(server_version, ui_version, filename=os.path.join(SCRIPT_DIR, "pin.json"))
+ make_server_pin(pin, "lemmy-server")
+ make_ui_pin(pin, "package.json", "lemmy-ui")