diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 03afdbf..063eab7 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,17 +1,20 @@ { - "name": "Alpine", - "build": { - "dockerfile": "Dockerfile" - }, - "remoteUser": "vscode", - "remoteEnv": { - "PATH": "${containerEnv:PATH}:/workspaces/lane" - }, - "customizations": { - "vscode": { - "extensions": [ - "EditorConfig.EditorConfig" - ] - } - } + "name": "Alpine", + "build": { + "dockerfile": "Dockerfile" + }, + "remoteUser": "vscode", + "remoteEnv": { + "PATH": "${containerEnv:PATH}:/workspaces/lane" + }, + "customizations": { + "vscode": { + "extensions": [ + "EditorConfig.EditorConfig", + "foxundermoon.shell-format", + "redhat.vscode-yaml", + "ms-azuretools.vscode-docker" + ] + } + } } diff --git a/.editorconfig b/.editorconfig index 7a36a04..fb90264 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,11 +2,20 @@ root = true [*] indent_style = space -indent_size = 2 +indent_size = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true +[*.sh] +indent_size = 2 + +[*.yaml] +indent_size = 2 + +[lane] +indent_size = 2 + [version] insert_final_newline = false diff --git a/.github/test-mocks/.editorconfig b/.github/test-mocks/.editorconfig new file mode 100644 index 0000000..b429316 --- /dev/null +++ b/.github/test-mocks/.editorconfig @@ -0,0 +1,2 @@ +[*] +indent_size = 2 diff --git a/.github/test-mocks/curl/curl b/.github/test-mocks/curl/curl index 809f827..89b4b31 100755 --- a/.github/test-mocks/curl/curl +++ b/.github/test-mocks/curl/curl @@ -1,13 +1,19 @@ #!/bin/sh -[ "$CURL_OVERRIDE" = "fail" ] && exit 22; -[ -f "$CURL_OVERRIDE" ] || { echo "CURL_OVERRIDE is not a file: $CURL_OVERRIDE"; exit 1; } +[ "$CURL_OVERRIDE" = "fail" ] && exit 22 +[ -f "$CURL_OVERRIDE" ] || { + echo "CURL_OVERRIDE is not a file: $CURL_OVERRIDE" + exit 1 +} while getopts ":o:" OPTION; do - case "$OPTION" in - o) cat "$CURL_OVERRIDE" > "$OPTARG"; exit 0 ;; - *) ;; - esac + case "$OPTION" in + o) + cat "$CURL_OVERRIDE" >"$OPTARG" + exit 0 + ;; + *) ;; + esac done cat "$CURL_OVERRIDE" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ec411c0..8bf94e3 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -26,7 +26,7 @@ jobs: MAIN=$(cat branch-main/version) VERSION=$(cat version) - sh lane.d/shell-github-action-semver-compare -m "$MAIN" -c "$VERSION" + sh lane.d/shell-github-action-semver-compare/run.sh -m "$MAIN" -c "$VERSION" - name: Output version id: version diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index ec4c155..b967ca6 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -1,4 +1,3 @@ - name: Tests on: @@ -86,8 +85,8 @@ jobs: - name: Test builtin lane run: | rm -rf lanes - mkdir lanes - echo 'echo hi' > lane.d/say-hi + mkdir lane.d/say-hi + echo 'echo hi' > lane.d/say-hi/run.sh set +e sh ./lane say-hi > result @@ -99,8 +98,10 @@ jobs: run: | rm -rf lanes mkdir lanes - echo 'echo false' > lanes/say - echo 'echo true' > lane.d/say + mkdir lanes/say + mkdir lane.d/say + echo 'echo false' > lanes/say/run.sh + echo 'echo true' > lane.d/say/run.sh set +e sh ./lane say > result @@ -108,6 +109,30 @@ jobs: echo 'true' > expected diff -q expected result || { echo "Unexpected difference:"; diff expected result; exit 1; } + lane-d-requirements: + name: Test lane.d Requirements + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Validate builtins has an entry point + run: | + find lane.d -mindepth 1 -maxdepth 1 -type d -exec sh -c 'test -f "$1/run.sh" || echo "$1"' -- {} \; > missing + count=$(wc -l missing | cut -d\ -f1) + [ $count -eq 0 ] || { cat missing; exit 1; } + + - name: Validate builtins has options descriptions + run: | + find lane.d -mindepth 1 -maxdepth 1 -type d -exec sh -c 'test -f "$1/options.md" || echo "$1"' -- {} \; > missing + count=$(wc -l missing | cut -d\ -f1) + [ $count -eq 0 ] || { cat missing; exit 1; } + + - name: Validate builtins has help available + run: | + find lane.d -mindepth 1 -maxdepth 1 -type d -exec sh -c 'test -f "$1/help.md" || echo "$1"' -- {} \; > missing + count=$(wc -l missing | cut -d\ -f1) + [ $count -eq 0 ] || { cat missing; exit 1; } + google-api-docs-sheets-download: name: Test Google API Docs Sheets Download runs-on: ubuntu-latest @@ -117,26 +142,26 @@ jobs: - name: Test missing arguments run: | set +e - sh lane.d/google-api-docs-sheets-download > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/google-api-docs-sheets-download/run.sh > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test missing output file run: | set +e - sh lane.d/google-api-docs-sheets-download -t a-token -i an-id > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/google-api-docs-sheets-download/run.sh -t a-token -i an-id > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test missing document id run: | set +e - sh lane.d/google-api-docs-sheets-download -t a-token -o result > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/google-api-docs-sheets-download/run.sh -t a-token -o result > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test missing token run: | set +e - sh lane.d/google-api-docs-sheets-download -i an-id -o result > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/google-api-docs-sheets-download/run.sh -i an-id -o result > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test missing curl command run: | @@ -145,7 +170,7 @@ jobs: chmod -x .github/test-mocks/curl/curl set +e - sh lane.d/google-api-docs-sheets-download -t a-token -i an-id -o result > /dev/null + sh lane.d/google-api-docs-sheets-download/run.sh -t a-token -i an-id -o result > /dev/null [ $? -eq 3 ] || exit 1 - name: Test google api curl error @@ -154,7 +179,7 @@ jobs: export CURL_OVERRIDE=fail set +e - sh lane.d/google-api-docs-sheets-download -t a-token -i an-id -o result + sh lane.d/google-api-docs-sheets-download/run.sh -t a-token -i an-id -o result [ $? -eq 22 ] || exit 1 - name: Test generated output matches expectation @@ -166,7 +191,7 @@ jobs: export CURL_OVERRIDE=expected set +e - sh lane.d/google-api-docs-sheets-download -t a-token -i an-id -o result + sh lane.d/google-api-docs-sheets-download/run.sh -t a-token -i an-id -o result [ $? -eq 0 ] || exit 1 diff -q expected result > /dev/null || { echo "Unexpected difference:"; diff expected result; exit 1; } @@ -178,7 +203,7 @@ jobs: export CURL_OVERRIDE=expected set +e - sh lane.d/google-api-docs-sheets-download -t a-token -i an-id -o will/be/created + sh lane.d/google-api-docs-sheets-download/run.sh -t a-token -i an-id -o will/be/created [ $? -eq 0 ] || exit 1 [ -d "./will/be" ] || { echo "Output folder was not created"; exit 1; } @@ -197,31 +222,31 @@ jobs: - name: Test missing arguments run: | set +e - sh lane.d/google-api-jwt-generate > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/google-api-jwt-generate/run.sh > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test missing issuer run: | set +e - sh lane.d/google-api-jwt-generate -s 'scope-a scope-b' -p key.p12 > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/google-api-jwt-generate/run.sh -s 'scope-a scope-b' -p key.p12 > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test missing scopes run: | set +e - sh lane.d/google-api-jwt-generate -i an-issuer -p key.p12 > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/google-api-jwt-generate/run.sh -i an-issuer -p key.p12 > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test missing p12 file run: | set +e - sh lane.d/google-api-jwt-generate -i an-issuer -s 'scope-a scope-b' > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/google-api-jwt-generate/run.sh -i an-issuer -s 'scope-a scope-b' > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test non-existing p12 file run: | set +e - sh lane.d/google-api-jwt-generate -i an-issuer -s 'scope-a scope-b' -p not-a-key.p12 > /dev/null + sh lane.d/google-api-jwt-generate/run.sh -i an-issuer -s 'scope-a scope-b' -p not-a-key.p12 > /dev/null [ $? -eq 4 ] || exit 1 - name: Test google api curl error @@ -230,7 +255,7 @@ jobs: export CURL_OVERRIDE=fail set +e - sh lane.d/google-api-jwt-generate -i an-issuer -s 'scope-a scope-b' -p key.p12 > result + sh lane.d/google-api-jwt-generate/run.sh -i an-issuer -s 'scope-a scope-b' -p key.p12 > result [ $? -eq 22 ] || exit 1 - name: Test generating token @@ -242,7 +267,7 @@ jobs: export CURL_OVERRIDE=override set +e - sh lane.d/google-api-jwt-generate -i an-issuer -s 'scope-a scope-b' -p key.p12 > result + sh lane.d/google-api-jwt-generate/run.sh -i an-issuer -s 'scope-a scope-b' -p key.p12 > result [ $? -eq 0 ] || exit 1 diff -q expected result > /dev/null || { echo "Unexpected difference:"; diff expected result; exit 1; } @@ -259,45 +284,45 @@ jobs: - name: Test missing arguments run: | set +e - sh lane.d/mobile-static-resources-images > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/mobile-static-resources-images/run.sh > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test missing input file run: | set +e - sh lane.d/mobile-static-resources-images -o result > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/mobile-static-resources-images/run.sh -o result > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test missing output file run: | set +e - sh lane.d/mobile-static-resources-images -i input > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/mobile-static-resources-images/run.sh -i input > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test non-existing input folder run: | set +e - sh lane.d/mobile-static-resources-images -i ./non-existing-folder -o result > /dev/null + sh lane.d/mobile-static-resources-images/run.sh -i ./non-existing-folder -o result > /dev/null [ $? -eq 3 ] || exit 1 - name: Test empty asset folder run: | set +e - sh lane.d/mobile-static-resources-images -i empty-folder -o result + sh lane.d/mobile-static-resources-images/run.sh -i empty-folder -o result [ $? -eq 0 ] || exit 1 diff -q .github/test-resources/mobile-static-resources-images/empty.swift result > /dev/null || { echo "Unexpected difference:"; diff .github/test-resources/mobile-static-resources-images/empty.swift result; exit 1; } - name: Test with asset folder run: | set +e - sh lane.d/mobile-static-resources-images -i .github/test-resources/mobile-static-resources-images/assets -o result + sh lane.d/mobile-static-resources-images/run.sh -i .github/test-resources/mobile-static-resources-images/assets -o result [ $? -eq 0 ] || exit 1 diff -q .github/test-resources/mobile-static-resources-images/assets.swift result > /dev/null || { echo "Unexpected difference:"; diff .github/test-resources/mobile-static-resources-images/assets.swift result; exit 1; } - name: Test creating output folder run: | set +e - sh lane.d/mobile-static-resources-images -i empty-folder -o will/be/created + sh lane.d/mobile-static-resources-images/run.sh -i empty-folder -o will/be/created [ $? -eq 0 ] || exit 1 [ -d "./will/be" ] || { echo "Output folder was not created"; exit 1; } @@ -310,43 +335,43 @@ jobs: - name: Test missing argument run: | set +e - sh lane.d/mobile-update-translations > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/mobile-update-translations/run.sh > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test missing configuration for missing input run: | set +e - sh lane.d/mobile-update-translations -t ios -k 1 -m EN -o result.swift -c "4 DA result.DA.strings" -c "3 EN result.EN.strings" - [ $? -eq 2 ] || exit 1 + sh lane.d/mobile-update-translations/run.sh -t ios -k 1 -m 3 -o result.swift -c "4 result.DA.strings" -c "3 result.EN.strings" + [ $? -eq 111 ] || exit 1 - name: Test missing configuration for input not a file run: | set +e - sh lane.d/mobile-update-translations -t ios -k 1 -m EN -o result.swift -c "4 DA result.DA.strings" -c "3 EN result.EN.strings" -i .github/test-resources/ - [ $? -eq 2 ] || exit 1 + sh lane.d/mobile-update-translations/run.sh -t ios -k 1 -m 3 -o result.swift -c "4 result.DA.strings" -c "3 result.EN.strings" -i .github/test-resources/ + [ $? -eq 111 ] || exit 1 - name: Test missing configuration for missing main language run: | set +e - sh lane.d/mobile-update-translations -t ios -k 1 -o result.swift -c "4 DA result.DA.strings" -c "3 EN result.EN.strings" -i .github/test-resources/mobile-update-translations/configuration/input.csv - [ $? -eq 2 ] || exit 1 + sh lane.d/mobile-update-translations/run.sh -t ios -k 1 -o result.swift -c "4 result.DA.strings" -c "3 result.EN.strings" -i .github/test-resources/mobile-update-translations/configuration/input.csv + [ $? -eq 111 ] || exit 1 - name: Test missing configuration for missing key row run: | set +e - sh lane.d/mobile-update-translations -t ios -m EN -o result.swift -c "4 DA result.DA.strings" -c "3 EN result.EN.strings" -i .github/test-resources/mobile-update-translations/configuration/input.csv - [ $? -eq 2 ] || exit 1 + sh lane.d/mobile-update-translations/run.sh -t ios -m 3 -o result.swift -c "4 result.DA.strings" -c "3 result.EN.strings" -i .github/test-resources/mobile-update-translations/configuration/input.csv + [ $? -eq 111 ] || exit 1 - name: Test missing configuration for missing output run: | set +e - sh lane.d/mobile-update-translations -t ios -k 1 -m EN -c "4 DA result.DA.strings" -c "3 EN result.EN.strings" -i .github/test-resources/mobile-update-translations/configuration/input.csv - [ $? -eq 2 ] || exit 1 + sh lane.d/mobile-update-translations/run.sh -t ios -k 1 -m 3 -c "4 result.DA.strings" -c "3 result.EN.strings" -i .github/test-resources/mobile-update-translations/configuration/input.csv + [ $? -eq 111 ] || exit 1 - name: Test ios output is created as expected run: | set +e - sh lane.d/mobile-update-translations -t ios -k 1 -m EN -o result.swift -c "4 DA result.DA.strings" -c "3 EN result.EN.strings" -i .github/test-resources/mobile-update-translations/configuration/input.csv + sh lane.d/mobile-update-translations/run.sh -t ios -k 1 -m 3 -o result.swift -c "4 result.DA.strings" -c "3 result.EN.strings" -i .github/test-resources/mobile-update-translations/configuration/input.csv [ $? -eq 0 ] || exit 1 diff -q .github/test-resources/mobile-update-translations/expected-ios/result.swift result.swift > /dev/null || { echo "Unexpected difference:"; diff .github/test-resources/mobile-update-translations/expected-ios/result.swift result.swift; exit 1; } diff -q .github/test-resources/mobile-update-translations/expected-ios/result.EN.strings result.EN.strings > /dev/null || { echo "Unexpected difference:"; diff .github/test-resources/mobile-update-translations/expected-ios/result.EN.strings result.EN.strings; exit 1; } @@ -355,7 +380,7 @@ jobs: - name: Test android output is created as expected run: | set +e - sh lane.d/mobile-update-translations -t android -k 1 -c "4 DA result.DA.strings" -c "3 EN result.EN.strings" -i .github/test-resources/mobile-update-translations/configuration/input.csv + sh lane.d/mobile-update-translations/run.sh -t android -k 1 -c "4 result.DA.strings" -c "3 result.EN.strings" -i .github/test-resources/mobile-update-translations/configuration/input.csv [ $? -eq 0 ] || exit 1 diff -q .github/test-resources/mobile-update-translations/expected-android/result.EN.strings result.EN.strings > /dev/null || { echo "Unexpected difference:"; diff .github/test-resources/mobile-update-translations/expected-android/result.EN.strings result.EN.strings; exit 1; } diff -q .github/test-resources/mobile-update-translations/expected-android/result.DA.strings result.DA.strings > /dev/null || { echo "Unexpected difference:"; diff .github/test-resources/mobile-update-translations/expected-android/result.DA.strings result.DA.strings; exit 1; } @@ -370,11 +395,11 @@ jobs: - name: Test non-existing file run: | set +e - sh lane.d/shell-run-github-workflow-tests -i ./non-existing-file > /dev/null - [ $? -eq 1 ] || exit 1 + sh lane.d/shell-run-github-workflow-tests/run.sh -i ./non-existing-file > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test running tests run: | - sh lane.d/shell-run-github-workflow-tests -i .github/test-resources/shell-run-github-workflow-tests/test.yaml + sh lane.d/shell-run-github-workflow-tests/run.sh -i .github/test-resources/shell-run-github-workflow-tests/test.yaml shell-github-action-semver-compare: name: Test Shell Github Action Semver Compare @@ -385,59 +410,68 @@ jobs: - name: Test missing arguments run: | set +e - sh lane.d/shell-github-action-semver-compare > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/shell-github-action-semver-compare/run.sh > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test missing main version argument run: | set +e - sh lane.d/shell-github-action-semver-compare -c "0.0.0" > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/shell-github-action-semver-compare/run.sh -c "0.0.0" > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test missing current version argument run: | set +e - sh lane.d/shell-github-action-semver-compare -m "0.0.0" > /dev/null - [ $? -eq 2 ] || exit 1 + sh lane.d/shell-github-action-semver-compare/run.sh -m "0.0.0" > /dev/null + [ $? -eq 111 ] || exit 1 - name: Test matching versions run: | set +e - sh lane.d/shell-github-action-semver-compare -m "0.0.0" -c "0.0.0" > /dev/null + sh lane.d/shell-github-action-semver-compare/run.sh -m "0.0.0" -c "0.0.0" > /dev/null [ $? -eq 10 ] || exit 1 - name: Test descending versions run: | set +e - sh lane.d/shell-github-action-semver-compare -m "1.2.3" -c "0.0.0" > /dev/null + sh lane.d/shell-github-action-semver-compare/run.sh -m "1.2.3" -c "0.0.0" > /dev/null [ $? -eq 20 ] || exit 1 - name: Test ascending versions run: | set +e - sh lane.d/shell-github-action-semver-compare -m "1.2.3" -c "3.2.1" > /dev/null + sh lane.d/shell-github-action-semver-compare/run.sh -m "1.2.3" -c "3.2.1" > /dev/null [ $? -eq 0 ] || exit 1 shellcheck: name: Shellcheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Make files executable for shellcheck action to check them - run: | - chmod +x lane - find lane.d -name "run.sh" -type f -exec chmod +x {} \; + - name: Make files executable for shellcheck action to check them + run: | + chmod +x lane + find lane.d -name "run.sh" -type f -exec chmod +x {} \; - - name: Run ShellCheck - uses: ludeeus/action-shellcheck@master - with: - scandir: './lane.d' - additional_files: 'lane' + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + scandir: "./lane.d" + additional_files: "lane" tests-succeeded: name: Tests Succeeded - needs: [lane, mobile-static-resources-images, mobile-update-translations, shell-github-action-semver-compare, shellcheck] + needs: + - lane + - lane-d-requirements + - google-api-docs-sheets-download + - google-api-jwt-generate + - mobile-static-resources-images + - mobile-update-translations + - shell-github-action-semver-compare + - shellcheck + runs-on: ubuntu-latest steps: - name: All clear diff --git a/.vscode/extensions.json b/.vscode/extensions.json index ae7c2f6..8037443 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,8 @@ { - "recommendations": [ - "EditorConfig.EditorConfig" - ] + "recommendations": [ + "editorconfig.editorconfig", + "foxundermoon.shell-format", + "redhat.vscode-yaml", + "ms-azuretools.vscode-docker" + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ad92582 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.formatOnSave": true +} diff --git a/README.md b/README.md index 16ef1c3..60ec34e 100644 --- a/README.md +++ b/README.md @@ -1 +1,22 @@ # lane + +`lane` is a task automation helper. + +## Manual/help + +[lane](lane.d/help.md) + +### Google APIs + +- [lane google-api-docs-sheets-download](lane.d/google-api-docs-sheets-download/help.md) +- [lane google-api-jwt-generate](lane.d/google-api-jwt-generate/help.md) + +### Mobile + +- [lane mobile-static-resources-images](lane.d/mobile-static-resources-images/help.md) +- [lane mobile-update-translations](lane.d/mobile-update-translations/help.md) + +### Shell + +- [lane shell-github-action-semver-compare](lane.d/shell-github-action-semver-compare/help.md) +- [lane shell-run-github-workflow-tests](lane.d/shell-run-github-workflow-tests/help.md) diff --git a/lane b/lane index 09207e0..138bc3b 100755 --- a/lane +++ b/lane @@ -1,57 +1,67 @@ #!/bin/sh OLD_PWD=$(pwd) -LANED_PWD="${0}.d" +LANED_PWD=$(realpath "${0}.d") trap 'set +x; cd "$OLD_PWD" >/dev/null 2>&1; rm -rf $TMP >/dev/null 2>&1' 0 trap 'exit 2' 1 2 3 15 -_DISPLAY_HELP() -{ - echo "$0: " - echo "Where the name of a lane indicates wich lane you wish to execute" -} - -_DISPLAY_VERSION() -{ - echo "Version: ${VERSION:-unreleased}" -} - -_DISPLAY_LANES() -{ - echo "The available lanes are:" - find lanes -type f -maxdepth 1 | sed 's|^lanes/||g;s|^| |g' | grep -vE '\.ini$' -} - if [ "$1" = "-h" ] || [ "$1" = "--help" ] || [ "$1" = "help" ]; then - _DISPLAY_HELP - exit 0 + grep -v '^```' /edit#gid=0 + + -t + A JWT, see `lane google-api-jwt-generate -h`. + + -f + The format of the output, defaults to csv. +``` diff --git a/lane.d/google-api-docs-sheets-download/options.md b/lane.d/google-api-docs-sheets-download/options.md new file mode 100644 index 0000000..faa16ca --- /dev/null +++ b/lane.d/google-api-docs-sheets-download/options.md @@ -0,0 +1,17 @@ +OPTIONS +``` + -h + Shows the full help. + + -o + A path to write the output to. + + -i + The document id of the sheet to download. Found in its url, e.g. https://docs.google.com/spreadsheets/d//edit#gid=0 + + -t + A JWT, see `lane google-api-jwt-generate -h`. + + -f + The format of the output, defaults to csv. +``` diff --git a/lane.d/google-api-docs-sheets-download b/lane.d/google-api-docs-sheets-download/run.sh similarity index 54% rename from lane.d/google-api-docs-sheets-download rename to lane.d/google-api-docs-sheets-download/run.sh index af14fd7..f2f86e6 100644 --- a/lane.d/google-api-docs-sheets-download +++ b/lane.d/google-api-docs-sheets-download/run.sh @@ -5,42 +5,22 @@ if [ ! -x "$(command -v curl)" ]; then exit 3 fi -usage() { -cat << END -OPTIONS - -h show this usage - -o output path - -i document id - -t bearer token, see google-api-jwt-generate - -f format, defaults to csv -END -} - unset -v output id token format='csv' -while getopts "hi:t:f:o:" option; do +while getopts "i:t:f:o:" option; do case $option in - o) output="$OPTARG" ;; - i) id="$OPTARG" ;; - t) token="$OPTARG" ;; - f) format="$OPTARG" ;; - \?) - echo "unknown option: $option" - usage - exit 1 - ;; - h) - usage - exit 0 - ;; + o) output="$OPTARG" ;; + i) id="$OPTARG" ;; + t) token="$OPTARG" ;; + f) format="$OPTARG" ;; + \?) exit 111 ;; esac done shift $((OPTIND - 1)) if [ -z "$id" ] || [ -z "$token" ] || [ -z "$output" ]; then - echo "Must provide output path, document id and bearer token." - usage - exit 2 + echo "Must provide output path, document id and bearer token." + exit 111 fi DIR=$(mktemp -dq) diff --git a/lane.d/google-api-jwt-generate/help.md b/lane.d/google-api-jwt-generate/help.md new file mode 100644 index 0000000..4526726 --- /dev/null +++ b/lane.d/google-api-jwt-generate/help.md @@ -0,0 +1,41 @@ +NAME +``` + google-api-jwt-generate + - a lane action +``` + +SYNOPSIS +``` + -i string -s string -p file + -h +``` + +DESCRIPTION +``` + Constructs JWT generation request for Google APIs and outputs a JWT. + + The purpose is to faciliate token generation for other usages of the Google APIs. +``` + +OPTIONS +``` + -h + Shows the full help. + + -i + The issuer of the JWT, e.g. @.iam.gserviceaccount.com + + -s + The scope(s) applied to the JWT. Apply multiple scopes by space separating them. + + -p + A path to the .p12 file issued by Google. See authentication section. +``` + +AUTHENTICATION + +Authentication happens using a specially created .p12 file issued by Google which must match the issuer. + +You get this .p12 key by creating a "Service Account Key", which if you do not have a service account, requires you to first create a service account. + +Creating both an account and a key is explaining here: https://developers.google.com/identity/protocols/oauth2/service-account#creatinganaccount diff --git a/lane.d/google-api-jwt-generate/options.md b/lane.d/google-api-jwt-generate/options.md new file mode 100644 index 0000000..3536e5f --- /dev/null +++ b/lane.d/google-api-jwt-generate/options.md @@ -0,0 +1,15 @@ +OPTIONS +``` + -h + Shows the full help. + + -i + The issuer of the JWT, e.g. @.iam.gserviceaccount.com + + -s + The scope(s) applied to the JWT. Apply multiple scopes by space separating them. + + -p + A path to the .p12 file issued by Google. See authentication section in the full help. +``` + diff --git a/lane.d/google-api-jwt-generate b/lane.d/google-api-jwt-generate/run.sh similarity index 61% rename from lane.d/google-api-jwt-generate rename to lane.d/google-api-jwt-generate/run.sh index 310e41f..a538eb0 100644 --- a/lane.d/google-api-jwt-generate +++ b/lane.d/google-api-jwt-generate/run.sh @@ -7,46 +7,25 @@ for command in curl base64 openssl jq; do fi done -usage() { -cat << END -OPTIONS - -h show this usage - -i issuer - -s scope(s) - -p p12 file -More information about obtaining a p12 file at https://developers.google.com/identity/protocols/oauth2/service-account -END -} - unset -v issuer scopes p12 -while getopts "hi:s:p:" option; do +while getopts "i:s:p:" option; do case $option in - i) issuer="$OPTARG" ;; - s) scopes="$OPTARG" ;; - p) p12="$OPTARG" ;; - \?) - echo "unknown option: $option" - usage - exit 1 - ;; - h) - usage - exit 0 - ;; + i) issuer="$OPTARG" ;; + s) scopes="$OPTARG" ;; + p) p12="$OPTARG" ;; + \?) exit 111 ;; esac done shift $((OPTIND - 1)) if [ -z "$issuer" ] || [ -z "$scopes" ] || [ -z "$p12" ]; then - echo "Must provide issuer, scopes and an p12 file." - usage - exit 2 + echo "Must provide issuer, scopes and an p12 file." + exit 111 fi if [ ! -f "$p12" ]; then - echo "Must provide a p12 file." - usage - exit 4 + echo "Must provide a p12 file." + exit 4 fi DIR=$(mktemp -dq) @@ -72,11 +51,11 @@ claim=$(printf '{ encoded_header=$(echo "$header" | encode) encoded_claim=$(echo "$claim" | encode) -printf '%s.%s' "${encoded_header}" "${encoded_claim}" > "$DIR/request" -printf '%s.%s.' "${encoded_header}" "${encoded_claim}" > "$DIR/token" +printf '%s.%s' "${encoded_header}" "${encoded_claim}" >"$DIR/request" +printf '%s.%s.' "${encoded_header}" "${encoded_claim}" >"$DIR/token" openssl pkcs12 -in "$p12" -out "$DIR/key" -nocerts -nodes -passin pass:notasecret -openssl dgst -sha256 -sign "$DIR/key" -out - "$DIR/request" | encode >> "$DIR/token" +openssl dgst -sha256 -sign "$DIR/key" -out - "$DIR/request" | encode >>"$DIR/token" assertion=$(cat "$DIR/token") diff --git a/lane.d/help.md b/lane.d/help.md new file mode 100644 index 0000000..f714ea2 --- /dev/null +++ b/lane.d/help.md @@ -0,0 +1,33 @@ +NAME +``` + lane +``` + +SYNOPSIS +``` + [@] + -h | --help | help + -v | --version | version +``` + +DESCRIPTION +``` + `lane` is a task automation helper. + + You can organize tasks in lanes. A task is written as a shell script. + + A task named `test` must be saved in 'lanes/test'. You can call a task from another task. + + There are builtin tasks that can be used in your lanes or in a stand-alone command. + + `lane` will search the current directory and all parent directories until it finds a 'lanes' directory, where it will look for user created lanes. +``` + +OPTIONS +``` + -h | --help | help + Shows the full help. + + -v | --version | version + Shows the version of lane. +``` diff --git a/lane.d/mobile-static-resources-images b/lane.d/mobile-static-resources-images deleted file mode 100755 index 5e25b2b..0000000 --- a/lane.d/mobile-static-resources-images +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/sh - -trap 'set +x; 2>&1' 0 -trap 'exit 2' 1 2 3 15 - -_USAGE() { -cat << END -OPTIONS - -h show this usage - -i input directory - -o output file -END -} - -unset -v INPUT -unset -v OUTPUT -while getopts "hi:o:" option; do - case $option in - i) INPUT=$OPTARG ;; - o) OUTPUT=$OPTARG ;; - \?) - echo "unknown option: $option" - _USAGE - exit 1 - ;; - h) - _USAGE - exit 0 - ;; - esac -done -shift $((OPTIND - 1)) - -if [ -z "$INPUT" ] || [ -z "$OUTPUT" ]; then - printf 'Provide both input and output arguments.\n\n' - _USAGE - exit 2 -fi - -if [ ! -d "$INPUT" ]; then - printf 'Provide a valid input directory.\n\n' - _USAGE - exit 3 -fi - -OUTPUT_DIRECTORY=$(dirname "$OUTPUT") -mkdir -p "$OUTPUT_DIRECTORY" 2>/dev/null - -{ - echo '// swiftlint:disable all' - echo 'import UIKit' - echo 'struct Images {' - - find "$INPUT" -type d -iname "*.imageset" | while read -r ITEM; do - NAME=$(basename "$ITEM" .imageset) - SAFE_NAME=$(echo "$NAME" | sed 's/-/_/g;s/ /_/g') - printf "\tstatic let %s = UIImage(named:\"%s\")!\n" "$SAFE_NAME" "$NAME" - done - - echo '}' -} > "$OUTPUT" diff --git a/lane.d/mobile-static-resources-images/help.md b/lane.d/mobile-static-resources-images/help.md new file mode 100644 index 0000000..8735983 --- /dev/null +++ b/lane.d/mobile-static-resources-images/help.md @@ -0,0 +1,44 @@ +NAME +``` + mobile-static-resources-images + - a lane action +``` + +SYNOPSIS +``` + -i directory -o file + -h +``` + +DESCRIPTION +``` + Searches an assets directory for .imagesets and generates swift code. + + The purpose is to enable compilation checks for image references to embedded assets. +``` + +OPTIONS +``` + -h + Shows the full help. + + -i + A directory to search for .imageset in. + + -o + A path for the generated output file. +``` + +EXAMPLES + +If `Assets.xcassets` contains a single .imageset named test. + +The output using `-i Assets.xcassets -o -` would be: + +```swift + // swiftlint:disable all + import UIKit + struct Images { + static let test = UIImage(named:"test")! + } +``` diff --git a/lane.d/mobile-static-resources-images/options.md b/lane.d/mobile-static-resources-images/options.md new file mode 100644 index 0000000..05cf94b --- /dev/null +++ b/lane.d/mobile-static-resources-images/options.md @@ -0,0 +1,11 @@ +OPTIONS +``` + -h + Shows the full help. + + -i + A directory to search for .imageset in. + + -o + A path for the generated output file. +``` diff --git a/lane.d/mobile-static-resources-images/run.sh b/lane.d/mobile-static-resources-images/run.sh new file mode 100755 index 0000000..99aa142 --- /dev/null +++ b/lane.d/mobile-static-resources-images/run.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +unset -v input +unset -v output +while getopts "i:o:" option; do + case $option in + i) input=$OPTARG ;; + o) output=$OPTARG ;; + \?) exit 111 ;; + esac +done +shift $((OPTIND - 1)) + +if [ -z "$input" ] || [ -z "$output" ]; then + printf 'Provide both input and output arguments.\n\n' + exit 111 +fi + +if [ ! -d "$input" ]; then + printf 'Provide a valid input directory.\n\n' + exit 3 +fi + +parent=$(dirname "$output") +mkdir -p "$parent" 2>/dev/null + +{ + echo '// swiftlint:disable all' + echo 'import UIKit' + echo 'struct Images {' + + find "$input" -type d -iname "*.imageset" | while read -r item; do + name=$(basename "$item" .imageset) + safe_name=$(echo "$name" | sed 's/-/_/g;s/ /_/g') + printf "\tstatic let %s = UIImage(named:\"%s\")!\n" "$safe_name" "$name" + done + + echo '}' +} >"$output" diff --git a/lane.d/mobile-update-translations b/lane.d/mobile-update-translations deleted file mode 100755 index a0f4633..0000000 --- a/lane.d/mobile-update-translations +++ /dev/null @@ -1,162 +0,0 @@ -#!/bin/sh - -TMP=$(mktemp -dq) -trap 'set +x; rm -rf $TMP 2>/dev/null 2>&1' 0 -trap 'exit 2' 1 2 3 15 - -_USAGE() { -cat << END -OPTIONS - -h show this usage - -t type ios|android - -i input file - -c configuration - space separated string consisting of row, language and output path - -k key row - -m main language (for ios only) - -o output file (for ios only) -END -} - -unset -v TYPE -unset -v INPUT -unset -v KEY_ROW -unset -v MAIN_LANGUAGE -unset -v OUTPUT -unset -v BETTER -while getopts "ht:i:c:k:m:o:" option; do - case $option in - c) echo "$OPTARG" >> "$TMP/mapping" ;; - t) TYPE=$OPTARG ;; - i) INPUT=$OPTARG ;; - k) KEY_ROW=$OPTARG ;; - m) MAIN_LANGUAGE=$OPTARG ;; - o) OUTPUT=$OPTARG ;; - \?) - echo "unknown option: $option" - _USAGE - exit 1 - ;; - h) - _USAGE - exit 0 - ;; - esac -done -shift $((OPTIND - 1)) - -if [ ! "$TYPE" = "ios" ] && [ ! "$TYPE" = "android" ]; then - printf 'Provide ios or android as type.\n\n' - _USAGE - exit 2 -fi - -if [ "$TYPE" = "ios" ]; then - if [ -z "$MAIN_LANGUAGE" ]; then - printf 'Provide main language.\n\n' - _USAGE - exit 2 - fi - if [ -z "$OUTPUT" ]; then - printf 'Provide output file.\n\n' - _USAGE - exit 2 - fi -fi - -if [ ! -f "$INPUT" ]; then - printf 'Provide input file.\n\n' - _USAGE - exit 2 -fi - -if [ -z "$KEY_ROW" ]; then - printf 'Provide key row.\n\n' - _USAGE - exit 2 -fi - -_MKDIR() { - DIRECTORY=$(dirname "$1") - mkdir -p "$DIRECTORY" 2>/dev/null -} - -_UNPACK_CSV() { - while read -r ITEM; do - LANG=$(echo "$ITEM" | cut -d\ -f2) - OFFSET=$(echo "$ITEM" | cut -d\ -f1 | tr -d "[:blank:]") - tail +2 "./$INPUT" | grep -v ^$ | sed 's|\\;|\\\\\\|g' | cut -d\; -f"$KEY_ROW,$OFFSET" | sed 's|\\\\\\|;|g' | sort > "${TMP}/${LANG}.csv" - done < "${TMP}/mapping" -} - -_GENERATE_XML() { - while read -r ITEM; do - LANG=$(echo "$ITEM" | cut -d\ -f2) - FILE=$(echo "$ITEM" | cut -d\ -f3- | tr -d "[:blank:]") - - _MKDIR "$FILE" - - echo "" > "$FILE" - while read -r LINE; do - KEY=$(echo "$LINE" | cut -d\; -f1 | tr "[:upper:]" "[:lower:]") - VALUE=$(echo "$LINE" | cut -d\; -f2- | sed -E 's|(%[0-9]+)|\1$s|g') - printf "\t%s\n" "$KEY" "$VALUE" >> "$FILE" - done < "${TMP}/${LANG}.csv" - echo "" >> "$FILE" - - done < "${TMP}/mapping" -} - -_GENERATE_STRINGS() { - while read -r ITEM; do - LANG=$(echo "$ITEM" | cut -d\ -f2) - FILE=$(echo "$ITEM" | cut -d\ -f3- | tr -d "[:blank:]") - - _MKDIR "$FILE" - - printf "" > "$FILE" - while read -r LINE; do - KEY=$(echo "$LINE" | cut -d\; -f1) - VALUE=$(echo "$LINE" | cut -d\; -f2- | sed 's|\"|\\"|g') - echo "\"$KEY\" = \"$VALUE\";" >> "$FILE" - done < "${TMP}/${LANG}.csv" - - done < "${TMP}/mapping" -} - -_GENERATE_STRUCT() { - echo '// swiftlint:disable all' - echo 'import Foundation' - echo 'struct Translations {' - - while read -r ITEM; do - KEY=$(echo "$ITEM" | cut -d\; -f1) - VALUE=$(echo "$ITEM" | cut -d\; -f2-) - PARAMETERS=$(echo "$VALUE" | grep -o -E '%[0-9]+' | wc -l | tr -d ' \n') - - if [ "$PARAMETERS" = "0" ]; then - printf "\tstatic let %s = NSLocalizedString(\"%s\", comment: \"\")\n" "$KEY" "$KEY" - else - ARGUMENTS=$(for i in $(seq 1 "$PARAMETERS"); do printf "p%s: String, _ " "$i"; done | rev | cut -c5- | rev) - REPLACEMENTS=$(for i in $(seq 1 "$PARAMETERS"); do printf ".replacingOccurrences(of: \"%%%s\", with: p%s)" "$i" "$i"; done) - printf "\tstatic func %s(_ %s) -> String {" "$KEY" "$ARGUMENTS" - printf " return NSLocalizedString(\"%s\", comment: \"\")" "$KEY" - printf "%s" "$REPLACEMENTS" - echo " }" - fi - done < "${TMP}/${MAIN_LANGUAGE}.csv" - - echo '}' -} - -_UNPACK_CSV - -if [ "$TYPE" = "ios" ]; then - _GENERATE_STRINGS - - _MKDIR "$OUTPUT" - _GENERATE_STRUCT > "$OUTPUT" -fi - -if [ "$TYPE" = "android" ]; then - _GENERATE_XML -fi diff --git a/lane.d/mobile-update-translations/help.md b/lane.d/mobile-update-translations/help.md new file mode 100644 index 0000000..c1f551b --- /dev/null +++ b/lane.d/mobile-update-translations/help.md @@ -0,0 +1,91 @@ +NAME +``` + mobile-update-translations + - a lane action +``` + +SYNOPSIS +``` + -t android -i file -c configuration -k index + -t ios -i file -c configuration -k index -m index -o file + -h +``` + +DESCRIPTION +``` + Reads a CSV file and uses configuration strings to generate static resource files for android or ios. + + Each translated string can have '%'-style placeholders, however the number of placeholder for each translated language must be the same. + The placeholders in the generated output will always take a string as input. + + The purpose is to enable compilation checks for translated strings with an external source for the actual strings. +``` + +OPTIONS +``` + -h + Shows the full help. + + -t + The type of output to generate, valid options are 'ios' or 'android'. + + -i + A CSV file containing a key row and a row for each language. + + -k + The index of the key row. + + -c + A configuration string consisting of space separated row index and output path. Multiple configurations can be added. + + -m + Relevant for ios only. The index of the main/default language row. + + -o + Relevant for ios only. A path for the generated output. +``` + +EXAMPLES + +If the contents of `input.csv` is: + +```csv + KEY;UPDATE NEEDED;English;Danish;COMMENT + SOMETHING;;Something;Noget; + SOMETHING_WITH_ARGUMENTS;;Something with %1 and %2;Noget med %1 og %2; +``` + +Android +--- + +The output using `-t android -i input.csv -c '3 en.xml' -k 1` would be: + +```xml + + Something + Something with %1$s and %2$s + +``` + +iOS +--- + +The output using `-t ios -i input.csv -c '3 en.strings' -k 1 -m 3 -o translations.swift` would be: + +en.strings: + +```ini + "SOMETHING" = "Something"; + "SOMETHING_WITH_ARGUMENTS" = "Something with %1 and %2"; +``` + +translations.swift: + +```swift + // swiftlint:disable all + import Foundation + struct Translations { + static let SOMETHING = NSLocalizedString("SOMETHING", comment: "") + static func SOMETHING_WITH_ARGUMENTS(_ p1: String, _ p2: String) -> String { return NSLocalizedString("SOMETHING_WITH_ARGUMENTS", comment: "").replacingOccurrences(of: "%1", with: p1).replacingOccurrences(of: "%2", with: p2) } + } +``` diff --git a/lane.d/mobile-update-translations/options.md b/lane.d/mobile-update-translations/options.md new file mode 100644 index 0000000..1fcbda8 --- /dev/null +++ b/lane.d/mobile-update-translations/options.md @@ -0,0 +1,23 @@ +OPTIONS +``` + -h + Shows the full help. + + -t + The type of output to generate, valid options are 'ios' or 'android'. + + -i + A CSV file containing a key row and a row for each language. + + -k + The index of the key row. + + -c + A configuration string consisting of space separated row index and output path. Multiple configurations can be added. + + -m + Relevant for ios only. The index of the main/default language row. + + -o + Relevant for ios only. A path for the generated output. +``` diff --git a/lane.d/mobile-update-translations/run.sh b/lane.d/mobile-update-translations/run.sh new file mode 100755 index 0000000..29deb4a --- /dev/null +++ b/lane.d/mobile-update-translations/run.sh @@ -0,0 +1,120 @@ +#!/bin/sh + +TMP=$(mktemp -dq) +trap 'set +x; rm -rf $TMP 2>/dev/null 2>&1' 0 +trap 'exit 2' 1 2 3 15 + +unset -v type +unset -v input +unset -v key_row +unset -v main_language +unset -v output +while getopts "t:i:c:k:m:o:" option; do + case $option in + t) type=$OPTARG ;; + i) input=$OPTARG ;; + c) echo "$OPTARG" >>"$TMP/mapping" ;; + k) key_row=$OPTARG ;; + m) main_language=$OPTARG ;; + o) output=$OPTARG ;; + \?) exit 111 ;; + esac +done +shift $((OPTIND - 1)) + +if [ ! "$type" = "ios" ] && [ ! "$type" = "android" ]; then + printf 'Provide ios or android as type.\n\n' + exit 111 +fi + +if [ "$type" = "ios" ]; then + if [ -z "$main_language" ]; then + printf 'Provide main language.\n\n' + exit 111 + fi + if [ -z "$output" ]; then + printf 'Provide output file.\n\n' + exit 111 + fi +fi + +if [ ! -f "$input" ]; then + printf 'Provide input file.\n\n' + exit 111 +fi + +if [ -z "$key_row" ]; then + printf 'Provide key row.\n\n' + exit 111 +fi + +makedir() { + parent=$(dirname "$1") + mkdir -p "$parent" 2>/dev/null +} + +while read -r item; do + offset=$(echo "$item" | cut -d\ -f1 | tr -d "[:blank:]") + tail +2 "./$input" | grep -v ^$ | sed 's|\\;|\\\\\\|g' | cut -d\; -f"$key_row,$offset" | sed 's|\\\\\\|;|g' | sort >"${TMP}/${offset}.csv" +done <"${TMP}/mapping" + +if [ "$type" = "ios" ]; then + while read -r item; do + offset=$(echo "$item" | cut -d\ -f1 | tr -d "[:blank:]") + file=$(echo "$item" | cut -d\ -f2- | tr -d "[:blank:]") + + makedir "$file" + + printf "" >"$file" + while read -r line; do + key=$(echo "$line" | cut -d\; -f1) + value=$(echo "$line" | cut -d\; -f2- | sed 's|\"|\\"|g') + echo "\"$key\" = \"$value\";" >>"$file" + done <"${TMP}/${offset}.csv" + + done <"${TMP}/mapping" + + makedir "$output" + { + echo '// swiftlint:disable all' + echo 'import Foundation' + echo 'struct Translations {' + + while read -r item; do + key=$(echo "$item" | cut -d\; -f1) + value=$(echo "$item" | cut -d\; -f2-) + parameters=$(echo "$value" | grep -o -E '%[0-9]+' | wc -l | tr -d ' \n') + + if [ "$parameters" = "0" ]; then + printf "\tstatic let %s = NSLocalizedString(\"%s\", comment: \"\")\n" "$key" "$key" + else + arguments=$(for i in $(seq 1 "$parameters"); do printf "p%s: String, _ " "$i"; done | rev | cut -c5- | rev) + replacements=$(for i in $(seq 1 "$parameters"); do printf ".replacingOccurrences(of: \"%%%s\", with: p%s)" "$i" "$i"; done) + printf "\tstatic func %s(_ %s) -> String {" "$key" "$arguments" + printf " return NSLocalizedString(\"%s\", comment: \"\")" "$key" + printf "%s" "$replacements" + echo " }" + fi + done <"${TMP}/${main_language}.csv" + + echo '}' + } >"$output" +fi + +if [ "$type" = "android" ]; then + while read -r item; do + offset=$(echo "$item" | cut -d\ -f1 | tr -d "[:blank:]") + file=$(echo "$item" | cut -d\ -f2- | tr -d "[:blank:]") + + makedir "$file" + + echo "" >"$file" + while read -r line; do + key=$(echo "$line" | cut -d\; -f1 | tr "[:upper:]" "[:lower:]") + value=$(echo "$line" | cut -d\; -f2- | sed -E 's|(%[0-9]+)|\1$s|g') + printf "\t%s\n" "$key" "$value" >>"$file" + done <"${TMP}/${offset}.csv" + echo "" >>"$file" + + done <"${TMP}/mapping" +fi diff --git a/lane.d/shell-github-action-semver-compare b/lane.d/shell-github-action-semver-compare deleted file mode 100755 index 821bd66..0000000 --- a/lane.d/shell-github-action-semver-compare +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh - -_USAGE() { -cat << END -OPTIONS - -h show this usage - -m main version - -c current version -END -} - -unset -v MAIN -unset -v CURRENT -while getopts "hm:c:" option; do - case $option in - m) MAIN=$OPTARG ;; - c) CURRENT=$OPTARG ;; - \?) - echo "unknown option: $option" - _USAGE - exit 1 - ;; - h) - _USAGE - exit 0 - ;; - esac -done -shift $((OPTIND - 1)) - -if [ -z "$MAIN" ] || [ -z "$CURRENT" ]; then - printf 'Provide both main and current versions.\n\n' - _USAGE - exit 2 -fi - -echo '::group::Resolved versions' -printf '%s - main version\n%s - current version\n' "$MAIN" "$CURRENT" -echo '::endgroup::' - -[ "$MAIN" = "$CURRENT" ] && { echo 'Version must be changed.'; exit 10; } - -VERIFY=$(printf '%s\n%s' "$MAIN" "$CURRENT" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -k 4,4 -k 5,5 -g | tail -n1) -[ "$MAIN" = "$VERIFY" ] && { echo 'Version must be greater than version on main.'; exit 20; } - -exit 0 diff --git a/lane.d/shell-github-action-semver-compare/help.md b/lane.d/shell-github-action-semver-compare/help.md new file mode 100644 index 0000000..84f80b0 --- /dev/null +++ b/lane.d/shell-github-action-semver-compare/help.md @@ -0,0 +1,40 @@ +NAME +``` + shell-github-action-semver-compare + - a lane action +``` + +SYNOPSIS +``` + -m main-version -c current-version + -h +``` + +DESCRIPTION +``` + Compares two semver-style versions. + The exit code will indicate if the current version is considered higher than the main version. + The output includes a GitHub-Action-style group text for easier debugging, and an error message when exit code > 0. + + The purpose is to enable sanity testing required version changes. +``` + +OPTIONS +``` + -h + Shows the full help. + + -m + The main version + + -c + The current version +``` + +EXIT CODES +``` + 10 + Indicates the versions match each other. + 20 + Indicates the main version is considered higher than the current version. +``` diff --git a/lane.d/shell-github-action-semver-compare/options.md b/lane.d/shell-github-action-semver-compare/options.md new file mode 100644 index 0000000..c4d76bc --- /dev/null +++ b/lane.d/shell-github-action-semver-compare/options.md @@ -0,0 +1,11 @@ +OPTIONS +``` + -h + Shows the full help. + + -m + The main version + + -c + The current version +``` diff --git a/lane.d/shell-github-action-semver-compare/run.sh b/lane.d/shell-github-action-semver-compare/run.sh new file mode 100755 index 0000000..0d6c179 --- /dev/null +++ b/lane.d/shell-github-action-semver-compare/run.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +unset -v MAIN +unset -v CURRENT +while getopts "m:c:" option; do + case $option in + m) MAIN=$OPTARG ;; + c) CURRENT=$OPTARG ;; + \?) exit 111 ;; + esac +done +shift $((OPTIND - 1)) + +if [ -z "$MAIN" ] || [ -z "$CURRENT" ]; then + printf 'Provide both main and current versions.\n\n' + exit 111 +fi + +echo '::group::Resolved versions' +printf '%s - main version\n%s - current version\n' "$MAIN" "$CURRENT" +echo '::endgroup::' + +[ "$MAIN" = "$CURRENT" ] && { + echo 'Version must be changed.' + exit 10 +} + +VERIFY=$(printf '%s\n%s' "$MAIN" "$CURRENT" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -k 4,4 -k 5,5 -g | tail -n1) +[ "$MAIN" = "$VERIFY" ] && { + echo 'Version must be greater than version on main.' + exit 20 +} + +exit 0 diff --git a/lane.d/shell-run-github-workflow-tests b/lane.d/shell-run-github-workflow-tests deleted file mode 100755 index 7b4cc8d..0000000 --- a/lane.d/shell-run-github-workflow-tests +++ /dev/null @@ -1,125 +0,0 @@ -#!/bin/sh - -for command in git yq jq; do - if ! [ -x "$(command -v $command)" ]; then - echo "Error: $command is not installed." >&2 - exit 1 - fi -done - -_USAGE() { -cat << END -OPTIONS - -h show this usage - -i workflow file - -j job id -END -} - -unset -v FILE -unset -v JOBID -while getopts "hi:j:" option; do - case $option in - i) FILE="$OPTARG" ;; - j) JOBID="$OPTARG" ;; - \?) - echo "unknown option: $option" - _USAGE - exit 1 - ;; - h) - _USAGE - exit 0 - ;; - esac -done -shift $((OPTIND - 1)) - -if [ ! -f "$FILE" ]; then - echo "Must provide a valid path to a GitHub workflow file with script-based tests." - if [ -n "$FILE" ]; then - printf "Given: %s\n\n" "$FILE" - else - echo - fi - _USAGE - exit 1 -fi - -DIR=$(mktemp -dq) - -trap 'set +x; rm -fr $DIR >/dev/null 2>&1' 0 -trap 'exit 2' 1 2 3 15 - -set -e - -printf 'Preparing runnner...' - -echo 'TOTAL_PASS=0; TOTAL_FAIL=0' > "$DIR/runner.sh" - -yq -o json "$FILE" | jq -rc '.jobs | to_entries[] | [{group: .key, groupName: .value.name, script: .value.steps}] | .[]' | while read -r JOB; do - GROUP=$(printf '%s\n' "$JOB" | jq -rj '.group') - GROUP_NAME=$(printf '%s\n' "$JOB" | jq -rj '.groupName') - - { - echo "cd '${DIR}/workspace/'; git reset HEAD --hard > /dev/null; git clean -fdx . > /dev/null; sh '${DIR}/${GROUP}.sh'" - echo "TOTAL_PASS=\$((TOTAL_PASS+\$(cat ${DIR}/PASS))); TOTAL_FAIL=\$((TOTAL_FAIL+\$(cat ${DIR}/FAIL)))" - } >> "$DIR/runner.sh" - - { - echo "echo; echo '$GROUP_NAME ($GROUP)'" - echo "GREEN=$'\e[0;32m'; RED=$'\e[0;31m'; NC=$'\e[0m'; PASS=0; FAIL=0" - } > "${DIR}/${GROUP}.sh" - - if [ -n "$JOBID" ] && [ ! "$JOBID" = "$GROUP" ]; then - { - echo "echo ' - Skipped'" - echo "printf 0 > '${DIR}/PASS'; printf 0 > '${DIR}/FAIL'" - } >> "${DIR}/${GROUP}.sh" - continue - fi - - I=0 - printf '%s\n' "$JOB" | jq -rc '.script[] | select(.run != null)' | while read -r STEP; do - STEP_NAME=$(printf '%s\n' "$STEP" | jq -rj '.name') - RUN=$(printf '%s\n' "$STEP" | jq -rj '.run') - - echo "$RUN" > "${DIR}/${GROUP}.${I}.sh" - - { - echo "printf ' - ${STEP_NAME}: '" - echo "set +e; sh '${DIR}/${GROUP}.${I}.sh' > messages 2>&1" - printf "if [ \$? -eq 0 ]; then printf \${GREEN}'Pass\n'\${NC}; PASS=\$((PASS+1)); else printf \${RED}'Fail\n'\${NC}; FAIL=\$((FAIL+1)); cat messages; fi;\n" - } >> "${DIR}/${GROUP}.sh" - - I=$((I+1)) - done - - echo "printf \$PASS > '${DIR}/PASS'; printf \$FAIL > '${DIR}/FAIL'" >> "${DIR}/${GROUP}.sh" - -done - -{ - # shellcheck disable=SC2016 - echo 'TOTAL=$((TOTAL_PASS+TOTAL_FAIL))' - # shellcheck disable=SC2016 - printf 'echo; printf "Tests; Total: \033[1m${TOTAL}\033[0m Passes: \033[1m${TOTAL_PASS}\033[0m Fails: \033[1m${TOTAL_FAIL}\033[0m\n"\n' -} >> "$DIR/runner.sh" - -echo ' done!' - -printf 'Preparing workspace... ' -mkdir "$DIR/workspace" -tar -c --exclude .git . | tar -x -C "$DIR/workspace/" - -cd "$DIR/workspace/" -git init > /dev/null 2>&1 -git add . > /dev/null -git commit -am "known state" > /dev/null -echo ' done!' - -trap 'set +x; cd - > /dev/null; rm -fr $DIR >/dev/null 2>&1' 0 -trap 'exit 2' 1 2 3 15 - -echo 'Executing runner...' -sh "$DIR/runner.sh" diff --git a/lane.d/shell-run-github-workflow-tests/help.md b/lane.d/shell-run-github-workflow-tests/help.md new file mode 100644 index 0000000..faf9a05 --- /dev/null +++ b/lane.d/shell-run-github-workflow-tests/help.md @@ -0,0 +1,73 @@ +NAME +``` + shell-run-github-workflow-tests + - a lane action +``` + +SYNOPSIS +``` + -i file [-j job] + -h +``` + +DESCRIPTION +``` + Reads a yaml file with the structure of a GitHub workflow and runs the 'run' steps. + Each step will reports if its exit code indicated success or failure and counts towards a tally. + The steps in each job is run on a fresh copy of the workspace. + + The purpose is to enable unit-test-style tests for shell-based tooling. +``` + +OPTIONS +``` + -h + Shows this help. + + -i + A path to a GitHub workflow file. + + -j + An ID of a job in the provided workflow file. Will limit the execution to just the steps in this job. +``` + +EXAMPLES + +If the contents of `test.yaml` is: + + +```yaml + name: Tests + + on: + workflow_dispatch: {} + + jobs: + test-run: + name: Test run + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 # steps without the 'run' is ignored + + - name: Test that passes + run: | + echo 'Test-in-tests' + + - name: Test that fails + run: | + exit 1 +``` + +The output would be: + +``` + Preparing runnner... done! + Preparing workspace... done! + Executing runner... + + Test run (test-run) + - Test that passes: Pass + - Test that fails: Fail + + Tests; Total: 2 Passes: 1 Fails: 1 +``` diff --git a/lane.d/shell-run-github-workflow-tests/options.md b/lane.d/shell-run-github-workflow-tests/options.md new file mode 100644 index 0000000..8f7ca31 --- /dev/null +++ b/lane.d/shell-run-github-workflow-tests/options.md @@ -0,0 +1,11 @@ +OPTIONS +``` + -h + Shows the full help. + + -i + A path to a GitHub workflow file. + + -j + An ID of a job in the provided workflow file. Will limit the execution to just the steps in this job. +``` diff --git a/lane.d/shell-run-github-workflow-tests/run.sh b/lane.d/shell-run-github-workflow-tests/run.sh new file mode 100755 index 0000000..eaa41be --- /dev/null +++ b/lane.d/shell-run-github-workflow-tests/run.sh @@ -0,0 +1,107 @@ +#!/bin/sh + +for command in git yq jq; do + if ! [ -x "$(command -v $command)" ]; then + echo "Error: $command is not installed." >&2 + exit 1 + fi +done + +unset -v FILE +unset -v JOBID +while getopts "i:j:" option; do + case $option in + i) FILE="$OPTARG" ;; + j) JOBID="$OPTARG" ;; + \?) exit 111 ;; + esac +done +shift $((OPTIND - 1)) + +if [ ! -f "$FILE" ]; then + echo "Must provide a valid path to a GitHub workflow file with script-based tests." + if [ -n "$FILE" ]; then + printf "Given: %s\n\n" "$FILE" + else + echo + fi + exit 111 +fi + +DIR=$(mktemp -dq) + +trap 'set +x; rm -fr $DIR >/dev/null 2>&1' 0 +trap 'exit 2' 1 2 3 15 + +set -e + +printf 'Preparing runnner...' + +echo 'TOTAL_PASS=0; TOTAL_FAIL=0' >"$DIR/runner.sh" + +yq -o json "$FILE" | jq -rc '.jobs | to_entries[] | [{group: .key, groupName: .value.name, script: .value.steps}] | .[]' | while read -r JOB; do + GROUP=$(printf '%s\n' "$JOB" | jq -rj '.group') + GROUP_NAME=$(printf '%s\n' "$JOB" | jq -rj '.groupName') + + { + echo "cd '${DIR}/workspace/'; git reset HEAD --hard > /dev/null; git clean -fdx . > /dev/null; sh '${DIR}/${GROUP}.sh'" + echo "TOTAL_PASS=\$((TOTAL_PASS+\$(cat ${DIR}/PASS))); TOTAL_FAIL=\$((TOTAL_FAIL+\$(cat ${DIR}/FAIL)))" + } >>"$DIR/runner.sh" + + { + echo "echo; echo '$GROUP_NAME ($GROUP)'" + echo "GREEN=$'\e[0;32m'; RED=$'\e[0;31m'; NC=$'\e[0m'; PASS=0; FAIL=0" + } >"${DIR}/${GROUP}.sh" + + if [ -n "$JOBID" ] && [ ! "$JOBID" = "$GROUP" ]; then + { + echo "echo ' - Skipped'" + echo "printf 0 > '${DIR}/PASS'; printf 0 > '${DIR}/FAIL'" + } >>"${DIR}/${GROUP}.sh" + continue + fi + + I=0 + printf '%s\n' "$JOB" | jq -rc '.script[] | select(.run != null)' | while read -r STEP; do + STEP_NAME=$(printf '%s\n' "$STEP" | jq -rj '.name') + RUN=$(printf '%s\n' "$STEP" | jq -rj '.run') + + echo "$RUN" >"${DIR}/${GROUP}.${I}.sh" + + { + echo "printf ' - ${STEP_NAME}: '" + echo "set +e; sh '${DIR}/${GROUP}.${I}.sh' > messages 2>&1" + printf "if [ \$? -eq 0 ]; then printf \${GREEN}'Pass\n'\${NC}; PASS=\$((PASS+1)); else printf \${RED}'Fail\n'\${NC}; FAIL=\$((FAIL+1)); cat messages; fi;\n" + } >>"${DIR}/${GROUP}.sh" + + I=$((I + 1)) + done + + echo "printf \$PASS > '${DIR}/PASS'; printf \$FAIL > '${DIR}/FAIL'" >>"${DIR}/${GROUP}.sh" + +done + +{ + # shellcheck disable=SC2016 + echo 'TOTAL=$((TOTAL_PASS+TOTAL_FAIL))' + # shellcheck disable=SC2016 + printf 'echo; printf "Tests; Total: \033[1m${TOTAL}\033[0m Passes: \033[1m${TOTAL_PASS}\033[0m Fails: \033[1m${TOTAL_FAIL}\033[0m\n"\n' +} >>"$DIR/runner.sh" + +echo ' done!' + +printf 'Preparing workspace... ' +mkdir "$DIR/workspace" +tar -c --exclude .git . | tar -x -C "$DIR/workspace/" + +cd "$DIR/workspace/" +git init >/dev/null 2>&1 +git add . >/dev/null +git commit -am "known state" >/dev/null +echo ' done!' + +trap 'set +x; cd - > /dev/null; rm -fr $DIR >/dev/null 2>&1' 0 +trap 'exit 2' 1 2 3 15 + +echo 'Executing runner...' +sh "$DIR/runner.sh" diff --git a/lanes/test b/lanes/test index c677c16..aa1c133 100644 --- a/lanes/test +++ b/lanes/test @@ -1,2 +1,2 @@ lane shell-run-github-workflow-tests -i .github/workflows/tests.yaml -shellcheck lane.d/* lane +shellcheck lane.d/*/run.sh lane diff --git a/version b/version index 5a5831a..d169b2f 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.0.7 +0.0.8