diff --git a/.github/workflows/container.yaml b/.github/workflows/container.yaml new file mode 100644 index 0000000..9663c05 --- /dev/null +++ b/.github/workflows/container.yaml @@ -0,0 +1,61 @@ +--- +name: Publish OCI Container +on: # yamllint disable-line rule:truthy + workflow_call: + inputs: + tag: + description: 'The tag to use for the container image' + required: false + type: string + workflow_dispatch: + inputs: + tag: + description: 'The tag to use for the container image' + required: false + type: string +jobs: + publish_github: + name: Publish the container image to GitHub Container Registry + runs-on: ubuntu-latest + strategy: + # Go hard on the builders + max-parallel: 5 + matrix: + alpine-version: ['3.21'] + ruby-version: ['3.4.7'] + steps: + - + name: Checkout repository + uses: actions/checkout@v4 + - + name: Publish to ghcr.io + env: + ALPINE_VERSION: ${{ matrix.alpine-version }} + REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RUBY_VERSION: ${{ matrix.ruby-version }} + TAG: ${{ github.event.inputs.tag || '' }} + TRACE: ${{ secrets.ACTIONS_STEP_DEBUG || 'false' }} + # yamllint disable rule:line-length + run: | + if [ -z "$TAG" ] + then + [ "$TRACE" = true ] && printf 'No tag provided, getting tag from .version.txt\n' >&2 + if [ -f ".version.txt" ] + then + version=$(<.version.txt) + else + [ "$TRACE" = true ] && printf 'No .version.txt found, getting version from git describe --tags --abbrev=0\n' >&2 + version=$(git describe --tags --abbrev=0) + fi + else + [ "$TRACE" = true ] && printf 'Using provided tag %s\n' "$TAG" >&2 + version=$TAG + fi + [ "$TRACE" = 'true' ] && printf 'Calling ./ci/build_image.sh -vvp "%s"\n' "$version" >&2 + IMAGE_NAME=$(basename "$GITHUB_REPOSITORY") \ + GITHUB_TOKEN=$REGISTRY_TOKEN \ + ALPINE_VERSION=$ALPINE_VERSION \ + RUBY_VERSION=$RUBY_VERSION \ + ./ci/build_image.sh -vvp "$version" + # yamllint enable rule:line-length + shell: bash diff --git a/.github/workflows/conventional_commits.yaml b/.github/workflows/conventional_commits.yaml new file mode 100644 index 0000000..6315812 --- /dev/null +++ b/.github/workflows/conventional_commits.yaml @@ -0,0 +1,65 @@ +--- +name: Conventional Commits And PR titles + +on: # yamllint disable-line rule:truthy + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + comventional_commits: + name: Validate Commit Subjects + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: webiny/action-conventional-commits@v1.3.0 + name: Validate Commit Subjects + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # All of the Angular commit types are allowed by default. + # Added types to this: + # * eyes - For observability related changes + # * sec - For security related changes + allowed-commit-types: "build,chore,ci,doc,documentation,docs,eyes,feat,fix,perf,refactor,revert,sec,style,test" # yamllint disable-line rule:line-length + conventional_pr_title: + name: Validate PR title + runs-on: ubuntu-latest + steps: + - + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + id: validate-pr-title + name: Validate PR title + uses: amannn/action-semantic-pull-request@v5 + with: + # All of the Angular commit types are allowed by default. + # Added types to this: + # * eyes - For observability related changes + # * sec - For security related changes + types: | + build + chore + ci + doc + documentation + docs + eyes + feat + fix + perf + refactor + revert + sec + style + test + # We don't enforce scopes + # scopes: + # - frontend + # - backend + # - ci + # We don't disallow any scopes + # disallowScopes: | + # release + wip: true diff --git a/.github/workflows/gem.yaml b/.github/workflows/gem.yaml new file mode 100644 index 0000000..a2b161e --- /dev/null +++ b/.github/workflows/gem.yaml @@ -0,0 +1,34 @@ +--- +name: Publish Ruby Gem +on: # yamllint disable-line rule:truthy + workflow_call: + workflow_dispatch: +jobs: + publish_gem: + name: Publish the gem to registries + runs-on: ubuntu-latest + strategy: + matrix: + registry: + - key: rubygems + secret: RUBYGEMS_TOKEN + steps: + - + name: Checkout repository + uses: actions/checkout@v4 + - + name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.4.1 + bundler-cache: false + - + name: Publish to ${{ matrix.registry }} + env: + TRACE: ${{ secrets.ACTIONS_STEP_DEBUG || 'false' }} + GEM_TOKEN: ${{ secrets[matrix.registry.secret] }} + REGISTRY: ${{ matrix.registry.key }} + run: | + bundle install + TRACE="$TRACE" GEM_TOKEN="$GEM_TOKEN" ./ci/publish-gem.sh "$REGISTRY" + shell: bash diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 0000000..3df2f46 --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,26 @@ +name: Ruby + +on: + workflow_call: + + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + name: Ruby ${{ matrix.ruby }} + strategy: + matrix: + ruby: + - '3.4.7' + steps: + - uses: actions/checkout@v4 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: false + - name: Install dependencies + run: bundle install --jobs 4 --retry 3 + - name: Run the default task + run: bundle exec rake diff --git a/.github/workflows/main.yml b/.github/workflows/outdated/main.yml similarity index 100% rename from .github/workflows/main.yml rename to .github/workflows/outdated/main.yml diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..adff340 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,17 @@ +--- +name: Publish + +on: # yamllint disable-line rule:truthy + workflow_dispatch: + workflow_call: + +jobs: + gem: + name: Build and publish gem + uses: ./.github/workflows/gem.yaml + secrets: inherit + +# containers: +# name: Build and publish OCI container images +# uses: ./.github/workflows/container.yaml +# secrets: inherit diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..6411443 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,51 @@ +--- +name: Release + +on: # yamllint disable-line rule:truthy + push: + branches: + - main + workflow_dispatch: + workflow_call: + +jobs: + validate: + name: Validations + uses: ./.github/workflows/validations.yaml + + release: + needs: [validate] + name: Create a release + runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs.release_created }} + steps: + - + uses: actions/checkout@v4 + id: git-checkout + with: + fetch-tags: true + - + uses: googleapis/release-please-action@v4 + id: release + with: + config-file: .release-please-config.json + manifest-file: .release-please-manifest.json + token: ${{ secrets.RELEASE_PLEASE_TOKEN }} + - + id: debug + env: + RELEASE_CREATED: ${{ steps.release.outputs.release_created }} + TRACE: ${{ secrets.ACTIONS_STEP_DEBUG || 'false' }} + run: | + if [ "$TRACE" != 'false' ] + then + printf 'Release created: %s\n' "$RELEASE_CREATED" + fi + + publish: + if: needs.release.outputs.release_created + needs: release + name: Build and publish artifacts + uses: ./.github/workflows/publish.yaml + secrets: inherit diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml new file mode 100644 index 0000000..bd9ea2c --- /dev/null +++ b/.github/workflows/validations.yaml @@ -0,0 +1,12 @@ +--- +name: Validations + +on: # yamllint disable-line rule:truthy + workflow_dispatch: + workflow_call: + pull_request: + +jobs: + validate_ruby: + name: Ruby Tests + uses: ./.github/workflows/main.yaml diff --git a/.release-please-config.json b/.release-please-config.json new file mode 100644 index 0000000..28c55aa --- /dev/null +++ b/.release-please-config.json @@ -0,0 +1,32 @@ +{ + "packages": { + ".": { + "changelog-path": "CHANGELOG.md", + "release-type": "simple", + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": true, + "draft": false, + "prerelease": false, + "version-file": ".version.txt", + "extra-files": [ + { + "type": "generic", + "path": "lib/linear/version.rb" + }, + { + "type": "generic", + "path": "oci/Gemfile" + } + ], + "exclude-paths": [ + ".release-please-manifest.json", + ".version.txt", + "lib/linear/version.rb", + ".rubocop.yml", + ".overcommit.yml", + "coverage/coverage.json" + ] + } + }, + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" +} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..37fcefa --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "1.0.0" +} diff --git a/.version.txt b/.version.txt new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/.version.txt @@ -0,0 +1 @@ +1.0.0 diff --git a/Readme.adoc b/Readme.adoc index 408c23f..72308f8 100644 --- a/Readme.adoc +++ b/Readme.adoc @@ -7,6 +7,7 @@ :note-caption: 📝 :experimental: :plc-url: https://raw.githubusercontent.com/rubyists/linear-cli/main/oci/plc +:conventional-commits: https://www.conventionalcommits.org/en/v1.0.0/[Conventional Commits] A command line interface to https://linear.app. @@ -207,3 +208,23 @@ $ lcls $ lcreate --description "This is a new issue" --labels Bug,Feature --team CRY $ lclose --reason "This issue sucks" CRY-1234 CRY-456 ---- + +== Development + +The project uses Minitest and RuboCop. Run tests with Rake: + +[source,bash] +---- +$ bundle exec rake +---- + +=== Conventional Commits (semantic commit messages) + +This project follows the {conventional-commits} specification. + +To contribute, please follow that commit message format, +or your pull request may be rejected. + +== License + +MIT diff --git a/ci/build_image.sh b/ci/build_image.sh new file mode 100755 index 0000000..d69e2bb --- /dev/null +++ b/ci/build_image.sh @@ -0,0 +1,248 @@ +#!/usr/bin/env bash + +if readlink -f . >/dev/null 2>&1 # {{{ makes readlink work on mac +then + readlink=readlink +else + if greadlink -f . >/dev/null 2>&1 + then + readlink=greadlink + else + printf "You must install greadlink to use this (brew install coreutils)\n" >&2 + fi +fi # }}} + +# Set here to the full path to this script +me=${BASH_SOURCE[0]} +[ -L "$me" ] && me=$($readlink -f "$me") +here=$(cd "$(dirname "$me")" && pwd) +just_me=$(basename "$me") + +repo_top=$(git rev-parse --show-toplevel) +cd "$repo_top" || { + printf "Could not cd to %s\n" "$repo_top" >&2 + exit 1 +} + +base_dir=$(basename "$(pwd)") +: "${BUILD_CONTEXT:=$(pwd)}" +: "${IMAGE_NAME:=$base_dir}" +: "${LICENSE:=MIT}" +: "${APP_VERSION:=$(< "$here"/../.version.txt)}" +: "${REGISTRY:=ghcr.io}" +: "${REGISTRY_TOKEN:=$GITHUB_TOKEN}" + +usage() { # {{{ + cat <<-EOT + Build an image, optionally pushing it to the registry + + Usage: $0 + Options: + -c CONTAINERFILE Path to the containerfile (default: ./oci/Containerfile) + -C CONTEXT Build context (default: $BUILD_CONTEXT) + -i NAME Name of the image (default: $IMAGE_NAME) + -l LICENSE License of the image (default: $LICENSE) + -r REGISTRY Registry to push the image to when -p is given (default: $REGISTRY) + -p Push the image to the registry + -h Show help / usage +EOT +} # }}} + +die() { # {{{ + local -i code + code=$1 + shift + error "$*" + printf "\n" >&2 + usage >&2 + # shellcheck disable=SC2086 + exit $code +} # }}} + +## Logging functions # {{{ +log() { # {{{ + printf "%s [%s] <%s> %s\n" "$(date '+%Y-%m-%d %H:%M:%S.%6N')" "$$" "${just_me:-$0}" "$*" +} # }}} + +debug() { # {{{ + [ $verbose -lt 2 ] && return 0 + # shellcheck disable=SC2059 + log_line=$(printf "$@") + log "[DEBUG] $log_line" >&2 +} # }}} + +warn() { # {{{ + # shellcheck disable=SC2059 + log_line=$(printf "$@") + log "[WARN] $log_line" >&2 +} # }}} + +error() { # {{{ + # shellcheck disable=SC2059 + log_line=$(printf "$@") + log "[ERROR] $log_line" >&2 +} # }}} + +info() { # {{{ + [ $verbose -lt 1 ] && return 0 + # shellcheck disable=SC2059 + log_line=$(printf "$@") + log "[INFO] $log_line" >&2 +} # }}} +# }}} + +push=0 +verbose=0 +while getopts :hpvc:C:i:l:r: opt # {{{ +do + case $opt in + c) + CONTAINERFILE=$OPTARG + ;; + C) + BUILD_CONTEXT=$OPTARG + ;; + i) + IMAGE_NAME=$OPTARG + ;; + l) + LICENSE=$OPTARG + ;; + r) + REGISTRY=$OPTARG + ;; + p) + push=1 + ;; + v) + verbose=$((verbose + 1)) + ;; + h) + usage + exit + ;; + :) + printf "Option %s requires an argument\n" "$OPTARG" >&2 + usage >&2 + exit 28 + ;; + ?) + printf "Invalid option '%s'\n" "$OPTARG" >&2 + usage >&2 + exit 27 + ;; + esac +done # }}} +shift $((OPTIND-1)) + +tag=$1 +[ -z "$tag" ] && die 1 "Missing image tag" +shift + +# Check for extra argument +if [ $# -gt 0 ]; then + # If we have the special argument '--' we shift it away, otherwise we die + [ "$1" != '--' ] && die 2 "Too many arguments" + # Once this is shifted away, the rest of the arguments are passed to the build command, below + shift +fi + +if [ -z "$CONTAINERFILE" ]; then + printf "No containerfile specified, looking for default locations\n" + for containerfile in Containerfile Dockerfile + do + if [ -f ./oci/"$containerfile" ]; then + debug "Found ./oci/%s\n" "$containerfile" + containerfile=./oci/"$containerfile" + break + fi + if [ -f "$containerfile" ]; then + debug "Found %s\n" "$containerfile" + break + fi + done +else + [ -f "$CONTAINERFILE" ] || die 3 "Containerfile '$CONTAINERFILE' not found" + debug "Using containerfile %s\n" "$CONTAINERFILE" + containerfile=$CONTAINERFILE +fi + +[ -f "$containerfile" ] || die 4 "No containerfile found" + +[ -d "$BUILD_CONTEXT" ] || die 5 "Build context '$BUILD_CONTEXT' not found" + +debug 'Building image from %s in in %s\n' "$containerfile" "$here" +# Build the image +if command -v podman 2>/dev/null +then + runtime=podman +elif command -v docker 2>/dev/null +then + runtime=docker +else + die 6 "No container runtime found" +fi + +revision=$(git rev-parse HEAD) +shortref=$(git rev-parse --short "$revision") +repo_url=$(git remote get-url origin) +if [ -z "$repo_url" ] +then + die 7 "No remote found" +fi +if [[ $repo_url == *github.com/* ]] +then + owner_and_repo=${repo_url#*github.com/} +else + owner_and_repo=${repo_url##*:} +fi +# Get rid of the trailing .git +service=$(basename "$owner_and_repo" .git) +owner=$(dirname "$owner_and_repo") + +full_tag=$IMAGE_NAME:$tag +created=$(date --utc --iso-8601=seconds 2>/dev/null || gdate --utc --iso-8601=seconds) +# Pass any extra arguments to the build command ("$@" contains the rest of the arguments) +$runtime build --tag "$full_tag" "$@" \ + --label org.opencontainers.image.created="$created" \ + --label org.opencontainers.image.description="Image for $service" \ + --label org.opencontainers.image.licenses="$LICENSE" \ + --label org.opencontainers.image.revision="$revision" \ + --label org.opencontainers.image.url="$repo_url" \ + --label org.opencontainers.image.title="$IMAGE_NAME" \ + --label org.opencontainers.image.source="Generated by ruby-automation's build_image.sh ($USER@$HOSTNAME)" \ + --label org.opencontainers.image.version="$full_tag" \ + --label shortref="$shortref" \ + --build-arg APP_VERSION="$APP_VERSION" \ + -f "$containerfile" "$BUILD_CONTEXT" || die 8 "Failed to build image" + +[ $push -eq 1 ] || exit 0 +if ! $runtime login --get-login "$REGISTRY" >/dev/null 2>/dev/null +then + printf "Not logged in to '%s', trying to login\n" "$REGISTRY" >&2 + [ -z "$REGISTRY_TOKEN" ] && die 9 "No REGISTRY_TOKEN (nor GITHUB_TOKEN) set, cannot login" + printf "%s" "$REGISTRY_TOKEN" | $runtime login -u "$REGISTRY_TOKEN" --password-stdin "$REGISTRY" || die 10 "Failed to login to $REGISTRY" +fi + +# Split 1.2.3 into 1.2.3, 1.2, 1. We want to tag our image with all 3 of these +mapfile -t tags < <(echo "$tag" | awk -F'.' 'NF==3{print; print $1"."$2; print $1; next} NF==2{print; print $1; next} {print}') +for t in "${tags[@]}" +do + new_tag=$IMAGE_NAME:$t + registry_image_name="$REGISTRY/$owner/$new_tag" + if [ "$runtime" = "podman" ] + then + if [ "$full_tag" != "$new_tag" ] + then + debug "Tagging %s as %s\n" "$full_tag" "$new_tag" + podman tag "$full_tag" "$new_tag" || die 11 "Failed to tag image $full_tag as $new_tag" + fi + podman push "$new_tag" "$registry_image_name" || die 12 "Failed to push image $new_tag to $registry_image_name" + else + debug "Tagging %s as %s\n" "$full_tag" "$registry_image_name" + docker tag "$full_tag" "$registry_image_name" || die 13 "Failed to tag image $full_tag as $registry_image_name" + docker push "$registry_image_name" || die 14 "Failed to push image $new_tag to $registry_image_name" + fi +done + +# vim: set foldmethod=marker et ts=4 sts=4 sw=4 ft=bash : diff --git a/ci/nats/accounts.txt b/ci/nats/accounts.txt new file mode 100644 index 0000000..793bb1d --- /dev/null +++ b/ci/nats/accounts.txt @@ -0,0 +1,27 @@ +# Client port of 4222 on all interfaces +port: 4222 + +# HTTP monitoring port +monitor_port: 8222 + +accounts: { + $SYS: { + users: [ + { user: sys, password: sys } + ] + } + ME: { + jetstream: enabled + users: [ + { user: me, password: youandme } + ] + } +} +no_auth_user: me + +authorization { + default_permissions = { + publish = ">" + subscribe = ">" + } +} diff --git a/ci/nats/start.sh b/ci/nats/start.sh new file mode 100755 index 0000000..0b237b4 --- /dev/null +++ b/ci/nats/start.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +NATS_VERSION=2 + +if readlink -f . >/dev/null 2>&1 # {{{ makes readlink work on mac +then + readlink=readlink +else + if greadlink -f . >/dev/null 2>&1 + then + readlink=greadlink + else + printf "You must install greadlink to use this (brew install coreutils)\n" >&2 + fi +fi # }}} + +# Set here to the full path to this script +me=${BASH_SOURCE[0]} +[ -L "$me" ] && me=$($readlink -f "$me") +here=$(cd "$(dirname "$me")" && pwd) +just_me=$(basename "$me") +export just_me + +cd "$here" || exit 1 +if command -v podman 2>/dev/null +then + runtime=podman +else + runtime=docker +fi + +set -x +exec "$runtime" run --rm -it -p 4222:4222 -p 6222:6222 -p 8222:8222 -v ./accounts.txt:/accounts.txt nats:"$NATS_VERSION" -js -c /accounts.txt "$@" diff --git a/ci/publish-gem.sh b/ci/publish-gem.sh new file mode 100755 index 0000000..adee0db --- /dev/null +++ b/ci/publish-gem.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash + +if readlink -f . >/dev/null 2>&1 # {{{ makes readlink work on mac +then + readlink=readlink +else + if greadlink -f . >/dev/null 2>&1 + then + readlink=greadlink + else + printf "You must install greadlink to use this (brew install coreutils)\n" >&2 + fi +fi # }}} + +# Set here to the full path to this script +me=${BASH_SOURCE[0]} +[ -L "$me" ] && me=$($readlink -f "$me") +here=$(cd "$(dirname "$me")" && pwd) +root=$(cd "$here/.." && pwd) +just_me=$(basename "$me") + +: "${GEM_NAME:=leopard}" +: "${GIT_ORG:=rubyists}" + +GEM_HOST=$1 +: "${GEM_HOST:=rubygems}" + +case "$GEM_HOST" in + rubygems) + gem_key='rubygems' + gem_host='https://rubygems.org' + ;; + github) + gem_key='github' + gem_host="https://rubygems.pkg.github.com/$GIT_ORG" + # Replace the gem host in the gemspec, so it allows pushing to the GitHub package registry + sed --in-place=.bak -e "s|https://rubygems.org|https://rubygems.pkg.github.com/$GIT_ORG|" "$here/../$GEM_NAME".gemspec + # Restore the original gemspec after the script finishes + trap 'mv -v "$here/../$GEM_NAME".gemspec.bak "$here/../$GEM_NAME".gemspec' EXIT + ;; + *) + printf 'Unknown GEM_HOST: %s\n' "$GEM_HOST" >&2 + exit 1 + ;; +esac + +# We only want this part running in CI, with no ~/.gem dir +# For local testing, you should have a ~/.gem/credentials file with +# the keys you need to push to rubygems or github +if [ ! -d ~/.gem ] +then + if [ -z "$GEM_TOKEN" ] + then + printf 'No GEM_TOKEN provided, cannot publish\n' >&2 + exit 1 + fi + mkdir -p ~/.gem + printf '%s\n:%s: %s\n' '---' "$gem_key" "$GEM_TOKEN" > ~/.gem/credentials + chmod 600 ~/.gem/credentials +fi + +bundle exec gem build + +if [ -f "$here"/../.version.txt ] +then + version=$(<"$here"/../.version.txt) +else + version=$(git describe --tags --abbrev=0 | sed -e 's/^v//') +fi + +if [ -z "$version" ] +then + gem="$(ls "$root"/"$GEM_NAME"-*.gem | tail -1)" +else + gem="$(printf '%s/%s-%s.gem' "$root" "$GEM_NAME" "$version")" +fi + +if [ ! -f "$gem" ] +then + printf 'No gem file found: %s\n' "$gem" >&2 + exit 1 +fi + +if [[ "${TRACE:-false}" == true || "${ACTIONS_STEP_DEBUG:-false}" == true ]] +then + printf "DEBUG: [%s] Building And Publishing %s to %s\n" "$just_me" "$gem" "$gem_host" >&2 +fi + +bundle exec gem push -k "$gem_key" --host "$gem_host" "$(basename "$gem")" + +# vim: set foldmethod=marker et ts=4 sts=4 sw=4 ft=bash : diff --git a/lib/linear/cli/version.rb b/lib/linear/cli/version.rb index dfc09f7..6458685 100644 --- a/lib/linear/cli/version.rb +++ b/lib/linear/cli/version.rb @@ -2,6 +2,8 @@ module Rubyists module Linear - VERSION = '0.9.20' + # x-release-please-start-version + VERSION = '1.0.0' + # x-release-please-end end end diff --git a/linear-cli.gemspec b/linear-cli.gemspec index 9133208..dcccac0 100644 --- a/linear-cli.gemspec +++ b/linear-cli.gemspec @@ -25,8 +25,8 @@ Gem::Specification.new do |spec| spec.files = Dir.chdir(__dir__) do `git ls-files -z`.split("\x0").reject do |f| (File.expand_path(f) == __FILE__) || - f.start_with?(*%w[changelog/ pkg/ bin/ test/ spec/ features/ .git .github appveyor .rspec .rubocop cucumber.yml - Gemfile]) + f.start_with?(*%w[changelog/ pkg/ oci/ ci/ bin/ test/ spec/ features/ .git .github appveyor + .rspec .rubocop cucumber.yml Gemfile]) end end spec.bindir = 'exe' diff --git a/oci/Containerfile b/oci/Containerfile index 762d860..c619d3a 100644 --- a/oci/Containerfile +++ b/oci/Containerfile @@ -1,5 +1,4 @@ -FROM docker.io/ruby:3.3.0-alpine3.18 AS build-env - +FROM docker.io/ruby:3-alpine AS build-env # Setting env up ARG APP_ROOT=/app @@ -14,6 +13,7 @@ RUN apk --update add bash ruby-dev build-base git sqlite-dev WORKDIR $APP_ROOT COPY . $APP_ROOT + RUN gem i semantic_logger && \ bundle config set build.nokogiri --use-system-libraries && \ bundle config set without 'test assets' && \ @@ -21,7 +21,7 @@ RUN gem i semantic_logger && \ bundle exec rake build ############### Build step done ############### -FROM docker.io/ruby:3.3.0-alpine3.18 +FROM docker.io/ruby:3-alpine ARG PACKAGES="bash sqlite github-cli openssh" ARG APP_VERSION ARG APP_ROOT=/app @@ -31,7 +31,7 @@ RUN apk --update --no-cache add $PACKAGES COPY --from=build-env /app /app COPY --from=build-env /usr/local/bundle /usr/local/bundle WORKDIR $APP_ROOT -RUN bundle exec gem i /app/pkg/linear-cli-${APP_VERSION}.gem +RUN echo "App version is ${APP_VERSION}" && bundle exec gem i /app/pkg/linear-cli-${APP_VERSION}.gem CMD ["lc"] diff --git a/oci/Gemfile b/oci/Gemfile new file mode 100644 index 0000000..8e0186f --- /dev/null +++ b/oci/Gemfile @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +# x-release-please-start-version +gem 'linear-cli', '= 1.0.0' +# x-release-please-end diff --git a/oci/build_image b/oci/build_image deleted file mode 100755 index 0a0fdcf..0000000 --- a/oci/build_image +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env bash - -: "${IMAGE_NAME:=$(basename "$(pwd)")}" -: "${BUILD_CONTEXT:=$(pwd)}" - -usage() { # {{{ - cat <<-EOT - Usage: $0 - Options: - -i NAME Name of the image (default: $IMAGE_NAME) - -c CONTAINERFILE Path to the containerfile (default: ./oci/Containerfile) - -C CONTEXT Build context (default: $BUILD_CONTEXT) - -p Push the image to the registry - -s Also mark the image as stable - -h Show help / usage -EOT -} # }}} - -die() { # {{{ - local -i code - code=$1 - shift - printf "Error! => %s\n" "$*" >&2 - printf "\n" >&2 - usage >&2 - # shellcheck disable=SC2086 - exit $code -} # }}} - -push=0 -stable=0 -while getopts :hpi:c:C:s opt # {{{ -do - case $opt in - c) - CONTAINERFILE=$OPTARG - ;; - C) - BUILD_CONTEXT=$OPTARG - ;; - i) - IMAGE_NAME=$OPTARG - ;; - p) - push=1 - ;; - s) - stable=1 - ;; - h) - usage - exit - ;; - :) - printf "Option %s requires an argument\n" "$OPTARG" >&2 - usage >&2 - exit 28 - ;; - ?) - printf "Invalid option '%s'\n" "$OPTARG" >&2 - usage >&2 - exit 27 - ;; - esac -done # }}} -shift $((OPTIND-1)) - -tag=$1 -[ -z "$tag" ] && die 1 "Missing image tag" -shift - -[ $# -gt 0 ] && die 2 "Too many arguments" - -if [ -z "$CONTAINERFILE" ]; then - printf "No containerfile specified, looking for default locations\n" - for containerfile in Containerfile Dockerfile - do - if [ -f ./oci/"$containerfile" ]; then - printf "Found ./oci/%s\n" "$containerfile" >&2 - containerfile=./oci/"$containerfile" - break - fi - if [ -f "$containerfile" ]; then - printf "Found %s\n" "$containerfile" >&2 - break - fi - done -else - [ -f "$CONTAINERFILE" ] || die 3 "Containerfile '$CONTAINERFILE' not found" - printf "Using containerfile %s\n" "$CONTAINERFILE" >&2 - containerfile=$CONTAINERFILE -fi - -[ -f "$containerfile" ] || die 4 "No containerfile found" - -[ -d "$BUILD_CONTEXT" ] || die 5 "Build context '$BUILD_CONTEXT' not found" - -# Build the image -if command -v podman 2>/dev/null -then - runtime=podman -elif command -v docker 2>/dev/null -then - runtime=docker -else - die 6 "No container runtime found" -fi - -app_version=$(bundle exec lc version) -$runtime build --build-arg "APP_VERSION=${app_version}" -t "$IMAGE_NAME:$tag" -f "$containerfile" "$BUILD_CONTEXT" || die 7 "Failed to build image" - -set -x -[ $push -eq 1 ] || exit 0 -# push the image -registry_image_name="ghcr.io/rubyists/$IMAGE_NAME:$tag" -if ! $runtime login --get-login ghcr.io -then - printf "Not logged in to ghcr.io, trying to login\n" >&2 - [ -z "$GITHUB_TOKEN" ] && die 8 "No GITHUB_TOKEN set, cannot login" - printf "%s" "$GITHUB_TOKEN" | $runtime login -u "$GITHUB_TOKEN" --password-stdin ghcr.io || die 9 "Failed to login to ghcr.io" -fi - -if [ "$runtime" = "podman" ] -then - podman push "$IMAGE_NAME:$tag" "$registry_image_name" || die 10 "Failed to push image" -else - docker push "$registry_image_name" || die 11 "Failed to push image" -fi - -[ "$stable" -eq 0 ] && exit 0 -# Mark the image as stable -registry_stable_name="ghcr.io/rubyists/$IMAGE_NAME:stable" -if [ "$runtime" = "podman" ] -then - podman push "$IMAGE_NAME:$tag" "$registry_stable_name" || die 10 "Failed to push image" -else - docker push "$registry_stable_name" || die 11 "Failed to push image" -fi