Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
a1a80bb
build: take the Go version from go.mod
jorgemoralespou Oct 26, 2026
d7fe218
test(github): check that API requests carry the configured token
jorgemoralespou Oct 26, 2026
34045d8
build(deps): update go-github to v93, cobra to 1.10.2 and testify to …
jorgemoralespou Oct 26, 2026
3a16609
refactor(github): authenticate with go-github's token option
jorgemoralespou Oct 26, 2026
060b6da
fix(version): report the main module version in go install builds
jorgemoralespou Oct 26, 2026
dc1d3d6
ci: pin actions by commit SHA to their latest release
jorgemoralespou Oct 26, 2026
10a3bd5
ci: pin golangci-lint to v2.15.0 and lint pull requests
jorgemoralespou Oct 26, 2026
48b603d
ci: pin GoReleaser to v2.19.0
jorgemoralespou Oct 26, 2026
9e75346
ci: allow a manual release to skip macOS signing
jorgemoralespou Oct 26, 2026
aa92518
build: name release assets educatesenv-<os>-<arch>
jorgemoralespou Oct 26, 2026
0cb83c1
ci: update GitHub Actions weekly with Dependabot
jorgemoralespou Oct 26, 2026
4df0089
Merge branch 'refresh-2026/01-toolchain-and-ci-refresh' into refresh-…
jorgemoralespou Oct 26, 2026
65939d1
refactor(cmd): build the command tree with a constructor
jorgemoralespou Oct 26, 2026
2919476
test: run commands in-process against a fake GitHub server
jorgemoralespou Oct 26, 2026
cad5938
feat(cmd): reject stray arguments to commands that take none
jorgemoralespou Oct 26, 2026
8ade19d
fix(cmd): write config init and config view output through cobra
jorgemoralespou Oct 26, 2026
e656dc7
fix(config): load the config with its own viper instance
jorgemoralespou Oct 26, 2026
1163d8e
fix(cmd): write every command's output through cobra's writers
jorgemoralespou Oct 26, 2026
612c770
fix(config): warn about a missing home directory through the caller's…
jorgemoralespou Oct 26, 2026
b66314f
test(config): keep the config tests out of the real home directory
jorgemoralespou Oct 26, 2026
43b541d
style: ignore output write errors explicitly
jorgemoralespou Oct 26, 2026
ca4612f
Merge branch 'refresh-2026/02-command-constructor-and-cli-harness' in…
jorgemoralespou Oct 26, 2026
784e61b
fix(cmd): clean up the development symlink only in commands that deal…
jorgemoralespou Oct 26, 2026
a1da99e
fix(version): compare the development symlink's target directory with…
jorgemoralespou Oct 26, 2026
480af86
feat(releases): select and order Educates releases by semver or publi…
jorgemoralespou Oct 26, 2026
63f31be
test(fakegithub): cut an asset download mid-transfer
jorgemoralespou Oct 26, 2026
760c5f2
fix(install): download into a temporary file and rename it into place
jorgemoralespou Oct 26, 2026
ebbe548
fix(use): report why switching versions failed
jorgemoralespou Oct 26, 2026
d15763f
Merge branch 'refresh-2026/08-active-binary-safety' into refresh-2026…
jorgemoralespou Oct 26, 2026
6a79149
test(config): move the config tests to the CLI harness
jorgemoralespou Oct 26, 2026
50e2670
feat(install): verify the binary against the release's checksums.txt
jorgemoralespou Oct 26, 2026
45b8fcf
feat(install): fail when checksums.txt has no entry for the binary
jorgemoralespou Oct 26, 2026
fe7ed0d
feat(cmd): add uninstall to remove installed versions
jorgemoralespou Oct 26, 2026
a08558e
Merge branch 'refresh-2026/integration' into refresh-2026/07-uninstall
jorgemoralespou Oct 26, 2026
71e42ec
test(fakegithub): build a checksums.txt asset for a release
jorgemoralespou Oct 26, 2026
cd43652
feat(install): warn on stderr when a release has no checksums.txt
jorgemoralespou Oct 26, 2026
52aea77
Merge branch 'refresh-2026/07-uninstall' into refresh-2026/integration
jorgemoralespou Oct 26, 2026
8f3adaf
feat(install): report a verified checksum
jorgemoralespou Oct 26, 2026
3316669
fix(config): read the config only from the educatesenv directory
jorgemoralespou Oct 26, 2026
0c4f7f5
fix(install): time out a stalled download without capping the transfer
jorgemoralespou Oct 26, 2026
f65f51f
test(config): cover malformed config values
jorgemoralespou Oct 26, 2026
0ebcdd4
Merge branch 'refresh-2026/integration' into refresh-2026/06-safe-dow…
jorgemoralespou Oct 26, 2026
79eb019
Merge branch 'refresh-2026/06-safe-downloads' into refresh-2026/integ…
jorgemoralespou Oct 26, 2026
ec497db
fix(config): create the default config readable only by its owner
jorgemoralespou Oct 26, 2026
9b23a06
test(config): check that config init creates the same config as init
jorgemoralespou Oct 26, 2026
47dc3a1
fix(init): give PATH instructions for the user's shell
jorgemoralespou Oct 26, 2026
f64b3a4
feat(list-remote): show the newest releases by semver with publish dates
jorgemoralespou Oct 26, 2026
d0fd183
Merge branch 'refresh-2026/integration' into refresh-2026/03-list-rem…
jorgemoralespou Oct 26, 2026
231c3b1
test(config): cover config files that set no values
jorgemoralespou Oct 26, 2026
48e678b
Merge branch 'refresh-2026/integration' into refresh-2026/09-config-w…
jorgemoralespou Oct 26, 2026
860d0ae
Merge branch 'refresh-2026/03-list-remote-newest-by-semver' into refr…
jorgemoralespou Oct 26, 2026
38cf166
Merge branch 'refresh-2026/09-config-without-viper' into refresh-2026…
jorgemoralespou Oct 26, 2026
2603f0d
feat(releases): order installed version names by semver
jorgemoralespou Oct 26, 2026
b19264a
feat(releases): pick latest as the highest stable release by semver
jorgemoralespou Oct 26, 2026
53ebfc8
feat(list): show installed versions newest first by semver
jorgemoralespou Oct 26, 2026
2d94594
Merge branch 'refresh-2026/04-list-orders-by-semver' into refresh-202…
jorgemoralespou Oct 26, 2026
07b31d2
feat(install): install latest as the highest stable release
jorgemoralespou Oct 26, 2026
ec8d2ba
fix(init): download the highest stable release by semver
jorgemoralespou Oct 26, 2026
5e02a73
Merge branch 'refresh-2026/integration' into refresh-2026/05-install-…
jorgemoralespou Oct 26, 2026
d327945
Merge branch 'refresh-2026/05-install-latest' into refresh-2026/integ…
jorgemoralespou Oct 26, 2026
c26faab
feat(token): resolve the GitHub token from the environment, gh and th…
jorgemoralespou Oct 26, 2026
9ef048b
Merge branch 'refresh-2026/integration' into refresh-2026/10-token-re…
jorgemoralespou Oct 26, 2026
948f52c
test(token): cover a gh that fails, prints nothing or is missing
jorgemoralespou Oct 26, 2026
c7bf10c
feat(token): warn that a token from the config file is deprecated
jorgemoralespou Oct 26, 2026
fc30fc6
feat(cmd): create the GitHub client only in commands that call GitHub
jorgemoralespou Oct 26, 2026
15e3121
fix(config): show where the token comes from in config view, never th…
jorgemoralespou Oct 26, 2026
bf55603
feat(github): say when a rate limit resets and which token GitHub rej…
jorgemoralespou Oct 26, 2026
c566c99
refactor(token): keep the config-file warning in a constant
jorgemoralespou Oct 26, 2026
c8bdf45
Merge branch 'refresh-2026/10-token-resolution' into refresh-2026/int…
jorgemoralespou Oct 26, 2026
ca5c40a
docs: rewrite the README to match the commands
jorgemoralespou Oct 26, 2026
4549bdd
Merge branch 'refresh-2026/11-readme-and-release-notes' into refresh-…
jorgemoralespou Oct 26, 2026
3316577
fix(release): fail a release that cannot sign the macOS binaries
jorgemoralespou Oct 26, 2026
8848d21
fix(config): reject stray arguments
jorgemoralespou Oct 26, 2026
b73062e
fix(config): read a boolean in another form as false, with a warning
jorgemoralespou Oct 26, 2026
46ae00d
fix(use): point use latest at install latest --use
jorgemoralespou Oct 26, 2026
fc5f3d7
fix(use): show the required argument and quote commands like the others
jorgemoralespou Oct 26, 2026
4407992
refactor(config): name the config paths ConfigDir and ConfigFile
jorgemoralespou Oct 26, 2026
79c2d89
refactor(token): give the token sources self-describing names
jorgemoralespou Oct 26, 2026
2e6214e
refactor(token): keep the GH_TOKEN and gh auth login hint in one cons…
jorgemoralespou Oct 26, 2026
3c1e61a
refactor(releases): name the selection types SelectOptions and SortOrder
jorgemoralespou Oct 26, 2026
076c7dd
refactor(releases): rename version to semverForm
jorgemoralespou Oct 26, 2026
0913e82
fix(list): find installed versions and the active one as uninstall does
jorgemoralespou Oct 26, 2026
43576a4
refactor(version): name the develop version and the educates symlink …
jorgemoralespou Oct 26, 2026
bd719bd
Merge branch 'refresh-2026/review-fixes' into refresh-2026/integration
jorgemoralespou Oct 26, 2026
79cad35
test(fakegithub): serve a download that stalls and one that trickles
jorgemoralespou Oct 27, 2026
024fb8c
feat(version): fail a download that receives no data for 60 seconds
jorgemoralespou Oct 27, 2026
fed1b95
ci(release): check out the full history for the changelog
jorgemoralespou Oct 27, 2026
64a81eb
docs(readme): say that a download fails when it stalls for 60 seconds
jorgemoralespou Oct 27, 2026
cc35bb1
Merge branch 'refresh-2026/open-points' into refresh-2026/integration
jorgemoralespou Oct 27, 2026
0cc9f5a
ci(release): fail a release when a signing or notary secret is empty
jorgemoralespou Oct 27, 2026
a6c8e5f
build(release): replace a draft release a failed run left behind
jorgemoralespou Oct 27, 2026
29f4d40
build(release): switch macOS signing with MACOS_SIGNING
jorgemoralespou Oct 27, 2026
d32c210
ci(release): set MACOS_SIGNING from the skip-signing input
jorgemoralespou Oct 27, 2026
4aae112
ci(release): let the MACOS_SIGNING repository variable turn signing off
jorgemoralespou Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Keeps the GitHub Actions in .github/workflows at their latest release.
# The actions are pinned by commit SHA with the release tag in a trailing
# comment, and Dependabot updates both. The golangci-lint and GoReleaser
# versions are action inputs, which Dependabot does not update.
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ jobs:
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.24'
go-version-file: go.mod
cache: true

- name: Run tests
Expand All @@ -46,7 +46,7 @@ jobs:
mv bin/educatesenv bin/${{ steps.binary.outputs.name }}

- name: Upload binary
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ${{ steps.binary.outputs.name }}
path: bin/${{ steps.binary.outputs.name }}
Expand Down
18 changes: 11 additions & 7 deletions .github/workflows/golangci-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,26 @@ on:
branches:
- develop
- main
pull_request:
branches:
- develop
- main

jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: '0'
- name: Install Go
uses: actions/setup-go@v5
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.24'
go-version-file: go.mod
cache: true
- uses: actions/checkout@v4
with:
fetch-depth: '0'
- name: golangci-lint
uses: golangci/golangci-lint-action@v7
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0
with:
version: latest
version: v2.15.0
args: -v
54 changes: 48 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,76 @@ on:
description: 'Tag or version to build (e.g., v1.2.3). If not set, uses latest commit.'
required: false
default: ''
skip-signing:
description: 'Release without signing and notarizing the macOS binaries, for when the Apple membership or certificate is unavailable.'
type: boolean
required: false
default: false

jobs:
release:
name: Create Release
runs-on: ubuntu-latest
env:
# false for a manual run with skip-signing, or for any run while the
# MACOS_SIGNING repository variable is false; true otherwise. The secrets
# check below and .goreleaser.yml both read it to decide whether to sign
# and notarize the macOS binaries.
MACOS_SIGNING: ${{ ((github.event_name == 'workflow_dispatch' && inputs.skip-signing) || vars.MACOS_SIGNING == 'false') && 'false' || 'true' }}
permissions:
contents: write
# packages: write
# issues: write
# id-token: write

