diff --git a/.drone.jsonnet b/.drone.jsonnet new file mode 100644 index 000000000..338d79b63 --- /dev/null +++ b/.drone.jsonnet @@ -0,0 +1,68 @@ + +local targets = [ + // + // Render these into .drone.yaml by running "make drone" + // + { "os": "linux", "name": "el9", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] }, + { "os": "linux", "name": "el8", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] }, + { "os": "linux", "name": "el7", "isas": [ "amd64", "ppc64le"], "events": [ "tag" ] }, + { "os": "linux", "name": "el6", "isas": [ "amd64" ], "events": [ "tag" ] }, + { "os": "linux", "name": "amzn2", "isas": [ "amd64", "arm64" ], "events": [ "tag" ] }, + { "os": "linux", "name": "fc37", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] }, + { "os": "linux", "name": "fc36", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] }, + { "os": "linux", "name": "fc35", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] }, + { "os": "linux", "name": "jammy", "isas": [ "amd64", "arm64", "armv7", "riscv64", "ppc64le", "s390x" ], "events": [ "tag" ] }, + { "os": "linux", "name": "focal", "isas": [ "amd64", "arm64", "armv7", "riscv64", "ppc64le" ], "events": [ "tag" ] }, + { "os": "linux", "name": "bionic", "isas": [ "amd64", "arm64", "386", "ppc64le", "s390x" ], "events": ["tag" ] }, + { "os": "linux", "name": "xenial", "isas": [ "amd64", "arm64", "386" ], "events": [ "tag" ] }, + { "os": "linux", "name": "sid", "isas": [ "386", "amd64", "arm64", "armv7", "riscv64", "mips64le", "ppc64le", "s390x" ], "events": [ "push", "tag" ] }, + { "os": "linux", "name": "bookworm", "isas": [ "amd64", "arm64", "armv7", "386", "mips64le", "ppc64le", "s390x" ], "events": [ "tag" ] }, + { "os": "linux", "name": "bullseye", "isas": [ "amd64", "arm64", "armv7", "386", "mips64le", "ppc64le", "s390x" ], "events": [ "tag" ] }, + { "os": "linux", "name": "buster", "isas": [ "amd64", "arm64", "armv7", "386", "mips64le", "ppc64le", "s390x" ], "events": [ "tag" ] }, + { "os": "linux", "name": "stretch", "isas": [ "amd64", "arm64", "386" ], "events": [ "tag" ] }, + // { "os": "windows", "name": "win2k19", "isas": [ "amd64" ], "events": ["push", "tag" ] } +]; + +local Build(platform, os, isa, events) = { + "kind": "pipeline", + "type": "docker", + "pull": "always", + "name": platform + " " + isa + " " + "build", + "clone": { "depth": 1 }, + "steps": [ + { + "name": "build", + "image": "registry.sean.farm/honda-builder", + "commands": [ "./ci/scripts/build.sh " + platform + " " + isa + " " + "100.0.0+${DRONE_COMMIT_SHA:0:8}" + " " + "${DRONE_BUILD_EVENT}" ] + }, + { + "name": "list", + "image": "registry.sean.farm/honda-builder", + "commands": [ "ls -la " + platform ] + }, + { + "name": "notify-mattermost", + "image": "registry.sean.farm/mattermost-notify", + "environment": { + "token": { "from_secret": "mattermost-token" }, + "host": { "from_secret": "mattermost-host" }, + "channel": { "from_secret": "mattermost-channel" }, + "maxRetry": 3, + }, + "when": { "status": [ "failure" ] } + } + ], + "image_pull_secrets": [ "dockerconfigjson" ], + [ if isa == "arm64" || isa == "armv7" then "platform" ]: { os: os, arch: "arm64" }, + "trigger": { "event": events } +}; + +// puttin on the bits + +std.flattenArrays([ + [ + Build(p.name, p.os, isa, p.events) + for isa in p.isas + ] + for p in targets +]) diff --git a/.drone.yml b/.drone.yml index 955fbbdea..54587abf4 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,267 +1,2655 @@ --- -kind: pipeline -type: docker -name: build 386 - clone: depth: 1 - -steps: - - name: build 386 - image: registry.sean.farm/honda-builder - commands: - - ./ci/scripts/build.sh linux 386 $${DRONE_COMMIT_SHA} - - name: notify-mattermost - pull: always - image: registry.sean.farm/mattermost-notify - environment: - token: - from_secret: mattermost-token - host: - from_secret: mattermost-host - channel: - from_secret: mattermost-channel - maxRetry: 3 - when: - status: - - failure - - success - image_pull_secrets: - - dockerconfigjson - ---- +- dockerconfigjson kind: pipeline +name: el9 amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh el9 amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la el9 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag type: docker -name: build amd64 - +--- clone: depth: 1 - -steps: - - name: build amd64 - image: registry.sean.farm/honda-builder - commands: - - ./ci/scripts/build.sh linux amd64 $${DRONE_COMMIT_SHA} - - name: notify-mattermost - pull: always - image: registry.sean.farm/mattermost-notify - environment: - token: - from_secret: mattermost-token - host: - from_secret: mattermost-host - channel: - from_secret: mattermost-channel - maxRetry: 3 - when: - status: - - failure - - success - image_pull_secrets: - - dockerconfigjson - ---- +- dockerconfigjson kind: pipeline -type: docker -name: build arm64 - -clone: - depth: 1 - -steps: - - name: build arm64 - image: registry.sean.farm/honda-builder - commands: - - ./ci/scripts/build.sh linux arm64 $${DRONE_COMMIT_SHA} - - name: notify-mattermost - pull: always - image: registry.sean.farm/mattermost-notify - environment: - token: - from_secret: mattermost-token - host: - from_secret: mattermost-host - channel: - from_secret: mattermost-channel - maxRetry: 3 - when: - status: - - failure - - success - +name: el9 arm64 build platform: - os: linux arch: arm64 - -image_pull_secrets: - - dockerconfigjson - ---- -kind: pipeline + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh el9 arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la el9 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag type: docker -name: build armv7 - +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: el9 ppc64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh el9 ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la el9 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: el9 s390x build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh el9 s390x 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la el9 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: el8 amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh el8 amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la el8 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: el8 arm64 build platform: - os: linux arch: arm64 - + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh el8 arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la el8 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- clone: depth: 1 - +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: el8 ppc64le build +pull: always steps: - - name: build armv7 - image: registry.sean.farm/honda-builder - commands: - - ./ci/scripts/build.sh linux armv7 $${DRONE_COMMIT_SHA} - - name: notify-mattermost - pull: always - image: registry.sean.farm/mattermost-notify - environment: - token: - from_secret: mattermost-token - host: - from_secret: mattermost-host - channel: - from_secret: mattermost-channel - maxRetry: 3 - when: - status: - - failure - - success - +- commands: + - ./ci/scripts/build.sh el8 ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la el8 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: el8 s390x build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh el8 s390x 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la el8 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: el7 amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh el7 amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la el7 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: el7 ppc64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh el7 ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la el7 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: el6 amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh el6 amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la el6 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: amzn2 amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh amzn2 amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la amzn2 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: amzn2 arm64 build platform: - os: linux arch: arm64 - -image_pull_secrets: - - dockerconfigjson - ---- -kind: pipeline + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh amzn2 arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la amzn2 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag type: docker -name: build riscv64 - +--- clone: depth: 1 - -steps: - - name: build riscv64 - image: registry.sean.farm/honda-builder - commands: - - ./ci/scripts/build.sh linux riscv64 $${DRONE_COMMIT_SHA} - - name: notify-mattermost - pull: always - image: registry.sean.farm/mattermost-notify - environment: - token: - from_secret: mattermost-token - host: - from_secret: mattermost-host - channel: - from_secret: mattermost-channel - maxRetry: 3 - when: - status: - - failure - - success - image_pull_secrets: - - dockerconfigjson - ---- +- dockerconfigjson kind: pipeline +name: fc37 amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh fc37 amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la fc37 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag type: docker -name: build mips64le - +--- clone: depth: 1 - -steps: - - name: build mips64le - image: registry.sean.farm/honda-builder - commands: - - ./ci/scripts/build.sh linux mips64le $${DRONE_COMMIT_SHA} - - name: notify-mattermost - pull: always - image: registry.sean.farm/mattermost-notify - environment: - token: - from_secret: mattermost-token - host: - from_secret: mattermost-host - channel: - from_secret: mattermost-channel - maxRetry: 3 - when: - status: - - failure - - success - image_pull_secrets: - - dockerconfigjson - ---- +- dockerconfigjson kind: pipeline +name: fc37 arm64 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh fc37 arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la fc37 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag type: docker -name: build ppc64le - +--- clone: depth: 1 - -steps: - - name: build ppc64le - image: registry.sean.farm/honda-builder - commands: - - ./ci/scripts/build.sh linux ppc64le $${DRONE_COMMIT_SHA} - - name: notify-mattermost - pull: always - image: registry.sean.farm/mattermost-notify - environment: - token: - from_secret: mattermost-token - host: - from_secret: mattermost-host - channel: - from_secret: mattermost-channel - maxRetry: 3 - when: - status: - - failure - - success - image_pull_secrets: - - dockerconfigjson - ---- +- dockerconfigjson kind: pipeline +name: fc37 ppc64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh fc37 ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la fc37 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag type: docker -name: build s390x - +--- clone: depth: 1 - -steps: - - name: build s390x - image: registry.sean.farm/honda-builder - commands: - - ./ci/scripts/build.sh linux s390x $${DRONE_COMMIT_SHA} - - name: notify-mattermost - pull: always - image: registry.sean.farm/mattermost-notify - environment: - token: - from_secret: mattermost-token - host: - from_secret: mattermost-host - channel: - from_secret: mattermost-channel - maxRetry: 3 - when: - status: - - failure - - success - image_pull_secrets: - - dockerconfigjson +- dockerconfigjson +kind: pipeline +name: fc37 s390x build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh fc37 s390x 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la fc37 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: fc36 amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh fc36 amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la fc36 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: fc36 arm64 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh fc36 arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la fc36 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: fc36 ppc64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh fc36 ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la fc36 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: fc36 s390x build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh fc36 s390x 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la fc36 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: fc35 amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh fc35 amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la fc35 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: fc35 arm64 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh fc35 arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la fc35 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: fc35 ppc64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh fc35 ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la fc35 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: fc35 s390x build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh fc35 s390x 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la fc35 + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: jammy amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh jammy amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la jammy + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: jammy arm64 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh jammy arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la jammy + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: jammy armv7 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh jammy armv7 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la jammy + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: jammy riscv64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh jammy riscv64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la jammy + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: jammy ppc64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh jammy ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la jammy + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: jammy s390x build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh jammy s390x 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la jammy + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: focal amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh focal amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la focal + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: focal arm64 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh focal arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la focal + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: focal armv7 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh focal armv7 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la focal + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: focal riscv64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh focal riscv64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la focal + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: focal ppc64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh focal ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la focal + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bionic amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bionic amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bionic + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bionic arm64 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bionic arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bionic + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bionic 386 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bionic 386 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bionic + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bionic ppc64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bionic ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bionic + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bionic s390x build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bionic s390x 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bionic + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: xenial amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh xenial amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la xenial + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: xenial arm64 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh xenial arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la xenial + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: xenial 386 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh xenial 386 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la xenial + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: sid 386 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh sid 386 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la sid + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - push + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: sid amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh sid amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la sid + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - push + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: sid arm64 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh sid arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la sid + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - push + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: sid armv7 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh sid armv7 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la sid + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - push + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: sid riscv64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh sid riscv64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la sid + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - push + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: sid mips64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh sid mips64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la sid + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - push + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: sid ppc64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh sid ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la sid + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - push + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: sid s390x build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh sid s390x 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la sid + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - push + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bookworm amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bookworm amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bookworm + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bookworm arm64 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bookworm arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bookworm + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bookworm armv7 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bookworm armv7 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bookworm + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bookworm 386 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bookworm 386 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bookworm + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bookworm mips64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bookworm mips64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bookworm + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bookworm ppc64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bookworm ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bookworm + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bookworm s390x build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bookworm s390x 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bookworm + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bullseye amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bullseye amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bullseye + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bullseye arm64 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bullseye arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bullseye + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bullseye armv7 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bullseye armv7 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bullseye + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bullseye 386 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bullseye 386 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bullseye + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bullseye mips64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bullseye mips64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bullseye + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bullseye ppc64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bullseye ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bullseye + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: bullseye s390x build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh bullseye s390x 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la bullseye + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: buster amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh buster amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la buster + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: buster arm64 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh buster arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la buster + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: buster armv7 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh buster armv7 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la buster + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: buster 386 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh buster 386 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la buster + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: buster mips64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh buster mips64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la buster + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: buster ppc64le build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh buster ppc64le 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la buster + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: buster s390x build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh buster s390x 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la buster + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: stretch amd64 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh stretch amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la stretch + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: stretch arm64 build +platform: + arch: arm64 + os: linux +pull: always +steps: +- commands: + - ./ci/scripts/build.sh stretch arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la stretch + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker +--- +clone: + depth: 1 +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: stretch 386 build +pull: always +steps: +- commands: + - ./ci/scripts/build.sh stretch 386 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} + image: registry.sean.farm/honda-builder + name: build +- commands: + - ls -la stretch + image: registry.sean.farm/honda-builder + name: list +- environment: + channel: + from_secret: mattermost-channel + host: + from_secret: mattermost-host + maxRetry: 3 + token: + from_secret: mattermost-token + image: registry.sean.farm/mattermost-notify + name: notify-mattermost + when: + status: + - failure +trigger: + event: + - tag +type: docker diff --git a/Makefile b/Makefile index 39dabafdd..0440f89a8 100644 --- a/Makefile +++ b/Makefile @@ -26,3 +26,7 @@ endif ifeq ($(OSTYPE),NetBSD) include make-netbsd.mk endif + +drone: + @echo "rendering .drone.yaml from .drone.jsonnet" + drone jsonnet --format --stream diff --git a/README.md b/README.md index a53dca476..44a6e68bd 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,10 @@ To build on Mac and Linux just type `make`. On FreeBSD and OpenBSD `gmake` (GNU - The minimum compiler versions required are GCC/G++ 4.9.3 or CLANG/CLANG++ 3.4.2. (Install `clang` on CentOS 7 as G++ is too old.) - Linux makefiles automatically detect and prefer clang/clang++ if present as it produces smaller and slightly faster binaries in most cases. You can override by supplying CC and CXX variables on the make command line. - **Windows** - - Windows 7 or newer is supported. This *may* work on Vista but isn't officially supported there. It will not work on Windows XP. + - Windows 8 or newer is supported. + - Windows 7 is supported by versions `<=1.6.6`. + - Windows Vista *may* work but isn't officially supported there. + - Windows XP is will not work. - We build with Visual Studio 2017. Older versions may not work. Clang or MinGW will also probably work but may require some makefile hacking. - **FreeBSD** - GNU make is required. Type `gmake` to build. diff --git a/ci/Dockerfile.deb b/ci/Dockerfile.deb new file mode 100644 index 000000000..91b383a6a --- /dev/null +++ b/ci/Dockerfile.deb @@ -0,0 +1,10 @@ +ARG PLATFORM +FROM registry.sean.farm/${PLATFORM}-builder as stage +WORKDIR /work/build +COPY . . +RUN make debian +RUN ls -ls /work + +FROM scratch AS export +ARG PLATFORM +COPY --from=stage /work/*.deb ./${PLATFORM}/ diff --git a/ci/Dockerfile.el6 b/ci/Dockerfile.el6 new file mode 100644 index 000000000..46ba13c42 --- /dev/null +++ b/ci/Dockerfile.el6 @@ -0,0 +1,36 @@ +ARG DOCKER_ARCH +FROM --platform=linux/${DOCKER_ARCH} alpine:edge AS builder + +RUN apk update +RUN apk add curl +RUN apk add bash +RUN apk add file +RUN apk add rust +RUN apk add cargo +RUN apk add make +RUN apk add cmake +RUN apk add clang +RUN apk add openssl-dev +RUN apk add linux-headers +RUN apk add build-base +RUN apk add openssl-libs-static + +COPY . . +RUN ZT_STATIC=1 make one +RUN ls -la + +ARG DOCKER_ARCH +FROM --platform=linux/${DOCKER_ARCH} centos:6 AS stage +WORKDIR /root/rpmbuild/BUILD +COPY . . +COPY --from=builder zerotier-one ./ +RUN curl https://gist.githubusercontent.com/someara/b363002ba6e57b3c474dd027d4daef85/raw/4ac5534139752fc92fbe1a53599a390214f69615/el6%2520vault --output /etc/yum.repos.d/CentOS-Base.repo +RUN uname -a +RUN yum -y install make gcc rpm-build +RUN pwd +RUN ls -la +RUN make redhat + +FROM scratch AS export +ARG PLATFORM +COPY --from=stage /root/rpmbuild/RPMS/*/*.rpm ./${PLATFORM}/ diff --git a/ci/Dockerfile.rpm b/ci/Dockerfile.rpm new file mode 100644 index 000000000..9969be05e --- /dev/null +++ b/ci/Dockerfile.rpm @@ -0,0 +1,9 @@ +ARG PLATFORM +FROM registry.sean.farm/${PLATFORM}-builder as stage +WORKDIR /root/rpmbuild/BUILD +COPY . . +RUN make redhat + +FROM scratch AS export +ARG PLATFORM +COPY --from=stage /root/rpmbuild/RPMS/*/*.rpm ./${PLATFORM}/ diff --git a/ci/Dockerfile.sid b/ci/Dockerfile.sid deleted file mode 100644 index 4af604aac..000000000 --- a/ci/Dockerfile.sid +++ /dev/null @@ -1,7 +0,0 @@ -FROM registry.sean.farm/sid-builder as stage -COPY . . -RUN /usr/bin/make -j 8 - -FROM scratch AS export -COPY --from=stage /zerotier-one . -COPY --from=stage /zerotier-cli . diff --git a/ci/scripts/build.sh b/ci/scripts/build.sh index eb33e80fe..e6aee6bc6 100755 --- a/ci/scripts/build.sh +++ b/ci/scripts/build.sh @@ -2,28 +2,115 @@ set -euo pipefail IFS=$'\n\t' -export GOOS=$1 -export GOARCH=$2 +export PLATFORM=$1 +export ZT_ISA=$2 export VERSION=$3 -export DOCKER_BUILDKIT=1 +export EVENT=$4 -echo "nproc: $(nproc)" - -case $GOARCH in - armv5) - export ARCH=arm/v5 - ;; - armv7) - export ARCH=arm/v7 - ;; - arm64) - export ARCH=arm64/v8 +case $PLATFORM in + el*|fc*|amzn*) + export PKGFMT=rpm ;; *) - export ARCH=$GOARCH + export PKGFMT=deb +esac + +# OSX +# x86_64-apple-darwin +# aarch64-apple-darwin + +# Windows +# x86_64-pc-windows-msvc +# i686-pc-windows-msvc +# aarch64-pc-windows-msvc + +# Linux +# i686-unknown-linux-gnu +# x86_64-unknown-linux-gnu +# arm-unknown-linux-gnueabi ? +# arm-unknown-linux-gnueabihf ? +# armv7-unknown-linux-gnueabihf +# + +case $ZT_ISA in + 386) + export DOCKER_ARCH=386 + export RUST_TRIPLET=i686-unknown-linux-gnu + ;; + amd64) + export DOCKER_ARCH=amd64 + export RUST_TRIPLET=x86_64-unknown-linux-gnu + ;; + armv6) + export DOCKER_ARCH=arm/v6 + export RUST_TRIPLET=arm-unknown-linux-gnueabi + ;; + armv7) + export DOCKER_ARCH=arm/v7 + export RUST_TRIPLET=arm-unknown-linux-gnueabihf + ;; + arm64) + export DOCKER_ARCH=arm64/v8 + export RUST_TRIPLET=aarch64-unknown-linux-gnu + ;; + riscv64) + export DOCKER_ARCH=riscv64 + export RUST_TRIPLET=riscv64gc-unknown-linux-gnu + ;; + ppc64le) + export DOCKER_ARCH=ppc64le + export RUST_TRIPLET=powerpc64le-unknown-linux-gnu + ;; + mips64le) + export DOCKER_ARCH=mips64le + export RUST_TRIPLET=mips64el-unknown-linux-gnuabi64 + ;; + s390x) + export DOCKER_ARCH=s390x + export RUST_TRIPLET=s390x-unknown-linux-gnu + ;; + *) + echo "ERROR: could not determine architecture settings. PLEASE FIX ME" + exit 1 ;; esac +if [ -f "ci/Dockerfile.${PLATFORM}" ]; then + export DOCKERFILE="ci/Dockerfile.${PLATFORM}" +else + export DOCKERFILE="ci/Dockerfile.${PKGFMT}" +fi + +echo "#~~~~~~~~~~~~~~~~~~~~" +echo "$0 variables:" +echo "nproc: $(nproc)" +echo "ZT_ISA: ${ZT_ISA}" +echo "DOCKER_ARCH: ${DOCKER_ARCH}" +echo "RUST_TRIPLET: ${RUST_TRIPLET}" +echo "VERSION: ${VERSION}" +echo "EVENT: ${EVENT}" +echo "PKGFMT: ${PKGFMT}" +echo "PWD: ${PWD}" +echo "DOCKERFILE: ${DOCKERFILE}" +echo "#~~~~~~~~~~~~~~~~~~~~" + +if [ ${EVENT} == "push" ]; then +make munge_rpm zerotier-one.spec VERSION=${VERSION} +make munge_deb debian/changelog VERSION=${VERSION} +fi + +export DOCKER_BUILDKIT=1 docker run --privileged --rm tonistiigi/binfmt --install all -docker pull registry.sean.farm/sid-builder -docker buildx build --platform ${GOOS}/${ARCH} -f ci/Dockerfile.sid --target export -t test . --output out/${GOOS}/${GOARCH} + +# docker pull --platform linux/${DOCKER_ARCH} registry.sean.farm/${PLATFORM}-builder + +docker buildx build \ + --build-arg PLATFORM="${PLATFORM}" \ + --build-arg RUST_TRIPLET="${RUST_TRIPLET}" \ + --build-arg DOCKER_ARCH="${DOCKER_ARCH}" \ + --platform linux/${DOCKER_ARCH} \ + -f ${DOCKERFILE} \ + -t build \ + . \ + --output type=local,dest=. \ + --target export diff --git a/ci/scripts/munge_debian_changelog.sh b/ci/scripts/munge_debian_changelog.sh new file mode 100755 index 000000000..86cedf72c --- /dev/null +++ b/ci/scripts/munge_debian_changelog.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -euo pipefail +IFS=$'\n\t' + +export FILE=$1 +export VERSION=$2 +export NAME=$3 +export MESSAGE=$4 +export DATE=$(date "+%a, %d %b %Y %T %z") +# export DATE=$(date "+%a %b %d %Y") + +set +e +grep --version | grep BSD &> /dev/null +if [ $? == 0 ]; then BSDGREP=true ; else BSDGREP=false ; fi +set -e + +# echo "#~~~~~~~~~~~~~~~~~~~~" +# echo "$0 variables:" +# echo "VERSION: ${VERSION}" +# echo "NAME: ${NAME}" +# echo "MESSAGE: ${MESSAGE}" +# echo "DATE: ${DATE}" +# echo "BSDGREP: ${BSDGREP}" +# echo "#~~~~~~~~~~~~~~~~~~~~" +# echo + +if $BSDGREP ; then + sed -i '' s/^Version:.*/"Version: ${VERSION}"/ ${FILE} +else + sed -i s/^Version:.*/"Version: ${VERSION}"/ ${FILE} +fi + +awk -v version=${VERSION} -v date=${DATE} -v name=${NAME} -v message=${MESSAGE} \ + 'BEGIN{print "zerotier-one (" version ") unstable; urgency=medium\n\n * " message "\n\n -- " name " " date "\n" }{ print }' \ + ${FILE} > ${FILE}.new + +mv ${FILE}.new ${FILE} diff --git a/ci/scripts/munge_rpm_spec.sh b/ci/scripts/munge_rpm_spec.sh new file mode 100755 index 000000000..289df1ed4 --- /dev/null +++ b/ci/scripts/munge_rpm_spec.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -euo pipefail +IFS=$'\n\t' + +export FILE=$1 +export VERSION=$2 +export NAME=$3 +export MESSAGE=$4 +export DATE=$(date "+%a %b %d %Y") + +set +e +grep --version | grep BSD &> /dev/null +if [ $? == 0 ]; then BSDGREP=true ; else BSDGREP=false ; fi +set -e + +# echo "#~~~~~~~~~~~~~~~~~~~~" +# echo "$0 variables:" +# echo "VERSION: ${VERSION}" +# echo "NAME: ${NAME}" +# echo "MESSAGE: ${MESSAGE}" +# echo "DATE: ${DATE}" +# echo "BSDGREP: ${BSDGREP}" +# echo "#~~~~~~~~~~~~~~~~~~~~" +# echo + +if $BSDGREP ; then + sed -i '' s/^Version:.*/"Version: ${VERSION}"/ ${FILE} +else + sed -i s/^Version:.*/"Version: ${VERSION}"/ ${FILE} +fi + +awk -v version=${VERSION} -v date=${DATE} -v name=${NAME} -v message=${MESSAGE} \ + 'FNR==NR{ if (/%changelog/) p=NR; next} 1; FNR==p{ print "* " date " " name " - " version "\n- " message "\n" }' \ + ${FILE} ${FILE} > ${FILE}.new + +mv ${FILE}.new ${FILE} diff --git a/controller/DB.hpp b/controller/DB.hpp index b792304e5..10adbec10 100644 --- a/controller/DB.hpp +++ b/controller/DB.hpp @@ -33,7 +33,7 @@ #include #include -#include "../ext/json/json.hpp" +#include #define ZT_MEMBER_AUTH_TIMEOUT_NOTIFY_BEFORE 25000 diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 49c1c36eb..bc95acb58 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -35,7 +35,7 @@ #include "../osdep/Thread.hpp" #include "../osdep/BlockingQueue.hpp" -#include "../ext/json/json.hpp" +#include #include "DB.hpp" #include "DBMirrorSet.hpp" diff --git a/ext/inja/LICENSE b/ext/inja/LICENSE new file mode 100644 index 000000000..9e06bea07 --- /dev/null +++ b/ext/inja/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-2021 Berscheid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ext/inja/README.md b/ext/inja/README.md new file mode 100644 index 000000000..9d66a2c28 --- /dev/null +++ b/ext/inja/README.md @@ -0,0 +1,391 @@ +[
](https://github.com/pantor/inja/releases) + +

+ + CI Status + + + + Documentation Status + + + + + + + + Github Releases + + + + Github Issues + + + + GitHub License + +

+ +Inja is a template engine for modern C++, loosely inspired by [jinja](http://jinja.pocoo.org) for python. It has an easy and yet powerful template syntax with all variables, loops, conditions, includes, callbacks, and comments you need, nested and combined as you like. Inja uses the wonderful [json](https://github.com/nlohmann/json) library by nlohmann for data input. Most importantly, inja needs only two header files, which is (nearly) as trivial as integration in C++ can get. Of course, everything is tested on all relevant compilers. Here is what it looks like: + +```.cpp +json data; +data["name"] = "world"; + +inja::render("Hello {{ name }}!", data); // Returns "Hello world!" +``` + +## Integration + +Inja is a headers only library, which can be downloaded from the [releases](https://github.com/pantor/inja/releases) or directly from the `include/` or `single_include/` folder. Inja uses `nlohmann/json.hpp` (>= v3.8.0) as its single dependency, so make sure it can be included from `inja.hpp`. json can be downloaded [here](https://github.com/nlohmann/json/releases). Then integration is as easy as: + +```.cpp +#include + +// Just for convenience +using namespace inja; +``` + +If you are using the [Meson Build System](http://mesonbuild.com), then you can wrap this repository as a subproject. + +If you are using [Conan](https://conan.io) to manage your dependencies, have a look at [this repository](https://github.com/DEGoodmanWilson/conan-inja). Please file issues [here](https://github.com/DEGoodmanWilson/conan-inja/issues) if you experience problems with the packages. + +You can also integrate inja in your project using [Hunter](https://github.com/cpp-pm/hunter), a package manager for C++. + +If you are using [vcpkg](https://github.com/Microsoft/vcpkg) on your project for external dependencies, then you can use the [inja package](https://github.com/Microsoft/vcpkg/tree/master/ports/inja). Please see the vcpkg project for any issues regarding the packaging. + +If you are using [cget](https://cget.readthedocs.io/en/latest/), you can install the latest development version with `cget install pantor/inja`. A specific version can be installed with `cget install pantor/inja@v2.1.0`. + +On macOS, you can install inja via [Homebrew](https://formulae.brew.sh/formula/inja#default) and `brew install inja`. + +If you are using [conda](https://docs.conda.io/en/latest/), you can install the latest version from [conda-forge](https://anaconda.org/conda-forge/inja) with `conda install -c conda-forge inja`. + +## Tutorial + +This tutorial will give you an idea how to use inja. It will explain the most important concepts and give practical advices using examples and executable code. Beside this tutorial, you may check out the [documentation](https://pantor.github.io/inja). + +### Template Rendering + +The basic template rendering takes a template as a `std::string` and a `json` object for all data. It returns the rendered template as an `std::string`. + +```.cpp +json data; +data["name"] = "world"; + +render("Hello {{ name }}!", data); // Returns std::string "Hello world!" +render_to(std::cout, "Hello {{ name }}!", data); // Writes "Hello world!" to stream +``` + +For more advanced usage, an environment is recommended. +```.cpp +Environment env; + +// Render a string with json data +std::string result = env.render("Hello {{ name }}!", data); // "Hello world!" + +// Or directly read a template file +Template temp = env.parse_template("./templates/greeting.txt"); +std::string result = env.render(temp, data); // "Hello world!" + +data["name"] = "Inja"; +std::string result = env.render(temp, data); // "Hello Inja!" + +// Or read the template file (and/or the json file) directly from the environment +result = env.render_file("./templates/greeting.txt", data); +result = env.render_file_with_json_file("./templates/greeting.txt", "./data.json"); + +// Or write a rendered template file +env.write(temp, data, "./result.txt"); +env.write_with_json_file("./templates/greeting.txt", "./data.json", "./result.txt"); +``` + +The environment class can be configured to your needs. +```.cpp +// With default settings +Environment env_default; + +// With global path to template files and where files will be saved +Environment env_1 {"../path/templates/"}; + +// With separate input and output path +Environment env_2 {"../path/templates/", "../path/results/"}; + +// With other opening and closing strings (here the defaults) +env.set_expression("{{", "}}"); // Expressions +env.set_comment("{#", "#}"); // Comments +env.set_statement("{%", "%}"); // Statements {% %} for many things, see below +env.set_line_statement("##"); // Line statements ## (just an opener) +``` + +### Variables + +Variables are rendered within the `{{ ... }}` expressions. +```.cpp +json data; +data["neighbour"] = "Peter"; +data["guests"] = {"Jeff", "Tom", "Patrick"}; +data["time"]["start"] = 16; +data["time"]["end"] = 22; + +// Indexing in array +render("{{ guests.1 }}", data); // "Tom" + +// Objects +render("{{ time.start }} to {{ time.end + 1 }}pm", data); // "16 to 23pm" +``` +If no variable is found, valid JSON is printed directly, otherwise an `inja::RenderError` is thrown. + +### Statements + +Statements can be written either with the `{% ... %}` syntax or the `##` syntax for entire lines. Note that `##` needs to start the line without indentation. The most important statements are loops, conditions and file includes. All statements can be nested. + +#### Loops + +```.cpp +// Combining loops and line statements +render(R"(Guest List: +## for guest in guests + {{ loop.index1 }}: {{ guest }} +## endfor )", data) + +/* Guest List: + 1: Jeff + 2: Tom + 3: Patrick */ +``` +In a loop, the special variables `loop.index (number)`, `loop.index1 (number)`, `loop.is_first (boolean)` and `loop.is_last (boolean)` are defined. In nested loops, the parent loop variables are available e.g. via `loop.parent.index`. You can also iterate over objects like `{% for key, value in time %}`. + +#### Conditions + +Conditions support the typical if, else if and else statements. Following conditions are for example possible: +```.cpp +// Standard comparisons with a variable +render("{% if time.hour >= 20 %}Serve{% else if time.hour >= 18 %}Make{% endif %} dinner.", data); // Serve dinner. + +// Variable in list +render("{% if neighbour in guests %}Turn up the music!{% endif %}", data); // Turn up the music! + +// Logical operations +render("{% if guest_count < (3+2) and all_tired %}Sleepy...{% else %}Keep going...{% endif %}", data); // Sleepy... + +// Negations +render("{% if not guest_count %}The End{% endif %}", data); // The End +``` + +#### Includes + +You can either include other in-memory templates or from the file system. +```.cpp +// To include in-memory templates, add them to the environment first +inja::Template content_template = env.parse("Hello {{ neighbour }}!"); +env.include_template("content", content_template); +env.render("Content: {% include \"content\" %}", data); // "Content: Hello Peter!" + +// Other template files are included relative from the current file location +render("{% include \"footer.html\" %}", data); +``` +If a corresponding template could not be found in the file system, the *include callback* is called: +```.cpp +// The callback takes the current path and the wanted include name and returns a template +env.set_include_callback([&env](const std::string& path, const std::string& template_name) { + return env.parse("Hello {{ neighbour }} from " + template_name); +}); + +// You can disable to search for templates in the file system via +env.set_search_included_templates_in_files(false); +``` + +Inja will throw an `inja::RenderError` if an included file is not found and no callback is specified. To disable this error, you can call `env.set_throw_at_missing_includes(false)`. + +#### Assignments + +Variables can also be defined within the template using the set statment. +```.cpp +render("{% set new_hour=23 %}{{ new_hour }}pm", data); // "23pm" +render("{% set time.start=18 %}{{ time.start }}pm", data); // using json pointers +``` + +Assignments only set the value within the rendering context; they do not modify the json object passed into the `render` call. + +### Functions + +A few functions are implemented within the inja template syntax. They can be called with +```.cpp +// Upper and lower function, for string cases +render("Hello {{ upper(neighbour) }}!", data); // "Hello PETER!" +render("Hello {{ lower(neighbour) }}!", data); // "Hello peter!" + +// Range function, useful for loops +render("{% for i in range(4) %}{{ loop.index1 }}{% endfor %}", data); // "1234" +render("{% for i in range(3) %}{{ at(guests, i) }} {% endfor %}", data); // "Jeff Tom Patrick " + +// Length function (please don't combine with range, use list directly...) +render("I count {{ length(guests) }} guests.", data); // "I count 3 guests." + +// Get first and last element in a list +render("{{ first(guests) }} was first.", data); // "Jeff was first." +render("{{ last(guests) }} was last.", data); // "Patir was last." + +// Sort a list +render("{{ sort([3,2,1]) }}", data); // "[1,2,3]" +render("{{ sort(guests) }}", data); // "[\"Jeff\", \"Patrick\", \"Tom\"]" + +// Join a list with a separator +render("{{ join([1,2,3], \" + \") }}", data); // "1 + 2 + 3" +render("{{ join(guests, \", \") }}", data); // "Jeff, Patrick, Tom" + +// Round numbers to a given precision +render("{{ round(3.1415, 0) }}", data); // 3 +render("{{ round(3.1415, 3) }}", data); // 3.142 + +// Check if a value is odd, even or divisible by a number +render("{{ odd(42) }}", data); // false +render("{{ even(42) }}", data); // true +render("{{ divisibleBy(42, 7) }}", data); // true + +// Maximum and minimum values from a list +render("{{ max([1, 2, 3]) }}", data); // 3 +render("{{ min([-2.4, -1.2, 4.5]) }}", data); // -2.4 + +// Convert strings to numbers +render("{{ int(\"2\") == 2 }}", data); // true +render("{{ float(\"1.8\") > 2 }}", data); // false + +// Set default values if variables are not defined +render("Hello {{ default(neighbour, \"my friend\") }}!", data); // "Hello Peter!" +render("Hello {{ default(colleague, \"my friend\") }}!", data); // "Hello my friend!" + +// Access an objects value dynamically +render("{{ at(time, \"start\") }} to {{ time.end }}", data); // "16 to 22" + +// Check if a key exists in an object +render("{{ exists(\"guests\") }}", data); // "true" +render("{{ exists(\"city\") }}", data); // "false" +render("{{ existsIn(time, \"start\") }}", data); // "true" +render("{{ existsIn(time, neighbour) }}", data); // "false" + +// Check if a key is a specific type +render("{{ isString(neighbour) }}", data); // "true" +render("{{ isArray(guests) }}", data); // "true" +// Implemented type checks: isArray, isBoolean, isFloat, isInteger, isNumber, isObject, isString, +``` + +### Callbacks + +You can create your own and more complex functions with callbacks. These are implemented with `std::function`, so you can for example use C++ lambdas. Inja `Arguments` are a vector of json pointers. +```.cpp +Environment env; + +/* + * Callbacks are defined by its: + * - name, + * - (optional) number of arguments, + * - callback function. + */ +env.add_callback("double", 1, [](Arguments& args) { + int number = args.at(0)->get(); // Adapt the index and type of the argument + return 2 * number; +}); + +// You can then use a callback like a regular function +env.render("{{ double(16) }}", data); // "32" + +// Inja falls back to variadic callbacks if the number of expected arguments is omitted. +env.add_callback("argmax", [](Arguments& args) { + auto result = std::max_element(args.begin(), args.end(), [](const json* a, const json* b) { return *a < *b;}); + return std::distance(args.begin(), result); +}); +env.render("{{ argmax(4, 2, 6) }}", data); // "2" +env.render("{{ argmax(0, 2, 6, 8, 3) }}", data); // "3" + +// A callback without argument can be used like a dynamic variable: +std::string greet = "Hello"; +env.add_callback("double-greetings", 0, [greet](Arguments args) { + return greet + " " + greet + "!"; +}); +env.render("{{ double-greetings }}", data); // "Hello Hello!" +``` +You can also add a void callback without return variable, e.g. for debugging: +```.cpp +env.add_void_callback("log", 1, [greet](Arguments args) { + std::cout << "logging: " << args[0] << std::endl; +}); +env.render("{{ log(neighbour) }}", data); // Prints nothing to result, only to cout... +``` + +### Template Inheritance + +Template inheritance allows you to build a base *skeleton* template that contains all the common elements and defines blocks that child templates can override. Lets show an example: The base template +```.html + + + + {% block head %} + + {% block title %}{% endblock %} - My Webpage + {% endblock %} + + +
{% block content %}{% endblock %}
+ + +``` +contains three `blocks` that child templates can fill in. The child template +```.html +{% extends "base.html" %} +{% block title %}Index{% endblock %} +{% block head %} + {{ super() }} + +{% endblock %} +{% block content %} +

Index

+

+ Welcome to my blog! +

+{% endblock %} +``` +calls a parent template with the `extends` keyword; it should be the first element in the template. It is possible to render the contents of the parent block by calling `super()`. In the case of multiple levels of `{% extends %}`, super references may be called with an argument (e.g. `super(2)`) to skip levels in the inheritance tree. + +### Whitespace Control + +In the default configuration, no whitespace is removed while rendering the file. To support a more readable template style, you can configure the environment to control whitespaces before and after a statement automatically. While enabling `set_trim_blocks` removes the first newline after a statement, `set_lstrip_blocks` strips tabs and spaces from the beginning of a line to the start of a block. + +```.cpp +Environment env; +env.set_trim_blocks(true); +env.set_lstrip_blocks(true); +``` + +With both `trim_blocks` and `lstrip_blocks` enabled, you can put statements on their own lines. Furthermore, you can also strip whitespaces for both statements and expressions by hand. If you add a minus sign (`-`) to the start or end, the whitespaces before or after that block will be removed: + +```.cpp +render("Hello {{- name -}} !", data); // "Hello Inja!" +render("{% if neighbour in guests -%} I was there{% endif -%} !", data); // Renders without any whitespaces +``` + +Stripping behind a statement or expression also removes any newlines. + +### Comments + +Comments can be written with the `{# ... #}` syntax. +```.cpp +render("Hello{# Todo #}!", data); // "Hello!" +``` + +### Exceptions + +Inja uses exceptions to handle ill-formed template input. However, exceptions can be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `INJA_NOEXCEPTION`. In this case, exceptions are replaced by `abort()` calls. + + +## Supported compilers + +Inja uses the `string_view` feature of the C++17 STL. Currently, the following compilers are tested: + +- GCC 7 - 11 (and possibly later) +- Clang 5 - 12 (and possibly later) +- Microsoft Visual C++ 2017 15.0 - 2022 (and possibly later) + +A list of supported compiler / os versions can be found in the [CI definition](https://github.com/pantor/inja/blob/master/.github/workflows/ci.yml). diff --git a/ext/inja/inja.hpp b/ext/inja/inja.hpp new file mode 100644 index 000000000..5b469745f --- /dev/null +++ b/ext/inja/inja.hpp @@ -0,0 +1,2949 @@ +/* + ___ _ Version 3.3 + |_ _|_ __ (_) __ _ https://github.com/pantor/inja + | || '_ \ | |/ _` | Licensed under the MIT License . + | || | | || | (_| | + |___|_| |_|/ |\__,_| Copyright (c) 2018-2021 Lars Berscheid + |__/ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef INCLUDE_INJA_INJA_HPP_ +#define INCLUDE_INJA_INJA_HPP_ + +#include + +namespace inja { +#ifndef INJA_DATA_TYPE +using json = nlohmann::json; +#else +using json = INJA_DATA_TYPE; +#endif +} // namespace inja + +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(INJA_NOEXCEPTION) +#ifndef INJA_THROW +#define INJA_THROW(exception) throw exception +#endif +#else +#include +#ifndef INJA_THROW +#define INJA_THROW(exception) \ + std::abort(); \ + std::ignore = exception +#endif +#ifndef INJA_NOEXCEPTION +#define INJA_NOEXCEPTION +#endif +#endif + +// #include "environment.hpp" +#ifndef INCLUDE_INJA_ENVIRONMENT_HPP_ +#define INCLUDE_INJA_ENVIRONMENT_HPP_ + +#include +#include +#include +#include +#include +#include + +// #include "config.hpp" +#ifndef INCLUDE_INJA_CONFIG_HPP_ +#define INCLUDE_INJA_CONFIG_HPP_ + +#include +#include + +// #include "template.hpp" +#ifndef INCLUDE_INJA_TEMPLATE_HPP_ +#define INCLUDE_INJA_TEMPLATE_HPP_ + +#include +#include +#include +#include + +// #include "node.hpp" +#ifndef INCLUDE_INJA_NODE_HPP_ +#define INCLUDE_INJA_NODE_HPP_ + +#include +#include +#include + +// #include "function_storage.hpp" +#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_ +#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_ + +#include +#include + +namespace inja { + +using Arguments = std::vector; +using CallbackFunction = std::function; +using VoidCallbackFunction = std::function; + +/*! + * \brief Class for builtin functions and user-defined callbacks. + */ +class FunctionStorage { +public: + enum class Operation { + Not, + And, + Or, + In, + Equal, + NotEqual, + Greater, + GreaterEqual, + Less, + LessEqual, + Add, + Subtract, + Multiplication, + Division, + Power, + Modulo, + AtId, + At, + Default, + DivisibleBy, + Even, + Exists, + ExistsInObject, + First, + Float, + Int, + IsArray, + IsBoolean, + IsFloat, + IsInteger, + IsNumber, + IsObject, + IsString, + Last, + Length, + Lower, + Max, + Min, + Odd, + Range, + Round, + Sort, + Upper, + Super, + Join, + Callback, + ParenLeft, + ParenRight, + None, + }; + + struct FunctionData { + explicit FunctionData(const Operation& op, const CallbackFunction& cb = CallbackFunction {}): operation(op), callback(cb) {} + const Operation operation; + const CallbackFunction callback; + }; + +private: + const int VARIADIC {-1}; + + std::map, FunctionData> function_storage = { + {std::make_pair("at", 2), FunctionData {Operation::At}}, + {std::make_pair("default", 2), FunctionData {Operation::Default}}, + {std::make_pair("divisibleBy", 2), FunctionData {Operation::DivisibleBy}}, + {std::make_pair("even", 1), FunctionData {Operation::Even}}, + {std::make_pair("exists", 1), FunctionData {Operation::Exists}}, + {std::make_pair("existsIn", 2), FunctionData {Operation::ExistsInObject}}, + {std::make_pair("first", 1), FunctionData {Operation::First}}, + {std::make_pair("float", 1), FunctionData {Operation::Float}}, + {std::make_pair("int", 1), FunctionData {Operation::Int}}, + {std::make_pair("isArray", 1), FunctionData {Operation::IsArray}}, + {std::make_pair("isBoolean", 1), FunctionData {Operation::IsBoolean}}, + {std::make_pair("isFloat", 1), FunctionData {Operation::IsFloat}}, + {std::make_pair("isInteger", 1), FunctionData {Operation::IsInteger}}, + {std::make_pair("isNumber", 1), FunctionData {Operation::IsNumber}}, + {std::make_pair("isObject", 1), FunctionData {Operation::IsObject}}, + {std::make_pair("isString", 1), FunctionData {Operation::IsString}}, + {std::make_pair("last", 1), FunctionData {Operation::Last}}, + {std::make_pair("length", 1), FunctionData {Operation::Length}}, + {std::make_pair("lower", 1), FunctionData {Operation::Lower}}, + {std::make_pair("max", 1), FunctionData {Operation::Max}}, + {std::make_pair("min", 1), FunctionData {Operation::Min}}, + {std::make_pair("odd", 1), FunctionData {Operation::Odd}}, + {std::make_pair("range", 1), FunctionData {Operation::Range}}, + {std::make_pair("round", 2), FunctionData {Operation::Round}}, + {std::make_pair("sort", 1), FunctionData {Operation::Sort}}, + {std::make_pair("upper", 1), FunctionData {Operation::Upper}}, + {std::make_pair("super", 0), FunctionData {Operation::Super}}, + {std::make_pair("super", 1), FunctionData {Operation::Super}}, + {std::make_pair("join", 2), FunctionData {Operation::Join}}, + }; + +public: + void add_builtin(std::string_view name, int num_args, Operation op) { + function_storage.emplace(std::make_pair(static_cast(name), num_args), FunctionData {op}); + } + + void add_callback(std::string_view name, int num_args, const CallbackFunction& callback) { + function_storage.emplace(std::make_pair(static_cast(name), num_args), FunctionData {Operation::Callback, callback}); + } + + FunctionData find_function(std::string_view name, int num_args) const { + auto it = function_storage.find(std::make_pair(static_cast(name), num_args)); + if (it != function_storage.end()) { + return it->second; + + // Find variadic function + } else if (num_args > 0) { + it = function_storage.find(std::make_pair(static_cast(name), VARIADIC)); + if (it != function_storage.end()) { + return it->second; + } + } + + return FunctionData {Operation::None}; + } +}; + +} // namespace inja + +#endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_ + +// #include "utils.hpp" +#ifndef INCLUDE_INJA_UTILS_HPP_ +#define INCLUDE_INJA_UTILS_HPP_ + +#include +#include +#include +#include +#include + +// #include "exceptions.hpp" +#ifndef INCLUDE_INJA_EXCEPTIONS_HPP_ +#define INCLUDE_INJA_EXCEPTIONS_HPP_ + +#include +#include + +namespace inja { + +struct SourceLocation { + size_t line; + size_t column; +}; + +struct InjaError : public std::runtime_error { + const std::string type; + const std::string message; + + const SourceLocation location; + + explicit InjaError(const std::string& type, const std::string& message) + : std::runtime_error("[inja.exception." + type + "] " + message), type(type), message(message), location({0, 0}) {} + + explicit InjaError(const std::string& type, const std::string& message, SourceLocation location) + : std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" + std::to_string(location.column) + ") " + message), + type(type), message(message), location(location) {} +}; + +struct ParserError : public InjaError { + explicit ParserError(const std::string& message, SourceLocation location): InjaError("parser_error", message, location) {} +}; + +struct RenderError : public InjaError { + explicit RenderError(const std::string& message, SourceLocation location): InjaError("render_error", message, location) {} +}; + +struct FileError : public InjaError { + explicit FileError(const std::string& message): InjaError("file_error", message) {} + explicit FileError(const std::string& message, SourceLocation location): InjaError("file_error", message, location) {} +}; + +struct DataError : public InjaError { + explicit DataError(const std::string& message, SourceLocation location): InjaError("data_error", message, location) {} +}; + +} // namespace inja + +#endif // INCLUDE_INJA_EXCEPTIONS_HPP_ + + +namespace inja { + +namespace string_view { +inline std::string_view slice(std::string_view view, size_t start, size_t end) { + start = std::min(start, view.size()); + end = std::min(std::max(start, end), view.size()); + return view.substr(start, end - start); +} + +inline std::pair split(std::string_view view, char Separator) { + size_t idx = view.find(Separator); + if (idx == std::string_view::npos) { + return std::make_pair(view, std::string_view()); + } + return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, std::string_view::npos)); +} + +inline bool starts_with(std::string_view view, std::string_view prefix) { + return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0); +} +} // namespace string_view + +inline SourceLocation get_source_location(std::string_view content, size_t pos) { + // Get line and offset position (starts at 1:1) + auto sliced = string_view::slice(content, 0, pos); + std::size_t last_newline = sliced.rfind("\n"); + + if (last_newline == std::string_view::npos) { + return {1, sliced.length() + 1}; + } + + // Count newlines + size_t count_lines = 0; + size_t search_start = 0; + while (search_start <= sliced.size()) { + search_start = sliced.find("\n", search_start) + 1; + if (search_start == 0) { + break; + } + count_lines += 1; + } + + return {count_lines + 1, sliced.length() - last_newline}; +} + +inline void replace_substring(std::string& s, const std::string& f, const std::string& t) { + if (f.empty()) { + return; + } + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +} // namespace inja + +#endif // INCLUDE_INJA_UTILS_HPP_ + + +namespace inja { + +class NodeVisitor; +class BlockNode; +class TextNode; +class ExpressionNode; +class LiteralNode; +class DataNode; +class FunctionNode; +class ExpressionListNode; +class StatementNode; +class ForStatementNode; +class ForArrayStatementNode; +class ForObjectStatementNode; +class IfStatementNode; +class IncludeStatementNode; +class ExtendsStatementNode; +class BlockStatementNode; +class SetStatementNode; + +class NodeVisitor { +public: + virtual ~NodeVisitor() = default; + + virtual void visit(const BlockNode& node) = 0; + virtual void visit(const TextNode& node) = 0; + virtual void visit(const ExpressionNode& node) = 0; + virtual void visit(const LiteralNode& node) = 0; + virtual void visit(const DataNode& node) = 0; + virtual void visit(const FunctionNode& node) = 0; + virtual void visit(const ExpressionListNode& node) = 0; + virtual void visit(const StatementNode& node) = 0; + virtual void visit(const ForStatementNode& node) = 0; + virtual void visit(const ForArrayStatementNode& node) = 0; + virtual void visit(const ForObjectStatementNode& node) = 0; + virtual void visit(const IfStatementNode& node) = 0; + virtual void visit(const IncludeStatementNode& node) = 0; + virtual void visit(const ExtendsStatementNode& node) = 0; + virtual void visit(const BlockStatementNode& node) = 0; + virtual void visit(const SetStatementNode& node) = 0; +}; + +/*! + * \brief Base node class for the abstract syntax tree (AST). + */ +class AstNode { +public: + virtual void accept(NodeVisitor& v) const = 0; + + size_t pos; + + AstNode(size_t pos): pos(pos) {} + virtual ~AstNode() {} +}; + +class BlockNode : public AstNode { +public: + std::vector> nodes; + + explicit BlockNode(): AstNode(0) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class TextNode : public AstNode { +public: + const size_t length; + + explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class ExpressionNode : public AstNode { +public: + explicit ExpressionNode(size_t pos): AstNode(pos) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class LiteralNode : public ExpressionNode { +public: + const json value; + + explicit LiteralNode(std::string_view data_text, size_t pos): ExpressionNode(pos), value(json::parse(data_text)) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class DataNode : public ExpressionNode { +public: + const std::string name; + const json::json_pointer ptr; + + static std::string convert_dot_to_ptr(std::string_view ptr_name) { + std::string result; + do { + std::string_view part; + std::tie(part, ptr_name) = string_view::split(ptr_name, '.'); + result.push_back('/'); + result.append(part.begin(), part.end()); + } while (!ptr_name.empty()); + return result; + } + + explicit DataNode(std::string_view ptr_name, size_t pos): ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_ptr(ptr_name))) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class FunctionNode : public ExpressionNode { + using Op = FunctionStorage::Operation; + +public: + enum class Associativity { + Left, + Right, + }; + + unsigned int precedence; + Associativity associativity; + + Op operation; + + std::string name; + int number_args; // Should also be negative -> -1 for unknown number + std::vector> arguments; + CallbackFunction callback; + + explicit FunctionNode(std::string_view name, size_t pos) + : ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) {} + explicit FunctionNode(Op operation, size_t pos): ExpressionNode(pos), operation(operation), number_args(1) { + switch (operation) { + case Op::Not: { + number_args = 1; + precedence = 4; + associativity = Associativity::Left; + } break; + case Op::And: { + number_args = 2; + precedence = 1; + associativity = Associativity::Left; + } break; + case Op::Or: { + number_args = 2; + precedence = 1; + associativity = Associativity::Left; + } break; + case Op::In: { + number_args = 2; + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::Equal: { + number_args = 2; + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::NotEqual: { + number_args = 2; + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::Greater: { + number_args = 2; + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::GreaterEqual: { + number_args = 2; + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::Less: { + number_args = 2; + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::LessEqual: { + number_args = 2; + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::Add: { + number_args = 2; + precedence = 3; + associativity = Associativity::Left; + } break; + case Op::Subtract: { + number_args = 2; + precedence = 3; + associativity = Associativity::Left; + } break; + case Op::Multiplication: { + number_args = 2; + precedence = 4; + associativity = Associativity::Left; + } break; + case Op::Division: { + number_args = 2; + precedence = 4; + associativity = Associativity::Left; + } break; + case Op::Power: { + number_args = 2; + precedence = 5; + associativity = Associativity::Right; + } break; + case Op::Modulo: { + number_args = 2; + precedence = 4; + associativity = Associativity::Left; + } break; + case Op::AtId: { + number_args = 2; + precedence = 8; + associativity = Associativity::Left; + } break; + default: { + precedence = 1; + associativity = Associativity::Left; + } + } + } + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class ExpressionListNode : public AstNode { +public: + std::shared_ptr root; + + explicit ExpressionListNode(): AstNode(0) {} + explicit ExpressionListNode(size_t pos): AstNode(pos) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class StatementNode : public AstNode { +public: + StatementNode(size_t pos): AstNode(pos) {} + + virtual void accept(NodeVisitor& v) const = 0; +}; + +class ForStatementNode : public StatementNode { +public: + ExpressionListNode condition; + BlockNode body; + BlockNode* const parent; + + ForStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent) {} + + virtual void accept(NodeVisitor& v) const = 0; +}; + +class ForArrayStatementNode : public ForStatementNode { +public: + const std::string value; + + explicit ForArrayStatementNode(const std::string& value, BlockNode* const parent, size_t pos): ForStatementNode(parent, pos), value(value) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class ForObjectStatementNode : public ForStatementNode { +public: + const std::string key; + const std::string value; + + explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode* const parent, size_t pos) + : ForStatementNode(parent, pos), key(key), value(value) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class IfStatementNode : public StatementNode { +public: + ExpressionListNode condition; + BlockNode true_statement; + BlockNode false_statement; + BlockNode* const parent; + + const bool is_nested; + bool has_false_statement {false}; + + explicit IfStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(false) {} + explicit IfStatementNode(bool is_nested, BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(is_nested) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class IncludeStatementNode : public StatementNode { +public: + const std::string file; + + explicit IncludeStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class ExtendsStatementNode : public StatementNode { +public: + const std::string file; + + explicit ExtendsStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + }; +}; + +class BlockStatementNode : public StatementNode { +public: + const std::string name; + BlockNode block; + BlockNode* const parent; + + explicit BlockStatementNode(BlockNode* const parent, const std::string& name, size_t pos): StatementNode(pos), name(name), parent(parent) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + }; +}; + +class SetStatementNode : public StatementNode { +public: + const std::string key; + ExpressionListNode expression; + + explicit SetStatementNode(const std::string& key, size_t pos): StatementNode(pos), key(key) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +} // namespace inja + +#endif // INCLUDE_INJA_NODE_HPP_ + +// #include "statistics.hpp" +#ifndef INCLUDE_INJA_STATISTICS_HPP_ +#define INCLUDE_INJA_STATISTICS_HPP_ + +// #include "node.hpp" + + +namespace inja { + +/*! + * \brief A class for counting statistics on a Template. + */ +class StatisticsVisitor : public NodeVisitor { + void visit(const BlockNode& node) { + for (auto& n : node.nodes) { + n->accept(*this); + } + } + + void visit(const TextNode&) {} + void visit(const ExpressionNode&) {} + void visit(const LiteralNode&) {} + + void visit(const DataNode&) { + variable_counter += 1; + } + + void visit(const FunctionNode& node) { + for (auto& n : node.arguments) { + n->accept(*this); + } + } + + void visit(const ExpressionListNode& node) { + node.root->accept(*this); + } + + void visit(const StatementNode&) {} + void visit(const ForStatementNode&) {} + + void visit(const ForArrayStatementNode& node) { + node.condition.accept(*this); + node.body.accept(*this); + } + + void visit(const ForObjectStatementNode& node) { + node.condition.accept(*this); + node.body.accept(*this); + } + + void visit(const IfStatementNode& node) { + node.condition.accept(*this); + node.true_statement.accept(*this); + node.false_statement.accept(*this); + } + + void visit(const IncludeStatementNode&) {} + + void visit(const ExtendsStatementNode&) {} + + void visit(const BlockStatementNode& node) { + node.block.accept(*this); + } + + void visit(const SetStatementNode&) {} + +public: + unsigned int variable_counter; + + explicit StatisticsVisitor(): variable_counter(0) {} +}; + +} // namespace inja + +#endif // INCLUDE_INJA_STATISTICS_HPP_ + + +namespace inja { + +/*! + * \brief The main inja Template. + */ +struct Template { + BlockNode root; + std::string content; + std::map> block_storage; + + explicit Template() {} + explicit Template(const std::string& content): content(content) {} + + /// Return number of variables (total number, not distinct ones) in the template + int count_variables() { + auto statistic_visitor = StatisticsVisitor(); + root.accept(statistic_visitor); + return statistic_visitor.variable_counter; + } +}; + +using TemplateStorage = std::map; + +} // namespace inja + +#endif // INCLUDE_INJA_TEMPLATE_HPP_ + + +namespace inja { + +/*! + * \brief Class for lexer configuration. + */ +struct LexerConfig { + std::string statement_open {"{%"}; + std::string statement_open_no_lstrip {"{%+"}; + std::string statement_open_force_lstrip {"{%-"}; + std::string statement_close {"%}"}; + std::string statement_close_force_rstrip {"-%}"}; + std::string line_statement {"##"}; + std::string expression_open {"{{"}; + std::string expression_open_force_lstrip {"{{-"}; + std::string expression_close {"}}"}; + std::string expression_close_force_rstrip {"-}}"}; + std::string comment_open {"{#"}; + std::string comment_open_force_lstrip {"{#-"}; + std::string comment_close {"#}"}; + std::string comment_close_force_rstrip {"-#}"}; + std::string open_chars {"#{"}; + + bool trim_blocks {false}; + bool lstrip_blocks {false}; + + void update_open_chars() { + open_chars = ""; + if (open_chars.find(line_statement[0]) == std::string::npos) { + open_chars += line_statement[0]; + } + if (open_chars.find(statement_open[0]) == std::string::npos) { + open_chars += statement_open[0]; + } + if (open_chars.find(statement_open_no_lstrip[0]) == std::string::npos) { + open_chars += statement_open_no_lstrip[0]; + } + if (open_chars.find(statement_open_force_lstrip[0]) == std::string::npos) { + open_chars += statement_open_force_lstrip[0]; + } + if (open_chars.find(expression_open[0]) == std::string::npos) { + open_chars += expression_open[0]; + } + if (open_chars.find(expression_open_force_lstrip[0]) == std::string::npos) { + open_chars += expression_open_force_lstrip[0]; + } + if (open_chars.find(comment_open[0]) == std::string::npos) { + open_chars += comment_open[0]; + } + if (open_chars.find(comment_open_force_lstrip[0]) == std::string::npos) { + open_chars += comment_open_force_lstrip[0]; + } + } +}; + +/*! + * \brief Class for parser configuration. + */ +struct ParserConfig { + bool search_included_templates_in_files {true}; + + std::function include_callback; +}; + +/*! + * \brief Class for render configuration. + */ +struct RenderConfig { + bool throw_at_missing_includes {true}; +}; + +} // namespace inja + +#endif // INCLUDE_INJA_CONFIG_HPP_ + +// #include "function_storage.hpp" + +// #include "parser.hpp" +#ifndef INCLUDE_INJA_PARSER_HPP_ +#define INCLUDE_INJA_PARSER_HPP_ + +#include +#include +#include +#include +#include + +// #include "config.hpp" + +// #include "exceptions.hpp" + +// #include "function_storage.hpp" + +// #include "lexer.hpp" +#ifndef INCLUDE_INJA_LEXER_HPP_ +#define INCLUDE_INJA_LEXER_HPP_ + +#include +#include + +// #include "config.hpp" + +// #include "token.hpp" +#ifndef INCLUDE_INJA_TOKEN_HPP_ +#define INCLUDE_INJA_TOKEN_HPP_ + +#include +#include + +namespace inja { + +/*! + * \brief Helper-class for the inja Lexer. + */ +struct Token { + enum class Kind { + Text, + ExpressionOpen, // {{ + ExpressionClose, // }} + LineStatementOpen, // ## + LineStatementClose, // \n + StatementOpen, // {% + StatementClose, // %} + CommentOpen, // {# + CommentClose, // #} + Id, // this, this.foo + Number, // 1, 2, -1, 5.2, -5.3 + String, // "this" + Plus, // + + Minus, // - + Times, // * + Slash, // / + Percent, // % + Power, // ^ + Comma, // , + Dot, // . + Colon, // : + LeftParen, // ( + RightParen, // ) + LeftBracket, // [ + RightBracket, // ] + LeftBrace, // { + RightBrace, // } + Equal, // == + NotEqual, // != + GreaterThan, // > + GreaterEqual, // >= + LessThan, // < + LessEqual, // <= + Unknown, + Eof, + }; + + Kind kind {Kind::Unknown}; + std::string_view text; + + explicit constexpr Token() = default; + explicit constexpr Token(Kind kind, std::string_view text): kind(kind), text(text) {} + + std::string describe() const { + switch (kind) { + case Kind::Text: + return ""; + case Kind::LineStatementClose: + return ""; + case Kind::Eof: + return ""; + default: + return static_cast(text); + } + } +}; + +} // namespace inja + +#endif // INCLUDE_INJA_TOKEN_HPP_ + +// #include "utils.hpp" + + +namespace inja { + +/*! + * \brief Class for lexing an inja Template. + */ +class Lexer { + enum class State { + Text, + ExpressionStart, + ExpressionStartForceLstrip, + ExpressionBody, + LineStart, + LineBody, + StatementStart, + StatementStartNoLstrip, + StatementStartForceLstrip, + StatementBody, + CommentStart, + CommentStartForceLstrip, + CommentBody, + }; + + enum class MinusState { + Operator, + Number, + }; + + const LexerConfig& config; + + State state; + MinusState minus_state; + std::string_view m_in; + size_t tok_start; + size_t pos; + + Token scan_body(std::string_view close, Token::Kind closeKind, std::string_view close_trim = std::string_view(), bool trim = false) { + again: + // skip whitespace (except for \n as it might be a close) + if (tok_start >= m_in.size()) { + return make_token(Token::Kind::Eof); + } + const char ch = m_in[tok_start]; + if (ch == ' ' || ch == '\t' || ch == '\r') { + tok_start += 1; + goto again; + } + + // check for close + if (!close_trim.empty() && inja::string_view::starts_with(m_in.substr(tok_start), close_trim)) { + state = State::Text; + pos = tok_start + close_trim.size(); + const Token tok = make_token(closeKind); + skip_whitespaces_and_newlines(); + return tok; + } + + if (inja::string_view::starts_with(m_in.substr(tok_start), close)) { + state = State::Text; + pos = tok_start + close.size(); + const Token tok = make_token(closeKind); + if (trim) { + skip_whitespaces_and_first_newline(); + } + return tok; + } + + // skip \n + if (ch == '\n') { + tok_start += 1; + goto again; + } + + pos = tok_start + 1; + if (std::isalpha(ch)) { + minus_state = MinusState::Operator; + return scan_id(); + } + + const MinusState current_minus_state = minus_state; + if (minus_state == MinusState::Operator) { + minus_state = MinusState::Number; + } + + switch (ch) { + case '+': + return make_token(Token::Kind::Plus); + case '-': + if (current_minus_state == MinusState::Operator) { + return make_token(Token::Kind::Minus); + } + return scan_number(); + case '*': + return make_token(Token::Kind::Times); + case '/': + return make_token(Token::Kind::Slash); + case '^': + return make_token(Token::Kind::Power); + case '%': + return make_token(Token::Kind::Percent); + case '.': + return make_token(Token::Kind::Dot); + case ',': + return make_token(Token::Kind::Comma); + case ':': + return make_token(Token::Kind::Colon); + case '(': + return make_token(Token::Kind::LeftParen); + case ')': + minus_state = MinusState::Operator; + return make_token(Token::Kind::RightParen); + case '[': + return make_token(Token::Kind::LeftBracket); + case ']': + minus_state = MinusState::Operator; + return make_token(Token::Kind::RightBracket); + case '{': + return make_token(Token::Kind::LeftBrace); + case '}': + minus_state = MinusState::Operator; + return make_token(Token::Kind::RightBrace); + case '>': + if (pos < m_in.size() && m_in[pos] == '=') { + pos += 1; + return make_token(Token::Kind::GreaterEqual); + } + return make_token(Token::Kind::GreaterThan); + case '<': + if (pos < m_in.size() && m_in[pos] == '=') { + pos += 1; + return make_token(Token::Kind::LessEqual); + } + return make_token(Token::Kind::LessThan); + case '=': + if (pos < m_in.size() && m_in[pos] == '=') { + pos += 1; + return make_token(Token::Kind::Equal); + } + return make_token(Token::Kind::Unknown); + case '!': + if (pos < m_in.size() && m_in[pos] == '=') { + pos += 1; + return make_token(Token::Kind::NotEqual); + } + return make_token(Token::Kind::Unknown); + case '\"': + return scan_string(); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + minus_state = MinusState::Operator; + return scan_number(); + case '_': + case '@': + case '$': + minus_state = MinusState::Operator; + return scan_id(); + default: + return make_token(Token::Kind::Unknown); + } + } + + Token scan_id() { + for (;;) { + if (pos >= m_in.size()) { + break; + } + const char ch = m_in[pos]; + if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') { + break; + } + pos += 1; + } + return make_token(Token::Kind::Id); + } + + Token scan_number() { + for (;;) { + if (pos >= m_in.size()) { + break; + } + const char ch = m_in[pos]; + // be very permissive in lexer (we'll catch errors when conversion happens) + if (!(std::isdigit(ch) || ch == '.' || ch == 'e' || ch == 'E' || (ch == '+' && (pos == 0 || m_in[pos-1] == 'e' || m_in[pos-1] == 'E')) || (ch == '-' && (pos == 0 || m_in[pos-1] == 'e' || m_in[pos-1] == 'E')))) { + break; + } + pos += 1; + } + return make_token(Token::Kind::Number); + } + + Token scan_string() { + bool escape {false}; + for (;;) { + if (pos >= m_in.size()) { + break; + } + const char ch = m_in[pos++]; + if (ch == '\\') { + escape = true; + } else if (!escape && ch == m_in[tok_start]) { + break; + } else { + escape = false; + } + } + return make_token(Token::Kind::String); + } + + Token make_token(Token::Kind kind) const { + return Token(kind, string_view::slice(m_in, tok_start, pos)); + } + + void skip_whitespaces_and_newlines() { + if (pos < m_in.size()) { + while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t' || m_in[pos] == '\n' || m_in[pos] == '\r')) { + pos += 1; + } + } + } + + void skip_whitespaces_and_first_newline() { + if (pos < m_in.size()) { + while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t')) { + pos += 1; + } + } + + if (pos < m_in.size()) { + const char ch = m_in[pos]; + if (ch == '\n') { + pos += 1; + } else if (ch == '\r') { + pos += 1; + if (pos < m_in.size() && m_in[pos] == '\n') { + pos += 1; + } + } + } + } + + static std::string_view clear_final_line_if_whitespace(std::string_view text) { + std::string_view result = text; + while (!result.empty()) { + const char ch = result.back(); + if (ch == ' ' || ch == '\t') { + result.remove_suffix(1); + } else if (ch == '\n' || ch == '\r') { + break; + } else { + return text; + } + } + return result; + } + +public: + explicit Lexer(const LexerConfig& config): config(config), state(State::Text), minus_state(MinusState::Number) {} + + SourceLocation current_position() const { + return get_source_location(m_in, tok_start); + } + + void start(std::string_view input) { + m_in = input; + tok_start = 0; + pos = 0; + state = State::Text; + minus_state = MinusState::Number; + + // Consume byte order mark (BOM) for UTF-8 + if (inja::string_view::starts_with(m_in, "\xEF\xBB\xBF")) { + m_in = m_in.substr(3); + } + } + + Token scan() { + tok_start = pos; + + again: + if (tok_start >= m_in.size()) { + return make_token(Token::Kind::Eof); + } + + switch (state) { + default: + case State::Text: { + // fast-scan to first open character + const size_t open_start = m_in.substr(pos).find_first_of(config.open_chars); + if (open_start == std::string_view::npos) { + // didn't find open, return remaining text as text token + pos = m_in.size(); + return make_token(Token::Kind::Text); + } + pos += open_start; + + // try to match one of the opening sequences, and get the close + std::string_view open_str = m_in.substr(pos); + bool must_lstrip = false; + if (inja::string_view::starts_with(open_str, config.expression_open)) { + if (inja::string_view::starts_with(open_str, config.expression_open_force_lstrip)) { + state = State::ExpressionStartForceLstrip; + must_lstrip = true; + } else { + state = State::ExpressionStart; + } + } else if (inja::string_view::starts_with(open_str, config.statement_open)) { + if (inja::string_view::starts_with(open_str, config.statement_open_no_lstrip)) { + state = State::StatementStartNoLstrip; + } else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip)) { + state = State::StatementStartForceLstrip; + must_lstrip = true; + } else { + state = State::StatementStart; + must_lstrip = config.lstrip_blocks; + } + } else if (inja::string_view::starts_with(open_str, config.comment_open)) { + if (inja::string_view::starts_with(open_str, config.comment_open_force_lstrip)) { + state = State::CommentStartForceLstrip; + must_lstrip = true; + } else { + state = State::CommentStart; + must_lstrip = config.lstrip_blocks; + } + } else if ((pos == 0 || m_in[pos - 1] == '\n') && inja::string_view::starts_with(open_str, config.line_statement)) { + state = State::LineStart; + } else { + pos += 1; // wasn't actually an opening sequence + goto again; + } + + std::string_view text = string_view::slice(m_in, tok_start, pos); + if (must_lstrip) { + text = clear_final_line_if_whitespace(text); + } + + if (text.empty()) { + goto again; // don't generate empty token + } + return Token(Token::Kind::Text, text); + } + case State::ExpressionStart: { + state = State::ExpressionBody; + pos += config.expression_open.size(); + return make_token(Token::Kind::ExpressionOpen); + } + case State::ExpressionStartForceLstrip: { + state = State::ExpressionBody; + pos += config.expression_open_force_lstrip.size(); + return make_token(Token::Kind::ExpressionOpen); + } + case State::LineStart: { + state = State::LineBody; + pos += config.line_statement.size(); + return make_token(Token::Kind::LineStatementOpen); + } + case State::StatementStart: { + state = State::StatementBody; + pos += config.statement_open.size(); + return make_token(Token::Kind::StatementOpen); + } + case State::StatementStartNoLstrip: { + state = State::StatementBody; + pos += config.statement_open_no_lstrip.size(); + return make_token(Token::Kind::StatementOpen); + } + case State::StatementStartForceLstrip: { + state = State::StatementBody; + pos += config.statement_open_force_lstrip.size(); + return make_token(Token::Kind::StatementOpen); + } + case State::CommentStart: { + state = State::CommentBody; + pos += config.comment_open.size(); + return make_token(Token::Kind::CommentOpen); + } + case State::CommentStartForceLstrip: { + state = State::CommentBody; + pos += config.comment_open_force_lstrip.size(); + return make_token(Token::Kind::CommentOpen); + } + case State::ExpressionBody: + return scan_body(config.expression_close, Token::Kind::ExpressionClose, config.expression_close_force_rstrip); + case State::LineBody: + return scan_body("\n", Token::Kind::LineStatementClose); + case State::StatementBody: + return scan_body(config.statement_close, Token::Kind::StatementClose, config.statement_close_force_rstrip, config.trim_blocks); + case State::CommentBody: { + // fast-scan to comment close + const size_t end = m_in.substr(pos).find(config.comment_close); + if (end == std::string_view::npos) { + pos = m_in.size(); + return make_token(Token::Kind::Eof); + } + + // Check for trim pattern + const bool must_rstrip = inja::string_view::starts_with(m_in.substr(pos + end - 1), config.comment_close_force_rstrip); + + // return the entire comment in the close token + state = State::Text; + pos += end + config.comment_close.size(); + Token tok = make_token(Token::Kind::CommentClose); + + if (must_rstrip || config.trim_blocks) { + skip_whitespaces_and_first_newline(); + } + return tok; + } + } + } + + const LexerConfig& get_config() const { + return config; + } +}; + +} // namespace inja + +#endif // INCLUDE_INJA_LEXER_HPP_ + +// #include "node.hpp" + +// #include "template.hpp" + +// #include "token.hpp" + +// #include "utils.hpp" + + +namespace inja { + +/*! + * \brief Class for parsing an inja Template. + */ +class Parser { + const ParserConfig& config; + + Lexer lexer; + TemplateStorage& template_storage; + const FunctionStorage& function_storage; + + Token tok, peek_tok; + bool have_peek_tok {false}; + + size_t current_paren_level {0}; + size_t current_bracket_level {0}; + size_t current_brace_level {0}; + + std::string_view literal_start; + + BlockNode* current_block {nullptr}; + ExpressionListNode* current_expression_list {nullptr}; + std::stack> function_stack; + std::vector> arguments; + + std::stack> operator_stack; + std::stack if_statement_stack; + std::stack for_statement_stack; + std::stack block_statement_stack; + + inline void throw_parser_error(const std::string& message) const { + INJA_THROW(ParserError(message, lexer.current_position())); + } + + inline void get_next_token() { + if (have_peek_tok) { + tok = peek_tok; + have_peek_tok = false; + } else { + tok = lexer.scan(); + } + } + + inline void get_peek_token() { + if (!have_peek_tok) { + peek_tok = lexer.scan(); + have_peek_tok = true; + } + } + + inline void add_literal(const char* content_ptr) { + std::string_view data_text(literal_start.data(), tok.text.data() - literal_start.data() + tok.text.size()); + arguments.emplace_back(std::make_shared(data_text, data_text.data() - content_ptr)); + } + + inline void add_operator() { + auto function = operator_stack.top(); + operator_stack.pop(); + + for (int i = 0; i < function->number_args; ++i) { + function->arguments.insert(function->arguments.begin(), arguments.back()); + arguments.pop_back(); + } + arguments.emplace_back(function); + } + + void add_to_template_storage(std::string_view path, std::string& template_name) { + if (template_storage.find(template_name) != template_storage.end()) { + return; + } + + std::string original_path = static_cast(path); + std::string original_name = template_name; + + if (config.search_included_templates_in_files) { + // Build the relative path + template_name = original_path + original_name; + if (template_name.compare(0, 2, "./") == 0) { + template_name.erase(0, 2); + } + + if (template_storage.find(template_name) == template_storage.end()) { + // Load file + std::ifstream file; + file.open(template_name); + if (!file.fail()) { + std::string text((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + + auto include_template = Template(text); + template_storage.emplace(template_name, include_template); + parse_into_template(template_storage[template_name], template_name); + return; + } else if (!config.include_callback) { + INJA_THROW(FileError("failed accessing file at '" + template_name + "'")); + } + } + } + + // Try include callback + if (config.include_callback) { + auto include_template = config.include_callback(original_path, original_name); + template_storage.emplace(template_name, include_template); + } + } + + std::string parse_filename(const Token& tok) const { + if (tok.kind != Token::Kind::String) { + throw_parser_error("expected string, got '" + tok.describe() + "'"); + } + + if (tok.text.length() < 2) { + throw_parser_error("expected filename, got '" + static_cast(tok.text) + "'"); + } + + // Remove first and last character "" + return std::string {tok.text.substr(1, tok.text.length() - 2)}; + } + + bool parse_expression(Template& tmpl, Token::Kind closing) { + while (tok.kind != closing && tok.kind != Token::Kind::Eof) { + // Literals + switch (tok.kind) { + case Token::Kind::String: { + if (current_brace_level == 0 && current_bracket_level == 0) { + literal_start = tok.text; + add_literal(tmpl.content.c_str()); + } + } break; + case Token::Kind::Number: { + if (current_brace_level == 0 && current_bracket_level == 0) { + literal_start = tok.text; + add_literal(tmpl.content.c_str()); + } + } break; + case Token::Kind::LeftBracket: { + if (current_brace_level == 0 && current_bracket_level == 0) { + literal_start = tok.text; + } + current_bracket_level += 1; + } break; + case Token::Kind::LeftBrace: { + if (current_brace_level == 0 && current_bracket_level == 0) { + literal_start = tok.text; + } + current_brace_level += 1; + } break; + case Token::Kind::RightBracket: { + if (current_bracket_level == 0) { + throw_parser_error("unexpected ']'"); + } + + current_bracket_level -= 1; + if (current_brace_level == 0 && current_bracket_level == 0) { + add_literal(tmpl.content.c_str()); + } + } break; + case Token::Kind::RightBrace: { + if (current_brace_level == 0) { + throw_parser_error("unexpected '}'"); + } + + current_brace_level -= 1; + if (current_brace_level == 0 && current_bracket_level == 0) { + add_literal(tmpl.content.c_str()); + } + } break; + case Token::Kind::Id: { + get_peek_token(); + + // Data Literal + if (tok.text == static_cast("true") || tok.text == static_cast("false") || + tok.text == static_cast("null")) { + if (current_brace_level == 0 && current_bracket_level == 0) { + literal_start = tok.text; + add_literal(tmpl.content.c_str()); + } + + // Operator + } else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") { + goto parse_operator; + + // Functions + } else if (peek_tok.kind == Token::Kind::LeftParen) { + operator_stack.emplace(std::make_shared(static_cast(tok.text), tok.text.data() - tmpl.content.c_str())); + function_stack.emplace(operator_stack.top().get(), current_paren_level); + + // Variables + } else { + arguments.emplace_back(std::make_shared(static_cast(tok.text), tok.text.data() - tmpl.content.c_str())); + } + + // Operators + } break; + case Token::Kind::Equal: + case Token::Kind::NotEqual: + case Token::Kind::GreaterThan: + case Token::Kind::GreaterEqual: + case Token::Kind::LessThan: + case Token::Kind::LessEqual: + case Token::Kind::Plus: + case Token::Kind::Minus: + case Token::Kind::Times: + case Token::Kind::Slash: + case Token::Kind::Power: + case Token::Kind::Percent: + case Token::Kind::Dot: { + + parse_operator: + FunctionStorage::Operation operation; + switch (tok.kind) { + case Token::Kind::Id: { + if (tok.text == "and") { + operation = FunctionStorage::Operation::And; + } else if (tok.text == "or") { + operation = FunctionStorage::Operation::Or; + } else if (tok.text == "in") { + operation = FunctionStorage::Operation::In; + } else if (tok.text == "not") { + operation = FunctionStorage::Operation::Not; + } else { + throw_parser_error("unknown operator in parser."); + } + } break; + case Token::Kind::Equal: { + operation = FunctionStorage::Operation::Equal; + } break; + case Token::Kind::NotEqual: { + operation = FunctionStorage::Operation::NotEqual; + } break; + case Token::Kind::GreaterThan: { + operation = FunctionStorage::Operation::Greater; + } break; + case Token::Kind::GreaterEqual: { + operation = FunctionStorage::Operation::GreaterEqual; + } break; + case Token::Kind::LessThan: { + operation = FunctionStorage::Operation::Less; + } break; + case Token::Kind::LessEqual: { + operation = FunctionStorage::Operation::LessEqual; + } break; + case Token::Kind::Plus: { + operation = FunctionStorage::Operation::Add; + } break; + case Token::Kind::Minus: { + operation = FunctionStorage::Operation::Subtract; + } break; + case Token::Kind::Times: { + operation = FunctionStorage::Operation::Multiplication; + } break; + case Token::Kind::Slash: { + operation = FunctionStorage::Operation::Division; + } break; + case Token::Kind::Power: { + operation = FunctionStorage::Operation::Power; + } break; + case Token::Kind::Percent: { + operation = FunctionStorage::Operation::Modulo; + } break; + case Token::Kind::Dot: { + operation = FunctionStorage::Operation::AtId; + } break; + default: { + throw_parser_error("unknown operator in parser."); + } + } + auto function_node = std::make_shared(operation, tok.text.data() - tmpl.content.c_str()); + + while (!operator_stack.empty() && + ((operator_stack.top()->precedence > function_node->precedence) || + (operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) && + (operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) { + add_operator(); + } + + operator_stack.emplace(function_node); + } break; + case Token::Kind::Comma: { + if (current_brace_level == 0 && current_bracket_level == 0) { + if (function_stack.empty()) { + throw_parser_error("unexpected ','"); + } + + function_stack.top().first->number_args += 1; + } + } break; + case Token::Kind::Colon: { + if (current_brace_level == 0 && current_bracket_level == 0) { + throw_parser_error("unexpected ':'"); + } + } break; + case Token::Kind::LeftParen: { + current_paren_level += 1; + operator_stack.emplace(std::make_shared(FunctionStorage::Operation::ParenLeft, tok.text.data() - tmpl.content.c_str())); + + get_peek_token(); + if (peek_tok.kind == Token::Kind::RightParen) { + if (!function_stack.empty() && function_stack.top().second == current_paren_level - 1) { + function_stack.top().first->number_args = 0; + } + } + } break; + case Token::Kind::RightParen: { + current_paren_level -= 1; + while (!operator_stack.empty() && operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft) { + add_operator(); + } + + if (!operator_stack.empty() && operator_stack.top()->operation == FunctionStorage::Operation::ParenLeft) { + operator_stack.pop(); + } + + if (!function_stack.empty() && function_stack.top().second == current_paren_level) { + auto func = function_stack.top().first; + auto function_data = function_storage.find_function(func->name, func->number_args); + if (function_data.operation == FunctionStorage::Operation::None) { + throw_parser_error("unknown function " + func->name); + } + func->operation = function_data.operation; + if (function_data.operation == FunctionStorage::Operation::Callback) { + func->callback = function_data.callback; + } + + if (operator_stack.empty()) { + throw_parser_error("internal error at function " + func->name); + } + + add_operator(); + function_stack.pop(); + } + } + default: + break; + } + + get_next_token(); + } + + while (!operator_stack.empty()) { + add_operator(); + } + + if (arguments.size() == 1) { + current_expression_list->root = arguments[0]; + arguments = {}; + } else if (arguments.size() > 1) { + throw_parser_error("malformed expression"); + } + + return true; + } + + bool parse_statement(Template& tmpl, Token::Kind closing, std::string_view path) { + if (tok.kind != Token::Kind::Id) { + return false; + } + + if (tok.text == static_cast("if")) { + get_next_token(); + + auto if_statement_node = std::make_shared(current_block, tok.text.data() - tmpl.content.c_str()); + current_block->nodes.emplace_back(if_statement_node); + if_statement_stack.emplace(if_statement_node.get()); + current_block = &if_statement_node->true_statement; + current_expression_list = &if_statement_node->condition; + + if (!parse_expression(tmpl, closing)) { + return false; + } + } else if (tok.text == static_cast("else")) { + if (if_statement_stack.empty()) { + throw_parser_error("else without matching if"); + } + auto& if_statement_data = if_statement_stack.top(); + get_next_token(); + + if_statement_data->has_false_statement = true; + current_block = &if_statement_data->false_statement; + + // Chained else if + if (tok.kind == Token::Kind::Id && tok.text == static_cast("if")) { + get_next_token(); + + auto if_statement_node = std::make_shared(true, current_block, tok.text.data() - tmpl.content.c_str()); + current_block->nodes.emplace_back(if_statement_node); + if_statement_stack.emplace(if_statement_node.get()); + current_block = &if_statement_node->true_statement; + current_expression_list = &if_statement_node->condition; + + if (!parse_expression(tmpl, closing)) { + return false; + } + } + } else if (tok.text == static_cast("endif")) { + if (if_statement_stack.empty()) { + throw_parser_error("endif without matching if"); + } + + // Nested if statements + while (if_statement_stack.top()->is_nested) { + if_statement_stack.pop(); + } + + auto& if_statement_data = if_statement_stack.top(); + get_next_token(); + + current_block = if_statement_data->parent; + if_statement_stack.pop(); + } else if (tok.text == static_cast("block")) { + get_next_token(); + + if (tok.kind != Token::Kind::Id) { + throw_parser_error("expected block name, got '" + tok.describe() + "'"); + } + + const std::string block_name = static_cast(tok.text); + + auto block_statement_node = std::make_shared(current_block, block_name, tok.text.data() - tmpl.content.c_str()); + current_block->nodes.emplace_back(block_statement_node); + block_statement_stack.emplace(block_statement_node.get()); + current_block = &block_statement_node->block; + auto success = tmpl.block_storage.emplace(block_name, block_statement_node); + if (!success.second) { + throw_parser_error("block with the name '" + block_name + "' does already exist"); + } + + get_next_token(); + } else if (tok.text == static_cast("endblock")) { + if (block_statement_stack.empty()) { + throw_parser_error("endblock without matching block"); + } + + auto& block_statement_data = block_statement_stack.top(); + get_next_token(); + + current_block = block_statement_data->parent; + block_statement_stack.pop(); + } else if (tok.text == static_cast("for")) { + get_next_token(); + + // options: for a in arr; for a, b in obj + if (tok.kind != Token::Kind::Id) { + throw_parser_error("expected id, got '" + tok.describe() + "'"); + } + + Token value_token = tok; + get_next_token(); + + // Object type + std::shared_ptr for_statement_node; + if (tok.kind == Token::Kind::Comma) { + get_next_token(); + if (tok.kind != Token::Kind::Id) { + throw_parser_error("expected id, got '" + tok.describe() + "'"); + } + + Token key_token = std::move(value_token); + value_token = tok; + get_next_token(); + + for_statement_node = std::make_shared(static_cast(key_token.text), static_cast(value_token.text), + current_block, tok.text.data() - tmpl.content.c_str()); + + // Array type + } else { + for_statement_node = + std::make_shared(static_cast(value_token.text), current_block, tok.text.data() - tmpl.content.c_str()); + } + + current_block->nodes.emplace_back(for_statement_node); + for_statement_stack.emplace(for_statement_node.get()); + current_block = &for_statement_node->body; + current_expression_list = &for_statement_node->condition; + + if (tok.kind != Token::Kind::Id || tok.text != static_cast("in")) { + throw_parser_error("expected 'in', got '" + tok.describe() + "'"); + } + get_next_token(); + + if (!parse_expression(tmpl, closing)) { + return false; + } + } else if (tok.text == static_cast("endfor")) { + if (for_statement_stack.empty()) { + throw_parser_error("endfor without matching for"); + } + + auto& for_statement_data = for_statement_stack.top(); + get_next_token(); + + current_block = for_statement_data->parent; + for_statement_stack.pop(); + } else if (tok.text == static_cast("include")) { + get_next_token(); + + std::string template_name = parse_filename(tok); + add_to_template_storage(path, template_name); + + current_block->nodes.emplace_back(std::make_shared(template_name, tok.text.data() - tmpl.content.c_str())); + + get_next_token(); + } else if (tok.text == static_cast("extends")) { + get_next_token(); + + std::string template_name = parse_filename(tok); + add_to_template_storage(path, template_name); + + current_block->nodes.emplace_back(std::make_shared(template_name, tok.text.data() - tmpl.content.c_str())); + + get_next_token(); + } else if (tok.text == static_cast("set")) { + get_next_token(); + + if (tok.kind != Token::Kind::Id) { + throw_parser_error("expected variable name, got '" + tok.describe() + "'"); + } + + std::string key = static_cast(tok.text); + get_next_token(); + + auto set_statement_node = std::make_shared(key, tok.text.data() - tmpl.content.c_str()); + current_block->nodes.emplace_back(set_statement_node); + current_expression_list = &set_statement_node->expression; + + if (tok.text != static_cast("=")) { + throw_parser_error("expected '=', got '" + tok.describe() + "'"); + } + get_next_token(); + + if (!parse_expression(tmpl, closing)) { + return false; + } + } else { + return false; + } + return true; + } + + void parse_into(Template& tmpl, std::string_view path) { + lexer.start(tmpl.content); + current_block = &tmpl.root; + + for (;;) { + get_next_token(); + switch (tok.kind) { + case Token::Kind::Eof: { + if (!if_statement_stack.empty()) { + throw_parser_error("unmatched if"); + } + if (!for_statement_stack.empty()) { + throw_parser_error("unmatched for"); + } + } + return; + case Token::Kind::Text: { + current_block->nodes.emplace_back(std::make_shared(tok.text.data() - tmpl.content.c_str(), tok.text.size())); + } break; + case Token::Kind::StatementOpen: { + get_next_token(); + if (!parse_statement(tmpl, Token::Kind::StatementClose, path)) { + throw_parser_error("expected statement, got '" + tok.describe() + "'"); + } + if (tok.kind != Token::Kind::StatementClose) { + throw_parser_error("expected statement close, got '" + tok.describe() + "'"); + } + } break; + case Token::Kind::LineStatementOpen: { + get_next_token(); + if (!parse_statement(tmpl, Token::Kind::LineStatementClose, path)) { + throw_parser_error("expected statement, got '" + tok.describe() + "'"); + } + if (tok.kind != Token::Kind::LineStatementClose && tok.kind != Token::Kind::Eof) { + throw_parser_error("expected line statement close, got '" + tok.describe() + "'"); + } + } break; + case Token::Kind::ExpressionOpen: { + get_next_token(); + + auto expression_list_node = std::make_shared(tok.text.data() - tmpl.content.c_str()); + current_block->nodes.emplace_back(expression_list_node); + current_expression_list = expression_list_node.get(); + + if (!parse_expression(tmpl, Token::Kind::ExpressionClose)) { + throw_parser_error("expected expression, got '" + tok.describe() + "'"); + } + + if (tok.kind != Token::Kind::ExpressionClose) { + throw_parser_error("expected expression close, got '" + tok.describe() + "'"); + } + } break; + case Token::Kind::CommentOpen: { + get_next_token(); + if (tok.kind != Token::Kind::CommentClose) { + throw_parser_error("expected comment close, got '" + tok.describe() + "'"); + } + } break; + default: { + throw_parser_error("unexpected token '" + tok.describe() + "'"); + } break; + } + } + } + +public: + explicit Parser(const ParserConfig& parser_config, const LexerConfig& lexer_config, TemplateStorage& template_storage, + const FunctionStorage& function_storage) + : config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) {} + + Template parse(std::string_view input, std::string_view path) { + auto result = Template(static_cast(input)); + parse_into(result, path); + return result; + } + + Template parse(std::string_view input) { + return parse(input, "./"); + } + + void parse_into_template(Template& tmpl, std::string_view filename) { + std::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1); + + // StringRef path = sys::path::parent_path(filename); + auto sub_parser = Parser(config, lexer.get_config(), template_storage, function_storage); + sub_parser.parse_into(tmpl, path); + } + + std::string load_file(const std::string& filename) { + std::ifstream file; + file.open(filename); + if (file.fail()) { + INJA_THROW(FileError("failed accessing file at '" + filename + "'")); + } + std::string text((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + return text; + } +}; + +} // namespace inja + +#endif // INCLUDE_INJA_PARSER_HPP_ + +// #include "renderer.hpp" +#ifndef INCLUDE_INJA_RENDERER_HPP_ +#define INCLUDE_INJA_RENDERER_HPP_ + +#include +#include +#include +#include +#include + +// #include "config.hpp" + +// #include "exceptions.hpp" + +// #include "node.hpp" + +// #include "template.hpp" + +// #include "utils.hpp" + + +namespace inja { + +/*! + * \brief Class for rendering a Template with data. + */ +class Renderer : public NodeVisitor { + using Op = FunctionStorage::Operation; + + const RenderConfig config; + const TemplateStorage& template_storage; + const FunctionStorage& function_storage; + + const Template* current_template; + size_t current_level {0}; + std::vector template_stack; + std::vector block_statement_stack; + + const json* data_input; + std::ostream* output_stream; + + json additional_data; + json* current_loop_data = &additional_data["loop"]; + + std::vector> data_tmp_stack; + std::stack data_eval_stack; + std::stack not_found_stack; + + bool break_rendering {false}; + + static bool truthy(const json* data) { + if (data->is_boolean()) { + return data->get(); + } else if (data->is_number()) { + return (*data != 0); + } else if (data->is_null()) { + return false; + } + return !data->empty(); + } + + void print_data(const std::shared_ptr value) { + if (value->is_string()) { + *output_stream << value->get_ref(); + } else if (value->is_number_integer()) { + *output_stream << value->get(); + } else if (value->is_null()) { + } else { + *output_stream << value->dump(); + } + } + + const std::shared_ptr eval_expression_list(const ExpressionListNode& expression_list) { + if (!expression_list.root) { + throw_renderer_error("empty expression", expression_list); + } + + expression_list.root->accept(*this); + + if (data_eval_stack.empty()) { + throw_renderer_error("empty expression", expression_list); + } else if (data_eval_stack.size() != 1) { + throw_renderer_error("malformed expression", expression_list); + } + + const auto result = data_eval_stack.top(); + data_eval_stack.pop(); + + if (!result) { + if (not_found_stack.empty()) { + throw_renderer_error("expression could not be evaluated", expression_list); + } + + auto node = not_found_stack.top(); + not_found_stack.pop(); + + throw_renderer_error("variable '" + static_cast(node->name) + "' not found", *node); + } + return std::make_shared(*result); + } + + void throw_renderer_error(const std::string& message, const AstNode& node) { + SourceLocation loc = get_source_location(current_template->content, node.pos); + INJA_THROW(RenderError(message, loc)); + } + + void make_result(const json&& result) { + auto result_ptr = std::make_shared(result); + data_tmp_stack.push_back(result_ptr); + data_eval_stack.push(result_ptr.get()); + } + + template std::array get_arguments(const FunctionNode& node) { + if (node.arguments.size() < N_start + N) { + throw_renderer_error("function needs " + std::to_string(N_start + N) + " variables, but has only found " + std::to_string(node.arguments.size()), node); + } + + for (size_t i = N_start; i < N_start + N; i += 1) { + node.arguments[i]->accept(*this); + } + + if (data_eval_stack.size() < N) { + throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node); + } + + std::array result; + for (size_t i = 0; i < N; i += 1) { + result[N - i - 1] = data_eval_stack.top(); + data_eval_stack.pop(); + + if (!result[N - i - 1]) { + const auto data_node = not_found_stack.top(); + not_found_stack.pop(); + + if (throw_not_found) { + throw_renderer_error("variable '" + static_cast(data_node->name) + "' not found", *data_node); + } + } + } + return result; + } + + template Arguments get_argument_vector(const FunctionNode& node) { + const size_t N = node.arguments.size(); + for (auto a : node.arguments) { + a->accept(*this); + } + + if (data_eval_stack.size() < N) { + throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node); + } + + Arguments result {N}; + for (size_t i = 0; i < N; i += 1) { + result[N - i - 1] = data_eval_stack.top(); + data_eval_stack.pop(); + + if (!result[N - i - 1]) { + const auto data_node = not_found_stack.top(); + not_found_stack.pop(); + + if (throw_not_found) { + throw_renderer_error("variable '" + static_cast(data_node->name) + "' not found", *data_node); + } + } + } + return result; + } + + void visit(const BlockNode& node) { + for (auto& n : node.nodes) { + n->accept(*this); + + if (break_rendering) { + break; + } + } + } + + void visit(const TextNode& node) { + output_stream->write(current_template->content.c_str() + node.pos, node.length); + } + + void visit(const ExpressionNode&) {} + + void visit(const LiteralNode& node) { + data_eval_stack.push(&node.value); + } + + void visit(const DataNode& node) { + if (additional_data.contains(node.ptr)) { + data_eval_stack.push(&(additional_data[node.ptr])); + } else if (data_input->contains(node.ptr)) { + data_eval_stack.push(&(*data_input)[node.ptr]); + } else { + // Try to evaluate as a no-argument callback + const auto function_data = function_storage.find_function(node.name, 0); + if (function_data.operation == FunctionStorage::Operation::Callback) { + Arguments empty_args {}; + const auto value = std::make_shared(function_data.callback(empty_args)); + data_tmp_stack.push_back(value); + data_eval_stack.push(value.get()); + } else { + data_eval_stack.push(nullptr); + not_found_stack.emplace(&node); + } + } + } + + void visit(const FunctionNode& node) { + switch (node.operation) { + case Op::Not: { + const auto args = get_arguments<1>(node); + make_result(!truthy(args[0])); + } break; + case Op::And: { + make_result(truthy(get_arguments<1, 0>(node)[0]) && truthy(get_arguments<1, 1>(node)[0])); + } break; + case Op::Or: { + make_result(truthy(get_arguments<1, 0>(node)[0]) || truthy(get_arguments<1, 1>(node)[0])); + } break; + case Op::In: { + const auto args = get_arguments<2>(node); + make_result(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end()); + } break; + case Op::Equal: { + const auto args = get_arguments<2>(node); + make_result(*args[0] == *args[1]); + } break; + case Op::NotEqual: { + const auto args = get_arguments<2>(node); + make_result(*args[0] != *args[1]); + } break; + case Op::Greater: { + const auto args = get_arguments<2>(node); + make_result(*args[0] > *args[1]); + } break; + case Op::GreaterEqual: { + const auto args = get_arguments<2>(node); + make_result(*args[0] >= *args[1]); + } break; + case Op::Less: { + const auto args = get_arguments<2>(node); + make_result(*args[0] < *args[1]); + } break; + case Op::LessEqual: { + const auto args = get_arguments<2>(node); + make_result(*args[0] <= *args[1]); + } break; + case Op::Add: { + const auto args = get_arguments<2>(node); + if (args[0]->is_string() && args[1]->is_string()) { + make_result(args[0]->get_ref() + args[1]->get_ref()); + } else if (args[0]->is_number_integer() && args[1]->is_number_integer()) { + make_result(args[0]->get() + args[1]->get()); + } else { + make_result(args[0]->get() + args[1]->get()); + } + } break; + case Op::Subtract: { + const auto args = get_arguments<2>(node); + if (args[0]->is_number_integer() && args[1]->is_number_integer()) { + make_result(args[0]->get() - args[1]->get()); + } else { + make_result(args[0]->get() - args[1]->get()); + } + } break; + case Op::Multiplication: { + const auto args = get_arguments<2>(node); + if (args[0]->is_number_integer() && args[1]->is_number_integer()) { + make_result(args[0]->get() * args[1]->get()); + } else { + make_result(args[0]->get() * args[1]->get()); + } + } break; + case Op::Division: { + const auto args = get_arguments<2>(node); + if (args[1]->get() == 0) { + throw_renderer_error("division by zero", node); + } + make_result(args[0]->get() / args[1]->get()); + } break; + case Op::Power: { + const auto args = get_arguments<2>(node); + if (args[0]->is_number_integer() && args[1]->get() >= 0) { + int result = static_cast(std::pow(args[0]->get(), args[1]->get())); + make_result(result); + } else { + double result = std::pow(args[0]->get(), args[1]->get()); + make_result(result); + } + } break; + case Op::Modulo: { + const auto args = get_arguments<2>(node); + make_result(args[0]->get() % args[1]->get()); + } break; + case Op::AtId: { + const auto container = get_arguments<1, 0, false>(node)[0]; + node.arguments[1]->accept(*this); + if (not_found_stack.empty()) { + throw_renderer_error("could not find element with given name", node); + } + const auto id_node = not_found_stack.top(); + not_found_stack.pop(); + data_eval_stack.pop(); + data_eval_stack.push(&container->at(id_node->name)); + } break; + case Op::At: { + const auto args = get_arguments<2>(node); + if (args[0]->is_object()) { + data_eval_stack.push(&args[0]->at(args[1]->get())); + } else { + data_eval_stack.push(&args[0]->at(args[1]->get())); + } + } break; + case Op::Default: { + const auto test_arg = get_arguments<1, 0, false>(node)[0]; + data_eval_stack.push(test_arg ? test_arg : get_arguments<1, 1>(node)[0]); + } break; + case Op::DivisibleBy: { + const auto args = get_arguments<2>(node); + const int divisor = args[1]->get(); + make_result((divisor != 0) && (args[0]->get() % divisor == 0)); + } break; + case Op::Even: { + make_result(get_arguments<1>(node)[0]->get() % 2 == 0); + } break; + case Op::Exists: { + auto&& name = get_arguments<1>(node)[0]->get_ref(); + make_result(data_input->contains(json::json_pointer(DataNode::convert_dot_to_ptr(name)))); + } break; + case Op::ExistsInObject: { + const auto args = get_arguments<2>(node); + auto&& name = args[1]->get_ref(); + make_result(args[0]->find(name) != args[0]->end()); + } break; + case Op::First: { + const auto result = &get_arguments<1>(node)[0]->front(); + data_eval_stack.push(result); + } break; + case Op::Float: { + make_result(std::stod(get_arguments<1>(node)[0]->get_ref())); + } break; + case Op::Int: { + make_result(std::stoi(get_arguments<1>(node)[0]->get_ref())); + } break; + case Op::Last: { + const auto result = &get_arguments<1>(node)[0]->back(); + data_eval_stack.push(result); + } break; + case Op::Length: { + const auto val = get_arguments<1>(node)[0]; + if (val->is_string()) { + make_result(val->get_ref().length()); + } else { + make_result(val->size()); + } + } break; + case Op::Lower: { + std::string result = get_arguments<1>(node)[0]->get(); + std::transform(result.begin(), result.end(), result.begin(), ::tolower); + make_result(std::move(result)); + } break; + case Op::Max: { + const auto args = get_arguments<1>(node); + const auto result = std::max_element(args[0]->begin(), args[0]->end()); + data_eval_stack.push(&(*result)); + } break; + case Op::Min: { + const auto args = get_arguments<1>(node); + const auto result = std::min_element(args[0]->begin(), args[0]->end()); + data_eval_stack.push(&(*result)); + } break; + case Op::Odd: { + make_result(get_arguments<1>(node)[0]->get() % 2 != 0); + } break; + case Op::Range: { + std::vector result(get_arguments<1>(node)[0]->get()); + std::iota(result.begin(), result.end(), 0); + make_result(std::move(result)); + } break; + case Op::Round: { + const auto args = get_arguments<2>(node); + const int precision = args[1]->get(); + const double result = std::round(args[0]->get() * std::pow(10.0, precision)) / std::pow(10.0, precision); + if (precision == 0) { + make_result(int(result)); + } else { + make_result(result); + } + } break; + case Op::Sort: { + auto result_ptr = std::make_shared(get_arguments<1>(node)[0]->get>()); + std::sort(result_ptr->begin(), result_ptr->end()); + data_tmp_stack.push_back(result_ptr); + data_eval_stack.push(result_ptr.get()); + } break; + case Op::Upper: { + std::string result = get_arguments<1>(node)[0]->get(); + std::transform(result.begin(), result.end(), result.begin(), ::toupper); + make_result(std::move(result)); + } break; + case Op::IsBoolean: { + make_result(get_arguments<1>(node)[0]->is_boolean()); + } break; + case Op::IsNumber: { + make_result(get_arguments<1>(node)[0]->is_number()); + } break; + case Op::IsInteger: { + make_result(get_arguments<1>(node)[0]->is_number_integer()); + } break; + case Op::IsFloat: { + make_result(get_arguments<1>(node)[0]->is_number_float()); + } break; + case Op::IsObject: { + make_result(get_arguments<1>(node)[0]->is_object()); + } break; + case Op::IsArray: { + make_result(get_arguments<1>(node)[0]->is_array()); + } break; + case Op::IsString: { + make_result(get_arguments<1>(node)[0]->is_string()); + } break; + case Op::Callback: { + auto args = get_argument_vector(node); + make_result(node.callback(args)); + } break; + case Op::Super: { + const auto args = get_argument_vector(node); + const size_t old_level = current_level; + const size_t level_diff = (args.size() == 1) ? args[0]->get() : 1; + const size_t level = current_level + level_diff; + + if (block_statement_stack.empty()) { + throw_renderer_error("super() call is not within a block", node); + } + + if (level < 1 || level > template_stack.size() - 1) { + throw_renderer_error("level of super() call does not match parent templates (between 1 and " + std::to_string(template_stack.size() - 1) + ")", node); + } + + const auto current_block_statement = block_statement_stack.back(); + const Template* new_template = template_stack.at(level); + const Template* old_template = current_template; + const auto block_it = new_template->block_storage.find(current_block_statement->name); + if (block_it != new_template->block_storage.end()) { + current_template = new_template; + current_level = level; + block_it->second->block.accept(*this); + current_level = old_level; + current_template = old_template; + } else { + throw_renderer_error("could not find block with name '" + current_block_statement->name + "'", node); + } + make_result(nullptr); + } break; + case Op::Join: { + const auto args = get_arguments<2>(node); + const auto separator = args[1]->get(); + std::ostringstream os; + std::string sep; + for (const auto& value : *args[0]) { + os << sep; + if (value.is_string()) { + os << value.get(); // otherwise the value is surrounded with "" + } else { + os << value.dump(); + } + sep = separator; + } + make_result(os.str()); + } break; + case Op::ParenLeft: + case Op::ParenRight: + case Op::None: + break; + } + } + + void visit(const ExpressionListNode& node) { + print_data(eval_expression_list(node)); + } + + void visit(const StatementNode&) {} + + void visit(const ForStatementNode&) {} + + void visit(const ForArrayStatementNode& node) { + const auto result = eval_expression_list(node.condition); + if (!result->is_array()) { + throw_renderer_error("object must be an array", node); + } + + if (!current_loop_data->empty()) { + auto tmp = *current_loop_data; // Because of clang-3 + (*current_loop_data)["parent"] = std::move(tmp); + } + + size_t index = 0; + (*current_loop_data)["is_first"] = true; + (*current_loop_data)["is_last"] = (result->size() <= 1); + for (auto it = result->begin(); it != result->end(); ++it) { + additional_data[static_cast(node.value)] = *it; + + (*current_loop_data)["index"] = index; + (*current_loop_data)["index1"] = index + 1; + if (index == 1) { + (*current_loop_data)["is_first"] = false; + } + if (index == result->size() - 1) { + (*current_loop_data)["is_last"] = true; + } + + node.body.accept(*this); + ++index; + } + + additional_data[static_cast(node.value)].clear(); + if (!(*current_loop_data)["parent"].empty()) { + const auto tmp = (*current_loop_data)["parent"]; + *current_loop_data = std::move(tmp); + } else { + current_loop_data = &additional_data["loop"]; + } + } + + void visit(const ForObjectStatementNode& node) { + const auto result = eval_expression_list(node.condition); + if (!result->is_object()) { + throw_renderer_error("object must be an object", node); + } + + if (!current_loop_data->empty()) { + (*current_loop_data)["parent"] = std::move(*current_loop_data); + } + + size_t index = 0; + (*current_loop_data)["is_first"] = true; + (*current_loop_data)["is_last"] = (result->size() <= 1); + for (auto it = result->begin(); it != result->end(); ++it) { + additional_data[static_cast(node.key)] = it.key(); + additional_data[static_cast(node.value)] = it.value(); + + (*current_loop_data)["index"] = index; + (*current_loop_data)["index1"] = index + 1; + if (index == 1) { + (*current_loop_data)["is_first"] = false; + } + if (index == result->size() - 1) { + (*current_loop_data)["is_last"] = true; + } + + node.body.accept(*this); + ++index; + } + + additional_data[static_cast(node.key)].clear(); + additional_data[static_cast(node.value)].clear(); + if (!(*current_loop_data)["parent"].empty()) { + *current_loop_data = std::move((*current_loop_data)["parent"]); + } else { + current_loop_data = &additional_data["loop"]; + } + } + + void visit(const IfStatementNode& node) { + const auto result = eval_expression_list(node.condition); + if (truthy(result.get())) { + node.true_statement.accept(*this); + } else if (node.has_false_statement) { + node.false_statement.accept(*this); + } + } + + void visit(const IncludeStatementNode& node) { + auto sub_renderer = Renderer(config, template_storage, function_storage); + const auto included_template_it = template_storage.find(node.file); + if (included_template_it != template_storage.end()) { + sub_renderer.render_to(*output_stream, included_template_it->second, *data_input, &additional_data); + } else if (config.throw_at_missing_includes) { + throw_renderer_error("include '" + node.file + "' not found", node); + } + } + + void visit(const ExtendsStatementNode& node) { + const auto included_template_it = template_storage.find(node.file); + if (included_template_it != template_storage.end()) { + const Template* parent_template = &included_template_it->second; + render_to(*output_stream, *parent_template, *data_input, &additional_data); + break_rendering = true; + } else if (config.throw_at_missing_includes) { + throw_renderer_error("extends '" + node.file + "' not found", node); + } + } + + void visit(const BlockStatementNode& node) { + const size_t old_level = current_level; + current_level = 0; + current_template = template_stack.front(); + const auto block_it = current_template->block_storage.find(node.name); + if (block_it != current_template->block_storage.end()) { + block_statement_stack.emplace_back(&node); + block_it->second->block.accept(*this); + block_statement_stack.pop_back(); + } + current_level = old_level; + current_template = template_stack.back(); + } + + void visit(const SetStatementNode& node) { + std::string ptr = node.key; + replace_substring(ptr, ".", "/"); + ptr = "/" + ptr; + additional_data[json::json_pointer(ptr)] = *eval_expression_list(node.expression); + } + +public: + Renderer(const RenderConfig& config, const TemplateStorage& template_storage, const FunctionStorage& function_storage) + : config(config), template_storage(template_storage), function_storage(function_storage) {} + + void render_to(std::ostream& os, const Template& tmpl, const json& data, json* loop_data = nullptr) { + output_stream = &os; + current_template = &tmpl; + data_input = &data; + if (loop_data) { + additional_data = *loop_data; + current_loop_data = &additional_data["loop"]; + } + + template_stack.emplace_back(current_template); + current_template->root.accept(*this); + + data_tmp_stack.clear(); + } +}; + +} // namespace inja + +#endif // INCLUDE_INJA_RENDERER_HPP_ + +// #include "template.hpp" + +// #include "utils.hpp" + + +namespace inja { + +/*! + * \brief Class for changing the configuration. + */ +class Environment { + std::string input_path; + std::string output_path; + + LexerConfig lexer_config; + ParserConfig parser_config; + RenderConfig render_config; + + FunctionStorage function_storage; + TemplateStorage template_storage; + +public: + Environment(): Environment("") {} + + explicit Environment(const std::string& global_path): input_path(global_path), output_path(global_path) {} + + Environment(const std::string& input_path, const std::string& output_path): input_path(input_path), output_path(output_path) {} + + /// Sets the opener and closer for template statements + void set_statement(const std::string& open, const std::string& close) { + lexer_config.statement_open = open; + lexer_config.statement_open_no_lstrip = open + "+"; + lexer_config.statement_open_force_lstrip = open + "-"; + lexer_config.statement_close = close; + lexer_config.statement_close_force_rstrip = "-" + close; + lexer_config.update_open_chars(); + } + + /// Sets the opener for template line statements + void set_line_statement(const std::string& open) { + lexer_config.line_statement = open; + lexer_config.update_open_chars(); + } + + /// Sets the opener and closer for template expressions + void set_expression(const std::string& open, const std::string& close) { + lexer_config.expression_open = open; + lexer_config.expression_open_force_lstrip = open + "-"; + lexer_config.expression_close = close; + lexer_config.expression_close_force_rstrip = "-" + close; + lexer_config.update_open_chars(); + } + + /// Sets the opener and closer for template comments + void set_comment(const std::string& open, const std::string& close) { + lexer_config.comment_open = open; + lexer_config.comment_open_force_lstrip = open + "-"; + lexer_config.comment_close = close; + lexer_config.comment_close_force_rstrip = "-" + close; + lexer_config.update_open_chars(); + } + + /// Sets whether to remove the first newline after a block + void set_trim_blocks(bool trim_blocks) { + lexer_config.trim_blocks = trim_blocks; + } + + /// Sets whether to strip the spaces and tabs from the start of a line to a block + void set_lstrip_blocks(bool lstrip_blocks) { + lexer_config.lstrip_blocks = lstrip_blocks; + } + + /// Sets the element notation syntax + void set_search_included_templates_in_files(bool search_in_files) { + parser_config.search_included_templates_in_files = search_in_files; + } + + /// Sets whether a missing include will throw an error + void set_throw_at_missing_includes(bool will_throw) { + render_config.throw_at_missing_includes = will_throw; + } + + Template parse(std::string_view input) { + Parser parser(parser_config, lexer_config, template_storage, function_storage); + return parser.parse(input); + } + + Template parse_template(const std::string& filename) { + Parser parser(parser_config, lexer_config, template_storage, function_storage); + auto result = Template(parser.load_file(input_path + static_cast(filename))); + parser.parse_into_template(result, input_path + static_cast(filename)); + return result; + } + + Template parse_file(const std::string& filename) { + return parse_template(filename); + } + + std::string render(std::string_view input, const json& data) { + return render(parse(input), data); + } + + std::string render(const Template& tmpl, const json& data) { + std::stringstream os; + render_to(os, tmpl, data); + return os.str(); + } + + std::string render_file(const std::string& filename, const json& data) { + return render(parse_template(filename), data); + } + + std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) { + const json data = load_json(filename_data); + return render_file(filename, data); + } + + void write(const std::string& filename, const json& data, const std::string& filename_out) { + std::ofstream file(output_path + filename_out); + file << render_file(filename, data); + file.close(); + } + + void write(const Template& temp, const json& data, const std::string& filename_out) { + std::ofstream file(output_path + filename_out); + file << render(temp, data); + file.close(); + } + + void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) { + const json data = load_json(filename_data); + write(filename, data, filename_out); + } + + void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) { + const json data = load_json(filename_data); + write(temp, data, filename_out); + } + + std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) { + Renderer(render_config, template_storage, function_storage).render_to(os, tmpl, data); + return os; + } + + std::string load_file(const std::string& filename) { + Parser parser(parser_config, lexer_config, template_storage, function_storage); + return parser.load_file(input_path + filename); + } + + json load_json(const std::string& filename) { + std::ifstream file; + file.open(input_path + filename); + if (file.fail()) { + INJA_THROW(FileError("failed accessing file at '" + input_path + filename + "'")); + } + + return json::parse(std::istreambuf_iterator(file), std::istreambuf_iterator()); + } + + /*! + @brief Adds a variadic callback + */ + void add_callback(const std::string& name, const CallbackFunction& callback) { + add_callback(name, -1, callback); + } + + /*! + @brief Adds a variadic void callback + */ + void add_void_callback(const std::string& name, const VoidCallbackFunction& callback) { + add_void_callback(name, -1, callback); + } + + /*! + @brief Adds a callback with given number or arguments + */ + void add_callback(const std::string& name, int num_args, const CallbackFunction& callback) { + function_storage.add_callback(name, num_args, callback); + } + + /*! + @brief Adds a void callback with given number or arguments + */ + void add_void_callback(const std::string& name, int num_args, const VoidCallbackFunction& callback) { + function_storage.add_callback(name, num_args, [callback](Arguments& args) { + callback(args); + return json(); + }); + } + + /** Includes a template with a given name into the environment. + * Then, a template can be rendered in another template using the + * include "" syntax. + */ + void include_template(const std::string& name, const Template& tmpl) { + template_storage[name] = tmpl; + } + + /*! + @brief Sets a function that is called when an included file is not found + */ + void set_include_callback(const std::function& callback) { + parser_config.include_callback = callback; + } +}; + +/*! +@brief render with default settings to a string +*/ +inline std::string render(std::string_view input, const json& data) { + return Environment().render(input, data); +} + +/*! +@brief render with default settings to the given output stream +*/ +inline void render_to(std::ostream& os, std::string_view input, const json& data) { + Environment env; + env.render_to(os, env.parse(input), data); +} + +} // namespace inja + +#endif // INCLUDE_INJA_ENVIRONMENT_HPP_ + +// #include "exceptions.hpp" + +// #include "parser.hpp" + +// #include "renderer.hpp" + +// #include "template.hpp" + + +#endif // INCLUDE_INJA_INJA_HPP_ diff --git a/ext/json/LICENSE.MIT b/ext/nlohmann/LICENSE.MIT similarity index 100% rename from ext/json/LICENSE.MIT rename to ext/nlohmann/LICENSE.MIT diff --git a/ext/json/README.md b/ext/nlohmann/README.md similarity index 100% rename from ext/json/README.md rename to ext/nlohmann/README.md diff --git a/ext/json/json.hpp b/ext/nlohmann/json.hpp similarity index 100% rename from ext/json/json.hpp rename to ext/nlohmann/json.hpp diff --git a/make-bsd.mk b/make-bsd.mk index 637f80d68..889e0e093 100644 --- a/make-bsd.mk +++ b/make-bsd.mk @@ -1,6 +1,6 @@ # This requires GNU make, which is typically "gmake" on BSD systems -INCLUDES= +INCLUDES=-isystem ext DEFS= LIBS= @@ -152,7 +152,7 @@ endif override DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE) -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\"" -CXXFLAGS+=$(CFLAGS) -std=c++11 #-D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_C99_MATH -D_GLIBCXX_USE_C99_MATH_TR1 +CXXFLAGS+=$(CFLAGS) -std=c++17 #-D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_C99_MATH -D_GLIBCXX_USE_C99_MATH_TR1 all: one diff --git a/make-linux.mk b/make-linux.mk index 1caf3acc1..e902659c1 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -9,7 +9,7 @@ ifeq ($(origin CXX),default) CXX:=$(shell if [ -e /opt/rh/devtoolset-8/root/usr/bin/g++ ]; then echo /opt/rh/devtoolset-8/root/usr/bin/g++; else echo $(CXX); fi) endif -INCLUDES?=-Izeroidc/target +INCLUDES?=-Izeroidc/target -isystem ext DEFS?= LDLIBS?= DESTDIR?= @@ -31,7 +31,7 @@ ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1) override DEFS+=-DZT_USE_SYSTEM_MINIUPNPC LDLIBS+=-lminiupnpc else - override DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING=\"Linux\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR + override DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING="\"Linux\"" -DMINIUPNPC_VERSION_STRING="\"2.0\"" -DUPNP_VERSION_STRING="\"UPnP/1.1\"" -DENABLE_STRNATPMPERR ONE_OBJS+=ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o endif ifeq ($(wildcard /usr/include/natpmp.h),) @@ -59,17 +59,17 @@ ifeq ($(ZT_SANITIZE),1) endif ifeq ($(ZT_DEBUG),1) override CFLAGS+=-Wall -Wno-deprecated -g -O -pthread $(INCLUDES) $(DEFS) - override CXXFLAGS+=-Wall -Wno-deprecated -g -O -std=c++11 -pthread $(INCLUDES) $(DEFS) + override CXXFLAGS+=-Wall -Wno-deprecated -g -O -std=c++17 -pthread $(INCLUDES) $(DEFS) ZT_TRACE=1 RUSTFLAGS= # The following line enables optimization for the crypto code, since # C25519 in particular is almost UNUSABLE in -O0 even on a 3ghz box! node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CXXFLAGS=-Wall -O2 -g -pthread $(INCLUDES) $(DEFS) else - CFLAGS?=-O3 -fstack-protector -fPIE + CFLAGS?=-O3 -fstack-protector override CFLAGS+=-Wall -Wno-deprecated -pthread $(INCLUDES) -DNDEBUG $(DEFS) - CXXFLAGS?=-O3 -fstack-protector -fPIE - override CXXFLAGS+=-Wall -Wno-deprecated -std=c++11 -pthread $(INCLUDES) -DNDEBUG $(DEFS) + CXXFLAGS?=-O3 -fstack-protector + override CXXFLAGS+=-Wall -Wno-deprecated -std=c++17 -pthread $(INCLUDES) -DNDEBUG $(DEFS) LDFLAGS=-pie -Wl,-z,relro,-z,now RUSTFLAGS=--release endif @@ -84,8 +84,6 @@ ifeq ($(ZT_UBIQUITI), 1) endif ifeq ($(ZT_SYNOLOGY), 1) - override CFLAGS+=-fPIC - override CXXFLAGS+=-fPIC override DEFS+=-D__SYNOLOGY__ ZT_EMBEDDED=1 endif @@ -336,12 +334,20 @@ ifeq ($(ZT_USE_ARM32_NEON_ASM_CRYPTO),1) override CORE_OBJS+=ext/arm32-neon-salsa2012-asm/salsa2012.o endif +# Position Independence +override CFLAGS+=-fPIC -fPIE +override CXXFLAGS+=-fPIC -fPIE + .PHONY: all all: one .PHONY: one one: zerotier-one zerotier-idtool zerotier-cli +from_builder: + ln -sf zerotier-one zerotier-idtool + ln -sf zerotier-one zerotier-cli + zerotier-one: $(CORE_OBJS) $(ONE_OBJS) one.o $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LDLIBS) @@ -354,7 +360,7 @@ zerotier-cli: zerotier-one $(ONE_OBJS): zeroidc libzerotiercore.a: FORCE - make CFLAGS="-O3 -fstack-protector -fPIC" CXXFLAGS="-O3 -std=c++11 -fstack-protector -fPIC" $(CORE_OBJS) + make CFLAGS="-O3 -fstack-protector -fPIC" CXXFLAGS="-O3 -std=c++17 -fstack-protector -fPIC" $(CORE_OBJS) ar rcs libzerotiercore.a $(CORE_OBJS) ranlib libzerotiercore.a @@ -452,6 +458,20 @@ uninstall: FORCE # These are just for convenience for building Linux packages +echo_flags: + @echo "=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~" + @echo "echo_flags :: CC=$(CC)" + @echo "echo_flags :: CXX=$(CXX)" + @echo "echo_flags :: CFLAGS=$(CFLAGS)" + @echo "echo_flags :: CXXFLAGS=$(CXXFLAGS)" + @echo "echo_flags :: LDFLAGS=$(LDFLAGS)" + @echo "echo_flags :: RUSTFLAGS=$(RUSTFLAGS)" + @echo "=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~" + +# debian: echo_flags +# @echo "building deb package" +# debuild --no-lintian -b -uc -us + debian: FORCE debuild --no-lintian -I -i -us -uc -nc -b @@ -491,9 +511,19 @@ snap-upload-stable: FORCE done synology-pkg: FORCE - cd synology ; ./build.sh build + cd pkg/synology ; ./build.sh build synology-docker: FORCE - cd synology/dsm7-docker/; ./build.sh build + cd pkg/synology/dsm7-docker/; ./build.sh build-and-push + +munge_rpm: + @:$(call check_defined, VERSION) + @echo "Updating rpm spec to $(VERSION)" + ci/scripts/munge_rpm_spec.sh zerotier-one.spec $(VERSION) "Adam Ierymenko " "see https://github.com/zerotier/ZeroTierOne for release notes" + +munge_deb: + @:$(call check_defined, VERSION) + @echo "Updating debian/changelog to $(VERSION)" + ci/scripts/munge_debian_changelog.sh debian/changelog $(VERSION) "Adam Ierymenko " "see https://github.com/zerotier/ZeroTierOne for release notes" FORCE: diff --git a/make-mac.mk b/make-mac.mk index e960b0528..5c00fb0c0 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -2,7 +2,7 @@ CC=clang CXX=clang++ TOPDIR=$(shell PWD) -INCLUDES=-I$(shell PWD)/zeroidc/target +INCLUDES=-I$(shell PWD)/zeroidc/target -isystem $(TOPDIR)/ext DEFS= LIBS= ARCH_FLAGS=-arch x86_64 -arch arm64 @@ -47,7 +47,7 @@ endif # Use fast ASM Salsa20/12 for x64 processors DEFS+=-DZT_USE_X64_ASM_SALSA2012 CORE_OBJS+=ext/x64-salsa2012-asm/salsa2012.o -CXXFLAGS=$(CFLAGS) -std=c++11 -stdlib=libc++ +CXXFLAGS=$(CFLAGS) -std=c++17 -stdlib=libc++ # Build miniupnpc and nat-pmp as included libraries -- extra defs are required for these sources DEFS+=-DMACOSX -DZT_SSO_SUPPORTED -DZT_USE_MINIUPNPC -DMINIUPNP_STATICLIB -D_DARWIN_C_SOURCE -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -DOS_STRING=\"Darwin/15.0.0\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR diff --git a/node/Bond.cpp b/node/Bond.cpp index d633d7c21..0c4037e75 100644 --- a/node/Bond.cpp +++ b/node/Bond.cpp @@ -236,7 +236,7 @@ void Bond::nominatePathToBond(const SharedPtr& path, int64_t now) /** * Ensure the link is allowed and the path is not already present */ - if (! RR->bc->linkAllowed(_policyAlias, getLink(path))) { + if (! RR->bc->linkAllowed(_policyAlias, getLinkBySocket(_policyAlias, path->localSocket(), true))) { debug("link %s is not permitted according to user-specified rules", pathToStr(path).c_str()); return; } diff --git a/one.cpp b/one.cpp index f718e09ab..828163896 100644 --- a/one.cpp +++ b/one.cpp @@ -88,7 +88,7 @@ #include "service/OneService.hpp" -#include "ext/json/json.hpp" +#include #ifdef __APPLE__ #include diff --git a/osdep/Binder.hpp b/osdep/Binder.hpp index 82568373a..49a30cef4 100644 --- a/osdep/Binder.hpp +++ b/osdep/Binder.hpp @@ -40,7 +40,7 @@ #endif #endif -#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) +#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) && !defined(ZT_SDK) #include #include #include @@ -311,7 +311,7 @@ class Binder { if (! gotViaProc) { struct ifaddrs* ifatbl = (struct ifaddrs*)0; struct ifaddrs* ifa; -#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) +#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) && !defined(ZT_SDK) // set up an IPv6 socket so we can check the state of interfaces via SIOCGIFAFLAG_IN6 int infoSock = socket(AF_INET6, SOCK_DGRAM, 0); #endif @@ -320,7 +320,7 @@ class Binder { while (ifa) { if ((ifa->ifa_name) && (ifa->ifa_addr)) { InetAddress ip = *(ifa->ifa_addr); -#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) +#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) && !defined(ZT_SDK) // Check if the address is an IPv6 Temporary Address, macOS/BSD version if (ifa->ifa_addr->sa_family == AF_INET6) { struct sockaddr_in6* sa6 = (struct sockaddr_in6*)ifa->ifa_addr; @@ -368,7 +368,7 @@ class Binder { else { interfacesEnumerated = false; } -#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) +#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) && !defined(ZT_SDK) close(infoSock); #endif } diff --git a/osdep/OSUtils.hpp b/osdep/OSUtils.hpp index 99a1ee0d8..021b3876f 100644 --- a/osdep/OSUtils.hpp +++ b/osdep/OSUtils.hpp @@ -43,7 +43,7 @@ #endif #ifndef OMIT_JSON_SUPPORT -#include "../ext/json/json.hpp" +#include #endif namespace ZeroTier { diff --git a/pkg/synology/dsm7-docker/build.sh b/pkg/synology/dsm7-docker/build.sh index 2b1fadd00..f8478157b 100755 --- a/pkg/synology/dsm7-docker/build.sh +++ b/pkg/synology/dsm7-docker/build.sh @@ -3,16 +3,17 @@ ZTO_VER=$(git describe --abbrev=0 --tags) ZTO_COMMIT=$(git rev-parse HEAD) -build() +build-and-push() { + # build + sudo docker build --load --rm -t zerotier-synology . --build-arg ZTO_COMMIT=${ZTO_COMMIT} --build-arg ZTO_VER=${ZTO_VER} LATEST_DOCKER_IMAGE_HASH=$(sudo docker images -q zerotier-synology) sudo docker tag ${LATEST_DOCKER_IMAGE_HASH} zerotier/zerotier-synology:${ZTO_VER} sudo docker tag ${LATEST_DOCKER_IMAGE_HASH} zerotier/zerotier-synology:latest -} -push() -{ + # push + sudo docker login --username=${DOCKERHUB_USERNAME} sudo docker push zerotier/zerotier-synology:${ZTO_VER} sudo docker push zerotier/zerotier-synology:latest diff --git a/service/OneService.cpp b/service/OneService.cpp index d547ca35f..f390a1497 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -92,7 +92,8 @@ extern "C" { } #endif -#include "../ext/json/json.hpp" +#include +#include using json = nlohmann::json; @@ -150,43 +151,54 @@ size_t curlResponseWrite(void *ptr, size_t size, size_t nmemb, std::string *data namespace ZeroTier { -const char *ssoResponseTemplate = "\ -\ -\ -\ -\ -





\ -
\ -
\ -
%s
\ -
\ -\ -"; +std::string ssoResponseTemplate = R"""( + + + + + + Network SSO Login {{ networkId }} + + + + + +
+
+
{{ messageText }}
+
+ + +)"""; // Configured networks class NetworkState @@ -286,16 +298,9 @@ public: } void setConfig(const ZT_VirtualNetworkConfig *nwc) { - char nwbuf[17] = {}; - const char* nwid = Utils::hex(nwc->nwid, nwbuf); - // fprintf(stderr, "NetworkState::setConfig(%s)\n", nwid); - memcpy(&_config, nwc, sizeof(ZT_VirtualNetworkConfig)); - // fprintf(stderr, "ssoEnabled: %s, ssoVersion: %d\n", - // _config.ssoEnabled ? "true" : "false", _config.ssoVersion); if (_config.ssoEnabled && _config.ssoVersion == 1) { - // fprintf(stderr, "ssoEnabled for %s\n", nwid); #if ZT_SSO_ENABLED if (_idc == nullptr) { @@ -303,10 +308,6 @@ public: assert(_config.ssoClientID != nullptr); assert(_config.centralAuthURL != nullptr); - // fprintf(stderr, "Issuer URL: %s\n", _config.issuerURL); - // fprintf(stderr, "Client ID: %s\n", _config.ssoClientID); - // fprintf(stderr, "Central Auth URL: %s\n", _config.centralAuthURL); - _idc = zeroidc::zeroidc_new( _config.issuerURL, _config.ssoClientID, @@ -318,8 +319,6 @@ public: fprintf(stderr, "idc is null\n"); return; } - - // fprintf(stderr, "idc created (%s, %s, %s)\n", _config.issuerURL, _config.ssoClientID, _config.centralAuthURL); } zeroidc::zeroidc_set_nonce_and_csrf( @@ -334,7 +333,6 @@ public: zeroidc::free_cstr(url); if (zeroidc::zeroidc_is_running(_idc) && nwc->status == ZT_NETWORK_STATUS_AUTHENTICATION_REQUIRED) { - // TODO: kick the refresh thread zeroidc::zeroidc_kick_refresh_thread(_idc); } #endif @@ -364,13 +362,14 @@ public: } char* doTokenExchange(const char *code) { + char *ret = nullptr; #if ZT_SSO_ENABLED if (_idc == nullptr) { fprintf(stderr, "ainfo or idc null\n"); - return ""; + return ret; } - char *ret = zeroidc::zeroidc_token_exchange(_idc, code); + ret = zeroidc::zeroidc_token_exchange(_idc, code); zeroidc::zeroidc_set_nonce_and_csrf( _idc, _config.ssoState, @@ -381,11 +380,8 @@ public: memcpy(_config.authenticationURL, url, strlen(url)); _config.authenticationURL[strlen(url)] = 0; zeroidc::free_cstr(url); - - return ret; -#else - return ""; #endif + return ret; } uint64_t getExpiryTime() { @@ -1702,23 +1698,37 @@ public: } #if ZT_SSO_ENABLED } else if (ps[0] == "sso") { - char resBuf[4096] = {0}; - const char *error = zeroidc::zeroidc_get_url_param_value("error", path.c_str()); + std::string htmlTemplatePath = _homePath + ZT_PATH_SEPARATOR + "sso-auth.template.html"; + std::string htmlTemplate; + if (!OSUtils::readFile(htmlTemplatePath.c_str(), htmlTemplate)) { + htmlTemplate = ssoResponseTemplate; + } + + responseContentType = "text/html"; + json outData; + + char *error = zeroidc::zeroidc_get_url_param_value("error", path.c_str()); if (error != nullptr) { - const char *desc = zeroidc::zeroidc_get_url_param_value("error_description", path.c_str()); + char *desc = zeroidc::zeroidc_get_url_param_value("error_description", path.c_str()); scode = 500; - char errBuff[256] = {0}; - sprintf(errBuff, "ERROR %s: %s", error, desc); - sprintf(resBuf, ssoResponseTemplate, errBuff); - responseBody = std::string(resBuf); - responseContentType = "text/html"; + + json data; + outData["isError"] = true; + outData["messageText"] = (std::string("ERROR ") + error + std::string(": ") + desc); + responseBody = inja::render(htmlTemplate, outData); + + zeroidc::free_cstr(desc); + zeroidc::free_cstr(error); + return scode; } // SSO redirect handling char* state = zeroidc::zeroidc_get_url_param_value("state", path.c_str()); char* nwid = zeroidc::zeroidc_network_id_from_state(state); - + + outData["networkId"] = std::string(nwid); + const uint64_t id = Utils::hexStrToU64(nwid); zeroidc::free_cstr(nwid); @@ -1729,14 +1739,30 @@ public: NetworkState& ns = _nets[id]; char* code = zeroidc::zeroidc_get_url_param_value("code", path.c_str()); char *ret = ns.doTokenExchange(code); - scode = 200; - sprintf(resBuf, ssoResponseTemplate, "Authentication Successful. You may now access the network."); - responseBody = std::string(resBuf); + json ssoResult = json::parse(ret); + if (ssoResult.is_object()) { + if (ssoResult.contains("errorMessage")) { + outData["isError"] = true; + outData["messageText"] = ssoResult["errorMessage"]; + responseBody = inja::render(htmlTemplate, outData); + scode = 500; + } else { + scode = 200; + outData["isError"] = false; + outData["messageText"] = "Authentication Successful. You may now access the network."; + responseBody = inja::render(htmlTemplate, outData); + } + } else { + // not an object? We got a problem + outData["isError"] = true; + outData["messageText"] = "ERROR: Unkown SSO response. Please contact your administrator."; + responseBody = inja::render(htmlTemplate, outData); + scode= 500; + } zeroidc::free_cstr(code); zeroidc::free_cstr(ret); - - responseContentType = "text/html"; + return scode; } else { scode = 404; @@ -1884,8 +1910,7 @@ public: scode = _controller->handleControlPlaneHttpPOST(std::vector(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType); else scode = 404; } - } - else { + } else { scode = 401; // isAuth == false } } else if (httpMethod == HTTP_DELETE) { diff --git a/service/SoftwareUpdater.hpp b/service/SoftwareUpdater.hpp index 46ceeb42b..a1ec340e3 100644 --- a/service/SoftwareUpdater.hpp +++ b/service/SoftwareUpdater.hpp @@ -27,7 +27,7 @@ #include "../node/Identity.hpp" #include "../node/Packet.hpp" -#include "../ext/json/json.hpp" +#include /** * VERB_USER_MESSAGE type ID for software update messages diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj index af77d8c71..b21d3f6bf 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj @@ -410,11 +410,11 @@ Level3 Disabled true - $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) + $(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_SSO_ENABLED=1;ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions) 4996 false - stdcpp14 + stdcpp17 true stdc11 false @@ -432,11 +432,11 @@ Level3 Disabled true - $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) + $(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_SSO_ENABLED=1;ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions) 4996 false - stdcpp14 + stdcpp17 stdc11 true false @@ -453,12 +453,12 @@ Level3 Disabled true - $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) + $(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_SSO_ENABLED=1;ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions) true 4996 false - stdcpp14 + stdcpp17 stdc11 false false @@ -476,7 +476,7 @@ Level3 Disabled true - $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) + $(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions) true 4996 @@ -499,12 +499,12 @@ Level3 Disabled true - $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) + $(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_SSO_ENABLED=1;ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions) true 4996 false - stdcpp14 + stdcpp17 stdc11 false false @@ -521,12 +521,12 @@ Level3 Disabled true - $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) + $(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions) true 4996 false - stdcpp14 + stdcpp17 stdc11 false false @@ -545,7 +545,7 @@ false true true - $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) + $(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_SSO_ENABLED=1;ZT_EXPORT;FD_SETSIZE=1024;STATICLIB;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=1;%(PreprocessorDefinitions) MultiThreaded true @@ -555,7 +555,7 @@ 4996 Guard false - stdcpp14 + stdcpp17 None false false @@ -581,7 +581,7 @@ false true true - $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) + $(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_SSO_ENABLED=1;ZT_EXPORT;FD_SETSIZE=1024;STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=2;%(PreprocessorDefinitions) MultiThreaded NotSet @@ -594,7 +594,7 @@ false Cdecl false - stdcpp14 + stdcpp17 None false false @@ -619,7 +619,7 @@ false true true - $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) + $(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_EXPORT;FD_SETSIZE=1024;STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=2;%(PreprocessorDefinitions) MultiThreaded NotSet @@ -632,7 +632,7 @@ false Cdecl false - stdcpp14 + stdcpp17 None false false diff --git a/zeroidc/build.rs b/zeroidc/build.rs index 0b417c084..6fe339847 100644 --- a/zeroidc/build.rs +++ b/zeroidc/build.rs @@ -34,4 +34,4 @@ fn target_dir() -> PathBuf { } else { PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("target") } -} \ No newline at end of file +} diff --git a/zeroidc/src/error.rs b/zeroidc/src/error.rs index 8c2bdf6ac..039429e6e 100644 --- a/zeroidc/src/error.rs +++ b/zeroidc/src/error.rs @@ -13,11 +13,24 @@ use thiserror::Error; #[derive(Error, Debug)] -pub enum ZeroIDCError -{ +pub enum ZeroIDCError { #[error(transparent)] - DiscoveryError(#[from] openidconnect::DiscoveryError>), + DiscoveryError( + #[from] openidconnect::DiscoveryError>, + ), #[error(transparent)] ParseError(#[from] url::ParseError), } + +#[derive(Error, Debug)] +#[error("SSO Exchange Error: {message:}")] +pub struct SSOExchangeError { + message: String, +} + +impl SSOExchangeError { + pub fn new(message: String) -> Self { + SSOExchangeError { message } + } +} diff --git a/zeroidc/src/ext.rs b/zeroidc/src/ext.rs index 4ce25b560..dfb25bd1a 100644 --- a/zeroidc/src/ext.rs +++ b/zeroidc/src/ext.rs @@ -12,19 +12,17 @@ use std::ffi::{CStr, CString}; use std::os::raw::c_char; -use url::{Url}; +use url::Url; use crate::ZeroIDC; -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] #[no_mangle] pub extern "C" fn zeroidc_new( issuer: *const c_char, @@ -56,25 +54,21 @@ pub extern "C" fn zeroidc_new( auth_endpoint.to_str().unwrap(), web_listen_port, ) { - Ok(idc) => { - return Box::into_raw(Box::new(idc)); - } + Ok(idc) => Box::into_raw(Box::new(idc)), Err(s) => { println!("Error creating ZeroIDC instance: {}", s); - return std::ptr::null_mut(); + std::ptr::null_mut() } } } -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] #[no_mangle] pub extern "C" fn zeroidc_delete(ptr: *mut ZeroIDC) { if ptr.is_null() { @@ -85,21 +79,19 @@ pub extern "C" fn zeroidc_delete(ptr: *mut ZeroIDC) { &mut *ptr }; idc.stop(); - + unsafe { Box::from_raw(ptr); } } -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] #[no_mangle] pub extern "C" fn zeroidc_start(ptr: *mut ZeroIDC) { let idc = unsafe { @@ -109,15 +101,13 @@ pub extern "C" fn zeroidc_start(ptr: *mut ZeroIDC) { idc.start(); } -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] #[no_mangle] pub extern "C" fn zeroidc_stop(ptr: *mut ZeroIDC) { let idc = unsafe { @@ -127,15 +117,13 @@ pub extern "C" fn zeroidc_stop(ptr: *mut ZeroIDC) { idc.stop(); } -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] #[no_mangle] pub extern "C" fn zeroidc_is_running(ptr: *mut ZeroIDC) -> bool { let idc = unsafe { @@ -156,20 +144,19 @@ pub extern "C" fn zeroidc_get_exp_time(ptr: *mut ZeroIDC) -> u64 { id.get_exp_time() } -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] #[no_mangle] pub extern "C" fn zeroidc_set_nonce_and_csrf( ptr: *mut ZeroIDC, csrf_token: *const c_char, - nonce: *const c_char) { + nonce: *const c_char, +) { let idc = unsafe { assert!(!ptr.is_null()); &mut *ptr @@ -193,19 +180,17 @@ pub extern "C" fn zeroidc_set_nonce_and_csrf( .to_str() .unwrap() .to_string(); - + idc.set_nonce_and_csrf(csrf_token, nonce); } -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] #[no_mangle] pub extern "C" fn free_cstr(s: *mut c_char) { if s.is_null() { @@ -218,40 +203,34 @@ pub extern "C" fn free_cstr(s: *mut c_char) { } } -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] #[no_mangle] pub extern "C" fn zeroidc_get_auth_url(ptr: *mut ZeroIDC) -> *mut c_char { if ptr.is_null() { println!("passed a null object"); return std::ptr::null_mut(); } - let idc = unsafe { - &mut *ptr - }; - + let idc = unsafe { &mut *ptr }; + let s = CString::new(idc.auth_url()).unwrap(); - return s.into_raw(); + s.into_raw() } -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] #[no_mangle] -pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, code: *const c_char ) -> *mut c_char { +pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, code: *const c_char) -> *mut c_char { if idc.is_null() { println!("idc is null"); return std::ptr::null_mut(); @@ -261,19 +240,37 @@ pub extern "C" fn zeroidc_token_exchange(idc: *mut ZeroIDC, code: *const c_char println!("code is null"); return std::ptr::null_mut(); } - let idc = unsafe { - &mut *idc - }; + let idc = unsafe { &mut *idc }; - let code = unsafe{CStr::from_ptr(code)}.to_str().unwrap(); + let code = unsafe { CStr::from_ptr(code) }.to_str().unwrap(); - let ret = idc.do_token_exchange( code); - let ret = CString::new(ret).unwrap(); - return ret.into_raw(); + let ret = idc.do_token_exchange(code); + match ret { + Ok(ret) => { + #[cfg(debug_assertions)] + { + println!("do_token_exchange ret: {}", ret); + } + let ret = CString::new(ret).unwrap(); + ret.into_raw() + } + Err(e) => { + #[cfg(debug_assertions)] + { + println!("do_token_exchange err: {}", e); + } + let errstr = format!("{{\"errorMessage\": \"{}\"}}", e); + let ret = CString::new(errstr).unwrap(); + ret.into_raw() + } + } } #[no_mangle] -pub extern "C" fn zeroidc_get_url_param_value(param: *const c_char, path: *const c_char) -> *mut c_char { +pub extern "C" fn zeroidc_get_url_param_value( + param: *const c_char, + path: *const c_char, +) -> *mut c_char { if param.is_null() { println!("param is null"); return std::ptr::null_mut(); @@ -282,21 +279,21 @@ pub extern "C" fn zeroidc_get_url_param_value(param: *const c_char, path: *const println!("path is null"); return std::ptr::null_mut(); } - let param = unsafe {CStr::from_ptr(param)}.to_str().unwrap(); - let path = unsafe {CStr::from_ptr(path)}.to_str().unwrap(); + let param = unsafe { CStr::from_ptr(param) }.to_str().unwrap(); + let path = unsafe { CStr::from_ptr(path) }.to_str().unwrap(); let url = "http://localhost:9993".to_string() + path; let url = Url::parse(&url).unwrap(); - let pairs = url.query_pairs(); + let pairs = url.query_pairs(); for p in pairs { if p.0 == param { let s = CString::new(p.1.into_owned()).unwrap(); - return s.into_raw() + return s.into_raw(); } } - return std::ptr::null_mut(); + std::ptr::null_mut() } #[no_mangle] @@ -306,36 +303,32 @@ pub extern "C" fn zeroidc_network_id_from_state(state: *const c_char) -> *mut c_ return std::ptr::null_mut(); } - let state = unsafe{CStr::from_ptr(state)}.to_str().unwrap(); + let state = unsafe { CStr::from_ptr(state) }.to_str().unwrap(); - let split = state.split("_"); + let split = state.split('_'); let split = split.collect::>(); if split.len() != 2 { return std::ptr::null_mut(); } let s = CString::new(split[1]).unwrap(); - return s.into_raw(); + s.into_raw() } -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] #[no_mangle] pub extern "C" fn zeroidc_kick_refresh_thread(idc: *mut ZeroIDC) { if idc.is_null() { println!("idc is null"); return; } - let idc = unsafe { - &mut *idc - }; + let idc = unsafe { &mut *idc }; idc.kick_refresh_thread(); -} \ No newline at end of file +} diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 9d452a250..dae3cc002 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -19,45 +19,44 @@ extern crate openidconnect; extern crate time; extern crate url; -use crate::error::ZeroIDCError; +use crate::error::*; use bytes::Bytes; -use jwt::{Token}; +use jwt::Token; use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; use openidconnect::reqwest::http_client; -use openidconnect::{AccessToken, AccessTokenHash, AuthorizationCode, AuthenticationFlow, ClientId, CsrfToken, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RefreshToken, Scope, TokenResponse}; +use openidconnect::{ + AccessToken, AccessTokenHash, AuthenticationFlow, AuthorizationCode, ClientId, CsrfToken, + IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, + RefreshToken, Scope, TokenResponse, +}; use std::error::Error; use std::str::from_utf8; use std::sync::{Arc, Mutex}; use std::thread::{sleep, spawn, JoinHandle}; -use std::time::{SystemTime, UNIX_EPOCH, Duration}; -use time::{OffsetDateTime, format_description}; - +use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use time::{format_description, OffsetDateTime}; use url::Url; -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] pub struct ZeroIDC { inner: Arc>, } -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] struct Inner { running: bool, auth_endpoint: String, @@ -82,40 +81,35 @@ impl Inner { } fn csrf_func(csrf_token: String) -> Box CsrfToken> { - return Box::new(move || CsrfToken::new(csrf_token.to_string())); + Box::new(move || CsrfToken::new(csrf_token.to_string())) } fn nonce_func(nonce: String) -> Box Nonce> { - return Box::new(move || Nonce::new(nonce.to_string())); + Box::new(move || Nonce::new(nonce.to_string())) } #[cfg(debug_assertions)] fn systemtime_strftime(dt: T, format: &str) -> String - where T: Into +where + T: Into, { let f = format_description::parse(format); match f { - Ok(f) => { - match dt.into().format(&f) { - Ok(s) => s, - Err(_e) => "".to_string(), - } - }, - Err(_e) => { - "".to_string() + Ok(f) => match dt.into().format(&f) { + Ok(s) => s, + Err(_e) => "".to_string(), }, + Err(_e) => "".to_string(), } } -#[cfg( - any( - all(target_os = "linux", target_arch = "x86"), - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "aarch64"), - target_os = "windows", - target_os = "macos", - ) -)] +#[cfg(any( + all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "aarch64"), + target_os = "windows", + target_os = "macos", +))] impl ZeroIDC { pub fn new( issuer: &str, @@ -137,12 +131,14 @@ impl ZeroIDC { url: None, csrf_token: None, nonce: None, - pkce_verifier: None, + pkce_verifier: None, })), }; - println!("issuer: {}, client_id: {}, auth_endopint: {}, local_web_port: {}", - issuer, client_id, auth_ep, local_web_port); + println!( + "issuer: {}, client_id: {}, auth_endopint: {}, local_web_port: {}", + issuer, client_id, auth_ep, local_web_port + ); let iss = IssuerUrl::new(issuer.to_string())?; let provider_meta = CoreProviderMetadata::discover(&iss, http_client)?; @@ -184,35 +180,53 @@ impl ZeroIDC { let nonce = (*inner_local.lock().unwrap()).nonce.clone(); while running { - let exp = UNIX_EPOCH + Duration::from_secs((*inner_local.lock().unwrap()).exp_time); + let exp = + UNIX_EPOCH + Duration::from_secs((*inner_local.lock().unwrap()).exp_time); let now = SystemTime::now(); - #[cfg(debug_assertions)] { - println!("refresh token thread tick, now: {}, exp: {}", systemtime_strftime(now, "[year]-[month]-[day] [hour]:[minute]:[second]"), systemtime_strftime(exp, "[year]-[month]-[day] [hour]:[minute]:[second]")); + #[cfg(debug_assertions)] + { + println!( + "refresh token thread tick, now: {}, exp: {}", + systemtime_strftime( + now, + "[year]-[month]-[day] [hour]:[minute]:[second]" + ), + systemtime_strftime( + exp, + "[year]-[month]-[day] [hour]:[minute]:[second]" + ) + ); } let refresh_token = (*inner_local.lock().unwrap()).refresh_token.clone(); - - if let Some(refresh_token) = refresh_token { + + if let Some(refresh_token) = refresh_token { let should_kick = (*inner_local.lock().unwrap()).kick; if now >= (exp - Duration::from_secs(30)) || should_kick { if should_kick { - #[cfg(debug_assertions)] { + #[cfg(debug_assertions)] + { println!("refresh thread kicked"); } (*inner_local.lock().unwrap()).kick = false; } - #[cfg(debug_assertions)] { + #[cfg(debug_assertions)] + { println!("Refresh Token: {}", refresh_token.secret()); } - let token_response = (*inner_local.lock().unwrap()).oidc_client.as_ref().map(|c| { - let res = c.exchange_refresh_token(&refresh_token) - .request(http_client); - - res - }); - + let token_response = (*inner_local.lock().unwrap()) + .oidc_client + .as_ref() + .map(|c| { + let res = c + .exchange_refresh_token(&refresh_token) + .request(http_client); + + res + }); + if let Some(res) = token_response { match res { Ok(res) => { @@ -223,78 +237,123 @@ impl ZeroIDC { None => "".to_string(), }; - let params = [("id_token", id_token.to_string()),("state", "refresh".to_string()),("extra_nonce", n)]; - #[cfg(debug_assertions)] { - println!("New ID token: {}", id_token.to_string()); + let params = [ + ("id_token", id_token.to_string()), + ("state", "refresh".to_string()), + ("extra_nonce", n), + ]; + #[cfg(debug_assertions)] + { + println!( + "New ID token: {}", + id_token.to_string() + ); } let client = reqwest::blocking::Client::new(); - let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) + let r = client + .post( + (*inner_local.lock().unwrap()) + .auth_endpoint + .clone(), + ) .form(¶ms) .send(); match r { Ok(r) => { if r.status().is_success() { - #[cfg(debug_assertions)] { - println!("hit url: {}", r.url().as_str()); + #[cfg(debug_assertions)] + { + println!( + "hit url: {}", + r.url().as_str() + ); println!("status: {}", r.status()); } let access_token = res.access_token(); let idt = &id_token.to_string(); - let t: Result>, jwt::Error> = - Token::parse_unverified(idt); - + let t: Result< + Token< + jwt::Header, + jwt::Claims, + jwt::Unverified<'_>, + >, + jwt::Error, + > = Token::parse_unverified(idt); + if let Ok(t) = t { - let claims = t.claims().registered.clone(); + let claims = + t.claims().registered.clone(); match claims.expiration { Some(exp) => { - (*inner_local.lock().unwrap()).exp_time = exp; - }, + (*inner_local + .lock() + .unwrap()) + .exp_time = exp; + } None => { panic!("expiration is None. This shouldn't happen") } } - } + } - (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); + (*inner_local.lock().unwrap()) + .access_token = + Some(access_token.clone()); if let Some(t) = res.refresh_token() { // println!("New Refresh Token: {}", t.secret()); - (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); + (*inner_local.lock().unwrap()) + .refresh_token = + Some(t.clone()); } - #[cfg(debug_assertions)] { + #[cfg(debug_assertions)] + { println!("Central post succeeded"); } } else { - println!("Central post failed: {}", r.status().to_string()); - println!("hit url: {}", r.url().as_str()); + println!( + "Central post failed: {}", + r.status() + ); + println!( + "hit url: {}", + r.url().as_str() + ); println!("Status: {}", r.status()); if let Ok(body) = r.bytes() { - if let Ok(body) = std::str::from_utf8(&body) { + if let Ok(body) = + std::str::from_utf8(&body) + { println!("Body: {}", body); } - } - - (*inner_local.lock().unwrap()).exp_time = 0; - (*inner_local.lock().unwrap()).running = false; + + (*inner_local.lock().unwrap()) + .exp_time = 0; + (*inner_local.lock().unwrap()) + .running = false; } - }, + } Err(e) => { - println!("Central post failed: {}", e.to_string()); - println!("hit url: {}", e.url().unwrap().as_str()); + println!("Central post failed: {}", e); + println!( + "hit url: {}", + e.url().unwrap().as_str() + ); println!("Status: {}", e.status().unwrap()); (*inner_local.lock().unwrap()).exp_time = 0; - (*inner_local.lock().unwrap()).running = false; + (*inner_local.lock().unwrap()).running = + false; } } - }, + } None => { println!("no id token?!?"); } } - }, + } Err(e) => { println!("token error: {}", e); } @@ -323,7 +382,7 @@ impl ZeroIDC { pub fn stop(&mut self) { let local = self.inner.clone(); - if self.is_running(){ + if self.is_running() { (*local.lock().unwrap()).running = false; } } @@ -341,81 +400,72 @@ impl ZeroIDC { pub fn set_nonce_and_csrf(&mut self, csrf_token: String, nonce: String) { let local = Arc::clone(&self.inner); - (*local.lock().expect("can't lock inner")).as_opt().map(|i| { - if i.running { - println!("refresh thread running. not setting new nonce or csrf"); - return - } + let _ = (*local.lock().expect("can't lock inner")) + .as_opt() + .map(|i| { + if i.running { + println!("refresh thread running. not setting new nonce or csrf"); + return; + } - let need_verifier = match i.pkce_verifier { - None => true, - _ => false, - }; + let need_verifier = matches!(i.pkce_verifier, None); - let csrf_diff = if let Some(csrf) = i.csrf_token.clone() { - if *csrf.secret() != csrf_token { - true + let csrf_diff = if let Some(csrf) = i.csrf_token.clone() { + *csrf.secret() != csrf_token } else { false - } - } else { - false - }; + }; - let nonce_diff = if let Some(n) = i.nonce.clone() { - if *n.secret() != nonce { - true + let nonce_diff = if let Some(n) = i.nonce.clone() { + *n.secret() != nonce } else { false + }; + + if need_verifier || csrf_diff || nonce_diff { + let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); + let r = i.oidc_client.as_ref().map(|c| { + let (auth_url, csrf_token, nonce) = c + .authorize_url( + AuthenticationFlow::::AuthorizationCode, + csrf_func(csrf_token), + nonce_func(nonce), + ) + .add_scope(Scope::new("profile".to_string())) + .add_scope(Scope::new("email".to_string())) + .add_scope(Scope::new("offline_access".to_string())) + .add_scope(Scope::new("openid".to_string())) + .set_pkce_challenge(pkce_challenge) + .url(); + + (auth_url, csrf_token, nonce) + }); + + if let Some(r) = r { + i.url = Some(r.0); + i.csrf_token = Some(r.1); + i.nonce = Some(r.2); + i.pkce_verifier = Some(pkce_verifier); + } } - } else { - false - }; - - if need_verifier || csrf_diff || nonce_diff { - let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); - let r = i.oidc_client.as_ref().map(|c| { - let (auth_url, csrf_token, nonce) = c - .authorize_url( - AuthenticationFlow::::AuthorizationCode, - csrf_func(csrf_token), - nonce_func(nonce), - ) - .add_scope(Scope::new("profile".to_string())) - .add_scope(Scope::new("email".to_string())) - .add_scope(Scope::new("offline_access".to_string())) - .add_scope(Scope::new("openid".to_string())) - .set_pkce_challenge(pkce_challenge) - .url(); - - (auth_url, csrf_token, nonce) - }); - - if let Some(r) = r { - i.url = Some(r.0); - i.csrf_token = Some(r.1); - i.nonce = Some(r.2); - i.pkce_verifier = Some(pkce_verifier); - } - } - }); + }); } pub fn auth_url(&self) -> String { - let url = (*self.inner.lock().expect("can't lock inner")).as_opt().map(|i| { - match i.url.clone() { + let url = (*self.inner.lock().expect("can't lock inner")) + .as_opt() + .map(|i| match i.url.clone() { Some(u) => u.to_string(), _ => "".to_string(), - } - }); + }); match url { - Some(url) => url.to_string(), + Some(url) => url, None => "".to_string(), } } - pub fn do_token_exchange(&mut self, code: &str) -> String { + pub fn do_token_exchange(&mut self, code: &str) -> Result { let local = Arc::clone(&self.inner); let mut should_start = false; let res = (*local.lock().unwrap()).as_opt().map(|i| { @@ -423,13 +473,14 @@ impl ZeroIDC { let token_response = i.oidc_client.as_ref().map(|c| { println!("auth code: {}", code); - let r = c.exchange_code(AuthorizationCode::new(code.to_string())) + let r = c + .exchange_code(AuthorizationCode::new(code.to_string())) .set_pkce_verifier(verifier) .request(http_client); // validate the token hashes match r { - Ok(res) =>{ + Ok(res) => { let n = match i.nonce.clone() { Some(n) => n, None => { @@ -437,7 +488,7 @@ impl ZeroIDC { return None; } }; - + let id = match res.id_token() { Some(t) => t, None => { @@ -463,7 +514,10 @@ impl ZeroIDC { }; if let Some(expected_hash) = claims.access_token_hash() { - let actual_hash = match AccessTokenHash::from_token(res.access_token(), &signing_algo) { + let actual_hash = match AccessTokenHash::from_token( + res.access_token(), + &signing_algo, + ) { Ok(h) => h, Err(e) => { println!("Error hashing access token: {}", e); @@ -477,111 +531,131 @@ impl ZeroIDC { } } Some(res) - }, + } Err(e) => { println!("token response error: {:?}", e.to_string()); println!("\t {:?}", e.source()); - - return None; - }, + + None + } } }); - + if let Some(Some(tok)) = token_response { let id_token = tok.id_token().unwrap(); - #[cfg(debug_assertions)] { + #[cfg(debug_assertions)] + { println!("ID token: {}", id_token.to_string()); } let mut split = "".to_string(); - match i.csrf_token.clone() { - Some(csrf_token) => { - split = csrf_token.secret().to_owned(); - }, - _ => (), + if let Some(tok) = i.csrf_token.clone() { + split = tok.secret().to_owned(); } - let split = split.split("_").collect::>(); - + let split = split.split('_').collect::>(); + if split.len() == 2 { - let params = [("id_token", id_token.to_string()),("state", split[0].to_string())]; + let params = [ + ("id_token", id_token.to_string()), + ("state", split[0].to_string()), + ]; let client = reqwest::blocking::Client::new(); - let res = client.post(i.auth_endpoint.clone()) - .form(¶ms) - .send(); + let res = client.post(i.auth_endpoint.clone()).form(¶ms).send(); match res { Ok(res) => { - #[cfg(debug_assertions)] { - println!("hit url: {}", res.url().as_str()); - println!("Status: {}", res.status()); - } + if res.status() == 200 { + #[cfg(debug_assertions)] + { + println!("hit url: {}", res.url().as_str()); + println!("Status: {}", res.status()); + } - let idt = &id_token.to_string(); + let idt = &id_token.to_string(); - let t: Result>, jwt::Error>= - Token::parse_unverified(idt); - - if let Ok(t) = t { - let claims = t.claims().registered.clone(); - match claims.expiration { - Some(exp) => { - i.exp_time = exp; - println!("Set exp time to: {:?}", i.exp_time); - }, - None => { - panic!("expiration is None. This shouldn't happen") + let t: Result< + Token>, + jwt::Error, + > = Token::parse_unverified(idt); + + if let Ok(t) = t { + let claims = t.claims().registered.clone(); + match claims.expiration { + Some(exp) => { + i.exp_time = exp; + println!("Set exp time to: {:?}", i.exp_time); + } + None => { + panic!("expiration is None. This shouldn't happen"); + } } } - } - i.access_token = Some(tok.access_token().clone()); - if let Some(t) = tok.refresh_token() { - i.refresh_token = Some(t.clone()); - should_start = true; + i.access_token = Some(tok.access_token().clone()); + if let Some(t) = tok.refresh_token() { + i.refresh_token = Some(t.clone()); + should_start = true; + } + #[cfg(debug_assertions)] + { + let access_token = tok.access_token(); + println!("Access Token: {}", access_token.secret()); + + let refresh_token = tok.refresh_token(); + println!("Refresh Token: {}", refresh_token.unwrap().secret()); + } + + let bytes = match res.bytes() { + Ok(bytes) => bytes, + Err(_) => Bytes::from(""), + }; + + let bytes = match from_utf8(bytes.as_ref()) { + Ok(bytes) => bytes.to_string(), + Err(_) => "".to_string(), + }; + + Ok(bytes) + } else if res.status() == 402 { + Err(SSOExchangeError::new( + "additional license seats required. Please contact your network administrator.".to_string(), + )) + } else { + Err(SSOExchangeError::new( + "error from central endpoint".to_string(), + )) } - #[cfg(debug_assertions)] { - let access_token = tok.access_token(); - println!("Access Token: {}", access_token.secret()); - - let refresh_token = tok.refresh_token(); - println!("Refresh Token: {}", refresh_token.unwrap().secret()); - } - - let bytes = match res.bytes() { - Ok(bytes) => bytes, - Err(_) => Bytes::from(""), - }; - - let bytes = match from_utf8(bytes.as_ref()) { - Ok(bytes) => bytes.to_string(), - Err(_) => "".to_string(), - }; - - return bytes; - }, + } Err(res) => { + println!("error result: {}", res); println!("hit url: {}", res.url().unwrap().as_str()); println!("Status: {}", res.status().unwrap()); - println!("Post error: {}", res.to_string()); + println!("Post error: {}", res); i.exp_time = 0; + Err(SSOExchangeError::new( + "error from central endpoint".to_string(), + )) } } - - } else { - println!("invalid split length?!?"); + Err(SSOExchangeError::new( + "error splitting state token".to_string(), + )) } + } else { + Err(SSOExchangeError::new("invalid token response".to_string())) } + } else { + Err(SSOExchangeError::new("invalid pkce verifier".to_string())) } - "".to_string() }); if should_start { self.start(); } - return match res { + match res { Some(res) => res, - _ => "".to_string(), - }; + _ => Err(SSOExchangeError::new("invalid result".to_string())), + } } } diff --git a/zerotier-one.spec b/zerotier-one.spec index 1b75a3ae5..b5c35d4d8 100644 --- a/zerotier-one.spec +++ b/zerotier-one.spec @@ -6,28 +6,56 @@ Summary: ZeroTier network virtualization service License: ZeroTier BSL 1.1 URL: https://www.zerotier.com -%if 0%{?rhel} >= 7 -BuildRequires: systemd -%endif +# Fedora -%if 0%{?fedora} >= 21 -BuildRequires: systemd -%endif - -Requires: iproute libstdc++ openssl -AutoReqProv: no - -%if 0%{?rhel} >= 7 -Requires: systemd +%if "%{?dist}" == ".fc35" +BuildRequires: systemd clang openssl openssl-devel +Requires: systemd openssl Requires(pre): /usr/sbin/useradd, /usr/bin/getent %endif -%if 0%{?rhel} <= 6 -Requires: chkconfig +%if "%{?dist}" == ".fc36" +BuildRequires: systemd clang openssl1.1 openssl1.1-devel +Requires: systemd openssl1.1 +Requires(pre): /usr/sbin/useradd, /usr/bin/getent %endif -%if 0%{?fedora} >= 21 -Requires: systemd +%if "%{?dist}" == ".fc37" +BuildRequires: systemd clang openssl1.1 openssl1.1-devel +Requires: systemd openssl1.1 +Requires(pre): /usr/sbin/useradd, /usr/bin/getent +%endif + +# RHEL + +%if "%{?dist}" == ".el6" +Requires: chkconfig +Requires(pre): /usr/sbin/useradd, /usr/bin/getent +%endif + +%if "%{?dist}" == ".el7" +BuildRequires: systemd openssl11-devel +Requires: systemd openssl11 +Requires(pre): /usr/sbin/useradd, /usr/bin/getent +%endif + +%if "%{?dist}" == ".el8" +BuildRequires: systemd openssl-devel +Requires: systemd openssl +Requires(pre): /usr/sbin/useradd, /usr/bin/getent +%endif + +%if "%{?dist}" == ".el9" +BuildRequires: systemd openssl-devel +Requires: systemd openssl +Requires(pre): /usr/sbin/useradd, /usr/bin/getent +%endif + +# Amazon + +%if "%{?dist}" == ".amzn2" +BuildRequires: systemd openssl-devel +Requires: systemd openssl Requires(pre): /usr/sbin/useradd, /usr/bin/getent %endif @@ -44,123 +72,60 @@ like conventional VPNs or VLANs. It can run on native systems, VMs, or containers (Docker, OpenVZ, etc.). %prep -%if 0%{?rhel} >= 7 -rm -rf * -ln -s %{getenv:PWD} %{name}-%{version} -tar --exclude=%{name}-%{version}/.git --exclude=%{name}-%{version}/%{name}-%{version} -czf %{_sourcedir}/%{name}-%{version}.tar.gz %{name}-%{version}/* -rm -f %{name}-%{version} -cp -a %{getenv:PWD}/* . -%endif +# ls -la +# %if 0%{?rhel} && 0%{?rhel} >= 7 +# rm -rf * +# ln -s %{getenv:PWD} %{name}-%{version} +# tar --exclude=%{name}-%{version}/.git --exclude=%{name}-%{version}/%{name}-%{version} -czf %{_sourcedir}/%{name}-%{version}.tar.gz %{name}-%{version}/* +# rm -f %{name}-%{version} +# cp -a %{getenv:PWD}/* . +# %endif %build -#%if 0%{?rhel} <= 7 -#make CFLAGS="`echo %{optflags} | sed s/stack-protector-strong/stack-protector/`" CXXFLAGS="`echo %{optflags} | sed s/stack-protector-strong/stack-protector/`" ZT_USE_MINIUPNPC=1 %{?_smp_mflags} one manpages selftest -#%else -%if 0%{?rhel} >= 7 -make ZT_USE_MINIUPNPC=1 %{?_smp_mflags} one +%if "%{?dist}" == ".el6" +make RUST_BACKTRACE=full ZT_USE_MINIUPNPC=1 %{?_smp_mflags} from_builder +%else +make RUST_BACKTRACE=full ZT_USE_MINIUPNPC=1 %{?_smp_mflags} one %endif %pre -%if 0%{?rhel} >= 7 /usr/bin/getent passwd zerotier-one || /usr/sbin/useradd -r -d /var/lib/zerotier-one -s /sbin/nologin zerotier-one -%endif -%if 0%{?fedora} >= 21 -/usr/bin/getent passwd zerotier-one || /usr/sbin/useradd -r -d /var/lib/zerotier-one -s /sbin/nologin zerotier-one -%endif %install -rm -rf $RPM_BUILD_ROOT -%if 0%{?rhel} < 7 -pushd %{getenv:PWD} -%endif +# rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT -%if 0%{?rhel} < 7 -popd -%endif -%if 0%{?rhel} >= 7 mkdir -p $RPM_BUILD_ROOT%{_unitdir} cp %{getenv:PWD}/debian/zerotier-one.service $RPM_BUILD_ROOT%{_unitdir}/%{name}.service -%endif -%if 0%{?fedora} >= 21 -mkdir -p $RPM_BUILD_ROOT%{_unitdir} -cp ${getenv:PWD}/debian/zerotier-one.service $RPM_BUILD_ROOT%{_unitdir}/%{name}.service -%endif -%if 0%{?rhel} <= 6 -mkdir -p $RPM_BUILD_ROOT/etc/init.d + +%if 0%{?rhel} && 0%{?rhel} <= 6 +mkdir -p $RPM_BUILD_ROOT/etc/init.d/ cp %{getenv:PWD}/ext/installfiles/linux/zerotier-one.init.rhel6 $RPM_BUILD_ROOT/etc/init.d/zerotier-one -chmod 0755 $RPM_BUILD_ROOT/etc/init.d/zerotier-one +chmod 700 $RPM_BUILD_ROOT/etc/init.d/${name} %endif %files %{_sbindir}/* %{_mandir}/* %{_localstatedir}/* -%if 0%{?rhel} >= 7 -%{_unitdir}/%{name}.service -%endif -%if 0%{?fedora} >= 21 -%{_unitdir}/%{name}.service -%endif -%if 0%{?rhel} <= 6 + +%if 0%{?rhel} && 0%{?rhel} <= 6 /etc/init.d/zerotier-one +%else +%{_unitdir}/%{name}.service %endif %post -%if 0%{?rhel} >= 7 +%if ! 0%{?rhel} && 0%{?rhel} <= 6 %systemd_post zerotier-one.service %endif -%if 0%{?fedora} >= 21 -%systemd_post zerotier-one.service -%endif -%if 0%{?rhel} <= 6 -case "$1" in - 1) - chkconfig --add zerotier-one - ;; - 2) - chkconfig --del zerotier-one - chkconfig --add zerotier-one - ;; -esac -if [ -x /usr/bin/checkmodule -a -x /usr/bin/semodule_package -a -x /usr/sbin/semodule ]; then - rm -f /var/lib/zerotier-one/zerotier-one.mod - /usr/bin/checkmodule -M -m -o /var/lib/zerotier-one/zerotier-one.mod /var/lib/zerotier-one/zerotier-one.te - if [ -f /var/lib/zerotier-one/zerotier-one.pp ]; then - rm -f /var/lib/zerotier-one/zerotier-one.pp - /usr/bin/semodule_package -o /var/lib/zerotier-one/zerotier-one.pp -m /var/lib/zerotier-one/zerotier-one.mod - /usr/sbin/semodule -u /var/lib/zerotier-one/zerotier-one.pp - else - /usr/bin/semodule_package -o /var/lib/zerotier-one/zerotier-one.pp -m /var/lib/zerotier-one/zerotier-one.mod - /usr/sbin/semodule -i /var/lib/zerotier-one/zerotier-one.pp - fi -fi -%endif %preun -%if 0%{?rhel} >= 7 +%if ! 0%{?rhel} && 0%{?rhel} <= 6 %systemd_preun zerotier-one.service %endif -%if 0%{?fedora} >= 21 -%systemd_preun zerotier-one.service -%endif -%if 0%{?rhel} <= 6 -case "$1" in - 0) - service zerotier-one stop - chkconfig --del zerotier-one - ;; - 1) - # This is an upgrade. - : - ;; -esac -%endif %postun -%if 0%{?rhel} >= 7 -%systemd_postun_with_restart zerotier-one.service -%endif -%if 0%{?fedora} >= 21 +%if ! 0%{?rhel} && 0%{?rhel} <= 6 %systemd_postun_with_restart zerotier-one.service %endif @@ -171,16 +136,16 @@ esac * Tue May 10 2022 Adam Ierymenko - 1.8.10 - see https://github.com/zerotier/ZeroTierOne for release notes -* Fri Apr 25 2022 Adam Ierymenko - 1.8.9 +* Mon Apr 25 2022 Adam Ierymenko - 1.8.9 - see https://github.com/zerotier/ZeroTierOne for release notes -* Fri Apr 11 2022 Adam Ierymenko - 1.8.8 +* Mon Apr 11 2022 Adam Ierymenko - 1.8.8 - see https://github.com/zerotier/ZeroTierOne for release notes -* Fri Mar 21 2022 Adam Ierymenko - 1.8.7 +* Mon Mar 21 2022 Adam Ierymenko - 1.8.7 - see https://github.com/zerotier/ZeroTierOne for release notes -* Fri Mar 07 2022 Adam Ierymenko - 1.8.6 +* Mon Mar 07 2022 Adam Ierymenko - 1.8.6 - see https://github.com/zerotier/ZeroTierOne for release notes * Fri Dec 17 2021 Adam Ierymenko - 1.8.5 @@ -198,7 +163,7 @@ esac * Wed Oct 20 2021 Adam Ierymenko - 1.8.1 - see https://github.com/zerotier/ZeroTierOne for release notes -* Tue Sep 15 2021 Adam Ierymenko - 1.8.0 +* Wed Sep 15 2021 Adam Ierymenko - 1.8.0 - see https://github.com/zerotier/ZeroTierOne for release notes * Tue Apr 13 2021 Adam Ierymenko - 1.6.5