From 2c431a9c9d915c890f4dd1ccd57350bd9305430d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Thu, 10 Oct 2024 08:07:24 +0200 Subject: [PATCH] update nix flakes, gh workflows and docs --- .github/workflows/pre-release.yml | 51 +++ .github/workflows/tests.yml | 24 -- README.md | 476 ++++++++++++++++++++++- config.sample.toml | 623 +++++++++++++++++++++++++++--- flake.lock | 18 +- flake.nix | 284 +++++++++----- rust-toolchain.nix | 27 +- rust-toolchain.toml | 2 +- 8 files changed, 1288 insertions(+), 217 deletions(-) create mode 100644 .github/workflows/pre-release.yml delete mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml new file mode 100644 index 0000000..8137961 --- /dev/null +++ b/.github/workflows/pre-release.yml @@ -0,0 +1,51 @@ +name: pre-release + +on: + push: + +jobs: + pre-release: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-linux + os: ubuntu-latest + - target: aarch64-linux + os: ubuntu-latest + - target: x86_64-windows + os: ubuntu-latest + - target: x86_64-darwin + os: macos-13 + - target: aarch64-darwin + os: macos-14 + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v27 + with: + nix_path: nixpkgs=channel:nixos-24.05 + enable_kvm: true + extra_nix_config: "experimental-features = nix-command flakes" + - name: Cache Nix store + uses: cachix/cachix-action@v15 + with: + name: soywod + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + extraPullNames: nix-community + - name: Build release + run: | + nix build -L .#${{ matrix.target }} + nix run -L .#${{ matrix.target }} -- --version + - name: Upload release artifact (tarball) + uses: actions/upload-artifact@v4 + with: + name: "himalaya.${{ matrix.target }}.tgz" + path: result/himalaya.tgz + - name: Upload release artifact (zip) + uses: actions/upload-artifact@v4 + with: + name: "himalaya.${{ matrix.target }}.zip" + path: result/himalaya.zip diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 57dc697..0000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: tests - -on: - pull_request: - push: - -jobs: - tests: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - - name: Install Nix - uses: cachix/install-nix-action@v26 - with: - nix_path: nixpkgs=channel:nixos-23.11 - extra_nix_config: | - experimental-features = nix-command flakes - - uses: cachix/cachix-action@v12 - with: - name: soywod - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - name: Build then test - run: nix build diff --git a/README.md b/README.md index b9edb5a..184fb11 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,481 @@ Repology Matrix

-

- 🚧 Work In Progress, stay tuned! 🚧 -

