diff --git a/.editorconfig b/.editorconfig index 56606a821..ffcea9382 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,6 @@ root = true -[*.{rs,html,js}] +[*.{rs,html,js,scss}] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true diff --git a/Cargo.toml b/Cargo.toml index 08500bd24..6484f147c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -132,8 +132,9 @@ aws-smithy-runtime = {version = "1.0.1", features = ["client", "test-util"]} aws-smithy-http = "0.60.0" indoc = "2.0.0" -[profile.dev.package.sqlx-macros] +[profile.dev.package."*"] opt-level = 2 +debug = "line-tables-only" [build-dependencies] time = "0.3" diff --git a/docker-compose.yml b/docker-compose.yml index ce503de9c..6bad85bf3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,9 @@ services: build: context: . dockerfile: ./dockerfiles/Dockerfile + args: + PROFILE: dev + PROFILE_DIR: debug platform: "linux/amd64" depends_on: - db @@ -16,6 +19,7 @@ services: - "/var/run/docker.sock:/var/run/docker.sock" - ".rustwide-docker:/opt/docsrs/rustwide" - "cratesio-index:/opt/docsrs/prefix/crates.io-index" + - "./static:/opt/docsrs/static:ro" environment: DOCSRS_RUSTWIDE_WORKSPACE: /opt/docsrs/rustwide DOCSRS_DATABASE_URL: postgresql://cratesfyi:password@db diff --git a/dockerfiles/Dockerfile b/dockerfiles/Dockerfile index 2ece0578d..f9ec2c4ad 100644 --- a/dockerfiles/Dockerfile +++ b/dockerfiles/Dockerfile @@ -14,7 +14,7 @@ FROM ubuntu:22.04 AS build # Install packaged dependencies RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ build-essential git curl cmake gcc g++ pkg-config libmagic-dev \ - libssl-dev zlib1g-dev ca-certificates + libssl-dev zlib1g-dev ca-certificates mold clang # Install the stable toolchain with rustup RUN curl https://sh.rustup.rs >/tmp/rustup-init && \ @@ -22,6 +22,11 @@ RUN curl https://sh.rustup.rs >/tmp/rustup-init && \ /tmp/rustup-init -y --no-modify-path --default-toolchain stable --profile minimal ENV PATH=/root/.cargo/bin:$PATH +# Configure linking to use mold instead for speed (need to use clang because gcc +# is too old on this image) +ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang +ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS=-Clink-arg=-fuse-ld=mold + # Build the dependencies in a separate step to avoid rebuilding all of them # every time the source code changes. This takes advantage of Docker's layer # caching, and it works by copying the Cargo.{toml,lock} with dummy source code @@ -36,7 +41,9 @@ RUN mkdir -p src/bin && \ echo "fn main() {}" > build.rs RUN cargo fetch -RUN cargo build --release + +ARG PROFILE=release +RUN cargo build --profile=$PROFILE # Dependencies are now cached, copy the actual source code and do another full # build. The touch on all the .rs files is needed, otherwise cargo assumes the @@ -54,7 +61,7 @@ COPY assets assets/ COPY .sqlx .sqlx/ COPY migrations migrations/ -RUN cargo build --release +RUN cargo build --profile=$PROFILE ###################### # Web server stage # @@ -69,7 +76,8 @@ RUN apt-get update \ tini \ && rm -rf /var/lib/apt/lists/* -COPY --from=build /build/target/release/cratesfyi /usr/local/bin +ARG PROFILE_DIR=release +COPY --from=build /build/target/$PROFILE_DIR/cratesfyi /usr/local/bin COPY static /srv/docsrs/static COPY templates /srv/docsrs/templates COPY vendor /srv/docsrs/vendor @@ -96,7 +104,8 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ RUN mkdir -p /opt/docsrs/prefix -COPY --from=build /build/target/release/cratesfyi /usr/local/bin +ARG PROFILE_DIR=release +COPY --from=build /build/target/$PROFILE_DIR/cratesfyi /usr/local/bin COPY static /opt/docsrs/static COPY templates /opt/docsrs/templates COPY dockerfiles/entrypoint.sh /opt/docsrs/ diff --git a/static/warnings.js b/static/warnings.js new file mode 100644 index 000000000..3be657d38 --- /dev/null +++ b/static/warnings.js @@ -0,0 +1,120 @@ +(function() { + function load(key, def = null) { + const value = window.localStorage.getItem(key) + if (value) { + try { + return JSON.parse(value) + } catch (ex) { + console.error(`Failed loading ${key} from local storage`, ex) + return def + } + } else { + return def + } + } + + function store(key, value) { + window.localStorage.setItem(key, JSON.stringify(value)) + } + + function create(tagName, attrs = {}, children = [], listeners = {}) { + const el = document.createElement(tagName) + for (const key of Object.keys(attrs)) { + if (typeof attrs[key] === 'object') { + for (const subkey of Object.keys(attrs[key])) { + el[key].setProperty(subkey, attrs[key][subkey]) + } + } else { + el.setAttribute(key, attrs[key]) + } + } + el.append(...children) + for (const key of Object.keys(listeners)) { + el.addEventListener(key, listeners[key]) + } + return el + } + + + if (!load('docs-rs-warnings-enabled', false)) { + return + } + + const parentEl = document.getElementById('warnings-menu-parent') + parentEl.removeAttribute('hidden') + + const current = JSON.parse(document.getElementById("crate-metadata")?.innerText || null) + + const menuEl = document.getElementById('warnings-menu') + + const followed = load('docs-rs-warnings-followed', []) + + function update() { + const children = [] + + if (followed.length > 0) { + children.push( + create('div', { class: 'pure-g' }, [ + create('div', { class: 'pure-u-1' }, [ + create('ul', { class: 'pure-menu-list', style: { width: '100%' } }, [ + create('li', { class: 'pure-menu-heading' }, [ + create('b', {}, ['Followed crates']), + ]), + ...followed.map(name => ( + create('li', { class: 'pure-menu-item followed' }, [ + create('a', { class: 'pure-menu-link', href: `/${name}` }, [ + name, + ]), + create('a', { class: 'pure-menu-link remove', href: '#' }, ['🗙'], { + click: (ev) => { + const index = followed.indexOf(name) + followed.splice(index, 1) + store('docs-rs-warnings-followed', followed) + update() + }, + }), + ]) + )), + ]), + ]), + ]), + ) + } + + if (current && !followed.includes(current.name)) { + children.push( + create('div', { class: 'pure-g' }, [ + create('div', { class: 'pure-u-1' }, [ + create('ul', { class: 'pure-menu-list', style: { width: '100%' } }, [ + create('li', { class: 'pure-menu-item' }, [ + create('a', { class: 'pure-menu-link', href: '#' }, [ + "Follow ", + create('b', {}, [current.name]), + ], { + click: () => { + const index = followed.findIndex((name) => name > current.name) + if (index >= 0) { + followed.splice(index, 0, current.name) + } else { + followed.push(current.name) + } + store('docs-rs-warnings-followed', followed) + update() + } + }), + ]), + ]), + ]), + ]), + ) + } + + for (const child of children.slice(0, -1)) { + child.classList.add('menu-item-divided') + } + + menuEl.replaceChildren(...children) + } + + update() +})() diff --git a/templates/base.html b/templates/base.html index 6f3fb9ba8..779c3b9a8 100644 --- a/templates/base.html +++ b/templates/base.html @@ -22,6 +22,7 @@ +
diff --git a/templates/header/topbar_end.html b/templates/header/topbar_end.html index 1e3f1cf2d..8a4f5e9ec 100644 --- a/templates/header/topbar_end.html +++ b/templates/header/topbar_end.html @@ -4,6 +4,8 @@ {# The global alert, if there is one #} {% include "header/global_alert.html" -%} + {% include "header/warnings.html" -%} +