Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
1ed542c2
Commit
1ed542c2
authored
Mar 17, 2026
by
johnnyjoygh
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add GitHub release installer and release workflow
parent
12e2205c
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
612 additions
and
184 deletions
+612
-184
build-stable-image.yml
.github/workflows/build-stable-image.yml
+0
-184
release.yml
.github/workflows/release.yml
+352
-0
README.md
README.md
+12
-0
install.sh
scripts/install.sh
+248
-0
No files found.
.github/workflows/build-stable-image.yml
deleted
100644 → 0
View file @
12e2205c
name
:
Build Stable Image
on
:
push
:
branches
:
-
"
release/**"
tags
:
-
"
v*.*.*"
jobs
:
prepare
:
runs-on
:
ubuntu-latest
outputs
:
version
:
${{ steps.version.outputs.version }}
steps
:
-
name
:
Extract version
id
:
version
run
:
|
if [[ "$GITHUB_REF_TYPE" == "tag" ]]; then
echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
else
echo "version=${GITHUB_REF_NAME#release/}" >> $GITHUB_OUTPUT
fi
build-frontend
:
runs-on
:
ubuntu-latest
steps
:
-
uses
:
actions/checkout@v6
-
uses
:
pnpm/action-setup@v4.2.0
with
:
version
:
10
-
uses
:
actions/setup-node@v6
with
:
node-version
:
"
24"
cache
:
pnpm
cache-dependency-path
:
"
web/pnpm-lock.yaml"
-
name
:
Get pnpm store directory
id
:
pnpm-cache
shell
:
bash
run
:
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
-
name
:
Setup pnpm cache
uses
:
actions/cache@v5
with
:
path
:
${{ steps.pnpm-cache.outputs.STORE_PATH }}
key
:
${{ runner.os }}-pnpm-store-${{ hashFiles('web/pnpm-lock.yaml') }}
restore-keys
:
${{ runner.os }}-pnpm-store-
-
run
:
pnpm install --frozen-lockfile
working-directory
:
web
-
name
:
Run frontend build
run
:
pnpm release
working-directory
:
web
-
name
:
Upload frontend artifacts
uses
:
actions/upload-artifact@v6
with
:
name
:
frontend-dist
path
:
server/router/frontend/dist
retention-days
:
1
build-push
:
needs
:
[
prepare
,
build-frontend
]
runs-on
:
ubuntu-latest
permissions
:
contents
:
read
packages
:
write
strategy
:
fail-fast
:
false
matrix
:
platform
:
-
linux/amd64
-
linux/arm/v7
-
linux/arm64
steps
:
-
uses
:
actions/checkout@v6
-
name
:
Download frontend artifacts
uses
:
actions/download-artifact@v7
with
:
name
:
frontend-dist
path
:
server/router/frontend/dist
-
name
:
Set up QEMU
uses
:
docker/setup-qemu-action@v3
-
name
:
Set up Docker Buildx
uses
:
docker/setup-buildx-action@v3
-
name
:
Login to Docker Hub
uses
:
docker/login-action@v3
with
:
username
:
${{ secrets.DOCKER_HUB_USERNAME }}
password
:
${{ secrets.DOCKER_HUB_TOKEN }}
-
name
:
Login to GitHub Container Registry
uses
:
docker/login-action@v3
with
:
registry
:
ghcr.io
username
:
${{ github.actor }}
password
:
${{ github.token }}
-
name
:
Build and push by digest
id
:
build
uses
:
docker/build-push-action@v6
with
:
context
:
.
file
:
./scripts/Dockerfile
platforms
:
${{ matrix.platform }}
cache-from
:
type=gha,scope=build-${{ matrix.platform }}
cache-to
:
type=gha,mode=max,scope=build-${{ matrix.platform }}
outputs
:
type=image,name=neosmemo/memos,push-by-digest=true,name-canonical=true,push=true
-
name
:
Export digest
run
:
|
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
-
name
:
Upload digest
uses
:
actions/upload-artifact@v6
with
:
name
:
digests-${{ strategy.job-index }}
path
:
/tmp/digests/*
if-no-files-found
:
error
retention-days
:
1
merge
:
needs
:
[
prepare
,
build-push
]
runs-on
:
ubuntu-latest
permissions
:
contents
:
read
packages
:
write
steps
:
-
name
:
Download digests
uses
:
actions/download-artifact@v7
with
:
pattern
:
digests-*
merge-multiple
:
true
path
:
/tmp/digests
-
name
:
Set up Docker Buildx
uses
:
docker/setup-buildx-action@v3
-
name
:
Docker meta
id
:
meta
uses
:
docker/metadata-action@v5
with
:
images
:
|
neosmemo/memos
ghcr.io/usememos/memos
tags
:
|
type=semver,pattern={{version}},value=${{ needs.prepare.outputs.version }}
type=semver,pattern={{major}}.{{minor}},value=${{ needs.prepare.outputs.version }}
type=raw,value=stable
flavor
:
|
latest=false
labels
:
|
org.opencontainers.image.version=${{ needs.prepare.outputs.version }}
-
name
:
Login to Docker Hub
uses
:
docker/login-action@v3
with
:
username
:
${{ secrets.DOCKER_HUB_USERNAME }}
password
:
${{ secrets.DOCKER_HUB_TOKEN }}
-
name
:
Login to GitHub Container Registry
uses
:
docker/login-action@v3
with
:
registry
:
ghcr.io
username
:
${{ github.actor }}
password
:
${{ github.token }}
-
name
:
Create manifest list and push
working-directory
:
/tmp/digests
run
:
|
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'neosmemo/memos@sha256:%s ' *)
env
:
DOCKER_METADATA_OUTPUT_JSON
:
${{ steps.meta.outputs.json }}
-
name
:
Inspect images
run
:
|
docker buildx imagetools inspect neosmemo/memos:stable
docker buildx imagetools inspect ghcr.io/usememos/memos:stable
.github/workflows/
build-binaries
.yml
→
.github/workflows/
release
.yml
View file @
1ed542c2
name
:
Build Binaries
name
:
Release
# Build multi-platform binaries on release or manual trigger
# Produces distributable packages for Linux, macOS, and Windows
on
:
on
:
release
:
push
:
types
:
[
published
]
tags
:
-
"
v*.*.*"
workflow_dispatch
:
workflow_dispatch
:
# Environment variables for build configuration
concurrency
:
group
:
${{ github.workflow }}-${{ github.ref }}
cancel-in-progress
:
true
env
:
env
:
GO_VERSION
:
"
1.26.1"
GO_VERSION
:
"
1.26.1"
NODE_VERSION
:
"
24"
NODE_VERSION
:
"
24"
PNPM_VERSION
:
"
10"
PNPM_VERSION
:
"
10"
ARTIFACT_RETENTION_DAYS
:
60
ARTIFACT_RETENTION_DAYS
:
60
# Artifact naming: {ARTIFACT_PREFIX}_{version}_{os}_{arch}.tar.gz|zip
ARTIFACT_PREFIX
:
memos
ARTIFACT_PREFIX
:
memos
jobs
:
jobs
:
# Job 1: Extract version information
# - For git tags: use tag version (e.g., v0.28.1 -> 0.28.1)
# - For branches: use branch-name-shortSHA format
prepare
:
prepare
:
name
:
Extract Version
name
:
Extract Version
runs-on
:
ubuntu-latest
runs-on
:
ubuntu-latest
outputs
:
outputs
:
version
:
${{ steps.version.outputs.version }}
version
:
${{ steps.version.outputs.version }}
tag
:
${{ steps.version.outputs.tag }}
steps
:
steps
:
-
name
:
Checkout code
uses
:
actions/checkout@v6
with
:
fetch-depth
:
0
# Full history for git describe
-
name
:
Extract version
-
name
:
Extract version
id
:
version
id
:
version
env
:
REF_NAME
:
${{ github.ref_name }}
EVENT_NAME
:
${{ github.event_name }}
run
:
|
run
:
|
# Try to get version from git tag
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
TAG=$(git describe --tags --exact-match 2>/dev/null || echo "")
echo "tag=" >> "$GITHUB_OUTPUT"
if [ -n "$TAG" ]; then
echo "version=manual-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
echo "version=${TAG#v}" >> $GITHUB_OUTPUT
exit 0
echo "Version from tag: ${TAG#v}"
else
# Use branch name + short SHA
BRANCH="${GITHUB_REF_NAME//\//-}"
SHORT_SHA="${GITHUB_SHA:0:7}"
echo "version=${BRANCH}-${SHORT_SHA}" >> $GITHUB_OUTPUT
echo "Version from branch: ${BRANCH}-${SHORT_SHA}"
fi
fi
# Job 2: Build frontend assets
echo "tag=${REF_NAME}" >> "$GITHUB_OUTPUT"
# - Builds React frontend with Vite
echo "version=${REF_NAME#v}" >> "$GITHUB_OUTPUT"
# - Produces static files that will be embedded in Go binary
# - Shared across all platform builds
build-frontend
:
build-frontend
:
name
:
Build Frontend
name
:
Build Frontend
needs
:
prepare
needs
:
prepare
...
@@ -74,10 +63,10 @@ jobs:
...
@@ -74,10 +63,10 @@ jobs:
-
name
:
Get pnpm store directory
-
name
:
Get pnpm store directory
id
:
pnpm-cache
id
:
pnpm-cache
shell
:
bash
shell
:
bash
run
:
echo "STORE_PATH=$(pnpm store path)" >>
$GITHUB_OUTPUT
run
:
echo "STORE_PATH=$(pnpm store path)" >>
"$GITHUB_OUTPUT"
-
name
:
Setup pnpm cache
-
name
:
Setup pnpm cache
uses
:
actions/cache@v
5
uses
:
actions/cache@v
4
with
:
with
:
path
:
${{ steps.pnpm-cache.outputs.STORE_PATH }}
path
:
${{ steps.pnpm-cache.outputs.STORE_PATH }}
key
:
${{ runner.os }}-pnpm-store-${{ hashFiles('web/pnpm-lock.yaml') }}
key
:
${{ runner.os }}-pnpm-store-${{ hashFiles('web/pnpm-lock.yaml') }}
...
@@ -87,22 +76,17 @@ jobs:
...
@@ -87,22 +76,17 @@ jobs:
working-directory
:
web
working-directory
:
web
run
:
pnpm install --frozen-lockfile
run
:
pnpm install --frozen-lockfile
-
name
:
Build frontend
-
name
:
Build frontend
release assets
working-directory
:
web
working-directory
:
web
run
:
pnpm release
run
:
pnpm release
-
name
:
Upload frontend artifacts
-
name
:
Upload frontend artifacts
uses
:
actions/upload-artifact@v
6
uses
:
actions/upload-artifact@v
4
with
:
with
:
name
:
frontend-dist
name
:
frontend-dist
path
:
server/router/frontend/dist
path
:
server/router/frontend/dist
retention-days
:
${{ env.ARTIFACT_RETENTION_DAYS }}
retention-days
:
1
# Job 3: Build Go binaries for multiple platforms
# - Cross-compiles using native Go toolchain
# - Embeds frontend assets built in previous job
# - Produces static binaries with no external dependencies
# - Packages as tar.gz (Unix) or zip (Windows)
build-binaries
:
build-binaries
:
name
:
Build ${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.goarm && format('v{0}', matrix.goarm) || '' }}
name
:
Build ${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.goarm && format('v{0}', matrix.goarm) || '' }}
needs
:
[
prepare
,
build-frontend
]
needs
:
[
prepare
,
build-frontend
]
...
@@ -111,7 +95,6 @@ jobs:
...
@@ -111,7 +95,6 @@ jobs:
fail-fast
:
false
fail-fast
:
false
matrix
:
matrix
:
include
:
include
:
# Linux targets
-
goos
:
linux
-
goos
:
linux
goarch
:
amd64
goarch
:
amd64
-
goos
:
linux
-
goos
:
linux
...
@@ -119,15 +102,12 @@ jobs:
...
@@ -119,15 +102,12 @@ jobs:
-
goos
:
linux
-
goos
:
linux
goarch
:
arm
goarch
:
arm
goarm
:
"
7"
goarm
:
"
7"
# macOS targets
-
goos
:
darwin
-
goos
:
darwin
goarch
:
amd64
# Intel Macs
goarch
:
amd64
-
goos
:
darwin
-
goos
:
darwin
goarch
:
arm64
# Apple Silicon
goarch
:
arm64
# Windows targets
-
goos
:
windows
-
goos
:
windows
goarch
:
amd64
goarch
:
amd64
steps
:
steps
:
-
name
:
Checkout code
-
name
:
Checkout code
uses
:
actions/checkout@v6
uses
:
actions/checkout@v6
...
@@ -139,7 +119,7 @@ jobs:
...
@@ -139,7 +119,7 @@ jobs:
cache
:
true
cache
:
true
-
name
:
Download frontend artifacts
-
name
:
Download frontend artifacts
uses
:
actions/download-artifact@v
7
uses
:
actions/download-artifact@v
4
with
:
with
:
name
:
frontend-dist
name
:
frontend-dist
path
:
server/router/frontend/dist
path
:
server/router/frontend/dist
...
@@ -151,27 +131,21 @@ jobs:
...
@@ -151,27 +131,21 @@ jobs:
GOARM
:
${{ matrix.goarm }}
GOARM
:
${{ matrix.goarm }}
CGO_ENABLED
:
"
0"
CGO_ENABLED
:
"
0"
run
:
|
run
:
|
# Determine output binary name
output_name="memos"
OUTPUT_NAME="memos"
if [ "$GOOS" = "windows" ]; then
if [ "$GOOS" = "windows" ]; then
OUTPUT_NAME
="memos.exe"
output_name
="memos.exe"
fi
fi
mkdir -p build
mkdir -p build
# Build static binary with optimizations
go build \
go build \
-trimpath \
-trimpath \
-ldflags="-s -w -extldflags '-static'" \
-ldflags="-s -w -
X github.com/usememos/memos/internal/version.Version=${{ needs.prepare.outputs.version }} -
extldflags '-static'" \
-tags netgo,osusergo \
-tags netgo,osusergo \
-o "build/${
OUTPUT_NAME
}" \
-o "build/${
output_name
}" \
./cmd/memos
./cmd/memos
echo "✓ Built: build/${OUTPUT_NAME}"
ls -lh build/
-
name
:
Package binary
-
name
:
Package binary
id
:
package
env
:
env
:
VERSION
:
${{ needs.prepare.outputs.version }}
VERSION
:
${{ needs.prepare.outputs.version }}
GOOS
:
${{ matrix.goos }}
GOOS
:
${{ matrix.goos }}
...
@@ -180,51 +154,199 @@ jobs:
...
@@ -180,51 +154,199 @@ jobs:
run
:
|
run
:
|
cd build
cd build
# Construct package name: {prefix}_{version}_{os}_{arch}[v{arm_version}]
package_name="${ARTIFACT_PREFIX}_${VERSION}_${GOOS}_${GOARCH}"
PACKAGE_NAME="${ARTIFACT_PREFIX}_${VERSION}_${GOOS}_${GOARCH}"
if [ -n "$GOARM" ]; then
if [ -n "$GOARM" ]; then
PACKAGE_NAME="${PACKAGE_NAME
}v${GOARM}"
package_name="${package_name
}v${GOARM}"
fi
fi
# Package based on platform
if [ "$GOOS" = "windows" ]; then
if [ "$GOOS" = "windows" ]; then
ARTIFACT_NAME="${PACKAGE_NAME
}.zip"
artifact_name="${package_name
}.zip"
zip -q "${
ARTIFACT_NAME
}" memos.exe
zip -q "${
artifact_name
}" memos.exe
else
else
ARTIFACT_NAME="${PACKAGE_NAME
}.tar.gz"
artifact_name="${package_name
}.tar.gz"
tar czf "${
ARTIFACT_NAME
}" memos
tar czf "${
artifact_name
}" memos
fi
fi
# Output for next step
echo "artifact_name=${artifact_name}" >> "$GITHUB_ENV"
echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
echo "✓ Package created: ${ARTIFACT_NAME} ($(du -h "${ARTIFACT_NAME}" | cut -f1))"
-
name
:
Upload binary artifact
-
name
:
Upload binary artifact
uses
:
actions/upload-artifact@v
6
uses
:
actions/upload-artifact@v
4
with
:
with
:
name
:
${{ env.ARTIFACT_NAME }}
name
:
${{ env.artifact_name }}
path
:
build/${{ env.ARTIFACT_NAME }}
path
:
build/${{ env.artifact_name }}
retention-days
:
${{ env.ARTIFACT_RETENTION_DAYS }}
checksums
:
name
:
Generate Checksums
needs
:
[
prepare
,
build-binaries
]
runs-on
:
ubuntu-latest
steps
:
-
name
:
Download binary artifacts
uses
:
actions/download-artifact@v4
with
:
path
:
artifacts
pattern
:
${{ env.ARTIFACT_PREFIX }}_*
merge-multiple
:
true
-
name
:
Generate checksums
working-directory
:
artifacts
run
:
sha256sum * > checksums.txt
-
name
:
Upload checksum artifact
uses
:
actions/upload-artifact@v4
with
:
name
:
checksums
path
:
artifacts/checksums.txt
retention-days
:
${{ env.ARTIFACT_RETENTION_DAYS }}
retention-days
:
${{ env.ARTIFACT_RETENTION_DAYS }}
# Job 4: Upload artifacts to GitHub Release
# - Only runs when triggered by a release publish event
# - Downloads all built artifacts and attaches them to the release
release
:
release
:
name
:
Upload Release Assets
name
:
Publish GitHub Release
needs
:
build-binaries
needs
:
[
prepare
,
build-binaries
,
checksums
]
if
:
github.event_name
== 'release
'
if
:
github.event_name
!= 'workflow_dispatch
'
runs-on
:
ubuntu-latest
runs-on
:
ubuntu-latest
permissions
:
permissions
:
contents
:
write
contents
:
write
steps
:
steps
:
-
name
:
Download
all
artifacts
-
name
:
Download
binary
artifacts
uses
:
actions/download-artifact@v
7
uses
:
actions/download-artifact@v
4
with
:
with
:
path
:
artifacts
path
:
artifacts
pattern
:
${{ env.ARTIFACT_PREFIX }}_*
pattern
:
${{ env.ARTIFACT_PREFIX }}_*
merge-multiple
:
true
merge-multiple
:
true
-
name
:
Upload to GitHub Release
-
name
:
Download checksum artifact
uses
:
actions/download-artifact@v4
with
:
name
:
checksums
path
:
artifacts
-
name
:
Publish release assets
uses
:
softprops/action-gh-release@v2
uses
:
softprops/action-gh-release@v2
with
:
with
:
tag_name
:
${{ needs.prepare.outputs.tag }}
name
:
${{ needs.prepare.outputs.tag }}
generate_release_notes
:
true
files
:
artifacts/*
files
:
artifacts/*
build-push
:
name
:
Build Image ${{ matrix.platform }}
needs
:
[
prepare
,
build-frontend
]
if
:
github.event_name != 'workflow_dispatch'
runs-on
:
ubuntu-latest
permissions
:
contents
:
read
packages
:
write
strategy
:
fail-fast
:
false
matrix
:
platform
:
-
linux/amd64
-
linux/arm/v7
-
linux/arm64
steps
:
-
name
:
Checkout code
uses
:
actions/checkout@v6
-
name
:
Download frontend artifacts
uses
:
actions/download-artifact@v4
with
:
name
:
frontend-dist
path
:
server/router/frontend/dist
-
name
:
Set up QEMU
uses
:
docker/setup-qemu-action@v3
-
name
:
Set up Docker Buildx
uses
:
docker/setup-buildx-action@v3
-
name
:
Login to Docker Hub
uses
:
docker/login-action@v3
with
:
username
:
${{ secrets.DOCKER_HUB_USERNAME }}
password
:
${{ secrets.DOCKER_HUB_TOKEN }}
-
name
:
Login to GitHub Container Registry
uses
:
docker/login-action@v3
with
:
registry
:
ghcr.io
username
:
${{ github.actor }}
password
:
${{ github.token }}
-
name
:
Build and push by digest
id
:
build
uses
:
docker/build-push-action@v6
with
:
context
:
.
file
:
./scripts/Dockerfile
platforms
:
${{ matrix.platform }}
build-args
:
|
VERSION=${{ needs.prepare.outputs.version }}
COMMIT=${{ github.sha }}
cache-from
:
type=gha,scope=release-${{ matrix.platform }}
cache-to
:
type=gha,mode=max,scope=release-${{ matrix.platform }}
outputs
:
type=image,name=neosmemo/memos,push-by-digest=true,name-canonical=true,push=true
-
name
:
Export digest
run
:
|
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
-
name
:
Upload digest
uses
:
actions/upload-artifact@v4
with
:
name
:
digests-${{ strategy.job-index }}
path
:
/tmp/digests/*
if-no-files-found
:
error
retention-days
:
1
merge-images
:
name
:
Publish Stable Image Tags
needs
:
[
prepare
,
build-push
]
if
:
github.event_name != 'workflow_dispatch'
runs-on
:
ubuntu-latest
permissions
:
contents
:
read
packages
:
write
steps
:
-
name
:
Download digests
uses
:
actions/download-artifact@v4
with
:
pattern
:
digests-*
merge-multiple
:
true
path
:
/tmp/digests
-
name
:
Set up Docker Buildx
uses
:
docker/setup-buildx-action@v3
-
name
:
Login to Docker Hub
uses
:
docker/login-action@v3
with
:
username
:
${{ secrets.DOCKER_HUB_USERNAME }}
password
:
${{ secrets.DOCKER_HUB_TOKEN }}
-
name
:
Login to GitHub Container Registry
uses
:
docker/login-action@v3
with
:
registry
:
ghcr.io
username
:
${{ github.actor }}
password
:
${{ github.token }}
-
name
:
Create manifest list and push
working-directory
:
/tmp/digests
run
:
|
version="${{ needs.prepare.outputs.version }}"
major_minor=$(echo "$version" | cut -d. -f1,2)
docker buildx imagetools create \
-t "neosmemo/memos:${version}" \
-t "neosmemo/memos:${major_minor}" \
-t "neosmemo/memos:stable" \
-t "ghcr.io/usememos/memos:${version}" \
-t "ghcr.io/usememos/memos:${major_minor}" \
-t "ghcr.io/usememos/memos:stable" \
$(printf 'neosmemo/memos@sha256:%s ' *)
-
name
:
Inspect images
run
:
|
docker buildx imagetools inspect neosmemo/memos:${{ needs.prepare.outputs.version }}
docker buildx imagetools inspect neosmemo/memos:stable
README.md
View file @
1ed542c2
...
@@ -70,6 +70,18 @@ docker run -d \
...
@@ -70,6 +70,18 @@ docker run -d \
Open
`http://localhost:5230`
and start writing!
Open
`http://localhost:5230`
and start writing!
### Native Binary
```
bash
curl
-fsSL
https://raw.githubusercontent.com/usememos/memos/main/scripts/install.sh | sh
```
To install a specific version:
```
bash
curl
-fsSL
https://raw.githubusercontent.com/usememos/memos/main/scripts/install.sh | sh
-s
--
--version
<version>
```
### Try the Live Demo
### Try the Live Demo
Don't want to install yet? Try our
[
live demo
](
https://demo.usememos.com/
)
first!
Don't want to install yet? Try our
[
live demo
](
https://demo.usememos.com/
)
first!
...
...
scripts/install.sh
0 → 100755
View file @
1ed542c2
#!/bin/sh
set
-eu
REPO
=
"
${
REPO
:-
usememos
/memos
}
"
BIN_NAME
=
"memos"
VERSION
=
"
${
MEMOS_VERSION
:-}
"
INSTALL_DIR
=
"
${
MEMOS_INSTALL_DIR
:-}
"
SKIP_CHECKSUM
=
"
${
MEMOS_SKIP_CHECKSUM
:-
0
}
"
usage
()
{
cat
<<
'
EOF
'
Install Memos from GitHub Releases.
Usage:
install.sh [--version <version>] [--install-dir <dir>] [--skip-checksum]
Environment:
MEMOS_VERSION Version to install without the leading "v". Defaults to latest release.
MEMOS_INSTALL_DIR Directory to install the binary into.
MEMOS_SKIP_CHECKSUM Set to 1 to skip checksum verification.
REPO GitHub repository in owner/name form. Defaults to usememos/memos.
Examples:
curl -fsSL https://raw.githubusercontent.com/usememos/memos/main/scripts/install.sh | sh
curl -fsSL https://raw.githubusercontent.com/usememos/memos/main/scripts/install.sh | sh -s -- --version 0.28.1
EOF
}
log
()
{
printf
'%s\n'
"
$*
"
}
fail
()
{
printf
'Error: %s\n'
"
$*
"
>
&2
exit
1
}
need_cmd
()
{
command
-v
"
$1
"
>
/dev/null 2>&1
||
fail
"required command not found:
$1
"
}
resolve_latest_version
()
{
latest_url
=
"
$(
curl
-fsSL
-o
/dev/null
-w
'%{url_effective}'
"https://github.com/
${
REPO
}
/releases/latest"
)
"
latest_tag
=
"
${
latest_url
##*/
}
"
[
-n
"
$latest_tag
"
]
||
fail
"failed to resolve latest release tag"
printf
'%s\n'
"
${
latest_tag
#v
}
"
}
detect_os
()
{
os
=
"
$(
uname
-s
|
tr
'[:upper:]'
'[:lower:]'
)
"
case
"
$os
"
in
linux
)
printf
'linux\n'
;;
darwin
)
printf
'darwin\n'
;;
*
)
fail
"unsupported operating system:
$os
"
;;
esac
}
detect_arch
()
{
arch
=
"
$(
uname
-m
)
"
case
"
$arch
"
in
x86_64|amd64
)
printf
'amd64\n'
;;
arm64|aarch64
)
printf
'arm64\n'
;;
armv7l|armv7
)
printf
'armv7\n'
;;
*
)
fail
"unsupported architecture:
$arch
"
;;
esac
}
resolve_install_dir
()
{
if
[
-n
"
$INSTALL_DIR
"
]
;
then
printf
'%s\n'
"
$INSTALL_DIR
"
return
fi
if
[
-w
"/usr/local/bin"
]
;
then
printf
'/usr/local/bin\n'
return
fi
if
command
-v
sudo
>
/dev/null 2>&1
;
then
printf
'/usr/local/bin\n'
return
fi
printf
'%s/.local/bin\n'
"
$HOME
"
}
download
()
{
src
=
"
$1
"
dest
=
"
$2
"
curl
-fsSL
"
$src
"
-o
"
$dest
"
}
verify_checksum
()
{
archive_path
=
"
$1
"
checksum_path
=
"
$2
"
if
[
"
$SKIP_CHECKSUM
"
=
"1"
]
;
then
log
"Skipping checksum verification"
return
fi
archive_name
=
"
$(
basename
"
$archive_path
"
)
"
expected_line
=
"
$(
grep
"
${
archive_name
}
\$
"
"
$checksum_path
"
||
true
)
"
[
-n
"
$expected_line
"
]
||
fail
"checksum entry not found for
${
archive_name
}
"
if
command
-v
sha256sum
>
/dev/null 2>&1
;
then
(
cd
"
$(
dirname
"
$archive_path
"
)
"
printf
'%s\n'
"
$expected_line
"
|
sha256sum
-c
-
)
return
fi
if
command
-v
shasum
>
/dev/null 2>&1
;
then
expected_sum
=
"
$(
printf
'%s'
"
$expected_line
"
|
awk
'{print $1}'
)
"
actual_sum
=
"
$(
shasum
-a
256
"
$archive_path
"
|
awk
'{print $1}'
)
"
[
"
$expected_sum
"
=
"
$actual_sum
"
]
||
fail
"checksum verification failed for
${
archive_name
}
"
return
fi
log
"Warning: sha256sum/shasum not found; skipping checksum verification"
}
extract_archive
()
{
archive_path
=
"
$1
"
dest_dir
=
"
$2
"
tar
-xzf
"
$archive_path
"
-C
"
$dest_dir
"
}
install_binary
()
{
src
=
"
$1
"
dest_dir
=
"
$2
"
mkdir
-p
"
$dest_dir
"
if
[
-w
"
$dest_dir
"
]
;
then
install
-m
755
"
$src
"
"
${
dest_dir
}
/
${
BIN_NAME
}
"
return
fi
if
command
-v
sudo
>
/dev/null 2>&1
;
then
sudo mkdir
-p
"
$dest_dir
"
sudo install
-m
755
"
$src
"
"
${
dest_dir
}
/
${
BIN_NAME
}
"
return
fi
fail
"install directory is not writable:
$dest_dir
"
}
parse_args
()
{
while
[
"$#"
-gt
0
]
;
do
case
"
$1
"
in
--version
)
[
"$#"
-ge
2
]
||
fail
"missing value for --version"
VERSION
=
"
$2
"
shift
2
;;
--install-dir
)
[
"$#"
-ge
2
]
||
fail
"missing value for --install-dir"
INSTALL_DIR
=
"
$2
"
shift
2
;;
--skip-checksum
)
SKIP_CHECKSUM
=
"1"
shift
;;
-h
|
--help
)
usage
exit
0
;;
*
)
fail
"unknown argument:
$1
"
;;
esac
done
}
main
()
{
parse_args
"
$@
"
need_cmd curl
need_cmd
tar
need_cmd
install
need_cmd
uname
need_cmd
grep
need_cmd
awk
os
=
"
$(
detect_os
)
"
arch
=
"
$(
detect_arch
)
"
if
[
-z
"
$VERSION
"
]
;
then
VERSION
=
"
$(
resolve_latest_version
)
"
fi
install_dir
=
"
$(
resolve_install_dir
)
"
tag
=
"v
${
VERSION
}
"
asset_suffix
=
"
${
arch
}
"
if
[
"
$arch
"
=
"armv7"
]
;
then
asset_suffix
=
"armv7"
fi
asset_name
=
"
${
BIN_NAME
}
_
${
VERSION
}
_
${
os
}
_
${
asset_suffix
}
.tar.gz"
checksums_name
=
"checksums.txt"
base_url
=
"https://github.com/
${
REPO
}
/releases/download/
${
tag
}
"
tmpdir
=
"
$(
mktemp
-d
)
"
trap
'rm -rf "$tmpdir"'
EXIT INT TERM
archive_path
=
"
${
tmpdir
}
/
${
asset_name
}
"
checksums_path
=
"
${
tmpdir
}
/
${
checksums_name
}
"
extract_dir
=
"
${
tmpdir
}
/extract"
mkdir
-p
"
$extract_dir
"
log
"Installing
${
BIN_NAME
}
${
VERSION
}
for
${
os
}
/
${
arch
}
"
download
"
${
base_url
}
/
${
asset_name
}
"
"
$archive_path
"
download
"
${
base_url
}
/
${
checksums_name
}
"
"
$checksums_path
"
verify_checksum
"
$archive_path
"
"
$checksums_path
"
extract_archive
"
$archive_path
"
"
$extract_dir
"
[
-f
"
${
extract_dir
}
/
${
BIN_NAME
}
"
]
||
fail
"archive did not contain
${
BIN_NAME
}
"
install_binary
"
${
extract_dir
}
/
${
BIN_NAME
}
"
"
$install_dir
"
log
"Installed
${
BIN_NAME
}
to
${
install_dir
}
/
${
BIN_NAME
}
"
if
!
command
-v
"
${
install_dir
}
/
${
BIN_NAME
}
"
>
/dev/null 2>&1
&&
!
printf
'%s'
":
$PATH
:"
|
grep
-q
":
${
install_dir
}
:"
;
then
log
"Add
${
install_dir
}
to your PATH to run
${
BIN_NAME
}
directly"
fi
}
main
"
$@
"
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment