Note
This channel features automation through GitHub Actions. That means bugs are to be expected.
If you want a safer experience but still close to source, check out the built-in emacs-next packages, or the ones available at the divya-lambda channel.
Guix channel that checks emacs upstream for updates every week, updating the packages to the latest GitHub mirror commit.
This channel is sort of a fork of divya-lambda1, which features a non-automated build of emacs-master
, taken from guix-channel-emacs-master2.
Commit updates are made with a GitHub Actions workflow, and are automatically signed3.
Use this for adding the channel to your configuration:
(cons* (channel
(name 'emacs-master)
(url "https://github.com/gs-101/emacs-master.git")
(branch "main")
(introduction
(make-channel-introduction
"568579841d0ca41a9d222a2cfcad9a7367f9073b"
(openpgp-fingerprint
"3049 BF6C 0829 94E4 38ED 4A15 3033 E0E9 F7E2 5FE4"))))
%default-channels)
This channel uses Guix Moe CI, a community build farm for Guix. For information on how to use it, read the announcement blog post.
Since this takes packages from divya-lambda, the following packages are available:
emacs-master
-
Regular Emacs package.
emacs-master-pgtk
-
Package featuring a GTK build. Best suited for Wayland users.
emacs-master-lucid
-
Package using the Lucid (Athena) X toolkit. Best suited for those on X11.
Some recommend it over PGTK, so try both of them out and use what best suits you.
emacs-master-igc
-
New garbage collection method in development.
emacs-master-pgtk-igc
-
IGC + GTK.
In case I stop maintaining this and someone else becomes interested, this section details how the workflow works.
Scheduling is done through the use of a cron job, taken from copr-lutris-git4.
At first, it ran every hour:
on:
workflow_dispatch:
schedule:
- cron: "0 * * * *"
But then I changed it to every two hours because I thought I was overloading Savannah (when this used to rely on Savannah):
on:
workflow_dispatch:
schedule:
- cron: "0 */2 * * *"
Now, it runs weekly because substitutes would occupy too much storage in CI.
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * 1"
To allow GitHub Actions to commit to the repository, you need to give the job write permissions:
jobs:
update-emacs:
runs-on: ubuntu-latest
permissions:
contents: write
This should’ve been clear to me from the start, but, to have Actions actually work with your repository, you have to use the checkout action:
- name: Checkout Repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
This makes the runner clone your repository.
This is going to be needed later. It cuts the exact space where the commit hash is found, storing it in a environment variable, so it can be used by the other steps.
- name: Get Current Commit
run: |
echo "current-commit=$(grep "(define emacs-master-commit" emacs-master.scm | awk '{print $3}' | cut -c 2-41)" >> $GITHUB_ENV
- name: Cache Emacs
id: cache-emacs
uses: actions/cache@640a1c2554105b57832a23eea0b4672fc7a790d5
with:
path: emacs
key: ${{ runner.os }}-emacs
Since Emacs is pretty slow to clone, I decided to just have it cached.
- name: Clone Emacs
if: ${{ steps.cache-emacs.outputs.cache-hit != 'true' }}
run: git clone https://github.com/emacs-mirror/emacs.git
Runs if Emacs can’t be restored from the cache. Pretty slow.
- name: Update Emacs
if: ${{ steps.cache-emacs.outputs.cache-hit == 'true' }}
run: cd emacs && git pull
If Emacs was successfully restored from the cache, we take a quicker route, by just updating it with a pull.
This is also going to be needed later, but it will have more use than current_commit
, as it will be used to actually update the packages.
- name: Get Source Commit
run: echo "source_commit=$(cd emacs && git rev-parse --verify HEAD)" >> $GITHUB_ENV
Taking advantage of the Emacs checkout we have cached, we use git
again. This time, to get the latest commit reported by it from the pull.
Despite what the echoed messages say, this doesn’t make the workflow exit directly. It’s a simple comparison for defining a boolean variable to be used by the other steps.
- name: Compare Commits
id: compare-commits
run: |
if [ "${{env.source_commit }}" != "${{ env.current_commit }}" ]; then
echo "The commits are different. Continue the workflow."
echo "different_commit='true'" >> $GITHUB_OUTPUT
else
echo "The commits are the same. Exiting the workflow..."
echo "different_commit='false'" >> $GITHUB_OUTPUT
fi
Guix is installed just to get the hash.
- name: Install Guix
if: ${{ contains(steps.compare-commits.Outputs.different_commit, 'true') }}
run: sudo apt-get install -y guix
It is installed from apt
as there’s no need to get it directly from source just to get a hash.
- name: Get Hash
if: ${{ contains(steps.compare-commits.Outputs.different_commit, 'true') }}
run: echo "hash="$(guix hash -x --serializer=nar emacs)"" >> $GITHUB_ENV
Uses Guix to, well, get the hash of the repository at that commit.
- name: Set Values
if: ${{ contains(steps.compare-commits.Outputs.different_commit, 'true') }}
run: |
sed -i "s/define emacs-master-commit \"\(.*\)\"/define emacs-master-commit \"${{ env.source_commit }}\"/" emacs-master.scm
sed -i "s/define emacs-master-hash \"\(.*\)\"/define emacs-master-hash \"${{ env.hash }}\"/" emacs-master.scm
With some nice sed
incantations the values are replaced in the file. The .*
is probably an exageration, I think I could use [a-z0-9]+
.
This is where we use those instructions from “Sign git commits with GPG in GitHub Actions”. This is the workflow’s key, not yours.
We’ll set up some repository secrets. See how repository is in bold? That indicates another mistake I made during this. I thought that GitHub Actions used enviroment secrets, so I wasted some time on this.
In case you didn’t know how, you can generate a GPG key with:
gpg --full-generate-key
- When choosing a key type, you can pick a signing only one if you want. We have no need for encryption here. I always choose RSA.
- For the keysize, same thing, you choose. I always go for 4096 because there’s no issue in doing this.
- Make it not expire if you want, though, that can be insecure.
- Use either your real name or your GitHub username.
- This should be the e-mail address you use for GitHub.
- Add a descriptive comment here, you’ll start to make a lot of these once you get used to them. Mine is “GitHub Actions Key”.
- Make a password.
- There is no other step, that was it!
Now we’ll get to the secrets. Save them to Settings → Secrets and variables → Actions → Repository secrets with these exact names.
GPG_KEY_PASSPHRASE
-
This is the password you set up for the key.
GPG_KEY_ID
-
This is the identification of the key, you can get this with:
gpg --list-secret-keys --keyid-format=long
sec something/YOU-WANT-THIS-HERE 1111-11-11 [SC] [expires: 9999-99-99] don't-bother-with-this uid [ultimate] your-name (GitHub Actions Key) <the-email-you-used@address.com>
You’ll want the numbers and letters that are in the same position as
YOU-WANT-THIS-HERE
in the example above.your-name
andthe-email-you-used@address.com
are also important, but will be explained later. GPG_KEY
-
THis is your key itself, exported in base64. Based on the previous variable, you’d run:
gpg --export-secret-keys YOU-WANT-THIS-HERE | base64
This will give you even more numbers and letters.
Note
If your terminal added newlines for the display, before adding this output to your secrets, remove the newlines and make everything a single line. I’m not sure if this is necessary, but seems like a good practice.
This just makes the runner import your base64 encoded key:
- name: Import GPG Key
if: ${{ contains(steps.compare-commits.Outputs.different_commit, 'true') }}
run: echo "$GPG_KEY" | base64 --decode | gpg --batch --import
env:
GPG_KEY: ${{ secrets.GPG_KEY }}
The if
statement comes from our previous comparison step. This and the next steps only run if different_commit
is true
.
Used in the next step for Git. Makes it so that the runner always inputs the passphrase, to keep the process automatic. It’s not like we can access it to input the password, and even if we could, that would be a manual step.
- name: Custom GPG Signing Program
if: ${{ contains(steps.compare-commits.Outputs.different_commit, 'true') }}
run: |
echo "#!/bin/bash" >> /tmp/gpg.sh
echo "gpg --batch --pinentry-mode=loopback --passphrase \$GPG_KEY_PASSPHRASE \"\$@\"" >> /tmp/gpg.sh
chmod +x /tmp/gpg.sh
env:
GPG_KEY_PASSPHRASE: ${{ secrets.GPG_KEY_PASSPHRASE }}
Nothing out of the ordinary. This just makes Git use our key.
- name: Set up Git
if: ${{ contains(steps.compare-commits.Outputs.different_commit, 'true') }}
run: |
git config commit.gpgsign true
git config user.signingkey $GPG_KEY_ID
git config gpg.program /tmp/gpg.sh
env:
GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }}
- name: Commit
if: ${{ contains(steps.compare-commits.Outputs.different_commit, 'true') }}
run: |
git add emacs-master.scm
short_commit=$(grep "(define emacs-master-commit" emacs-master.scm | awk '{print $3}' | cut -c 2-8)
git commit -m "feat(emacs-master.scm): update Emacs to $short_commit" --gpg-sign=$GPG_KEY_ID
git push --set-upstream origin main
env:
GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }}
GPG_KEY_PASSPHRASE: ${{ secrets.GPG_KEY_PASSPHRASE }}
GIT_COMMITTER_NAME: ${{ secrets.GIT_COMMITTER_NAME }}
GIT_COMMITTER_EMAIL: ${{ secrets.GIT_COMMITTER_EMAIL }}
GIT_AUTHOR_NAME: github-actions
GIT_AUTHOR_EMAIL: github-actions@example.com
Not sure if --gpg-sign=$GPG_KEY_ID
is necessary, but I don’t want to change as everything is working now. Try doing a run without it to see the outcome.
Now, remember when I said that your-name
and the-email-you-used@address.com
were important? This is where they are used. Add them as GIT_COMMITTER_NAME
and GIT_COMMITTER_EMAIL
, respectively.
GIT_AUTHOR_NAME
should preferably be the name of your workflow bot (we use GitHub Actions, so I named it github-actions
here). GIT_AUTHOR_EMAIL
can be anything.
And that was it for the workflow! Hope you could understand everything.
1 Ranjan, D. (2024) “Divya-lambda.” Available at: https://codeberg.org/divyaranjan/divya-lambda (Accessed: January 16, 2025).
2 Azmain Turja, A. (2023) “guix-channel-emacs-master.” Available at: https://codeberg.org/akib/guix-channel-emacs-master (Accessed: January 16, 2025).
3 Bakulin, S. “Sign git commits with GPG in GitHub Actions” Available at: https://gist.github.com/vansergen/88eb7e71fea2e3bdaf6aa3e752371eb7 (Accessed: January 16, 2025).
4 Greiner, J. (2025) “Projectsynchro/copr-lutris-git.” Available at: https://github.com/ProjectSynchro/copr-lutris-git (Accessed: January 18, 2025).