Build improvements (#6759)

* test

* ci improvements

* fix image names

* improve docs

* Update .github/workflows/release.yml

Co-authored-by: Pascal Jufer <paescuj@users.noreply.github.com>

* Update docker/pack.js

Co-authored-by: Pascal Jufer <paescuj@users.noreply.github.com>

* improvements proposed by @paescuj

* improve Dockerfile comment

* fix .PHONY

* rm tarballs before building final image

Co-authored-by: Pascal Jufer <paescuj@users.noreply.github.com>
This commit is contained in:
Sean Goff
2021-07-14 22:40:38 +02:00
committed by GitHub
parent 10d0b5623c
commit b67b9d824a
21 changed files with 227 additions and 814 deletions

View File

@@ -1,15 +0,0 @@
FROM docker:stable
RUN \
apk update && \
apk upgrade && \
apk add bash
COPY ./rootfs/ /
RUN \
chmod +x /usr/bin/lib/argsf && \
chmod +x /usr/bin/entrypoint && \
chmod +x /usr/bin/semver
ENTRYPOINT ["entrypoint"]

View File

@@ -1,47 +0,0 @@
name: "Build and publish Directus images"
description: "GitHub Action to publish Directus container images."
branding:
icon: archive
color: gray-dark
inputs:
repository:
description: "Repository name"
required: true
registry:
description: "Registry"
required: true
username:
description: "Registry user"
required: true
password:
description: "Registry password"
required: true
version:
description: "Version"
required: true
push:
description: "Push"
required: false
default: "false"
latest:
description: "Latest"
required: false
default: "false"
runs:
using: "docker"
image: "Dockerfile"
args:
- --registry
- ${{ inputs.registry }}
- --repository
- ${{ inputs.repository }}
- --username
- ${{ inputs.username }}
- --password
- ${{ inputs.password }}
- --version
- ${{ inputs.version }}
- --push
- ${{ inputs.push }}
- --latest
- ${{ inputs.latest }}

View File

@@ -1,13 +0,0 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
tab_width = 2
trim_trailing_whitespace = true
[Makefile]
indent_style = tab

View File

@@ -1,66 +0,0 @@
#!/usr/bin/env bash
set -e
bootstrap() {
local warn=false
if [[ -z $KEY && -z $KEY_FILE ]]; then
KEY=$(uuidgen)
export KEY
warn=true
fi
if [[ -z $SECRET && -z $SECRET_FILE ]]; then
SECRET=$(node -e 'console.log(require("nanoid").nanoid(32))')
export SECRET
warn=true
fi
if [[ $warn == 'true' ]]; then
print --level=warn --stdin <<WARN
>
> WARNING!
>
> The KEY and SECRET environment variables are not set. Some
> temporary variables were generated to fill the gap, but in
> production this is going to cause problems.
>
> Reference:
> https://docs.directus.io/reference/environment-variables.html
>
>
WARN
fi
# Create folder if using sqlite and file doesn't exist
if [[ $DB_CLIENT == 'sqlite3' ]]; then
if [[ -z $DB_FILENAME ]]; then
print --level=error "Missing DB_FILENAME environment variable"
exit 1
fi
if [[ ! -f $DB_FILENAME ]]; then
mkdir -p "$(dirname "$DB_FILENAME")"
fi
fi
npx directus bootstrap
}
command=''
if [[ $# -eq 0 ]]; then
command="start"
elif [[ $1 == 'bash' || $1 == 'shell' ]]; then
shift
exec bash "$@"
elif [[ $1 == 'command' ]]; then
shift
exec "$@"
else
command="$1"
shift
fi
bootstrap
exec npx directus "$command" "$@"

View File

@@ -1,48 +0,0 @@
#!/usr/bin/env node
// Workarounds?
process.env.NODE_PATH = "/usr/local/lib/node_modules";
require("module").Module._initPaths();
/**
* Read lines from stdin
*/
async function readlines() {
const chunks = [];
for await (const chunk of process.stdin) {
chunks.push(chunk);
}
const lines = chunks.join("").split("\n");
lines.pop();
return lines;
}
(async function () {
// Logger
const yargs = require("yargs");
const logger = require("pino")({
prettyPrint: process.env.LOG_STYLE !== "raw",
prettifier: require("pino-colada"),
level: process.env.LOG_LEVEL || "info",
});
function write(...message) {
if (level in logger) {
logger[level](...message);
} else {
logger.info(...message);
}
}
const args = yargs.argv;
const level = args.level || "info";
const stdin = args.stdin || false;
if (stdin) {
const lines = await readlines();
lines.forEach((line) => write(line));
} else {
write(...args._);
}
})();

View File

@@ -1,138 +0,0 @@
#!/usr/bin/env bash
set -e
root=$(dirname ${0})
source ${root}/lib/argsf
#
# Makes a set of tags
#
function make_tags() {
local prefix=""
local version=${1}
semver get major ${version} > /dev/null 2>&1
if [ "$?" != "0" ]; then
echo "${version}"
else
if [ "${version:0:1}" == "v" ]; then
prefix="v"
fi
major="$(semver get major ${version})"
minor="${major}.$(semver get minor ${version})"
patch="${minor}.$(semver get patch ${version})"
prerel="$(semver get prerel ${version})"
if [ "${prerel}" == "" ]; then
is_prerel=false
else
is_prerel=true
fi
build="$(semver get build ${version})"
if [ "${build}" == "" ]; then
is_build=false
else
is_build=true
fi
if [ "${is_prerel}" == "true" ]; then
echo "${prefix}${major}-${prerel}"
echo "${prefix}${minor}-${prerel}"
echo "${prefix}${patch}-${prerel}"
if [ "${is_build}" == "true" ]; then
echo "${prefix}${major}-${prerel}-${build}"
fi
else
echo "${prefix}${major}"
echo "${prefix}${minor}"
echo "${prefix}${patch}"
if [ "${is_build}" == "true" ]; then
echo "${prefix}${patch}-${build}"
fi
fi
fi
}
#
# Build script
#
function main() {
username=$(argument username)
password=$(argument password)
push=$(argument push "false")
latest=$(argument latest "false")
registry=$(argument registry "")
registry=$(echo "${registry}" | tr '[:upper:]' '[:lower:]')
repository=$(argument repository "directus/directus")
repository=$(echo "${repository}" | tr '[:upper:]' '[:lower:]')
version=$(argument version "")
context=$(argument context ".")
image="${repository}"
if [ "${registry}" != "" ]; then
image="${registry}/${image}"
fi
# Normalize tag
if [ "${version}" == "" ]; then
version=${GITHUB_REF##*/}
else
version=${version##*/}
fi
if [ "${version}" == "" ]; then
version=$(echo ${GITHUB_SHA:-"000000000000"} | cut -c1-12)
fi
tags=$(make_tags ${version})
echo "Tags = ${tags}"
# build image
docker build \
-t directus:main \
--build-arg VERSION=${version} \
--build-arg REPOSITORY=${repository} \
/directus/images/main
# login into registry
docker login -u "${username}" -p "${password}" "${registry}"
# Push latest
# TODO: check if it's really the latest
if [ "${latest}" == "true" ]; then
fqin="${image}:latest"
echo "Tagging ${fqin}"
docker tag directus:main ${fqin}
if [ "${push}" == "true" ]; then
echo "Pushing tag ${fqin}"
docker push "${fqin}"
fi
fi
# Push tags
for tag in $tags
do
tag=$(echo "${tag}" | tr '[:upper:]' '[:lower:]')
fqin="${image}:${tag}"
echo "Tagging ${fqin}"
docker tag directus:main "${fqin}"
if [ "${push}" == "true" ]; then
echo "Pushing tag ${fqin}"
docker push "${fqin}"
fi
done
echo "Finished."
exit $?
}
main
exit $?

View File

@@ -1,98 +0,0 @@
#
# Arguments and Flags (argsf)
# This is meant to work with bash shell
# To use, source this file into your bash scripts
#
# Implemented by João Biondo <wolfulus@gmail.com>
# https://github.com/WoLfulus/argsf
#
declare _ARGCOUNT=$#
declare _ARGDATA=("$@")
declare -A _ARGMAP
declare -A _FLAGMAP
for ((_arg_index_key=1;_arg_index_key<=$#;_arg_index_key++))
do
_arg_index_value=$(expr $_arg_index_key + 1)
_arg_key=${!_arg_index_key}
_arg_value=${!_arg_index_value}
if [[ $_arg_key == *"--"* ]]; then
if [[ $_arg_key == *" "* ]]; then
continue
fi
_arg_name="${_arg_key:2}"
_FLAGMAP[${_arg_name}]=1
if [[ $_arg_value != *"--"* ]] || [[ $_arg_value == *" "* ]] ; then
_ARGMAP[${_arg_name}]="$_arg_value"
else
_ARGMAP[${_arg_name}]=""
fi
fi
done
function _argument() {
if test "${_ARGMAP[${ARG_NAME}]+isset}" ; then
echo ${_ARGMAP[${ARG_NAME}]}
else
if [ ${ARG_DEFAULT} -eq 0 ]; then
echo "Error: required argument '--${ARG_NAME}' not specified" 1>&2
exit 1
else
echo ${ARG_DEFAULT_VALUE}
fi
fi
}
function argument() {
if [ $# -eq 1 ]; then
ARG_NAME="$1" ARG_DEFAULT=0 ARG_DEFAULT_VALUE= _argument "${_ARGUMENT_DATA}"
elif [ $# -eq 2 ]; then
ARG_NAME="$1" ARG_DEFAULT=1 ARG_DEFAULT_VALUE="$2" _argument "${_ARGUMENT_DATA}"
else
echo "argument: invalid number of arguments" 1>&2
return 1
fi
return 0
}
function flage() {
if [ $# -eq 1 ]; then
if [[ ${_FLAGMAP[$1]} ]] ; then
echo "true"
return 0
elif [[ ${_FLAGMAP[no-$1]} ]] ; then
echo "false"
return 0
else
echo "true"
return 0
fi
else
echo "flag: invalid number of arguments" 1>&2
return 1
fi
}
function flagd() {
if [ $# -eq 1 ]; then
if [[ ${_FLAGMAP[$1]} ]] ; then
echo "true"
return 0
elif [[ ${_FLAGMAP[no-$1]} ]] ; then
echo "false"
return 0
else
echo "false"
return 0
fi
else
echo "flag: invalid number of arguments" 1>&2
return 1
fi
}
function flag() {
flagd $1
return $?
}

View File

@@ -1,284 +0,0 @@
#!/usr/bin/env bash
#
# Copyright (c) 2014-2015 François Saint-Jacques <fsaintjacques@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
#
set -o errexit -o nounset -o pipefail
NAT='0|[1-9][0-9]*'
ALPHANUM='[0-9]*[A-Za-z-][0-9A-Za-z-]*'
IDENT="$NAT|$ALPHANUM"
FIELD='[0-9A-Za-z-]+'
SEMVER_REGEX="\
^[vV]?\
($NAT)\\.($NAT)\\.($NAT)\
(\\-(${IDENT})(\\.(${IDENT}))*)?\
(\\+${FIELD}(\\.${FIELD})*)?$"
PROG=semver
PROG_VERSION="3.0.0"
USAGE="\
Usage:
$PROG bump (major|minor|patch|release|prerel <prerel>|build <build>) <version>
$PROG compare <version> <other_version>
$PROG get (major|minor|patch|release|prerel|build) <version>
$PROG --help
$PROG --version
Arguments:
<version> A version must match the following regular expression:
\"${SEMVER_REGEX}\"
In English:
-- The version must match X.Y.Z[-PRERELEASE][+BUILD]
where X, Y and Z are non-negative integers.
-- PRERELEASE is a dot separated sequence of non-negative integers and/or
identifiers composed of alphanumeric characters and hyphens (with
at least one non-digit). Numeric identifiers must not have leading
zeros. A hyphen (\"-\") introduces this optional part.
-- BUILD is a dot separated sequence of identifiers composed of alphanumeric
characters and hyphens. A plus (\"+\") introduces this optional part.
<other_version> See <version> definition.
<prerel> A string as defined by PRERELEASE above.
<build> A string as defined by BUILD above.
Options:
-v, --version Print the version of this tool.
-h, --help Print this help message.
Commands:
bump Bump by one of major, minor, patch; zeroing or removing
subsequent parts. \"bump prerel\" sets the PRERELEASE part and
removes any BUILD part. \"bump build\" sets the BUILD part.
\"bump release\" removes any PRERELEASE or BUILD parts.
The bumped version is written to stdout.
compare Compare <version> with <other_version>, output to stdout the
following values: -1 if <other_version> is newer, 0 if equal, 1 if
older. The BUILD part is not used in comparisons.
get Extract given part of <version>, where part is one of major, minor,
patch, prerel, build, or release.
See also:
https://semver.org -- Semantic Versioning 2.0.0"
function error {
echo -e "$1" >&2
exit 1
}
function usage-help {
error "$USAGE"
}
function usage-version {
echo -e "${PROG}: $PROG_VERSION"
exit 0
}
function validate-version {
local version=$1
if [[ "$version" =~ $SEMVER_REGEX ]]; then
# if a second argument is passed, store the result in var named by $2
if [ "$#" -eq "2" ]; then
local major=${BASH_REMATCH[1]}
local minor=${BASH_REMATCH[2]}
local patch=${BASH_REMATCH[3]}
local prere=${BASH_REMATCH[4]}
local build=${BASH_REMATCH[8]}
eval "$2=(\"$major\" \"$minor\" \"$patch\" \"$prere\" \"$build\")"
else
echo "$version"
fi
else
error "version $version does not match the semver scheme 'X.Y.Z(-PRERELEASE)(+BUILD)'. See help for more information."
fi
}
function is-nat {
[[ "$1" =~ ^($NAT)$ ]]
}
function is-null {
[ -z "$1" ]
}
function order-nat {
[ "$1" -lt "$2" ] && { echo -1 ; return ; }
[ "$1" -gt "$2" ] && { echo 1 ; return ; }
echo 0
}
function order-string {
[[ $1 < $2 ]] && { echo -1 ; return ; }
[[ $1 > $2 ]] && { echo 1 ; return ; }
echo 0
}
# given two (named) arrays containing NAT and/or ALPHANUM fields, compare them
# one by one according to semver 2.0.0 spec. Return -1, 0, 1 if left array ($1)
# is less-than, equal, or greater-than the right array ($2). The longer array
# is considered greater-than the shorter if the shorter is a prefix of the longer.
#
function compare-fields {
local l="$1[@]"
local r="$2[@]"
local leftfield=( "${!l}" )
local rightfield=( "${!r}" )
local left
local right
local i=$(( -1 ))
local order=$(( 0 ))
while true
do
[ $order -ne 0 ] && { echo $order ; return ; }
: $(( i++ ))
left="${leftfield[$i]}"
right="${rightfield[$i]}"
is-null "$left" && is-null "$right" && { echo 0 ; return ; }
is-null "$left" && { echo -1 ; return ; }
is-null "$right" && { echo 1 ; return ; }
is-nat "$left" && is-nat "$right" && { order=$(order-nat "$left" "$right") ; continue ; }
is-nat "$left" && { echo -1 ; return ; }
is-nat "$right" && { echo 1 ; return ; }
{ order=$(order-string "$left" "$right") ; continue ; }
done
}
# shellcheck disable=SC2206 # checked by "validate"; ok to expand prerel id's into array
function compare-version {
local order
validate-version "$1" V
validate-version "$2" V_
# compare major, minor, patch
local left=( "${V[0]}" "${V[1]}" "${V[2]}" )
local right=( "${V_[0]}" "${V_[1]}" "${V_[2]}" )
order=$(compare-fields left right)
[ "$order" -ne 0 ] && { echo "$order" ; return ; }
# compare pre-release ids when M.m.p are equal
local prerel="${V[3]:1}"
local prerel_="${V_[3]:1}"
local left=( ${prerel//./ } )
local right=( ${prerel_//./ } )
# if left and right have no pre-release part, then left equals right
# if only one of left/right has pre-release part, that one is less than simple M.m.p
[ -z "$prerel" ] && [ -z "$prerel_" ] && { echo 0 ; return ; }
[ -z "$prerel" ] && { echo 1 ; return ; }
[ -z "$prerel_" ] && { echo -1 ; return ; }
# otherwise, compare the pre-release id's
compare-fields left right
}
function command-bump {
local new; local version; local sub_version; local command;
case $# in
2) case $1 in
major|minor|patch|release) command=$1; version=$2;;
*) usage-help;;
esac ;;
3) case $1 in
prerel|build) command=$1; sub_version=$2 version=$3 ;;
*) usage-help;;
esac ;;
*) usage-help;;
esac
validate-version "$version" parts
# shellcheck disable=SC2154
local major="${parts[0]}"
local minor="${parts[1]}"
local patch="${parts[2]}"
local prere="${parts[3]}"
local build="${parts[4]}"
case "$command" in
major) new="$((major + 1)).0.0";;
minor) new="${major}.$((minor + 1)).0";;
patch) new="${major}.${minor}.$((patch + 1))";;
release) new="${major}.${minor}.${patch}";;
prerel) new=$(validate-version "${major}.${minor}.${patch}-${sub_version}");;
build) new=$(validate-version "${major}.${minor}.${patch}${prere}+${sub_version}");;
*) usage-help ;;
esac
echo "$new"
exit 0
}
function command-compare {
local v; local v_;
case $# in
2) v=$(validate-version "$1"); v_=$(validate-version "$2") ;;
*) usage-help ;;
esac
set +u # need unset array element to evaluate to null
compare-version "$v" "$v_"
exit 0
}
# shellcheck disable=SC2034
function command-get {
local part version
if [[ "$#" -ne "2" ]] || [[ -z "$1" ]] || [[ -z "$2" ]]; then
usage-help
exit 0
fi
part="$1"
version="$2"
validate-version "$version" parts
local major="${parts[0]}"
local minor="${parts[1]}"
local patch="${parts[2]}"
local prerel="${parts[3]:1}"
local build="${parts[4]:1}"
local release="${major}.${minor}.${patch}"
case "$part" in
major|minor|patch|release|prerel|build) echo "${!part}" ;;
*) usage-help ;;
esac
exit 0
}
case $# in
0) echo "Unknown command: $*"; usage-help;;
esac
case $1 in
--help|-h) echo -e "$USAGE"; exit 0;;
--version|-v) usage-version ;;
bump) shift; command-bump "$@";;
get) shift; command-get "$@";;
compare) shift; command-compare "$@";;
*) echo "Unknown arguments: $*"; usage-help;;
esac