+*🚧 Himalaya REPL is still experimental. For a more stable alternative, have a look at [Himalaya CLI](https://github.com/pimalaya/himalaya).* + +## Features + +- Multi-accounting +- Interactive configuration via **wizard** (requires `wizard` feature) +- Mailbox, envelope, message and flag management +- Message composition based on `$EDITOR` +- **IMAP** backend (requires `imap` feature) +- **Maildir** backend (requires `maildir` feature) +- **Notmuch** backend (requires `notmuch` feature) +- **SMTP** backend (requires `smtp` feature) +- **Sendmail** backend (requires `sendmail` feature) +- Global system **keyring** for managing secrets (requires `keyring` feature) +- **OAuth 2.0** authorization (requires `oauth2` feature) +- **PGP** encryption: + - via shell commands (requires `pgp-commands` feature) + - via [GPG](https://www.gnupg.org/) bindings (requires `pgp-gpg` feature) + - via native implementation (requires `pgp-native` feature) + +*Himalaya REPL is written in [Rust](https://www.rust-lang.org/), and relies on [cargo features](https://doc.rust-lang.org/cargo/reference/features.html) to enable or disable functionalities. Default features can be found in the `features` section of the [`Cargo.toml`](https://github.com/pimalaya/himalaya-repl/blob/master/Cargo.toml#L18).* + +## Installation + +### Pre-built binary + +Himalaya REPL can be installed with a pre-built binary. Find the latest [`pre-release`](https://github.com/pimalaya/himalaya-repl/actions/workflows/pre-release.yml) GitHub workflow and look for the *Artifacts* section. You should find a pre-built binary matching your OS. + +### Cargo (git) + +Himalaya REPL can also be installed with [cargo](https://doc.rust-lang.org/cargo/): + +```bash +$ cargo install --locked --git https://github.com/pimalaya/himalaya-repl.git +``` + +## Configuration + +Just run `himalaya-repl`, the wizard will help you to configure your default account. + +You can also manually edit your own configuration, from scratch: + +- Copy the content of the documented [`./config.sample.toml`](./config.sample.toml) +- Paste it in a new file `~/.config/himalaya/config.toml` +- Edit, then comment or uncomment the options you want + +
+ Proton Mail (Bridge) + + When using Proton Bridge, emails are synchronized locally and exposed via a local IMAP/SMTP server. This implies 2 things: + + - Id order may be reversed or shuffled, but envelopes will still be sorted by date. + - SSL/TLS needs to be deactivated manually. + - The password to use is the one generated by Proton Bridge, not the one from your Proton Mail account. + + ```toml + [accounts.proton] + email = "example@proton.me" + + backend = "imap" + imap.host = "127.0.0.1" + imap.port = 1143 + imap.encryption = false + imap.login = "example@proton.me" + imap.auth.type = "password" + imap.auth.raw = "" + + message.send.backend = "smtp" + smtp.host = "127.0.0.1" + smtp.port = 1025 + smtp.encryption = false + smtp.login = "example@proton.me" + smtp.auth.type = "password" + smtp.auth.raw = "" + ``` + + Keeping your password inside the configuration file is good for testing purpose, but it is not safe. You have 2 better alternatives: + + - Save your password in any password manager that can be queried via the CLI: + + ```toml + imap.auth.type = "password" + imap.auth.cmd = "pass show proton" + ``` + + - Use the global keyring of your system (requires the `keyring` cargo feature): + + ```toml + imap.auth.type = "password" + imap.auth.keyring = "proton-example" + ``` + + Running `himalaya configure -a proton` will ask for your IMAP password, just paste the one generated previously. +
+ +
+ Gmail + + Google passwords cannot be used directly. There is two ways to authenticate yourself: + + ### Using [App Passwords](https://support.google.com/mail/answer/185833) + + This option is the simplest and the fastest. First, be sure that: + + - IMAP is enabled + - Two-step authentication is enabled + - Less secure app access is enabled + + First create a [dedicated password](https://myaccount.google.com/apppasswords) for Himalaya. + + ```toml + [accounts.gmail] + email = "example@gmail.com" + + folder.alias.inbox = "INBOX" + folder.alias.sent = "[Gmail]/Sent Mail" + folder.alias.drafts = "[Gmail]/Drafts" + folder.alias.trash = "[Gmail]/Trash" + + backend = "imap" + imap.host = "imap.gmail.com" + imap.port = 993 + imap.login = "example@gmail.com" + imap.auth.type = "password" + imap.auth.cmd = "pass show gmail" + + message.send.backend = "smtp" + smtp.host = "smtp.gmail.com" + smtp.port = 465 + smtp.login = "example@gmail.com" + smtp.auth.type = "password" + smtp.auth.cmd = "pass show gmail" + ``` + + Keeping your password inside the configuration file is good for testing purpose, but it is not safe. You have 2 better alternatives: + + - Save your password in any password manager that can be queried via the CLI: + + ```toml + imap.auth.type = "password" + imap.auth.cmd = "pass show gmail" + ``` + + - Use the global keyring of your system (requires the `keyring` cargo feature): + + ```toml + imap.auth.type = "password" + imap.auth.keyring = "gmail-example" + ``` + + Running `himalaya configure -a gmail` will ask for your IMAP password, just paste the one generated previously. + + ### Using OAuth 2.0 + + This option is the most secure but the hardest to configure. It requires the `oauth2` and `keyring` cargo features. + + First, you need to get your OAuth 2.0 credentials by following [this guide](https://developers.google.com/identity/protocols/oauth2#1.-obtain-oauth-2.0-credentials-from-the-dynamic_data.setvar.console_name-.). Once you get your client id and your client secret, you can configure your Himalaya account this way: + + ```toml + [accounts.gmail] + email = "example@gmail.com" + + folder.alias.inbox = "INBOX" + folder.alias.sent = "[Gmail]/Sent Mail" + folder.alias.drafts = "[Gmail]/Drafts" + folder.alias.trash = "[Gmail]/Trash" + + backend = "imap" + imap.host = "imap.gmail.com" + imap.port = 993 + imap.login = "example@gmail.com" + imap.auth.type = "oauth2" + imap.auth.client-id = "" + imap.auth.auth-url = "https://accounts.google.com/o/oauth2/v2/auth" + imap.auth.token-url = "https://www.googleapis.com/oauth2/v3/token" + imap.auth.pkce = true + imap.auth.scope = "https://mail.google.com/" + + message.send.backend = "smtp" + smtp.host = "smtp.gmail.com" + smtp.port = 465 + smtp.login = "example@gmail.com" + smtp.auth.type = "oauth2" + smtp.auth.client-id = "" + smtp.auth.auth-url = "https://accounts.google.com/o/oauth2/v2/auth" + smtp.auth.token-url = "https://www.googleapis.com/oauth2/v3/token" + smtp.auth.pkce = true + smtp.auth.scope = "https://mail.google.com/" + + # If you want your SMTP to share the same client id (and so the same access token) + # as your IMAP config, you can add the following: + # + # imap.auth.type = "oauth2" + # imap.auth.client-id = "" + # imap.auth.client-secret.keyring = "gmail-oauth2-client-secret" + # imap.auth.access-token.keyring = "gmail-oauth2-access-token" + # imap.auth.refresh-token.keyring = "gmail-oauth2-refresh-token" + # + # smtp.auth.type = "oauth2" + # smtp.auth.client-id = "" + # smtp.auth.client-secret.keyring = "gmail-oauth2-client-secret" + # smtp.auth.access-token.keyring = "gmail-oauth2-access-token" + # smtp.auth.refresh-token.keyring = "gmail-oauth2-refresh-token" + ``` + + Running `himalaya configure -a gmail` will complete your OAuth 2.0 setup and ask for your client secret. +
+ +
+ Outlook + + ```toml + [accounts.outlook] + email = "example@outlook.com" + + backend = "imap" + imap.host = "outlook.office365.com" + imap.port = 993 + imap.login = "example@outlook.com" + imap.auth.type = "password" + imap.auth.cmd = "pass show outlook" + + message.send.backend = "smtp" + smtp.host = "smtp.mail.outlook.com" + smtp.port = 587 + smtp.encryption = "start-tls" + smtp.login = "example@outlook.com" + smtp.auth.type = "password" + smtp.auth.cmd = "pass show outlook" + ``` + + ### Using OAuth 2.0 + + This option is the most secure but the hardest to configure. First, you need to get your OAuth 2.0 credentials by following [this guide](https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth). Once you get your client id and your client secret, you can configure your Himalaya account this way: + + ```toml + [accounts.outlook] + email = "example@outlook.com" + + backend = "imap" + imap.host = "outlook.office365.com" + imap.port = 993 + imap.login = "example@outlook.com" + imap.auth.type = "oauth2" + imap.auth.client-id = "" + imap.auth.auth-url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" + imap.auth.token-url = "https://login.microsoftonline.com/common/oauth2/v2.0/token" + imap.auth.pkce = true + imap.auth.scope = "https://outlook.office.com/IMAP.AccessAsUser.All" + + message.send.backend = "smtp" + smtp.host = "smtp.mail.outlook.com" + smtp.port = 587 + smtp.starttls = true + smtp.login = "example@outlook.com" + smtp.auth.type = "oauth2" + smtp.auth.client-id = "" + smtp.auth.auth-url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" + smtp.auth.token-url = "https://login.microsoftonline.com/common/oauth2/v2.0/token" + smtp.auth.pkce = true + smtp.auth.scope = "https://outlook.office.com/SMTP.Send" + + # If you want your SMTP to share the same client id (and so the same access token) + # as your IMAP config, you can add the following: + # + # imap.auth.type = "oauth2" + # imap.auth.client-id = "" + # imap.auth.client-secret.keyring = "outlook-oauth2-client-secret" + # imap.auth.access-token.keyring = "outlook-oauth2-access-token" + # imap.auth.refresh-token.keyring = "outlook-oauth2-refresh-token" + # + # smtp.auth.type = "oauth2" + # smtp.auth.client-id = "" + # smtp.auth.client-secret.keyring = "outlook-oauth2-client-secret" + # smtp.auth.access-token.keyring = "outlook-oauth2-access-token" + # smtp.auth.refresh-token.keyring = "outlook-oauth2-refresh-token" + ``` + + Running `himalaya configure -a outlook` will complete your OAuth 2.0 setup and ask for your client secret. +
+ +
+ iCloud Mail + + From the [iCloud Mail](https://support.apple.com/en-us/HT202304) support page: + + - IMAP port = `993`. + - IMAP login = name of your iCloud Mail email address (for example, `johnappleseed`, not `johnappleseed@icloud.com`) + - SMTP port = `587` with `STARTTLS` + - SMTP login = full iCloud Mail email address (for example, `johnappleseed@icloud.com`, not `johnappleseed`) + + ```toml + [accounts.icloud] + email = "johnappleseed@icloud.com" + + backend = "imap" + imap.host = "imap.mail.me.com" + imap.port = 993 + imap.login = "johnappleseed" + imap.auth.type = "password" + imap.auth.cmd = "pass show icloud" + + message.send.backend = "smtp" + smtp.host = "smtp.mail.me.com" + smtp.port = 587 + smtp.encryption = "start-tls" + smtp.login = "johnappleseed@icloud.com" + smtp.auth.type = "password" + smtp.auth.cmd = "pass show icloud" + ``` +
+ +## FAQ + +
+ How to compose a message? + + An email message is a list of **headers** (`key: val`) followed by a **body**. They form together a template: + + ```eml + Header: value + Header: value + Header: value + + Body + ``` + + ***Headers and body must be separated by an empty line.*** + + ### Headers + + Here a non-exhaustive list of valid email message template headers: + + - `Message-ID`: represents the message identifier (you usually do not need to set up it manually) + - `In-Reply-To`: represents the identifier of the replied message + - `Date`: represents the date of the message + - `Subject`: represents the subject of the message + - `From`: represents the address of the sender + - `To`: represents the addresses of the receivers + - `Reply-To`: represents the address the receiver should reply to instead of the `From` header + - `Cc`: represents the addresses of the other receivers (carbon copy) + - `Bcc`: represents the addresses of the other hidden receivers (blind carbon copy) + + An address can be: + + - a single email address `user@domain` + - a named address `Name ` + - a quoted named address `"Name" ` + + Multiple address are separated by a coma `,`: `user@domain, Name , "Name" `. + + ### Plain text body + + Email message template body can be written in plain text. The result will be compiled into a single `text/plain` MIME part: + + ```eml + From: alice@localhost + To: Bob + Subject: Hello from Himalaya + + Hello, world! + ``` + + ### MML boby + + Email message template body can also be written in MML. The MIME Meta Language was introduced by the Emacs [`mml`](https://www.gnu.org/software/emacs/manual/html_node/emacs-mime/Composing.html) ELisp module. Pimalaya [ported it](https://github.com/pimalaya/core/tree/master/mml) in Rust. + + A raw email message is structured according to the [MIME](https://www.rfc-editor.org/rfc/rfc2045) standard. This standard produces verbose, non-friendly messages. Here comes MML: it simplifies the way email message body are structured. Thanks to its simple XML-based syntax, it allows you to easily add multiple parts, attach a binary file, or attach inline image to your body without dealing with the MIME standard. + + For instance, this MML template: + + ```eml + From: alice@localhost + To: bob@localhost + Subject: MML simple + + <#multipart type=alternative> + This is a plain text part. + <#part type=text/enriched> +
This is a centered enriched part
+ <#/multipart> + ``` + + compiles into the following MIME Message: + + ```eml + Subject: MML simple + To: bob@localhost + From: alice@localhost + MIME-Version: 1.0 + Date: Tue, 29 Nov 2022 13:07:01 +0000 + Content-Type: multipart/alternative; + boundary="4CV1Cnp7mXkDyvb55i77DcNSkKzB8HJzaIT84qZe" + + --4CV1Cnp7mXkDyvb55i77DcNSkKzB8HJzaIT84qZe + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: 7bit + + This is a plain text part. + --4CV1Cnp7mXkDyvb55i77DcNSkKzB8HJzaIT84qZe + Content-Type: text/enriched + Content-Transfer-Encoding: 7bit + +
This is a centered enriched part
+ --4CV1Cnp7mXkDyvb55i77DcNSkKzB8HJzaIT84qZe-- + ``` + + *See more examples at [pimalaya/core/mml](https://github.com/pimalaya/core/tree/master/mml/examples).* +
+ +
+ How to add attachments to a message? + + *Read first about the FAQ: How to compose a message?*. + + ```eml + From: alice@localhost + To: bob@localhost + Subject: How to attach stuff + + Regular binary attachment: + <#part filename=/path/to/file.pdf><#/part> + + Custom file name: + <#part filename=/path/to/file.pdf name=custom.pdf><#/part> + + Inline image: + <#part disposition=inline filename=/path/to/image.png><#/part> + ``` + + *See more examples at [pimalaya/core/mml](https://github.com/pimalaya/core/tree/master/mml/examples).* +
+ +
+ How to debug Himalaya REPL? + + The simplest way is to use `--debug` and `--trace` arguments. + + The advanced way is based on environment variables: + + - `RUST_LOG=`: determines the log level filter, can be one of `off`, `error`, `warn`, `info`, `debug` and `trace`. + - `RUST_SPANTRACE=1`: enables the spantrace (a span represent periods of time in which a program was executing in a particular context). + - `RUST_BACKTRACE=1`: enables the error backtrace. + - `RUST_BACKTRACE=full`: enables the full error backtrace, which include source lines where the error originated from. + + Logs are written to the `stderr`, which means that you can redirect them easily to a file: + + ``` + RUST_LOG=debug himalaya 2>/tmp/himalaya.log + ``` +
+ +
+ How the wizard discovers IMAP/SMTP configs? + + All the lookup mechanisms use the email address domain as base for the lookup. It is heavily inspired from the Thunderbird [Autoconfiguration](https://udn.realityripple.com/docs/Mozilla/Thunderbird/Autoconfiguration) protocol. For example, for the email address `test@example.com`, the lookup is performed as (in this order): + + 1. check for `autoconfig.example.com` + 2. look up of `example.com` in the ISPDB (the Thunderbird central database) + 3. look up `MX example.com` in DNS, and for `mx1.mail.hoster.com`, look up `hoster.com` in the ISPDB + 4. look up `SRV example.com` in DNS + 5. try to guess (`imap.example.com`, `smtp.example.com`…) +
+ ## Sponsoring -[![nlnet](https://nlnet.nl/logo/banner-160x60.png)](https://nlnet.nl/project/Himalaya/index.html) +[![nlnet](https://nlnet.nl/logo/banner-160x60.png)](https://nlnet.nl/) -Special thanks to the [NLnet foundation](https://nlnet.nl/project/Himalaya/index.html) and the [European Commission](https://www.ngi.eu/) that helped the project to receive financial support from: +Special thanks to the [NLnet foundation](https://nlnet.nl/) that helped the project to receive financial support from various programs of the [European Commission](https://www.ngi.eu/): -- [NGI Assure](https://nlnet.nl/assure/) in 2022 -- [NGI Zero Entrust](https://nlnet.nl/entrust/) in 2023 +- [NGI Assure](https://nlnet.nl/project/Himalaya/) in 2022-2023 +- [NGI Zero Entrust](https://nlnet.nl/project/Pimalaya/) in 2023-2024 +- [NGI Zero Core](https://nlnet.nl/project/Pimalaya-PIM/) in 2024-2025 If you appreciate the project, feel free to donate using one of the following providers: diff --git a/config.sample.toml b/config.sample.toml index ca1b709..b87c562 100644 --- a/config.sample.toml +++ b/config.sample.toml @@ -1,98 +1,603 @@ -# The account name. +################################################################################ +#### Global configuration ###################################################### +################################################################################ + +# Default display name for all accounts. It is used to build the full +# email address of an account: "Example" +# +display-name = "Example" + +# Default signature for all accounts. The signature is put at the +# bottom of all messages. It can be a path or a string. Supports TOML +# multilines. +# +# signature = "/path/to/signature/file" +# signature = """ +# Thanks you, +# Regards +# """ +signature = "Regards,\n" + +# Default signature delimiter for all accounts. It delimits the end of +# the message body from the signature. +# +signature-delim = "-- \n" + +# Default downloads directory path for all accounts. It is mostly used +# for downloading attachments. Defaults to the system temporary +# directory. +# +downloads-dir = "~/downloads" + +# Customizes the charset used to build the table. Defaults to markdown +# table style. +# +# See . +# +account.list.table.preset = "|| |-||| " + +# Customizes the color of the NAME column of the account listing +# table. +# +account.list.table.name-color = "green" + +# Customizes the color of the BACKENDS column of the account listing +# table. +# +account.list.table.backends-color = "blue" + +# Customizes the color of the DEFAULT column of the account listing +# table. +# +account.list.table.default-color = "reset" + +# Customizes the keybindings of command prompts. +# +# It does not yet apply to interactive prompts like folder selection, +# but it should in the future. +# +#repl.keybinds = "vi" +repl.keybinds = "emacs" + +################################################################################ +#### Account configuration ##################################################### +################################################################################ + [accounts.example] -# The current account will be used by default for all other commands. +# Defaultness of the account. The current account will be used by +# default in all commands. +# default = true -# The display-name and the email are used to build the full email -# address: "My example account" -display-name = "My example account" +# The email address associated to the current account. +# email = "example@localhost" -# The signature can be a string or a path to a file. -signature = "Regards," -signature-delim = "-- \n" +# The display name of the account. This and the email are used to +# build the full email address: "Example" +# +display-name = "Example" -# Enable the synchronization for this account. Running the command -# `account sync example` will synchronize all folders and all emails -# to a local Maildir at `$XDG_DATA_HOME/himalaya/example`. -sync.enable = false +# The signature put at the bottom of composed messages. It can be a +# path or a string. Supports TOML multilines. +# +#signature = "/path/to/signature/file" +#signature = """ +# Thanks you, +# Regards +#""" +signature = "Regards,\n" -# Override the default Maildir path for synchronization. -sync.dir = "/tmp/himalaya-sync-example" +# Signature delimiter. It delimits the end of the message body from +# the signature. +# +signature-delim = "-- \n" + +# Downloads directory path. It is mostly used for downloading +# attachments. Defaults to the system temporary directory. +downloads-dir = "~/downloads" -# Filter folders to sync -folder.sync.filter.include = ["INBOX"] -# folder.sync.filter.exclude = ["All mails"] -# folder.sync.filter = "all" +######################################## +#### Folder configuration ############## +######################################## -# Define main folder aliases +# Defines aliases for your mailboxes. There are 4 special aliases used +# by the tool: inbox, sent, drafts and trash. Other aliases can be +# defined as well. +# folder.alias.inbox = "INBOX" folder.alias.sent = "Sent" folder.alias.drafts = "Drafts" folder.alias.trash = "Trash" +folder.alias.a23 = "Archives/2023" -# Also define custom folder aliases -folder.alias.prev-year = "Archives/2023" +# Customizes the number of folders to show by page. +# +folder.list.page-size = 10 -# Default backend used for all the features like adding folders, -# listing envelopes or copying messages. -backend = "imap" +# Customizes the charset used to build the table. Defaults to markdown +# table style. +# +# See . +# +folder.list.table.preset = "|| |-||| " + +# Customizes the color of the NAME column of the folder listing table. +# +folder.list.table.name-color = "blue" +# Customizes the color of the DESC column of the folder listing table. +# +folder.list.table.desc-color = "green" + +######################################## +#### Envelope configuration ############ +######################################## + +# Customizes the number of envelopes to show by page. +# envelope.list.page-size = 10 + +# Customizes the format of the envelope date. +# +# See supported formats at . +# envelope.list.datetime-fmt = "%F %R%:z" -# Date are converted to the user's local timezone. +# Transforms envelopes date timezone into the user's local one. For +# example, if the user's local timezone is UTC, the envelope date +# `2023-06-15T09:00:00+02:00` becomes `2023-06-15T07:00:00-00:00`. +# envelope.list.datetime-local-tz = true -# Override the backend used for listing envelopes. -# envelope.list.backend = "imap" +# Customizes the charset used to build the table. Defaults to markdown +# table style. +# +# See . +# +envelope.list.table.preset = "|| |-||| " -# Send notification on receiving new envelopes -envelope.watch.received.notify.summary = "📬 New message from {sender}" +# Customizes the character of the unseen flag of the envelope listing +# table. +# +envelope.list.table.unseen-char = "*" -# Available placeholders: id, subject, sender, sender.name, -# sender.address, recipient, recipient.name, recipient.address. -envelope.watch.received.notify.body = "{subject}" +# Customizes the character of the replied flag of the envelope listing +# table. +# +envelope.list.table.replied-char = "R" -# Shell commands can also be executed when envelopes change -# envelope.watch.any.cmd = "mbsync -a" +# Customizes the character of the flagged flag of the envelope listing +# table. +# +envelope.list.table.flagged-char = "!" -# Override the backend used for sending messages. -message.send.backend = "smtp" +# Customizes the character of the attachment property of the envelope +# listing table. +# +envelope.list.table.attachment-char = "@" + +# Customizes the color of the ID column of the envelope listing table. +# +envelope.list.table.id-color = "red" + +# Customizes the color of the FLAGS column of the envelope listing +# table. +# +envelope.list.table.flags-color = "reset" + +# Customizes the color of the SUBJECT column of the envelope listing +# table. +# +envelope.list.table.subject-color = "green" + +# Customizes the color of the SENDER column of the envelope listing +# table. +# +envelope.list.table.sender-color = "blue" + +# Customizes the color of the DATE column of the envelope listing +# table. +# +envelope.list.table.date-color = "yellow" + +######################################## +#### Message configuration ############# +######################################## + +# Defines headers to show at the top of messages when reading them. +# +message.read.headers = ["From", "To", "Cc", "Subject"] + +# Represents the message text/plain format as defined in the +# RFC2646. +# +# See . +# +#message.read.format.fixed = 80 +#message.read.format = "flowed" +message.read.format = "auto" + +# Defines headers to show at the top of messages when writing them. +# +message.write.headers = ["From", "To", "In-Reply-To", "Cc", "Subject"] + +# Saves a copy of sent messages to the sent folder. The sent folder is +# taken from folder.alias, defaults to Sent. +# +message.send.save-copy = true + +# Hook called just before sending a message. The command should take a +# raw message as standard input (stdin) and returns the modified raw +# message to the standard output (stdout). +# +message.send.pre-hook = "process-markdown.sh" + +# Customizes the message deletion style. Message deletion can be +# performed either by moving messages to the Trash folder or by adding +# the Deleted flag to their respective envelopes. +# +#message.delete.style = "flag" +message.delete.style = "folder" -# Save a copy of sent messages to the sent folder. -message.send.save-copy = false +######################################## +#### Template configuration ############ +######################################## -# IMAP config +# Defines how and where the signature should be displayed when writing +# a new message. +# +#template.new.signature-style = "hidden" +#template.new.signature-style = "attached" +template.new.signature-style = "inlined" + +# Defines the posting style when replying to a message. +# +# See . +# +#template.reply.posting-style = "interleaved" +#template.reply.posting-style = "bottom" +template.reply.posting-style = "top" + +# Defines how and where the signature should be displayed when +# repyling to a message. +# +#template.reply.signature-style = "hidden" +#template.reply.signature-style = "attached" +#template.reply.signature-style = "above-quote" +template.reply.signature-style = "below-quote" + +# Defines the headline format put at the top of a quote when replying +# to a message. +# +# Available placeholders: {senders} +# See supported date formats at . +# +template.reply.quote-headline-fmt = "On %d/%m/%Y %H:%M, {senders} wrote:\n" + +# Defines the posting style when forwarding a message. +# +# See . +# +#template.forward.posting-style = "attached" +template.forward.posting-style = "top" + +# Defines how and where the signature should be displayed when +# forwarding a message. +# +#template.forward.signature-style = "hidden" +#template.forward.signature-style = "attached" +template.forward.signature-style = "inlined" + +# Defines the headline format put at the top of the quote when +# forwarding a message. +# +template.forward.quote-headline = "-------- Forwarded Message --------\n" + +######################################## +#### PGP configuration ################# +######################################## + +# TODO +#pgp.backend = "commands" +#pgp.backend = "gpg" +#pgp.backend = "native" + +######################################## +#### IMAP configuration ################ +######################################## + +# Defines the IMAP backend as the default one for all features. +# +backend = "imap" + +# IMAP server host name. +# imap.host = "localhost" -imap.port = 3143 + +# IMAP server port. +# +#imap.port = 143 +imap.port = 993 + +# IMAP server encryption. +# +#imap.encryption = "none" # or false +#imap.encryption = "start-tls" +imap.encryption = "tls" # or true + +# IMAP server login. +# imap.login = "example@localhost" -# Encryption can be either "tls" (or true), "start-tls" or "none" (or false). -imap.encryption = "none" +# IMAP server password authentication configuration. +# +imap.auth.type = "password" +# +# Password can be inlined (not recommended). +# +#imap.auth.raw = "p@assw0rd" +# +# Password can be stored inside your system global keyring (requires +# the keyring cargo feature). You must run at least once `himalaya +# account configure` to set up the password. +# +#imap.auth.keyring = "example-imap" +# +# Password can be retrieved from a shell command. +# +imap.auth.cmd = "pass show example-imap" + +# IMAP server OAuth 2.0 authorization configuration. +# +#imap.auth.type = "oauth2" +# +# Client identifier issued to the client during the registration +# process described in RFC6749. +# See . +# +#imap.auth.client-id = "client-id" +# +# Client password issued to the client during the registration process +# described in RFC6749. +# +# Defaults to keyring "-imap-client-secret". +# See . +# +#imap.auth.client-secret.raw = "" +#imap.auth.client-secret.keyring = "example-imap-client-secret" +#imap.auth.client-secret.cmd = "pass show example-imap-client-secret" +# +# Method for presenting an OAuth 2.0 bearer token to a service for +# authentication. +# +#imap.auth.method = "oauthbearer" +#imap.auth.method = "xoauth2" +# +# URL of the authorization server's authorization endpoint. +# +#imap.auth.auth-url = "https://accounts.google.com/o/oauth2/v2/auth" +# +# URL of the authorization server's token endpoint. +# +#imap.auth.token-url = "https://www.googleapis.com/oauth2/v3/token" +# +# Access token returned by the token endpoint and used to access +# protected resources. It is recommended to use the keyring variant, +# as it will refresh automatically. +# +# Defaults to keyring "-imap-access-token". +# +#imap.auth.access-token.raw = "" +#imap.auth.access-token.keyring = "example-imap-access-token" +#imap.auth.access-token.cmd = "pass show example-imap-access-token" +# +# Refresh token used to obtain a new access token (if supported by the +# authorization server). It is recommended to use the keyring variant, +# as it will refresh automatically. +# +# Defaults to keyring "-imap-refresh-token". +# +#imap.auth.refresh-token.raw = "" +#imap.auth.refresh-token.keyring = "example-imap-refresh-token" +#imap.auth.refresh-token.cmd = "pass show example-imap-refresh-token" +# +# Enable the protection, as defined in RFC7636. +# +# See . +# +#imap.auth.pkce = true +# +# Access token scope(s), as defined by the authorization server. +# +#imap.auth.scope = "unique scope" +#imap.auth.scopes = ["multiple", "scopes"] +# +# Host name of the redirect server. +# Defaults to localhost. +# +#imap.auth.redirect-host = "localhost" +# +# Port of the redirect server. +# Defaults to the first available one. +# +#imap.auth.redirect-port = 9999 -# Get password from a raw string (not safe) -imap.passwd.raw = "password" +######################################## +#### Maildir configuration ############# +######################################## -# Get password from a shell command -# imap.passwd.cmd = "echo password" +# Defines the Maildir backend as the default one for all features. +# +#backend = "maildir" -# Get password from your global system keyring using secret service -# Keyring secrets can be (re)set with the command `account configure example` -# imap.passwd.keyring = "example-imap-password" +# The Maildir root directory. The path should point to the root level +# of the Maildir directory. +# +#maildir.root-dir = "~/.Mail/example" -# Customize at which period, in seconds, the IMAP IDLE mode should refresh. -# Defaults to 1740 (29 min), as defined in the RFC. -# imap.watch.timeout = 25 +# Does the Maildir folder follows the Maildir++ standard? +# +# See . +# +#maildir.maildirpp = false -# SMTP config +######################################## +#### Notmuch configuration ############# +######################################## + +# Defines the Notmuch backend as the default one for all features. +# +#backend = "notmuch" + +# The path to the Notmuch database. The path should point to the root +# directory containing the Notmuch database (usually the root Maildir +# directory). +# +#notmuch.db-path = "~/.Mail/example" + +# Overrides the default path to the Maildir folder. +# +#notmuch.maildir-path = "~/.Mail/example" + +# Overrides the default Notmuch configuration file path. +# +#notmuch.config-path = "~/.notmuchrc" + +# Override the default Notmuch profile name. +# +#notmuch.profile = "example" + +######################################## +#### SMTP configuration ################ +######################################## + +# Defines the SMTP backend for the message sending feature. +# +message.send.backend = "smtp" + +# SMTP server host name. +# smtp.host = "localhost" -smtp.port = 3025 + +# SMTP server port. +# +#smtp.port = 25 +#smtp.port = 465 +smtp.port = 587 + +# SMTP server encryption. +# +#smtp.encryption = "none" # or false +#smtp.encryption = "start-tls" +smtp.encryption = "tls" # or true + +# SMTP server login. +# smtp.login = "example@localhost" -smtp.encryption = false -smtp.passwd.raw = "password" -# PGP needs to be enabled with one of those cargo feature: -# pgp-commands, pgp-gpg or pgp-native -# pgp.backend = "gpg" +# SMTP server password authentication configuration. +# +smtp.auth.type = "password" +# +# Password can be inlined (not recommended). +# +#smtp.auth.raw = "p@assw0rd" +# +# Password can be stored inside your system global keyring (requires +# the keyring cargo feature). You must run at least once `himalaya +# account configure` to set up the password. +# +#smtp.auth.keyring = "example-smtp" +# +# Password can be retrieved from a shell command. +# +smtp.auth.cmd = "pass show example-smtp" + +# SMTP server OAuth 2.0 authorization configuration. +# +#smtp.auth.type = "oauth2" +# +# Client identifier issued to the client during the registration +# process described in RFC6749. +# See . +# +#smtp.auth.client-id = "client-id" +# +# Client password issued to the client during the registration process +# described in RFC6749. +# +# Defaults to keyring "-smtp-client-secret". +# See . +# +#smtp.auth.client-secret.raw = "" +#smtp.auth.client-secret.keyring = "example-smtp-client-secret" +#smtp.auth.client-secret.cmd = "pass show example-smtp-client-secret" +# +# Method for presenting an OAuth 2.0 bearer token to a service for +# authentication. +# +#smtp.auth.method = "oauthbearer" +#smtp.auth.method = "xoauth2" +# +# URL of the authorization server's authorization endpoint. +# +#smtp.auth.auth-url = "https://accounts.google.com/o/oauth2/v2/auth" +# +# URL of the authorization server's token endpoint. +# +#smtp.auth.token-url = "https://www.googleapis.com/oauth2/v3/token" +# +# Access token returned by the token endpoint and used to access +# protected resources. It is recommended to use the keyring variant, +# as it will refresh automatically. +# +# Defaults to keyring "-smtp-access-token". +# +#smtp.auth.access-token.raw = "" +#smtp.auth.access-token.keyring = "example-smtp-access-token" +#smtp.auth.access-token.cmd = "pass show example-smtp-access-token" +# +# Refresh token used to obtain a new access token (if supported by the +# authorization server). It is recommended to use the keyring variant, +# as it will refresh automatically. +# +# Defaults to keyring "-smtp-refresh-token". +# +#smtp.auth.refresh-token.raw = "" +#smtp.auth.refresh-token.keyring = "example-smtp-refresh-token" +#smtp.auth.refresh-token.cmd = "pass show example-smtp-refresh-token" +# +# Enable the protection, as defined in RFC7636. +# +# See . +# +#smtp.auth.pkce = true +# +# Access token scope(s), as defined by the authorization server. +# +#smtp.auth.scope = "unique scope" +#smtp.auth.scopes = ["multiple", "scopes"] +# +# Host name of the redirect server. +# Defaults to localhost. +# +#smtp.auth.redirect-host = "localhost" +# +# Port of the redirect server. +# Defaults to the first available one. +# +#smtp.auth.redirect-port = 9999 + +######################################## +#### Sendmail configuration ############ +######################################## + +# Defines the Sendmail backend for the message sending feature. +# +#message.send.backend = "sendmail" + +# Customizes the sendmail shell command. +# +#sendmail.cmd = "/usr/bin/sendmail" diff --git a/flake.lock b/flake.lock index 06ce42e..5a4deaf 100644 --- a/flake.lock +++ b/flake.lock @@ -8,15 +8,15 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1708842026, - "narHash": "sha256-1r+7l66CFPCjRDZEKq6r7aCpfjqhIkrF3ZTQyEFnkTs=", - "owner": "nix-community", + "lastModified": 1713081044, + "narHash": "sha256-ZwbJDrizU+nzU7wTgokYuu5yK71wLPmOLukiunm5B6Y=", + "owner": "soywod", "repo": "fenix", - "rev": "b7fff64b5bc36b1662f8317632b11b15b3839b2a", + "rev": "af99e7e9c87389c0a5aaf953478664d7126c2b14", "type": "github" }, "original": { - "owner": "nix-community", + "owner": "soywod", "repo": "fenix", "type": "github" } @@ -79,16 +79,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1708702655, - "narHash": "sha256-qxT5jSLhelfLhQ07+AUxSTm1VnVH+hQxDkQSZ/m/Smo=", + "lastModified": 1728328465, + "narHash": "sha256-a0a0M1TmXMK34y3M0cugsmpJ4FJPT/xsblhpiiX1CXo=", "owner": "nixos", "repo": "nixpkgs", - "rev": "c5101e457206dd437330d283d6626944e28794b3", + "rev": "1bfbbbe5bbf888d675397c66bfdb275d0b99361c", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-23.11", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 64e442f..cac2f28 100644 --- a/flake.nix +++ b/flake.nix @@ -2,13 +2,15 @@ description = "REPL to manage emails"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05"; gitignore = { url = "github:hercules-ci/gitignore.nix"; inputs.nixpkgs.follows = "nixpkgs"; }; fenix = { - url = "github:nix-community/fenix"; + # https://github.com/nix-community/fenix/pull/145 + # url = "github:nix-community/fenix"; + url = "github:soywod/fenix"; inputs.nixpkgs.follows = "nixpkgs"; }; naersk = { @@ -23,132 +25,210 @@ outputs = { self, nixpkgs, gitignore, fenix, naersk, ... }: let + inherit (nixpkgs) lib; inherit (gitignore.lib) gitignoreSource; - mkToolchain = import ./rust-toolchain.nix fenix; + crossSystems = { + x86_64-linux = { + x86_64-linux = { + rustTarget = "x86_64-unknown-linux-musl"; + }; - mkDevShells = buildPlatform: - let - pkgs = import nixpkgs { system = buildPlatform; }; - rust-toolchain = mkToolchain.fromFile { system = buildPlatform; }; - in - { - default = pkgs.mkShell { - nativeBuildInputs = with pkgs; [ - pkg-config - ]; - buildInputs = with pkgs; [ - # Nix - rnix-lsp - nixpkgs-fmt + aarch64-linux = rec { + rustTarget = "aarch64-unknown-linux-musl"; + runner = { pkgs, himalaya }: "${pkgs.qemu}/bin/qemu-aarch64 ${himalaya}"; + mkPackage = { system, pkgs }: package: + let + inherit (mkPkgsCross system rustTarget) stdenv; + cc = "${stdenv.cc}/bin/${stdenv.cc.targetPrefix}cc"; + in + package // { + TARGET_CC = cc; + CARGO_BUILD_RUSTFLAGS = package.CARGO_BUILD_RUSTFLAGS ++ [ "-Clinker=${cc}" ]; + }; + }; - # Rust - rust-toolchain - cargo-watch + x86_64-windows = { + rustTarget = "x86_64-pc-windows-gnu"; + runner = { pkgs, himalaya }: + let wine = pkgs.wine.override { wineBuild = "wine64"; }; + in "${wine}/bin/wine64 ${himalaya}.exe"; + mkPackage = { system, pkgs }: package: + let + inherit (pkgs.pkgsCross.mingwW64) stdenv windows; + cc = "${stdenv.cc}/bin/${stdenv.cc.targetPrefix}cc"; + in + package // { + depsBuildBuild = [ stdenv.cc windows.pthreads ]; + TARGET_CC = cc; + CARGO_BUILD_RUSTFLAGS = package.CARGO_BUILD_RUSTFLAGS ++ [ "-Clinker=${cc}" ]; + }; + }; + }; + + aarch64-linux = { + aarch64-linux = { + rustTarget = "aarch64-unknown-linux-musl"; + }; + }; - # OpenSSL - openssl.dev + x86_64-darwin = { + x86_64-darwin = { + rustTarget = "x86_64-apple-darwin"; + mkPackage = { pkgs, ... }: package: + let inherit (pkgs.darwin.apple_sdk.frameworks) AppKit Cocoa; + in + package // { + buildInputs = [ Cocoa ]; + NIX_LDFLAGS = "-F${AppKit}/Library/Frameworks -framework AppKit"; + }; + }; - # Notmuch - notmuch + # FIXME: https://github.com/NixOS/nixpkgs/issues/273442 + aarch64-darwin = { + rustTarget = "aarch64-apple-darwin"; + runner = { pkgs, himalaya }: "${pkgs.qemu}/bin/qemu-aarch64 ${himalaya}"; + mkPackage = { system, pkgs }: package: + let + inherit ((mkPkgsCross system "aarch64-darwin").pkgsStatic) stdenv darwin; + inherit (darwin.apple_sdk.frameworks) AppKit Cocoa; + cc = "${stdenv.cc}/bin/${stdenv.cc.targetPrefix}cc"; + in + package // { + buildInputs = [ Cocoa ]; + NIX_LDFLAGS = "-F${AppKit}/Library/Frameworks -framework AppKit"; + TARGET_CC = cc; + CARGO_BUILD_RUSTFLAGS = package.CARGO_BUILD_RUSTFLAGS ++ [ "-Clinker=${cc}" ]; + }; + }; + }; - # GPG - gnupg - gpgme - ]; + aarch64-darwin = { + aarch64-darwin = { + rustTarget = "aarch64-apple-darwin"; + mkPackage = { pkgs, ... }: package: + let inherit (pkgs.darwin.apple_sdk.frameworks) AppKit Cocoa; + in + package // { + buildInputs = [ Cocoa ]; + NIX_LDFLAGS = "-F${AppKit}/Library/Frameworks -framework AppKit"; + }; }; }; + }; + + eachBuildSystem = lib.genAttrs (builtins.attrNames crossSystems); - mkPackage = pkgs: buildPlatform: targetPlatform: package: + mkPkgsCross = buildSystem: crossSystem: import nixpkgs { + system = buildSystem; + crossSystem.config = crossSystem; + }; + + mkToolchain = import ./rust-toolchain.nix fenix; + + mkApp = { pkgs, buildSystem, targetSystem ? buildSystem }: let + himalaya = lib.getExe self.packages.${buildSystem}.${targetSystem}; + wrapper = crossSystems.${buildSystem}.${targetSystem}.runner or (_: himalaya) { inherit pkgs himalaya; }; + program = lib.getExe (pkgs.writeShellScriptBin "himalaya" "${wrapper} $@"); + app = { inherit program; type = "app"; }; + in + app; + + mkApps = buildSystem: + let + pkgs = import nixpkgs { system = buildSystem; }; + mkApp' = targetSystem: _: mkApp { inherit pkgs buildSystem targetSystem; }; + defaultApp = mkApp { inherit pkgs buildSystem; }; + apps = builtins.mapAttrs mkApp' crossSystems.${buildSystem}; + in + apps // { default = defaultApp; }; + + mkPackage = { pkgs, buildSystem, targetSystem ? buildSystem }: + let + targetConfig = crossSystems.${buildSystem}.${targetSystem}; toolchain = mkToolchain.fromTarget { - inherit pkgs buildPlatform targetPlatform; + inherit pkgs buildSystem; + targetSystem = targetConfig.rustTarget; }; - naersk' = naersk.lib.${buildPlatform}.override { + rust = naersk.lib.${buildSystem}.override { cargo = toolchain; rustc = toolchain; }; - package' = { - name = "himalaya-repl"; + mkPackage' = targetConfig.mkPackage or (_: p: p); + himalaya = "./himalaya"; + runner = targetConfig.runner or (_: himalaya) { inherit pkgs himalaya; }; + package = mkPackage' { inherit pkgs; system = buildSystem; } { + name = "himalaya"; src = gitignoreSource ./.; - overrideMain = _: { - postInstall = '' - mkdir -p $out/share/applications/ - cp assets/himalaya-repl.desktop $out/share/applications/ - ''; - }; - doCheck = true; - cargoTestOptions = opts: opts ++ [ "--lib" ]; - } // pkgs.lib.optionalAttrs (!isNull targetPlatform) { - CARGO_BUILD_TARGET = targetPlatform; - } // package; + strictDeps = true; + doCheck = false; + auditable = false; + nativeBuildInputs = with pkgs; [ pkg-config ]; + CARGO_BUILD_TARGET = targetConfig.rustTarget; + CARGO_BUILD_RUSTFLAGS = [ "-Ctarget-feature=+crt-static" ]; + postInstall = '' + export WINEPREFIX="$(mktemp -d)" + + mkdir -p $out/bin/share/{applications,completions,man,services} + cp assets/himalaya.desktop $out/bin/share/applications/ + cp assets/himalaya-watch@.service $out/bin/share/services/ + + cd $out/bin + ${runner} man ./share/man + ${runner} completion bash > ./share/completions/himalaya.bash + ${runner} completion elvish > ./share/completions/himalaya.elvish + ${runner} completion fish > ./share/completions/himalaya.fish + ${runner} completion powershell > ./share/completions/himalaya.powershell + ${runner} completion zsh > ./share/completions/himalaya.zsh + tar -czf himalaya.tgz himalaya* share + ${pkgs.zip}/bin/zip -r himalaya.zip himalaya* share + + mv share ../ + mv himalaya.tgz himalaya.zip ../ + ''; + }; in - naersk'.buildPackage package'; + rust.buildPackage package; - mkPackages = buildPlatform: + mkPackages = buildSystem: let - pkgs = import nixpkgs { system = buildPlatform; }; - mkPackage' = mkPackage pkgs buildPlatform; + pkgs = import nixpkgs { system = buildSystem; }; + mkPackage' = targetSystem: _: mkPackage { inherit pkgs buildSystem targetSystem; }; + defaultPackage = mkPackage { inherit pkgs buildSystem; }; + packages = builtins.mapAttrs mkPackage' crossSystems.${buildSystem}; in - rec { - default = if pkgs.stdenv.isDarwin then macos else linux; - linux = mkPackage' null { }; - linux-musl = mkPackage' "x86_64-unknown-linux-musl" (with pkgs.pkgsStatic; { - CARGO_BUILD_RUSTFLAGS = "-C target-feature=+crt-static"; - hardeningDisable = [ "all" ]; - }); - macos = mkPackage' null (with pkgs.darwin.apple_sdk.frameworks; { - # NOTE: needed to prevent error Undefined symbols - # "_OBJC_CLASS_$_NSImage" and - # "_LSCopyApplicationURLsForBundleIdentifier" - NIX_LDFLAGS = "-F${AppKit}/Library/Frameworks -framework AppKit"; - buildInputs = [ Cocoa ]; - }); - # FIXME: bzlip: fatal error: windows.h: No such file or directory - # May be related to SQLite. - windows = mkPackage' "x86_64-pc-windows-gnu" { - strictDeps = true; - depsBuildBuild = with pkgs.pkgsCross.mingwW64; [ - stdenv.cc - windows.pthreads + packages // { default = defaultPackage; }; + + mkDevShells = buildSystem: + let + pkgs = import nixpkgs { system = buildSystem; }; + rust-toolchain = mkToolchain.fromFile { inherit buildSystem; }; + defaultShell = pkgs.mkShell { + nativeBuildInputs = with pkgs; [ pkg-config ]; + buildInputs = with pkgs; [ + # Nix + nixd + nixpkgs-fmt + + # Rust + rust-toolchain + cargo-watch + + # Email env + gnupg + gpgme + msmtp + notmuch ]; }; - }; - - mkApp = drv: - let exePath = drv.passthru.exePath or "/bin/himalaya-repl"; in - { - type = "app"; - program = "${drv}${exePath}"; - }; + { default = defaultShell; }; - mkApps = buildPlatform: - let - pkgs = import nixpkgs { system = buildPlatform; }; - in - rec { - default = if pkgs.stdenv.isDarwin then macos else linux; - linux = mkApp self.packages.${buildPlatform}.linux; - linux-musl = mkApp self.packages.${buildPlatform}.linux-musl; - macos = mkApp self.packages.${buildPlatform}.macos; - windows = - let - wine = pkgs.wine.override { wineBuild = "wine64"; }; - himalaya = self.packages.${buildPlatform}.windows; - app = pkgs.writeShellScriptBin "himalaya" '' - export WINEPREFIX="$(mktemp -d)" - ${wine}/bin/wine64 ${himalaya}/bin/himalaya.exe $@ - ''; - in - mkApp app; - }; - supportedSystems = [ "aarch64-linux" "aarch64-darwin" "x86_64-darwin" "x86_64-linux" ]; - forEachSupportedSystem = f: nixpkgs.lib.genAttrs supportedSystems f; in { - apps = forEachSupportedSystem mkApps; - packages = forEachSupportedSystem mkPackages; - devShells = forEachSupportedSystem mkDevShells; + apps = eachBuildSystem mkApps; + packages = eachBuildSystem mkPackages; + devShells = eachBuildSystem mkDevShells; }; } diff --git a/rust-toolchain.nix b/rust-toolchain.nix index ba00e1f..57140c2 100644 --- a/rust-toolchain.nix +++ b/rust-toolchain.nix @@ -2,26 +2,23 @@ fenix: let file = ./rust-toolchain.toml; - sha256 = "e4mlaJehWBymYxJGgnbuCObVlqMlQSilZ8FljG9zPHY="; + sha256 = "+syqAd2kX8KVa8/U2gz3blIQTTsYYt3U63xBWaGOSc8="; in { - fromFile = { system }: fenix.packages.${system}.fromToolchainFile { + fromFile = { buildSystem }: fenix.packages.${buildSystem}.fromToolchainFile { inherit file sha256; }; - fromTarget = { pkgs, buildPlatform, targetPlatform ? null }: + fromTarget = { pkgs, buildSystem, targetSystem }: let - inherit ((pkgs.lib.importTOML file).toolchain) channel; - toolchain = fenix.packages.${buildPlatform}; + name = (pkgs.lib.importTOML file).toolchain.channel; + fenixPackage = fenix.packages.${buildSystem}; + toolchain = fenixPackage.fromToolchainName { inherit name sha256; }; + targetToolchain = fenixPackage.targets.${targetSystem}.fromToolchainName { inherit name sha256; }; in - if - isNull targetPlatform - then - fenix.packages.${buildPlatform}.${channel}.toolchain - else - toolchain.combine [ - toolchain.${channel}.rustc - toolchain.${channel}.cargo - toolchain.targets.${targetPlatform}.${channel}.rust-std - ]; + fenixPackage.combine [ + toolchain.rustc + toolchain.cargo + targetToolchain.rust-std + ]; } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 3ccec48..a727497 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "stable" +channel = "1.77.0" profile = "default" components = [ "rust-src", "rust-analyzer" ]