summaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/qu/quarto/package.nix
blob: 30ecca6d5ea91746d3ac2d553002d6f9f7c125a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
{
  stdenv,
  lib,
  pandoc,
  typst,
  esbuild,
  deno,
  fetchurl,
  dart-sass,
  installShellFiles,
  rWrapper,
  rPackages,
  extraRPackages ? [ ],
  makeWrapper,
  runCommand,
  python3,
  extraPythonPackages ? ps: [ ],
  sysctl,
  which,
  autoPatchelfHook,
  bashNonInteractive,
  coreutils,
  versionCheckHook,
  writeShellScript,
  curl,
  jq,
  common-updater-scripts,
}:

let
  rWithPackages = rWrapper.override {
    packages = [
      rPackages.rmarkdown
    ]
    ++ extraRPackages;
  };

  pythonWithPackages = python3.withPackages (
    ps:
    with ps;
    [
      jupyter
      ipython
    ]
    ++ (extraPythonPackages ps)
  );

  # bin/quarto resolves every bundled tool through bin/tools/$ARCH_DIR, which it
  # derives from uname and can only ever be x86_64 or aarch64
  archDir = stdenv.hostPlatform.parsed.cpu.name;
  denoDomExt = if stdenv.hostPlatform.isDarwin then "dylib" else "so";
  denoDomPlugin = "libplugin.${denoDomExt}";
