Skip to content

Commit 1abbcbe

Browse files
committed
feat: setup project & add lefthook devcontainers feature
0 parents  commit 1abbcbe

12 files changed

Lines changed: 387 additions & 0 deletions

File tree

.devcontainer/devcontainer.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"image": "mcr.microsoft.com/devcontainers/javascript-node:1-20-bookworm",
3+
"customizations": {
4+
"vscode": {
5+
"settings": {
6+
"json.schemas": [
7+
{
8+
"fileMatch": [
9+
"*/devcontainer-feature.json"
10+
],
11+
"url": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainerFeature.schema.json"
12+
}
13+
]
14+
},
15+
"extensions": [
16+
"mads-hartmann.bash-ide-vscode",
17+
"github.vscode-github-actions"
18+
]
19+
}
20+
},
21+
"features": {
22+
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
23+
},
24+
"remoteUser": "node",
25+
"updateContentCommand": "npm install -g @devcontainers/cli"
26+
}

.github/workflows/release.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: "Release dev container features & Generate Documentation"
2+
on:
3+
workflow_dispatch:
4+
5+
jobs:
6+
deploy:
7+
if: ${{ github.ref == 'refs/heads/main' }}
8+
runs-on: ubuntu-latest
9+
permissions:
10+
contents: write
11+
pull-requests: write
12+
packages: write
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: "Publish Features"
17+
uses: devcontainers/action@v1
18+
with:
19+
publish-features: "true"
20+
base-path-to-features: "./src"
21+
generate-docs: "true"
22+
23+
env:
24+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
25+
26+
- name: Create PR for Documentation
27+
id: push_image_info
28+
env:
29+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30+
run: |
31+
set -e
32+
echo "Start."
33+
# Configure git and Push updates
34+
git config --global user.email github-actions[bot]@users.noreply.github.com
35+
git config --global user.name github-actions[bot]
36+
git config pull.rebase false
37+
branch=automated-documentation-update-$GITHUB_RUN_ID
38+
git checkout -b $branch
39+
message='Automated documentation update'
40+
# Add / update and commit
41+
git add */**/README.md
42+
git commit -m 'Automated documentation update [skip ci]' || export NO_UPDATES=true
43+
# Push
44+
if [ "$NO_UPDATES" != "true" ] ; then
45+
git push origin "$branch"
46+
gh pr create --title "$message" --body "$message"
47+
fi

.github/workflows/test.yaml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: "CI - Test Features"
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request:
7+
workflow_dispatch:
8+
9+
jobs:
10+
test-autogenerated:
11+
runs-on: ubuntu-latest
12+
continue-on-error: true
13+
strategy:
14+
matrix:
15+
features:
16+
- lefthook
17+
baseImage:
18+
- debian:latest
19+
- ubuntu:latest
20+
- mcr.microsoft.com/devcontainers/base:ubuntu
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- name: "Install latest devcontainer CLI"
25+
run: npm install -g @devcontainers/cli
26+
27+
- name: "Generating tests for '${{ matrix.features }}' against '${{ matrix.baseImage }}'"
28+
run: devcontainer features test --skip-scenarios -f ${{ matrix.features }} -i ${{ matrix.baseImage }} .
29+
30+
test-scenarios:
31+
runs-on: ubuntu-latest
32+
continue-on-error: true
33+
strategy:
34+
matrix:
35+
features:
36+
- lefthook
37+
steps:
38+
- uses: actions/checkout@v4
39+
40+
- name: "Install latest devcontainer CLI"
41+
run: npm install -g @devcontainers/cli
42+
43+
- name: "Generating tests for '${{ matrix.features }}' scenarios"
44+
run: devcontainer features test -f ${{ matrix.features }} --skip-autogenerated --skip-duplicated .
45+
46+
test-global:
47+
runs-on: ubuntu-latest
48+
continue-on-error: true
49+
steps:
50+
- uses: actions/checkout@v4
51+
52+
- name: "Install latest devcontainer CLI"
53+
run: npm install -g @devcontainers/cli
54+
55+
- name: "Testing global scenarios"
56+
run: devcontainer features test --global-scenarios-only .

