diff --git a/.github/workflows/binder.yaml.disable b/.github/workflows/binder.yaml.disable new file mode 100644 index 0000000..d5571b3 --- /dev/null +++ b/.github/workflows/binder.yaml.disable @@ -0,0 +1,30 @@ +# Reference https://mybinder.readthedocs.io/en/latest/howto/gh-actions-badges.html +name: Test this PR on Binder Badge +on: + pull_request_target: + types: [opened] + +permissions: + pull-requests: + write + +jobs: + binder: + runs-on: ubuntu-latest + steps: + - name: comment on PR with Binder link + uses: actions/github-script@v3 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + var PR_HEAD_USERREPO = process.env.PR_HEAD_USERREPO; + var PR_HEAD_REF = process.env.PR_HEAD_REF; + github.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/${PR_HEAD_USERREPO}/${PR_HEAD_REF}) :point_left: Test this PR on Binder` + }) + env: + PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} + PR_HEAD_USERREPO: ${{ github.event.pull_request.head.repo.full_name }} diff --git a/.github/workflows/build-push-image-commit.yaml b/.github/workflows/build-push-image-commit.yaml new file mode 100644 index 0000000..3c3d7f5 --- /dev/null +++ b/.github/workflows/build-push-image-commit.yaml @@ -0,0 +1,117 @@ +name: Build and push container image, and push update to datahub repo if needed +on: + push: + branches: + - main + +jobs: + build-and-push: + runs-on: ubuntu-latest + env: + DOCKER_CONFIG: $HOME/.docker + IMAGE: ${{ vars.IMAGE }} + outputs: + image-tag: ${{ steps.build-and-push.outputs.IMAGE_SHA_TAG }} + + steps: + - name: Cleanup disk space + run: | + sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc + df -h + + - name: Check out the image repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 # OR "2" -> To retrieve the preceding commit. + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v44 + with: + files_ignore: | + README.md + CONTRIBUTING.md + LICENSE + .github/** + images/** + + - name: Log in to GAR + if: steps.changed-files.outputs.any_changed == 'true' + uses: docker/login-action@v3 + with: + registry: us-central1-docker.pkg.dev + username: _json_key + password: ${{ secrets.GAR_SECRET_KEY }} + + - name: Build the image and push to artifact registry + id: build-and-push + if: steps.changed-files.outputs.any_changed == 'true' + uses: jupyterhub/repo2docker-action@master + with: + DOCKER_REGISTRY: us-central1-docker.pkg.dev + IMAGE_NAME: ${{ env.IMAGE }} + # Disable pushing a 'latest' tag, as this often just causes confusion + LATEST_TAG_OFF: true + # Put repo contents in /srv/repo, rather than the default (/home/jovyan). The home directory + # is mounted over by persistent storage when we are using the built image in a JupyterHub, and + # so all contents put in /home/jovyan are lost. This particularly prevents any 'start' script from + # working, as it is needed in runtime. + REPO_DIR: /srv/repo + + # Lets us monitor disks getting full as images get bigger over time + - name: Show how much disk space is left + run: df -h + + update-deployment-image-tag: + runs-on: ubuntu-latest + needs: build-and-push + env: + HUB: ${{ vars.HUB }} + IMAGE: ${{ vars.IMAGE }} + IMAGE_TAG: ${{ needs.build-and-push.outputs.image-tag }} + + steps: + - name: Checkout the datahub repo + if: ${{ env.IMAGE_TAG }} + uses: actions/checkout@v4 + with: + token: ${{ secrets.DATAHUB_CREATE_PR }} + fetch-depth: 0 + repository: 'berkeley-dsep-infra/datahub' + sparse-checkout: | + deployments/ + hub/ + + - name: Set git identity + if: ${{ env.IMAGE_TAG }} + run: | + git config --global user.email "${{ vars.IMAGE_BUILDER_BOT_EMAIL }}" + git config --global user.name "${{ vars.IMAGE_BUILDER_BOT_NAME }}" + + - name: Update the tag for any deployments that use this image + if: ${{ env.IMAGE_TAG }} + run: | + for deployment in $(grep -lr ${IMAGE} deployments/ | grep hubploy.yaml); do + old_hash=$(grep ${IMAGE} ${deployment} | awk -F":" '{print $3}') + new_hash=${IMAGE_TAG} + sed -i -e "s/${old_hash}/${new_hash}/g" ${deployment} + echo "Updated ${deployment} with new image tag ${new_hash}" + done + + - name: Create feature branch, add, commit and push changes + if: ${{ env.IMAGE_TAG }} + run: | + CHANGED_FILES=$(git status --porcelain -uno | awk '{print $2}') + git diff + git checkout -b update-${HUB}-image-tag-${IMAGE_TAG} + # to be safe, only add files that have changed + for file in $(echo -e ${CHANGED_FILES}); do + git add ${file} + done + git commit -m "update ${HUB} image tag to ${IMAGE_TAG}: ${CHANGED_FILES}" + git push origin update-${HUB}-image-tag-${IMAGE_TAG} + + - name: Print out a message if no PR is created + if: ${{ ! env.IMAGE_TAG }} + run: | + echo "Image not updated, no push to datahub repo required" diff --git a/.github/workflows/build-test-image.yaml b/.github/workflows/build-test-image.yaml new file mode 100644 index 0000000..c28fd9d --- /dev/null +++ b/.github/workflows/build-test-image.yaml @@ -0,0 +1,48 @@ +name: Build and test container image + +on: + pull_request: + +jobs: + test-build: + runs-on: ubuntu-latest + env: + DOCKER_CONFIG: $HOME/.docker + steps: + - name: cleanup disk space + run: | + sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc + df -h + + - name: Checkout files in repo + uses: actions/checkout@v4 + + - name: See if the image config changed + id: changed-files + uses: tj-actions/changed-files@v44 + with: + files_ignore: | + README.md + CONTRIBUTING.md + LICENSE + .github/** + images/** + + - name: What files changed? + if: steps.changed-files.outputs.any_changed == 'true' + env: + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} + run: | + echo "One or more image file(s) has changed:" + echo "$CHANGED_FILES" + + - name: Build and test the image if any image file(s) changed + if: steps.changed-files.outputs.any_changed == 'true' + uses: jupyterhub/repo2docker-action@master + with: + REPO_DIR: /srv/repo + NO_PUSH: true + + # Lets us monitor disks getting full as images get bigger over time + - name: Show how much disk space is left + run: df -h diff --git a/.github/workflows/yaml-lint.yaml b/.github/workflows/yaml-lint.yaml new file mode 100644 index 0000000..787d5e3 --- /dev/null +++ b/.github/workflows/yaml-lint.yaml @@ -0,0 +1,15 @@ +name: "Yaml lint" +on: + - pull_request # yamllint disable-line rule:truthy + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install yamllint + run: pip install yamllint==1.35.1 + + - name: Lint YAML files + run: yamllint --no-warnings . diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6e4761 --- /dev/null +++ b/.gitignore @@ -0,0 +1,129 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..1511315 --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,46 @@ +# This config represents running the command `yamllint -d relaxed .` with the +# following extra rules: +# +# new-line-at-end-of-file: +# level: warning +# trailing-spaces: +# level: warning +# +# We also ignore the cookiecutter directories as these often contain +# jinja-style templating functions that yamllint doesn't play nicely with +# +# cribbed from https://github.com/2i2c-org/infrastructure/blob/main/.yamllint.yaml +--- +extends: default + +ignore: | + **/template/** + **/templates/** + +rules: + braces: + level: warning + max-spaces-inside: 1 + brackets: + level: warning + max-spaces-inside: 1 + colons: + level: warning + commas: + level: warning + comments: disable + comments-indentation: disable + document-start: disable + empty-lines: + level: warning + hyphens: + level: warning + indentation: + level: warning + indent-sequences: consistent + line-length: disable + new-line-at-end-of-file: + level: warning + trailing-spaces: + level: warning + truthy: disable diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8fac084 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,150 @@ +# How to contribute and make changes to your user image + +## Setting up your fork and clones +First, go to your [github profile settings](https://github.com/settings/keys) +and make sure you have an SSH key uploaded. + +Next, go to the github repo of the image that you'd like to work on and create +a fork. To do this, click on the `fork` button and then `Create fork`. + +![Forking](images/create-fork.png) + + +After you create your fork of the new image repository, you should disable Github Actions **only for your fork**. To do this, navigate to `Settings` --> `Actions` --> `General` and select `Disable actions`. Then click `Save`: + +![Disable fork actions](images/disable-fork-actions.png) + +Now clone the primary image repo on your local device. You can get the URL to do +this by clicking on the green `Code` button in the primary image repo (*not* your fork) +and clicking on `SSH` and copying the text located therein: + +![Remote](images/remote.png) + +Now `cd` in to a handy folder on your device, and clone the primary repo by +running the following command (replace `` with the actual name +of the image): + +``` +git clone git@github.com:berkeley-dsep-infra/.git +``` + +Now `cd` in to `` and set up your local repo to point both at the primary +image repo (`upstream`) and your fork (`origin`). After the initial clone, +`origin` will be pointing to the main repo and we'll need to change that. + +``` +$ cd +$ git remote -v # confirm that origin points to the primary repo +origin git@github.com:berkeley-dsep-infra/.git (fetch) +origin git@github.com:berkeley-dsep-infra/.git (push) +$ git remote rename origin upstream # rename origin to upstream +$ git remote add origin git@github.com:/.git # add your fork as origin +$ git remote -v # confirm the settings +origin git@github.com:/.git (fetch) +origin git@github.com:/.git (push) +upstream git@github.com:berkeley-dsep-infra/.git (fetch) +upstream git@github.com:berkeley-dsep-infra/.git (push) +``` + +Now you can sync your local repo from `upstream`, and push those changes to your +fork (`origin`): + +``` +git checkout main && \ +git fetch --prune --all && \ +git rebase upstream/main && \ +git push origin main +``` + +## Procedure + +When developing for this deployment, always work in a fork of this repo. +You should also make sure that your repo is up-to-date with this one prior +to making changes. This is because other contributors may have pushed changes +after you last synced with this repo but before you upstreamed your changes. + +``` +git checkout main && \ +git fetch --prune --all && \ +git rebase upstream/main && \ +git push origin main +``` + +To create a new feature branch and switch to it, run the following command: + +``` +git checkout -b +``` + +After you make your changes, you can use the following commands to see +what's been modified and check out the diffs: `git status` and `git diff`. + +### Building the image locally + +You should use [repo2-docker](https://repo2docker.readthedocs.io/en/latest/) to build and use/test the image on your own device before you push and create a PR. It's better (and typically faster) to do this first before using CI/CD. There's no need to waste Github Action minutes to test build images when you can do this on your own device! + +Run `repo2docker` from inside the cloned image repo. To run on a linux/WSL2 linux shell: +``` +repo2docker . # <--- the path to the repo +``` + +If you are using an ARM CPU (Apple M* silicon), you will need to run `jupyter-repo2docker` with the following arguments: + +``` +jupyter-repo2docker --user-id=1000 --user-name=jovyan \ + --Repo2Docker.platform=linux/amd64 \ + --target-repo-dir=/home/jovyan/.cache \ + -e PLAYWRIGHT_BROWSERS_PATH=/srv/conda \ + . # <--- the path to the repo +``` + +If you just want to see if the image builds, but not automatically launch the server, add `--no-run` to the arguments (before the final `.`). + +When you're ready to push these changes, first you'll need to stage them for a +commit: + +``` +git add +``` + +Commit these changes locally: + +``` +git commit -m "some pithy commit description" +``` + +Now push to your fork: + +``` +git push origin +``` + +Once you've pushed to your fork, you can go to the image repo and there should +be a big green button on the top that says `Compare and pull request`. +Click on that, check out the commits and file diffs, edit the title and +description if needed and then click `Create pull request`. + +![Compare and create PR](images/compare-and-create-pr.png) + +![Create PR](images/create-pr.png) + +If you're having issues, you can refer to the [github documentation for pull +requests](https://help.github.com/articles/about-pull-requests/). +Keep the choice for `base` in the GitHub PR user interface, while the choice +for `head` is your fork. + +Once this is complete and if there are no problems, a github action will +automatically [build and test](https://github.com/berkeley-dsep-infra/hub-user-image-template/blob/main/.github/workflows/build-test-image.yaml) +the image. If this fails, please check the output of the workflow in the +action, and make any changes required to get the build to pass. + +Once the image build has completed successfully, you can request that +someone review the PR before merging, or you can merge yourself if you are +confident. This merge will trigger a [second giuthub workflow](https://github.com/berkeley-dsep-infra/hub-user-image-template/blob/main/.github/workflows/build-push-image-commit.yaml) +that builds the image again, pushes it to the appropriate location in our +Google Artifact Registry and finally creates and pushes a commit to the +[Datahub](https://github.com/berkeley-dsep-infra/datahub) repo updating the +image hash of the deployment to point at the newly built image. + +You will now need to create a pull request in the Datahub repo to merge these changes +and deploy them to `staging` for testing. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4ac657e --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2021, 2i2c-org +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..11edf40 --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# hub-user-image-template :paperclip: + +This is a template repository for creating dedicated user images for UC Berkeley hubs. + +## Overall workflow :gear: + +The overall workflow is to: + +1. Create a new repository using this one as a template. Be sure to set the owner as `berkeley-dsep-infra`. + +2. Fork that repository to create your image repository (optional, but recommended). + +3. Set the appropriate values in the Actions environment variables for `HUB` and `IMAGE`. + +4. Customize the image by editing repo2docker files in your image repository. + + Changes can either be done by direct commits to main on your image repository, or through a pull request from a fork of your image repository. Direct commits will build the image and push it to Google Artifact Registry (GAR) on merge. PRs will also build the image and offer a link to test it using Binder (currently disabled). Merging the PR will also create and push a commit to the [datahub repo](https://github.com/berkeley-dsep-infra/datahub/), which requires a human to open a PR to merge said commit and deploy that image to the proper hub(s). + +5. Configure your Hub to use this new image + +More detailed instructions are [located below](https://github.com/berkeley-dsep-infra/hub-user-image-template/#in-depth-guide). + +### Modifying the new image + +Detailed instructions showing the workflow to modify an image and push it +the CI/CD workflow are located in the [contribution guide](CONTRIBUTING.md) + +### In-depth guide + +Check out the 2i2c docs for an in-depth guide on how to use this template repository to create a custom user image and use it for your hub :arrow_right: https://infrastructure.2i2c.org/howto/update-env/#split-up-an-image-for-use-with-the-repo2docker-action. + +Here's a rough guide on how to create your own fresh user image :arrow_right: https://docs.datahub.berkeley.edu/en/latest/admins/howto/new-image.html. + +After creating a new image repo from here as a template, and bringing in the commit history (if any) of the image, you will need to set two [Github Actions Repository Variables](https://docs.github.com/en/actions/learn-github-actions/variables) for the image: `HUB` and `IMAGE`. + +`HUB` is the short name of the hub (eg: `data100`, `datahub`, etc). +`IMAGE` is the path to the image in the Artifact Registry (eg: `ucb-datahub-2018/user-images/-user-image`) + +Next, you will need to give the newly created repo access to two organizational-level [secrets in the berkeley-dsep-infra repo](https://github.com/organizations/berkeley-dsep-infra/settings/secrets/actions): `GAR_SECRET_KEY` (to allow pushes to the Artifact Registry) and `DATAHUB_USER_IMAGE_BRANCH_PUSH` (to allow commits to be pushed to the [datahub](https://github.com/berkeley-dsep-infra/datahub) repo). + +## About this template repository :information_source: + +This template repository enables [jupyterhub/repo2docker-action](https://github.com/jupyterhub/repo2docker-action). +This GitHub action builds a Docker image using the contents of this repo and pushes it to the [Google Artifact Registry](https://cloud.google.com/artifact-registry) registry. + +### The environment + +It provides an example of a `environment.yml` conda configuration file for repo2docker to use. +This file can be used to list all the conda packages that need to be installed by `repo2docker` in your environment. +The `repo2docker-action` will update the [base repo2docker](https://github.com/jupyterhub/repo2docker/blob/HEAD/repo2docker/buildpacks/conda/environment.yml) conda environment with the packages listed in this `environment.yml` file. + +**Note:** +A complete list of possible configuration files that can be added to the repository and be used by repo2docker to build the Docker image, can be found in the [repo2docker docs](https://repo2docker.readthedocs.io/en/latest/config_files.html#configuration-files). + +### Making changes to a single user server image + +Once you've created the new image repo from this template, please refer to [the contribution instructions](CONTRIBUTING.md) located in the repo for detailed instructions. + +### The GitHub Action workflows + +This template repository provides some GitHub Action workflows that can build and push the image to Google Artifact Repository when configured, and test the image on Binder. + +![Workflows](images/workflows.png) + +#### 1. Build and test container image :arrow_right: [test.yaml](https://github.com/berkeley-dsep-infra/hub-user-image-template/blob/main/.github/workflows/test.yaml) + +This workflow is triggered when a Pull Request is opened against the default branch (`main`).. +During PR builds, the image is **only** built and **not** pushed, unless explicitly configured to do so. + +#### 2. Test this PR on Binder Badge :arrow_right: [binder.yaml](https://github.com/berkeley-dsep-infra/hub-user-image-template/blob/main/.github/workflows/binder.yaml.disable) + +*Temporarily disabled* + +Since our images are typically large and take > 10m to build, this means that Binderhub builds will currently time out. + +This workflow posts a comment inside a pull request, every time a pull request gets opened. The comment contains a "Test this PR on Binder" badge, which can be used to access the image defined by the PR in [mybinder.org](https://mybinder.org/). + +![Test this PR on Binder](images/binder-badge.png) + +#### 3. Build, test and push container image :arrow_right: [build-push-open-pr.yaml](https://github.com/berkeley-dsep-infra/hub-user-image-template/blob/main/.github/workflows/build-push-image-commit.yaml) + +After a PR is merged to `main`, this workflow builds the image again, pushes to the Artifact Registry and will create a push to the [Datahub repo](https://github.com/berkeley-dsep-infra/datahub) to update the image tag for any hubs that use this image. The PR there will need to be created manually. \ No newline at end of file diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..10d9a47 --- /dev/null +++ b/environment.yml @@ -0,0 +1,16 @@ +# This is the standard conda configuration file. Use this file to list +# the conda packages that you need installed in your environment. +channels: + - conda-forge + +dependencies: + - jupyter_contrib_nbextensions==0.5.1 + # Required until https://github.com/jupyterhub/repo2docker/pull/1196 is merged + - jupyterhub-singleuser>=3.0,<4.0 + # Set default python version to 3.10 - repo2docker sets it to 3.7 instead by default, + # which can limit us to older package versions + - python=3.10 + # Everyone wants to use nbgitpuller for everything, so let's do that + - nbgitpuller=1.1.* + # Add other packages here + # - diff --git a/images/binder-badge.png b/images/binder-badge.png new file mode 100644 index 0000000..9326987 Binary files /dev/null and b/images/binder-badge.png differ diff --git a/images/compare-and-create-pr.png b/images/compare-and-create-pr.png new file mode 100644 index 0000000..f0f7adc Binary files /dev/null and b/images/compare-and-create-pr.png differ diff --git a/images/create-fork.png b/images/create-fork.png new file mode 100644 index 0000000..fa826b4 Binary files /dev/null and b/images/create-fork.png differ diff --git a/images/create-pr.png b/images/create-pr.png new file mode 100644 index 0000000..75342bc Binary files /dev/null and b/images/create-pr.png differ diff --git a/images/disable-fork-actions.png b/images/disable-fork-actions.png new file mode 100644 index 0000000..688c5c5 Binary files /dev/null and b/images/disable-fork-actions.png differ diff --git a/images/remote.png b/images/remote.png new file mode 100644 index 0000000..3552b76 Binary files /dev/null and b/images/remote.png differ diff --git a/images/repository-vars.png b/images/repository-vars.png new file mode 100644 index 0000000..442c42a Binary files /dev/null and b/images/repository-vars.png differ diff --git a/images/workflows.png b/images/workflows.png new file mode 100644 index 0000000..b68f355 Binary files /dev/null and b/images/workflows.png differ