in
stdenv.mkDerivation (finalAttrs: {
  pname = "quarto";
  version = "1.10.18";

  src =
    finalAttrs.passthru.sources.${stdenv.hostPlatform.system}
      or (throw "Unsupported system: ${stdenv.hostPlatform.system}");

  # the macOS tarball unpacks flat instead of into a versioned directory
  sourceRoot = if stdenv.hostPlatform.isDarwin then "." else "quarto-${finalAttrs.version}";

  strictDeps = true;

  nativeBuildInputs = [
    installShellFiles
    makeWrapper
  ]
  ++ lib.optionals stdenv.hostPlatform.isLinux [ autoPatchelfHook ];

  buildInputs = [
    # bin/quarto is a bash script; under strictDeps patchShebangs only resolves
    # its interpreter from the host dependencies
    bashNonInteractive
  ]
  # libgcc_s.so.1 for the retained typst-gather and deno_dom binaries
  ++ lib.optionals stdenv.hostPlatform.isLinux [ (stdenv.cc.cc.libgcc or null) ];

  dontStrip = true;

  preFixup = ''
    wrapProgram $out/bin/quarto \
      --set-default QUARTO_DENO ${lib.getExe deno} \
      --set-default QUARTO_PANDOC ${lib.getExe pandoc} \
      --set-default QUARTO_ESBUILD ${lib.getExe esbuild} \
      --set-default QUARTO_DART_SASS ${lib.getExe dart-sass} \
      --set-default QUARTO_TYPST ${lib.getExe typst} \
      --set-default QUARTO_DENO_DOM $out/bin/tools/${archDir}/deno_dom/${denoDomPlugin} \
      --suffix PATH : ${
        lib.makeBinPath [
          coreutils
          which
        ]
      } \
      ${lib.optionalString (rWrapper != null) "--set-default QUARTO_R ${rWithPackages}/bin/R"} \
      ${
        lib.optionalString (python3 != null) "--set-default QUARTO_PYTHON ${pythonWithPackages}/bin/python3"
      } \
      ${lib.optionalString (
        rWrapper != null
      ) "--set-default RETICULATE_PYTHON ${pythonWithPackages.interpreter}"}
  '';

  installPhase = ''
    runHook preInstall

    mkdir -p $out/bin $out/share
  ''
  + lib.optionalString stdenv.hostPlatform.isDarwin ''
    # the macOS asset bundles a complete tool tree for every architecture,
    # alongside AppleDouble metadata files
    find bin/tools -mindepth 1 -maxdepth 1 -type d ! -name ${archDir} -exec rm -r {} +
    find . -name '._*' -delete
  ''
  + ''
    tools=bin/tools/${archDir}
    if [ ! -d "$tools" ]; then
      echo "upstream no longer ships $tools, which is where quarto looks up its tools" >&2
      exit 1
    fi

    # swap the vendored tools for the nixpkgs builds. typst-gather and deno_dom
    # have no nixpkgs equivalent and are kept: dropping the whole directory
    # broke `quarto call typst-gather` and the DENO_DOM_PLUGIN export.
    rm -r "$tools"/deno "$tools"/pandoc "$tools"/typst "$tools"/esbuild "$tools"/dart-sass

    ln -s ${lib.getExe deno} "$tools"/deno
    ln -s ${lib.getExe pandoc} "$tools"/pandoc
    ln -s ${lib.getExe typst} "$tools"/typst
    ln -s ${lib.getExe esbuild} "$tools"/esbuild
    mkdir "$tools"/dart-sass
    ln -s ${lib.getExe dart-sass} "$tools"/dart-sass/sass

    # the linux-arm64 asset names the plugin libplugin-linux-aarch64.so, while
    # bin/quarto and QUARTO_DENO_DOM only ever point at the unsuffixed name; a
    # dlopen miss is swallowed and silently downgrades quarto to the wasm parser
    if [ ! -e "$tools"/deno_dom/${denoDomPlugin} ]; then
      mv "$tools"/deno_dom/libplugin*.${denoDomExt} "$tools"/deno_dom/${denoDomPlugin}
    fi

    # pre-1.4 location, still probed by the VS Code extension
    ln -s ${archDir}/pandoc bin/tools/pandoc

    # share/man also holds the qmd the page is generated from, which
    # compressManPages would gzip in place for mandb to trip over
    installManPage --name quarto.1 share/man/quarto-man.man
    rm -r share/man

    mv bin/* $out/bin
    mv share/* $out/share

    runHook postInstall
  '';

  doInstallCheck = true;
  nativeInstallCheckInputs = [ versionCheckHook ];

  passthru = {
    sources = {
      x86_64-linux = fetchurl {
        url = "https://github.com/quarto-dev/quarto-cli/releases/download/v${finalAttrs.version}/quarto-${finalAttrs.version}-linux-amd64.tar.gz";
        hash = "sha256-r60HG1vSLALy0wBpV0MYnTZQ4FN6Uwc+ZUtjDP8rDHM=";
      };
      aarch64-linux = fetchurl {
        url = "https://github.com/quarto-dev/quarto-cli/releases/download/v${finalAttrs.version}/quarto-${finalAttrs.version}-linux-arm64.tar.gz";
        hash = "sha256-9qB99o4lMwtd809l099mvKYFrM47gwxZOljpGITUz2w=";
      };
      # the macOS asset is a universal binary carrying both architectures
      aarch64-darwin = fetchurl {
        url = "https://github.com/quarto-dev/quarto-cli/releases/download/v${finalAttrs.version}/quarto-${finalAttrs.version}-macos.tar.gz";
        hash = "sha256-3danGp4ESKsV+2VbxYnhHLZYmiSOw1zNf39EE3UxaI4=";
      };
    };

    tests = {
      quarto-check =
        runCommand "quarto-check"
          {
            nativeBuildInputs = [ which ] ++ lib.optionals stdenv.hostPlatform.isDarwin [ sysctl ];
          }
          ''
            export HOME="$(mktemp -d)"
            ${lib.getExe finalAttrs.finalPackage} check
            touch $out
          '';
    };

    updateScript = writeShellScript "update-quarto" ''
      set -o errexit -o pipefail
      export PATH="${
        lib.makeBinPath [
          curl
          jq
          common-updater-scripts
        ]
      }"
      NEW_VERSION=$(curl --fail --silent --show-error --location https://api.github.com/repos/quarto-dev/quarto-cli/releases/latest | jq '.tag_name | ltrimstr("v")' --raw-output)
      # a truncated response still leaves jq at exit 0, and an empty version
      # would reach update-source-version
      if [[ -z "$NEW_VERSION" || "$NEW_VERSION" = "null" ]]; then
          echo "Could not read the latest release tag from the GitHub API." >&2
          exit 1
      fi
      if [[ "${finalAttrs.version}" = "$NEW_VERSION" ]]; then
          echo "The new version same as the old version."
          exit 0
      fi
      for platform in ${lib.escapeShellArgs finalAttrs.meta.platforms}; do
        update-source-version "''${UPDATE_NIX_ATTR_PATH:-quarto}" "$NEW_VERSION" --ignore-same-version --source-key="sources.$platform"
      done
    '';
  };

  meta = {
    description = "Open-source scientific and technical publishing system built on Pandoc";
    mainProgram = "quarto";
    longDescription = ''
      Quarto is an open-source scientific and technical publishing system built on Pandoc.
      Quarto documents are authored using markdown, an easy to write plain text format.
    '';
    homepage = "https://quarto.org/";
    changelog = "https://github.com/quarto-dev/quarto-cli/releases/tag/v${finalAttrs.version}";
    license = lib.licenses.mit;
    maintainers = with lib.maintainers; [
      mrtarantoga
    ];
    platforms = builtins.attrNames finalAttrs.passthru.sources;
    sourceProvenance = with lib.sourceTypes; [
      binaryNativeCode
      binaryBytecode
    ];
  };
})