.github/workflows/validate.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: "Validate devcontainer-feature.json files"
2+
on:
3+
workflow_dispatch:
4+
pull_request:
5+
6+
jobs:
7+
validate:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v4
11+
12+
- name: "Validate devcontainer-feature.json files"
13+
uses: devcontainers/action@v1
14+
with:
15+
validate-only: "true"
16+
base-path-to-features: "./src"

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Fazle Adyuta Utomo
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Devcontainers Features
2+
3+
Custom features for devcontainers used in development of Omoxyz software projects using Visual Studio Code. It ensures all developers have consistent tools and configurations.
4+
5+
## Features
6+
7+
- **Lefthook** (`lefthook`) – fast polyglot Git hooks manager to automate code checks, formatting, and tests before commits and pushes.
8+
9+
## Usage
10+
11+
*Please refer to `README.md` file of the feature you want to use located in `src/{feature-id}` folder.*

src/lefthook/NOTES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## OS Support
2+
3+
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
4+
5+
`bash` is required to execute the `install.sh` script.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "Lefthook",
3+
"id": "lefthook",
4+
"version": "1.0.0",
5+
"documentationURL": "http://github.com/omoxyz/devcontainer-features/tree/main/src/lefthook",
6+
"description": "Install Lefthook fast polyglot Git hooks manager.",
7+
"options": {
8+
"version": {
9+
"default": "latest",
10+
"description": "Select the version to install.",
11+
"proposals": [
12+
"latest"
13+
],
14+
"type": "string"
15+
},
16+
"installDirectlyFromGitHubRelease": {
17+
"type": "boolean",
18+
"default": true
19+
}
20+
}
21+
}

