diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..a6a7cb06bb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,16 @@ +**/.cache +**/.dockerignore +**/.DS_Store +**/.env +**/.git +**/.github +**/.gitignore +**/.vscode +**/bin +**/dist +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +CHANGELOG.md +README.md diff --git a/.github/workflows/package.yaml b/.github/workflows/package.yaml new file mode 100644 index 0000000000..d3c5b1b9d8 --- /dev/null +++ b/.github/workflows/package.yaml @@ -0,0 +1,22 @@ +name: Package +on: + push: + branches: + - main + - tdstein/release +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: extractions/setup-just@v1 + - uses: docker/setup-buildx-action@v2 + - run: just image + - run: just build + - uses: actions/upload-artifact@v3 + with: + path: | + bin/**/*.bin + bin/**/*.exe diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 770a1666c8..6f89182dbe 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -1,6 +1,9 @@ name: Pull Request on: - pull_request + pull_request: + push: + branches: + - tdstein/release # A note on debugging a github step: # You can ssh into the github action environment and poke around by using diff --git a/build/package/Dockerfile b/build/package/Dockerfile index 72b94cc107..2e09e5cee4 100644 --- a/build/package/Dockerfile +++ b/build/package/Dockerfile @@ -1,35 +1,22 @@ -# To build and run shell: `docker run -it $(docker build -q .)` from within `build/package` subdirectory -FROM centos:7 +FROM rockylinux:8 -RUN yum update -y \ - && yum install -y curl git openssl mailcap tzdata xz nodejs xorg-x11-server-Xvfb gtk2-devel gtk3-devel libnotify-devel GConf2 nss libXScrnSaver alsa-lib python3 \ - && yum groupinstall -y "Development Tools" +RUN dnf -y update +RUN dnf -y install\ + curl\ + tar -RUN export GOLANG_VERSION=1.21.0 \ - && export GOLANG_DOWNLOAD_SHA256=d0398903a16ba2232b389fb31032ddf57cac34efda306a0eebac34f0965a0742 \ - && curl -fsSL "https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz" -o golang.tar.gz \ - && echo "${GOLANG_DOWNLOAD_SHA256} golang.tar.gz" | sha256sum -c - \ - && tar -C /usr/local -xzf golang.tar.gz \ - && rm golang.tar.gz -ENV PATH="$PATH:/usr/local/go/bin" +RUN dnf -y install epel-release +RUN dnf -y install ShellCheck -RUN export GOPATH=/usr/local/gotools \ - && go install -v mvdan.cc/sh/v3/cmd/shfmt@latest \ - && go install -v golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest \ - && go install honnef.co/go/tools/cmd/staticcheck@latest -ENV PATH="$PATH:/usr/local/gotools/bin" +RUN dnf -y module enable nodejs:18 +RUN dnf -y module install nodejs:18 -RUN export SHELLCHECK_VERSION=0.9.0 \ - && curl -fsSL https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/shellcheck-v${SHELLCHECK_VERSION}.linux.x86_64.tar.xz \ - | tar -C /usr/local/bin -xJ --strip=1 shellcheck-v${SHELLCHECK_VERSION}/shellcheck \ - && chown $(id -u):$(id -g) /usr/local/bin/shellcheck +RUN curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin -# task runner -RUN export JUST_VERSION=1.14.0 \ - && curl -fsSL https://github.com/casey/just/releases/download/${JUST_VERSION}/just-${JUST_VERSION}-x86_64-unknown-linux-musl.tar.gz \ - | tar -C /usr/local/bin -xz just +RUN curl -L -O https://go.dev/dl/go1.21.1.linux-amd64.tar.gz +RUN rm -rf /usr/local/go +RUN tar -C /usr/local -xzf go1.21.1.linux-amd64.tar.gz +ENV PATH="$PATH:/usr/local/go/bin" -# UX stack -RUN curl -sL https://rpm.nodesource.com/setup_16.x | bash - -RUN yum -y install nodejs -RUN node -v && npm version && npx -v +RUN go env -w GOBIN=/usr/local/go/bin +RUN go install honnef.co/go/tools/cmd/staticcheck@latest diff --git a/internal/project/project.go b/internal/project/project.go index 75072753a8..168e357baf 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -14,5 +14,5 @@ func UserAgent() string { } func DevelopmentBuild() bool { - return Mode == "development" + return Mode == "dev" } diff --git a/justfile b/justfile index 2f5f60104b..b231d75350 100644 --- a/justfile +++ b/justfile @@ -5,6 +5,8 @@ _interactive := `tty -s && echo "-it" || echo ""` _ci := "${CI:-false}" +_mode := "${MODE:-dev}" + _tag := "rstudio/connect-client:latest" _with_runner := if env_var_or_default("DOCKER", "true") == "true" { @@ -21,69 +23,37 @@ _uid_args := if "{{ os() }}" == "Linux" { # bootstrap any supporting packages (such as go package or web UX javascript/typescript dependencies) bootstrap: - # No initialization needed for go code at this time. - # bootstrap client + #!/usr/bin/env bash + set -euo pipefail + {{ _with_runner }} just web/bootstrap -# Clean the agent and web UX build artifacts as well as remove all web UX dependency packages. -clean: clean-agent - just web/clean +# Remove built artifacts +clean: + #!/usr/bin/env bash + set -euo pipefail -# Clean the agent's build artifacts -clean-agent: - rm -rf ./bin/**/connect-client + {{ _with_runner }} just web/clean + {{ _with_runner }} rm -rf ./bin # create the security certificates certs: + #!/usr/bin/env bash + set -euo pipefail + mkdir -p certs mkcert -cert-file ./certs/localhost-cert.pem -key-file ./certs/localhost-key.pem localhost 127.0.0.1 ::1 0.0.0.0 # Build both the web UX and agent for production usage -build: build-web build-agent - -# Build the production agent using the existing build of the Web UX -build-agent: +build: #!/usr/bin/env bash set -euo pipefail - # Have to remove linked server executable, so that switching from production - # to development modes (and vise-versa) will work. - just clean-agent + {{ _with_runner }} env MODE={{ _mode }} just web/build - echo "" - if [ "${BUILD_MODE:-}" == "development" ]; then - echo "Generating a development build of connect-client." - else - echo "Generating production builds of connect-client." - fi - - if {{ _with_runner }} ./scripts/build.bash ./cmd/connect-client; then - echo "Build was successful" - else - echo "" - echo "An error has occurred while building." - echo "" - if [ ! -f "./web/dist/index.html" ]; then - echo "No web SPA artifacts can be found. A web build is required for the backend" - echo "to build. Possibly resolve with 'just web/build' or 'just build'." - fi - fi - -# Build the development agent using the existing build of the Web UX -build-agent-dev: - #!/bin/bash - set -euo pipefail - export BUILD_MODE=development - - just build-agent - -# Build the web UX -build-web: - {{ _with_runner }} just web/build - -# Build the developer stack -build-dev: - just clean image bootstrap build-web build-agent-dev + # _with_runner is not invoked since `./scripts/get-version.bash` executes `docker run`, which would result in a docker-in-docker scenario. + version=$(./scripts/get-version.bash) + {{ _with_runner }} env MODE={{ _mode }} ./scripts/build.bash ./cmd/connect-client $version # Validate the agent and the web UX source code, along with checking for copyrights. validate: @@ -152,7 +122,7 @@ run-agent *args: # Build the image. Typically does not need to be done very often. image: - #!/bin/bash + #!/usr/bin/env bash set -euo pipefail if "${DOCKER:-true}" == "true" ; then @@ -164,12 +134,12 @@ image: fi # Start the agent and show the UI -# NOTE: this must be called from within a docker container if so +# NOTE: this must be called from within a docker container if so # needed (it will not automatically use a running if defined) # This is because this recipe is called from the web/justfile, which # is already executing within a docker container (if configured to use) start-agent-for-e2e: - #!/bin/bash + #!/usr/bin/env bash set -exuo pipefail GOOS=$(go env GOOS) @@ -189,6 +159,9 @@ start-agent-for-e2e: [private] _with_docker *args: + #!/usr/bin/env bash + set -euo pipefail + docker run --rm {{ _interactive }} \ -e CI={{ _ci }} \ -e GOCACHE=/work/.cache/go/cache \ diff --git a/scripts/build.bash b/scripts/build.bash index 1550865e61..dddf64c319 100755 --- a/scripts/build.bash +++ b/scripts/build.bash @@ -1,18 +1,23 @@ #!/usr/bin/env bash -set -eo pipefail +set -e package=$1 -if [[ -z "$package" ]]; then - echo "usage: $0 " +version=$2 +if [[ -z "$package" || -z "$version" ]]; then + echo "usage: $0 " exit 1 fi +echo "Package: $package" +echo "Version: $version" -package_name=$(basename "$package") -platforms=("$(go env GOOS)/$(go env GOARCH)") -version=$(git describe --always --tags) -developmentMode=${BUILD_MODE:-"production"} +name=$(basename "$package") +echo "Name: $name" + +mode=${MODE:-"dev"} +echo "Mode: $mode" +platforms=("$(go env GOOS)/$(go env GOARCH)") if [ "$CI" = "true" ]; then platforms=( "darwin/amd64" @@ -47,23 +52,31 @@ fi for platform in "${platforms[@]}" do - echo "Building $platform" + echo "Building: $platform" platform_split=(${platform//\// }) GOOS=${platform_split[0]} GOARCH=${platform_split[1]} - output_name='./bin/'$GOOS'-'$GOARCH/$package_name + + executable=./bin/$GOOS-$GOARCH/$name-$version if [ "$GOOS" = "windows" ]; then - output_name+='.exe' + executable+='.exe' + else + executable+='.bin' fi - env GOOS="$GOOS" GOARCH="$GOARCH" go build -o "$output_name" -ldflags \ - "-X 'github.com/rstudio/connect-client/internal/project.Version=$version' -X 'github.com/rstudio/connect-client/internal/project.Mode=$developmentMode'" \ + if [ ! -d "./web/dist" ]; then + echo "Error: Missing frontend distribution. Run just web/build." 1>&2 + exit 1 + fi + + env\ + GOOS="$GOOS"\ + GOARCH="$GOARCH"\ + go build\ + -o "$executable"\ + -ldflags "-X 'github.com/rstudio/connect-client/internal/project.Version=$version' -X 'github.com/rstudio/connect-client/internal/project.Mode=$mode'"\ "$package" - if [ $? -ne 0 ]; then - echo 'An error has occurred! Aborting the script execution...' - exit 1 - else - echo "Making $output_name executable" - chmod +x "$output_name" - fi -done \ No newline at end of file + + echo "Executable: $executable" + chmod +x "$executable" +done diff --git a/scripts/get-version.bash b/scripts/get-version.bash new file mode 100755 index 0000000000..b947571895 --- /dev/null +++ b/scripts/get-version.bash @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +docker pull -q ghcr.io/choffmeister/git-describe-semver:latest > /dev/null +docker run --rm -v $PWD:/workdir ghcr.io/choffmeister/git-describe-semver:latest \ + -drop-prefix \ + --fallback v0.0.0 diff --git a/web/justfile b/web/justfile index 1d538e031a..0488d78b12 100644 --- a/web/justfile +++ b/web/justfile @@ -9,7 +9,7 @@ bootstrap: set -euo pipefail # set this environment variable to keep Cypress from attempting - # to create a cache within root's home dir. + # to create a cache within roots home dir. export CYPRESS_CACHE_FOLDER="./cypress/.cache" echo "NOTE: npm install may take five minutes or more from within a docker container..." npm install --no-audit @@ -23,12 +23,7 @@ validate-fix: npm run lint-fix # build the web artifacts for the SPA (into dest/spa) -build: - #!/bin/bash - set -euo pipefail - - just bootstrap - +build: bootstrap npm run build # perform unit, go race and e2e tests @@ -44,7 +39,7 @@ test-e2e-dev: set -euo pipefail # set this environment variable to keep Cypress from attempting to create - # cache within root's home dir. + # cache within roots home dir. export CYPRESS_CACHE_FOLDER="./cypress/.cache" # install cypress npx cypress install @@ -57,7 +52,7 @@ test-e2e: set -euo pipefail # set this environment variable to keep Cypress from attempting to create - # cache within root's home dir. + # cache within roots home dir. export CYPRESS_CACHE_FOLDER="./cypress/.cache" # install cypress npx cypress install @@ -69,7 +64,7 @@ build-and-test-ci-e2e target: set -euo pipefail just build # set this environment variable to keep Cypress from attempting to create - # cache within root's home dir. + # cache within roots home dir. # use a different cache per os export CYPRESS_CACHE_FOLDER="./cypress/{{target}}/.cache" # install cypress