View File

@@ -1,39 +0,0 @@
name: build-images
on:
release:
types:
- published
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Sleep for 30 seconds
uses: jakejarvis/wait-action@master
with:
time: '30s'
- name: Checkout
uses: actions/checkout@v2
- name: Build GitHub Container Registry
uses: ./.github/actions/build-images
with:
registry: "ghcr.io"
repository: "${{ github.repository }}"
username: "${{ secrets.REGISTRY_USERNAME }}"
password: "${{ secrets.REGISTRY_PASSWORD }}"
version: "${{ github.ref }}"
latest: "true"
push: "true"
- name: Build Docker Hub
uses: ./.github/actions/build-images
with:
registry: "docker.io"
repository: "${{ github.repository }}"
username: "${{ secrets.DOCKERHUB_USERNAME }}"
password: "${{ secrets.DOCKERHUB_PASSWORD }}"
version: "${{ github.ref }}"
latest: "true"
push: "true"

View File

@@ -1,24 +0,0 @@
name: create-release
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
body: |
Directus ${{ github.ref }}
draft: false
prerelease: false

118
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,118 @@
name: build-images
on:
push:
tags:
- 'v*'
env:
GHCR_IMAGE: ghcr.io/${{ github.repository }}
DOCKERHUB_IMAGE: ${{ github.repository }}
jobs:
create-release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
body: |
Directus ${{ github.ref }}
draft: false
prerelease: false
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '16.x'
- uses: c-hive/gha-npm-cache@v1
- run: npm ci
- run: npm run build
- run: node docker/pack
- name: Cache build artifacts
uses: actions/cache@v2
with:
path: '**/dist'
key: build-artifacts-${{ github.sha }}
publish-npm:
runs-on: ubuntu-latest
needs: build
steps:
- name: Check NPM_TOKEN secret
if: secrets.NPM_TOKEN == null || secrets.NPM_TOKEN == ''
run: exit 1
- uses: actions/checkout@v2
- name: Restore build artifacts
uses: actions/cache@v2
with:
path: '**/dist'
key: build-artifacts-${{ github.sha }}
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '16.x'
- run: npx lerna publish --force-publish --exact from-git --yes
build-images:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v2
- name: Restore build artifacts
uses: actions/cache@v2
with:
path: '**/dist'
key: build-artifacts-${{ github.sha }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: |
${{ env.DOCKERHUB_IMAGE }}
${{ env.GHCR_IMAGE }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Login to DockerHub
uses: docker/login-action@v1
if: ${{ env.DOCKERHUB_IMAGE }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v1
if: ${{ env.GHCR_IMAGE }}
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: './docker/Dockerfile'
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
push: true

1
.npmrc
View File

@@ -1 +1,2 @@
engine-strict=true
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

View File

@@ -1,21 +1,9 @@
# Builder image
FROM alpine:latest AS builder
FROM node:16-alpine as builder
ARG VERSION
ARG REPOSITORY=directus/directus
# Get runtime dependencies from optional dependencies
# defined in package.json of Directus API package
WORKDIR /directus
RUN apk add --no-cache jq \
&& wget -O directus-api-package.json "https://raw.githubusercontent.com/${REPOSITORY}/${VERSION}/api/package.json" \
&& jq '{ \
name: "directus-project", \
version: "1.0.0", \
description: "Directus Project", \
dependencies: .optionalDependencies \
}' \
directus-api-package.json > package.json
COPY /dist .
RUN npm i --only=production --no-package-lock
RUN rm *.tgz
# Directus image
FROM node:16-alpine
@@ -54,19 +42,9 @@ ENV \
EMAIL_SENDMAIL_PATH="/usr/sbin/sendmail"
RUN \
# Install system dependencies
# - 'bash' for entrypoint script
# - 'ssmtp' to be able to send mails
# - 'util-linux' not sure if this is required
# Upgrade system and install 'ssmtp' to be able to send mails
apk upgrade --no-cache && apk add --no-cache \
bash \
ssmtp \
util-linux \
# Install global node dependencies
&& npm install -g \
yargs \
pino \
pino-colada \
# Create directory for Directus with corresponding ownership
# (can be omitted on newer Docker versions since WORKDIR below will do the same)
&& mkdir /directus && chown node:node /directus
@@ -75,32 +53,22 @@ RUN \
USER node
WORKDIR /directus
# Get package.json from builder image
COPY --from=builder --chown=node:node /directus/package.json .
COPY --from=builder --chown=node:node /directus .
RUN \
# Install Directus and runtime dependencies
# (retry if it fails for some reason, e.g. release not published yet)
for i in $(seq 15); do npm install --save-exact "directus@${VERSION}" && break || if [ $i -eq 15 ]; then exit 1; else sleep 30; fi; done && \
npm install \
# Create data directories
&& mkdir -p \
mkdir -p \
database \
extensions/displays \
extensions/interfaces \
extensions/layouts \
extensions/modules \
uploads
# Expose data directories as volumes
VOLUME \
/directus/database \
/directus/extensions \
/directus/uploads
# Copy rootfs files
COPY ./rootfs /
EXPOSE 8055
SHELL ["/bin/bash", "-c"]
ENTRYPOINT ["entrypoint"]
CMD npx directus bootstrap && npx directus start

View File

@@ -10,12 +10,14 @@ repository=directus/directus
.PHONY: build-images
build-images:
npm run build
npm run pack
docker build \
--build-arg VERSION=$(version) \
--build-arg REPOSITORY=$(repository) \
-t directus:temp \
-f ./build-images/rootfs/directus/images/main/Dockerfile \
./build-images/rootfs/directus/images/main
-f ./Dockerfile \
..
docker tag directus:temp $(registry)/$(repository):$(version)
docker tag directus:temp $(registry)/$(repository):$(tag)

41
docker/pack.js Normal file
View File

@@ -0,0 +1,41 @@
const { execSync } = require('child_process');
const { writeFileSync, mkdirSync, existsSync } = require('fs');
const path = require('path/posix');
const lernaListResult = execSync('npx lerna list --json'); //The "proper" way to do this with --include-dependencies and --scope won't work here because it includes devDependencies!
const list = JSON.parse(String(lernaListResult));
const apiPackageJSON = require(path.resolve(__dirname, '../api/package.json'));
const projectPackageJSON = {
name: 'directus-project',
private: true,
description: 'Directus Project',
dependencies: apiPackageJSON.optionalDependencies,
};
const directusPackage = list.find((list) => list.name === 'directus');
if (!existsSync('dist')) {
mkdirSync('dist');
}
function addPackageRecursive(package) {
const tarName = String(
execSync(`npm pack ${package.location}`, { cwd: path.resolve(__dirname, '..', 'dist') })
).trim();
projectPackageJSON.dependencies[package.name] = `file:${tarName}`;
const packageJSON = require(path.join(package.location, 'package.json'));
Object.keys(packageJSON.dependencies || {}).forEach((dependencyName) => {
if (!projectPackageJSON.dependencies[dependencyName]) {
const package = list.find((list) => list.name === dependencyName);
if (package) {
addPackageRecursive(package);
}
}
});
}
addPackageRecursive(directusPackage);
writeFileSync(path.resolve(__dirname, '../dist/package.json'), JSON.stringify(projectPackageJSON, null, 4));

View File

@@ -805,6 +805,11 @@ module.exports = {
path: '/contributing/running-locally',
title: 'Running Locally',
},
{
type: 'page',
path: '/contributing/github-ci',
title: 'GitHub CI',
},
{
type: 'page',
path: '/contributing/translations',

View File

@@ -0,0 +1,48 @@
# GitHub CI
> This guide explains how to publish a forked version of Directus on NPM, dockerhub and GHCR. You need to first
> [create a fork](/contributing/running-locally/)
::: warning Using a fork in production is neither supported nor recommended.
Only do so if:
1. The feature you want to implement doesn't satisfy the 80/20 principle
2. There is a technical limitation preventing you from implementing it as an extension
Forks are not officially supported - please don't submit fork-specific bugs and questions on the official repo.
If you suspect a bug to not be part of your changes please confirm this by reproducing it with the official version
**before** submitting an issue.
:::
::: tip Minimum Requirements
If you want to publish your fork to hub.docker.com or NPM you need to have an account there.
Note: to publish to NPM you probably have to change all the package names. Publishing to NPM should almost never be
necessary.
:::
## 1. Setup environment variables/secrets
## General
| Variable | Description | Example |
| ----------------- | ---------------------------------------------------------------------- | --------------------------- |
| `GHCR_IMAGE` | Image name for GitHub Container Registry. Be sure to use the full URL. | `ghcr.io/directus/directus` |
| `DOCKERHUB_IMAGE` | Image name for hub.docker.com, no prefix. | `directus/directus` |
| Secret | Description | Example |
| -------------------- | ------------------------------------------------------- | -------------- |
| `NPM_TOKEN` | Your NPM token. Make sure to use the type "automation". | `12345678-...` |
| `DOCKERHUB_USERNAME` | Dockerhub Username | `directus` |
| `DOCKERHUB_TOKEN` | Dockerhub Password | `hunter2` |
## 2. Create a release
```bash
npm run release
```

View File

@@ -74,6 +74,7 @@ issue doesn't already exist, you can [submit a bug report](https://github.com/di
Issues. Please ensure the following for all submissions:
- You are experiencing an actual bug, not a configuration issue
- You are using the official (pre-built) version of directus and not a fork
- You are not asking a question or requesting a new feature
- You have checked that no similar issue already exists
- You have entered a clear and concise title

View File

@@ -14,6 +14,7 @@
"format": "prettier --write \"**/*.{js,ts,vue,md,yaml}\"",
"dev": "lerna run dev --stream --parallel",
"build": "lerna run build",
"pack": "node docker/pack",
"release": "lerna publish --force-publish --exact",
"test": "lerna run test",
"test:e2e": "jest tests -c tests/jest.config.js",