src/lefthook/install.sh

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#! /usr/bin/bash
2+
3+
source ./utils.sh
4+
5+
LEFTHOOK_VERSION=${VERSION:-"latest"}
6+
INSTALL_DIRECTLY_FROM_GITHUB_RELEASE=${INSTALLDIRECTLYFROMGITHUBRELEASE:-"false"}
7+
GITHUB_REPO=https://github.com/evilmartians/lefthook
8+
9+
# Exit immediately if a command exits with a non-zero status.
10+
set -e
11+
12+
apt_get_update
13+
14+
# Clean up
15+
rm -rf /var/lib/apt/lists/*
16+
17+
if [ "$(id -u)" -ne 0 ]; then
18+
echo -e 'Scripts must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
19+
exit 1
20+
fi
21+
22+
export DEBIAN_FRONTEND=noninteractive
23+
24+
get_github_filename() {
25+
local version=$1
26+
local arch=$2
27+
echo "lefthook_${version}_${arch}.deb"
28+
}
29+
30+
install_from_github() {
31+
local version_list=$(git ls-remote --tags ${GITHUB_REPO})
32+
33+
versions=($(find_latest_version $LEFTHOOK_VERSION version_list "tags/v"))
34+
latest_version=${versions[0]}
35+
prev_version=${versions[1]}
36+
37+
if [ $? -eq 1 ]; then
38+
echo "Can't find appropriate version"
39+
exit 1
40+
fi
41+
42+
echo "Downloading lefthook v${latest_version}...."
43+
44+
check_packages wget
45+
local arch=$(dpkg --print-architecture)
46+
47+
local filename=$(get_github_filename $latest_version $arch)
48+
49+
mkdir -p /tmp/lefthook
50+
pushd /tmp/lefthook
51+
wget ${GITHUB_REPO}/releases/download/v${latest_version}/${filename}
52+
local exit_code=$?
53+
54+
set -e
55+
if [ "$exit_code" != "0" ]; then
56+
# Handle situation where git tags are ahead of what was is available to actually download
57+
echo "(!) lefthook version ${latest_version} failed to download. Attempting to fall back to ${prev_version} to retry..."
58+
filename=$(get_github_filename $prev_version $arch)
59+
wget ${GITHUB_REPO}/releases/download/v${prev_version}/${filename}
60+
fi
61+
62+
dpkg -i /tmp/lefthook/${filename}
63+
popd
64+
rm -rf /tmp/lefthook
65+
}
66+
67+
install_from_package_manager() {
68+
curl -1sLf 'https://dl.cloudsmith.io/public/evilmartians/lefthook/setup.deb.sh' | bash
69+
70+
local version_list=$(get_apt_versions lefthook)
71+
versions=($(find_latest_version $LEFTHOOK_VERSION version_list))
72+
latest_version=${versions[0]}
73+
74+
if [ $? -eq 1 ]; then
75+
echo "Can't find appropriate version"
76+
exit 1
77+
fi
78+
79+
echo "Downloading Lefthook v${latest_version}..."
80+
apt-get install -y --no-install-recommends lefthook=${latest_version}
81+
}
82+
83+
# Install curl, ca-certificates, git if missing
84+
check_packages curl ca-certificates
85+
if ! type git > /dev/null 2>&1; then
86+
check_packages git
87+
fi
88+
89+
# Install Lefthook
90+
if [ "${INSTALL_DIRECTLY_FROM_GITHUB_RELEASE}" = "true" ]; then
91+
install_from_github
92+
else
93+
install_from_package_manager
94+
fi
95+
96+
# Clean up
97+
rm -rf /var/lib/apt/lists/*

src/lefthook/utils.sh

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Refresh the local package index if no package list entries are stored on the system.
2+
apt_get_update() {
3+
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
4+
echo "Running apt-get update..."
5+
apt-get update -y
6+
fi
7+
}
8+
9+
# Checks if packages are installed and installs them if not
10+
check_packages() {
11+
if ! dpkg -s "$@" > /dev/null 2>&1; then
12+
apt_get_update
13+
apt-get -y --no-install-recommends "$@"
14+
fi
15+
}
16+
17+
find_latest_version() {
18+
local requested_version=$1
19+
local version_list=${!2}
20+
21+
# Version prefix such as "tags/v"
22+
local prefix_regex=${3:-''}
23+
24+
# Version number part separator such as "." in "1.0.0"
25+
local separator=${4:-"."}
26+
local escaped_separator=${separator//./\\.}
27+
28+
local suffix_regex=${5:-''}
29+
30+
# Format and sort version list
31+
local version_regex="${prefix_regex}\\K[0-9]+(${escaped_separator}[0-9]+){0,2}${suffix_regex}$"
32+
version_list="$(printf "%s\n" "${version_list[@]}" | grep -oP $version_regex| tr -d ' ' | tr $separator '.' | sort -rV)"
33+
34+
if [ "${requested_version}" = "latest" ]; then
35+
echo "$(echo "${version_list}" | head -n 2)"
36+
else
37+
# Try to get latest matching version
38+
39+
set +e
40+
local regex="^"
41+
42+
# Get major version or exit
43+
local major="$(echo "${requested_version}" | grep -oE '^[0-9]+')"
44+
if [ $major != '' ]; then
45+
regex="${regex}${major}"
46+
else
47+
echo "Invalid version \"${requested_version}\". Use \"latest\" or MAJOR[.MINOR][.PATCH]"
48+
return 1
49+
fi
50+
51+
# Get minor number or accept any
52+
local minor="$(echo "${requested_version}" | grep -oP '^[0-9]+\.\K[0-9]+')"
53+
regex="${regex}$([ "$minor" != '' ] && echo "${escaped_separator}${minor}" || echo "(${escaped_separator}[0-9]+)?")"
54+
55+
56+
# Get patch number or accept any
57+
local patch="$(echo "${requested_version}" | grep -oP '^[0-9]+\.[0-9]+\.\K[0-9]+')"
58+
regex="${regex}$([ "$patch" != '' ] && echo "${escaped_separator}${patch}" || echo "(${escaped_separator}[0-9]+)?")"
59+
set -e
60+
61+
echo "$(echo "${version_list}" | grep -E -m 2 "^${regex}$")"
62+
fi
63+
}
64+
65+
get_apt_versions() {
66+
package="$1"
67+
apt list -a "$package" 2>/dev/null \
68+
| awk -F' ' 'NR>1 {print $2}' \
69+
| sort -rV
70+
}

0 commit comments

Comments
 (0)