steps:
# Every release signs and notarizes the macOS binaries unless MACOS_SIGNING
# is false. Without this check, an empty notary secret would make
# GoReleaser sign the binaries, skip notarization and still succeed.
- name: Report a release without macOS signing
if: env.MACOS_SIGNING == 'false'
run: echo "::warning::This release does not sign or notarize the macOS binaries, because the skip-signing input is set or the MACOS_SIGNING repository variable is false."

- name: Check the macOS signing and notarization secrets
if: env.MACOS_SIGNING != 'false'
env:
MACOS_SIGN_P12: ${{ secrets.MACOS_SIGN_P12 }}
MACOS_SIGN_PASSWORD: ${{ secrets.MACOS_SIGN_PASSWORD }}
MACOS_NOTARY_KEY: ${{ secrets.MACOS_NOTARY_KEY }}
MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }}
MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }}
run: |
missing=""
for name in MACOS_SIGN_P12 MACOS_SIGN_PASSWORD MACOS_NOTARY_KEY MACOS_NOTARY_KEY_ID MACOS_NOTARY_ISSUER_ID; do
if [ -z "${!name}" ]; then
missing="$missing $name"
fi
done
if [ -n "$missing" ]; then
echo "::error::These secrets are empty or missing:$missing. Set them, or run the workflow manually with skip-signing to release unsigned."
exit 1
fi

- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.inputs.release_tag != '' && github.event.inputs.release_tag || github.ref_type == 'tag' && github.ref_name || github.sha }}
# GoReleaser's changelog needs the full history to find the previous
# tag.
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.24'
go-version-file: go.mod
cache: true

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
uses: goreleaser/goreleaser-action@a4f02ab5355a589de60d917a659ce617ed19b3de # v7.3.0
with:
# 'latest', 'nightly', or a semver
version: "~> v2"
version: v2.19.0
# .goreleaser.yml signs and notarizes unless MACOS_SIGNING, set for the
# job above, is false.
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
1 change: 0 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
version: "2"

run:
go: '1.24'
timeout: 5m
modules-download-mode: readonly
relative-path-mode: gomod
Expand Down
21 changes: 19 additions & 2 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ version: 2
builds:
- main: ./cmd/educatesenv
id: educatesenv
binary: educatesenv-{{ .Os }}-{{ .Arch }}
binary: educatesenv
env:
- CGO_ENABLED=0
goos:
Expand All @@ -22,6 +22,9 @@ builds:
archives:
- formats:
- binary
# Release assets are named educatesenv-<os>-<arch>, as the download
# instructions in the release notes header expect.
name_template: "{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}"

checksum:
name_template: 'checksums.txt'
Expand All @@ -45,6 +48,12 @@ release:
# We wait so that we don't make the release public until we have notarize email from Apple.
draft: true

# Delete a draft release with the same name, the tag, before creating the
# release. A run that fails while uploading leaves a draft behind, and
# GitHub's lookup by tag never finds drafts, so without this a rerun would
# leave two drafts for the same tag.
replace_existing_draft: true

# What to do with the release notes in case there the release already exists.
#
# Valid options are:
Expand Down Expand Up @@ -103,10 +112,18 @@ changelog:
notarize:
macos:
- # Whether this configuration is enabled or not.
#
# MACOS_SIGNING switches signing and notarization, the same way for
# goreleaser release and goreleaser build. A release signs unless
# MACOS_SIGNING is false, so it fails when MACOS_SIGN_P12 is empty or
# missing, as well as when signing fails. A snapshot signs only when
# MACOS_SIGNING is true, so that snapshots build without the
# certificate. The release workflow sets MACOS_SIGNING from its
# skip-signing input.
#
# Default: false.
# Templates: allowed.
enabled: '{{ isEnvSet "MACOS_SIGN_P12" }}'
enabled: '{{ if .IsSnapshot }}{{ eq (tolower (trim (envOrDefault "MACOS_SIGNING" "false"))) "true" }}{{ else }}{{ ne (tolower (trim (envOrDefault "MACOS_SIGNING" "true"))) "false" }}{{ end }}'

# Before notarizing, we need to sign the binary.
# This block defines the configuration for doing so.
Expand Down
Loading
Loading